Merge "rdbms: set the schema in the LBFactory local domain"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Fri, 2 Feb 2018 04:25:46 +0000 (04:25 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Fri, 2 Feb 2018 04:25:46 +0000 (04:25 +0000)
211 files changed:
.mailmap
CREDITS
RELEASE-NOTES-1.31
composer.json
docs/extension.schema.v2.json
includes/DefaultSettings.php
includes/EditPage.php
includes/HistoryBlob.php
includes/Message.php
includes/Preferences.php
includes/Setup.php
includes/WebRequest.php
includes/api/ApiMain.php
includes/api/ApiModuleManager.php
includes/api/ApiPageSet.php
includes/api/ApiParse.php
includes/api/ApiQuery.php
includes/api/ApiQueryInfo.php
includes/api/ApiQueryRecentChanges.php
includes/api/ApiQueryRevisions.php
includes/api/ApiQuerySiteinfo.php
includes/api/ApiQueryUsers.php
includes/api/ApiTokens.php
includes/api/ApiUpload.php
includes/api/i18n/de.json
includes/api/i18n/en.json
includes/api/i18n/fr.json
includes/api/i18n/hu.json
includes/api/i18n/it.json
includes/api/i18n/ja.json
includes/api/i18n/pt.json
includes/api/i18n/zh-hans.json
includes/cache/MessageCache.php
includes/cache/localisation/LocalisationCache.php
includes/changes/CategoryMembershipChange.php
includes/changetags/ChangeTags.php
includes/changetags/ChangeTagsList.php
includes/db/DatabaseOracle.php
includes/db/MWLBFactory.php
includes/debug/logger/LegacySpi.php
includes/debug/logger/MonologSpi.php
includes/debug/logger/NullSpi.php
includes/deferred/MWCallableUpdate.php
includes/diff/DifferenceEngine.php
includes/exception/MWException.php
includes/exception/MWExceptionHandler.php
includes/exception/MWExceptionRenderer.php
includes/filebackend/FileBackendGroup.php
includes/filebackend/lockmanager/LockManagerGroup.php
includes/filerepo/FileRepo.php
includes/filerepo/ForeignAPIRepo.php
includes/filerepo/ForeignDBRepo.php
includes/filerepo/ForeignDBViaLBRepo.php
includes/filerepo/LocalRepo.php
includes/filerepo/file/File.php
includes/filerepo/file/ForeignAPIFile.php
includes/filerepo/file/LocalFile.php
includes/gallery/ImageGalleryBase.php
includes/htmlform/HTMLForm.php
includes/htmlform/HTMLFormElement.php
includes/htmlform/HTMLFormField.php
includes/htmlform/fields/HTMLMultiSelectField.php
includes/htmlform/fields/HTMLRadioField.php
includes/http/MWHttpRequest.php
includes/import/WikiImporter.php
includes/installer/DatabaseUpdater.php
includes/installer/Installer.php
includes/installer/InstallerOverrides.php
includes/installer/MysqlUpdater.php
includes/installer/SqliteInstaller.php
includes/installer/i18n/pt.json
includes/installer/i18n/sr-ec.json
includes/installer/i18n/uk.json
includes/libs/filebackend/FileBackend.php
includes/libs/filebackend/FileBackendStore.php
includes/libs/lockmanager/MemcLockManager.php
includes/libs/objectcache/WANObjectCache.php
includes/libs/rdbms/TransactionProfiler.php
includes/libs/rdbms/database/Database.php
includes/libs/rdbms/database/DatabaseMssql.php
includes/libs/rdbms/database/DatabaseMysqlBase.php
includes/libs/rdbms/database/DatabaseMysqli.php
includes/libs/rdbms/database/DatabasePostgres.php
includes/libs/rdbms/database/DatabaseSqlite.php
includes/libs/rdbms/database/IDatabase.php
includes/libs/rdbms/database/resultwrapper/MssqlResultWrapper.php
includes/libs/rdbms/loadbalancer/LoadBalancerSingle.php
includes/page/CategoryPage.php
includes/parser/Parser.php
includes/parser/ParserOptions.php
includes/parser/ParserOutput.php
includes/parser/Preprocessor_DOM.php
includes/parser/Preprocessor_Hash.php
includes/parser/Sanitizer.php
includes/password/PasswordFactory.php
includes/preferences/DefaultPreferencesFactory.php
includes/preferences/PreferencesFactory.php
includes/profiler/Profiler.php
includes/profiler/ProfilerSectionOnly.php
includes/profiler/ProfilerXhprof.php
includes/resourceloader/ResourceLoader.php
includes/resourceloader/ResourceLoaderUserOptionsModule.php
includes/resourceloader/ResourceLoaderUserTokensModule.php
includes/revisiondelete/RevisionDeleter.php
includes/search/SearchEngineFactory.php
includes/shell/Command.php
includes/site/SiteList.php
includes/skins/QuickTemplate.php
includes/skins/SkinApi.php
includes/skins/SkinFallback.php
includes/skins/SkinTemplate.php
includes/specialpage/AuthManagerSpecialPage.php
includes/specialpage/QueryPage.php
includes/specialpage/SpecialPageFactory.php
includes/specials/SpecialBotPasswords.php
includes/specials/SpecialDiff.php
includes/specials/SpecialEditWatchlist.php
includes/specials/SpecialExpandTemplates.php
includes/specials/SpecialExport.php
includes/specials/SpecialProtectedpages.php
includes/specials/SpecialTags.php
includes/specials/SpecialUserrights.php
includes/specials/forms/UploadForm.php
includes/specials/pagers/UsersPager.php
includes/user/User.php
includes/watcheditem/WatchedItemStore.php
languages/i18n/ast.json
languages/i18n/be-tarask.json
languages/i18n/bg.json
languages/i18n/bs.json
languages/i18n/ca.json
languages/i18n/ce.json
languages/i18n/cs.json
languages/i18n/de.json
languages/i18n/diq.json
languages/i18n/en.json
languages/i18n/es.json
languages/i18n/et.json
languages/i18n/eu.json
languages/i18n/fa.json
languages/i18n/fr.json
languages/i18n/he.json
languages/i18n/hr.json
languages/i18n/hu.json
languages/i18n/is.json
languages/i18n/it.json
languages/i18n/ja.json
languages/i18n/jv.json
languages/i18n/lb.json
languages/i18n/lij.json
languages/i18n/lv.json
languages/i18n/mk.json
languages/i18n/nl.json
languages/i18n/pt-br.json
languages/i18n/pt.json
languages/i18n/qqq.json
languages/i18n/ru.json
languages/i18n/sah.json
languages/i18n/sd.json
languages/i18n/sl.json
languages/i18n/sr-ec.json
languages/i18n/sty.json [new file with mode: 0644]
languages/i18n/te.json
languages/i18n/tr.json
languages/i18n/yi.json
languages/i18n/zh-hans.json
maintenance/benchmarks/Benchmarker.php
maintenance/cleanupPreferences.php
maintenance/cleanupTitles.php
maintenance/resources/update-oojs-ui.sh
resources/Resources.php
resources/src/jquery/jquery.textSelection.js
resources/src/mediawiki.action/mediawiki.action.view.postEdit.js
resources/src/mediawiki.language/specialcharacters.json
resources/src/mediawiki.less/mediawiki.ui/variables.less
resources/src/mediawiki.rcfilters/styles/mw.rcfilters.less
resources/src/mediawiki.special/mediawiki.special.apisandbox.css
resources/src/mediawiki.special/mediawiki.special.userlogin.login.css
resources/src/mediawiki.widgets.datetime/DateTimeInputWidget.js
resources/src/mediawiki.widgets.datetime/mediawiki.widgets.datetime.definitions.less
resources/src/mediawiki/htmlform/autoinfuse.js
resources/src/mediawiki/htmlform/htmlform.Element.js
resources/src/mediawiki/mediawiki.feedback.js
resources/src/oojs-ui-local.css
resources/src/oojs-ui-local.js
tests/integration/includes/http/MWHttpRequestTestCase.php
tests/parser/ParserTestRunner.php
tests/phpunit/includes/ExtraParserTest.php
tests/phpunit/includes/GlobalFunctions/wfArrayFilterTest.php
tests/phpunit/includes/LinkerTest.php
tests/phpunit/includes/WikiReferenceTest.php
tests/phpunit/includes/db/DatabaseTestHelper.php
tests/phpunit/includes/deferred/MWCallableUpdateTest.php
tests/phpunit/includes/installer/OracleInstallerTest.php
tests/phpunit/includes/libs/ArrayUtilsTest.php
tests/phpunit/includes/libs/IPTest.php
tests/phpunit/includes/libs/objectcache/WANObjectCacheTest.php
tests/phpunit/includes/libs/rdbms/TransactionProfilerTest.php
tests/phpunit/includes/libs/rdbms/database/DatabaseMysqlBaseTest.php
tests/phpunit/includes/libs/rdbms/database/DatabaseSQLTest.php
tests/phpunit/includes/libs/rdbms/database/DatabaseTest.php
tests/phpunit/includes/parser/ParserOptionsTest.php
tests/phpunit/includes/parser/ParserOutputTest.php
tests/phpunit/includes/parser/TagHooksTest.php
tests/phpunit/includes/session/SessionBackendTest.php
tests/phpunit/includes/session/SessionManagerTest.php
tests/phpunit/includes/session/TestBagOStuff.php
tests/phpunit/includes/skins/SkinTemplateTest.php
tests/phpunit/includes/specials/ImageListPagerTest.php
tests/qunit/data/testrunner.js
thumb.php

index c2337c8..08e1aaa 100644 (file)
--- a/.mailmap
+++ b/.mailmap
@@ -197,7 +197,7 @@ Jack Phoenix <jack@countervandalism.net> <ashley@users.mediawiki.org>
 Jackmcbarn <jackmcbarn@gmail.com>
 Jackmcbarn <jackmcbarn@gmail.com> <jackmcbarn@users.noreply.github.com>
 jagori <jagori79@gmail.com>
-James Forrester <jforrester@wikimedia.org>
+James D. Forrester <jforrester@wikimedia.org>
 Jaime Crespo <jcrespo@wikimedia.org>
 Jan Gerber <j@thing.net> <j@users.mediawiki.org>
 Jan Luca Naumann <jan@jans-seite.de>
diff --git a/CREDITS b/CREDITS
index a8ca294..95d6a6c 100644 (file)
--- a/CREDITS
+++ b/CREDITS
@@ -281,7 +281,7 @@ The following list can be found parsed under Special:Version/Credits -->
 * Jaime Crespo
 * Jakub Vrana
 * James Earl Douglas
-* James Forrester
+* James D. Forrester
 * Jan Berkel
 * Jan Drewniak
 * Jan Gerber
index 264113e..ad24852 100644 (file)
@@ -50,7 +50,7 @@ production.
 ==== Upgraded external libraries ====
 * Updated jquery.chosen from v0.9.14 to v1.8.2.
 * Updated composer/spdx-licenses from 1.1.4 to
-  1.2.0 (development dependency).
+  1.3.0 (development dependency).
 * Updated nikic/php-parser from 2.1.0 to 3.1.3
   (development dependency).
 * Updated wikimedia/ip-set from 1.1.0 to 1.2.0.
@@ -190,6 +190,12 @@ changes to languages because of Phabricator reports.
   * PreparedEdit->newText
   * PreparedEdit->oldText
   * PreparedEdit->pst
+* QuickTemplate::setRef() was deprecated in favour of QuickTemplate::set().
+  Setting template variables by reference allowed violating the principle of data being
+  immutable once added to the skin template. In practice, this method was not being
+  used for that. Rather, setRef() existed as memory optimisation for PHP 4.
+* Passing false to ParserOptions::setWrapOutputClass() is deprecated. Use the
+  'unwrap' transform to ParserOutput::getText() instead.
 
 == Compatibility ==
 MediaWiki 1.31 requires PHP 5.5.9 or later. Although HHVM 3.18.5 or later is supported,
index 4596c4c..1730942 100644 (file)
@@ -49,7 +49,7 @@
                "zordius/lightncandy": "0.23"
        },
        "require-dev": {
-               "composer/spdx-licenses": "1.2.0",
+               "composer/spdx-licenses": "1.3.0",
                "hamcrest/hamcrest-php": "^2.0",
                "jakub-onderka/php-parallel-lint": "0.9.2",
                "jetbrains/phpstorm-stubs": "dev-master#1b9906084d6635456fcf3f3a01f0d7d5b99a578a",
index 51f9417..e13129b 100644 (file)
                },
                "SkinOOUIThemes": {
                        "type": "object",
-                       "description": "Map of skin names to OOjs UI themes to use. Same format as ResourceLoaderOOUIModule::$builtinSkinThemeMap."
+                       "description": "Map of skin names to OOUI themes to use. Same format as ResourceLoaderOOUIModule::$builtinSkinThemeMap."
                },
                "PasswordPolicy": {
                        "type": "object",
index 2b2695c..4a625cb 100644 (file)
@@ -1004,15 +1004,15 @@ $wgParserTestMediaHandlers = [
  */
 $wgContentHandlers = [
        // the usual case
-       CONTENT_MODEL_WIKITEXT => 'WikitextContentHandler',
+       CONTENT_MODEL_WIKITEXT => WikitextContentHandler::class,
        // dumb version, no syntax highlighting
-       CONTENT_MODEL_JAVASCRIPT => 'JavaScriptContentHandler',
+       CONTENT_MODEL_JAVASCRIPT => JavaScriptContentHandler::class,
        // simple implementation, for use by extensions, etc.
-       CONTENT_MODEL_JSON => 'JsonContentHandler',
+       CONTENT_MODEL_JSON => JsonContentHandler::class,
        // dumb version, no syntax highlighting
-       CONTENT_MODEL_CSS => 'CssContentHandler',
+       CONTENT_MODEL_CSS => CssContentHandler::class,
        // plain text, for use by extensions, etc.
-       CONTENT_MODEL_TEXT => 'TextContentHandler',
+       CONTENT_MODEL_TEXT => TextContentHandler::class,
 ];
 
 /**
@@ -1817,7 +1817,7 @@ $wgDBtype = 'mysql';
  * Whether to use SSL in DB connection.
  *
  * This setting is only used if $wgLBFactoryConf['class'] is set to
- * 'LBFactorySimple' and $wgDBservers is an empty array; otherwise
+ * '\Wikimedia\Rdbms\LBFactorySimple' and $wgDBservers is an empty array; otherwise
  * the DBO_SSL flag must be set in the 'flags' option of the database
  * connection to achieve the same functionality.
  */
@@ -1827,7 +1827,7 @@ $wgDBssl = false;
  * Whether to use compression in DB connection.
  *
  * This setting is only used $wgLBFactoryConf['class'] is set to
- * 'LBFactorySimple' and $wgDBservers is an empty array; otherwise
+ * '\Wikimedia\Rdbms\LBFactorySimple' and $wgDBservers is an empty array; otherwise
  * the DBO_COMPRESS flag must be set in the 'flags' option of the database
  * connection to achieve the same functionality.
  */
@@ -1998,7 +1998,7 @@ $wgDBservers = false;
  * The LBFactoryMulti class is provided for this purpose, please see
  * includes/db/LBFactoryMulti.php for configuration information.
  */
-$wgLBFactoryConf = [ 'class' => 'LBFactorySimple' ];
+$wgLBFactoryConf = [ 'class' => \Wikimedia\Rdbms\LBFactorySimple::class ];
 
 /**
  * After a state-changing request is done by a client, this determines
@@ -2142,7 +2142,7 @@ $wgExternalStores = [];
  * ];
  * @endcode
  *
- * Used by LBFactorySimple, may be ignored if $wgLBFactoryConf is set to
+ * Used by \Wikimedia\Rdbms\LBFactorySimple, may be ignored if $wgLBFactoryConf is set to
  * another class.
  */
 $wgExternalServers = [];
@@ -2310,34 +2310,34 @@ $wgLanguageConverterCacheType = CACHE_ANYTHING;
  * given, giving a callable function which will generate a suitable cache object.
  */
 $wgObjectCaches = [
-       CACHE_NONE => [ 'class' => 'EmptyBagOStuff', 'reportDupes' => false ],
-       CACHE_DB => [ 'class' => 'SqlBagOStuff', 'loggroup' => 'SQLBagOStuff' ],
+       CACHE_NONE => [ 'class' => EmptyBagOStuff::class, 'reportDupes' => false ],
+       CACHE_DB => [ 'class' => SqlBagOStuff::class, 'loggroup' => 'SQLBagOStuff' ],
 
        CACHE_ANYTHING => [ 'factory' => 'ObjectCache::newAnything' ],
        CACHE_ACCEL => [ 'factory' => 'ObjectCache::getLocalServerInstance' ],
-       CACHE_MEMCACHED => [ 'class' => 'MemcachedPhpBagOStuff', 'loggroup' => 'memcached' ],
+       CACHE_MEMCACHED => [ 'class' => MemcachedPhpBagOStuff::class, 'loggroup' => 'memcached' ],
 
        'db-replicated' => [
-               'class'       => 'ReplicatedBagOStuff',
+               'class'       => ReplicatedBagOStuff::class,
                'readFactory' => [
-                       'class' => 'SqlBagOStuff',
+                       'class' => SqlBagOStuff::class,
                        'args'  => [ [ 'slaveOnly' => true ] ]
                ],
                'writeFactory' => [
-                       'class' => 'SqlBagOStuff',
+                       'class' => SqlBagOStuff::class,
                        'args'  => [ [ 'slaveOnly' => false ] ]
                ],
                'loggroup'  => 'SQLBagOStuff',
                'reportDupes' => false
        ],
 
-       'apc' => [ 'class' => 'APCBagOStuff', 'reportDupes' => false ],
-       'apcu' => [ 'class' => 'APCUBagOStuff', 'reportDupes' => false ],
-       'xcache' => [ 'class' => 'XCacheBagOStuff', 'reportDupes' => false ],
-       'wincache' => [ 'class' => 'WinCacheBagOStuff', 'reportDupes' => false ],
-       'memcached-php' => [ 'class' => 'MemcachedPhpBagOStuff', 'loggroup' => 'memcached' ],
-       'memcached-pecl' => [ 'class' => 'MemcachedPeclBagOStuff', 'loggroup' => 'memcached' ],
-       'hash' => [ 'class' => 'HashBagOStuff', 'reportDupes' => false ],
+       'apc' => [ 'class' => APCBagOStuff::class, 'reportDupes' => false ],
+       'apcu' => [ 'class' => APCUBagOStuff::class, 'reportDupes' => false ],
+       'xcache' => [ 'class' => XCacheBagOStuff::class, 'reportDupes' => false ],
+       'wincache' => [ 'class' => WinCacheBagOStuff::class, 'reportDupes' => false ],
+       'memcached-php' => [ 'class' => MemcachedPhpBagOStuff::class, 'loggroup' => 'memcached' ],
+       'memcached-pecl' => [ 'class' => MemcachedPeclBagOStuff::class, 'loggroup' => 'memcached' ],
+       'hash' => [ 'class' => HashBagOStuff::class, 'reportDupes' => false ],
 ];
 
 /**
@@ -2374,13 +2374,13 @@ $wgMainWANCache = false;
  */
 $wgWANObjectCaches = [
        CACHE_NONE => [
-               'class'    => 'WANObjectCache',
+               'class'    => WANObjectCache::class,
                'cacheId'  => CACHE_NONE,
                'channels' => []
        ]
        /* Example of a simple single data-center cache:
        'memcached-php' => [
-               'class'    => 'WANObjectCache',
+               'class'    => WANObjectCache::class,
                'cacheId'  => 'memcached-php',
                'channels' => [ 'purge' => 'wancache-main-memcached-purge' ]
        ]
@@ -2527,7 +2527,7 @@ $wgAdaptiveMessageCache = false;
  *                  Use maintenance/rebuildLocalisationCache.php instead.
  */
 $wgLocalisationCacheConf = [
-       'class' => 'LocalisationCache',
+       'class' => LocalisationCache::class,
        'store' => 'detect',
        'storeClass' => false,
        'storeDirectory' => false,
@@ -3783,10 +3783,11 @@ $wgResourceLoaderValidateStaticJS = false;
  */
 $wgResourceLoaderLESSVars = [
        /**
-        * Minimum available screen width at which a device can be considered a tablet/desktop
+        * Minimum available screen width at which a device can be considered a tablet
         * The number is currently based on the device width of a Samsung Galaxy S5 mini and is low
         * enough to cover iPad (768px). Number is prone to change with new information.
         * @since 1.27
+        * @deprecated 1.31 Use mediawiki.ui/variables instead
         */
        'deviceWidthTablet' => '720px',
 ];
@@ -4166,8 +4167,8 @@ $wgInvalidRedirectTargets = [ 'Filepath', 'Mypage', 'Mytalk', 'Redirect' ];
  * an extension setup function.
  */
 $wgParserConf = [
-       'class' => 'Parser',
-       # 'preprocessorClass' => 'Preprocessor_Hash',
+       'class' => Parser::class,
+       # 'preprocessorClass' => Preprocessor_Hash::class,
 ];
 
 /**
@@ -4471,7 +4472,7 @@ $wgActiveUserDays = 30;
  * @since 1.27
  */
 $wgCentralIdLookupProviders = [
-       'local' => [ 'class' => 'LocalIdLookup' ],
+       'local' => [ 'class' => LocalIdLookup::class ],
 ];
 
 /**
@@ -4759,7 +4760,7 @@ $wgPasswordDefault = 'pbkdf2';
  * An advanced example:
  * @code
  * $wgPasswordConfig['bcrypt-peppered'] = [
- *     'class' => 'EncryptedPassword',
+ *     'class' => EncryptedPassword::class,
  *     'underlying' => 'bcrypt',
  *     'secrets' => [],
  *     'cipher' => MCRYPT_RIJNDAEL_256,
@@ -4772,31 +4773,31 @@ $wgPasswordDefault = 'pbkdf2';
  */
 $wgPasswordConfig = [
        'A' => [
-               'class' => 'MWOldPassword',
+               'class' => MWOldPassword::class,
        ],
        'B' => [
-               'class' => 'MWSaltedPassword',
+               'class' => MWSaltedPassword::class,
        ],
        'pbkdf2-legacyA' => [
-               'class' => 'LayeredParameterizedPassword',
+               'class' => LayeredParameterizedPassword::class,
                'types' => [
                        'A',
                        'pbkdf2',
                ],
        ],
        'pbkdf2-legacyB' => [
-               'class' => 'LayeredParameterizedPassword',
+               'class' => LayeredParameterizedPassword::class,
                'types' => [
                        'B',
                        'pbkdf2',
                ],
        ],
        'bcrypt' => [
-               'class' => 'BcryptPassword',
+               'class' => BcryptPassword::class,
                'cost' => 9,
        ],
        'pbkdf2' => [
-               'class' => 'Pbkdf2Password',
+               'class' => Pbkdf2Password::class,
                'algo' => 'sha512',
                'cost' => '30000',
                'length' => '64',
@@ -6106,7 +6107,7 @@ $wgDebugComments = false;
  * Write SQL queries to the debug log.
  *
  * This setting is only used $wgLBFactoryConf['class'] is set to
- * 'LBFactorySimple' and $wgDBservers is an empty array; otherwise
+ * '\Wikimedia\Rdbms\LBFactorySimple' and $wgDBservers is an empty array; otherwise
  * the DBO_DEBUG flag must be set in the 'flags' option of the database
  * connection to achieve the same functionality.
  */
@@ -6211,7 +6212,7 @@ $wgDebugLogGroups = [];
  *
  * @par To completely disable logging:
  * @code
- * $wgMWLoggerDefaultSpi = [ 'class' => '\\MediaWiki\\Logger\\NullSpi' ];
+ * $wgMWLoggerDefaultSpi = [ 'class' => \MediaWiki\Logger\NullSpi::class ];
  * @endcode
  *
  * @since 1.25
@@ -6219,7 +6220,7 @@ $wgDebugLogGroups = [];
  * @see MwLogger
  */
 $wgMWLoggerDefaultSpi = [
-       'class' => '\\MediaWiki\\Logger\\LegacySpi',
+       'class' => \MediaWiki\Logger\LegacySpi::class,
 ];
 
 /**
@@ -6771,7 +6772,7 @@ $wgRCLinkDays = [ 1, 3, 7, 14, 30 ];
  *             'omit_bots' => true,
  *     ];
  * @example $wgRCFeeds['example'] = [
- *             'class' => 'ExampleRCFeed',
+ *             'class' => ExampleRCFeed::class,
  *     ];
  * @since 1.22
  */
@@ -6783,8 +6784,8 @@ $wgRCFeeds = [];
  * @since 1.22
  */
 $wgRCEngines = [
-       'redis' => 'RedisPubSubFeedEngine',
-       'udp' => 'UDPRCFeedEngine',
+       'redis' => RedisPubSubFeedEngine::class,
+       'udp' => UDPRCFeedEngine::class,
 ];
 
 /**
@@ -6908,8 +6909,8 @@ $wgOverrideSiteFeed = [];
  * $wgOut->isSyndicated() is true.
  */
 $wgFeedClasses = [
-       'rss' => 'RSSFeed',
-       'atom' => 'AtomFeed',
+       'rss' => RSSFeed::class,
+       'atom' => AtomFeed::class,
 ];
 
 /**
@@ -7436,24 +7437,24 @@ $wgServiceWiringFiles = [
  * or (since 1.30) a callback to use for creating the job object.
  */
 $wgJobClasses = [
-       'refreshLinks' => 'RefreshLinksJob',
-       'deleteLinks' => 'DeleteLinksJob',
-       'htmlCacheUpdate' => 'HTMLCacheUpdateJob',
-       'sendMail' => 'EmaillingJob',
-       'enotifNotify' => 'EnotifNotifyJob',
-       'fixDoubleRedirect' => 'DoubleRedirectJob',
-       'AssembleUploadChunks' => 'AssembleUploadChunksJob',
-       'PublishStashedFile' => 'PublishStashedFileJob',
-       'ThumbnailRender' => 'ThumbnailRenderJob',
-       'recentChangesUpdate' => 'RecentChangesUpdateJob',
-       'refreshLinksPrioritized' => 'RefreshLinksJob',
-       'refreshLinksDynamic' => 'RefreshLinksJob',
-       'activityUpdateJob' => 'ActivityUpdateJob',
-       'categoryMembershipChange' => 'CategoryMembershipChangeJob',
-       'clearUserWatchlist' => 'ClearUserWatchlistJob',
-       'cdnPurge' => 'CdnPurgeJob',
-       'enqueue' => 'EnqueueJob', // local queue for multi-DC setups
-       'null' => 'NullJob'
+       'refreshLinks' => RefreshLinksJob::class,
+       'deleteLinks' => DeleteLinksJob::class,
+       'htmlCacheUpdate' => HTMLCacheUpdateJob::class,
+       'sendMail' => EmaillingJob::class,
+       'enotifNotify' => EnotifNotifyJob::class,
+       'fixDoubleRedirect' => DoubleRedirectJob::class,
+       'AssembleUploadChunks' => AssembleUploadChunksJob::class,
+       'PublishStashedFile' => PublishStashedFileJob::class,
+       'ThumbnailRender' => ThumbnailRenderJob::class,
+       'recentChangesUpdate' => RecentChangesUpdateJob::class,
+       'refreshLinksPrioritized' => RefreshLinksJob::class,
+       'refreshLinksDynamic' => RefreshLinksJob::class,
+       'activityUpdateJob' => ActivityUpdateJob::class,
+       'categoryMembershipChange' => CategoryMembershipChangeJob::class,
+       'clearUserWatchlist' => ClearUserWatchlistJob::class,
+       'cdnPurge' => CdnPurgeJob::class,
+       'enqueue' => EnqueueJob::class, // local queue for multi-DC setups
+       'null' => NullJob::class,
 ];
 
 /**
@@ -7502,7 +7503,7 @@ $wgJobSerialCommitThreshold = false;
  * These settings should be global to all wikis.
  */
 $wgJobTypeConf = [
-       'default' => [ 'class' => 'JobQueueDB', 'order' => 'random', 'claimTTL' => 3600 ],
+       'default' => [ 'class' => JobQueueDB::class, 'order' => 'random', 'claimTTL' => 3600 ],
 ];
 
 /**
@@ -7510,7 +7511,7 @@ $wgJobTypeConf = [
  * These settings should be global to all wikis.
  */
 $wgJobQueueAggregator = [
-       'class' => 'JobQueueAggregatorNull'
+       'class' => JobQueueAggregatorNull::class
 ];
 
 /**
@@ -7531,7 +7532,7 @@ $wgJobQueueIncludeInMaxLagFactor = false;
  * Expensive Querypages are already updated.
  */
 $wgSpecialPageCacheUpdates = [
-       'Statistics' => [ 'SiteStatsUpdate', 'cacheUpdate' ]
+       'Statistics' => [ SiteStatsUpdate::class, 'cacheUpdate' ]
 ];
 
 /**
@@ -7721,42 +7722,42 @@ $wgLogActions = [];
  * @see LogFormatter
  */
 $wgLogActionsHandlers = [
-       'block/block' => 'BlockLogFormatter',
-       'block/reblock' => 'BlockLogFormatter',
-       'block/unblock' => 'BlockLogFormatter',
-       'contentmodel/change' => 'ContentModelLogFormatter',
-       'contentmodel/new' => 'ContentModelLogFormatter',
-       'delete/delete' => 'DeleteLogFormatter',
-       'delete/delete_redir' => 'DeleteLogFormatter',
-       'delete/event' => 'DeleteLogFormatter',
-       'delete/restore' => 'DeleteLogFormatter',
-       'delete/revision' => 'DeleteLogFormatter',
-       'import/interwiki' => 'ImportLogFormatter',
-       'import/upload' => 'ImportLogFormatter',
-       'managetags/activate' => 'LogFormatter',
-       'managetags/create' => 'LogFormatter',
-       'managetags/deactivate' => 'LogFormatter',
-       'managetags/delete' => 'LogFormatter',
-       'merge/merge' => 'MergeLogFormatter',
-       'move/move' => 'MoveLogFormatter',
-       'move/move_redir' => 'MoveLogFormatter',
-       'patrol/patrol' => 'PatrolLogFormatter',
-       'patrol/autopatrol' => 'PatrolLogFormatter',
-       'protect/modify' => 'ProtectLogFormatter',
-       'protect/move_prot' => 'ProtectLogFormatter',
-       'protect/protect' => 'ProtectLogFormatter',
-       'protect/unprotect' => 'ProtectLogFormatter',
-       'rights/autopromote' => 'RightsLogFormatter',
-       'rights/rights' => 'RightsLogFormatter',
-       'suppress/block' => 'BlockLogFormatter',
-       'suppress/delete' => 'DeleteLogFormatter',
-       'suppress/event' => 'DeleteLogFormatter',
-       'suppress/reblock' => 'BlockLogFormatter',
-       'suppress/revision' => 'DeleteLogFormatter',
-       'tag/update' => 'TagLogFormatter',
-       'upload/overwrite' => 'UploadLogFormatter',
-       'upload/revert' => 'UploadLogFormatter',
-       'upload/upload' => 'UploadLogFormatter',
+       'block/block' => BlockLogFormatter::class,
+       'block/reblock' => BlockLogFormatter::class,
+       'block/unblock' => BlockLogFormatter::class,
+       'contentmodel/change' => ContentModelLogFormatter::class,
+       'contentmodel/new' => ContentModelLogFormatter::class,
+       'delete/delete' => DeleteLogFormatter::class,
+       'delete/delete_redir' => DeleteLogFormatter::class,
+       'delete/event' => DeleteLogFormatter::class,
+       'delete/restore' => DeleteLogFormatter::class,
+       'delete/revision' => DeleteLogFormatter::class,
+       'import/interwiki' => ImportLogFormatter::class,
+       'import/upload' => ImportLogFormatter::class,
+       'managetags/activate' => LogFormatter::class,
+       'managetags/create' => LogFormatter::class,
+       'managetags/deactivate' => LogFormatter::class,
+       'managetags/delete' => LogFormatter::class,
+       'merge/merge' => MergeLogFormatter::class,
+       'move/move' => MoveLogFormatter::class,
+       'move/move_redir' => MoveLogFormatter::class,
+       'patrol/patrol' => PatrolLogFormatter::class,
+       'patrol/autopatrol' => PatrolLogFormatter::class,
+       'protect/modify' => ProtectLogFormatter::class,
+       'protect/move_prot' => ProtectLogFormatter::class,
+       'protect/protect' => ProtectLogFormatter::class,
+       'protect/unprotect' => ProtectLogFormatter::class,
+       'rights/autopromote' => RightsLogFormatter::class,
+       'rights/rights' => RightsLogFormatter::class,
+       'suppress/block' => BlockLogFormatter::class,
+       'suppress/delete' => DeleteLogFormatter::class,
+       'suppress/event' => DeleteLogFormatter::class,
+       'suppress/reblock' => BlockLogFormatter::class,
+       'suppress/revision' => DeleteLogFormatter::class,
+       'tag/update' => TagLogFormatter::class,
+       'upload/overwrite' => UploadLogFormatter::class,
+       'upload/revert' => UploadLogFormatter::class,
+       'upload/upload' => UploadLogFormatter::class,
 ];
 
 /**
@@ -7887,7 +7888,7 @@ $wgActions = [
        'credits' => true,
        'delete' => true,
        'edit' => true,
-       'editchangetags' => 'SpecialPageAction',
+       'editchangetags' => SpecialPageAction::class,
        'history' => true,
        'info' => true,
        'markpatrolled' => true,
@@ -7896,7 +7897,7 @@ $wgActions = [
        'raw' => true,
        'render' => true,
        'revert' => true,
-       'revisiondelete' => 'SpecialPageAction',
+       'revisiondelete' => SpecialPageAction::class,
        'rollback' => true,
        'submit' => true,
        'unprotect' => true,
@@ -8045,12 +8046,12 @@ $wgDebugAPI = false;
  * @code
  *  $wgAPIModules['foo'] = 'ApiFoo';
  *  $wgAPIModules['bar'] = [
- *    'class' => 'ApiBar',
+ *    'class' => ApiBar::class,
  *    'factory' => function( $main, $name ) { ... }
  *  ];
  *  $wgAPIModules['xyzzy'] = [
- *    'class' => 'ApiXyzzy',
- *    'factory' => [ 'XyzzyFactory', 'newApiModule' ]
+ *    'class' => ApiXyzzy::class,
+ *    'factory' => [ XyzzyFactory::class, 'newApiModule' ]
  *  ];
  * @endcode
  *
@@ -8473,7 +8474,7 @@ $wgRedirectOnLogin = null;
  * @par Example using local redis instance:
  * @code
  *   $wgPoolCounterConf = [ 'ArticleView' => [
- *     'class' => 'PoolCounterRedis',
+ *     'class' => PoolCounterRedis::class,
  *     'timeout' => 15, // wait timeout in seconds
  *     'workers' => 1, // maximum number of active threads in each pool
  *     'maxqueue' => 5, // maximum number of total threads in each pool
@@ -8485,7 +8486,7 @@ $wgRedirectOnLogin = null;
  * @par Example using C daemon from https://www.mediawiki.org/wiki/Extension:PoolCounter:
  * @code
  *   $wgPoolCounterConf = [ 'ArticleView' => [
- *     'class' => 'PoolCounter_Client',
+ *     'class' => PoolCounter_Client::class,
  *     'timeout' => 15, // wait timeout in seconds
  *     'workers' => 5, // maximum number of active threads in each pool
  *     'maxqueue' => 50, // maximum number of total threads in each pool
@@ -8559,7 +8560,7 @@ $wgTextModelsToParse = [
  * @since 1.20
  */
 $wgSiteTypes = [
-       'mediawiki' => 'MediaWikiSite',
+       'mediawiki' => MediaWikiSite::class,
 ];
 
 /**
@@ -8633,7 +8634,7 @@ $wgPageLanguageUseDB = false;
  * Auto-mounting example for Parsoid:
  *
  * $wgVirtualRestConfig['paths']['/parsoid/'] = [
- *     'class' => 'ParsoidVirtualRESTService',
+ *     'class' => ParsoidVirtualRESTService::class,
  *     'options' => [
  *         'url' => 'http://localhost:8000',
  *         'prefix' => 'enwiki',
@@ -8730,7 +8731,7 @@ $wgMaxJobDBWriteDuration = false;
  */
 $wgEventRelayerConfig = [
        'default' => [
-               'class' => 'EventRelayerNull',
+               'class' => EventRelayerNull::class,
        ]
 ];
 
index d49f205..6bf3c89 100644 (file)
@@ -2472,12 +2472,22 @@ ERROR;
                        $displayTitle = $contextTitle->getPrefixedText();
                }
                $out->setPageTitle( $this->context->msg( $msg, $displayTitle ) );
+
+               $config = $this->context->getConfig();
+
                # Transmit the name of the message to JavaScript for live preview
                # Keep Resources.php/mediawiki.action.edit.preview in sync with the possible keys
                $out->addJsConfigVars( [
                        'wgEditMessage' => $msg,
-                       'wgAjaxEditStash' => $this->context->getConfig()->get( 'AjaxEditStash' ),
+                       'wgAjaxEditStash' => $config->get( 'AjaxEditStash' ),
                ] );
+
+               // Add whether to use 'save' or 'publish' messages to JavaScript for post-edit, other
+               // editors, etc.
+               $out->addJsConfigVars(
+                       'wgEditSubmitButtonLabelPublish',
+                       $config->get( 'EditSubmitButtonLabelPublish' )
+               );
        }
 
        /**
index 075b48d..26a6d45 100644 (file)
@@ -707,7 +707,7 @@ if ( false ) {
        // autoload entries for the lowercase variants of these classes (T166759).
        // The code below is never executed, but it is picked up by the AutoloadGenerator
        // parser, which scans for class_alias() calls.
-       class_alias( 'ConcatenatedGzipHistoryBlob', 'concatenatedgziphistoryblob' );
-       class_alias( 'HistoryBlobCurStub', 'historyblobcurstub' );
-       class_alias( 'HistoryBlobStub', 'historyblobstub' );
+       class_alias( ConcatenatedGzipHistoryBlob::class, 'concatenatedgziphistoryblob' );
+       class_alias( HistoryBlobCurStub::class, 'historyblobcurstub' );
+       class_alias( HistoryBlobStub::class, 'historyblobstub' );
 }
index e55eaaf..fac9a59 100644 (file)
@@ -1245,7 +1245,14 @@ class Message implements MessageSpecifier, Serializable {
                );
 
                return $out instanceof ParserOutput
-                       ? $out->getText( [ 'enableSectionEditLinks' => false ] )
+                       ? $out->getText( [
+                               'enableSectionEditLinks' => false,
+                               // Wrapping messages in an extra <div> is probably not expected. If
+                               // they're outside the content area they probably shouldn't be
+                               // targeted by CSS that's targeting the parser output, and if
+                               // they're inside they already are from the outer div.
+                               'unwrap' => true,
+                       ] )
                        : $out;
        }
 
index f65b2ce..26e28ba 100644 (file)
@@ -267,7 +267,7 @@ class Preferences {
        public static function getFormObject(
                $user,
                IContextSource $context,
-               $formClass = 'PreferencesForm',
+               $formClass = PreferencesForm::class,
                array $remove = []
        ) {
                $preferencesFactory = self::getDefaultPreferencesFactory();
index 3e37c9c..c9fe8d0 100644 (file)
@@ -249,12 +249,12 @@ $wgNamespaceAliases['Image_talk'] = NS_FILE_TALK;
  */
 $wgLockManagers[] = [
        'name' => 'fsLockManager',
-       'class' => 'FSLockManager',
+       'class' => FSLockManager::class,
        'lockDirectory' => "{$wgUploadDirectory}/lockdir",
 ];
 $wgLockManagers[] = [
        'name' => 'nullLockManager',
-       'class' => 'NullLockManager',
+       'class' => NullLockManager::class,
 ];
 
 /**
@@ -276,7 +276,7 @@ $wgGalleryOptions += [
  */
 if ( !$wgLocalFileRepo ) {
        $wgLocalFileRepo = [
-               'class' => 'LocalRepo',
+               'class' => LocalRepo::class,
                'name' => 'local',
                'directory' => $wgUploadDirectory,
                'scriptDirUrl' => $wgScriptPath,
@@ -295,7 +295,7 @@ if ( !$wgLocalFileRepo ) {
 if ( $wgUseSharedUploads ) {
        if ( $wgSharedUploadDBname ) {
                $wgForeignFileRepos[] = [
-                       'class' => 'ForeignDBRepo',
+                       'class' => ForeignDBRepo::class,
                        'name' => 'shared',
                        'directory' => $wgSharedUploadDirectory,
                        'url' => $wgSharedUploadPath,
@@ -315,7 +315,7 @@ if ( $wgUseSharedUploads ) {
                ];
        } else {
                $wgForeignFileRepos[] = [
-                       'class' => 'FileRepo',
+                       'class' => FileRepo::class,
                        'name' => 'shared',
                        'directory' => $wgSharedUploadDirectory,
                        'url' => $wgSharedUploadPath,
@@ -329,7 +329,7 @@ if ( $wgUseSharedUploads ) {
 }
 if ( $wgUseInstantCommons ) {
        $wgForeignFileRepos[] = [
-               'class' => 'ForeignAPIRepo',
+               'class' => ForeignAPIRepo::class,
                'name' => 'wikimediacommons',
                'apibase' => 'https://commons.wikimedia.org/w/api.php',
                'url' => 'https://upload.wikimedia.org/wikipedia/commons',
@@ -349,7 +349,7 @@ if ( !isset( $wgLocalFileRepo['backend'] ) ) {
        $wgLocalFileRepo['backend'] = $wgLocalFileRepo['name'] . '-backend';
 }
 foreach ( $wgForeignFileRepos as &$repo ) {
-       if ( !isset( $repo['directory'] ) && $repo['class'] === 'ForeignAPIRepo' ) {
+       if ( !isset( $repo['directory'] ) && $repo['class'] === ForeignAPIRepo::class ) {
                $repo['directory'] = $wgUploadDirectory; // b/c
        }
        if ( !isset( $repo['backend'] ) ) {
@@ -538,16 +538,16 @@ if ( $wgNewUserLog ) {
        $wgLogTypes[] = 'newusers';
        $wgLogNames['newusers'] = 'newuserlogpage';
        $wgLogHeaders['newusers'] = 'newuserlogpagetext';
-       $wgLogActionsHandlers['newusers/newusers'] = 'NewUsersLogFormatter';
-       $wgLogActionsHandlers['newusers/create'] = 'NewUsersLogFormatter';
-       $wgLogActionsHandlers['newusers/create2'] = 'NewUsersLogFormatter';
-       $wgLogActionsHandlers['newusers/byemail'] = 'NewUsersLogFormatter';
-       $wgLogActionsHandlers['newusers/autocreate'] = 'NewUsersLogFormatter';
+       $wgLogActionsHandlers['newusers/newusers'] = NewUsersLogFormatter::class;
+       $wgLogActionsHandlers['newusers/create'] = NewUsersLogFormatter::class;
+       $wgLogActionsHandlers['newusers/create2'] = NewUsersLogFormatter::class;
+       $wgLogActionsHandlers['newusers/byemail'] = NewUsersLogFormatter::class;
+       $wgLogActionsHandlers['newusers/autocreate'] = NewUsersLogFormatter::class;
 }
 
 if ( $wgPageLanguageUseDB ) {
        $wgLogTypes[] = 'pagelang';
-       $wgLogActionsHandlers['pagelang/pagelang'] = 'PageLangLogFormatter';
+       $wgLogActionsHandlers['pagelang/pagelang'] = PageLangLogFormatter::class;
 }
 
 if ( $wgCookieSecure === 'detect' ) {
@@ -698,7 +698,7 @@ if ( $wgMainWANCache === false ) {
        // Sites using multiple datacenters can configure a relayer.
        $wgMainWANCache = 'mediawiki-main-default';
        $wgWANObjectCaches[$wgMainWANCache] = [
-               'class'    => 'WANObjectCache',
+               'class'    => WANObjectCache::class,
                'cacheId'  => $wgMainCacheType,
                'channels' => [ 'purge' => 'wancache-main-default-purge' ]
        ];
@@ -736,20 +736,22 @@ if ( !$wgDBerrorLogTZ ) {
 
 // Initialize the request object in $wgRequest
 $wgRequest = RequestContext::getMain()->getRequest(); // BackCompat
-// Set user IP/agent information for causal consistency purposes.
-// The cpPosIndex cookie has no prefix and is set by MediaWiki::preOutputCommit().
-$cpPosIndex = $wgRequest->getInt( 'cpPosIndex', (int)$wgRequest->getCookie( 'cpPosIndex', '' ) );
+// Set user IP/agent information for agent session consistency purposes
 MediaWikiServices::getInstance()->getDBLoadBalancerFactory()->setRequestInfo( [
        'IPAddress' => $wgRequest->getIP(),
        'UserAgent' => $wgRequest->getHeader( 'User-Agent' ),
        'ChronologyProtection' => $wgRequest->getHeader( 'ChronologyProtection' ),
-       'ChronologyPositionIndex' => $cpPosIndex
+       // The cpPosIndex cookie has no prefix and is set by MediaWiki::preOutputCommit()
+       'ChronologyPositionIndex' =>
+               $wgRequest->getInt( 'cpPosIndex', (int)$wgRequest->getCookie( 'cpPosIndex', '' ) )
 ] );
-// Make sure that caching does not compromise the consistency improvements
-if ( $cpPosIndex ) {
+// Make sure that object caching does not undermine the ChronologyProtector improvements
+if ( $wgRequest->getCookie( 'UseDC', '' ) === 'master' ) {
+       // The user is pinned to the primary DC, meaning that they made recent changes which should
+       // be reflected in their subsequent web requests. Avoid the use of interim cache keys because
+       // they use a blind TTL and could be stale if an object changes twice in a short time span.
        MediaWikiServices::getInstance()->getMainWANObjectCache()->useInterimHoldOffCaching( false );
 }
-unset( $cpPosIndex );
 
 // Useful debug output
 if ( $wgCommandLineMode ) {
index cd62737..7bfb5a4 100644 (file)
@@ -956,7 +956,7 @@ class WebRequest {
        public function response() {
                /* Lazy initialization of response object for this request */
                if ( !is_object( $this->response ) ) {
-                       $class = ( $this instanceof FauxRequest ) ? 'FauxResponse' : 'WebResponse';
+                       $class = ( $this instanceof FauxRequest ) ? FauxResponse::class : WebResponse::class;
                        $this->response = new $class();
                }
                return $this->response;
index 5056b6d..2c2dd9a 100644 (file)
@@ -55,72 +55,72 @@ class ApiMain extends ApiBase {
         * List of available modules: action name => module class
         */
        private static $Modules = [
-               'login' => 'ApiLogin',
-               'clientlogin' => 'ApiClientLogin',
-               'logout' => 'ApiLogout',
-               'createaccount' => 'ApiAMCreateAccount',
-               'linkaccount' => 'ApiLinkAccount',
-               'unlinkaccount' => 'ApiRemoveAuthenticationData',
-               'changeauthenticationdata' => 'ApiChangeAuthenticationData',
-               'removeauthenticationdata' => 'ApiRemoveAuthenticationData',
-               'resetpassword' => 'ApiResetPassword',
-               'query' => 'ApiQuery',
-               'expandtemplates' => 'ApiExpandTemplates',
-               'parse' => 'ApiParse',
-               'stashedit' => 'ApiStashEdit',
-               'opensearch' => 'ApiOpenSearch',
-               'feedcontributions' => 'ApiFeedContributions',
-               'feedrecentchanges' => 'ApiFeedRecentChanges',
-               'feedwatchlist' => 'ApiFeedWatchlist',
-               'help' => 'ApiHelp',
-               'paraminfo' => 'ApiParamInfo',
-               'rsd' => 'ApiRsd',
-               'compare' => 'ApiComparePages',
-               'tokens' => 'ApiTokens',
-               'checktoken' => 'ApiCheckToken',
-               'cspreport' => 'ApiCSPReport',
-               'validatepassword' => 'ApiValidatePassword',
+               'login' => ApiLogin::class,
+               'clientlogin' => ApiClientLogin::class,
+               'logout' => ApiLogout::class,
+               'createaccount' => ApiAMCreateAccount::class,
+               'linkaccount' => ApiLinkAccount::class,
+               'unlinkaccount' => ApiRemoveAuthenticationData::class,
+               'changeauthenticationdata' => ApiChangeAuthenticationData::class,
+               'removeauthenticationdata' => ApiRemoveAuthenticationData::class,
+               'resetpassword' => ApiResetPassword::class,
+               'query' => ApiQuery::class,
+               'expandtemplates' => ApiExpandTemplates::class,
+               'parse' => ApiParse::class,
+               'stashedit' => ApiStashEdit::class,
+               'opensearch' => ApiOpenSearch::class,
+               'feedcontributions' => ApiFeedContributions::class,
+               'feedrecentchanges' => ApiFeedRecentChanges::class,
+               'feedwatchlist' => ApiFeedWatchlist::class,
+               'help' => ApiHelp::class,
+               'paraminfo' => ApiParamInfo::class,
+               'rsd' => ApiRsd::class,
+               'compare' => ApiComparePages::class,
+               'tokens' => ApiTokens::class,
+               'checktoken' => ApiCheckToken::class,
+               'cspreport' => ApiCSPReport::class,
+               'validatepassword' => ApiValidatePassword::class,
 
                // Write modules
-               'purge' => 'ApiPurge',
-               'setnotificationtimestamp' => 'ApiSetNotificationTimestamp',
-               'rollback' => 'ApiRollback',
-               'delete' => 'ApiDelete',
-               'undelete' => 'ApiUndelete',
-               'protect' => 'ApiProtect',
-               'block' => 'ApiBlock',
-               'unblock' => 'ApiUnblock',
-               'move' => 'ApiMove',
-               'edit' => 'ApiEditPage',
-               'upload' => 'ApiUpload',
-               'filerevert' => 'ApiFileRevert',
-               'emailuser' => 'ApiEmailUser',
-               'watch' => 'ApiWatch',
-               'patrol' => 'ApiPatrol',
-               'import' => 'ApiImport',
-               'clearhasmsg' => 'ApiClearHasMsg',
-               'userrights' => 'ApiUserrights',
-               'options' => 'ApiOptions',
-               'imagerotate' => 'ApiImageRotate',
-               'revisiondelete' => 'ApiRevisionDelete',
-               'managetags' => 'ApiManageTags',
-               'tag' => 'ApiTag',
-               'mergehistory' => 'ApiMergeHistory',
-               'setpagelanguage' => 'ApiSetPageLanguage',
+               'purge' => ApiPurge::class,
+               'setnotificationtimestamp' => ApiSetNotificationTimestamp::class,
+               'rollback' => ApiRollback::class,
+               'delete' => ApiDelete::class,
+               'undelete' => ApiUndelete::class,
+               'protect' => ApiProtect::class,
+               'block' => ApiBlock::class,
+               'unblock' => ApiUnblock::class,
+               'move' => ApiMove::class,
+               'edit' => ApiEditPage::class,
+               'upload' => ApiUpload::class,
+               'filerevert' => ApiFileRevert::class,
+               'emailuser' => ApiEmailUser::class,
+               'watch' => ApiWatch::class,
+               'patrol' => ApiPatrol::class,
+               'import' => ApiImport::class,
+               'clearhasmsg' => ApiClearHasMsg::class,
+               'userrights' => ApiUserrights::class,
+               'options' => ApiOptions::class,
+               'imagerotate' => ApiImageRotate::class,
+               'revisiondelete' => ApiRevisionDelete::class,
+               'managetags' => ApiManageTags::class,
+               'tag' => ApiTag::class,
+               'mergehistory' => ApiMergeHistory::class,
+               'setpagelanguage' => ApiSetPageLanguage::class,
        ];
 
        /**
         * List of available formats: format name => format class
         */
        private static $Formats = [
-               'json' => 'ApiFormatJson',
-               'jsonfm' => 'ApiFormatJson',
-               'php' => 'ApiFormatPhp',
-               'phpfm' => 'ApiFormatPhp',
-               'xml' => 'ApiFormatXml',
-               'xmlfm' => 'ApiFormatXml',
-               'rawfm' => 'ApiFormatJson',
-               'none' => 'ApiFormatNone',
+               'json' => ApiFormatJson::class,
+               'jsonfm' => ApiFormatJson::class,
+               'php' => ApiFormatPhp::class,
+               'phpfm' => ApiFormatPhp::class,
+               'xml' => ApiFormatXml::class,
+               'xmlfm' => ApiFormatXml::class,
+               'rawfm' => ApiFormatJson::class,
+               'none' => ApiFormatNone::class,
        ];
 
        /**
index 2619a7c..e02c862 100644 (file)
@@ -78,12 +78,12 @@ class ApiModuleManager extends ContextSource {
         * @code
         *  $modules['foo'] = 'ApiFoo';
         *  $modules['bar'] = [
-        *      'class' => 'ApiBar',
+        *      'class' => ApiBar::class,
         *      'factory' => function( $main, $name ) { ... }
         *  ];
         *  $modules['xyzzy'] = [
-        *      'class' => 'ApiXyzzy',
-        *      'factory' => [ 'XyzzyFactory', 'newApiModule' ]
+        *      'class' => ApiXyzzy::class,
+        *      'factory' => [ XyzzyFactory::class, 'newApiModule' ]
         *  ];
         * @endcode
         *
index 30611b6..48303a5 100644 (file)
@@ -1528,7 +1528,7 @@ class ApiPageSet extends ApiBase {
                        $prefix = $query->getModulePath() . '+';
                        $mgr = $query->getModuleManager();
                        foreach ( $mgr->getNamesWithClasses() as $name => $class ) {
-                               if ( is_subclass_of( $class, 'ApiQueryGeneratorBase' ) ) {
+                               if ( is_subclass_of( $class, ApiQueryGeneratorBase::class ) ) {
                                        $gens[$name] = $prefix . $name;
                                }
                        }
index cf1fd1e..2839ab9 100644 (file)
@@ -344,6 +344,7 @@ class ApiParse extends ApiBase {
                        $result_array['text'] = $p_result->getText( [
                                'allowTOC' => !$params['disabletoc'],
                                'enableSectionEditLinks' => !$params['disableeditsection'],
+                               'unwrap' => $params['wrapoutputclass'] === '',
                        ] );
                        $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'text';
                }
@@ -538,9 +539,9 @@ class ApiParse extends ApiBase {
                if ( $params['disabletidy'] ) {
                        $popts->setTidy( false );
                }
-               $popts->setWrapOutputClass(
-                       $params['wrapoutputclass'] === '' ? false : $params['wrapoutputclass']
-               );
+               if ( $params['wrapoutputclass'] !== '' ) {
+                       $popts->setWrapOutputClass( $params['wrapoutputclass'] );
+               }
 
                $reset = null;
                $suppressCache = false;
index 5bbd211..e49024d 100644 (file)
@@ -40,26 +40,26 @@ class ApiQuery extends ApiBase {
         * @var array
         */
        private static $QueryPropModules = [
-               'categories' => 'ApiQueryCategories',
-               'categoryinfo' => 'ApiQueryCategoryInfo',
-               'contributors' => 'ApiQueryContributors',
-               'deletedrevisions' => 'ApiQueryDeletedRevisions',
-               'duplicatefiles' => 'ApiQueryDuplicateFiles',
-               'extlinks' => 'ApiQueryExternalLinks',
-               'fileusage' => 'ApiQueryBacklinksprop',
-               'images' => 'ApiQueryImages',
-               'imageinfo' => 'ApiQueryImageInfo',
-               'info' => 'ApiQueryInfo',
-               'links' => 'ApiQueryLinks',
-               'linkshere' => 'ApiQueryBacklinksprop',
-               'iwlinks' => 'ApiQueryIWLinks',
-               'langlinks' => 'ApiQueryLangLinks',
-               'pageprops' => 'ApiQueryPageProps',
-               'redirects' => 'ApiQueryBacklinksprop',
-               'revisions' => 'ApiQueryRevisions',
-               'stashimageinfo' => 'ApiQueryStashImageInfo',
-               'templates' => 'ApiQueryLinks',
-               'transcludedin' => 'ApiQueryBacklinksprop',
+               'categories' => ApiQueryCategories::class,
+               'categoryinfo' => ApiQueryCategoryInfo::class,
+               'contributors' => ApiQueryContributors::class,
+               'deletedrevisions' => ApiQueryDeletedRevisions::class,
+               'duplicatefiles' => ApiQueryDuplicateFiles::class,
+               'extlinks' => ApiQueryExternalLinks::class,
+               'fileusage' => ApiQueryBacklinksprop::class,
+               'images' => ApiQueryImages::class,
+               'imageinfo' => ApiQueryImageInfo::class,
+               'info' => ApiQueryInfo::class,
+               'links' => ApiQueryLinks::class,
+               'linkshere' => ApiQueryBacklinksprop::class,
+               'iwlinks' => ApiQueryIWLinks::class,
+               'langlinks' => ApiQueryLangLinks::class,
+               'pageprops' => ApiQueryPageProps::class,
+               'redirects' => ApiQueryBacklinksprop::class,
+               'revisions' => ApiQueryRevisions::class,
+               'stashimageinfo' => ApiQueryStashImageInfo::class,
+               'templates' => ApiQueryLinks::class,
+               'transcludedin' => ApiQueryBacklinksprop::class,
        ];
 
        /**
@@ -67,41 +67,41 @@ class ApiQuery extends ApiBase {
         * @var array
         */
        private static $QueryListModules = [
-               'allcategories' => 'ApiQueryAllCategories',
-               'alldeletedrevisions' => 'ApiQueryAllDeletedRevisions',
-               'allfileusages' => 'ApiQueryAllLinks',
-               'allimages' => 'ApiQueryAllImages',
-               'alllinks' => 'ApiQueryAllLinks',
-               'allpages' => 'ApiQueryAllPages',
-               'allredirects' => 'ApiQueryAllLinks',
-               'allrevisions' => 'ApiQueryAllRevisions',
-               'mystashedfiles' => 'ApiQueryMyStashedFiles',
-               'alltransclusions' => 'ApiQueryAllLinks',
-               'allusers' => 'ApiQueryAllUsers',
-               'backlinks' => 'ApiQueryBacklinks',
-               'blocks' => 'ApiQueryBlocks',
-               'categorymembers' => 'ApiQueryCategoryMembers',
-               'deletedrevs' => 'ApiQueryDeletedrevs',
-               'embeddedin' => 'ApiQueryBacklinks',
-               'exturlusage' => 'ApiQueryExtLinksUsage',
-               'filearchive' => 'ApiQueryFilearchive',
-               'imageusage' => 'ApiQueryBacklinks',
-               'iwbacklinks' => 'ApiQueryIWBacklinks',
-               'langbacklinks' => 'ApiQueryLangBacklinks',
-               'logevents' => 'ApiQueryLogEvents',
-               'pageswithprop' => 'ApiQueryPagesWithProp',
-               'pagepropnames' => 'ApiQueryPagePropNames',
-               'prefixsearch' => 'ApiQueryPrefixSearch',
-               'protectedtitles' => 'ApiQueryProtectedTitles',
-               'querypage' => 'ApiQueryQueryPage',
-               'random' => 'ApiQueryRandom',
-               'recentchanges' => 'ApiQueryRecentChanges',
-               'search' => 'ApiQuerySearch',
-               'tags' => 'ApiQueryTags',
-               'usercontribs' => 'ApiQueryContributions',
-               'users' => 'ApiQueryUsers',
-               'watchlist' => 'ApiQueryWatchlist',
-               'watchlistraw' => 'ApiQueryWatchlistRaw',
+               'allcategories' => ApiQueryAllCategories::class,
+               'alldeletedrevisions' => ApiQueryAllDeletedRevisions::class,
+               'allfileusages' => ApiQueryAllLinks::class,
+               'allimages' => ApiQueryAllImages::class,
+               'alllinks' => ApiQueryAllLinks::class,
+               'allpages' => ApiQueryAllPages::class,
+               'allredirects' => ApiQueryAllLinks::class,
+               'allrevisions' => ApiQueryAllRevisions::class,
+               'mystashedfiles' => ApiQueryMyStashedFiles::class,
+               'alltransclusions' => ApiQueryAllLinks::class,
+               'allusers' => ApiQueryAllUsers::class,
+               'backlinks' => ApiQueryBacklinks::class,
+               'blocks' => ApiQueryBlocks::class,
+               'categorymembers' => ApiQueryCategoryMembers::class,
+               'deletedrevs' => ApiQueryDeletedrevs::class,
+               'embeddedin' => ApiQueryBacklinks::class,
+               'exturlusage' => ApiQueryExtLinksUsage::class,
+               'filearchive' => ApiQueryFilearchive::class,
+               'imageusage' => ApiQueryBacklinks::class,
+               'iwbacklinks' => ApiQueryIWBacklinks::class,
+               'langbacklinks' => ApiQueryLangBacklinks::class,
+               'logevents' => ApiQueryLogEvents::class,
+               'pageswithprop' => ApiQueryPagesWithProp::class,
+               'pagepropnames' => ApiQueryPagePropNames::class,
+               'prefixsearch' => ApiQueryPrefixSearch::class,
+               'protectedtitles' => ApiQueryProtectedTitles::class,
+               'querypage' => ApiQueryQueryPage::class,
+               'random' => ApiQueryRandom::class,
+               'recentchanges' => ApiQueryRecentChanges::class,
+               'search' => ApiQuerySearch::class,
+               'tags' => ApiQueryTags::class,
+               'usercontribs' => ApiQueryContributions::class,
+               'users' => ApiQueryUsers::class,
+               'watchlist' => ApiQueryWatchlist::class,
+               'watchlistraw' => ApiQueryWatchlistRaw::class,
        ];
 
        /**
@@ -109,12 +109,12 @@ class ApiQuery extends ApiBase {
         * @var array
         */
        private static $QueryMetaModules = [
-               'allmessages' => 'ApiQueryAllMessages',
-               'authmanagerinfo' => 'ApiQueryAuthManagerInfo',
-               'siteinfo' => 'ApiQuerySiteinfo',
-               'userinfo' => 'ApiQueryUserInfo',
-               'filerepoinfo' => 'ApiQueryFileRepoInfo',
-               'tokens' => 'ApiQueryTokens',
+               'allmessages' => ApiQueryAllMessages::class,
+               'authmanagerinfo' => ApiQueryAuthManagerInfo::class,
+               'siteinfo' => ApiQuerySiteinfo::class,
+               'userinfo' => ApiQueryUserInfo::class,
+               'filerepoinfo' => ApiQueryFileRepoInfo::class,
+               'tokens' => ApiQueryTokens::class,
        ];
 
        /**
index 23a327b..5294b1d 100644 (file)
@@ -103,15 +103,15 @@ class ApiQueryInfo extends ApiQueryBase {
                }
 
                $this->tokenFunctions = [
-                       'edit' => [ 'ApiQueryInfo', 'getEditToken' ],
-                       'delete' => [ 'ApiQueryInfo', 'getDeleteToken' ],
-                       'protect' => [ 'ApiQueryInfo', 'getProtectToken' ],
-                       'move' => [ 'ApiQueryInfo', 'getMoveToken' ],
-                       'block' => [ 'ApiQueryInfo', 'getBlockToken' ],
-                       'unblock' => [ 'ApiQueryInfo', 'getUnblockToken' ],
-                       'email' => [ 'ApiQueryInfo', 'getEmailToken' ],
-                       'import' => [ 'ApiQueryInfo', 'getImportToken' ],
-                       'watch' => [ 'ApiQueryInfo', 'getWatchToken' ],
+                       'edit' => [ self::class, 'getEditToken' ],
+                       'delete' => [ self::class, 'getDeleteToken' ],
+                       'protect' => [ self::class, 'getProtectToken' ],
+                       'move' => [ self::class, 'getMoveToken' ],
+                       'block' => [ self::class, 'getBlockToken' ],
+                       'unblock' => [ self::class, 'getUnblockToken' ],
+                       'email' => [ self::class, 'getEmailToken' ],
+                       'import' => [ self::class, 'getImportToken' ],
+                       'watch' => [ self::class, 'getWatchToken' ],
                ];
                Hooks::run( 'APIQueryInfoTokens', [ &$this->tokenFunctions ] );
 
@@ -314,7 +314,7 @@ class ApiQueryInfo extends ApiQueryBase {
                $this->everything = $this->titles + $this->missing;
                $result = $this->getResult();
 
-               uasort( $this->everything, [ 'Title', 'compare' ] );
+               uasort( $this->everything, [ Title::class, 'compare' ] );
                if ( !is_null( $this->params['continue'] ) ) {
                        // Throw away any titles we're gonna skip so they don't
                        // clutter queries
index 4549987..517cf1f 100644 (file)
@@ -61,7 +61,7 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
                }
 
                $this->tokenFunctions = [
-                       'patrol' => [ 'ApiQueryRecentChanges', 'getPatrolToken' ]
+                       'patrol' => [ self::class, 'getPatrolToken' ]
                ];
                Hooks::run( 'APIQueryRecentChangesTokens', [ &$this->tokenFunctions ] );
 
index aa9a39c..ef0223a 100644 (file)
@@ -56,7 +56,7 @@ class ApiQueryRevisions extends ApiQueryRevisionsBase {
                }
 
                $this->tokenFunctions = [
-                       'rollback' => [ 'ApiQueryRevisions', 'getRollbackToken' ]
+                       'rollback' => [ self::class, 'getRollbackToken' ]
                ];
                Hooks::run( 'APIQueryRevisionsTokens', [ &$this->tokenFunctions ] );
 
index acae889..f924736 100644 (file)
@@ -848,7 +848,7 @@ class ApiQuerySiteinfo extends ApiQueryBase {
                foreach ( $myWgHooks as $name => $subscribers ) {
                        $arr = [
                                'name' => $name,
-                               'subscribers' => array_map( [ 'SpecialVersion', 'arrayToString' ], $subscribers ),
+                               'subscribers' => array_map( [ SpecialVersion::class, 'arrayToString' ], $subscribers ),
                        ];
 
                        ApiResult::setArrayType( $arr['subscribers'], 'array' );
index 9261003..6de512b 100644 (file)
@@ -73,7 +73,7 @@ class ApiQueryUsers extends ApiQueryBase {
                }
 
                $this->tokenFunctions = [
-                       'userrights' => [ 'ApiQueryUsers', 'getUserrightsToken' ],
+                       'userrights' => [ self::class, 'getUserrightsToken' ],
                ];
                Hooks::run( 'APIQueryUsersTokens', [ &$this->tokenFunctions ] );
 
index 606967d..ff1914c 100644 (file)
@@ -62,11 +62,11 @@ class ApiTokens extends ApiBase {
                if ( $types ) {
                        return $types;
                }
-               $types = [ 'patrol' => [ 'ApiQueryRecentChanges', 'getPatrolToken' ] ];
+               $types = [ 'patrol' => [ ApiQueryRecentChanges::class, 'getPatrolToken' ] ];
                $names = [ 'edit', 'delete', 'protect', 'move', 'block', 'unblock',
                        'email', 'import', 'watch', 'options' ];
                foreach ( $names as $name ) {
-                       $types[$name] = [ 'ApiQueryInfo', 'get' . ucfirst( $name ) . 'Token' ];
+                       $types[$name] = [ ApiQueryInfo::class, 'get' . ucfirst( $name ) . 'Token' ];
                }
                Hooks::run( 'ApiTokensGetTokenTypes', [ &$types ] );
 
index e8a2061..93e432b 100644 (file)
@@ -724,26 +724,26 @@ class ApiUpload extends ApiBase {
         */
        protected function handleStashException( $e ) {
                switch ( get_class( $e ) ) {
-                       case 'UploadStashFileNotFoundException':
+                       case UploadStashFileNotFoundException::class:
                                $wrap = 'apierror-stashedfilenotfound';
                                break;
-                       case 'UploadStashBadPathException':
+                       case UploadStashBadPathException::class:
                                $wrap = 'apierror-stashpathinvalid';
                                break;
-                       case 'UploadStashFileException':
+                       case UploadStashFileException::class:
                                $wrap = 'apierror-stashfilestorage';
                                break;
-                       case 'UploadStashZeroLengthFileException':
+                       case UploadStashZeroLengthFileException::class:
                                $wrap = 'apierror-stashzerolength';
                                break;
-                       case 'UploadStashNotLoggedInException':
+                       case UploadStashNotLoggedInException::class:
                                return StatusValue::newFatal( ApiMessage::create(
                                        [ 'apierror-mustbeloggedin', $this->msg( 'action-upload' ) ], 'stashnotloggedin'
                                ) );
-                       case 'UploadStashWrongOwnerException':
+                       case UploadStashWrongOwnerException::class:
                                $wrap = 'apierror-stashwrongowner';
                                break;
-                       case 'UploadStashNoSuchKeyException':
+                       case UploadStashNoSuchKeyException::class:
                                $wrap = 'apierror-stashnosuchfilekey';
                                break;
                        default:
index 218454d..4da9509 100644 (file)
@@ -22,7 +22,7 @@
                        "Tacsipacsi"
                ]
        },
-       "apihelp-main-extended-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:Special:MyLanguage/API:Main_page|Dokumentation]]\n* [[mw:Special:MyLanguage/API:FAQ|Häufig gestellte Fragen]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api Mailingliste]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce API-Ankündigungen]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Fehlerberichte und Anfragen]\n</div>\n<strong>Status:</strong> Alle auf dieser Seite gezeigten Funktionen sollten funktionieren, allerdings ist die API in aktiver Entwicklung und kann sich zu jeder Zeit ändern. Abonniere die [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ MediaWiki-API-Ankündigungs-Mailingliste], um über Aktualisierungen informiert zu werden.\n\n<strong>Fehlerhafte Anfragen:</strong> Wenn fehlerhafte Anfragen an die API gesendet werden, wird ein HTTP-Header mit dem Schlüssel „MediaWiki-API-Error“ gesendet. Der Wert des Headers und der Fehlercode werden auf den gleichen Wert gesetzt. Für weitere Informationen siehe [[mw:Special:MyLanguage/API:Errors_and_warnings|API: Fehler und Warnungen]].\n\n<strong>Testen:</strong> Zum einfachen Testen von API-Anfragen, siehe [[Special:ApiSandbox]].",
+       "apihelp-main-extended-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:Special:MyLanguage/API:Main_page|Dokumentation]]\n* [[mw:Special:MyLanguage/API:FAQ|Häufig gestellte Fragen]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api Mailingliste]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce API-Ankündigungen]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Fehlerberichte und Anfragen]\n</div>\n<strong>Status:</strong> Alle auf dieser Seite gezeigten Funktionen sollten funktionieren, allerdings ist die API in aktiver Entwicklung und kann sich zu jeder Zeit ändern. Abonniere die [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ MediaWiki-API-Ankündigungs-Mailingliste], um über Aktualisierungen informiert zu werden.\n\n<strong>Fehlerhafte Anfragen:</strong> Wenn fehlerhafte Anfragen an die API gesendet werden, wird ein HTTP-Header mit dem Schlüssel „MediaWiki-API-Error“ gesendet. Der Wert des Headers und der Fehlercode werden auf den gleichen Wert gesetzt. Für weitere Informationen siehe [[mw:Special:MyLanguage/API:Errors_and_warnings|API: Fehler und Warnungen]].\n\n<p class=\"mw-apisandbox-link\"><strong>Testen:</strong> Zum einfachen Testen von API-Anfragen, siehe [[Special:ApiSandbox]].</p>",
        "apihelp-main-param-action": "Auszuführende Aktion.",
        "apihelp-main-param-format": "Format der Ausgabe.",
        "apihelp-main-param-maxlag": "maxlag kann verwendet werden, wenn MediaWiki auf einem datenbankreplizierten Cluster installiert ist. Um weitere Replikationsrückstände zu verhindern, lässt dieser Parameter den Client warten, bis der Replikationsrückstand kleiner als der angegebene Wert (in Sekunden) ist. Bei einem größerem Rückstand wird der Fehlercode <samp>maxlag</samp> zurückgegeben mit einer Nachricht wie <samp>Waiting for $host: $lag seconds lagged</samp>.<br />Siehe [[mw:Special:MyLanguage/Manual:Maxlag_parameter|Handbuch: Maxlag parameter]] für weitere Informationen.",
index a897f06..729c4c7 100644 (file)
@@ -7,7 +7,7 @@
        },
 
        "apihelp-main-summary": "",
-       "apihelp-main-extended-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:Special:MyLanguage/API:Main_page|Documentation]]\n* [[mw:Special:MyLanguage/API:FAQ|FAQ]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api Mailing list]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce API Announcements]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Bugs & requests]\n</div>\n<strong>Status:</strong> All features shown on this page should be working, but the API is still in active development, and may change at any time. Subscribe to [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ the mediawiki-api-announce mailing list] for notice of updates.\n\n<strong>Erroneous requests:</strong> When erroneous requests are sent to the API, an HTTP header will be sent with the key \"MediaWiki-API-Error\" and then both the value of the header and the error code sent back will be set to the same value. For more information see [[mw:Special:MyLanguage/API:Errors_and_warnings|API: Errors and warnings]].\n\n<strong>Testing:</strong> For ease of testing API requests, see [[Special:ApiSandbox]].",
+       "apihelp-main-extended-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:Special:MyLanguage/API:Main_page|Documentation]]\n* [[mw:Special:MyLanguage/API:FAQ|FAQ]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api Mailing list]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce API Announcements]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Bugs & requests]\n</div>\n<strong>Status:</strong> All features shown on this page should be working, but the API is still in active development, and may change at any time. Subscribe to [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ the mediawiki-api-announce mailing list] for notice of updates.\n\n<strong>Erroneous requests:</strong> When erroneous requests are sent to the API, an HTTP header will be sent with the key \"MediaWiki-API-Error\" and then both the value of the header and the error code sent back will be set to the same value. For more information see [[mw:Special:MyLanguage/API:Errors_and_warnings|API: Errors and warnings]].\n\n<p class=\"mw-apisandbox-link\"><strong>Testing:</strong> For ease of testing API requests, see [[Special:ApiSandbox]].</p>",
        "apihelp-main-param-action": "Which action to perform.",
        "apihelp-main-param-format": "The format of the output.",
        "apihelp-main-param-maxlag": "Maximum lag can be used when MediaWiki is installed on a database replicated cluster. To save actions causing any more site replication lag, this parameter can make the client wait until the replication lag is less than the specified value. In case of excessive lag, error code <samp>maxlag</samp> is returned with a message like <samp>Waiting for $host: $lag seconds lagged</samp>.<br />See [[mw:Special:MyLanguage/Manual:Maxlag_parameter|Manual: Maxlag parameter]] for more information.",
index 2f27b58..d72b218 100644 (file)
@@ -33,7 +33,7 @@
                        "Kenjiraw"
                ]
        },
-       "apihelp-main-extended-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:Special:MyLanguage/API:Main_page|Documentation]]\n* [[mw:Special:MyLanguage/API:FAQ|FAQ]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api Liste de diffusion]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce Annonces de l’API]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Bogues et demandes]\n</div>\n<strong>État :</strong> Toutes les fonctionnalités affichées sur cette page devraient fonctionner, mais l’API est encore en cours de développement et peut changer à tout moment. Inscrivez-vous à [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ la liste de diffusion mediawiki-api-announce] pour être informé des mises à jour.\n\n<strong>Requêtes erronées :</strong> Si des requêtes erronées sont envoyées à l’API, un entête HTTP sera renvoyé avec la clé « MediaWiki-API-Error ». La valeur de cet entête et le code d’erreur renvoyé prendront la même valeur. Pour plus d’information, voyez [[mw:Special:MyLanguage/API:Errors_and_warnings|API: Errors and warnings]].\n\n<strong>Test :</strong> Pour faciliter le test des requêtes de l’API, voyez [[Special:ApiSandbox]].",
+       "apihelp-main-extended-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:Special:MyLanguage/API:Main_page|Documentation]]\n* [[mw:Special:MyLanguage/API:FAQ|FAQ]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api Liste de diffusion]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce Annonces de l’API]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Bogues et demandes]\n</div>\n<strong>État :</strong> Toutes les fonctionnalités affichées sur cette page devraient fonctionner, mais l’API est encore en cours de développement et peut changer à tout moment. Inscrivez-vous à [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ la liste de diffusion mediawiki-api-announce] pour être informé des mises à jour.\n\n<strong>Requêtes erronées :</strong> Si des requêtes erronées sont envoyées à l’API, un entête HTTP sera renvoyé avec la clé « MediaWiki-API-Error ». La valeur de cet entête et le code d’erreur renvoyé prendront la même valeur. Pour plus d’information, voyez [[mw:Special:MyLanguage/API:Errors_and_warnings|API: Errors and warnings]].\n\n<p class=\"mw-apisandbox-link\"><strong>Test :</strong> Pour faciliter le test des requêtes de l’API, voyez [[Special:ApiSandbox]].</p>",
        "apihelp-main-param-action": "Quelle action effectuer.",
        "apihelp-main-param-format": "Le format de sortie.",
        "apihelp-main-param-maxlag": "La latence maximale peut être utilisée quand MédiaWiki est installé sur un cluster de base de données répliqué. Pour éviter des actions provoquant un supplément de latence de réplication de site, ce paramètre peut faire attendre le client jusqu’à ce que la latence de réplication soit inférieure à une valeur spécifiée. En cas de latence excessive, le code d’erreur <samp>maxlag</samp> est renvoyé avec un message tel que <samp>Attente de $host : $lag secondes de délai</samp>.<br />Voyez [[mw:Special:MyLanguage/Manual:Maxlag_parameter|Manuel: Maxlag parameter]] pour plus d’information.",
index 02915a7..7c03097 100644 (file)
@@ -10,7 +10,7 @@
                        "Dj"
                ]
        },
-       "apihelp-main-extended-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:Special:MyLanguage/API:Main_page|Dokumentáció]]\n* [[mw:Special:MyLanguage/API:FAQ|GYIK]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api Levelezőlista]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce API-bejelentések]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Hibabejelentések és kérések]\n</div>\n<strong>Státusz:</strong> Minden ezen a lapon látható funkciónak működnie kell, de az API jelenleg is aktív fejlesztés alatt áll, és bármikor változhat. Iratkozz fel a [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ mediawiki-api-announce levelezőlistára] a frissítések követéséhez.\n\n<strong>Hibás kérések:</strong> Ha az API hibás kérést kap, egy HTTP-fejlécet küld vissza „MediaWiki-API-Error” kulccsal, és a fejléc értéke és a visszaküldött hibakód ugyanarra az értékre lesz állítva. További információért lásd: [[mw:Special:MyLanguage/API:Errors_and_warnings|API: Hibák és figyelmeztetések]].\n\n<strong>Tesztelés:</strong> Az API-kérések könnyebb teszteléséhez használható az [[Special:ApiSandbox|API-homokozó]].",
+       "apihelp-main-extended-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:Special:MyLanguage/API:Main_page|Dokumentáció]]\n* [[mw:Special:MyLanguage/API:FAQ|GYIK]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api Levelezőlista]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce API-bejelentések]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Hibabejelentések és kérések]\n</div>\n<strong>Státusz:</strong> Minden ezen a lapon látható funkciónak működnie kell, de az API jelenleg is aktív fejlesztés alatt áll, és bármikor változhat. Iratkozz fel a [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ mediawiki-api-announce levelezőlistára] a frissítések követéséhez.\n\n<strong>Hibás kérések:</strong> Ha az API hibás kérést kap, egy HTTP-fejlécet küld vissza „MediaWiki-API-Error” kulccsal, és a fejléc értéke és a visszaküldött hibakód ugyanarra az értékre lesz állítva. További információért lásd: [[mw:Special:MyLanguage/API:Errors_and_warnings|API: Hibák és figyelmeztetések]].\n\n<p class=\"mw-apisandbox-link\"><strong>Tesztelés:</strong> Az API-kérések könnyebb teszteléséhez használható az [[Special:ApiSandbox|API-homokozó]].</p>",
        "apihelp-main-param-action": "Milyen műveletet hajtson végre.",
        "apihelp-main-param-format": "A kimenet formátuma.",
        "apihelp-main-param-smaxage": "Az <code>s-maxage</code> gyorsítótár-vezérlő HTTP-fejléc beállítása ennyi másodpercre. A hibák soha nincsenek gyorsítótárazva.",
index 5682036..25da0e6 100644 (file)
@@ -19,7 +19,7 @@
                        "Margherita.mignanelli"
                ]
        },
-       "apihelp-main-extended-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:Special:MyLanguage/API:Main_page|Documentazione]] (in inglese)\n* [[mw:Special:MyLanguage/API:FAQ|FAQ]] (in inglese)\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api Mailing list]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce Annunci sull'API]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Bug & richieste]\n</div>\n<strong>Stato:</strong> tutte le funzioni e caratteristiche mostrate su questa pagina dovrebbero funzionare, ma le API sono ancora in fase attiva di sviluppo, e potrebbero cambiare in qualsiasi momento. Iscriviti alla [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ la mailing list sugli annunci delle API MediaWiki] per essere informato sugli aggiornamenti.\n\n<strong>Istruzioni sbagliate:</strong> quando vengono impartite alle API delle istruzioni sbagliate, un'intestazione HTTP verrà inviata col messaggio \"MediaWiki-API-Error\" e, sia il valore dell'intestazione, sia il codice d'errore, verranno impostati con lo stesso valore. Per maggiori informazioni leggi [[mw:Special:MyLanguage/API:Errors_and_warnings|API:Errori ed avvertimenti]] (in inglese).\n\n<strong>Test:</strong> per testare facilmente le richieste API, vedi [[Special:ApiSandbox]].",
+       "apihelp-main-extended-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:Special:MyLanguage/API:Main_page|Documentazione]] (in inglese)\n* [[mw:Special:MyLanguage/API:FAQ|FAQ]] (in inglese)\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api Mailing list]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce Annunci sull'API]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Bug & richieste]\n</div>\n<strong>Stato:</strong> tutte le funzioni e caratteristiche mostrate su questa pagina dovrebbero funzionare, ma le API sono ancora in fase attiva di sviluppo, e potrebbero cambiare in qualsiasi momento. Iscriviti alla [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ la mailing list sugli annunci delle API MediaWiki] per essere informato sugli aggiornamenti.\n\n<strong>Istruzioni sbagliate:</strong> quando vengono impartite alle API delle istruzioni sbagliate, un'intestazione HTTP verrà inviata col messaggio \"MediaWiki-API-Error\" e, sia il valore dell'intestazione, sia il codice d'errore, verranno impostati con lo stesso valore. Per maggiori informazioni leggi [[mw:Special:MyLanguage/API:Errors_and_warnings|API:Errori ed avvertimenti]] (in inglese).\n\n<p class=\"mw-apisandbox-link\"><strong>Test:</strong> per testare facilmente le richieste API, vedi [[Special:ApiSandbox]].</p>",
        "apihelp-main-param-action": "Azione da compiere.",
        "apihelp-main-param-format": "Formato dell'output.",
        "apihelp-main-param-assert": "Verifica che l'utente abbia effettuato l'accesso se si è impostato <kbd>user</kbd>, o che abbia i permessi di bot se si è impostato <kbd>bot</kbd>.",
index 094c406..f51b03f 100644 (file)
@@ -15,7 +15,7 @@
                        "Omotecho"
                ]
        },
-       "apihelp-main-extended-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:Special:MyLanguage/API:Main_page|説明文書]]\n* [[mw:Special:MyLanguage/API:FAQ|よくある質問]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api メーリングリスト]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce API 告知]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R バグの報告とリクエスト]\n</div>\n<strong>状態:</strong> このページに表示されている機能は全て動作するはずですが、この API は未だ活発に開発されており、変更される可能性があります。アップデートの通知を受け取るには、[https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ the mediawiki-api-announce メーリングリスト]に参加してください。\n\n<strong>誤ったリクエスト:</strong> 誤ったリクエストが API に送られた場合、\"MediaWiki-API-Error\" HTTP ヘッダーが送信され、そのヘッダーの値と送り返されるエラーコードは同じ値にセットされます。より詳しい情報は [[mw:Special:MyLanguage/API:Errors_and_warnings|API: Errors and warnings]] を参照してください。\n\n<strong>テスト:</strong> API のリクエストのテストは、[[Special:ApiSandbox]]で簡単に行えます。",
+       "apihelp-main-extended-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:Special:MyLanguage/API:Main_page|説明文書]]\n* [[mw:Special:MyLanguage/API:FAQ|よくある質問]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api メーリングリスト]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce API 告知]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R バグの報告とリクエスト]\n</div>\n<strong>状態:</strong> このページに表示されている機能は全て動作するはずですが、この API は未だ活発に開発されており、変更される可能性があります。アップデートの通知を受け取るには、[https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ the mediawiki-api-announce メーリングリスト]に参加してください。\n\n<strong>誤ったリクエスト:</strong> 誤ったリクエストが API に送られた場合、\"MediaWiki-API-Error\" HTTP ヘッダーが送信され、そのヘッダーの値と送り返されるエラーコードは同じ値にセットされます。より詳しい情報は [[mw:Special:MyLanguage/API:Errors_and_warnings|API: Errors and warnings]] を参照してください。\n\n<p class=\"mw-apisandbox-link\"><strong>テスト:</strong> API のリクエストのテストは、[[Special:ApiSandbox]]で簡単に行えます。</p>",
        "apihelp-main-param-action": "実行する操作です。",
        "apihelp-main-param-format": "出力する形式です。",
        "apihelp-main-param-smaxage": "<code>s-maxage</code> HTTP キャッシュ コントロール ヘッダー に、この秒数を設定します。エラーがキャッシュされることはありません。",
index f4a4662..aa697fb 100644 (file)
@@ -8,13 +8,14 @@
                        "Hamilton Abreu",
                        "Mansil",
                        "Felipe L. Ewald",
-                       "Athena in Wonderland"
+                       "Athena in Wonderland",
+                       "Waldir"
                ]
        },
-       "apihelp-main-extended-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:Special:MyLanguage/API:Main_page|Documentação]]\n* [[mw:Special:MyLanguage/API:FAQ|FAQ]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api Lista de discussão]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce Anúncios da API]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Erros e pedidos]\n</div>\n<strong>Estado:</strong> Todas as funcionalidades mostradas nesta página devem ter o comportamento documentado, mas a API ainda está em desenvolvimento ativo e pode ser alterada a qualquer momento. Inscreva-se na [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ lista de discussão mediawiki-api-announce] para ser informado acerca das atualizações.\n\n<strong>Pedidos incorretos:</strong> Quando são enviados pedidos incorretos à API, será devolvido um cabeçalho HTTP com a chave \"MediaWiki-API-Error\" e depois tanto o valor desse cabeçalho como o código de erro devolvido serão definidos com o mesmo valor. Para mais informação, consulte [[mw:Special:MyLanguage/API:Errors_and_warnings|API:Erros e avisos]].\n\n<strong>Testes:</strong> Para testar facilmente pedidos à API, visite [[Special:ApiSandbox|Testes da API]].",
+       "apihelp-main-extended-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:Special:MyLanguage/API:Main_page|Documentação]]\n* [[mw:Special:MyLanguage/API:FAQ|FAQ]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api Lista de discussão]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce Anúncios da API]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Erros e pedidos]\n</div>\n<strong>Estado:</strong> Todas as funcionalidades mostradas nesta página devem ter o comportamento documentado, mas a API ainda está em desenvolvimento ativo e pode ser alterada a qualquer momento. Inscreva-se na [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ lista de discussão mediawiki-api-announce] para ser informado acerca das atualizações.\n\n<strong>Pedidos incorretos:</strong> Quando são enviados pedidos incorretos à API, será devolvido um cabeçalho HTTP com a chave \"MediaWiki-API-Error\" e depois tanto o valor desse cabeçalho como o código de erro devolvido serão definidos com o mesmo valor. Para mais informação, consulte [[mw:Special:MyLanguage/API:Errors_and_warnings|API:Erros e avisos]].\n\n<p class=\"mw-apisandbox-link\">\n<strong>Testes:</strong> Para testar facilmente pedidos à API, visite [[Special:ApiSandbox|Testes da API]].\n</p>",
        "apihelp-main-param-action": "A operação a ser realizada.",
        "apihelp-main-param-format": "O formato do resultado.",
-       "apihelp-main-param-maxlag": "O atraso máximo pode ser usado quando o MediaWiki é instalado num ''cluster'' de bases de dados replicadas. Para impedir que as operações causem ainda mais atrasos de replicação do ''site'', este parâmetro pode fazer o cliente aguardar até que o atraso de replicação seja inferior ao valor especificado. Caso o atraso atual exceda esse valor, o código de erro <samp>maxlag</samp> é devolvido com uma mensagem como <samp>À espera do servidor $host: $lag segundos de atraso</samp>.<br />Consulte [[mw:Special:MyLanguage/Manual:Maxlag_parameter|Manual: Parâmetro maxlag]] para mais informações.",
+       "apihelp-main-param-maxlag": "O atraso máximo pode ser usado quando o MediaWiki é instalado num ''cluster'' de bases de dados replicadas. Para impedir que as operações causem ainda mais atrasos de replicação do sítio, este parâmetro pode fazer o cliente aguardar até que o atraso de replicação seja inferior ao valor especificado. Caso o atraso atual exceda esse valor, o código de erro <samp>maxlag</samp> é devolvido com uma mensagem como <samp>À espera do servidor $host: $lag segundos de atraso</samp>.<br />Consulte [[mw:Special:MyLanguage/Manual:Maxlag_parameter|Manual: Parâmetro maxlag]] para mais informações.",
        "apihelp-main-param-smaxage": "Definir no cabeçalho HTTP <code>s-maxage</code> de controlo da cache este número de segundos. Os erros nunca são armazenados na cache.",
        "apihelp-main-param-maxage": "Definir no cabeçalho HTTP <code>max-age</code> de controlo da cache este número de segundos. Os erros nunca são armazenados na cache.",
        "apihelp-main-param-assert": "Se definido com o valor <kbd>user</kbd>, verificar que o utilizador está autenticado. Se definido com o valor <kbd>bot</kbd>, verificar que o utilizador tem o privilégio de conta robô.",
        "apihelp-opensearch-example-te": "Encontrar as páginas que começam por <kbd>Te</kbd>.",
        "apihelp-options-summary": "Alterar as preferências do utilizador atual.",
        "apihelp-options-extended-description": "Só podem ser definidas as opções que estão registadas no núcleo do MediaWiki ou numa das extensões instaladas, ou opções cuja chave tem o prefixo <code>userjs-</code> (que são supostas ser usadas por ''scripts'' de utilizador).",
-       "apihelp-options-param-reset": "Reiniciar preferências para os valores por omissão do ''site''.",
+       "apihelp-options-param-reset": "Reiniciar preferências para os valores por omissão do sítio.",
        "apihelp-options-param-resetkinds": "Lista dos tipos de opções a reiniciar quando a opção <var>$1reset</var> está definida.",
        "apihelp-options-param-change": "Listas das alterações, na forma nome=valor (isto é, skin=vector). Se não for fornecido nenhum valor (nem sequer um sinal de igualdade), por exemplo, nomedaopção|outraopção|..., a opção será reiniciada para o seu valor por omissão. Se qualquer dos valores passados contém uma barra vertical (<kbd>|</kbd>), use um [[Special:ApiHelp/main#main/datatypes|separador alternativo para valores múltiplos]] de forma a obter o comportamento correto.",
        "apihelp-options-param-optionname": "O nome da opção que deve ser configurada com o valor dado por <var>$1optionvalue</var>.",
        "apihelp-purge-example-simple": "Purgar as páginas <kbd>Main Page</kbd> e <kbd>API</kbd>.",
        "apihelp-purge-example-generator": "Purgar as primeiras 10 páginas no espaço nominal principal.",
        "apihelp-query-summary": "Obter dados de, e sobre, o MediaWiki.",
-       "apihelp-query-extended-description": "Todas as modificações de dados terão primeiro que usar uma consulta para adquirir uma chave, o que visa impedir abusos de sites maliciosos.",
+       "apihelp-query-extended-description": "Todas as modificações de dados terão primeiro que usar uma consulta para adquirir uma chave, o que visa impedir abusos de sítios maliciosos.",
        "apihelp-query-param-prop": "As propriedades a serem obtidas para as páginas consultadas.",
        "apihelp-query-param-list": "As listas a serem obtidas.",
        "apihelp-query-param-meta": "Os metadados a serem obtidos.",
        "apihelp-query-param-exportnowrap": "Devolver o XML de exportação sem envolvê-lo num resultado XML (o mesmo formato que [[Special:Export]]). Só pode ser usado com $1export.",
        "apihelp-query-param-iwurl": "Indica se deve ser obtido o URL completo quando o título é uma hiperligação interwikis.",
        "apihelp-query-param-rawcontinue": "Devolver os dados em bruto de <samp>query-continue</samp> para continuar.",
-       "apihelp-query-example-revisions": "Obter [[Special:ApiHelp/query+siteinfo|informação do ''site'']] e as [[Special:ApiHelp/query+revisions|revisões]] da página <kbd>Main Page</kbd>.",
+       "apihelp-query-example-revisions": "Obter [[Special:ApiHelp/query+siteinfo|informação do sítio]] e as [[Special:ApiHelp/query+revisions|revisões]] da página <kbd>Main Page</kbd>.",
        "apihelp-query-example-allpages": "Obter as revisões das páginas que começam por <kbd>API/</kbd>.",
        "apihelp-query+allcategories-summary": "Enumerar todas as categorias.",
        "apihelp-query+allcategories-param-from": "A categoria a partir da qual será começada a enumeração.",
        "apihelp-query+alllinks-example-unique": "Listar os títulos únicos para os quais existem hiperligações.",
        "apihelp-query+alllinks-example-unique-generator": "Obtém todos os títulos para os quais existem hiperligações, marcando aqueles em falta.",
        "apihelp-query+alllinks-example-generator": "Obtém as páginas que contêm as ligações.",
-       "apihelp-query+allmessages-summary": "Devolver as mensagens deste ''site''.",
+       "apihelp-query+allmessages-summary": "Devolver as mensagens deste sítio.",
        "apihelp-query+allmessages-param-messages": "Mensagens a serem produzidas no resultado. <kbd>*</kbd> (o valor por omissão) significa todas as mensagens.",
        "apihelp-query+allmessages-param-prop": "As propriedades a serem obtidas:",
        "apihelp-query+allmessages-param-enableparser": "Definir para ativar o analisador sintático; irá pré-processar o texto wiki da mensagem (substituir palavras mágicas, processar predefinições, etc.).",
        "apihelp-query+embeddedin-example-simple": "Mostrar as páginas que transcluem <kbd>Template:Stub</kbd>.",
        "apihelp-query+embeddedin-example-generator": "Obter informação sobre as páginas que transcluem <kbd>Template:Stub</kbd>.",
        "apihelp-query+extlinks-summary": "Devolve todos os URL externos (que não sejam interwikis) das páginas especificadas.",
-       "apihelp-query+extlinks-param-limit": "O número de ''links'' a serem devolvidos.",
+       "apihelp-query+extlinks-param-limit": "O número de hiperligações a serem devolvidas.",
        "apihelp-query+extlinks-param-protocol": "Protocolo do URL. Se vazio e <var>$1query</var> está definido, o protocolo é <kbd>http</kbd>. Deixe este parâmetro e <var>$1query</var> vazios para listar todas as hiperligações externas.",
        "apihelp-query+extlinks-param-query": "Texto de pesquisa sem protocolo. Útil para verificar se uma determinada página contém um determinado URL externo.",
        "apihelp-query+extlinks-param-expandurl": "Expandir os URL relativos a protocolo com o protocolo canónico.",
        "apihelp-query+info-param-token": "Em substituição, usar [[Special:ApiHelp/query+tokens|action=query&meta=tokens]].",
        "apihelp-query+info-example-simple": "Obter informações sobre a página <kbd>Main Page</kbd>.",
        "apihelp-query+info-example-protection": "Obter informação geral e de proteção sobre a página <kbd>Main Page</kbd>.",
-       "apihelp-query+iwbacklinks-summary": "Encontrar todas as páginas que contêm ''links'' para as páginas indicadas.",
+       "apihelp-query+iwbacklinks-summary": "Encontrar todas as páginas que contêm hiperligações para as páginas indicadas.",
        "apihelp-query+iwbacklinks-extended-description": "Pode ser usado para encontrar todas as hiperligações com um prefixo, ou todas as hiperligações para um título (com um prefixo especificado). Se nenhum dos parâmetros for usado, isso efetivamente significa \"todas as hiperligações interwikis\".",
        "apihelp-query+iwbacklinks-param-prefix": "O prefixo interwikis.",
        "apihelp-query+iwbacklinks-param-title": "A hiperligação interwikis a ser procurada. Tem de ser usado em conjunto com <var>$1blprefix</var>.",
        "apihelp-query+langlinks-example-simple": "Obter as hiperligações interlínguas da página <kbd>Main Page</kbd>.",
        "apihelp-query+links-summary": "Devolve todas as hiperligações das páginas indicadas.",
        "apihelp-query+links-param-namespace": "Mostrar apenas as hiperligações destes espaços nominais.",
-       "apihelp-query+links-param-limit": "O número de ''links'' a serem devolvidos.",
+       "apihelp-query+links-param-limit": "O número de hiperligações a serem devolvidas.",
        "apihelp-query+links-param-titles": "Listar só as hiperligações para estes títulos. Útil para verificar se uma determinada página contém hiperligações para um determinado título.",
        "apihelp-query+links-param-dir": "A direção de listagem.",
-       "apihelp-query+links-example-simple": "Obter os ''links'' da página <kbd>Main Page</kbd>.",
+       "apihelp-query+links-example-simple": "Obter as hiperligações da página <kbd>Main Page</kbd>.",
        "apihelp-query+links-example-generator": "Obter informação sobre as páginas ligadas na página <kbd>Main Page</kbd>.",
        "apihelp-query+links-example-namespaces": "Obter as hiperligações da página <kbd>Main Page</kbd> nos espaços nominais {{ns:user}} e {{ns:template}}.",
        "apihelp-query+linkshere-summary": "Encontrar todas as páginas que contêm hiperligações para as páginas indicadas.",
        "apihelp-query+search-example-simple": "Pesquisar <kbd>meaning</kbd>.",
        "apihelp-query+search-example-text": "Pesquisar <kbd>meaning</kbd> nos textos.",
        "apihelp-query+search-example-generator": "Obter informação sobre as páginas devolvidas por uma pesquisa do termo <kbd>meaning</kbd>.",
-       "apihelp-query+siteinfo-summary": "Devolver informação geral sobre o ''site''.",
+       "apihelp-query+siteinfo-summary": "Devolver informação geral sobre o sítio.",
        "apihelp-query+siteinfo-param-prop": "A informação a ser obtida:",
        "apihelp-query+siteinfo-paramvalue-prop-general": "Informação global do sistema.",
        "apihelp-query+siteinfo-paramvalue-prop-namespaces": "Uma lista dos espaços nominais registados e dos seus nomes canónicos.",
        "apihelp-query+siteinfo-paramvalue-prop-namespacealiases": "Uma lista dos nomes alternativos dos espaços nominais registados.",
        "apihelp-query+siteinfo-paramvalue-prop-specialpagealiases": "Uma lista dos nomes alternativos das páginas especiais.",
        "apihelp-query+siteinfo-paramvalue-prop-magicwords": "Uma lista das palavras mágicas e dos seus nomes alternativos.",
-       "apihelp-query+siteinfo-paramvalue-prop-statistics": "Devolve as estatísticas do ''site''.",
+       "apihelp-query+siteinfo-paramvalue-prop-statistics": "Devolve as estatísticas do sítio.",
        "apihelp-query+siteinfo-paramvalue-prop-interwikimap": "Devolve o mapa de interwikis (opcionalmente filtrado, opcionalmente localizado usando <var>$1inlanguagecode</var>).",
        "apihelp-query+siteinfo-paramvalue-prop-dbrepllag": "Devolve o servidor da base de dados com o maior atraso de replicação.",
        "apihelp-query+siteinfo-paramvalue-prop-usergroups": "Devolve os grupos de utilizadores e as permissões associadas.",
        "apihelp-query+siteinfo-paramvalue-prop-functionhooks": "Devolve uma lista dos ''hooks'' de funções do analisador sintático.",
        "apihelp-query+siteinfo-paramvalue-prop-showhooks": "Devolve uma lista de todos os ''hooks'' subscritos (conteúdo de <var>[[mw:Special:MyLanguage/Manual:$wgHooks|$wgHooks]]</var>).",
        "apihelp-query+siteinfo-paramvalue-prop-variables": "Devolve uma lista de identificadores de variáveis.",
-       "apihelp-query+siteinfo-paramvalue-prop-protocols": "Devolve uma lista dos protocolos permitidos nos ''links'' externos.",
+       "apihelp-query+siteinfo-paramvalue-prop-protocols": "Devolve uma lista dos protocolos permitidos nas hiperligações externas.",
        "apihelp-query+siteinfo-paramvalue-prop-defaultoptions": "Devolve os valores padrão para as preferências dos utilizadores.",
        "apihelp-query+siteinfo-paramvalue-prop-uploaddialog": "Devolve a configuração do diálogo de carregamento.",
        "apihelp-query+siteinfo-param-filteriw": "Devolver só as entradas locais, ou só as não locais, do mapa de interwikis.",
        "apihelp-query+siteinfo-param-showalldb": "Listar todos os servidores da base de dados, não só aquele que tem maior atraso.",
        "apihelp-query+siteinfo-param-numberingroup": "Lista o número de utilizadores nos grupos de utilizadores.",
        "apihelp-query+siteinfo-param-inlanguagecode": "O código de língua dos nomes localizados (o melhor possível) das línguas e dos temas.",
-       "apihelp-query+siteinfo-example-simple": "Obter as informações do ''site''.",
+       "apihelp-query+siteinfo-example-simple": "Obter as informações do sítio.",
        "apihelp-query+siteinfo-example-interwiki": "Obter uma lista dos prefixos interwikis locais.",
        "apihelp-query+siteinfo-example-replag": "Verificar o atraso de replicação atual.",
        "apihelp-query+stashimageinfo-summary": "Devolve informações dos ficheiros escondidos.",
index bb87dd1..c081ffc 100644 (file)
@@ -26,7 +26,7 @@
                        "NeverBehave"
                ]
        },
-       "apihelp-main-extended-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:Special:MyLanguage/API:Main_page|文档]]\n* [[mw:Special:MyLanguage/API:FAQ|常见问题]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api 邮件列表]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce API公告]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R 程序错误与功能请求]\n</div>\n<strong>状态信息:</strong>本页所展示的所有特性都应正常工作,但是API仍在开发当中,将会随时变化。请订阅[https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ mediawiki-api-announce 邮件列表]以便获得更新通知。\n\n<strong>错误请求:</strong>当API收到错误请求时,HTTP header将会返回一个包含\"MediaWiki-API-Error\"的值,随后header的值与error code将会送回并设置为相同的值。详细信息请参阅[[mw:Special:MyLanguage/API:Errors_and_warnings|API:错误与警告]]。\n\n<strong>测试中:</strong>测试API请求的易用性,请参见[[Special:ApiSandbox]]。",
+       "apihelp-main-extended-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:Special:MyLanguage/API:Main_page|文档]]\n* [[mw:Special:MyLanguage/API:FAQ|常见问题]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api 邮件列表]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce API公告]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R 程序错误与功能请求]\n</div>\n<strong>状态信息:</strong>本页所展示的所有特性都应正常工作,但是API仍在开发当中,将会随时变化。请订阅[https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ mediawiki-api-announce 邮件列表]以便获得更新通知。\n\n<strong>错误请求:</strong>当API收到错误请求时,HTTP header将会返回一个包含\"MediaWiki-API-Error\"的值,随后header的值与error code将会送回并设置为相同的值。详细信息请参阅[[mw:Special:MyLanguage/API:Errors_and_warnings|API:错误与警告]]。\n\n<p class=\"mw-apisandbox-link\"><strong>测试中:</strong>测试API请求的易用性,请参见[[Special:ApiSandbox]]。</p>",
        "apihelp-main-param-action": "要执行的操作。",
        "apihelp-main-param-format": "输出的格式。",
        "apihelp-main-param-maxlag": "最大延迟可被用于MediaWiki安装于数据库复制集中。要保存导致更多网站复制延迟的操作,此参数可使客户端等待直到复制延迟少于指定值时。万一发生过多延迟,错误代码<samp>maxlag</samp>会返回消息,例如<samp>等待$host中:延迟$lag秒</samp>。<br />参见[[mw:Special:MyLanguage/Manual:Maxlag_parameter|手册:Maxlag参数]]以获取更多信息。",
index 4872186..63c03af 100644 (file)
@@ -193,7 +193,6 @@ class MessageCache {
                                $po = ParserOptions::newFromAnon();
                                $po->setEditSection( false );
                                $po->setAllowUnsafeRawHtml( false );
-                               $po->setWrapOutputClass( false );
                                return $po;
                        }
 
@@ -203,11 +202,6 @@ class MessageCache {
                        // from malicious sources. As a precaution, disable
                        // the <html> parser tag when parsing messages.
                        $this->mParserOptions->setAllowUnsafeRawHtml( false );
-                       // Wrapping messages in an extra <div> is probably not expected. If
-                       // they're outside the content area they probably shouldn't be
-                       // targeted by CSS that's targeting the parser output, and if
-                       // they're inside they already are from the outer div.
-                       $this->mParserOptions->setWrapOutputClass( false );
                }
 
                return $this->mParserOptions;
@@ -1126,7 +1120,7 @@ class MessageCache {
                        $wgParser->firstCallInit();
                        # Clone it and store it
                        $class = $wgParserConf['class'];
-                       if ( $class == 'ParserDiffTest' ) {
+                       if ( $class == ParserDiffTest::class ) {
                                # Uncloneable
                                $this->mParser = new $class( $wgParserConf );
                        } else {
index 5e0a688..26382aa 100644 (file)
@@ -199,22 +199,22 @@ class LocalisationCache {
                        switch ( $conf['store'] ) {
                                case 'files':
                                case 'file':
-                                       $storeClass = 'LCStoreCDB';
+                                       $storeClass = LCStoreCDB::class;
                                        break;
                                case 'db':
-                                       $storeClass = 'LCStoreDB';
+                                       $storeClass = LCStoreDB::class;
                                        break;
                                case 'array':
-                                       $storeClass = 'LCStoreStaticArray';
+                                       $storeClass = LCStoreStaticArray::class;
                                        break;
                                case 'detect':
                                        if ( !empty( $conf['storeDirectory'] ) ) {
-                                               $storeClass = 'LCStoreCDB';
+                                               $storeClass = LCStoreCDB::class;
                                        } elseif ( $wgCacheDirectory ) {
                                                $storeConf['directory'] = $wgCacheDirectory;
-                                               $storeClass = 'LCStoreCDB';
+                                               $storeClass = LCStoreCDB::class;
                                        } else {
-                                               $storeClass = 'LCStoreDB';
+                                               $storeClass = LCStoreDB::class;
                                        }
                                        break;
                                default:
index 6fa6907..f095b64 100644 (file)
@@ -71,7 +71,7 @@ class CategoryMembershipChange {
                        $this->timestamp = $revision->getTimestamp();
                }
                $this->revision = $revision;
-               $this->newForCategorizationCallback = [ 'RecentChange', 'newForCategorization' ];
+               $this->newForCategorizationCallback = [ RecentChange::class, 'newForCategorization' ];
        }
 
        /**
index 95848ea..7e4dd00 100644 (file)
@@ -408,19 +408,24 @@ class ChangeTags {
                sort( $prevTags );
                sort( $newTags );
                if ( $prevTags == $newTags ) {
-                       // No change.
                        return false;
                }
 
                if ( !$newTags ) {
-                       // no tags left, so delete the row altogether
+                       // No tags left, so delete the row altogether
                        $dbw->delete( 'tag_summary', $tsConds, __METHOD__ );
                } else {
-                       $dbw->replace( 'tag_summary',
-                               [ 'ts_rev_id', 'ts_rc_id', 'ts_log_id' ],
-                               array_filter( array_merge( $tsConds, [ 'ts_tags' => implode( ',', $newTags ) ] ) ),
-                               __METHOD__
-                       );
+                       // Specify the non-DEFAULT value columns in the INSERT/REPLACE clause
+                       $row = array_filter( [ 'ts_tags' => implode( ',', $newTags ) ] + $tsConds );
+                       // Check the unique keys for conflicts, ignoring any NULL *_id values
+                       $uniqueKeys = [];
+                       foreach ( [ 'ts_rev_id', 'ts_rc_id', 'ts_log_id' ] as $uniqueColumn ) {
+                               if ( isset( $row[$uniqueColumn] ) ) {
+                                       $uniqueKeys[] = [ $uniqueColumn ];
+                               }
+                       }
+
+                       $dbw->replace( 'tag_summary', $uniqueKeys, $row, __METHOD__ );
                }
 
                return true;
index afbbb2b..1559e1d 100644 (file)
@@ -43,10 +43,10 @@ abstract class ChangeTagsList extends RevisionListBase {
        ) {
                switch ( $typeName ) {
                        case 'revision':
-                               $className = 'ChangeTagsRevisionList';
+                               $className = ChangeTagsRevisionList::class;
                                break;
                        case 'logentry':
-                               $className = 'ChangeTagsLogList';
+                               $className = ChangeTagsLogList::class;
                                break;
                        default:
                                throw new Exception( "Class $typeName requested, but does not exist" );
index e2feb1f..90fabaf 100644 (file)
@@ -354,7 +354,7 @@ class DatabaseOracle extends Database {
                return $e['code'];
        }
 
-       function affectedRows() {
+       protected function fetchAffectedRowCount() {
                return $this->mAffectedRows;
        }
 
index 5607332..5c79117 100644 (file)
@@ -64,7 +64,7 @@ abstract class MWLBFactory {
                // When making changes here, remember to also specify MediaWiki-specific options
                // for Database classes in the relevant Installer subclass.
                // Such as MysqlInstaller::openConnection and PostgresInstaller::openConnectionWithParams.
-               if ( $lbConf['class'] === 'LBFactorySimple' ) {
+               if ( $lbConf['class'] === Wikimedia\Rdbms\LBFactorySimple::class ) {
                        if ( isset( $lbConf['servers'] ) ) {
                                // Server array is already explicitly configured; leave alone
                        } elseif ( is_array( $mainConfig->get( 'DBservers' ) ) ) {
@@ -132,7 +132,7 @@ abstract class MWLBFactory {
                        if ( !isset( $lbConf['externalClusters'] ) ) {
                                $lbConf['externalClusters'] = $mainConfig->get( 'ExternalServers' );
                        }
-               } elseif ( $lbConf['class'] === 'LBFactoryMulti' ) {
+               } elseif ( $lbConf['class'] === Wikimedia\Rdbms\LBFactoryMulti::class ) {
                        if ( isset( $lbConf['serverTemplate'] ) ) {
                                if ( in_array( $lbConf['serverTemplate']['type'], $typesWithSchema, true ) ) {
                                        $lbConf['serverTemplate']['schema'] = $mainConfig->get( 'DBmwschema' );
index 8e750ca..cb0e066 100644 (file)
@@ -26,7 +26,7 @@ namespace MediaWiki\Logger;
  * Usage:
  * @code
  * $wgMWLoggerDefaultSpi = [
- *   'class' => '\\MediaWiki\\Logger\\LegacySpi',
+ *   'class' => \MediaWiki\Logger\LegacySpi::class,
  * ];
  * @endcode
  *
index 197b269..645eca9 100644 (file)
@@ -40,7 +40,7 @@ use ObjectFactory;
  * default SPI provider:
  * @code
  * $wgMWLoggerDefaultSpi = [
- *   'class' => '\\MediaWiki\\Logger\\MonologSpi',
+ *   'class' => \MediaWiki\Logger\MonologSpi::class,
  *   'args' => [ [
  *       'loggers' => [
  *           '@default' => [
@@ -54,29 +54,29 @@ use ObjectFactory;
  *       ],
  *       'processors' => [
  *           'wiki' => [
- *               'class' => '\\MediaWiki\\Logger\\Monolog\\WikiProcessor',
+ *               'class' => \MediaWiki\Logger\Monolog\WikiProcessor::class,
  *           ],
  *           'psr' => [
- *               'class' => '\\Monolog\\Processor\\PsrLogMessageProcessor',
+ *               'class' => \Monolog\Processor\PsrLogMessageProcessor::class,
  *           ],
  *           'pid' => [
- *               'class' => '\\Monolog\\Processor\\ProcessIdProcessor',
+ *               'class' => \Monolog\Processor\ProcessIdProcessor::class,
  *           ],
  *           'uid' => [
- *               'class' => '\\Monolog\\Processor\\UidProcessor',
+ *               'class' => \Monolog\Processor\UidProcessor::class,
  *           ],
  *           'web' => [
- *               'class' => '\\Monolog\\Processor\\WebProcessor',
+ *               'class' => \Monolog\Processor\WebProcessor::class,
  *           ],
  *       ],
  *       'handlers' => [
  *           'stream' => [
- *               'class'     => '\\Monolog\\Handler\\StreamHandler',
+ *               'class'     => \Monolog\Handler\StreamHandler::class,
  *               'args'      => [ 'path/to/your.log' ],
  *               'formatter' => 'line',
  *           ],
  *           'redis' => [
- *               'class'     => '\\Monolog\\Handler\\RedisHandler',
+ *               'class'     => \Monolog\Handler\RedisHandler::class,
  *               'args'      => [ function() {
  *                       $redis = new Redis();
  *                       $redis->connect( '127.0.0.1', 6379 );
@@ -88,7 +88,7 @@ use ObjectFactory;
  *               'buffer' => true,
  *           ],
  *           'udp2log' => [
- *               'class' => '\\MediaWiki\\Logger\\Monolog\\LegacyHandler',
+ *               'class' => \MediaWiki\Logger\Monolog\LegacyHandler::class,
  *               'args' => [
  *                   'udp://127.0.0.1:8420/mediawiki
  *               ],
@@ -97,10 +97,10 @@ use ObjectFactory;
  *       ],
  *       'formatters' => [
  *           'line' => [
- *               'class' => '\\Monolog\\Formatter\\LineFormatter',
+ *               'class' => \Monolog\Formatter\LineFormatter::class,
  *            ],
  *            'logstash' => [
- *                'class' => '\\Monolog\\Formatter\\LogstashFormatter',
+ *                'class' => \Monolog\Formatter\LogstashFormatter::class,
  *                'args'  => [ 'mediawiki', php_uname( 'n' ), null, '', 1 ],
  *            ],
  *       ],
index 4862157..d65c185 100644 (file)
@@ -29,7 +29,7 @@ use Psr\Log\NullLogger;
  * Usage:
  *
  *     $wgMWLoggerDefaultSpi = [
- *         'class' => '\\MediaWiki\\Logger\\NullSpi',
+ *         'class' => \MediaWiki\Logger\NullSpi::class,
  *     ];
  *
  * @see \MediaWiki\Logger\LoggerFactory
index 5b822af..9803b7a 100644 (file)
@@ -14,14 +14,18 @@ class MWCallableUpdate implements DeferrableUpdate, DeferrableCallback {
        /**
         * @param callable $callback
         * @param string $fname Calling method
-        * @param IDatabase|null $dbw Abort if this DB is rolled back [optional] (since 1.28)
+        * @param IDatabase|IDatabase[]|null $dbws Abort if any of the specified DB handles have
+        *   a currently pending transaction which later gets rolled back [optional] (since 1.28)
         */
-       public function __construct( callable $callback, $fname = 'unknown', IDatabase $dbw = null ) {
+       public function __construct( callable $callback, $fname = 'unknown', $dbws = [] ) {
                $this->callback = $callback;
                $this->fname = $fname;
 
-               if ( $dbw && $dbw->trxLevel() ) {
-                       $dbw->onTransactionResolution( [ $this, 'cancelOnRollback' ], $fname );
+               $dbws = is_array( $dbws ) ? $dbws : [ $dbws ];
+               foreach ( $dbws as $dbw ) {
+                       if ( $dbw && $dbw->trxLevel() ) {
+                               $dbw->onTransactionResolution( [ $this, 'cancelOnRollback' ], $fname );
+                       }
                }
        }
 
index 7e05be6..e76bffc 100644 (file)
@@ -650,6 +650,12 @@ class DifferenceEngine extends ContextSource {
                }
        }
 
+       /**
+        * @param WikiPage $page
+        * @param Revision $rev
+        *
+        * @return ParserOutput|bool False if the revision was not found
+        */
        protected function getParserOutput( WikiPage $page, Revision $rev ) {
                $parserOptions = $page->makeParserOptions( $this->getContext() );
 
index 6d95919..16f226c 100644 (file)
@@ -55,7 +55,7 @@ class MWException extends Exception {
                global $wgLang;
 
                foreach ( $this->getTrace() as $frame ) {
-                       if ( isset( $frame['class'] ) && $frame['class'] === 'LocalisationCache' ) {
+                       if ( isset( $frame['class'] ) && $frame['class'] === LocalisationCache::class ) {
                                return false;
                        }
                }
index d863a2b..205ec77 100644 (file)
@@ -300,7 +300,7 @@ TXT;
                $logger = LoggerFactory::getInstance( 'fatal' );
                $logger->error( $msg, [
                        'fatal_exception' => [
-                               'class' => 'ErrorException',
+                               'class' => ErrorException::class,
                                'message' => "PHP Fatal Error: {$message}",
                                'code' => $level,
                                'file' => $file,
index b22e87b..dc8dfd0 100644 (file)
@@ -90,7 +90,7 @@ class MWExceptionRenderer {
        private static function useOutputPage( $e ) {
                // Can the extension use the Message class/wfMessage to get i18n-ed messages?
                foreach ( $e->getTrace() as $frame ) {
-                       if ( isset( $frame['class'] ) && $frame['class'] === 'LocalisationCache' ) {
+                       if ( isset( $frame['class'] ) && $frame['class'] === LocalisationCache::class ) {
                                return false;
                        }
                }
index 8182d62..9239c6c 100644 (file)
@@ -91,7 +91,7 @@ class FileBackendGroup {
                        // Get the FS backend configuration
                        $autoBackends[] = [
                                'name' => $backendName,
-                               'class' => 'FSFileBackend',
+                               'class' => FSFileBackend::class,
                                'lockManager' => 'fsLockManager',
                                'containerPaths' => [
                                        "{$repoName}-public" => "{$directory}",
@@ -155,7 +155,7 @@ class FileBackendGroup {
                        $config = $this->config( $name );
 
                        $class = $config['class'];
-                       if ( $class === 'FileBackendMultiWrite' ) {
+                       if ( $class === FileBackendMultiWrite::class ) {
                                foreach ( $config['backends'] as $index => $beConfig ) {
                                        if ( isset( $beConfig['template'] ) ) {
                                                // Config is just a modified version of a registered backend's.
@@ -190,9 +190,9 @@ class FileBackendGroup {
                        'wikiId' => wfWikiID(), // e.g. "my_wiki-en_"
                        'mimeCallback' => [ $this, 'guessMimeInternal' ],
                        'obResetFunc' => 'wfResetOutputBuffers',
-                       'streamMimeFunc' => [ 'StreamFile', 'contentTypeFromPath' ],
+                       'streamMimeFunc' => [ StreamFile::class, 'contentTypeFromPath' ],
                        'tmpDirectory' => wfTempDir(),
-                       'statusWrapper' => [ 'Status', 'wrap' ],
+                       'statusWrapper' => [ Status::class, 'wrap' ],
                        'wanCache' => MediaWikiServices::getInstance()->getMainWANObjectCache(),
                        'srvCache' => ObjectCache::getLocalServerInstance( 'hash' ),
                        'logger' => LoggerFactory::getInstance( 'FileOperation' ),
@@ -202,7 +202,7 @@ class FileBackendGroup {
                        LockManagerGroup::singleton( $config['wikiId'] )->get( $config['lockManager'] );
                $config['fileJournal'] = isset( $config['fileJournal'] )
                        ? FileJournal::factory( $config['fileJournal'], $name )
-                       : FileJournal::factory( [ 'class' => 'NullFileJournal' ], $name );
+                       : FileJournal::factory( [ 'class' => NullFileJournal::class ], $name );
 
                return $config;
        }
index e6f992c..5d79dac 100644 (file)
@@ -116,7 +116,7 @@ class LockManagerGroup {
                if ( !isset( $this->managers[$name]['instance'] ) ) {
                        $class = $this->managers[$name]['class'];
                        $config = $this->managers[$name]['config'];
-                       if ( $class === 'DBLockManager' ) {
+                       if ( $class === DBLockManager::class ) {
                                $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
                                $lb = $lbFactory->newMainLB( $config['domain'] );
                                $dbw = $lb->getLazyConnectionRef( DB_MASTER, [], $config['domain'] );
index 5d22b8d..6b32953 100644 (file)
@@ -124,7 +124,7 @@ class FileRepo {
        protected $isPrivate;
 
        /** @var array callable Override these in the base class */
-       protected $fileFactory = [ 'UnregisteredLocalFile', 'newFromTitle' ];
+       protected $fileFactory = [ UnregisteredLocalFile::class, 'newFromTitle' ];
        /** @var array callable|bool Override these in the base class */
        protected $oldFileFactory = false;
        /** @var array callable|bool Override these in the base class */
@@ -132,6 +132,13 @@ class FileRepo {
        /** @var array callable|bool Override these in the base class */
        protected $oldFileFactoryKey = false;
 
+       /** @var string URL of where to proxy thumb.php requests to.
+        *    Example: http://127.0.0.1:8888/wiki/dev/thumb/
+        */
+       protected $thumbProxyUrl;
+       /** @var string Secret key to pass as an X-Swift-Secret header to the proxied thumb service */
+       protected $thumbProxySecret;
+
        /**
         * @param array|null $info
         * @throws MWException
@@ -159,7 +166,7 @@ class FileRepo {
                $optionalSettings = [
                        'descBaseUrl', 'scriptDirUrl', 'articleUrl', 'fetchDescription',
                        'thumbScriptUrl', 'pathDisclosureProtection', 'descriptionCacheExpiry',
-                       'scriptExtension', 'favicon'
+                       'scriptExtension', 'favicon', 'thumbProxyUrl', 'thumbProxySecret'
                ];
                foreach ( $optionalSettings as $var ) {
                        if ( isset( $info[$var] ) ) {
@@ -611,6 +618,24 @@ class FileRepo {
                return $this->thumbScriptUrl;
        }
 
+       /**
+        * Get the URL thumb.php requests are being proxied to
+        *
+        * @return string
+        */
+       public function getThumbProxyUrl() {
+               return $this->thumbProxyUrl;
+       }
+
+       /**
+        * Get the secret key for the proxied thumb service
+        *
+        * @return string
+        */
+       public function getThumbProxySecret() {
+               return $this->thumbProxySecret;
+       }
+
        /**
         * Returns true if the repository can transform files via a 404 handler
         *
@@ -1722,7 +1747,7 @@ class FileRepo {
         * @return Status
         */
        public function newFatal( $message /*, parameters...*/ ) {
-               $status = call_user_func_array( [ 'Status', 'newFatal' ], func_get_args() );
+               $status = call_user_func_array( [ Status::class, 'newFatal' ], func_get_args() );
                $status->cleanCallback = $this->getErrorCleanupFunction();
 
                return $status;
index 45a5c82..5a37701 100644 (file)
@@ -29,7 +29,7 @@ use MediaWiki\Logger\LoggerFactory;
  * Example config:
  *
  * $wgForeignFileRepos[] = [
- *   'class'                  => 'ForeignAPIRepo',
+ *   'class'                  => ForeignAPIRepo::class,
  *   'name'                   => 'shared',
  *   'apibase'                => 'https://en.wikipedia.org/w/api.php',
  *   'fetchDescription'       => true, // Optional
@@ -53,7 +53,7 @@ class ForeignAPIRepo extends FileRepo {
                'timestamp',
        ];
 
-       protected $fileFactory = [ 'ForeignAPIFile', 'newFromTitle' ];
+       protected $fileFactory = [ ForeignAPIFile::class, 'newFromTitle' ];
        /** @var int Check back with Commons after this expiry */
        protected $apiThumbCacheExpiry = 86400; // 1 day (24*3600)
 
index bce3005..7879448 100644 (file)
@@ -58,9 +58,9 @@ class ForeignDBRepo extends LocalRepo {
        protected $dbConn;
 
        /** @var callable */
-       protected $fileFactory = [ 'ForeignDBFile', 'newFromTitle' ];
+       protected $fileFactory = [ ForeignDBFile::class, 'newFromTitle' ];
        /** @var callable */
-       protected $fileFromRowFactory = [ 'ForeignDBFile', 'newFromRow' ];
+       protected $fileFromRowFactory = [ ForeignDBFile::class, 'newFromRow' ];
 
        /**
         * @param array|null $info
index bcd253f..249cd27 100644 (file)
@@ -37,10 +37,10 @@ class ForeignDBViaLBRepo extends LocalRepo {
        protected $tablePrefix;
 
        /** @var array */
-       protected $fileFactory = [ 'ForeignDBFile', 'newFromTitle' ];
+       protected $fileFactory = [ ForeignDBFile::class, 'newFromTitle' ];
 
        /** @var array */
-       protected $fileFromRowFactory = [ 'ForeignDBFile', 'newFromRow' ];
+       protected $fileFromRowFactory = [ ForeignDBFile::class, 'newFromRow' ];
 
        /** @var bool */
        protected $hasSharedCache;
index f5b83ae..1bf5346 100644 (file)
@@ -34,17 +34,17 @@ use Wikimedia\Rdbms\IDatabase;
  */
 class LocalRepo extends FileRepo {
        /** @var callable */
-       protected $fileFactory = [ 'LocalFile', 'newFromTitle' ];
+       protected $fileFactory = [ LocalFile::class, 'newFromTitle' ];
        /** @var callable */
-       protected $fileFactoryKey = [ 'LocalFile', 'newFromKey' ];
+       protected $fileFactoryKey = [ LocalFile::class, 'newFromKey' ];
        /** @var callable */
-       protected $fileFromRowFactory = [ 'LocalFile', 'newFromRow' ];
+       protected $fileFromRowFactory = [ LocalFile::class, 'newFromRow' ];
        /** @var callable */
-       protected $oldFileFromRowFactory = [ 'OldLocalFile', 'newFromRow' ];
+       protected $oldFileFromRowFactory = [ OldLocalFile::class, 'newFromRow' ];
        /** @var callable */
-       protected $oldFileFactory = [ 'OldLocalFile', 'newFromTitle' ];
+       protected $oldFileFactory = [ OldLocalFile::class, 'newFromTitle' ];
        /** @var callable */
-       protected $oldFileFactoryKey = [ 'OldLocalFile', 'newFromKey' ];
+       protected $oldFileFactoryKey = [ OldLocalFile::class, 'newFromKey' ];
 
        function __construct( array $info = null ) {
                parent::__construct( $info );
index 4e79de2..cfbd062 100644 (file)
@@ -148,7 +148,7 @@ abstract class File implements IDBAccessObject {
        protected $isSafeFile;
 
        /** @var string Required Repository class type */
-       protected $repoClass = 'FileRepo';
+       protected $repoClass = FileRepo::class;
 
        /** @var array Cache of tmp filepaths pointing to generated bucket thumbnails, keyed by width */
        protected $tmpBucketedThumbCache = [];
index 16c154f..8dcb289 100644 (file)
@@ -33,7 +33,7 @@ class ForeignAPIFile extends File {
        /** @var array */
        private $mInfo = [];
 
-       protected $repoClass = 'ForeignApiRepo';
+       protected $repoClass = ForeignApiRepo::class;
 
        /**
         * @param Title|string|bool $title
index 4248f95..7be8f06 100644 (file)
@@ -84,7 +84,7 @@ class LocalFile extends File {
        protected $deleted;
 
        /** @var string */
-       protected $repoClass = 'LocalRepo';
+       protected $repoClass = LocalRepo::class;
 
        /** @var int Number of line to return by nextHistoryLine() (constructor) */
        private $historyLine;
index 700c8ee..3183297 100644 (file)
@@ -113,12 +113,12 @@ abstract class ImageGalleryBase extends ContextSource {
        private static function loadModes() {
                if ( self::$modeMapping === false ) {
                        self::$modeMapping = [
-                               'traditional' => 'TraditionalImageGallery',
-                               'nolines' => 'NolinesImageGallery',
-                               'packed' => 'PackedImageGallery',
-                               'packed-hover' => 'PackedHoverImageGallery',
-                               'packed-overlay' => 'PackedOverlayImageGallery',
-                               'slideshow' => 'SlideshowImageGallery',
+                               'traditional' => TraditionalImageGallery::class,
+                               'nolines' => NolinesImageGallery::class,
+                               'packed' => PackedImageGallery::class,
+                               'packed-hover' => PackedHoverImageGallery::class,
+                               'packed-overlay' => PackedOverlayImageGallery::class,
+                               'slideshow' => SlideshowImageGallery::class,
                        ];
                        // Allow extensions to make a new gallery format.
                        Hooks::run( 'GalleryGetModes', [ &self::$modeMapping ] );
index 296c4b3..afb815f 100644 (file)
 class HTMLForm extends ContextSource {
        // A mapping of 'type' inputs onto standard HTMLFormField subclasses
        public static $typeMappings = [
-               'api' => 'HTMLApiField',
-               'text' => 'HTMLTextField',
-               'textwithbutton' => 'HTMLTextFieldWithButton',
-               'textarea' => 'HTMLTextAreaField',
-               'select' => 'HTMLSelectField',
-               'combobox' => 'HTMLComboboxField',
-               'radio' => 'HTMLRadioField',
-               'multiselect' => 'HTMLMultiSelectField',
-               'limitselect' => 'HTMLSelectLimitField',
-               'check' => 'HTMLCheckField',
-               'toggle' => 'HTMLCheckField',
-               'int' => 'HTMLIntField',
-               'float' => 'HTMLFloatField',
-               'info' => 'HTMLInfoField',
-               'selectorother' => 'HTMLSelectOrOtherField',
-               'selectandother' => 'HTMLSelectAndOtherField',
-               'namespaceselect' => 'HTMLSelectNamespace',
-               'namespaceselectwithbutton' => 'HTMLSelectNamespaceWithButton',
-               'tagfilter' => 'HTMLTagFilter',
-               'sizefilter' => 'HTMLSizeFilterField',
-               'submit' => 'HTMLSubmitField',
-               'hidden' => 'HTMLHiddenField',
-               'edittools' => 'HTMLEditTools',
-               'checkmatrix' => 'HTMLCheckMatrix',
-               'cloner' => 'HTMLFormFieldCloner',
-               'autocompleteselect' => 'HTMLAutoCompleteSelectField',
-               'date' => 'HTMLDateTimeField',
-               'time' => 'HTMLDateTimeField',
-               'datetime' => 'HTMLDateTimeField',
+               'api' => HTMLApiField::class,
+               'text' => HTMLTextField::class,
+               'textwithbutton' => HTMLTextFieldWithButton::class,
+               'textarea' => HTMLTextAreaField::class,
+               'select' => HTMLSelectField::class,
+               'combobox' => HTMLComboboxField::class,
+               'radio' => HTMLRadioField::class,
+               'multiselect' => HTMLMultiSelectField::class,
+               'limitselect' => HTMLSelectLimitField::class,
+               'check' => HTMLCheckField::class,
+               'toggle' => HTMLCheckField::class,
+               'int' => HTMLIntField::class,
+               'float' => HTMLFloatField::class,
+               'info' => HTMLInfoField::class,
+               'selectorother' => HTMLSelectOrOtherField::class,
+               'selectandother' => HTMLSelectAndOtherField::class,
+               'namespaceselect' => HTMLSelectNamespace::class,
+               'namespaceselectwithbutton' => HTMLSelectNamespaceWithButton::class,
+               'tagfilter' => HTMLTagFilter::class,
+               'sizefilter' => HTMLSizeFilterField::class,
+               'submit' => HTMLSubmitField::class,
+               'hidden' => HTMLHiddenField::class,
+               'edittools' => HTMLEditTools::class,
+               'checkmatrix' => HTMLCheckMatrix::class,
+               'cloner' => HTMLFormFieldCloner::class,
+               'autocompleteselect' => HTMLAutoCompleteSelectField::class,
+               'date' => HTMLDateTimeField::class,
+               'time' => HTMLDateTimeField::class,
+               'datetime' => HTMLDateTimeField::class,
                // HTMLTextField will output the correct type="" attribute automagically.
                // There are about four zillion other HTML5 input types, like range, but
                // we don't use those at the moment, so no point in adding all of them.
-               'email' => 'HTMLTextField',
-               'password' => 'HTMLTextField',
-               'url' => 'HTMLTextField',
-               'title' => 'HTMLTitleTextField',
-               'user' => 'HTMLUserTextField',
-               'usersmultiselect' => 'HTMLUsersMultiselectField',
+               'email' => HTMLTextField::class,
+               'password' => HTMLTextField::class,
+               'url' => HTMLTextField::class,
+               'title' => HTMLTitleTextField::class,
+               'user' => HTMLUserTextField::class,
+               'usersmultiselect' => HTMLUsersMultiselectField::class,
        ];
 
        public $mFieldData;
index 66d6143..2830b9c 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Allows custom data specific to HTMLFormField to be set for OOjs UI forms. A matching JS widget
+ * Allows custom data specific to HTMLFormField to be set for OOUI forms. A matching JS widget
  * (defined in htmlform.Element.js) picks up the extra config when constructed using OO.ui.infuse().
  *
  * Currently only supports passing 'hide-if' data.
@@ -21,7 +21,7 @@ trait HTMLFormElement {
                        $this->addClasses( [ 'mw-htmlform-hide-if' ] );
                }
                if ( $this->modules ) {
-                       // JS code must be able to read this before infusing (before OOjs UI is even loaded),
+                       // JS code must be able to read this before infusing (before OOUI is even loaded),
                        // so we put this in a separate attribute (not with the rest of the config).
                        // And it's not needed anymore after infusing, so we don't put it in JS config at all.
                        $this->setAttributes( [ 'data-mw-modules' => implode( ',', $this->modules ) ] );
index 9c301e6..aab8811 100644 (file)
@@ -673,7 +673,7 @@ abstract class HTMLFormField {
        }
 
        /**
-        * Whether the field should be automatically infused. Note that all OOjs UI HTMLForm fields are
+        * Whether the field should be automatically infused. Note that all OOUI HTMLForm fields are
         * infusable (you can call OO.ui.infuse() on them), but not all are infused by default, since
         * there is no benefit in doing it e.g. for buttons and it's a small performance hit on page load.
         *
@@ -686,7 +686,7 @@ abstract class HTMLFormField {
 
        /**
         * Get the list of extra ResourceLoader modules which must be loaded client-side before it's
-        * possible to infuse this field's OOjs UI widget.
+        * possible to infuse this field's OOUI widget.
         *
         * @return string[]
         */
index 09bacad..e8a7e99 100644 (file)
@@ -102,7 +102,7 @@ class HTMLMultiSelectField extends HTMLFormField implements HTMLNestedFilterable
                if ( $this->mParent instanceof OOUIHTMLForm ) {
                        throw new MWException( 'HTMLMultiSelectField#getOneCheckbox() is not supported' );
                } else {
-                       $elementFunc = [ 'Html', $this->mOptionsLabelsNotFromMessage ? 'rawElement' : 'element' ];
+                       $elementFunc = [ Html::class, $this->mOptionsLabelsNotFromMessage ? 'rawElement' : 'element' ];
                        $checkbox =
                                Xml::check( "{$this->mName}[]", $checked, $attribs ) .
                                '&#160;' .
index c98e1ec..f3bcc0e 100644 (file)
@@ -78,7 +78,7 @@ class HTMLRadioField extends HTMLFormField {
                $html = '';
 
                $attribs = $this->getAttributes( [ 'disabled', 'tabindex' ] );
-               $elementFunc = [ 'Html', $this->mOptionsLabelsNotFromMessage ? 'rawElement' : 'element' ];
+               $elementFunc = [ Html::class, $this->mOptionsLabelsNotFromMessage ? 'rawElement' : 'element' ];
 
                # @todo Should this produce an unordered list perhaps?
                foreach ( $options as $label => $info ) {
index fff72ec..ac16032 100644 (file)
@@ -181,7 +181,7 @@ abstract class MWHttpRequest implements LoggerAwareInterface {
         * @return MWHttpRequest
         * @see MWHttpRequest::__construct
         */
-       public static function factory( $url, $options = null, $caller = __METHOD__ ) {
+       public static function factory( $url, array $options = [], $caller = __METHOD__ ) {
                return \MediaWiki\MediaWikiServices::getInstance()
                        ->getHttpRequestFactory()
                        ->create( $url, $options, $caller );
index 5978550..8991f5e 100644 (file)
@@ -65,7 +65,7 @@ class WikiImporter {
                $this->config = $config;
 
                if ( !in_array( 'uploadsource', stream_get_wrappers() ) ) {
-                       stream_wrapper_register( 'uploadsource', 'UploadSourceAdapter' );
+                       stream_wrapper_register( 'uploadsource', UploadSourceAdapter::class );
                }
                $id = UploadSourceAdapter::registerSource( $source );
 
index 176d0af..2083500 100644 (file)
@@ -1072,7 +1072,7 @@ abstract class DatabaseUpdater {
                                "maintenance/populateLogUsertext.php.\n"
                        );
 
-                       $task = $this->maintenance->runChild( 'PopulateLogUsertext' );
+                       $task = $this->maintenance->runChild( PopulateLogUsertext::class );
                        $task->execute();
                        $this->output( "done.\n" );
                }
@@ -1088,7 +1088,7 @@ abstract class DatabaseUpdater {
                                "databases, you may want to hit Ctrl-C and do this manually with\n" .
                                "maintenance/populateLogSearch.php.\n" );
 
-                       $task = $this->maintenance->runChild( 'PopulateLogSearch' );
+                       $task = $this->maintenance->runChild( PopulateLogSearch::class );
                        $task->execute();
                        $this->output( "done.\n" );
                }
@@ -1128,7 +1128,7 @@ abstract class DatabaseUpdater {
                        }
 
                        $this->output( "Updating category collations..." );
-                       $task = $this->maintenance->runChild( 'UpdateCollation' );
+                       $task = $this->maintenance->runChild( UpdateCollation::class );
                        $task->execute();
                        $this->output( "...done.\n" );
                }
@@ -1139,7 +1139,7 @@ abstract class DatabaseUpdater {
         */
        protected function doMigrateUserOptions() {
                if ( $this->db->tableExists( 'user_properties' ) ) {
-                       $cl = $this->maintenance->runChild( 'ConvertUserOptions', 'convertUserOptions.php' );
+                       $cl = $this->maintenance->runChild( ConvertUserOptions::class, 'convertUserOptions.php' );
                        $cl->execute();
                        $this->output( "done.\n" );
                }
@@ -1177,7 +1177,9 @@ abstract class DatabaseUpdater {
                /**
                 * @var $cl RebuildLocalisationCache
                 */
-               $cl = $this->maintenance->runChild( 'RebuildLocalisationCache', 'rebuildLocalisationCache.php' );
+               $cl = $this->maintenance->runChild(
+                       RebuildLocalisationCache::class, 'rebuildLocalisationCache.php'
+               );
                $this->output( "Rebuilding localisation cache...\n" );
                $cl->setForce();
                $cl->execute();
@@ -1224,7 +1226,7 @@ abstract class DatabaseUpdater {
                                "databases, you may want to hit Ctrl-C and do this manually with\n" .
                                "maintenance/migrateComments.php.\n"
                        );
-                       $task = $this->maintenance->runChild( 'MigrateComments', 'migrateComments.php' );
+                       $task = $this->maintenance->runChild( MigrateComments::class, 'migrateComments.php' );
                        $task->execute();
                        $this->output( "done.\n" );
                }
@@ -1236,7 +1238,7 @@ abstract class DatabaseUpdater {
         */
        protected function migrateArchiveText() {
                $this->output( "Migrating archive ar_text to modern storage.\n" );
-               $task = $this->maintenance->runChild( 'MigrateArchiveText', 'migrateArchiveText.php' );
+               $task = $this->maintenance->runChild( MigrateArchiveText::class, 'migrateArchiveText.php' );
                $task->execute();
                $this->output( "done.\n" );
        }
index 5ea9bfe..e42146d 100644 (file)
@@ -364,7 +364,7 @@ abstract class Installer {
 
                // disable (problematic) object cache types explicitly, preserving all other (working) ones
                // bug T113843
-               $emptyCache = [ 'class' => 'EmptyBagOStuff' ];
+               $emptyCache = [ 'class' => EmptyBagOStuff::class ];
 
                $objectCaches = [
                                CACHE_NONE => $emptyCache,
@@ -447,7 +447,6 @@ abstract class Installer {
                $this->parserTitle = Title::newFromText( 'Installer' );
                $this->parserOptions = new ParserOptions( $wgUser ); // language will be wrong :(
                $this->parserOptions->setEditSection( false );
-               $this->parserOptions->setWrapOutputClass( false );
                // Don't try to access DB before user language is initialised
                $this->setParserLanguage( Language::factory( 'en' ) );
        }
@@ -689,6 +688,7 @@ abstract class Installer {
                        $out = $wgParser->parse( $text, $this->parserTitle, $this->parserOptions, $lineStart );
                        $html = $out->getText( [
                                'enableSectionEditLinks' => false,
+                               'unwrap' => true,
                        ] );
                } catch ( MediaWiki\Services\ServiceDisabledException $e ) {
                        $html = '<!--DB access attempted during parse-->  ' . htmlspecialchars( $text );
@@ -1677,7 +1677,7 @@ abstract class Installer {
                // implementation that won't stomp on PHP's cookies.
                $GLOBALS['wgSessionProviders'] = [
                        [
-                               'class' => 'InstallerSessionProvider',
+                               'class' => InstallerSessionProvider::class,
                                'args' => [ [
                                        'priority' => 1,
                                ] ]
index eba3a20..c9154b9 100644 (file)
@@ -30,9 +30,9 @@ class InstallerOverrides {
 
                if ( !$overrides ) {
                        $overrides = [
-                               'LocalSettingsGenerator' => 'LocalSettingsGenerator',
-                               'WebInstaller' => 'WebInstaller',
-                               'CliInstaller' => 'CliInstaller',
+                               'LocalSettingsGenerator' => LocalSettingsGenerator::class,
+                               'WebInstaller' => WebInstaller::class,
+                               'CliInstaller' => CliInstaller::class,
                        ];
                        foreach ( glob( "$IP/mw-config/overrides/*.php" ) as $file ) {
                                require $file;
index 44086a1..bce5405 100644 (file)
@@ -432,7 +432,7 @@ class MysqlUpdater extends DatabaseUpdater {
        }
 
        protected function doOldLinksUpdate() {
-               $cl = $this->maintenance->runChild( 'ConvertLinks' );
+               $cl = $this->maintenance->runChild( ConvertLinks::class );
                $cl->execute();
        }
 
@@ -942,7 +942,7 @@ class MysqlUpdater extends DatabaseUpdater {
                $this->output( "done.\n" );
 
                $this->output( "Migrating old restrictions to new table...\n" );
-               $task = $this->maintenance->runChild( 'UpdateRestrictions' );
+               $task = $this->maintenance->runChild( UpdateRestrictions::class );
                $task->execute();
        }
 
@@ -965,7 +965,7 @@ class MysqlUpdater extends DatabaseUpdater {
                        "may want to hit Ctrl-C and do this manually with maintenance/\n" .
                        "populateCategory.php.\n"
                );
-               $task = $this->maintenance->runChild( 'PopulateCategory' );
+               $task = $this->maintenance->runChild( PopulateCategory::class );
                $task->execute();
                $this->output( "Done populating category table.\n" );
        }
@@ -977,7 +977,7 @@ class MysqlUpdater extends DatabaseUpdater {
                                "databases, you may want to hit Ctrl-C and do this manually with\n" .
                                "maintenance/populateParentId.php.\n" );
 
-                       $task = $this->maintenance->runChild( 'PopulateParentId' );
+                       $task = $this->maintenance->runChild( PopulateParentId::class );
                        $task->execute();
                }
        }
index d5909f4..31718fe 100644 (file)
@@ -321,7 +321,7 @@ EOT;
                return "# SQLite-specific settings
 \$wgSQLiteDataDir = \"{$dir}\";
 \$wgObjectCaches[CACHE_DB] = [
-       'class' => 'SqlBagOStuff',
+       'class' => SqlBagOStuff::class,
        'loggroup' => 'SQLBagOStuff',
        'server' => [
                'type' => 'sqlite',
index 760986d..2280644 100644 (file)
@@ -67,7 +67,7 @@
        "config-env-php": "O PHP $1 está instalado.",
        "config-env-hhvm": "HHVM $1 está instalado.",
        "config-unicode-using-intl": "A usar a [http://pecl.php.net/intl extensão intl PECL] para a normalização Unicode.",
-       "config-unicode-pure-php-warning": "<strong>Aviso:</strong> A [http://pecl.php.net/intl extensão intl PECL] não está disponível para efetuar a normalização Unicode. Irá recorrer-se à implementação em PHP puro, que é mais lenta.\nSe o seu site tem alto volume de tráfego, devia informar-se um pouco sobre a [https://www.mediawiki.org/wiki/Special:MyLanguage/Unicode_normalization_considerations/pt normalização Unicode].",
+       "config-unicode-pure-php-warning": "<strong>Aviso:</strong> A [http://pecl.php.net/intl extensão intl PECL] não está disponível para efetuar a normalização Unicode. Irá recorrer-se à implementação em PHP puro, que é mais lenta.\nSe o seu sítio tem alto volume de tráfego, devia informar-se um pouco sobre a [https://www.mediawiki.org/wiki/Special:MyLanguage/Unicode_normalization_considerations/pt normalização Unicode].",
        "config-unicode-update-warning": "<strong>Aviso:</strong> A versão instalada do wrapper de normalização Unicode usa uma versão mais antiga da biblioteca do [http://site.icu-project.org/ projeto ICU].\nDevia [https://www.mediawiki.org/wiki/Special:MyLanguage/Unicode_normalization_considerations atualizá-la] se tem quaisquer preocupações sobre o uso do Unicode.",
        "config-no-db": "Não foi possível encontrar um controlador apropriado da base de dados! Precisa de instalar um controlador da base de dados para o PHP. {{PLURAL:$2|É aceite o seguinte tipo|São aceites os seguintes tipos}} de base de dados: $1.\n\nSe fez a compilação do PHP, reconfigure-o com um cliente de base de dados ativado; por exemplo, usando <code>./configure --with-mysqli</code>.\nSe instalou o PHP a partir de um pacote Debian ou Ubuntu, então precisa de instalar também, por exemplo, o pacote <code>php5-mysql</code>.",
        "config-outdated-sqlite": "<strong>Aviso:</strong> Tem a versão $1 do SQLite, que é anterior à versão mínima necessária, a $2. O SQLite não estará disponível.",
        "config-mssql-windowsauth": "Autenticação do Windows",
        "config-site-name": "Nome da wiki:",
        "config-site-name-help": "Este nome aparecerá no título da janela do seu navegador e em vários outros sítios.",
-       "config-site-name-blank": "Introduza o nome do site.",
+       "config-site-name-blank": "Introduza o nome do sítio.",
        "config-project-namespace": "Espaço nominal do projeto:",
        "config-ns-generic": "Projeto",
        "config-ns-site-name": "O mesmo que o nome da wiki: $1",
        "config-logo": "URL do logótipo:",
        "config-logo-help": "O tema padrão do MediaWiki inclui espaço para um logótipo de 135x160 píxeis acima do menu da barra lateral.\nColoque na wiki uma imagem com estas dimensões e introduza aqui o URL dessa imagem.\n\nSe não pretende usar um logótipo, deixe este campo em branco.",
        "config-instantcommons": "Ativar Instant Commons",
-       "config-instantcommons-help": "O [https://www.mediawiki.org/wiki/InstantCommons Instant Commons] é uma funcionalidade que permite que as wikis usem imagens, áudio e outros ficheiros multimédia disponíveis no site [https://commons.wikimedia.org/ Wikimedia Commons].\nPara poder usá-los, o MediaWiki necessita de acesso à Internet.\n\nPara mais informações sobre esta funcionalidade, incluindo instruções sobre como configurá-la para usar outras wikis em vez da Wikimedia Commons, consulte o [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:$wgForeignFileRepos Manual Técnico].",
+       "config-instantcommons-help": "O [https://www.mediawiki.org/wiki/InstantCommons Instant Commons] é uma funcionalidade que permite que as wikis usem imagens, áudio e outros ficheiros multimédia disponíveis no sítio [https://commons.wikimedia.org/ Wikimedia Commons].\nPara poder usá-los, o MediaWiki necessita de acesso à Internet.\n\nPara mais informações sobre esta funcionalidade, incluindo instruções sobre como configurá-la para usar outras wikis em vez da Wikimedia Commons, consulte o [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:$wgForeignFileRepos Manual Técnico].",
        "config-cc-error": "O auxiliar de escolha de licenças da Creative Commons não produziu resultados.\nIntroduza o nome da licença manualmente.",
        "config-cc-again": "Escolha outra vez...",
        "config-cc-not-chosen": "Escolha a licença da Creative Commons que pretende e clique \"proceed\".",
        "config-advanced-settings": "Configuração avançada",
        "config-cache-options": "Configuração da cache de objetos:",
-       "config-cache-help": "A cache de objetos é usada para melhorar o desempenho do MediaWiki. Armazena dados usados com frequência.\nSites de tamanho médio ou grande são altamente encorajados a ativar esta funcionalidade e os sites pequenos também terão alguns benefícios em fazê-lo.",
+       "config-cache-help": "A cache de objetos é usada para melhorar o desempenho do MediaWiki. Armazena dados usados com frequência.\nSítios de tamanho médio ou grande são altamente encorajados a ativar esta funcionalidade e os sítios pequenos também terão alguns benefícios em fazê-lo.",
        "config-cache-none": "Sem cache (não é removida nenhuma funcionalidade, mas a velocidade de operação pode ser afectada nas wikis grandes)",
        "config-cache-accel": "Cache de objetos do PHP (APC, APCu, XCache ou WinCache)",
        "config-cache-memcached": "Usar Memcached (requer instalação e configurações adicionais)",
index b492208..2e65c87 100644 (file)
@@ -83,7 +83,7 @@
        "config-license-cc-by": "Creative Commons Ауторство (CC BY)",
        "config-license-cc-by-nc-sa": "Creative Commons Ауторство-Некомерцијално-Делити под истим условима (CC BY-NC-SA)",
        "config-license-cc-0": "Creative Commons Zero (јавно власништво)",
-       "config-license-gfdl": "Ð\93Ð\9dУ-ова Ð»Ð¸Ñ\86енÑ\86а Ð·Ð° Ñ\81лободнÑ\83 Ð´Ð¾ÐºÑ\83менÑ\82аÑ\86иÑ\98Ñ\83 Ð²ÐµÑ\80зиÑ\98а 1.3 Ð¸Ð»Ð¸ Ð½Ð¾Ð²Ð¸Ñ\98а Ð²ÐµÑ\80зиÑ\98а",
+       "config-license-gfdl": "Ð\93Ð\9dУ-ова Ð»Ð¸Ñ\86енÑ\86а Ð·Ð° Ñ\81лободнÑ\83 Ð´Ð¾ÐºÑ\83менÑ\82аÑ\86иÑ\98Ñ\83 Ð¸Ð·Ð´Ð°Ñ\9aе 1.3 Ð¸Ð»Ð¸ Ð½Ð¾Ð²Ð¸Ñ\98е",
        "config-license-pd": "Јавно власништво",
        "config-email-settings": "Подешавања имејла",
        "config-cc-not-chosen": "Одаберите која Кријејтив комонс лиценца вам одговара и потврдите.",
        "config-skins-screenshots": "„$1” (снимци екрана: $2)",
        "config-screenshot": "снимак екрана",
        "mainpagetext": "<strong>Медијавики је успешно инсталиран.</strong>",
-       "mainpagedocfooter": "Ð\9fогледаÑ\98Ñ\82е [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents ÐºÐ¾Ñ\80иÑ\81ниÑ\87ки Ð²Ð¾Ð´Ð¸Ñ\87] Ð·Ð° ÐºÐ¾Ñ\80иÑ\88Ñ\9bеÑ\9aе Ð¿Ñ\80огÑ\80ама.\n\n== Ð£Ð²Ð¾Ð´ ==\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Ð\9fомоÑ\9b Ñ\83 Ð²ÐµÐ·Ð¸ Ñ\81а Ð¿Ð¾Ð´ÐµÑ\88аваÑ\9aима]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ Ð§ÐµÑ\81Ñ\82о Ð¿Ð¾Ñ\81Ñ\82авÑ\99ена Ð¿Ð¸Ñ\82аÑ\9aа]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce Ð\94опиÑ\81на Ð»Ð¸Ñ\81Ñ\82а Ð¾ Ð¸Ð·Ð´Ð°Ñ\9aима Ð\9cедиÑ\98авикиÑ\98а]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam Ð\9dаÑ\83Ñ\87иÑ\82е ÐºÐ°ÐºÐ¾ Ð´Ð° Ñ\81е Ð±Ð¾Ñ\80ете против спама на Вашој вики]"
+       "mainpagedocfooter": "Ð\9fогледаÑ\98Ñ\82е [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents ÐºÐ¾Ñ\80иÑ\81ниÑ\87ки Ð²Ð¾Ð´Ð¸Ñ\87] Ð·Ð° ÐºÐ¾Ñ\80иÑ\88Ñ\9bеÑ\9aе Ð¿Ñ\80огÑ\80ама.\n\n== Ð£Ð²Ð¾Ð´ ==\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Ð\9fомоÑ\9b Ñ\83 Ð²ÐµÐ·Ð¸ Ñ\81а Ð¿Ð¾Ð´ÐµÑ\88аваÑ\9aима]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ Ð§ÐµÑ\81Ñ\82о Ð¿Ð¾Ñ\81Ñ\82авÑ\99ена Ð¿Ð¸Ñ\82аÑ\9aа]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce Ð\94опиÑ\81ни Ñ\81пиÑ\81ак Ð¾ Ð¸Ð·Ð´Ð°Ñ\9aима Ð\9cедиÑ\98авикиÑ\98а]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam Ð\9dаÑ\83Ñ\87иÑ\82е ÐºÐ°ÐºÐ¾ Ð´Ð° Ñ\81е Ð±Ð¾Ñ\80ите против спама на Вашој вики]"
 }
index 54339c3..a5b630a 100644 (file)
@@ -12,7 +12,8 @@
                        "아라",
                        "Amire80",
                        "Piramidion",
-                       "Macofe"
+                       "Macofe",
+                       "Movses"
                ]
        },
        "config-desc": "Інсталятор MediaWiki",
        "config-install-mainpage-failed": "Не вдається вставити головну сторінку: $1",
        "config-install-done": "<strong>Вітаємо!</strong>\nВи успішно встановили MediaWiki.\n\nІнсталятор згенерував файл <code>LocalSettings.php</code>, який містить усі Ваші налаштування.\n\nВам необхідно завантажити його і помістити у кореневу папку Вашої вікі (туди ж, де index.php). Завантаження мало початись автоматично.\n\nЯкщо завантаження не почалось або Ви його скасували, можете заново його почати, натиснувши на посилання внизу:\n\n$3\n\n<strong>Примітка</strong>: Якщо Ви не зробите цього зараз, цей файл не буде доступним пізніше, коли Ви вийдете з встановлення, не скачавши його.\n\nПісля виконання дій, описаних вище, Ви зможете <strong>[$2 увійти у свою вікі]</strong>.",
        "config-install-done-path": "<strong>Вітаємо!</strong>\nВи встановили Медіавікі.\n\nІнсталятор створив файл <code>LocalSettings.php</code>.\nУ ньому містяться всі Ваші налаштування.\n\nВам потрібно завантажити його й помістити в <code>$4</code>. Завантаження повинно було автоматично розпочатись.\n\nЯкщо завантаження не було запропоновано, або Ви його скасували, Ви можете перезапустити завантаження натиснувши на посилання нижче:\n\n$3\n\n<strong>Зверніть увагу:</strong> Якщо Ви не зробите це зараз, цей згенерований файл налаштувань не буде доступним для Вас пізніше якщо Ви вийдете зі встановлення не завантаживши його.\n\nКоли це було зроблено Ви можете <strong>[$2 зайти до своєї вікі]</strong>.",
+       "config-install-success": "Mediawiki успішно встановлено. Зараз ви можете перейти до <$1$2>, щоб переглянути свою вікі. Якщо у вас є питання, ознайомтеся з нашим FAQ: <https://www.mediawiki.org/wiki/Manual:FAQ> або використовуйте один з форумів підтримки, які вказано на цій сторінці.",
        "config-download-localsettings": "Завантажити <code>LocalSettings.php</code>",
        "config-help": "допомога",
        "config-help-tooltip": "натисніть, щоб розгорнути",
index 51308c1..08f960a 100644 (file)
@@ -175,7 +175,7 @@ abstract class FileBackend implements LoggerAwareInterface {
                        : new NullLockManager( [] );
                $this->fileJournal = isset( $config['fileJournal'] )
                        ? $config['fileJournal']
-                       : FileJournal::factory( [ 'class' => 'NullFileJournal' ], $this->name );
+                       : FileJournal::factory( [ 'class' => NullFileJournal::class ], $this->name );
                $this->readOnly = isset( $config['readOnly'] )
                        ? (string)$config['readOnly']
                        : '';
@@ -1597,7 +1597,7 @@ abstract class FileBackend implements LoggerAwareInterface {
        final protected function newStatus() {
                $args = func_get_args();
                if ( count( $args ) ) {
-                       $sv = call_user_func_array( [ 'StatusValue', 'newFatal' ], $args );
+                       $sv = call_user_func_array( [ StatusValue::class, 'newFatal' ], $args );
                } else {
                        $sv = StatusValue::newGood();
                }
index b8eec3f..da8b4ce 100644 (file)
@@ -1008,13 +1008,13 @@ abstract class FileBackendStore extends FileBackend {
         */
        final public function getOperationsInternal( array $ops ) {
                $supportedOps = [
-                       'store' => 'StoreFileOp',
-                       'copy' => 'CopyFileOp',
-                       'move' => 'MoveFileOp',
-                       'delete' => 'DeleteFileOp',
-                       'create' => 'CreateFileOp',
-                       'describe' => 'DescribeFileOp',
-                       'null' => 'NullFileOp'
+                       'store' => StoreFileOp::class,
+                       'copy' => CopyFileOp::class,
+                       'move' => MoveFileOp::class,
+                       'delete' => DeleteFileOp::class,
+                       'create' => CreateFileOp::class,
+                       'describe' => DescribeFileOp::class,
+                       'null' => NullFileOp::class
                ];
 
                $performOps = []; // array of FileOp objects
index aecdf60..6274d60 100644 (file)
@@ -69,10 +69,10 @@ class MemcLockManager extends QuorumLockManager {
                $this->srvsByBucket = array_values( $this->srvsByBucket ); // consecutive
 
                $memcConfig = isset( $config['memcConfig'] ) ? $config['memcConfig'] : [];
-               $memcConfig += [ 'class' => 'MemcachedPhpBagOStuff' ]; // default
+               $memcConfig += [ 'class' => MemcachedPhpBagOStuff::class ]; // default
 
                $class = $memcConfig['class'];
-               if ( !is_subclass_of( $class, 'MemcachedBagOStuff' ) ) {
+               if ( !is_subclass_of( $class, MemcachedBagOStuff::class ) ) {
                        throw new InvalidArgumentException( "$class is not of type MemcachedBagOStuff." );
                }
 
index 409f507..d939819 100644 (file)
@@ -47,17 +47,23 @@ use Psr\Log\NullLogger;
  * There are three supported ways to handle broadcasted operations:
  *   - a) Configure the 'purge' EventRelayer to point to a valid PubSub endpoint
  *         that has subscribed listeners on the cache servers applying the cache updates.
- *   - b) Ignore the 'purge' EventRelayer configuration (default is NullEventRelayer)
- *         and set up mcrouter as the underlying cache backend, using one of the memcached
- *         BagOStuff classes as 'cache'. Use OperationSelectorRoute in the mcrouter settings
- *         to configure 'set' and 'delete' operations to go to all DCs via AllAsyncRoute and
- *         configure other operations to go to the local DC via PoolRoute (for reference,
- *         see https://github.com/facebook/mcrouter/wiki/List-of-Route-Handles).
- *   - c) Ignore the 'purge' EventRelayer configuration (default is NullEventRelayer)
- *         and set up dynomite as cache middleware between the web servers and either
- *         memcached or redis. This will also broadcast all key setting operations, not just purges,
- *         which can be useful for cache warming. Writes are eventually consistent via the
- *         Dynamo replication model (see https://github.com/Netflix/dynomite).
+ *   - b) Ommit the 'purge' EventRelayer parameter and set up mcrouter as the underlying cache
+ *        backend, using a memcached BagOStuff class for the 'cache' parameter. The 'region'
+ *        and 'cluster' parameters must be provided and 'mcrouterAware' must be set to 'true'.
+ *        Configure mcrouter as follows:
+ *          - 1) Use Route Prefixing based on region (datacenter) and cache cluster.
+ *                See https://github.com/facebook/mcrouter/wiki/Routing-Prefix and
+ *                https://github.com/facebook/mcrouter/wiki/Multi-cluster-broadcast-setup
+ *          - 2) To increase the consistency of delete() and touchCheckKey() during cache
+ *                server membership changes, you can use the OperationSelectorRoute to
+ *                configure 'set' and 'delete' operations to go to all servers in the cache
+ *                cluster, instead of just one server determined by hashing.
+ *                See https://github.com/facebook/mcrouter/wiki/List-of-Route-Handles
+ *   - c) Ommit the 'purge' EventRelayer parameter and set up dynomite as cache middleware
+ *         between the web servers and either memcached or redis. This will also broadcast all
+ *         key setting operations, not just purges, which can be useful for cache warming.
+ *         Writes are eventually consistent via the Dynamo replication model.
+ *         See https://github.com/Netflix/dynomite
  *
  * Broadcasted operations like delete() and touchCheckKey() are done asynchronously
  * in all datacenters this way, though the local one should likely be near immediate.
@@ -87,6 +93,12 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
        protected $purgeChannel;
        /** @var EventRelayer Bus that handles purge broadcasts */
        protected $purgeRelayer;
+       /** @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 */
@@ -200,6 +212,16 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
         *       callback supplied by the getWithSetCallback() caller. The result will be saved
         *       as normal. The handler is expected to call the WAN cache callback at an opportune
         *       time (e.g. HTTP post-send), though generally within a few 100ms. [optional]
+        *   - region: the current physical region. This is required when using mcrouter as the
+        *       backing store proxy. [optional]
+        *   - cluster: name of the cache cluster used by this WAN cache. The name must be the
+        *       same in all datacenters; the ("region","cluster") tuple is what distinguishes
+        *       the counterpart cache clusters among all the datacenter. The contents of
+        *       https://github.com/facebook/mcrouter/wiki/Config-Files give background on this.
+        *       This is required when using mcrouter as the backing store proxy. [optional]
+        *   - mcrouterAware: set as true if mcrouter is the backing store proxy and mcrouter
+        *       is configured to interpret /<region>/<cluster>/ key prefixes as routes. This
+        *       requires that "region" and "cluster" are both set above. [optional]
         */
        public function __construct( array $params ) {
                $this->cache = $params['cache'];
@@ -209,6 +231,10 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
                $this->purgeRelayer = isset( $params['relayers']['purge'] )
                        ? $params['relayers']['purge']
                        : new EventRelayerNull( [] );
+               $this->region = isset( $params['region'] ) ? $params['region'] : 'main';
+               $this->cluster = isset( $params['cluster'] ) ? $params['cluster'] : 'wan-main';
+               $this->mcrouterAware = !empty( $params['mcrouterAware'] );
+
                $this->setLogger( isset( $params['logger'] ) ? $params['logger'] : new NullLogger() );
                $this->stats = isset( $params['stats'] ) ? $params['stats'] : new NullStatsdDataFactory();
                $this->asyncHandler = isset( $params['asyncHandler'] ) ? $params['asyncHandler'] : null;
@@ -419,6 +445,12 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
         *
         * Setting 'lag' and 'since' help avoids keys getting stuck in stale states.
         *
+        * Be aware that this does not update the process cache for getWithSetCallback()
+        * callers. Keys accessed via that method are not generally meant to also be set
+        * using this primitive method.
+        *
+        * Do not use this method on versioned keys accessed via getWithSetCallback().
+        *
         * Example usage:
         * @code
         *     $dbr = wfGetDB( DB_REPLICA );
@@ -535,6 +567,10 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
         *
         * Note that set() can also be lag-aware and lower the TTL if it's high.
         *
+        * Be aware that this does not clear the process cache. Even if it did, callbacks
+        * used by getWithSetCallback() might still return stale data in the case of either
+        * uncommitted or not-yet-replicated changes (callback generally use replica DBs).
+        *
         * When using potentially long-running ACID transactions, a good pattern is
         * to use a pre-commit hook to issue the delete. This means that immediately
         * after commit, callers will see the tombstone in cache upon purge relay.
@@ -1769,9 +1805,18 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
         * @return bool Success
         */
        protected function relayPurge( $key, $ttl, $holdoff ) {
-               if ( $this->purgeRelayer instanceof EventRelayerNull ) {
+               if ( $this->mcrouterAware ) {
+                       // See https://github.com/facebook/mcrouter/wiki/Multi-cluster-broadcast-setup
+                       // 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 ),
+                               $ttl
+                       );
+               } elseif ( $this->purgeRelayer instanceof EventRelayerNull ) {
                        // This handles the mcrouter and the single-DC case
-                       $ok = $this->cache->set( $key,
+                       $ok = $this->cache->set(
+                               $key,
                                $this->makePurgeValue( $this->getCurrentTime(), self::HOLDOFF_NONE ),
                                $ttl
                        );
@@ -1800,8 +1845,12 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
         * @return bool Success
         */
        protected function relayDelete( $key ) {
-               if ( $this->purgeRelayer instanceof EventRelayerNull ) {
-                       // This handles the mcrouter and the single-DC case
+               if ( $this->mcrouterAware ) {
+                       // See https://github.com/facebook/mcrouter/wiki/Multi-cluster-broadcast-setup
+                       // Wildcards select all matching routes, e.g. the WAN cluster on all DCs
+                       $ok = $this->cache->delete( "/*/{$this->cluster}/{$key}" );
+               } elseif ( $this->purgeRelayer instanceof EventRelayerNull ) {
+                       // Some other proxy handles broadcasting or there is only one datacenter
                        $ok = $this->cache->delete( $key );
                } else {
                        $event = $this->cache->modifySimpleRelayEvent( [
index a828cd3..c353a22 100644 (file)
@@ -177,7 +177,7 @@ class TransactionProfiler implements LoggerAwareInterface {
        public function transactionWritingIn( $server, $db, $id ) {
                $name = "{$server} ({$db}) (TRX#$id)";
                if ( isset( $this->dbTrxHoldingLocks[$name] ) ) {
-                       $this->logger->info( "Nested transaction for '$name' - out of sync." );
+                       $this->logger->warning( "Nested transaction for '$name' - out of sync." );
                }
                $this->dbTrxHoldingLocks[$name] = [
                        'start' => microtime( true ),
@@ -206,7 +206,7 @@ class TransactionProfiler implements LoggerAwareInterface {
                $elapsed = ( $eTime - $sTime );
 
                if ( $isWrite && $n > $this->expect['maxAffected'] ) {
-                       $this->logger->info(
+                       $this->logger->warning(
                                "Query affected $n row(s):\n" . $query . "\n" .
                                ( new RuntimeException() )->getTraceAsString() );
                }
@@ -271,7 +271,7 @@ class TransactionProfiler implements LoggerAwareInterface {
        public function transactionWritingOut( $server, $db, $id, $writeTime = 0.0, $affected = 0 ) {
                $name = "{$server} ({$db}) (TRX#$id)";
                if ( !isset( $this->dbTrxMethodTimes[$name] ) ) {
-                       $this->logger->info( "Detected no transaction for '$name' - out of sync." );
+                       $this->logger->warning( "Detected no transaction for '$name' - out of sync." );
                        return;
                }
 
@@ -317,7 +317,7 @@ class TransactionProfiler implements LoggerAwareInterface {
                                list( $query, $sTime, $end ) = $info;
                                $trace .= sprintf( "%d\t%.6f\t%s\n", $i, ( $end - $sTime ), $query );
                        }
-                       $this->logger->info( "Sub-optimal transaction on DB(s) [{dbs}]: \n{trace}", [
+                       $this->logger->warning( "Sub-optimal transaction on DB(s) [{dbs}]: \n{trace}", [
                                'dbs' => implode( ', ', array_keys( $this->dbTrxHoldingLocks[$name]['conns'] ) ),
                                'trace' => $trace
                        ] );
@@ -336,7 +336,7 @@ class TransactionProfiler implements LoggerAwareInterface {
                        return;
                }
 
-               $this->logger->info(
+               $this->logger->warning(
                        "Expectation ({measure} <= {max}) by {by} not met (actual: {actual}):\n{query}\n" .
                        ( new RuntimeException() )->getTraceAsString(),
                        [
index 1efa9a1..f3877fb 100644 (file)
@@ -126,6 +126,8 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
        protected $delimiter = ';';
        /** @var DatabaseDomain */
        protected $currentDomain;
+       /** @var integer|null Rows affected by the last query to query() or its CRUD wrappers */
+       protected $affectedRowCount;
 
        /**
         * Either 1 if a transaction is active or 0 otherwise.
@@ -1002,8 +1004,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
        }
 
        /**
-        * Helper method for query() that handles profiling and logging and sends
-        * the query to doQuery()
+        * Wrapper for query() that also handles profiling, logging, and affected row count updates
         *
         * @param string $sql Original SQL query
         * @param string $commentedSql SQL query with debugging/trace comment
@@ -1029,7 +1030,9 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                if ( $this->profiler ) {
                        call_user_func( [ $this->profiler, 'profileIn' ], $queryProf );
                }
+               $this->affectedRowCount = null;
                $ret = $this->doQuery( $commentedSql );
+               $this->affectedRowCount = $this->affectedRows();
                if ( $this->profiler ) {
                        call_user_func( [ $this->profiler, 'profileOut' ], $queryProf );
                }
@@ -2238,51 +2241,49 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
        }
 
        public function replace( $table, $uniqueIndexes, $rows, $fname = __METHOD__ ) {
-               $quotedTable = $this->tableName( $table );
-
                if ( count( $rows ) == 0 ) {
                        return;
                }
 
-               # Single row case
+               // Single row case
                if ( !is_array( reset( $rows ) ) ) {
                        $rows = [ $rows ];
                }
 
-               // @FXIME: this is not atomic, but a trx would break affectedRows()
+               $affectedRowCount = 0;
                foreach ( $rows as $row ) {
-                       # Delete rows which collide
-                       if ( $uniqueIndexes ) {
-                               $sql = "DELETE FROM $quotedTable WHERE ";
-                               $first = true;
-                               foreach ( $uniqueIndexes as $index ) {
-                                       if ( $first ) {
-                                               $first = false;
-                                               $sql .= '( ';
-                                       } else {
-                                               $sql .= ' ) OR ( ';
-                                       }
-                                       if ( is_array( $index ) ) {
-                                               $first2 = true;
-                                               foreach ( $index as $col ) {
-                                                       if ( $first2 ) {
-                                                               $first2 = false;
-                                                       } else {
-                                                               $sql .= ' AND ';
-                                                       }
-                                                       $sql .= $col . '=' . $this->addQuotes( $row[$col] );
-                                               }
-                                       } else {
-                                               $sql .= $index . '=' . $this->addQuotes( $row[$index] );
-                                       }
+                       // Delete rows which collide with this one
+                       $indexWhereClauses = [];
+                       foreach ( $uniqueIndexes as $index ) {
+                               $indexColumns = (array)$index;
+                               $indexRowValues = array_intersect_key( $row, array_flip( $indexColumns ) );
+                               if ( count( $indexRowValues ) != count( $indexColumns ) ) {
+                                       throw new DBUnexpectedError(
+                                               $this,
+                                               'New record does not provide all values for unique key (' .
+                                                       implode( ', ', $indexColumns ) . ')'
+                                       );
+                               } elseif ( in_array( null, $indexRowValues, true ) ) {
+                                       throw new DBUnexpectedError(
+                                               $this,
+                                               'New record has a null value for unique key (' .
+                                                       implode( ', ', $indexColumns ) . ')'
+                                       );
                                }
-                               $sql .= ' )';
-                               $this->query( $sql, $fname );
+                               $indexWhereClauses[] = $this->makeList( $indexRowValues, LIST_AND );
+                       }
+
+                       if ( $indexWhereClauses ) {
+                               $this->delete( $table, $this->makeList( $indexWhereClauses, LIST_OR ), $fname );
+                               $affectedRowCount += $this->affectedRows();
                        }
 
-                       # Now insert the row
+                       // Now insert the row
                        $this->insert( $table, $row, $fname );
+                       $affectedRowCount += $this->affectedRows();
                }
+
+               $this->affectedRowCount = $affectedRowCount;
        }
 
        /**
@@ -2347,6 +2348,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                        $where = false;
                }
 
+               $affectedRowCount = 0;
                $useTrx = !$this->mTrxLevel;
                if ( $useTrx ) {
                        $this->begin( $fname, self::TRANSACTION_INTERNAL );
@@ -2355,11 +2357,13 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                        # Update any existing conflicting row(s)
                        if ( $where !== false ) {
                                $ok = $this->update( $table, $set, $where, $fname );
+                               $affectedRowCount += $this->affectedRows();
                        } else {
                                $ok = true;
                        }
                        # Now insert any non-conflicting row(s)
                        $ok = $this->insert( $table, $rows, $fname, [ 'IGNORE' ] ) && $ok;
+                       $affectedRowCount += $this->affectedRows();
                } catch ( Exception $e ) {
                        if ( $useTrx ) {
                                $this->rollback( $fname, self::FLUSHING_INTERNAL );
@@ -2369,6 +2373,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                if ( $useTrx ) {
                        $this->commit( $fname, self::FLUSHING_INTERNAL );
                }
+               $this->affectedRowCount = $affectedRowCount;
 
                return $ok;
        }
@@ -3187,6 +3192,17 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                }
        }
 
+       public function affectedRows() {
+               return ( $this->affectedRowCount === null )
+                       ? $this->fetchAffectedRowCount() // default to driver value
+                       : $this->affectedRowCount;
+       }
+
+       /**
+        * @return int Number of retrieved rows according to the driver
+        */
+       abstract protected function fetchAffectedRowCount();
+
        /**
         * Take the result from a query, and wrap it in a ResultWrapper if
         * necessary. Boolean values are passed through as is, to indicate success
index 53beb65..832ed9e 100644 (file)
@@ -353,7 +353,7 @@ class DatabaseMssql extends Database {
        /**
         * @return int
         */
-       public function affectedRows() {
+       protected function fetchAffectedRowCount() {
                return $this->mAffectedRows;
        }
 
index 305a056..6a545ce 100644 (file)
@@ -63,6 +63,8 @@ abstract class DatabaseMysqlBase extends Database {
 
        /** @var string|null */
        private $serverVersion = null;
+       /** @var bool|null */
+       private $insertSelectIsSafe = null;
 
        /**
         * Additional $params include:
@@ -75,6 +77,7 @@ abstract class DatabaseMysqlBase extends Database {
         *       ID of this server's master will be used. Set the "conds" field to
         *       override the query conditions, e.g. ['shard' => 's1'].
         *   - useGTIDs : use GTID methods like MASTER_GTID_WAIT() when possible.
+        *   - insertSelectIsSafe : force that native INSERT SELECT is or is not safe [default: null]
         *   - sslKeyPath : path to key file [default: null]
         *   - sslCertPath : path to certificate file [default: null]
         *   - sslCAFile: path to a single certificate authority PEM file [default: null]
@@ -98,6 +101,8 @@ abstract class DatabaseMysqlBase extends Database {
                }
                $this->sqlMode = isset( $params['sqlMode'] ) ? $params['sqlMode'] : '';
                $this->utf8Mode = !empty( $params['utf8Mode'] );
+               $this->insertSelectIsSafe = isset( $params['insertSelectIsSafe'] )
+                       ? (bool)$params['insertSelectIsSafe'] : null;
 
                parent::__construct( $params );
        }
@@ -501,6 +506,56 @@ abstract class DatabaseMysqlBase extends Database {
                return $this->nativeReplace( $table, $rows, $fname );
        }
 
+       protected function nativeInsertSelect(
+               $destTable, $srcTable, $varMap, $conds,
+               $fname = __METHOD__, $insertOptions = [], $selectOptions = [], $selectJoinConds = []
+       ) {
+               if ( $this->insertSelectIsSafe === null ) {
+                       // In MySQL, an INSERT SELECT is only replication safe with row-based
+                       // replication or if innodb_autoinc_lock_mode is 0. When those
+                       // conditions aren't met, use non-native mode.
+                       // While we could try to determine if the insert is safe anyway by
+                       // checking if the target table has an auto-increment column that
+                       // isn't set in $varMap, that seems unlikely to be worth the extra
+                       // complexity.
+                       $row = $this->selectRow(
+                               false,
+                               [
+                                       'innodb_autoinc_lock_mode' => '@@innodb_autoinc_lock_mode',
+                                       'binlog_format' => '@@binlog_format',
+                               ],
+                               [],
+                               __METHOD__
+                       );
+                       $this->insertSelectIsSafe = $row->binlog_format === 'ROW' ||
+                               (int)$row->innodb_autoinc_lock_mode === 0;
+               }
+
+               if ( !$this->insertSelectIsSafe ) {
+                       return $this->nonNativeInsertSelect(
+                               $destTable,
+                               $srcTable,
+                               $varMap,
+                               $conds,
+                               $fname,
+                               $insertOptions,
+                               $selectOptions,
+                               $selectJoinConds
+                       );
+               } else {
+                       return parent::nativeInsertSelect(
+                               $destTable,
+                               $srcTable,
+                               $varMap,
+                               $conds,
+                               $fname,
+                               $insertOptions,
+                               $selectOptions,
+                               $selectJoinConds
+                       );
+               }
+       }
+
        /**
         * Estimate rows in dataset
         * Returns estimated count, based on EXPLAIN output
@@ -834,8 +889,10 @@ abstract class DatabaseMysqlBase extends Database {
                        return 0; // already reached this point for sure
                }
 
+               $useGTID = ( $this->useGTIDs && $pos->gtids );
+
                // Call doQuery() directly, to avoid opening a transaction if DBO_TRX is set
-               if ( $this->useGTIDs && $pos->gtids ) {
+               if ( $useGTID ) {
                        // Wait on the GTID set (MariaDB only)
                        $gtidArg = $this->addQuotes( implode( ',', $pos->gtids ) );
                        $res = $this->doQuery( "SELECT MASTER_GTID_WAIT($gtidArg, $timeout)" );
@@ -855,14 +912,17 @@ abstract class DatabaseMysqlBase extends Database {
                // Result can be NULL (error), -1 (timeout), or 0+ per the MySQL manual
                $status = ( $row[0] !== null ) ? intval( $row[0] ) : null;
                if ( $status === null ) {
-                       // T126436: jobs programmed to wait on master positions might be referencing binlogs
-                       // with an old master hostname. Such calls make MASTER_POS_WAIT() return null. Try
-                       // to detect this and treat the replica DB as having reached the position; a proper master
-                       // switchover already requires that the new master be caught up before the switch.
-                       $replicationPos = $this->getReplicaPos();
-                       if ( $replicationPos && !$replicationPos->channelsMatch( $pos ) ) {
-                               $this->lastKnownReplicaPos = $replicationPos;
-                               $status = 0;
+                       if ( !$useGTID ) {
+                               // T126436: jobs programmed to wait on master positions might be referencing
+                               // binlogs with an old master hostname; this makes MASTER_POS_WAIT() return null.
+                               // Try to detect this case and treat the replica DB as having reached the given
+                               // position (any master switchover already requires that the new master be caught
+                               // up before the switch).
+                               $replicationPos = $this->getReplicaPos();
+                               if ( $replicationPos && !$replicationPos->channelsMatch( $pos ) ) {
+                                       $this->lastKnownReplicaPos = $replicationPos;
+                                       $status = 0;
+                               }
                        }
                } elseif ( $status >= 0 ) {
                        // Remember that this position was reached to save queries next time
index c1a5698..09ea66c 100644 (file)
@@ -172,7 +172,7 @@ class DatabaseMysqli extends DatabaseMysqlBase {
        /**
         * @return int
         */
-       function affectedRows() {
+       protected function fetchAffectedRowCount() {
                $conn = $this->getBindingHandle();
 
                return $conn->affected_rows;
index 8c21d72..5bf845b 100644 (file)
@@ -384,7 +384,7 @@ class DatabasePostgres extends Database {
                }
        }
 
-       public function affectedRows() {
+       protected function fetchAffectedRowCount() {
                if ( !is_null( $this->mAffectedRows ) ) {
                        // Forced result for simulated queries
                        return $this->mAffectedRows;
index 2b06607..01772cf 100644 (file)
@@ -56,6 +56,9 @@ class DatabaseSqlite extends Database {
        /** @var FSLockManager (hopefully on the same server as the DB) */
        protected $lockMgr;
 
+       /** @var array List of shared database already attached to this connection */
+       private $alreadyAttached = [];
+
        /**
         * Additional params include:
         *   - dbDirectory : directory containing the DB and the lock file directory
@@ -82,16 +85,7 @@ class DatabaseSqlite extends Database {
                        parent::__construct( $p );
                        // Super doesn't open when $user is false, but we can work with $dbName
                        if ( $p['dbname'] && !$this->isOpen() ) {
-                               if ( $this->open( $p['host'], $p['user'], $p['password'], $p['dbname'] ) ) {
-                                       $done = [];
-                                       foreach ( $this->tableAliases as $params ) {
-                                               if ( isset( $done[$params['dbname']] ) ) {
-                                                       continue;
-                                               }
-                                               $this->attachDatabase( $params['dbname'] );
-                                               $done[$params['dbname']] = 1;
-                                       }
-                               }
+                               $this->open( $p['host'], $p['user'], $p['password'], $p['dbname'] );
                        }
                }
 
@@ -302,6 +296,14 @@ class DatabaseSqlite extends Database {
                return parent::isWriteQuery( $sql ) && !preg_match( '/^(ATTACH|PRAGMA)\b/i', $sql );
        }
 
+       protected function isTransactableQuery( $sql ) {
+               return parent::isTransactableQuery( $sql ) && !in_array(
+                       $this->getQueryVerb( $sql ),
+                       [ 'ATTACH', 'PRAGMA' ],
+                       true
+               );
+       }
+
        /**
         * SQLite doesn't allow buffered results or data seeking etc, so we'll use fetchAll as the result
         *
@@ -494,7 +496,7 @@ class DatabaseSqlite extends Database {
        /**
         * @return int
         */
-       function affectedRows() {
+       protected function fetchAffectedRowCount() {
                return $this->mAffectedRows;
        }
 
@@ -1033,6 +1035,17 @@ class DatabaseSqlite extends Database {
                return $this->query( $sql, $fName );
        }
 
+       public function setTableAliases( array $aliases ) {
+               parent::setTableAliases( $aliases );
+               foreach ( $this->tableAliases as $params ) {
+                       if ( isset( $this->alreadyAttached[$params['dbname']] ) ) {
+                               continue;
+                       }
+                       $this->attachDatabase( $params['dbname'] );
+                       $this->alreadyAttached[$params['dbname']] = true;
+               }
+       }
+
        protected function requiresDatabaseUser() {
                return false; // just a file
        }
index 85b3481..0964dd5 100644 (file)
@@ -1255,6 +1255,11 @@ interface IDatabase {
         * INSERT SELECT wrapper. Takes data from a SELECT query and inserts it
         * into another table.
         *
+        * @warning If the insert will use an auto-increment or sequence to
+        *  determine the value of a column, this may break replication on
+        *  databases using statement-based replication if the SELECT is not
+        *  deterministically ordered.
+        *
         * @param string $destTable The table name to insert into
         * @param string|array $srcTable May be either a table name, or an array of table names
         *    to include in a join.
index 298ec61..4b79044 100644 (file)
@@ -15,7 +15,7 @@ class MssqlResultWrapper extends ResultWrapper {
                $res = $this->result;
 
                if ( $this->mSeekTo !== null ) {
-                       $result = sqlsrv_fetch_object( $res, 'stdClass', [],
+                       $result = sqlsrv_fetch_object( $res, stdClass::class, [],
                                SQLSRV_SCROLL_ABSOLUTE, $this->mSeekTo );
                        $this->mSeekTo = null;
                } else {
index 89472e7..be80cc5 100644 (file)
@@ -77,4 +77,4 @@ class LoadBalancerSingle extends LoadBalancer {
        }
 }
 
-class_alias( 'Wikimedia\Rdbms\LoadBalancerSingle', 'LoadBalancerSingle' );
+class_alias( LoadBalancerSingle::class, 'LoadBalancerSingle' );
index 2d7e8f2..491726b 100644 (file)
@@ -27,7 +27,7 @@
  */
 class CategoryPage extends Article {
        # Subclasses can change this to override the viewer class.
-       protected $mCategoryViewerClass = 'CategoryViewer';
+       protected $mCategoryViewerClass = CategoryViewer::class;
 
        /**
         * @var WikiCategoryPage
index 831c1ff..b2d8511 100644 (file)
@@ -270,15 +270,15 @@ class Parser {
                        $this->mPreprocessorClass = $conf['preprocessorClass'];
                } elseif ( defined( 'HPHP_VERSION' ) ) {
                        # Preprocessor_Hash is much faster than Preprocessor_DOM under HipHop
-                       $this->mPreprocessorClass = 'Preprocessor_Hash';
+                       $this->mPreprocessorClass = Preprocessor_Hash::class;
                } elseif ( extension_loaded( 'domxml' ) ) {
                        # PECL extension that conflicts with the core DOM extension (T15770)
                        wfDebug( "Warning: you have the obsolete domxml extension for PHP. Please remove it!\n" );
-                       $this->mPreprocessorClass = 'Preprocessor_Hash';
+                       $this->mPreprocessorClass = Preprocessor_Hash::class;
                } elseif ( extension_loaded( 'dom' ) ) {
-                       $this->mPreprocessorClass = 'Preprocessor_DOM';
+                       $this->mPreprocessorClass = Preprocessor_DOM::class;
                } else {
-                       $this->mPreprocessorClass = 'Preprocessor_Hash';
+                       $this->mPreprocessorClass = Preprocessor_Hash::class;
                }
                wfDebug( __CLASS__ . ": using preprocessor: {$this->mPreprocessorClass}\n" );
        }
index f99089b..1405c45 100644 (file)
@@ -781,6 +781,7 @@ class ParserOptions {
         * CSS class to use to wrap output from Parser::parse()
         * @since 1.30
         * @param string|bool $className Set false to disable wrapping.
+        *   Passing false is deprecated since MediaWiki 1.31
         * @return string|bool Current value
         */
        public function setWrapOutputClass( $className ) {
@@ -1059,8 +1060,8 @@ class ParserOptions {
                                'printable' => false,
                                'allowUnsafeRawHtml' => true,
                                'wrapclass' => 'mw-parser-output',
-                               'currentRevisionCallback' => [ 'Parser', 'statelessFetchRevision' ],
-                               'templateCallback' => [ 'Parser', 'statelessFetchTemplate' ],
+                               'currentRevisionCallback' => [ Parser::class, 'statelessFetchRevision' ],
+                               'templateCallback' => [ Parser::class, 'statelessFetchTemplate' ],
                                'speculativeRevIdCallback' => null,
                        ];
 
index 153a770..e2efaff 100644 (file)
  */
 class ParserOutput extends CacheTime {
        /**
-        * Feature flag to indicate to extensions that MediaWiki core supports and
+        * Feature flags to indicate to extensions that MediaWiki core supports and
         * uses getText() stateless transforms.
         */
        const SUPPORTS_STATELESS_TRANSFORMS = 1;
+       const SUPPORTS_UNWRAP_TRANSFORM = 1;
 
        /**
         * @var string $mText The output text
@@ -266,29 +267,47 @@ class ParserOutput extends CacheTime {
         *     to generate one and `__NOTOC__` wasn't used. Default is true,
         *     but might be statefully overridden.
         *  - enableSectionEditLinks: (bool) Include section edit links, assuming
-        *    section edit link tokens are present in the HTML. Default is true,
+        *     section edit link tokens are present in the HTML. Default is true,
         *     but might be statefully overridden.
+        *  - unwrap: (bool) Remove a wrapping mw-parser-output div. Default is false.
         * @return string HTML
         */
        public function getText( $options = [] ) {
-               // @todo Warn if !array_key_exists( 'allowTOC', $options ) && empty( $this->mTOCEnabled )
+               if ( !array_key_exists( 'allowTOC', $options ) && empty( $this->mTOCEnabled ) ) {
+                       wfDeprecated( 'ParserOutput stateful allowTOC', '1.31' );
+               }
 
-               // @todo Warn if !array_key_exists( 'enableSectionEditLinks', $options )
-               //     && !$this->mEditSectionTokens
                //  Note that while $this->mEditSectionTokens formerly defaulted to false,
                //  ParserOptions->getEditSection() defaults to true and Parser copies
                //  that to us so true makes more sense as the stateless default.
+               if ( !array_key_exists( 'enableSectionEditLinks', $options ) && !$this->mEditSectionTokens ) {
+                       wfDeprecated( 'ParserOutput stateful enableSectionEditLinks', '1.31' );
+               }
 
                $options += [
                        // empty() here because old cached versions might lack the field somehow.
                        // In that situation, the historical behavior (possibly buggy) is to remove the TOC.
                        'allowTOC' => !empty( $this->mTOCEnabled ),
                        'enableSectionEditLinks' => $this->mEditSectionTokens,
+                       'unwrap' => false,
                ];
                $text = $this->mText;
 
                Hooks::runWithoutAbort( 'ParserOutputPostCacheTransform', [ $this, &$text, &$options ] );
 
+               if ( $options['unwrap'] !== false ) {
+                       $start = Html::openElement( 'div', [
+                               'class' => 'mw-parser-output'
+                       ] );
+                       $startLen = strlen( $start );
+                       $end = Html::closeElement( 'div' );
+                       $endLen = strlen( $end );
+
+                       if ( substr( $text, 0, $startLen ) === $start && substr( $text, -$endLen ) === $end ) {
+                               $text = substr( $text, $startLen, -$endLen );
+                       }
+               }
+
                if ( $options['enableSectionEditLinks'] ) {
                        $text = preg_replace_callback(
                                self::EDITSECTION_REGEX,
index 79021dd..4db2855 100644 (file)
@@ -823,7 +823,7 @@ class PPDStack {
         */
        public $top;
        public $out;
-       public $elementClass = 'PPDStackElement';
+       public $elementClass = PPDStackElement::class;
 
        public static $false = false;
 
@@ -931,7 +931,7 @@ class PPDStackElement {
         */
        public $lineStart;
 
-       public $partClass = 'PPDPart';
+       public $partClass = PPDPart::class;
 
        public function __construct( $data = [] ) {
                $class = $this->partClass;
index 468b009..c4c0c5a 100644 (file)
@@ -792,7 +792,7 @@ class Preprocessor_Hash extends Preprocessor {
 class PPDStack_Hash extends PPDStack {
 
        public function __construct() {
-               $this->elementClass = 'PPDStackElement_Hash';
+               $this->elementClass = PPDStackElement_Hash::class;
                parent::__construct();
                $this->rootAccum = [];
        }
@@ -805,7 +805,7 @@ class PPDStack_Hash extends PPDStack {
 class PPDStackElement_Hash extends PPDStackElement {
 
        public function __construct( $data = [] ) {
-               $this->partClass = 'PPDPart_Hash';
+               $this->partClass = PPDPart_Hash::class;
                parent::__construct( $data );
        }
 
@@ -1766,12 +1766,12 @@ class PPNode_Hash_Tree implements PPNode {
 
                $descriptor = $store[$index];
                if ( is_string( $descriptor ) ) {
-                       $class = 'PPNode_Hash_Text';
+                       $class = PPNode_Hash_Text::class;
                } elseif ( is_array( $descriptor ) ) {
                        if ( $descriptor[self::NAME][0] === '@' ) {
-                               $class = 'PPNode_Hash_Attr';
+                               $class = PPNode_Hash_Attr::class;
                        } else {
-                               $class = 'PPNode_Hash_Tree';
+                               $class = self::class;
                        }
                } else {
                        throw new MWException( __METHOD__.': invalid node descriptor' );
index b570a43..de7a6fa 100644 (file)
@@ -1171,7 +1171,9 @@ class Sanitizer {
                # Stupid hack
                $encValue = preg_replace_callback(
                        '/((?i)' . wfUrlProtocols() . ')/',
-                       [ 'Sanitizer', 'armorLinksCallback' ],
+                       function ( $matches ) {
+                               return str_replace( ':', '&#58;', $matches[1] );
+                       },
                        $encValue );
                return $encValue;
        }
@@ -1416,15 +1418,6 @@ class Sanitizer {
                return $html;
        }
 
-       /**
-        * Regex replace callback for armoring links against further processing.
-        * @param array $matches
-        * @return string
-        */
-       private static function armorLinksCallback( $matches ) {
-               return str_replace( ':', '&#58;', $matches[1] );
-       }
-
        /**
         * Return an associative array of attribute names and values from
         * a partial tag string. Attribute names are forced to lowercase,
@@ -1549,7 +1542,7 @@ class Sanitizer {
        static function normalizeCharReferences( $text ) {
                return preg_replace_callback(
                        self::CHAR_REFS_REGEX,
-                       [ 'Sanitizer', 'normalizeCharReferencesCallback' ],
+                       [ self::class, 'normalizeCharReferencesCallback' ],
                        $text );
        }
 
@@ -1649,7 +1642,7 @@ class Sanitizer {
        public static function decodeCharReferences( $text ) {
                return preg_replace_callback(
                        self::CHAR_REFS_REGEX,
-                       [ 'Sanitizer', 'decodeCharReferencesCallback' ],
+                       [ self::class, 'decodeCharReferencesCallback' ],
                        $text );
        }
 
@@ -1667,7 +1660,7 @@ class Sanitizer {
                global $wgContLang;
                $text = preg_replace_callback(
                        self::CHAR_REFS_REGEX,
-                       [ 'Sanitizer', 'decodeCharReferencesCallback' ],
+                       [ self::class, 'decodeCharReferencesCallback' ],
                        $text,
                        -1, //limit
                        $count
index 3383fe3..bc37b48 100644 (file)
@@ -41,7 +41,7 @@ final class PasswordFactory {
         * @see Setup.php
         */
        private $types = [
-               '' => [ 'type' => '', 'class' => 'InvalidPassword' ],
+               '' => [ 'type' => '', 'class' => InvalidPassword::class ],
        ];
 
        /**
index a23d644..c1e9a59 100644 (file)
@@ -205,7 +205,7 @@ class DefaultPreferencesFactory implements PreferencesFactory {
 
                // Handling for multiselect preferences
                if ( ( isset( $info['type'] ) && $info['type'] == 'multiselect' ) ||
-                               ( isset( $info['class'] ) && $info['class'] == 'HTMLMultiSelectField' ) ) {
+                               ( isset( $info['class'] ) && $info['class'] == \HTMLMultiSelectField::class ) ) {
                        $options = HTMLFormField::flattenOptions( $info['options'] );
                        $prefix = isset( $info['prefix'] ) ? $info['prefix'] : $name;
                        $val = [];
@@ -219,7 +219,7 @@ class DefaultPreferencesFactory implements PreferencesFactory {
 
                // Handling for checkmatrix preferences
                if ( ( isset( $info['type'] ) && $info['type'] == 'checkmatrix' ) ||
-                               ( isset( $info['class'] ) && $info['class'] == 'HTMLCheckMatrix' ) ) {
+                               ( isset( $info['class'] ) && $info['class'] == \HTMLCheckMatrix::class ) ) {
                        $columns = HTMLFormField::flattenOptions( $info['columns'] );
                        $rows = HTMLFormField::flattenOptions( $info['rows'] );
                        $prefix = isset( $info['prefix'] ) ? $info['prefix'] : $name;
@@ -799,7 +799,7 @@ class DefaultPreferencesFactory implements PreferencesFactory {
                }
 
                $defaultPreferences['timecorrection'] = [
-                       'class' => 'HTMLSelectOrOtherField',
+                       'class' => \HTMLSelectOrOtherField::class,
                        'label-message' => 'timezonelegend',
                        'options' => $tzOptions,
                        'default' => $tzSetting,
@@ -1401,7 +1401,7 @@ class DefaultPreferencesFactory implements PreferencesFactory {
        public function getForm(
                User $user,
                IContextSource $context,
-               $formClass = 'PreferencesForm',
+               $formClass = PreferencesForm::class,
                array $remove = []
        ) {
                $formDescriptor = $this->getFormDescriptor( $user, $context );
index 35237be..685f78c 100644 (file)
@@ -62,7 +62,7 @@ interface PreferencesFactory {
        public function getForm(
                User $user,
                IContextSource $contextSource,
-               $formClass = 'PreferencesForm',
+               $formClass = \PreferencesForm::class,
                array $remove = []
        );
 
index 81449be..9021652 100644 (file)
@@ -64,7 +64,7 @@ abstract class Profiler {
                        global $wgProfiler, $wgProfileLimit;
 
                        $params = [
-                               'class'     => 'ProfilerStub',
+                               'class'     => ProfilerStub::class,
                                'sampling'  => 1,
                                'threshold' => $wgProfileLimit,
                                'output'    => [],
@@ -76,7 +76,7 @@ abstract class Profiler {
                        $inSample = mt_rand( 0, $params['sampling'] - 1 ) === 0;
                        // wfIsCLI() is not available yet
                        if ( PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg' || !$inSample ) {
-                               $params['class'] = 'ProfilerStub';
+                               $params['class'] = ProfilerStub::class;
                        }
 
                        if ( !is_array( $params['output'] ) ) {
index 41260a8..a7bc137 100644 (file)
@@ -22,7 +22,7 @@
  * Profiler that only tracks explicit profiling sections
  *
  * @code
- * $wgProfiler['class'] = 'ProfilerSectionOnly';
+ * $wgProfiler['class'] = ProfilerSectionOnly::class;
  * $wgProfiler['output'] = 'text';
  * $wgProfiler['visible'] = true;
  * @endcode
index 09191ee..ffa441e 100644 (file)
  * Profiler wrapper for XHProf extension.
  *
  * @code
- * $wgProfiler['class'] = 'ProfilerXhprof';
+ * $wgProfiler['class'] = ProfilerXhprof::class;
  * $wgProfiler['flags'] = XHPROF_FLAGS_NO_BUILTINS;
  * $wgProfiler['output'] = 'text';
  * $wgProfiler['visible'] = true;
  * @endcode
  *
  * @code
- * $wgProfiler['class'] = 'ProfilerXhprof';
+ * $wgProfiler['class'] = ProfilerXhprof::class;
  * $wgProfiler['flags'] = XHPROF_FLAGS_CPU | XHPROF_FLAGS_MEMORY | XHPROF_FLAGS_NO_BUILTINS;
  * $wgProfiler['output'] = 'udp';
  * @endcode
index 36bf656..830dbb4 100644 (file)
@@ -553,7 +553,7 @@ class ResourceLoader implements LoggerAwareInterface {
                                $object->setLogger( $this->logger );
                        } else {
                                if ( !isset( $info['class'] ) ) {
-                                       $class = 'ResourceLoaderFileModule';
+                                       $class = ResourceLoaderFileModule::class;
                                } else {
                                        $class = $info['class'];
                                }
@@ -586,8 +586,8 @@ class ResourceLoader implements LoggerAwareInterface {
                }
                if (
                        isset( $info['class'] ) &&
-                       $info['class'] !== 'ResourceLoaderFileModule' &&
-                       !is_subclass_of( $info['class'], 'ResourceLoaderFileModule' )
+                       $info['class'] !== ResourceLoaderFileModule::class &&
+                       !is_subclass_of( $info['class'], ResourceLoaderFileModule::class )
                ) {
                        return false;
                }
@@ -1227,7 +1227,11 @@ MESSAGE;
                $name, $scripts, $styles, $messages, $templates
        ) {
                if ( $scripts instanceof XmlJsCode ) {
-                       $scripts = new XmlJsCode( "function ( $, jQuery, require, module ) {\n{$scripts->value}\n}" );
+                       if ( self::inDebugMode() ) {
+                               $scripts = new XmlJsCode( "function ( $, jQuery, require, module ) {\n{$scripts->value}\n}" );
+                       } else {
+                               $scripts = new XmlJsCode( 'function($,jQuery,require,module){'. $scripts->value . '}' );
+                       }
                } elseif ( !is_string( $scripts ) && !is_array( $scripts ) ) {
                        throw new MWException( 'Invalid scripts error. Array of URLs or string of code expected.' );
                }
index 458c44d..ffa55c0 100644 (file)
@@ -51,10 +51,12 @@ class ResourceLoaderUserOptionsModule extends ResourceLoaderModule {
         * @return string JavaScript code
         */
        public function getScript( ResourceLoaderContext $context ) {
-               return Xml::encodeJsCall( 'mw.user.options.set',
+               // Use FILTER_NOMIN annotation to prevent needless minification and caching (T84960).
+               return ResourceLoader::FILTER_NOMIN . Xml::encodeJsCall(
+                       'mw.user.options.set',
                        [ $context->getUserObj()->getOptions( User::GETOPTIONS_EXCLUDE_DEFAULTS ) ],
                        ResourceLoader::inDebugMode()
-               ) . ResourceLoader::FILTER_NOMIN;
+               );
        }
 
        /**
index e933f1f..ae4fb67 100644 (file)
@@ -48,19 +48,16 @@ class ResourceLoaderUserTokensModule extends ResourceLoaderModule {
        }
 
        /**
-        * Generate the JavaScript content of this module.
-        *
-        * Add FILTER_NOMIN annotation to prevent needless minification and caching (T84960).
-        *
         * @param ResourceLoaderContext $context
         * @return string JavaScript code
         */
        public function getScript( ResourceLoaderContext $context ) {
-               return Xml::encodeJsCall(
+               // Use FILTER_NOMIN annotation to prevent needless minification and caching (T84960).
+               return ResourceLoader::FILTER_NOMIN . Xml::encodeJsCall(
                        'mw.user.tokens.set',
                        [ $this->contextUserTokens( $context ) ],
                        ResourceLoader::inDebugMode()
-               ) . ResourceLoader::FILTER_NOMIN;
+               );
        }
 
        /**
index 76fa590..7b2147a 100644 (file)
 class RevisionDeleter {
        /** List of known revdel types, with their corresponding list classes */
        private static $allowedTypes = [
-               'revision' => 'RevDelRevisionList',
-               'archive' => 'RevDelArchiveList',
-               'oldimage' => 'RevDelFileList',
-               'filearchive' => 'RevDelArchivedFileList',
-               'logging' => 'RevDelLogList',
+               'revision' => RevDelRevisionList::class,
+               'archive' => RevDelArchiveList::class,
+               'oldimage' => RevDelFileList::class,
+               'filearchive' => RevDelArchivedFileList::class,
+               'logging' => RevDelLogList::class,
        ];
 
        /** Type map to support old log entries */
index 613d33c..8cdca57 100644 (file)
@@ -49,17 +49,17 @@ class SearchEngineFactory {
        public static function getSearchEngineClass( IDatabase $db ) {
                switch ( $db->getType() ) {
                        case 'sqlite':
-                               return 'SearchSqlite';
+                               return SearchSqlite::class;
                        case 'mysql':
-                               return 'SearchMySQL';
+                               return SearchMySQL::class;
                        case 'postgres':
-                               return 'SearchPostgres';
+                               return SearchPostgres::class;
                        case 'mssql':
-                               return 'SearchMssql';
+                               return SearchMssql::class;
                        case 'oracle':
-                               return 'SearchOracle';
+                               return SearchOracle::class;
                        default:
-                               return 'SearchEngineDummy';
+                               return SearchEngineDummy::class;
                }
        }
 }
index 4f65e4d..d6f9578 100644 (file)
@@ -404,11 +404,20 @@ class Command {
                $eintr = defined( 'SOCKET_EINTR' ) ? SOCKET_EINTR : 4;
                $eintrMessage = "stream_select(): unable to select [$eintr]";
 
+               /* The select(2) system call only guarantees a "sufficiently small write"
+                * can be made without blocking. And on Linux the read might block too
+                * in certain cases, although I don't know if any of them can occur here.
+                * Regardless, set all the pipes to non-blocking to avoid T184171.
+                */
+               foreach ( $pipes as $pipe ) {
+                       stream_set_blocking( $pipe, false );
+               }
+
                $running = true;
                $timeout = null;
                $numReadyPipes = 0;
 
-               while ( $running === true || $numReadyPipes !== 0 ) {
+               while ( $pipes && ( $running === true || $numReadyPipes !== 0 ) ) {
                        if ( $running ) {
                                $status = proc_get_status( $proc );
                                // If the process has terminated, switch to nonblocking selects
@@ -465,14 +474,17 @@ class Command {
                                }
 
                                if ( $res === '' || $res === 0 ) {
-                                       // End of file
-                                       fclose( $pipes[$fd] );
-                                       unset( $pipes[$fd] );
-                                       if ( !$pipes ) {
-                                               break 2;
+                                       // End of file?
+                                       if ( feof( $pipe ) ) {
+                                               fclose( $pipes[$fd] );
+                                               unset( $pipes[$fd] );
                                        }
                                } elseif ( $isWrite ) {
-                                       $buffers[$fd] = substr( $buffers[$fd], $res );
+                                       $buffers[$fd] = (string)substr( $buffers[$fd], $res );
+                                       if ( $buffers[$fd] === '' ) {
+                                               fclose( $pipes[$fd] );
+                                               unset( $pipes[$fd] );
+                                       }
                                } else {
                                        $buffers[$fd] .= $res;
                                        if ( $fd === 3 && strpos( $res, "\n" ) !== false ) {
index a94aa0b..b942d6e 100644 (file)
@@ -63,7 +63,7 @@ class SiteList extends GenericArrayObject {
         * @return string
         */
        public function getObjectType() {
-               return 'Site';
+               return Site::class;
        }
 
        /**
index d1be4bb..19b41ba 100644 (file)
@@ -91,10 +91,14 @@ abstract class QuickTemplate {
        }
 
        /**
+        * @deprecated since 1.31 This function is a now-redundant optimisation intended
+        *  for very old versions of PHP. The use of references here makes the code
+        *  more fragile and is incompatible with plans like T140664. Use set() instead.
         * @param string $name
         * @param mixed &$value
         */
        public function setRef( $name, &$value ) {
+               wfDeprecated( __METHOD__, '1.31' );
                $this->data[$name] =& $value;
        }
 
index a14afe0..6966ff7 100644 (file)
@@ -30,7 +30,7 @@
  */
 class SkinApi extends SkinTemplate {
        public $skinname = 'apioutput';
-       public $template = 'SkinApiTemplate';
+       public $template = SkinApiTemplate::class;
 
        public function setupSkinUserCss( OutputPage $out ) {
                parent::setupSkinUserCss( $out );
index 96ff228..d5f764c 100644 (file)
@@ -14,7 +14,7 @@
 class SkinFallback extends SkinTemplate {
 
        public $skinname = 'fallback';
-       public $template = 'SkinFallbackTemplate';
+       public $template = SkinFallbackTemplate::class;
 
        /**
         * Add CSS via ResourceLoader
index 8b698e8..4587533 100644 (file)
@@ -46,7 +46,7 @@ class SkinTemplate extends Skin {
         * @var string For QuickTemplate, the name of the subclass which will
         *   actually fill the template.  Child classes should override the default.
         */
-       public $template = 'QuickTemplate';
+       public $template = QuickTemplate::class;
 
        public $thispage;
        public $titletxt;
index 95729f3..b9745de 100644 (file)
@@ -609,7 +609,7 @@ abstract class AuthManagerSpecialPage extends SpecialPage {
                        } elseif ( array_key_exists( 'type', $definition ) ) {
                                $class = HTMLForm::$typeMappings[$definition['type']];
                        }
-                       if ( $class !== 'HTMLInfoField' ) {
+                       if ( $class !== HTMLInfoField::class ) {
                                $definition['tabindex'] = $i;
                                $i++;
                        }
index 33b9ccb..b20f222 100644 (file)
@@ -69,40 +69,40 @@ abstract class QueryPage extends SpecialPage {
                if ( $qp === null ) {
                        // QueryPage subclass, Special page name
                        $qp = [
-                               [ 'AncientPagesPage', 'Ancientpages' ],
-                               [ 'BrokenRedirectsPage', 'BrokenRedirects' ],
-                               [ 'DeadendPagesPage', 'Deadendpages' ],
-                               [ 'DoubleRedirectsPage', 'DoubleRedirects' ],
-                               [ 'FileDuplicateSearchPage', 'FileDuplicateSearch' ],
-                               [ 'ListDuplicatedFilesPage', 'ListDuplicatedFiles' ],
-                               [ 'LinkSearchPage', 'LinkSearch' ],
-                               [ 'ListredirectsPage', 'Listredirects' ],
-                               [ 'LonelyPagesPage', 'Lonelypages' ],
-                               [ 'LongPagesPage', 'Longpages' ],
-                               [ 'MediaStatisticsPage', 'MediaStatistics' ],
-                               [ 'MIMEsearchPage', 'MIMEsearch' ],
-                               [ 'MostcategoriesPage', 'Mostcategories' ],
-                               [ 'MostimagesPage', 'Mostimages' ],
-                               [ 'MostinterwikisPage', 'Mostinterwikis' ],
-                               [ 'MostlinkedCategoriesPage', 'Mostlinkedcategories' ],
-                               [ 'MostlinkedTemplatesPage', 'Mostlinkedtemplates' ],
-                               [ 'MostlinkedPage', 'Mostlinked' ],
-                               [ 'MostrevisionsPage', 'Mostrevisions' ],
-                               [ 'FewestrevisionsPage', 'Fewestrevisions' ],
-                               [ 'ShortPagesPage', 'Shortpages' ],
-                               [ 'UncategorizedCategoriesPage', 'Uncategorizedcategories' ],
-                               [ 'UncategorizedPagesPage', 'Uncategorizedpages' ],
-                               [ 'UncategorizedImagesPage', 'Uncategorizedimages' ],
-                               [ 'UncategorizedTemplatesPage', 'Uncategorizedtemplates' ],
-                               [ 'UnusedCategoriesPage', 'Unusedcategories' ],
-                               [ 'UnusedimagesPage', 'Unusedimages' ],
-                               [ 'WantedCategoriesPage', 'Wantedcategories' ],
-                               [ 'WantedFilesPage', 'Wantedfiles' ],
-                               [ 'WantedPagesPage', 'Wantedpages' ],
-                               [ 'WantedTemplatesPage', 'Wantedtemplates' ],
-                               [ 'UnwatchedpagesPage', 'Unwatchedpages' ],
-                               [ 'UnusedtemplatesPage', 'Unusedtemplates' ],
-                               [ 'WithoutInterwikiPage', 'Withoutinterwiki' ],
+                               [ AncientPagesPage::class, 'Ancientpages' ],
+                               [ BrokenRedirectsPage::class, 'BrokenRedirects' ],
+                               [ DeadendPagesPage::class, 'Deadendpages' ],
+                               [ DoubleRedirectsPage::class, 'DoubleRedirects' ],
+                               [ FileDuplicateSearchPage::class, 'FileDuplicateSearch' ],
+                               [ ListDuplicatedFilesPage::class, 'ListDuplicatedFiles' ],
+                               [ LinkSearchPage::class, 'LinkSearch' ],
+                               [ ListredirectsPage::class, 'Listredirects' ],
+                               [ LonelyPagesPage::class, 'Lonelypages' ],
+                               [ LongPagesPage::class, 'Longpages' ],
+                               [ MediaStatisticsPage::class, 'MediaStatistics' ],
+                               [ MIMEsearchPage::class, 'MIMEsearch' ],
+                               [ MostcategoriesPage::class, 'Mostcategories' ],
+                               [ MostimagesPage::class, 'Mostimages' ],
+                               [ MostinterwikisPage::class, 'Mostinterwikis' ],
+                               [ MostlinkedCategoriesPage::class, 'Mostlinkedcategories' ],
+                               [ MostlinkedTemplatesPage::class, 'Mostlinkedtemplates' ],
+                               [ MostlinkedPage::class, 'Mostlinked' ],
+                               [ MostrevisionsPage::class, 'Mostrevisions' ],
+                               [ FewestrevisionsPage::class, 'Fewestrevisions' ],
+                               [ ShortPagesPage::class, 'Shortpages' ],
+                               [ UncategorizedCategoriesPage::class, 'Uncategorizedcategories' ],
+                               [ UncategorizedPagesPage::class, 'Uncategorizedpages' ],
+                               [ UncategorizedImagesPage::class, 'Uncategorizedimages' ],
+                               [ UncategorizedTemplatesPage::class, 'Uncategorizedtemplates' ],
+                               [ UnusedCategoriesPage::class, 'Unusedcategories' ],
+                               [ UnusedimagesPage::class, 'Unusedimages' ],
+                               [ WantedCategoriesPage::class, 'Wantedcategories' ],
+                               [ WantedFilesPage::class, 'Wantedfiles' ],
+                               [ WantedPagesPage::class, 'Wantedpages' ],
+                               [ WantedTemplatesPage::class, 'Wantedtemplates' ],
+                               [ UnwatchedpagesPage::class, 'Unwatchedpages' ],
+                               [ UnusedtemplatesPage::class, 'Unusedtemplates' ],
+                               [ WithoutInterwikiPage::class, 'Withoutinterwiki' ],
                        ];
                        Hooks::run( 'wgQueryPages', [ &$qp ] );
                }
index 4433ddb..af688e2 100644 (file)
@@ -50,143 +50,143 @@ class SpecialPageFactory {
         */
        private static $coreList = [
                // Maintenance Reports
-               'BrokenRedirects' => 'BrokenRedirectsPage',
-               'Deadendpages' => 'DeadendPagesPage',
-               'DoubleRedirects' => 'DoubleRedirectsPage',
-               'Longpages' => 'LongPagesPage',
-               'Ancientpages' => 'AncientPagesPage',
-               'Lonelypages' => 'LonelyPagesPage',
-               'Fewestrevisions' => 'FewestrevisionsPage',
-               'Withoutinterwiki' => 'WithoutInterwikiPage',
-               'Protectedpages' => 'SpecialProtectedpages',
-               'Protectedtitles' => 'SpecialProtectedtitles',
-               'Shortpages' => 'ShortPagesPage',
-               'Uncategorizedcategories' => 'UncategorizedCategoriesPage',
-               'Uncategorizedimages' => 'UncategorizedImagesPage',
-               'Uncategorizedpages' => 'UncategorizedPagesPage',
-               'Uncategorizedtemplates' => 'UncategorizedTemplatesPage',
-               'Unusedcategories' => 'UnusedCategoriesPage',
-               'Unusedimages' => 'UnusedimagesPage',
-               'Unusedtemplates' => 'UnusedtemplatesPage',
-               'Unwatchedpages' => 'UnwatchedpagesPage',
-               'Wantedcategories' => 'WantedCategoriesPage',
-               'Wantedfiles' => 'WantedFilesPage',
-               'Wantedpages' => 'WantedPagesPage',
-               'Wantedtemplates' => 'WantedTemplatesPage',
+               'BrokenRedirects' => BrokenRedirectsPage::class,
+               'Deadendpages' => DeadendPagesPage::class,
+               'DoubleRedirects' => DoubleRedirectsPage::class,
+               'Longpages' => LongPagesPage::class,
+               'Ancientpages' => AncientPagesPage::class,
+               'Lonelypages' => LonelyPagesPage::class,
+               'Fewestrevisions' => FewestrevisionsPage::class,
+               'Withoutinterwiki' => WithoutInterwikiPage::class,
+               'Protectedpages' => SpecialProtectedpages::class,
+               'Protectedtitles' => SpecialProtectedtitles::class,
+               'Shortpages' => ShortPagesPage::class,
+               'Uncategorizedcategories' => UncategorizedCategoriesPage::class,
+               'Uncategorizedimages' => UncategorizedImagesPage::class,
+               'Uncategorizedpages' => UncategorizedPagesPage::class,
+               'Uncategorizedtemplates' => UncategorizedTemplatesPage::class,
+               'Unusedcategories' => UnusedCategoriesPage::class,
+               'Unusedimages' => UnusedimagesPage::class,
+               'Unusedtemplates' => UnusedtemplatesPage::class,
+               'Unwatchedpages' => UnwatchedpagesPage::class,
+               'Wantedcategories' => WantedCategoriesPage::class,
+               'Wantedfiles' => WantedFilesPage::class,
+               'Wantedpages' => WantedPagesPage::class,
+               'Wantedtemplates' => WantedTemplatesPage::class,
 
                // List of pages
-               'Allpages' => 'SpecialAllPages',
-               'Prefixindex' => 'SpecialPrefixindex',
-               'Categories' => 'SpecialCategories',
-               'Listredirects' => 'ListredirectsPage',
-               'PagesWithProp' => 'SpecialPagesWithProp',
-               'TrackingCategories' => 'SpecialTrackingCategories',
+               'Allpages' => SpecialAllPages::class,
+               'Prefixindex' => SpecialPrefixindex::class,
+               'Categories' => SpecialCategories::class,
+               'Listredirects' => ListredirectsPage::class,
+               'PagesWithProp' => SpecialPagesWithProp::class,
+               'TrackingCategories' => SpecialTrackingCategories::class,
 
                // Authentication
-               'Userlogin' => 'SpecialUserLogin',
-               'Userlogout' => 'SpecialUserLogout',
-               'CreateAccount' => 'SpecialCreateAccount',
-               'LinkAccounts' => 'SpecialLinkAccounts',
-               'UnlinkAccounts' => 'SpecialUnlinkAccounts',
-               'ChangeCredentials' => 'SpecialChangeCredentials',
-               'RemoveCredentials' => 'SpecialRemoveCredentials',
+               'Userlogin' => SpecialUserLogin::class,
+               'Userlogout' => SpecialUserLogout::class,
+               'CreateAccount' => SpecialCreateAccount::class,
+               'LinkAccounts' => SpecialLinkAccounts::class,
+               'UnlinkAccounts' => SpecialUnlinkAccounts::class,
+               'ChangeCredentials' => SpecialChangeCredentials::class,
+               'RemoveCredentials' => SpecialRemoveCredentials::class,
 
                // Users and rights
-               'Activeusers' => 'SpecialActiveUsers',
-               'Block' => 'SpecialBlock',
-               'Unblock' => 'SpecialUnblock',
-               'BlockList' => 'SpecialBlockList',
-               'AutoblockList' => 'SpecialAutoblockList',
-               'ChangePassword' => 'SpecialChangePassword',
-               'BotPasswords' => 'SpecialBotPasswords',
-               'PasswordReset' => 'SpecialPasswordReset',
-               'DeletedContributions' => 'DeletedContributionsPage',
-               'Preferences' => 'SpecialPreferences',
-               'ResetTokens' => 'SpecialResetTokens',
-               'Contributions' => 'SpecialContributions',
-               'Listgrouprights' => 'SpecialListGroupRights',
-               'Listgrants' => 'SpecialListGrants',
-               'Listusers' => 'SpecialListUsers',
-               'Listadmins' => 'SpecialListAdmins',
-               'Listbots' => 'SpecialListBots',
-               'Userrights' => 'UserrightsPage',
-               'EditWatchlist' => 'SpecialEditWatchlist',
+               'Activeusers' => SpecialActiveUsers::class,
+               'Block' => SpecialBlock::class,
+               'Unblock' => SpecialUnblock::class,
+               'BlockList' => SpecialBlockList::class,
+               'AutoblockList' => SpecialAutoblockList::class,
+               'ChangePassword' => SpecialChangePassword::class,
+               'BotPasswords' => SpecialBotPasswords::class,
+               'PasswordReset' => SpecialPasswordReset::class,
+               'DeletedContributions' => DeletedContributionsPage::class,
+               'Preferences' => SpecialPreferences::class,
+               'ResetTokens' => SpecialResetTokens::class,
+               'Contributions' => SpecialContributions::class,
+               'Listgrouprights' => SpecialListGroupRights::class,
+               'Listgrants' => SpecialListGrants::class,
+               'Listusers' => SpecialListUsers::class,
+               'Listadmins' => SpecialListAdmins::class,
+               'Listbots' => SpecialListBots::class,
+               'Userrights' => UserrightsPage::class,
+               'EditWatchlist' => SpecialEditWatchlist::class,
 
                // Recent changes and logs
-               'Newimages' => 'SpecialNewFiles',
-               'Log' => 'SpecialLog',
-               'Watchlist' => 'SpecialWatchlist',
-               'Newpages' => 'SpecialNewpages',
-               'Recentchanges' => 'SpecialRecentChanges',
-               'Recentchangeslinked' => 'SpecialRecentChangesLinked',
-               'Tags' => 'SpecialTags',
+               'Newimages' => SpecialNewFiles::class,
+               'Log' => SpecialLog::class,
+               'Watchlist' => SpecialWatchlist::class,
+               'Newpages' => SpecialNewpages::class,
+               'Recentchanges' => SpecialRecentChanges::class,
+               'Recentchangeslinked' => SpecialRecentChangesLinked::class,
+               'Tags' => SpecialTags::class,
 
                // Media reports and uploads
-               'Listfiles' => 'SpecialListFiles',
-               'Filepath' => 'SpecialFilepath',
-               'MediaStatistics' => 'MediaStatisticsPage',
-               'MIMEsearch' => 'MIMEsearchPage',
-               'FileDuplicateSearch' => 'FileDuplicateSearchPage',
-               'Upload' => 'SpecialUpload',
-               'UploadStash' => 'SpecialUploadStash',
-               'ListDuplicatedFiles' => 'ListDuplicatedFilesPage',
+               'Listfiles' => SpecialListFiles::class,
+               'Filepath' => SpecialFilepath::class,
+               'MediaStatistics' => MediaStatisticsPage::class,
+               'MIMEsearch' => MIMEsearchPage::class,
+               'FileDuplicateSearch' => FileDuplicateSearchPage::class,
+               'Upload' => SpecialUpload::class,
+               'UploadStash' => SpecialUploadStash::class,
+               'ListDuplicatedFiles' => ListDuplicatedFilesPage::class,
 
                // Data and tools
-               'ApiSandbox' => 'SpecialApiSandbox',
-               'Statistics' => 'SpecialStatistics',
-               'Allmessages' => 'SpecialAllMessages',
-               'Version' => 'SpecialVersion',
-               'Lockdb' => 'SpecialLockdb',
-               'Unlockdb' => 'SpecialUnlockdb',
+               'ApiSandbox' => SpecialApiSandbox::class,
+               'Statistics' => SpecialStatistics::class,
+               'Allmessages' => SpecialAllMessages::class,
+               'Version' => SpecialVersion::class,
+               'Lockdb' => SpecialLockdb::class,
+               'Unlockdb' => SpecialUnlockdb::class,
 
                // Redirecting special pages
-               'LinkSearch' => 'LinkSearchPage',
-               'Randompage' => 'RandomPage',
-               'RandomInCategory' => 'SpecialRandomInCategory',
-               'Randomredirect' => 'SpecialRandomredirect',
-               'Randomrootpage' => 'SpecialRandomrootpage',
-               'GoToInterwiki' => 'SpecialGoToInterwiki',
+               'LinkSearch' => LinkSearchPage::class,
+               'Randompage' => RandomPage::class,
+               'RandomInCategory' => SpecialRandomInCategory::class,
+               'Randomredirect' => SpecialRandomredirect::class,
+               'Randomrootpage' => SpecialRandomrootpage::class,
+               'GoToInterwiki' => SpecialGoToInterwiki::class,
 
                // High use pages
-               'Mostlinkedcategories' => 'MostlinkedCategoriesPage',
-               'Mostimages' => 'MostimagesPage',
-               'Mostinterwikis' => 'MostinterwikisPage',
-               'Mostlinked' => 'MostlinkedPage',
-               'Mostlinkedtemplates' => 'MostlinkedTemplatesPage',
-               'Mostcategories' => 'MostcategoriesPage',
-               'Mostrevisions' => 'MostrevisionsPage',
+               'Mostlinkedcategories' => MostlinkedCategoriesPage::class,
+               'Mostimages' => MostimagesPage::class,
+               'Mostinterwikis' => MostinterwikisPage::class,
+               'Mostlinked' => MostlinkedPage::class,
+               'Mostlinkedtemplates' => MostlinkedTemplatesPage::class,
+               'Mostcategories' => MostcategoriesPage::class,
+               'Mostrevisions' => MostrevisionsPage::class,
 
                // Page tools
-               'ComparePages' => 'SpecialComparePages',
-               'Export' => 'SpecialExport',
-               'Import' => 'SpecialImport',
-               'Undelete' => 'SpecialUndelete',
-               'Whatlinkshere' => 'SpecialWhatLinksHere',
-               'MergeHistory' => 'SpecialMergeHistory',
-               'ExpandTemplates' => 'SpecialExpandTemplates',
+               'ComparePages' => SpecialComparePages::class,
+               'Export' => SpecialExport::class,
+               'Import' => SpecialImport::class,
+               'Undelete' => SpecialUndelete::class,
+               'Whatlinkshere' => SpecialWhatLinksHere::class,
+               'MergeHistory' => SpecialMergeHistory::class,
+               'ExpandTemplates' => SpecialExpandTemplates::class,
 
                // Other
-               'Booksources' => 'SpecialBookSources',
+               'Booksources' => SpecialBookSources::class,
 
                // Unlisted / redirects
-               'ApiHelp' => 'SpecialApiHelp',
-               'Blankpage' => 'SpecialBlankpage',
-               'Diff' => 'SpecialDiff',
-               'EditTags' => 'SpecialEditTags',
-               'Emailuser' => 'SpecialEmailUser',
-               'Movepage' => 'MovePageForm',
-               'Mycontributions' => 'SpecialMycontributions',
-               'MyLanguage' => 'SpecialMyLanguage',
-               'Mypage' => 'SpecialMypage',
-               'Mytalk' => 'SpecialMytalk',
-               'Myuploads' => 'SpecialMyuploads',
-               'AllMyUploads' => 'SpecialAllMyUploads',
-               'PermanentLink' => 'SpecialPermanentLink',
-               'Redirect' => 'SpecialRedirect',
-               'Revisiondelete' => 'SpecialRevisionDelete',
-               'RunJobs' => 'SpecialRunJobs',
-               'Specialpages' => 'SpecialSpecialpages',
-               'PageData' => 'SpecialPageData'
+               'ApiHelp' => SpecialApiHelp::class,
+               'Blankpage' => SpecialBlankpage::class,
+               'Diff' => SpecialDiff::class,
+               'EditTags' => SpecialEditTags::class,
+               'Emailuser' => SpecialEmailUser::class,
+               'Movepage' => MovePageForm::class,
+               'Mycontributions' => SpecialMycontributions::class,
+               'MyLanguage' => SpecialMyLanguage::class,
+               'Mypage' => SpecialMypage::class,
+               'Mytalk' => SpecialMytalk::class,
+               'Myuploads' => SpecialMyuploads::class,
+               'AllMyUploads' => SpecialAllMyUploads::class,
+               'PermanentLink' => SpecialPermanentLink::class,
+               'Redirect' => SpecialRedirect::class,
+               'Revisiondelete' => SpecialRevisionDelete::class,
+               'RunJobs' => SpecialRunJobs::class,
+               'Specialpages' => SpecialSpecialpages::class,
+               'PageData' => SpecialPageData::class,
        ];
 
        private static $list;
@@ -237,27 +237,27 @@ class SpecialPageFactory {
                        self::$list = self::$coreList;
 
                        if ( !$wgDisableInternalSearch ) {
-                               self::$list['Search'] = 'SpecialSearch';
+                               self::$list['Search'] = SpecialSearch::class;
                        }
 
                        if ( $wgEmailAuthentication ) {
-                               self::$list['Confirmemail'] = 'EmailConfirmation';
-                               self::$list['Invalidateemail'] = 'EmailInvalidation';
+                               self::$list['Confirmemail'] = EmailConfirmation::class;
+                               self::$list['Invalidateemail'] = EmailInvalidation::class;
                        }
 
                        if ( $wgEnableEmail ) {
-                               self::$list['ChangeEmail'] = 'SpecialChangeEmail';
+                               self::$list['ChangeEmail'] = SpecialChangeEmail::class;
                        }
 
                        if ( $wgEnableJavaScriptTest ) {
-                               self::$list['JavaScriptTest'] = 'SpecialJavaScriptTest';
+                               self::$list['JavaScriptTest'] = SpecialJavaScriptTest::class;
                        }
 
                        if ( $wgPageLanguageUseDB ) {
-                               self::$list['PageLanguage'] = 'SpecialPageLanguage';
+                               self::$list['PageLanguage'] = SpecialPageLanguage::class;
                        }
                        if ( $wgContentHandlerUseDB ) {
-                               self::$list['ChangeContentModel'] = 'SpecialChangeContentModel';
+                               self::$list['ChangeContentModel'] = SpecialChangeContentModel::class;
                        }
 
                        // Add extension special pages
index dfdbb06..f76c318 100644 (file)
@@ -146,7 +146,7 @@ class SpecialBotPasswords extends FormSpecialPage {
                        ];
 
                        $fields['restrictions'] = [
-                               'class' => 'HTMLRestrictionsField',
+                               'class' => HTMLRestrictionsField::class,
                                'required' => true,
                                'default' => $this->botPassword->getRestrictions(),
                        ];
index 28cd0d1..b27a8b4 100644 (file)
@@ -78,7 +78,7 @@ class SpecialDiff extends RedirectSpecialPage {
                        ],
                        'diff' => [
                                'name' => 'diff',
-                               'class' => 'HTMLTextField',
+                               'class' => HTMLTextField::class,
                                'label-message' => 'diff-form-revid',
                        ],
                ], $this->getContext(), 'diff-form' );
index 09ea9ea..e3c5d8c 100644 (file)
@@ -573,7 +573,7 @@ class SpecialEditWatchlist extends UnlistedSpecialPage {
                        // checkTitle can filter some options out, avoid empty sections
                        if ( count( $options ) > 0 ) {
                                $fields['TitlesNs' . $namespace] = [
-                                       'class' => 'EditWatchlistCheckboxSeriesField',
+                                       'class' => EditWatchlistCheckboxSeriesField::class,
                                        'options' => $options,
                                        'section' => "ns$namespace",
                                ];
index 0c18a48..73ca76b 100644 (file)
@@ -164,7 +164,6 @@ class SpecialExpandTemplates extends SpecialPage {
                                'size' => 60,
                                'default' => $title,
                                'autofocus' => true,
-                               'cssclass' => 'mw-ui-input-inline',
                        ],
                        'input' => [
                                'type' => 'textarea',
index 78f9253..4b5dedf 100644 (file)
@@ -238,7 +238,7 @@ class SpecialExport extends SpecialPage {
 
                $formDescriptor += [
                        'textarea' => [
-                               'class' => 'HTMLTextAreaField',
+                               'class' => HTMLTextAreaField::class,
                                'name' => 'pages',
                                'label-message' => 'export-manual',
                                'nodata' => true,
index ac34996..d693b99 100644 (file)
@@ -97,7 +97,7 @@ class SpecialProtectedpages extends SpecialPage {
        ) {
                $formDescriptor = [
                        'namespace' => [
-                               'class' => 'HTMLSelectNamespace',
+                               'class' => HTMLSelectNamespace::class,
                                'name' => 'namespace',
                                'id' => 'namespace',
                                'cssclass' => 'namespaceselector',
@@ -125,7 +125,7 @@ class SpecialProtectedpages extends SpecialPage {
                                'id' => 'noredirect',
                        ],
                        'sizelimit' => [
-                               'class' => 'HTMLSizeFilterField',
+                               'class' => HTMLSizeFilterField::class,
                                'name' => 'size',
                        ]
                ];
index 605ee00..6b0598c 100644 (file)
@@ -441,7 +441,7 @@ class SpecialTags extends SpecialPage {
                $out = $context->getOutput();
 
                $tag = $data['HiddenTag'];
-               $status = call_user_func( [ 'ChangeTags', "{$form->tagAction}TagWithChecks" ],
+               $status = call_user_func( [ ChangeTags::class, "{$form->tagAction}TagWithChecks" ],
                        $tag, $data['Reason'], $context->getUser(), true );
 
                if ( $status->isGood() ) {
index fd066ac..e62731f 100644 (file)
@@ -873,6 +873,10 @@ class UserrightsPage extends SpecialPage {
                                                } else {
                                                        $expiryHtml = $this->msg( 'userrights-expiry-none' )->text();
                                                }
+                                               // T171345: Add a hidden form element so that other groups can still be manipulated,
+                                               // otherwise saving errors out with an invalid expiry time for this group.
+                                               $expiryHtml .= Html::Hidden( "wpExpiry-$group",
+                                                       $currentExpiry ? 'existing' : 'infinite' );
                                                $expiryHtml .= "<br />\n";
                                        } else {
                                                $expiryHtml = Xml::element( 'span', null,
index 44d91a8..eacdace 100644 (file)
@@ -159,7 +159,7 @@ class UploadForm extends HTMLForm {
                }
 
                $descriptor['UploadFile'] = [
-                       'class' => 'UploadSourceField',
+                       'class' => UploadSourceField::class,
                        'section' => 'source',
                        'type' => 'file',
                        'id' => 'wpUploadFile',
@@ -174,7 +174,7 @@ class UploadForm extends HTMLForm {
                if ( $canUploadByUrl ) {
                        $this->mMaxUploadSize['url'] = UploadBase::getMaxUploadSize( 'url' );
                        $descriptor['UploadFileURL'] = [
-                               'class' => 'UploadSourceField',
+                               'class' => UploadSourceField::class,
                                'section' => 'source',
                                'id' => 'wpUploadFileURL',
                                'radio-id' => 'wpSourceTypeurl',
@@ -322,7 +322,7 @@ class UploadForm extends HTMLForm {
                } else {
                        $descriptor['License'] = [
                                'type' => 'select',
-                               'class' => 'Licenses',
+                               'class' => Licenses::class,
                                'section' => 'description',
                                'id' => 'wpLicense',
                                'label-message' => 'license',
index 4efe92d..09d4b5e 100644 (file)
@@ -277,7 +277,7 @@ class UsersPager extends AlphabeticPager {
 
                $formDescriptor = [
                        'user' => [
-                               'class' => 'HTMLUserTextField',
+                               'class' => HTMLUserTextField::class,
                                'label' => $this->msg( 'listusersfrom' )->text(),
                                'name' => 'username',
                                'default' => $this->requestedUser,
@@ -286,7 +286,7 @@ class UsersPager extends AlphabeticPager {
                                'label' => $this->msg( 'group' )->text(),
                                'name' => 'group',
                                'default' => $this->requestedGroup,
-                               'class' => 'HTMLSelectField',
+                               'class' => HTMLSelectField::class,
                                'options' => $groupOptions,
                        ],
                        'editsOnly' => [
@@ -311,7 +311,7 @@ class UsersPager extends AlphabeticPager {
                                'default' => $this->mDefaultDirection
                        ],
                        'limithiddenfield' => [
-                               'class' => 'HTMLHiddenField',
+                               'class' => HTMLHiddenField::class,
                                'name' => 'limit',
                                'default' => $this->mLimit
                        ]
@@ -322,14 +322,14 @@ class UsersPager extends AlphabeticPager {
 
                if ( $beforeSubmitButtonHookOut !== '' ) {
                        $formDescriptior[ 'beforeSubmitButtonHookOut' ] = [
-                               'class' => 'HTMLInfoField',
+                               'class' => HTMLInfoField::class,
                                'raw' => true,
                                'default' => $beforeSubmitButtonHookOut
                        ];
                }
 
                $formDescriptor[ 'submit' ] = [
-                       'class' => 'HTMLSubmitField',
+                       'class' => HTMLSubmitField::class,
                        'buttonlabel-message' => 'listusers-submit',
                ];
 
@@ -338,7 +338,7 @@ class UsersPager extends AlphabeticPager {
 
                if ( $beforeClosingFieldsetHookOut !== '' ) {
                        $formDescriptior[ 'beforeClosingFieldsetHookOut' ] = [
-                               'class' => 'HTMLInfoField',
+                               'class' => HTMLInfoField::class,
                                'raw' => true,
                                'default' => $beforeClosingFieldsetHookOut
                        ];
index 5bab876..97035c2 100644 (file)
@@ -3100,7 +3100,7 @@ class User implements IDBAccessObject, UserIdentity {
                $multiselectOptions = [];
                foreach ( $prefs as $name => $info ) {
                        if ( ( isset( $info['type'] ) && $info['type'] == 'multiselect' ) ||
-                                       ( isset( $info['class'] ) && $info['class'] == 'HTMLMultiSelectField' ) ) {
+                                       ( isset( $info['class'] ) && $info['class'] == HTMLMultiSelectField::class ) ) {
                                $opts = HTMLFormField::flattenOptions( $info['options'] );
                                $prefix = isset( $info['prefix'] ) ? $info['prefix'] : $name;
 
@@ -3114,7 +3114,7 @@ class User implements IDBAccessObject, UserIdentity {
                $checkmatrixOptions = [];
                foreach ( $prefs as $name => $info ) {
                        if ( ( isset( $info['type'] ) && $info['type'] == 'checkmatrix' ) ||
-                                       ( isset( $info['class'] ) && $info['class'] == 'HTMLCheckMatrix' ) ) {
+                                       ( isset( $info['class'] ) && $info['class'] == HTMLCheckMatrix::class ) ) {
                                $columns = HTMLFormField::flattenOptions( $info['columns'] );
                                $rows = HTMLFormField::flattenOptions( $info['rows'] );
                                $prefix = isset( $info['prefix'] ) ? $info['prefix'] : $name;
index c68d6d7..d6d9ff0 100644 (file)
@@ -74,8 +74,8 @@ class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterfac
                $this->cache = $cache;
                $this->readOnlyMode = $readOnlyMode;
                $this->stats = new NullStatsdDataFactory();
-               $this->deferredUpdatesAddCallableUpdateCallback = [ 'DeferredUpdates', 'addCallableUpdate' ];
-               $this->revisionGetTimestampFromIdCallback = [ 'Revision', 'getTimestampFromId' ];
+               $this->deferredUpdatesAddCallableUpdateCallback = [ DeferredUpdates::class, 'addCallableUpdate' ];
+               $this->revisionGetTimestampFromIdCallback = [ Revision::class, 'getTimestampFromId' ];
        }
 
        /**
index de3b6af..10c3855 100644 (file)
        "filereadonlyerror": "Nun pudo camudase'l ficheru «$1» porque l'estoyu de ficheros «$2» ta en mou de sólo llectura.\n\nL'alministrador del sistema que lu bloquió dio esti motivu: «$3».",
        "invalidtitle-knownnamespace": "Títulu inválidu col espaciu de nomes «$2» ya'l testu «$3»",
        "invalidtitle-unknownnamespace": "Títulu inválidu col númberu $1 d'espaciu de nomes desconocíu ya'l testu «$2»",
-       "exception-nologin": "Nun anició sesión",
+       "exception-nologin": "Nun aniciasti sesión",
        "exception-nologin-text": "Por favor, anicia sesión pa tener accesu a esta páxina o aición.",
        "exception-nologin-text-manual": "Por favor, $1 pa tener accesu a esta páxina o aición.",
        "virus-badscanner": "Fallu de configuración: Escáner de virus desconocíu: ''$1''",
        "nav-login-createaccount": "Entrar / crear cuenta",
        "logout": "Salir",
        "userlogout": "Salir",
-       "notloggedin": "Nun anició sesión",
+       "notloggedin": "Nun aniciasti sesión",
        "userlogin-noaccount": "¿Nun tien una cuenta?",
        "userlogin-joinproject": "Xunise a {{SITENAME}}",
        "createaccount": "Crear una cuenta",
        "postedit-confirmation-created": "Creóse la páxina.",
        "postedit-confirmation-restored": "Restauróse la páxina.",
        "postedit-confirmation-saved": "Guardóse la edición.",
+       "postedit-confirmation-published": "Publicóse la to edición.",
        "edit-already-exists": "Nun pudo crease una páxina nueva.\nEsta yá esiste.",
        "defaultmessagetext": "Testu predetermináu",
        "content-failed-to-parse": "Fallu al analizar el conteníu $2 pal modelu $1: $3",
        "rcfilters-activefilters": "Filtros activos",
        "rcfilters-advancedfilters": "Filtros avanzaos",
        "rcfilters-limit-title": "Resultancies qu'amosar",
-       "rcfilters-limit-and-date-label": "{{PLURAL:$1|cambiu|$1 cambios}}, $2",
+       "rcfilters-limit-and-date-label": "$1 {{PLURAL:$1|cambiu|$1 cambios}}, $2",
        "rcfilters-date-popup-title": "Periodu de tiempu a buscar",
        "rcfilters-days-title": "Últimos díes",
        "rcfilters-hours-title": "Últimes hores",
        "reuploaddesc": "Encaboxar la xubida y tornar al formulariu de xubíes",
        "upload-tryagain": "Unviar descripción camudada del ficheru",
        "upload-tryagain-nostash": "Unviar el ficheru vueltu a xubir y la descripción cambiada",
-       "uploadnologin": "Nun anició sesión",
+       "uploadnologin": "Nun aniciasti sesión",
        "uploadnologintext": "Tien d'$1 pa xubir ficheros.",
        "upload_directory_missing": "El direutoriu de xubida ($1) nun esiste y nun pudo crease pol sirvidor web.",
        "upload_directory_read_only": "El sirvidor web nun pue escribir nel direutoriu de xubíes ($1).",
        "uploadwarning-text": "Por favor, camuda más abaxo la descripción del ficheru y vuelve a tentalo.",
        "uploadwarning-text-nostash": "Vuelve a unviar el ficheru, cambia la descripción más abaxo y tenta otra vuelta.",
        "savefile": "Guardar ficheru",
-       "uploaddisabled": "Deshabilitaes les xubíes",
+       "uploaddisabled": "Desactivaes les xubíes.",
        "copyuploaddisabled": "Xubir por URL ta desactivao.",
        "uploaddisabledtext": "Les xubíes de ficheros tán desactivaes.",
        "php-uploaddisabledtext": "Les xubíes de ficheros tan desactivaes en PHP.\nPor favor, comprueba la configuración de file_uploads.",
        "lockmanager-fail-closelock": "Nun se pudo zarrar el ficheru de bloquéu pa \"$1\".",
        "lockmanager-fail-deletelock": "Non se pudo desaniciar el ficheru de bloquéu pa \"$1\".",
        "lockmanager-fail-acquirelock": "Nun se pudo adquirir el bloquéu pa \"$1\".",
-       "lockmanager-fail-openlock": "Nun se pudo abrir el ficheru de bloquéu pa \"$1\".",
+       "lockmanager-fail-openlock": "Nun se pudo abrir el ficheru de bloquéu pa \"$1\". Por favor comprueba que'l direutoriu de xubíes ta configuráu correutamente y que'l to sirvidor web tien permisos d'escritura nesi direutoriu. Ver https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:$wgUploadDirectory pa más información.",
        "lockmanager-fail-releaselock": "Nun se pudo lliberar el bloquéu pa \"$1\".",
        "lockmanager-fail-db-bucket": "Nun se pudo contautar con bloqueos de bases de datos bastantes nel conxuntu $1.",
        "lockmanager-fail-db-release": "Nun se pudieron lliberar los bloqueos na base de datos $1.",
        "doubleredirects": "Redireiciones dobles",
        "doubleredirectstext": "Esta páxina llista páxines que redireicionen a otres páxines de redireición.\nCada filera contien enllaces a la primera y segunda redireición, asina como al oxetivu de la segunda redireición, que de vezu ye la páxina oxetivu \"real\", onde tendría d'empobinar la primera redireición.\nLes entraes <del>tachaes</del> tan resueltes.",
        "double-redirect-fixed-move": "[[$1]] treslladóse.\nAnovóse automáticamente y agora redirixe haza [[$2]].",
-       "double-redirect-fixed-maintenance": "Iguando automáticamente la doble redireición de [[$1]] a [[$2]] nuna xera de mantenimientu.",
+       "double-redirect-fixed-maintenance": "Iguando automáticamente la doble redireición de [[$1]] a [[$2]] nuna xera de mantenimientu",
        "double-redirect-fixer": "Iguador de redireiciones",
        "brokenredirects": "Redireiciones frañaes",
        "brokenredirectstext": "Les redireiciones siguientes enllacien a páxines que nun esisten:",
        "undelete-error": "Error al restaurar la páxina",
        "undelete-error-short": "Error al restaurar l'archivu: $1",
        "undelete-error-long": "Atopáronse errores al restaurar l'archivu:\n\n$1",
-       "undelete-show-file-confirm": "¿Tas seguru de que quies ver una versión desaniciada del ficheru \"<nowiki>$1</nowiki>\" del $2 a les $3?",
+       "undelete-show-file-confirm": "¿Tas seguru de que quies ver la versión desaniciada del ficheru «<nowiki>$1</nowiki>» del $2 a les $3?",
        "undelete-show-file-submit": "Sí",
        "namespace": "Espaciu de nomes:",
        "invert": "Invertir seleición",
        "month": "Dende'l mes (y anteriores):",
        "year": "Dende l'añu (y anteriores):",
        "sp-contributions-newbies": "Amosar namái les contribuciones de cuentes nueves",
-       "sp-contributions-newbies-sub": "Namái les cuentes nueves",
+       "sp-contributions-newbies-sub": "Pa cuentes nueves",
        "sp-contributions-newbies-title": "Contribuciones d'usuariu pa cuentes nueves",
        "sp-contributions-blocklog": "rexistru de bloqueos",
        "sp-contributions-suppresslog": "collaboraciones {{GENDER:$1|del usuariu|de la usuaria}} suprimíes",
        "whatlinkshere-page": "Páxina:",
        "linkshere": "Les páxines siguientes enllacien a '''[[:$1]]''':",
        "nolinkshere": "Nenguna páxina enllaza a '''[[:$1]]'''.",
-       "nolinkshere-ns": "Nenguna páxina enllaza a '''[[:$1]]''' nel espaciu de nome conseñáu.",
+       "nolinkshere-ns": "Nenguna páxina enllaza con <strong>[[:$1]]</strong> nel espaciu de nomes escoyíu.",
        "isredirect": "páxina de redireición",
        "istemplate": "tresclusión",
        "isimage": "enllaz al ficheru",
        "whatlinkshere-hideredirs": "$1 les redireiciones",
        "whatlinkshere-hidetrans": "$1 les tresclusiones",
        "whatlinkshere-hidelinks": "$1 los enllaces",
-       "whatlinkshere-hideimages": "$1 los enllaces al ficheru",
+       "whatlinkshere-hideimages": "$1 enllaces a ficheros",
        "whatlinkshere-filters": "Peñeres",
        "whatlinkshere-submit": "Dir",
        "autoblockid": "Autobloquiar #$1",
        "ipbreason": "Motivu:",
        "ipbreason-dropdown": "*Motivos comunes de bloquéu\n** Enxertamientu d'información falso\n** Dexar les páxines en blanco\n** Enllaces spam a páxines esternes\n** Enxertamientu de babayaes/enguedeyos nes páxines\n** Comportamientu intimidatoriu o d'acosu\n** Abusu de cuentes múltiples\n** Nome d'usuariu inaceutable",
        "ipb-hardblock": "Torgar que los usuarios coneutaos editen dende esta direición IP",
-       "ipbcreateaccount": "Evitar creación de cuentes",
+       "ipbcreateaccount": "Torgar la creación de cuentes",
        "ipbemailban": "Torgar al usuariu l'unviu de corréu electrónicu",
        "ipbenableautoblock": "Bloquiar automáticamente la cabera direición IP usada por esti usuariu y toles IP posteriores dende les qu'intente editar",
        "ipbsubmit": "Bloquiar esti usuariu",
        "ipb-disableusertalk": "Torgar qu'esti usuariu edite la so páxina d'alderique mentanto tea bloquiáu",
        "ipb-change-block": "Volver a bloquiar l'usuariu con estos parámetros",
        "ipb-confirm": "Confirmar bloquéu",
-       "badipaddress": "IP non válida",
+       "badipaddress": "Dirección IP inválida",
        "blockipsuccesssub": "Bloquéu fechu correctamente",
        "blockipsuccesstext": "[[Special:Contributions/$1|$1]] ta {{GENDER:$1|bloquiáu|bloquiada}}.<br />\nMira na [[Special:BlockList|llista de bloqueos]] pa revisar los bloqueos.",
        "ipb-blockingself": "¡Tas a piques de bloquiate tú mesmu! ¿Tas seguru de que quies facer eso?",
        "expiringblock": "caduca'l $1 a les $2",
        "anononlyblock": "namái anón.",
        "noautoblockblock": "bloquéu automáticu desactiváu",
-       "createaccountblock": "bloquiada la creación de cuentes",
+       "createaccountblock": "creación de cuentes desactivada",
        "emailblock": "corréu electrónicu desactiváu",
-       "blocklist-nousertalk": "nun pue editar la so páxina d'alderique",
+       "blocklist-nousertalk": "nun puede editar la páxina d'alderique propia",
        "ipblocklist-empty": "La llista de bloqueos ta vacia.",
        "ipblocklist-no-results": "La direición IP o nome d'usuariu solicitáu nun ta bloquiáu.",
        "blocklink": "bloquiar",
        "blocklogentry": "bloquió [[$1]] con una caducidá de $2 $3",
        "reblock-logentry": "camudó los parámetros de bloquéu de [[$1]] con una caducidá de $2 $3",
        "blocklogtext": "Esti ye un rexistru de los bloqueos y desbloqueos d'usuarios.\nLes direcciones IP bloquiaes automáticamente nun salen equí.\nPa ver los bloqueos qu'hai agora mesmo, mira na [[Special:BlockList|llista de bloqueos]].",
-       "unblocklogentry": "desbloquió $1",
+       "unblocklogentry": "desbloquió $1",
        "block-log-flags-anononly": "namái usuarios anónimos",
        "block-log-flags-nocreate": "creación de cuentes desactivada",
-       "block-log-flags-noautoblock": "bloquéu automáticu deshabilitáu",
+       "block-log-flags-noautoblock": "bloquéu automáticu desactiváu",
        "block-log-flags-noemail": "corréu electrónicu desactiváu",
-       "block-log-flags-nousertalk": "nun pue editar la páxina d'alderique propia",
+       "block-log-flags-nousertalk": "nun puede editar la páxina d'alderique propia",
        "block-log-flags-angry-autoblock": "autobloquéu ameyoráu activáu",
        "block-log-flags-hiddenname": "nome d'usuariu anubríu",
        "range_block_disabled": "La capacidá d'alministrador pa crear bloqueos d'intervalos ta desactivada.",
-       "ipb_expiry_invalid": "Tiempu incorrectu.",
+       "ipb_expiry_invalid": "El tiempu de caducidá nun ye válidu.",
        "ipb_expiry_old": "La fecha de caducidá ta pasada.",
-       "ipb_expiry_temp": "Los bloqueos de nome d'usuariu escondíos han ser permanentes.",
+       "ipb_expiry_temp": "Los bloqueos a nomes d'usuariu tapecíos tienen de ser permanentes.",
        "ipb_hide_invalid": "Nun se pue desaniciar esta cuenta; tien más {{PLURAL:$1|d'una edición|de $1 ediciones}}.",
        "ipb_already_blocked": "\"$1\" yá ta bloquiáu",
        "ipb-needreblock": "$1 yá ta bloquiáu. ¿Quies camudar los parámetros?",
        "ipb-otherblocks-header": "{{PLURAL:$1|Otru bloquéu|Otros bloqueos}}",
        "unblock-hideuser": "Nun pues desbloquiar esti usuariu, porque tien el nome d'usuariu anubríu.",
        "ipb_cant_unblock": "Error: Nun s'atopó'l bloquéu númberu $1. Seique yá fuera desbloquiáu.",
-       "ipb_blocked_as_range": "Error: La IP $1 nun ta bloquiada direutamente, polo que nun pue ser desloquiada. Sicasí, foi bloquiada como parte del intervalu $2, que pue ser desbloquiáu.",
-       "ip_range_invalid": "Rangu IP non válidu.",
+       "ipb_blocked_as_range": "Error: La IP $1 nun ta bloquiada direutamente y nun puede desbloquiase.\nSicasí, bloquióse como parte del intervalu $2, que puede desbloquiase.",
+       "ip_range_invalid": "Intervalu de direiciones IP non válidu.",
        "ip_range_toolarge": "Nun se permiten bloqueos mayores de /$1.",
        "ip_range_exceeded": "El rangu d'IP pasa del máximu. Rangu permitíu: /$1.",
        "ip_range_toolow": "Nun se permitieron efeutivamente los rangos d'IP.",
        "proxyblocker": "Bloquiador de proxys",
-       "proxyblockreason": "La to direición IP foi bloquiada porque ye un proxy abiertu. Por favor contauta col to proveedor de serviciones d'Internet o col to servicio d'asistencia téunica y infórmalos d'esti seriu problema de seguridá.",
+       "proxyblockreason": "La to direición IP bloquióse porque ye un proxy abiertu.\nComunícate col to proveedor de serviciu d'Internet o col serviciu d'asistencia téunica de la to organización pa informa-yos d'esti grave problema de seguridá.",
        "sorbsreason": "La to direición IP sal na llista de proxys abiertos en DNSBL usada por {{SITENAME}}.",
-       "sorbs_create_account_reason": "La to direición IP sal na llista de proxys abiertos en DNSBL usada por {{SITENAME}}. Nun pues crear una cuenta",
+       "sorbs_create_account_reason": "La to direición IP sal na llista de proxys abiertos en DNSBL usada por {{SITENAME}}.\nNun puedes crear una cuenta",
        "softblockrangesreason": "Nun se permiten les contribuciones anónimes dende la to dirección IP ($1). Por favor, anicia sesión.",
        "xffblockreason": "Una direición IP presente na testera X-Forwarded-For, o suya o d'un sirvidor proxy que ta usando, ta bloquiada. El motivu orixinal del bloquéu foi: $1",
        "cant-see-hidden-user": "L'usuariu que tentes bloquiar yá ta bloquiáu y anubríu.\nComo nun tienes permisos p'anubrir usuarios, nun pues ver o editar el bloquéu del usuariu.",
        "ipbblocked": "Nun pues bloquiar o desbloquiar a otros usuarios, porque tas bloquiáu tu mesmu",
        "ipbnounblockself": "Nun tienes permisu pa desbloquiate tu mesmu",
-       "lockdb": "Protexer la base de datos",
-       "unlockdb": "Desprotexer la base de datos",
+       "lockdb": "Bloquiar la base de datos",
+       "unlockdb": "Desbloquiar la base de datos",
        "lockdbtext": "Al bloquiar la base de datos suspenderáse la capacidá de tolos usuarios pa editar páxines, camudar les sos preferencies, editar les llistes de siguimientu y otres aiciones que requieran cambios na base de datos. Por favor confirma que ye lo que quies facer, y que desbloquiarás la base de datos cuando fines col mantenimientu.",
        "unlockdbtext": "Al desbloquiar la base de datos restauraráse la capacidá de tolos usuarios pa editar páxines, camudar les sos preferencies, editar les sos llistes de siguimientu y otres aiciones que requieren cambios na base de datos. Por favor confirma que ye lo quies facer.",
-       "lockconfirm": "Si, quiero candar daveres la base de datos.",
-       "unlockconfirm": "Sí, quiero descandar daveres la base de datos.",
-       "lockbtn": "Protexer la base de datos",
-       "unlockbtn": "Desprotexer la base de datos",
-       "locknoconfirm": "Nun activasti la caxa de confirmación.",
-       "lockdbsuccesssub": "Candáu de la base de datos efeutuáu correutamente",
-       "unlockdbsuccesssub": "Candáu de la base de datos elimináu",
-       "lockdbsuccesstext": "Candóse la base de datos.\n<br />Alcuérdate de [[Special:UnlockDB|descandala]] depués d'acabar el so mantenimientu.",
-       "unlockdbsuccesstext": "La base de datos foi descandada.",
-       "lockfilenotwritable": "L'archivu de candáu de la base de datos nun ye escribible. Pa candar o descandar la base de datos esti tien que poder ser modificáu pol sirvidor.",
+       "lockconfirm": "Sí, realmente quiero bloquiar la base de datos.",
+       "unlockconfirm": "Sí, realmente quiero desbloquiar la base de datos.",
+       "lockbtn": "Bloquiar la base de datos",
+       "unlockbtn": "Desbloquiar la base de datos",
+       "locknoconfirm": "Nun marcasti'l caxellu de confirmación.",
+       "lockdbsuccesssub": "Bloquióse correutamente la base de datos",
+       "unlockdbsuccesssub": "Quitóse'l bloquéu de la base de datos",
+       "lockdbsuccesstext": "Bloquióse la base de datos.<br />\nAlcuérdate de [[Special:UnlockDB|quitar el bloquéu]] depués d'acabar el mantenimientu.",
+       "unlockdbsuccesstext": "La base de datos ta desbloquiada.",
+       "lockfilenotwritable": "El ficheru de bloquéu de la base de datos nun puede escribise.\nPa bloquiar o desbloquiar la base de datos esti tien que poder escribise pol sirvidor web.",
        "databaselocked": "La base de datos yá ta bloquiada.",
-       "databasenotlocked": "La base de datos nun ta candada.",
+       "databasenotlocked": "La base de datos nun ta bloquiada.",
        "lockedbyandtime": "(por $1 el $2 a les $3)",
        "move-page": "Treslladar $1",
        "move-page-legend": "Treslladar páxina",
        "movepagetalktext": "Si marques esti cuadru, la páxina d'alderique asociada va treslladase automáticamente al títulu nuevu, a nun ser que yá esista una páxina d'alderique non vacia allí.\n\nNesti casu tendrás que treslladar o fusionar la páxina manualmente si lo desees.",
        "moveuserpage-warning": "'''Atención:''' Tas a piques de mover una páxina d'usuariu. Atalanta que namái se va mover la páxina y que ''nun'' se va renomar l'usuariu.",
        "movecategorypage-warning": "<strong>Avisu:</strong> Tas a piques de treslladar una páxina de categoría. Ten en cuenta que sólo se treslladará la páxina y que cualquier páxina que tuviera na categoría antigua <em>nun</em> se recategorizará na nueva.",
-       "movenologintext": "Tienes que ser un usuariu rexistráu y tar [[Special:UserLogin|identificáu]] pa treslladar una páxina.",
-       "movenotallowed": "Nun tienes permisu pa mover páxines.",
-       "movenotallowedfile": "Nun tienes permisu pa mover ficheros.",
+       "movenologintext": "Tienes de ser un usuariu rexistráu y con [[Special:UserLogin|sesión aniciada]] pa treslladar una páxina.",
+       "movenotallowed": "Nun tienes permisu pa treslladar páxines.",
+       "movenotallowedfile": "Nun tienes permisu pa treslladar ficheros.",
        "cant-move-user-page": "Nun tienes permisu pa treslladar páxines d'usuariu (independientemente de les subpáxines).",
        "cant-move-to-user-page": "Nun tienes permisu pa treslladar una páxina a una páxina d'usuariu (sacante a una subpáxina d'usuariu).",
        "cant-move-category-page": "Nun tienes permisu pa treslladar páxines de categoría.",
        "cant-move-subpages": "Nun tienes permisu pa mover subpáxines.",
        "namespace-nosubpages": "L'espaciu de nomes «$1» nun permite subpáxines.",
        "newtitle": "Títulu nuevu:",
-       "move-watch": "Vixilar esta páxina",
+       "move-watch": "Vixilar les páxines d'orixe y destín",
        "movepagebtn": "Treslladar la páxina",
        "pagemovedsub": "Treslláu correctu",
        "movepage-moved": "<strong>«$1» treslladóse a «$2»</strong>",
        "movepage-moved-redirect": "Creóse una redireición.",
        "movepage-moved-noredirect": "Desaniciose la creación d'una redireición.",
        "articleexists": "Yá hai una páxina con esi nome, o'l nome qu'escoyisti nun ye válidu. Por favor, escueyi otru nome.",
-       "cantmove-titleprotected": "Nun pues mover una páxina a esti llugar porque'l nuevu títulu foi protexíu de la so creación",
+       "cantmove-titleprotected": "Nun puedes treslladar una páxina a esti llugar porque ta protexida la creación del títulu nuevu.",
        "movetalk": "Mover la páxina d'alderique asociada",
        "move-subpages": "Treslladar les subpáxines (hasta $1)",
        "move-talk-subpages": "Treslladar les subpáxines de la páxina d'alderique (hasta $1)",
-       "movepage-page-exists": "La páxina $1 yá esiste y nun se pue sobreescribir automáticamente.",
+       "movepage-page-exists": "La páxina $1 yá esiste y nun puede sustituise automáticamente.",
        "movepage-page-moved": "Treslladóse la páxina $1 a $2.",
-       "movepage-page-unmoved": "Nun se pudo treslladar la páxina $1 a $2.",
+       "movepage-page-unmoved": "Nun pudo treslladase la páxina $1 a $2.",
        "movepage-max-pages": "Treslladóse'l máximu de $1 {{PLURAL:$1|páxina|páxinees}} y nun van treslladase más automáticamente.",
        "movelogpage": "Rexistru de tresllaos",
        "movelogpagetext": "Esta ye la llista de páxines treslladaes.",
        "movereason": "Motivu:",
        "revertmove": "revertir",
        "delete_and_move_text": "==Necesítase esborrar==\n\nLa páxina de destín «[[:$1]]» yá esiste. ¿Quies desaniciala pa dexar sitiu pal treslláu?",
-       "delete_and_move_confirm": "Sí, esborrar la páxina",
+       "delete_and_move_confirm": "Sí, desaniciar la páxina",
        "delete_and_move_reason": "Desaniciada pa facer sitiu pa treslladar dende «[[$1]]»",
        "selfmove": "El títulu ye'l mesmu;\nnun puede treslladase una páxina a ella mesma.",
-       "immobile-source-namespace": "Nun se puen treslladar páxines nel espaciu de nomes \"$1\"",
-       "immobile-target-namespace": "Nun se puen treslladar páxines al espaciu de nomes \"$1\"",
+       "immobile-source-namespace": "Nun pueden treslladase páxines nel espaciu de nomes «$1».",
+       "immobile-target-namespace": "Nun pueden treslladase páxines al espaciu de nomes «$1».",
        "immobile-target-namespace-iw": "Nun puedes mover una páxina a un enllaz d'Interwiki.",
        "immobile-source-page": "Esta páxina nun ye treslladable.",
-       "immobile-target-page": "Nun se pue treslladar a esi títulu de destín.",
+       "immobile-target-page": "Nun puede treslladase a esi títulu de destín.",
        "bad-target-model": "El destín deseáu utiliza un modelu de conteníu diferente. Nun se pue convertir de $1 a $2.",
-       "imagenocrossnamespace": "Nun se pue treslladar una imaxe a nun espaciu de nomes que nun ye d'imáxenes",
+       "imagenocrossnamespace": "Nun puede treslladase un ficheru a un espaciu de nomes que nun ye de ficheros.",
        "nonfile-cannot-move-to-file": "Nun se pue treslladar más que ficheros al espaciu de nomes de ficheros",
        "imagetypemismatch": "La estensión nueva del archivu nun concueya cola so mena",
-       "imageinvalidfilename": "El nome del archivu oxetivu nun ye válidu",
-       "fix-double-redirects": "Actualizar cualesquier redireición que señale al títulu orixinal",
+       "imageinvalidfilename": "El nome de ficheru de destín nun ye válidu.",
+       "fix-double-redirects": "Actualizar toles redireiciones qu'apunten al títulu orixinal",
        "move-leave-redirect": "Dexar una redireición detrás",
        "protectedpagemovewarning": "'''Avisu: Esta páxina ta candada pa que sólo los alministradores puedan treslladala.'''\nLa cabera entrada del rexistru s'ufre darréu pa referencia:",
        "semiprotectedpagemovewarning": "'''Nota:''' Esta páxina ta candada pa que namái los usuarios rexistraos puedan treslladala.\nLa cabera entrada del rexistru s'ufre darréu pa referencia:",
        "export": "Esportar páxines",
        "exporttext": "Pues esportar el testu y l'historial d'ediciones d'una páxina en particular o d'una\nriestra páxines endolcaes nun documentu XML. Esti se pue importar depués n'otra wiki\nqu'use MediaWiki al traviés de la páxina [[Special:Import|importar]].\n\nPa esportar páxines, pon los títulos na caxa de testu d'embaxo, un títulu por llinia,\ny seleiciona si quies la versión actual xunto con toles versiones antigües, xunto col\nso historial, o namái la versión actual cola información de la postrer edición.\n\nPor último, tamién pues usar un enllaz: p.e. [[{{#Special:Export}}/{{MediaWiki:Mainpage}}]] pa la páxina \"[[{{MediaWiki:Mainpage}}]]\".",
        "exportall": "Esportar toles páxines",
-       "exportcuronly": "Amestar namái la revisión actual, non tol historial",
-       "exportnohistory": "----\n'''Nota:''' Desactivóse la esportación del historial completu de páxines al traviés d'esti formulariu por motivos de rendimientu.",
+       "exportcuronly": "Amestar namái la revisión actual, non l'historial completu",
+       "exportnohistory": "----\n<strong>Nota:</strong> Desactivóse la esportación del historial completu de páxines con esti formulariu por motivos de rindimientu.",
        "exportlistauthors": "Incluir una llista completa de collaboradores pa cada páxina",
        "export-submit": "Esportar",
-       "export-addcattext": "Añader páxines dende la categoría:",
+       "export-addcattext": "Añadir páxines dende la categoría:",
        "export-addcat": "Amestar",
        "export-addnstext": "Amestar páxines del espaciu de nomes:",
        "export-addns": "Amestar",
-       "export-download": "Guardar como archivu",
-       "export-templates": "Inxerir plantíes",
+       "export-download": "Guardar como ficheru",
+       "export-templates": "Incluir plantíes",
        "export-pagelinks": "Incluyir páxines enllazaes fasta una profundidá de:",
        "export-manual": "Amestar páxines a mano:",
-       "allmessages": "Tolos mensaxes del sistema",
+       "allmessages": "Mensaxes del sistema",
        "allmessagesname": "Nome",
        "allmessagesdefault": "Testu predetermináu",
-       "allmessagescurrent": "Testu actual",
+       "allmessagescurrent": "Testu actual del mensaxe",
        "allmessagestext": "Esta ye una llista de los mensaxes de sistema disponibles nel espaciu de nomes de MediaWiki.\nPor favor visita [https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation Llocalización de MediaWiki] y [https://translatewiki.net translatewiki.net] si quies contribuyer a la llocalización xenérica de MediaWiki.",
-       "allmessagesnotsupportedDB": "Nun pue usase '''{{ns:special}}:Allmessages''' porque '''$wgUseDatabaseMessages''' ta deshabilitáu.",
+       "allmessagesnotsupportedDB": "Esta páxina nun puede usase porque ta desactivao <strong>$wgUseDatabaseMessages</strong>.",
        "allmessages-filter-legend": "Peñerar",
        "allmessages-filter": "Peñerar por estáu de personalización:",
        "allmessages-filter-unmodified": "Ensin cambeos",
        "allmessages-filter-submit": "Dir",
        "allmessages-filter-translate": "Traducir",
        "thumbnail-more": "Agrandar",
-       "filemissing": "Falta archivu",
+       "filemissing": "Falta el ficheru",
        "thumbnail_error": "Error al crear la miniatura: $1",
        "thumbnail_error_remote": "Mensaxe d'error de $1:\n$2",
-       "djvu_page_error": "Páxina DjVu fuera de llímites",
-       "djvu_no_xml": "Nun se pudo obtener el XML pal archivu DjVu",
+       "djvu_page_error": "Páxina DjVu fuera de llendes",
+       "djvu_no_xml": "Nun pudo llograse'l XML pal ficheru DjVu",
        "thumbnail-temp-create": "Nun se pudo crear el ficheru temporal de miniatura",
        "thumbnail-dest-create": "Nun se pudo guardar la miniatura nel destín",
        "thumbnail_invalid_params": "Parámetros de miniatura non válidos",
        "thumbnail_toobigimagearea": "Ficheru con dimensiones mayores que $1",
-       "thumbnail_dest_directory": "Nun se pue crear el direutoriu de destín",
+       "thumbnail_dest_directory": "Nun pudo crease'l direutoriu de destín",
        "thumbnail_image-type": "Triba d'imaxe ensin sofitu",
        "thumbnail_gd-library": "Configuración incompleta de la biblioteca GD: falta la función $1",
        "thumbnail_image-missing": "Paez que falta'l ficheru: $1",
        "import-revision-count": "$1 {{PLURAL:$1|revisión|revisiones}}",
        "importnopages": "Nun hai páxines pa importar.",
        "imported-log-entries": "Importao $1 {{PLURAL:$1|entrada del rexistru|entraes del rexistru}}.",
-       "importfailed": "Falló la importación: $1",
+       "importfailed": "Falló la importación: <nowiki>$1</nowiki>",
        "importunknownsource": "Triba d'orixe d'importación desconocida",
        "importnoprefix": "Nun se dio nengún prefixu d'interwiki",
        "importcantopen": "Nun se pudo abrir el ficheru d'importación",
        "importbadinterwiki": "Enllaz interwiki incorreutu",
        "importsuccess": "¡Importación finalizada!",
        "importnosources": "Nun se definieron wikis de onde importar y les xubíes direutes del historial tán desactivaes.",
-       "importnofile": "Nun se xubió nengún archivu d'importación.",
-       "importuploaderrorsize": "Falló la xubida del archivu d'importación. L'archivu ye más grande que'l tamañu permitíu de xubida.",
-       "importuploaderrorpartial": "Falló la xubida del archivu d'importación. L'archivu xubióse solo parcialmente.",
-       "importuploaderrortemp": "Falló la xubida del archivu d'importación. Falta una carpeta temporal.",
-       "import-parse-failure": "Fallu nel análisis d'importación XML",
+       "importnofile": "Nun se xubió nengún ficheru d'importación.",
+       "importuploaderrorsize": "Falló la xubida del ficheru d'importación.\nEl ficheru ye mayor que'l tamañu permitíu de xubida.",
+       "importuploaderrorpartial": "Falló la xubida del ficheru d'importación.\nEl ficheru xubióse sólo parcialmente.",
+       "importuploaderrortemp": "Falló la xubida del ficheru d'importación.\nFalta una carpeta temporal.",
+       "import-parse-failure": "Falló l'analís d'importación XML",
        "import-noarticle": "¡Nun hai páxina pa importar!",
        "import-nonewrevisions": "Nun s'importó denguna revision (o yá taben toes presentes, o se saltaron por errores).",
        "xml-error-string": "$1 na llinia $2, col $3 (byte $4): $5",
        "import-upload": "Xubir datos XML",
        "import-token-mismatch": "Perda de datos de la sesión.\n\nSeique zarróse la sesión. '''Comprueba si tienes abierta la sesión y tenta nuevamente'''.\nSi entá nun funciona, tenta [[Special:UserLogout|colar]] y volver a aniciar sesión y comprueba que'l restolador permite les cookies d'esti sitiu.",
-       "import-invalid-interwiki": "Nun se puede importar d'esi wiki.",
+       "import-invalid-interwiki": "Nun puede importase de la wiki especificada.",
        "import-error-edit": "La páxina «$1» nun s'importó porque nun tienes permisu pa editala.",
        "import-error-create": "La páxina «$1» nun s'importó porque nun tienes permisu pa creala.",
        "import-error-interwiki": "La páxina «$1» nun s'importó porque'l so nome ta acutáu pa enllaces esternos (interwiki).",
        "import-rootpage-invalid": "La páxina raíz dada ye un títulu inválidu.",
        "import-rootpage-nosubpage": "L'espaciu de nomes \"$1\" de la páxina raíz nun permite subpáxines.",
        "importlogpage": "Rexistru d'importaciones",
-       "importlogpagetext": "Importaciones alministrativas de páxines con historial d'ediciones d'otres wikis.",
+       "importlogpagetext": "Importaciones alministratives de páxines con historial d'ediciones d'otres wikis.",
        "import-logentry-upload-detail": "$1 {{PLURAL:$1|revisión importada|revisiones importaes}}",
        "import-logentry-interwiki-detail": "$1 {{PLURAL:$1|revisión importada|revisiones importaes}} dende $2",
        "javascripttest": "Prueba de JavaScript",
        "javascripttest-pagetext-unknownaction": "Aición desconocida «$1».",
        "javascripttest-qunit-intro": "Ver la [$1 documentación de les pruebes] en mediawiki.org.",
        "tooltip-pt-userpage": "La to páxina d'{{GENDER:|usuariu|usuaria}}",
-       "tooltip-pt-anonuserpage": "La páxina d'usuariu de la IP cola que tas editando",
+       "tooltip-pt-anonuserpage": "La páxina d'usuariu de la direición IP cola que tas editando",
        "tooltip-pt-mytalk": "La {{GENDER:|to}} páxina d'alderique",
        "tooltip-pt-anontalk": "Alderique de les ediciones feches con esta direición IP",
        "tooltip-pt-preferences": "Les {{GENDER:|tos}} preferencies",
        "tooltip-ca-protect": "Protexer esta páxina",
        "tooltip-ca-unprotect": "Camudar la proteición desta páxina",
        "tooltip-ca-delete": "Desaniciar esta páxina",
-       "tooltip-ca-undelete": "Restaura les ediciones feches nesta páxina enantes de que fuera esborrada",
+       "tooltip-ca-undelete": "Restaurar les ediciones feches nesta páxina enantes de que se desaniciara",
        "tooltip-ca-move": "Tresllada esta páxina",
        "tooltip-ca-watch": "Amiesta esta páxina a la to llista de siguimientu",
        "tooltip-ca-unwatch": "Desaniciar esta páxina de la to llista de siguimientu",
        "tooltip-t-permalink": "Enllaz permanente a esta revisión de la páxina",
        "tooltip-ca-nstab-main": "Ver la páxina de conteníu",
        "tooltip-ca-nstab-user": "Ver la páxina d'usuariu",
-       "tooltip-ca-nstab-media": "Amuesa la páxina de multimedia",
+       "tooltip-ca-nstab-media": "Ver la páxina de multimedia",
        "tooltip-ca-nstab-special": "Esta ye una páxina especial y nun pué editase",
        "tooltip-ca-nstab-project": "Vera la páxina de proyeutu",
        "tooltip-ca-nstab-image": "Ver la páxina del ficheru",
-       "tooltip-ca-nstab-mediawiki": "Amuesa'l mensaxe de sistema",
+       "tooltip-ca-nstab-mediawiki": "Ver el mensaxe del sistema",
        "tooltip-ca-nstab-template": "Amuesa la plantía",
        "tooltip-ca-nstab-help": "Amuesa la páxina d'ayuda",
        "tooltip-ca-nstab-category": "Ver la páxina de categoría",
        "tooltip-watch": "Amiesta esta páxina a la to llista de siguimientu",
        "tooltip-watchlistedit-normal-submit": "Desaniciar títulos",
        "tooltip-watchlistedit-raw-submit": "Anovar llista de vixilancia",
-       "tooltip-recreate": "Vuelve a crear la páxina magar que se tenga esborrao",
+       "tooltip-recreate": "Volver a crear la páxina anque se desaniciara",
        "tooltip-upload": "Empecipiar la xubida",
        "tooltip-rollback": "\"Revertir\" desfái nún clic la edición(es) d'esta páxina del postrer collaborador.",
        "tooltip-undo": "\"Esfacer\" revierte esta edición y abre'l formulariu d'edición en mou de vista previa. Permite añader un motivu nel resume.",
        "markedaspatrolledtext": "La revisión seleicionada de [[:$1]] se marcó como supervisada.",
        "rcpatroldisabled": "Supervisión de cambios recientes desactivada",
        "rcpatroldisabledtext": "La función de supervisión de cambios recientes ta desactivada nestos momentos.",
-       "markedaspatrollederror": "Nun se pue marcar como supervisada",
+       "markedaspatrollederror": "Nun puede marcase como supervisada",
        "markedaspatrollederrortext": "Necesites conseñar una revisión pa marcala como supervisada.",
        "markedaspatrollederror-noautopatrol": "Nun tienes permisu pa marcar los cambios propios como supervisaos.",
        "markedaspatrollednotify": "Esti cambiu en $1 marcóse como revisáu.",
        "log-show-hide-tag": "$1 rexistru d'etiquetes",
        "confirm-markpatrolled-button": "Aceutar",
        "confirm-markpatrolled-top": "¿Marcar la revisión $3 de $2 como patrullada?",
-       "deletedrevision": "Esborrada la reversión vieya $1",
-       "filedeleteerror-short": "Error al esborrar l'archivu: $1",
-       "filedeleteerror-long": "Atopáronse errores al esborrar l'archivu:\n\n$1",
-       "filedelete-missing": "L'archivu \"$1\" nun pue esborrase porque nun esiste.",
-       "filedelete-old-unregistered": "La revisión d'archivu \"$1\" nun ta na base de datos.",
-       "filedelete-current-unregistered": "L'archivu especificáu \"$1\" nun ta na base de datos.",
-       "filedelete-archive-read-only": "El direutoriu d'archivos \"$1\" nun pue ser modificáu pol sirvidor.",
+       "deletedrevision": "Desaniciada la revisión antigua $1",
+       "filedeleteerror-short": "Error al desaniciar el ficheru: $1",
+       "filedeleteerror-long": "Atopáronse errores al desaniciar el ficheru:\n\n$1",
+       "filedelete-missing": "El ficheru «$1» nun puede desaniciase porque nun esiste.",
+       "filedelete-old-unregistered": "La revisión especificada del ficheru «$1» nun ta na base de datos.",
+       "filedelete-current-unregistered": "El ficheru especificáu «$1» nun ta na base de datos.",
+       "filedelete-archive-read-only": "El sirvidor web nun puede escribir nel direutoriu d'archivos «$1».",
        "previousdiff": "← Edición más antigua",
        "nextdiff": "Edición más nueva →",
        "mediawarning": "'''Avisu''': Esta triba de ficheru pue contener códigu maliciosu.\nAl executalu pues comprometer el to sistema.",
        "file-info-png-frames": "$1 {{PLURAL:$1|cuadru|cuadros}}",
        "file-no-thumb-animation": "'''Nota: pola mor de llímites téunicos, les miniatures d'esti ficheru nun tan animaes.'''",
        "file-no-thumb-animation-gif": "'''Nota: pola mor de llímites téunicos, les miniatures d'imaxes GIF d'alta resolución como esta nun tan animaes.'''",
-       "newimages": "Galería d'imáxenes nueves",
+       "newimages": "Galería de ficheros nuevos",
        "imagelisttext": "Embaxo ta la llista {{PLURAL:$1|d'un archivu ordenáu|de '''$1''' archivos ordenaos}} $2.",
        "newimages-summary": "Esta páxina especial amuesa los caberos archivos xubíos.",
        "newimages-legend": "Peñera",
index a03318a..6f0bd53 100644 (file)
        "postedit-confirmation-created": "Старонка была створаная.",
        "postedit-confirmation-restored": "Старонка была адноўленая.",
        "postedit-confirmation-saved": "Вашая праўка была захаваная.",
+       "postedit-confirmation-published": "Вашая праўка была апублікаваная.",
        "edit-already-exists": "Немагчыма стварыць новую старонку.\nЯна ўжо існуе.",
        "defaultmessagetext": "Перадвызначаны тэкст паведамленьня",
        "content-failed-to-parse": "Зьмест «$2» не адпавядае мадэлі $1: $3.",
        "right-createtalk": "Стварэньне старонак абмеркаваньня",
        "right-createaccount": "Стварэньне новых рахункаў удзельнікаў",
        "right-autocreateaccount": "Аўтаматычны ўваход з вонкавага рахунку ўдзельніка",
-       "right-minoredit": "пазнаÑ\87Ñ\8dнÑ\8cне Ñ\80Ñ\8dдагаванÑ\8cнÑ\8fÑ\9e Ñ\8fк Ð´Ñ\80обнÑ\8bÑ\8f",
-       "right-move": "перанос старонак",
-       "right-move-subpages": "перанос старонак разам зь іх падстаронкамі",
+       "right-minoredit": "Ð\9fазнаÑ\87Ñ\8dнÑ\8cне Ñ\80Ñ\8dдагаванÑ\8cнÑ\8fÑ\9e Ñ\8fк Ð´Ñ\80обнÑ\8bÑ\85",
+       "right-move": "Ð\9fеранос старонак",
+       "right-move-subpages": "Ð\9fеранос старонак разам зь іх падстаронкамі",
        "right-move-rootuserpages": "перанос карэнных старонак удзельнікаў",
        "right-move-categorypages": "перанос старонак катэгорыяў",
        "right-movefile": "перайменаваньне файлаў",
index a569fc9..e8f0f45 100644 (file)
        "zip-file-open-error": "Възникна грешка при отваряне на файла за проверка на ZIP.",
        "zip-wrong-format": "Указаният файл не е ZIP файл.",
        "zip-bad": "Файлът е повреден или е нечетим ZIP файл.\nСигурността му не може да бъде проверена.",
-       "zip-unsupported": "Файлът е ZIP файл, който използва ZIP компоненти, които не се поддържат от МедияУики.\nСигурността му не може да бъде коректно проверена.",
+       "zip-unsupported": "Файлът е ZIP файл, който използва функционалност, която не се поддържа от МедияУики.\nСигурността му не може да бъде коректно проверена.",
        "uploadstash": "Скрити качвания",
        "uploadstash-summary": "Тази страница предоставя достъп до файловете, които са качени (или са в процес на качване), но все още не са публикувани в уикито. Тези файлове не са достъпни само за потребителя, който ги е качил.",
        "uploadstash-clear": "Изчистване на скритите качвания",
        "doubleredirects": "Двойни пренасочвания",
        "doubleredirectstext": "Тази страница съдържа списък със страници, които пренасочват към друга пренасочваща страница.\nВсеки ред съдържа препратки към първото и второто пренасочване, както и целта на второто пренасочване, която обикновено е „истинската“ целева страница, към която първото пренасочване би трябвало да сочи.\n<del>Задрасканите</del> записи са коригирани.",
        "double-redirect-fixed-move": "Страница [[$1]] беше преместена.\nБеше автоматично обновена и сега пренасочва към [[$2]].",
-       "double-redirect-fixed-maintenance": "Автоматично коригиране на двойно пренасочване от [[$1]] към [[$2]] при извършване на техническо обслужване.",
+       "double-redirect-fixed-maintenance": "Автоматично коригиране на двойно пренасочване от [[$1]] към [[$2]] при извършване на техническо обслужване",
        "double-redirect-fixer": "Коректор на пренасочвания",
        "brokenredirects": "Невалидни пренасочвания",
        "brokenredirectstext": "Следните пренасочващи страници сочат към несъществуващи страници:",
        "expandtemplates": "Разгръщане на шаблони",
        "expand_templates_intro": "Тази специална страница взима текст и рекурсивно разгръща всички шаблони в нея.\nТя разгръща и всички поддържани парсерни функции като\n<code><nowiki>{{</nowiki>#language:…}}</code> и променливи като\n<code><nowiki>{{</nowiki>CURRENTDAY}}</code>.\nНа практика тя разгръща почти всичко в двойни скоби.",
        "expand_templates_title": "Заглавие на страницата (напр. за {{FULLPAGENAME}}):",
-       "expand_templates_input": "Входящ текст:",
+       "expand_templates_input": "Ð\92Ñ\85одÑ\8fÑ\89 Ñ\83икиÑ\82екÑ\81Ñ\82:",
        "expand_templates_output": "Резултат",
        "expand_templates_xml_output": "Изход на XML",
        "expand_templates_ok": "ОК",
index 1780f90..2715118 100644 (file)
        "category-empty": "<em>Ova kategorija trenutno ne sadrži članke ni medije.</em>",
        "hidden-categories": "{{PLURAL:$1|Sakrivena kategorija|Sakrivene kategorije}}",
        "hidden-category-category": "Skrivene kategorije",
-       "category-subcat-count": "{{PLURAL:$2|1=Ova kategorija samo ima sljedeću potkategoriju.|Ova kategorija ima {{PLURAL:$1|sljedeću potkategoriju|sljedeće $1 potkategorije|sljedećih $1 potkategorija}}, od $2 ukupno.}}",
+       "category-subcat-count": "{{PLURAL:$2|1=Ova kategorija sadrži samo sljedeću potkategoriju.|{{PLURAL:$1|Prikazana je $1 potkategorija|Prikazane su $1 potkategorije|Prikazano je $1 potkategorija}}, od ukupno $2.}}",
        "category-subcat-count-limited": "Ova kategorija sadrži {{PLURAL:$1|sljedeću $1 potkategoriju|sljedeće $1 potkategorije|sljedećih $1 potkategorija}}.",
-       "category-article-count": "{{PLURAL:$2|1=Ova kategorija sadrži samo sljedeću stranicu.|{{PLURAL:$1|Sljedeća stranica je|Sljedeće $1 stranice su|Sljedećih $1 stranica je}} u ovoj kategoriji, od ukupno $2.}}",
+       "category-article-count": "{{PLURAL:$2|1=Ova kategorija sadrži samo sljedeću stranicu.|{{PLURAL:$1|Prikazana je $1 stranica|Prikazane su $1 stranice|Prikazano je $1 stranica}}, od ukupno $2.}}",
        "category-article-count-limited": "{{PLURAL:$1|Slijedeća $1 stranica je|Slijedeće $1 stranice su|Slijedećih $1 stranica je}} u ovoj kategoriji.",
        "category-file-count": "{{PLURAL:$2|Ova kategorija ima slijedeću $1 datoteku.|{{PLURAL:$1|Prikazana je $1 datoteka|Prikazane su $1 datoteke|Prikazano je $1 datoteka}} u ovoj kategoriji, od ukupno $2.}}",
        "category-file-count-limited": "{{PLURAL:$1|Slijedeća $1 datoteka je|Slijedeće $1 datoteke su|Slijedećih $1 datoteka je}} u ovoj kategoriji.",
index 95a0ba1..5657fb2 100644 (file)
        "postedit-confirmation-created": "S'ha creat la pàgina.",
        "postedit-confirmation-restored": "S'ha restaurat la pàgina.",
        "postedit-confirmation-saved": "S'ha desat la modificació.",
+       "postedit-confirmation-published": "S’ha publicat la vostra modificació.",
        "edit-already-exists": "No s'ha pogut crear una pàgina.\nJa existeix.",
        "defaultmessagetext": "Missatge per defecte",
        "content-failed-to-parse": "Ha fallat l'anàlisi del contingut de $2 per al model $1: $3",
        "authprovider-confirmlink-failed": "L'enllaçament de comptes no ha funcionat satisfactòriament: $1",
        "authprovider-resetpass-skip-label": "Omet",
        "authprovider-resetpass-skip-help": "Omet el restabliment de contrasenya.",
+       "authform-nosession-login": "L’autenticació fou exitosa, però el vostre navegador no pot «recordar» haver iniciat la sessió.\n\n$1",
+       "authform-nosession-signup": "S’ha creat el compte, però el vostre navegador no pot «recordar» haver iniciat la sessió.\n\n$1",
        "specialpage-securitylevel-not-allowed-title": "No permès",
        "specialpage-securitylevel-not-allowed": "Ho sentim, no podeu utilitzar la pàgina perquè no es pot verificar la vostra identitat.",
        "authpage-cannot-login": "No s'ha pogut iniciar la sessió.",
        "edit-error-long": "Errors:\n\n$1",
        "revid": "revisió $1",
        "pageid": "ID de pàgina $1",
+       "rawhtml-notallowed": "No és possible fer servir les etiquetes &lt;html&gt; fora de les pàgines normals.",
        "gotointerwiki": "A punt d’abandonar {{SITENAME}}",
        "gotointerwiki-invalid": "El títol especificat no és vàlid.",
        "gotointerwiki-external": "Esteu a punt d’abandonar {{SITENAME}} per a visitar [[$2]], un lloc web diferent.\n\n'''[$1 Continua a $1]'''",
index 0e9bed7..14e189c 100644 (file)
        "movepage-moved-noredirect": "ДӀасхьажорг кхоллар дохина.",
        "articleexists": "ХӀарасанна цӀе йолу агӀо йолуш ю йа ахьа гойтуш йолу цӀе магош яц.\nДехар до, харжа кхин цӀе.",
        "movetalk": "Цуьнца йогӀуш йолу дийцаре агӀон цӀе хийцар",
-       "move-subpages": "ЦÓ\80еÑ\80Ñ\88 Ñ\85ийÑ\86а Ð±Ñ\83Ñ\85аÑ\80а Ð°Ð³Ó\80онийн ($1 кхаччалц)",
+       "move-subpages": "Ð\91Ñ\83Ñ\85аÑ\80а Ð°Ð³Ó\80онийн Ñ\86Ó\80еÑ\80Ñ\88 Ñ\85ийÑ\86а($1 кхаччалц)",
        "move-talk-subpages": "ЦӀе хийца бухара агӀонийн а, агӀонийн дийцаре а ($1  кхаччалц)",
        "movepage-page-exists": "Агӏо $1 йолуш ю цундела и ша юху дӏаязъян йиш яц.",
        "movepage-page-moved": "АгӀона $1 цӀе хийцина → $2.",
index fa1f3db..afaa232 100644 (file)
@@ -36,7 +36,8 @@
                        "Martin Urbanec",
                        "Marek Pavlica",
                        "Asmen",
-                       "Meliganai"
+                       "Meliganai",
+                       "Ilimanaq29"
                ]
        },
        "tog-underline": "Podtrhávat odkazy:",
        "postedit-confirmation-created": "Stránka byla založena.",
        "postedit-confirmation-restored": "Stránka byla obnovena.",
        "postedit-confirmation-saved": "Vaše změny byly uloženy.",
+       "postedit-confirmation-published": "Vaše editace byla uložena.",
        "edit-already-exists": "Nepodařilo se vytvořit novou stránku, protože již existuje.",
        "defaultmessagetext": "Výchozí text hlášení",
        "content-failed-to-parse": "Nepodařilo se zpracovat data $2 do modelu $1: $3",
        "lockmanager-fail-closelock": "Soubor se zámkem pro „$1“ nelze zavřít.",
        "lockmanager-fail-deletelock": "Soubor se zámkem pro „$1“ nelze smazat.",
        "lockmanager-fail-acquirelock": "Zámek pro „$1“ nelze získat.",
-       "lockmanager-fail-openlock": "Soubor se zámkem pro „$1“ nelze otevřít.",
+       "lockmanager-fail-openlock": "Soubor se zámkem „$1“ nelze otevřít. Ujistěte se, že vámi nahraný adresář je správně nakonfigurován a že váš webový server má povolení k editaci tohoto adresáře. Pro další informace vizte https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:$wgUploadDirectory.",
        "lockmanager-fail-releaselock": "Zámek pro „$1“ nelze uvolnit.",
        "lockmanager-fail-db-bucket": "Nelze navázat spojení s dostatečným počtem databází zámků v bloku $1.",
        "lockmanager-fail-db-release": "Uzamčení databáze $1 nelze uvolnit.",
index 828804d..3a018c8 100644 (file)
        "postedit-confirmation-created": "Die Seite wurde erstellt.",
        "postedit-confirmation-restored": "Die Seite wurde wiederhergestellt.",
        "postedit-confirmation-saved": "Deine Bearbeitung wurde gespeichert.",
+       "postedit-confirmation-published": "Deine Bearbeitung wurde veröffentlicht.",
        "edit-already-exists": "Die neue Seite konnte nicht erstellt werden, da sie bereits vorhanden ist.",
        "defaultmessagetext": "Standardtext",
        "content-failed-to-parse": "Parsen des Inhalts $2 für Modell $1 fehlgeschlagen: $3",
index 745a432..ca5672d 100644 (file)
        "seconds-ago": "Verê $1 {{PLURAL:$1|saniya|saniyeyan}}",
        "monday-at": "Dışeme $1 de",
        "tuesday-at": "Sêşeme $1 de",
-       "wednesday-at": "Çarşeme $1 de",
+       "wednesday-at": "Çeharşeme $1 de",
        "thursday-at": "Pancşeme $1 de",
        "friday-at": "Êne $1 de",
        "saturday-at": "Şeme $1 de",
index 3726ae9..a638833 100644 (file)
        "postedit-confirmation-created": "The page has been created.",
        "postedit-confirmation-restored": "The page has been restored.",
        "postedit-confirmation-saved": "Your edit was saved.",
+       "postedit-confirmation-published": "Your edit was published.",
        "edit-already-exists": "Could not create a new page.\nIt already exists.",
        "addsection-preload": "",
        "addsection-editintro": "",
index 3bf879f..64c62c9 100644 (file)
        "postedit-confirmation-created": "Se ha creado la página.",
        "postedit-confirmation-restored": "Se ha restaurado la página.",
        "postedit-confirmation-saved": "Se ha guardado tu edición.",
+       "postedit-confirmation-published": "Se publicó tu edición.",
        "edit-already-exists": "No se pudo crear una página nueva.\nYa existe.",
        "defaultmessagetext": "Texto predeterminado",
        "content-failed-to-parse": "No se ha podido procesar el contenido $2 del modelo de $1: $3",
        "lockmanager-fail-closelock": "No se pudo cerrar la referencia para el archivo de bloqueo de \"$1\".",
        "lockmanager-fail-deletelock": "No se pudo eliminar el archivo de bloqueo para \"$1\".",
        "lockmanager-fail-acquirelock": "No pudo adquirir el bloqueo para \"$1\".",
-       "lockmanager-fail-openlock": "No se pudo abrir el archivo de bloqueo para \"$1\". Por favor verifica que el directorio de subidas está configurado correctamente y que tu servidor web tiene permisos de escritura en ese directorio. Ver https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:$wgUploadDirectory para más información.",
+       "lockmanager-fail-openlock": "No se pudo abrir el archivo de bloqueo de «$1». Revisa que el directorio de cargas esté configurado correctamente y que tu servidor web tenga permisos de escritura en ese directorio. Consulta https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:$wgUploadDirectory para obtener más información.",
        "lockmanager-fail-releaselock": "No se pudo liberar el bloqueo de \"$1\".",
        "lockmanager-fail-db-bucket": "No se pudo contactar con las suficientes bases de datos del conjunto $1.",
        "lockmanager-fail-db-release": "No se pudieron liberar los bloqueos registrados en la base de datos $1.",
        "protect-otherreason": "Otra razón:",
        "protect-otherreason-op": "Otro motivo",
        "protect-dropdown": "*Razones de protección habituales\n**Vandalismo excesivo\n**Spam excesivo\n**Guerra de ediciones\n**Página muy visitada",
-       "protect-edit-reasonlist": "Editar las razones de protección",
+       "protect-edit-reasonlist": "Editar motivos de protección",
        "protect-expiry-options": "1 hora:1 hour,1 día:1 day,1 semana:1 week,2 semanas:2 weeks,1 mes:1 month,3 meses:3 months,6 meses:6 months,1 año:1 year,para siempre:infinite",
        "restriction-type": "Permiso:",
        "restriction-level": "Nivel de restricción:",
        "authprovider-confirmlink-ok-help": "Continuar luego de mostrar los mensajes de error en la vinculación.",
        "authprovider-resetpass-skip-label": "Omitir",
        "authprovider-resetpass-skip-help": "Saltar el reestablecimiento de la contraseña.",
-       "authform-nosession-login": "La autenticación fue exitosa, pero tu navegador no puede \"recordar\" haber iniciado sesión.\n\n$1",
-       "authform-nosession-signup": "La cuenta ha sido creada, pero tu navegador no \"recuerda\" haber iniciado sesión.\n\n$1",
+       "authform-nosession-login": "La autenticación fue exitosa, pero tu navegador no «recuerda» haber accedido a la cuenta.\n\n$1",
+       "authform-nosession-signup": "Se ha creado la cuenta, pero tu navegador no «recuerda» haber accedido a ella.\n\n$1",
        "authform-newtoken": "Falta token. $1",
        "authform-notoken": "Falta token",
        "authform-wrongtoken": "Token incorrecto",
index 706d8ba..3030d0a 100644 (file)
        "postedit-confirmation-created": "See lehekülg on alustatud.",
        "postedit-confirmation-restored": "See lehekülg on taastatud.",
        "postedit-confirmation-saved": "Sinu muudatus on salvestatud.",
+       "postedit-confirmation-published": "Sinu muudatus on avaldatud.",
        "edit-already-exists": "Ei saanud alustada uut lehekülge.\nSee on juba olemas.",
        "defaultmessagetext": "Sõnumi vaiketekst",
        "content-failed-to-parse": "Ebaõnnestus $1i mudeli $2-tüüpi sisu liigendamine: $3",
        "lockmanager-fail-closelock": "Raja \"$1\" lukustusfaili ei saa sulgeda.",
        "lockmanager-fail-deletelock": "Raja \"$1\" lukustusfaili ei saa kustutada.",
        "lockmanager-fail-acquirelock": "Ressursi \"$1\" luku hankimine ebaõnnestus.",
-       "lockmanager-fail-openlock": "Raja \"$1\" lukustusfaili ei saa avada.",
+       "lockmanager-fail-openlock": "Raja \"$1\" lukustusfaili ei saa avada. Veendu, et üleslaadimise kataloog on õigesti häälestatud ja et sinu veebiserveril on õigus sellesse kataloogi kirjutada. Vaata lisateavet aadressil https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:$wgUploadDirectory.",
        "lockmanager-fail-releaselock": "Ressursi \"$1\" lukku ei õnnestunud vabastada.",
        "lockmanager-fail-db-bucket": "Komplektis $1 ei õnnestunud saada ühendust piisavalt paljude lukustusandmebaasidega.",
        "lockmanager-fail-db-release": "Andmebaasis $1 ei õnnestunud lukke vabastada.",
        "pageinfo-category-subcats": "Alamkategooriaid",
        "pageinfo-category-files": "Faile",
        "pageinfo-user-id": "Kasutaja identifikaator",
+       "pageinfo-file-hash": "Räsiväärtus",
        "markaspatrolleddiff": "Märgi kontrollituks",
        "markaspatrolledtext": "Märgi see leht kontrollituks",
        "markaspatrolledtext-file": "Märgi see failiversioon kontrollituks",
index a92336b..de21953 100644 (file)
        "lockmanager-fail-closelock": "Ezin izan da \"$1\" fitxategiaren giltza itxi.",
        "lockmanager-fail-deletelock": "Ezin izan da \"$1\" fitxategia desblokeatu.",
        "lockmanager-fail-acquirelock": "Ezin izan da \"$1\" blokeoa eskuratu.",
-       "lockmanager-fail-openlock": "Ezin izan da \"$1\" blokeo fitxategia ireki.",
+       "lockmanager-fail-openlock": "Ezin izan da \"$1\"-rentzako blokeo fitxategia ireki. Ziurtatu igoera direktorioa behar bezala konfiguratuta dagoela eta zure web zerbitzariak baimena duela idazteko. Ikusi informazio gehiagorako https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:$wgUploadDirectory.",
        "lockmanager-fail-releaselock": "Ezin izan da \"$1\" blokeoa askatu.",
        "lockmanager-fail-db-bucket": "Ezin izan dut $1 buketean datubase nahikoa ziurtatu.",
        "lockmanager-fail-db-release": "Ezin izan da $1 datubasean giltzaraporik askatu.",
        "restrictionsfield-badip": "Baliogabeko IP helbide edo eremua: $1",
        "restrictionsfield-label": "Onartutako IP eremuak:",
        "restrictionsfield-help": "Linea bakoitzeko IP helbide edo CIDR eremu bakarra. Guztia gaitzeko, erabili: \n<pre>0.0.0.0/0\n::/0</pre>",
+       "edit-error-short": "Akatsa: $1",
+       "edit-error-long": "Akatsak:\n\n$1",
        "revid": "$1 berrikusi",
        "pageid": "$1 orri IDa",
        "rawhtml-notallowed": "&lt;html&gt; etiketak ezin dira orri arruntetatik kanpo erabili.",
index 09db746..b4d1e23 100644 (file)
        "ipb-hardblock": "جلوگیری از ویرایش کردن کاربران ثبت نام کرده از طریق این نشانی آی‌پی",
        "ipbcreateaccount": "جلوگیری از ایجاد حساب",
        "ipbemailban": "جلوگیری از ارسال ایمیل",
-       "ipbenableautoblock": "بستن  خودکار آخرین نشانی آی‌پی استفاده شده توسط کاربر و نشانی‌های دیگری که از آن‌ها برای ویرایش تلاش می‌کند",
+       "ipbenableautoblock": "بستن خودکار آخرین نشانی آی‌پی استفاده شده توسط کاربر و نشانی‌های دیگری که از آن‌ها برای ویرایش تلاش می‌کند",
        "ipbsubmit": "این کاربر بسته شود",
        "ipbother": "زمانی دیگر",
        "ipboptions": "۲ ساعت:2 hours,۱ روز:1 day,۳ روز:3 days,۱ هفته:1 week,۲ هفته:2 weeks,۱ ماه:1 month,۳ ماه:3 months,۶ ماه:6 months,۱ سال:1 year,بی‌پایان:infinite",
index c33aab2..a5f9597 100644 (file)
        "postedit-confirmation-created": "La page a été créée.",
        "postedit-confirmation-restored": "La page a été restaurée.",
        "postedit-confirmation-saved": "Votre modification a été enregistrée.",
+       "postedit-confirmation-published": "Votre modification a été publiée.",
        "edit-already-exists": "La nouvelle page n’a pas pu être créée.\nElle existe déjà.",
        "defaultmessagetext": "Message par défaut",
        "content-failed-to-parse": "Échec de l’analyse syntaxique du contenu de $2 pour le modèle $1 : $3",
index e79ee5f..e309eec 100644 (file)
        "postedit-confirmation-created": "הדף נוצר.",
        "postedit-confirmation-restored": "הדף שוחזר.",
        "postedit-confirmation-saved": "העריכה שלך נשמרה.",
+       "postedit-confirmation-published": "העריכה שלך פורסמה.",
        "edit-already-exists": "לא ניתן ליצור דף חדש.\nהוא כבר קיים.",
        "defaultmessagetext": "טקסט ההודעה המקורי",
        "content-failed-to-parse": "פענוח $2 כתוכן מסוג $1 נכשל: $3",
        "lockmanager-fail-closelock": "לא הייתה אפשרות לסגור את קובץ הנעילה עבור \"$1\".",
        "lockmanager-fail-deletelock": "לא הייתה אפשרות למחוק את קובץ הנעילה עבור \"$1\".",
        "lockmanager-fail-acquirelock": "לא הייתה אפשרות לקבל נעילה עבור \"$1\".",
-       "lockmanager-fail-openlock": "לא הייתה אפשרות לפתוח את קובץ הנעילה עבור \"$1\".",
+       "lockmanager-fail-openlock": "לא הייתה אפשרות לפתוח את קובץ הנעילה עבור \"$1\". יש לוודא שתיקיית ההעלאות מוגדרת בצורה תקינה ושלשרת האינטרנט יש הרשאות לכתוב לתיקייה זו. [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:$wgUploadDirectory למידע נוסף].",
        "lockmanager-fail-releaselock": "לא הייתה אפשרות לשחרר את הנעילה עבור \"$1\".",
        "lockmanager-fail-db-bucket": "לא הייתה אפשרות לקבל מספיק מסדי נתונים של נעילות בדלי $1.",
        "lockmanager-fail-db-release": "לא הייתה אפשרות לשחרר נעילות על מסד הנתונים $1.",
index f250133..060c444 100644 (file)
        "watchlist": "Popis praćenja",
        "mywatchlist": "Popis praćenja",
        "watchlistfor2": "Za $1 $2",
-       "nowatchlist": "Na Vašem popisu praćenja nema nijednog članka.",
+       "nowatchlist": "Na Vašem popisu praćenja nema ijedne stavke.",
        "watchlistanontext": "Molimo Vas prijavite se kako biste mogli vidjeti ili uređivati Vaš popis praćenih stranica.",
        "watchnologin": "Niste prijavljeni",
        "addwatch": "Dodaj u popis praćenja",
index 1a1353d..082760f 100644 (file)
        "postedit-confirmation-created": "Az oldal létrehozva.",
        "postedit-confirmation-restored": "Az oldal helyre lett állítva.",
        "postedit-confirmation-saved": "A szerkesztésedet elmentettük.",
+       "postedit-confirmation-published": "A szerkesztésed közzé lett téve.",
        "edit-already-exists": "Az új lap nem készíthető el.\nMár létezik.",
        "defaultmessagetext": "Alapértelmezett szöveg",
        "content-failed-to-parse": "Hiba történt a $2 tartalom $1 modellre történő konvertálása során: $3",
        "search-external": "Külső kereső",
        "searchdisabled": "Elnézésed kérjük, de a teljes szöveges keresés terhelési okok miatt átmenetileg nem használható. Ezidő alatt használhatod a lenti Google keresést, mely viszont lehetséges, hogy nem teljesen friss adatokkal dolgozik.",
        "search-error": "A keresés közben hiba történt: $1",
-       "search-warning": "$1 keresése közben hiba történt",
+       "search-warning": "Hiba történt a keresés közben: $1",
        "preferences": "Beállítások",
        "mypreferences": "Beállítások",
        "prefs-edits": "Szerkesztéseid száma:",
        "lockmanager-fail-closelock": "Nem sikerült a „$1” zárolási fájljának bezárása.",
        "lockmanager-fail-deletelock": "Nem sikerült a(z) „$1” zárolási fájljának törlése.",
        "lockmanager-fail-acquirelock": "Nem sikerült zárolást igényelni a „$1” fájlhoz.",
-       "lockmanager-fail-openlock": "Nem sikerült a „$1” zárolási fájljának megnyitása.",
+       "lockmanager-fail-openlock": "Nem sikerült a „$1” zárolási fájljának megnyitása. Győződj meg róla, hogy a feltöltési könyvtárad jól van konfigurálva és a webszerverednek van engedélye írni a könyvtárat. Lásd még további információért: https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:$wgUploadDirectory",
        "lockmanager-fail-releaselock": "Nem sikerült a(z) „$1” fájl zárolásának feloldása.",
        "lockmanager-fail-db-bucket": "Nem sikerült kapcsolatot létesíteni elég adatbázis zároláshoz a $1 vödörben.",
        "lockmanager-fail-db-release": "Nem lehet a $1 adatbázis zárolását feloldani.",
index 340ab40..8aeac3c 100644 (file)
        "timezoneregion-indian": "Indlandshaf",
        "timezoneregion-pacific": "Kyrrahaf",
        "allowemail": "Leyfa öðrum notendum að senda mér tölvupóst",
+       "email-allow-new-users-label": "Leyfa tölvupóst frá nýskráðum notendum",
        "prefs-searchoptions": "Leit",
        "prefs-namespaces": "Nafnrými",
        "default": "sjálfgefið",
        "lockmanager-fail-closelock": "Gat ekki lokað lásaskrá vegna „$1“.",
        "lockmanager-fail-deletelock": "Gat ekki eytt lásaskrá vegna „$1“.",
        "lockmanager-fail-acquirelock": "Gat ekki nálgast lás vegna „$1“.",
-       "lockmanager-fail-openlock": "Gat ekki opnað lásaskrá vegna „$1“.",
+       "lockmanager-fail-openlock": "Gat ekki opnað lásaskrá vegna „$1“. Gakkt úr skugga um að innsendingamappan þín sé rétt uppsett, og að vefþjónninn þinn hafi heimildir til að skrifa í þá möppu. Skoðaðu https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:$wgUploadDirectory til að sjá nánari upplýsingar.",
        "lockmanager-fail-releaselock": "Gat ekki opnað lás vegna „$1“.",
        "lockmanager-fail-db-bucket": "Náði ekki sambandi við nógu marga lása í fötunni $1.",
        "lockmanager-fail-db-release": "Gat ekki opnað lása á gagnagrunninum $1.",
        "uploadstash-bad-path": "Slóðin er ekki til.",
        "uploadstash-bad-path-invalid": "Slóðin er ógild.",
        "uploadstash-bad-path-unknown-type": "Óþekkt gerð \"$1\".",
+       "uploadstash-bad-path-unrecognized-thumb-name": "Óþekkt heiti á smámynd.",
+       "uploadstash-file-not-found-no-thumb": "Gat ekki náð í smámynd.",
        "invalid-chunk-offset": "Ógild raðbreyting bunka",
        "img-auth-accessdenied": "Aðgangur óheimill",
        "img-auth-nopathinfo": "PATH_INFO vantar.\nBiðlarinn þínn er ekki stilltur til að gefa upp þessar upplýsingar.\nÞær mega vera CGI-byggðar og mega ekki styðja img_auth.\nhttps://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization",
        "doubleredirects": "Tvöfaldar tilvísanir",
        "doubleredirectstext": "Þessi síða er listi yfir skrár sem eru tilvísanir á aðrar tilvísanir.\nHver lína inniheldur tengla á fyrstu og aðra tilvísun auk þeirrar síðu sem seinni tilvísunin beinist að, sem er oftast sú síða sem allar tilvísanirnar eiga að benda á.\n<del>Yfirstrikaðar</del> færslur hafa verið leiðréttar.",
        "double-redirect-fixed-move": "[[$1]] hefur verið færð.\nHún var uppfærð sjálfkrafa og tilvísar núna á [[$2]].",
-       "double-redirect-fixed-maintenance": "Laga sjálfvirkt tvöfalda tilvísun frá [[$1]] til [[$2]] í viðhalds aðgerð.",
+       "double-redirect-fixed-maintenance": "Laga sjálfvirkt tvöfalda endurbeiningu frá [[$1]] til [[$2]] í viðhaldsaðgerð",
        "double-redirect-fixer": "Laga tilvísun",
        "brokenredirects": "Rofnar endurbeiningar",
        "brokenredirectstext": "Eftirfarandi tilvísanir vísa á síður sem ekki eru til:",
        "newimages-hidepatrolled": "Fela yfirfarnar innsendingar",
        "newimages-mediatype": "Skrátegund:",
        "noimages": "Ekkert að sjá.",
+       "gallery-slideshow-toggle": "Víxla smámyndum af/á",
        "ilsubmit": "Leita",
        "bydate": "eftir dagsetningu",
        "sp-newimages-showfrom": "Leita af nýjum skráum frá $2, $1",
index 5e5eb09..88a605b 100644 (file)
        "tog-enotifwatchlistpages": "Inviami una email quando viene modificata una pagina o un file presente tra gli osservati speciali",
        "tog-enotifusertalkpages": "Inviami una email quando viene modificata la mia pagina di discussione",
        "tog-enotifminoredits": "Inviami una email anche per le modifiche minori di pagine e file",
-       "tog-enotifrevealaddr": "Mostra il mio indirizzo nelle e-mail di notifica",
+       "tog-enotifrevealaddr": "Mostra il mio indirizzo nelle email di notifica",
        "tog-shownumberswatching": "Mostra il numero di utenti che hanno la pagina in osservazione",
        "tog-oldsig": "La tua firma attuale:",
        "tog-fancysig": "Gestisci la firma come wikitesto (senza collegamento automatico)",
        "mailmypassword": "Reimposta password",
        "passwordremindertitle": "Servizio Password Reminder di {{SITENAME}}",
        "passwordremindertext": "Qualcuno (probabilmente tu, con indirizzo IP $1) ha richiesto l'invio di una nuova password di accesso a {{SITENAME}} ($4).\nUna password temporanea per l'utente \"$2\" è stata impostata a \"$3\".\nÈ opportuno eseguire un accesso quanto prima e cambiare la password immediatamente. La password temporanea scadrà dopo {{PLURAL:$5|un giorno|$5 giorni}}.\n\nSe non sei stato tu a fare la richiesta, oppure hai ritrovato la password e non desideri più cambiarla, puoi ignorare questo messaggio e continuare a usare la vecchia password.",
-       "noemail": "Nessun indirizzo e-mail registrato per l'utente \"$1\".",
-       "noemailcreate": "È necessario fornire un indirizzo e-mail valido",
+       "noemail": "Nessun indirizzo email registrato per l'utente \"$1\".",
+       "noemailcreate": "È necessario fornire un indirizzo email valido",
        "passwordsent": "Una nuova password è stata inviata all'indirizzo e-mail registrato per l'utente \"$1\".\nPer favore, effettua un accesso non appena la ricevi.",
        "blocked-mailpassword": "Il tuo indirizzo IP è bloccato alla modifica. Per prevenire abusi, non è consentito usare la funzione di recupero password da questo indirizzo IP.",
        "eauthentsent": "Un messaggio email di conferma è stato spedito all'indirizzo indicato.\nPer abilitare l'invio di messaggi email per questo utente è necessario seguire le istruzioni che vi sono indicate, in modo da confermare che si è i legittimi proprietari dell'indirizzo.",
        "acct_creation_throttle_hit": "{{PLURAL:$1|1 registrazione è già stata effettuata|$1 registrazioni sono già state effettuate}} da qualcuno con il tuo stesso indirizzo IP negli ultimi $2, che è il massimo consentito in questo periodo di tempo.\nPerciò, gli utenti che usano questo indirizzo IP non possono più registrarsi per il momento.",
        "emailauthenticated": "L'indirizzo email è stato confermato il $2 alle $3.",
        "emailnotauthenticated": "L'indirizzo di posta elettronica non è stato ancora confermato.\nNon verranno inviati messaggi email per le funzioni elencate di seguito.",
-       "noemailprefs": "Indicare un indirizzo e-mail per attivare queste funzioni.",
+       "noemailprefs": "Indicare un indirizzo email per attivare queste funzioni.",
        "emailconfirmlink": "Conferma il tuo indirizzo email",
        "invalidemailaddress": "L'indirizzo e-mail indicato ha un formato non valido. Inserire un indirizzo valido o svuotare la casella.",
        "cannotchangeemail": "Gli indirizzi e-mail non possono essere modificati in questo wiki.",
        "pt-createaccount": "registrati",
        "pt-userlogout": "esci",
        "php-mail-error-unknown": "Errore sconosciuto nella funzione PHP mail()",
-       "user-mail-no-addy": "Hai cercato di inviare una e-mail senza un indirizzo.",
+       "user-mail-no-addy": "Hai cercato di inviare una email senza un indirizzo.",
        "user-mail-no-body": "Tentato di inviare una e-mail con un testo vuoto o estremamente breve.",
        "changepassword": "Cambia password",
        "resetpass_announce": "Per completare l'accesso, è necessario impostare una nuova password.",
        "changeemail-password": "La password su {{SITENAME}}:",
        "changeemail-submit": "Modifica email",
        "changeemail-throttled": "Sono stati effettuati troppi tentativi di accesso.\nAttendi $1 e riprova in seguito.",
-       "changeemail-nochange": "Per favore inserisci un nuovo indirizzo e-mail.",
+       "changeemail-nochange": "Per favore inserisci un nuovo indirizzo email.",
        "resettokens": "Reimposta token",
        "resettokens-text": "Qui puoi reimpostare le chiavi che permettono l'accesso a determinati dati privati associati alla tua utenza.\n\nDovresti farlo se le hai accidentalmente condivise con qualcuno o se la tua utenza è stato compromessa.",
        "resettokens-no-tokens": "Non ci sono token da reimpostare.",
        "postedit-confirmation-created": "La pagina è stata creata.",
        "postedit-confirmation-restored": "La pagina è stata ripristinata.",
        "postedit-confirmation-saved": "La modifica è stata salvata.",
+       "postedit-confirmation-published": "La tua modifica è stata pubblicata.",
        "edit-already-exists": "Impossibile creare una nuova pagina.\nEsiste già.",
        "defaultmessagetext": "Testo predefinito",
        "content-failed-to-parse": "Impossibile analizzare $2 per il modello $1: $3",
        "defemailsubject": "Messaggio da {{SITENAME}} dall'utente \"$1\"",
        "usermaildisabled": "e-mail utente disabilitata",
        "usermaildisabledtext": "Non è possibile inviare e-mail ad altri utenti su questo wiki",
-       "noemailtitle": "Nessun indirizzo e-mail",
-       "noemailtext": "Questo utente non ha indicato un indirizzo e-mail valido.",
+       "noemailtitle": "Nessun indirizzo email",
+       "noemailtext": "Questo utente non ha indicato un indirizzo email valido.",
        "nowikiemailtext": "Questo utente ha scelto di non ricevere messaggi di posta elettronica dagli altri utenti.",
        "emailnotarget": "Nome utente del destinatario inesistente o non valido.",
        "emailtarget": "Inserisci il nome utente del destinatario",
        "confirmemail_oncreate": "Un codice di conferma è stato spedito all'indirizzo\ndi posta elettronica indicato. Il codice non è necessario per accedere al sito,\nma è necessario fornirlo per poter abilitare tutte le funzioni del sito che fanno\nuso della posta elettronica.",
        "confirmemail_sendfailed": "{{SITENAME}} non può inviare il messaggio e-mail di conferma. Verificare che il proprio indirizzo e-mail non contenga caratteri non validi.\n\nMessaggio di errore del mailer: $1",
        "confirmemail_invalid": "Codice di conferma non valido. Il codice potrebbe essere scaduto.",
-       "confirmemail_needlogin": "È necessario $1 per confermare il proprio indirizzo e-mail.",
+       "confirmemail_needlogin": "È necessario $1 per confermare il proprio indirizzo email.",
        "confirmemail_success": "L'indirizzo e-mail è confermato. Ora è possibile [[Special:UserLogin|eseguire l'accesso]] e fare pieno uso del sito.",
        "confirmemail_loggedin": "L'indirizzo email è stato confermato.",
        "confirmemail_subject": "{{SITENAME}}: richiesta di conferma dell'indirizzo",
        "confirmemail_body": "Qualcuno, probabilmente tu stesso dall'indirizzo IP $1, ha registrato l'account \"$2\" su {{SITENAME}} indicando questo indirizzo e-mail.\n\nPer confermare che l'account ti appartiene veramente e attivare le funzioni relative all'invio di e-mail su {{SITENAME}}, apri il collegamento seguente con il tuo browser:\n\n$3\n\nSe *non* hai registrato tu l'account, segui questo collegamento per annullare la conferma dell'indirizzo e-mail:\n\n$5\n\nQuesto codice di conferma scadrà automaticamente alle $4.",
        "confirmemail_body_changed": "Qualcuno, probabilmente tu stesso dall'indirizzo IP $1,\nha modificato l'indirizzo e-mail dell'account \"$2\" su {{SITENAME}} indicando questo indirizzo e-mail.\n\nPer confermare che l'account ti appartiene veramente e riattivare le funzioni relative all'invio\ndi e-mail su {{SITENAME}}, apri il collegamento seguente con il tuo browser:\n\n$3\n\nSe l'account *non* ti appartiene, segui questo collegamento\nper annullare la conferma dell'indirizzo e-mail:\n\n$5\n\nQuesto codice di conferma scadrà automaticamente alle $4.",
        "confirmemail_body_set": "Qualcuno, probabilmente tu stesso dall'indirizzo IP $1,\nha impostato l'indirizzo email dell'account \"$2\" su {{SITENAME}} indicando questo indirizzo email.\n\nPer confermare che l'account ti appartiene veramente e attivare le funzioni relative all'invio\ndi email su {{SITENAME}}, apri il collegamento seguente con il tuo browser:\n\n$3\n\nSe l'account *non* ti appartiene, segui questo collegamento\nper annullare la conferma dell'indirizzo email:\n\n$5\n\nQuesto codice di conferma scadrà automaticamente alle $4.",
-       "confirmemail_invalidated": "Richiesta di conferma indirizzo e-mail annullata",
+       "confirmemail_invalidated": "Richiesta di conferma indirizzo email annullata",
        "invalidateemail": "Annulla richiesta di conferma e-mail",
        "notificationemail_subject_changed": "L'indirizzo di posta elettronica registrato su {{SITENAME}} è stato modificato",
        "notificationemail_subject_removed": "L'indirizzo di posta elettronica registrato su {{SITENAME}} è stato rimosso",
index a50f7bc..6be5b5d 100644 (file)
        "recentchangeslinked-feed": "関連ページの更新状況",
        "recentchangeslinked-toolbox": "関連ページの更新状況",
        "recentchangeslinked-title": "「$1」と関連する変更",
-       "recentchangeslinked-summary": "ã\81\93ã\82\8cã\81¯æ\8c\87å®\9aã\81\97ã\81\9fã\83\9aã\83¼ã\82¸ã\81\8bã\82\89ã\83ªã\83³ã\82¯ã\81\95ã\82\8cã\81¦ã\81\84ã\82\8b (ã\81¾ã\81\9fã\81¯æ\8c\87å®\9aã\81\97ã\81\9fã\82«ã\83\86ã\82´ã\83ªã\81«å\90«ã\81¾ã\82\8cã\81¦ã\81\84ã\82\8b) ã\83\9aã\83¼ã\82¸ã\81®æ\9c\80è¿\91ã\81®å¤\89æ\9b´ã\81®ä¸\80覧ã\81§ã\81\99ã\80\82\n[[Special:Watchlist|è\87ªå\88\86ã\81®ã\82¦ã\82©ã\83\83ã\83\81ã\83ªã\82¹ã\83\88]]ã\81«ã\81\82ã\82\8bã\83\9aã\83¼ã\82¸は<strong>太字</strong>で表示されます。",
+       "recentchangeslinked-summary": "ã\83\9aã\83¼ã\82¸å\90\8dã\82\92å\85¥å\8a\9bã\81\99ã\82\8bã\81¨ã\80\81ã\83ªã\83³ã\82¯é\96¢ä¿\82 (ã\81\9dã\81®ã\83\9aã\83¼ã\82¸ã\81®ã\83ªã\83³ã\82¯å\85\88ã\82\82ã\81\97ã\81\8fã\81¯ä»\96ã\81®ã\83\9aã\83¼ã\82¸ã\81\8bã\82\89ã\81®ã\83ªã\83³ã\82¯) ã\81®æ\9c\80è¿\91ã\81®å¤\89æ\9b´ã\82\92調ã\81¹ã\82\8bã\81\93ã\81¨ã\81\8cã\81§ã\81\8dã\81¾ã\81\99ã\80\82 (ä¸\8bä½\8dã\82«ã\83\86ã\82´ã\83ªã\82\92å\8f\82ç\85§ã\81\99ã\82\8bã\81«ã\81¯ã\80\81ã\82«ã\83\86ã\82´ã\83ª:ã\82«ã\83\86ã\82´ã\83ªå\90\8d (Category:Name of category) ã\82\92å\85¥å\8a\9b\80\82[[Special:Watchlist|è\87ªå\88\86ã\81®ã\82¦ã\82©ã\83\83ã\83\81ã\83ªã\82¹ã\83\88]]ã\81«ã\81\82ã\82\8bã\83\9aã\83¼ã\82¸ã\81®å¤\89æ\9b´は<strong>太字</strong>で表示されます。",
        "recentchangeslinked-page": "ページ名:",
        "recentchangeslinked-to": "このページへのリンク元での変更の表示に切り替え",
        "recentchanges-page-added-to-category": "[[:$1]]をカテゴリに追加",
        "uploaded-script-svg": "アップロードされたSVGファイルにスクリプト可能な要素「$1」が見つかりました。",
        "uploaded-hostile-svg": "アップロードされたSVGファイルのスタイル要素に安全ではないCSSが見つかりました。",
        "uploaded-event-handler-on-svg": "イベントハンドラをセットする属性 <code>$1=\"$2\"</code> は、SVGファイルを許可されていません。",
-       "uploaded-href-attribute-svg": "SVG ファイルの href 属性が http:// または https:// のターゲットのみにリンクする <code>&lt;$1 $2=\"$3\"&gt;</code> が見つかりました。",
+       "uploaded-href-attribute-svg": "<a> 要素では、データ (埋め込みファイル)、http://、https://、フラグメント (#、ID 属性など) のターゲットにのみリンクできます。<image> などの他の要素では、データとフラグメントのみが使用できます。SVG をエクスポートする際は画像を埋め込むようにしてください。アップロードされたファイルには <code>&lt;$1 $2=\"$3\"&gt;</code> が含まれています。",
        "uploaded-href-unsafe-target-svg": "アップロードされた SVG ファイルの、安全ではないデータ URI にターゲット <code>&lt;$1 $2=\"$3\"&gt;</code> の href が見つかりました。",
        "uploaded-animate-svg": "アップロードされたSVGファイルに、「from」属性 <code>&lt;$1 $2=\"$3\"&gt;</code> を使用した、href を変更させる可能性がある「animate」タグが見つかりました。",
        "uploaded-setting-event-handler-svg": "アップロードされたSVGファイルに、ブロックされているイベントハンドラ属性が設定された <code>&lt;$1 $2=\"$3\"&gt;</code> が見つかりました。",
        "lockmanager-fail-closelock": "「$1」用のロックファイルを閉じることができませんでした。",
        "lockmanager-fail-deletelock": "「$1」用のロックファイルを削除できませんでした。",
        "lockmanager-fail-acquirelock": "「$1」用のロックを取得できませんでした。",
-       "lockmanager-fail-openlock": "「$1」用のロックファイルを開くことができませんでした。",
+       "lockmanager-fail-openlock": "「$1」用のロックファイルを開くことができませんでした。アップロードディレクトリが正しく設定されており、ウェブサーバーによる書き込みの権限が許可されていることを確認してください。詳しくは https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:$wgUploadDirectory をご覧ください。",
        "lockmanager-fail-releaselock": "「$1」用のロックを解放できませんでした。",
        "lockmanager-fail-db-bucket": "バケツ $1 で十分な数のロックデータベースに接触できませんでした。",
        "lockmanager-fail-db-release": "データベース $1 上のロックを解放できませんでした。",
        "uploadstash-refresh": "ファイルの一覧を更新",
        "uploadstash-thumbnail": "サムネイルを表示",
        "uploadstash-exception": "スタッシュにアップロードできませんでした ($1): \"$2\"",
+       "uploadstash-bad-path": "パスが存在しません。",
+       "uploadstash-bad-path-invalid": "パスが無効です。",
+       "uploadstash-bad-path-unknown-type": "「$1」は不明なタイプです。",
+       "uploadstash-bad-path-unrecognized-thumb-name": "サムネイル名が認識できません。",
+       "uploadstash-bad-path-no-handler": "ファイル $2 の MIME $1 に対するハンドラが見つかりません。",
+       "uploadstash-bad-path-bad-format": "キー「$1」は適切な形式ではありません。",
        "uploadstash-zero-length": "ファイルのサイズがゼロです。",
        "invalid-chunk-offset": "無効なチャンクオフセット",
        "img-auth-accessdenied": "アクセスが拒否されました",
        "import-mapping-namespace": "名前空間へインポート:",
        "import-mapping-subpage": "次のページの下位ページとしてインポート:",
        "import-upload-filename": "ファイル名:",
+       "import-upload-username-prefix": "インターウィキ接頭辞:",
        "import-comment": "コメント:",
        "importtext": "元のウィキで[[Special:Export|書き出し機能]]を使用してファイルに書き出してください。\nそれをコンピューターに保存した後、こちらへアップロードしてください。",
        "importstart": "ページを取り込み中...",
        "pageinfo-category-subcats": "下位カテゴリ数",
        "pageinfo-category-files": "ファイル数",
        "pageinfo-user-id": "利用者 ID",
+       "pageinfo-file-hash": "ハッシュ値",
        "markaspatrolleddiff": "巡回済みにする",
        "markaspatrolledtext": "このページを巡回済みにする",
        "markaspatrolledtext-file": "このファイルの版を巡回済みにする",
        "limitreport-expensivefunctioncount": "高負荷パーサー関数の数",
        "limitreport-expensivefunctioncount-value": "$1/$2",
        "expandtemplates": "テンプレートを展開",
-       "expand_templates_intro": "この特別ページは、入力したテキストに含まれるすべてのテンプレートを再帰的に展開します。\n<code><nowiki>{{</nowiki>#language:…}}</code> のようなパーサー関数や、\n<code><nowiki>{{</nowiki>CURRENTDAY}}</code> のような変数も展開します。\nつまり、二重中括弧で囲まれたものほぼすべてを展開します。",
+       "expand_templates_intro": "ã\81\93ã\81®ç\89¹å\88¥ã\83\9aã\83¼ã\82¸ã\81¯ã\80\81å\85¥å\8a\9bã\81\97ã\81\9fã\82¦ã\82£ã\82­ã\83\86ã\82­ã\82¹ã\83\88ã\81«å\90«ã\81¾ã\82\8cã\82\8bã\81\99ã\81¹ã\81¦ã\81®ã\83\86ã\83³ã\83\97ã\83¬ã\83¼ã\83\88ã\82\92å\86\8d帰ç\9a\84ã\81«å±\95é\96\8bã\81\97ã\81¾ã\81\99ã\80\82\n<code><nowiki>{{</nowiki>#language:â\80¦}}</code> ã\81®ã\82\88ã\81\86ã\81ªã\83\91ã\83¼ã\82µã\83¼é\96¢æ\95°ã\82\84ã\80\81\n<code><nowiki>{{</nowiki>CURRENTDAY}}</code> ã\81®ã\82\88ã\81\86ã\81ªå¤\89æ\95°ã\82\82å±\95é\96\8bã\81\97ã\81¾ã\81\99ã\80\82\nã\81¤ã\81¾ã\82\8aã\80\81äº\8cé\87\8d中æ\8b¬å¼§ã\81§å\9b²ã\81¾ã\82\8cã\81\9fã\82\82ã\81®ã\81»ã\81¼ã\81\99ã\81¹ã\81¦ã\82\92å±\95é\96\8bã\81\97ã\81¾ã\81\99ã\80\82",
        "expand_templates_title": "{{FULLPAGENAME}} などで使用するページ名:",
-       "expand_templates_input": "展開するテキスト:",
+       "expand_templates_input": "å±\95é\96\8bã\81\99ã\82\8bã\82¦ã\82£ã\82­ã\83\86ã\82­ã\82¹ã\83\88:",
        "expand_templates_output": "展開結果",
        "expand_templates_xml_output": "XML 出力",
        "expand_templates_html_output": "出力される HTML ソース",
        "expand_templates_preview": "プレビュー",
        "expand_templates_preview_fail_html": "<em>{{SITENAME}} ではHTMLソースが有効になっており、セッションデータの損失が生じているので、JavaScript の攻撃に対する予防措置としてプレビューは表示されません。</em>\n\n<strong>これが合法的なプレビューの試みである場合には、もう一度試してください。</strong>\nそれでも動作しない場合は、[[Special:UserLogout|ログアウト]]してからログインし直し、現在使用しているブラウザでこのサイトからのクッキーが許可されていることを確認してください。",
        "expand_templates_preview_fail_html_anon": "<em>{{SITENAME}} ではHTMLソースが有効になっており、ログインしていないため、JavaScript の攻撃に対する予防措置としてプレビューは表示されません。</em>\n\n<strong>これが合法的なプレビューの試みである場合には、[[Special:UserLogin|ログイン]]してもう一度試してください。</strong>",
-       "expand_templates_input_missing": "文章を入力してください。",
+       "expand_templates_input_missing": "ウィキテキストを入力してください。",
        "pagelanguage": "ページ言語の変更",
        "pagelang-name": "ページ",
        "pagelang-language": "言語",
        "restrictionsfield-label": "許可する IP の範囲:",
        "restrictionsfield-help": "一行につき、単一の IP アドレス、もしくは CIDR による範囲。全帯域からの接続を許可する場合: <pre>0.0.0.0/0\n::/0</pre>",
        "edit-error-short": "エラー: $1",
-       "edit-error-long": "エラー\n\n\n\n$1",
+       "edit-error-long": "エラー:\n\n\n\n$1",
        "revid": "版 $1",
        "pageid": "ページID $1",
        "rawhtml-notallowed": "&lt;html&gt;タグは通常ページ以外では使用できません。",
index 500e25e..9bb875a 100644 (file)
        "site-rss-feed": "$1 ''RSS Feed''",
        "site-atom-feed": "Lebon atom $1",
        "page-rss-feed": "\"$1\" ''RSS Feed''",
-       "page-atom-feed": "\"$1\" ''Atom Feed''",
+       "page-atom-feed": "Pakan atom \"$1\"",
        "red-link-title": "$1 (kaca ora ana)",
        "sort-descending": "Urutaké medhun",
        "sort-ascending": "Urutaké munggah",
        "nosuchaction": "Ora ana tumindak mangkono",
        "nosuchactiontext": "Tumindak sing dikarepaké déning URL ora trep.\nPanjenengan bokmanawa salah ngetik URL-é, utawa salah nurut pranala.\nIki bokmanawa uga nuduhaké yèn ana ama ing piranti alus sing dianggo déning {{SITENAME}}.",
        "nosuchspecialpage": "Ora ana kaca mirunggan mangkono",
-       "nospecialpagetext": "Panjenengan nyuwun kaca astaméwa sing ora sah. Daftar kaca astaméwa sing sah bisa dipirsani ing [[Special:SpecialPages|daftar kaca astaméwa]].",
+       "nospecialpagetext": "<strong>Panjenengan nyuwun kaca mirunggan sing ora sah.</strong>\n\nPratélan kaca mirunggan sing sah bisa tinemu ing [[Special:SpecialPages|{{int:specialpages}}]].",
        "error": "Masalah",
        "databaseerror": "Masalah sasana dhata",
        "databaseerror-text": "Ana kerusakan ing basis data (query error).\n\nMungkin ana masalah ing software-e.",
        "showingresults": "Ing ngisor iki dituduhaké {{PLURAL:$1|'''1''' kasil|'''$1''' kasil}}, wiwitané saking #<strong>$2</strong>.",
        "showingresultsinrange": "Nuduhaké nganti {{PLURAL:$1|<strong>1</strong> kasil|<strong>$1</strong> kasil}} sajeroning penthangan #<strong>$2</strong> tekan #<strong>$3</strong>.",
        "search-showingresults": "{{PLURAL:$4|Asil <strong>$1</strong> saka <strong>$3</strong>|Asil <strong>$1 – $2</strong> saka <strong>$3</strong>}}",
-       "search-nonefound": "Ora ana kasil sing cocog karo pitakonan (''query'').",
+       "search-nonefound": "Ora ana kasil sing mathuk karo pitakoné.",
        "search-nonefound-thiswiki": "Ora ana kasil sing jumbuh karo panjalukan ing situs iki.",
        "powersearch-legend": "Panggolèkan sabanjuré (''advance search'')",
        "powersearch-ns": "Golèk ing mandala aran:",
        "recentchanges": "Owahan-owahan anyar",
        "recentchanges-legend": "Opsi owah-owahan anyar",
        "recentchanges-summary": "Lacak owah-owahan anyar wiki iki ing kaca iki.",
-       "recentchanges-noresult": "Ora ana pangowahan ing periode sing diwiwiti kriteria iki.",
+       "recentchanges-noresult": "Ora ana owahan ing kalamangsa sing mathuk karo pranatan iki.",
        "recentchanges-feed-description": "Urutna owah-owahan anyar ing wiki ing ''feed'' iki.",
        "recentchanges-label-newpage": "Besutan iki nggawé kaca anyar",
        "recentchanges-label-minor": "Iki besutan cilik",
        "recentchangeslinked-title": "Owah-owahan sing magepokan \"$1\"",
        "recentchangeslinked-summary": "Iki pratélaning owah-owahan sing mentas digawé tumrap ing kaca-kaca sing nggayut sawijining kaca (utawa kaca-kaca anggotaning sawijining kategori).\nKaca ing [[Special:Watchlist|pawawangané panjenegan]] <strong>dikandeli</strong>.",
        "recentchangeslinked-page": "Jeneng kaca:",
-       "recentchangeslinked-to": "Nuduhaké owah-owahan menyang kaca sing disambung menyang kaca-kaca iki",
+       "recentchangeslinked-to": "Nuduhaké owah-owahan kaca-kaca sing nggayut kaca iki",
        "recentchanges-page-added-to-category": "[[:$1]] ditambahaké ing kategori",
        "recentchanges-page-added-to-category-bundled": "[[:$1]] ditambahaké ing kategori, [[Special:WhatLinksHere/$1|kaca iki kalebu ing njeroné kaca liyané]]",
        "recentchanges-page-removed-from-category": "[[:$1]] dibusak saka kategori",
        "mywatchlist": "Pawawangan",
        "watchlistfor2": "Kanggo $1 $2",
        "nowatchlist": "Ora ana apa-apa ing pawawanganing sampéyan.",
-       "watchlistanontext": "Mangga $1 kanggo mirsani utawa nyunting daftar pangawasan panjenengan.",
+       "watchlistanontext": "Sumangga mlebu log saperlu ndeleng utawa mbesut barang ing pawawangané panjenengan.",
        "watchnologin": "Durung mlebu log",
        "addwatch": "Tambah nèng daptar pangawasan",
        "addedwatchtext": "Kaca \"[[:$1]]\" wis ditambahaké menyang [[Special:Watchlist|daftar pangawasan]].\nOwah-owahan sing dumadi ing tembé ing kaca iku lan kaca dhiskusi sing kagandhèng, bakal dipacak ing kéné.",
        "import-upload": "Ngunggahaké data XML",
        "import-token-mismatch": "Kélangan data sèsi. Mangga dijajal manèh.",
        "import-invalid-interwiki": "Ora bisa ngimport saka wiki sing kapilih.",
-       "import-error-edit": "Kaca \"$1\" ora diimpor amarga Sampéyan ora dililakaké nyunting kuwi.",
+       "import-error-edit": "Kaca \"$1\" ora kaimpor amarga panjenengan ora kawogan mbesut iku.",
        "import-error-create": "Kaca \"$1\" ora diimpor amarga Sampéyan ora dililakaké nggawé kuwi.",
        "import-error-interwiki": "Kaca \"$1\" ora diimpor amarga jenengé dicadhangaké kango pranala njaba (interwiki).",
        "import-error-special": "Kaca \"$1\" ora diimpor amarga darbèké mandala aran mirunggan sing ora nglilakaké anané kaca.",
        "tooltip-t-whatlinkshere": "Pratélané kabèh kaca wiki sing nggayut mréné",
        "tooltip-t-recentchangeslinked": "Owah-owahan anyar saka kaca-kaca sing nggayut kaca iki",
        "tooltip-feed-rss": "''RSS feed'' kanggo kaca iki",
-       "tooltip-feed-atom": "''Atom feed'' kanggo kaca iki",
+       "tooltip-feed-atom": "Pakan atom tumrap kaca iki",
        "tooltip-t-contributions": "Pratélaning pisumbanging {{GENDER:$1|panganggo iki}}",
        "tooltip-t-emailuser": "Kirimi layang-èl nyang {{GENDER:$1|panganggo iki}}",
        "tooltip-t-info": "Katerangan liyané ngenani kaca iki",
        "exif-referenceblackwhite": "Wiji réferènsi pasangan ireng putih",
        "exif-datetime": "Tanggal lan wektu owahé barkas",
        "exif-imagedescription": "Sesirah gambar",
-       "exif-make": "Produsèn kamera",
+       "exif-make": "Prodhusèn kodhak",
        "exif-model": "Modhèl kaméra",
        "exif-software": "Piranti alus sing dianggo",
        "exif-artist": "Prodhusèn",
        "scarytranscludetoolong": "[URL-é kedawan]",
        "deletedwhileediting": "<strong>Pélik:</strong> Kaca iki wis dibusak kawit pisanan panjenengan mbesut!",
        "confirmrecreate": "Panganggo [[User:$1|$1]] ([[User talk:$1|Wicara]]) wis mbusak kaca iki nalika panjenengan miwiti panyuntingan mawa alesan:\n: ''$2''\nMangga didhedhes (dikonfirmasi) menawa panjenengan kersa nggawé ulang kaca iki.",
-       "confirmrecreate-noreason": "Panganggo [[User:$1|$1]] ([[User talk:$1|wicara]]) mbusak kaca iki sakbaré Sampéyan lekas nyunting. Mangga pesthèkaké yèn Sampéyan pancen pingin tenan nggawé manèh kaca iki.",
+       "confirmrecreate-noreason": "Panganggo [[User:$1|$1]] ([[User talk:$1|rembug]]) {{GENDER:$1|mbusak}} kaca iki sawisé panjenengan wiwit mbesut. Sumangga konfirmasi yèn panjenengan temen arep nggawé manèh kaca iki.",
        "recreate": "Gawé ulang",
        "confirm-purge-title": "Buwang kaca iki",
        "confirm_purge_button": "OK",
        "redirect": "Lih-lihan miturut barkas, panganggo, kaca, owahan, utawa cathetan",
        "redirect-summary": "Kaca mirunggan iki ngener ing sawijining barkas (jeneng gambar diwenehi), kaca (ID revisi utama ID kaca diwenehi), utawa kaca panganggo (ID panganggo diwenehi). Cara nganggo: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/page/64308]], [[{{#Special:Redirect}}/revision/328429]], or [[{{#Special:Redirect}}/user/101]].",
        "redirect-submit": "Menyang",
-       "redirect-lookup": "Golek:",
+       "redirect-lookup": "Luru:",
        "redirect-value": "Aji:",
        "redirect-user": "ID panganggo",
        "redirect-page": "ID kaca",
        "logentry-patrol-patrol": "$1 {{GENDER:$2|nengeri}} révisi $4 saka kaca $3 sing diawasi",
        "logentry-patrol-patrol-auto": "$1 otomatis {{GENDER:$2|nandhani}} benahan $4 saka kaca $3 kaawasake",
        "logentry-newusers-newusers": "Akun panganggo $1 {{GENDER:$2|digawé}}",
-       "logentry-newusers-create": "Akun panganggo $1 {{GENDER:$2|digawé}}",
+       "logentry-newusers-create": "Akun panganggo $1 wis {{GENDER:$2|kagawé}}",
        "logentry-newusers-create2": "Akun panganggo $3 {{GENDER:$2|digawé}} déning $1",
        "logentry-newusers-byemail": "Akun panganggo $3 {{GENDER:$2|digawé}} déning $1 lan tembung wadi dikirim lumantar layang-èl",
        "logentry-newusers-autocreate": "Akun panganggo $1 otomatis {{GENDER:$2|digawé}}",
index 9de362c..3b5714e 100644 (file)
        "postedit-confirmation-created": "D'Säit gouf ugeluecht.",
        "postedit-confirmation-restored": "D'Säit gouf restauréiert.",
        "postedit-confirmation-saved": "Är Ännerung gouf gespäichert.",
+       "postedit-confirmation-published": "Är Ännerung gouf verëffentlecht.",
        "edit-already-exists": "Déi nei Säit konnt net ugeluecht ginn, well et se scho gëtt.",
        "defaultmessagetext": "Standardtext",
        "content-failed-to-parse": "Den $2-Inhalt fir den $1-Modell konnt net geparst ginn: $3",
index 87fd05c..171b989 100644 (file)
        "mediawikipage": "Vizualizza o messaggio",
        "templatepage": "Vizualizza o modello",
        "viewhelppage": "Móstra a pagina d'agiutto",
-       "categorypage": "Veddi a pagina da categorîa",
+       "categorypage": "Amia a pagina da categoria",
        "viewtalkpage": "Amîa a pagina de discusción",
        "otherlanguages": "In âtre léngoe",
        "redirectedfrom": "(Rendirissou da $1)",
        "perfcached": "I dæti chì apreuvo son estræti da 'na coppia ''cache'' do database, e porrieivan no ese agiornæ. Un mascimo de {{PLURAL:$1|un risultou o l'è disponibbile|$1 risultæ son disponibbili}} into cache.",
        "perfcachedts": "I dæti chì apreuvo son estræti da una coppia ''cache'' do database, o quæ urtimo agiornamento o remonta a-o $1. Un mascimo de {{PLURAL:$4|un risultou o l'è disponibbile|$4 risultæ son disponibili}} into cache.",
        "querypage-no-updates": "I agiornamenti da pagina son temporaniamente sospeixi. I dæti in lé contegnui no saian aggiornæ.",
-       "viewsource": "Veddi a fonte",
+       "viewsource": "Vixualizza wikitesto",
        "viewsource-title": "Visualizza sorgente de $1",
        "actionthrottled": "Assion ritardâ",
        "actionthrottledtext": "Comme mesua de segueça contra o spam, l'esecuçion de çerte açioin a l'è limitâ a un nummero mascimo de votte inte 'n determinòu periodo de tempo, limmite che ti t'hæ superòu. Se prega de riprovâ tra quarche menuto.",
        "publishpage": "Pubbrica a paggina",
        "publishchanges": "Pubbrica e modiffiche",
        "preview": "Anteprìmma",
-       "showpreview": "Veddi l'anteprimma",
-       "showdiff": "Veddi i cangiamenti",
+       "showpreview": "Amia l'anteprimma",
+       "showdiff": "Mostra modiffiche",
        "blankarticle": "<strong>Atençión:</strong> a pàgina che ti çerchi a l'é vêua.\nClicando tórna in sce \"$1\", a pàgina a saiâ creâ sénsa contegnûi.",
        "anoneditwarning": "<strong>Attension:</strong> No t'ê introu. Se ti fæ di cangi o teu adresso IP o saiâ vixibile pubbricamente. Se <strong>[$1 ti intri]</strong> ò <strong>[$2 ti crei un'utensa]</strong>, e teu modifiche saian attribuie a-o teu nomme utente, insemme a di atri benefiççi.",
        "anonpreviewwarning": "No t'hæ fæto l'accesso. Se ti sarvi inta stoia da paggina ghe saiâ solo o to adresso IP",
        "undo-summary-username-hidden": "Anullou a modiffica $1 de un utente ascoso",
        "cantcreateaccount-text": "A registrassion da questo addresso IP (<b>$1</b>) a l'è stæta bloccâ da [[User:$3|$3]].\n\nA raxon dæta a l'è ''$2''",
        "cantcreateaccount-range-text": "A registraçion da di addressi IP inte l'intervallo <strong>$1</strong>, ch'o  l'includde o teu (<strong>$4</strong>), a l'è stæta bloccâ da [[User:$3|$3]].\n\nA raxon dæta da $3 a l'è <em>$2</em>",
-       "viewpagelogs": "Veddi i log relativi a 'sta paggina.",
+       "viewpagelogs": "Amia i log relativi a 'sta paggina.",
        "nohistory": "A stoia de verscioin de sta paggina a no gh'è.",
        "currentrev": "Verscion attuâle",
        "currentrev-asof": "Ùrtima revixón do $1",
        "recentchanges-label-unpatrolled": "Sto cangiaménto o no l'é stæto ancón verificòu",
        "recentchanges-label-plusminus": "Variassion da paggina in nummero de byte",
        "recentchanges-legend-heading": "<strong>Legenda:</strong>",
-       "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (veddi e [[Special:NewPages|neuve paggine]])",
+       "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (amia e [[Special:NewPages|noeuve paggine]])",
        "recentchanges-submit": "Fanni vedde",
        "rcfilters-activefilters": "Filtri attivi",
        "rcfilters-advancedfilters": "Filtri avançæ",
        "recentchanges-page-removed-from-category-bundled": "[[:$1]] rimossa da-a categoria, [[Special:WhatLinksHere/$1|questa pagina a l'è inclusa a l'interno di atre pagine]]",
        "autochange-username": "Modiffica aotomattica MediaWiki",
        "upload": "Carrega 'n file",
-       "uploadbtn": "Carrega 'n file",
+       "uploadbtn": "Carreghilo",
        "reuploaddesc": "Torna a-o moddulo pe-o caregamento.",
        "upload-tryagain": "Invia a descrission do file modificou",
        "uploadnologin": "No t'ê introu",
        "upload-curl-error6-text": "Imposcibile razonze a URL specificâ. Verifica che a URL a sæ scrita correttamente e che o scito in question o sæ attivo.",
        "upload-curl-error28": "Tempo descheito pe l'upload",
        "upload-curl-error28-text": "O scito remoto o l'ha impiegou troppo tempo a risponde. Verifica che o sito o sæ attivo, attendi quarche menuto e proeuva torna, se mai inte 'n momento con meno traffego.",
-       "license": "Licénsa:",
-       "license-header": "Licénsa",
+       "license": "Liçençia:",
+       "license-header": "Liçençia",
        "nolicense": "Nisciûnn-a liçensa indicâa",
        "licenses-edit": "Modiffica opçioin de liçença",
        "license-nopreview": "(Anteprimma non disponibbile)",
        "modifiedarticleprotection-comment": "{{GENDER:$2|Cangiou o livello de proteçion}} pe \"[[$1]]\"",
        "unprotectedarticle-comment": "{{GENDER:$2|Rimosso a proteçion}} da \"[[$1]]\"",
        "protect-title": "Cangio do livello de proteçion pe \"$1\"",
-       "protect-title-notallowed": "Veddi o livello de proteçion de \" $1 \"",
+       "protect-title-notallowed": "Amia o livello de proteçion de \" $1 \"",
        "prot_1movedto2": "[[$1]] mesciòu a [[$2]]",
        "protect-badnamespace-title": "Namespace non protezibbile",
        "protect-badnamespace-text": "E pagine de questo namespace no poeuan ese protezue.",
        "restriction-level-autoconfirmed": "semi-protezua",
        "restriction-level-all": "Tutti i livelli",
        "undelete": "Amîa e paggine scassæ",
-       "undeletepage": "Veddi e recuppera e pagine scançellæ",
+       "undeletepage": "Amia e recuppera e paggine scassæ",
        "undeletepagetitle": "'''Quanto segue o l'è composto da de revixoin scassæ de [[:$1|$1]]'''.",
-       "viewdeletedpage": "Veddi e paggine scassæ",
+       "viewdeletedpage": "Amia e paggine scassæ",
        "undeletepagetext": "{{PLURAL:$1|A seguente pagina a l'è stæta scassâ, ma a l'è ancon in archivio e pertanto a poeu ese recuperâ|E seguente pagine son stæte scassæ, ma son ancon in archivio e pertanto poeuan ese recuperæ}}. L'archivio o poeu ese vuou periodicamente.",
        "undelete-fieldset-title": "Ripristina revixoin",
        "undeleteextrahelp": "Pe recuperâ l'intrega cronologia da pagina, lascia tutte e caselle deseleçionæ e fanni clic insce '''''{{int:undeletebtn}}'''''.\nPe effettuâ un ripristino selettivo, seleçion-a e caselle corrispondente a-e verscioin da ripristinâ e fanni clic insce '''''{{int:undeletebtn}}'''''.",
        "tooltip-t-permalink": "Colegaménto fisso a sta revixión da pàgina",
        "tooltip-ca-nstab-main": "Véddi a vôxe",
        "tooltip-ca-nstab-user": "Amîa a paggina utente",
-       "tooltip-ca-nstab-media": "Veddi a paggina do file murtimediâ",
+       "tooltip-ca-nstab-media": "Amia a paggina do file murtimediâ",
        "tooltip-ca-nstab-special": "Sta chi l'è 'na pàgina speciâle e a no peu êse cangiâ",
        "tooltip-ca-nstab-project": "Véddi a pàgina de servìçio",
-       "tooltip-ca-nstab-image": "Veddi a paggina do file",
-       "tooltip-ca-nstab-mediawiki": "Veddi o messaggio de scistema",
-       "tooltip-ca-nstab-template": "Veddi o template",
-       "tooltip-ca-nstab-help": "Veddi a paggina d'agiûtto",
+       "tooltip-ca-nstab-image": "Amia a paggina do file",
+       "tooltip-ca-nstab-mediawiki": "Amia o messaggio de scistema",
+       "tooltip-ca-nstab-template": "Amia o template",
+       "tooltip-ca-nstab-help": "Amia a paggina d'agiutto",
        "tooltip-ca-nstab-category": "Véddi a pàgina da categorîa",
        "tooltip-minoredit": "Marchilo comme cangiaménto minô",
        "tooltip-save": "Sarva i cangiaménti",
        "watchlistedit-clear-removed": "L'è stæto eliminou {{PLURAL:$1|una paggina|$1 paggine}}:",
        "watchlistedit-too-many": "Gh'è troppe paggine da visualizzâ chì.",
        "watchlisttools-clear": "Svoeua a lista sott'öservaçion",
-       "watchlisttools-view": "Veddi e modiffiche pertinenti",
-       "watchlisttools-edit": "Veddi e modiffica a lista",
+       "watchlisttools-view": "amia e modiffiche pertinenti",
+       "watchlisttools-edit": "Amia e modiffica a lista",
        "watchlisttools-raw": "Modiffica a lista in formato testo",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|discuscioin]])",
        "timezone-local": "Locale",
index bd14009..1c96495 100644 (file)
        "postedit-confirmation-created": "Lapa ir izveidota.",
        "postedit-confirmation-restored": "Lapa ir atjaunota.",
        "postedit-confirmation-saved": "Jūsu labojums tika saglabāts.",
+       "postedit-confirmation-published": "Tavs labojums tika publicēts.",
        "edit-already-exists": "Nevar izveidot jaunu lapu.\nTā jau eksistē.",
        "defaultmessagetext": "Noklusētais ziņojuma teksts",
        "invalid-content-data": "Nederīgi satura dati",
        "right-writeapi": "Rakstīšanas API lietošana",
        "right-delete": "Dzēst lapas",
        "right-bigdelete": "Dzēst lapas ar lielām hronoloģijām",
+       "right-deletelogentry": "Dzēst un atjaunot konkrētus žurnāla ierakstus",
        "right-deleterevision": "Dzēst un atjaunot lapu noteiktas versijas",
        "right-deletedhistory": "Skatīt izdzēstos hronoloģijas ierakstus, bez tiem piesaistītā teksta",
        "right-deletedtext": "Apskatīt izdzēsto tekstu un izmaiņas starp izdzēstām versijām",
        "doubleredirects": "Divkāršas pāradresācijas lapas",
        "doubleredirectstext": "Šajā lapā ir uzskaitītas pāradresācijas lapas, kuras pāradresē uz citām pāradresācijas lapām.\nKatrā rindiņā ir saites uz pirmo un otro pāradresācijas lapu, kā arī pirmā rindiņa no otrās pāradresācijas lapas teksta, kas parasti ir faktiskā \"gala\" lapa, uz kuru vajadzētu būt saitei pirmajā lapā.\n<del>Nosvītrotie</del> ieraksti jau ir tikuši salaboti.",
        "double-redirect-fixed-move": "[[$1]] bija ticis pārvietots, tas tagad ir pāradresācija uz [[$2]]",
-       "double-redirect-fixed-maintenance": "Labota dubultā pāradresācija no [[$1]] uz [[$2]].",
+       "double-redirect-fixed-maintenance": "Automātiski ar apkopes procesu labota dubultā pāradresācija no [[$1]] uz [[$2]].",
        "double-redirect-fixer": "Pāradresāciju labotājs",
        "brokenredirects": "Kļūdainas pāradresācijas",
        "brokenredirectstext": "Šīs ir pāradresācijas lapas uz neesošām lapām:",
index 57ba371..342a1af 100644 (file)
        "postedit-confirmation-created": "Страницата е создадена.",
        "postedit-confirmation-restored": "Страницата е повратена.",
        "postedit-confirmation-saved": "Вашето уредување е зачувано.",
+       "postedit-confirmation-published": "Вашето уредување е објавено.",
        "edit-already-exists": "Не може да се создаде нова страница.\nИстата веќе постои.",
        "defaultmessagetext": "Текст на пораката по основно",
        "content-failed-to-parse": "Не успеав да ја предадам содржината од типот $2 за моделот $1: $3",
        "lockmanager-fail-closelock": "Не можев да ја затворам податотеката-катанец за „$1“.",
        "lockmanager-fail-deletelock": "Не можев да ја избришам податотеката-катанец за „$1“.",
        "lockmanager-fail-acquirelock": "Не можев да добијам катанец за „$1“.",
-       "lockmanager-fail-openlock": "Ð\9dе Ð¼Ð¾Ð¶ÐµÐ² Ð´Ð° Ñ\98а Ð¾Ñ\82воÑ\80ам Ð¿Ð¾Ð´Ð°Ñ\82оÑ\82екаÑ\82а-каÑ\82анеÑ\86 Ð·Ð° â\80\9e$1â\80\9c.",
+       "lockmanager-fail-openlock": "Ð\9dе Ð¼Ð¾Ð¶ÐµÐ² Ð´Ð° Ñ\98а Ð¾Ñ\82воÑ\80ам Ð·Ð°ÐºÐ»Ñ\83Ñ\87наÑ\82а Ð¿Ð¾Ð´Ð°Ñ\82оÑ\82ека â\80\9e$1â\80\9c. Ð\9fÑ\80овеÑ\80еÑ\82е Ð´Ð°Ð»Ð¸ Ð¿Ð°Ð¿ÐºÐ°Ñ\82а Ð·Ð° Ð¿Ð¾Ð´Ð¸Ð³Ð°Ñ\9aа Ðµ Ð¿Ñ\80авилно Ð¿Ð¾Ñ\81Ñ\82авена Ð¸ Ð²Ð°Ñ\88иоÑ\82 Ñ\81емÑ\80ежен Ð¾Ð¿Ñ\81лÑ\83жÑ\83ваÑ\87 Ð¸Ð¼Ð° Ð´Ð¾Ð·Ð²Ð¾Ð»Ð° Ð´Ð° Ñ\81нима Ð²Ð¾ Ð½ÐµÐ°. Ð\9fовеÑ\9cе Ð¸Ð½Ñ\84оÑ\80маÑ\86ии Ñ\9cÑ\87е Ð½Ð°Ñ\98деÑ\82е Ð½Ð° https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:$wgUploadDirectory.",
        "lockmanager-fail-releaselock": "Не можев да го ослободам катанецот за „$1“.",
        "lockmanager-fail-db-bucket": "Не можев да контактирам доволно податотеки-катанци во садот $1",
        "lockmanager-fail-db-release": "Не можев да ги ослободам катанците на базата $1",
index 574d312..8fa9c61 100644 (file)
        "postedit-confirmation-created": "De pagina is aangemaakt.",
        "postedit-confirmation-restored": "De pagina is hersteld.",
        "postedit-confirmation-saved": "Uw bewerking is opgeslagen.",
+       "postedit-confirmation-published": "Uw bewerking is opgeslagen.",
        "edit-already-exists": "De pagina is niet aangemaakt.\nDeze bestaat al.",
        "defaultmessagetext": "Standaardinhoud",
        "content-failed-to-parse": "Het was niet mogelijk de inhoud van het MIME-type $2 voor het model $1 te verwerken: $3.",
index a3f3ce6..3820aa8 100644 (file)
        "postedit-confirmation-created": "A página foi criada.",
        "postedit-confirmation-restored": "Esta página foi restaurada.",
        "postedit-confirmation-saved": "Sua edição foi salva",
+       "postedit-confirmation-published": "A sua edição foi publicada.",
        "edit-already-exists": "Não foi possível criar uma nova página.\nEla já existia.",
        "defaultmessagetext": "Texto da mensagem padrão",
        "content-failed-to-parse": "Falha ao analisar o conteúdo $2 para o modelo $1: $3",
index a4e13c6..15d950f 100644 (file)
        "emailconfirmlink": "Confirme o seu endereço de correio eletrónico",
        "invalidemailaddress": "O endereço de correio eletrónico não pode ser aceite porque parece ter um formato inválido.\nIntroduza um endereço formatado corretamente ou deixe o campo vazio.",
        "cannotchangeemail": "Os endereços de correio eletrónico das contas não podem ser alterados nesta wiki.",
-       "emaildisabled": "Este site não consegue enviar mensagens de correio eletrónico.",
+       "emaildisabled": "Este sítio não consegue enviar mensagens de correio eletrónico.",
        "accountcreated": "Conta criada",
        "accountcreatedtext": "A conta de utilizador para [[{{ns:User}}:$1|$1]] ([[{{ns:User talk}}:$1|talk]]) foi criada.",
        "createaccount-title": "Criação de conta na wiki {{SITENAME}}",
        "previewnote": "<strong>Lembre-se de que esta é apenas uma antevisão do resultado.</strong>\nAs modificações ainda não foram gravadas!",
        "continue-editing": "Ir para a área de edição",
        "previewconflict": "Esta antevisão do resultado apresenta o texto da caixa de edição acima tal como este aparecerá se escolher gravá-lo.",
-       "session_fail_preview": "Desculpe! Não foi possível processar a edição devido à perda dos dados da sua sessão.\n\nA sua sessão poderá ter sido encerrada. <strong>Verifique se ainda está autenticado e tente novamente, por favor</strong>. \nCaso continue a não funcionar, tente [[Special:UserLogout|sair]] e voltar a entrar na sua conta, e verifique se o seu navegador permite cookies deste site.",
-       "session_fail_preview_html": "Desculpe! Não foi possível processar a edição devido à perda de dados da sua sessão.\n\n<em>Como a wiki {{SITENAME}} possibilita o uso de HTML puro, a antevisão está oculta por precaução contra ataques com JavaScript.</em>\n\n<strong>Se esta é uma tentativa legítima de edição tente novamente, por favor</strong>. \nCaso continue a não funcionar, tente [[Special:UserLogout|sair]] e voltar a entrar na sua conta, e verifique se o seu navegador permite <em>cookies</em> deste <em>site</em>.",
+       "session_fail_preview": "Desculpe! Não foi possível processar a edição devido à perda dos dados da sua sessão.\n\nA sua sessão poderá ter sido encerrada. <strong>Verifique se ainda está autenticado e tente novamente, por favor</strong>. \nCaso continue a não funcionar, tente [[Special:UserLogout|sair]] e voltar a entrar na sua conta, e verifique se o seu navegador permite cookies deste sítio.",
+       "session_fail_preview_html": "Desculpe! Não foi possível processar a edição devido à perda de dados da sua sessão.\n\n<em>Como a wiki {{SITENAME}} possibilita o uso de HTML puro, a antevisão está oculta por precaução contra ataques com JavaScript.</em>\n\n<strong>Se esta é uma tentativa legítima de edição tente novamente, por favor</strong>. \nCaso continue a não funcionar, tente [[Special:UserLogout|sair]] e voltar a entrar na sua conta, e verifique se o seu navegador permite <em>cookies</em> deste sítio.",
        "token_suffix_mismatch": "<strong>A edição foi rejeitada porque o seu navegador alterou os sinais de pontuação no editor.</strong>\nA edição foi rejeitada para evitar perdas no texto da página.\nIsto acontece ocasionalmente quando se usa um serviço de proxy anonimizador mal configurado.",
        "edit_form_incomplete": "<strong>Algumas partes do formulário de edição não chegaram ao servidor; verifique que a sua edição continua intacta e tente novamente, por favor.</strong>",
        "editing": "A editar $1",
        "postedit-confirmation-created": "A página foi criada.",
        "postedit-confirmation-restored": "A página foi restaurada.",
        "postedit-confirmation-saved": "A sua edição foi gravada.",
+       "postedit-confirmation-published": "A sua edição foi publicada.",
        "edit-already-exists": "Não foi possível criar uma página nova.\nEla já existia.",
        "defaultmessagetext": "Texto da mensagem padrão",
        "content-failed-to-parse": "Falha ao analisar conteúdo $2 para modelo $1:$3",
        "showingresultsinrange": "Apresenta-se abaixo {{PLURAL:$1|<strong>1</strong> resultado|até <strong>$1</strong> resultados}} no intervalo #<strong>$2</strong> a #<strong>$3</strong>.",
        "search-showingresults": "{{PLURAL:$4|Resultado <strong>$1</strong> de <strong>$3</strong>|Resultados <strong>$1 - $2</strong> de <strong>$3</strong>}}",
        "search-nonefound": "A pesquisa não produziu resultados.",
-       "search-nonefound-thiswiki": "Não existem resultados que correspondam à consulta neste site.",
+       "search-nonefound-thiswiki": "Não existem resultados que correspondam à consulta neste sítio.",
        "powersearch-legend": "Pesquisa avançada",
        "powersearch-ns": "Pesquisar nos domínios:",
        "powersearch-togglelabel": "Marcar:",
        "prefs-custom-css": "CSS personalizado",
        "prefs-custom-js": "JS personalizado",
        "prefs-common-css-js": "CSS/JS partilhado por todos os temas:",
-       "prefs-reset-intro": "Pode usar esta página para repor as configurações padrão das preferências.\nAs suas preferências serão modificadas para os valores predefinidos do site.\nEsta operação não pode ser desfeita.",
+       "prefs-reset-intro": "Pode usar esta página para repor as configurações padrão das preferências.\nAs suas preferências serão modificadas para os valores predefinidos do sítio.\nEsta operação não pode ser desfeita.",
        "prefs-emailconfirm-label": "Confirmação do correio eletrónico:",
        "youremail": "Correio eletrónico:",
        "username": "Nome de {{GENDER:$1|utilizador|utilizadora|utilizador(a)}}:",
        "lockmanager-fail-closelock": "Não foi possível encerrar a referência de bloqueio para \"$1\".",
        "lockmanager-fail-deletelock": "Não foi possível eliminar a referência de bloqueio para \"$1\".",
        "lockmanager-fail-acquirelock": "Não foi possível adquirir bloqueio para \"$1\".",
-       "lockmanager-fail-openlock": "Não foi possível abrir o ficheiro de bloqueio de \"$1\".",
+       "lockmanager-fail-openlock": "Não foi possível abrir o ficheiro de bloqueio de \"$1\". Verifique que o seu diretório de carregamento está devidamente configurado e que o seu servidor de Internet tem permissão para escrever nesse diretório. Para mais informações, consulte https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:$wgUploadDirectory.",
        "lockmanager-fail-releaselock": "Não foi possível libertar o bloqueio de \"$1\".",
        "lockmanager-fail-db-bucket": "Não foi possível contactar bases de dados de bloqueio suficientes no \"bucket\" $1.",
        "lockmanager-fail-db-release": "Não foi possível libertar bloqueios na base de dados $1.",
        "http-curl-error": "Ocorreu um erro ao aceder ao URL: $1",
        "http-bad-status": "Ocorreu um problema durante o pedido HTTP: $1 $2",
        "upload-curl-error6": "Não foi possível aceder ao URL",
-       "upload-curl-error6-text": "Não foi possível aceder ao URL.\nVerifique se o endereço está correto e o site disponível, por favor.",
+       "upload-curl-error6-text": "Não foi possível aceder ao URL.\nVerifique se o endereço está correto e o sítio disponível, por favor.",
        "upload-curl-error28": "Tempo limite para o envio do ficheiro excedido",
-       "upload-curl-error28-text": "O site demorou demasiado tempo a responder. Verifique que o site está disponível, aguarde alguns momentos e tente novamente, por favor. Talvez queira voltar a tentar numa altura menos congestionada.",
+       "upload-curl-error28-text": "O sítio demorou demasiado tempo a responder. Verifique que o sítio está disponível, aguarde alguns momentos e tente novamente, por favor. Talvez queira voltar a tentar numa altura menos congestionada.",
        "license": "Licença:",
        "license-header": "Licenciamento",
        "nolicense": "Nenhuma selecionada",
        "apihelp-no-such-module": "Módulo \"$1\" não encontrado.",
        "apisandbox": "Testes da API",
        "apisandbox-jsonly": "Para usar a área de testes da API é necessário o JavaScript.",
-       "apisandbox-api-disabled": "A API está desativada neste site.",
+       "apisandbox-api-disabled": "A API está desativada neste sítio.",
        "apisandbox-intro": "Use esta página para fazer experiências com a <strong>API operacional do MediaWiki</strong>.\nConsulte a [[mw:API:Main page|documentação da API]] para informações sobre o seu uso. Exemplo: [https://www.mediawiki.org/wiki/API#A_simple_example obter o conteúdo da Página Principal]. Selecione uma operação para ver mais exemplos.\n\nNote que, embora esta seja uma área de testes, as operações que executar nesta página podem modificar a wiki.",
        "apisandbox-fullscreen": "Expandir painel",
        "apisandbox-fullscreen-tooltip": "Expandir o painel da página de testes para preencher a janela do navegador.",
        "booksources-invalid-isbn": "O número ISBN fornecido não parece ser válido; verifique a existência de erros ao copiar da fonte original.",
        "magiclink-tracking-rfc": "Páginas que usam hiperligações mágicas RFC",
        "magiclink-tracking-rfc-desc": "Esta página usa hiperligações mágicas RFC. Consulte a [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Magic_links mediawiki.org] para saber como migrar.",
-       "magiclink-tracking-pmid": "Páginas que usam links mágicos PMID",
+       "magiclink-tracking-pmid": "Páginas que usam hiperligações mágicas PMID",
        "magiclink-tracking-pmid-desc": "Esta página usa hiperligações mágicas PMID. Consulte a [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Magic_links mediawiki.org] para saber como migrar.",
-       "magiclink-tracking-isbn": "Páginas que usam links mágicos ISBN",
+       "magiclink-tracking-isbn": "Páginas que usam hiperligações mágicas ISBN",
        "magiclink-tracking-isbn-desc": "Esta página usa hiperligações mágicas ISBN. Consulte a [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Magic_links mediawiki.org] para saber como migrar.",
        "specialloguserlabel": "Executante:",
        "speciallogtitlelabel": "Alvo (título ou página ou {{ns:user}}:nome de utilizador):",
        "import-nonewrevisions": "Não foi importada nenhuma revisão (já estavam todas presentes ou foram ignoradas devido a erros).",
        "xml-error-string": "$1 na linha $2, coluna $3 (byte $4): $5",
        "import-upload": "Enviar dados em XML",
-       "import-token-mismatch": "Perda de dados da sessão.\n\nA sua sessão pode ter sido encerrada. '''Verifique se ainda está autenticado e tente novamente, por favor'''. \nCaso continue a não funcionar, tente [[Special:UserLogout|sair]] e voltar a entrar na sua conta, e verifique se o seu navegador permite cookies deste site.",
+       "import-token-mismatch": "Perda de dados da sessão.\n\nA sua sessão pode ter sido encerrada. '''Verifique se ainda está autenticado e tente novamente, por favor'''. \nCaso continue a não funcionar, tente [[Special:UserLogout|sair]] e voltar a entrar na sua conta, e verifique se o seu navegador permite cookies deste sítio.",
        "import-invalid-interwiki": "Não é possível importar da wiki especificada.",
        "import-error-edit": "A página \"$1\" não foi importada porque não tem permissão para editá-la.",
        "import-error-create": "A página \"$1\" não foi importada porque não tem permissão para criá-la.",
        "creditspage": "Créditos da página",
        "nocredits": "Não há informação disponível sobre os créditos desta página.",
        "spamprotectiontitle": "Filtro de proteção contra spam",
-       "spamprotectiontext": "O texto que pretendia gravar foi bloqueado pelo filtro de spam.\nEste bloqueio foi provavelmente causado por uma hiperligação para um ''site'' externo que está na lista negra.",
+       "spamprotectiontext": "O texto que pretendia gravar foi bloqueado pelo filtro de spam.\nEste bloqueio foi provavelmente causado por uma hiperligação para um sítio externo que está na lista negra.",
        "spamprotectionmatch": "O seguinte texto ativou o filtro de <i>spam</i>: $1",
        "spambot_username": "MediaWiki limpeza de spam",
        "spam_reverting": "A reverter para a última revisão que não contém ligação para $1",
        "invalidateemail": "Cancelar confirmação do correio eletrónico",
        "notificationemail_subject_changed": "O endereço de correio eletrónico registado na wiki {{SITENAME}} foi alterado",
        "notificationemail_subject_removed": "O endereço de correio eletrónico registado na wiki {{SITENAME}} foi removido",
-       "notificationemail_body_changed": "Alguém, provavelmente você, a partir do endereço IP $1, alterou o endereço de correio eletrónico da conta \"$2\" para \"$3\" na wiki {{SITENAME}}.\n\nCaso não o tenha alterado, contacte imediatamente um administrador do site.",
-       "notificationemail_body_removed": "Alguém, provavelmente você, a partir do endereço IP $1, eliminou o endereço de correio eletrónico da conta \"$2\" na wiki {{SITENAME}}.\n\nCaso não o tenha eliminado, contacte imediatamente um administrador do site.",
+       "notificationemail_body_changed": "Alguém, provavelmente você, a partir do endereço IP $1, alterou o endereço de correio eletrónico da conta \"$2\" para \"$3\" na wiki {{SITENAME}}.\n\nCaso não o tenha alterado, contacte imediatamente um administrador do sítio.",
+       "notificationemail_body_removed": "Alguém, provavelmente você, a partir do endereço IP $1, eliminou o endereço de correio eletrónico da conta \"$2\" na wiki {{SITENAME}}.\n\nCaso não o tenha eliminado, contacte imediatamente um administrador do sítio.",
        "scarytranscludedisabled": "[Transclusão interwikis foi impossibilitada]",
        "scarytranscludefailed": "[Não foi possível obter a predefinição a partir de $1]",
        "scarytranscludefailed-httpstatus": "[Não foi possível obter a predefinição a partir de $1: HTTP $2]",
        "permanentlink": "Hiperligação permanente",
        "permanentlink-revid": "Identificador de revisão",
        "permanentlink-submit": "Ir para a revisão",
-       "dberr-problems": "Desculpe! Este site está com dificuldades técnicas.",
+       "dberr-problems": "Desculpe! Este sítio está com dificuldades técnicas.",
        "dberr-again": "Experimente esperar alguns minutos e atualizar.",
        "dberr-info": "(Não foi possível aceder ao servidor da base de dados: $1)",
        "dberr-info-hidden": "(Não foi possível aceder ao servidor da base de dados)",
        "expand_templates_generate_xml": "Mostrar a árvore de análise sintáctica do XML",
        "expand_templates_generate_rawhtml": "Mostrar o HTML puro",
        "expand_templates_preview": "Antevisão do resultado",
-       "expand_templates_preview_fail_html": "<em>Porque a wiki {{SITENAME}} permite código HTML puro e ocorreu uma perda de dados da sessão, a antevisão ficará ocultada como precaução contra ataques por JavaScript.</em>\n\n<strong>Se esta é uma tentativa legítima de visionamento, por favor tente novamente.</strong>\nCaso continue a não funcionar, tente [[Special:UserLogout|sair]] e voltar a entrar na sua conta, e verifique se o seu navegador permite que este site crie ''cookies''.",
+       "expand_templates_preview_fail_html": "<em>Porque a wiki {{SITENAME}} permite código HTML puro e ocorreu uma perda de dados da sessão, a antevisão ficará ocultada como precaução contra ataques por JavaScript.</em>\n\n<strong>Se esta é uma tentativa legítima de visionamento, por favor tente novamente.</strong>\nCaso continue a não funcionar, tente [[Special:UserLogout|sair]] e voltar a entrar na sua conta, e verifique se o seu navegador permite que este sítio crie ''cookies''.",
        "expand_templates_preview_fail_html_anon": "<em>Porque a wiki {{SITENAME}} permite código HTML puro e não iniciou uma sessão, a antevisão ficará ocultada como precaução contra ataques por JavaScript.</em>\n\n<strong>Se esta é uma tentativa legítima de visionamento, por favor [[Special:UserLogin|inicie uma sessão]] e tente novamente.</strong>",
        "expand_templates_input_missing": "Necessita de fornecer pelo menos algum texto wiki de entrada.",
        "pagelanguage": "Alterar a língua da página",
        "rawhtml-notallowed": "As etiquetas &lt;html&gt; não podem ser utilizadas fora de páginas normais.",
        "gotointerwiki": "A sair da wiki {{SITENAME}}",
        "gotointerwiki-invalid": "O título especificado é inválido.",
-       "gotointerwiki-external": "Está prestes a sair da wiki {{SITENAME}} para visitar [[$2]], que é um site externo.\n\n'''[$1 Continuar para $1]'''",
+       "gotointerwiki-external": "Está prestes a sair da wiki {{SITENAME}} para visitar [[$2]], que é um sítio externo.\n\n'''[$1 Continuar para $1]'''",
        "undelete-cantedit": "Não pode restaurar esta página porque não tem privilégios para a editar.",
        "undelete-cantcreate": "Não pode restaurar esta página porque não existe nenhuma página com este nome e não tem privilégios para criar esta página.",
        "pagedata-title": "Dados de página",
index e29bcad..a4f1d64 100644 (file)
        "postedit-confirmation-created": "{{gender}}\nShown after a user creates a new page. Parameters:\n* $1 - the current user, for GENDER support",
        "postedit-confirmation-restored": "{{gender}}\nShown after a user restores a page to a previous revision. Parameters:\n* $1 - the current user, for GENDER support",
        "postedit-confirmation-saved": "{{gender}}\nShown after a user saves a page. Parameters:\n* $1 - the current user, for GENDER support",
+       "postedit-confirmation-published": "{{gender}}\nShown after a user publishes a page. Parameters:\n* $1 - the current user, for GENDER support",
        "edit-already-exists": "Used as error message.\n\nSee also:\n* {{msg-mw|edit-hook-aborted}}\n* {{msg-mw|edit-gone-missing}}\n* {{msg-mw|edit-conflict}}\n* {{msg-mw|edit-no-change}}",
        "addsection-preload": "{{notranslate}}",
        "addsection-editintro": "{{notranslate}}",
index 9452252..d7c8990 100644 (file)
        "tog-hidepatrolled": "Скрывать патрулированные правки в списке свежих правок",
        "tog-newpageshidepatrolled": "Скрывать отпатрулированные страницы в списке новых страниц",
        "tog-hidecategorization": "Скрывать категоризацию страниц",
-       "tog-extendwatchlist": "РаÑ\81Ñ\88иÑ\80иÑ\82Ñ\8c Ñ\81пиÑ\81ок Ð½Ð°Ð±Ð»Ñ\8eдениÑ\8f, Ð²ÐºÐ»Ñ\8eÑ\87аÑ\8eÑ\89аÑ\8f Ð²Ñ\81е Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ\8f, Ð° Ð½Ðµ Ñ\82олÑ\8cко Ð¿Ð¾Ñ\81ледние <small>(они Ð¼Ð¾Ð³Ñ\83Ñ\82 Ð±Ñ\8bÑ\82Ñ\8c Ñ\81гÑ\80Ñ\83ппиÑ\80ованÑ\8b Ð½Ð°Ñ\81Ñ\82Ñ\80ойкой Ð½Ð° Ð²ÐºÐ»Ð°Ð´ÐºÐµ Â«[[СлÑ\83жебнаÑ\8f\9dаÑ\81Ñ\82Ñ\80ойки#mw-prefsection-rc|Свежие Ð¿Ñ\80авки]]»)</small>",
+       "tog-extendwatchlist": "Расширить список наблюдения, включая все изменения, а не только последние <small>(они могут быть сгруппированы настройкой на вкладке «[[Служебная:Настройки#mw-prefsection-rc|Свежие правки]]»)</small>",
        "tog-usenewrc": "Группировать изменения в свежих правках и списке наблюдения",
        "tog-numberheadings": "Автоматически нумеровать заголовки",
        "tog-showtoolbar": "Показывать панель инструментов при редактировании",
        "postedit-confirmation-created": "Страница создана.",
        "postedit-confirmation-restored": "Страница была восстановлена.",
        "postedit-confirmation-saved": "Ваша правка сохранена.",
+       "postedit-confirmation-published": "Ваша правка была опубликована.",
        "edit-already-exists": "Невозможно создать новую страницу.\nОна уже существует.",
        "defaultmessagetext": "Текст по умолчанию",
        "content-failed-to-parse": "Содержимое $2 не соответствует типу $1: $3.",
        "lockmanager-fail-closelock": "Не удалось закрыть файл блокировки для  «$1».",
        "lockmanager-fail-deletelock": "Не удалось удалить файл блокировки для «$1».",
        "lockmanager-fail-acquirelock": "Не удалось добиться блокировки «$1».",
-       "lockmanager-fail-openlock": "Не удалось открыть файл блокировки для «$1».",
+       "lockmanager-fail-openlock": "Не удалось открыть файл блокировки для «$1». Убедитесь, что ваш каталог загрузки настроен правильно, а ваш веб-сервер имеет разрешение на запись в этот каталог. Дополнительную информацию см. на https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:$wgUploadDirectory.",
        "lockmanager-fail-releaselock": "Не удалось разблокировать «$1».",
        "lockmanager-fail-db-bucket": "Не удалось связаться с достаточным количеством баз блокировок в сегменте $1.",
        "lockmanager-fail-db-release": "Не удалось снять блокировку базы данных  $1 .",
index 676b3e3..55494e2 100644 (file)
        "postedit-confirmation-created": "Сирэй оҥоһулунна.",
        "postedit-confirmation-restored": "Сирэй сөргүтүлүннэ.",
        "postedit-confirmation-saved": "Көннөрүүҥ бигэргэннэ.",
+       "postedit-confirmation-published": "Уларытыыҥ олоххо киирдэ.",
        "edit-already-exists": "Саҥа сирэйи оҥорор табыллыбат.\nМаннык сирэй баар эбит.",
        "defaultmessagetext": "Туспа этиллибэтэҕинэ суруллар тиэкис",
        "content-failed-to-parse": "$2 иһинээҕитэ $1 көрүҥэр сөп түбэспэт: $3.",
index 31abcc0..7bd0919 100644 (file)
        "search-showingresults": "{{PLURAL:$4|نتيجو <strong>$1</strong> of <strong>$3</strong>|نتيجا <strong>$1 - $2</strong> of <strong>$3</strong>}}",
        "search-nonefound": "توھان جي ڳولا جي نتيجي ۾ ڪجھہ بہ نہ لڌو.",
        "powersearch-legend": "اعليٰ ڳولا",
+       "powersearch-ns": "نالن لاءِ ڇڏين وڇوٽين ۾ ڳولھيو (سرچ اِن نيم اسپيسز)",
        "powersearch-togglelabel": "چڪاسيو:",
        "powersearch-toggleall": "سڀ",
        "powersearch-togglenone": "ڪو بہ نہ",
        "nrevisions": "$1 {{PLURAL:$1|مسودو|مسودا}}",
        "nimagelinks": "$1 {{PLURAL:$1|صفحي|صفحن}} ۾ استعمال ٿيل",
        "ntransclusions": "$1 {{PLURAL:$1|صفحي|صفحن}} ۾ استعمال ٿيل",
-       "specialpage-empty": "ھن رپورٽ لاءِ ڪي-بہ نتيجا ناھن.",
+       "specialpage-empty": "ھن رپورٽ لاءِ ڪي بہ نتيجا ناھن.",
        "lonelypages": "يتيم صفحا",
        "uncategorizedpages": "اڻ زمريل صفحا",
        "uncategorizedcategories": "اڻزمرايل زمرا",
        "restriction-level-sysop": "مڪمل طور تحفظيل",
        "restriction-level-autoconfirmed": "نيم تحفظيل",
        "viewdeletedpage": "ڊاٺل صفحا ڏسو",
-       "undelete-nodiff": "ڪوبہ پويون مسودو نہ لڌو",
+       "undelete-nodiff": "ڪوبہ اڳيون مسودو نہ لڌو",
        "undeletebtn": "بحاليو",
        "undeleteviewlink": "ڏسو",
        "undeletecomment": "سبب:",
        "version-libraries-description": "تشريح",
        "version-libraries-authors": "ليکڪ",
        "redirect-submit": "ھلو",
+       "redirect-lookup": "ڏسو:",
        "redirect-value": "قدر:",
        "redirect-user": "واپرائيندڙ آءِڊي",
        "redirect-page": "صفحي جي آءِڊي",
index 05a1229..2fd1892 100644 (file)
        "postedit-confirmation-created": "Stran je bila ustvarjena.",
        "postedit-confirmation-restored": "Stran je bila obnovljena.",
        "postedit-confirmation-saved": "Vaše urejanje smo shranili.",
+       "postedit-confirmation-published": "Vaše urejanje smo objavili.",
        "edit-already-exists": "Ni bilo mogoče ustvariti nove strani, ker že obstaja.",
        "defaultmessagetext": "Prednastavljeno besedilo",
        "content-failed-to-parse": "Nisem mogel razčleniti vsebine $2 za obliko $1: $3",
        "lockmanager-fail-closelock": "Ne morem zapreti zaklenitvene datoteke »$1«.",
        "lockmanager-fail-deletelock": "Ne morem izbrisati zaklenitvene datoteke »$1«.",
        "lockmanager-fail-acquirelock": "Ne morem pridobiti zaklenitvene datoteke »$1«.",
-       "lockmanager-fail-openlock": "Ne morem odpreti zaklenitvene datoteke »$1«.",
+       "lockmanager-fail-openlock": "Ne morem odpreti zaklenitvene datoteke »$1«. Prepričajte se, da je mapa za nalaganje nastavljena pravilno in da ima vaš spletni strežnik pravice za pisanje v tej mapi. Oglejte si https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:$wgUploadDirectory za več informacij.",
        "lockmanager-fail-releaselock": "Ne morem sprostiti zaklepa »$1«.",
        "lockmanager-fail-db-bucket": "Ne morem kontaktirati zadostnega števila zaklenitvenih zbirk podatkov v vedru $1.",
        "lockmanager-fail-db-release": "Ne morem sprostiti zaklepov zbirke podatkov $1.",
index 3814e18..f1e57fa 100644 (file)
        "exception-nologin": "Нисте пријављени",
        "exception-nologin-text": "Пријавите се да бисте приступили овој страници или радњи.",
        "exception-nologin-text-manual": "Морате бити $1 да бисте приступили овој страници или радњи.",
-       "virus-badscanner": "Ð\9dеиÑ\81пÑ\80авна Ð¿Ð¾Ñ\81Ñ\82авка: непознати скенер за вирусе: <em>$1</em>",
+       "virus-badscanner": "Ð\9dеиÑ\81пÑ\80авно Ð¿Ð¾Ð´ÐµÑ\88аваÑ\9aе: непознати скенер за вирусе: <em>$1</em>",
        "virus-scanfailed": "неуспешно скенирање (код $1)",
        "virus-unknownscanner": "непознати антивирус:",
        "logouttext": "<strong>Одјављени сте.</strong>\n\nИмајте на уму да неке странице могу наставити да се приказују као да сте још пријављени, све док не очистите привремену меморију Вашег прегледача.",
        "userpage-userdoesnotexist": "Кориснички налог „<nowiki>$1</nowiki>“ није отворен.\nРазмислите да ли заиста желите да направите/уредите ову страницу.",
        "userpage-userdoesnotexist-view": "Кориснички налог „$1“ није отворен.",
        "blocked-notice-logextract": "Овај корисник је тренутно блокиран.\nИзвештај о последњем блокирању можете погледати испод:",
-       "clearyourcache": "<strong>Ð\9dапомена:</strong> Ð½Ð°ÐºÐ¾Ð½ Ñ\87Ñ\83ваÑ\9aа, Ð¼Ð¾Ð¶Ð´Ð° Ñ\9bеÑ\82е Ð¼Ð¾Ñ\80аÑ\82и Ð´Ð° Ð¾Ñ\87иÑ\81Ñ\82иÑ\82е ÐºÐµÑ\88 Ð¿Ñ\80егледаÑ\87а ÐºÐ°ÐºÐ¾ Ð±Ð¸Ñ\81Ñ\82е Ð²Ð¸Ð´ÐµÐ»Ð¸ Ð¿Ñ\80омене.\n* <strong>Фајерфокс / Сафари:</strong> држите <em>Shift</em> и кликните на <em>Освежи</em>, или притисните <em>Ctrl-F5</em> или <em>Ctrl-R</em> (<em>⌘-R</em> на Меку)\n* <strong>Гугл кроум:</strong> притисните <em>Ctrl-Shift-R</em> (<em>⌘-Shift-R</em> на Меку)\n* <strong>Интернет експлорер:</strong> држите <em>Ctrl</em> и кликните на <em>Освежи</em> или притисните <em>Ctrl-F5</em>\n* <strong>Опера:</strong> идите на <em>Алатке → Подешавања</em> (<em>Опера → Поставке</em> на Меку) и затим <em>Приватност и безбедност → Очистите податке о прегледима → Кеширане слике и датотеке</em>.",
-       "usercssyoucanpreview": "'''Савет:''' кориситите дугме „{{int:showpreview}}“ да испробате свој нови CSS пре него што га сачувате.",
-       "userjsyoucanpreview": "'''Савет:''' кориситите дугме „{{int:showpreview}}“ да испробате свој нови јаваскрипт пре него што га сачувате.",
-       "usercsspreview": "'''Ово је само преглед CSS-а.'''\n'''Страница још није сачувана!'''",
-       "userjspreview": "'''Ово је само преглед јаваскрипта.'''\n'''Страница још није сачувана!'''",
-       "sitecsspreview": "'''Ово је само преглед CSS-а.'''\n'''Страница још није сачувана!'''",
-       "sitejspreview": "'''Ово је само преглед јаваскрипта.'''\n'''Страница још није сачувана!'''",
+       "clearyourcache": "<strong>Ð\9dапомена:</strong> Ð½Ð°ÐºÐ¾Ð½ Ñ\87Ñ\83ваÑ\9aа, Ð¼Ð¾Ð¶Ð´Ð° Ñ\9bеÑ\82е Ð¼Ð¾Ñ\80аÑ\82и Ð´Ð° Ð¾Ñ\87иÑ\81Ñ\82иÑ\82е ÐºÐµÑ\88 Ð¿Ñ\80егледаÑ\87а ÐºÐ°ÐºÐ¾ Ð±Ð¸Ñ\81Ñ\82е Ð²Ð¸Ð´ÐµÐ»Ð¸ Ð¸Ð·мене.\n* <strong>Фајерфокс / Сафари:</strong> држите <em>Shift</em> и кликните на <em>Освежи</em>, или притисните <em>Ctrl-F5</em> или <em>Ctrl-R</em> (<em>⌘-R</em> на Меку)\n* <strong>Гугл кроум:</strong> притисните <em>Ctrl-Shift-R</em> (<em>⌘-Shift-R</em> на Меку)\n* <strong>Интернет експлорер:</strong> држите <em>Ctrl</em> и кликните на <em>Освежи</em> или притисните <em>Ctrl-F5</em>\n* <strong>Опера:</strong> идите на <em>Алатке → Подешавања</em> (<em>Опера → Поставке</em> на Меку) и затим <em>Приватност и безбедност → Очистите податке о прегледима → Кеширане слике и датотеке</em>.",
+       "usercssyoucanpreview": "<strong>Савет:<strong> кориситите дугме „{{int:showpreview}}“ да испробате свој нови CSS пре него што га сачувате.",
+       "userjsyoucanpreview": "<strong>Савет:</strong> кориситите дугме „{{int:showpreview}}“ да испробате свој нови јаваскрипт пре него што га сачувате.",
+       "usercsspreview": "<strong>Ово је само преглед CSS-а.\nСтраница још није сачувана!</strong>",
+       "userjspreview": "<strong>Ово је само преглед јаваскрипта.\nСтраница још није сачувана!</strong>",
+       "sitecsspreview": "<strong>Ово је само преглед CSS-а.\nСтраница још није сачувана!</strong>",
+       "sitejspreview": "<strong>Ово је само преглед јаваскрипта.\nСтраница још није сачувана!</strong>",
        "userinvalidcssjstitle": "<strong>Упозорење:</strong> не постоји тема „$1“.\nПрилагођене странице CSS и јаваскрипт почињу малим словом, нпр. {{ns:user}}:Foo/vector.css, а не {{ns:user}}:Foo/Vector.css.",
        "updated": "(ажурирано)",
        "note": "<strong>Напомена:</strong>",
        "previewnote": "<strong>Не заборавите да је ово само претпреглед.</strong>\nВаше измене још нису сачуване!",
        "continue-editing": "Иди на уређивачки оквир",
-       "previewconflict": "Овај преглед осликава како ће текст у текстуалном оквиру изгледати.",
-       "session_fail_preview": "Ð\98звиÑ\9aавамо Ñ\81е! Ð\9dиÑ\81мо Ð¼Ð¾Ð³Ð»Ð¸ Ð´Ð° Ð¾Ð±Ñ\80адимо Ð\92аÑ\88Ñ\83 Ð¸Ð·Ð¼ÐµÐ½Ñ\83 Ð·Ð±Ð¾Ð³ Ð³Ñ\83биÑ\82ка Ð¿Ð¾Ð´Ð°Ñ\82ака Ñ\81еÑ\81иÑ\98е.\n\nÐ\9cожда Ñ\81Ñ\82е Ð¾Ð´Ñ\98авÑ\99ени. <strong>Ð\9fÑ\80овеÑ\80иÑ\82е Ð´Ð° Ð»Ð¸ Ñ\81Ñ\82е Ð¿Ñ\80иÑ\98авÑ\99ени Ð¸ Ð¿Ð¾ÐºÑ\83Ñ\88аÑ\98Ñ\82е Ð¿Ð¾Ð½Ð¾Ð²Ð¾</strong>.\nÐ\90ко Ð¸ Ð´Ð°Ñ\99е Ð½Ðµ Ñ\80ади, Ð¿Ð¾ÐºÑ\83Ñ\88аÑ\98Ñ\82е Ð´Ð° Ñ\81е [[Special:UserLogout|одÑ\98авиÑ\82е]] Ð¸ Ð¿Ð¾Ð½Ð¾Ð²Ð¾ Ð¿Ñ\80иÑ\98авиÑ\82е, Ñ\82е Ð¿Ñ\80овеÑ\80иÑ\82е Ð´Ð° Ð»Ð¸ Ñ\81Ñ\83 Ð½Ð° Ð\92аÑ\88ем Ð¿Ñ\80еÑ\82Ñ\80аживачу дозвољени колачићи са овог сајта.",
+       "previewconflict": "Овај преглед осликава како ће изгледати текст у текстуалном оквиру.",
+       "session_fail_preview": "Ð\9dиÑ\81мо Ð¼Ð¾Ð³Ð»Ð¸ Ð´Ð° Ð¾Ð±Ñ\80адимо Ð\92аÑ\88Ñ\83 Ð¸Ð·Ð¼ÐµÐ½Ñ\83 Ð·Ð±Ð¾Ð³ Ð³Ñ\83биÑ\82ка Ð¿Ð¾Ð´Ð°Ñ\82ака Ñ\81еÑ\81иÑ\98е.\n\nÐ\9cожда Ñ\81Ñ\82е Ð¾Ð´Ñ\98авÑ\99ени. <strong>Ð\9fÑ\80овеÑ\80иÑ\82е Ð´Ð° Ð»Ð¸ Ñ\81Ñ\82е Ð¿Ñ\80иÑ\98авÑ\99ени Ð¸ Ð¿Ð¾ÐºÑ\83Ñ\88аÑ\98Ñ\82е Ð¿Ð¾Ð½Ð¾Ð²Ð¾</strong>.\nÐ\90ко Ð¸ Ð´Ð°Ñ\99е Ð½Ðµ Ñ\80ади, Ð¿Ð¾ÐºÑ\83Ñ\88аÑ\98Ñ\82е Ð´Ð° Ñ\81е [[Special:UserLogout|одÑ\98авиÑ\82е]] Ð¸ Ð¿Ð¾Ð½Ð¾Ð²Ð¾ Ð¿Ñ\80иÑ\98авиÑ\82е, Ñ\82е Ð¿Ñ\80овеÑ\80иÑ\82е Ð´Ð° Ð»Ð¸ Ñ\81Ñ\83 Ð½Ð° Ð\92аÑ\88ем Ð¿Ñ\80егледачу дозвољени колачићи са овог сајта.",
        "session_fail_preview_html": "Нисмо могли да обрадимо вашу измену због губитка података сесије.\n\n<em>Будући да је на овом викију омогућен унос HTML ознака, преглед је сакривен као мера предострожности против напада преко јаваскрипта.</em>\n\n<strong>Ако сте покушали да направите праву измену, покушајте поново.<strong>\nАко и даље не ради, покушајте да се [[Special:UserLogout|одјавите]] и поново пријавите и проверите да ли Ваш прегледач дозвољава колачиће са овог сајта.",
-       "token_suffix_mismatch": "'''Ваша измена је одбачена јер је ваш прегледач убацио знакове интерпункције у новчић уређивања.\nТо се понекад догађа када се користи неисправан посредник.'''",
+       "token_suffix_mismatch": "<strong>Ваша измена је одбачена јер је ваш прегледач убацио знакове интерпункције у новчић уређивања.</strong>\nТо се понекад догађа када се користи неисправан посредник.",
        "edit_form_incomplete": "<strong>Неки делови обрасца за уређивање нису стигли до сервера. Проверите да ли су ваше измене непромењене и покушајте поново.</strong>",
        "editing": "Уређујете $1",
        "creating": "Прављење странице $1",
        "editingsection": "Уређујете $1 (одељак)",
        "editingcomment": "Уређујете $1 (нови одељак)",
        "editconflict": "Сукобљене измене: $1",
-       "explainconflict": "Ð\9dеко Ð´Ñ\80Ñ\83ги Ñ\98е Ñ\83 Ð¼ÐµÑ\92Ñ\83вÑ\80еменÑ\83 Ð¿Ñ\80оменио Ð¾Ð²Ñ\83 Ñ\81Ñ\82Ñ\80аниÑ\86Ñ\83.\nÐ\93оÑ\80Ñ\9aи Ð¾ÐºÐ²Ð¸Ñ\80 Ñ\81адÑ\80жи Ñ\81адаÑ\88Ñ\9aи Ñ\82екÑ\81Ñ\82 Ñ\81Ñ\82Ñ\80аниÑ\86е.\nÐ\92аÑ\88е Ð¸Ð·Ð¼ÐµÐ½Ðµ Ñ\81Ñ\83 Ð¿Ñ\80иказане Ñ\83 Ð´Ð¾Ñ\9aем Ð¾ÐºÐ²Ð¸Ñ\80Ñ\83.\nÐ\9cоÑ\80аÑ\9bеÑ\82е Ð´Ð° Ñ\83неÑ\81еÑ\82е Ñ\81воÑ\98е Ð¿Ñ\80омене у садашњи текст странице.\n<strong>Само</strong> ће текст у горњем оквиру за уређивање бити сачуван када кликнете на „$1”.",
+       "explainconflict": "Ð\9dеко Ð´Ñ\80Ñ\83ги Ñ\98е Ñ\83 Ð¼ÐµÑ\92Ñ\83вÑ\80еменÑ\83 Ð¿Ñ\80оменио Ð¾Ð²Ñ\83 Ñ\81Ñ\82Ñ\80аниÑ\86Ñ\83.\nÐ\93оÑ\80Ñ\9aи Ð¾ÐºÐ²Ð¸Ñ\80 Ñ\81адÑ\80жи Ñ\81адаÑ\88Ñ\9aи Ñ\82екÑ\81Ñ\82 Ñ\81Ñ\82Ñ\80аниÑ\86е.\nÐ\92аÑ\88е Ð¸Ð·Ð¼ÐµÐ½Ðµ Ñ\81Ñ\83 Ð¿Ñ\80иказане Ñ\83 Ð´Ð¾Ñ\9aем Ð¾ÐºÐ²Ð¸Ñ\80Ñ\83.\nÐ\9cоÑ\80аÑ\9bеÑ\82е Ð´Ð° Ñ\83неÑ\81еÑ\82е Ñ\81воÑ\98е Ð¸Ð·мене у садашњи текст странице.\n<strong>Само</strong> ће текст у горњем оквиру за уређивање бити сачуван када кликнете на „$1”.",
        "yourtext": "Ваш текст",
        "storedversion": "Ускладиштена измена",
        "editingold": "<strong>Упозорење: уређујете застарелу измену ове странице.</strong>\nАко је сачувате, све новије измене ће бити изгубљене.",
        "yourdiff": "Разлике",
-       "copyrightwarning": "Ð\98маÑ\98Ñ\82е Ð½Ð° Ñ\83мÑ\83 Ð´Ð° Ñ\81е Ñ\81ви Ð´Ð¾Ð¿Ñ\80иноÑ\81и Ð½Ð° Ð¾Ð²Ð¾Ð¼ Ð²Ð¸ÐºÐ¸Ñ\98Ñ\83 Ñ\81маÑ\82Ñ\80аÑ\98Ñ\83 ÐºÐ°Ð¾ Ð¾Ð±Ñ\98авÑ\99ени Ð¿Ð¾Ð´ Ð»Ð¸Ñ\86енÑ\86ом $2 (виÑ\88е Ð½Ð° $1).\nÐ\90ко Ð½Ðµ Ð¶ÐµÐ»Ð¸Ñ\82е Ð´Ð° Ñ\81е Ð²Ð°Ñ\88и Ñ\82екÑ\81Ñ\82ови Ð¼ÐµÑ\9aаÑ\98Ñ\83 Ð¸ Ñ\80азмеÑ\9aÑ\83Ñ\98Ñ\83 Ð±ÐµÐ· Ð¾Ð³Ñ\80аниÑ\87еÑ\9aа, Ð¾Ð½Ð´Ð° Ð¸Ñ\85 Ð½Ðµ Ñ\88аÑ\99иÑ\82е Ð¾Ð²Ð´Ðµ.<br />\nÐ\98Ñ\81Ñ\82о Ñ\82ако Ð¾Ð±ÐµÑ\9bаваÑ\82е Ð´Ð° Ñ\81Ñ\82е Ð²и аутор текста, или да сте га умножили с извора који је у јавном власништву.\n<strong>Не шаљите радове заштићене ауторским правима без дозволе!</strong>",
+       "copyrightwarning": "Ð\98маÑ\98Ñ\82е Ð½Ð° Ñ\83мÑ\83 Ð´Ð° Ñ\81е Ñ\81ви Ð´Ð¾Ð¿Ñ\80иноÑ\81и Ð½Ð° Ð¾Ð²Ð¾Ð¼ Ð²Ð¸ÐºÐ¸Ñ\98Ñ\83 Ñ\81маÑ\82Ñ\80аÑ\98Ñ\83 ÐºÐ°Ð¾ Ð¾Ð±Ñ\98авÑ\99ени Ð¿Ð¾Ð´ Ð»Ð¸Ñ\86енÑ\86ом $2 (виÑ\88е Ð½Ð° $1).\nÐ\90ко Ð½Ðµ Ð¶ÐµÐ»Ð¸Ñ\82е Ð´Ð° Ñ\81е Ð²Ð°Ñ\88и Ñ\82екÑ\81Ñ\82ови Ð¼ÐµÑ\9aаÑ\98Ñ\83 Ð¸ Ñ\80азмеÑ\9aÑ\83Ñ\98Ñ\83 Ð±ÐµÐ· Ð¾Ð³Ñ\80аниÑ\87еÑ\9aа, Ð¾Ð½Ð´Ð° Ð¸Ñ\85 Ð½Ðµ Ñ\88аÑ\99иÑ\82е Ð¾Ð²Ð´Ðµ.<br />\nÐ\98Ñ\81Ñ\82о Ñ\82ако Ð¾Ð±ÐµÑ\9bаваÑ\82е Ð´Ð° Ñ\81Ñ\82е Ð\92и аутор текста, или да сте га умножили с извора који је у јавном власништву.\n<strong>Не шаљите радове заштићене ауторским правима без дозволе!</strong>",
        "copyrightwarning2": "Имајте на уму да се сви доприноси на овом викију могу мењати, враћати или брисати од других корисника.\nАко не желите да се ваши текстови слободно мењају и расподељују, не шаљите их овде.<br />\nИсто тако обећавате да сте ви аутор текста, или да сте га умножили с извора који је у јавном власништву (више на $1).\n<strong>Не шаљите радове заштићене ауторским правима без дозволе!</strong>",
        "editpage-cannot-use-custom-model": "Модел садржаја ове странице се не може променити.",
-       "longpageerror": "'''Грешка: текст који сте унели је величине {{PLURAL:$1|један килобајт|$1 килобајта|$1 килобајта}}, што је веће од {{PLURAL:$2|дозвољеног једног килобајта|дозвољена $2 килобајта|дозвољених $2 килобајта}}.'''\nСтраница не може бити сачувана.",
+       "longpageerror": "<strong>Грешка: текст који сте унели је величине {{PLURAL:$1|један килобајт|$1 килобајта}}, што је веће од {{PLURAL:$2|дозвољеног једног килобајта|дозвољена $2 килобајта|дозвољених $2 килобајта}}.</strong>\nСтраница не може бити сачувана.",
        "readonlywarning": "<strong>Упозорење: база података је закључана ради одржавања, тако да тренутно нећете моћи да сачувате измене.</strong>\nМожда бисте желели сачувати текст за касније у некој текстуалној датотеци.\n\nСистемски администратор је навео следеће објашњење: $1",
        "protectedpagewarning": "<strong>Упозорење: ова страница је заштићена, тако да само администратори могу да је мењају.</strong>\nПоследњи запис у дневнику је приказан испод:",
-       "semiprotectedpagewarning": "<strong>Напомена:</strong> Ова страница је заштићена, тако да само регистровани корисници могу да је уређују.\nПоследњи запис у дневнику приказан је испод као референца:",
+       "semiprotectedpagewarning": "<strong>Напомена:</strong> Ова страница је заштићена, тако да само регистровани корисници могу да је уређују.\nПоследњи запис у дневнику је приказан испод:",
        "cascadeprotectedwarning": "<strong>Упозорење:</strong> Ова страница је заштићена тако да је могу уређивати само корисници са [[Special:ListGroupRights|одређеним правима]] (администратори), јер је иста укључена у {{PLURAL:$1|следећу страницу која је заштићена|следеће странице које су заштићене}} „преносивом” заштитом:",
-       "titleprotectedwarning": "<strong>УпозоÑ\80еÑ\9aе: Ð¾Ð²Ñ\83 Ñ\81Ñ\82Ñ\80аниÑ\86Ñ\83 Ð¼Ð¾Ð³Ñ\83 Ð½Ð°Ð¿Ñ\80авиÑ\82и Ñ\81амо ÐºÐ¾Ñ\80иÑ\81ниÑ\86и [[Special:ListGroupRights|Ñ\81 Ð¾Ð´Ñ\80еÑ\92еним Ð¿Ñ\80авима]].</strong>\nÐ\98Ñ\81под Ñ\81Ñ\83 Ð½Ð°Ð²ÐµÐ´ÐµÐ½Ð¸ Ð¿Ð¾Ñ\81ледÑ\9aи Ð·Ð°Ð¿Ð¸Ñ\81и Ñ\83 Ð´Ð½ÐµÐ²Ð½Ð¸ÐºÑ\83:",
+       "titleprotectedwarning": "<strong>УпозоÑ\80еÑ\9aе: Ð¾Ð²Ñ\83 Ñ\81Ñ\82Ñ\80аниÑ\86Ñ\83 Ð¼Ð¾Ð³Ñ\83 Ð½Ð°Ð¿Ñ\80авиÑ\82и Ñ\81амо ÐºÐ¾Ñ\80иÑ\81ниÑ\86и [[Special:ListGroupRights|Ñ\81 Ð¾Ð´Ñ\80еÑ\92еним Ð¿Ñ\80авима]].</strong>\nÐ\9fоÑ\81ледÑ\9aи Ð·Ð°Ð¿Ð¸Ñ\81 Ñ\83 Ð´Ð½ÐµÐ²Ð½Ð¸ÐºÑ\83 Ñ\98е Ð¿Ñ\80иказан Ð¸Ñ\81под:",
        "templatesused": "{{PLURAL:$1|Шаблон|Шаблони}} на овој страници:",
        "templatesusedpreview": "{{PLURAL:$1|Шаблон|Шаблони}} у овом претпрегледу:",
        "templatesusedsection": "{{PLURAL:$1|Шаблон|Шаблони}} у овом одељку:",
        "permissionserrorstext-withaction": "Немате дозволу за $2 из {{PLURAL:$1|следећег|следећих}} разлога:",
        "recreate-moveddeleted-warn": "<strong>Упозорење: поново правите страницу која је претходно обрисана.</strong>\n\nРазмотрите да ли је прикладно да наставите с уређивањем ове странице.\nОвде је наведена историја брисања и премештања с образложењем:",
        "moveddeleted-notice": "Ова страница је обрисана.\nИсторија њеног брисања, заштите и премештања налази се испод:",
-       "moveddeleted-notice-recent": "Ð\96ао Ð½Ð°Ð¼ Ñ\98е, Ð¾Ð²Ð° Ñ\81Ñ\82Ñ\80аниÑ\86а Ñ\98е Ð½ÐµÐ´Ð°Ð²Ð½Ð¾ Ð¾Ð±Ñ\80иÑ\81ана (Ñ\83 Ð¿Ð¾Ñ\81ледÑ\9aиÑ\85 24 Ñ\81аÑ\82а).\nÐ\9eвде Ñ\98е Ð½Ð°Ð²ÐµÐ´ÐµÐ½Ð° Ð¸Ñ\81Ñ\82оÑ\80иÑ\98а Ð±Ñ\80иÑ\81аÑ\9aа Ð¸ Ð¿Ñ\80емеÑ\88Ñ\82аÑ\9aа Ñ\81 Ð¾Ð±Ñ\80азложеÑ\9aем.",
+       "moveddeleted-notice-recent": "Ð\96ао Ð½Ð°Ð¼ Ñ\98е, Ð¾Ð²Ð° Ñ\81Ñ\82Ñ\80аниÑ\86а Ñ\98е Ð½ÐµÐ´Ð°Ð²Ð½Ð¾ Ð¾Ð±Ñ\80иÑ\81ана (Ñ\83 Ð¿Ð¾Ñ\81ледÑ\9aиÑ\85 24 Ñ\81аÑ\82а).\nÐ\98Ñ\81Ñ\82оÑ\80иÑ\98а Ñ\9aеног Ð±Ñ\80иÑ\81аÑ\9aа, Ð·Ð°Ñ\88Ñ\82иÑ\82е Ð¸ Ð¿Ñ\80емеÑ\88Ñ\82аÑ\9aа Ð½Ð°Ð»Ð°Ð·Ð¸ Ñ\81е Ð¸Ñ\81под:",
        "log-fulllog": "Погледај целу историју",
        "edit-hook-aborted": "Измену је прекинула кука.\nНије дато никакво образложење.",
        "edit-gone-missing": "Не могу да ажурирам страницу.\nИзгледа да је обрисана.",
        "duplicate-args-warning": "<strong>Упозорење:</strong> [[:$1]] позива [[:$2]] са више од једне вредности за параметар „$3“. Само последња наведена вредност ће бити коришћена.",
        "duplicate-args-category": "Странице с дуплираним аргументима код позива шаблона",
        "duplicate-args-category-desc": "Страница садржи позиве шаблона који користе двоструке аргументе, као што су <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code> или <code><nowiki>{{foo|bar|1=baz}}</nowiki></code>.",
-       "expensive-parserfunction-warning": "'''Упозорење:''' ова страница садржи превише позива за рашчлањивање.\n\nТребало би да има мање од $2 {{PLURAL:$2|позив|позива|позива}}, а сада има $1.",
+       "expensive-parserfunction-warning": "<strong>Упозорење:</strong> ова страница садржи превише позива за рашчлањивање.\n\nТребало би да има мање од $2 {{PLURAL:$2|позив|позива}}, а сада има $1.",
        "expensive-parserfunction-category": "Странице с превише позива за рашчлањивање",
-       "post-expand-template-inclusion-warning": "'''Упозорење:''' величина укљученог шаблона је превелика.\nНеки шаблони неће бити укључени.",
-       "post-expand-template-inclusion-category": "Странице где су укључени шаблони превелики",
+       "post-expand-template-inclusion-warning": "<strong>Упозорење:</strong> величина обухваћеног шаблона је превелика.\nНеки шаблони неће бити обухваћени.",
+       "post-expand-template-inclusion-category": "Странице где су обухваћени превелики шаблони",
        "post-expand-template-argument-warning": "'''Упозорење:''' ова страница садржи најмање један аргумент у шаблону који има превелику величину.\nОвакве аргументе би требало избегавати.",
        "post-expand-template-argument-category": "Странице које садрже изостављене аргументе у шаблону",
        "parser-template-loop-warning": "Откривена је петља шаблона: [[$1]]",
        "recentchangesdays-max": "Највише $1 {{PLURAL:$1|дан|дана}}",
        "recentchangescount": "Број измена за приказ:",
        "prefs-help-recentchangescount": "Подразумева скорашње измене, историје страница и дневнике.",
-       "prefs-help-watchlist-token2": "Ово је тајни кључ за веб-довод Вашег списка надгледања. \nСвако ко зна овај кључ биће у могућности да види Ваша надгледања; стога, кључ немојте одавати никоме. \nАко је потребно, кључ можете [[Special:ResetTokens|ресетовати]].",
+       "prefs-help-watchlist-token2": "Ово је тајни кључ за веб-довод Вашег списка надгледања. \nСвако ко зна овај кључ биће у могућности да види Ваша списак надгледања, зато кључ немојте одавати никоме. \nАко је потребно, кључ [[Special:ResetTokens|можете ресетовати]].",
        "savedprefs": "Ваша подешавања су сачувана.",
        "savedrights": "Корисничке групе за {{GENDER:$1|$1}} су сачуване.",
        "timezonelegend": "Временска зона:",
        "prefs-custom-css": "Прилагођени CSS",
        "prefs-custom-js": "Прилагођени јаваскрипт",
        "prefs-common-css-js": "Дељени CSS/Јаваскрипт за све теме:",
-       "prefs-reset-intro": "Ð\9cожеÑ\82е ÐºÐ¾Ñ\80иÑ\81Ñ\82иÑ\82и Ð¾Ð²Ñ\83 Ñ\81Ñ\82Ñ\80аниÑ\86Ñ\83 Ð´Ð° Ð¿Ð¾Ð½Ð¸Ñ\88Ñ\82иÑ\82е Ñ\81воÑ\98е Ð¿Ð¾Ñ\81Ñ\82авке на подразумеване вредности.\nОва радња се не може вратити.",
+       "prefs-reset-intro": "Ð\9cожеÑ\82е ÐºÐ¾Ñ\80иÑ\81Ñ\82иÑ\82и Ð¾Ð²Ñ\83 Ñ\81Ñ\82Ñ\80аниÑ\86Ñ\83 Ð´Ð° Ð¿Ð¾Ð½Ð¸Ñ\88Ñ\82иÑ\82е Ñ\81воÑ\98а Ð¿Ð¾Ð´ÐµÑ\88аваÑ\9aа на подразумеване вредности.\nОва радња се не може вратити.",
        "prefs-emailconfirm-label": "Потврда имејла:",
        "youremail": "Имејл:",
        "username": "{{GENDER:$1|Корисничко име}}:",
        "rcfilters-savedqueries-apply-label": "Направи филтер",
        "rcfilters-savedqueries-apply-and-setdefault-label": "Направи подразумевани филтер",
        "rcfilters-savedqueries-cancel-label": "Откажи",
-       "rcfilters-savedqueries-add-new-title": "СаÑ\87Ñ\83ваÑ\98 Ñ\82Ñ\80енÑ\83Ñ\82не Ð¿Ð¾Ñ\81Ñ\82авке филтера",
+       "rcfilters-savedqueries-add-new-title": "СаÑ\87Ñ\83ваÑ\98 Ñ\82Ñ\80енÑ\83Ñ\82на Ð¿Ð¾Ð´ÐµÑ\88аваÑ\9aа филтера",
        "rcfilters-savedqueries-already-saved": "Ови филтери су већ упамћени. Измените Ваша подешавања како бисте направили нове сачуване филтере.",
        "rcfilters-restore-default-filters": "Враћање подразумеваних филтера",
        "rcfilters-clear-all-filters": "Уклони све филтере",
        "filerevert-submit": "Врати",
        "filerevert-success": "Датотека '''[[Media:$1|$1]]''' је враћена на [$4 издање од $2; $3].",
        "filerevert-badversion": "Не постоји раније локално издање датотеке с наведеним временским подацима.",
-       "filerevert-identical": "ТÑ\80енÑ\83Ñ\82на Ð²ÐµÑ\80зиÑ\98а датотеке индентична је изабраној.",
+       "filerevert-identical": "ТÑ\80енÑ\83Ñ\82на Ð¸Ð·Ð¼ÐµÐ½а датотеке индентична је изабраној.",
        "filedelete": "Обриши $1",
        "filedelete-legend": "Обриши датотеку",
        "filedelete-intro": "Бришете датотеку '''[[Media:$1|$1]]''' заједно с њеном историјом.",
        "doubleredirects": "Двострука преусмерења",
        "doubleredirectstext": "Ова страница приказује странице које преусмеравају на друга преусмерења.\nСваки ред садржи везе према првом и другом преусмерењу, као и одредишну страницу другог преусмерења која је обично „прави“ чланак на кога прво преусмерење треба да упућује.\n<del>Прецртани</del> уноси су већ решени.",
        "double-redirect-fixed-move": "[[$1]] је премештен.\nАутоматски је ажурирано и сада преусмерава на [[$2]].",
-       "double-redirect-fixed-maintenance": "Аутоматски исправља двострука преусмерења из [[$1]] у [[$2]]. Као део одржавања.",
+       "double-redirect-fixed-maintenance": "Аутоматски исправља двострука преусмерења из [[$1]] у [[$2]] као део одржавања",
        "double-redirect-fixer": "Исправљач преусмерења",
        "brokenredirects": "Покварена преусмерења",
        "brokenredirectstext": "Следећа преусмерења упућују на непостојеће странице:",
        "wlshowhidepatr": "патролиране измене",
        "wlshowhidemine": "моје измене",
        "wlshowhidecategorization": "категоризацију страница",
-       "watchlist-options": "Поставке списка надгледања",
+       "watchlist-options": "Подешавања списка надгледања",
        "watching": "Надгледање…",
        "unwatching": "Уклањање са списка надгледања...",
        "watcherrortext": "Дошло је до грешке при промени поставки вашег списка надгледања за „$1“.",
        "protect-badnamespace-text": "Странице у овом именском простору се не могу заштитити.",
        "protect-norestrictiontypes-text": "Ова страница се не може заштитити јер нема доступних врста ограничења.",
        "protect-norestrictiontypes-title": "Незаштитљива страна",
-       "protect-legend": "Поставке заштите",
+       "protect-legend": "Подешавања заштите",
        "protectcomment": "Разлог:",
        "protectexpiry": "Истиче:",
        "protect_expiry_invalid": "Време истека је неисправно.",
        "protect_expiry_old": "Време истека је старије од тренутног времена.",
-       "protect-unchain-permissions": "Ð\9eÑ\82кÑ\99Ñ\83Ñ\87аÑ\98 Ð´Ð°Ñ\99Ñ\9aе Ð¿Ð¾Ñ\81Ñ\82авке заштите",
+       "protect-unchain-permissions": "Ð\9eÑ\82кÑ\99Ñ\83Ñ\87аÑ\98 Ð´Ð°Ñ\99Ñ\9aа Ð¿Ð¾Ð´ÐµÑ\88аваÑ\9aа заштите",
        "protect-text": "Овде можете да погледате и промените степен заштите за страницу '''$1'''.",
-       "protect-locked-blocked": "Ð\9dе Ð¼Ð¾Ð¶ÐµÑ\82е Ð¼ÐµÑ\9aаÑ\82и Ñ\81Ñ\82епене Ð·Ð°Ñ\88Ñ\82иÑ\82е Ð´Ð¾Ðº Ñ\81Ñ\82е Ð±Ð»Ð¾ÐºÐ¸Ñ\80ани.\nÐ\9eво Ñ\81Ñ\83 Ñ\82Ñ\80енÑ\83Ñ\82не Ð¿Ð¾Ñ\81Ñ\82авке странице '''$1''':",
-       "protect-locked-dblock": "Степени заштите се не могу мењати јер је активна база података закључана.\nОво су поставке странице '''$1''':",
-       "protect-locked-access": "Ð\9dемаÑ\82е Ð¾Ð²Ð»Ð°Ñ\88Ñ\9bеÑ\9aа Ð·Ð° Ð¼ÐµÑ\9aаÑ\9aе Ñ\81Ñ\82епена Ð·Ð°Ñ\88Ñ\82иÑ\82е Ñ\81Ñ\82Ñ\80аниÑ\86е.\nÐ\9eво Ñ\81Ñ\83 Ñ\82Ñ\80енÑ\83Ñ\82не Ð¿Ð¾Ñ\81Ñ\82авке странице '''$1''':",
+       "protect-locked-blocked": "Ð\9dе Ð¼Ð¾Ð¶ÐµÑ\82е Ð¼ÐµÑ\9aаÑ\82и Ñ\81Ñ\82епене Ð·Ð°Ñ\88Ñ\82иÑ\82е Ð´Ð¾Ðº Ñ\81Ñ\82е Ð±Ð»Ð¾ÐºÐ¸Ñ\80ани.\nÐ\9eво Ñ\81Ñ\83 Ñ\82Ñ\80енÑ\83Ñ\82на Ð¿Ð¾Ð´ÐµÑ\88аваÑ\9aа странице '''$1''':",
+       "protect-locked-dblock": "Степени заштите се не могу мењати јер је активна база података закључана.\nОво су подешавања странице '''$1''':",
+       "protect-locked-access": "Ð\9dемаÑ\82е Ð¾Ð²Ð»Ð°Ñ\88Ñ\9bеÑ\9aа Ð·Ð° Ð¼ÐµÑ\9aаÑ\9aе Ñ\81Ñ\82епена Ð·Ð°Ñ\88Ñ\82иÑ\82е Ñ\81Ñ\82Ñ\80аниÑ\86е.\nÐ\9eво Ñ\81Ñ\83 Ñ\82Ñ\80енÑ\83Ñ\82на Ð¿Ð¾Ð´ÐµÑ\88аваÑ\9aа странице '''$1''':",
        "protect-cascadeon": "Ова страница је тренутно заштићена јер се налази на {{PLURAL:$1|страници која има|страницама које имају}} преносиву заштиту.\nМожете да промените степен заштите, али то неће утицати на преносиву заштиту.",
        "protect-default": "Допуштено свим корисницима",
        "protect-fallback": "Дозвољено само корисницима са дозволом „$1“",
        "thumbnail_invalid_params": "Неисправни параметри за минијатуру",
        "thumbnail_dest_directory": "Не могу да направим одредишну фасциклу",
        "thumbnail_image-type": "Врста слике није подржана",
-       "thumbnail_gd-library": "Ð\9dедовÑ\80Ñ\88ене Ð¿Ð¾Ñ\81Ñ\82авке графичке библиотеке: недостаје функција $1",
+       "thumbnail_gd-library": "Ð\9dедовÑ\80Ñ\88ена Ð¿Ð¾Ð´ÐµÑ\88аваÑ\9aа графичке библиотеке: недостаје функција $1",
        "thumbnail_image-missing": "Датотека недостаје: $1",
        "import": "Увоз страница",
        "importinterwiki": "Увоз са другог викија",
        "tooltip-upload": "Започните отпремање",
        "tooltip-rollback": "„Врати“ враћа измене последњег корисника једним кликом",
        "tooltip-undo": "Опција „поништи” враћа ову измену и отвара образац за уређивање у претпрегледном моду. Омогућава додавање разлога у опису измене.",
-       "tooltip-preferences-save": "Сачувај поставке",
+       "tooltip-preferences-save": "Сачувај подешавања",
        "tooltip-summary": "Унесите кратак опис",
        "interlanguage-link-title": "$1 — $2",
        "interlanguage-link-title-nonlang": "$1 — $2",
        "duplicate-defaultsort": "<strong>Упозорење:</strong> Подразумевани кључ сврставања „$2“ мења ранији подразумевани кључ сврставања „$1“.",
        "duplicate-displaytitle": "<strong>Упозорење:</strong> наслов за приказ „$2“ замениће постојећи „$1“.",
        "restricted-displaytitle": "<strong>Упозорење:</strong> Наслов за приказ „$1” је игнорисан пошто није еквивалентан стварном наслову странице.",
-       "version": "Ð\92еÑ\80зиÑ\98а",
+       "version": "Ð\98здаÑ\9aе",
        "version-extensions": "Инсталирана проширења",
        "version-skins": "Инсталиране теме",
        "version-specialpages": "Посебне странице",
        "version-ext-license": "Лиценца",
        "version-ext-colheader-name": "Екстензија",
        "version-skin-colheader-name": "Тема",
-       "version-ext-colheader-version": "Ð\92еÑ\80зиÑ\98а",
+       "version-ext-colheader-version": "Ð\98здаÑ\9aе",
        "version-ext-colheader-license": "Лиценца",
        "version-ext-colheader-description": "Опис",
        "version-ext-colheader-credits": "Аутори",
        "version-license-info": "Медијавики је слободан софтвер можете га редистрибуирати и/или модификовати под условима ГНУ-ове опште јавне лиценце верзија 2 или сваке следеће коју објави Задужбина за слободан софтвер.\n\nМедијавики се редистрибуира у нади да ће бити од користи, али <em>БЕЗ ИКАКВЕ ГАРАНЦИЈЕ</em> чак и без <strong>ПОДРАЗУМЕВАНЕ ГАРАНЦИЈЕ ФУНКЦИОНАЛНОСТИ</strong> или <strong>ПРИКЛАДНОСТИ ЗА ОДРЕЂЕНЕУ НАМЕНУ</strong>. Погледајте ГНУ-ову општу јавну лиценцу за више информација.\n\nТребало би да сте добили [{{SERVER}}{{SCRIPTPATH}}/COPYING примерак ГНУ-ове опште јавне лиценце] заједно са овим програмом. Ако нисте, пишите на Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA или [//www.gnu.org/licenses/old-licenses/gpl-2.0.html прочитајте овде].",
        "version-software": "Инсталирани софтвер",
        "version-software-product": "Производ",
-       "version-software-version": "Ð\92еÑ\80зиÑ\98а",
+       "version-software-version": "Ð\98здаÑ\9aе",
        "version-entrypoints": "Адресе улазне тачке",
        "version-entrypoints-header-entrypoint": "Улазна тачка",
        "version-entrypoints-header-url": "Адреса",
        "version-libraries": "Инсталиране библиотеке",
        "version-libraries-library": "Библиотека",
-       "version-libraries-version": "Ð\92еÑ\80зиÑ\98а",
+       "version-libraries-version": "Ð\98здаÑ\9aе",
        "version-libraries-license": "Лиценца",
        "version-libraries-description": "Опис",
        "version-libraries-authors": "Аутори",
        "redirect-value": "Вредност:",
        "redirect-user": "Кориснички ID",
        "redirect-page": "ID странице",
-       "redirect-revision": "Ð\92еÑ\80зиÑ\98а странице",
+       "redirect-revision": "Ð\98змена странице",
        "redirect-file": "Назив датотеке",
        "redirect-not-exists": "Вредност није пронађена",
        "fileduplicatesearch": "Претрага дупликата",
        "compare-title-not-exists": "Наведени наслов не постоји.",
        "compare-revision-not-exists": "Наведена измена не постоји.",
        "diff-form": "Разлике",
+       "diff-form-submit": "Прикажи разлике",
        "permanentlink": "Стална веза",
        "dberr-problems": "Дошло је до техничких проблема.",
        "dberr-again": "Сачекајте неколико минута и поново учитајте страницу.",
        "expandtemplates": "Замена шаблона",
        "expand_templates_intro": "Ова посебна страница узима викитекст и мења све шаблоне у њему рекурзивно.\nТакође мења функције парсера као што је <code><nowiki>{{</nowiki>#language:…}}</code> и променљиве као што је <code><nowiki>{{</nowiki>CURRENTDAY}}</code>. \nЗаправо практично све што се налази између витичастих заграда.",
        "expand_templates_title": "Назив контекста; за {{СТРАНИЦА}} итд.:",
-       "expand_templates_input": "Унос:",
+       "expand_templates_input": "Унос викитекста:",
        "expand_templates_output": "Резултат",
        "expand_templates_xml_output": "XML излаз",
        "expand_templates_html_output": "Сиров HTML излаз",
        "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": "Додај још...",
        "date-range-from": "Од датума:",
        "date-range-to": "До датума:",
        "log-action-filter-rights-autopromote": "аутоматски",
        "log-action-filter-upload-upload": "Ново отпремање",
        "log-action-filter-upload-overwrite": "промена постојећег",
+       "authmanager-authplugin-setpass-failed-title": "Неуспешна промена лозинке",
        "authmanager-email-label": "Имејл",
        "authmanager-email-help": "Имејл адреса",
+       "authmanager-realname-label": "Право име",
+       "authmanager-realname-help": "Право име корисника",
        "authprovider-resetpass-skip-label": "Прескочи",
+       "cannotauth-not-allowed-title": "Приступ је одбијен",
        "changecredentials": "Промјена акредитива",
        "changecredentials-submit": "Промени",
        "removecredentials": "Уклањање акредитива",
        "credentialsform-account": "Назив налога:",
        "userjsispublic": "Напомена: JavaScript подстранице не би требале садржавати поверљиве информације будући да су видљиве другим корисницима.",
        "usercssispublic": "Напомена: CSS подстранице не би требале садржавати поверљиве информације будући да су видљиве другим корисницима.",
+       "edit-error-short": "Грешка: $1",
+       "edit-error-long": "Грешке:\n\n$1",
+       "revid": "измена $1",
        "rawhtml-notallowed": "&lt;html&gt; тагови не могу да се користе ван нормалних страница.",
        "gotointerwiki": "Напуштам пројекат {{SITENAME}}",
        "gotointerwiki-invalid": "Одабрани наслов је неисправан.",
diff --git a/languages/i18n/sty.json b/languages/i18n/sty.json
new file mode 100644 (file)
index 0000000..9b40b5c
--- /dev/null
@@ -0,0 +1,630 @@
+{
+       "@metadata": {
+               "authors": [
+                       "Khanmarat",
+                       "Sorbat",
+                       "Stephanecbisson",
+                       "Рустам Нурыев"
+               ]
+       },
+       "sunday": "Йәкшәмпе",
+       "monday": "Түшәмпе",
+       "tuesday": "Сишәмпе",
+       "wednesday": "Царшампы",
+       "thursday": "Пәйшәмпе",
+       "friday": "Йома",
+       "saturday": "Шәмпе",
+       "sun": "Йәк",
+       "mon": "Түш",
+       "tue": "Сиш",
+       "wed": "Цар",
+       "thu": "Пәй",
+       "fri": "Йом",
+       "sat": "Шәм",
+       "january": "январь",
+       "february": "февраль",
+       "march": "март",
+       "april": "апрель",
+       "may_long": "май",
+       "june": "июнь",
+       "july": "июль",
+       "august": "август",
+       "september": "сентябрь",
+       "october": "октябрь",
+       "november": "ноябрь",
+       "december": "декабрь",
+       "january-gen": "январь",
+       "february-gen": "февраль",
+       "march-gen": "март",
+       "april-gen": "апрель",
+       "may-gen": "май",
+       "june-gen": "июнь",
+       "july-gen": "июль",
+       "august-gen": "август",
+       "september-gen": "сентябрь",
+       "october-gen": "октябрь",
+       "november-gen": "ноябрь",
+       "december-gen": "декабрь",
+       "jan": "янв",
+       "feb": "фев",
+       "mar": "мар",
+       "apr": "апр",
+       "may": "май",
+       "jun": "июн",
+       "jul": "июл",
+       "aug": "авг",
+       "sep": "сен",
+       "oct": "окт",
+       "nov": "ноя",
+       "dec": "дек",
+       "pagecategories": "{{PLURAL:$1|1=Категория|Категориялар}}",
+       "category_header": "«$1» категориятағы питләр",
+       "subcategories": "Эцке категориялар",
+       "category-media-header": "«$1» категориясынтағы файллар",
+       "category-empty": "Пы категория ҡәсерге уаҡытта пуш.",
+       "hidden-categories": "{{PLURAL:$1|Йәшерен категория|Йәшерен категориялар}}",
+       "category-subcat-count": "{{PLURAL:$2|1=Пы төргөнтә түмәнтәге астөргөн кенә пар. |Пы төргөнтә пулған $2-тән {{PLURAL:$1|астөргөн|астөргөннәр}} $1 ҡына күрсәтеләте.}}",
+       "category-article-count": "{{PLURAL:$2|1=Пы төргөнтә пер генә пит пар. Пы категориятан пулған {{PLURAL:$2|пулған|пулғаннар}}}} $2 -тән {{PLURAL:$1|$1 пит күрсәтелгән|$1 питләр күрсәтелгәннәр}}.",
+       "category-file-count": "{{PLURAL:$2|Пы категорията пер генә файл пар.|Категориятағы $2 файлныңҡы {{PLURAL:$1|$1 файлы күргәселгән}}.}}",
+       "listingcontinuesabbrev": "ҡушлау",
+       "noindex-category": "Индексланмайтығын питләр",
+       "broken-file-category": "Файлға эшләмәйтеген ссылкалары пулған питләр",
+       "about": "Сүрәтләмә",
+       "newwindow": "(йаңа тәрәсәтә)",
+       "cancel": "Пулмайын иткәле",
+       "mytalk": "Уйлашыу",
+       "navigation": "Навигация",
+       "and": "&#32;әм",
+       "namespaces": "Исемнәрнең киңнеге",
+       "variants": "Вариантлар",
+       "navigation-heading": "Навигация",
+       "returnto": "$1 питкә ҡайтыу.",
+       "tagline": "{{SITENAME}} проекттан",
+       "help": "Пелешмә",
+       "search": "Эстәү",
+       "searchbutton": "Тапҡалы",
+       "searcharticle": "‎Күцкәле",
+       "history": "Тариҡ",
+       "history_short": "Тариҡ",
+       "printableversion": "Пастырыр өцөн версия",
+       "permalink": "Келәң ссылка",
+       "view": "Ҡарау",
+       "view-foreign": "$1 сайтта ҡарағалы",
+       "edit": "Төсәткәле",
+       "create": "Пултырғалы",
+       "create-local": "Локаль сүрәтләмәне өстәгәле",
+       "delete": "Йуҡ иткәле",
+       "newpage": "Йаңа пит",
+       "talkpagelinktext": "уйлашыу",
+       "personaltools": "Персональ ҡораллар",
+       "talk": "Уйлап ҡарау",
+       "views": "Ҡараулар",
+       "toolbox": "‎Ҡораллар",
+       "otherlanguages": "Пашҡа телләртә",
+       "redirectedfrom": "($1 питтән йебәрелгән)",
+       "redirectpagesub": "Йусыҡлау пит",
+       "redirectto": "Йусыҡлағалы",
+       "lastmodifiedat": "Пы пит $1гә, $2 пуйта суңҡа төсәйтелгән.",
+       "jumpto": "Анта күцкәле:",
+       "jumptonavigation": "навигация",
+       "jumptosearch": "эстәү",
+       "aboutsite": "‎{{SITENAME}} турлы",
+       "aboutpage": "Project:Сүрәтләмә",
+       "copyright": "Эцтәлеге $1 лицензия пелән кенә ацыҡ (пашҡа исем күргәселмәсә).",
+       "copyrightpage": "{{ns:project}}:Авторның ҡаҡы",
+       "currentevents": "Ҡәсерге пулған ҡәлләр",
+       "currentevents-url": "Project:Ҡәсерге пулған ҡәлләр",
+       "disclaimers": "Пелеклелектән паш тартыу",
+       "disclaimerpage": "Project:Пелеклелектән паш тартыу",
+       "edithelp": "Төсәтеүгә йәртәм",
+       "mainpage": "Паш пит",
+       "mainpage-description": "Паш пит",
+       "portal": "Перләшмә",
+       "portal-url": "Project:Порталның перләшмәсе",
+       "privacy": "Серне саҡлау сәйәсәт",
+       "privacypage": "Project:Серне саҡлайтығын сәйәсәт",
+       "retrievedfrom": "Аҡма — «$1»",
+       "youhavenewmessages": "{{PLURAL:$3|Сестә}} $1 ($2) пар.",
+       "youhavenewmessagesfromusers": "{{PLURAL:$4|Сескә}} {{PLURAL:$3|$3 ҡатнашыуцытан}} $1 килте ($2).",
+       "newmessageslinkplural": "{{PLURAL:$1|1=йаңа ҡәбәр|999=йаңа ҡәбәрләр}}",
+       "newmessagesdifflinkplural": "{{PLURAL:$1|1=суңҡы пашҡартыу|999=суңҡы пашҡартыулар}}",
+       "editsection": "төсәткәле",
+       "editold": "төсәткәле",
+       "viewsourceold": "пашлапҡы кодны ҡарағалы",
+       "editlink": "төсәткәле",
+       "viewsourcelink": "паш кодны ҡарағалы",
+       "editsectionhint": "$1 ‎пүлекне төсәткәле",
+       "toc": "Эцтәлек",
+       "site-atom-feed": "$1 — Atom тасма",
+       "page-atom-feed": "«$1» — Atom-тасма",
+       "red-link-title": "$1 (пы пит йуҡ)",
+       "nstab-main": "Мәҡәлә",
+       "nstab-user": "Ҡатнашыуцы",
+       "nstab-special": "Ҡесмәт пит",
+       "nstab-project": "Проетның пите",
+       "nstab-image": "Файл",
+       "nstab-mediawiki": "Пелтереү",
+       "nstab-template": "Ҡалып",
+       "nstab-category": "Категория",
+       "mainpage-nstab": "Паш пит",
+       "nosuchspecialpage": "Мынтайын ҡесмәт пит йуҡ",
+       "nospecialpagetext": "<strong>Сес сураған ҡесмәт пит йуҡ.</strong>\n* Ҡесмәт питләр күцермәлекне ҡараң: [[Special:SpecialPages|{{int:specialpages}}]].",
+       "badtitle": "Йарамаған исем",
+       "badtitletext": "Питнең суралған исеме төрөс түгел, пуш йә интервикиныңҡы исеме төрөс күрсәтелмәгән. Пәйәкпәр, исемтә йарамаған символлар пар ты.",
+       "viewsource": "Вики-текстны ҡарау",
+       "viewsource-title": "$1 питнең паш текстын ҡарау",
+       "viewsourcetext": "Сес пы питнеңке паш текстын ҡарап күцерә аласыс.",
+       "userlogin-yourname": "Ҡулланыуцыныңҡы исеме",
+       "userlogin-yourname-ph": "Исәп йасмағысныңҡы исемен кергесең",
+       "userlogin-yourpassword": "Пароль",
+       "userlogin-yourpassword-ph": "Үсегеснең парольны йасың",
+       "createacct-yourpassword-ph": "Парольны йасың",
+       "createacct-yourpasswordagain": "Парольны төрөс тигәле",
+       "createacct-yourpasswordagain-ph": "Парольны тағын та йасың",
+       "userlogin-remembermypassword": "Системата ҡалғалы",
+       "login": "Кергәле",
+       "userlogin-noaccount": "Исәп йасмағыс йуҡ ма?",
+       "userlogin-joinproject": "{{SITENAME}} ‎проектҡа ҡушылғалы",
+       "createaccount": "Йаңа ҡатнашыуцыны теркәү",
+       "userlogin-resetpassword-link": "Паролеғысны оноттоғос ма?",
+       "userlogin-helplink2": "Керер өцөн йәртәм",
+       "createacct-emailoptional": "Электрон почтағысның адресы (тейешлецә түгел)",
+       "createacct-email-ph": "Электрон почтағысның адресын йасың",
+       "createacct-submit": "Исәп йасманы пултырғалы",
+       "createacct-benefit-heading": "{{SITENAME}} — сеснең шигелле кешеләрнең эше.",
+       "createacct-benefit-body1": "{{PLURAL:$1|төсәтеү}}",
+       "createacct-benefit-body2": "{{PLURAL:$1|мәҡәлә|мәҡәләнең}}",
+       "createacct-benefit-body3": "Суңҡы уаҡыттағы {{PLURAL:$1|ҡатнашыуцы}}",
+       "loginlanguagelabel": "Тел: $1",
+       "pt-login": "Кергәле",
+       "pt-login-button": "Кергәле",
+       "pt-createaccount": "Йаңа аккаунт пултырғалы",
+       "pt-userlogout": "Цыҡҡалы",
+       "passwordreset": "Парольны пөтөрөү",
+       "bold_sample": "Ҡалын пелән йасыу",
+       "bold_tip": "Ҡалын пелән йасыу",
+       "italic_sample": "Курсив пелән йасыу",
+       "italic_tip": "Курсив пелән йасыу",
+       "link_sample": "Ссылканыңҡы төп исеме",
+       "link_tip": "Эцке ссылка",
+       "extlink_sample": "http://www.example.com ссылканың төп исеме",
+       "extlink_tip": "Тышҡы ссылка (http:// префиксны онотмаң)",
+       "headline_sample": "Төп исем",
+       "headline_tip": "2-нце ҡат төп исем",
+       "nowiki_sample": "Мынта форматлау кәрәкмәйтеген текстны өстәң",
+       "nowiki_tip": "Вики-форматлауға ҡолаҡ салмау",
+       "image_tip": "Кергеселгән файл",
+       "media_tip": "Файлға ссылка",
+       "sig_tip": "Ҡул ҡуйыуығыс пелән уаҡыт",
+       "hr_tip": "Горизонталь цыйыҡ (кел ҡулланмаң)",
+       "summary": "Пашҡартыуларны аңнатыу:",
+       "minoredit": "Кецкенә пашҡартыу",
+       "watchthis": "Пы питне көсәткәле",
+       "savearticle": "Питне йасып ҡуйғалы",
+       "preview": "Алттан ҡарау",
+       "showpreview": "Алттан ҡарап цығыу",
+       "showdiff": "Кергеселгән пашҡартыулар",
+       "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-адресы ҡулланылаты. Әгәр сес аноним ҡулланыуцы пулсағыс, сескә йебәрелмәгән ҡатлар алтым тисәгес (пер IP-адрес кән ҡулланыуцыларта пулғалы мөмкин), пашҡа мынтайын аңнашылмауцылыҡлар килеп цыҡмасын өцөн, системаға керең йә теркәлең.",
+       "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» исемле исәп йасма йуҡ.",
+       "clearyourcache": "<strong>Искәрмә:</strong> Питне саҡлағаннан суң пашҡартыуларығыс күренер өцөн, браузерығысныңҡы кэшын тасартҡалы кәрәк пулыр.\n* <strong>Firefox / Safari:</strong> <em>Shift</em> төймәгә пасып, йебәрмәйен, <em>Йаңартҡалы</em> төймәгә пасың, йә <em>Ctrl-F5</em> йә <em>Ctrl-R</em> (Mac-та <em>⌘-R</em>) төймәләргә пасың\n* <strong>Google Chrome:</strong> <em>Ctrl-Shift-R </em>төймәгә пасың (Mac-та <em>⌘-Shift-R</em>)\n* <strong>Internet Explorer:</strong> <em>Ctrl </em>төймәгә пасып, йебәрмәйен, <em>Йаңартҡалы</em>төймәгә пасың, йә <em>Ctrl-F5 </em>төймәгә пасың\n* <strong>Opera:</strong> <em>Ҡораллар → Ҡоролошлар</em> (Mac-та <em>Opera → Ҡоролошлар</em>) пүлеккә күцең, суңынан <em>Ҡауапсыслыҡ → Кереүләр тариҡны тасартыу → Рәсемнәр кэшлау</em> менюта кэш тасартыуны сайлаң",
+       "previewnote": "<strong>Онотмаң, пы алттан ҡарап цығыу ғына.</strong>\nПашҡартыуларығыс әле саҡланмаған!",
+       "continue-editing": "Төсәтеүне ҡушлағалы",
+       "editing": "«$1» битне төсәтеү",
+       "creating": "«$1» питне пултырыу",
+       "editingsection": "«$1» питтә төсәтеү",
+       "templatesused": "Пы питтә ҡулланылған {{PLURAL:$1|1=ҡалып|ҡалыплар}}:",
+       "templatesusedpreview": "Алттан ҡарау режимта ҡулланылған {{PLURAL:$1|1=ҡалып|ҡалыплар}}:",
+       "template-protected": "(саҡланҡан)",
+       "template-semiprotected": "(өлөшө пелән саҡланҡан)",
+       "hiddencategories": "Пы пит $1 {{PLURAL:$1|йәшерен төргөнкә|$1 йәшерен төргөннәргә}} керәте:",
+       "permissionserrors": "Кереүнең ҡаҡ ҡатасы",
+       "permissionserrorstext-withaction": "$2 әмәлен ҡыла алмайсыс. {{PLURAL:$1|1=сәбәбе пуйынца|сәбәпләре пуйынца}}:",
+       "recreate-moveddeleted-warn": "Саҡлыҡ: Сес элек йуҡ ителгән питне йаңатан ҡылмаҡцы пуласыс.\n\n* Сескә пы питне йаңатан ҡылыу кәрәклекне йаңатан уйлап ҡараң.\nТүмәнтә питнең йуҡ итеү пелән исемен пашҡартыу журнал пар.",
+       "moveddeleted-notice": "Пы пит уйылған. Пелешмә өцөн астыта пөтөрөү пелән исем алмаштырыу журналларның йасылғаннары күргәселгән.",
+       "content-model-wikitext": "вики-текст",
+       "undo-failure": "Аралыҡ пашҡартыулар туры килмәгәнкә, төсәтеүне кире алып пулмайты.",
+       "viewpagelogs": "Пы питнең журналын ҡарағалы",
+       "currentrev-asof": "$1, ҡәсерге версия",
+       "revisionasof": "$1 версия",
+       "revision-info": "$1 аңышы; {{GENDER:$6|$2}}$7",
+       "previousrevision": "← Алттағы",
+       "nextrevision": "Мынан суңҡысы →",
+       "currentrevisionlink": "Ҡәсерге версия",
+       "cur": "ҡәсерге",
+       "last": "алттағы",
+       "histlegend": "Аңышларны сайлау: сес туры килтергәле теләгән питләрнең аңышларын сайлап алың та  {{int:compare-submit}}.<br /> -ға пасың. Аңнатмалар: ({{int:cur}}) — ҡәсерге аңыштан айырмалар; ({{int:last}}) — алтта пулған аңыштан айырмалар; {{int:minoreditletter}} — сур пулмаған айырмалар.",
+       "history-fieldset-title": "Төсәтеүләрне эстәү",
+       "histfirst": "иң элекеләре",
+       "histlast": "иң суңҡылары",
+       "history-feed-title": "Пашҡартыулар тариғы",
+       "history-feed-description": "Пы питнең викитағы пашҡартыулар тариғы",
+       "history-feed-item-nocomment": "$2-та $1",
+       "rev-delundel": "күргәскәле/йәшергәле",
+       "mergelog": "Перләштереүләр журнал",
+       "history-title": "$1 питенең пашҡартыу тариғы",
+       "difference-title": "$1 — версиялар арасынтағы айырмалар",
+       "lineno": "$1 йул:‎",
+       "compareselectedversions": "Сайланҡан аңышларны туры килтергәле",
+       "editundo": "пулмайын иткәле",
+       "diff-empty": "(айырмалар йуҡ)",
+       "diff-multi-sameuser": "(пы ҡулланыуцының {{PLURAL:$1|аралыҡ версия $1|аралыҡ версиялары $1}} күргәселмәгән)",
+       "diff-multi-otherusers": "({{PLURAL:$2|ҡатнашыуцының|$2 ҡатнашыуцыларның}} {{PLURAL:$1|ара аңышы|$1 арадаш аңышлары}} күргәселмәгән)",
+       "searchresults": "Эстәүнең йомҡаҡлары",
+       "searchresults-title": "«$1»ны эстәгәле",
+       "prevn": "алттағы {{PLURAL:$1|$1}}",
+       "nextn": "киләсе {{PLURAL:$1|$1}}",
+       "prevn-title": "Алттағы $1 {{PLURAL:$1|1=йасма|йасмалар}}",
+       "nextn-title": "{{PLURAL:$1|Киләсе $1 йасма}}",
+       "shown-title": "$1 пер питтә {{PLURAL:$1|йасыу|йасыулар}} күргәскәле",
+       "viewprevnext": "($1 {{int:pipe-separator}} $2) ($3) ҡарап паҡҡалы",
+       "searchmenu-exists": "Пы викита [[:$1]] тигән пит пар",
+       "searchmenu-new": "<strong>«[[:$1]]» Вики-проектта пит йасағалы!</strong> {{PLURAL:$2|0=|Шалай уҡ, эстәгән арағыстағы табылған питне караң.|Шалай уҡ, эстәгәнегестә табылған питләрне караң.}}",
+       "searchprofile-articles": "Төп питләр",
+       "searchprofile-images": "Мультимедиа",
+       "searchprofile-everything": "Пөтөн йертә",
+       "searchprofile-advanced": "Киңәйтелгән",
+       "searchprofile-articles-tooltip": "$1 тә эстәү",
+       "searchprofile-images-tooltip": "Файлларны эстәү",
+       "searchprofile-everything-tooltip": "Пөтөн питләртә (уйлашыу питләртә тә) эстәү",
+       "searchprofile-advanced-tooltip": "Пирелгән исемнәр киңнектә эстәгәгәле",
+       "search-result-size": "$1 ({{PLURAL:$2|1=$2 сүс|$2 сүс}})",
+       "search-result-category-size": "$1 {{PLURAL:$1|аса}} ($2 {{PLURAL:$2|астөргөн}}, $3 {{PLURAL:$3|файл}})",
+       "search-redirect": "($1 питтән йусыҡлау)",
+       "search-section": "($1 пүлек)",
+       "search-file-match": "(файлның эцтәлеге пелән туры киләте)",
+       "search-suggest": "Пәйәкпәр, ошоно эстәйсес пулыр: $1",
+       "searchall": "пөтөннәйе",
+       "search-showingresults": "{{PLURAL:$4|<strong>$3</strong> нәтижәтән <strong>$1</strong>| <strong>$3</strong> нәтижәләртән <strong>$1 — $2</strong>}}",
+       "search-nonefound": "Сурауға туры килгән йауаплар табылматы.",
+       "mypreferences": "Ҡороулар",
+       "group-bot": "Ботлар",
+       "group-sysop": "Администраторлар",
+       "grouppage-bot": "{{ns:project}}:Ботлар",
+       "grouppage-sysop": "{{ns:project}}:Администраторлар",
+       "right-writeapi": "Йасыр өцөн API-ны ҡулланыу",
+       "newuserlogpage": "Йаңа ҡулланыуцыларны теркәү журналы",
+       "rightslog": "Ҡатнашыуцының ҡаҡлары журналы",
+       "action-edit": "пы питне төсәткәле",
+       "action-createaccount": "Пы исәп йасманы ҡылыу",
+       "enhancedrc-history": "тариҡ",
+       "recentchanges": "Йаңа төсәтеүләр",
+       "recentchanges-legend": "Суңҡы төсәтеүлернең ҡоролошо",
+       "recentchanges-summary": "Төрлө питләртә эшләнкән суңҡы пашҡартыуларның күцермәлеге",
+       "recentchanges-noresult": "Күрсәтелгән уаҡытта тейешле шартларға туры килгән пашҡартыулар йуҡ.",
+       "recentchanges-feed-description": "Викита суңҡы пашҡартыуларны көсәтеү.",
+       "recentchanges-label-newpage": "Пы төсәтеү йаңа пит ҡылтырты",
+       "recentchanges-label-minor": "Пы сур пулмаған төсәтеү",
+       "recentchanges-label-bot": "Пы төсәтеүне бот ҡылған",
+       "recentchanges-label-unpatrolled": "Пы төсәтеүне әле иц кем тикшермәте",
+       "recentchanges-label-plusminus": "Питнең сурлығы ошо ҡәтәр байтҡа алмашынты",
+       "recentchanges-legend-heading": "<strong>Аңнатма:</strong>",
+       "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (шалай уҡ [[Special:NewPages|йаңа питләр күцермәлеген]] ҡараң)",
+       "rcfilters-filterlist-title": "Фильтрлар",
+       "rcfilters-filterlist-noresults": "Иц фильтр табылматы",
+       "rcnotefrom": "Астта <strong>$3, $4</strong> -ға туры килгән {{PLURAL:$5|пашҡартыулар күргәселгән}} (<strong>$1</strong> артыҡ түгел).",
+       "rclistfrom": "$3 $2 алып йаңа пашҡартыуларны күргәскәле",
+       "rcshowhideminor": "кецкенә төсәтеүләр $1",
+       "rcshowhideminor-show": "Күргәскәле",
+       "rcshowhideminor-hide": "Йәшергәле",
+       "rcshowhidebots": "ботларны $1",
+       "rcshowhidebots-show": "Күргәскәле",
+       "rcshowhidebots-hide": "Йәшергәле",
+       "rcshowhideliu": "$1 танылған ҡулланыуцылар",
+       "rcshowhideliu-show": "Күргәскәле",
+       "rcshowhideliu-hide": "Йәшергәле",
+       "rcshowhideanons": "танылмаған ҡулланыуцылар $1",
+       "rcshowhideanons-show": "Күргәскәле",
+       "rcshowhideanons-hide": "Йәшергәле",
+       "rcshowhidepatr": "$1 ҡараған төсәтеүләр",
+       "rcshowhidemine": "үс төсәтеүләр $1",
+       "rcshowhidemine-show": "Күргәскәле",
+       "rcshowhidemine-hide": "Йәшергәле",
+       "rclinks": "Суңҡы $2 көн арата йасалған $1 пашҡартыуларны күргәскәле",
+       "diff": "Төрлө",
+       "hist": "тариҡ",
+       "hide": "Йәшергәле",
+       "show": "Күргәскәле",
+       "minoreditletter": "м",
+       "newpageletter": "Й",
+       "boteditletter": "б",
+       "rc-change-size-new": "Төсәткәннән суңҡы сурлыҡ: $1 {{PLURAL:$1|1=байт|байт}}",
+       "rc-old-title": "\"$1\" итеп пашта ҡылынҡан",
+       "recentchangeslinked": "Пәйләнкән төсәтеүләр",
+       "recentchangeslinked-feed": "Пәйле пулған пашҡартыулар",
+       "recentchangeslinked-toolbox": "Пәйле төсәтеүләр",
+       "recentchangeslinked-title": "\"$1\" өцөн пәйләнкән төсәтеүләр",
+       "recentchangeslinked-summary": "Пы, күрсәтелгән пит йебәрәтеген (йә күрсәтелгән төргөнкә керәтегән), питләртә суңҡы пашҡартыуларның күцермәлеге. [[Special:Watchlist|көсәтеү күцермәлегегескә]] керәтеген питләр '''ҡалын''' итеп күрсәтелгән.",
+       "recentchangeslinked-page": "Питнең төп исеме:",
+       "recentchangeslinked-to": "Кирецә, пы питкә пәйләнкән питләртәге пашҡартыуларны күргәскәле",
+       "upload": "Файлны төйәгәлә",
+       "uploadlogpage": "Төйәүләр журнал",
+       "filedesc": "Ҡысҡа аңнатыу",
+       "license": "Лицензиясы:",
+       "license-header": "Лицензирование",
+       "imgfile": "файл",
+       "listfiles": "Файлларның күцермәлеге",
+       "file-anchor-link": "Файл",
+       "filehist": "Файлның тарығы",
+       "filehist-help": "Үткән уаҡытта файлныңҡы нинтәйен пулғанын күргегес килсә, көнө/уаҡытына пасың.",
+       "filehist-revert": "ҡайтарғалы",
+       "filehist-current": "ҡәсерге",
+       "filehist-datetime": "Көнө/уаҡыты",
+       "filehist-thumb": "Уаҡ рәсем",
+       "filehist-thumbtext": "$1 версиятан пулған сүрәт",
+       "filehist-nothumb": "Миниатюрасы йуҡ",
+       "filehist-user": "Ҡатнашыуцы",
+       "filehist-dimensions": "Сурлығы",
+       "filehist-comment": "Искәрмә",
+       "imagelinks": "Файлны ҡулланыу",
+       "linkstoimage": "Пы файлға {{PLURAL:$1|1=пит|$1 пит}} йебәрәте:",
+       "linkstoimage-more": "Пы файлға $1-на пашҡа та  {{PLURAL:$1|пит}} ссылка ҡылатылар.\nТүмәнтәге күцермәлектә пы файлға $1 {{PLURAL:$1|ссылка}} ғына күргәселгән.\nШалай уҡ тулы күцермәлекне ҡарап пулаты.",
+       "nolinkstoimage": "Пы файлға йебәргән питләр йуҡ.",
+       "linkstoimage-redirect": "$1 (файл йусыҡлау) $2",
+       "sharedupload-desc-here": "Пы файл $1-нан, ул пашҡа проектларта ҡулланыла алаты. Файл турлы [$2 сүрәтләү пите] тулыраҡ пелешмә түмәнтәрәк пирелгән.",
+       "filepage-nofile": "Мынтайын исемле файл йуҡ.",
+       "upload-disallowed-here": "Сес пы файлны йаңатан йастыра алмайсыс.",
+       "randompage": "Уйланмаған мәҡәлә",
+       "statistics": "Статистика",
+       "double-redirect-fixer": "Йаңа йусыҡлауның төсәтеүцесе",
+       "nbytes": "$1 {{PLURAL:$1|байт}}",
+       "nmembers": "$1 {{PLURAL:$1|объект}}",
+       "prefixindex": "Питләрнең исемнәренең пашы пуйынца күстәргец",
+       "listusers": "Ҡатнашыуцыларның күцермәлеге",
+       "newpages": "Йаңа питләр",
+       "move": "Йаңа исем ататҡалы",
+       "pager-newer-n": "{{PLURAL:$1|$1 йаңараҡ}}",
+       "pager-older-n": "$1 {{PLURAL:$1|искерәк}}",
+       "booksources": "Китапларның аҡмалары",
+       "booksources-search-legend": "Китап турлы информация эстәү",
+       "booksources-search": "Эстәгәле",
+       "specialloguserlabel": "Ҡылыуцы:",
+       "speciallogtitlelabel": "Морат (исеме йә {{ns:user}}:ҡулланыуцының исеме):",
+       "log": "Журналлар",
+       "all-logs-page": "Пөтөн ацыҡ журналлар",
+       "alllogstext": "{{SITENAME}} сайтыныңҡы журналларының тула күцермәлеге.\nСес йомҡаҡларны журнал төрөнә, ҡатаншыуцының исеменә ҡарап (сур йә кецкенә ҡәрептән йасылыуына ҡарап ) йә күргәселгән питнең исеменә ҡарап (шулай уҡ сур йә кецкенә ҡәрептән йасылыуына ҡарап) сайлап ала аласыс.",
+       "logempty": "Журналта йарығытайын асмалар йуҡ.",
+       "allpages": "Пөтөн питләр",
+       "allarticles": "Пөтөн питләр",
+       "allpagessubmit": "Ҡылғалы",
+       "allpages-hide-redirects": "Йусыҡлауларны йәшергәле",
+       "categories": "Төргөннәр",
+       "listgrouprights-members": "(ҡатнашыуцыларның күцермәлеге)",
+       "emailuser": "Ҡатнашыуцыға ҡат",
+       "usermessage-editor": "Системалыҡ тапшырыу",
+       "watchlist": "Көсәтеү күцермәлек",
+       "mywatchlist": "Көсәтеү күцермәлек",
+       "watchlistfor2": "$1 $2-ға тип",
+       "watch": "Көсәткәле",
+       "unwatch": "Көсәтмәгәле",
+       "watchlist-details": "Көсәтеү күцермәлегегестә, уйлап ҡарау питләрне исәпләмәйен, {{PLURAL:$1|$1 пит}} пар.",
+       "wlheader-showupdated": "Сеснең суңҡы пашҡартыулартан суң пашҡарған питләр <strong>ҡалын</strong> шрифт пелән күргәселгән.",
+       "wlnote": "Түмәнтә $3 $4 уаҡытта суңҡы {{PLURAL:$2|1=сәғәт|$2 сәғәт}} эцтә эшләнкән {{PLURAL:$1|1=пашҡартыу|$1пашҡартыулар}} күргәселгән.",
+       "wlshowlast": "Суңҡы $1 сәғәт $2 көн эцентәгесен күргәскәле",
+       "watchlist-options": "Көсәтеү күцермәлекнең ҡоролошлары",
+       "enotif_reset": "Пөтөн питләрне ҡаралған тип пилгеләгәле",
+       "dellogpage": "Уйыуларның журналы",
+       "rollbacklink": "кире ҡайтарғалы",
+       "rollbacklinkcount": "$1 {{PLURAL:$1|1=төсәтеүне|төсәтеүне}} кире алғалы",
+       "protectlogpage": "Саҡлау журнал",
+       "protectedarticle": "[[$1]] питен йаҡлаған",
+       "modifiedarticleprotection": "[[$1]]-ныңҡы питенең йаҡлау ҡаты пашҡартылты",
+       "protect-default": "Пөтөн ҡулланыуцыларға ацыҡ",
+       "restriction-edit": "Төсәткәле",
+       "restriction-move": "Йаңа исем ҡушыу",
+       "namespace": "Исемнәрнең киңнеге:",
+       "invert": "Сайланҡанны әйләнтергәле",
+       "tooltip-invert": "Сайланҡан исемнәр арасынта (күрсәтелгән пулса, исемнәрнең пәйле киңнегентә тә) питләртәге пашҡартыуларны йәшерер өцөн пы пилгене ҡороң",
+       "namespace_association": "Пәйле киңнек",
+       "tooltip-namespace_association": "Сайланҡан исемнәр киңнек пелән пәйле пулған уйлап ҡаралған исемнәрнең киңнеген кергесер өцөн пы пилгене ҡороң",
+       "blanknamespace": "(Төп)",
+       "contributions": "{{GENDER:$1|ҡатнашыуцының}} ҡылған эше",
+       "contributions-title": "$1 исемле ҡатнашыуцының ҡылған эше",
+       "mycontris": "Кергескән эшләр",
+       "anoncontribs": "Кергескән эшләр",
+       "contribsub2": "{{GENDER:$3|$1}}-ның ҡылған эше ($2)",
+       "nocontribs": "Күргәселгән шартларға туры килгән пашҡартыулар табылматы.",
+       "uctop": "(ҡәсерге)",
+       "month": "Айтан пашлап (анан алттараҡ та):",
+       "year": "Йылтан пашлап (анан алттараҡ та):",
+       "sp-contributions-newbies": "Йаңа исәп йасмалартан ҡылынҡан эшне генә күргәскәле",
+       "sp-contributions-blocklog": "тыйыулар",
+       "sp-contributions-uploads": "төйәүләр",
+       "sp-contributions-logs": "журналлар",
+       "sp-contributions-talk": "уйлап ҡарау",
+       "sp-contributions-search": "Ҡылынҡан эшне эстәү",
+       "sp-contributions-username": "Ҡатнашыуцының IP-адресы йә исеме:",
+       "sp-contributions-toponly": "Иң суңҡы аңышлар пулған төсәтеүләрне генә күргәскәле",
+       "sp-contributions-newonly": "Йаңа пит ацатығын төсәтеүләрне генә күргәскәле",
+       "sp-contributions-submit": "Тапҡалы",
+       "whatlinkshere": "Мынта ссылкалар",
+       "whatlinkshere-title": "«$1» питкә йебәргән питләр",
+       "whatlinkshere-page": "Пит:",
+       "linkshere": "''[[:$1]]''' питкә киләсе питләр тайанатылар:",
+       "nolinkshere": "[[:$1]] питкә пер питтән тә ссылка йуҡ.",
+       "isredirect": "йусыҡлау пит",
+       "istemplate": "ҡушылыу",
+       "isimage": "файллы ссылка",
+       "whatlinkshere-prev": "{{PLURAL:$1|1=алттағы}} $1",
+       "whatlinkshere-next": "{{PLURAL:$1|1=киләсе}} $1",
+       "whatlinkshere-links": "← ссылкалар",
+       "whatlinkshere-hideredirs": "$1 йусыҡлаулар",
+       "whatlinkshere-hidetrans": "Кергесеүләр $1",
+       "whatlinkshere-hidelinks": "$1 ссылкалар",
+       "whatlinkshere-hideimages": "$1 файлның ссылкалары",
+       "whatlinkshere-filters": "Фильтрлар",
+       "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",
+       "infiniteblock": "пилгеле пер уаҡытсыс",
+       "blocklink": "тыйғалы",
+       "contribslink": "өлөш",
+       "blocklogpage": "Тыйыулар журнал",
+       "blocklogentry": "[[$1]] $2 $3-ҡа пикләте",
+       "reblock-logentry": "[[$1]] ҡатнашыуцының тыйыуын пашҡартҡан, пөтөү уаҡыты — $2 $3",
+       "block-log-flags-nocreate": "исәп йасманың теркелеүе тыйылған",
+       "proxyblocker": "Проксины пикләү",
+       "movelogpage": "Исем алмаштырыуның журналы",
+       "export": "Питләрне цығарыу",
+       "thumbnail-more": "Сурайтҡалы",
+       "importlogpage": "Кергесеү журнал",
+       "tooltip-pt-userpage": "{{GENDER:|Сеснең}} ҡатнашыуцының пите",
+       "tooltip-pt-mytalk": "{{GENDER:|Сеснең}} уйлашыу питегес",
+       "tooltip-pt-preferences": "{{GENDER:|Сеснең}} ҡоролошларығыс",
+       "tooltip-pt-watchlist": "Сес көсәтеп парған питләрнең күцермәлеге",
+       "tooltip-pt-mycontris": "{{GENDER:|Сеснең}} төсәтеүләрнең күцермәлеге",
+       "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-protect": "Питне пашҡартыулартан саҡлағалы",
+       "tooltip-ca-delete": "Пы питне йуҡ иткәле",
+       "tooltip-ca-move": "Питкә йаңа исем пиргәле",
+       "tooltip-ca-watch": "Пы питне сеснең көсәтеү күцермәлеккә өстәгәле",
+       "tooltip-ca-unwatch": "Пы питне көсәтеү күцермәлегемнән алып ташлағалы",
+       "tooltip-search": "{{SITENAME}} эцтә эстәгәле",
+       "tooltip-search-go": "Шалай уҡ аталған питкә күцкәле",
+       "tooltip-search-fulltext": "Ошонтайын эцтәлекле питләрне тапҡалы",
+       "tooltip-p-logo": "Паш питкә күцкәле",
+       "tooltip-n-mainpage": "Паш питкә күцкәле",
+       "tooltip-n-mainpage-description": "Паш питкә күцкәле",
+       "tooltip-n-portal": "Проект турлы, мынта нимә ҡылғалы йарағаны турлы, ҡайта нимә урнашҡан турлы",
+       "tooltip-n-currentevents": "Ҡәсер пулып йатҡан пулған ҡәлләр турлы пелешмә",
+       "tooltip-n-recentchanges": "Суңҡы төсәтеүләрнең күцермәлеге",
+       "tooltip-n-randompage": "Көтөлмәгән питне ҡарап паҡҡалы",
+       "tooltip-n-help": "Пелешмәне алырлыҡ урын",
+       "tooltip-t-whatlinkshere": "Пы питкә тапшырған пөтөн питләрнең күцермәлеге",
+       "tooltip-t-recentchangeslinked": "Пы питтән тапшырылған питләрнең суңҡы пашҡартыулары",
+       "tooltip-feed-atom": "Пы пит өцөн Atom-ҡа трансляция",
+       "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-special": "Пы ҡесмәт пит, аны төсәткәле пулмайты",
+       "tooltip-ca-nstab-project": "Проектның пите",
+       "tooltip-ca-nstab-image": "Файлның пите",
+       "tooltip-ca-nstab-mediawiki": "MediaWiki-ның ҡәбәрләре пите",
+       "tooltip-ca-nstab-template": "Ҡалыпның пите",
+       "tooltip-ca-nstab-category": "Категорияның пите",
+       "tooltip-minoredit": "Пы пашҡартыуны пайтаҡ түгел итеп пилгеләгәле",
+       "tooltip-save": "Пашҡартыуларығысны ҡалтырғалы",
+       "tooltip-preview": "Питне алттан ҡарап цығыу; төсәтеүләрегесне ҡалтырыр алттан ҡулланың!",
+       "tooltip-diff": "Текстта эшләнкән үсегеснең пашҡартыуларығысны генә күргәскәле",
+       "tooltip-compareselectedversions": "Пы питнең ике сайланҡан аңышылары арасынтағы айырманы ҡарағалы.",
+       "tooltip-watch": "Пы питне көсәтеү күцермәлегемә өстәгәле",
+       "tooltip-rollback": "Пер пасыу пелән суңҡы пашҡартыуларны алып уйғалы",
+       "tooltip-undo": "Пулмайын итеүнең сәбәбен күргәсә алыу мөмкинцелеге пелән кергеселгән төсәтеүне уйып ташлағалы.",
+       "tooltip-summary": "Ҡысҡаца сүрәтләмә кергесең",
+       "simpleantispam-label": "Спамҡа ҡаршы тикшереү. Мыны <strong>ТУЛТЫРМАҢ</strong>!",
+       "pageinfo-title": "«$1» турлы ҡәбәр-пелем",
+       "pageinfo-header-basic": "Төп ҡәбәр-пелемнәр",
+       "pageinfo-header-edits": "Төсәтеүләр тариғы",
+       "pageinfo-header-restrictions": "Питне саҡлау",
+       "pageinfo-header-properties": "Питнең үслекләре",
+       "pageinfo-display-title": "Күренкән исем",
+       "pageinfo-default-sort": "Тик сайлау ацҡыц",
+       "pageinfo-length": "Питнеңке оссонноғо (байтларта)",
+       "pageinfo-article-id": "Питнең аңныштырғыцы",
+       "pageinfo-language": "Пы питнең теле",
+       "pageinfo-content-model": "Пит эцтәлекнең моделе",
+       "pageinfo-robot-policy": "Эстәйтеген роботлар индексацялағанннар",
+       "pageinfo-robot-index": "Рөҡсәт ителгән",
+       "pageinfo-robot-noindex": "Рөҡсәт ителмәгән",
+       "pageinfo-watchers": "Көсәткәннәрнең исәбе",
+       "pageinfo-few-watchers": "$1 асыраҡ {{PLURAL:$1|көсәтеүце}}",
+       "pageinfo-redirects-name": "Пы питкә йусыҡлауларның исәбе",
+       "pageinfo-subpages-name": "Пы питнең эцке питләре",
+       "pageinfo-subpages-value": "$1 ($2 {{PLURAL:$2|йусыҡлау}}; $3 {{PLURAL:$3|тик}})",
+       "pageinfo-firstuser": "Питне ҡылыуцы",
+       "pageinfo-firsttime": "Пит ҡылыуның датасы",
+       "pageinfo-lastuser": "Суңҡы төсәткер",
+       "pageinfo-lasttime": "Суңҡы төсәтеүнең датасы",
+       "pageinfo-edits": "Пөттөрә төсәтеүләр исәбе",
+       "pageinfo-authors": "Төрлө авторларның тулы исәбе",
+       "pageinfo-recent-edits": "Суңҡы уаҡыттағы төсәтеүләр ($1 уаҡытны)",
+       "pageinfo-recent-authors": "Суңҡы уаҡыттағы авторлар",
+       "pageinfo-magic-words": "{{PLURAL:$1|1=Сиғерле сүс|Сиғерле сүсләр}} ($1)",
+       "pageinfo-hidden-categories": "{{PLURAL:$1|1=Йәшерен категория|Йәшерен категориялар}} ($1)",
+       "pageinfo-templates": "{{PLURAL:$1|1=Ҡалып|Ҡалыплар}} ($1)",
+       "pageinfo-toolboxlink": "\tПит турлы ҡәбәр-пелемнәр",
+       "pageinfo-contentpage": "Эцтәлекле пит тип исәпләнәте",
+       "pageinfo-contentpage-yes": "Әйә",
+       "patrol-log-page": "Тикшереү журнал",
+       "previousdiff": "← Алттағы төсәтеү",
+       "nextdiff": "Мынан суңҡы төсәтеү →",
+       "widthheightpage": "$1 × $2, $3 {{PLURAL:$3 пит}}",
+       "file-info-size": "$1 × $2 {{PLURAL:$2|пиксель}}, файлның сурлығы: $3, MIME төр: $4",
+       "file-info-size-pages": "$1 × $2 пиксель, файл үлцәме: $3, MIME-тибы: $4, $5 {{PLURAL:$5|пит|питләр}}",
+       "file-nohires": "Йуғары ацыҡлыҡлы аңыш йуҡ",
+       "svg-long-desc": "SVG-файл, номиналь $1 × $2 {{PLURAL:$2|пиксель|}}, файлның сурлығы: $3",
+       "show-big-image": "Пашлапҡы файл",
+       "show-big-image-preview": "Алттан ҡарау уаҡыттағы сурлыҡ: $1.",
+       "show-big-image-other": "{{PLURAL:$2|1=Пашҡа сурлыҡ|Пашҡа сурлыҡлар}}: $1.",
+       "show-big-image-size": "$1 × $2 пиксель",
+       "metadata": "Метапиремнәр",
+       "metadata-help": "Пы файлта тик цифралы камера йә сканер пелән өстәлгән мәғлүмәтләр пар. Әгәр пы файл ҡороу уаҡытынан суң алмашынҡан пулса, аныңҡы ҡәйбер параметрлары төрөс пулмасҡа мөмкин.",
+       "metadata-fields": "Пы күцермәлеккә кергән метапиремнәрнең ҡырлары сүрәт питтә күрсәтелер. Ҡалғаннары килешеү пуйынца йәшерелер. * make * model * datetimeoriginal * exposuretime * fnumber * isospeedratings * focallength * artist * copyright * imagedescription * gpslatitude * gpslongitude * gpsaltitude",
+       "exif-orientation": "Кадрныңҡы утысылыуы",
+       "exif-xresolution": "Горизонталь сурлыҡ",
+       "exif-yresolution": "Вертикаль сурлыҡ",
+       "exif-datetime": "Файлны пашҡартыу көнө пелән уаҡыты",
+       "exif-make": "Камераны йасауцы",
+       "exif-model": "Камераның төрө",
+       "exif-software": "Ҡулланҡан программа",
+       "exif-exifversion": "Exif-ныңҡы версиясы",
+       "exif-colorspace": "Төсләрнең киңнеге",
+       "exif-datetimeoriginal": "Цын көнө пелән уаҡыты",
+       "exif-datetimedigitized": "Саннаштырыуның көнө пелән уаҡыты",
+       "exif-orientation-1": "Нормаль",
+       "namespacesall": "парысы",
+       "monthsall": "пөттөрә",
+       "imgmultipagenext": "алттағы пит →",
+       "imgmultigo": "Күцкәле!",
+       "imgmultigoto": "$1 питкә күцкәле",
+       "watchlisttools-clear": "Төсәтеү күцермәлекне тасартҡалы",
+       "watchlisttools-view": "Күцермәлектәге питләртә пашҡарыулар",
+       "watchlisttools-edit": "Күцермәлекне ҡарағалы та төсәткәле",
+       "watchlisttools-raw": "Тик текст итеп төсәтеү",
+       "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|уйлап ҡарау]])",
+       "redirect": "Файлның аңныштырғыцынан, ҡатнашыуцытан, питтән, версиятан йә журналтан йусыҡлау",
+       "redirect-summary": "Пы ҡесмәт пит файлға (файлның исеменән), питкә (версияның аңныштырғыцынан йә питенән), ҡатнашыуцының питен (ҡатнашыуцының исәп аңныштырғыцынан) йә ҡатнашыуцының питенә (аңныштырғыцның журналынан) йебәрәте.",
+       "redirect-submit": "Күцкәле",
+       "redirect-lookup": "Эстәү:",
+       "redirect-value": "Мәғнә:",
+       "redirect-user": "Ҡатнашыуцының аңыштырғыцы",
+       "redirect-page": "Питнең аңныштырғыцы",
+       "redirect-revision": "Питнең аңышы",
+       "redirect-file": "Файлның исеме",
+       "specialpages": "Атайы питләр",
+       "tag-filter": "[[Special:Tags|Пилгеләр]] фильтры:",
+       "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|Тамғалар}}]]: $2)",
+       "tags-active-yes": "Әйә",
+       "tags-active-no": "Йуҡ",
+       "tags-hitcount": "$1 {{PLURAL:$1|1=пашҡартыу|пашҡартыулар|пашҡартыуларны}}",
+       "logentry-delete-delete": "$1 $3 питне {{GENDER:$2|пөтөрөп ҡуйты}}",
+       "logentry-delete-restore": "$1 $3 ($4) питен {{GENDER:$2|йаңатан торғосто}}",
+       "logentry-delete-revision": "Ҡатнашыуцы $1 $3: $4 питенең {{PLURAL:$5|$5 версиялары|$5 версияларының|1=версиялар}} күренешен {{GENDER:$2|пашҡартты|пашҡартты}}.",
+       "revdelete-content-hid": "эцтәлеге йәшерелшән",
+       "logentry-move-move": "$1 $3 питнең исемен {{GENDER:$2| алмаштырты}}. Йаңа исеме: $4",
+       "logentry-move-move-noredirect": "$1 йусыҡ ҡалтырмайын $3 питне $4 итеп йаңа исем пирте",
+       "logentry-move-move_redir": "$1 йусыҡлап $3 питне $4 итеп күцерте",
+       "logentry-patrol-patrol-auto": "$1 $3 питнең $4 версиясын автоматиклап {{GENDER:$2|тикшерте}}",
+       "logentry-newusers-create": "{{GENDER:$2|ҡатнашыуцы}} $1 исәп йасманы ҡылты.",
+       "logentry-newusers-autocreate": "Автоматик килеш {{GENDER:$2| ҡатнашыуцының}} $1 исәп йасмасы ҡылынты",
+       "logentry-upload-upload": "$1 $3 {{GENDER:$2|төйәте}}",
+       "logentry-upload-overwrite": "$1 йаңа аңыш{{GENDER:$2||тейәте}} $3",
+       "searchsuggest-search": "{{SITENAME}}та эстәгәле‎",
+       "duration-days": "$1 {{PLURAL:$1|көн}}",
+       "randomrootpage": "Көтөлмәгән тамыр пит"
+}
index 372ae46..696bcfc 100644 (file)
        "cannotlogoutnow-title": "ఇప్పుడు లాగౌట్ అవలేరు",
        "cannotlogoutnow-text": "$1 ను వాడుతూండగా లాగౌట్ అవలేరు.",
        "welcomeuser": "స్వాగతం, $1!",
-       "welcomecreation-msg": "మీ ఖాతాని సృష్టించాం.\nమీ [[Special:Preferences|{{SITENAME}} అభిరుచులను]] మార్చుకోవడం మరువకండి.\nతెలుగు వికీపీడియాలో తెలుగులోనే రాయాలి. వికీలో రచనలు చేసే ముందు, కింది సూచనలను గమనించండి.\nతెలుగు {{SITENAME}}లో తెలుగులోనే రాయాలి. వికీలో రచనలు చేసే ముందు, కింది సూచనలను గమనించండి.\n*వికీని త్వరగా అర్థం చేసుకునేందుకు [[వికీపీడియా:5 నిమిషాల్లో వికీ|5 నిమిషాల్లో వికీ]] పేజీని చూడండి.\n*తెలుగులో రాసేందుకు ఇంగ్లీషు అక్షరాల ఉచ్ఛారణతో తెలుగు టైపు చేసే [[వికీపీడియా:టైపింగు సహాయం| టైపింగ్  సహాయం]] వాడవచ్చు. మరిన్ని ఉపకరణాల కొరకు [[కీ బోర్డు]] మరియు   తెరపై తెలుగు సరిగా లేకపోతే[[వికీపీడియా:Setting up your browser for Indic scripts|ఈ పేజీ]]  చూడండి.",
+       "welcomecreation-msg": "మీ ఖాతాని సృష్టించాం.\nమీరు మీ {{SITENAME}} [[Special:Preferences|అభిరుచులను]] మార్చుకోవచ్చు.",
        "yourname": "వాడుకరి పేరు:",
        "userlogin-yourname": "వాడుకరి పేరు",
        "userlogin-yourname-ph": "మీ వాడుకరి పేరును ఇవ్వండి",
index a838dbf..d0d8ceb 100644 (file)
        "nosuchusershort": "\"$1\" adında bir kullanıcı bulunmamaktadır. Yazılışı kontrol edin.",
        "nouserspecified": "Bir kullanıcı adı belirtmek zorundasınız.",
        "login-userblocked": "Bu kullanıcı engellenmiş. Giriş yapmaya izin verilmiyor.",
-       "wrongpassword": "Parolayı yanlış girdiniz. Lütfen tekrar deneyiniz.",
+       "wrongpassword": "Hatalı kullanıcı adı ya da parola girildi. Lütfen tekrar deneyin.",
        "wrongpasswordempty": "Boş parola girdiniz. Lütfen tekrar deneyiniz.",
        "passwordtooshort": "Parolalar en az {{PLURAL:$1|1 karakter|$1 karakter}} uzunluğunda olmalı.",
        "passwordtoolong": "Parolalar $1 karakterden uzun olamaz.",
        "grant-editmywatchlist": "İzleme listeni düzenle",
        "grant-editprotected": "Korumalı sayfaları Düzenle",
        "grant-patrol": "Sayfadaki değişiklikleri incele",
+       "grant-sendemail": "Diğer kullanıcılara e-posta gönder",
+       "grant-uploadeditmovefile": "Dosya yükle, yenisiyle değiştir ve taşı",
        "grant-uploadfile": "Dosya yükle",
        "grant-basic": "Basit haklar",
        "grant-viewdeleted": "Silinen dosya ve sayfaları görüntüle",
        "grant-viewmywatchlist": "İzleme listeni gör",
+       "grant-viewrestrictedlogs": "Kısıtlanmış günlük girdilerini görüntüle",
        "newuserlogpage": "Kullanıcı oluşturma günlüğü",
        "newuserlogpagetext": "Bu bir kullanıcı oluşturma günlüğüdür.",
        "rightslog": "Kullanıcı hakları günlüğü",
        "rightslogtext": "Bu, kullanıcı hakları değişiklikleri için bir günlüktür.",
        "action-read": "bu sayfayı okumaya",
        "action-edit": "bu sayfayı değiştirmeye",
-       "action-createpage": "sayfa oluşturmaya",
-       "action-createtalk": "tartışma sayfası oluşturmaya",
+       "action-createpage": "sayfayı oluştur",
+       "action-createtalk": "tartışma sayfasını oluştur",
        "action-createaccount": "bu kullanıcı hesabını oluşturmaya",
+       "action-autocreateaccount": "bu harici kullanıcı hesabını otomatik olarak oluştur",
        "action-history": "sayfa geçmişini görüntüle",
        "action-minoredit": "bu değişikliği küçük olarak işaretlemeye",
        "action-move": "bu sayfayı taşımaya",
        "action-upload_by_url": "bir URL adresinden bu dosyayı yüklemeye",
        "action-writeapi": "API yaz kullanmaya",
        "action-delete": "bu sayfayı silmeye",
-       "action-deleterevision": "bu revizyonu silmeye",
-       "action-deletedhistory": "bu sayfanın silinme geçmişini görmeye",
+       "action-deleterevision": "revizyonları sil",
+       "action-deletelogentry": "günlük girdilerini sil",
+       "action-deletedhistory": "sayfanın silme geçmişini görüntüle",
+       "action-deletedtext": "silinmiş revizyon metnini görüntüle",
        "action-browsearchive": "silinen sayfaları aramaya",
        "action-undelete": "sayfaları geri getir",
        "action-suppressrevision": "gizli sürümleri gözden geçir ve geri getir",
        "rcfilters-activefilters": "Etkin süzgeçler",
        "rcfilters-advancedfilters": "Gelişmiş süzgeçler",
        "rcfilters-limit-title": "Gösterilecek sonuçlar",
+       "rcfilters-limit-and-date-label": "$1 değişiklik, $2",
        "rcfilters-days-title": "Son günler",
        "rcfilters-hours-title": "Son saatler",
+       "rcfilters-days-show-days": "$1 gün",
+       "rcfilters-days-show-hours": "$1 saat",
        "rcfilters-quickfilters": "Kaydedilmiş süzgeçler",
        "rcfilters-quickfilters-placeholder-title": "Henüz hiçbir süzgeç kaydedilmedi",
        "rcfilters-quickfilters-placeholder-description": "Süzgeç ayarlarınızı kaydetmek ve sonrasında bunları kullanmak için, aşağıda Aktif Süzgeçler alanındaki yer imi simgesine tıklayın.",
        "rcfilters-filtergroup-userExpLevel": "Deneyim düzeyi (yalnızca kayıtlı kullanıcılar için)",
        "rcfilters-filter-user-experience-level-registered-label": "Kayıtlı",
        "rcfilters-filter-user-experience-level-registered-description": "Oturum açmış editörler.",
-       "rcfilters-filter-user-experience-level-unregistered-label": "Kayıtsız",
-       "rcfilters-filter-user-experience-level-unregistered-description": "Oturum açmamış editörler.",
+       "rcfilters-filter-user-experience-level-unregistered-label": "Kayıtlı olmayan",
+       "rcfilters-filter-user-experience-level-unregistered-description": "Oturum açmamış kullanıcılar.",
        "rcfilters-filter-user-experience-level-newcomer-label": "Yeni gelenler",
        "rcfilters-filter-user-experience-level-newcomer-description": "10'dan az düzenlemesi veya 4 günden az etkinliği olan kayıtlı kullanıcılar.",
        "rcfilters-filter-user-experience-level-learner-label": "Öğreniciler",
        "rcfilters-filter-minor-description": "Yazarın küçük olarak etiketlediği düzenlemeler.",
        "rcfilters-filter-major-label": "Küçük olmayan düzenlemeler",
        "rcfilters-filter-major-description": "Küçük olarak etiketlenmemiş düzenlemeler.",
+       "rcfilters-filtergroup-watchlistactivity": "İzleme listesi faaliyetleri",
+       "rcfilters-filter-watchlistactivity-unseen-label": "Görülmemiş değişiklikler",
+       "rcfilters-filter-watchlistactivity-seen-label": "Görülmüş değişiklikler",
        "rcfilters-filtergroup-changetype": "Değişiklik türü",
        "rcfilters-filter-pageedits-label": "Sayfa düzenlemeleri",
        "rcfilters-filter-pageedits-description": "Viki içeriği, tartışmalar, kategori açıklamalarındaki düzenlemeler...",
index d3fa9be..5ccc507 100644 (file)
        "tag-filter": "[[Special:Tags|מאַרקירונג]] פֿילטער:",
        "tag-filter-submit": "פֿילטער",
        "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|טאַג|טאַגן}}]]: $2)",
+       "tag-mw-new-redirect": "נייע ווייטערפֿירונג",
+       "tag-mw-undo": "אַנולירן",
        "tags-title": "טאַגן",
        "tags-intro": "דער בלאַט ווײַזט די טאַגן מיט וואס דאס ווייכווארג קען צייכענען אַ רעדאַגירונג, און זייער באַטייַט.",
        "tags-tag": "טאַג נאָמען",
index 3b03955..a28c457 100644 (file)
        "postedit-confirmation-created": "页面已创建。",
        "postedit-confirmation-restored": "页面已恢复。",
        "postedit-confirmation-saved": "您的编辑已保存。",
+       "postedit-confirmation-published": "您的编辑已发布。",
        "edit-already-exists": "不可以建立一个新页面。\n它已经存在。",
        "defaultmessagetext": "默认消息文本",
        "content-failed-to-parse": "未能将 $2 内容转换为 $1:$3",
index f1e7dbb..e1eef07 100644 (file)
@@ -100,7 +100,7 @@ abstract class Benchmarker extends Maintenance {
                                'name' => $name,
                                'count' => $stat->getCount(),
                                // Get rate per second from mean (in ms)
-                               'rate' => 1.0 / ( $stat->getMean() / 1000.0 ),
+                               'rate' => $stat->getMean() == 0 ? INF : ( 1.0 / ( $stat->getMean() / 1000.0 ) ),
                                'total' => $stat->getMean() * $stat->getCount(),
                                'mean' => $stat->getMean(),
                                'max' => $stat->max,
index a55b539..a21bc73 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /**
- * Remove hidden preferences from the database.
+ * Clean up user preferences from the database.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -19,6 +19,7 @@
  *
  * @file
  * @author TyA <tya.wiki@gmail.com>
+ * @author Chad <chad@wikimedia.org>
  * @see https://phabricator.wikimedia.org/T32976
  * @ingroup Maintenance
  */
 require_once __DIR__ . '/Maintenance.php';
 
 /**
- * Maintenance script that removes hidden preferences from the database.
+ * Maintenance script that removes bogus preferences from the database.
  *
  * @ingroup Maintenance
  */
 class CleanupPreferences extends Maintenance {
+       public function __construct() {
+               parent::__construct();
+               $this->mDescription = 'Clean up hidden preferences, removed preferences, and normalizes values';
+               $this->setBatchSize( 50 );
+               $this->addOption( 'dry-run', 'Print debug info instead of actually deleting' );
+               $this->addOption( 'hidden', 'Drop hidden preferences ($wgHiddenPrefs)' );
+               $this->addOption( 'unknown',
+                       'Drop unknown preferences (not in $wgDefaultUserOptions or a gadget or userjs)' );
+               // TODO: actually implement this
+               // $this->addOption( 'bogus', 'Drop preferences that have invalid/unaccepted values' );
+       }
+
+       /**
+        * We will do this in three passes
+        *   1) The easiest is to drop the hidden preferences from the database. We
+        *      don't actually want them
+        *   2) Drop preference keys that we don't know about. They could've been
+        *      removed from core, provided by a now-disabled extension, or the result
+        *      of a bug. We don't want them.
+        *   3) TODO: Normalize accepted preference values. This is the biggest part of the work.
+        *      For each preference we know about, iterate over it and if it's got a
+        *      limited set of accepted values (so it's not text, basically), make sure
+        *      all values are in that range. Drop ones that aren't.
+        */
        public function execute() {
-               global $wgHiddenPrefs;
+               global $wgHiddenPrefs, $wgDefaultUserOptions;
 
                $dbw = $this->getDB( DB_MASTER );
-               $this->beginTransaction( $dbw, __METHOD__ );
-               foreach ( $wgHiddenPrefs as $item ) {
-                       $dbw->delete(
+               $didWork = false;
+               $hidden = $this->hasOption( 'hidden' );
+               $unknown = $this->hasOption( 'unknown' );
+               $bogus = $this->hasOption( 'bogus' );
+
+               if ( !$hidden && !$unknown && !$bogus ) {
+                       $this->output( "Did not select one of --hidden, --unknown or --bogus, exiting\n" );
+                       return;
+               }
+
+               // Remove hidden prefs. Iterate over them to avoid the IN on a large table
+               if ( $hidden ) {
+                       if ( !$wgHiddenPrefs ) {
+                               $this->output( "No hidden preferences, skipping\n" );
+                       }
+                       foreach ( $wgHiddenPrefs as $hiddenPref ) {
+                               $this->deleteByWhere(
+                                       $dbw,
+                                       'Dropping hidden preferences',
+                                       [ 'up_property' => $hiddenPref ]
+                               );
+                       }
+               }
+
+               // Remove unknown preferences. Special-case gadget- and userjs- as we can't
+               // control those names.
+               if ( $unknown ) {
+                       $this->deleteByWhere(
+                               $dbw,
+                               'Dropping unknown preferences',
+                               [
+                                       'up_property NOT' . $dbw->buildLike( 'gadget-', $dbw->anyString() ),
+                                       'up_property NOT' . $dbw->buildLike( 'userjs-', $dbw->anyString() ),
+                                       'up_property NOT IN (' . $dbw->makeList( array_keys( $wgDefaultUserOptions ) ) . ')',
+                               ]
+                       );
+               }
+
+               // Something something phase 3
+               if ( $bogus ) {
+               }
+       }
+
+       /**
+        *
+        */
+       private function deleteByWhere( $dbw, $startMessage, $where ) {
+               $this->output( $startMessage . "...\n" );
+               $total = 0;
+               while ( true ) {
+                       $res = $dbw->select(
                                'user_properties',
-                               [ 'up_property' => $item ],
-                               __METHOD__
+                               '*', // The table lacks a primary key, so select the whole row
+                               $where,
+                               __METHOD__,
+                               [ 'LIMIT' => $this->mBatchSize ]
                        );
-               };
-               $this->commitTransaction( $dbw, __METHOD__ );
-               $this->output( "Finished!\n" );
+
+                       $numRows = $res->numRows();
+                       $total += $numRows;
+                       if ( $res->numRows() <= 0 ) {
+                               // All done!
+                               $this->output( "DONE! (handled $total entries)\n" );
+                               break;
+                       }
+
+                       // Progress or something
+                       $this->output( "..doing $numRows entries\n" );
+
+                       // Delete our batch, then wait
+                       foreach ( $res as $row ) {
+                               if ( $this->hasOption( 'dry-run' ) ) {
+                                       $this->output(
+                                               "    DRY RUN, would drop: " .
+                                               "[up_user] => '{$row->up_user}' " .
+                                               "[up_property] => '{$row->up_property}' " .
+                                               "[up_value] => '{$row->up_value}'\n"
+                                       );
+                                       continue;
+                               }
+                               $this->beginTransaction( $dbw, __METHOD__ );
+                               $dbw->delete(
+                                       'user_properties',
+                                       [
+                                               'up_user'     => $row->up_user,
+                                               'up_property' => $row->up_property,
+                                               'up_value'    => $row->up_value,
+                                       ],
+                                       __METHOD__
+                               );
+                               $this->commitTransaction( $dbw, __METHOD__ );
+                       }
+               }
        }
 }
 
index 8c782d9..5b441f9 100644 (file)
@@ -137,7 +137,8 @@ class TitleCleanup extends TableCleanup {
                        || $title->getInterwiki()
                        || !$title->canExist()
                ) {
-                       if ( $title->getInterwiki() || !$title->canExist() ) {
+                       $titleImpossible = $title->getInterwiki() || !$title->canExist();
+                       if ( $titleImpossible ) {
                                $prior = $title->getPrefixedDBkey();
                        } else {
                                $prior = $title->getDBkey();
@@ -155,7 +156,12 @@ class TitleCleanup extends TableCleanup {
                                $ns = 0;
                        }
 
-                       $clean = 'Broken/' . $prior;
+                       if ( !$titleImpossible && !$title->exists() ) {
+                               // Looks like the current title, after cleaning it up, is valid and available
+                               $clean = $prior;
+                       } else {
+                               $clean = 'Broken/' . $prior;
+                       }
                        $verified = Title::makeTitleSafe( $ns, $clean );
                        if ( !$verified || $verified->exists() ) {
                                $blah = "Broken/id:" . $row->page_id;
index 799af4c..d1e6496 100755 (executable)
@@ -1,6 +1,6 @@
 #!/bin/bash -eu
 
-# This script generates a commit that updates our copy of OOjs UI
+# This script generates a commit that updates our copy of OOUI
 
 if [ -n "${2:-}" ]
 then
@@ -20,7 +20,7 @@ git checkout composer.json
 git reset -- $TARGET_DIR
 git checkout -- $TARGET_DIR
 git fetch origin
-git checkout -B upstream-oojs-ui origin/master
+git checkout -B upstream-ooui origin/master
 
 # Fetch upstream version
 cd $NPM_DIR
@@ -31,10 +31,10 @@ else
        npm install oojs-ui
 fi
 
-OOJSUI_VERSION=$(node -e 'console.log(require("./node_modules/oojs-ui/package.json").version);')
-if [ "$OOJSUI_VERSION" == "" ]
+OOUI_VERSION=$(node -e 'console.log(require("./node_modules/oojs-ui/package.json").version);')
+if [ "$OOUI_VERSION" == "" ]
 then
-       echo 'Could not find OOjs UI version'
+       echo 'Could not find OOUI version'
        exit 1
 fi
 
@@ -68,15 +68,15 @@ rm -rf "$NPM_DIR"
 cd $REPO_DIR
 
 COMMITMSG=$(cat <<END
-Update OOjs UI to v$OOJSUI_VERSION
+Update OOUI to v$OOUI_VERSION
 
 Release notes:
- https://phabricator.wikimedia.org/diffusion/GOJU/browse/master/History.md;v$OOJSUI_VERSION
+ https://phabricator.wikimedia.org/diffusion/GOJU/browse/master/History.md;v$OOUI_VERSION
 END
 )
 
 # Update composer.json as well
-composer require oojs/oojs-ui $OOJSUI_VERSION --no-update
+composer require oojs/oojs-ui $OOUI_VERSION --no-update
 
 # Stage deletion, modification and creation of files. Then commit.
 git add --update $TARGET_DIR
index e55e0fe..2142f1f 100644 (file)
@@ -29,18 +29,18 @@ return [
        /**
         * Special modules who have their own classes
         */
-       'startup' => [ 'class' => 'ResourceLoaderStartUpModule' ],
+       'startup' => [ 'class' => ResourceLoaderStartUpModule::class ],
 
        // Scripts managed by the local wiki (stored in the MediaWiki namespace)
-       'site' => [ 'class' => 'ResourceLoaderSiteModule' ],
-       'site.styles' => [ 'class' => 'ResourceLoaderSiteStylesModule' ],
+       'site' => [ 'class' => ResourceLoaderSiteModule::class ],
+       'site.styles' => [ 'class' => ResourceLoaderSiteStylesModule::class ],
        'noscript' => [
-               'class' => 'ResourceLoaderWikiModule',
+               'class' => ResourceLoaderWikiModule::class,
                'styles' => [ 'MediaWiki:Noscript.css' ],
                'group' => 'noscript',
        ],
        'filepage' => [
-               'class' => 'ResourceLoaderWikiModule',
+               'class' => ResourceLoaderWikiModule::class,
                'styles' => [ 'MediaWiki:Filepage.css' ],
        ],
        'user.groups' => [
@@ -50,16 +50,16 @@ return [
        ],
 
        // Scripts managed by the current user (stored in their user space)
-       'user' => [ 'class' => 'ResourceLoaderUserModule' ],
-       'user.styles' => [ 'class' => 'ResourceLoaderUserStylesModule' ],
+       'user' => [ 'class' => ResourceLoaderUserModule::class ],
+       'user.styles' => [ 'class' => ResourceLoaderUserStylesModule::class ],
 
        // Populate mediawiki.user placeholders with information about the current user
-       'user.defaults' => [ 'class' => 'ResourceLoaderUserDefaultsModule' ],
-       'user.options' => [ 'class' => 'ResourceLoaderUserOptionsModule' ],
-       'user.tokens' => [ 'class' => 'ResourceLoaderUserTokensModule' ],
+       'user.defaults' => [ 'class' => ResourceLoaderUserDefaultsModule::class ],
+       'user.options' => [ 'class' => ResourceLoaderUserOptionsModule::class ],
+       'user.tokens' => [ 'class' => ResourceLoaderUserTokensModule::class ],
 
        // Scripts for the dynamic language specific data, like grammar forms.
-       'mediawiki.language.data' => [ 'class' => 'ResourceLoaderLanguageDataModule' ],
+       'mediawiki.language.data' => [ 'class' => ResourceLoaderLanguageDataModule::class ],
 
        /* MediaWiki base skinning modules */
 
@@ -105,7 +105,7 @@ return [
        ],
        // Used in the web installer. Test it after modifying this definition!
        'mediawiki.skinning.interface' => [
-               'class' => 'ResourceLoaderSkinModule',
+               'class' => ResourceLoaderSkinModule::class,
                'styles' => [
                        'resources/src/mediawiki.skinning/elements.css' => [ 'media' => 'screen' ],
                        'resources/src/mediawiki.skinning/content.css' => [ 'media' => 'screen' ],
@@ -832,7 +832,7 @@ return [
        /* MediaWiki */
 
        'mediawiki' => [
-               'class' => 'ResourceLoaderRawFileModule',
+               'class' => ResourceLoaderRawFileModule::class,
                // Keep in sync with maintenance/jsduck/eg-iframe.html
                'scripts' => [
                        'resources/src/mediawiki/mediawiki.js',
@@ -1017,7 +1017,7 @@ return [
        ],
        'mediawiki.ForeignApi' => [
                'targets' => [ 'desktop', 'mobile' ],
-               'class' => 'ResourceLoaderForeignApiModule',
+               'class' => ResourceLoaderForeignApiModule::class,
                // Additional dependencies generated dynamically
                'dependencies' => 'mediawiki.ForeignApi.core',
        ],
@@ -1227,7 +1227,7 @@ return [
                ]
        ],
        'mediawiki.ForeignStructuredUpload.config' => [
-               'class' => 'ResourceLoaderUploadDialogModule',
+               'class' => ResourceLoaderUploadDialogModule::class,
        ],
        'mediawiki.ForeignStructuredUpload' => [
                'scripts' => 'resources/src/mediawiki/mediawiki.ForeignStructuredUpload.js',
@@ -1366,7 +1366,7 @@ return [
                ]
        ],
        'mediawiki.util' => [
-               'class' => 'ResourceLoaderMediaWikiUtilModule',
+               'class' => ResourceLoaderMediaWikiUtilModule::class,
                'scripts' => 'resources/src/mediawiki/mediawiki.util.js',
                'dependencies' => [
                        'jquery.accessKeyLabel',
@@ -1391,7 +1391,7 @@ return [
                'targets' => [ 'desktop', 'mobile' ],
        ],
        'mediawiki.toolbar' => [
-               'class' => 'ResourceLoaderEditToolbarModule',
+               'class' => ResourceLoaderEditToolbarModule::class,
                'scripts' => 'resources/src/mediawiki.toolbar/toolbar.js',
                'styles' => 'resources/src/mediawiki.toolbar/toolbar.less',
                'dependencies' => 'jquery.textSelection',
@@ -1508,6 +1508,7 @@ return [
                        'postedit-confirmation-created',
                        'postedit-confirmation-restored',
                        'postedit-confirmation-saved',
+                       'postedit-confirmation-published',
                ],
        ],
        'mediawiki.action.view.redirect' => [
@@ -1596,7 +1597,7 @@ return [
 
        'mediawiki.jqueryMsg' => [
                // Add data for mediawiki.jqueryMsg, such as allowed tags
-               'class' => 'ResourceLoaderJqueryMsgModule',
+               'class' => ResourceLoaderJqueryMsgModule::class,
                'scripts' => 'resources/src/mediawiki/mediawiki.jqueryMsg.js',
                'dependencies' => [
                        'mediawiki.util',
@@ -1616,10 +1617,10 @@ return [
                )
        ],
 
-       'mediawiki.language.names' => [ 'class' => 'ResourceLoaderLanguageNamesModule' ],
+       'mediawiki.language.names' => [ 'class' => ResourceLoaderLanguageNamesModule::class ],
 
        'mediawiki.language.specialCharacters' => [
-               'class' => 'ResourceLoaderSpecialCharacterDataModule'
+               'class' => ResourceLoaderSpecialCharacterDataModule::class
        ],
 
        /* MediaWiki Libs */
@@ -2724,7 +2725,7 @@ return [
 
        // The core JavaScript library.
        'oojs-ui-core' => [
-               'class' => 'ResourceLoaderOOUIFileModule',
+               'class' => ResourceLoaderOOUIFileModule::class,
                'scripts' => [
                        'resources/lib/oojs-ui/oojs-ui-core.js',
                        'resources/src/oojs-ui-local.js',
@@ -2744,7 +2745,7 @@ return [
        ],
        // This contains only the styles required by core widgets.
        'oojs-ui-core.styles' => [
-               'class' => 'ResourceLoaderOOUIFileModule',
+               'class' => ResourceLoaderOOUIFileModule::class,
                'styles' => [
                        'resources/lib/oojs-ui/wikimedia-ui-base.less', // Providing Wikimedia UI LESS variables to all
                        'resources/src/oojs-ui-local.css', // HACK, see inside the file
@@ -2754,7 +2755,7 @@ return [
        ],
        // Additional widgets and layouts module.
        'oojs-ui-widgets' => [
-               'class' => 'ResourceLoaderOOUIFileModule',
+               'class' => ResourceLoaderOOUIFileModule::class,
                'scripts' => 'resources/lib/oojs-ui/oojs-ui-widgets.js',
                'themeStyles' => 'widgets',
                'dependencies' => [
@@ -2779,7 +2780,7 @@ return [
        ],
        // Toolbar and tools module.
        'oojs-ui-toolbars' => [
-               'class' => 'ResourceLoaderOOUIFileModule',
+               'class' => ResourceLoaderOOUIFileModule::class,
                'scripts' => 'resources/lib/oojs-ui/oojs-ui-toolbars.js',
                'themeStyles' => 'toolbars',
                'dependencies' => [
@@ -2795,7 +2796,7 @@ return [
        ],
        // Windows and dialogs module.
        'oojs-ui-windows' => [
-               'class' => 'ResourceLoaderOOUIFileModule',
+               'class' => ResourceLoaderOOUIFileModule::class,
                'scripts' => 'resources/lib/oojs-ui/oojs-ui-windows.js',
                'themeStyles' => 'windows',
                'dependencies' => [
@@ -2814,71 +2815,71 @@ return [
        ],
 
        'oojs-ui.styles.indicators' => [
-               'class' => 'ResourceLoaderOOUIImageModule',
+               'class' => ResourceLoaderOOUIImageModule::class,
                'themeImages' => 'indicators',
        ],
        'oojs-ui.styles.textures' => [
-               'class' => 'ResourceLoaderOOUIImageModule',
+               'class' => ResourceLoaderOOUIImageModule::class,
                'themeImages' => 'textures',
        ],
        'oojs-ui.styles.icons-accessibility' => [
-               'class' => 'ResourceLoaderOOUIImageModule',
+               'class' => ResourceLoaderOOUIImageModule::class,
                'themeImages' => 'icons-accessibility',
        ],
        'oojs-ui.styles.icons-alerts' => [
-               'class' => 'ResourceLoaderOOUIImageModule',
+               'class' => ResourceLoaderOOUIImageModule::class,
                'themeImages' => 'icons-alerts',
        ],
        'oojs-ui.styles.icons-content' => [
-               'class' => 'ResourceLoaderOOUIImageModule',
+               'class' => ResourceLoaderOOUIImageModule::class,
                'themeImages' => 'icons-content',
        ],
        'oojs-ui.styles.icons-editing-advanced' => [
-               'class' => 'ResourceLoaderOOUIImageModule',
+               'class' => ResourceLoaderOOUIImageModule::class,
                'themeImages' => 'icons-editing-advanced',
        ],
        'oojs-ui.styles.icons-editing-core' => [
-               'class' => 'ResourceLoaderOOUIImageModule',
+               'class' => ResourceLoaderOOUIImageModule::class,
                'themeImages' => 'icons-editing-core',
        ],
        'oojs-ui.styles.icons-editing-list' => [
-               'class' => 'ResourceLoaderOOUIImageModule',
+               'class' => ResourceLoaderOOUIImageModule::class,
                'themeImages' => 'icons-editing-list',
        ],
        'oojs-ui.styles.icons-editing-styling' => [
-               'class' => 'ResourceLoaderOOUIImageModule',
+               'class' => ResourceLoaderOOUIImageModule::class,
                'themeImages' => 'icons-editing-styling',
        ],
        'oojs-ui.styles.icons-interactions' => [
-               'class' => 'ResourceLoaderOOUIImageModule',
+               'class' => ResourceLoaderOOUIImageModule::class,
                'themeImages' => 'icons-interactions',
        ],
        'oojs-ui.styles.icons-layout' => [
-               'class' => 'ResourceLoaderOOUIImageModule',
+               'class' => ResourceLoaderOOUIImageModule::class,
                'themeImages' => 'icons-layout',
        ],
        'oojs-ui.styles.icons-location' => [
-               'class' => 'ResourceLoaderOOUIImageModule',
+               'class' => ResourceLoaderOOUIImageModule::class,
                'themeImages' => 'icons-location',
        ],
        'oojs-ui.styles.icons-media' => [
-               'class' => 'ResourceLoaderOOUIImageModule',
+               'class' => ResourceLoaderOOUIImageModule::class,
                'themeImages' => 'icons-media',
        ],
        'oojs-ui.styles.icons-moderation' => [
-               'class' => 'ResourceLoaderOOUIImageModule',
+               'class' => ResourceLoaderOOUIImageModule::class,
                'themeImages' => 'icons-moderation',
        ],
        'oojs-ui.styles.icons-movement' => [
-               'class' => 'ResourceLoaderOOUIImageModule',
+               'class' => ResourceLoaderOOUIImageModule::class,
                'themeImages' => 'icons-movement',
        ],
        'oojs-ui.styles.icons-user' => [
-               'class' => 'ResourceLoaderOOUIImageModule',
+               'class' => ResourceLoaderOOUIImageModule::class,
                'themeImages' => 'icons-user',
        ],
        'oojs-ui.styles.icons-wikimedia' => [
-               'class' => 'ResourceLoaderOOUIImageModule',
+               'class' => ResourceLoaderOOUIImageModule::class,
                'themeImages' => 'icons-wikimedia',
        ],
 ];
index 522e95b..394b42f 100644 (file)
@@ -2,66 +2,11 @@
  * These plugins provide extra functionality for interaction with textareas.
  */
 ( function ( $ ) {
-       if ( document.selection && document.selection.createRange ) {
-               // On IE, patch the focus() method to restore the windows' scroll position
-               // (T34241)
-               $.fn.extend( {
-                       focus: ( function ( jqFocus ) {
-                               return function () {
-                                       var $w, state, result;
-                                       if ( arguments.length === 0 ) {
-                                               $w = $( window );
-                                               state = { top: $w.scrollTop(), left: $w.scrollLeft() };
-                                               result = jqFocus.apply( this, arguments );
-                                               window.scrollTo( state.top, state.left );
-                                               return result;
-                                       }
-                                       return jqFocus.apply( this, arguments );
-                               };
-                       }( $.fn.focus ) )
-               } );
-       }
-
        $.fn.textSelection = function ( command, options ) {
                var fn,
                        alternateFn,
-                       context,
-                       hasWikiEditor,
-                       needSave,
                        retval;
 
-               /**
-                * Helper function to get an IE TextRange object for an element
-                *
-                * @param {HTMLElement} element
-                * @return {TextRange}
-                */
-               function rangeForElementIE( element ) {
-                       var sel;
-                       if ( element.nodeName.toLowerCase() === 'input' ) {
-                               return element.createTextRange();
-                       } else {
-                               sel = document.body.createTextRange();
-                               sel.moveToElementText( element );
-                               return sel;
-                       }
-               }
-
-               /**
-                * Helper function for IE for activating the textarea. Called only in the
-                * IE-specific code paths below; makes use of IE-specific non-standard
-                * function setActive() if possible to avoid screen flicker.
-                *
-                * @param {HTMLElement} element
-                */
-               function activateElementOnIE( element ) {
-                       if ( element.setActive ) {
-                               element.setActive(); // T34241: doesn't scroll
-                       } else {
-                               $( element ).focus(); // may scroll (but we patched it above)
-                       }
-               }
-
                fn = {
                        /**
                         * Get the contents of the textarea
                                this.val( content );
                        },
                        /**
-                        * Get the currently selected text in this textarea. Will focus the textarea
-                        * in some browsers (IE/Opera)
+                        * Get the currently selected text in this textarea.
                         *
                         * @return {string}
                         */
                        getSelection: function () {
-                               var retval, range,
+                               var retval,
                                        el = this.get( 0 );
 
                                if ( !el || $( el ).is( ':hidden' ) ) {
                                        retval = '';
-                               } else if ( document.selection && document.selection.createRange ) {
-                                       activateElementOnIE( el );
-                                       range = document.selection.createRange();
-                                       retval = range.text;
                                } else if ( el.selectionStart || el.selectionStart === 0 ) {
                                        retval = el.value.substring( el.selectionStart, el.selectionEnd );
                                }
                        encapsulateSelection: function ( options ) {
                                return this.each( function () {
                                        var selText, scrollTop, insertText,
-                                               isSample, range, range2, range3, startPos, endPos,
+                                               isSample, startPos, endPos,
                                                pre = options.pre,
                                                post = options.post;
 
                                        isSample = false;
                                        // Do nothing if display none
                                        if ( this.style.display !== 'none' ) {
-                                               if ( document.selection && document.selection.createRange ) {
-                                                       // IE
-
-                                                       // Note that IE9 will trigger the next section unless we check this first.
-                                                       // See bug T37201.
-
-                                                       activateElementOnIE( this );
-                                                       if ( options.selectionStart !== undefined ) {
-                                                               $( this ).textSelection( 'setSelection', { start: options.selectionStart, end: options.selectionEnd } );
-                                                       }
-
-                                                       selText = $( this ).textSelection( 'getSelection' );
-                                                       scrollTop = this.scrollTop;
-                                                       range = document.selection.createRange();
-
-                                                       checkSelectedText();
-                                                       insertText = pre + selText + post;
-                                                       if ( options.splitlines ) {
-                                                               insertText = doSplitLines( selText, pre, post );
-                                                       }
-                                                       if ( options.ownline && range.moveStart ) {
-                                                               range2 = document.selection.createRange();
-                                                               range2.collapse();
-                                                               range2.moveStart( 'character', -1 );
-                                                               // FIXME: Which check is correct?
-                                                               if ( range2.text !== '\r' && range2.text !== '\n' && range2.text !== '' ) {
-                                                                       insertText = '\n' + insertText;
-                                                                       pre += '\n';
-                                                               }
-                                                               range3 = document.selection.createRange();
-                                                               range3.collapse( false );
-                                                               range3.moveEnd( 'character', 1 );
-                                                               if ( range3.text !== '\r' && range3.text !== '\n' && range3.text !== '' ) {
-                                                                       insertText += '\n';
-                                                                       post += '\n';
-                                                               }
-                                                       }
-
-                                                       range.text = insertText;
-                                                       if ( isSample && options.selectPeri && range.moveStart ) {
-                                                               range.moveStart( 'character', -post.length - selText.length );
-                                                               range.moveEnd( 'character', -post.length );
-                                                       }
-                                                       range.select();
-                                                       // Restore the scroll position
-                                                       this.scrollTop = scrollTop;
-                                               } else if ( this.selectionStart || this.selectionStart === 0 ) {
-                                                       // Mozilla/Opera
-
+                                               if ( this.selectionStart || this.selectionStart === 0 ) {
                                                        $( this ).focus();
                                                        if ( options.selectionStart !== undefined ) {
                                                                $( this ).textSelection( 'setSelection', { start: options.selectionStart, end: options.selectionEnd } );
                        /**
                         * Ported from Wikia's LinkSuggest extension
                         * https://svn.wikia-code.com/wikia/trunk/extensions/wikia/LinkSuggest
-                        * Some code copied from
-                        * http://www.dedestruct.com/2008/03/22/howto-cross-browser-cursor-position-in-textareas/
                         *
                         * Get the position (in resolution of bytes not necessarily characters)
                         * in a textarea
                         *
-                        * Will focus the textarea in some browsers (IE/Opera)
-                        *
                         * @param {Object} options Options
                         * FIXME document the options parameters
                         * @return {number} Position
                        getCaretPosition: function ( options ) {
                                function getCaret( e ) {
                                        var caretPos = 0,
-                                               endPos = 0,
-                                               preText, rawPreText, periText,
-                                               rawPeriText, postText,
-                                               // IE Support
-                                               preFinished,
-                                               periFinished,
-                                               postFinished,
-                                               // Range containing text in the selection
-                                               periRange,
-                                               // Range containing text before the selection
-                                               preRange,
-                                               // Range containing text after the selection
-                                               postRange;
-
-                                       if ( e && document.selection && document.selection.createRange ) {
-                                               // IE doesn't properly report non-selected caret position through
-                                               // the selection ranges when textarea isn't focused. This can
-                                               // lead to saving a bogus empty selection, which then screws up
-                                               // whatever we do later (T33847).
-                                               activateElementOnIE( e );
-
-                                               preFinished = false;
-                                               periFinished = false;
-                                               postFinished = false;
-                                               periRange = document.selection.createRange().duplicate();
-
-                                               preRange = rangeForElementIE( e );
-                                               // Move the end where we need it
-                                               preRange.setEndPoint( 'EndToStart', periRange );
-
-                                               postRange = rangeForElementIE( e );
-                                               // Move the start where we need it
-                                               postRange.setEndPoint( 'StartToEnd', periRange );
+                                               endPos = 0;
 
-                                               // Load the text values we need to compare
-                                               preText = rawPreText = preRange.text;
-                                               periText = rawPeriText = periRange.text;
-                                               postText = postRange.text;
-
-                                               /*
-                                                * Check each range for trimmed newlines by shrinking the range by 1
-                                                * character and seeing if the text property has changed. If it has
-                                                * not changed then we know that IE has trimmed a \r\n from the end.
-                                                */
-                                               do {
-                                                       if ( !preFinished ) {
-                                                               if ( preRange.compareEndPoints( 'StartToEnd', preRange ) === 0 ) {
-                                                                       preFinished = true;
-                                                               } else {
-                                                                       preRange.moveEnd( 'character', -1 );
-                                                                       if ( preRange.text === preText ) {
-                                                                               rawPreText += '\r\n';
-                                                                       } else {
-                                                                               preFinished = true;
-                                                                       }
-                                                               }
-                                                       }
-                                                       if ( !periFinished ) {
-                                                               if ( periRange.compareEndPoints( 'StartToEnd', periRange ) === 0 ) {
-                                                                       periFinished = true;
-                                                               } else {
-                                                                       periRange.moveEnd( 'character', -1 );
-                                                                       if ( periRange.text === periText ) {
-                                                                               rawPeriText += '\r\n';
-                                                                       } else {
-                                                                               periFinished = true;
-                                                                       }
-                                                               }
-                                                       }
-                                                       if ( !postFinished ) {
-                                                               if ( postRange.compareEndPoints( 'StartToEnd', postRange ) === 0 ) {
-                                                                       postFinished = true;
-                                                               } else {
-                                                                       postRange.moveEnd( 'character', -1 );
-                                                                       if ( postRange.text !== postText ) {
-                                                                               postFinished = true;
-                                                                       }
-                                                               }
-                                                       }
-                                               } while ( ( !preFinished || !periFinished || !postFinished ) );
-                                               caretPos = rawPreText.replace( /\r\n/g, '\n' ).length;
-                                               endPos = caretPos + rawPeriText.replace( /\r\n/g, '\n' ).length;
-                                       } else if ( e && ( e.selectionStart || e.selectionStart === 0 ) ) {
-                                               // Firefox support
+                                       if ( e && ( e.selectionStart || e.selectionStart === 0 ) ) {
                                                caretPos = e.selectionStart;
                                                endPos = e.selectionEnd;
                                        }
                         */
                        setSelection: function ( options ) {
                                return this.each( function () {
-                                       var selection, length, newLines;
                                        // Do nothing if hidden
                                        if ( !$( this ).is( ':hidden' ) ) {
                                                if ( this.selectionStart || this.selectionStart === 0 ) {
                                                                this.selectionStart = options.start;
                                                                this.selectionEnd = options.end;
                                                        }
-                                               } else if ( document.body.createTextRange ) {
-                                                       selection = rangeForElementIE( this );
-                                                       length = this.value.length;
-                                                       // IE doesn't count \n when computing the offset, so we won't either
-                                                       newLines = this.value.match( /\n/g );
-                                                       if ( newLines ) {
-                                                               length = length - newLines.length;
-                                                       }
-                                                       selection.moveStart( 'character', options.start );
-                                                       selection.moveEnd( 'character', -length + options.end );
-
-                                                       // This line can cause an error under certain circumstances (textarea empty, no selection)
-                                                       // Silence that error
-                                                       try {
-                                                               selection.select();
-                                                       } catch ( e ) { }
                                                }
                                        }
                                } );
                                        return ( $.client.profile().platform === 'mac' ? 13 : ( $.client.profile().platform === 'linux' ? 15 : 16 ) ) * row;
                                }
                                return this.each( function () {
-                                       var scroll, range, savedRange, pos, oldScrollTop;
+                                       var scroll;
                                        // Do nothing if hidden
                                        if ( !$( this ).is( ':hidden' ) ) {
                                                if ( this.selectionStart || this.selectionStart === 0 ) {
-                                                       // Mozilla
                                                        scroll = getCaretScrollPosition( this );
                                                        if ( options.force || scroll < $( this ).scrollTop() ||
                                                                        scroll > $( this ).scrollTop() + $( this ).height() ) {
                                                                $( this ).scrollTop( scroll );
                                                        }
-                                               } else if ( document.selection && document.selection.createRange ) {
-                                                       // IE / Opera
-                                                       /*
-                                                        * IE automatically scrolls the selected text to the
-                                                        * bottom of the textarea at range.select() time, except
-                                                        * if it was already in view and the cursor position
-                                                        * wasn't changed, in which case it does nothing. To
-                                                        * cover that case, we'll force it to act by moving one
-                                                        * character back and forth.
-                                                        */
-                                                       range = document.body.createTextRange();
-                                                       savedRange = document.selection.createRange();
-                                                       pos = $( this ).textSelection( 'getCaretPosition' );
-                                                       oldScrollTop = this.scrollTop;
-                                                       range.moveToElementText( this );
-                                                       range.collapse();
-                                                       range.move( 'character', pos + 1 );
-                                                       range.select();
-                                                       if ( this.scrollTop !== oldScrollTop ) {
-                                                               this.scrollTop += range.offsetTop;
-                                                       } else if ( options.force ) {
-                                                               range.move( 'character', -1 );
-                                                               range.select();
-                                                       }
-                                                       savedRange.select();
                                                }
                                        }
                                        $( this ).trigger( 'scrollToPosition' );
                                        // Return [start, end] instead of just start
                                        startAndEnd: false
                                }, options );
-                               // FIXME: We may not need character position-based functions if we insert markers in the right places
                                break;
                        case 'setSelection':
                                options = $.extend( {
                                if ( options.end === undefined ) {
                                        options.end = options.start;
                                }
-                               // FIXME: We may not need character position-based functions if we insert markers in the right places
                                break;
                        case 'scrollToCaretPosition':
                                options = $.extend( {
                                return;
                }
 
-               context = $( this ).data( 'wikiEditor-context' );
-               hasWikiEditor = ( context !== undefined && context.$iframe !== undefined );
-
-               // IE selection restore voodoo
-               needSave = false;
-               if ( hasWikiEditor && context.savedSelection !== null ) {
-                       context.fn.restoreSelection();
-                       needSave = true;
-               }
                retval = ( alternateFn && alternateFn[ command ] || fn[ command ] ).call( this, options );
-               if ( hasWikiEditor && needSave ) {
-                       context.fn.saveSelection();
-               }
 
                return retval;
        };
index e0ab45a..5e859ac 100644 (file)
                data = data || {};
 
                if ( data.message === undefined ) {
-                       data.message = $.parseHTML( mw.message( 'postedit-confirmation-saved', data.user || mw.user ).escaped() );
+                       data.message = $.parseHTML( mw.message(
+                               mw.config.get( 'wgEditSubmitButtonLabelPublish' ) ?
+                                       'postedit-confirmation-published' :
+                                       'postedit-confirmation-saved',
+                               data.user || mw.user
+                       ).escaped() );
                }
 
                $content = $( '<div>' ).addClass( 'postedit-icon postedit-icon-checkmark postedit-content' );
index 1c9294c..afa7799 100644 (file)
@@ -3,7 +3,7 @@
                "Á", "á", "À", "à", "Â", "â", "Ä", "ä", "Ã", "ã", "Ǎ", "ǎ", "Ā", "ā", "Ă", "ă", "Ą", "ą", "Å", "å", "Ć", "ć", "Ĉ", "ĉ", "Ç", "ç", "Č", "č", "Ċ", "ċ", "Đ", "đ", "Ď", "ď", "É", "é", "È", "è", "Ê", "ê", "Ë", "ë", "Ě", "ě", "Ē", "ē", "Ĕ", "ĕ", "Ė", "ė", "Ę", "ę", "Ĝ", "ĝ", "Ģ", "ģ", "Ğ", "ğ", "Ġ", "ġ", "Ĥ", "ĥ", "Ħ", "ħ", "Í", "í", "Ì", "ì", "Î", "î", "Ï", "ï", "Ĩ", "ĩ", "Ǐ", "ǐ", "Ī", "ī", "Ĭ", "ĭ", "İ", "ı", "Į", "į", "Ĵ", "ĵ", "Ķ", "ķ", "Ĺ", "ĺ", "Ļ", "ļ", "Ľ", "ľ", "Ł", "ł", "Ń", "ń", "Ñ", "ñ", "Ņ", "ņ", "Ň", "ň", "Ó", "ó", "Ò", "ò", "Ô", "ô", "Ö", "ö", "Õ", "õ", "Ǒ", "ǒ", "Ō", "ō", "Ŏ", "ŏ", "Ǫ", "ǫ", "Ő", "ő", "Ŕ", "ŕ", "Ŗ", "ŗ", "Ř", "ř", "Ś", "ś", "Ŝ", "ŝ", "Ş", "ş", "Š", "š", "Ș", "ș", "Ț", "ț", "Ť", "ť", "Ú", "ú", "Ù", "ù", "Û", "û", "Ü", "ü", "Ũ", "ũ", "Ů", "ů", "Ǔ", "ǔ", "Ū", "ū", "ǖ", "ǘ", "ǚ", "ǜ", "Ŭ", "ŭ", "Ų", "ų", "Ű", "ű", "Ŵ", "ŵ", "Ý", "ý", "Ŷ", "ŷ", "Ÿ", "ÿ", "Ȳ", "ȳ", "Ź", "ź", "Ž", "ž", "Ż", "ż", "Æ", "æ", "Ǣ", "ǣ", "Ø", "ø", "Œ", "œ", "ß", "Ð", "ð", "Þ", "þ", "Ə", "ə"
        ],
        "latinextended": [
-               "Ḁ", "ḁ", "ẚ", "Ạ", "ạ", "Ả", "ả", "Ấ", "ấ", "Ầ", "ầ", "Ẩ", "ẩ", "Ẫ", "ẫ", "Ậ", "ậ", "Ắ", "ắ", "Ằ", "ằ", "Ẳ", "ẳ", "Ẵ", "ẵ", "Ặ", "ặ", "Ḃ", "ḃ", "Ḅ", "ḅ", "Ḇ", "ḇ", "Ḉ", "ḉ", "Ḋ", "ḋ", "Ḍ", "ḍ", "Ḏ", "ḏ", "Ḑ", "ḑ", "Ḓ", "ḓ", "Ḕ", "ḕ", "Ḗ", "ḗ", "Ḙ", "ḙ", "Ḛ", "ḛ", "Ḝ", "ḝ", "Ẹ", "ẹ", "Ẻ", "ẻ", "Ẽ", "ẽ", "Ế", "ế", "Ề", "ề", "Ể", "ể", "Ễ", "ễ", "Ệ", "ệ", "Ḟ", "ḟ", "Ḡ", "ḡ", "Ḣ", "ḣ", "Ḥ", "ḥ", "Ḧ", "ḧ", "Ḩ", "ḩ", "Ḫ", "ḫ", "ẖ", "Ḭ", "ḭ", "Ḯ", "ḯ", "Ỉ", "ỉ", "Ị", "ị", "Ḱ", "ḱ", "Ḳ", "ḳ", "Ḵ", "ḵ", "Ḷ", "ḷ", "Ḹ", "ḹ", "Ḻ", "ḻ", "Ḽ", "ḽ", "Ỻ", "ỻ", "Ḿ", "ḿ", "Ṁ", "ṁ", "Ṃ", "ṃ", "Ṅ", "ṅ", "Ṇ", "ṇ", "Ṉ", "ṉ", "Ṋ", "ṋ", "Ṍ", "ṍ", "Ṏ", "ṏ", "Ṑ", "ṑ", "Ṓ", "ṓ", "Ọ", "ọ", "Ỏ", "ỏ", "Ố", "ố", "Ồ", "ồ", "Ổ", "ổ", "Ỗ", "ỗ", "Ộ", "ộ", "Ớ", "ớ", "Ờ", "ờ", "Ở", "ở", "Ỡ", "ỡ", "Ợ", "ợ", "Ǿ", "ǿ", "Ơ", "ơ", "Ṕ", "ṕ", "Ṗ", "ṗ", "Ṙ", "ṙ", "Ṛ", "ṛ", "Ṝ", "ṝ", "Ṟ", "ṟ", "Ṡ", "ṡ", "ẛ", "Ṣ", "ṣ", "Ṥ", "ṥ", "Ṧ", "ṧ", "Ṩ", "ṩ", "ẜ", "ẝ", "Ṫ", "ṫ", "Ṭ", "ṭ", "Ṯ", "ṯ", "Ṱ", "ṱ", "ẗ", "Ṳ", "ṳ", "Ṵ", "ṵ", "Ṷ", "ṷ", "Ṹ", "ṹ", "Ṻ", "ṻ", "Ụ", "ụ", "Ủ", "ủ", "Ứ", "ứ", "Ừ", "ừ", "Ử", "ử", "Ữ", "ữ", "Ự", "ự", "Ư", "ư", "Ǖ", "Ǘ", "Ǚ", "Ǜ", "Ṽ", "ṽ", "Ṿ", "ṿ", "Ỽ", "ỽ", "Ẁ", "ẁ", "Ẃ", "ẃ", "Ẅ", "ẅ", "Ẇ", "ẇ", "Ẉ", "ẉ", "ẘ", "Ẋ", "ẋ", "Ẍ", "ẍ", "Ẏ", "ẏ", "ẙ", "Ỳ", "ỳ", "Ỵ", "ỵ", "Ỷ", "ỷ", "Ỹ", "ỹ", "Ỿ", "ỿ", "Ẑ", "ẑ", "Ẓ", "ẓ", "Ẕ", "ẕ", "Ǽ", "ǽ", "ẞ", "ẟ"
+               "Ḁ", "ḁ", "ẚ", "Ạ", "ạ", "Ả", "ả", "Ấ", "ấ", "Ầ", "ầ", "Ẩ", "ẩ", "Ẫ", "ẫ", "Ậ", "ậ", "Ắ", "ắ", "Ằ", "ằ", "Ẳ", "ẳ", "Ẵ", "ẵ", "Ặ", "ặ", "Ḃ", "ḃ", "Ḅ", "ḅ", "Ḇ", "ḇ", "Ḉ", "ḉ", "Ḋ", "ḋ", "Ḍ", "ḍ", "Ḏ", "ḏ", "Ḑ", "ḑ", "Ḓ", "ḓ", "Ḕ", "ḕ", "Ḗ", "ḗ", "Ḙ", "ḙ", "Ḛ", "ḛ", "Ḝ", "ḝ", "Ẹ", "ẹ", "Ẻ", "ẻ", "Ẽ", "ẽ", "Ế", "ế", "Ề", "ề", "Ể", "ể", "Ễ", "ễ", "Ệ", "ệ", "Ḟ", "ḟ", "Ḡ", "ḡ", "Ḣ", "ḣ", "Ḥ", "ḥ", "Ḧ", "ḧ", "Ḩ", "ḩ", "Ḫ", "ḫ", "ẖ", "Ḭ", "ḭ", "Ḯ", "ḯ", "Ỉ", "ỉ", "Ị", "ị", "Ḱ", "ḱ", "Ḳ", "ḳ", "Ḵ", "ḵ", "ĸ", "Ḷ", "ḷ", "Ḹ", "ḹ", "Ḻ", "ḻ", "Ḽ", "ḽ", "Ỻ", "ỻ", "Ḿ", "ḿ", "Ṁ", "ṁ", "Ṃ", "ṃ", "Ṅ", "ṅ", "Ṇ", "ṇ", "Ṉ", "ṉ", "Ṋ", "ṋ", "Ṍ", "ṍ", "Ṏ", "ṏ", "Ṑ", "ṑ", "Ṓ", "ṓ", "Ọ", "ọ", "Ỏ", "ỏ", "Ố", "ố", "Ồ", "ồ", "Ổ", "ổ", "Ỗ", "ỗ", "Ộ", "ộ", "Ớ", "ớ", "Ờ", "ờ", "Ở", "ở", "Ỡ", "ỡ", "Ợ", "ợ", "Ǿ", "ǿ", "Ơ", "ơ", "Ṕ", "ṕ", "Ṗ", "ṗ", "Ṙ", "ṙ", "Ṛ", "ṛ", "Ṝ", "ṝ", "Ṟ", "ṟ", "Ṡ", "ṡ", "ẛ", "Ṣ", "ṣ", "Ṥ", "ṥ", "Ṧ", "ṧ", "Ṩ", "ṩ", "ẜ", "ẝ", "Ṫ", "ṫ", "Ṭ", "ṭ", "Ṯ", "ṯ", "Ṱ", "ṱ", "ẗ", "Ṳ", "ṳ", "Ṵ", "ṵ", "Ṷ", "ṷ", "Ṹ", "ṹ", "Ṻ", "ṻ", "Ụ", "ụ", "Ủ", "ủ", "Ứ", "ứ", "Ừ", "ừ", "Ử", "ử", "Ữ", "ữ", "Ự", "ự", "Ư", "ư", "Ǖ", "Ǘ", "Ǚ", "Ǜ", "Ṽ", "ṽ", "Ṿ", "ṿ", "Ỽ", "ỽ", "Ẁ", "ẁ", "Ẃ", "ẃ", "Ẅ", "ẅ", "Ẇ", "ẇ", "Ẉ", "ẉ", "ẘ", "Ẋ", "ẋ", "Ẍ", "ẍ", "Ẏ", "ẏ", "ẙ", "Ỳ", "ỳ", "Ỵ", "ỵ", "Ỷ", "ỷ", "Ỹ", "ỹ", "Ỿ", "ỿ", "Ẑ", "ẑ", "Ẓ", "ẓ", "Ẕ", "ẕ", "Ǽ", "ǽ", "ẞ", "ẟ"
        ],
        "ipa": [
                "p", "t̪", "t", "ʈ", "c", "k", "q", "ʡ", "ʔ", "b", "d̪", "d", "ɖ", "ɟ", "ɡ", "ɢ", "ɓ", "ɗ", "ʄ", "ɠ", "ʛ", "t͡s", "t͡ʃ", "t͡ɕ", "d͡z", "d͡ʒ", "d͡ʑ", "ɸ", "f", "θ", "s", "ʃ", "ʅ", "ʆ", "ʂ", "ɕ", "ç", "ɧ", "x", "χ", "ħ", "ʜ", "h", "β", "v", "ʍ", "ð", "z", "ʒ", "ʓ", "ʐ", "ʑ", "ʝ", "ɣ", "ʁ", "ʕ", "ʖ", "ʢ", "ɦ", "ɬ", "ɮ", "m", "m̩", "ɱ", "ɱ̩", "ɱ̍", "n̪", "n̪̍", "n", "n̩", "ɳ", "ɳ̩", "ɲ", "ɲ̩", "ŋ", "ŋ̍", "ŋ̩", "ɴ", "ɴ̩", "ʙ", "ʙ̩", "r", "r̩", "ʀ", "ʀ̩", "ɾ", "ɽ", "ɿ", "ɺ", "l̪", "l̪̩", "l", "l̩", "ɫ", "ɫ̩", "ɭ", "ɭ̩", "ʎ", "ʎ̩", "ʟ", "ʟ̩", "w", "ɥ", "ʋ", "ɹ", "ɻ", "j", "ɰ", "ʘ", "ǂ", "ǀ", "!", "ǁ", "ʰ", "ʱ", "ʷ", "ʸ", "ʲ", "ʳ", "ⁿ", "ˡ", "ʴ", "ʵ", "ˢ", "ˣ", "ˠ", "ʶ", "ˤ", "ˁ", "ˀ", "ʼ", "i", "i̯", "ĩ", "y", "y̯", "ỹ", "ɪ", "ɪ̯", "ɪ̃", "ʏ", "ʏ̯", "ʏ̃", "ɨ", "ɨ̯", "ɨ̃", "ʉ", "ʉ̯", "ʉ̃", "ɯ", "ɯ̯", "ɯ̃", "u", "u̯", "ũ", "ʊ", "ʊ̯", "ʊ̃", "e", "e̯", "ẽ", "ø", "ø̯", "ø̃", "ɘ", "ɘ̯", "ɘ̃", "ɵ", "ɵ̯", "ɵ̃", "ɤ", "ɤ̯", "ɤ̃", "o", "o̯", "õ", "ɛ", "ɛ̯", "ɛ̃", "œ", "œ̯", "œ̃", "ɜ", "ɜ̯", "ɜ̃", "ə", "ə̯", "ə̃", "ɞ", "ɞ̯", "ɞ̃", "ʌ", "ʌ̯", "ʌ̃", "ɔ", "ɔ̯", "ɔ̃", "æ", "æ̯", "æ̃", "ɶ", "ɶ̯", "ɶ̃", "a", "a̯", "ã", "ɐ", "ɐ̯", "ɐ̃", "ɑ", "ɑ̯", "ɑ̃", "ɒ", "ɒ̯", "ɒ̃", "ˈ", "ˌ", "ː", "ˑ", "˘", ".", "‿", "|", "‖", "ɚ", "ɝ"
index 0c897dc..731bd2d 100644 (file)
@@ -1,3 +1,25 @@
+/**
+ * Minimum available screen width at which a device can be considered a mobile device
+ * Many older feature phones have screens smaller than this value.
+ * Number is prone to change with new information.
+ * @since 1.31
+ */
+@width-breakpoint-mobile: 320px;
+
+/**
+ * Minimum available screen width at which a device can be considered a tablet
+ * The number is currently based on the device width of a Samsung Galaxy S5 mini and is low
+ * enough to cover iPad (768px). Number is prone to change with new information.
+ * @since 1.31
+ */
+@width-breakpoint-tablet: 720px;
+/**
+ * Minimum available screen width at which a device can be considered a desktop
+ * Number is prone to change with new information.
+ * @since 1.31
+ */
+@width-breakpoint-desktop: 1000px;
+
 // Colors for use in mediawiki.ui and elsewhere
 
 // Although this defines many shades, be parsimonious in your own use of grays.
index db439e3..413d45b 100644 (file)
                display: none;
        }
 
+       #jump-to-nav {
+               margin-top: -0.5em;
+               margin-bottom: 0.5em;
+       }
+
        // Make the watchlist-details message display while loading, but make it not take up any
        // space. This makes the min-height trick work better.
        .watchlistDetails {
index acb3998..7ef0263 100644 (file)
@@ -7,6 +7,10 @@
        padding: 0.5em;
 }
 
+#mw-apisandbox-ui .mw-apisandbox-link {
+       display: none;
+}
+
 .mw-apisandbox-popup .oo-ui-popupWidget-body > .oo-ui-widget {
        vertical-align: middle;
 }
index cf77a96..fe013bc 100644 (file)
@@ -8,7 +8,7 @@
        font-weight: bold;
 }
 
-/* Login Button, following `ButtonWidget (progressive)‎` from OOjs UI */
+/* Login Button, following 'ButtonWidget (progressive)' from OOUI */
 #mw-createaccount-join {
        background-color: #f8f9fa;
        color: #36c;
index 9233eef..05180fd 100644 (file)
@@ -3,7 +3,7 @@
        /**
         * DateTimeInputWidgets can be used to input a date, a time, or a date and
         * time, in either UTC or the user's local timezone.
-        * Please see the [OOjs UI documentation on MediaWiki] [1] for more information and examples.
+        * Please see the [OOUI documentation on MediaWiki] [1] for more information and examples.
         *
         * This widget can be used inside a HTML form, such as a OO.ui.FormLayout.
         *
@@ -12,7 +12,7 @@
         *     var dateTimeInput = new mw.widgets.datetime.DateTimeInputWidget( {} )
         *     $( 'body' ).append( dateTimeInput.$element );
         *
-        * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Inputs
+        * [1]: https://www.mediawiki.org/wiki/OOUI/Widgets/Inputs
         *
         * @class
         * @extends OO.ui.InputWidget
index fa45d5a..a9c2dd2 100644 (file)
@@ -1,6 +1,6 @@
 /*!
- * OOJS-UI defines used by the existing CSS (will make it easier to put this
- * widget in OOJS-UI once OOJS-UI is capable of handling it)
+ * OOUI defines used by the existing CSS (will make it easier to put this
+ * widget in OOUI once OOUI is capable of handling it)
  */
 
 .oo-ui-box-sizing( @type: border-box ) {
index f2e0f4d..c39e43a 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * HTMLForm enhancements:
- * Infuse some OOjs UI HTMLForm fields (those which benefit from always being infused).
+ * Infuse some OOUI HTMLForm fields (those which benefit from always being infused).
  */
 ( function ( mw, $ ) {
 
index 4f672fc..01108e6 100644 (file)
@@ -3,7 +3,7 @@
        mw.htmlform = {};
 
        /**
-        * Allows custom data specific to HTMLFormField to be set for OOjs UI forms. This picks up the
+        * Allows custom data specific to HTMLFormField to be set for OOUI forms. This picks up the
         * extra config from a matching PHP widget (defined in HTMLFormElement.php) when constructed using
         * OO.ui.infuse().
         *
index bfd5c06..ca4d239 100644 (file)
@@ -57,6 +57,7 @@
                this.feedbackPageTitle = config.title || new mw.Title( 'Feedback' );
 
                this.messagePosterPromise = mw.messagePoster.factory.create( this.feedbackPageTitle, config.apiUrl );
+               this.foreignApi = config.apiUrl ? new mw.ForeignApi( config.apiUrl ) : null;
 
                // Links
                this.bugsTaskSubmissionLink = config.bugsLink || '//phabricator.wikimedia.org/maniphest/task/edit/form/1/';
         * Respond to dialog submit event. If the information was
         * submitted successfully, open a MessageDialog to thank the user.
         *
-        * @param {string} [status] A status of the end of operation
+        * @param {string} status A status of the end of operation
         *  of the main feedback dialog. Empty if the dialog was
         *  dismissed with no action or the user followed the button
         *  to the external task reporting site.
+        * @param {string} feedbackPageName
+        * @param {string} feedbackPageUrl
         */
-       mw.Feedback.prototype.onDialogSubmit = function ( status ) {
+       mw.Feedback.prototype.onDialogSubmit = function ( status, feedbackPageName, feedbackPageUrl ) {
                var dialogConfig;
 
                if ( status !== 'submitted' ) {
                        title: mw.msg( 'feedback-thanks-title' ),
                        message: $( '<span>' ).msg(
                                'feedback-thanks',
-                               this.feedbackPageTitle.getNameText(),
+                               feedbackPageName,
                                $( '<a>' ).attr( {
                                        target: '_blank',
-                                       href: this.feedbackPageTitle.getUrl()
+                                       href: feedbackPageUrl
                                } )
                        ),
                        actions: [
                        this.constructor.static.dialog,
                        {
                                title: mw.msg( this.dialogTitleMessageKey ),
+                               foreignApi: this.foreignApi,
                                settings: {
                                        messagePosterPromise: this.messagePosterPromise,
                                        title: this.feedbackPageTitle,
         */
        mw.Feedback.Dialog.prototype.getSetupProcess = function ( data ) {
                return mw.Feedback.Dialog.parent.prototype.getSetupProcess.call( this, data )
+                       .next( function () {
+                               // Get the URL of the target page, we want to use that in links in the intro
+                               // and in the success dialog
+                               var dialog = this;
+                               if ( data.foreignApi ) {
+                                       return data.foreignApi.get( {
+                                               action: 'query',
+                                               prop: 'info',
+                                               inprop: 'url',
+                                               formatversion: 2,
+                                               titles: data.settings.title.getPrefixedText()
+                                       } ).then( function ( data ) {
+                                               dialog.feedbackPageUrl = OO.getProp( data, 'query', 'pages', 0, 'canonicalurl' );
+                                       } );
+                               } else {
+                                       this.feedbackPageUrl = data.settings.title.getUrl();
+                               }
+                       }, this )
                        .next( function () {
                                var plainMsg, parsedMsg,
                                        settings = data.settings;
                                this.setBugReportLink( settings.bugsTaskSubmissionLink );
                                this.feedbackPageTitle = settings.title;
                                this.feedbackPageName = settings.title.getNameText();
-                               this.feedbackPageUrl = settings.title.getUrl();
 
                                // Useragent checkbox
                                if ( settings.useragentCheckbox.show ) {
index 148c4c7..b98ba13 100644 (file)
@@ -1,4 +1,4 @@
-/* HACK: Set sane font-size for OOjs UI dialogs (and menus/popups inside the default overlay), in
+/* HACK: Set sane font-size for OOUI dialogs (and menus/popups inside the default overlay), in
    the most common case. This should be skin's responsibility, but alas our skins tend to have the
    weirdest font-sizes on body. This shall be removed when we make the MediaWiki skins bundled with
    tarball sane. (T91152) */
index dffa863..0c65512 100644 (file)
@@ -1,9 +1,9 @@
 ( function ( mw ) {
        var isMobile;
-       // Connect OOjs UI to MediaWiki's localisation system
+       // Connect OOUI to MediaWiki's localisation system
        OO.ui.getUserLanguages = mw.language.getFallbackLanguageChain;
        OO.ui.msg = mw.msg;
-       // Connect OOjs UI's deprecation warnings to MediaWiki's logging system
+       // Connect OOUI's deprecation warnings to MediaWiki's logging system
        OO.ui.warnDeprecation = function ( message ) {
                mw.track( 'mw.deprecate', 'oojs-ui' );
                mw.log.warn( message );
index 3b02e28..cc769d7 100644 (file)
@@ -195,6 +195,11 @@ abstract class MWHttpRequestTestCase extends PHPUnit_Framework_TestCase {
                $this->assertSame( 401, $request->getStatus() );
        }
 
+       public function testFactoryDefaults() {
+               $request = MWHttpRequest::factory( 'http://acme.test' );
+               $this->assertInstanceOf( MWHttpRequest::class, $request );
+       }
+
        // --------------------
 
        /**
@@ -242,4 +247,5 @@ abstract class MWHttpRequestTestCase extends PHPUnit_Framework_TestCase {
                $this->assertArrayNotHasKey( strtolower( $name ),
                        array_change_key_case( $cookieJar->cookie, CASE_LOWER ) );
        }
+
 }
index 9b5897c..4dd4bc6 100644 (file)
@@ -811,10 +811,6 @@ class ParserTestRunner {
                $options = ParserOptions::newFromContext( $context );
                $options->setTimestamp( $this->getFakeTimestamp() );
 
-               if ( !isset( $opts['wrap'] ) ) {
-                       $options->setWrapOutputClass( false );
-               }
-
                if ( isset( $opts['tidy'] ) ) {
                        if ( !$this->tidySupport->isEnabled() ) {
                                $this->recorder->skipped( $test, 'tidy extension is not installed' );
@@ -854,7 +850,8 @@ class ParserTestRunner {
                } else {
                        $output = $parser->parse( $test['input'], $title, $options, true, true, 1337 );
                        $out = $output->getText( [
-                               'allowTOC' => !isset( $opts['notoc'] )
+                               'allowTOC' => !isset( $opts['notoc'] ),
+                               'unwrap' => !isset( $opts['wrap'] ),
                        ] );
                        if ( isset( $opts['tidy'] ) ) {
                                $out = preg_replace( '/\s+$/', '', $out );
index aaa135d..75ebd31 100644 (file)
@@ -26,7 +26,6 @@ class ExtraParserTest extends MediaWikiTestCase {
                // FIXME: This test should pass without setting global content language
                $this->options = ParserOptions::newFromUserAndLang( new User, $contLang );
                $this->options->setTemplateCallback( [ __CLASS__, 'statelessFetchTemplate' ] );
-               $this->options->setWrapOutputClass( false );
                $this->parser = new Parser;
 
                MagicWord::clearCache();
@@ -41,9 +40,8 @@ class ExtraParserTest extends MediaWikiTestCase {
 
                $title = Title::newFromText( 'Unit test' );
                $options = ParserOptions::newFromUser( new User() );
-               $options->setWrapOutputClass( false );
                $this->assertEquals( "<p>$longLine</p>",
-                       $this->parser->parse( $longLine, $title, $options )->getText() );
+                       $this->parser->parse( $longLine, $title, $options )->getText( [ 'unwrap' => true ] ) );
        }
 
        /**
@@ -55,7 +53,7 @@ class ExtraParserTest extends MediaWikiTestCase {
                $parserOutput = $this->parser->parse( "Test\n{{Foo}}\n{{Bar}}", $title, $this->options );
                $this->assertEquals(
                        "<p>Test\nContent of <i>Template:Foo</i>\nContent of <i>Template:Bar</i>\n</p>",
-                       $parserOutput->getText()
+                       $parserOutput->getText( [ 'unwrap' => true ] )
                );
        }
 
index 388aee7..8adb55f 100644 (file)
@@ -1,5 +1,10 @@
 <?php
 
+/**
+ * @group GlobalFunctions
+ * @covers ::wfArrayFilter
+ * @covers ::wfArrayFilterByKey
+ */
 class WfArrayFilterTest extends \PHPUnit_Framework_TestCase {
        public function testWfArrayFilter() {
                $arr = [ 'a' => 1, 'b' => 2, 'c' => 3 ];
index 7f42254..f9e2cc1 100644 (file)
@@ -5,7 +5,6 @@ use MediaWiki\MediaWikiServices;
 /**
  * @group Database
  */
-
 class LinkerTest extends MediaWikiLangTestCase {
 
        /**
index 227dd87..dc2e9ae 100644 (file)
@@ -3,7 +3,6 @@
 /**
  * @covers WikiReference
  */
-
 class WikiReferenceTest extends PHPUnit_Framework_TestCase {
 
        use MediaWikiCoversValidator;
index d19d998..fa9898d 100644 (file)
@@ -174,7 +174,7 @@ class DatabaseTestHelper extends Database {
                return false;
        }
 
-       function affectedRows() {
+       function fetchAffectedRowCount() {
                return -1;
        }
 
index 088ab4f..6977aef 100644 (file)
@@ -29,8 +29,54 @@ class MWCallableUpdateTest extends PHPUnit_Framework_TestCase {
                // Emulate rollback
                $db->rollback( __METHOD__ );
 
+               $update->doUpdate();
+
+               // Ensure it was cancelled
+               $this->assertSame( 0, $ran );
+       }
+
+       public function testCancelSome() {
+               // Prepare update and DB
+               $db1 = new DatabaseTestHelper( __METHOD__ );
+               $db1->begin( __METHOD__ );
+               $db2 = new DatabaseTestHelper( __METHOD__ );
+               $db2->begin( __METHOD__ );
+               $ran = 0;
+               $update = new MWCallableUpdate( function () use ( &$ran ) {
+                       $ran++;
+               }, __METHOD__, [ $db1, $db2 ] );
+
+               // Emulate rollback
+               $db1->rollback( __METHOD__ );
+
+               $update->doUpdate();
+
+               // Prevents: "Notice: DB transaction writes or callbacks still pending"
+               $db2->rollback( __METHOD__ );
+
                // Ensure it was cancelled
+               $this->assertSame( 0, $ran );
+       }
+
+       public function testCancelAll() {
+               // Prepare update and DB
+               $db1 = new DatabaseTestHelper( __METHOD__ );
+               $db1->begin( __METHOD__ );
+               $db2 = new DatabaseTestHelper( __METHOD__ );
+               $db2->begin( __METHOD__ );
+               $ran = 0;
+               $update = new MWCallableUpdate( function () use ( &$ran ) {
+                       $ran++;
+               }, __METHOD__, [ $db1, $db2 ] );
+
+               // Emulate rollbacks
+               $db1->rollback( __METHOD__ );
+               $db2->rollback( __METHOD__ );
+
                $update->doUpdate();
+
+               // Ensure it was cancelled
                $this->assertSame( 0, $ran );
        }
+
 }
index bd1412e..2811a9c 100644 (file)
@@ -6,7 +6,6 @@
  * @group Database
  * @group Installer
  */
-
 class OracleInstallerTest extends MediaWikiTestCase {
 
        /**
index 76b4ee1..8831265 100644 (file)
@@ -4,7 +4,6 @@
  *
  * @group Database
  */
-
 class ArrayUtilsTest extends PHPUnit_Framework_TestCase {
 
        use MediaWikiCoversValidator;
index 8a120ea..a36b259 100644 (file)
@@ -8,7 +8,6 @@
  * @todo Test methods in this call should be split into a method and a
  * dataprovider.
  */
-
 class IPTest extends PHPUnit_Framework_TestCase {
 
        use MediaWikiCoversValidator;
index a2c3bdc..ca78f65 100644 (file)
@@ -1481,7 +1481,10 @@ class WANObjectCacheTest extends PHPUnit_Framework_TestCase {
                $wanCache = new WANObjectCache( [
                        'cache' => $localBag,
                        'pool' => 'testcache-hash',
-                       'relayer' => new EventRelayerNull( [] )
+                       'relayer' => new EventRelayerNull( [] ),
+                       'mcrouterAware' => true,
+                       'region' => 'pmtpa',
+                       'cluster' => 'mw-wan'
                ] );
                $valFunc = function () {
                        return 1;
@@ -1498,6 +1501,60 @@ class WANObjectCacheTest extends PHPUnit_Framework_TestCase {
                $wanCache->reap( 'zzz', time() - 300 );
        }
 
+       public function testMcRouterSupportBroadcastDelete() {
+               $localBag = $this->getMockBuilder( EmptyBagOStuff::class )
+                       ->setMethods( [ 'set' ] )->getMock();
+               $wanCache = new WANObjectCache( [
+                       'cache' => $localBag,
+                       'pool' => 'testcache-hash',
+                       'relayer' => new EventRelayerNull( [] ),
+                       'mcrouterAware' => true,
+                       'region' => 'pmtpa',
+                       'cluster' => 'mw-wan'
+               ] );
+
+               $localBag->expects( $this->once() )->method( 'set' )
+                       ->with( "/*/mw-wan/" . $wanCache::VALUE_KEY_PREFIX . "test" );
+
+               $wanCache->delete( 'test' );
+       }
+
+       public function testMcRouterSupportBroadcastTouchCK() {
+               $localBag = $this->getMockBuilder( EmptyBagOStuff::class )
+                       ->setMethods( [ 'set' ] )->getMock();
+               $wanCache = new WANObjectCache( [
+                       'cache' => $localBag,
+                       'pool' => 'testcache-hash',
+                       'relayer' => new EventRelayerNull( [] ),
+                       'mcrouterAware' => true,
+                       'region' => 'pmtpa',
+                       'cluster' => 'mw-wan'
+               ] );
+
+               $localBag->expects( $this->once() )->method( 'set' )
+                       ->with( "/*/mw-wan/" . $wanCache::TIME_KEY_PREFIX . "test" );
+
+               $wanCache->touchCheckKey( 'test' );
+       }
+
+       public function testMcRouterSupportBroadcastResetCK() {
+               $localBag = $this->getMockBuilder( EmptyBagOStuff::class )
+                       ->setMethods( [ 'delete' ] )->getMock();
+               $wanCache = new WANObjectCache( [
+                       'cache' => $localBag,
+                       'pool' => 'testcache-hash',
+                       'relayer' => new EventRelayerNull( [] ),
+                       'mcrouterAware' => true,
+                       'region' => 'pmtpa',
+                       'cluster' => 'mw-wan'
+               ] );
+
+               $localBag->expects( $this->once() )->method( 'delete' )
+                       ->with( "/*/mw-wan/" . $wanCache::TIME_KEY_PREFIX . "test" );
+
+               $wanCache->resetCheckKey( 'test' );
+       }
+
        /**
         * @dataProvider provideAdaptiveTTL
         * @covers WANObjectCache::adaptiveTTL()
index 54706d5..25613fe 100644 (file)
@@ -12,7 +12,7 @@ class TransactionProfilerTest extends PHPUnit_Framework_TestCase {
 
        public function testAffected() {
                $logger = $this->getMockBuilder( LoggerInterface::class )->getMock();
-               $logger->expects( $this->exactly( 3 ) )->method( 'info' );
+               $logger->expects( $this->exactly( 3 ) )->method( 'warning' );
 
                $tp = new TransactionProfiler();
                $tp->setLogger( $logger );
@@ -27,7 +27,7 @@ class TransactionProfilerTest extends PHPUnit_Framework_TestCase {
        public function testReadTime() {
                $logger = $this->getMockBuilder( LoggerInterface::class )->getMock();
                // 1 per query
-               $logger->expects( $this->exactly( 2 ) )->method( 'info' );
+               $logger->expects( $this->exactly( 2 ) )->method( 'warning' );
 
                $tp = new TransactionProfiler();
                $tp->setLogger( $logger );
@@ -42,7 +42,7 @@ class TransactionProfilerTest extends PHPUnit_Framework_TestCase {
        public function testWriteTime() {
                $logger = $this->getMockBuilder( LoggerInterface::class )->getMock();
                // 1 per query, 1 per trx, and one "sub-optimal trx" entry
-               $logger->expects( $this->exactly( 4 ) )->method( 'info' );
+               $logger->expects( $this->exactly( 4 ) )->method( 'warning' );
 
                $tp = new TransactionProfiler();
                $tp->setLogger( $logger );
@@ -56,7 +56,7 @@ class TransactionProfilerTest extends PHPUnit_Framework_TestCase {
 
        public function testAffectedTrx() {
                $logger = $this->getMockBuilder( LoggerInterface::class )->getMock();
-               $logger->expects( $this->exactly( 1 ) )->method( 'info' );
+               $logger->expects( $this->exactly( 1 ) )->method( 'warning' );
 
                $tp = new TransactionProfiler();
                $tp->setLogger( $logger );
@@ -69,7 +69,7 @@ class TransactionProfilerTest extends PHPUnit_Framework_TestCase {
        public function testWriteTimeTrx() {
                $logger = $this->getMockBuilder( LoggerInterface::class )->getMock();
                // 1 per trx, and one "sub-optimal trx" entry
-               $logger->expects( $this->exactly( 2 ) )->method( 'info' );
+               $logger->expects( $this->exactly( 2 ) )->method( 'warning' );
 
                $tp = new TransactionProfiler();
                $tp->setLogger( $logger );
@@ -81,7 +81,7 @@ class TransactionProfilerTest extends PHPUnit_Framework_TestCase {
 
        public function testConns() {
                $logger = $this->getMockBuilder( LoggerInterface::class )->getMock();
-               $logger->expects( $this->exactly( 2 ) )->method( 'info' );
+               $logger->expects( $this->exactly( 2 ) )->method( 'warning' );
 
                $tp = new TransactionProfiler();
                $tp->setLogger( $logger );
@@ -95,7 +95,7 @@ class TransactionProfilerTest extends PHPUnit_Framework_TestCase {
 
        public function testMasterConns() {
                $logger = $this->getMockBuilder( LoggerInterface::class )->getMock();
-               $logger->expects( $this->exactly( 2 ) )->method( 'info' );
+               $logger->expects( $this->exactly( 2 ) )->method( 'warning' );
 
                $tp = new TransactionProfiler();
                $tp->setLogger( $logger );
@@ -112,7 +112,7 @@ class TransactionProfilerTest extends PHPUnit_Framework_TestCase {
 
        public function testReadQueryCount() {
                $logger = $this->getMockBuilder( LoggerInterface::class )->getMock();
-               $logger->expects( $this->exactly( 2 ) )->method( 'info' );
+               $logger->expects( $this->exactly( 2 ) )->method( 'warning' );
 
                $tp = new TransactionProfiler();
                $tp->setLogger( $logger );
@@ -126,7 +126,7 @@ class TransactionProfilerTest extends PHPUnit_Framework_TestCase {
 
        public function testWriteQueryCount() {
                $logger = $this->getMockBuilder( LoggerInterface::class )->getMock();
-               $logger->expects( $this->exactly( 2 ) )->method( 'info' );
+               $logger->expects( $this->exactly( 2 ) )->method( 'warning' );
 
                $tp = new TransactionProfiler();
                $tp->setLogger( $logger );
index a23a2a0..caf1281 100644 (file)
@@ -53,6 +53,9 @@ class FakeDatabaseMysqlBase extends DatabaseMysqlBase {
        protected function doQuery( $sql ) {
        }
 
+       protected function fetchAffectedRowCount() {
+       }
+
        // From DatabaseMysqli
        protected function mysqlConnect( $realServer ) {
        }
index 23f7865..d8ebeff 100644 (file)
@@ -560,7 +560,7 @@ class DatabaseSQLTest extends PHPUnit_Framework_TestCase {
                                        'rows' => [ 'field' => 'text', 'field2' => 'text2' ],
                                ],
                                "DELETE FROM replace_table " .
-                                       "WHERE ( field='text' ); " .
+                                       "WHERE (field = 'text'); " .
                                        "INSERT INTO replace_table " .
                                        "(field,field2) " .
                                        "VALUES ('text','text2')"
@@ -576,7 +576,7 @@ class DatabaseSQLTest extends PHPUnit_Framework_TestCase {
                                        ],
                                ],
                                "DELETE FROM module_deps " .
-                                       "WHERE ( md_module='module' AND md_skin='skin' ); " .
+                                       "WHERE (md_module = 'module' AND md_skin = 'skin'); " .
                                        "INSERT INTO module_deps " .
                                        "(md_module,md_skin,md_deps) " .
                                        "VALUES ('module','skin','deps')"
@@ -598,12 +598,12 @@ class DatabaseSQLTest extends PHPUnit_Framework_TestCase {
                                        ],
                                ],
                                "DELETE FROM module_deps " .
-                                       "WHERE ( md_module='module' AND md_skin='skin' ); " .
+                                       "WHERE (md_module = 'module' AND md_skin = 'skin'); " .
                                        "INSERT INTO module_deps " .
                                        "(md_module,md_skin,md_deps) " .
                                        "VALUES ('module','skin','deps'); " .
                                        "DELETE FROM module_deps " .
-                                       "WHERE ( md_module='module2' AND md_skin='skin2' ); " .
+                                       "WHERE (md_module = 'module2' AND md_skin = 'skin2'); " .
                                        "INSERT INTO module_deps " .
                                        "(md_module,md_skin,md_deps) " .
                                        "VALUES ('module2','skin2','deps2')"
@@ -625,12 +625,12 @@ class DatabaseSQLTest extends PHPUnit_Framework_TestCase {
                                        ],
                                ],
                                "DELETE FROM module_deps " .
-                                       "WHERE ( md_module='module' ) OR ( md_skin='skin' ); " .
+                                       "WHERE (md_module = 'module') OR (md_skin = 'skin'); " .
                                        "INSERT INTO module_deps " .
                                        "(md_module,md_skin,md_deps) " .
                                        "VALUES ('module','skin','deps'); " .
                                        "DELETE FROM module_deps " .
-                                       "WHERE ( md_module='module2' ) OR ( md_skin='skin2' ); " .
+                                       "WHERE (md_module = 'module2') OR (md_skin = 'skin2'); " .
                                        "INSERT INTO module_deps " .
                                        "(md_module,md_skin,md_deps) " .
                                        "VALUES ('module2','skin2','deps2')"
index 4a0a7e7..e131506 100644 (file)
@@ -322,7 +322,7 @@ class DatabaseTest extends PHPUnit_Framework_TestCase {
         */
        private function getMockDB( $methods = [] ) {
                static $abstractMethods = [
-                       'affectedRows',
+                       'fetchAffectedRowCount',
                        'closeConnection',
                        'dataSeek',
                        'doQuery',
index 93ab35c..232b0bb 100644 (file)
@@ -62,7 +62,7 @@ class ParserOptionsTest extends MediaWikiTestCase {
                        'No overrides' => [ true, [] ],
                        'In-key options are ok' => [ true, [
                                'thumbsize' => 1e100,
-                               'wrapclass' => false,
+                               'printable' => false,
                        ] ],
                        'Non-in-key options are not ok' => [ false, [
                                'removeComments' => false,
@@ -102,7 +102,7 @@ class ParserOptionsTest extends MediaWikiTestCase {
        }
 
        public static function provideOptionsHash() {
-               $used = [ 'wrapclass', 'printable' ];
+               $used = [ 'thumbsize', 'printable' ];
 
                $classWrapper = TestingAccessWrapper::newFromClass( ParserOptions::class );
                $classWrapper->getDefaults();
@@ -116,9 +116,9 @@ class ParserOptionsTest extends MediaWikiTestCase {
                        'Canonical options, used some options' => [ $used, 'canonical', [] ],
                        'Used some options, non-default values' => [
                                $used,
-                               'printable=1!wrapclass=foobar',
+                               'printable=1!thumbsize=200',
                                [
-                                       'wrapclass' => 'foobar',
+                                       'thumbsize' => 200,
                                        'printable' => true,
                                ]
                        ],
index 9642bbc..efcc4e0 100644 (file)
@@ -105,6 +105,8 @@ class ParserOutputTest extends MediaWikiTestCase {
                        'wgScriptPath' => '/w',
                        'wgScript' => '/w/index.php',
                ] );
+               $this->hideDeprecated( 'ParserOutput stateful allowTOC' );
+               $this->hideDeprecated( 'ParserOutput stateful enableSectionEditLinks' );
 
                $po = new ParserOutput( $text );
 
@@ -125,7 +127,7 @@ class ParserOutputTest extends MediaWikiTestCase {
        public static function provideGetText() {
                // phpcs:disable Generic.Files.LineLength
                $text = <<<EOF
-<p>Test document.
+<div class="mw-parser-output"><p>Test document.
 </p>
 <mw:toc><div id="toc" class="toc"><div class="toctitle"><h2>Contents</h2></div>
 <ul>
@@ -150,13 +152,13 @@ class ParserOutputTest extends MediaWikiTestCase {
 </p>
 <h2><span class="mw-headline" id="Section_3">Section 3</span><mw:editsection page="Test Page" section="4">Section 3</mw:editsection></h2>
 <p>Three
-</p>
+</p></div>
 EOF;
 
                return [
                        'No stateless options, default state' => [
                                [], [], $text, <<<EOF
-<p>Test document.
+<div class="mw-parser-output"><p>Test document.
 </p>
 <div id="toc" class="toc"><div class="toctitle"><h2>Contents</h2></div>
 <ul>
@@ -181,12 +183,12 @@ EOF;
 </p>
 <h2><span class="mw-headline" id="Section_3">Section 3</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=Test_Page&amp;action=edit&amp;section=4" title="Edit section: Section 3">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
 <p>Three
-</p>
+</p></div>
 EOF
                        ],
                        'No stateless options, TOC statefully disabled' => [
                                [], [ 'mTOCEnabled' => false ], $text, <<<EOF
-<p>Test document.
+<div class="mw-parser-output"><p>Test document.
 </p>
 
 <h2><span class="mw-headline" id="Section_1">Section 1</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=Test_Page&amp;action=edit&amp;section=1" title="Edit section: Section 1">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
@@ -200,12 +202,12 @@ EOF
 </p>
 <h2><span class="mw-headline" id="Section_3">Section 3</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=Test_Page&amp;action=edit&amp;section=4" title="Edit section: Section 3">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
 <p>Three
-</p>
+</p></div>
 EOF
                        ],
                        'No stateless options, section edits statefully disabled' => [
                                [], [ 'mEditSectionTokens' => false ], $text, <<<EOF
-<p>Test document.
+<div class="mw-parser-output"><p>Test document.
 </p>
 <div id="toc" class="toc"><div class="toctitle"><h2>Contents</h2></div>
 <ul>
@@ -230,14 +232,14 @@ EOF
 </p>
 <h2><span class="mw-headline" id="Section_3">Section 3</span></h2>
 <p>Three
-</p>
+</p></div>
 EOF
                        ],
                        'Stateless options override stateful settings' => [
                                [ 'allowTOC' => true, 'enableSectionEditLinks' => true ],
                                [ 'mTOCEnabled' => false, 'mEditSectionTokens' => false ],
                                $text, <<<EOF
-<p>Test document.
+<div class="mw-parser-output"><p>Test document.
 </p>
 <div id="toc" class="toc"><div class="toctitle"><h2>Contents</h2></div>
 <ul>
@@ -262,12 +264,12 @@ EOF
 </p>
 <h2><span class="mw-headline" id="Section_3">Section 3</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=Test_Page&amp;action=edit&amp;section=4" title="Edit section: Section 3">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
 <p>Three
-</p>
+</p></div>
 EOF
                        ],
                        'Statelessly disable section edit links' => [
                                [ 'enableSectionEditLinks' => false ], [], $text, <<<EOF
-<p>Test document.
+<div class="mw-parser-output"><p>Test document.
 </p>
 <div id="toc" class="toc"><div class="toctitle"><h2>Contents</h2></div>
 <ul>
@@ -292,13 +294,43 @@ EOF
 </p>
 <h2><span class="mw-headline" id="Section_3">Section 3</span></h2>
 <p>Three
-</p>
+</p></div>
 EOF
                        ],
                        'Statelessly disable TOC' => [
                                [ 'allowTOC' => false ], [], $text, <<<EOF
+<div class="mw-parser-output"><p>Test document.
+</p>
+
+<h2><span class="mw-headline" id="Section_1">Section 1</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=Test_Page&amp;action=edit&amp;section=1" title="Edit section: Section 1">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
+<p>One
+</p>
+<h2><span class="mw-headline" id="Section_2">Section 2</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=Test_Page&amp;action=edit&amp;section=2" title="Edit section: Section 2">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
+<p>Two
+</p>
+<h3><span class="mw-headline" id="Section_2.1">Section 2.1</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=Test_Page&amp;action=edit&amp;section=3" title="Edit section: Section 2.1">edit</a><span class="mw-editsection-bracket">]</span></span></h3>
+<p>Two point one
+</p>
+<h2><span class="mw-headline" id="Section_3">Section 3</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=Test_Page&amp;action=edit&amp;section=4" title="Edit section: Section 3">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
+<p>Three
+</p></div>
+EOF
+                       ],
+                       'Statelessly unwrap text' => [
+                               [ 'unwrap' => true ], [], $text, <<<EOF
 <p>Test document.
 </p>
+<div id="toc" class="toc"><div class="toctitle"><h2>Contents</h2></div>
+<ul>
+<li class="toclevel-1 tocsection-1"><a href="#Section_1"><span class="tocnumber">1</span> <span class="toctext">Section 1</span></a></li>
+<li class="toclevel-1 tocsection-2"><a href="#Section_2"><span class="tocnumber">2</span> <span class="toctext">Section 2</span></a>
+<ul>
+<li class="toclevel-2 tocsection-3"><a href="#Section_2.1"><span class="tocnumber">2.1</span> <span class="toctext">Section 2.1</span></a></li>
+</ul>
+</li>
+<li class="toclevel-1 tocsection-4"><a href="#Section_3"><span class="tocnumber">3</span> <span class="toctext">Section 3</span></a></li>
+</ul>
+</div>
 
 <h2><span class="mw-headline" id="Section_1">Section 1</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=Test_Page&amp;action=edit&amp;section=1" title="Edit section: Section 1">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
 <p>One
@@ -314,6 +346,9 @@ EOF
 </p>
 EOF
                        ],
+                       'Unwrap without a mw-parser-output wrapper' => [
+                               [ 'unwrap' => true ], [], '<div class="foobar">Content</div>', '<div class="foobar">Content</div>'
+                       ],
                ];
                // phpcs:enable
        }
index 7e31cba..2fdaa18 100644 (file)
@@ -46,7 +46,6 @@ class TagHooksTest extends MediaWikiTestCase {
        private function getParserOptions() {
                global $wgContLang;
                $popt = ParserOptions::newFromUserAndLang( new User, $wgContLang );
-               $popt->setWrapOutputClass( false );
                return $popt;
        }
 
@@ -63,7 +62,7 @@ class TagHooksTest extends MediaWikiTestCase {
                        Title::newFromText( 'Test' ),
                        $this->getParserOptions()
                );
-               $this->assertEquals( "<p>FooOneBaz\n</p>", $parserOutput->getText() );
+               $this->assertEquals( "<p>FooOneBaz\n</p>", $parserOutput->getText( [ 'unwrap' => true ] ) );
 
                $parser->mPreprocessor = null; # Break the Parser <-> Preprocessor cycle
        }
@@ -98,7 +97,7 @@ class TagHooksTest extends MediaWikiTestCase {
                        Title::newFromText( 'Test' ),
                        $this->getParserOptions()
                );
-               $this->assertEquals( "<p>FooOneBaz\n</p>", $parserOutput->getText() );
+               $this->assertEquals( "<p>FooOneBaz\n</p>", $parserOutput->getText( [ 'unwrap' => true ] ) );
 
                $parser->mPreprocessor = null; # Break the Parser <-> Preprocessor cycle
        }
index 88f58cf..e460960 100644 (file)
@@ -2,6 +2,7 @@
 
 namespace MediaWiki\Session;
 
+use Config;
 use MediaWikiTestCase;
 use User;
 use Wikimedia\TestingAccessWrapper;
@@ -14,9 +15,16 @@ use Wikimedia\TestingAccessWrapper;
 class SessionBackendTest extends MediaWikiTestCase {
        const SESSIONID = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
 
+       /** @var SessionManager */
        protected $manager;
+
+       /** @var Config */
        protected $config;
+
+       /** @var SessionProvider */
        protected $provider;
+
+       /** @var TestBagOStuff */
        protected $store;
 
        protected $onSessionMetadataCalled = false;
@@ -25,6 +33,7 @@ class SessionBackendTest extends MediaWikiTestCase {
         * Returns a non-persistent backend that thinks it has at least one session active
         * @param User|null $user
         * @param string $id
+        * @return SessionBackend
         */
        protected function getBackend( User $user = null, $id = null ) {
                if ( !$this->config ) {
@@ -149,7 +158,7 @@ class SessionBackendTest extends MediaWikiTestCase {
                $this->assertSame( $info->forceHTTPS(), $backend->shouldForceHTTPS() );
 
                $expire = time() + 100;
-               $this->store->setSessionMeta( self::SESSIONID, [ 'expires' => $expire ], 2 );
+               $this->store->setSessionMeta( self::SESSIONID, [ 'expires' => $expire ] );
 
                $info = new SessionInfo( SessionInfo::MIN_PRIORITY, [
                        'provider' => $this->provider,
index 6c989f3..e042f76 100644 (file)
@@ -14,7 +14,14 @@ use Wikimedia\TestingAccessWrapper;
  */
 class SessionManagerTest extends MediaWikiTestCase {
 
-       protected $config, $logger, $store;
+       /** @var \HashConfig */
+       private $config;
+
+       /** @var \TestLogger */
+       private $logger;
+
+       /** @var TestBagOStuff */
+       private $store;
 
        protected function getManager() {
                \ObjectCache::$instances['testSessionStore'] = new TestBagOStuff();
index bac2088..f9e30f0 100644 (file)
@@ -14,53 +14,44 @@ class TestBagOStuff extends \CachedBagOStuff {
        /**
         * @param string $id Session ID
         * @param array $data Session data
-        * @param int $expiry
-        * @param User $user User for metadata
         */
-       public function setSessionData( $id, array $data, $expiry = 0, User $user = null ) {
-               $this->setSession( $id, [ 'data' => $data ], $expiry, $user );
+       public function setSessionData( $id, array $data ) {
+               $this->setSession( $id, [ 'data' => $data ] );
        }
 
        /**
         * @param string $id Session ID
         * @param array $metadata Session metadata
-        * @param int $expiry
         */
-       public function setSessionMeta( $id, array $metadata, $expiry = 0 ) {
-               $this->setSession( $id, [ 'metadata' => $metadata ], $expiry );
+       public function setSessionMeta( $id, array $metadata ) {
+               $this->setSession( $id, [ 'metadata' => $metadata ] );
        }
 
        /**
         * @param string $id Session ID
         * @param array $blob Session metadata and data
-        * @param int $expiry
-        * @param User $user User for metadata
         */
-       public function setSession( $id, array $blob, $expiry = 0, User $user = null ) {
+       public function setSession( $id, array $blob ) {
                $blob += [
                        'data' => [],
                        'metadata' => [],
                ];
                $blob['metadata'] += [
-                       'userId' => $user ? $user->getId() : 0,
-                       'userName' => $user ? $user->getName() : null,
-                       'userToken' => $user ? $user->getToken( true ) : null,
+                       'userId' => 0,
+                       'userName' => null,
+                       'userToken' => null,
                        'provider' => 'DummySessionProvider',
                ];
 
-               $this->setRawSession( $id, $blob, $expiry, $user );
+               $this->setRawSession( $id, $blob );
        }
 
        /**
         * @param string $id Session ID
         * @param array|mixed $blob Session metadata and data
-        * @param int $expiry
         */
-       public function setRawSession( $id, $blob, $expiry = 0 ) {
-               if ( $expiry <= 0 ) {
-                       $expiry = \RequestContext::getMain()->getConfig()->get( 'ObjectCacheSessionExpiry' );
-               }
-
+       public function setRawSession( $id, $blob ) {
+               $expiry = \RequestContext::getMain()->getConfig()->get( 'ObjectCacheSessionExpiry' );
                $this->set( $this->makeKey( 'MWSession', $id ), $blob, $expiry );
        }
 
index 5b3fbfa..06b0667 100644 (file)
@@ -7,7 +7,6 @@
  *
  * @author Bene* < benestar.wikimedia@gmail.com >
  */
-
 class SkinTemplateTest extends MediaWikiTestCase {
        /**
         * @dataProvider makeListItemProvider
index 22bdefd..10c6d04 100644 (file)
@@ -8,7 +8,6 @@
  *
  * @group Database
  */
-
 class ImageListPagerTest extends MediaWikiTestCase {
        /**
         * @expectedException MWException
index 5943294..06c146c 100644 (file)
@@ -6,18 +6,21 @@
 
        /**
         * Make a safe copy of localEnv:
-        * - Creates a copy so that when the same object reference to module hooks is
-        *   used by multipe test hooks, our QUnit.module extension will not wrap the
-        *   callbacks multiple times. Instead, they wrap using a new object.
-        * - Normalise setup/teardown to avoid having to repeat this in each extension
+        * - Creates a new object that inherits, instead of modifying the original.
+        *   This prevents recursion in the event that a test suite stores inherits
+        *   hooks object statically and passes it to multiple QUnit.module() calls.
+        * - Supporting QUnit 1.x 'setup' and 'teardown' hooks
         *   (deprecated in QUnit 1.16, removed in QUnit 2).
-        * - Strip any other properties.
         */
        function makeSafeEnv( localEnv ) {
-               return {
-                       beforeEach: localEnv.setup || localEnv.beforeEach,
-                       afterEach: localEnv.teardown || localEnv.afterEach
-               };
+               var wrap = localEnv ? Object.create( localEnv ) : {};
+               if ( wrap.setup ) {
+                       wrap.beforeEach = wrap.beforeEach || wrap.setup;
+               }
+               if ( wrap.teardown ) {
+                       wrap.afterEach = wrap.afterEach || wrap.teardown;
+               }
+               return wrap;
        }
 
        /**
                useFakeTimers: false,
                useFakeServer: false
        };
-       // Extend QUnit.module to provide a Sinon sandbox.
+       // Extend QUnit.module with:
+       // - Add support for QUnit 1.x 'setup' and 'teardown' hooks
+       // - Add a Sinon sandbox to the test context.
+       // - Add a test fixture to the test context.
        ( function () {
                var orgModule = QUnit.module;
                QUnit.module = function ( name, localEnv, executeNow ) {
-                       var orgBeforeEach, orgAfterEach, orgExecute;
+                       var orgExecute, orgBeforeEach, orgAfterEach;
                        if ( nested ) {
-                               // In a nested module, don't re-run our handlers.
+                               // In a nested module, don't re-add our hooks, QUnit does that already.
                                return orgModule.apply( this, arguments );
                        }
                        if ( arguments.length === 2 && typeof localEnv === 'function' ) {
                                };
                        }
 
-                       localEnv = localEnv || {};
+                       localEnv = makeSafeEnv( localEnv );
                        orgBeforeEach = localEnv.beforeEach;
                        orgAfterEach = localEnv.afterEach;
+
                        localEnv.beforeEach = function () {
+                               // Sinon sandbox
                                var config = sinon.getConfig( sinon.config );
                                config.injectInto = this;
                                sinon.sandbox.create( config );
 
-                               if ( orgBeforeEach ) {
-                                       return orgBeforeEach.apply( this, arguments );
-                               }
-                       };
-                       localEnv.afterEach = function () {
-                               var ret;
-                               if ( orgAfterEach ) {
-                                       ret = orgAfterEach.apply( this, arguments );
-                               }
-
-                               this.sandbox.verifyAndRestore();
-                               return ret;
-                       };
-                       return orgModule( name, localEnv, executeNow );
-               };
-       }() );
-
-       // Extend QUnit.module to provide a fixture element.
-       ( function () {
-               var orgModule = QUnit.module;
-               QUnit.module = function ( name, localEnv, executeNow ) {
-                       var orgBeforeEach, orgAfterEach;
-                       if ( nested ) {
-                               // In a nested module, don't re-run our handlers.
-                               return orgModule.apply( this, arguments );
-                       }
-                       if ( arguments.length === 2 && typeof localEnv === 'function' ) {
-                               executeNow = localEnv;
-                               localEnv = undefined;
-                       }
-
-                       localEnv = localEnv || {};
-                       orgBeforeEach = localEnv.beforeEach;
-                       orgAfterEach = localEnv.afterEach;
-                       localEnv.beforeEach = function () {
+                               // Fixture element
                                this.fixture = document.createElement( 'div' );
                                this.fixture.id = 'qunit-fixture';
                                document.body.appendChild( this.fixture );
                                if ( orgAfterEach ) {
                                        ret = orgAfterEach.apply( this, arguments );
                                }
-
+                               this.sandbox.verifyAndRestore();
                                this.fixture.parentNode.removeChild( this.fixture );
                                return ret;
                        };
-                       return orgModule( name, localEnv, executeNow );
-               };
-       }() );
 
-       // Extend QUnit.module to normalise localEnv.
-       // NOTE: This MUST be the last QUnit.module extension so that the above extensions
-       // may safely modify the object and assume beforeEach/afterEach.
-       ( function () {
-               var orgModule = QUnit.module;
-               QUnit.module = function ( name, localEnv, executeNow ) {
-                       if ( typeof localEnv === 'object' ) {
-                               localEnv = makeSafeEnv( localEnv );
-                       }
                        return orgModule( name, localEnv, executeNow );
                };
        }() );
                }
 
                return function ( orgEnv ) {
-                       var localEnv = orgEnv ? makeSafeEnv( orgEnv ) : {};
-                       // MediaWiki env testing
-                       localEnv.config = orgEnv && orgEnv.config || {};
-                       localEnv.messages = orgEnv && orgEnv.messages || {};
-
-                       return {
-                               beforeEach: function () {
-                                       // Greetings, mock environment!
-                                       mw.config = new MwMap();
-                                       mw.config.set( freshConfigCopy( localEnv.config ) );
-                                       mw.messages = new MwMap();
-                                       mw.messages.set( freshMessagesCopy( localEnv.messages ) );
-                                       // Update reference to mw.messages
-                                       mw.jqueryMsg.setParserDefaults( {
-                                               messages: mw.messages
-                                       } );
-
-                                       this.suppressWarnings = suppressWarnings;
-                                       this.restoreWarnings = restoreWarnings;
+                       var localEnv, orgBeforeEach, orgAfterEach;
 
-                                       // Start tracking ajax requests
-                                       $( document ).on( 'ajaxSend', trackAjax );
-
-                                       if ( localEnv.beforeEach ) {
-                                               return localEnv.beforeEach.apply( this, arguments );
-                                       }
-                               },
+                       localEnv = makeSafeEnv( orgEnv );
+                       // MediaWiki env testing
+                       localEnv.config = localEnv.config || {};
+                       localEnv.messages = localEnv.messages || {};
 
-                               afterEach: function () {
-                                       var timers, pending, $activeLen, ret;
+                       orgBeforeEach = localEnv.beforeEach;
+                       orgAfterEach = localEnv.afterEach;
 
-                                       if ( localEnv.afterEach ) {
-                                               ret = localEnv.afterEach.apply( this, arguments );
-                                       }
+                       localEnv.beforeEach = function () {
+                               // Greetings, mock environment!
+                               mw.config = new MwMap();
+                               mw.config.set( freshConfigCopy( localEnv.config ) );
+                               mw.messages = new MwMap();
+                               mw.messages.set( freshMessagesCopy( localEnv.messages ) );
+                               // Update reference to mw.messages
+                               mw.jqueryMsg.setParserDefaults( {
+                                       messages: mw.messages
+                               } );
+
+                               this.suppressWarnings = suppressWarnings;
+                               this.restoreWarnings = restoreWarnings;
+
+                               // Start tracking ajax requests
+                               $( document ).on( 'ajaxSend', trackAjax );
 
-                                       // Stop tracking ajax requests
-                                       $( document ).off( 'ajaxSend', trackAjax );
+                               if ( orgBeforeEach ) {
+                                       return orgBeforeEach.apply( this, arguments );
+                               }
+                       };
+                       localEnv.afterEach = function () {
+                               var timers, pending, $activeLen, ret;
 
-                                       // As a convenience feature, automatically restore warnings if they're
-                                       // still suppressed by the end of the test.
-                                       restoreWarnings();
+                               if ( orgAfterEach ) {
+                                       ret = orgAfterEach.apply( this, arguments );
+                               }
 
-                                       // Farewell, mock environment!
-                                       mw.config = liveConfig;
-                                       mw.messages = liveMessages;
-                                       // Restore reference to mw.messages
-                                       mw.jqueryMsg.setParserDefaults( {
-                                               messages: liveMessages
+                               // Stop tracking ajax requests
+                               $( document ).off( 'ajaxSend', trackAjax );
+
+                               // As a convenience feature, automatically restore warnings if they're
+                               // still suppressed by the end of the test.
+                               restoreWarnings();
+
+                               // Farewell, mock environment!
+                               mw.config = liveConfig;
+                               mw.messages = liveMessages;
+                               // Restore reference to mw.messages
+                               mw.jqueryMsg.setParserDefaults( {
+                                       messages: liveMessages
+                               } );
+
+                               // Tests should use fake timers or wait for animations to complete
+                               // Check for incomplete animations/requests/etc and throw if there are any.
+                               if ( $.timers && $.timers.length !== 0 ) {
+                                       timers = $.timers.length;
+                                       $.each( $.timers, function ( i, timer ) {
+                                               var node = timer.elem;
+                                               mw.log.warn( 'Unfinished animation #' + i + ' in ' + timer.queue + ' queue on ' +
+                                                       mw.html.element( node.nodeName.toLowerCase(), $( node ).getAttrs() )
+                                               );
                                        } );
+                                       // Force animations to stop to give the next test a clean start
+                                       $.timers = [];
+                                       $.fx.stop();
 
-                                       // Tests should use fake timers or wait for animations to complete
-                                       // Check for incomplete animations/requests/etc and throw if there are any.
-                                       if ( $.timers && $.timers.length !== 0 ) {
-                                               timers = $.timers.length;
-                                               $.each( $.timers, function ( i, timer ) {
-                                                       var node = timer.elem;
-                                                       mw.log.warn( 'Unfinished animation #' + i + ' in ' + timer.queue + ' queue on ' +
-                                                               mw.html.element( node.nodeName.toLowerCase(), $( node ).getAttrs() )
-                                                       );
-                                               } );
-                                               // Force animations to stop to give the next test a clean start
-                                               $.timers = [];
-                                               $.fx.stop();
-
-                                               throw new Error( 'Unfinished animations: ' + timers );
-                                       }
+                                       throw new Error( 'Unfinished animations: ' + timers );
+                               }
 
-                                       // Test should use fake XHR, wait for requests, or call abort()
-                                       $activeLen = $.active;
-                                       if ( $activeLen !== undefined && $activeLen !== 0 ) {
-                                               pending = ajaxRequests.filter( function ( ajax ) {
-                                                       return ajax.xhr.state() === 'pending';
-                                               } );
-                                               if ( pending.length !== $activeLen ) {
-                                                       mw.log.warn( 'Pending requests does not match jQuery.active count' );
-                                               }
-                                               // Force requests to stop to give the next test a clean start
-                                               ajaxRequests.forEach( function ( ajax, i ) {
-                                                       mw.log.warn(
-                                                               'AJAX request #' + i + ' (state: ' + ajax.xhr.state() + ')',
-                                                               ajax.options
-                                                       );
-                                                       ajax.xhr.abort();
-                                               } );
-                                               ajaxRequests = [];
-
-                                               throw new Error( 'Pending AJAX requests: ' + pending.length + ' (active: ' + $activeLen + ')' );
+                               // Test should use fake XHR, wait for requests, or call abort()
+                               $activeLen = $.active;
+                               if ( $activeLen !== undefined && $activeLen !== 0 ) {
+                                       pending = ajaxRequests.filter( function ( ajax ) {
+                                               return ajax.xhr.state() === 'pending';
+                                       } );
+                                       if ( pending.length !== $activeLen ) {
+                                               mw.log.warn( 'Pending requests does not match jQuery.active count' );
                                        }
+                                       // Force requests to stop to give the next test a clean start
+                                       ajaxRequests.forEach( function ( ajax, i ) {
+                                               mw.log.warn(
+                                                       'AJAX request #' + i + ' (state: ' + ajax.xhr.state() + ')',
+                                                       ajax.options
+                                               );
+                                               ajax.xhr.abort();
+                                       } );
+                                       ajaxRequests = [];
 
-                                       return ret;
+                                       throw new Error( 'Pending AJAX requests: ' + pending.length + ' (active: ' + $activeLen + ')' );
                                }
+
+                               return ret;
                        };
+                       return localEnv;
                };
        }() );
 
                } );
        } );
 
+       QUnit.module( 'testrunner-hooks-outer', function () {
+               var beforeHookWasExecuted = false,
+                       afterHookWasExecuted = false;
+               QUnit.module( 'testrunner-hooks', {
+                       before: function () {
+                               beforeHookWasExecuted = true;
+
+                               // This way we can be sure that module `testrunner-hook-after` will always
+                               // be executed after module `testrunner-hooks`
+                               QUnit.module( 'testrunner-hooks-after' );
+                               QUnit.test(
+                                       '`after` hook for module `testrunner-hooks` was executed',
+                                       function ( assert ) {
+                                               assert.ok( afterHookWasExecuted );
+                                       }
+                               );
+                       },
+                       after: function () {
+                               afterHookWasExecuted = true;
+                       }
+               } );
+
+               QUnit.test( '`before` hook was executed', function ( assert ) {
+                       assert.ok( beforeHookWasExecuted );
+               } );
+       } );
+
 }( jQuery, mediaWiki, QUnit ) );
index 02ac0b0..c4b40dc 100644 (file)
--- a/thumb.php
+++ b/thumb.php
@@ -337,7 +337,16 @@ function wfStreamThumb( array $params ) {
                return;
        }
 
-       list( $thumb, $errorMsg ) = wfGenerateThumbnail( $img, $params, $thumbName, $thumbPath );
+       $thumbProxyUrl = $img->getRepo()->getThumbProxyUrl();
+
+       if ( strlen( $thumbProxyUrl ) ) {
+               wfProxyThumbnailRequest( $img, $thumbName );
+               // No local fallback when in proxy mode
+               return;
+       } else {
+               // Generate the thumbnail locally
+               list( $thumb, $errorMsg ) = wfGenerateThumbnail( $img, $params, $thumbName, $thumbPath );
+       }
 
        /** @var MediaTransformOutput|MediaTransformError|bool $thumb */
 
@@ -377,6 +386,43 @@ function wfStreamThumb( array $params ) {
        }
 }
 
+/**
+ * Proxies thumbnail request to a service that handles thumbnailing
+ *
+ * @param File $img
+ * @param string $thumbName
+ */
+function wfProxyThumbnailRequest( $img, $thumbName ) {
+       $thumbProxyUrl = $img->getRepo()->getThumbProxyUrl();
+
+       // Instead of generating the thumbnail ourselves, we proxy the request to another service
+       $thumbProxiedUrl = $thumbProxyUrl . $img->getThumbRel( $thumbName );
+
+       $req = MWHttpRequest::factory( $thumbProxiedUrl );
+       $secret = $img->getRepo()->getThumbProxySecret();
+
+       // Pass a secret key shared with the proxied service if any
+       if ( strlen( $secret ) ) {
+               $req->setHeader( 'X-Swift-Secret', $secret );
+       }
+
+       // Send request to proxied service
+       $status = $req->execute();
+
+       // Simply serve the response from the proxied service as-is
+       header( 'HTTP/1.1 ' . $req->getStatus() );
+
+       $headers = $req->getResponseHeaders();
+
+       foreach ( $headers as $key => $values ) {
+               foreach ( $values as $value ) {
+                       header( $key . ': ' . $value, false );
+               }
+       }
+
+       echo $req->getContent();
+}
+
 /**
  * Actually try to generate a new thumbnail
  *