Merge "HistoryAction: Implement HistoryPageToolLinks hook for adding more links"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Thu, 26 Jul 2018 23:20:35 +0000 (23:20 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Thu, 26 Jul 2018 23:20:35 +0000 (23:20 +0000)
215 files changed:
.phpcs.xml
INSTALL
RELEASE-NOTES-1.32
autoload.php
composer.json
docs/uidesign/table-layout.html [deleted file]
img_auth.php
includes/DefaultSettings.php
includes/DevelopmentSettings.php
includes/EditPage.php
includes/GlobalFunctions.php
includes/MWGrants.php
includes/MediaWikiServices.php
includes/OutputPage.php
includes/PathRouter.php
includes/Revision.php
includes/ServiceWiring.php
includes/Status.php
includes/Storage/DerivedPageDataUpdater.php
includes/Storage/RevisionSlots.php
includes/Storage/RevisionStore.php
includes/Title.php
includes/WebRequest.php
includes/api/ApiBase.php
includes/api/ApiMain.php
includes/api/ApiQuerySiteinfo.php
includes/api/i18n/ar.json
includes/api/i18n/en.json
includes/api/i18n/es.json
includes/api/i18n/fr.json
includes/api/i18n/gl.json
includes/api/i18n/he.json
includes/api/i18n/ja.json
includes/api/i18n/ko.json
includes/api/i18n/lt.json
includes/api/i18n/nb.json
includes/api/i18n/pl.json
includes/api/i18n/pt-br.json
includes/api/i18n/pt.json
includes/api/i18n/qqq.json
includes/api/i18n/ru.json
includes/api/i18n/uk.json
includes/api/i18n/zh-hans.json
includes/cache/MessageCache.php
includes/config/ConfigRepository.php [new file with mode: 0644]
includes/debug/DeprecationHelper.php [new file with mode: 0644]
includes/deferred/LinksUpdate.php
includes/diff/DifferenceEngine.php
includes/exception/MWExceptionRenderer.php
includes/filebackend/FileBackendGroup.php
includes/filerepo/RepoGroup.php
includes/htmlform/fields/HTMLCheckField.php
includes/htmlform/fields/HTMLMultiSelectField.php
includes/htmlform/fields/HTMLSelectAndOtherField.php
includes/htmlform/fields/HTMLSelectOrOtherField.php
includes/http/CurlHttpRequest.php
includes/http/MWHttpRequest.php
includes/installer/Installer.php
includes/installer/WebInstallerName.php
includes/installer/i18n/ar.json
includes/installer/i18n/es.json
includes/installer/i18n/he.json
includes/installer/i18n/it.json
includes/installer/i18n/nb.json
includes/installer/i18n/nl.json
includes/installer/i18n/sl.json
includes/installer/i18n/tr.json
includes/installer/i18n/vi.json
includes/jobqueue/JobQueueGroup.php
includes/jobqueue/JobQueueSecondTestQueue.php [deleted file]
includes/libs/CSSMin.php
includes/libs/EasyDeflate.php [new file with mode: 0644]
includes/libs/MultiHttpClient.php
includes/libs/http/HttpAcceptParser.php
includes/libs/lockmanager/DBLockManager.php
includes/libs/objectcache/BagOStuff.php
includes/libs/rdbms/database/Database.php
includes/libs/rdbms/database/IDatabase.php
includes/linkeddata/PageDataRequestHandler.php
includes/logging/LogEventsList.php
includes/logging/LogPager.php
includes/page/Article.php
includes/page/PageArchive.php
includes/parser/ParserOptions.php
includes/registration/ExtensionJsonValidator.php
includes/registration/ExtensionProcessor.php
includes/registration/ExtensionRegistry.php
includes/registration/Processor.php
includes/resourceloader/ResourceLoaderLanguageDataModule.php
includes/site/DBSiteStore.php
includes/skins/Skin.php
includes/specials/SpecialBlock.php
includes/specials/SpecialFileDuplicateSearch.php
includes/user/User.php
includes/utils/UIDGenerator.php
includes/widget/SelectWithInputWidget.php
languages/FakeConverter.php
languages/Language.php
languages/LanguageCode.php
languages/LanguageConverter.php
languages/data/Names.php
languages/data/README.md [new file with mode: 0644]
languages/data/normalize-ar.php
languages/data/normalize-ml.php
languages/i18n/af.json
languages/i18n/ar.json
languages/i18n/ase.json
languages/i18n/az.json
languages/i18n/be-tarask.json
languages/i18n/be.json
languages/i18n/bg.json
languages/i18n/bn.json
languages/i18n/bs.json
languages/i18n/ca.json
languages/i18n/cdo.json
languages/i18n/ckb.json
languages/i18n/cy.json
languages/i18n/da.json
languages/i18n/de.json
languages/i18n/el.json
languages/i18n/en.json
languages/i18n/es.json
languages/i18n/eu.json
languages/i18n/fi.json
languages/i18n/fr.json
languages/i18n/gd.json
languages/i18n/he.json
languages/i18n/hr.json
languages/i18n/hu.json
languages/i18n/it.json
languages/i18n/jv.json
languages/i18n/km.json
languages/i18n/ko.json
languages/i18n/kw.json
languages/i18n/mg.json
languages/i18n/mk.json
languages/i18n/mr.json
languages/i18n/nan.json
languages/i18n/nb.json
languages/i18n/nl.json
languages/i18n/nn.json
languages/i18n/pl.json
languages/i18n/pt-br.json
languages/i18n/pt.json
languages/i18n/qqq.json
languages/i18n/ro.json
languages/i18n/ru.json
languages/i18n/sat.json
languages/i18n/sd.json
languages/i18n/sl.json
languages/i18n/sr-ec.json
languages/i18n/sv.json
languages/i18n/sw.json
languages/i18n/te.json
languages/i18n/tl.json
languages/i18n/tr.json
languages/i18n/uk.json
languages/i18n/ur.json
languages/i18n/vi.json
languages/i18n/yue.json
languages/i18n/zgh.json
languages/i18n/zh-hans.json
languages/i18n/zh-hant.json
languages/i18n/zh-hk.json
languages/messages/MessagesSat.php
maintenance/createAndPromote.php
maintenance/language/generateNormalizerDataAr.php
maintenance/mysql.php
maintenance/resetAuthenticationThrottle.php [new file with mode: 0644]
resources/Resources.php
resources/lib/easy-deflate/README.md [new file with mode: 0644]
resources/lib/easy-deflate/deflate.js [new file with mode: 0644]
resources/lib/easy-deflate/easydeflate.js [new file with mode: 0644]
resources/lib/easy-deflate/inflate.js [new file with mode: 0644]
resources/src/mediawiki.language/mediawiki.language.init.js
resources/src/mediawiki.language/mediawiki.language.js
resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.ItemMenuOptionWidget.less
tests/common/TestsAutoLoader.php
tests/parser/ParserTestRunner.php
tests/parser/parserTests.php
tests/phpunit/includes/EditPageTest.php
tests/phpunit/includes/ExtraParserTest.php
tests/phpunit/includes/GlobalFunctions/WfExpandUrlTest.php [new file with mode: 0644]
tests/phpunit/includes/GlobalFunctions/wfExpandUrlTest.php [deleted file]
tests/phpunit/includes/MediaWikiServicesTest.php
tests/phpunit/includes/MediaWikiTest.php
tests/phpunit/includes/MultiHttpClientTest.php [new file with mode: 0644]
tests/phpunit/includes/PathRouterTest.php
tests/phpunit/includes/SampleTest.php
tests/phpunit/includes/api/ApiBaseTest.php
tests/phpunit/includes/api/ApiEditPageTest.php
tests/phpunit/includes/api/ApiMainTest.php
tests/phpunit/includes/api/format/ApiFormatBaseTest.php
tests/phpunit/includes/auth/AuthManagerTest.php
tests/phpunit/includes/content/ContentHandlerTest.php
tests/phpunit/includes/debug/DeprecationHelperTest.php [new file with mode: 0644]
tests/phpunit/includes/debug/TestDeprecatedClass.php [new file with mode: 0644]
tests/phpunit/includes/debug/TestDeprecatedSubclass.php [new file with mode: 0644]
tests/phpunit/includes/htmlform/HTMLFormTest.php
tests/phpunit/includes/libs/CSSMinTest.php
tests/phpunit/includes/libs/EasyDeflateTest.php [new file with mode: 0644]
tests/phpunit/includes/parser/MagicVariableTest.php
tests/phpunit/includes/parser/ParserOptionsTest.php
tests/phpunit/includes/preferences/FiltersTest.php
tests/phpunit/includes/specialpage/SpecialPageFactoryTest.php
tests/phpunit/includes/specialpage/SpecialPageTest.php
tests/phpunit/includes/title/NaiveImportTitleFactoryTest.php
tests/phpunit/includes/title/NamespaceImportTitleFactoryTest.php
tests/phpunit/includes/title/SubpageImportTitleFactoryTest.php
tests/phpunit/includes/user/UserTest.php
tests/phpunit/languages/LanguageCodeTest.php
tests/phpunit/languages/LanguageConverterTest.php
tests/phpunit/maintenance/backup_PageTest.php
tests/phpunit/structure/AvailableRightsTest.php
tests/qunit/suites/resources/mediawiki/mediawiki.language.test.js

index 8d33f69..2a34154 100644 (file)
@@ -2,6 +2,7 @@
 <ruleset name="MediaWiki">
        <rule ref="./vendor/mediawiki/mediawiki-codesniffer/MediaWiki">
                <exclude name="Generic.ControlStructures.InlineControlStructure" />
+               <exclude name="MediaWiki.Commenting.FunctionAnnotations.UnrecognizedAnnotation" />
                <exclude name="MediaWiki.Commenting.FunctionComment.MissingDocumentationProtected" />
                <exclude name="MediaWiki.Commenting.FunctionComment.MissingDocumentationPublic" />
                <exclude name="MediaWiki.Commenting.FunctionComment.MissingParamTag" />
                <exclude name="MediaWiki.Commenting.IllegalSingleLineComment.IllegalSingleLineCommentStart" />
                <exclude name="MediaWiki.Commenting.IllegalSingleLineComment.IllegalSingleLineCommentEnd" />
                <exclude name="MediaWiki.ControlStructures.AssignmentInControlStructures.AssignmentInControlStructures" />
+               <exclude name="MediaWiki.Commenting.MissingCovers.MissingCovers" />
                <exclude name="MediaWiki.NamingConventions.LowerCamelFunctionsName.FunctionName" />
-               <exclude name="MediaWiki.WhiteSpace.SpaceBeforeSingleLineComment.NewLineComment" />
-               <exclude name="MediaWiki.WhiteSpace.SpaceBeforeSingleLineComment.SingleSpaceBeforeSingleLineComment" />
                <exclude name="MediaWiki.Usage.DbrQueryUsage.DbrQueryFound" />
                <exclude name="MediaWiki.Usage.ExtendClassUsage.FunctionVarUsage" />
+               <exclude name="MediaWiki.Usage.ForbiddenFunctions.passthru" />
                <exclude name="MediaWiki.VariableAnalysis.ForbiddenGlobalVariables.ForbiddenGlobal$wgTitle" />
+               <exclude name="MediaWiki.WhiteSpace.SpaceBeforeSingleLineComment.NewLineComment" />
+               <exclude name="MediaWiki.WhiteSpace.SpaceBeforeSingleLineComment.SingleSpaceBeforeSingleLineComment" />
                <exclude name="Squiz.Scope.MethodScope.Missing" />
                <exclude name="Squiz.Scope.MemberVarScope.Missing" />
-               <exclude name="MediaWiki.Commenting.MissingCovers.MissingCovers" />
        </rule>
        <rule ref="MediaWiki.NamingConventions.PrefixedGlobalFunctions">
                <properties>
                        <property name="ignoreList" type="array" value="$IP,$messageMemc" />
                </properties>
        </rule>
+       <rule ref="MediaWiki.NamingConventions.ValidGlobalName.allowedPrefix">
+               <exclude-pattern>profileinfo\.php</exclude-pattern>
+               <exclude-pattern>maintenance/language/checkLanguage\.inc</exclude-pattern>
+               <exclude-pattern>maintenance/doMaintenance\.php</exclude-pattern>
+               <exclude-pattern>maintenance/mergeMessageFileList\.php</exclude-pattern>
+               <exclude-pattern>maintenance/commandLine\.inc</exclude-pattern>
+       </rule>
        <rule ref="Generic.Files.LineLength">
                <exclude-pattern>*/languages/messages/Messages*\.php</exclude-pattern>
        </rule>
diff --git a/INSTALL b/INSTALL
index 91dcbea..d68342c 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -17,9 +17,6 @@ Required software:
 MediaWiki is developed and tested mainly on Unix/Linux platforms, but should
 work on Windows as well.
 
-If your PHP is configured as a CGI plug-in rather than an Apache module you may
-experience problems, as this configuration is not well tested.
-
 Support for rendering mathematical formulas requires installing the Math extension,
 see https://www.mediawiki.org/wiki/Special:MyLanguage/Extension:Math
 
index 90396ea..0ffbdea 100644 (file)
@@ -29,6 +29,14 @@ production.
 * The archive table's ar_rev_id field is now unique.
 * Special:BotPasswords now requires reauthentication.
 * (T194414) The default watchlist view time has been increased from 3 to 7 days.
+* The right to edit sitewide Javascript (e.g. MediaWiki:Common.js), CSS or JSON
+  was separated from 'editinterface' and is available under
+  'editsitejs'/'editsitecss'/'editsitejson'. Having 'editinterface' is still
+  necessary to edit such pages.
+* A new user group, 'interface-admin', is added for controlling access to
+  sitewide CSS/JS (and editing other users' CSS/JS). No other group has
+  'editsitecss', 'editusercss', 'editsitejs' or 'edituserjs' by default.
+* A new grant group, 'editsiteconfig', is added for granting the above rights.
 
 === New features in 1.32 ===
 * (T112474) Generalized the ResourceLoader mechanism for overriding modules
@@ -67,6 +75,8 @@ production.
 * Updated wikimedia/scoped-callback from 1.0.0 to 2.0.0.
 ** ScopedCallback objects can no longer be serialized.
 * Updated wikimedia/wrappedstring from 2.3.0 to 3.0.1.
+* Updated mediawiki/mediawiki-codesniffer from v20.0.0 to v21.0.0.
+* Updated composer/spdx-licenses from 1.3.0 to 1.4.0.
 
 ==== New external libraries ====
 * Added wikimedia/xmp-reader 0.5.1
@@ -97,6 +107,9 @@ production.
 * (T198935) User list preferences such as `email-blacklist` and similar
   extension preferences are no longer represented as arrays when returned by
   action=query&meta=userinfo&uiprop=options.
+* 'missingparam' errors will now use the prefixed parameter name in the code
+  and error text, e.g. "noxxfoo" and "The 'xxfoo' parameter must be set" rather
+  than "nofoo" and "The 'foo' parameter must be set".
 
 === Action API internal changes in 1.32 ===
 * Added 'ApiParseMakeOutputPage' hook.
@@ -194,9 +207,6 @@ because of Phabricator reports.
   CapsuleMultiselectWidget. The following methods may no longer be used:
   * setItemsFromData: Use setValue instead
   * getItemsData: Use getItems instead and get the data property
-* LanguageCode::bcp47() now always returns a valid BCP 47 code.  This means
-  that some MediaWiki-specific language codes, such as `simple`, are mapped
-  into valid BCP 47 codes (eg `en-simple`).
 
 === Deprecations in 1.32 ===
 * Use of a StartProfiler.php file is deprecated in favour of placing
@@ -281,6 +291,16 @@ because of Phabricator reports.
   'log-show-hide-[type]' format. Instead use 'logeventslist-[type]-log'.
 * Global functions  wfArrayFilter() and wfArrayFilterByKey() are deprecated.
   use array_filter() directly.
+* The $wgShowSQLErrors global is deprecated and nonfunctional.
+  Set $wgShowExceptionDetails and/or $wgShowHostnames instead.
+* The $wgShowDBErrorBacktrace global is deprecated and nonfunctional.
+  Set $wgShowExceptionDetails instead.
+* Public access to the DifferenceEngine properties mOldid, mNewid, mOldPage,
+  mNewPage, mOldContent, mNewContent, mRevisionsLoaded, mTextLoaded and
+  mCacheHit is deprecated. Use getOldid() / getNewid() for the first two,
+  do your own lookup for page/content. mNewRev / mOldRev remains public.
+* The $wgExternalDiffEngine value 'wikidiff2' is deprecated. To use wikidiff2
+  just enable the PHP extension, and it will be autodetected.
 
 === Other changes in 1.32 ===
 * (T198811) The following tables have had their UNIQUE indexes turned into
index 40b8acf..7588060 100644 (file)
@@ -387,6 +387,7 @@ $wgAutoloadLocalClasses = [
        'DependencyWrapper' => __DIR__ . '/includes/cache/CacheDependency.php',
        'DeprecatedGlobal' => __DIR__ . '/includes/DeprecatedGlobal.php',
        'DeprecatedInterfaceFinder' => __DIR__ . '/maintenance/findDeprecated.php',
+       'DeprecationHelper' => __DIR__ . '/includes/debug/DeprecationHelper.php',
        'DerivativeContext' => __DIR__ . '/includes/context/DerivativeContext.php',
        'DerivativeRequest' => __DIR__ . '/includes/DerivativeRequest.php',
        'DerivativeResourceLoaderContext' => __DIR__ . '/includes/resourceloader/DerivativeResourceLoaderContext.php',
@@ -433,6 +434,7 @@ $wgAutoloadLocalClasses = [
        'DumpStringOutput' => __DIR__ . '/includes/export/DumpStringOutput.php',
        'DumpUploads' => __DIR__ . '/maintenance/dumpUploads.php',
        'DuplicateJob' => __DIR__ . '/includes/jobqueue/jobs/DuplicateJob.php',
+       'EasyDeflate' => __DIR__ . '/includes/libs/EasyDeflate.php',
        'EditAction' => __DIR__ . '/includes/actions/EditAction.php',
        'EditCLI' => __DIR__ . '/maintenance/edit.php',
        'EditPage' => __DIR__ . '/includes/EditPage.php',
@@ -703,7 +705,6 @@ $wgAutoloadLocalClasses = [
        'JobQueueMemory' => __DIR__ . '/includes/jobqueue/JobQueueMemory.php',
        'JobQueueReadOnlyError' => __DIR__ . '/includes/jobqueue/JobQueue.php',
        'JobQueueRedis' => __DIR__ . '/includes/jobqueue/JobQueueRedis.php',
-       'JobQueueSecondTestQueue' => __DIR__ . '/includes/jobqueue/JobQueueSecondTestQueue.php',
        'JobRunner' => __DIR__ . '/includes/jobqueue/JobRunner.php',
        'JobSpecification' => __DIR__ . '/includes/jobqueue/JobSpecification.php',
        'JpegHandler' => __DIR__ . '/includes/media/JpegHandler.php',
@@ -889,6 +890,7 @@ $wgAutoloadLocalClasses = [
        'MediaWiki\\Auth\\Throttler' => __DIR__ . '/includes/auth/Throttler.php',
        'MediaWiki\\Auth\\UserDataAuthenticationRequest' => __DIR__ . '/includes/auth/UserDataAuthenticationRequest.php',
        'MediaWiki\\Auth\\UsernameAuthenticationRequest' => __DIR__ . '/includes/auth/UsernameAuthenticationRequest.php',
+       'MediaWiki\\Config\\ConfigRepository' => __DIR__ . '/includes/config/ConfigRepository.php',
        'MediaWiki\\DB\\PatchFileLocation' => __DIR__ . '/includes/db/PatchFileLocation.php',
        'MediaWiki\\Diff\\ComplexityException' => __DIR__ . '/includes/diff/ComplexityException.php',
        'MediaWiki\\Diff\\WordAccumulator' => __DIR__ . '/includes/diff/WordAccumulator.php',
@@ -1211,6 +1213,7 @@ $wgAutoloadLocalClasses = [
        'ReplicatedBagOStuff' => __DIR__ . '/includes/libs/objectcache/ReplicatedBagOStuff.php',
        'RepoGroup' => __DIR__ . '/includes/filerepo/RepoGroup.php',
        'RequestContext' => __DIR__ . '/includes/context/RequestContext.php',
+       'ResetAuthenticationThrottle' => __DIR__ . '/maintenance/resetAuthenticationThrottle.php',
        'ResetUserEmail' => __DIR__ . '/maintenance/resetUserEmail.php',
        'ResetUserTokens' => __DIR__ . '/maintenance/resetUserTokens.php',
        'ResourceFileCache' => __DIR__ . '/includes/cache/ResourceFileCache.php',
index 3cd5ea3..dbc59a2 100644 (file)
                "zordius/lightncandy": "0.23"
        },
        "require-dev": {
-               "composer/spdx-licenses": "1.3.0",
+               "cache/integration-tests": "0.16.0",
+               "composer/spdx-licenses": "1.4.0",
                "hamcrest/hamcrest-php": "^2.0",
                "jakub-onderka/php-parallel-lint": "0.9.2",
-               "jetbrains/phpstorm-stubs": "dev-master#1b9906084d6635456fcf3f3a01f0d7d5b99a578a",
+               "jetbrains/phpstorm-stubs": "dev-master#38ff1a581b297f7901e961b8c923862ea80c3b96",
                "justinrainbow/json-schema": "~5.2",
-               "mediawiki/mediawiki-codesniffer": "20.0.0",
+               "mediawiki/mediawiki-codesniffer": "21.0.0",
                "monolog/monolog": "~1.22.1",
                "nikic/php-parser": "3.1.3",
                "seld/jsonlint": "1.7.1",
diff --git a/docs/uidesign/table-layout.html b/docs/uidesign/table-layout.html
deleted file mode 100644 (file)
index 2c26819..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-       <style>
-               /** This is just for coloring: */
-               table { border: 1px solid #CC0; }
-               td { border: 1px solid  #CCC; }
-
-               table {
-                       width: 100%;
-                       table-layout: fixed;
-               }
-
-               #first {
-                       width: 300px;
-               }
-       </style>
-</head>
-<body>
-
-<p>
-This play with table-layout:fixed; and applying the width to colgroup or col element. Firefox only recongize the width if it is applied on col element!</p>
-<p>
-On a perfect browser, both tables should look the same</p>
-
-<dl>
-       <dt>colgroup</dt>
-       <dd>300 px width is applied to the first colgroup element</dd>
-</dl>
-<div style="width: 400px;">
-<table>
-       <colgroup id="first" /></colgroup>
-       <colgroup id="second"/></colgroup>
-       <colgroup id="third" /></colgroup>
-       <tr>
-               <td>Very long?</td>
-               <td>#</td>
-               <td>$</td>
-       </tr>
-</table>
-</div>
-
-<dl>
-       <dt>col</dt>
-       <dd>Each colgroup has an additional col element. The first col element is applied the 300 px width</dd>
-</dl>
-
-<div style="width: 400px;">
-<table>
-       <colgroup><col id="first" /></colgroup>
-       <colgroup><col id="second"/></colgroup>
-       <colgroup><col id="third" /></colgroup>
-       <tr>
-               <td>Very long?</td>
-               <td>#</td>
-               <td>$</td>
-       </tr>
-</table>
-</div>
index 2052809..ca69d31 100644 (file)
@@ -17,7 +17,8 @@
  *  just that it was. If you want to change this, you can set $wgImgAuthDetails to 'true'
  *  in localsettings.php and it will give the user the reason why access was denied.
  *
- * Your server needs to support PATH_INFO; CGI-based configurations usually don't.
+ * Your server needs to support REQUEST_URI or PATH_INFO; CGI-based
+ * configurations sometimes don't.
  *
  * 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
index 9a08c8c..a3fdcfc 100644 (file)
@@ -4548,6 +4548,12 @@ $wgPasswordPolicy = [
                        'PasswordCannotMatchUsername' => true,
                        'PasswordCannotBePopular' => 25,
                ],
+               'interface-admin' => [
+                       'MinimalPasswordLength' => 8,
+                       'MinimumPasswordLengthToLogin' => 1,
+                       'PasswordCannotMatchUsername' => true,
+                       'PasswordCannotBePopular' => 25,
+               ],
                'bot' => [
                        'MinimalPasswordLength' => 8,
                        'MinimumPasswordLengthToLogin' => 1,
@@ -5217,9 +5223,8 @@ $wgGroupPermissions['sysop']['deletedhistory'] = true;
 $wgGroupPermissions['sysop']['deletedtext'] = true;
 $wgGroupPermissions['sysop']['undelete'] = true;
 $wgGroupPermissions['sysop']['editinterface'] = true;
-$wgGroupPermissions['sysop']['editusercss'] = true;
+$wgGroupPermissions['sysop']['editsitejson'] = true;
 $wgGroupPermissions['sysop']['edituserjson'] = true;
-$wgGroupPermissions['sysop']['edituserjs'] = true;
 $wgGroupPermissions['sysop']['import'] = true;
 $wgGroupPermissions['sysop']['importupload'] = true;
 $wgGroupPermissions['sysop']['move'] = true;
@@ -5252,6 +5257,14 @@ $wgGroupPermissions['sysop']['mergehistory'] = true;
 $wgGroupPermissions['sysop']['managechangetags'] = true;
 $wgGroupPermissions['sysop']['deletechangetags'] = true;
 
+$wgGroupPermissions['interface-admin']['editinterface'] = true;
+$wgGroupPermissions['interface-admin']['editsitecss'] = true;
+$wgGroupPermissions['interface-admin']['editsitejson'] = true;
+$wgGroupPermissions['interface-admin']['editsitejs'] = true;
+$wgGroupPermissions['interface-admin']['editusercss'] = true;
+$wgGroupPermissions['interface-admin']['edituserjson'] = true;
+$wgGroupPermissions['interface-admin']['edituserjs'] = true;
+
 // Permission to change users' group assignments
 $wgGroupPermissions['bureaucrat']['userrights'] = true;
 $wgGroupPermissions['bureaucrat']['noratelimit'] = true;
@@ -5858,9 +5871,14 @@ $wgGrantPermissions['editmyoptions']['editmyoptions'] = true;
 
 $wgGrantPermissions['editinterface'] = $wgGrantPermissions['editpage'];
 $wgGrantPermissions['editinterface']['editinterface'] = true;
-$wgGrantPermissions['editinterface']['editusercss'] = true;
 $wgGrantPermissions['editinterface']['edituserjson'] = true;
-$wgGrantPermissions['editinterface']['edituserjs'] = true;
+$wgGrantPermissions['editinterface']['editsitejson'] = true;
+
+$wgGrantPermissions['editsiteconfig'] = $wgGrantPermissions['editinterface'];
+$wgGrantPermissions['editsiteconfig']['editusercss'] = true;
+$wgGrantPermissions['editsiteconfig']['edituserjs'] = true;
+$wgGrantPermissions['editsiteconfig']['editsitecss'] = true;
+$wgGrantPermissions['editsiteconfig']['editsitejs'] = true;
 
 $wgGrantPermissions['createeditmovepage'] = $wgGrantPermissions['editpage'];
 $wgGrantPermissions['createeditmovepage']['createpage'] = true;
@@ -5939,6 +5957,7 @@ $wgGrantPermissionGroups = [
        'editmyoptions'       => 'customization',
 
        'editinterface'       => 'administration',
+       'editsiteconfig'      => 'administration',
        'rollback'            => 'administration',
        'blockusers'          => 'administration',
        'delete'              => 'administration',
@@ -6296,14 +6315,17 @@ $wgSpecialVersionShowHooks = false;
  * Whether to show "we're sorry, but there has been a database error" pages.
  * Displaying errors aids in debugging, but may display information useful
  * to an attacker.
+ *
+ * @deprecated and nonfunctional since 1.32: set $wgShowExceptionDetails and/or
+ * $wgShowHostnames instead.
  */
 $wgShowSQLErrors = false;
 
 /**
- * If set to true, uncaught exceptions will print a complete stack trace
- * to output. This should only be used for debugging, as it may reveal
- * private information in function parameters due to PHP's backtrace
- * formatting.
+ * If set to true, uncaught exceptions will print the exception message and a
+ * complete stack trace to output. This should only be used for debugging, as it
+ * may reveal private information in function parameters due to PHP's backtrace
+ * formatting.  If set to false, only the exception's class will be shown.
  */
 $wgShowExceptionDetails = false;
 
@@ -6314,6 +6336,8 @@ $wgShowExceptionDetails = false;
  * reported in the normal manner. $wgShowExceptionDetails applies in other cases,
  * including those in which an uncaught exception is thrown from within the
  * exception handler.
+ *
+ * @deprecated and nonfunctional since 1.32: set $wgShowExceptionDetails instead.
  */
 $wgShowDBErrorBacktrace = false;
 
@@ -8669,9 +8693,14 @@ $wgSiteTypes = [
 $wgPagePropsHaveSortkey = true;
 
 /**
- * Port where you have HTTPS running
- * Supports HTTPS on non-standard ports
- * @see T67184
+ * For installations where the canonical server is HTTP but HTTPS is optionally
+ * supported, you can specify a non-standard HTTPS port here. $wgServer should
+ * be a protocol-relative URL.
+ *
+ * If HTTPS is always used, just specify the port number in $wgServer.
+ *
+ * @see https://phabricator.wikimedia.org/T67184
+ *
  * @since 1.24
  */
 $wgHttpsPort = 443;
@@ -8975,7 +9004,7 @@ $wgCommentTableSchemaMigrationStage = MIGRATION_OLD;
  * @since 1.32
  * @var int An appropriate combination of SCHEMA_COMPAT_XXX flags.
  */
-$wgMultiContentRevisionSchemaMigrationStage = MIGRATION_OLD;
+$wgMultiContentRevisionSchemaMigrationStage = SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD;
 
 /**
  * Actor table schema migration stage.
index 96ed56b..8b32de4 100644 (file)
@@ -24,18 +24,16 @@ ini_set( 'display_errors', 1 );
 /**
  * Debugging: MediaWiki
  */
-global $wgDevelopmentWarnings, $wgShowDBErrorBacktrace, $wgShowExceptionDetails,
-       $wgShowSQLErrors, $wgDebugRawPage,
-       $wgDebugComments, $wgDebugDumpSql, $wgDebugTimestamps,
+global $wgDevelopmentWarnings, $wgShowExceptionDetails, $wgShowHostnames,
+       $wgDebugRawPage, $wgDebugComments, $wgDebugDumpSql, $wgDebugTimestamps,
        $wgCommandLineMode, $wgDebugLogFile, $wgDBerrorLog, $wgDebugLogGroups;
 
 // Use of wfWarn() should cause tests to fail
 $wgDevelopmentWarnings = true;
 
 // Enable showing of errors
-$wgShowDBErrorBacktrace = true;
 $wgShowExceptionDetails = true;
-$wgShowSQLErrors = true;
+$wgShowHostnames = true;
 $wgDebugRawPage = true; // T49960
 
 // Enable log files
index a1cf3e2..16f1c5a 100644 (file)
@@ -2361,7 +2361,7 @@ ERROR;
         * Returns the revision that was current at the time editing was initiated on the client,
         * even if the edit was based on an old revision.
         *
-        * @warning: this method is very poorly named. If the user opened the form with ?oldid=X,
+        * @warning this method is very poorly named. If the user opened the form with ?oldid=X,
         *        one might think of X as the "base revision", which is NOT what this returns,
         *        see oldid for that. One might further assume that this corresponds to the $baseRevId
         *        parameter of WikiPage::doEditContent, which is not the case either.
index afad5c4..aa8aee5 100644 (file)
@@ -548,17 +548,24 @@ function wfExpandUrl( $url, $defaultProto = PROTO_CURRENT ) {
        } elseif ( substr( $url, 0, 1 ) == '/' ) {
                // If $serverUrl is protocol-relative, prepend $defaultProtoWithoutSlashes,
                // otherwise leave it alone.
-               $url = ( $serverHasProto ? '' : $defaultProtoWithoutSlashes ) . $serverUrl . $url;
+               if ( $serverHasProto ) {
+                       $url = $serverUrl . $url;
+               } else {
+                       // If an HTTPS URL is synthesized from a protocol-relative $wgServer, allow the
+                       // user to override the port number (T67184)
+                       if ( $defaultProto === PROTO_HTTPS && $wgHttpsPort != 443 ) {
+                               if ( isset( $bits['port'] ) ) {
+                                       throw new Exception( 'A protocol-relative $wgServer may not contain a port number' );
+                               }
+                               $url = $defaultProtoWithoutSlashes . $serverUrl . ':' . $wgHttpsPort . $url;
+                       } else {
+                               $url = $defaultProtoWithoutSlashes . $serverUrl . $url;
+                       }
+               }
        }
 
        $bits = wfParseUrl( $url );
 
-       // ensure proper port for HTTPS arrives in URL
-       // https://phabricator.wikimedia.org/T67184
-       if ( $defaultProto === PROTO_HTTPS && $wgHttpsPort != 443 ) {
-               $bits['port'] = $wgHttpsPort;
-       }
-
        if ( $bits && isset( $bits['path'] ) ) {
                $bits['path'] = wfRemoveDotSegments( $bits['path'] );
                return wfAssembleUrl( $bits );
@@ -574,6 +581,19 @@ function wfExpandUrl( $url, $defaultProto = PROTO_CURRENT ) {
        return false;
 }
 
+/**
+ * Get the wiki's "server", i.e. the protocol and host part of the URL, with a
+ * protocol specified using a PROTO_* constant as in wfExpandUrl()
+ *
+ * @since 1.32
+ * @param string|int|null $proto One of the PROTO_* constants.
+ * @return string The URL
+ */
+function wfGetServerUrl( $proto ) {
+       $url = wfExpandUrl( '/', $proto );
+       return substr( $url, 0, -1 );
+}
+
 /**
  * This function will reassemble a URL parsed with wfParseURL.  This is useful
  * if you need to edit part of a URL and put it back together.
@@ -866,20 +886,13 @@ function wfParseUrl( $url ) {
 function wfExpandIRI( $url ) {
        return preg_replace_callback(
                '/((?:%[89A-F][0-9A-F])+)/i',
-               'wfExpandIRI_callback',
+               function ( array $matches ) {
+                       return urldecode( $matches[1] );
+               },
                wfExpandUrl( $url )
        );
 }
 
-/**
- * Private callback for wfExpandIRI
- * @param array $matches
- * @return string
- */
-function wfExpandIRI_callback( $matches ) {
-       return urldecode( $matches[1] );
-}
-
 /**
  * Make URL indexes, appropriate for the el_index field of externallinks.
  *
index ba22590..769e5b4 100644 (file)
@@ -58,11 +58,11 @@ class MWGrants {
                // Give grep a chance to find the usages:
                // grant-blockusers, grant-createeditmovepage, grant-delete,
                // grant-editinterface, grant-editmycssjs, grant-editmywatchlist,
-               // grant-editpage, grant-editprotected, grant-highvolume,
-               // grant-oversight, grant-patrol, grant-protect, grant-rollback,
-               // grant-sendemail, grant-uploadeditmovefile, grant-uploadfile,
-               // grant-basic, grant-viewdeleted, grant-viewmywatchlist,
-               // grant-createaccount
+               // grant-editsiteconfig, grant-editpage, grant-editprotected,
+               // grant-highvolume, grant-oversight, grant-patrol, grant-protect,
+               // grant-rollback, grant-sendemail, grant-uploadeditmovefile,
+               // grant-uploadfile, grant-basic, grant-viewdeleted,
+               // grant-viewmywatchlist, grant-createaccount
                $msg = wfMessage( "grant-$grant" );
                if ( $lang !== null ) {
                        if ( is_string( $lang ) ) {
index a756d50..7a4c2df 100644 (file)
@@ -27,6 +27,7 @@ use Wikimedia\Rdbms\LBFactory;
 use LinkCache;
 use Wikimedia\Rdbms\LoadBalancer;
 use MediaHandlerFactory;
+use MediaWiki\Config\ConfigRepository;
 use MediaWiki\Linker\LinkRenderer;
 use MediaWiki\Linker\LinkRendererFactory;
 use MediaWiki\Services\SalvageableService;
@@ -855,6 +856,13 @@ class MediaWikiServices extends ServiceContainer {
                return $this->getService( 'OldRevisionImporter' );
        }
 
+       /**
+        * @return ConfigRepository
+        */
+       public function getConfigRepository() {
+               return $this->getService( 'ConfigRepository' );
+       }
+
        ///////////////////////////////////////////////////////////////////////////
        // NOTE: When adding a service getter here, don't forget to add a test
        // case for it in MediaWikiServicesTest::provideGetters() and in
index 5965cbe..4e7c0bb 100644 (file)
@@ -55,9 +55,8 @@ class OutputPage extends ContextSource {
        protected $mCanonicalUrl = false;
 
        /**
-        * @var string Should be private - has getter and setter. Contains
-        *   the HTML title */
-       public $mPagetitle = '';
+        * @var string The contents of <h1> */
+       private $mPageTitle = '';
 
        /**
         * @var string Contains all of the "<body>" content. Should be private we
@@ -992,7 +991,7 @@ class OutputPage extends ContextSource {
                # change "<script>foo&bar</script>" to "&lt;script&gt;foo&amp;bar&lt;/script&gt;"
                # but leave "<i>foobar</i>" alone
                $nameWithTags = Sanitizer::normalizeCharReferences( Sanitizer::removeHTMLtags( $name ) );
-               $this->mPagetitle = $nameWithTags;
+               $this->mPageTitle = $nameWithTags;
 
                # change "<i>foo&amp;bar</i>" to "foo&bar"
                $this->setHTMLTitle(
@@ -1007,7 +1006,7 @@ class OutputPage extends ContextSource {
         * @return string
         */
        public function getPageTitle() {
-               return $this->mPagetitle;
+               return $this->mPageTitle;
        }
 
        /**
index f24e298..faf4db4 100644 (file)
@@ -240,6 +240,28 @@ class PathRouter {
                // matches are tested first
                $this->sortByWeight();
 
+               $matches = $this->internalParse( $path );
+               if ( is_null( $matches ) ) {
+                       // Try with the normalized path (T100782)
+                       $path = wfRemoveDotSegments( $path );
+                       $path = preg_replace( '#/+#', '/', $path );
+                       $matches = $this->internalParse( $path );
+               }
+
+               // We know the difference between null (no matches) and
+               // array() (a match with no data) but our WebRequest caller
+               // expects array() even when we have no matches so return
+               // a array() when we have null
+               return is_null( $matches ) ? [] : $matches;
+       }
+
+       /**
+        * Match a path against each defined pattern
+        *
+        * @param string $path
+        * @return array|null
+        */
+       protected function internalParse( $path ) {
                $matches = null;
 
                foreach ( $this->patterns as $pattern ) {
@@ -248,12 +270,7 @@ class PathRouter {
                                break;
                        }
                }
-
-               // We know the difference between null (no matches) and
-               // array() (a match with no data) but our WebRequest caller
-               // expects array() even when we have no matches so return
-               // a array() when we have null
-               return is_null( $matches ) ? [] : $matches;
+               return $matches;
        }
 
        /**
index d6ff384..7ff73dd 100644 (file)
@@ -739,7 +739,7 @@ class Revision implements IDBAccessObject {
        /**
         * Set the title of the revision
         *
-        * @deprecated: since 1.31, this is now a noop. Pass the Title to the constructor instead.
+        * @deprecated since 1.31, this is now a noop. Pass the Title to the constructor instead.
         *
         * @param Title $title
         */
@@ -927,7 +927,7 @@ class Revision implements IDBAccessObject {
         * used to determine the content model to use. If no title is know, CONTENT_MODEL_WIKITEXT
         * is used as a last resort.
         *
-        * @todo: drop this, with MCR, there no longer is a single model associated with a revision.
+        * @todo drop this, with MCR, there no longer is a single model associated with a revision.
         *
         * @return string The content model id associated with this revision,
         *     see the CONTENT_MODEL_XXX constants.
@@ -942,7 +942,7 @@ class Revision implements IDBAccessObject {
         * If no content format was stored in the database, the default format for this
         * revision's content model is returned.
         *
-        * @todo: drop this, the format is irrelevant to the revision!
+        * @todo drop this, the format is irrelevant to the revision!
         *
         * @return string The content format id associated with this revision,
         *     see the CONTENT_FORMAT_XXX constants.
index 6da537d..7d49080 100644 (file)
@@ -38,6 +38,7 @@
  */
 
 use MediaWiki\Auth\AuthManager;
+use MediaWiki\Config\ConfigRepository;
 use MediaWiki\Interwiki\ClassicInterwikiLookup;
 use MediaWiki\Linker\LinkRendererFactory;
 use MediaWiki\Logger\LoggerFactory;
@@ -104,6 +105,10 @@ return [
                return $factory;
        },
 
+       'ConfigRepository' => function ( MediaWikiServices $services ) {
+               return new ConfigRepository( $services->getConfigFactory() );
+       },
+
        'MainConfig' => function ( MediaWikiServices $services ) {
                // Use the 'main' config from the ConfigFactory service.
                return $services->getConfigFactory()->makeConfig( 'main' );
index 8cd72a5..877d221 100644 (file)
@@ -359,7 +359,7 @@ class Status extends StatusValue {
        /**
         * Returns a list of status messages of the given type (or all if false)
         *
-        * @note: this handles RawMessage poorly
+        * @note this handles RawMessage poorly
         *
         * @param string|bool $type
         * @return array
index 73463b5..6f241ba 100644 (file)
@@ -679,11 +679,11 @@ class DerivedPageDataUpdater implements IDBAccessObject {
         *
         * @see docs/pageupdater.txt for more information on when thie method can and should be called.
         *
-        * @note: Calling this method more than once with the same $slotsUpdate
+        * @note Calling this method more than once with the same $slotsUpdate
         * has no effect. Calling this method multiple times with different content will cause
         * an exception.
         *
-        * @note: Calling this method after prepareUpdate() has been called will cause an exception.
+        * @note Calling this method after prepareUpdate() has been called will cause an exception.
         *
         * @param User $user The user to act as context for pre-save transformation (PST).
         *        Type hint should be reduced to UserIdentity at some point.
@@ -826,7 +826,7 @@ class DerivedPageDataUpdater implements IDBAccessObject {
        /**
         * Whether the edit created, or should create, a new revision (that is, it's not a null-edit).
         *
-        * @warning: at present, "null-revisions" that do not change content but do have a revision
+        * @warning at present, "null-revisions" that do not change content but do have a revision
         * record would return false after prepareContent(), but true after prepareUpdate()!
         * This should probably be fixed.
         *
@@ -931,11 +931,11 @@ class DerivedPageDataUpdater implements IDBAccessObject {
         *
         * @see docs/pageupdater.txt for more information on when thie method can and should be called.
         *
-        * @note: Calling this method more than once with the same revision has no effect.
+        * @note Calling this method more than once with the same revision has no effect.
         * $options are only used for the first call. Calling this method multiple times with
         * different revisions will cause an exception.
         *
-        * @note: If grabCurrentRevision() (or prepareContent()) has been called before
+        * @note If grabCurrentRevision() (or prepareContent()) has been called before
         * calling this method, $revision->getParentRevision() has to refer to the revision that
         * was the current revision at the time grabCurrentRevision() was called.
         *
index ba9780f..91969fc 100644 (file)
@@ -139,7 +139,7 @@ class RevisionSlots {
        /**
         * Computes the total nominal size of the revision's slots, in bogo-bytes.
         *
-        * @warn This is potentially expensive! It may cause all slot's content to be loaded
+        * @warning This is potentially expensive! It may cause all slot's content to be loaded
         * and deserialized.
         *
         * @return int
@@ -181,7 +181,7 @@ class RevisionSlots {
         * is that slot's hash. For consistency, the combined hash of an empty set of slots
         * is the hash of the empty string.
         *
-        * @warn This is potentially expensive! It may cause all slot's content to be loaded
+        * @warning This is potentially expensive! It may cause all slot's content to be loaded
         * and deserialized, then re-serialized and hashed.
         *
         * @return string
index 602364c..d090d8b 100644 (file)
@@ -917,7 +917,7 @@ class RevisionStore
         * Such revisions can for instance identify page rename
         * operations and other such meta-modifications.
         *
-        * @note: This method grabs a FOR UPDATE lock on the relevant row of the page table,
+        * @note This method grabs a FOR UPDATE lock on the relevant row of the page table,
         * to prevent a new revision from being inserted before the null revision has been written
         * to the database.
         *
index 8586ad7..d3e0622 100644 (file)
@@ -37,7 +37,7 @@ use MediaWiki\MediaWikiServices;
  *       and does not rely on global state or the database.
  */
 class Title implements LinkTarget {
-       /** @var HashBagOStuff */
+       /** @var MapCacheLRU */
        static private $titleCache = null;
 
        /**
@@ -306,6 +306,10 @@ class Title implements LinkTarget {
        public static function newFromTextThrow( $text, $defaultNamespace = NS_MAIN ) {
                if ( is_object( $text ) ) {
                        throw new MWException( '$text must be a string, given an object' );
+               } elseif ( $text === null ) {
+                       // Legacy code relies on MalformedTitleException being thrown in this case
+                       // (happens when URL with no title in it is parsed). TODO fix
+                       throw new MalformedTitleException( 'title-invalid-empty' );
                }
 
                $titleCache = self::getTitleCache();
@@ -371,11 +375,11 @@ class Title implements LinkTarget {
        }
 
        /**
-        * @return HashBagOStuff
+        * @return MapCacheLRU
         */
        private static function getTitleCache() {
                if ( self::$titleCache == null ) {
-                       self::$titleCache = new HashBagOStuff( [ 'maxKeys' => self::CACHE_MAX ] );
+                       self::$titleCache = new MapCacheLRU( self::CACHE_MAX );
                }
                return self::$titleCache;
        }
@@ -1289,12 +1293,9 @@ class Title implements LinkTarget {
         */
        public function isSiteConfigPage() {
                return (
-                       NS_MEDIAWIKI == $this->mNamespace
-                       && (
-                               $this->hasContentModel( CONTENT_MODEL_CSS )
-                               || $this->hasContentModel( CONTENT_MODEL_JSON )
-                               || $this->hasContentModel( CONTENT_MODEL_JAVASCRIPT )
-                       )
+                       $this->isSiteCssConfigPage()
+                       || $this->isSiteJsonConfigPage()
+                       || $this->isSiteJsConfigPage()
                );
        }
 
@@ -1317,13 +1318,9 @@ class Title implements LinkTarget {
         */
        public function isUserConfigPage() {
                return (
-                       NS_USER == $this->mNamespace
-                       && $this->isSubpage()
-                       && (
-                               $this->hasContentModel( CONTENT_MODEL_CSS )
-                               || $this->hasContentModel( CONTENT_MODEL_JSON )
-                               || $this->hasContentModel( CONTENT_MODEL_JAVASCRIPT )
-                       )
+                       $this->isUserCssConfigPage()
+                       || $this->isUserJsonConfigPage()
+                       || $this->isUserJsConfigPage()
                );
        }
 
@@ -1423,6 +1420,60 @@ class Title implements LinkTarget {
                return $this->isUserJsConfigPage();
        }
 
+       /**
+        * Is this a sitewide CSS "config" page?
+        *
+        * @return bool
+        * @since 1.32
+        */
+       public function isSiteCssConfigPage() {
+               return (
+                       NS_MEDIAWIKI == $this->mNamespace
+                       && (
+                               $this->hasContentModel( CONTENT_MODEL_CSS )
+                               // paranoia - a MediaWiki: namespace page with mismatching extension and content
+                               // model is probably by mistake and might get handled incorrectly (see e.g. T112937)
+                               || substr( $this->getDBkey(), -4 ) === '.css'
+                       )
+               );
+       }
+
+       /**
+        * Is this a sitewide JSON "config" page?
+        *
+        * @return bool
+        * @since 1.32
+        */
+       public function isSiteJsonConfigPage() {
+               return (
+                       NS_MEDIAWIKI == $this->mNamespace
+                       && (
+                               $this->hasContentModel( CONTENT_MODEL_JSON )
+                               // paranoia - a MediaWiki: namespace page with mismatching extension and content
+                               // model is probably by mistake and might get handled incorrectly (see e.g. T112937)
+                               || substr( $this->getDBkey(), -5 ) === '.json'
+                       )
+               );
+       }
+
+       /**
+        * Is this a sitewide JS "config" page?
+        *
+        * @return bool
+        * @since 1.31
+        */
+       public function isSiteJsConfigPage() {
+               return (
+                       NS_MEDIAWIKI == $this->mNamespace
+                       && (
+                               $this->hasContentModel( CONTENT_MODEL_JAVASCRIPT )
+                               // paranoia - a MediaWiki: namespace page with mismatching extension and content
+                               // model is probably by mistake and might get handled incorrectly (see e.g. T112937)
+                               || substr( $this->getDBkey(), -3 ) === '.js'
+                       )
+               );
+       }
+
        /**
         * Is this a talk page of some sort?
         *
@@ -2313,6 +2364,33 @@ class Title implements LinkTarget {
                return $errors;
        }
 
+       /**
+        * Check sitewide CSS/JSON/JS permissions
+        *
+        * @param string $action The action to check
+        * @param User $user User to check
+        * @param array $errors List of current errors
+        * @param string $rigor Same format as Title::getUserPermissionsErrors()
+        * @param bool $short Short circuit on first error
+        *
+        * @return array List of errors
+        */
+       private function checkSiteConfigPermissions( $action, $user, $errors, $rigor, $short ) {
+               if ( $action != 'patrol' ) {
+                       // Sitewide CSS/JSON/JS changes, like all NS_MEDIAWIKI changes, also require the
+                       // editinterface right. That's implemented as a restriction so no check needed here.
+                       if ( $this->isSiteCssConfigPage() && !$user->isAllowed( 'editsitecss' ) ) {
+                               $errors[] = [ 'sitecssprotected', $action ];
+                       } elseif ( $this->isSiteJsonConfigPage() && !$user->isAllowed( 'editsitejson' ) ) {
+                               $errors[] = [ 'sitejsonprotected', $action ];
+                       } elseif ( $this->isSiteJsConfigPage() && !$user->isAllowed( 'editsitejs' ) ) {
+                               $errors[] = [ 'sitejsprotected', $action ];
+                       }
+               }
+
+               return $errors;
+       }
+
        /**
         * Check CSS/JSON/JS sub-page permissions
         *
@@ -2701,10 +2779,10 @@ class Title implements LinkTarget {
                                'checkReadPermissions',
                                'checkUserBlock', // for wgBlockDisablesLogin
                        ];
-               # Don't call checkSpecialsAndNSPermissions or checkUserConfigPermissions
-               # here as it will lead to duplicate error messages. This is okay to do
-               # since anywhere that checks for create will also check for edit, and
-               # those checks are called for edit.
+               # Don't call checkSpecialsAndNSPermissions, checkSiteConfigPermissions
+               # or checkUserConfigPermissions here as it will lead to duplicate
+               # error messages. This is okay to do since anywhere that checks for
+               # create will also check for edit, and those checks are called for edit.
                } elseif ( $action == 'create' ) {
                        $checks = [
                                'checkQuickPermissions',
@@ -2719,6 +2797,7 @@ class Title implements LinkTarget {
                                'checkQuickPermissions',
                                'checkPermissionHooks',
                                'checkSpecialsAndNSPermissions',
+                               'checkSiteConfigPermissions',
                                'checkUserConfigPermissions',
                                'checkPageRestrictions',
                                'checkCascadingSourcesRestrictions',
index f3edebc..e31d3f9 100644 (file)
@@ -856,7 +856,7 @@ class WebRequest {
         * @return string
         */
        public function getFullRequestURL() {
-               return wfExpandUrl( $this->getRequestURL(), PROTO_CURRENT );
+               return wfGetServerUrl( PROTO_CURRENT ) .  $this->getRequestURL();
        }
 
        /**
index be4eeef..e546c4a 100644 (file)
@@ -1289,7 +1289,7 @@ abstract class ApiBase extends ContextSource {
                                        case 'text':
                                        case 'password':
                                                if ( $required && $value === '' ) {
-                                                       $this->dieWithError( [ 'apierror-missingparam', $paramName ] );
+                                                       $this->dieWithError( [ 'apierror-missingparam', $encParamName ] );
                                                }
                                                break;
                                        case 'integer': // Force everything using intval() and optionally validate limits
@@ -1443,7 +1443,7 @@ abstract class ApiBase extends ContextSource {
                                }
                        }
                } elseif ( $required ) {
-                       $this->dieWithError( [ 'apierror-missingparam', $paramName ] );
+                       $this->dieWithError( [ 'apierror-missingparam', $encParamName ] );
                }
 
                return $value;
index 610ecf5..e5e384c 100644 (file)
@@ -24,8 +24,6 @@
 use MediaWiki\Logger\LoggerFactory;
 use MediaWiki\MediaWikiServices;
 use Wikimedia\Timestamp\TimestampException;
-use Wikimedia\Rdbms\DBQueryError;
-use Wikimedia\Rdbms\DBError;
 
 /**
  * This is the main API class, used for both external and internal processing.
@@ -486,7 +484,7 @@ class ApiMain extends ApiBase {
         * @return ApiFormatBase
         */
        public function createPrinterByName( $format ) {
-               $printer = $this->mModuleMgr->getModule( $format, 'format' );
+               $printer = $this->mModuleMgr->getModule( $format, 'format', /* $ignoreCache */ true );
                if ( $printer === null ) {
                        $this->dieWithError(
                                [ 'apierror-unknownformat', wfEscapeWikiText( $format ) ], 'unknown_format'
@@ -535,11 +533,9 @@ class ApiMain extends ApiBase {
                        $this->executeAction();
                        $runTime = microtime( true ) - $t;
                        $this->logRequest( $runTime );
-                       if ( $this->mModule->isWriteMode() && $this->getRequest()->wasPosted() ) {
-                               MediaWikiServices::getInstance()->getStatsdDataFactory()->timing(
-                                       'api.' . $this->mModule->getModuleName() . '.executeTiming', 1000 * $runTime
-                               );
-                       }
+                       MediaWikiServices::getInstance()->getStatsdDataFactory()->timing(
+                               'api.' . $this->mModule->getModuleName() . '.executeTiming', 1000 * $runTime
+                       );
                } catch ( Exception $e ) {
                        $this->handleException( $e );
                        $this->logRequest( microtime( true ) - $t, $e );
@@ -1040,9 +1036,7 @@ class ApiMain extends ApiBase {
                        $config = $this->getConfig();
                        $class = preg_replace( '#^Wikimedia\\\Rdbms\\\#', '', get_class( $e ) );
                        $code = 'internal_api_error_' . $class;
-                       if ( ( $e instanceof DBQueryError ) && !$config->get( 'ShowSQLErrors' ) ) {
-                               $params = [ 'apierror-databaseerror', WebRequest::getRequestId() ];
-                       } else {
+                       if ( $config->get( 'ShowExceptionDetails' ) ) {
                                if ( $e instanceof ILocalizedException ) {
                                        $msg = $e->getMessageObject();
                                } elseif ( $e instanceof MessageSpecifier ) {
@@ -1051,7 +1045,10 @@ class ApiMain extends ApiBase {
                                        $msg = wfEscapeWikiText( $e->getMessage() );
                                }
                                $params = [ 'apierror-exceptioncaught', WebRequest::getRequestId(), $msg ];
+                       } else {
+                               $params = [ 'apierror-exceptioncaughttype', WebRequest::getRequestId(), get_class( $e ) ];
                        }
+
                        $messages[] = ApiMessage::create( $params, $code );
                }
                return $messages;
@@ -1115,9 +1112,7 @@ class ApiMain extends ApiBase {
                                )
                        );
                } else {
-                       if ( $config->get( 'ShowExceptionDetails' ) &&
-                               ( !$e instanceof DBError || $config->get( 'ShowDBErrorBacktrace' ) )
-                       ) {
+                       if ( $config->get( 'ShowExceptionDetails' ) ) {
                                $result->addContentValue(
                                        $path,
                                        'trace',
index f23c6a6..4b408fc 100644 (file)
@@ -705,10 +705,7 @@ class ApiQuerySiteinfo extends ApiQueryBase {
                $data = [];
 
                foreach ( $langNames as $code => $name ) {
-                       $lang = [
-                               'code' => $code,
-                               'bcp47' => LanguageCode::bcp47( $code ),
-                       ];
+                       $lang = [ 'code' => $code ];
                        ApiResult::setContentValue( $lang, 'name', $name );
                        $data[] = $lang;
                }
index 4b4177f..313cb02 100644 (file)
        "apierror-copyuploadbadurl": "لا يُسمَح بالرفع من هذا المسار.",
        "apierror-create-titleexists": "لا يمكن حماية العناوين الموجودة باستخدام <kbd>create</kbd>.",
        "apierror-csp-report": "خطأ في معالجة تقرير CSP: $1.",
-       "apierror-databaseerror": "[$1] خطأ في استعلام قاعدة البيانات.",
        "apierror-deletedrevs-param-not-1-2": "لا يمكن استخدام الوسيط <var>$1</var> في الأوضاع 1 أو 2.",
        "apierror-deletedrevs-param-not-3": "لا يمكن استخدام الوسيط <var>$1</var> في الوضع 3.",
        "apierror-emptynewsection": "إنشاء أقسام جديدة فارغة غير ممكن.",
index 74efd82..d7cdc6c 100644 (file)
        "apierror-copyuploadbadurl": "Upload not allowed from this URL.",
        "apierror-create-titleexists": "Existing titles can't be protected with <kbd>create</kbd>.",
        "apierror-csp-report": "Error processing CSP report: $1.",
-       "apierror-databaseerror": "[$1] Database query error.",
        "apierror-deletedrevs-param-not-1-2": "The <var>$1</var> parameter cannot be used in modes 1 or 2.",
        "apierror-deletedrevs-param-not-3": "The <var>$1</var> parameter cannot be used in mode 3.",
        "apierror-emptynewsection": "Creating empty new sections is not possible.",
        "apierror-emptypage": "Creating new, empty pages is not allowed.",
        "apierror-exceptioncaught": "[$1] Exception caught: $2",
+       "apierror-exceptioncaughttype": "[$1] Caught exception of type $2",
        "apierror-filedoesnotexist": "File does not exist.",
        "apierror-fileexists-sharedrepo-perm": "The target file exists on a shared repository. Use the <var>ignorewarnings</var> parameter to override it.",
        "apierror-filenopath": "Cannot get local file path.",
index 8328006..00759e8 100644 (file)
        "apierror-copyuploadbadurl": "No se permite realizar cargas a partir de este URL.",
        "apierror-create-titleexists": "Los títulos existentes no se pueden proteger con <kbd>create</kbd>.",
        "apierror-csp-report": "Error de procesamiento del informe CSP: $1.",
-       "apierror-databaseerror": "[$1] Error en la consulta de la base de datos.",
        "apierror-deletedrevs-param-not-1-2": "El parámetro <var>$1</var> no se puede utilizar en los modos 1 o 2.",
        "apierror-deletedrevs-param-not-3": "El parámetro <var>$1</var> no se puede usar en modo 3.",
        "apierror-emptynewsection": "Crear secciones vacías no es posible.",
index 0759e35..9f2a06f 100644 (file)
        "apierror-copyuploadbadurl": "Les téléversements ne sont pas autorisés depuis cette URL.",
        "apierror-create-titleexists": "Les titres existants ne peuvent pas être protégés avec <kbd>create</kbd>.",
        "apierror-csp-report": "Erreur lors du traitement du rapport CSP: $1.",
-       "apierror-databaseerror": "[$1] erreur de requête de base de données.",
        "apierror-deletedrevs-param-not-1-2": "Le paramètre <var>$1</var> ne peut pas être utilisé dans les modes 1 ou 2.",
        "apierror-deletedrevs-param-not-3": "Le paramètre <var>$1</var> ne peut pas être utilisé dans le mode 3.",
        "apierror-emptynewsection": "Il n'est pas possible de créer de nouvelles sections vides.",
index 1731323..da839f4 100644 (file)
        "apierror-copyuploadbadurl": "As subas non están permitidas para esta URL.",
        "apierror-create-titleexists": "Os títulos existentes non poden ser protexidos con <kbd>create</kbd>.",
        "apierror-csp-report": "Erro procesando o informe CSPː $1.",
-       "apierror-databaseerror": "[$1] erro de consulta da base de datos.",
        "apierror-deletedrevs-param-not-1-2": "O parámetro <var>$1</var> non pode usarse nos modos 1 e 2.",
        "apierror-deletedrevs-param-not-3": "O parámetro <var>$1</var> non pode usarse no modo 3.",
        "apierror-emptynewsection": "Non é posible crear novas seccións baleiras.",
index 44910c8..5cc5795 100644 (file)
        "apierror-copyuploadbadurl": "העלאה אינה מותרת מה־URL הזה.",
        "apierror-create-titleexists": "כותרות קיימות אינם יכולות מוגנות עם <kbd>create</kbd>.",
        "apierror-csp-report": "בעיבוד דו\"ח CSP אירעה שגיאה: $1",
-       "apierror-databaseerror": "[$1] שגיאת שאילתת מסד נתונים.",
        "apierror-deletedrevs-param-not-1-2": "הפרמטר <var>$1</var> אינו יכול לשמש במצבים 1 או 2.",
        "apierror-deletedrevs-param-not-3": "הפרמטר <var>$1</var> אינו יכול במצב 3.",
        "apierror-emptynewsection": "יצירת פסקאות חדשות ריקות בלתי־אפשרי.",
index 97309ff..399fc1f 100644 (file)
        "apihelp-undelete-param-timestamps": "復元する版のタイムスタンプ。<var>$1timestamps</var> と <var>$1fileids</var> の両方が空の場合、すべての版が復元されます。",
        "apihelp-undelete-example-page": "<kbd>Main Page</kbd> を復元する。",
        "apihelp-undelete-example-revisions": "<kbd>Main Page</kbd> の2つの版を復元する。",
+       "apihelp-upload-param-filename": "対象のファイル名。",
+       "apihelp-upload-param-comment": "アップロードのコメント。新規ファイルのアップロードで <var>$1text</var> が指定されていない場合、初期ページテキストとしても使用されます。",
+       "apihelp-upload-param-text": "新しいファイルの初期ページテキスト。",
        "apihelp-upload-param-watch": "このページをウォッチする。",
        "apihelp-upload-param-ignorewarnings": "あらゆる警告を無視する。",
        "apihelp-upload-param-url": "ファイル取得元のURL.",
index e2b6bb4..56a6507 100644 (file)
        "apierror-cantimport-upload": "업로드된 페이지를 가져올 권한이 없습니다.",
        "apierror-cantimport": "페이지를 가져올 권한이 없습니다.",
        "apierror-cantsend": "로그인하지 않았거나 인증된 이메일 주소가 없거나 다른 사용자로 이메일을 보낼 권한이 없기 때문에 이메일을 보낼 수 없습니다.",
-       "apierror-databaseerror": "[$1] 데이터베이스 쿼리 오류.",
        "apierror-emptynewsection": "비어있는 새 문단을 만들 수 없습니다.",
        "apierror-emptypage": "새 문서로 빈 문서를 만들 수 없습니다.",
        "apierror-exceptioncaught": "[$1] 예외가 발생했습니다: $2",
index f470a72..09b6671 100644 (file)
        "apierror-cantimport-upload": "Neturite teisės importuoti įkeltų puslapių.",
        "apierror-cantimport": "Neturite teisės importuoti puslapių.",
        "apierror-copyuploadbadurl": "Įkėlimas neleidžiamas iš šio URL.",
-       "apierror-databaseerror": "[$1] Duomenų bazės užklausos klaida.",
        "apierror-emptynewsection": "Neįmanoma kurti naujų tuščių skyrių.",
        "apierror-filedoesnotexist": "Failas neegzistuoja.",
        "apierror-filetypecannotberotated": "Failo tipas negali būti pasuktas.",
index a1c9238..c43960b 100644 (file)
        "apierror-copyuploadbadurl": "Opplasting tillates ikke fra denne URL-en.",
        "apierror-create-titleexists": "Eksisterende titler kan ikke beskyttes med <kbd>create</kbd>.",
        "apierror-csp-report": "Feil under prosessering av CSP-rapport: $1.",
-       "apierror-databaseerror": "[$1] Databasespørringsfeil.",
        "apierror-deletedrevs-param-not-1-2": "Parameteren <var>$1</var> kan ikke brukes i modus 1 eller 2.",
        "apierror-deletedrevs-param-not-3": "Parameteren <var>$1</var> kan ikke brukes i modus 3.",
        "apierror-emptynewsection": "Oppretting av tomme nye seksjoner er ikke mulig.",
index 9b4ac5f..1ded789 100644 (file)
        "apierror-cantimport": "Nie masz uprawnień do importowania stron.",
        "apierror-cantsend": "Nie jesteś zalogowany, nie masz potwierdzonego adresu e-mail, albo nie masz prawa wysyłać e-maili do innych użytkowników, więc nie możesz wysłać wiadomości e-mail.",
        "apierror-cantundelete": "Nie można przywrócić: dana wersja nie istnieje albo została już przywrócona.",
-       "apierror-databaseerror": "[$1] Błąd zapytania do bazy danych.",
        "apierror-exceptioncaught": "[$1] Stwierdzono wyjątek: $2",
        "apierror-filedoesnotexist": "Plik nie istnieje.",
        "apierror-import-unknownerror": "Nieznany błąd podczas importowania: $1.",
index 439f811..e6724d4 100644 (file)
        "apierror-copyuploadbadurl": "Envio não permitido a partir deste URL.",
        "apierror-create-titleexists": "Os títulos existentes não podem ser protegidos com <kbd>create</kbd>.",
        "apierror-csp-report": "Erro ao processar o relatório CSP: $1.",
-       "apierror-databaseerror": "[$1] Houve um erro na consulta ao banco de dados.",
        "apierror-deletedrevs-param-not-1-2": "O parâmetro <var>$1</var> não pode ser usado nos modos 1 ou 2.",
        "apierror-deletedrevs-param-not-3": "O parâmetro <var>$1</var> não pode ser usado no modo 3.",
        "apierror-emptynewsection": "A criação de novas seções vazias não é possível.",
index ca43981..a58dec1 100644 (file)
        "api-help-param-default": "Valor por omissão: $1",
        "api-help-param-default-empty": "Padrão: <span class=\"apihelp-empty\">(vazio)</span>",
        "api-help-param-token": "Uma chave \"$1\" obtida de [[Special:ApiHelp/query+tokens|action=query&meta=tokens]]",
-       "api-help-param-token-webui": "Para efeitos de compatibilidade, a chave usada na interface ''web'' também é aceite.",
+       "api-help-param-token-webui": "Para efeitos de compatibilidade, a chave usada na interface da Internet também é aceite.",
        "api-help-param-disabled-in-miser-mode": "Desativado devido ao [[mw:Special:MyLanguage/Manual:$wgMiserMode|modo avarento]] (''miser mode'').",
        "api-help-param-limited-in-miser-mode": "<strong>Nota:</strong> devido ao [[mw:Special:MyLanguage/Manual:$wgMiserMode|modo avarento]] (''miser mode''), usar isto pode resultar na devolução de menos de <var>$1limit</var> resultados antes de continuar; em casos extremos pode não ser devolvido qualquer resultado.",
        "api-help-param-direction": "A direção da enumeração:\n;newer:Listar o mais antigo primeiro. Nota: $1start tem de estar antes de $1end.\n;older:Listar o mais recente primeiro (padrão). Nota: $1start tem de estar depois de $1end.",
        "api-help-authmanagerhelper-messageformat": "Formato a usar nas mensagens de saída.",
        "api-help-authmanagerhelper-mergerequestfields": "Combinar a informação de todos os pedidos de autenticação numa única matriz.",
        "api-help-authmanagerhelper-preservestate": "Preservar o estado de uma tentativa de autenticação anterior falhada, se possível.",
-       "api-help-authmanagerhelper-returnurl": "O URL de retorno para processos de autenticação por terceiros tem de ser absoluto. É obrigatório fornecer este URL ou <var>$1continue</var>.\n\nTipicamente, após receber uma resposta <samp>REDIRECT</samp>, abrirá um ''browser'' ou uma ''web view'' para o URL <samp>redirecttarget</samp> especificado, para dar lugar ao processo de autenticação por terceiros. Quando esse processo terminar, a terceira entidade encaminhará o ''browser'' ou a ''web view'' para este URL. Deve extrair do URL quaisquer parâmetros de consulta ou de POST, e passá-los como um pedido <var>$1continue</var> a este módulo da API.",
+       "api-help-authmanagerhelper-returnurl": "O URL de retorno para processos de autenticação por terceiros tem de ser absoluto. É obrigatório fornecer este URL ou <var>$1continue</var>.\n\nTipicamente, após receber uma resposta <samp>REDIRECT</samp>, abrirá um ''browser'' ou uma vista da Internet para o URL <samp>redirecttarget</samp> especificado, para dar lugar ao processo de autenticação por terceiros. Quando esse processo terminar, a terceira entidade encaminhará o ''browser'' ou a vista da Internet para este URL. Deve extrair do URL quaisquer parâmetros de consulta ou de POST, e passá-los como um pedido <var>$1continue</var> a este módulo da API.",
        "api-help-authmanagerhelper-continue": "Este pedido é uma continuação após uma resposta anterior com o valor <samp>UI</samp> ou <samp>REDIRECT</samp>. É obrigatório fornecer este parâmetro ou o parâmetro <var>$1returnurl</var>.",
        "api-help-authmanagerhelper-additional-params": "Este módulo aceita parâmetros adicionais, dependendo dos pedidos de autenticação disponíveis. Use <kbd>[[Special:ApiHelp/query+authmanagerinfo|action=query&meta=authmanagerinfo]]</kbd> com <kbd>amirequestsfor=$1</kbd> (ou uma resposta anterior deste módulo, se aplicável) para determinar os pedidos disponíveis e os campos que estes utilizam.",
        "apierror-allimages-redirect": "Usar <kbd>gaifilterredir=nonredirects</kbd> em vez de <var>redirects</var> ao utilizar <kbd>allimages</kbd> como gerador.",
        "apierror-copyuploadbadurl": "Não são permitidos carregamentos a partir deste URL.",
        "apierror-create-titleexists": "Os títulos existentes não podem ser protegidos com  <kbd>create</kbd>.",
        "apierror-csp-report": "Ocorreu um erro no processamento do relatório CSP: $1.",
-       "apierror-databaseerror": "[$1] Erro na consulta da base de dados.",
        "apierror-deletedrevs-param-not-1-2": "O parâmetro <var>$1</var> não pode ser usado nos modos 1 e 2.",
        "apierror-deletedrevs-param-not-3": "O parâmetro <var>$1</var> não pode ser usado no modo 3.",
        "apierror-emptynewsection": "Não é possível criar secções novas vazias.",
index 2b4a587..dd8e529 100644 (file)
        "apierror-copyuploadbadurl": "{{doc-apierror}}",
        "apierror-create-titleexists": "{{doc-apierror}}",
        "apierror-csp-report": "{{doc-apierror}}\n\nParameters:\n* $1 - Error code, e.g. \"toobig\".",
-       "apierror-databaseerror": "{{doc-apierror}}\n\nParameters:\n* $1 - Exception log ID code. This is meaningless to the end user, but can be used by people with access to the logs to easily find the logged error.",
        "apierror-deletedrevs-param-not-1-2": "{{doc-apierror}}\n\nParameters:\n* $1 - Parameter name.\n\nSee also:\n* {{msg-mw|apihelp-query+deletedrevs-extended-description}}",
        "apierror-deletedrevs-param-not-3": "{{doc-apierror}}\n\nParameters:\n* $1 - Parameter name.\n\nSee also:\n* {{msg-mw|apihelp-query+deletedrevs-extended-description}}",
        "apierror-emptynewsection": "{{doc-apierror}}",
        "apierror-emptypage": "{{doc-apierror}}",
        "apierror-exceptioncaught": "{{doc-apierror}}\n\nParameters:\n* $1 - Exception log ID code. This is meaningless to the end user, but can be used by people with access to the logs to easily find the logged error.\n* $2 - Exception message, which may end with punctuation. Probably in English.",
+       "apierror-exceptioncaughttype": "{{doc-apierror}}\n\nParameters:\n* $1 - Exception log ID code. This is meaningless to the end user, but can be used by people with access to the logs to easily find the logged error.\n* $2 - Exception type (for example, DBError).",
        "apierror-filedoesnotexist": "{{doc-apierror}}",
        "apierror-fileexists-sharedrepo-perm": "{{doc-apierror}}",
        "apierror-filenopath": "{{doc-apierror}}",
index 4df71ba..6bfe059 100644 (file)
@@ -34,7 +34,8 @@
                        "AttemptToCallNil",
                        "Movses",
                        "Stjn",
-                       "Edward Chernenko"
+                       "Edward Chernenko",
+                       "Vlad5250"
                ]
        },
        "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> MediaWiki API — зрелый и стабильный интерфейс, активно поддерживаемый и улучшаемый. Мы стараемся избегать ломающих изменений, однако изредка они могут быть необходимы. Подпишитесь на [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ почтовую рассылку mediawiki-api-announce], чтобы быть в курсе обновлений.\n\n<strong>Ошибочные запросы:</strong> Если API получает запрос с ошибкой, вернётся заголовок HTTP с ключом «MediaWiki-API-Error», после чего значение заголовка и код ошибки будут отправлены обратно и установлены в то же значение. Более подробную информацию см. [[mw:Special:MyLanguage/API:Errors_and_warnings|API: Ошибки и предупреждения]].\n\n<p class=\"mw-apisandbox-link\"><strong>Тестирование:</strong> для удобства тестирования API-запросов, см. [[Special:ApiSandbox]].</p>",
        "apihelp-query+filearchive-example-simple": "Список всех удалённых файлов.",
        "apihelp-query+filerepoinfo-summary": "Возвращает мета-информацию о файловых репозиториях, настроенных в вики.",
        "apihelp-query+filerepoinfo-param-prop": "Какие свойства хранилища получить (доступность свойств может отличаться в разных вики).",
+       "apihelp-query+filerepoinfo-paramvalue-prop-rootUrl": "Корневой URL для изображений.",
+       "apihelp-query+filerepoinfo-paramvalue-prop-url": "URL путь публичной зоны.",
        "apihelp-query+filerepoinfo-example-simple": "Получить информацию о файловых репозиториях.",
        "apihelp-query+fileusage-summary": "Поиск всех страниц, использующих данный файл.",
        "apihelp-query+fileusage-param-prop": "Какие свойства получить:",
        "apierror-copyuploadbadurl": "Загрузка по этой ссылке недоступна.",
        "apierror-create-titleexists": "Существующие названия не могут быть защищены с помощью <kbd>create</kbd>.",
        "apierror-csp-report": "Ошибка при обработке отчёта CSP: $1.",
-       "apierror-databaseerror": "[$1] Ошибка запроса к базе данных.",
        "apierror-deletedrevs-param-not-1-2": "Параметр <var>$1</var> не может быть использован в режимах 1 и 2.",
        "apierror-deletedrevs-param-not-3": "Параметр <var>$1</var> не может быть использован в третьем режиме.",
        "apierror-emptynewsection": "Создание пустых секций невозможно.",
index 3039ef4..95cd709 100644 (file)
        "apierror-copyuploadbadurl": "Завантаження з цієї URL-адреси недозволені.",
        "apierror-create-titleexists": "Наявні назви не можна захистити за допомогою <kbd>create</kbd>.",
        "apierror-csp-report": "Помилка при опрацюванні CSP-звіту: $1.",
-       "apierror-databaseerror": "[$1] Помилка запиту до бази даних.",
        "apierror-deletedrevs-param-not-1-2": "Параметр <var>$1</var> не може використовуватись у режимах 1 або 2.",
        "apierror-deletedrevs-param-not-3": "Параметр <var>$1</var> не може використовуватись у режимі 3.",
        "apierror-emptynewsection": "Створення нових порожніх розділів неможливе.",
index 1cc7c5c..5b9c2e4 100644 (file)
        "apihelp-purge-example-simple": "刷新<kbd>Main Page</kbd>和<kbd>API</kbd>页面。",
        "apihelp-purge-example-generator": "刷新主名字空间的前10个页面。",
        "apihelp-query-summary": "取得来自并有关MediaWiki的数据。",
-       "apihelp-query-extended-description": "所有数据修改将首先不得不使用查询来获得令牌,以阻止来自恶意网站的滥用行为。",
+       "apihelp-query-extended-description": "所有数据修改须首先使用查询来获得令牌,以阻止来自恶意网站的滥用行为。",
        "apihelp-query-param-prop": "要为已查询页面获取的属性。",
        "apihelp-query-param-list": "要获取的列表。",
        "apihelp-query-param-meta": "要获取的元数据。",
        "apierror-copyuploadbadurl": "不允许从此URL上传。",
        "apierror-create-titleexists": "现有标题不能通过<kbd>create</kbd>保护。",
        "apierror-csp-report": "处理CSP报告时出错:$1。",
-       "apierror-databaseerror": "[$1]数据库查询错误。",
        "apierror-deletedrevs-param-not-1-2": "<var>$1</var>参数不能用于模式1或2。",
        "apierror-deletedrevs-param-not-3": "<var>$1</var>参数不能用于模式3。",
        "apierror-emptynewsection": "无法创建空白新章节。",
index 78fb24a..7a172be 100644 (file)
@@ -55,7 +55,7 @@ class MessageCache {
        /**
         * @var bool[] Map of (language code => boolean)
         */
-       protected $mCacheVolatile = [];
+       protected $cacheVolatile = [];
 
        /**
         * Should mean that database cannot be used, but check
@@ -272,7 +272,7 @@ class MessageCache {
                # Hash of the contents is stored in memcache, to detect if data-center cache
                # or local cache goes out of date (e.g. due to replace() on some other server)
                list( $hash, $hashVolatile ) = $this->getValidationHash( $code );
-               $this->mCacheVolatile[$code] = $hashVolatile;
+               $this->cacheVolatile[$code] = $hashVolatile;
 
                # Try the local cache and check against the cluster hash key...
                $cache = $this->getLocalCache( $code );
@@ -592,13 +592,15 @@ class MessageCache {
                                                [ 'title' => $title, 'code' => $code ] );
                                        return;
                                }
-                               // Load the messages from the master DB to avoid race conditions
+                               // Reload messages from the database and pre-populate dc-local caches
+                               // as optimisation. Use the master DB to avoid race conditions.
                                $cache = $this->loadFromDB( $code, self::FOR_UPDATE );
                                // Check if an individual cache key should exist and update cache accordingly
                                $page = WikiPage::factory( Title::makeTitle( NS_MEDIAWIKI, $title ) );
                                $page->loadPageData( $page::READ_LATEST );
                                $text = $this->getMessageTextFromContent( $page->getContent() );
                                if ( is_string( $text ) && strlen( $text ) > $wgMaxMsgCacheEntrySize ) {
+                                       // Match logic of loadCachedMessagePageEntry()
                                        $this->wanCache->set(
                                                $this->bigMessageCacheKey( $cache['HASH'], $title ),
                                                ' ' . $text,
@@ -966,10 +968,13 @@ class MessageCache {
         * @return string|bool The message, or false if it does not exist or on error
         */
        public function getMsgFromNamespace( $title, $code ) {
+               // Load all MediaWiki page definitions into cache. Note that individual keys
+               // already loaded into cache during this request remain in the cache, which
+               // includes the value of hook-defined messages.
                $this->load( $code );
 
-               if ( $this->cache->hasField( $code, $title ) ) {
-                       $entry = $this->cache->getField( $code, $title );
+               $entry = $this->cache->getField( $code, $title );
+               if ( $entry !== null ) {
                        if ( substr( $entry, 0, 1 ) === ' ' ) {
                                // The message exists and is not '!TOO BIG'
                                return (string)substr( $entry, 1 );
@@ -978,6 +983,7 @@ class MessageCache {
                        }
                        // Fall through and try invididual message cache below
                } else {
+                       // Message does not have a MediaWiki page definition
                        $message = false;
                        Hooks::run( 'MessagesPreLoad', [ $title, &$message, $code ] );
                        if ( $message !== false ) {
@@ -989,75 +995,82 @@ class MessageCache {
                        return $message;
                }
 
-               // Individual message cache key
-               $bigKey = $this->bigMessageCacheKey( $this->cache->getField( $code, 'HASH' ), $title );
-
-               if ( $this->mCacheVolatile[$code] ) {
+               if ( $this->cacheVolatile[$code] ) {
                        $entry = false;
                        // Make sure that individual keys respect the WAN cache holdoff period too
                        LoggerFactory::getInstance( 'MessageCache' )->debug(
                                __METHOD__ . ': loading volatile key \'{titleKey}\'',
-                               [ 'titleKey' => $bigKey, 'code' => $code ] );
+                               [ 'titleKey' => $title, 'code' => $code ] );
                } else {
                        // Try the individual message cache
-                       $entry = $this->wanCache->get( $bigKey );
-               }
-
-               if ( $entry !== false ) {
-                       if ( substr( $entry, 0, 1 ) === ' ' ) {
-                               $this->cache->setField( $code, $title, $entry );
-                               // The message exists, so make sure a string is returned
-                               return (string)substr( $entry, 1 );
-                       } elseif ( $entry === '!NONEXISTENT' ) {
-                               $this->cache->setField( $code, $title, '!NONEXISTENT' );
-
-                               return false;
-                       } else {
-                               // Corrupt/obsolete entry, delete it
-                               $this->wanCache->delete( $bigKey );
-                       }
-               }
-
-               // Try loading the message from the database
-               $dbr = wfGetDB( DB_REPLICA );
-               $cacheOpts = Database::getCacheSetOptions( $dbr );
-               // Use newKnownCurrent() to avoid querying revision/user tables
-               $titleObj = Title::makeTitle( NS_MEDIAWIKI, $title );
-               if ( $titleObj->getLatestRevID() ) {
-                       $revision = Revision::newKnownCurrent(
-                               $dbr,
-                               $titleObj
+                       $entry = $this->loadCachedMessagePageEntry(
+                               $title,
+                               $code,
+                               $this->cache->getField( $code, 'HASH' )
                        );
-               } else {
-                       $revision = false;
                }
 
-               if ( $revision ) {
-                       $content = $revision->getContent();
-                       if ( $content ) {
-                               $message = $this->getMessageTextFromContent( $content );
-                               if ( is_string( $message ) ) {
-                                       $this->cache->setField( $code, $title, ' ' . $message );
-                                       $this->wanCache->set( $bigKey, ' ' . $message, $this->mExpiry, $cacheOpts );
-                               }
-                       } else {
-                               // A possibly temporary loading failure
-                               LoggerFactory::getInstance( 'MessageCache' )->warning(
-                                       __METHOD__ . ': failed to load message page text for \'{titleKey}\'',
-                                       [ 'titleKey' => $bigKey, 'code' => $code ] );
-                               $message = null; // no negative caching
-                       }
-               } else {
-                       $message = false; // negative caching
+               if ( $entry !== false && substr( $entry, 0, 1 ) === ' ' ) {
+                       $this->cache->setField( $code, $title, $entry );
+                       // The message exists, so make sure a string is returned
+                       return (string)substr( $entry, 1 );
                }
 
-               if ( $message === false ) {
-                       // Negative caching in case a "too big" message is no longer available (deleted)
-                       $this->cache->setField( $code, $title, '!NONEXISTENT' );
-                       $this->wanCache->set( $bigKey, '!NONEXISTENT', $this->mExpiry, $cacheOpts );
-               }
+               $this->cache->setField( $code, $title, '!NONEXISTENT' );
 
-               return $message;
+               return false;
+       }
+
+       /**
+        * @param string $dbKey
+        * @param string $code
+        * @param string $hash
+        * @return string Either " <MESSAGE>" or "!NONEXISTANT"
+        */
+       private function loadCachedMessagePageEntry( $dbKey, $code, $hash ) {
+               return $this->srvCache->getWithSetCallback(
+                       $this->srvCache->makeKey( 'messages-big', $hash, $dbKey ),
+                       IExpiringStore::TTL_MINUTE,
+                       function () use ( $code, $dbKey, $hash ) {
+                               return $this->wanCache->getWithSetCallback(
+                                       $this->bigMessageCacheKey( $hash, $dbKey ),
+                                       $this->mExpiry,
+                                       function ( $oldValue, &$ttl, &$setOpts ) use ( $dbKey, $code ) {
+                                               // Try loading the message from the database
+                                               $dbr = wfGetDB( DB_REPLICA );
+                                               $setOpts += Database::getCacheSetOptions( $dbr );
+                                               // Use newKnownCurrent() to avoid querying revision/user tables
+                                               $title = Title::makeTitle( NS_MEDIAWIKI, $dbKey );
+                                               $revision = Revision::newKnownCurrent( $dbr, $title );
+                                               if ( !$revision ) {
+                                                       // The wiki doesn't have a local override page. Cache absence with normal TTL.
+                                                       // When overrides are created, self::replace() takes care of the cache.
+                                                       return '!NONEXISTENT';
+                                               }
+                                               $content = $revision->getContent();
+                                               if ( $content ) {
+                                                       $message = $this->getMessageTextFromContent( $content );
+                                               } else {
+                                                       LoggerFactory::getInstance( 'MessageCache' )->warning(
+                                                               __METHOD__ . ': failed to load page text for \'{titleKey}\'',
+                                                               [ 'titleKey' => $dbKey, 'code' => $code ]
+                                                       );
+                                                       $message = null;
+                                               }
+
+                                               if ( !is_string( $message ) ) {
+                                                       // Revision failed to load Content, or Content is incompatible with wikitext.
+                                                       // Possibly a temporary loading failure.
+                                                       $ttl = 5;
+
+                                                       return '!NONEXISTENT';
+                                               }
+
+                                               return ' ' . $message;
+                                       }
+                               );
+                       }
+               );
        }
 
        /**
diff --git a/includes/config/ConfigRepository.php b/includes/config/ConfigRepository.php
new file mode 100644 (file)
index 0000000..c87a344
--- /dev/null
@@ -0,0 +1,224 @@
+<?php
+/**
+ * Copyright 2016
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
+
+namespace MediaWiki\Config;
+
+use MediaWiki\Services\SalvageableService;
+use Wikimedia\Assert\Assert;
+
+/**
+ * Object which holds currently registered configuration options.
+ *
+ * @since 1.32
+ */
+class ConfigRepository implements SalvageableService {
+       /** @var \ConfigFactory */
+       private $configFactory;
+
+       /** @var array */
+       private $configItems = [
+               'private' => [],
+               'public' => [],
+       ];
+
+       /**
+        * @param \ConfigFactory $configFactory
+        */
+       public function __construct( \ConfigFactory $configFactory ) {
+               $this->configFactory = $configFactory;
+       }
+
+       /**
+        * Returns true, if this repository contains a configuration with a specific name.
+        *
+        * @param string $name The name of the config to check the existence of
+        * @param bool $alsoPrivate If set to true, will check the private config options, too
+        * @return bool
+        */
+       public function has( $name, $alsoPrivate = false ) {
+               return isset( $this->configItems['public'][$name] ) ||
+                       ( $alsoPrivate && isset( $this->configItems['private'][$name] ) );
+       }
+
+       /**
+        * Returns the ConfigItem with the given name, if there's one. Throws a ConfigException
+        * otherwise.
+        *
+        * @param string $name The name of the configuration option to get
+        * @return array
+        * @throws \ConfigException
+        */
+       public function get( $name ) {
+               if ( !$this->has( $name, true ) ) {
+                       throw new \ConfigException( 'The configuration option ' . $name . ' does not exist.' );
+               }
+               if ( isset( $this->configItems['public'][$name] ) ) {
+                       return $this->configItems['public'][$name];
+               }
+               return $this->configItems['private'][$name];
+       }
+
+       /**
+        * Returns an array of all configuration items saved in this ConfigRepository. This includes
+        * all configuration options, including the ones marked as private and public.
+        *
+        * Note: This function does not do any permission checks or something similar. You should not
+        * use this function, if the output will be available to the public audience! Use
+        * ConfigRepository::getPublic() instead.
+        *
+        * @return array
+        */
+       public function getAll() {
+               return array_merge( $this->configItems['private'], $this->configItems['public'] );
+       }
+
+       /**
+        * Returns an array of all public configuration options saved in this ConfigRepository.
+        *
+        * @return array
+        */
+       public function getPublic() {
+               return $this->configItems['public'];
+       }
+
+       /**
+        * Returns the current value of the configuration option. If no ConfigRegistry was provided
+        * when the config was added to the repository, the default value will be returned.
+        *
+        * @param string $name The name of the configuration option to get the value of
+        * @return mixed
+        * @throws \ConfigException
+        */
+       public function getValueOf( $name ) {
+               $config = $this->get( $name );
+               if ( !isset( $config['configregistry'] ) ) {
+                       return $config['value'];
+               }
+
+               return $this->configFactory->makeConfig( $config['configregistry'] )->get( $name );
+       }
+
+       /**
+        * Returns the description of the given config option, This can be either a localized
+        * description, if one such, or the (maybe english only) description provided in the
+        * definition of the configuration. If both is not provided an empty string is returned.
+        *
+        * @param string $name The name of the configuration option to get the description of
+        * @return string HTML-escaped string
+        */
+       public function getDescriptionOf( $name ) {
+               $config = $this->get( $name );
+               if ( isset( $config['descriptionmsg'] ) ) {
+                       return wfMessage( $config['descriptionmsg'] )->escaped();
+               }
+               if ( isset( $config['description'] ) ) {
+                       return htmlspecialchars( $config['description'] );
+               }
+               return '';
+       }
+
+       /**
+        * Adds the definition of a configuration to this repository.
+        *
+        * @param string $name the name of the config
+        * @param array $config Options of this config. Values are:
+        *  - value: The default value of this configuration, required
+        *  - providedby: The name of the provider of this config (an extension, core, ...), required
+        *  - configregistry: The name of the config to retrieve the value with, required
+        *  - public: whether this option is public or not, if not set, the option is considered as
+        *    "private", optional
+        *  - description: the not localized description of this config option, optional
+        *  - descriptionmsg: The message key of the localized description of this configuration
+        *    option, optional
+        * @throws \ConfigException
+        */
+       public function add( $name, array $config ) {
+               if ( $this->has( $name ) ) {
+                       throw new \ConfigException( 'A configuration with the name ' . $name .
+                               'does already exist. It is provided by: ' .
+                               $this->get( $name )['providedby'] );
+               }
+               if ( isset( $config['public'] ) && $config['public'] ) {
+                       $this->configItems['public'][$name] = $config;
+               } else {
+                       $this->configItems['private'][$name] = $config;
+               }
+       }
+
+       /**
+        * Returns true, if there're no elements in this instance, otherwise false.
+        *
+        * @param bool $includePrivate Whether configuration options, that are marked as private
+        * should be included in this check.
+        * @return bool
+        */
+       public function isEmpty( $includePrivate = false ) {
+               if ( $includePrivate ) {
+                       return empty( $this->configItems['private'] ) &&
+                               empty( $this->configItems[ 'public'] );
+               }
+               return empty( $this->configItems['public'] );
+       }
+
+       /**
+        * Re-uses existing Cache objects from $other. Cache objects are only re-used if the
+        * registered factory function for both is the same.
+        *
+        * @see SalvageableService::salvage()
+        *
+        * @param SalvageableService $other The object to salvage state from. $other must have the
+        * exact same type as $this.
+        */
+       public function salvage( SalvageableService $other ) {
+               Assert::parameterType( self::class, $other, '$other' );
+
+               /** @var ConfigRepository $other */
+               $otherCurrentObj = $other->current();
+               foreach ( $other->configItems['public'] as $name => $otherConfig ) {
+                       if ( isset( $this->configItems['public'][$name] ) ) {
+                               continue;
+                       }
+
+                       $this->add( $name, $otherConfig );
+
+                       // recover the pointer of the other config repository
+                       if ( $otherCurrentObj === $otherConfig ) {
+                               end( $this->configItems['public'] );
+                       }
+               }
+               foreach ( $other->configItems['private'] as $name => $otherConfig ) {
+                       if ( isset( $this->configItems['private'][$name] ) ) {
+                               continue;
+                       }
+
+                       $this->add( $name, $otherConfig );
+
+                       // recover the pointer of the other config repository
+                       if ( $otherCurrentObj === $otherConfig ) {
+                               end( $this->configItems['private'] );
+                       }
+               }
+
+               // disable $other
+               $other->configItems = [];
+       }
+}
diff --git a/includes/debug/DeprecationHelper.php b/includes/debug/DeprecationHelper.php
new file mode 100644 (file)
index 0000000..cd78005
--- /dev/null
@@ -0,0 +1,148 @@
+<?php
+/**
+ * Trait for issuing warnings on deprecated access.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
+
+/**
+ * Use this trait in classes which have properties for which public access
+ * is deprecated. Set the list of properties in $deprecatedPublicProperties
+ * and make the properties non-public. The trait will preserve public access
+ * but issue deprecation warnings when it is needed.
+ *
+ * Example usage:
+ *     class Foo {
+ *         use DeprecationHelper;
+ *         protected $bar;
+ *         public function __construct() {
+ *             $this->deprecatePublicProperty( 'bar', '1.21', __CLASS__ );
+ *         }
+ *     }
+ *
+ *     $foo = new Foo;
+ *     $foo->bar; // works but logs a warning
+ *
+ * Cannot be used with classes that have their own __get/__set methods.
+ *
+ * @since 1.32
+ */
+trait DeprecationHelper {
+
+       /**
+        * List of deprecated properties, in <property name> => [<version>, <class>, <component>] format
+        * where <version> is the MediaWiki version where the property got deprecated, <class> is the
+        * the name of the class defining the property, <component> is the MediaWiki component
+        * (extension, skin etc.) for use in the deprecation warning) or null if it is MediaWiki.
+        * E.g. [ 'mNewRev' => [ '1.32', 'DifferenceEngine', null ]
+        * @var string[]
+        */
+       protected $deprecatedPublicProperties = [];
+
+       /**
+        * Mark a property as deprecated. Only use this for properties that used to be public and only
+        *   call it in the constructor.
+        * @param string $property The name of the property.
+        * @param string $version MediaWiki version where the property became deprecated.
+        * @param string|null $class The class which has the deprecated property. This can usually be
+        *   guessed, but PHP can get confused when both the parent class and the subclass use the
+        *   trait, so it should be specified in classes meant for subclassing.
+        * @param string|null $component
+        * @see wfDeprecated()
+        */
+       protected function deprecatePublicProperty(
+               $property, $version, $class = null, $component = null
+       ) {
+               $this->deprecatedPublicProperties[$property] = [ $version, $class ?: get_class(), $component ];
+       }
+
+       public function __get( $name ) {
+               if ( isset( $this->deprecatedPublicProperties[$name] ) ) {
+                       list( $version, $class, $component ) = $this->deprecatedPublicProperties[$name];
+                       $qualifiedName = $class . '::$' . $name;
+                       wfDeprecated( $qualifiedName, $version, $component, 3 );
+                       return $this->$name;
+               }
+
+               $qualifiedName = get_class() . '::$' . $name;
+               if ( $this->deprecationHelperGetPropertyOwner( $name ) ) {
+                       // Someone tried to access a normal non-public property. Try to behave like PHP would.
+                       trigger_error( "Cannot access non-public property $qualifiedName", E_USER_ERROR );
+               } else {
+                       // Non-existing property. Try to behave like PHP would.
+                       trigger_error( "Undefined property: $qualifiedName", E_USER_NOTICE );
+               }
+               return null;
+       }
+
+       public function __set( $name, $value ) {
+               if ( isset( $this->deprecatedPublicProperties[$name] ) ) {
+                       list( $version, $class, $component ) = $this->deprecatedPublicProperties[$name];
+                       $qualifiedName = $class . '::$' . $name;
+                       wfDeprecated( $qualifiedName, $version, $component, 3 );
+                       $this->$name = $value;
+                       return;
+               }
+
+               $qualifiedName = get_class() . '::$' . $name;
+               if ( $this->deprecationHelperGetPropertyOwner( $name ) ) {
+                       // Someone tried to access a normal non-public property. Try to behave like PHP would.
+                       trigger_error( "Cannot access non-public property $qualifiedName", E_USER_ERROR );
+               } else {
+                       // Non-existing property. Try to behave like PHP would.
+                       $this->$name = $value;
+               }
+       }
+
+       /**
+        * Like property_exists but also check for non-visible private properties and returns which
+        * class in the inheritance chain declared the property.
+        * @param string $property
+        * @return string|bool Best guess for the class in which the property is defined.
+        */
+       private function deprecationHelperGetPropertyOwner( $property ) {
+               // Easy branch: check for protected property / private property of the current class.
+               if ( property_exists( $this, $property ) ) {
+                       // The class name is not necessarily correct here but getting the correct class
+                       // name would be expensive, this will work most of the time and getting it
+                       // wrong is not a big deal.
+                       return __CLASS__;
+               }
+               // property_exists() returns false when the property does exist but is private (and not
+               // defined by the current class, for some value of "current" that differs slightly
+               // between engines).
+               // Since PHP triggers an error on public access of non-public properties but happily
+               // allows public access to undefined properties, we need to detect this case as well.
+               // Reflection is slow so use array cast hack to check for that:
+               $obfuscatedProps = array_keys( (array)$this );
+               $obfuscatedPropTail = "\0$property";
+               foreach ( $obfuscatedProps as $obfuscatedProp ) {
+                       // private props are in the form \0<classname>\0<propname>
+                       if ( strpos( $obfuscatedProp, $obfuscatedPropTail, 1 ) !== false ) {
+                               $classname = substr( $obfuscatedProp, 1, -strlen( $obfuscatedPropTail ) );
+                               if ( $classname === '*' ) {
+                                       // sanity; this shouldn't be possible as protected properties were handled earlier
+                                       $classname = __CLASS__;
+                               }
+                               return $classname;
+                       }
+               }
+               return false;
+       }
+
+}
index 5c82f09..39e8bd9 100644 (file)
@@ -161,7 +161,7 @@ class LinksUpdate extends DataUpdate implements EnqueueableDataUpdate {
        /**
         * Update link tables with outgoing links from an updated article
         *
-        * @note: this is managed by DeferredUpdates::execute(). Do not run this in a transaction.
+        * @note this is managed by DeferredUpdates::execute(). Do not run this in a transaction.
         */
        public function doUpdate() {
                if ( $this->ticket ) {
index 1d9ad05..aa0a1f7 100644 (file)
@@ -28,6 +28,9 @@ use MediaWiki\Shell\Shell;
  * @ingroup DifferenceEngine
  */
 class DifferenceEngine extends ContextSource {
+
+       use DeprecationHelper;
+
        /**
         * Constant to indicate diff cache compatibility.
         * Bump this when changing the diff formatting in a way that
@@ -36,47 +39,54 @@ class DifferenceEngine extends ContextSource {
         */
        const DIFF_VERSION = '1.12';
 
-       /** @var int */
-       public $mOldid;
+       /** @var int Revision ID or 0 for current */
+       protected $mOldid;
 
-       /** @var int */
-       public $mNewid;
+       /** @var int|string Revision ID or null for current or an alias such as 'next' */
+       protected $mNewid;
 
        private $mOldTags;
        private $mNewTags;
 
-       /** @var Content */
-       public $mOldContent;
+       /** @var Content|null */
+       protected $mOldContent;
 
-       /** @var Content */
-       public $mNewContent;
+       /** @var Content|null */
+       protected $mNewContent;
 
        /** @var Language */
        protected $mDiffLang;
 
        /** @var Title */
-       public $mOldPage;
+       protected $mOldPage;
 
        /** @var Title */
-       public $mNewPage;
+       protected $mNewPage;
 
-       /** @var Revision */
+       /** @var Revision|null */
        public $mOldRev;
 
-       /** @var Revision */
+       /** @var Revision|null */
        public $mNewRev;
 
        /** @var bool Have the revisions IDs been loaded */
        private $mRevisionsIdsLoaded = false;
 
        /** @var bool Have the revisions been loaded */
-       public $mRevisionsLoaded = false;
+       protected $mRevisionsLoaded = false;
 
        /** @var int How many text blobs have been loaded, 0, 1 or 2? */
-       public $mTextLoaded = 0;
+       protected $mTextLoaded = 0;
+
+       /**
+        * Was the content overridden via setContent()?
+        * If the content was overridden, most internal state (e.g. mOldid or mOldRev) should be ignored.
+        * @var bool
+        */
+       protected $isContentOverridden = false;
 
        /** @var bool Was the diff fetched from cache? */
-       public $mCacheHit = false;
+       protected $mCacheHit = false;
 
        /**
         * Set this to true to add debug info to the HTML output.
@@ -112,6 +122,16 @@ class DifferenceEngine extends ContextSource {
        public function __construct( $context = null, $old = 0, $new = 0, $rcid = 0,
                $refreshCache = false, $unhide = false
        ) {
+               $this->deprecatePublicProperty( 'mOldid', '1.32', __CLASS__ );
+               $this->deprecatePublicProperty( 'mNewid', '1.32', __CLASS__ );
+               $this->deprecatePublicProperty( 'mOldPage', '1.32', __CLASS__ );
+               $this->deprecatePublicProperty( 'mNewPage', '1.32', __CLASS__ );
+               $this->deprecatePublicProperty( 'mOldContent', '1.32', __CLASS__ );
+               $this->deprecatePublicProperty( 'mNewContent', '1.32', __CLASS__ );
+               $this->deprecatePublicProperty( 'mRevisionsLoaded', '1.32', __CLASS__ );
+               $this->deprecatePublicProperty( 'mTextLoaded', '1.32', __CLASS__ );
+               $this->deprecatePublicProperty( 'mCacheHit', '1.32', __CLASS__ );
+
                if ( $context instanceof IContextSource ) {
                        $this->setContext( $context );
                }
@@ -946,7 +966,7 @@ class DifferenceEngine extends ContextSource {
                        wfDeprecated( "\$wgExternalDiffEngine = '{$wgExternalDiffEngine}'", '1.27' );
                        $wgExternalDiffEngine = false;
                } elseif ( $wgExternalDiffEngine == 'wikidiff2' ) {
-                       // Same as above, but with no deprecation warnings
+                       wfDeprecated( "\$wgExternalDiffEngine = '{$wgExternalDiffEngine}'", '1.32' );
                        $wgExternalDiffEngine = false;
                } elseif ( !is_string( $wgExternalDiffEngine ) && $wgExternalDiffEngine !== false ) {
                        // And prevent people from shooting themselves in the foot...
@@ -1341,6 +1361,7 @@ class DifferenceEngine extends ContextSource {
 
                $this->mTextLoaded = 2;
                $this->mRevisionsLoaded = true;
+               $this->isContentOverridden = true;
        }
 
        /**
@@ -1420,11 +1441,11 @@ class DifferenceEngine extends ContextSource {
         * to false. This is impossible via ordinary user input, and is provided for
         * API convenience.
         *
-        * @return bool
+        * @return bool Whether both revisions were loaded successfully.
         */
        public function loadRevisionData() {
                if ( $this->mRevisionsLoaded ) {
-                       return true;
+                       return $this->isContentOverridden || $this->mNewRev && $this->mOldRev;
                }
 
                // Whether it succeeds or fails, we don't want to try again
@@ -1500,11 +1521,11 @@ class DifferenceEngine extends ContextSource {
        /**
         * Load the text of the revisions, as well as revision data.
         *
-        * @return bool
+        * @return bool Whether the content of both revisions could be loaded successfully.
         */
        public function loadText() {
                if ( $this->mTextLoaded == 2 ) {
-                       return true;
+                       return $this->loadRevisionData() && $this->mOldContent && $this->mNewContent;
                }
 
                // Whether it succeeds or fails, we don't want to try again
@@ -1535,11 +1556,11 @@ class DifferenceEngine extends ContextSource {
        /**
         * Load the text of the new revision, not the old one
         *
-        * @return bool
+        * @return bool Whether the content of the new revision could be loaded successfully.
         */
        public function loadNewText() {
                if ( $this->mTextLoaded >= 1 ) {
-                       return true;
+                       return $this->loadRevisionData();
                }
 
                $this->mTextLoaded = 1;
index 88b28df..49cf71e 100644 (file)
@@ -19,7 +19,6 @@
  */
 
 use Wikimedia\Rdbms\DBConnectionError;
-use Wikimedia\Rdbms\DBError;
 use Wikimedia\Rdbms\DBReadOnlyError;
 use Wikimedia\Rdbms\DBExpectedError;
 
@@ -37,7 +36,7 @@ class MWExceptionRenderer {
         * @param Exception|Throwable|null $eNew New exception from attempting to show the first
         */
        public static function output( $e, $mode, $eNew = null ) {
-               global $wgMimeType;
+               global $wgMimeType, $wgShowExceptionDetails;
 
                if ( defined( 'MW_API' ) ) {
                        // Unhandled API exception, we can't be sure that format printer is alive
@@ -58,7 +57,7 @@ class MWExceptionRenderer {
                        self::header( "Content-Type: $wgMimeType; charset=utf-8" );
                        if ( $eNew ) {
                                $message = "MediaWiki internal error.\n\n";
-                               if ( self::showBackTrace( $e ) ) {
+                               if ( $wgShowExceptionDetails ) {
                                        $message .= 'Original exception: ' .
                                                MWExceptionHandler::getLogMessage( $e ) .
                                                "\nBacktrace:\n" . MWExceptionHandler::getRedactedTraceAsString( $e ) .
@@ -73,7 +72,7 @@ class MWExceptionRenderer {
                                }
                                $message .= "\n";
                        } else {
-                               if ( self::showBackTrace( $e ) ) {
+                               if ( $wgShowExceptionDetails ) {
                                        $message = MWExceptionHandler::getLogMessage( $e ) .
                                                "\nBacktrace:\n" .
                                                MWExceptionHandler::getRedactedTraceAsString( $e ) . "\n";
@@ -162,7 +161,9 @@ class MWExceptionRenderer {
         * @return string Html to output
         */
        public static function getHTML( $e ) {
-               if ( self::showBackTrace( $e ) ) {
+               global $wgShowExceptionDetails;
+
+               if ( $wgShowExceptionDetails ) {
                        $html = "<div class=\"errorbox mw-content-ltr\"><p>" .
                                nl2br( htmlspecialchars( MWExceptionHandler::getLogMessage( $e ) ) ) .
                                '</p><p>Backtrace:</p><p>' .
@@ -209,7 +210,9 @@ class MWExceptionRenderer {
         * @return string
         */
        private static function getText( $e ) {
-               if ( self::showBackTrace( $e ) ) {
+               global $wgShowExceptionDetails;
+
+               if ( $wgShowExceptionDetails ) {
                        return MWExceptionHandler::getLogMessage( $e ) .
                                "\nBacktrace:\n" .
                                MWExceptionHandler::getRedactedTraceAsString( $e ) . "\n";
@@ -218,34 +221,13 @@ class MWExceptionRenderer {
                }
        }
 
-       /**
-        * @param Exception|Throwable $e
-        * @return bool
-        */
-       private static function showBackTrace( $e ) {
-               global $wgShowExceptionDetails, $wgShowDBErrorBacktrace;
-
-               return (
-                       $wgShowExceptionDetails &&
-                       ( !( $e instanceof DBError ) || $wgShowDBErrorBacktrace )
-               );
-       }
-
        /**
         * @param Exception|Throwable $e
         * @return string
         */
        private static function getShowBacktraceError( $e ) {
-               global $wgShowExceptionDetails, $wgShowDBErrorBacktrace;
-               $vars = [];
-               if ( !$wgShowExceptionDetails ) {
-                       $vars[] = '$wgShowExceptionDetails = true;';
-               }
-               if ( $e instanceof DBError && !$wgShowDBErrorBacktrace ) {
-                       $vars[] = '$wgShowDBErrorBacktrace = true;';
-               }
-               $vars = implode( ' and ', $vars );
-               return "Set $vars at the bottom of LocalSettings.php to show detailed debugging information.";
+               $var = '$wgShowExceptionDetails = true;';
+               return "Set $var at the bottom of LocalSettings.php to show detailed debugging information.";
        }
 
        /**
@@ -294,7 +276,7 @@ class MWExceptionRenderer {
         * @param Exception|Throwable $e
         */
        private static function reportOutageHTML( $e ) {
-               global $wgShowDBErrorBacktrace, $wgShowHostnames, $wgShowSQLErrors, $wgSitename;
+               global $wgShowExceptionDetails, $wgShowHostnames, $wgSitename;
 
                $sorry = htmlspecialchars( self::msg(
                        'dberr-problems',
@@ -305,7 +287,7 @@ class MWExceptionRenderer {
                        'Try waiting a few minutes and reloading.'
                ) );
 
-               if ( $wgShowHostnames || $wgShowSQLErrors ) {
+               if ( $wgShowHostnames ) {
                        $info = str_replace(
                                '$1',
                                Html::element( 'span', [ 'dir' => 'ltr' ], $e->getMessage() ),
@@ -327,7 +309,7 @@ class MWExceptionRenderer {
                                '<style>body { font-family: sans-serif; margin: 0; padding: 0.5em 2em; }</style>' .
                                "</head><body><h1>$sorry</h1><p>$again</p><p><small>$info</small></p>";
 
-               if ( $wgShowDBErrorBacktrace ) {
+               if ( $wgShowExceptionDetails ) {
                        $html .= '<p>Backtrace:</p><pre>' .
                                htmlspecialchars( $e->getTraceAsString() ) . '</pre>';
                }
index dc6dbe8..503acdc 100644 (file)
@@ -123,9 +123,7 @@ class FileBackendGroup {
                        }
                        $class = $config['class'];
 
-                       $config['readOnly'] = !empty( $config['readOnly'] )
-                               ? $config['readOnly']
-                               : $readOnlyReason;
+                       $config['readOnly'] = $config['readOnly'] ?? $readOnlyReason;
 
                        unset( $config['class'] ); // backend won't need this
                        $this->backends[$name] = [
index 90c8707..f3fed57 100644 (file)
@@ -125,10 +125,12 @@ class RepoGroup {
                if ( isset( $options['bypassCache'] ) ) {
                        $options['latest'] = $options['bypassCache']; // b/c
                }
+               $options += [ 'time' => false ];
 
                if ( !$this->reposInitialised ) {
                        $this->initialiseRepos();
                }
+
                $title = File::normalizeTitle( $title );
                if ( !$title ) {
                        return false;
@@ -136,17 +138,16 @@ class RepoGroup {
 
                # Check the cache
                $dbkey = $title->getDBkey();
+               $timeKey = is_string( $options['time'] ) ? $options['time'] : '';
                if ( empty( $options['ignoreRedirect'] )
                        && empty( $options['private'] )
                        && empty( $options['latest'] )
                ) {
-                       $time = $options['time'] ?? '';
-                       if ( $this->cache->hasField( $dbkey, $time, 60 ) ) {
-                               return $this->cache->getField( $dbkey, $time );
+                       if ( $this->cache->hasField( $dbkey, $timeKey, 60 ) ) {
+                               return $this->cache->getField( $dbkey, $timeKey );
                        }
                        $useCache = true;
                } else {
-                       $time = false;
                        $useCache = false;
                }
 
@@ -166,7 +167,7 @@ class RepoGroup {
                $image = $image ?: false; // type sanity
                # Cache file existence or non-existence
                if ( $useCache && ( !$image || $image->isCacheable() ) ) {
-                       $this->cache->setField( $dbkey, $time, $image );
+                       $this->cache->setField( $dbkey, $timeKey, $image );
                }
 
                return $image;
index 7523b5f..aad9f6e 100644 (file)
@@ -116,10 +116,9 @@ class HTMLCheckField extends HTMLFormField {
        public function loadDataFromRequest( $request ) {
                $invert = isset( $this->mParams['invert'] ) && $this->mParams['invert'];
 
-               // GetCheck won't work like we want for checks.
                // Fetch the value in either one of the two following case:
-               // - we have a valid submit attempt (form was just submitted, or a GET URL forged by the user)
-               // - checkbox name has a value (false or true), ie is not null
+               // - we have a valid submit attempt (form was just submitted)
+               // - we have a value (an URL manually built by the user, or GET form with no wpFormIdentifier)
                if ( $this->isSubmitAttempt( $request ) || $request->getVal( $this->mName ) !== null ) {
                        return $invert
                                ? !$request->getBool( $this->mName )
index 4b1bc55..2038606 100644 (file)
@@ -214,10 +214,14 @@ class HTMLMultiSelectField extends HTMLFormField implements HTMLNestedFilterable
         * @return string|array
         */
        public function loadDataFromRequest( $request ) {
-               if ( $this->isSubmitAttempt( $request ) ) {
+               $fromRequest = $request->getArray( $this->mName, [] );
+               // Fetch the value in either one of the two following case:
+               // - we have a valid submit attempt (form was just submitted)
+               // - we have a value (an URL manually built by the user, or GET form with no wpFormIdentifier)
+               if ( $this->isSubmitAttempt( $request ) || $fromRequest ) {
                        // Checkboxes are just not added to the request arrays if they're not checked,
                        // so it's perfectly possible for there not to be an entry at all
-                       return $request->getArray( $this->mName, [] );
+                       return $fromRequest;
                } else {
                        // That's ok, the user has not yet submitted the form, so show the defaults
                        return $this->getDefault();
index a4d9bca..4e64e9d 100644 (file)
@@ -119,8 +119,14 @@ class HTMLSelectAndOtherField extends HTMLSelectField {
                        $dropdownInputAttribs['classes'] = [ $this->mClass ];
                }
 
+               $disabled = false;
+               if ( isset( $this->mParams[ 'disabled' ] ) && $this->mParams[ 'disabled' ] ) {
+                       $disabled = true;
+               }
+
                return $this->getInputWidget( [
                        'id' => $this->mID,
+                       'disabled' => $disabled,
                        'textinput' => $textAttribs,
                        'dropdowninput' => $dropdownInputAttribs,
                        'or' => false,
index 91050a0..47c1f18 100644 (file)
@@ -125,8 +125,14 @@ class HTMLSelectOrOtherField extends HTMLTextField {
                        $textAttribs['placeholder'] = $this->mPlaceholder;
                }
 
+               $disabled = false;
+               if ( isset( $this->mParams[ 'disabled' ] ) && $this->mParams[ 'disabled' ] ) {
+                       $disabled = true;
+               }
+
                return $this->getInputWidget( [
                        'id' => $this->mID,
+                       'disabled' => $disabled,
                        'textinput' => $textAttribs,
                        'dropdowninput' => $dropdownAttribs,
                        'or' => true,
index f457b21..bab8521 100644 (file)
@@ -98,6 +98,7 @@ class CurlHttpRequest extends MWHttpRequest {
                $curlHandle = curl_init( $this->url );
 
                if ( !curl_setopt_array( $curlHandle, $this->curlOptions ) ) {
+                       $this->status->fatal( 'http-internal-error' );
                        throw new InvalidArgumentException( "Error setting curl options." );
                }
 
index 71e08a8..257955c 100644 (file)
@@ -332,6 +332,7 @@ abstract class MWHttpRequest implements LoggerAwareInterface {
                if ( is_null( $callback ) ) {
                        $callback = [ $this, 'read' ];
                } elseif ( !is_callable( $callback ) ) {
+                       $this->status->fatal( 'http-internal-error' );
                        throw new InvalidArgumentException( __METHOD__ . ': invalid callback' );
                }
                $this->callback = $callback;
@@ -387,6 +388,11 @@ abstract class MWHttpRequest implements LoggerAwareInterface {
        protected function parseHeader() {
                $lastname = "";
 
+               // Failure without (valid) headers gets a response status of zero
+               if ( !$this->status->isOK() ) {
+                       $this->respStatus = '0';
+               }
+
                foreach ( $this->headerList as $header ) {
                        if ( preg_match( "#^HTTP/([0-9.]+) (.*)#", $header, $match ) ) {
                                $this->respVersion = $match[1];
index 3905ba0..367149d 100644 (file)
@@ -1606,7 +1606,7 @@ abstract class Installer {
        }
 
        /**
-        * Create the first user account, grant it sysop and bureaucrat rights
+        * Create the first user account, grant it sysop, bureaucrat and interface-admin rights
         *
         * @return Status
         */
@@ -1630,6 +1630,7 @@ abstract class Installer {
 
                        $user->addGroup( 'sysop' );
                        $user->addGroup( 'bureaucrat' );
+                       $user->addGroup( 'interface-admin' );
                        if ( $this->getVar( '_AdminEmail' ) ) {
                                $user->setEmail( $this->getVar( '_AdminEmail' ) );
                        }
@@ -1725,13 +1726,10 @@ abstract class Installer {
                $GLOBALS['wgLanguageConverterCacheType'] = CACHE_NONE;
                // Debug-friendly
                $GLOBALS['wgShowExceptionDetails'] = true;
+               $GLOBALS['wgShowHostnames'] = true;
                // Don't break forms
                $GLOBALS['wgExternalLinkTarget'] = '_blank';
 
-               // Extended debugging
-               $GLOBALS['wgShowSQLErrors'] = true;
-               $GLOBALS['wgShowDBErrorBacktrace'] = true;
-
                // Allow multiple ob_flush() calls
                $GLOBALS['wgDisableOutputCompression'] = true;
 
index 50c88ae..09fc0f3 100644 (file)
@@ -223,7 +223,7 @@ class WebInstallerName extends WebInstallerPage {
                        $status = $upp->checkUserPasswordForGroups(
                                $user,
                                $pwd,
-                               [ 'bureaucrat', 'sysop' ]  // per Installer::createSysop()
+                               [ 'bureaucrat', 'sysop', 'interface-admin' ]  // per Installer::createSysop()
                        );
                        $valid = $status->isGood() ? true : $status->getMessage();
                } else {
index 856b44b..dcccd22 100644 (file)
        "config-sqlite-dir-help": "يخزن SQLite جميع البيانات في ملف واحد. \n\nيجب أن يكون الدليل الذي توفره قابلا للكتابة بواسطة خادم الويب أثناء التثبيت. \n\nيجب أن يكون <strong>غير</strong> متاحا عبر الويب؛ هذا هو سبب عدم وضعه في مكان ملفات PHP الخاصة بك. \n\nسيقوم المثبت بكتابة ملف <code>.htaccess</code> معه، ولكن إذا فشل ذلك، يمكن لشخص ما الوصول إلى قاعدة بياناتك الأولية، \nيتضمن ذلك بيانات المستخدم الأولية (عناوين البريد الإلكتروني وكلمات المرور المجزأة) بالإضافة إلى المراجعات المحذوفة والبيانات المحظورة الأخرى على الويكي. \n\nفكر في وضع قاعدة البيانات في مكان آخر تماما، على سبيل المثال في <code>/var/lib/mediawiki/yourwiki</code.",
        "config-oracle-def-ts": "جدولية افتراضية:",
        "config-oracle-temp-ts": "جدولية مؤقتة:",
-       "config-type-mysql": "MySQL (أو متوافق)",
+       "config-type-mysql": "MariaDB أو MySQL أو متوافق",
        "config-type-postgres": "بوستجر إس كيو إل",
        "config-type-sqlite": "إس كيو لايت",
        "config-type-oracle": "أوراكل",
        "config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] هو نظام قاعدة بيانات خفيف مدعوم بشكل جيد. ([https://secure.php.net/manual/en/pdo.installation.php كيفية ترجمة PHP باستخدام دعم SQLite]، يستخدم PDO)",
        "config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] هي قاعدة بيانات مؤسسة تجارية. ([https://secure.php.net/manual/en/oci8.installation.php كيفية تجميع PHP مع دعم OCI8])",
        "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] هي قاعدة بيانات مؤسسة تجارية لويندوز. ([https://secure.php.net/manual/en/sqlsrv.installation.php كيفية تجميع PHP مع دعم SQLSRV])",
-       "config-header-mysql": "إعدادات MySQL",
+       "config-header-mysql": "إعدادات MariaDB/MySQL",
        "config-header-postgres": "إعدادات PostgreSQL",
        "config-header-sqlite": "إعدادات SQLite",
        "config-header-oracle": "إعدادات أوراكل",
        "config-db-web-create": "إنشئ حساب إذا لم يكن موجودا بالفعل",
        "config-db-web-no-create-privs": "الحساب الذي حددته لتركيب ليس لديه امتيازات كافية لإنشاء حساب.\nالحساب الذي حددته هنا موجود بالفعل.",
        "config-mysql-engine": "محرك التخزين",
-       "config-mysql-innodb": "إنو دي بي",
+       "config-mysql-innodb": "InnoDB (مستحسن)",
        "config-mysql-myisam": "ماي إسام",
        "config-mysql-myisam-dep": "<strong>تحذير:</strong> لقد اخترت MyISAM كمحرك تخزين لـMySQL، والذي لا يُنصَح باستخدامه مع ميدياويكي; لأنه:\n* بالكاد يدعم التزامن بسبب قفل الجدول \n* أكثر عرضة للفساد من المحركات الأخرى\n* لا يقوم الكود الأساسي لميدياويكي بمعالجة MyISAM دائما كما يجب\n\nإذا كان تثبيت MySQL يدعم InnoDB، فمن المستحسن جدا أن تختاره بدلا منه، \nإذا كان تثبيت MySQL لا يدعم InnoDB، فربما حان الوقت للترقية.",
        "config-mysql-only-myisam-dep": "<strong>تحذير:</strong> MyISAMهو محرك التخزين الوحيد المتاح لـMySQL على هذا الجهاز، ولا يُنصَح باستخدامه مع ميدياويكي; لأنه:\n* بالكاد يدعم التزامن بسبب قفل الجدول \n* أكثر عرضة للفساد من المحركات الأخرى\n* لا يقوم الكود الأساسي لميدياويكي بمعالجة MyISAM دائما كما يجب\n\nتثبيت MySQL لا يدعم InnoDB; ربما حان الوقت للترقية.",
index ee89d1f..9c24b34 100644 (file)
        "config-sqlite-dir-help": "SQLite almacena todos los datos en un único archivo.\n\nEl directorio que proporciones debe poder escribirse por el servidor web durante la instalación.\n\n'''No''' debería ser accesible a través de Internet. Por eso no vamos a ponerlo en el sitio donde están los archivos PHP.\n\nEl instalador escribirá un archivo <code>.htaccess</code> junto con él, pero si falla alguien podría tener acceso a la base de datos en bloque.\nEso incluye los datos de usuario en bloque (direcciones de correo electrónico, las contraseñas con hash) así como revisiones eliminadas y otros datos restringidos del wiki.\n\nConsidera poner la base de datos en algún otro sitio, por ejemplo en <code>/var/lib/mediawiki/tuwiki</code> .",
        "config-oracle-def-ts": "Espacio de tablas predeterminado:",
        "config-oracle-temp-ts": "Espacio de tablas temporal:",
-       "config-type-mysql": "MySQL (o compatible)",
+       "config-type-mysql": "MariaDB, MySQL o un sistema compatible",
        "config-type-postgres": "PostgreSQL",
        "config-type-sqlite": "SQLite",
        "config-type-oracle": "Oracle",
index a680353..6d1b894 100644 (file)
@@ -59,7 +59,7 @@
        "config-unicode-using-intl": "משתמש ב[https://pecl.php.net/intl הרחבת intl PECL] לנרמול יוניקוד.",
        "config-unicode-pure-php-warning": "<strong>אזהרה:</strong> [https://pecl.php.net/intl הרחבת intl PECL] אינה זמינה לטיפול בנרמול יוניקוד. משתמש ביישום PHP טהור ואטי יותר.\nאם זהו אתר בעל תעבורה גבוהה, כדאי לקרוא את המסמך הבא: [https://www.mediawiki.org/wiki/Special:MyLanguage/Unicode_normalization_considerations Unicode normalization].",
        "config-unicode-update-warning": "'''אזהרה''': הגרסה המותקנת של מעטפת נרמול יוניקוד משתמשת בגרסה ישנה של הספרייה של [http://site.icu-project.org/ פרויקט ICU].\nכדאי [https://www.mediawiki.org/wiki/Special:MyLanguage/Unicode_normalization_considerations לעדכן] אם הטיפול ביוניקוד חשוב לך.",
-       "config-no-db": "לא נמצא דרייבר מסד נתונים מתאים. יש להתקין דרייבר מסד נתונים ל־PHP.\n{{PLURAL:$2|נתמך הסוג הבא של מסד נתונים|נתמכים הסוגים הבאים של מסדי נתונים}}: $1.\n\nאם קִמפלת את PHP בעצמך, יש להגדיר אותו מחדש ולהפעיל את לקוח מסד נתונים, למשל באמצעות <code dir=\"ltr\">./configure --with-mysqli</code>.\nאם התקנת את PHP מחבילה של דביאן או של אובונטו, יש להתקין, למשל, גם את המודול <code dir=\"ltr\">php5-mysql</code>.",
+       "config-no-db": "לא נמצא דרייבר מסד נתונים מתאים. יש להתקין דרייבר מסד נתונים ל־PHP.\n{{PLURAL:$2|נתמך הסוג הבא של מסד נתונים|נתמכים הסוגים הבאים של מסדי נתונים}}: $1.\n\nאם קִמפלת את PHP בעצמך, יש להגדיר אותו מחדש ולהפעיל את לקוח מסד נתונים, למשל באמצעות <code dir=\"ltr\">./configure --with-mysqli</code>.\nאם התקנת את PHP מחבילה של דביאן או של אובונטו, יש להתקין, למשל, גם את המודול <code dir=\"ltr\">php-mysql</code>.",
        "config-outdated-sqlite": "'''אזהרה''': במערכת מתוקן SQLite $1. גרסה זו לא נתמכת ולשימוש ב־SQLite נדרשת גרסה $2 לפחות. SQLlite לא יהיה זמין.",
        "config-no-fts3": "'''אזהרה''': SQLite מהודר ללא [//sqlite.org/fts3.html מודול FTS]. יכולות חיפוש לא יהיו זמינות בהתקנה הזאת.",
        "config-pcre-old": "<strong>שגיאה סופנית:</strong> חובה להתקין PCRE מגרסה $1 או גרסה חדשה יותר.\nקובץ הרצת ה־PHP שלך מקושר עם PCRE מגרסה $2.\n[https://www.mediawiki.org/wiki/Manual:Errors_and_symptoms/PCRE מידע נוסף].",
index bea4607..b8db489 100644 (file)
        "config-sqlite-dir-help": "SQLite memorizza tutti i dati in un unico file.\n\nLa directory che indicherai deve essere scrivibile dal server web durante l'installazione.\n\nDovrebbe essere <strong>non accessibile via web</strong>, è per questo che non la stiamo mettendo dove ci sono i file PHP.\n\nL'installatore scriverà insieme ad essa un file <code>.htaccess</code>, ma se il tentativo fallisse qualcuno potrebbe avere accesso al database grezzo.\nQuesto include dati utente grezzi (indirizzi, password cifrate) così come versioni eliminate e altri dati ad accesso limitato del wiki.\n\nConsidera l'opportunità di sistemare allo stesso tempo il database da qualche altra parte, per esempio in <code>/var/lib/mediawiki/tuowiki</code>.",
        "config-oracle-def-ts": "Tablespace di default:",
        "config-oracle-temp-ts": "Tablespace temporaneo:",
-       "config-type-mysql": "MySQL (o compatibile)",
+       "config-type-mysql": "MariaDB, MySQL o compatibile",
        "config-type-mssql": "Microsoft SQL Server",
        "config-support-info": "MediaWiki supporta i seguenti sistemi di database:\n\n$1\n\nSe fra quelli elencati qui sotto non vedi il sistema di database che vorresti utilizzare, seguire le istruzioni linkate sopra per abilitare il supporto.",
-       "config-dbsupport-mysql": "* [{{int:version-db-mysql-url}} MySQL] è la configurazione preferibile per MediaWiki ed è quella meglio supportata. MediaWiki funziona anche con [{{int:version-db-mariadb-url}} MariaDB] e [{{int:version-db-percona-url}} Percona Server], che sono compatibili con MySQL.([https://secure.php.net/manual/en/mysqli.installation.php Come compilare PHP con supporto MySQL])",
+       "config-dbsupport-mysql": "* [{{int:version-db-mariadb-url}} MariaDB] è la configurazione preferibile per MediaWiki ed è quella meglio supportata. MediaWiki funziona anche con [{{int:version-db-mysql-url}} MySQL] e [{{int:version-db-percona-url}} Percona Server], che sono compatibili con MariaDB.([https://secure.php.net/manual/en/mysqli.installation.php Come compilare PHP con supporto MySQL])",
        "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] è un popolare sistema di database open source come alternativa a MySQL. ([https://secure.php.net/manual/en/pgsql.installation.php Come compilare PHP con supporto PostgreSQL])",
        "config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite]  è un sistema di database leggero, che è supportato molto bene. ([https://secure.php.net/manual/en/pdo.installation.php Come compilare PHP con supporto SQLite], utilizza PDO)",
        "config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] è un database di un'impresa commerciale. ([https://secure.php.net/manual/en/oci8.installation.php Come compilare PHP con supporto OCI8])",
index 671127e..1e88f7b 100644 (file)
        "config-sqlite-dir-help": "SQLite lagrer alle data i en enkelt fil.\n\nMappen du oppgir må være skrivbar for nettjeneren under installasjonen.\n\nDen bør '''ikke''' være tilgjengelig fra nettet, dette er grunnen til at vi ikke legger det der PHP-filene dine er.\n\nInstallasjonsprogrammet vil skrive en <code>.htaccess</code>-fil sammen med det, men om det mislykkes kan noen få tilgang til din råe database. Dette inkluderer rå brukerdata (e-postadresser, hashede passord) samt slettede revisjoner og andre begrensede data på wikien.\n\nVurder å plassere databasen et helt annet sted, for eksempel i <code>/var/lib/mediawiki/yourwiki</code>.",
        "config-oracle-def-ts": "Standard tabellrom:",
        "config-oracle-temp-ts": "Midlertidig tabellrom:",
-       "config-type-mysql": "MySQL (eller kompatibelt)",
+       "config-type-mysql": "MariaDB, MySQL eller kompatibel",
        "config-type-postgres": "PostgreSQL",
        "config-type-sqlite": "SQLite",
        "config-type-oracle": "Oracle",
        "config-type-mssql": "Microsoft SQLServer",
        "config-support-info": "MediaWiki støtter følgende databasesystem:\n\n$1\n\nHvis du ikke ser databasesystemet du prøver å bruke i listen nedenfor, følg instruksjonene det er lenket til over for å aktivere støtte.",
-       "config-dbsupport-mysql": "* [{{int:version-db-mysql-url}} MySQL] er den foretrukne databasetypen for MediaWiki og har best støtte.  MediaWiki fungerer også med [{{int:version-db-mariadb-url}} MariaDB] og [{{int:version-db-percona-url}} Percona Server], som begge er MySQL-kompatible. ([https://secure.php.net/manual/en/mysqli.installation.php Hvordan kompilere PHP med MySQL-støtte])",
+       "config-dbsupport-mysql": "* [{{int:version-db-mariadb-url}} MariaDB] er den foretrukne databasetypen for MediaWiki og har best støtte.  MediaWiki fungerer også med [{{int:version-db-mysql-url}} MySQL] og [{{int:version-db-percona-url}} Percona Server], som begge er MariaDB-kompatible. ([https://secure.php.net/manual/en/mysqli.installation.php Hvordan kompilere PHP med MySQL-støtte])",
        "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] er et populært åpen kildekode-databasesystem og et alternativ til MySQL.  ([https://secure.php.net/manual/en/pgsql.installation.php Hvordan kompilere PHP med PostgreSQL-støtte])",
        "config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] er et lettvekts-databasesystem som har veldig god støtte. ([http://www.php.net/manual/en/pdo.installation.php Hvordan kompilere PHP med SQLite-støtte], bruker PDO)",
        "config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] er en kommersiell database for bedrifter. ([https://secure.php.net/manual/en/oci8.installation.php Hvordan kompilere PHP med OCI8-støtte])",
        "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] er et kommersielt databasesystem under Windows for bedrifter. ([https://secure.php.net/manual/en/sqlsrv.installation.php Hvordan kompilere PHP med SQLSRV-støtte])",
-       "config-header-mysql": "MySQL-innstillinger",
+       "config-header-mysql": "MariadB/MySQL-innstillinger",
        "config-header-postgres": "PostgreSQL-innstillinger",
        "config-header-sqlite": "SQLite-innstillinger",
        "config-header-oracle": "Oracle-innstillinger",
        "config-db-web-create": "Opprett kontoen om den ikke finnes allerede",
        "config-db-web-no-create-privs": "Kontoen du oppga for installasjonen har ikke nok privilegier til å opprette en konto.\nKontoen du oppgir her må finnes allerede.",
        "config-mysql-engine": "Lagringsmotor:",
-       "config-mysql-innodb": "InnoDB",
+       "config-mysql-innodb": "InnoDB (anbefalt)",
        "config-mysql-myisam": "MyISAM",
        "config-mysql-myisam-dep": "'''Advarsel:''' Du har valgt MyISAM som lagringsmotor for MySQL, noe som ikke er anbefalt for bruk med MediaWiki, fordi:\n* den knapt støtter samtidighet pga. tabell-låsing\n* den har større tilbøyelighet for å bli korrupt enn andre motorer\n* MediaWiki-koden håndterer ikke alltid MyISAM som den burde\n\nHvis din MySQL-installasjon støtter InnoDB, er det sterkt å anbefale at du i stedet velger den.\nHvis din MySQL-installasjon ikke støtter InnoDB, kan det være på tide med en oppgradering.",
        "config-mysql-only-myisam-dep": "'''Advarsel:''' MyISAM er den eneste tilgjengelig lagringsmotoren for MySQL på denne maskinen, og det er ikke anbefalt brukt for MediaWiki, fordi:\n* den knapt støtter samtidighet pga. tabell-låsing\n* den har større tilbøyelighet for å bli korrupt enn andre motorer\n* MediaWiki-koden håndterer ikke alltid MyISAM som den burde\n\nHvis din MySQL-installasjon ikke støtter InnoDB, kan det være på tide med en oppgradering.",
        "config-license-help": "Mange åpne wikier legger alle bidrag under en [https://freedomdefined.org/Definition gratislisens].\nDette gir en følelse av felleseie og stimulerer til langvarige bidrag.\nDette er normalt unødvendig for en privat eller virksomhetsbegrenset wiki.\n\nHvis du ønsker å kunne bruke tekst fra Wikipedia, og at Wikipedia skal kunne ta i mot tekst kopiert fra din wiki, bør du velge <strong>{{int:config-license-cc-by-sa}}</strong>.\n\nWikipedia brukte tidligere GNU Free Documentation License.\nGFDL er en grei lisens, med vanskelig å forstå.\nDet er også vanskelig å gjenbruke innhold lisensiert under GFDL.",
        "config-email-settings": "E-postinnstillinger",
        "config-enable-email": "Aktiver utgående e-post",
-       "config-enable-email-help": "Hvis du vil at e-post skal virke, må [https://secure.php.net/manual/en/mail.configuration.php PHPs e-post-innstillinger] konfigureres riktig.\nHvis du ikke ønsker noen e-postfunksjoner, kan du deaktivere dem her.",
+       "config-enable-email-help": "Hvis du vil at e-post skal virke, må [https://secure.php.net/manual/en/mail.configuration.php PHPs epostinnstillinger] konfigureres riktig.\nHvis du ikke ønsker noen epostfunksjoner, kan du deaktivere dem her.",
        "config-email-user": "Aktiver e-post mellom brukere",
        "config-email-user-help": "Tillat alle brukere å sende hverandre e-post hvis de har aktivert det i deres innstillinger.",
        "config-email-usertalk": "Aktiver brukerdiskusjonssidevarsler",
index 807a627..ca4bcc2 100644 (file)
        "config-sqlite-dir-help": "SQLite slaat alle gegevens op in een enkel bestand.\n\nDe map die u opgeeft moet beschrijfbaar zijn voor de webserver tijdens de installatie.\n\nDeze mag '''niet toegankelijk''' zijn via het web en het bestand mag dus niet tussen de PHP-bestanden staan.\n\nHet installatieprogramma schrijft het bestand <code>.htaccess</code> weg met het databasebestand, maar als dat niet werkt kan iemand zich toegang tot het ruwe databasebestand verschaffen.\nOok de gebruikersgegevens (e-mailadressen, wachtwoordhashes) en verwijderde versies en overige gegevens met beperkte toegang via MediaWiki zijn dan onbeschermd.\n\nOverweeg om de database op een totaal andere plaats neer te zetten, bijvoorbeeld in <code>/var/lib/mediawiki/yourwiki</code>.",
        "config-oracle-def-ts": "Standaard tablespace:",
        "config-oracle-temp-ts": "Tijdelijke tablespace:",
-       "config-type-mysql": "MySQL (of compatibel)",
+       "config-type-mysql": "MariaDB, MySQL of compatibele systemen",
        "config-type-postgres": "PostgreSQL",
        "config-type-sqlite": "SQLite",
        "config-type-oracle": "Oracle",
        "config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] is een zeer goed ondersteund lichtgewicht databasesysteem ([https://secure.php.net/manual/en/pdo.installation.php hoe PHP gecompileerd moet zijn met ondersteuning voor SQLite]; gebruikt PDO).",
        "config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] is een commerciële database voor grote bedrijven ([https://secure.php.net/manual/en/oci8.installation.php PHP compileren met ondersteuning voor OCI8]).",
        "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] is een commerciële enterprisedatabase voor Windows ([https://secure.php.net/manual/en/sqlsrv.installation.php PHP compileren met ondersteuning voor SQLSRV]).",
-       "config-header-mysql": "MySQL-instellingen",
+       "config-header-mysql": "MariaDB/MySQL-instellingen",
        "config-header-postgres": "PostgreSQL-instellingen",
        "config-header-sqlite": "SQLite-instellingen",
        "config-header-oracle": "Oracle-instellingen",
        "config-db-web-create": "Maak de gebruiker aan als deze nog niet bestaat",
        "config-db-web-no-create-privs": "Het account dat u voor de installatie hebt opgegeven, heeft niet voldoende rechten om een account aan te maken.\nHet account dat u hier opgeeft, moet al bestaan.",
        "config-mysql-engine": "Opslagmethode:",
-       "config-mysql-innodb": "InnoDB",
+       "config-mysql-innodb": "InnoDB (aanbevolen)",
        "config-mysql-myisam": "MyISAM",
        "config-mysql-myisam-dep": "'''Waarschuwing''': u hebt MyISAM geselecteerd als opslagengine voor MySQL. Dit is niet aan te raden voor MediaWiki omdat:\n* het nauwelijks ondersteuning biedt voor gebruik door meerdere gebruikers tegelijkertijd door het locken van tabellen;\n* het meer vatbaar is voor corruptie dan andere engines;\n* de code van MediaWiki niet alstijd omgaat met MyISAM zoals dat zou moeten.\n\nAls uw installatie van MySQL InnoDB ondersteunt, gebruik dat dan vooral.\nAls uw installatie van MySQL geen ondersteuning heeft voor InnoDB, denk dan na over upgraden.",
        "config-mysql-only-myisam-dep": "'''Waarschuwing:''' MyISAM is enige beschikbare opslagmethode voor MySQL in deze omgeving, en deze wordt niet aangeraden voor gebruik met MediaWiki, omdat:\n* er nauwelijks ondersteuning is voor meerdere gelijktijdige transacties omdat tabellen op slot gezet worden;\n* tabellen makkelijker stuk kunnen gaan;\n* de code van MediaWiki niet altijd op de juiste wijze omgaat met MyISAM.\n\nUw installatie van MySQL heeft geen ondersteuning voor InnoDB. We raden u aan om een meer recente versie te gebruiken.",
index 7e83f56..ce43339 100644 (file)
@@ -4,7 +4,8 @@
                        "Dbc334",
                        "Eleassar",
                        "Yerpo",
-                       "HairyFotr"
+                       "HairyFotr",
+                       "Janezdrilc"
                ]
        },
        "config-desc": "Namestitveni program za MediaWiki",
@@ -76,9 +77,9 @@
        "config-db-schema-help": "Ta shema je po navadi v redu.\nSpremenite jo samo, če veste, da jo morate.",
        "config-pg-test-error": "Ne morem se povezati z zbirko podatkov <strong>$1</strong>: $2",
        "config-sqlite-dir": "Mapa podatkov SQLite:",
-       "config-type-mysql": "MySQL (ali združljiv)",
+       "config-type-mysql": "MySQL, MySQL ali združljiv",
        "config-support-info": "MediaWiki podpira naslednje sisteme zbirk podatkov:\n\n$1\n\nČe zgoraj ne vidite navedenega sistema zbirk podatkov, ki ga poskušate uporabiti, sledite navodilom na spodnji povezavi, da omogočite podporo.",
-       "config-header-mysql": "Nastavitve MySQL",
+       "config-header-mysql": "Nastavitve MariaDB/MySQL",
        "config-header-postgres": "Nastavitve PostgreSQL",
        "config-header-sqlite": "Nastavitve SQLite",
        "config-header-oracle": "Nastavitve Oracle",
        "config-db-web-account-same": "Uporabi enak račun kot za namestitev",
        "config-db-web-create": "Ustvari račun, če že ne obstaja",
        "config-mysql-engine": "Pogon skladiščenja:",
-       "config-mysql-innodb": "InnoDB",
+       "config-mysql-innodb": "InnoDB (priporočeno)",
        "config-mysql-myisam": "MyISAM",
        "config-mssql-auth": "Tip avtentikacije:",
        "config-site-name": "Ime wikija:",
index 633d8cb..93d44e4 100644 (file)
@@ -75,9 +75,9 @@
        "config-pcre-no-utf8": "<strong>Önemli hata:</strong> PHP'nin PCRE modülü PCRE_UTF8 desteği olmadan derlenmiş gözüküyor.\nMediaWiki'nin doğru çalışabilmesi için UTF-8 desteği gereklidir.",
        "config-memory-raised": "PHP'nin <code>memory_limit</code> (hafıza sınırı) değeri $1, $2'ye yükseltildi.",
        "config-memory-bad": "<strong>Uyarı:</strong> PHP'nin <code>memory_limit</code> (hafıza sınırı) değeri $1.\nBu büyük ihtimalle çok düşük.\nKurulum başarısız olabilir!",
-       "config-apc": "[https://secure.php.net/apc APC] kurulu",
-       "config-apcu": "[https://secure.php.net/apcu APCu] yüklendi",
-       "config-wincache": "[https://www.iis.net/downloads/microsoft/wincache-extension WinCache] kurulu",
+       "config-apc": "[https://secure.php.net/apc APC] kuruldu",
+       "config-apcu": "[https://secure.php.net/apcu APCu] kuruldu",
+       "config-wincache": "[https://www.iis.net/downloads/microsoft/wincache-extension WinCache] kuruldu",
        "config-no-cache-apcu": "<strong>Uyarı:</strong> [https://secure.php.net/apcu APCu] ya da [https://www.iis.net/downloads/microsoft/wincache-extension WinCache] kurulumu bulunamadı.\nNesne önbellekleme etkin değil.",
        "config-mod-security": "<strong>'''Uyarı:'''</strong> Web sunucunuz [https://modsecurity.org/mod_security2 mod_security] etkin. Bunun birçok yaygın yapılandırması bulunur ve eğer yanlış yapılandırılmış ise, bu MediaWiki ve kullanıcılara isteğe bağlı içerik göndermesine izin veren diğer yazılımlar için sorun oluşturabilir.\nMümkünse bu devre dışı bırakılmalıdır. Aksi takdirde rastgele hatalar alırsanız [https://modsecurity.org/documentation/ mod_security belgelemesine] bakın ya da sunucunuzun desteğine başvurun.",
        "config-diff3-bad": "GNU diff3 bulunamadı.",
@@ -90,6 +90,8 @@
        "config-no-cli-uri": "<strong>Uyarı:</strong> Herhangi bir <code>--scriptpath</code> belirlenmemiş, varsayılan kullanılıyor: <code>$1</code>.",
        "config-using-server": "Sunucu adı olarak \"<nowiki>$1</nowiki>\" kullanılıyor.",
        "config-using-uri": "Sunucu URLsi olarak \"<nowiki>$1$2</nowiki>\" kullanılıyor.",
+       "config-uploads-not-safe": "<strong>Uyarı:</strong> Yüklemeler için varsayılan dizininiz <code>$1</code>, rastgele komut dosyalarının yürütülmesine karşı savunmasızdır.\nMediaWiki, karşıya yüklenen tüm dosyaları güvenlik tehditlerine karşı denetlese de, yüklemeleri etkinleştirmeden önce [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Security#Upload_security bu güvenlik açığını kapatmanız] önemle tavsiye edilir.",
+       "config-no-cli-uploads-check": "<strong>Uyarı:</strong> Yüklemeler için varsayılan dizininiz (<code>$1</code>), CLI yüklemesi sırasında rastgele kod yürütme güvenlik açığı açısından denetlenmez.",
        "config-db-type": "Veritabanı tipi:",
        "config-db-host": "Veritabanı sunucusu:",
        "config-db-host-help": "Veritabanı sunucunuz farklı bir sunucu üzerinde ise, ana bilgisayar adını veya IP adresini buraya girin.\n\nPaylaşılan ağ barındırma hizmeti kullanıyorsanız, barındırma sağlayıcınız size doğru bir ana bilgisayar adını kendi belgelerinde vermiştir.\n\nEğer MySQL kullanan bir Windows sunucusuna yükleme yapıyorsanız, sunucu adı olarak \"localhost\" kullanırsanız çalışmayabilir. Çalışmazsa, yerel IP adresi için \"127.0.0.1\" deneyin.\n\nPostgreSQL kullanıyorsanız, bu alanı bir Unix soketi ile bağlanmak için boş bırakın.",
index 7b3462d..ca87d60 100644 (file)
        "config-sqlite-dir-help": "SQLite lưu tất cả các dữ liệu trong một tập tin duy nhất.\n\nThư mục mà bạn cung cấp phải cho phép máy chủ Web ghi vào khi cài đặt.\n\n<strong>Không</strong> nên làm cho nó truy cập được qua Web; đây là lý do chúng tôi không đặt nó vào cùng thư mục với các tập tin PHP của bạn.\n\nTrình cài đặt sẽ ghi một tập tin <code>.htaccess</code> đi kèm, nhưng nếu thất bại người nào đó có thể truy cập vào cơ sở dữ liệu thô của bạn.\nĐiều đó bao gồm dữ liệu người dùng thô (địa chỉ thư điện tử, mật khẩu được băm) cũng như các phiên bản bị xóa và dữ liệu bị hạn chế khác trên wiki.\n\nXem xét đặt cơ sở dữ liệu tại nơi nào khác hẳn, ví dụ trong <code>/var/lib/mediawiki/wiki_cua_ban</code>.",
        "config-oracle-def-ts": "Không gian bảng mặc định:",
        "config-oracle-temp-ts": "Không gian bảng tạm:",
-       "config-type-mysql": "MySQL (hoặc tương hợp)",
+       "config-type-mysql": "MariaDB, MySQL, hoặc tương hợp",
        "config-type-mssql": "Microsoft SQL Server",
        "config-support-info": "MediaWiki hỗ trợ các hệ thống cơ sở dữ liệu sau đây:\n\n$1\n\nNếu bạn không thấy hệ thống cơ sở dữ liệu mà bạn đang muốn sử dụng được liệt kê dưới đây, thì hãy theo chỉ dẫn được liên kết ở trên để kích hoạt tính năng hỗ trợ.",
-       "config-dbsupport-mysql": "* [{{int:version-db-mysql-url}} MySQL] là mục tiêu chính cho MediaWiki và được hỗ trợ tốt nhất. MediaWiki cũng làm việc với [{{int:version-db-mariadb-url}} MariaDB] và [{{int:version-db-percona-url}} Percona Server], là những cơ sở dữ liệu tương thích với MySQL. ([https://secure.php.net/manual/en/mysqli.installation.php Làm thế nào để biên dịch PHP với sự hỗ trợ của MySQL])",
+       "config-dbsupport-mysql": "* [{{int:version-db-mariadb-url}} MariaDB] là mục tiêu chính cho MediaWiki và được hỗ trợ tốt nhất. MediaWiki cũng làm việc với [{{int:version-db-mysql-url}} MySQL] và [{{int:version-db-percona-url}} Percona Server], là những cơ sở dữ liệu tương thích với MariaDB. ([https://secure.php.net/manual/en/mysqli.installation.php Làm thế nào để biên dịch PHP với sự hỗ trợ của MySQL])",
        "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] là một hệ thống cơ sở dữ liệu mã nguồn mở phổ biến như là một thay thế cho MySQL. ([https://secure.php.net/manual/en/pgsql.installation.php Làm thế nào để biên dịch PHP với sự hỗ trợ của PostgreSQL])",
        "config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] là một hệ thống cơ sở dữ liệu dung lượng nhẹ được hỗ trợ rất tốt. ([https://secure.php.net/manual/en/pdo.installation.php Làm thế nào để biên dịch PHP với sự hỗ trợ của SQLite], sử dụng PDO)",
        "config-dbsupport-oracle": "* [{{int:version-db-oracle-url}} Oracle] là một cơ sở dữ liệu doanh nghiệp thương mại. ([https://secure.php.net/manual/en/oci8.installation.php Làm thế nào để biên dịch PHP với sự hỗ trợ của OCI8])",
        "config-dbsupport-mssql": "* [{{int:version-db-mssql-url}} Microsoft SQL Server] là một cơ sở dữ liệu doanh nghiệp thương mại cho Windows. ([https://secure.php.net/manual/en/sqlsrv.installation.php Làm thế nào để biên dịch PHP với sự hỗ trợ của SQLSRV])",
-       "config-header-mysql": "Thiết lập MySQL",
+       "config-header-mysql": "Thiết lập MariaDB/MySQL",
        "config-header-postgres": "Thiết lập PostgreSQL",
        "config-header-sqlite": "Thiết lập SQLite",
        "config-header-oracle": "Thiết lập Oracle",
        "config-db-web-create": "Mở tài khoản nếu chưa tồn tại",
        "config-db-web-no-create-privs": "Tài khoản mà bạn xác định để cài đặt không có đủ quyền để tạo một tài khoản. Tài khoản mà bạn chỉ ra ở đây phải thực sự tồn tại trước đó.",
        "config-mysql-engine": "Máy lưu trữ:",
-       "config-mysql-innodb": "InnoDB",
+       "config-mysql-innodb": "InnoDB (khuyến khích)",
        "config-mysql-myisam": "MyISAM",
        "config-mysql-myisam-dep": "<strong>Cảnh báo:</strong> Bạn đã chọn MyISAM làm động cơ lưu trữ cho MySQL, điều này không được khuyến khích sử dụng với MediaWiki, bởi vì:\n* Nó ít hỗ trợ đồng thời do việc khóa bảng\n* Nó dễ bị lỗi hơn so với các động cơ khác\n* Kho mã nguồn của MediaWiki không phải khi nào cũng xử lý MyISAM như mong muốn\n\nNếu cài đặt MySQL của bạn hỗ trợ InnoDB, đặc biệt khuyến cáo bạn nên chọn để thay thế.\nNếu cài đặt MySQL của bạn không hỗ trợ InnoDB, có lẽ đã đến lúc để nâng cấp.",
        "config-mysql-only-myisam-dep": "<strong>Cảnh báo:</strong> MyISAM chỉ là công cụ lưu trữ có sẵn cho MySQL trên máy tính này, và điều này không được khuyến khích sử dụng với MediaWiki, bởi vì:\n* Nó ít hỗ trợ đồng thời do việc khóa khóa\n* Nó là dễ bị hư hỏng hơn các engine khác\n* Codebase MediaWiki không phải khi nào cũng xử lý MyISAM như mong muốn\n\nCài đặt MySQL của bạn không hỗ trợ InnoDB, có lẽ đã đến lúc để nâng cấp.",
index 37c8890..820c492 100644 (file)
@@ -111,7 +111,7 @@ class JobQueueGroup {
                        $conf = $conf + $wgJobTypeConf['default'];
                }
                $conf['aggregator'] = JobQueueAggregator::singleton();
-               if ( $this->readOnlyReason !== false ) {
+               if ( !isset( $conf['readOnlyReason'] ) ) {
                        $conf['readOnlyReason'] = $this->readOnlyReason;
                }
 
diff --git a/includes/jobqueue/JobQueueSecondTestQueue.php b/includes/jobqueue/JobQueueSecondTestQueue.php
deleted file mode 100644 (file)
index e63f01f..0000000
+++ /dev/null
@@ -1,290 +0,0 @@
-<?php
-
-/**
- * A wrapper for the JobQueue that delegates all the method calls to a single,
- * main queue, and also pushes all the jobs to a second job queue that's being
- * debugged.
- *
- * This class was temporary added to test transitioning to the JobQueueEventBus
- * and will removed after the transition is completed. This code is only needed
- * while we are testing the new infrastructure to be able to compare the results
- * between the queue implementations and make sure that they process the same jobs,
- * deduplicate correctly, compare the delays, backlogs and make sure no jobs are lost.
- * When the new infrastructure is well tested this will not be needed any more.
- *
- * @deprecated since 1.30
- * @since 1.30
- */
-class JobQueueSecondTestQueue extends JobQueue {
-
-       /**
-        * @var JobQueue
-        */
-       private $mainQueue;
-
-       /**
-        * @var JobQueue
-        */
-       private $debugQueue;
-
-       /**
-        * @var bool
-        */
-       private $onlyWriteToDebugQueue;
-
-       protected function __construct( array $params ) {
-               if ( !isset( $params['mainqueue'] ) ) {
-                       throw new MWException( "mainqueue parameter must be provided to the debug queue" );
-               }
-
-               if ( !isset( $params['debugqueue'] ) ) {
-                       throw new MWException( "debugqueue parameter must be provided to the debug queue" );
-               }
-
-               $conf = [ 'wiki' => $params['wiki'], 'type' => $params['type'] ];
-               $this->mainQueue = JobQueue::factory( $params['mainqueue'] + $conf );
-               $this->debugQueue = JobQueue::factory( $params['debugqueue'] + $conf );
-               $this->onlyWriteToDebugQueue = $params['readonly'] ?? false;
-
-               // We need to construct parent after creating the main and debug queue
-               // because super constructor calls some methods we delegate to the main queue.
-               parent::__construct( $params );
-       }
-
-       /**
-        * Get the allowed queue orders for configuration validation
-        *
-        * @return array Subset of (random, timestamp, fifo, undefined)
-        */
-       protected function supportedOrders() {
-               return $this->mainQueue->supportedOrders();
-       }
-
-       /**
-        * Get the default queue order to use if configuration does not specify one
-        *
-        * @return string One of (random, timestamp, fifo, undefined)
-        */
-       protected function optimalOrder() {
-               return $this->mainQueue->optimalOrder();
-       }
-
-       /**
-        * Find out if delayed jobs are supported for configuration validation
-        *
-        * @return bool Whether delayed jobs are supported
-        */
-       protected function supportsDelayedJobs() {
-               return $this->mainQueue->supportsDelayedJobs();
-       }
-
-       /**
-        * @see JobQueue::isEmpty()
-        * @return bool
-        */
-       protected function doIsEmpty() {
-               return $this->mainQueue->doIsEmpty();
-       }
-
-       /**
-        * @see JobQueue::getSize()
-        * @return int
-        */
-       protected function doGetSize() {
-               return $this->mainQueue->doGetSize();
-       }
-
-       /**
-        * @see JobQueue::getAcquiredCount()
-        * @return int
-        */
-       protected function doGetAcquiredCount() {
-               return $this->mainQueue->doGetAcquiredCount();
-       }
-
-       /**
-        * @see JobQueue::getDelayedCount()
-        * @return int
-        */
-       protected function doGetDelayedCount() {
-               return $this->mainQueue->doGetDelayedCount();
-       }
-
-       /**
-        * @see JobQueue::getAbandonedCount()
-        * @return int
-        */
-       protected function doGetAbandonedCount() {
-               return $this->mainQueue->doGetAbandonedCount();
-       }
-
-       /**
-        * @see JobQueue::batchPush()
-        * @param IJobSpecification[] $jobs
-        * @param int $flags
-        */
-       protected function doBatchPush( array $jobs, $flags ) {
-               if ( !$this->onlyWriteToDebugQueue ) {
-                       $this->mainQueue->doBatchPush( $jobs, $flags );
-               }
-
-               try {
-                       $this->debugQueue->doBatchPush( $jobs, $flags );
-               } catch ( Exception $exception ) {
-                       MWExceptionHandler::logException( $exception );
-               }
-       }
-
-       /**
-        * @see JobQueue::pop()
-        * @return Job|bool
-        */
-       protected function doPop() {
-               return $this->mainQueue->doPop();
-       }
-
-       /**
-        * @see JobQueue::ack()
-        * @param Job $job
-        * @return Job|bool
-        */
-       protected function doAck( Job $job ) {
-               return $this->mainQueue->doAck( $job );
-       }
-
-       /**
-        * @see JobQueue::deduplicateRootJob()
-        * @param IJobSpecification $job
-        * @throws MWException
-        * @return bool
-        */
-       protected function doDeduplicateRootJob( IJobSpecification $job ) {
-               return $this->mainQueue->doDeduplicateRootJob( $job );
-       }
-
-       /**
-        * @see JobQueue::isRootJobOldDuplicate()
-        * @param Job $job
-        * @return bool
-        */
-       protected function doIsRootJobOldDuplicate( Job $job ) {
-               return $this->mainQueue->doIsRootJobOldDuplicate( $job );
-       }
-
-       /**
-        * @param string $signature Hash identifier of the root job
-        * @return string
-        */
-       protected function getRootJobCacheKey( $signature ) {
-               return $this->mainQueue->getRootJobCacheKey( $signature );
-       }
-
-       /**
-        * @see JobQueue::delete()
-        * @return bool
-        * @throws MWException
-        */
-       protected function doDelete() {
-               return $this->mainQueue->doDelete();
-       }
-
-       /**
-        * @see JobQueue::waitForBackups()
-        * @return void
-        */
-       protected function doWaitForBackups() {
-               $this->mainQueue->doWaitForBackups();
-       }
-
-       /**
-        * @see JobQueue::flushCaches()
-        * @return void
-        */
-       protected function doFlushCaches() {
-               $this->mainQueue->doFlushCaches();
-       }
-
-       /**
-        * Get an iterator to traverse over all available jobs in this queue.
-        * This does not include jobs that are currently acquired or delayed.
-        * Note: results may be stale if the queue is concurrently modified.
-        *
-        * @return Iterator
-        * @throws JobQueueError
-        */
-       public function getAllQueuedJobs() {
-               return $this->mainQueue->getAllQueuedJobs();
-       }
-
-       /**
-        * Get an iterator to traverse over all delayed jobs in this queue.
-        * Note: results may be stale if the queue is concurrently modified.
-        *
-        * @return Iterator
-        * @throws JobQueueError
-        * @since 1.22
-        */
-       public function getAllDelayedJobs() {
-               return $this->mainQueue->getAllDelayedJobs();
-       }
-
-       /**
-        * Get an iterator to traverse over all claimed jobs in this queue
-        *
-        * Callers should be quick to iterator over it or few results
-        * will be returned due to jobs being acknowledged and deleted
-        *
-        * @return Iterator
-        * @throws JobQueueError
-        * @since 1.26
-        */
-       public function getAllAcquiredJobs() {
-               return $this->mainQueue->getAllAcquiredJobs();
-       }
-
-       /**
-        * Get an iterator to traverse over all abandoned jobs in this queue
-        *
-        * @return Iterator
-        * @throws JobQueueError
-        * @since 1.25
-        */
-       public function getAllAbandonedJobs() {
-               return $this->mainQueue->getAllAbandonedJobs();
-       }
-
-       /**
-        * Do not use this function outside of JobQueue/JobQueueGroup
-        *
-        * @return string
-        * @since 1.22
-        */
-       public function getCoalesceLocationInternal() {
-               return $this->mainQueue->getCoalesceLocationInternal();
-       }
-
-       /**
-        * @see JobQueue::getSiblingQueuesWithJobs()
-        * @param array $types List of queues types
-        * @return array|null (list of queue types) or null if unsupported
-        */
-       protected function doGetSiblingQueuesWithJobs( array $types ) {
-               return $this->mainQueue->doGetSiblingQueuesWithJobs( $types );
-       }
-
-       /**
-        * @see JobQueue::getSiblingQueuesSize()
-        * @param array $types List of queues types
-        * @return array|null (list of queue types) or null if unsupported
-        */
-       protected function doGetSiblingQueueSizes( array $types ) {
-               return $this->mainQueue->doGetSiblingQueueSizes( $types );
-       }
-
-       /**
-        * @throws JobQueueReadOnlyError
-        */
-       protected function assertNotReadOnly() {
-               $this->mainQueue->assertNotReadOnly();
-       }
-}
index 74e8b54..92a4f9e 100644 (file)
@@ -72,15 +72,24 @@ class CSSMin {
                                $url = $match['file'][0];
 
                                // Skip fully-qualified and protocol-relative URLs and data URIs
-                               // Also skips the rare `behavior` property specifying application's default behavior
                                if (
                                        substr( $url, 0, 2 ) === '//' ||
-                                       parse_url( $url, PHP_URL_SCHEME ) ||
-                                       substr( $url, 0, 9 ) === '#default#'
+                                       parse_url( $url, PHP_URL_SCHEME )
                                ) {
                                        break;
                                }
 
+                               // Strip trailing anchors - T115436
+                               $anchor = strpos( $url, '#' );
+                               if ( $anchor !== false ) {
+                                       $url = substr( $url, 0, $anchor );
+
+                                       // '#some-anchors' is not a file
+                                       if ( $url === '' ) {
+                                               break;
+                                       }
+                               }
+
                                $files[] = $path . $url;
                        }
                }
@@ -485,11 +494,11 @@ class CSSMin {
 
                // Pass thru fully-qualified and protocol-relative URLs and data URIs, as well as local URLs if
                // we can't expand them.
-               // Also skips the rare `behavior` property specifying application's default behavior
+               // Also skips anchors or the rare `behavior` property specifying application's default behavior
                if (
                        self::isRemoteUrl( $url ) ||
                        self::isLocalUrl( $url ) ||
-                       substr( $url, 0, 9 ) === '#default#'
+                       substr( $url, 0, 1 ) === '#'
                ) {
                        return $url;
                }
diff --git a/includes/libs/EasyDeflate.php b/includes/libs/EasyDeflate.php
new file mode 100644 (file)
index 0000000..6102746
--- /dev/null
@@ -0,0 +1,72 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+/**
+ * Server-side helper for the easy-deflate library
+ *
+ * @since 1.32
+ */
+class EasyDeflate {
+
+       /**
+        * Whether the content is deflated
+        *
+        * @param string $data
+        *
+        * @return bool
+        */
+       public static function isDeflated( $data ) {
+               return substr( $data, 0, 11 ) === 'rawdeflate,';
+       }
+
+       /**
+        * For content that has been compressed with deflate in the client,
+        * try to uncompress it with inflate.
+        *
+        * If data is not prefixed with 'rawdeflate,' it will be returned unmodified.
+        *
+        * Data can be compressed in the client using the 'easy-deflate.deflate'
+        * module:
+        *
+        * @code
+        *    mw.loader.using( 'easy-deflate.deflate' ).then( function () {
+        *        var deflated = EasyDeflate.deflate( myContent );
+        *    } );
+        * @endcode
+        *
+        * @param string $data Deflated data
+        * @return StatusValue Inflated data will be set as the value
+        * @throws InvalidArgumentException If the data wasn't deflated
+        */
+       public static function inflate( $data ) {
+               if ( !self::isDeflated( $data ) ) {
+                       throw new InvalidArgumentException( 'Data does not begin with deflated prefix' );
+               }
+               $deflated = base64_decode( substr( $data, 11 ), true );
+               if ( $deflated === false ) {
+                       return StatusValue::newFatal( 'easydeflate-invaliddeflate' );
+               }
+               Wikimedia\suppressWarnings();
+               $inflated = gzinflate( $deflated );
+               Wikimedia\restoreWarnings();
+               if ( $inflated === false ) {
+                       return StatusValue::newFatal( 'easydeflate-invaliddeflate' );
+               }
+               return StatusValue::newGood( $inflated );
+       }
+}
index 20ddf72..36f49e0 100644 (file)
 use Psr\Log\LoggerAwareInterface;
 use Psr\Log\LoggerInterface;
 use Psr\Log\NullLogger;
+use MediaWiki\MediaWikiServices;
 
 /**
- * Class to handle concurrent HTTP requests
+ * Class to handle multiple HTTP requests
+ *
+ * If curl is available, requests will be made concurrently.
+ * Otherwise, they will be made serially.
  *
  * HTTP request maps are arrays that use the following format:
  *   - method   : GET/HEAD/PUT/POST/DELETE
@@ -78,6 +82,8 @@ class MultiHttpClient implements LoggerAwareInterface {
         *   - usePipelining   : whether to use HTTP pipelining if possible (for all hosts)
         *   - maxConnsPerHost : maximum number of concurrent connections (per host)
         *   - userAgent       : The User-Agent header value to send
+        *   - logger          : a \Psr\Log\LoggerInterface instance for debug logging
+        *   - caBundlePath    : path to specific Certificate Authority bundle (if any)
         * @throws Exception
         */
        public function __construct( array $options ) {
@@ -105,11 +111,11 @@ class MultiHttpClient implements LoggerAwareInterface {
         * Execute an HTTP(S) request
         *
         * This method returns a response map of:
-        *   - code    : HTTP response code or 0 if there was a serious cURL error
-        *   - reason  : HTTP response reason (empty if there was a serious cURL error)
+        *   - code    : HTTP response code or 0 if there was a serious error
+        *   - reason  : HTTP response reason (empty if there was a serious error)
         *   - headers : <header name/value associative array>
         *   - body    : HTTP response body or resource (if "stream" was set)
-        *   - error     : Any cURL error string
+        *   - error     : Any error string
         * The map also stores integer-indexed copies of these values. This lets callers do:
         * @code
         *              list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $http->run( $req );
@@ -125,14 +131,17 @@ class MultiHttpClient implements LoggerAwareInterface {
        }
 
        /**
-        * Execute a set of HTTP(S) requests concurrently
+        * Execute a set of HTTP(S) requests.
+        *
+        * If curl is available, requests will be made concurrently.
+        * Otherwise, they will be made serially.
         *
         * The maps are returned by this method with the 'response' field set to a map of:
-        *   - code    : HTTP response code or 0 if there was a serious cURL error
-        *   - reason  : HTTP response reason (empty if there was a serious cURL error)
+        *   - code    : HTTP response code or 0 if there was a serious error
+        *   - reason  : HTTP response reason (empty if there was a serious error)
         *   - headers : <header name/value associative array>
         *   - body    : HTTP response body or resource (if "stream" was set)
-        *   - error   : Any cURL error string
+        *   - error   : Any error string
         * The map also stores integer-indexed copies of these values. This lets callers do:
         * @code
         *        list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $req['response'];
@@ -151,47 +160,45 @@ class MultiHttpClient implements LoggerAwareInterface {
         * @throws Exception
         */
        public function runMulti( array $reqs, array $opts = [] ) {
+               $this->normalizeRequests( $reqs );
+               if ( $this->isCurlEnabled() ) {
+                       return $this->runMultiCurl( $reqs, $opts );
+               } else {
+                       return $this->runMultiHttp( $reqs, $opts );
+               }
+       }
+
+       /**
+        * Determines if the curl extension is available
+        *
+        * @return bool true if curl is available, false otherwise.
+        */
+       protected function isCurlEnabled() {
+               return extension_loaded( 'curl' );
+       }
+
+       /**
+        * Execute a set of HTTP(S) requests concurrently
+        *
+        * @see MultiHttpClient::runMulti()
+        *
+        * @param array $reqs Map of HTTP request arrays
+        * @param array $opts
+        *   - connTimeout     : connection timeout per request (seconds)
+        *   - reqTimeout      : post-connection timeout per request (seconds)
+        *   - usePipelining   : whether to use HTTP pipelining if possible
+        *   - maxConnsPerHost : maximum number of concurrent connections (per host)
+        * @return array $reqs With response array populated for each
+        * @throws Exception
+        */
+       private function runMultiCurl( array $reqs, array $opts = [] ) {
                $chm = $this->getCurlMulti();
 
                $selectTimeout = $this->getSelectTimeout( $opts );
 
-               // Normalize $reqs and add all of the required cURL handles...
+               // Add all of the required cURL handles...
                $handles = [];
                foreach ( $reqs as $index => &$req ) {
-                       $req['response'] = [
-                               'code'     => 0,
-                               'reason'   => '',
-                               'headers'  => [],
-                               'body'     => '',
-                               'error'    => ''
-                       ];
-                       if ( isset( $req[0] ) ) {
-                               $req['method'] = $req[0]; // short-form
-                               unset( $req[0] );
-                       }
-                       if ( isset( $req[1] ) ) {
-                               $req['url'] = $req[1]; // short-form
-                               unset( $req[1] );
-                       }
-                       if ( !isset( $req['method'] ) ) {
-                               throw new Exception( "Request has no 'method' field set." );
-                       } elseif ( !isset( $req['url'] ) ) {
-                               throw new Exception( "Request has no 'url' field set." );
-                       }
-                       $this->logger->debug( "{$req['method']}: {$req['url']}" );
-                       $req['query'] = $req['query'] ?? [];
-                       $headers = []; // normalized headers
-                       if ( isset( $req['headers'] ) ) {
-                               foreach ( $req['headers'] as $name => $value ) {
-                                       $headers[strtolower( $name )] = $value;
-                               }
-                       }
-                       $req['headers'] = $headers;
-                       if ( !isset( $req['body'] ) ) {
-                               $req['body'] = '';
-                               $req['headers']['content-length'] = 0;
-                       }
-                       $req['flags'] = $req['flags'] ?? [];
                        $handles[$index] = $this->getCurlHandle( $req, $opts );
                        if ( count( $reqs ) > 1 ) {
                                // https://github.com/guzzle/guzzle/issues/349
@@ -391,7 +398,13 @@ class MultiHttpClient implements LoggerAwareInterface {
                                        return $length;
                                }
                                list( $name, $value ) = explode( ":", $header, 2 );
-                               $req['response']['headers'][strtolower( $name )] = trim( $value );
+                               $name = strtolower( $name );
+                               $value = trim( $value );
+                               if ( isset( $req['response']['headers'][$name] ) ) {
+                                       $req['response']['headers'][$name] .= ', ' . $value;
+                               } else {
+                                       $req['response']['headers'][$name] = $value;
+                               }
                                return $length;
                        }
                );
@@ -417,6 +430,148 @@ class MultiHttpClient implements LoggerAwareInterface {
                return $ch;
        }
 
+       /**
+        * @return resource
+        * @throws Exception
+        */
+       protected function getCurlMulti() {
+               if ( !$this->multiHandle ) {
+                       if ( !function_exists( 'curl_multi_init' ) ) {
+                               throw new Exception( "PHP cURL function curl_multi_init missing. " .
+                                                                        "Check https://www.mediawiki.org/wiki/Manual:CURL" );
+                       }
+                       $cmh = curl_multi_init();
+                       curl_multi_setopt( $cmh, CURLMOPT_PIPELINING, (int)$this->usePipelining );
+                       curl_multi_setopt( $cmh, CURLMOPT_MAXCONNECTS, (int)$this->maxConnsPerHost );
+                       $this->multiHandle = $cmh;
+               }
+               return $this->multiHandle;
+       }
+
+       /**
+        * Execute a set of HTTP(S) requests sequentially.
+        *
+        * @see MultiHttpClient::runMulti()
+        * @todo Remove dependency on MediaWikiServices: use a separate HTTP client
+        *  library or copy code from PhpHttpRequest
+        * @param array $reqs Map of HTTP request arrays
+        * @param array $opts
+        *   - connTimeout     : connection timeout per request (seconds)
+        *   - reqTimeout      : post-connection timeout per request (seconds)
+        * @return array $reqs With response array populated for each
+        * @throws Exception
+        */
+       private function runMultiHttp( array $reqs, array $opts = [] ) {
+               $httpOptions = [
+                       'timeout' => $opts['reqTimeout'] ?? $this->reqTimeout,
+                       'connectTimeout' => $opts['connTimeout'] ?? $this->connTimeout,
+                       'logger' => $this->logger,
+                       'caInfo' => $this->caBundlePath,
+               ];
+               foreach ( $reqs as &$req ) {
+                       $reqOptions = $httpOptions + [
+                               'method' => $req['method'],
+                               'proxy' => $req['proxy'] ?? $this->proxy,
+                               'userAgent' => $req['headers']['user-agent'] ?? $this->userAgent,
+                               'postData' => $req['body'],
+                       ];
+
+                       $url = $req['url'];
+                       $query = http_build_query( $req['query'], '', '&', PHP_QUERY_RFC3986 );
+                       if ( $query != '' ) {
+                               $url .= strpos( $req['url'], '?' ) === false ? "?$query" : "&$query";
+                       }
+
+                       $httpRequest = MediaWikiServices::getInstance()->getHttpRequestFactory()->create(
+                               $url, $reqOptions );
+                       $sv = $httpRequest->execute()->getStatusValue();
+
+                       $respHeaders = array_map(
+                               function ( $v ) {
+                                       return implode( ', ', $v );
+                               },
+                               $httpRequest->getResponseHeaders() );
+
+                       $req['response'] = [
+                               'code' => $httpRequest->getStatus(),
+                               'reason' => '',
+                               'headers' => $respHeaders,
+                               'body' => $httpRequest->getContent(),
+                               'error' => '',
+                       ];
+
+                       if ( !$sv->isOk() ) {
+                               $svErrors = $sv->getErrors();
+                               if ( isset( $svErrors[0] ) ) {
+                                       $req['response']['error'] = $svErrors[0]['message'];
+
+                                       // param values vary per failure type (ex. unknown host vs unknown page)
+                                       if ( isset( $svErrors[0]['params'][0] ) ) {
+                                               if ( is_numeric( $svErrors[0]['params'][0] ) ) {
+                                                       if ( isset( $svErrors[0]['params'][1] ) ) {
+                                                               $req['response']['reason'] = $svErrors[0]['params'][1];
+                                                       }
+                                               } else {
+                                                       $req['response']['reason'] = $svErrors[0]['params'][0];
+                                               }
+                                       }
+                               }
+                       }
+
+                       $req['response'][0] = $req['response']['code'];
+                       $req['response'][1] = $req['response']['reason'];
+                       $req['response'][2] = $req['response']['headers'];
+                       $req['response'][3] = $req['response']['body'];
+                       $req['response'][4] = $req['response']['error'];
+               }
+
+               return $reqs;
+       }
+
+       /**
+        * Normalize request information
+        *
+        * @param array $reqs the requests to normalize
+        */
+       private function normalizeRequests( array &$reqs ) {
+               foreach ( $reqs as &$req ) {
+                       $req['response'] = [
+                               'code'     => 0,
+                               'reason'   => '',
+                               'headers'  => [],
+                               'body'     => '',
+                               'error'    => ''
+                       ];
+                       if ( isset( $req[0] ) ) {
+                               $req['method'] = $req[0]; // short-form
+                               unset( $req[0] );
+                       }
+                       if ( isset( $req[1] ) ) {
+                               $req['url'] = $req[1]; // short-form
+                               unset( $req[1] );
+                       }
+                       if ( !isset( $req['method'] ) ) {
+                               throw new Exception( "Request has no 'method' field set." );
+                       } elseif ( !isset( $req['url'] ) ) {
+                               throw new Exception( "Request has no 'url' field set." );
+                       }
+                       $this->logger->debug( "{$req['method']}: {$req['url']}" );
+                       $req['query'] = $req['query'] ?? [];
+                       $headers = []; // normalized headers
+                       if ( isset( $req['headers'] ) ) {
+                               foreach ( $req['headers'] as $name => $value ) {
+                                       $headers[strtolower( $name )] = $value;
+                               }
+                       }
+                       $req['headers'] = $headers;
+                       if ( !isset( $req['body'] ) ) {
+                               $req['body'] = '';
+                               $req['headers']['content-length'] = 0;
+                       }
+                       $req['flags'] = $req['flags'] ?? [];
+               }
+       }
+
        /**
         * Get a suitable select timeout for the given options.
         *
@@ -439,24 +594,6 @@ class MultiHttpClient implements LoggerAwareInterface {
                return $selectTimeout;
        }
 
-       /**
-        * @return resource
-        * @throws Exception
-        */
-       protected function getCurlMulti() {
-               if ( !$this->multiHandle ) {
-                       if ( !function_exists( 'curl_multi_init' ) ) {
-                               throw new Exception( "PHP cURL extension missing. " .
-                                                                        "Check https://www.mediawiki.org/wiki/Manual:CURL" );
-                       }
-                       $cmh = curl_multi_init();
-                       curl_multi_setopt( $cmh, CURLMOPT_PIPELINING, (int)$this->usePipelining );
-                       curl_multi_setopt( $cmh, CURLMOPT_MAXCONNECTS, (int)$this->maxConnsPerHost );
-                       $this->multiHandle = $cmh;
-               }
-               return $this->multiHandle;
-       }
-
        /**
         * Register a logger
         *
index df22b41..93f5b0b 100644 (file)
@@ -23,7 +23,7 @@ class HttpAcceptParser {
         * Note that type parameters and accept extension like the "level" parameter
         * are not supported, weights are derived from "q" values only.
         *
-        * @todo: If additional type parameters are present, ignore them cleanly.
+        * @todo If additional type parameters are present, ignore them cleanly.
         *        At present, they often confuse the result.
         *
         * See HTTP/1.1 section 14 for details.
index aec9f25..5e8c22b 100644 (file)
@@ -94,7 +94,7 @@ abstract class DBLockManager extends QuorumLockManager {
        }
 
        /**
-        * @TODO change this code to work in one batch
+        * @todo change this code to work in one batch
         * @param string $lockSrv
         * @param array $pathsByType
         * @return StatusValue
index 782f4c6..82ae5ae 100644 (file)
@@ -133,7 +133,7 @@ abstract class BagOStuff implements IExpiringStore, LoggerAwareInterface {
 
        /**
         * @param LoggerInterface $logger
-        * @return null
+        * @return void
         */
        public function setLogger( LoggerInterface $logger ) {
                $this->logger = $logger;
@@ -238,7 +238,7 @@ abstract class BagOStuff implements IExpiringStore, LoggerAwareInterface {
        abstract protected function doGet( $key, $flags = 0 );
 
        /**
-        * @note: This method is only needed if merge() uses mergeViaCas()
+        * @note This method is only needed if merge() uses mergeViaCas()
         *
         * @param string $key
         * @param mixed &$casToken
@@ -308,6 +308,11 @@ abstract class BagOStuff implements IExpiringStore, LoggerAwareInterface {
                        $this->reportDupes = $reportDupes;
 
                        if ( $this->getLastError() ) {
+                               $this->logger->warning(
+                                       __METHOD__ . ' failed due to I/O error on get() for {key}.',
+                                       [ 'key' => $key ]
+                               );
+
                                return false; // don't spam retries (retry only on races)
                        }
 
@@ -325,6 +330,11 @@ abstract class BagOStuff implements IExpiringStore, LoggerAwareInterface {
                                $success = $this->cas( $casToken, $key, $value, $exptime );
                        }
                        if ( $this->getLastError() ) {
+                               $this->logger->warning(
+                                       __METHOD__ . ' failed due to I/O error for {key}.',
+                                       [ 'key' => $key ]
+                               );
+
                                return false; // IO error; don't spam retries
                        }
                } while ( !$success && --$attempts );
@@ -352,6 +362,11 @@ abstract class BagOStuff implements IExpiringStore, LoggerAwareInterface {
                if ( $casToken === $curCasToken ) {
                        $success = $this->set( $key, $value, $exptime );
                } else {
+                       $this->logger->info(
+                               __METHOD__ . ' failed due to race condition for {key}.',
+                               [ 'key' => $key ]
+                       );
+
                        $success = false; // mismatched or failed
                }
 
@@ -388,6 +403,11 @@ abstract class BagOStuff implements IExpiringStore, LoggerAwareInterface {
                $this->reportDupes = $reportDupes;
 
                if ( $this->getLastError() ) {
+                       $this->logger->warning(
+                               __METHOD__ . ' failed due to I/O error on get() for {key}.',
+                               [ 'key' => $key ]
+                       );
+
                        $success = false;
                } else {
                        // Derive the new value from the old value
@@ -443,13 +463,19 @@ abstract class BagOStuff implements IExpiringStore, LoggerAwareInterface {
                        }
                }
 
+               $fname = __METHOD__;
                $expiry = min( $expiry ?: INF, self::TTL_DAY );
                $loop = new WaitConditionLoop(
-                       function () use ( $key, $timeout, $expiry ) {
+                       function () use ( $key, $timeout, $expiry, $fname ) {
                                $this->clearLastError();
                                if ( $this->add( "{$key}:lock", 1, $expiry ) ) {
                                        return true; // locked!
                                } elseif ( $this->getLastError() ) {
+                                       $this->logger->warning(
+                                               $fname . ' failed due to I/O error for {key}.',
+                                               [ 'key' => $key ]
+                                       );
+
                                        return WaitConditionLoop::CONDITION_ABORTED; // network partition?
                                }
 
@@ -458,9 +484,15 @@ abstract class BagOStuff implements IExpiringStore, LoggerAwareInterface {
                        $timeout
                );
 
-               $locked = ( $loop->invoke() === $loop::CONDITION_REACHED );
+               $code = $loop->invoke();
+               $locked = ( $code === $loop::CONDITION_REACHED );
                if ( $locked ) {
                        $this->locks[$key] = [ 'class' => $rclass, 'depth' => 1 ];
+               } elseif ( $code === $loop::CONDITION_TIMED_OUT ) {
+                       $this->logger->warning(
+                               "$fname failed due to timeout for {key}.",
+                               [ 'key' => $key, 'timeout' => $timeout ]
+                       );
                }
 
                return $locked;
@@ -476,7 +508,15 @@ abstract class BagOStuff implements IExpiringStore, LoggerAwareInterface {
                if ( isset( $this->locks[$key] ) && --$this->locks[$key]['depth'] <= 0 ) {
                        unset( $this->locks[$key] );
 
-                       return $this->delete( "{$key}:lock" );
+                       $ok = $this->delete( "{$key}:lock" );
+                       if ( !$ok ) {
+                               $this->logger->warning(
+                                       __METHOD__ . ' failed to release lock for {key}.',
+                                       [ 'key' => $key ]
+                               );
+                       }
+
+                       return $ok;
                }
 
                return true;
@@ -511,7 +551,10 @@ abstract class BagOStuff implements IExpiringStore, LoggerAwareInterface {
                        $latency = 0.050; // latency skew (err towards keeping lock present)
                        $age = ( $this->getCurrentTime() - $lSince + $latency );
                        if ( ( $age + $latency ) >= $expiry ) {
-                               $this->logger->warning( "Lock for $key held too long ($age sec)." );
+                               $this->logger->warning(
+                                       "Lock for {key} held too long ({age} sec).",
+                                       [ 'key' => $key, 'age' => $age ]
+                               );
                                return; // expired; it's not "safe" to delete the key
                        }
                        $this->unlock( $key );
@@ -573,6 +616,7 @@ abstract class BagOStuff implements IExpiringStore, LoggerAwareInterface {
         * @return bool Success
         */
        public function add( $key, $value, $exptime = 0 ) {
+               // @note: avoid lock() here since that method uses *this* method by default
                if ( $this->get( $key ) === false ) {
                        return $this->set( $key, $value, $exptime );
                }
@@ -586,7 +630,7 @@ abstract class BagOStuff implements IExpiringStore, LoggerAwareInterface {
         * @return int|bool New value or false on failure
         */
        public function incr( $key, $value = 1 ) {
-               if ( !$this->lock( $key ) ) {
+               if ( !$this->lock( $key, 1 ) ) {
                        return false;
                }
                $n = $this->get( $key );
@@ -624,14 +668,15 @@ abstract class BagOStuff implements IExpiringStore, LoggerAwareInterface {
         * @since 1.24
         */
        public function incrWithInit( $key, $ttl, $value = 1, $init = 1 ) {
+               $this->clearLastError();
                $newValue = $this->incr( $key, $value );
-               if ( $newValue === false ) {
+               if ( $newValue === false && !$this->getLastError() ) {
                        // No key set; initialize
                        $newValue = $this->add( $key, (int)$init, $ttl ) ? $init : false;
-               }
-               if ( $newValue === false ) {
-                       // Raced out initializing; increment
-                       $newValue = $this->incr( $key, $value );
+                       if ( $newValue === false && !$this->getLastError() ) {
+                               // Raced out initializing; increment
+                               $newValue = $this->incr( $key, $value );
+                       }
                }
 
                return $newValue;
index dc3260d..4070a02 100644 (file)
@@ -287,7 +287,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
        const STATUS_TRX_NONE = 3;
 
        /**
-        * @note: exceptions for missing libraries/drivers should be thrown in initConnection()
+        * @note exceptions for missing libraries/drivers should be thrown in initConnection()
         * @param array $params Parameters passed from Database::factory()
         */
        protected function __construct( array $params ) {
index 1ac7d09..61788e1 100644 (file)
@@ -32,7 +32,7 @@ use stdClass;
 /**
  * Basic database interface for live and lazy-loaded relation database handles
  *
- * @note: IDatabase and DBConnRef should be updated to reflect any changes
+ * @note IDatabase and DBConnRef should be updated to reflect any changes
  * @ingroup Database
  */
 interface IDatabase {
@@ -1500,7 +1500,7 @@ interface IDatabase {
         *
         * This is useful for combining cooperative locks and DB transactions.
         *
-        * @note: do not assume that *other* IDatabase instances will be AUTOCOMMIT mode
+        * @note do not assume that *other* IDatabase instances will be AUTOCOMMIT mode
         *
         * The callback takes the following arguments:
         *   - How the transaction ended (IDatabase::TRIGGER_COMMIT or IDatabase::TRIGGER_ROLLBACK)
@@ -1535,7 +1535,7 @@ interface IDatabase {
         *
         * Updates will execute in the order they were enqueued.
         *
-        * @note: do not assume that *other* IDatabase instances will be AUTOCOMMIT mode
+        * @note do not assume that *other* IDatabase instances will be AUTOCOMMIT mode
         *
         * The callback takes the following arguments:
         *   - How the transaction ended (IDatabase::TRIGGER_COMMIT or IDatabase::TRIGGER_IDLE)
@@ -1623,7 +1623,7 @@ interface IDatabase {
         *   - The failures are from contention solvable via onTransactionPreCommitOrIdle()
         *   - The failures are deadlocks; the RDBMs usually discard the whole transaction
         *
-        * @note: callers must use additional measures for situations involving two or more
+        * @note callers must use additional measures for situations involving two or more
         *   (peer) transactions (e.g. updating two database servers at once). The transaction
         *   and savepoint logic of this method only applies to this specific IDatabase instance.
         *
@@ -1702,7 +1702,7 @@ interface IDatabase {
         * corresponding startAtomic() implicitly started a transaction, that
         * transaction is rolled back.
         *
-        * @note: callers must use additional measures for situations involving two or more
+        * @note callers must use additional measures for situations involving two or more
         *   (peer) transactions (e.g. updating two database servers at once). The transaction
         *   and savepoint logic of startAtomic() are bound to specific IDatabase instances.
         *
index c8d111d..d70688f 100644 (file)
@@ -72,7 +72,7 @@ class PageDataRequestHandler {
         *        - oldid|revision: the revision ID
         * @param OutputPage $output
         *
-        * @note: Instead of an output page, a WebResponse could be sufficient, but
+        * @note Instead of an output page, a WebResponse could be sufficient, but
         *        redirect logic is currently implemented in OutputPage.
         *
         * @throws HttpError
index 73aaa4f..559ac87 100644 (file)
@@ -110,8 +110,6 @@ class LogEventsList extends ContextSource {
        public function showOptions( $types = [], $user = '', $page = '', $pattern = false, $year = 0,
                $month = 0, $day = 0, $filter = null, $tagFilter = '', $action = null
        ) {
-               $title = SpecialPage::getTitleFor( 'Log' );
-
                // For B/C, we take strings, but make sure they are converted...
                $types = ( $types === '' ) ? [] : (array)$types;
 
@@ -120,7 +118,7 @@ class LogEventsList extends ContextSource {
                // Basic selectors
                $formDescriptor['type'] = $this->getTypeMenuDesc( $types );
                $formDescriptor['user'] = $this->getUserInputDesc( $user );
-               $formDescriptor['page'] = $this->getTitleInputDesc( $title );
+               $formDescriptor['page'] = $this->getTitleInputDesc( $page );
 
                // Add extra inputs if any
                // This could either be a form descriptor array or a string with raw HTML.
@@ -170,7 +168,9 @@ class LogEventsList extends ContextSource {
                        $formDescriptor['subtype'] = $this->getActionSelectorDesc( $types, $action );
                }
 
-               $htmlForm = new HTMLForm( $formDescriptor, $this->getContext() );
+               $context = new DerivativeContext( $this->getContext() );
+               $context->setTitle( SpecialPage::getTitleFor( 'Log' ) ); // Remove subpage
+               $htmlForm = new HTMLForm( $formDescriptor, $context );
                $htmlForm
                        ->setSubmitText( $this->msg( 'logeventslist-submit' )->text() )
                        ->setMethod( 'get' )
@@ -203,7 +203,7 @@ class LogEventsList extends ContextSource {
                        }
                        $options[ $message->text() ] = $type;
 
-                       if ( $val === 0 ) {
+                       if ( $val === false ) {
                                $default[] = $type;
                        }
                }
@@ -273,6 +273,7 @@ class LogEventsList extends ContextSource {
                        'class' => 'HTMLUserTextField',
                        'label-message' => 'specialloguserlabel',
                        'name' => 'user',
+                       'default' => $user,
                ];
        }
 
index 2efb462..e7096c4 100644 (file)
@@ -107,12 +107,17 @@ class LogPager extends ReverseChronologicalPager {
                        return $filters;
                }
 
-               $request_filters = $this->getRequest()->getArray( "wpfilters" );
-               $request_filters = $request_filters === null ? [] : $request_filters;
+               $wpfilters = $this->getRequest()->getArray( "wpfilters" );
+               $request_filters = $wpfilters === null ? [] : $wpfilters;
 
                foreach ( $wgFilterLogTypes as $type => $default ) {
                        $hide = !in_array( $type, $request_filters );
 
+                       // Back-compat: Check old URL params if the new param wasn't passed
+                       if ( $wpfilters === null ) {
+                               $hide = $this->getRequest()->getBool( "hide_{$type}_log", $default );
+                       }
+
                        $filters[$type] = $hide;
                        if ( $hide ) {
                                $this->mConds[] = 'log_type != ' . $this->mDb->addQuotes( $type );
index 9015a32..49912c7 100644 (file)
@@ -733,7 +733,7 @@ class Article implements Page {
                );
 
                // DifferenceEngine directly fetched the revision:
-               $this->mRevIdFetched = $de->mNewid;
+               $this->mRevIdFetched = $de->getNewid();
                $de->showDiffPage( $diffOnly );
 
                // Run view updates for the newer revision being diffed (and shown
index aa34dd2..dfc7c02 100644 (file)
@@ -767,7 +767,7 @@ class PageArchive {
                                        0,
                                        $this->title,
                                        [
-                                               'page' => $pageId,
+                                               'page_id' => $pageId,
                                                'deleted' => $unsuppress ? 0 : $row->ar_deleted
                                        ]
                                );
index c8e68b2..e4b064e 100644 (file)
@@ -666,7 +666,7 @@ class ParserOptions {
        /**
         * Get the user language used by the parser for this page and split the parser cache.
         *
-        * @warning: Calling this causes the parser cache to be fragmented by user language!
+        * @warning Calling this causes the parser cache to be fragmented by user language!
         * To avoid cache fragmentation, output should not depend on the user language.
         * Use Parser::getFunctionLang() or Parser::getTargetLanguage() instead!
         *
@@ -687,7 +687,7 @@ class ParserOptions {
        /**
         * Same as getUserLangObj() but returns a string instead.
         *
-        * @warning: Calling this causes the parser cache to be fragmented by user language!
+        * @warning Calling this causes the parser cache to be fragmented by user language!
         * To avoid cache fragmentation, output should not depend on the user language.
         * Use Parser::getFunctionLang() or Parser::getTargetLanguage() instead!
         *
index 7d59a02..9d6c1a5 100644 (file)
@@ -25,6 +25,15 @@ use Seld\JsonLint\JsonParser;
 use Seld\JsonLint\ParsingException;
 
 /**
+ * Validate extension.json files against their JSON schema.
+ *
+ * This is used for static validation from the command-line via
+ * validateRegistrationFile.php, and the PHPUnit structure test suite
+ * (ExtensionJsonValidationTest).
+ *
+ * The files are normally read by the ExtensionRegistry
+ * and ExtensionProcessor classes.
+ *
  * @since 1.29
  */
 class ExtensionJsonValidator {
index a803e3a..d0a9871 100644 (file)
@@ -162,6 +162,11 @@ class ExtensionProcessor implements Processor {
         */
        protected $credits = [];
 
+       /**
+        * @var array
+        */
+       protected $config = [];
+
        /**
         * Any thing else in the $info that hasn't
         * already been processed
@@ -290,6 +295,7 @@ class ExtensionProcessor implements Processor {
 
                return [
                        'globals' => $this->globals,
+                       'config' => $this->config,
                        'defines' => $this->defines,
                        'callbacks' => $this->callbacks,
                        'credits' => $this->credits,
@@ -493,6 +499,11 @@ class ExtensionProcessor implements Processor {
                                        $value = "$dir/$value";
                                }
                                $this->addConfigGlobal( "$prefix$key", $value, $info['name'] );
+                               $data['providedby'] = $info['name'];
+                               if ( isset( $info['ConfigRegistry'][0] ) ) {
+                                       $data['configregistry'] = array_keys( $info['ConfigRegistry'] )[0];
+                               }
+                               $this->config[$key] = $data;
                        }
                }
        }
index c58b55e..d21ae41 100644 (file)
@@ -36,7 +36,7 @@ class ExtensionRegistry {
        /**
         * Bump whenever the registration cache needs resetting
         */
-       const CACHE_VERSION = 6;
+       const CACHE_VERSION = 7;
 
        /**
         * Special key that defines the merge strategy
index 210deb1..636d3b3 100644 (file)
@@ -25,6 +25,7 @@ interface Processor {
         * @return array With following keys:
         *     'globals' - variables to be set to $GLOBALS
         *     'defines' - constants to define
+        *     'config' - configuration information
         *     'callbacks' - functions to be executed by the registry
         *     'credits' - metadata to be stored by registry
         *     'attributes' - registration info which isn't a global variable
index f718e5f..4b24081 100644 (file)
@@ -46,7 +46,6 @@ class ResourceLoaderLanguageDataModule extends ResourceLoaderFileModule {
                        'pluralRules' => $language->getPluralRules(),
                        'digitGroupingPattern' => $language->digitGroupingPattern(),
                        'fallbackLanguages' => $language->getFallbackLanguages(),
-                       'bcp47Map' => LanguageCode::getNonstandardLanguageCodeMapping(),
                ];
        }
 
index 54d9e9c..b2403ce 100644 (file)
@@ -45,7 +45,7 @@ class DBSiteStore implements SiteStore {
        /**
         * @since 1.27
         *
-        * @todo: inject some kind of connection manager that is aware of the target wiki,
+        * @todo inject some kind of connection manager that is aware of the target wiki,
         * instead of injecting a LoadBalancer.
         *
         * @param LoadBalancer $dbLoadBalancer
index b6de510..ff2a9ea 100644 (file)
@@ -401,15 +401,14 @@ abstract class Skin extends ContextSource {
         * @param string|null $nonce OutputPage::getCSPNonce()
         * @return string|WrappedString HTML
         */
-       static function makeVariablesScript( $data, $nonce = null ) {
+       public static function makeVariablesScript( $data, $nonce = null ) {
                if ( $data ) {
                        return ResourceLoader::makeInlineScript(
                                ResourceLoader::makeConfigSetScript( $data ),
                                $nonce
                        );
-               } else {
-                       return '';
                }
+               return '';
        }
 
        /**
index 92c6f50..3b25c6c 100644 (file)
@@ -854,9 +854,11 @@ class SpecialBlock extends FormSpecialPage {
         *     to the standard "**<duration>|<displayname>" format?
         * @param Language|null $lang The language to get the durations in, or null to use
         *     the wiki's content language
+        * @param bool $includeOther Whether to include the 'other' option in the list of
+        *     suggestions
         * @return array
         */
-       public static function getSuggestedDurations( $lang = null ) {
+       public static function getSuggestedDurations( $lang = null, $includeOther = true ) {
                $a = [];
                $msg = $lang === null
                        ? wfMessage( 'ipboptions' )->inContentLanguage()->text()
@@ -875,7 +877,7 @@ class SpecialBlock extends FormSpecialPage {
                        $a[$show] = $value;
                }
 
-               if ( $a ) {
+               if ( $a && $includeOther ) {
                        // if options exist, add other to the end instead of the begining (which
                        // is what happens by default).
                        $a[ wfMessage( 'ipbother' )->text() ] = 'other';
index cf0ca48..83e18c2 100644 (file)
@@ -121,7 +121,7 @@ class FileDuplicateSearchPage extends QueryPage {
                                'label-message' => 'fileduplicatesearch-filename',
                                'id' => 'filename',
                                'size' => 50,
-                               'value' => $this->filename,
+                               'default' => $this->filename,
                        ],
                ];
                $hiddenFields = [
index 2ba01ff..8ebd2d2 100644 (file)
@@ -147,6 +147,9 @@ class User implements IDBAccessObject, UserIdentity {
                'editmyuserjs',
                'editmywatchlist',
                'editsemiprotected',
+               'editsitecss',
+               'editsitejson',
+               'editsitejs',
                'editusercss',
                'edituserjson',
                'edituserjs',
index c23d999..d184e92 100644 (file)
@@ -84,7 +84,7 @@ class UIDGenerator {
        }
 
        /**
-        * @todo: move to MW-specific factory class and inject temp dir
+        * @todo move to MW-specific factory class and inject temp dir
         * @return UIDGenerator
         */
        protected static function singleton() {
index 5ceed4c..262903d 100644 (file)
@@ -36,6 +36,11 @@ class SelectWithInputWidget extends \OOUI\Widget {
                        $config
                );
 
+               if ( isset( $config['disabled'] ) && $config['disabled'] == true ) {
+                       $config['textinput']['disabled'] = true;
+                       $config['dropdowninput']['disabled'] = true;
+               }
+
                parent::__construct( $config );
 
                // Properties
index 6d98920..22377c2 100644 (file)
@@ -116,10 +116,6 @@ class FakeConverter {
        }
 
        function validateVariant( $variant = null ) {
-               if ( $variant === null ) {
-                       return null;
-               }
-               $variant = strtolower( $variant );
                return $variant === $this->mLang->getCode() ? $variant : null;
        }
 
index 9792095..7f04a68 100644 (file)
@@ -3413,32 +3413,26 @@ class Language {
         * Take a list of strings and build a locale-friendly comma-separated
         * list, using the local comma-separator message.
         * The last two strings are chained with an "and".
-        * NOTE: This function will only work with standard numeric array keys (0, 1, 2…)
         *
-        * @param string[] $l
+        * @param string[] $list
         * @return string
         */
-       function listToText( array $l ) {
-               $m = count( $l ) - 1;
-               if ( $m < 0 ) {
+       public function listToText( array $list ) {
+               $itemCount = count( $list );
+               if ( $itemCount < 1 ) {
                        return '';
                }
-               if ( $m > 0 ) {
+               $text = array_pop( $list );
+               if ( $itemCount > 1 ) {
                        $and = $this->msg( 'and' )->escaped();
                        $space = $this->msg( 'word-separator' )->escaped();
-                       if ( $m > 1 ) {
+                       $comma = '';
+                       if ( $itemCount > 2 ) {
                                $comma = $this->msg( 'comma-separator' )->escaped();
                        }
+                       $text = implode( $comma, $list ) . $and . $space . $text;
                }
-               $s = $l[$m];
-               for ( $i = $m - 1; $i >= 0; $i-- ) {
-                       if ( $i == $m - 1 ) {
-                               $s = $l[$i] . $and . $space . $s;
-                       } else {
-                               $s = $l[$i] . $comma . $s;
-                       }
-               }
-               return $s;
+               return $text;
        }
 
        /**
index 54d7fbe..f50c55f 100644 (file)
@@ -30,85 +30,22 @@ class LanguageCode {
        /**
         * Mapping of deprecated language codes that were used in previous
         * versions of MediaWiki to up-to-date, current language codes.
-        * These may or may not be valid BCP 47 codes; they are included here
-        * because MediaWiki remapped these particular codes at some point.
         *
         * @var array Mapping from language code to language code
         *
         * @since 1.30
-        * @see https://meta.wikimedia.org/wiki/Special_language_codes
         */
        private static $deprecatedLanguageCodeMapping = [
                // Note that als is actually a valid ISO 639 code (Tosk Albanian), but it
                // was previously used in MediaWiki for Alsatian, which comes under gsw
-               'als' => 'gsw', // T25215
-               'bat-smg' => 'sgs', // T27522
-               'be-x-old' => 'be-tarask', // T11823
-               'fiu-vro' => 'vro', // T31186
-               'roa-rup' => 'rup', // T17988
-               'zh-classical' => 'lzh', // T30443
-               'zh-min-nan' => 'nan', // T30442
-               'zh-yue' => 'yue', // T30441
-       ];
-
-       /**
-        * Mapping of non-standard language codes used in MediaWiki to
-        * standardized BCP 47 codes.  These are not deprecated (yet?):
-        * IANA may eventually recognize the subtag, in which case the `-x-`
-        * infix could be removed, or else we could rename the code in
-        * MediaWiki, in which case they'd move up to the above mapping
-        * of deprecated codes.
-        *
-        * As a rule, we preserve all distinctions made by MediaWiki
-        * internally.  For example, `de-formal` becomes `de-x-formal`
-        * instead of just `de` because MediaWiki distinguishes `de-formal`
-        * from `de` (for example, for interface translations).  Similarly,
-        * BCP 47 indicates that `kk-Cyrl` SHOULD not be used because it
-        * "typically does not add information", but in our case MediaWiki
-        * LanguageConverter distinguishes `kk` (render content in a mix of
-        * Kurdish variants) from `kk-Cyrl` (convert content to be uniformly
-        * Cyrillic).  As the BCP 47 requirement is a SHOULD not a MUST,
-        * `kk-Cyrl` is a valid code, although some validators may emit
-        * a warning note.
-        *
-        * @var array Mapping from nonstandard codes to BCP 47 codes
-        *
-        * @since 1.32
-        * @see https://meta.wikimedia.org/wiki/Special_language_codes
-        * @see https://phabricator.wikimedia.org/T125073
-        */
-       private static $nonstandardLanguageCodeMapping = [
-               // All codes returned by Language::fetchLanguageNames() validated
-               // against IANA registry at
-               //   https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry
-               // with help of validator at
-               //   http://schneegans.de/lv/
-               'cbk-zam' => 'cbk', // T124657
-               'de-formal' => 'de-x-formal',
-               'eml' => 'egl', // T36217
-               'en-rtl' => 'en-x-rtl',
-               'es-formal' => 'es-x-formal',
-               'hu-formal' => 'hu-x-formal',
-               'map-bms' => 'jv-x-bms', // [[en:Banyumasan_dialect]] T125073
-               'mo' => 'ro-MD', // T125073
-               'nrm' => 'nrf', // [[en:Norman_language]] T25216
-               'nl-informal' => 'nl-x-informal',
-               'roa-tara' => 'nap-x-tara', // [[en:Tarantino_dialect]]
-               'simple' => 'en-simple',
-               'sr-ec' => 'sr-Cyrl', // T117845
-               'sr-el' => 'sr-Latn', // T117845
-
-               // Although these next codes aren't *wrong* per se, including
-               // both the script and the country code helps compatibility with
-               // other BCP 47 users. Note that MW also uses `zh-Hans`/`zh-Hant`,
-               // without a country code, and those should be left alone.
-               // (See $variantfallbacks in LanguageZh.php for Hans/Hant id.)
-               'zh-cn' => 'zh-Hans-CN',
-               'zh-sg' => 'zh-Hans-SG',
-               'zh-my' => 'zh-Hans-MY',
-               'zh-tw' => 'zh-Hant-TW',
-               'zh-hk' => 'zh-Hant-HK',
-               'zh-mo' => 'zh-Hant-MO',
+               'als' => 'gsw',
+               'bat-smg' => 'sgs',
+               'be-x-old' => 'be-tarask',
+               'fiu-vro' => 'vro',
+               'roa-rup' => 'rup',
+               'zh-classical' => 'lzh',
+               'zh-min-nan' => 'nan',
+               'zh-yue' => 'yue',
        ];
 
        /**
@@ -127,29 +64,6 @@ class LanguageCode {
                return self::$deprecatedLanguageCodeMapping;
        }
 
-       /**
-        * Returns a mapping of non-standard language codes used by
-        * (current and previous version of) MediaWiki, mapped to standard
-        * BCP 47 names.
-        *
-        * This array is exported to JavaScript to ensure
-        * mediawiki.language.bcp47 stays in sync with LanguageCode::bcp47().
-        *
-        * @return string[]
-        *
-        * @since 1.32
-        */
-       public static function getNonstandardLanguageCodeMapping() {
-               $result = [];
-               foreach ( self::$deprecatedLanguageCodeMapping as $code => $ignore ) {
-                       $result[$code] = self::bcp47( $code );
-               }
-               foreach ( self::$nonstandardLanguageCodeMapping as $code => $ignore ) {
-                       $result[$code] = self::bcp47( $code );
-               }
-               return $result;
-       }
-
        /**
         * Replace deprecated language codes that were used in previous
         * versions of MediaWiki to up-to-date, current language codes.
@@ -173,15 +87,11 @@ class LanguageCode {
         * See mediawiki.language.bcp47 for the JavaScript implementation.
         *
         * @param string $code The language code.
-        * @return string A language code complying with BCP 47 standards.
+        * @return string The language code which complying with BCP 47 standards.
         *
         * @since 1.31
         */
        public static function bcp47( $code ) {
-               $code = self::replaceDeprecatedCodes( strtolower( $code ) );
-               if ( isset( self::$nonstandardLanguageCodeMapping[$code] ) ) {
-                       $code = self::$nonstandardLanguageCodeMapping[$code];
-               }
                $codeSegment = explode( '-', $code );
                $codeBCP = [];
                foreach ( $codeSegment as $segNo => $seg ) {
index 494280c..dcc2cf3 100644 (file)
@@ -175,13 +175,11 @@ class LanguageConverter {
                        $req = $this->validateVariant( $wgDefaultLanguageVariant );
                }
 
-               $req = $this->validateVariant( $req );
-
                // This function, unlike the other get*Variant functions, is
                // not memoized (i.e. there return value is not cached) since
                // new information might appear during processing after this
                // is first called.
-               if ( $req ) {
+               if ( $this->validateVariant( $req ) ) {
                        return $req;
                }
                return $this->mMainLanguageCode;
@@ -217,25 +215,9 @@ class LanguageConverter {
         * @return mixed Returns the variant if it is valid, null otherwise
         */
        public function validateVariant( $variant = null ) {
-               if ( $variant === null ) {
-                       return null;
-               }
-               // Our internal variants are always lower-case; the variant we
-               // are validating may have mixed case.
-               $variant = LanguageCode::replaceDeprecatedCodes( strtolower( $variant ) );
-               if ( in_array( $variant, $this->mVariants ) ) {
+               if ( $variant !== null && in_array( $variant, $this->mVariants ) ) {
                        return $variant;
                }
-               // Browsers are supposed to use BCP 47 standard in the
-               // Accept-Language header, but not all of our internal
-               // mediawiki variant codes are BCP 47.  Map BCP 47 code
-               // to our internal code.
-               foreach ( $this->mVariants as $v ) {
-                       // Case-insensitive match (BCP 47 is mixed case)
-                       if ( strtolower( LanguageCode::bcp47( $v ) ) === $variant ) {
-                               return $v;
-                       }
-               }
                return null;
        }
 
@@ -311,7 +293,7 @@ class LanguageConverter {
                        return $this->mHeaderVariant;
                }
 
-               // See if some supported language variant is set in the
+               // see if some supported language variant is set in the
                // HTTP header.
                $languages = array_keys( $wgRequest->getAcceptLang() );
                if ( empty( $languages ) ) {
@@ -563,18 +545,17 @@ class LanguageConverter {
                $convTable = $convRule->getConvTable();
                $action = $convRule->getRulesAction();
                foreach ( $convTable as $variant => $pair ) {
-                       $v = $this->validateVariant( $variant );
-                       if ( !$v ) {
+                       if ( !$this->validateVariant( $variant ) ) {
                                continue;
                        }
 
                        if ( $action == 'add' ) {
                                // More efficient than array_merge(), about 2.5 times.
                                foreach ( $pair as $from => $to ) {
-                                       $this->mTables[$v]->setPair( $from, $to );
+                                       $this->mTables[$variant]->setPair( $from, $to );
                                }
                        } elseif ( $action == 'remove' ) {
-                               $this->mTables[$v]->removeArray( $pair );
+                               $this->mTables[$variant]->removeArray( $pair );
                        }
                }
        }
index 67da06b..277bd02 100644 (file)
@@ -82,7 +82,7 @@ class Names {
                'ba' => 'башҡортса', # Bashkir
                'ban' => 'Basa Bali', # Balinese
                'bar' => 'Boarisch', # Bavarian (Austro-Bavarian and South Tyrolean)
-               'bat-smg' => 'žemaitėška', # Samogitian (deprecated code, 'sgs' in ISO 639-3 since 2010-06-30 )
+               'bat-smg' => 'žemaitėška', # Samogitian (deprecated code, 'sgs' in ISO 693-3 since 2010-06-30 )
                'bbc' => 'Batak Toba', # Batak Toba (falls back to bbc-latn)
                'bbc-latn' => 'Batak Toba', # Batak Toba
                'bcc' => 'جهلسری بلوچی', # Southern Balochi
@@ -287,7 +287,7 @@ class Names {
                'lzh' => '文言', # Literary Chinese, T10217
                'lzz' => 'Lazuri', # Laz
                'mai' => 'मैथिली', # Maithili
-               'map-bms' => 'Basa Banyumasan', # Banyumasan ('jv-x-bms')
+               'map-bms' => 'Basa Banyumasan', # Banyumasan
                'mdf' => 'мокшень', # Moksha
                'mg' => 'Malagasy', # Malagasy
                'mh' => 'Ebon', # Marshallese
@@ -298,7 +298,7 @@ class Names {
                'ml' => 'മലയാളം', # Malayalam
                'mn' => 'монгол', # Halh Mongolian (Cyrillic) (ISO 639-3: khk)
                'mni' => 'মেইতেই লোন্', # Manipuri/Meitei
-               'mo' => 'молдовеняскэ', # Moldovan, deprecated (ISO 639-2: ro-MD)
+               'mo' => 'молдовеняскэ', # Moldovan, deprecated
                'mr' => 'मराठी', # Marathi
                'mrj' => 'кырык мары', # Hill Mari
                'ms' => 'Bahasa Melayu', # Malay
@@ -309,7 +309,7 @@ class Names {
                'myv' => 'эрзянь', # Erzya
                'mzn' => 'مازِرونی', # Mazanderani
                'na' => 'Dorerin Naoero', # Nauruan
-               'nah' => 'Nāhuatl', # Nahuatl (added to ISO 639-3 on 2006-10-31)
+               'nah' => 'Nāhuatl', # Nahuatl (not in ISO 639-3)
                'nan' => 'Bân-lâm-gú', # Min-nan, T10217
                'nap' => 'Napulitano', # Neapolitan, T45793
                'nb' => 'norsk bokmål', # Norwegian (Bokmal)
@@ -324,7 +324,7 @@ class Names {
                'nn' => 'norsk nynorsk', # Norwegian (Nynorsk)
                'no' => 'norsk', # Norwegian macro language (falls back to nb).
                'nov' => 'Novial', # Novial
-               'nrm' => 'Nouormand', # Norman (invalid code; 'nrf' in ISO 639 since 2014)
+               'nrm' => 'Nouormand', # Norman
                'nso' => 'Sesotho sa Leboa', # Northern Sotho
                'nv' => 'Diné bizaad', # Navajo
                'ny' => 'Chi-Chewa', # Chichewa
@@ -360,8 +360,8 @@ class Names {
                'rmy' => 'Romani', # Vlax Romany
                'rn' => 'Kirundi', # Rundi/Kirundi/Urundi
                'ro' => 'română', # Romanian
-               'roa-rup' => 'armãneashti', # Aromanian (deprecated code, 'rup' exists in ISO 639-3)
-               'roa-tara' => 'tarandíne', # Tarantino ('nap-x-tara')
+               'roa-rup' => 'armãneashti', # Aromanian (deprecated code, 'rup' exists in ISO 693-3)
+               'roa-tara' => 'tarandíne', # Tarantino
                'ru' => 'русский', # Russian
                'rue' => 'русиньскый', # Rusyn
                'rup' => 'armãneashti', # Aromanian
@@ -437,7 +437,7 @@ class Names {
                'tt-cyrl' => 'татарча', # Tatar (Cyrillic script) (default)
                'tt-latn' => 'tatarça', # Tatar (Latin script)
                'tum' => 'chiTumbuka', # Tumbuka
-               'tw' => 'Twi', # Twi
+               'tw' => 'Twi', # Twi, (FIXME!)
                'ty' => 'reo tahiti', # Tahitian
                'tyv' => 'тыва дыл', # Tyvan
                'tzm' => 'ⵜⴰⵎⴰⵣⵉⵖⵜ', # Tamazight
diff --git a/languages/data/README.md b/languages/data/README.md
new file mode 100644 (file)
index 0000000..8a13fdc
--- /dev/null
@@ -0,0 +1,7 @@
+## normalize-ar.phpc
+
+Generated by `maintenance/language/generateNormalizerDataAr.php`.
+
+## normalize-ml.php
+
+Generated by `maintenance/language/generateNormalizerDataMl.php`.
index c1daf20..f54b231 100644 (file)
@@ -1,5 +1,5 @@
 <?php
-// File created by maintenance/generateNormalizerDataAr.php
+// File created by generateNormalizerDataAr.php
 return [
        'ﭐ' => 'ٱ',
        'ﭑ' => 'ٱ',
index ca89a5a..1cf8548 100644 (file)
@@ -1,5 +1,5 @@
 <?php
-// File created by maintenance/generateNormalizerDataMl.php
+// File created by generateNormalizerDataMl.php
 return [
        'ണ്‍' => 'ൺ',
        'ന്‍' => 'ൻ',
index 744400a..dc6ed48 100644 (file)
        "timezoneregion-indian": "Indiese Oseaan",
        "timezoneregion-pacific": "Stille Oseaan",
        "allowemail": "Laat e-pos van ander toe",
+       "email-blacklist-label": "Verbied hierdie gebruikers om my te e-pos:",
        "prefs-searchoptions": "Soek",
        "prefs-namespaces": "Naamruimtes",
        "default": "verstek",
        "rcfilters-view-tags-tooltip": "Filter resultate volgens wysigingsetikette",
        "rcfilters-liveupdates-button": "Monitor bywerkings",
        "rcfilters-liveupdates-button-title-off": "Wys nuwe wysigings soos hulle inrol.",
-       "rcfilters-preference-label": "Versteek die verbeter weergawe van 'Onlangse wysigings'",
+       "rcfilters-preference-label": "Versteek die verbeterde weergawe van 'Onlangse wysigings'",
+       "rcfilters-watchlist-preference-label": "Versteek die verbeterde weergawe van die dophoulys.",
        "rcnotefrom": "{{PLURAL:$5|Wysiging|Wysigings}} sedert <strong>$3 om $4</strong> (maksimum van <strong>$1</strong> word gewys).",
        "rclistfrom": "Vertoon wysigings vanaf $3 $2",
        "rcshowhideminor": "$1 klein wysigings",
index b08c8f0..5f04e02 100644 (file)
        "diff-paragraph-moved-toold": "الفقرة تم نقلها. اضغط للذهاب للموقع القديم.",
        "difference-missing-revision": "{{PLURAL:$2|مراجعة واحدة|$2 مراجعات}} لهذا الفرق ($1) {{PLURAL:$2|لم|لم}} يتم إيجادها.\n\nهذا يحدث عادة عن طريق اتباع وصلة فرق قديمة لصفحة تم حذفها.\nالتفاصيل يمكن إيجادها في [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} سجل الحذف].",
        "searchresults": "نتائج البحث",
+       "search-filter-title-prefix": "البحث فقط في الصفحات التي يبدأ عنوانها بـ \"$1\"",
+       "search-filter-title-prefix-reset": "البحث في جميع الصفحات",
        "searchresults-title": "نتائج البحث عن \"$1\"",
        "titlematches": "عنوان الصفحة يطابق",
        "textmatches": "نص الصفحة يطابق",
        "speciallogtitlelabel": "الهدف (عنوان أو {{ns:user}}:اسم المستخدم للمستخدم):",
        "log": "سجلات",
        "logeventslist-submit": "أظهر",
-       "logeventslist-more-filters": "المزيد من المرشحات:",
+       "logeventslist-more-filters": "إظهار السجلات الإضافية:",
+       "logeventslist-patrol-log": "سجل الخفر",
+       "logeventslist-tag-log": "سجل الوسم",
        "all-logs-page": "كل السجلات العامة",
        "alllogstext": "عرض شامل لكل السجلات المتوفرة في {{SITENAME}}.\nباستطاعتك جعل القائمة أكثر تحديداً، وذلك باختيار نوع السجل واسم المستخدم (حساس لحالة الحروف)، أو الصفحة المتأثرة (أيضاً حساس لحالة الحروف).",
        "logempty": "لا توجد مدخلات مطابقة في السجل.",
        "uctop": "(حالية)",
        "month": "من شهر (وأقدم):",
        "year": "من سنة (وأقدم):",
+       "date": "من تاريخ (وأقدم).",
        "sp-contributions-newbies": "اعرض مساهمات الحسابات الجديدة فقط",
        "sp-contributions-newbies-sub": "للحسابات الجديدة",
        "sp-contributions-newbies-title": "مساهمات المستخدم للحسابات الجديدة",
index 382ce27..b3783e9 100644 (file)
        "rcshowhideminor": "$1 M515x512S1f010486x488S2f900502x507 M521x532S10609498x496S10621487x512S21100500x483S2df20479x468",
        "rcshowhideminor-hide": "M546x533S15a56519x474S2ff00482x483S2c601516x507S1f740521x487S1f711484x508",
        "rcshowhidebots-show": "M521x531S10012491x516S15a18479x503S26620486x469",
+       "rcshowhidebots-hide": "M546x533S15a56519x474S2ff00482x483S2c601516x507S1f740521x487S1f711484x508",
        "rcshowhideliu": "$1 M538x521S2a200497x480S20b00462x508S11530523x482S15a37462x483S11551470x493 M526x522S15a56499x510S11520503x479S20e00489x496S26a02474x489",
        "rcshowhideliu-hide": "M546x533S15a56519x474S2ff00482x483S2c601516x507S1f740521x487S1f711484x508",
        "rcshowhideanons-hide": "M546x533S15a56519x474S2ff00482x483S2c601516x507S1f740521x487S1f711484x508",
        "logentry-newusers-autocreate": "M526x522S15a56499x510S11520503x479S20e00489x496S26a02474x489 M518x517S15a30482x489S1f750484x484S20500496x506S22a04505x495 $1 M531x531S14c30506x469S2a204508x503S2a21c470x503S14c38470x469 {{GENDER:$2|M532x519S20302493x485S2030a489x502S21100509x504S26900516x482S26910468x501}} M520x525S15a18480x490S10a02483x476S23100494x508S20e00500x494",
        "log-name-managetags": "M519x514S15a28482x487S11502489x498 M530x529S10640507x503S10648479x489S26c08503x484S26c18470x471 M518x536S15a37483x465S18550493x476S15a37483x509S15a51486x513",
        "log-name-tag": "M519x514S15a28482x487S11502489x498 M518x536S15a37483x465S18550493x476S15a37483x509S15a51486x513",
-       "searchsuggest-search": "M546x525S2ff00482x483S16d10492x505S2e502519x502",
+       "searchsuggest-search": "M546x525S2ff00482x483S16d10492x505S2e502519x502 {{SITENAME}}",
        "api-error-unknownerror": "M536x518S2ff00482x483S15a11513x486S28108513x453 M518x526S2ff00482x483S19a00487x506 S38700463x496 $1",
        "expandtemplates": "M554x524S14c40530x477S14c48446x493S26506515x490S26512468x508S20340495x490S20348487x507 M510x522S15c10490x513S16d12490x495S22a04494x478",
        "expand_templates_ok": "M515x525S17620499x475S14020485x495",
index f567f5d..fa2b983 100644 (file)
        "redirectedfrom": "($1 səhifəsindən yönləndirilmişdir)",
        "redirectpagesub": "Yönləndirmə səhifəsi",
        "redirectto": "İstiqamətləndirilir:",
-       "lastmodifiedat": "Bu səhifə sonuncu dəfə $1 tarixində saat $2-də redaktə edilib.",
+       "lastmodifiedat": "Bu səhifə sonuncu dəfə $1 tarixində, saat $2-də redaktə edilib.",
        "viewcount": "Bu səhifəyə $1 {{PLURAL:$1|dəfə}} müraciət olunub.",
        "protectedpage": "Mühafizəli səhifə",
        "jumpto": "Keçid et:",
        "createacct-submit": "İstifadəçi hesabı yarat",
        "createacct-another-submit": "İstifadəçi hesabı yarat",
        "createacct-benefit-heading": "{{SITENAME}} sizin kimi insanlar tərəfindən yaradılır.",
-       "createacct-benefit-body1": "$1 {{PLURAL:$1|redaktə}}",
+       "createacct-benefit-body1": "{{PLURAL:$1|redaktə}}",
        "createacct-benefit-body2": "{{PLURAL:$1|səhifə|səhifə}}",
-       "createacct-benefit-body3": "ən son {{PLURAL:$1|iştirakçılar|iştirakçılar}}",
+       "createacct-benefit-body3": "nəfər yenicə {{PLURAL:$1|redaktə edən}}",
        "badretype": "Daxil etdiyiniz parol uyğun gəlmir.",
        "userexists": "Daxil edilmiş ad artıq istifadədədir.\nLütfən başqa ad seçin.",
        "loginerror": "Daxil olma xətası",
        "recentchanges-page-added-to-category": "[[:$1]] kateqoriyaya əlavə edildi",
        "upload": "Fayl yüklə",
        "uploadbtn": "Sənəd yüklə",
-       "reuploaddesc": "Return to the upload form.",
-       "upload-tryagain": "Dəyşdirilmiş fayl izahını göndər",
+       "reuploaddesc": "Yükləmə formasına geri qayıt",
+       "upload-tryagain": "Dəyişdirilmiş fayl izahını göndər",
        "uploadnologin": "Daxil olmamısınız",
        "uploadnologintext": "Fayl yükləmək üçün siz sistemə $1.",
        "upload_directory_missing": "($1) yükləmə qaydası axtarılır və vebserverdə yaradılması qeyri-mümkündür.",
        "upload_directory_read_only": "\"$1\" kataloqunun arxivi veb-server yazıları üçün qapalıdır.",
        "uploaderror": "Yükləmə xətası",
        "upload-recreate-warning": "'''Diqqət: Bu adda fayl silinib, yaxud adı dəyişdirilib.'''\n\nBu səhifənin silinmə və addəyişmə jurnalı aşağıda göstərilmişdir:",
-       "uploadtext": "Fayl yükləmək üçün aşağıdakı formadan istifadə edin.\nƏvvəllər yüklənmiş fayllara baxmaq üçün [[Special:FileList|yüklənmiş fayllar siyahısına]] keçin, həmçinin (təkrar) yüklənmiş fayllara [[Special:Log/upload|yükləmə jurnalında]], silinmiş fayllara [[Special:Log/delete|silinmə jurnalında]] baxa bilərsiniz.\n\nMəqaləyə fayl yerləşdirmək üçün aşağıdaki formalardan birini istifadə edin:\n* Faylın tam versiyasını yerləşdirmək üçün: '''<code><nowiki>[[</nowiki>{{ns:file}}<nowiki>:File.jpg]]</nowiki></code>''';\n* Faylın 200 pikselədək kiçildilmiş versiyasını mətndən solda, altında izahla yerləşdirmək üçün: '''<code><nowiki>[[</nowiki>{{ns:file}}<nowiki>:File.png|200px|thumb|left|təsvir]]</nowiki></code>''';\n* Səhifədə faylın özünü göstərmədən ona birbaşa keçid yerləşdirmək üçün: '''<code><nowiki>[[</nowiki>{{ns:media}}<nowiki>:File.ogg]]</nowiki></code>'''.",
+       "uploadtext": "Fayl yükləmək üçün aşağıdakı formadan istifadə edin.\nƏvvəllər yüklənmiş fayllara baxmaq üçün [[Special:FileList|yüklənmiş fayllar siyahısına]] keçin, həmçinin (təkrar) yüklənmiş fayllara [[Special:Log/upload|yükləmə jurnalında]], silinmiş fayllara [[Special:Log/delete|silinmə jurnalında]] baxa bilərsiniz.\n\nMəqaləyə fayl yerləşdirmək üçün aşağıdakı formalardan birini istifadə edin:\n* Faylın tam versiyasını yerləşdirmək üçün: '''<code><nowiki>[[</nowiki>{{ns:file}}<nowiki>:File.jpg]]</nowiki></code>''';\n* Faylın 200 pikselədək kiçildilmiş versiyasını mətndən solda və altında izahla yerləşdirmək üçün: '''<code><nowiki>[[</nowiki>{{ns:file}}<nowiki>:File.png|200px|thumb|left|təsvir]]</nowiki></code>''';\n* Səhifədə faylın özünü göstərmədən ona birbaşa keçid yerləşdirmək üçün: '''<code><nowiki>[[</nowiki>{{ns:media}}<nowiki>:File.ogg]]</nowiki></code>'''.",
        "upload-permitted": "İcazə verilən fayl {{PLURAL:$2|tipi|tipləri}}: $1.",
        "upload-preferred": "Üstünlük verilən fayl {{PLURAL:$2|tipi|tipləri}}: $1.",
        "upload-prohibited": "Qadağan olunan fayl {{PLURAL:$2|tipi|tipləri}}: $1.",
        "anonymous": "{{SITENAME}} saytının anonim {{PLURAL:$1|istifadəçisi|istifadəçiləri}}",
        "siteuser": "{{SITENAME}} istifadəçisi $1",
        "anonuser": "{{SITENAME}} anonim istifadəçisi $1",
+       "lastmodifiedatby": "Bu səhifə sonuncu dəfə $3 tərəfindən $1 tarixində, saat $2-də redaktə edilib.",
        "othercontribs": "$1-in işinə əsaslanıb.",
        "others": "digərləri",
        "siteusers": "{{SITENAME}} {{PLURAL:$2|user|istifadəçi}} $1",
index 7adacba..dbbffdc 100644 (file)
        "difference-missing-revision": "{{PLURAL:$2|$2 вэрсія|$2 вэрсіі|$2 вэрсіяў}} з гэтымі адрозьненьнямі ($1) {{PLURAL:$2|не была|не былі}} знойдзеныя.\n\nЗвычайна гэта здараецца з-за пераходу па састарэлай спасылцы на старонку, якая была выдаленая.\nПадрабязнасьці можна знайсьці ў [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} журнале выдаленьняў].",
        "searchresults": "Вынікі пошуку",
        "search-filter-title-prefix": "Шукаем толькі на старонках, назвы якіх пачынаюцца з «$1»",
+       "search-filter-title-prefix-reset": "Шукаць сярод усіх старонак",
        "searchresults-title": "Вынікі пошуку для «$1»",
        "titlematches": "Супадзеньні ў назвах старонак",
        "textmatches": "Супадзеньні ў тэкстах старонак",
        "upload-source": "Крынічны файл",
        "sourcefilename": "Пачатковая назва файлу:",
        "sourceurl": "URL-адрас крыніцы:",
-       "destfilename": "Канчатковая назва файла:",
-       "upload-maxfilesize": "Максымальны памер файла: $1",
-       "upload-description": "Апісаньне файла",
+       "destfilename": "Канчатковая назва файлу:",
+       "upload-maxfilesize": "Максымальны памер файлу: $1",
+       "upload-description": "Апісаньне файлу",
        "upload-options": "Налады загрузкі",
        "watchthisupload": "Назіраць за гэтым файлам",
        "filewasdeleted": "Файл з такой назвай загружаўся, але быў выдалены.\nВам трэба праверыць $1 перад новай загрузкай.",
        "http-timed-out": "Скончыўся час чаканьня HTTP-запыту.",
        "http-curl-error": "Памылка выбаркі URL-адрасу: $1",
        "http-bad-status": "Адбылася памылка пад час выкананьня HTTP-запыту: $1 $2",
+       "http-internal-error": "Унутраная памылка HTTP.",
        "upload-curl-error6": "Немагчыма дасягнуць URL-адрас",
        "upload-curl-error6-text": "Немагчыма адкрыць пазначаны URL-адрас.\nКалі ласка, упэўніцеся, што адрас слушны і сайт працуе.",
        "upload-curl-error28": "Ліміт часу загрузкі скончыўся",
index 3d04e0e..bd5ac50 100644 (file)
@@ -33,7 +33,8 @@
                        "Artsiom91",
                        "Andrus",
                        "Da voli",
-                       "OlegCinema"
+                       "OlegCinema",
+                       "Gorgich"
                ]
        },
        "tog-underline": "Падкрэсліваць спасылкі:",
        "diff-paragraph-moved-toold": "Параграф быў перамешчаны. Клікніце, каб перайсці да старога месцазнаходжання.",
        "difference-missing-revision": "{{PLURAL:$2|$2 версія|$2 версіі|$2 версій}} гэтай розніцы ($1) {{PLURAL:$2|не знойдзена|не знойдзены}}.\n\nЗвычайна такое здараецца з-за пераходу па састарэлай спасылцы на розніцу ў старонцы, якая была выдалена.\nПадрабязнасці могуць быць у [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} журнале выдаленняў].",
        "searchresults": "Вынікі пошуку",
+       "search-filter-title-prefix": "Шукаць толькі на старонках, назва якіх пачынаецца з «$1»",
        "searchresults-title": "Вынікі пошуку «$1»",
        "titlematches": "Знойдзена ў назвах",
        "textmatches": "Знойдзена ў тэкстах",
        "rcfilters-other-review-tools": "Іншыя інструменты праверкі",
        "rcfilters-group-results-by-page": "Групіраваць вынікі паводле старонкі",
        "rcfilters-activefilters": "Актыўныя фільтры",
+       "rcfilters-activefilters-show": "Паказаць",
        "rcfilters-advancedfilters": "Пашыраныя фільтры",
        "rcfilters-limit-title": "Вынікі для паказу",
        "rcfilters-limit-and-date-label": "$1 {{PLURAL:$1|змена|змены|змен}}, $2",
        "apisandbox-dynamic-error-exists": "Параметр з назвай \"$1\" ужо існуе.",
        "apisandbox-deprecated-parameters": "Састарэлыя параметры",
        "apisandbox-fetch-token": "Аўтазапаўненне токена",
+       "apisandbox-add-multi": "Дадаць",
        "apisandbox-submit-invalid-fields-title": "Некаторыя палі недапушчальныя",
        "apisandbox-submit-invalid-fields-message": "Калі ласка, выпраўце адзначаныя палі і паспрабуйце ізноў.",
        "apisandbox-results": "Вынікі",
index c93390d..af77e32 100644 (file)
        "rcfilters-other-review-tools": "Други инструменти за проверка",
        "rcfilters-group-results-by-page": "Групиране на резултатите по страница",
        "rcfilters-activefilters": "Активни филтри",
+       "rcfilters-activefilters-hide": "Скриване",
        "rcfilters-advancedfilters": "Разширени филтри",
        "rcfilters-limit-title": "Резултати за показване",
        "rcfilters-limit-and-date-label": "$1 {{PLURAL:$1|промяна|промени}}, $2",
        "rcfilters-filter-humans-label": "Човек (не бот)",
        "rcfilters-filter-humans-description": "Редакции, направени от редактори.",
        "rcfilters-filtergroup-reviewstatus": "Проверка на статуса",
-       "rcfilters-filter-reviewstatus-unpatrolled-label": "Ð\9dепаÑ\82Ñ\80Ñ\83лиÑ\80ано",
+       "rcfilters-filter-reviewstatus-unpatrolled-label": "Ð\9dепаÑ\82Ñ\80Ñ\83лиÑ\80ани",
        "rcfilters-filter-reviewstatus-manual-label": "Ръчно патрулирани",
        "rcfilters-filter-reviewstatus-auto-label": "Автоматично патрулирани",
        "rcfilters-filtergroup-significance": "Значимост",
        "rcfilters-filter-watchlist-notwatched-label": "Извън списъка за наблюдение",
        "rcfilters-filter-watchlist-notwatched-description": "Всички, освен промените в страници от списъка за наблюдение.",
        "rcfilters-filtergroup-watchlistactivity": "Активност по списъка за наблюдение",
-       "rcfilters-filter-watchlistactivity-unseen-label": "Ð\9dевидÑ\8fни промени",
+       "rcfilters-filter-watchlistactivity-unseen-label": "Ð\9dепÑ\80егледани промени",
        "rcfilters-filter-watchlistactivity-unseen-description": "Промени по страници, които не сте посетили откакто са настъпили промените.",
-       "rcfilters-filter-watchlistactivity-seen-label": "Ð\92идени промени",
+       "rcfilters-filter-watchlistactivity-seen-label": "Ð\9fÑ\80егледани промени",
        "rcfilters-filter-watchlistactivity-seen-description": "Промени по страници, които сте посетили откакто са настъпили промените.",
        "rcfilters-filtergroup-changetype": "Вид на промяната",
        "rcfilters-filter-pageedits-label": "Редакции на страници",
        "rcfilters-liveupdates-button": "Обновяване на живо",
        "rcfilters-liveupdates-button-title-on": "Изключване на обновяването в реално време",
        "rcfilters-liveupdates-button-title-off": "Показване на новите промени в реално време",
-       "rcfilters-watchlist-markseen-button": "Ð\9eÑ\82белÑ\8fзване Ð½Ð° Ð²Ñ\81иÑ\87ки Ð¿Ñ\80омени ÐºÐ°Ñ\82о Ð²Ð¸Ð´Ðµни",
+       "rcfilters-watchlist-markseen-button": "Ð\9eÑ\82белÑ\8fзване Ð½Ð° Ð²Ñ\81иÑ\87ки Ð¿Ñ\80омени ÐºÐ°Ñ\82о Ð¿Ñ\80егледани",
        "rcfilters-watchlist-edit-watchlist-button": "Редактиране на списъка за наблюдение",
        "rcfilters-watchlist-showupdated": "Промени по страници, които не сте посетили откакто са внесени промените, са в <strong>получер</strong>, с удебелени маркери.",
        "rcfilters-preference-label": "Скриване на подобрената версия на Последни промени",
        "dellogpage": "Дневник на изтриванията",
        "dellogpagetext": "Списък на последните изтривания.",
        "deletionlog": "дневник на изтриванията",
+       "log-name-create": "Дневник на създадените страници",
+       "log-description-create": "По-долу е показан списък на последно създадените страници.",
        "logentry-create-create": "$1 {{GENDER:$2|създаде}} страница $3",
        "reverted": "Възвръщане към предишна версия",
        "deletecomment": "Причина:",
        "tag-filter-submit": "Филтриране",
        "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|Етикет|Етикети}}]]: $2)",
        "tag-mw-contentmodelchange": "промяна на модела на съдържание",
+       "tag-mw-new-redirect": "Ново пренасочване",
+       "tag-mw-removed-redirect": "Премахнато пренасочване",
+       "tag-mw-blank": "Изтриване на съдържанието",
+       "tag-mw-replace": "Заменено",
        "tag-mw-rollback": "Отмяна",
        "tag-mw-undo": "Отмяна",
        "tags-title": "Етикети",
index 6b86915..f8b9881 100644 (file)
        "http-timed-out": "HTTP অনুরোধের সময় পার হয়েছে।",
        "http-curl-error": "ইউআরএল নিয়ে আসার ক্ষেত্রে ত্রুটি: $1",
        "http-bad-status": "HTTP অনুরোধের সময় কোন সমস্যা হয়েছে: $1 $2",
+       "http-internal-error": "HTTP অভ্যন্তরীণ ত্রুটি।",
        "upload-curl-error6": "URL-এ পৌঁছানো যায়নি",
        "upload-curl-error6-text": "প্রদত্ত URLটিতে পৌঁছানো যায়নি। অনুগ্রহ করে আবার পরীক্ষা করে দেখুন URLটি সঠিক কি না এবং সাইটটি চালু আছে কি না।",
        "upload-curl-error28": "আপলোড সময়োত্তীর্ণ",
index e31278b..8249df5 100644 (file)
        "speciallogtitlelabel": "Cilj (naslov ili {{ns:user}}:korisničko ime):",
        "log": "Zapisnici",
        "logeventslist-submit": "Prikaži",
+       "logeventslist-more-filters": "Prikaži dodatne zapisnike:",
        "all-logs-page": "Svi javni zapisnici",
        "alllogstext": "Skupni prikaz svih dostupnih zapisnika sa {{GRAMMAR:genitiv|{{SITENAME}}}}.\nMožete suziti prikaz izabiranjem specifičnog zapisnika, korisničkog imena (razlikovati velika i mala slova) ili izmijenjenog članka (također treba razlikovati velika i mala slova).",
        "logempty": "Nema zatraženih stavki u zapisniku.",
index d2854d6..3cd1b13 100644 (file)
        "diff-paragraph-moved-toold": "S'ha mogut el paràgraf. Feu clic per a saltar la ubicació antiga.",
        "difference-missing-revision": "{{PLURAL:$2|Una revisió|$2 revisions}} d'aquesta diferència ($1) no {{PLURAL:$2|s'ha|s'han}} trobat.\n\nAixò passa generalment en seguir un enllaç obsolet de diferències a una pàgina que s'ha suprimit.\nEs pot trobar més informació en el [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} registre de supressions].",
        "searchresults": "Resultats de la cerca",
+       "search-filter-title-prefix-reset": "Cerca a totes les pàgines",
        "searchresults-title": "Resultats de la cerca de «$1»",
        "titlematches": "Coincidències de títol de la pàgina",
        "textmatches": "Coincidències de text de pàgina",
        "rcfilters-filter-humans-description": "Modificacions fetes per editors humans.",
        "rcfilters-filtergroup-reviewstatus": "Estat de revisió",
        "rcfilters-filter-reviewstatus-unpatrolled-label": "No patrullat",
+       "rcfilters-filter-reviewstatus-manual-description": "Modificacions marcades manualment com a patrullades.",
        "rcfilters-filter-reviewstatus-manual-label": "Patrullat manualment",
        "rcfilters-filter-reviewstatus-auto-label": "Autopatrullat",
        "rcfilters-filtergroup-significance": "Significació",
        "rcfilters-watchlist-edit-watchlist-button": "Editeu la vostra llista de pàgines seguides",
        "rcfilters-watchlist-showupdated": "Els canvis fets en pàgines que no heu visitat des que s'efectuaren apareixen en <strong>negreta</strong> amb un punt sòlid al costat.",
        "rcfilters-preference-label": "Amaga la versió millorada de Canvis recents",
+       "rcfilters-watchlist-preference-label": "Amaga la versió millorada de la llista de seguiment",
        "rcfilters-filter-showlinkedfrom-label": "Mostra els canvis en les pàgines enllaçades des de",
        "rcfilters-filter-showlinkedfrom-option-label": "<strong>Pàgines enllaçades des de</strong> la pàgina seleccionada",
        "rcfilters-filter-showlinkedto-label": "Mostra els canvis a les pàgines que enllacin a",
        "uploadstash-bad-path-invalid": "El camí no és vàlid.",
        "uploadstash-bad-path-unknown-type": "El tipus «$1» és desconegut.",
        "uploadstash-bad-path-unrecognized-thumb-name": "Nom de miniatura no reconegut.",
+       "uploadstash-file-not-found-not-exists": "No es pot trobar el camí, o bé no és un fitxer pla.",
+       "uploadstash-file-too-large": "No es pot servir un fitxer més gran de $1 bytes.",
        "uploadstash-no-extension": "L’extensió és nul·la.",
        "uploadstash-zero-length": "El fitxer té mida zero.",
        "invalid-chunk-offset": "El desplaçament del fragment no és vàlid",
        "log": "Registres",
        "logeventslist-submit": "Mostra",
        "logeventslist-more-filters": "Més filtres:",
+       "logeventslist-tag-log": "Registre d'etiquetes",
        "all-logs-page": "Tots els registres públics",
        "alllogstext": "Presentació combinada de tots els registres disponibles de {{SITENAME}}.\nPodeu reduir l'extensió seleccionant el tipus de registre, el nom d'usuari realitzador (distingeix entre majúscules i minúscules), o la pàgina objectiu (també en distingeix).",
        "logempty": "No hi ha cap coincidència en el registre.",
        "dellogpagetext": "Davall hi ha una llista dels esborraments més recents.",
        "deletionlog": "registre de supressions",
        "log-name-create": "Registre de creació de pàgines",
+       "log-description-create": "A continuació hi ha una llista de les pàgines creades més recentment.",
+       "logentry-create-create": "$1 {{GENDER:$2|va crear}} la pàgina $3",
        "reverted": "Invertit amb una revisió anterior",
        "deletecomment": "Motiu:",
        "deleteotherreason": "Motiu diferent o addicional:",
        "logentry-delete-delete": "$1 {{GENDER:$2|ha suprimit}} la pàgina $3",
        "logentry-delete-delete_redir": "$1 {{GENDER:$2|ha esborrat}} la redirecció $3 sobreescrivint-la",
        "logentry-delete-restore": "$1 {{GENDER:$2|ha restaurat}} la pàgina $3 ($4)",
+       "logentry-delete-restore-nocount": "$1 {{GENDER:$2|va restaurar}} la pàgina $3",
        "restore-count-revisions": "{{PLURAL:$1|Una revisió|$1 revisions}}",
        "restore-count-files": "{{PLURAL:$1|Un fitxer|$1 fitxers}}",
        "logentry-delete-event": "$1 {{GENDER:$2|ha canviat}} la visibilitat {{PLURAL:$5|d'un esdeveniment al registre|de $5 esdeveniments al registre}} de $3: $4",
index 9d04fa5..2516889 100644 (file)
        "nextn": "âu $1 bĭk",
        "shown-title": "Mūi hiĕk hiēng-sê $1{{PLURAL:$1|bĭk giék-guō}}",
        "viewprevnext": "Káng ($1 {{int:pipe-separator}} $2) ($3)",
+       "searchmenu-exists": "cī siŏh bĭh wiki ī-gĭng ô „$1“ mìng-chĭng gì dèu-mŭk",
        "searchprofile-articles": "Nô̤i-ṳ̀ng hiĕk",
        "searchprofile-images": "Dŏ̤-mùi-tā̤",
        "searchprofile-everything": "Sū-iū-nó̤h",
index 6ea5b13..4d64e06 100644 (file)
        "dellogpage": "لۆگی سڕینەوە",
        "dellogpagetext": "ئەوەی خوارەوە لیستێكە لە دوایین سڕینەوەکان",
        "deletionlog": "لۆگی سڕینەوە",
+       "log-name-create": "لۆگی دروستکردنی پەڕە",
+       "logentry-create-create": "$1 پەڕەی $3ی دروست کرد",
        "reverted": "گەڕێندراوە بۆ پێداچوونەوەی پێشووتر",
        "deletecomment": "ھۆکار:",
        "deleteotherreason": "ھۆکاری تر/زیاتر:",
index 5ebb20d..6e5cfb8 100644 (file)
@@ -20,7 +20,8 @@
                        "Dafyddt",
                        "Jdforrester",
                        "Irus",
-                       "Fitoschido"
+                       "Fitoschido",
+                       "Nrowe"
                ]
        },
        "tog-underline": "Tanlinellu cysylltiadau:",
        "rcfilters-filter-newpages-description": "Golygiadau creu dalennau",
        "rcfilters-filter-categorization-label": "Newidiadau i'r Categoriau",
        "rcfilters-filter-categorization-description": "Cofnodion y dalennau a ychwanegwyd neu a symudwyd o'r categoriau.",
-       "rcfilters-filter-logactions-label": "Gweithredoedd a logiwyd",
+       "rcfilters-filter-logactions-label": "Gwriansow kovskrifys",
        "rcfilters-filtergroup-lastRevision": "Yr adolygiadau diweddaraf",
        "rcfilters-filter-lastrevision-label": "Yr adolygiad diweddaraf",
        "rcfilters-filter-lastrevision-description": "Dim ond yn newidiadau diweddaraf i'r ddalen.",
index 618c606..a7dd8c0 100644 (file)
        "dellogpage": "Sletningslog",
        "dellogpagetext": "Herunder vises de nyeste sletninger. Alle tider er serverens tid.",
        "deletionlog": "sletningslog",
+       "log-name-create": "Sideoprettelseslog",
+       "log-description-create": "Nedenfor er en liste over de senest oprettede sider.",
+       "logentry-create-create": "$1 {{GENDER:$2|oprettede}} siden $3",
        "reverted": "Gendannet en tidligere version",
        "deletecomment": "Begrundelse:",
        "deleteotherreason": "Anden/uddybende begrundelse:",
index 5c67e95..7fb78b2 100644 (file)
        "http-timed-out": "Zeitüberschreitung bei der HTTP-Anfrage.",
        "http-curl-error": "Fehler beim Abruf der URL: $1",
        "http-bad-status": "Während der HTTP-Anfrage ist ein Fehler aufgetreten: $1 $2",
+       "http-internal-error": "Interner HTTP-Fehler.",
        "upload-curl-error6": "URL ist nicht erreichbar",
        "upload-curl-error6-text": "Die angegebene URL ist nicht erreichbar. Prüfe sowohl die URL auf Fehler als auch den Online-Status der Seite.",
        "upload-curl-error28": "Zeitüberschreitung beim Hochladen",
index 9e8b647..a0e9bca 100644 (file)
        "diff-paragraph-moved-toold": "Η παράγραφος αφαιρέθηκε. Πατήστε στο κουμπί για να πάτε σε προηγούμενη τοποθεσία.",
        "difference-missing-revision": "{{PLURAL:$2|Μία αναθεώρηση|$2 αναθεωρήσεις}} αυτής της διαφοράς ($1) δεν {{PLURAL:$2|μπόρεσε να βρεθεί|μπόρεσαν να βρεθούν}}.\n\nΑυτό συνήθως προκαλείται από παλιό σύνδεσμο διαφοράς προς σελίδα που έχει διαγραφεί.\nΛεπτομέρειες θα βρείτε στο [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} ημερολόγιο καταγραφής διαγραφών].",
        "searchresults": "Αποτελέσματα αναζήτησης",
+       "search-filter-title-prefix-reset": "Αναζήτηση όλων των σελίδων",
        "searchresults-title": "Αποτελέσματα αναζήτησης για \"$1\"",
        "titlematches": "Τίτλοι άρθρων που ανταποκρίνονται",
        "textmatches": "Κείμενα σελίδων που ανταποκρίνονται:",
index bc35c9e..a9bd1f5 100644 (file)
        "customcssprotected": "You do not have permission to edit this CSS page because it contains another user's personal settings.",
        "customjsonprotected": "You do not have permission to edit this JSON page because it contains another user's personal settings.",
        "customjsprotected": "You do not have permission to edit this JavaScript page because it contains another user's personal settings.",
+       "sitecssprotected": "You do not have permission to edit this CSS page because it may affect all visitors",
+       "sitejsonprotected": "You do not have permission to edit this JSON page because it may affect all visitors",
+       "sitejsprotected": "You do not have permission to edit this JavaScript page because it may affect all visitors",
        "mycustomcssprotected": "You do not have permission to edit this CSS page.",
        "mycustomjsonprotected": "You do not have permission to edit this JSON page.",
        "mycustomjsprotected": "You do not have permission to edit this JavaScript page.",
        "group-autoconfirmed": "Autoconfirmed users",
        "group-bot": "Bots",
        "group-sysop": "Administrators",
+       "group-interface-admin": "Interface administrators",
        "group-bureaucrat": "Bureaucrats",
        "group-suppress": "Suppressors",
        "group-all": "(all)",
        "group-autoconfirmed-member": "{{GENDER:$1|autoconfirmed user}}",
        "group-bot-member": "{{GENDER:$1|bot}}",
        "group-sysop-member": "{{GENDER:$1|administrator}}",
+       "group-interface-admin-member": "{{GENDER:$1|interface administrator}}",
        "group-bureaucrat-member": "{{GENDER:$1|bureaucrat}}",
        "group-suppress-member": "{{GENDER:$1|suppressor}}",
        "grouppage-user": "{{ns:project}}:Users",
        "grouppage-autoconfirmed": "{{ns:project}}:Autoconfirmed users",
        "grouppage-bot": "{{ns:project}}:Bots",
        "grouppage-sysop": "{{ns:project}}:Administrators",
+       "grouppage-interface-admin": "{{ns:project}}:Interface administrators",
        "grouppage-bureaucrat": "{{ns:project}}:Bureaucrats",
        "grouppage-suppress": "{{ns:project}}:Suppress",
        "right-read": "Read pages",
        "right-editusercss": "Edit other users' CSS files",
        "right-edituserjson": "Edit other users' JSON files",
        "right-edituserjs": "Edit other users' JavaScript files",
+       "right-editsitecss": "Edit sitewide CSS",
+       "right-editsitejson": "Edit sitewide JSON",
+       "right-editsitejs": "Edit sitewide JavaScript",
        "right-editmyusercss": "Edit your own user CSS files",
        "right-editmyuserjson": "Edit your own user JSON files",
        "right-editmyuserjs": "Edit your own user JavaScript files",
        "grant-createaccount": "Create accounts",
        "grant-createeditmovepage": "Create, edit, and move pages",
        "grant-delete": "Delete pages, revisions, and log entries",
-       "grant-editinterface": "Edit the MediaWiki namespace and user CSS/JSON/JavaScript",
+       "grant-editinterface": "Edit the MediaWiki namespace and sitewide/user JSON",
        "grant-editmycssjs": "Edit your user CSS/JSON/JavaScript",
        "grant-editmyoptions": "Edit your user preferences",
        "grant-editmywatchlist": "Edit your watchlist",
+       "grant-editsiteconfig": "Edit sitewide and user CSS/JS",
        "grant-editpage": "Edit existing pages",
        "grant-editprotected": "Edit protected pages",
        "grant-highvolume": "High-volume editing",
        "uploadstash-zero-length": "File is zero length.",
        "invalid-chunk-offset": "Invalid chunk offset",
        "img-auth-accessdenied": "Access denied",
-       "img-auth-nopathinfo": "Missing PATH_INFO.\nYour server is not set up to pass this information.\nIt may be CGI-based and cannot support img_auth.\nSee https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization.",
+       "img-auth-nopathinfo": "Missing path information.\nYour server must be set up to pass the REQUEST_URI and/or PATH_INFO variables.\nIf it is, try enabling $wgUsePathInfo.\nSee https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization.",
        "img-auth-notindir": "Requested path is not in the configured upload directory.",
        "img-auth-badtitle": "Unable to construct a valid title from \"$1\".",
        "img-auth-nologinnWL": "You are not logged in and \"$1\" is not in the whitelist.",
        "http-timed-out": "HTTP request timed out.",
        "http-curl-error": "Error fetching URL: $1",
        "http-bad-status": "There was a problem during the HTTP request: $1 $2",
+       "http-internal-error": "HTTP internal error.",
        "upload-curl-error6": "Could not reach URL",
        "upload-curl-error6-text": "The URL provided could not be reached.\nPlease double-check that the URL is correct and the site is up.",
        "upload-curl-error28": "Upload timeout",
        "passwordpolicies-policy-passwordcannotmatchusername": "Password cannot be the same as username",
        "passwordpolicies-policy-passwordcannotmatchblacklist": "Password cannot match specifically blacklisted passwords",
        "passwordpolicies-policy-maximalpasswordlength": "Password must be less than $1 {{PLURAL:$1|character|characters}} long",
-       "passwordpolicies-policy-passwordcannotbepopular": "Password cannot be {{PLURAL:$1|the popular password|in the list of $1 popular passwords}}"
+       "passwordpolicies-policy-passwordcannotbepopular": "Password cannot be {{PLURAL:$1|the popular password|in the list of $1 popular passwords}}",
+       "easydeflate-invaliddeflate": "Content provided is not properly deflated"
 }
index c30014a..82eb62a 100644 (file)
                        "Truebamateo",
                        "La Mantis",
                        "Amaia",
-                       "Tiberius1701"
+                       "Tiberius1701",
+                       "Astroemi"
                ]
        },
        "tog-underline": "Subrayar los enlaces:",
        "title-invalid-interwiki": "El título de página solicitado contiene un enlace interwiki que no se puede usar en los títulos.",
        "title-invalid-talk-namespace": "El título de la página solicitada apunta a una página de discusión imposible.",
        "title-invalid-characters": "El título  de la página solicitada contiene caracteres no válidos: \"$1\".",
-       "title-invalid-relative": "El título contiene una ruta relativa. Los títulos relativos (./, ../) no son válidos porque a menudo no los pueden manejar los navegadores web.",
+       "title-invalid-relative": "El título contiene una ruta relativa. Los títulos relativos (./, ../) no son válidos porque a menudo no los pueden manipular los navegadores web.",
        "title-invalid-magic-tilde": "El título de la página solicitada contiene una secuencia de virgulillas no válida (<nowiki>~~~</nowiki>).",
        "title-invalid-too-long": "El título de la página solicitada es muy largo. No debe exceder $1 {{PLURAL:$1|byte|bytes}} en codificación UTF-8.",
        "title-invalid-leading-colon": "El título de la página solicitada contiene un caracater (:) no válido en el comienzo.",
        "diff-paragraph-moved-toold": "Se trasladó el párrafo. Pulsa para saltar a la ubicación anterior.",
        "difference-missing-revision": "No se {{PLURAL:$2|ha encontrado una revisión|han encontrado $2 revisiones}} de la comparación solicitada ($1).\n\nLa causa de esto suele ser un enlace obsoleto hacia una edición de una página que ha sido borrada.\nPara más información, consulta el [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} registro de borrados].",
        "searchresults": "Resultados de la búsqueda",
+       "search-filter-title-prefix": "Buscar solo en las páginas cuyos títulos comiencen por «$1»",
+       "search-filter-title-prefix-reset": "Buscar en todas las páginas",
        "searchresults-title": "Resultados de la búsqueda de «$1»",
        "titlematches": "Resultados por título de página",
        "textmatches": "Resultados por texto de página",
        "rcfilters-watchlist-edit-watchlist-button": "Edita tu lista de seguimiento",
        "rcfilters-watchlist-showupdated": "Los cambios hechos a páginas que no has visitado desde que se efectuaron aparecen en <strong>negrita</strong>, acompañados de marcadores sólidos.",
        "rcfilters-preference-label": "Ocultar la versión mejorada de Cambios recientes",
-       "rcfilters-preference-help": "Revierte el rediseño de la interfaz de 2017 y desactiva todas las herramientas añadidas desde entonces.",
+       "rcfilters-preference-help": "Revierte el rediseño de interfaz de 2017 e indisponibiliza todas las herramientas añadidas desde entonces.",
        "rcfilters-watchlist-preference-label": "Ocultar la versión mejorada de la lista de seguimiento",
-       "rcfilters-watchlist-preference-help": "Revierte el rediseño de la interfaz de 2017 y desactiva todas las herramientas añadidas desde entonces.",
+       "rcfilters-watchlist-preference-help": "Revierte el rediseño de la interfaz de 2017 e indisponibiliza todas las herramientas añadidas desde entonces.",
        "rcfilters-filter-showlinkedfrom-label": "Mostrar cambios en páginas enlazadas desde",
        "rcfilters-filter-showlinkedfrom-option-label": "<strong>Páginas enlazadas desde</strong> la página seleccionada",
        "rcfilters-filter-showlinkedto-label": "Mostrar cambios en páginas que enlazan a",
        "uploadstash-bad-path-invalid": "La ruta no es válida.",
        "uploadstash-bad-path-unknown-type": "El tipo «$1» es desconocido.",
        "uploadstash-bad-path-unrecognized-thumb-name": "No se reconoce el nombre de la miniatura.",
-       "uploadstash-bad-path-no-handler": "No se encontró ningún manejador para el MIME $1 del archivo $2.",
+       "uploadstash-bad-path-no-handler": "No se encontró ningún manipulador para el MIME $1 del archivo $2.",
        "uploadstash-bad-path-bad-format": "El formato de la clave «$1» es incorrecto.",
        "uploadstash-file-not-found": "No se encuentra la clave «$1» en el almacén provisional.",
        "uploadstash-file-not-found-no-thumb": "No se pudo obtener la miniatura.",
        "http-timed-out": "La solicitud HTTP ha expirado.",
        "http-curl-error": "Error al recuperar el URL: $1",
        "http-bad-status": "Ha habido un problema durante la solicitud HTTP: $1 $2",
+       "http-internal-error": "Error interno de HTTP.",
        "upload-curl-error6": "No se pudo alcanzar la URL",
        "upload-curl-error6-text": "La URL no pudo ser alcanzada. Por favor comprueba que la URL es correcta y el sitio web está funcionando.",
        "upload-curl-error28": "Tiempo de espera excedido",
        "speciallogtitlelabel": "Objetivo (título o {{ns:user}}:nombre de usuario):",
        "log": "Registros",
        "logeventslist-submit": "Mostrar",
-       "logeventslist-more-filters": "Más filtros:",
+       "logeventslist-more-filters": "Mostrar registros adicionales:",
        "logeventslist-patrol-log": "Registro de revisiones",
        "logeventslist-tag-log": "Registro de etiquetas",
        "all-logs-page": "Todos los registros públicos",
        "watchlistedit-raw-removed": "{{PLURAL:$1|Una página ha sido borrada|$1 páginas han sido borradas}}:",
        "watchlistedit-clear-title": "Vaciar la lista de seguimiento",
        "watchlistedit-clear-legend": "Vaciar la lista de seguimiento",
-       "watchlistedit-clear-explain": "Todos los títulos serán eliminados de tu lista de seguimiento",
+       "watchlistedit-clear-explain": "Se quitarán todos los títulos de la lista de seguimiento",
        "watchlistedit-clear-titles": "Títulos:",
        "watchlistedit-clear-submit": "Vaciar la lista de seguimiento (¡permanente!)",
        "watchlistedit-clear-done": "Se ha vaciado tu lista de seguimiento.",
        "version-editors": "Editores",
        "version-antispam": "Prevención de spam",
        "version-other": "Otro",
-       "version-mediahandlers": "Manejadores multimedia",
+       "version-mediahandlers": "Manipuladores multimedia",
        "version-hooks": "Extensiones",
        "version-parser-extensiontags": "Etiquetas de extensiones sintácticas",
        "version-parser-function-hooks": "Extensiones de funciones sintácticas",
index 126d314..97ce51e 100644 (file)
        "diff-paragraph-moved-toold": "Paragrafoa mugitu egin da. Egin klik aurretiko kokalekura salto egiteko.",
        "difference-missing-revision": " ($1) ezberdinatasunaren  {{PLURAL:$2|Berrikusketa bat|$2 berrikusketa}} ez {{PLURAL:$2|da|dira}} aurkitu.\n\nHau, orokorrean ezabatu egin den orri batera deskonektatua dagoen esteka desegonkor baten ondorioz gertatzen da.\n\nHemen xehetasunak aurki daitezke: [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} deletion log].",
        "searchresults": "Bilaketaren emaitzak",
+       "search-filter-title-prefix": "\"$1\" hasiera duten orriak bakarrik bilatzen",
+       "search-filter-title-prefix-reset": "Orri guztiak bilatu",
        "searchresults-title": "«$1» bilaketaren  emaitzak",
        "titlematches": "Emaitzak artikuluen izenburuetan",
        "textmatches": "Emaitza orrialde testuetan",
        "speciallogtitlelabel": "Helburua (izenburua edo {{ns:user}}: lankidea):",
        "log": "Erregistroak",
        "logeventslist-submit": "Erakutsi",
-       "logeventslist-more-filters": "Iragazki gehiago:",
+       "logeventslist-more-filters": "Erakutsi erregistro gehiago:",
        "logeventslist-tag-log": "Etiketen erregistroa",
        "all-logs-page": "Erregistro publiko guztiak",
        "alllogstext": "{{SITENAME}} orrialdearen erregistro guztien erakusketa konbinatua.\nErregistro mota, erabiltzailearen izena edota orrialdearen izena iragaziz bistaratu daiteke. Letra larriak eta xeheak bereizten dira.",
index 7bbd46b..57a0bd0 100644 (file)
        "stub-threshold": "Tynkälinkkien muotoilun kynnysarvo ($1):",
        "stub-threshold-sample-link": "näyte",
        "stub-threshold-disabled": "Ei käytössä",
-       "recentchangesdays": "Näytettävien päivien määrä tuoreissa&nbsp;muutoksissa",
+       "recentchangesdays": "Näytettävien päivien määrä tuoreissa&nbsp;muutoksissa:",
        "recentchangesdays-max": "Enintään $1 {{PLURAL:$1|päivä|päivää}}",
-       "recentchangescount": "Näytettävien muutoksien määrä tuoreissa muutoksissa, sivujen historioissa ja logeissa oletuksena",
+       "recentchangescount": "Näytettävien muutoksien määrä tuoreissa muutoksissa, sivuhistorioissa ja lokeissa oletusarvoisesti:",
        "prefs-help-recentchangescount": "Maksimiluku 1000",
        "prefs-help-watchlist-token2": "Tämä on salainen avain tarkkailulistasi verkkosyötteeseen.\nKuka tahansa, joka tietää sen voi lukea tarkkailulistaasi, joten älä paljasta sitä.\nJos sinun täytyy, [[Special:ResetTokens|voit uudistaa sen]].",
        "prefs-help-tokenmanagement": "Voit nähdä ja nollata tilisi salaisen avaimen, jota käyttämällä pääset katsomaan tarkastuslistasi verkkosyötettä. Kuka tahansa sen tietävä voi lukea tarkkailulistaasi, joten älä paljasta sitä.",
        "uctop": "(uusin)",
        "month": "Alkaen kuukaudesta (ja aiemmin):",
        "year": "Vuosi",
-       "date": "Alkaen päiväydestä (ja aiemmin):",
+       "date": "Alkaen päivämäärästä (tai sitä aikaisemmasta):",
        "sp-contributions-newbies": "Näytä uusien tulokkaiden muutokset",
        "sp-contributions-newbies-sub": "Uusien käyttäjien muokkaukset",
        "sp-contributions-newbies-title": "Uusien käyttäjien muokkaukset",
index 723ab6d..f7baf07 100644 (file)
        "diff-paragraph-moved-toold": "Le paragraphe a été déplacé. Cliquez pour accéder à l'ancien emplacement.",
        "difference-missing-revision": "{{PLURAL:$2|Une révision|$2 révisions}} de cette différence ($1) {{PLURAL:$2|n’a pas été trouvée|n’ont pas été trouvées}}.\n\nCela survient en général en suivant un lien de différence désuet vers une page qui a été supprimée.\nVous pouvez trouver des détails dans le [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} journal des suppressions].",
        "searchresults": "Résultats de la recherche",
-       "search-filter-title-prefix": "Recherche seulement des pages dont le titre commence par « $1 »",
+       "search-filter-title-prefix": "Recherche seulement les pages dont le titre commence par « $1 »",
        "search-filter-title-prefix-reset": "Rechercher toutes les pages",
        "searchresults-title": "Résultats de recherche pour « $1 »",
        "titlematches": "Correspondances dans les titres des pages",
        "prevn-title": "$1 {{PLURAL:$1|résultat précédent|résultats précédents}}",
        "nextn-title": "$1 {{PLURAL:$1|résultat suivant|résultats suivants}}",
        "shown-title": "Afficher $1 résultat{{PLURAL:$1||s}} par page",
-       "viewprevnext": "Voir ($1 {{int:pipe-separator}} $2) ($3).",
+       "viewprevnext": "Voir ($1 {{int:pipe-separator}} $2) ($3)",
        "searchmenu-exists": "<strong>Il existe une page nommée « [[:$1]] » sur ce wiki.</strong> {{PLURAL:$2|0=|Voyez également les autres résultats de votre recherche.}}",
        "searchmenu-new": "<strong>Créer la page « [[:$1|$1]] » sur ce wiki !</strong> {{PLURAL:$2|0=Voyez également la page trouvée avec votre recherche.|Voyez également les résultats de votre recherche.}}",
        "searchprofile-articles": "Pages de contenu",
        "recentchanges-legend-plusminus": "(''±123'')",
        "recentchanges-submit": "Lister",
        "rcfilters-tag-remove": "Supprimer « $1 »",
-       "rcfilters-legend-heading": "<strong>Liste des abréviations :</strong>",
+       "rcfilters-legend-heading": "<strong>Liste des abréviations : </strong>",
        "rcfilters-other-review-tools": "Autres outils de relecture",
        "rcfilters-group-results-by-page": "Grouper les résultats par page",
        "rcfilters-activefilters": "Filtres actifs",
        "http-timed-out": "La requête HTTP a expiré.",
        "http-curl-error": "Erreur lors de la récupération de l'URL : $1",
        "http-bad-status": "Il y a eu un problème lors de la requête HTTP : $1 $2",
+       "http-internal-error": "Erreur interne HTTP.",
        "upload-curl-error6": "URL injoignable",
        "upload-curl-error6-text": "L’URL fournie ne peut pas être atteinte. \nVeuillez vérifier que l’URL est correcte et que le site est en ligne.",
        "upload-curl-error28": "Dépassement du délai lors de l'import",
index 6ebfeab..9bdd762 100644 (file)
        "rcfilters-filterlist-whatsthis": "Ciamar a dh’obraicheas iad seo?",
        "rcfilters-highlightbutton-title": "Soillsich toraidhean",
        "rcfilters-filterlist-noresults": "Cha do lorg sinn criathrag",
+       "rcfilters-filter-editsbyself-label": "Mùthaidhean a rinn thusa",
+       "rcfilters-filter-editsbyself-description": "Na mùthaidhean a rinn thu fhèin.",
+       "rcfilters-filter-editsbyother-label": "Mùthaidhean a rinn daoine eile",
+       "rcfilters-filter-editsbyother-description": "Gach mùthadh ach an fheadhainn agad fhèin.",
        "rcfilters-filtergroup-userExpLevel": "Clàradh is eòlas a’ chleachdaiche",
        "rcfilters-filter-user-experience-level-registered-label": "Air clàradh a-steach",
+       "rcfilters-filter-user-experience-level-registered-description": "Deasaichean a rinn clàradh a-steach.",
        "rcfilters-filter-user-experience-level-unregistered-label": "Gun chlàradh a-steach",
+       "rcfilters-filter-user-experience-level-unregistered-description": "Deasaichean nach do rinn clàradh a-steach.",
+       "rcfilters-filter-user-experience-level-newcomer-label": "Ùranaich",
        "rcfilters-filter-user-experience-level-newcomer-description": "Deasaichean a chlàraich a-steach ach a rinn nas lugha na 10 deasachaidhean no aig a bheil nas lugha na 4 làithean de ghnìomhachd.",
+       "rcfilters-filter-user-experience-level-learner-label": "Luchd-ionnsachaidh",
        "rcfilters-filter-user-experience-level-learner-description": "Deasaichean a rinn clàradh a-steach is aig a bheil eòlas eadar feadhainn air ùr-thighinn is feadhainn shàr-eòlach.",
+       "rcfilters-filter-user-experience-level-experienced-label": "Seann-eòlaich",
        "rcfilters-filter-user-experience-level-experienced-description": "Deasaichean a chlàraich a-steach ach a rinn barrachd air 500 deasachadh is aig a bheil barrachd air 30 latha de ghnìomhachd.",
+       "rcfilters-filtergroup-automated": "Mùthaidhean fèin-obrachail",
+       "rcfilters-filter-bots-description": "Mùthaidhean a rinn innealan fèin-obrachail.",
+       "rcfilters-filter-humans-label": "Duine (chan e bot)",
+       "rcfilters-filter-humans-description": "Deasachaidhean a rinn deasaichean daonna.",
+       "rcfilters-filtergroup-significance": "Cudromachd",
+       "rcfilters-filter-minor-label": "Mùthaidhean beaga",
+       "rcfilters-filter-major-label": "Mùthaichean nach eil beag",
+       "rcfilters-filter-major-description": "Deasachaidhean ris nach eil comharra gur e deasachadh beag a tha ann.",
        "rcfilters-filtergroup-watchlistactivity": "Gnìomhachd a’ chlàir-fhaire",
        "rcfilters-filter-watchlistactivity-unseen-label": "Atharraichean gun choimhead",
        "rcfilters-filter-watchlistactivity-unseen-description": "Atharraichean air duilleagan air nach do thadhail thu on a chaidh an atharrachadh.",
        "rcfilters-filter-newpages-description": "Deasachadh a chruthaich duilleag ùr.",
        "rcfilters-filter-logactions-label": "Gnìomhan logaichte",
        "rcfilters-filtergroup-lastRevision": "Na mùthaidhean as ùire",
+       "rcfilters-filter-lastrevision-label": "Am mùthadh mu dheireadh",
        "rcfilters-filter-lastrevision-description": "Dìreach an t-àtharrachadh as ùire air duilleag.",
        "rcfilters-liveupdates-button": "Ùrachadh beò",
        "rcfilters-liveupdates-button-title-off": "Seall atharraichean ùra fhad ’s a thathar gan dèanamh",
        "rcfilters-watchlist-markseen-button": "Comharraich gun dug thu sùil air gach atharrachadh",
        "rcfilters-watchlist-edit-watchlist-button": "Deasaich liosta nan duilleagan air a’ chlàr-fhaire agad",
+       "rcfilters-watchlist-showupdated": "Tha atharraichean air duilleagan nach do thadhail thu orra on a chaidh an atharrachadh ann an clò <strong>trom</strong> le comharran soladach.",
        "rcnotefrom": "Chì thu {{PLURAL:$5|am mùthadh|na mùthaidhean|na mùthaidhean|na mùthaidhean}} o <strong>$3 $4</strong> (gu ruige <strong>$1</strong> dhiubh) gu h-ìosal.",
        "rclistfrom": "Seall na mùthaidhean ùra a-mach o $3 $2",
        "rcshowhideminor": "$1 mùthaidhean beaga",
index 66fc366..0dd4a49 100644 (file)
        "diff-paragraph-moved-toold": "הפסקה הועברה. ניתן ללחוץ כאן כדי לעבור למיקומה הישן.",
        "difference-missing-revision": "{{PLURAL:$2|גרסה אחת|$2 גרסאות}} מתוך הגרסאות שביקשת להשוות ($1) {{PLURAL:$2|לא נמצאה|לא נמצאו}}.\n\nזה נגרם בדרך־כלל עקב לחיצה על קישור ישן להבדלים בין גרסאות של דף שנמחק.\nאפשר למצוא פרטים ב[{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} יומן המחיקות].",
        "searchresults": "תוצאות החיפוש",
+       "search-filter-title-prefix": "חיפוש רק בדפים ששמם מתחיל ב\"$1\"",
+       "search-filter-title-prefix-reset": "חיפוש בכל הדפים",
        "searchresults-title": "תוצאות החיפוש \"$1\"",
        "titlematches": "כותרות דפים תואמות",
        "textmatches": "דפים עם תוכן תואם",
        "speciallogtitlelabel": "יעד (שם הדף, או \"{{ns:user}}:שם\" עבור משתמש):",
        "log": "יומנים",
        "logeventslist-submit": "הצגה",
-       "logeventslist-more-filters": "×\9eסננים נוספים:",
+       "logeventslist-more-filters": "×\94צ×\92ת ×\99×\95×\9eנים נוספים:",
        "logeventslist-patrol-log": "יומן שינויים בדוקים",
        "logeventslist-tag-log": "יומן תגיות",
        "all-logs-page": "כל היומנים הציבוריים",
index ccdca67..dc678af 100644 (file)
        "magiclink-tracking-pmid-desc": "Ova stranica rabi čarobne PMID poveznice. Za njihovu migraciju vidi [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Magic_links mediawiki.org].",
        "magiclink-tracking-isbn": "Stranice s čarobnim ISBN poveznicama",
        "magiclink-tracking-isbn-desc": "Ova stranica rabi čarobne ISBN poveznice. Za njihovu migraciju vidi [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Magic_links mediawiki.org].",
-       "specialloguserlabel": "Suradnik:",
-       "speciallogtitlelabel": "Cilj (naslov ili {{ns:user}}:suradničko ime):",
+       "specialloguserlabel": "Izvršitelj:",
+       "speciallogtitlelabel": "Cilj (naslov ili {{ns:user}}:suradničko ime za suradnika):",
        "log": "Evidencije",
        "logeventslist-submit": "Prikaži",
+       "logeventslist-more-filters": "Prikaži dodatne evidencije:",
+       "logeventslist-patrol-log": "Evidencija ophodnje",
+       "logeventslist-tag-log": "Evidencija oznaka",
        "all-logs-page": "Sve javne evidencije",
        "alllogstext": "Skupni prikaz svih dostupnih evidencija projekta {{SITENAME}}.\nMožete suziti prikaz odabirući tip evidencije, suradničko ime ili stranicu u upitu.",
        "logempty": "Nema pronađenih stavki u evidenciji.",
        "dellogpage": "Evidencija brisanja",
        "dellogpagetext": "Dolje je popis nedavnih brisanja.\nSva vremena su prema poslužiteljevom vremenu.",
        "deletionlog": "evidencija brisanja",
+       "log-name-create": "Evidencija stvaranja stranica",
        "logentry-create-create": "$1 {{GENDER:$2|stvorio|stvorila}} je stranicu $3",
        "reverted": "Vraćeno na prijašnju inačicu",
        "deletecomment": "Razlog:",
        "uctop": "(vrh)",
        "month": "Od mjeseca (i ranije):",
        "year": "Od godine (i ranije):",
+       "date": "Do nadnevka (i prije):",
        "sp-contributions-newbies": "Prikaži samo doprinose novih suradnika",
        "sp-contributions-newbies-sub": "Za nove suradnike",
        "sp-contributions-newbies-title": "Doprinosi novih suradnika",
index a21edc9..8552dfc 100644 (file)
        "diff-paragraph-moved-toold": "A szakaszt áthelyezték. Kattints ide a régi helyére való ugráshoz.",
        "difference-missing-revision": "Az összehasonlítandó változatok {{PLURAL:$2|egyike ($1) nem található|($1) nem találhatóak}}.\n\nEzt általában egy elavult, törölt oldalra mutató laptörténeti hivatkozás használata okozza. Részletek a [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} törlési naplóban] találhatóak.",
        "searchresults": "A keresés eredménye",
+       "search-filter-title-prefix": "Keresés csak olyan lapokban, amelyek címe a következőképp kezdődik: „$1”",
+       "search-filter-title-prefix-reset": "Keresés az összes lapban",
        "searchresults-title": "Keresési eredmények: „$1”",
        "titlematches": "Címbeli egyezések",
        "textmatches": "Szövegbeli egyezések",
index f18538b..153d249 100644 (file)
        "http-timed-out": "Richiesta HTTP scaduta.",
        "http-curl-error": "Errore durante il recupero dell'URL: $1",
        "http-bad-status": "Si è verificato un problema durante la richiesta HTTP: $1 $2",
+       "http-internal-error": "Errore interno HTTP.",
        "upload-curl-error6": "URL non raggiungibile",
        "upload-curl-error6-text": "Impossibile raggiungere la URL specificata. Verificare che la URL sia scritta correttamente e che il sito in questione sia attivo.",
        "upload-curl-error28": "Tempo scaduto per l'upload",
        "speciallogtitlelabel": "Azione effettuata su (titolo della pagina o {{ns:user}}:Nome utente):",
        "log": "Registri",
        "logeventslist-submit": "Mostra",
-       "logeventslist-more-filters": "Altri filtri:",
+       "logeventslist-more-filters": "Mostra registri aggiuntivi:",
        "logeventslist-patrol-log": "Modifiche verificate",
        "logeventslist-tag-log": "Etichette",
        "all-logs-page": "Tutti i registri pubblici",
index d4fa406..66df3aa 100644 (file)
        "policy-url": "Project:Kabijakan",
        "portal": "Gapura paguyuban",
        "portal-url": "Project:Garupa paguyuban",
-       "privacy": "Pranatan bab privasi",
-       "privacypage": "Project:Pranatan bab privasi",
+       "privacy": "Niti privasi",
+       "privacypage": "Project:Niti privasi",
        "badaccess": "Aksès ora olèh",
        "badaccess-group0": "Panjenengan ora pareng nglakokaké tindhakan sing panjenengan gayuh.",
        "badaccess-groups": "Tumindak sing panjenengan péngini winates marang panganggo ing {{PLURAL:$2|golongan|golongan}}: $1.",
        "title-invalid-empty": "Sesirah kaca sing dikarepaké kosong utawa mung ngemu jenengé mandala-aran.",
        "title-invalid-utf8": "Sesirah kaca sing dikarepaké ngemu reroncèn UTF-8 sing ora sah.",
        "title-invalid-interwiki": "Sesirah kaca sing dikarepaké ngemu pranala interwiki sing ora bisa dicakaké dadi sesirah",
-       "title-invalid-talk-namespace": "Sesirah kaca sing dikarepaké nggayut kaca parembugan sing ora ana.",
+       "title-invalid-talk-namespace": "Sesirah kaca kang dikarepaké ngener ing kaca parembugan kang ora ana.",
        "title-invalid-characters": "Sesirah kaca sing dikarepaké ngemu karakter sing ora sah: \"$1\".",
        "title-invalid-relative": "Sesirah ngemu alamat rélatif. Sesirah kaca relatif (./, ../) iku ora sah amarga ora bisa ditekani lumantar pangluru.",
        "title-invalid-magic-tilde": "Sesirah kaca sing dikarepaké ngemu reroncèn tilda (<nowiki>~~~</nowiki>) sing ora sah.",
        "newpageletter": "A",
        "boteditletter": "b",
        "number_of_watching_users_pageview": "[$1 {{PLURAL:$1|cacahé sing ngawasi|cacahé sing ngawasi}}]",
-       "rc-change-size-new": "$1 {{PLURAL:$1|bét|bét}} sawisé diowahi",
+       "rc-change-size-new": "$1 {{PLURAL:$1|bèt|bèt}} sawisé kaowahan",
        "newsectionsummary": "/* $1 */ pérangan anyar",
        "rc-enhanced-expand": "Tuduhaké princèn",
        "rc-enhanced-hide": "Dhelikaké princèn",
        "sp-contributions-newonly": "Tuduhaké besutan mligi kaca gawéan",
        "sp-contributions-hideminor": "Dhelikaké besutan cilik",
        "sp-contributions-submit": "Golèk",
-       "whatlinkshere": "Sing nggayut mréné",
+       "whatlinkshere": "Kang nggayut mréné",
        "whatlinkshere-title": "Kaca mawa pranala nggayut \"$1\"",
        "whatlinkshere-page": "Kaca:",
        "linkshere": "Kaca-kaca ing ngisor iki nggayut menyang <strong>$2</strong>:",
        "tooltip-ca-watch": "Tambahaké kaca iki menyang pawawangané panjenengan",
        "tooltip-ca-unwatch": "Busak kaca iki saka pawawanganing panjenengan",
        "tooltip-search": "Golèk ing {{SITENAME}}",
-       "tooltip-search-go": "Jujug kaca asesirah persis mangkéné yèn ana",
-       "tooltip-search-fulltext": "Golèk kaca isi tèks kaya mangkéné",
+       "tooltip-search-go": "Jujug kaca kang sesirah persis mangkéné yèn ana",
+       "tooltip-search-fulltext": "Golèk kaca kang isi tèks mangkéné",
        "tooltip-p-logo": "Menyang tepas",
        "tooltip-n-mainpage": "Menyang tepas",
        "tooltip-n-mainpage-description": "Menyang tepas",
        "tooltip-n-recentchanges": "Pratélaning owah-owahan anyar sajeroning wiki.",
        "tooltip-n-randompage": "Mot sembarang kaca",
        "tooltip-n-help": "Papan kanggo golèk pitulung",
-       "tooltip-t-whatlinkshere": "Pratélané kabèh kaca wiki sing nggayut mréné",
+       "tooltip-t-whatlinkshere": "Pratélan kabèh kaca wiki kang 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": "Pakan atom tumrap kaca iki",
        "tooltip-t-specialpages": "Pratélaning kabèh kaca mirunggan",
        "tooltip-t-print": "Vèrsi céthak kaca iki",
        "tooltip-t-permalink": "Pranala permanèn saka owahan iki",
-       "tooltip-ca-nstab-main": "Deleng kaca isi",
+       "tooltip-ca-nstab-main": "Deleng kaca kontèn",
        "tooltip-ca-nstab-user": "Deleng kaca panganggo",
        "tooltip-ca-nstab-media": "Deleng kaca médhia",
        "tooltip-ca-nstab-special": "Iki kaca mirunggan lan ora bisa dibesut",
index abb26c9..e130589 100644 (file)
        "tag-filter": "[[Special:Tags|ស្លាក​]] តម្រង​:",
        "tag-filter-submit": "តម្រង",
        "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|ស្លាក|ស្លាក}}]]: $2)",
+       "tag-mw-undo": "មិនធ្វើវិញ",
        "tags-title": "ស្លាក​",
        "tags-intro": "ទំព័រ​រាយ​នាម​ស្លាក​ទាំង​ឡាយ​ដែល​កម្មវិធី​ software អាចកត់​សម្គាល់កំណែ​ជាមួយ​ និង​អត្ថ​ន័យ​របស់​វា។​",
        "tags-tag": "ឈ្មោះ​ស្លាក",
index 9f8a587..e87831c 100644 (file)
        "diff-paragraph-moved-toold": "문단이 이동되었습니다. 오래된 위치로 이동하려면 클릭하십시오.",
        "difference-missing-revision": "문서 비교에서 {{PLURAL:$2|하나|$2개}}의 판($1)을 찾을 수 {{PLURAL:$2|없습니다}}.\n\n이 문제는 주로 삭제된 문서를 가리키는 오래된 문서 비교 링크로 인해 발생합니다.\n자세한 내용은 [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} 삭제 기록]에서 확인할 수 있습니다.",
        "searchresults": "검색 결과",
+       "search-filter-title-prefix-reset": "모든 문서 검색",
        "searchresults-title": "\"$1\"에 대한 검색 결과",
        "titlematches": "문서 제목 일치",
        "textmatches": "문서 내용 일치",
        "http-timed-out": "HTTP 요청 시간 초과입니다.",
        "http-curl-error": "URL 열기 오류: $1",
        "http-bad-status": "HTTP 요청 중 오류 발생: $1 $2",
+       "http-internal-error": "HTTP 내부 오류.",
        "upload-curl-error6": "URL 접근 불가",
        "upload-curl-error6-text": "URL에 접근할 수 없습니다.\nURL이 맞고 해당 웹사이트가 작동하는지 확인해주세요.",
        "upload-curl-error28": "올리기 시간 초과",
        "speciallogtitlelabel": "대상 (문서 제목 또는 {{ns:user}}:사용자 이름으로 사용자 검색):",
        "log": "기록 목록",
        "logeventslist-submit": "보기",
-       "logeventslist-more-filters": "더 많은 필터:",
+       "logeventslist-more-filters": "추가 기록 표시:",
        "logeventslist-patrol-log": "점검 기록",
        "logeventslist-tag-log": "태그 기록",
        "all-logs-page": "모든 공개 기록",
index 6c41f42..466d339 100644 (file)
        "viewhelppage": "Gweles an folen weres",
        "categorypage": "Gweles folen an klass",
        "viewtalkpage": "Gweles an dadhlow",
-       "otherlanguages": "Yn yethow erel",
-       "redirectedfrom": "(Daskedyes dhyworth $1)",
+       "otherlanguages": "Yn tavosow erel",
+       "redirectedfrom": "(Daskevarwodhys dhyworth $1)",
        "redirectpagesub": "Folen dhaskedya",
-       "lastmodifiedat": "Gwrys veu diwettha chanj an folen ma an $1, dhe $2.",
+       "lastmodifiedat": "Diwettha chanj an folen ma a veu an $1, dhe $2.",
        "protectedpage": "Folen dhifresys",
        "jumpto": "Lamma dhe:",
        "jumptonavigation": "kevrennow lewya",
        "disclaimers": "Avisyansow",
        "disclaimerpage": "Project:Avisyans ollgemmyn",
        "edithelp": "Gweres ow chanjya",
-       "mainpage": "Dynnargh",
-       "mainpage-description": "Dynnargh",
+       "mainpage": "Folen dre",
+       "mainpage-description": "Folen dre",
        "policy-url": "Project:Polici",
        "portal": "Porth an gemeneth",
        "portal-url": "Project:Porth an gemeneth",
        "toc": "Rol an folen",
        "showtoc": "diskwedhes",
        "hidetoc": "kudha",
+       "collapsible-collapse": "Diskara",
        "collapsible-expand": "Efani",
        "thisisdeleted": "Gweles po daskor $1?",
        "viewdeleted": "Gweles $1?",
        "site-atom-feed": "Feed Atom $1",
        "page-rss-feed": "Feed RSS \"$1\"",
        "page-atom-feed": "Feed Atom \"$1\"",
-       "red-link-title": "$1 (nyns eus folen henwys yndelma)",
+       "red-link-title": "$1 (nyns eus an folen ma)",
        "nstab-main": "Folen",
        "nstab-user": "Folen dhevnydhyer",
        "nstab-media": "Folen media",
        "nstab-template": "Skantlyn",
        "nstab-help": "Gweres",
        "nstab-category": "Klass",
+       "mainpage-nstab": "Folen dre",
        "error": "Gwall",
        "databaseerror": "Gwall database",
        "readonly": "Alhwedhys yw an database",
        "badtitle": "Titel drog",
        "viewsource": "Gweles an bennfenten",
        "viewsource-title": "Gweles an bennfenten rag $1",
-       "protectedpagetext": "Difresys re beu an folen-ma rag gwitha rag chanjya po gwriansow erel.",
-       "viewsourcetext": "Hwi a yll gweles ha kopia pennfenten an folen-ma:",
+       "protectedpagetext": "Difresys yw an folen ma dhe witha rag chanjya po gwriansow erel.",
+       "viewsourcetext": "Hwi a yll gweles ha kopia pennfenten an folen ma.",
        "mycustomcssprotected": "Ny'gas beus kummyes dhe janjya an folen CSS-ma.",
        "mycustomjsprotected": "Ny'gas beus kummyes dhe janjya an folen JavaScript-ma.",
        "ns-specialprotected": "Ny yllir chanjya folennow arbennek.",
        "password-change-forbidden": "Ny yllir chanjya geryow tremena war an wiki-ma.",
        "login": "Omgelmi",
        "nav-login-createaccount": "Omgelmi / Gwruthyl akont nowyth",
-       "logout": "Digelmi",
-       "userlogout": "Digelmi",
+       "logout": "Omdenna",
+       "userlogout": "Omdenna",
        "notloggedin": "Digelmys",
        "userlogin-noaccount": "A nyns eus akont dhywgh?",
        "userlogin-joinproject": "Junya {{SITENAME}}",
        "pt-login": "Omgelmi",
        "pt-login-button": "Omgelmi",
        "pt-createaccount": "Gwruthyl akont",
-       "pt-userlogout": "Digelmi",
+       "pt-userlogout": "Omdenna",
        "changepassword": "Chanjya an ger-tremena",
        "resetpass_announce": "Hwi a omgelmis dre goden ebostyes anbarthus.\nRag gorfenna omgelmi, res yw dhywgh settya ger tremena nowyth omma:",
        "resetpass_header": "Chanjya ger tremena an akont",
        "media_tip": "Kevren restren",
        "sig_tip": "Agas sinans ha stampa-termyn",
        "summary": "Berrskrif:",
-       "subject": "Testen/Pennlinen:",
+       "subject": "Testen:",
        "minoredit": "Chanj byghan yw hemma",
        "watchthis": "Golya an folen ma",
        "savearticle": "Gwitha an folen",
+       "savechanges": "Gwitha an chanjyow",
+       "publishpage": "Dyllo an folen",
+       "publishchanges": "Dyllo an chanjyow",
+       "savearticle-start": "Gwitha an folen…",
+       "savechanges-start": "Gwitha an chanjyow…",
+       "publishpage-start": "Dyllo an folen…",
+       "publishchanges-start": "Dyllo an chanjyow…",
        "preview": "Ragwel",
        "showpreview": "Diskwedhes ragwel",
        "showdiff": "Diskwedhes an chanjyow",
+       "blankarticle": "<strong>Bedhowgh war:</strong> Gwag yw an folen esowgh ow kul.\nMar klyckyowgh \"$1\" arta, an folen a vydh gwrys heb dalgh.",
        "anoneditwarning": "<strong>Gwarnyans:</strong> Nyns owgh hwi omgelmys. Gweladow yn foblek vydh agas trigva IP mar kwrewgh chanjyow. Mars <strong>[$1 omgelmowgh]</strong> po <strong>[$2 gwruthyl akont]</strong>, agas chanjyow a vydh askrifys dhe'gas hanow devnydher, keffrys ha lesow erel.",
        "anonpreviewwarning": "''Nyns owgh omgelmys. Dre witha, agas trigva IP a vydh rekordyes istori chanjya an folen-ma.''",
+       "missingsummary": "<strong>Bedhowgh war:</strong> Nyns eus berrskrif skrifys genowgh.\nMar klyckyowgh \"$1\" arta, agas chanj a vydh gwithys heb berrskrif.",
        "summary-preview": "Ragwel a'n berrskrif:",
        "loginreqtitle": "Res yw omgelmi",
        "loginreqlink": "omgelmi",
        "accmailtitle": "Ger-tremena danvenys.",
        "newarticle": "(Nowyth)",
        "newarticletext": "Hwi re holyas kevren dhe folen nag yw gwruthys hwath.\nRag gwruthyl an folen, dallethewgh jynnskrifa y'n gist a-woles (gwelewgh an [$1 folen weres] rag moy kedhlow).\nMar teuthowgh omma yn kamm, klyckyewgh boton '''war-dhelergh''' agas peurell.",
-       "noarticletext": "Nyns eus tekst y'n folen ma a-lemmyn.\nHwi a yll [[Special:Search/{{PAGENAME}}|hwilas titel an folen ma]] yn folennow erel,\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} hwilas y'n kovnotennow kelmys],\npo [{{fullurl:{{FULLPAGENAME}}|action=edit}} chanjya an folen ma]</span>.",
+       "noarticletext": "Nyns eus tekst y'n folen ma a-lemmyn.\nHwi a yll [[Special:Search/{{PAGENAME}}|hwilas titel an folen ma]] yn folennow erel,\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} hwilas y'n kovnotennow kelmys],\npo [{{fullurl:{{FULLPAGENAME}}|action=edit}} gwruthyl an folen ma]</span>.",
        "noarticletext-nopermission": "Nyns eus tekst y'n folen ma a-lemmyn.\nHwi a yll [[Special:Search/{{PAGENAME}}|hwilas titel an folen ma]] yn folennow erel, po <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} hwilas y'n kovnotennow kelmys]</span>, mes ny'gas beus kummyas dhe wruthyl an folen ma.",
        "userpage-userdoesnotexist": "Nyns yw kovskrifys an akont devnydhyer \"$1\".\nCheckyewgh mar pleg mara'gas beus hwans dhe wruthyl/dhe janjya an folen-ma.",
        "userpage-userdoesnotexist-view": "Nyns yw kovskrifys an akont devnydhyer \"$1\".",
        "viewpagelogs": "Gweles kovnotennow an folen ma",
        "currentrev": "Amendyans diwettha",
        "currentrev-asof": "An amendyans diwettha a-dhia $1",
-       "revisionasof": "Versyon an folen a-dhia $1",
+       "revisionasof": "Versyon a-dhia $1",
        "revision-info": "Amendyans a-dhia $1 gans {{GENDER:$6|$2}}$7",
        "previousrevision": "← Amendyans kottha",
        "nextrevision": "Amendyans nowyttha →",
        "compareselectedversions": "Keheveli an amendyansow dewisyes",
        "showhideselectedversions": "Diskwedhes/kudha amendyansow dewisyes",
        "editundo": "diswul",
-       "searchresults": "Sewyansow an hwilans",
-       "searchresults-title": "Sewyansow an hwilans rag \"$1\"",
+       "searchresults": "Sewyansow hwilas",
+       "searchresults-title": "Sewyansow hwilas rag \"$1\"",
        "notextmatches": "Nyns eus tekst folen vyth owth omdhesedha",
        "prevn": "{{PLURAL:$1|$1}} kyns",
        "nextn": "nessa {{PLURAL:$1|$1}}",
        "prevn-title": "$1 {{PLURAL:$1|sewyans}} kyns",
        "nextn-title": "$1 {{PLURAL:$1|sewyans}} nessa",
-       "shown-title": "Diskwedhes $1 {{PLURAL:$1|sewyans}} yn folen",
+       "shown-title": "Diskwedhes $1 {{PLURAL:$1|sewyans}} war pub folen",
        "viewprevnext": "Gweles ($1 {{int:pipe-separator}} $2) ($3)",
        "searchmenu-exists": "''Yma folen henwys \"[[:$1]]\" war an wiki-ma'''",
        "searchmenu-new": "<strong>Gwrewgh an folen \"[[:$1]]\" war an wiki ma!</strong> {{PLURAL:$2|0=|Gweler ynwegh an folen a veu kevys yn unn hwilas.|Gweler ynwedh an sewyans hwilas kevys.}}",
        "searchprofile-advanced-tooltip": "Hwilas yn spasys hanow personelhes",
        "search-result-size": "$1 ({{PLURAL:$2|1 ger|$2 ger}})",
        "search-result-category-size": "{{PLURAL:$1|1 esel|$1 esel}} ({{PLURAL:$2|1 isglass|$2 isglass}}, {{PLURAL:$3|1 restren|$3 restren}})",
-       "search-redirect": "(daskedyans $1)",
+       "search-redirect": "(daskevarwodhyans a $1)",
        "search-section": "(tregh $1)",
        "search-suggest": "Esewgh hwi ow menya: $1",
        "search-interwiki-caption": "Ragdresow hwor",
        "searchrelated": "kelmys",
        "searchall": "oll",
        "search-showingresults": "{{PLURAL:$4|Sewyans <strong>$1</strong> a <strong>$3</strong>|Sewyansow <strong>$1 - $2</strong> a <strong>$3</strong>}}",
-       "search-nonefound": "Nyns esa sewyans vyth owth omdhesedha orth an govyn.",
+       "search-nonefound": "Ny veu sewyans vyth par dhe'n govyn.",
        "powersearch-legend": "Hwilans avonsys",
        "powersearch-ns": "Hwilas yn spasys-hanow:",
        "powersearch-togglelabel": "Dewis:",
        "newuserlogpage": "Kovnoten gwruthyl akontow devnydhyer",
        "rightslog": "Kovnoten wiryow an devnydhyer",
        "action-edit": "chanjya an folen-ma",
-       "action-move": "gwaya an folen-ma",
+       "action-move": "gwaya an folen ma",
        "action-movefile": "gwaya an restren-ma",
        "action-upload": "ughkarga an restren-ma",
-       "action-delete": "dilea an folen-ma",
+       "action-delete": "dilea an folen ma",
        "nchanges": "$1 {{PLURAL:$1|chanj|chanj}}",
        "enhancedrc-history": "istori",
        "recentchanges": "Chanjyow a-dhiwedhes",
-       "recentchanges-legend": "Etholyow an chanjyow a-dhiwedhes",
-       "recentchanges-summary": "War an folen-ma y hyllir helerghi an chanjyow diwettha eus gwrys dhe'n wiki.",
+       "recentchanges-legend": "Dewisyow an chanjyow a-dhiwedhes",
+       "recentchanges-summary": "Homm yw rol a janjyow a-dhiwedhes dhe'n wiki ma.",
        "recentchanges-noresult": "Nyns eus chanj vyth dres an termyn res a omdhesedh orth an etholyow-ma.",
        "recentchanges-feed-description": "Y hyllir helerhi an chanjyow diwettha gwrys dhe'n wiki y'n feed-ma.",
-       "recentchanges-label-newpage": "Gans an chanj ma y feu gwruthys folen nowydh",
-       "recentchanges-label-minor": "Chanj byghan yw hemma",
-       "recentchanges-label-bot": "Gwrys veu an chanj ma gans bott",
-       "recentchanges-label-unpatrolled": "Ny veu an chanj ma patrolyes hwath",
+       "recentchanges-label-newpage": "Folen nowydh",
+       "recentchanges-label-minor": "Chanj byghan",
+       "recentchanges-label-bot": "Chanj gans bott",
+       "recentchanges-label-unpatrolled": "Ny veu patrolyes hwath",
+       "recentchanges-label-plusminus": "Chanj dhe vyns an folen (baytys)",
        "recentchanges-legend-heading": "<strong>Alhwedh:</strong>",
-       "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (gweler ynwedh an [[Special:NewPages|rol a folennow nowydh]])",
-       "rclistfrom": "Diskwedhes chanjyow nowydh yn unn dhalleth dhyworth $3 $2",
+       "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} ([[Special:NewPages|rol leun]])",
+       "rcfilters-legend-heading": "<strong>Berrheansow:</strong>",
+       "rcfilters-activefilters": "Sidhlow byw",
+       "rcfilters-days-show-days": "$1 {{PLURAL:$1|jydh|dydh}}",
+       "rcfilters-search-placeholder": "Sidhla an chanjyow (usyowgh an rol po hwilas hanow an sidhel)",
+       "rcfilters-filter-humans-label": "Den (nyns yw bott)",
+       "rcfilters-filter-pageedits-label": "Chanjyow dhe folennow",
+       "rcfilters-filter-newpages-label": "Folennow gwrys",
+       "rcfilters-liveupdates-button": "Nowedhansow byw",
+       "rclistfrom": "Diskwedhes chanjyow nowydh a-dhia $3 $2",
        "rcshowhideminor": "$1 chanjyow byghan",
        "rcshowhideminor-hide": "Kudha",
        "rcshowhidebots": "$1 bottow",
        "rcshowhidebots-show": "Diskwedhes",
+       "rcshowhidebots-hide": "Kudha",
        "rcshowhideliu": "$1 devnydhoryon govskrifys",
        "rcshowhideliu-hide": "Kudha",
        "rcshowhideanons": "$1 devnydhyoryon dhihanow",
        "rcshowhideanons-show": "Diskwedhes",
        "rcshowhideanons-hide": "Kudha",
+       "rcshowhidepatr-hide": "Kudha",
        "rcshowhidemine": "$1 ow chanjyow",
        "rcshowhidemine-hide": "Kudha",
+       "rcshowhidecategorization-hide": "Kudha",
        "rclinks": "Diskwedhes an $1 chanj diwettha gwrys y'n $2 dydh diwettha",
-       "diff": "dyffrans",
+       "diff": "dihevelepter",
        "hist": "istori",
        "hide": "Kudha",
        "show": "Diskwedhes",
        "minoreditletter": "B",
        "newpageletter": "N",
-       "boteditletter": "bott",
+       "boteditletter": "b",
        "rc-change-size-new": "$1 {{PLURAL:$1|byte|bytes}} wosa an chanj",
        "newsectionsummary": "/* $1 */ tregh nowyth",
        "rc-enhanced-expand": "Diskwedhes an manylyon (res yw JavaScript)",
        "listfiles_count": "Versyons",
        "file-anchor-link": "Restren",
        "filehist": "Istori an restren",
-       "filehist-help": "Klyckyewgh war dhedhyans/eur rag gweles an folen dell omdhiskwedhas nena.",
+       "filehist-help": "Klyckyewgh war dhedhyans/eur rag gweles an folen dell o an termyn na.",
        "filehist-deleteall": "dilea oll",
        "filehist-deleteone": "dilea",
        "filehist-revert": "gorthtreylya",
        "filehist-current": "a-lemmyn",
        "filehist-datetime": "Dedhyans/Eur",
        "filehist-thumb": "Skeusennik",
-       "filehist-thumbtext": "Skeusennik rag an versyon a-dhia $1",
+       "filehist-thumbtext": "Skeusennik an versyon a-dhia $1",
        "filehist-nothumb": "Nyns eus skeusennik",
        "filehist-user": "Devnydhyer",
        "filehist-dimensions": "Mynsow",
        "filehist-filesize": "Mens an restren",
-       "filehist-comment": "Kampoll",
+       "filehist-comment": "Kampol",
        "imagelinks": "Devnydh an restren",
        "linkstoimage": "Yma an {{PLURAL:$1|folen|$1 folen}} a syw ow kevrenna dhe'n restren ma:",
        "linkstoimage-more": "Yma moy es $1 {{PLURAL:$1|folen}} ow kevrenna dhe'n restren-ma.\nNy dhiskwa an rol a syw marnas an {{PLURAL:$1|kynsa kevren folen|kynsa $1 kevren folen}} dhe'n restren-ma.\nYma [[Special:WhatLinksHere/$2|rol leun]] kavadow.",
        "nolinkstoimage": "Nyns eus folen vyth ow kevrenna dhe'n restren-ma.",
        "morelinkstoimage": "Gweles [[Special:WhatLinksHere/$1|moy kevrennow]] dhe'n restren-ma.",
-       "sharedupload": "Yma an folen-ma ow tos dhyworth $1 ha hy a alsa bos yn-dann devnydh gans ragdresow erel.",
-       "sharedupload-desc-here": "Yma an restren ma dhe $1 ha ragdresow erel a allsa bos orth hy devnydhya.\nDiskwedhys a-woles yw an deskrifans war hy [$2 folen dheskrifans] ena.",
+       "sharedupload": "An folen ma a dheu a $1 hag a allsa bos usys gans ragdresow erel.",
+       "sharedupload-desc-here": "Yma an restren ma dhe $1 ha ragdresow erel a allsa bos orth hy devnydhya.\nDiskwedhys a-woles yma an deskrifans war hy [$2 folen dheskrifans] ena.",
        "uploadnewversion-linktext": "Ughkarga versyon nowyth a'n restren-ma",
        "filedelete": "Dilea $1",
        "filedelete-legend": "Dilea an restren",
        "listredirects": "Rol an daswedyansow",
        "unusedtemplates": "Skantlyns heb devnydh",
        "unusedtemplateswlh": "kevrennow erel",
-       "randompage": "Folen dre happ",
+       "randompage": "Folen jonsus",
        "statistics": "Statystygyon",
        "statistics-pages": "Folennow",
        "brokenredirects-edit": "chanjya",
        "newpages-username": "Hanow-usyer:",
        "ancientpages": "An kottha folennow",
        "move": "Gwaya",
-       "movethispage": "Gwaya an folen-ma",
+       "movethispage": "Gwaya an folen ma",
        "pager-newer-n": "{{PLURAL:$1|1 nowyttha|$1 nowyttha}}",
        "pager-older-n": "{{PLURAL:$1|1 kottha|$1 kottha}}",
        "booksources": "Pennfentynyow lyver",
        "mywatchlist": "Rol wolya",
        "watchlistfor2": "Rag $1 ($2)",
        "watch": "Golya",
-       "watchthispage": "Golya an folen-ma",
+       "watchthispage": "Golya an folen ma",
        "unwatch": "Diswolya",
        "watchlist-details": "Yma {{PLURAL:$1|$1 folen}} war agas rol wolya, marnas folennow keskows.",
        "wlheader-showupdated": "Yn '''tew''' y tiskwedhir folennow re beu chanjyes a-dhia agas vysytyans diwettha.",
        "wlnote": "A-woles yma an {{PLURAL:$1|chanj diwettha|'''$1''' chanj diwettha}} y'n {{PLURAL:$2|our|'''$2''' our}} diwettha, a-dhia $3, $4.",
        "wlshowlast": "Diskwedhes an $1 our diwettha, an $2 dydh diwettha, po",
+       "watchlist-hide": "Kudha",
        "watchlist-options": "Etholyow an rol wolya",
        "watching": "Ow kolya...",
        "unwatching": "Ow tisgolya...",
        "deletecomment": "Acheson:",
        "deleteotherreason": "Acheson aral/keworansel:",
        "deletereasonotherlist": "Acheson aral",
-       "rollbacklink": "revya war-dhelergh",
+       "rollbacklink": "rolya war-dhelergh",
        "protectlogpage": "Kovnoten dhifres",
        "protectedarticle": "\"[[$1]]\" difresys",
        "prot_1movedto2": "[[$1]] gwayys dhe [[$2]]",
        "sp-contributions-username": "Trigva IP po hanow devnydhyer:",
        "sp-contributions-toponly": "Diskwedhes yn unnik chanjyow yw amendyansow diwettha",
        "sp-contributions-submit": "Hwilas",
-       "whatlinkshere": "Pyth a gevren dhe omma",
+       "whatlinkshere": "Folennow kevrennys",
        "whatlinkshere-title": "Folennow ow kevrenna dhe \"$1\"",
        "whatlinkshere-page": "Folen:",
        "linkshere": "Yma an folennow a syw ow kevrenna dhe '''$2''':",
        "movepagetext": "Devnydhya an furvlen a-woles a dhashenow folen, yn unn waya oll y istori dhe'n hanow nowyth.\nAn titel koth a vydh folen dhaskedyans dhe'n titel nowyth.\nHwi a yll nowedhi daskedyansow a boynt dhe'n titel derowel yn awtomatek.\nMar ny wrewgh, surhewgh hwi dhe jeckya rag [[Special:DoubleRedirects|daskedyansow dobyl]] po [[Special:BrokenRedirects|terrys]].\nOmgemeryansek owgh rag surhe y pes kevrennow poyntya dhe'n tyller ewn.\n\nNotyewgh '''na wayir''' an folen mars eus folen orth an titel nowyth seulabrys, marnas bos an pyth kampollys diwettha daskedyans ha ny'n jeves istori chanjya kyns vyth.\nHemm a styr y hyllowgh diswul dashenwel folen mar kwrewgh kammwrians, ha ny yllowgh gorskrifa folen eus ena seulabrys.\n\n'''Gwarnyans!'''\nHemm a yll bos chanj tromm ha bras dres ehen rag folen gerys-da;\nSurhewgh mar pleg hwi dhe gonvedhes sewyansow an gwrians-ma kyns mos yn-rag.",
        "moveuserpage-warning": "'''Gwarnyans''': Yth esowgh ow mos dhe waya folen dhevnydhyer. Notyewgh mar pleg ny vydh marnas an folen gwayys ha ''ny vydh'' an devnydhyer dashenwys.",
        "newtitle": "Dhe ditel nowyth:",
-       "move-watch": "Golya an folen-ma",
+       "move-watch": "Golya an folen ma",
        "movepagebtn": "Gwaya an folen",
        "pagemovedsub": "Gwaya a sewenis",
        "movepage-moved": "'''Gwayys re beu \"$1\" dhe \"$2\"'''",
        "tooltip-pt-preferences": "Agas dewisyow",
        "tooltip-pt-watchlist": "Rol a folennow esowgh ow kolya rag chanjyow",
        "tooltip-pt-mycontris": "Rol a'gas kevrohow",
-       "tooltip-pt-login": "Ni a gomend hwi dhe omgelmi, mes nyns yw besi",
-       "tooltip-pt-logout": "Digelmi",
-       "tooltip-pt-createaccount": "Ni a gomend hwi dhe wruthyl akont hag omgelmi; byttegyns nyns yw besi",
+       "tooltip-pt-login": "Ni a gomend hwi dhe omgelmi, mes nyns yw bysi",
+       "tooltip-pt-logout": "Omdenna",
+       "tooltip-pt-createaccount": "Ni a gomend hwi dhe wruthyl akont hag omgelmi; byttegyns nyns yw bysi",
        "tooltip-ca-talk": "Dadhlow a-dro dhe'n folen",
        "tooltip-ca-edit": "Chanjya an folen ma",
        "tooltip-ca-addsection": "Dalleth tregh nowydh",
        "tooltip-ca-protect": "Difres an folen-ma",
        "tooltip-ca-delete": "Dilea an folen-ma",
        "tooltip-ca-move": "Gwaya an folen ma",
-       "tooltip-ca-watch": "Keworra an folen ma dhe'gas rol wolya",
+       "tooltip-ca-watch": "Keworra an folen ma dh'agas rol wolya",
        "tooltip-ca-unwatch": "Dilea an folen-ma dhyworth agas rol wolya",
        "tooltip-search": "Hwilas yn {{SITENAME}}",
        "tooltip-search-go": "Mos dhe folen dhedhi an keth hanow ma, mars eus",
        "tooltip-search-fulltext": "Hwilas an tekst ma y'n folennow",
-       "tooltip-p-logo": "Mos dhe'n folen dynnargh",
-       "tooltip-n-mainpage": "Mos dhe'n folen dynnargh",
-       "tooltip-n-mainpage-description": "Mos dhe'n folen dynnargh",
-       "tooltip-n-portal": "A-dro dhe'n ragdres, an pyth a yllowgh gul, ple hyllir kavos taklow",
+       "tooltip-p-logo": "Mos dhe'n folen dre",
+       "tooltip-n-mainpage": "Mos dhe'n folen dre",
+       "tooltip-n-mainpage-description": "Mos dhe'n folen dre",
+       "tooltip-n-portal": "A-dro dhe'n ragdres, pyth a yllowgh gul, ple hyllir kavos taklow",
        "tooltip-n-currentevents": "Kavos kedhlow a-dro dhe hwarvosow a-lemmyn",
        "tooltip-n-recentchanges": "Rol a janjyow a-dhiwedhes y'n wiki",
        "tooltip-n-randompage": "Karga folen dre happ",
        "tooltip-n-help": "Gweres",
-       "tooltip-t-whatlinkshere": "Rol a bub folen wiki a gevren dhe omma",
-       "tooltip-t-recentchangeslinked": "Chanjyow a-dhiwedhes yn folennow a gevren an folen ma dhedha",
+       "tooltip-t-whatlinkshere": "Rol a bub folen wiki kevrennys dhe'n folen ma",
+       "tooltip-t-recentchangeslinked": "Chanjyow a-dhiwedhes yn folennow kevrennys dhyworth an folen ma",
        "tooltip-feed-rss": "Feed RSS rag an folen-ma",
        "tooltip-feed-atom": "Feed Atom rag an folen ma",
        "tooltip-t-contributions": "Gweles rol a gevrohow an devnydhyer ma",
        "tooltip-t-permalink": "Kevren fast dhe'n amendyans ma a'n folen",
        "tooltip-ca-nstab-main": "Gweles an folen",
        "tooltip-ca-nstab-user": "Gweles an folen dhevnydhyer",
-       "tooltip-ca-nstab-special": "Folen arbennek yw homma; ny yllowgh chanjya an folen hy honan.",
+       "tooltip-ca-nstab-special": "Folen arbennek yw homma; ny yllir hy chanjya",
        "tooltip-ca-nstab-project": "Gweles folen an wiki",
        "tooltip-ca-nstab-image": "Gweles folen an restren",
        "tooltip-ca-nstab-template": "Gweles an skantlyn",
        "tooltip-diff": "Diskwedhes an chanjyow a wrussowgh dhe'n tekst",
        "tooltip-compareselectedversions": "Gweles an dyffransow ynter dew amendyansow dewisyes an folen-ma",
        "tooltip-watch": "Keworra an folen-ma dhe'gas rol wolya",
-       "tooltip-rollback": "\"Revya war-dhelergh\" a worthtreyl chanjyow an diwettha devnydhyer der unn glyck",
+       "tooltip-rollback": "\"Rolya war-dhelergh\" a worthtreyl chanj(yow) an diwettha devnydhyer der unn glyck",
        "tooltip-undo": "\"Diswul\" a worthtreyl an chanj ma hag ygeri an furvlen janjya y'n modh ragweles. Y hyllir keworra acheson y'n berrskrif.",
-       "tooltip-summary": "Entrewgh berrskrif",
+       "tooltip-summary": "Keworrewgh berrskrif",
        "siteuser": "devnydhyer {{SITENAME}} $1",
-       "lastmodifiedatby": "Chanj diwettha an folen-ma o dhe $2, $1 gans $3.",
+       "lastmodifiedatby": "Chanj diwettha an folen ma a veu dhe $2 an $1 gans $3.",
        "siteusers": "{{PLURAL:$2|devnydhyer|devnydhyoryon}} {{SITENAME}} $1",
        "pageinfo-toolboxlink": "Kedhlow an folen",
        "previousdiff": "← Chanj kottha",
        "blankpage": "Folen wag",
        "tag-filter": "Sidhel [[Special:Tags|tagyow]]:",
        "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|Tagg|Taggys}}]]: $2)",
+       "tags-title": "Taggys",
        "tags-edit": "chanjya",
        "dberr-problems": "Drog yw genen! An wiasva-ma a's teves kaletter teknogel.",
        "dberr-again": "Assayewgh gortos pols ha daskarga.",
index 264389c..8d0cf62 100644 (file)
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (jereo koa ny [[Special:NewPage|lisitry ny pejy vaovao]])",
        "recentchanges-submit": "Aseho",
        "rcfilters-activefilters": "Sivana miasa",
+       "rcfilters-activefilters-hide-tooltip": "Hanafina ny faritry ny sivana miasa",
        "rcfilters-advancedfilters": "Sivana havanana kokoa",
        "rcfilters-quickfilters": "Sivana notehirizina",
        "rcfilters-quickfilters-placeholder-title": "Tsy mbola misy sivana notehirizina",
        "rcfilters-savedqueries-defaultlabel": "Sivana voatahiry",
        "rcfilters-savedqueries-rename": "Hanova ny anarana",
+       "rcfilters-savedqueries-setdefault": "Ampiasaina ho solon'ny tsy misy",
        "rcfilters-savedqueries-remove": "Esorina",
        "rcfilters-savedqueries-new-name-label": "Anarana",
+       "rcfilters-savedqueries-new-name-placeholder": "Visavisao eto ny tanjon'ilay sivana",
        "rcfilters-savedqueries-apply-label": "Hamorona rohy haingana",
+       "rcfilters-savedqueries-apply-and-setdefault-label": "Hamorona sivana solon'ny tsy misy",
        "rcfilters-savedqueries-cancel-label": "Avela",
+       "rcfilters-savedqueries-already-saved": "Efa voatahiry ireo sivana ireo. Ovay ny parametatra hamoronana sivana voatahiry vaovao.",
        "rcfilters-restore-default-filters": "Hamerina sivana ampiasaina raha tsy misy",
        "rcfilters-clear-all-filters": "Hamafa ny sivana rehetra",
        "rcfilters-search-placeholder": "Hanivana ny fiovana farany (tetezo na manomboha manoratra)",
        "rcfilters-filter-excluded": "Foanana",
        "rcfilters-tag-prefix-namespace-inverted": "<strong>:not</strong> $1",
        "rcfilters-view-tags": "Fiovam-pejy voamarika",
+       "rcfilters-view-namespaces-tooltip": "Hanasivana ny valiny araka ny anaran-tsehatra",
+       "rcfilters-view-return-to-default-tooltip": "Hiverina amin'ny menion'ny sivana",
        "rcfilters-liveupdates-button": "Fihavaozana mivantana",
        "rcnotefrom": "Eo ambany dia ahitana ireo fiovana{{PLURAL:$5}} hatry ny <strong>$3, $4</strong> (naseho hatramin'ny <strong>$1</strong>).",
        "rclistfrom": "Asehoy izay vao niova manomboka ny $3 $2",
index e7e4e6c..4580593 100644 (file)
        "diff-paragraph-moved-toold": "Пасусот е преместен. Стиснете за да прејдете на старото место.",
        "difference-missing-revision": "Не пронајдов {{PLURAL:$2|една преработка|$2 преработки}} од оваа разлика ($1).\n\nОва обично се должи на застарена врска за разлики што води кон избришана страница.\nПовеќе подробности ќе најдете во [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} дневникот на бришења].",
        "searchresults": "Исход од пребарувањето",
+       "search-filter-title-prefix": "Пребарување по страници чиј наслов почнува со „$1“",
+       "search-filter-title-prefix-reset": "Пребарај по сите страници",
        "searchresults-title": "Исход од пребарувањето на „$1“",
        "titlematches": "Совпаднати наслови",
        "textmatches": "Совпаднат текст во страниците",
        "http-timed-out": "HTTP-барањето истече.",
        "http-curl-error": "Грешка при добивањето на URL: $1",
        "http-bad-status": "Се појави проблем во текот на обработката на HTTP-барањето: $1 $2",
+       "http-internal-error": "Внатрешна грешка со HTTP.",
        "upload-curl-error6": "Не може да се пристапи до URL-то",
        "upload-curl-error6-text": "Наведеното URL не е достапно.\nПроверете дали URL-то е исправно и достапно.",
        "upload-curl-error28": "Истече времето за подигање",
        "speciallogtitlelabel": "Цел (наслов или {{ns:user}}:корисничко име на корисникот):",
        "log": "Дневници",
        "logeventslist-submit": "Прикажи",
-       "logeventslist-more-filters": "Повеќе филтри:",
+       "logeventslist-more-filters": "Прикажи повеќе дневници:",
        "logeventslist-patrol-log": "Дневник на патролирања",
        "logeventslist-tag-log": "Дневник на ознаки",
        "all-logs-page": "Сите јавни дневници",
index 7fa61b8..57e3609 100644 (file)
        "prefs-dateformat": "दिनांक प्रारुपण",
        "prefs-timeoffset": "वेळ बरोबरी",
        "prefs-advancedediting": "सर्वसामान्य पर्याय",
+       "prefs-developertools": "विकसक उपकरण",
        "prefs-editor": "संपादक",
        "prefs-preview": "झलक",
        "prefs-advancedrc": "प्रगत पर्याय",
        "logentry-protect-protect-cascade": "$1 ने $3 $4 [निपतन]ला {{GENDER:$2|सुरक्षित केले}}",
        "logentry-protect-modify": "$1 ने $3 $4 ची  सुरक्षा पातळी {{GENDER:$2|बदलली}}",
        "logentry-protect-modify-cascade": "$1 ने $3 $4 [निपतन]ची  सुरक्षा पातळी {{GENDER:$2|बदलली}}",
-       "logentry-rights-rights": "$1 ने {{GENDER:$6|$3}} साठी $4 वरुन $5 ला गट सदस्यता{{GENDER:$2|बदलली}}",
+       "logentry-rights-rights": "$1 ने {{GENDER:$6|$3}} साठी $4 वरुन $5 ला गट सदस्यता {{GENDER:$2|बदलली}}",
        "logentry-rights-rights-legacy": "$1 ने $3 साठी गट सदस्यता {{GENDER:$2|बदलली}}",
        "logentry-rights-autopromote": "$1 ला स्वयंचलितरित्या $4 वरुन $5 ला {{GENDER:$2|बढती दिल्या गेली}}",
        "logentry-upload-upload": "$1 {{GENDER:$2|अपभारीत केली}} $3",
index 3a93f60..38beb03 100644 (file)
        "movelogpagetext": "Ē-kha lia̍t-chhut hông soá-ūi ê ia̍h.",
        "movereason": "Lí-iû:",
        "revertmove": "hôe-tńg",
+       "delete_and_move_reason": "Thâi-ia̍h hō͘ \"[[$1]]\" thang sóa-ia̍h kòe--lâi",
        "selfmove": "Goân piau-tê kap sin piau-tê sio-siâng; bô hoat-tō· sóa.",
        "protectedpagemovewarning": "'''KÉNG-KÒ: Pún ia̍h só tiâu leh. Kan-taⁿ ū hêng-chèng te̍k-koân ê iōng-chiá (sysop) ē-sái soá tín-tāng.'''\nĒ-kha ū choè-kīn ê kì-lio̍k thang chham-khó:",
        "export": "Su-chhut ia̍h",
        "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|piau-chhiam}}]]: $2)",
        "tag-mw-new-redirect": "Sin choán-ia̍h",
        "tag-mw-changed-redirect-target": "Choán-ia̍h bo̍k-phiau kái-piàn",
+       "logentry-delete-delete_redir": "$1 ēng têng-siá lâi kā choán-ia̍h $3 {{GENDER:$2|thâi-tiāu}}",
        "logentry-move-move": "$1 {{GENDER:$2|sóa}} $3 chit ia̍h khì $4",
        "logentry-move-move_redir": "$1 iōng choán-ia̍h {{GENDER:$2|sóa}} ia̍h-bīn $3 kòe $4",
        "logentry-newusers-create": "已經{{GENDER:$2|開好}}用者口座 $1",
index 16b9bcb..857f8b0 100644 (file)
        "subject-preview": "Forhåndsvisning av overskrift:",
        "previewerrortext": "En feil oppsto mens dine endringer skulle forhåndsvises.",
        "blockedtitle": "Brukeren er blokkert",
-       "blockedtext": "<strong>Ditt brukernavn eller din IP-adresse har blitt blokkert.</strong>\n\nBlokkeringen ble utført av $1. Grunnen som ble oppgitt var <em>$2</em>.\n\n* Blokkeringen begynte:  $8\n* Blokkeringen opphører: $6\n* Blokkeringen ment for: $7\n\nDu kan kontakte $1 eller en annen [[{{MediaWiki:Grouppage-sysop}}|administrator]] for å diskutere blokkeringen.\nDu kan ikke bruke \"{{int:emailuser}}\"-funksjonen med mindre du har oppgitt en gyldig e-postadresse i [[Special:Preferences|innstillingene dine]] og du ikke har blitt blokkert fra å sende e-post.\nDin nåværende IP-adresse er $3, og blokkerings-ID-en er #$5.\nVennligst ta med all denne informasjonen ved henvendelser.",
+       "blockedtext": "<strong>Ditt brukernavn eller din IP-adresse har blitt blokkert.</strong>\n\nBlokkeringen ble utført av $1. Grunnen som ble oppgitt var <em>$2</em>.\n\n* Blokkeringen begynte:  $8\n* Blokkeringen opphører: $6\n* Blokkeringen ment for: $7\n\nDu kan kontakte $1 eller en annen [[{{MediaWiki:Grouppage-sysop}}|administrator]] for å diskutere blokkeringen.\nDu kan ikke bruke «{{int:emailuser}}»-funksjonen med mindre du har oppgitt en gyldig e-postadresse i [[Special:Preferences|innstillingene dine]] og du ikke har blitt blokkert fra å sende e-post.\nDin nåværende IP-adresse er $3, og blokkerings-ID-en er #$5.\nVennligst ta med all denne informasjonen ved henvendelser.",
        "autoblockedtext": "Din IP-adresse har blitt automatisk blokkert fordi den ble brukt av en annen bruker som ble blokkert av $1.\nDen oppgitte grunnen var:\n\n:'''$2'''\n\n* Blokkeringen begynte: $8\n* Blokkeringen utgår: $6\n* Blokkeringen er ment for: $7\n\nDu kan kontakte $1 eller en av de andre [[{{MediaWiki:Grouppage-sysop}}|administratorene]] for å diskutere blokkeringen.\n\nMerk at du ikke kan bruke «{{int:emailuser}}»-funksjonen med mindre du har registrert en gyldig e-postadresse i [[Special:Preferences|innstillingene dine]].\n\nDin IP-adresse er $3, og blokkerings-ID-en er #$5.\nVennligst ta med all denne informasjonen ved henvendelser.",
        "systemblockedtext": "Ditt brukernavn eller IP-adresse har blitt blokkert automatisk av MediaWiki.\n\nBlokkeringen grunnes:\n\n:<em>$2</em>\n\n* Blokkeringen startet: $8\n* Blokkeringen gjelder til: $6\n* Blokkeringen er ment for: $7\n\nDin nåværende IP-adresse er $3.\nVennligst inkluder informasjonen over i alle spørsmål du spør angående dette.",
        "blockednoreason": "ingen grunn gitt",
        "diff-paragraph-moved-toold": "Avsnittet ble flyttet. Klikk for å hoppe til den gamle plasseringen.",
        "difference-missing-revision": "{{PLURAL:$2|En revisjon|$2 revisjoner}} av denne forskjellen ($1) {{PLURAL:$2|ble|ble}} ikke funnet.\n\nDette skyldes som regel at en gammel forskjell-lenke er fulgt til en side som er slettet.\nDetaljer kan finnes i [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} sletteloggen].",
        "searchresults": "Søkeresultater",
+       "search-filter-title-prefix": "Søker bare i sider der tittelen begynner med «$1»",
        "search-filter-title-prefix-reset": "Søk i alle sider",
        "searchresults-title": "Søkeresultater for «$1»",
        "titlematches": "Artikkeltitler med treff på forespørselen",
        "rcfilters-watchlist-showupdated": "Endringer til sider du ikke har besøkt siden endringene ble gjort vises med <strong>fet</strong> skrift.",
        "rcfilters-preference-label": "Skjul den forbedrede versjonen av siste endringer",
        "rcfilters-preference-help": "Fjerner grensesnittendringen fra 2017 og alle verktøyene som ble lagt fra og med da.",
-       "rcfilters-watchlist-preference-label": "Skjul den forbedrede versjonen av bevåkningslisten",
+       "rcfilters-watchlist-preference-label": "Skjul den forbedrede versjonen av overvåkningslisten",
        "rcfilters-watchlist-preference-help": "Ruller tilbake det omarbeidede grensesnittet fra 2017 og alle verktøy som ble lagt til da og etterpå.",
        "rcfilters-filter-showlinkedfrom-label": "Vis endringer på sider som lenkes fra",
        "rcfilters-filter-showlinkedfrom-option-label": "<strong>Sider som lenkes fra</strong> den valgte siden",
        "apisandbox-dynamic-parameters-add-label": "Legg til parameter:",
        "apisandbox-dynamic-parameters-add-placeholder": "Parameternavn",
        "apisandbox-dynamic-error-exists": "En parameter med navn «$1» finnes fra før.",
-       "apisandbox-templated-parameter-reason": "Denne [[Special:ApiHelp/main#main/templatedparams|templated parameteren]] tilbys basert på {{PLURAL:$1|verdi|verdier}} av $2.",
+       "apisandbox-templated-parameter-reason": "Denne [[Special:ApiHelp/main#main/templatedparams|malparameteren]] tilbys basert på {{PLURAL:$1|verdien|verdiene}} til $2.",
        "apisandbox-deprecated-parameters": "Utgåtte parametre",
        "apisandbox-fetch-token": "Fyll inn nøkkelen automatisk",
        "apisandbox-add-multi": "Legg til",
        "speciallogtitlelabel": "Mål (tittel eller {{ns:user}}:brukernavn for brukeren):",
        "log": "Logger",
        "logeventslist-submit": "Vis",
-       "logeventslist-more-filters": "Flere filtre:",
+       "logeventslist-more-filters": "Vis flere logger:",
        "logeventslist-patrol-log": "Patruljeringslogg",
        "logeventslist-tag-log": "Merkelogg",
        "all-logs-page": "Alle offentlige logger",
        "deletionlog": "slettelogg",
        "log-name-create": "Logg over nye sider",
        "log-description-create": "Under er ei liste over nylige sideopprettelser.",
-       "logentry-create-create": "$1 {{GENDER:$2|opprettet}} side $3",
+       "logentry-create-create": "$1 {{GENDER:$2|opprettet}} siden $3",
        "reverted": "Gjenopprettet en tidligere versjon",
        "deletecomment": "Årsak:",
        "deleteotherreason": "Annen/utdypende grunn:",
        "pagedata-text": "Denne siden gir et datagrensesnitt til sidene. Oppgi en sidetittel i URL-en, med undersidesyntaks.\n* Innholdsforhandlingen er basert på din klients Accept-header. Dette betyr at data for siden blir angitt på formatet som foretrekkes av din klient.",
        "pagedata-not-acceptable": "Ingen passende format funnet. Støttede MIME-typer: $1",
        "pagedata-bad-title": "Ugyldig tittel: $1.",
-       "unregistered-user-config": "For å ivareta sikkerhet kan JavaScript, CSS and JSON ikks brukes i underliggende sider for uregistrerte brukere.",
+       "unregistered-user-config": "For å ivareta sikkerhet kan ikke JavaScript-, CSS- og JSON-undersider lastes for uregistrerte brukere.",
        "passwordpolicies": "Passord-regler",
        "passwordpolicies-summary": "Det finnes en liste over gode passord-regler for brukergrupper definert i denne wikien.",
        "passwordpolicies-group": "Gruppe",
index 3613d2d..297b2bb 100644 (file)
        "http-timed-out": "Timeout bij het HTTP-verzoek.",
        "http-curl-error": "Fout bij het ophalen van URL: $1",
        "http-bad-status": "Er is een probleem opgetreden bij het HTTP-verzoek: $1 $2",
+       "http-internal-error": "HTTP interne fout.",
        "upload-curl-error6": "Kon de URL niet bereiken",
        "upload-curl-error6-text": "De opgegeven URL is niet bereikbaar.\nControleer of de URL juist is, en of de website beschikbaar is.",
        "upload-curl-error28": "Uploadtime-out",
index 81cb5e8..4b5d69f 100644 (file)
        "cascadeprotected": "Denne sida er verna mot endring fordi ho er inkludert i fylgjande {{PLURAL:$1|side|sider}} som har djupvern slått på:\n$2",
        "namespaceprotected": "Du har ikkje tilgang til å endre sidene i '''$1'''-namnerommet.",
        "customcssprotected": "↓Du har ikkje tilgang til å endre denne sida, fordi ho inneheld ein annan brukar sine personlege innstillingar.",
-       "customjsprotected": "↓Du har ikkje tilgang til å endra denne JavaScript-sida fordi ho inneheld ein annen brukar sine personlege innstillingar.",
+       "customjsprotected": "↓Du har ikkje løyve til å endra denne JavaScript-sida fordi ho inneheld dei personlege innstillingane til ein annan brukar.",
        "mycustomcssprotected": "Du har ikkje løyve til å endra denne CSS-sida.",
        "mycustomjsprotected": "Du har ikkje løyve til å endra denne JavaScript-sida.",
        "myprivateinfoprotected": "Du har ikkje løyve til endra den private informasjonen din.",
index a897ee6..ecfb2e5 100644 (file)
        "diff-paragraph-moved-toold": "Akapit został przeniesiony. Kliknij aby przeskoczyć do jego poprzedniego położenia.",
        "difference-missing-revision": "{{PLURAL:$2|Wersja|$2 wersje|$2 wersji}} #$1 strony \"{{PAGENAME}}\" nie {{PLURAL:$2|została znaleziona|zostały znalezione|zostało znalezionych}}.\n\nZazwyczaj jest to spowodowane przestarzałym linkiem do usuniętej strony. Powód usunięcia znajduje się w [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} rejestrze].",
        "searchresults": "Wyniki wyszukiwania",
+       "search-filter-title-prefix": "Wyszukiwanie tylko na stronach, których tytuł zaczyna się od „$1”",
        "searchresults-title": "Wyniki wyszukiwania „$1”",
        "titlematches": "Znaleziono w tytułach",
        "textmatches": "Znaleziono w treści stron",
        "http-timed-out": "Przekroczony czas żądania HTTP.",
        "http-curl-error": "Błąd pobierania z adresu $1",
        "http-bad-status": "Wystąpił problem z realizacją żądania HTTP $1 $2",
+       "http-internal-error": "Błąd wewnętrzny HTTP.",
        "upload-curl-error6": "Adres URL jest nieosiągalny",
        "upload-curl-error6-text": "Podany adres URL jest nieosiągalny. Upewnij się, czy podany adres URL jest prawidłowy i czy dana strona jest dostępna.",
        "upload-curl-error28": "Upłynął limit czasu odpowiedzi",
        "speciallogtitlelabel": "Co (tytuł lub {{ns:user}}:nick użytkownika):",
        "log": "Rejestr operacji",
        "logeventslist-submit": "Pokaż",
-       "logeventslist-more-filters": "Więcej filtrów:",
+       "logeventslist-more-filters": "Pokaż dodatkowe rejestry:",
        "logeventslist-patrol-log": "Rejestr patrolowania",
        "logeventslist-tag-log": "Rejestr znaczników",
        "all-logs-page": "Wszystkie publiczne operacje",
index 1551ac1..07604da 100644 (file)
        "diff-paragraph-moved-toold": "O parágrafo foi movido. Clique para saltar para a posição anterior.",
        "difference-missing-revision": "{{PLURAL:$2|Uma revisão|$2 revisões}} desta diferença ($1) não {{PLURAL:$2|foi encontrada|foram encontradas}}.\n\nIsto é geralmente causado por seguir um link de histórico desatualizado para uma página que foi eliminada.\nOs detalhes podem ser encontrados no [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} registro de eliminação].",
        "searchresults": "Resultados da pesquisa",
+       "search-filter-title-prefix": "A pesquisar só nas páginas cujo título começa por \"$1\"",
+       "search-filter-title-prefix-reset": "Pesquisar em todas as páginas",
        "searchresults-title": "Resultados da pesquisa por \"$1\"",
        "titlematches": "Resultados nos títulos das páginas",
        "textmatches": "Resultados nos textos das páginas",
        "http-timed-out": "Esgotado o tempo de espera da requisição HTTP.",
        "http-curl-error": "Erro ao requisitar a URL: $1",
        "http-bad-status": "Ocorreu um problema durante a requisição HTTP: $1 $2",
+       "http-internal-error": "Erro interno do HTTP.",
        "upload-curl-error6": "Não foi possível acessar a URL",
        "upload-curl-error6-text": "Não foi possível acessar o endereço (URL) fornecido. Por gentileza, se certifique que o endereço foi fornecido corretamente e de que o site esteja acessível.",
        "upload-curl-error28": "Tempo limite para o envio do arquivo excedido",
        "speciallogtitlelabel": "Alvo (título da página ou {{ns:user}}:'nomedeusuário' para usuários):",
        "log": "Registros",
        "logeventslist-submit": "Exibir",
-       "logeventslist-more-filters": "Mais filtros:",
+       "logeventslist-more-filters": "Mostrar registos adicionais:",
        "logeventslist-patrol-log": "Registro de edições patrulhadas",
        "logeventslist-tag-log": "Registro de etiquetas",
        "all-logs-page": "Todos os registros públicos",
index aedf188..87f4f61 100644 (file)
        "http-timed-out": "O pedido HTTP expirou.",
        "http-curl-error": "Ocorreu um erro ao aceder ao URL: $1",
        "http-bad-status": "Ocorreu um problema durante o pedido HTTP: $1 $2",
+       "http-internal-error": "Erro interno do HTTP.",
        "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 sítio disponível, por favor.",
        "upload-curl-error28": "Tempo limite para o envio do ficheiro excedido",
        "listgrouprights-namespaceprotection-namespace": "Domínio",
        "listgrouprights-namespaceprotection-restrictedto": "Direito(s) do utilizador para editar",
        "listgrants": "Concessões de permissões a aplicações ligadas",
-       "listgrants-summary": "Esta é uma lista das possíveis concessões de permissões e das respetivas permissões dos utilizadores que são atribuídas por cada concessão. Os utilizadores podem autorizar aplicações a utilizar a sua conta, agindo assim em seu nome mas com as permissões limitadas com base nestas concessões. Uma aplicação que age em nome de um utilizador não pode utilizar permissões que o utilizador não possui.\nPoderá existir [[{{MediaWiki:Listgrouprights-helppage}}|informação adicional]] sobre as permissões individuais.",
+       "listgrants-summary": "Esta é uma lista das possíveis concessões de permissões e das respetivas permissões dos utilizadores que são atribuídas por cada concessão. Os utilizadores podem autorizar aplicações a utilizar a sua conta, agindo assim em seu nome mas com as permissões limitadas com base nestas concessões. Uma aplicação que age em nome de um utilizador não pode utilizar permissões que o utilizador não tem.\nPoderá existir [[{{MediaWiki:Listgrouprights-helppage}}|informação adicional]] sobre as permissões individuais.",
        "listgrants-grant": "Concessão de permissões",
        "listgrants-rights": "Permissões",
        "trackingcategories": "Categorias de monitorização",
        "confirm-purge-title": "Purgar esta página",
        "confirm_purge_button": "OK",
        "confirm-purge-top": "Limpar esta página da memória cache?",
-       "confirm-purge-bottom": "Recarregar uma página limpa a cache e força a sua versão mais recente a aparecer.",
+       "confirm-purge-bottom": "Purgar uma página limpa-a da ''cache'' e força a sua versão mais recente a aparecer.",
        "confirm-watch-button": "OK",
        "confirm-watch-top": "Adicionar esta página à lista de páginas vigiadas?",
        "confirm-unwatch-button": "OK",
index 573c029..2ba449a 100644 (file)
        "customcssprotected": "Used as error message. Parameters:\n* $1 - (Unused) the action the user attempted to perform",
        "customjsonprotected": "Used as error message. Parameters:\n* $1 - (Unused) the action the user attempted to perform",
        "customjsprotected": "Used as error message. Parameters:\n* $1 - (Unused) the action the user attempted to perform",
+       "sitecssprotected": "Used as error message. Parameters:\n* $1 - (Unused) the action the user attempted to perform",
+       "sitejsonprotected": "Used as error message. Parameters:\n* $1 - (Unused) the action the user attempted to perform",
+       "sitejsprotected": "Used as error message. Parameters:\n* $1 - (Unused) the action the user attempted to perform",
        "mycustomcssprotected": "Used as error message. Parameters:\n* $1 - (Unused) the action the user attempted to perform",
        "mycustomjsonprotected": "Used as error message. Parameters:\n* $1 - (Unused) the action the user attempted to perform",
        "mycustomjsprotected": "Used as error message. Parameters:\n* $1 - (Unused) the action the user attempted to perform",
        "group-autoconfirmed": "{{doc-group|autoconfirmed}}\nOn Wikimedia sites autoconfirmed users are users which are older than 4 days. After those 4 days, they have more rights.",
        "group-bot": "{{doc-group|bot}}\n{{Identical|Bot}}",
        "group-sysop": "{{doc-group|sysop}}\n{{Identical|Administrator}}",
+       "group-interface-admin": "{{doc-group|interface-admin}}",
        "group-bureaucrat": "{{doc-group|bureaucrat}}",
        "group-suppress": "{{doc-group|suppress}}\nThis is an optional (disabled by default) user group, meant for the suppression feature in [[mw:Flow|Flow]]. It is not to be confused with the Oversighters group, which also has access to the [[mw:RevisionDelete|RevisionDelete]] feature, to change the visibility of revisions through [[Special:RevisionDelete]].\n\n{{Identical|Suppress}}",
        "group-all": "The name of the user group that contains all users, including anonymous users\n\n{{Identical|All}}",
        "group-autoconfirmed-member": "{{doc-group|autoconfirmed|member}}",
        "group-bot-member": "{{doc-group|bot|member}}",
        "group-sysop-member": "{{doc-group|sysop|member}}\n{{Identical|Administrator}}",
+       "group-interface-admin-member": "{{doc-group|interface-admin|member}}",
        "group-bureaucrat-member": "{{doc-group|bureaucrat|member}}",
        "group-suppress-member": "{{doc-group|suppress|member}}\nThis is a member of the optional (disabled by default) user group, meant for the [[mw:RevisionDelete|RevisionDelete]] feature, to change the visibility of revisions through [[Special:RevisionDelete]].\n\n{{Identical|Suppress}}",
        "grouppage-user": "{{doc-group|user|page}}\n{{Identical|User}}",
        "grouppage-autoconfirmed": "{{doc-group|autoconfirmed|page}}",
        "grouppage-bot": "{{doc-group|bot|page}}\n{{Identical|Bot}}",
        "grouppage-sysop": "{{doc-group|sysop|page}}",
+       "grouppage-interface-admin": "{{doc-group|interface-admin|page}}",
        "grouppage-bureaucrat": "{{doc-group|bureaucrat|page}}",
        "grouppage-suppress": "{{doc-group|suppress|page}}\n{{Identical|Suppress}}",
        "right-read": "{{doc-right|read}}\nBasic right to read any page.",
        "right-editusercss": "{{doc-right|editusercss}}\nSee also:\n* {{msg-mw|Right-editmyusercss}}",
        "right-edituserjson": "{{doc-right|edituserjson}}\nSee also:\n* {{msg-mw|Right-editmyuserjson}}",
        "right-edituserjs": "{{doc-right|edituserjs}}\nSee also:\n* {{msg-mw|Right-editmyuserjs}}",
+       "right-editsitecss": "{{doc-right|editsitecss}}",
+       "right-editsitejson": "{{doc-right|editsitejson}}",
+       "right-editsitejs": "{{doc-right|editsitejs}}",
        "right-editmyusercss": "{{doc-right|editmyusercss}}\nSee also:\n* {{msg-mw|Right-editusercss}}",
        "right-editmyuserjson": "{{doc-right|editmyuserjson}}\nSee also:\n* {{msg-mw|Right-edituserjson}}",
        "right-editmyuserjs": "{{doc-right|editmyuserjs}}\nSee also:\n* {{msg-mw|Right-edituserjs}}",
        "right-applychangetags": "{{doc-right|applychangetags}}",
        "right-changetags": "{{doc-right|changetags}}",
        "right-deletechangetags": "{{doc-right|deletechangetags}}",
-       "grant-generic": "Used if the grant name is not defined. Parameters:\n* $1 - grant name\n\nDefined grants (grant name refers: blockusers, createeditmovepage, ...):\n* {{msg-mw|grant-checkuser}}\n* {{msg-mw|grant-blockusers}}\n* {{msg-mw|grant-createaccount}}\n* {{msg-mw|grant-createeditmovepage}}\n* {{msg-mw|grant-delete}}\n* {{msg-mw|grant-editinterface}}\n* {{msg-mw|grant-editmycssjs}}\n* {{msg-mw|grant-editmyoptions}}\n* {{msg-mw|grant-editmywatchlist}}\n* {{msg-mw|grant-editpage}}\n* {{msg-mw|grant-editprotected}}\n* {{msg-mw|grant-highvolume}}\n* {{msg-mw|grant-oversight}}\n* {{msg-mw|grant-patrol}}\n* {{msg-mw|grant-privateinfo}}\n* {{msg-mw|grant-protect}}\n* {{msg-mw|grant-rollback}}\n* {{msg-mw|grant-sendemail}}\n* {{msg-mw|grant-uploadeditmovefile}}\n* {{msg-mw|grant-uploadfile}}\n* {{msg-mw|grant-basic}}\n* {{msg-mw|grant-viewdeleted}}\n* {{msg-mw|grant-viewmywatchlist}}",
+       "grant-generic": "Used if the grant name is not defined. Parameters:\n* $1 - grant name\n\nDefined grants (grant name refers: blockusers, createeditmovepage, ...):\n* {{msg-mw|grant-checkuser}}\n* {{msg-mw|grant-blockusers}}\n* {{msg-mw|grant-createaccount}}\n* {{msg-mw|grant-createeditmovepage}}\n* {{msg-mw|grant-delete}}\n* {{msg-mw|grant-editinterface}}\n* {{msg-mw|grant-editmycssjs}}\n* {{msg-mw|grant-editmyoptions}}\n* {{msg-mw|grant-editmywatchlist}}\n* {{msg-mw|grant-editsiteconfig}}\n* {{msg-mw|grant-editpage}}\n* {{msg-mw|grant-editprotected}}\n* {{msg-mw|grant-highvolume}}\n* {{msg-mw|grant-oversight}}\n* {{msg-mw|grant-patrol}}\n* {{msg-mw|grant-privateinfo}}\n* {{msg-mw|grant-protect}}\n* {{msg-mw|grant-rollback}}\n* {{msg-mw|grant-sendemail}}\n* {{msg-mw|grant-uploadeditmovefile}}\n* {{msg-mw|grant-uploadfile}}\n* {{msg-mw|grant-basic}}\n* {{msg-mw|grant-viewdeleted}}\n* {{msg-mw|grant-viewmywatchlist}}",
        "grant-group-page-interaction": "{{Related|Grant-group}}",
        "grant-group-file-interaction": "{{Related|Grant-group}}",
        "grant-group-watchlist-interaction": "{{Related|Grant-group}}",
        "grant-createaccount": "Name for grant \"createaccount\".\n{{Related|Grant}}\n{{Identical|Create account}}",
        "grant-createeditmovepage": "Name for grant \"createeditmovepage\".\n{{Related|Grant}}",
        "grant-delete": "Name for grant \"delete\".\n{{Related|Grant}}",
-       "grant-editinterface": "Name for grant \"editinterface\".\n\n\"JS\" stands for \"JavaScript\".\n{{Related|Grant}}",
+       "grant-editinterface": "Name for grant \"editinterface\".\n{{Related|Grant}}",
        "grant-editmycssjs": "Name for grant \"editmycssjs\".\n\n\"JS\" stands for \"JavaScript\".\n{{Related|Grant}}",
        "grant-editmyoptions": "Name for grant \"editmyoptions\".\n{{Related|Grant}}",
        "grant-editmywatchlist": "Name for grant \"editmywatchlist\".\n{{Related|Grant}}\n{{Identical|Edit your watchlist}}",
+       "grant-editsiteconfig": "Name for grant \"editsiteconfig\".\n{{Related|Grant}}",
        "grant-editpage": "Name for grant \"editpage\".\n{{Related|Grant}}",
        "grant-editprotected": "Name for grant \"editprotected\".\n{{Related|Grant}}",
        "grant-highvolume": "Name for grant \"highvolume\".\n{{Related|Grant}}",
        "http-timed-out": "Used as error message when executing HTTP request.\n\nSee also:\n* {{msg-mw|Http-request-error}}\n* {{msg-mw|Http-read-error}}\n* {{msg-mw|Http-host-unreachable|6}}",
        "http-curl-error": "Used as curl error message when the error is other than known messages.\n* $1 - error code; not URL\nKnown messages are:\n* {{msg-mw|http-host-unreachable}}\n* {{msg-mw|http-timed-out}}",
        "http-bad-status": "Parameters:\n* $1 - an HTTP error code (e.g. 404)\n* $2 - the HTTP error message (e.g. File Not Found)",
+       "http-internal-error": "Used as generic error message when executing HTTP request and no more specific message is available.",
        "upload-curl-error6": "See also:\n* {{msg-mw|Upload-curl-error6|title}}\n* {{msg-mw|Upload-curl-error6-text|body}}",
        "upload-curl-error6-text": "See also:\n* {{msg-mw|Upload-curl-error6|title}}\n* {{msg-mw|Upload-curl-error6-text|body}}",
        "upload-curl-error28": "See also:\n* {{msg-mw|Upload-curl-error28|title}}\n* {{msg-mw|Upload-curl-error28-text|body}}",
        "variantname-gan-hans": "{{Optional}}\n\nVariant option for wikis with variants conversion enabled.",
        "variantname-gan-hant": "{{Optional}}\n\nVariant option for wikis with variants conversion enabled.",
        "variantname-gan": "{{Optional}}\n\nVariant option for wikis with variants conversion enabled.",
-       "variantname-sr-ec": "{{optional}}\nVariant Option for wikis with variants conversion enabled.\n\nNote that <code>sr-ec</code> is not a conforming BCP 47 language tag. Wikis should be migrated by:\n* allowing it only as a legacy alias of the preferred tag <code>sr-cyrl</code> (possibly insert a tracking category in templates as long as they must support the legacy tag),\n* making the new tag the default to look first, before looking for the old tag,\n* moving the translations to the new code by renaming them,\n* checking links in source pages still using the legacy tag to change it to the new tag,\n* possibly cleanup the redirect pages.",
-       "variantname-sr-el": "{{optional}}\nVariant Option for wikis with variants conversion enabled.\n\nNote that <code>sr-el</code> is not a conforming BCP 47 language tag. Wikis should be migrated by:\n* allowing it only as a legacy alias of the preferred tag <code>sr-latn</code> (possibly insert a tracking category in templates as long as they must support the legacy tag),\n* making the new tag the default to look first, before looking for the old tag,\n* moving the translations to the new code by renaming them,\n* checking links in source pages still using the legacy tag to change it to the new tag,\n* possibly cleanup the redirect pages.",
+       "variantname-sr-ec": "{{optional}}\nVariant Option for wikis with variants conversion enabled.\n\nNote that <code>sr-ec</code> is not a conforming BCP47 language tag. Wikis should be migrated by:\n* allowing it only as a legacy alias of the preferred tag <code>sr-cyrl</code> (possibly insert a tracking category in templates as long as they must support the legacy tag),\n* making the new tag the default to look first, before looking for the old tag,\n* moving the translations to the new code by renaming them,\n* checking links in source pages still using the legacy tag to change it to the new tag,\n* possibly cleanup the redirect pages.",
+       "variantname-sr-el": "{{optional}}\nVariant Option for wikis with variants conversion enabled.\n\nNote that <code>sr-el</code> is not a conforming BCP47 language tag. Wikis should be migrated by:\n* allowing it only as a legacy alias of the preferred tag <code>sr-latn</code> (possibly insert a tracking category in templates as long as they must support the legacy tag),\n* making the new tag the default to look first, before looking for the old tag,\n* moving the translations to the new code by renaming them,\n* checking links in source pages still using the legacy tag to change it to the new tag,\n* possibly cleanup the redirect pages.",
        "variantname-sr": "{{optional}}\nVariant Option for wikis with variants conversion enabled.",
        "variantname-kk-kz": "{{optional}}\nVariant Option for wikis with variants conversion enabled.",
        "variantname-kk-tr": "{{optional}}\nVariant Option for wikis with variants conversion enabled.",
        "passwordpolicies-policy-passwordcannotmatchusername": "Password policy that enforces that the password of the account cannot be the same as the username",
        "passwordpolicies-policy-passwordcannotmatchblacklist": "Password policy that enforces that passwords are not on a list of blacklisted passwords (often previously used during MediaWiki automated testing)",
        "passwordpolicies-policy-maximalpasswordlength": "Password policy that enforces a maximum number of characters a password must be. $1 - maximum number of characters that a password can be",
-       "passwordpolicies-policy-passwordcannotbepopular": "Password policy that enforces that a password is not in a list of $1 number of \"popular\" passwords. $1 - number of popular passwords the password will be checked against"
+       "passwordpolicies-policy-passwordcannotbepopular": "Password policy that enforces that a password is not in a list of $1 number of \"popular\" passwords. $1 - number of popular passwords the password will be checked against",
+       "easydeflate-invaliddeflate": "Error message if the content passed to easydeflate was not deflated (compressed) properly"
 }
index 8598d92..e0c49ab 100644 (file)
@@ -72,7 +72,7 @@
        "tog-watchlisthideminor": "Ascunde modificările minore din lista de pagini urmărite",
        "tog-watchlisthideliu": "Ascunde modificările efectuate de utilizatori autentificați din lista de pagini urmărite",
        "tog-watchlistreloadautomatically": "Reîncarcă automat lista paginilor urmărite de fiecare dată când un filtru este modificat (necesită JavaScript)",
-       "tog-watchlistunwatchlinks": "Adaugă legături directe pentru urmărire/neurmărire inrărilor din lista de pagini urmărite (este nevoie de JavaScript pentru a activa funcționalitatea)",
+       "tog-watchlistunwatchlinks": "Adaugă legături directe pentru urmărirea/neurmărirea ({{int:Watchlist-unwatch}}/{{int:Watchlist-unwatch-undo}}) intrărilor din lista de pagini urmărite (este nevoie de JavaScript pentru a activa funcționalitatea)",
        "tog-watchlisthideanons": "Ascunde modificările făcute de utilizatori anonimi din lista de pagini urmărite",
        "tog-watchlisthidepatrolled": "Ascunde paginile patrulate din lista de pagini urmărite",
        "tog-watchlisthidecategorization": "Ascunde categorisirea paginilor",
        "cascadeprotected": "Această pagină a fost protejată la modificare, deoarece este inclusă în {{PLURAL:$1|următoarea pagină|următoarele pagini}} {{PLURAL:$1|protejată|protejate}} cu opțiunea „în cascadă”:\n$2",
        "namespaceprotected": "Nu aveți permisiunea de a modifica pagini din spațiul de nume '''$1'''.",
        "customcssprotected": "Nu aveți permisiunea de a modifica această pagină CSS, deoarece conține setările personale ale altui utilizator.",
+       "customjsonprotected": "Nu aveți permisiunea de a modifica această pagină JSON, deoarece conține setările personale ale altui utilizator.",
        "customjsprotected": "Nu aveți permisiunea de a modifica această pagină JavaScript, deoarece conține setările personale ale altui utilizator.",
        "mycustomcssprotected": "Nu aveți permisiunea să modificați această pagină CSS.",
+       "mycustomjsonprotected": "Nu aveți permisiunea să modificați această pagină JSON.",
        "mycustomjsprotected": "Nu aveți permisiunea să modificați această pagină JavaScript.",
        "myprivateinfoprotected": "Nu aveți permisiunea să vă modificați informațiile personale.",
        "mypreferencesprotected": "Nu aveți permisiunea să vă modificați preferințele.",
        "wrongpasswordempty": "Spațiul pentru introducerea parolei nu a fost completat. Vă rugăm să încercați din nou.",
        "passwordtooshort": "Parola trebuie să aibă cel puțin {{PLURAL:$1|1 caracter|$1 caractere|$1 de caractere}}.",
        "passwordtoolong": "Parolele nu pot fi mai lungi de {{PLURAL:$1|un caracter|$1 caractere|$1 de caractere}}.",
-       "passwordtoopopular": "Parolele comune nu pot fi utilizate. Alegeți o parolă mai puțin obișnuită.",
+       "passwordtoopopular": "Parolele comune nu pot fi utilizate. Alegeți o parolă mai greu de ghicit.",
        "password-name-match": "Parola dumneavoastră trebuie să fie diferită de numele de utilizator.",
        "password-login-forbidden": "Utilizarea acestui nume de utilizator și a acestei parole este interzisă.",
        "mailmypassword": "Resetează parola",
        "passwordremindertitle": "Noua parolă temporară la {{SITENAME}}",
-       "passwordremindertext": "Cineva (probabil dumneavoastră, de la adresa $1)\na cerut să vi se trimită o nouă parolă pentru {{SITENAME}} ($4).\nO parolă temporară pentru utilizatorul „$2” a fost generată și este acum „$3”.\nParola temporară va expira {{PLURAL:$5|într-o zi|în $5 zile|în $5 de zile}}.\n\nDacă această cerere a fost efectuată de altcineva sau dacă v-ați amintit\nparola și nu doriți să o schimbați, ignorați acest mesaj și continuați\nsă folosiți vechea parolă.",
+       "passwordremindertext": "Cineva (de la adresa IP $1)\na cerut să vi se trimită o nouă parolă pentru {{SITENAME}} ($4).\nO parolă temporară pentru utilizatorul „$2” a fost generată și este acum „$3”.\nParola temporară va expira {{PLURAL:$5|într-o zi|în $5 zile|în $5 de zile}}.\n\nDacă această cerere a fost efectuată de altcineva sau dacă v-ați amintit\nparola și nu doriți să o schimbați, ignorați acest mesaj și continuați\nsă folosiți vechea parolă.",
        "noemail": "Nu este nici o adresă de e-mail înregistrată pentru utilizatorul „$1”.",
        "noemailcreate": "Trebuie oferită o adresă e e-mail validă.",
        "passwordsent": "O nouă parolă a fost trimisă la adresa de e-mail a utilizatorului \"$1\". Te rugăm să te autentifici pe {{SITENAME}} după ce o primești.",
        "userjsonpreview": "<strong>Rețineți că vedeți doar o previzualizare a JSON-ului dumneavoastră de utilizator.\nAcesta nu a fost încă salvat!</strong>",
        "userjspreview": "'''Rețineți că vizualizați doar o previzualizare/versiune de testare a JavaScript-ului dumneavoastră de utilizator.'''\n'''Acesta nu a fost încă salvat!'''",
        "sitecsspreview": "'''Rețineți că doar previzualizați această foaie de stil.'''\n'''Ea nu a fost salvată încă!'''",
+       "sitejsonpreview": "<strong>Rețineți că doar previzualizați această configurație JSON.\nEl nu a fost salvat încă!</strong>",
        "sitejspreview": "'''Rețineți că doar previzualizați acest cod JavaScript.'''\n'''El nu a fost salvat încă!'''",
-       "userinvalidconfigtitle": "'''Avertizare:''' Nu există aspectul „$1”.\nPaginile .css și .js specifice utilizatorilor au titluri care încep cu literă mică; de exemplu {{ns:user}}:Foo/vector.css în comparație cu {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "<strong>Avertizare:</strong> Nu există skinul „$1”.\nPaginile .css, .json și .js specifice utilizatorilor au titluri care încep cu literă mică; de exemplu {{ns:user}}:Foo/vector.css în loc de {{ns:user}}:Foo/Vector.css.",
        "updated": "(Actualizat)",
        "note": "'''Notă:'''",
        "previewnote": "'''Țineți cont că aceasta este doar o previzualizare.'''\nModificările dumneavoastră nu au fost încă salvate!",
        "longpageerror": "'''Eroare: Textul pe care l-ați trimis are o lungime de {{PLURAL:$1|un kilooctet|$1 kiloocteți|$1 de kiloocteți}}, ceea ce înseamnă mai mult decât maximul de {{PLURAL:$2|un kilooctet|$2 kiloocteți|$2 de kiloocteți}}.'''\nSalvarea nu este posibilă.",
        "readonlywarning": "<strong>Atenție: Baza de date a fost blocată pentru întreținere, deci nu veți putea salva modificările în acest moment.</strong>\nPuteți copia textul într-un fișier text, păstrându-l pentru mai târziu.\n\nAdministratorul de sistem care a efectuat blocarea a oferit următoarea explicație: $1",
        "protectedpagewarning": "'''Atenție: această pagină a fost protejată astfel încât poate fi modificată doar de către administratori.'''\nUltima intrare în jurnal este afișată mai jos pentru referință:",
-       "semiprotectedpagewarning": "'''Observație: această pagină a fost protejată și poate fi modificată doar de către utilizatorii înregistrați.'''\nUltima intrare în jurnal este afișată mai jos pentru referință:",
+       "semiprotectedpagewarning": "<strong>Observație:</strong> această pagină a fost protejată și poate fi modificată doar de către utilizatorii autoconfirmați.\nUltima intrare în jurnal este afișată mai jos pentru referință:",
        "cascadeprotectedwarning": "<strong>Atenție:</strong> Această pagină a fost protejată, astfel încât numai [[Special:ListGroupRights|utilizatori cu drepturi specifice]] o pot modifica, fiind inclusă în {{PLURAL:$1|următoarea pagină protejată|următoarele pagini protejate}} în cascadă:",
        "titleprotectedwarning": "'''Atenție: această pagină a fost protejată astfel încât doar anumiți [[Special:ListGroupRights|utilizatori]] o pot crea.'''\nUltima intrare în jurnal este afișată mai jos pentru referință:",
        "templatesused": "{{PLURAL:$1|Format folosit|Formate folosite}} în această pagină:",
        "diff-paragraph-moved-toold": "Paragraful a fost mutat. Clic pentru a ajunge la vechea locație.",
        "difference-missing-revision": "{{PLURAL:$2|O versiune a|$2 versiuni ale|$2 de versiuni ale}} acestei diferențe ($1) nu {{PLURAL:$2|a fost găsită|au fost găsite}}.\n\nAcest lucru se întâmplă de obicei atunci când se accesează o legătură expirată către istoricul unei pagini șterse.\nDetalii se pot găsi în [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} jurnalul ștergerilor].",
        "searchresults": "Rezultatele căutării",
+       "search-filter-title-prefix-reset": "Caută toate paginile",
        "searchresults-title": "Rezultatele căutării pentru „$1”",
        "titlematches": "Rezultate din titlurile paginilor",
        "textmatches": "Rezultate din conținutul paginilor",
        "rcfilters-filter-humans-description": "Modificări făcute de oameni.",
        "rcfilters-filtergroup-reviewstatus": "Statutul reviziei",
        "rcfilters-filter-reviewstatus-unpatrolled-label": "Nepatrulate",
+       "rcfilters-filter-reviewstatus-manual-description": "Modificări marcate manual ca patrulate.",
        "rcfilters-filter-reviewstatus-manual-label": "Patrulate manual",
        "rcfilters-filter-reviewstatus-auto-label": "Patrulate automat",
        "rcfilters-filtergroup-significance": "Semnificație",
        "deadendpages": "Pagini fără legături",
        "deadendpagestext": "Următoarele pagini nu se leagă de alte pagini din acestă wiki.",
        "protectedpages": "Pagini protejate",
+       "protectedpages-filters": "Filtre:",
        "protectedpages-indef": "Doar protejări pe termen nelimitat",
        "protectedpages-summary": "Această pagină enumeră paginile protejate în acest moment. Pentru o listă a titlurilor protejate la creare, vedeți [[{{#special:ProtectedTitles}}|{{int:protectedtitles}}]].",
        "protectedpages-cascade": "Doar protejări în cascadă",
        "apisandbox-dynamic-error-exists": "Un parametru cu numele „$1” există deja.",
        "apisandbox-deprecated-parameters": "Parametri învechiți",
        "apisandbox-fetch-token": "Completează automat jetonul",
+       "apisandbox-add-multi": "Adaugă",
        "apisandbox-submit-invalid-fields-title": "Anumite câmpuri nu sunt valide",
        "apisandbox-submit-invalid-fields-message": "Corectați câmpurile marcate și încercați din nou.",
        "apisandbox-results": "Rezultate",
        "speciallogtitlelabel": "Destinație (titlu sau {{ns:user}}:numeutilizator pentru utilizator):",
        "log": "Jurnale",
        "logeventslist-submit": "Afișează",
+       "logeventslist-more-filters": "Arată jurnale adiționale:",
+       "logeventslist-patrol-log": "Jurnal de patrulare",
+       "logeventslist-tag-log": "Jurnal etichete",
        "all-logs-page": "Toate jurnalele publice",
        "alllogstext": "Afișare combinată a tuturor jurnalelor {{SITENAME}}.\nPuteți limita vizualizarea selectând tipul jurnalului, numele de utilizator sau pagina afectată.",
        "logempty": "Nici o înregistrare în jurnal.",
        "dellogpage": "Jurnal ștergeri",
        "dellogpagetext": "Mai jos se află lista celor mai recente elemente șterse.",
        "deletionlog": "jurnal pagini șterse",
+       "log-name-create": "Jurnal creare pagini",
+       "log-description-create": "Mai jos se află lista celor mai recente pagini create.",
+       "logentry-create-create": "$1 {{GENDER:$2|a creat|a creat}} pagina $3",
        "reverted": "Revenire la o versiune mai veche",
        "deletecomment": "Motiv:",
        "deleteotherreason": "Motiv diferit/suplimentar:",
        "uctop": "(actuală)",
        "month": "Din luna (și dinainte):",
        "year": "Din anul (și dinainte):",
+       "date": "Din data (și dinainte):",
        "sp-contributions-newbies": "Arată doar contribuțiile conturilor noi",
        "sp-contributions-newbies-sub": "Pentru începători",
        "sp-contributions-newbies-title": "Contribuțiile utilizatorului pentru conturile noi",
        "version-specialpages": "Pagini speciale",
        "version-parserhooks": "Hook-uri parser",
        "version-variables": "Variabile",
+       "version-editors": "Editori",
        "version-antispam": "Prevenirea spamului",
        "version-other": "Altele",
        "version-mediahandlers": "Suport media",
        "diff-form": "Diferențe",
        "diff-form-submit": "Arată diferențele",
        "permanentlink": "Legătură permanentă",
+       "permanentlink-submit": "Mergi la versiunea",
        "dberr-problems": "Ne cerem scuze! Acest site întâmpină dificultăți tehnice.",
        "dberr-again": "Așteptați câteva minute și încercați din nou.",
        "dberr-info": "(Nu se poate accesa baza de date: $1)",
        "limitreport-templateargumentsize-value": "$1/$2 {{PLURAL:$2|octet|octeți|de octeți}}",
        "limitreport-expansiondepth": "Cea mai mare profunzime a expansiunii",
        "limitreport-expensivefunctioncount": "Număr de funcții de analiză costisitoare",
+       "limitreport-unstrip-size-value": "$1/$2 {{PLURAL:$2|octet|octeți|de octeți}}",
        "expandtemplates": "Expandare formate",
        "expand_templates_intro": "Această pagină specială servește la expandarea recursivă a tuturor formatelor dintr-un wikitext. Ea acționează și asupra funcțiilor de analiză (''parser'') de tipul <nowiki>{{</nowiki>#if:...}}, a variabilelor precum <nowiki>{{</nowiki>CURRENTDAY}} și în general asupra oricăror coduri cuprinse între acolade duble.",
        "expand_templates_title": "Titlul contextului (de exemplu pentru {{PAGENAME}}):",
        "pagelang-reason": "Motiv",
        "pagelang-submit": "Trimite",
        "pagelang-nonexistent-page": "Pagina $1 nu există.",
+       "pagelang-unchanged-language": "Pagina $1 e deja în limba $2.",
        "right-pagelang": "Modifică limba paginii",
        "action-pagelang": "modificați limba paginii",
        "log-name-pagelang": "Jurnal modificare limbă",
        "special-characters-group-thai": "Thailandeză",
        "special-characters-group-lao": "Laoțiană",
        "special-characters-group-khmer": "Khmeră",
+       "special-characters-group-canadianaboriginal": "Aborigen Canadian",
        "special-characters-title-endash": "linie de pauză (en dash)",
        "special-characters-title-emdash": "linie de pauză (em dash)",
        "special-characters-title-minus": "semnul minus",
        "mw-widgets-dateinput-no-date": "Nicio dată selectată",
        "mw-widgets-dateinput-placeholder-day": "AAAA-LL-ZZ",
        "mw-widgets-dateinput-placeholder-month": "AAAA-LL",
+       "mw-widgets-mediasearch-input-placeholder": "Căutare de multimedia",
        "mw-widgets-mediasearch-noresults": "Niciun rezultat găsit.",
        "mw-widgets-titleinput-description-new-page": "pagina nu există încă",
        "mw-widgets-titleinput-description-redirect": "redirecționare către $1",
        "log-action-filter-managetags-delete": "Ștergere tag",
        "log-action-filter-managetags-activate": "Activare tag",
        "log-action-filter-managetags-deactivate": "Dezactivare tag",
+       "log-action-filter-newusers-create": "Creare de utilizator anonim",
+       "log-action-filter-newusers-create2": "Creare de un utilizator înregistrat",
        "log-action-filter-newusers-autocreate": "Creare automată",
        "log-action-filter-patrol-patrol": "Patrulă manuală",
        "log-action-filter-patrol-autopatrol": "Patrulă automată",
        "gotointerwiki-invalid": "Titlul specificat nu este valid.",
        "pagedata-title": "Datele paginii",
        "pagedata-not-acceptable": "Niciun format corespunzător găsit. Tipuri MIME acceptate: $1",
-       "pagedata-bad-title": "Titlu invalid: $1."
+       "pagedata-bad-title": "Titlu invalid: $1.",
+       "passwordpolicies": "Politică parole",
+       "passwordpolicies-group": "Grup",
+       "passwordpolicies-policies": "Politici",
+       "passwordpolicies-policy-minimalpasswordlength": "Parola trebuie să aibă cel puțin $1 {{PLURAL:$1|caracter|caractere|de caractere}}.",
+       "passwordpolicies-policy-minimumpasswordlengthtologin": "Parola trebuie să aibă cel puțin $1 {{PLURAL:$1|caracter|caractere|de caractere}} pentru a vă putea autentifica.",
+       "passwordpolicies-policy-passwordcannotmatchusername": "Parola nu poate fi identică cu numele de utilizator",
+       "passwordpolicies-policy-passwordcannotmatchblacklist": "Parolele nu pot fi cele de pe lista neagră",
+       "passwordpolicies-policy-maximalpasswordlength": "Parola trebuie să aibă cel puțin $1 {{PLURAL:$1|caracter|caractere|de caractere}}.",
+       "passwordpolicies-policy-passwordcannotbepopular": "Parola nu poate fi {{PLURAL:$1|o parolă populară|în lista celor $1 parole populare|în lista celor $1 de parole populare}}."
 }
index 36834f6..bd66ad8 100644 (file)
        "diff-paragraph-moved-toold": "Пункт был перемещен. Нажмите, чтобы перейти к старому местоположению.",
        "difference-missing-revision": "Не {{PLURAL:$2|1=найдена|найдены}} {{PLURAL:$2|$2 версия|$2 версий|$2 версии|1=одна из версий}} для этого сравнения ($1).\n\nТакое обычно случается при переходе по устаревшей ссылке сравнения версий для страницы, которая была удалена.\nПодробности могут быть в [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} журнале удалений].",
        "searchresults": "Результаты поиска",
+       "search-filter-title-prefix": "Искать только на страницах, название которых начинается с «$1»",
        "search-filter-title-prefix-reset": "Искать все страницы",
        "searchresults-title": "Поиск «$1»",
        "titlematches": "Совпадения в названиях страниц",
        "http-timed-out": "Истекло время ожидания HTTP-запроса.",
        "http-curl-error": "Ошибка обращения к URL: $1",
        "http-bad-status": "Во время обработки HTTP-запроса обнаружена проблема: $1 $2",
+       "http-internal-error": "Внутренняя ошибка HTTP.",
        "upload-curl-error6": "Невозможно обратить по указанному адресу.",
        "upload-curl-error6-text": "Невозможно обратить по указанному адресу. Пожалуйста, проверьте, что адрес верен, а сайт доступен.",
        "upload-curl-error28": "Время, отведённое на загрузку, истекло",
index 76e4181..3f62e10 100644 (file)
        "protectedpagetext": "ᱱᱚᱣᱟ ᱥᱟᱦᱴᱟ ᱫᱚ ᱚᱞ ᱥᱟᱯᱲᱟᱣ ᱞᱟᱹᱜᱤᱛᱛᱮ ᱫᱚ ᱵᱟᱸᱪᱟᱣ ᱜᱮᱭᱟ᱾",
        "viewsourcetext": "ᱱᱚᱭᱟ ᱥᱟᱦᱴᱟᱢ ᱧᱮᱞ ᱫᱟᱲᱮᱭᱟᱜ-ᱟ ᱟᱨᱮᱢ ᱠᱚᱯᱤ ᱫᱟᱲᱮᱭᱟᱜ-ᱟ᱾",
        "viewyourtext": "ᱟᱢ ᱫᱚ '''ᱟᱢᱟᱜ ᱥᱟᱯᱲᱟᱣ''' ᱥᱟᱦᱴᱟ ᱧᱮᱞ ᱟᱨ ᱮᱢ ᱠᱚᱯᱤ ᱦᱟᱛᱟᱣ ᱫᱟᱲᱮᱭᱟᱜ-ᱟ:",
-       "protectedinterface": "Noa sakam reaḱ babotko do wiki sofṭoyer reaḱ mit́ṭen inṭarfes khobore ema, onate noa do rukhiyạ doho hoeakana.",
-       "cascadeprotected": "Noa sakam do sompadon khon rukhiyạre menaḱa, karon sakam do latar reaḱ {{PLURAL:$1 gan sakam reaḱ gan sakam reaḱ}} bhitrire, oka sakam do (cascading) te rukhiyạ menaḱa:\n$2",
+       "protectedinterface": "ᱱᱚᱣᱟ ᱥᱟᱦᱴᱟ ᱨᱮᱭᱟᱜ ᱵᱟᱵᱚᱛᱠᱚ ᱫᱚ ᱣᱤᱠᱤ ᱥᱚᱯᱷᱴᱚᱭᱟᱨ ᱨᱮᱭᱟᱜ ᱢᱤᱫᱴᱮᱱ ᱤᱱᱴᱟᱨᱯᱷᱮᱥ ᱠᱷᱚᱵᱚᱨᱮ ᱮᱢᱟ, ᱚᱱᱟᱛᱮ ᱱᱚᱣᱟ ᱫᱟ ᱨᱩᱠᱷᱤᱭᱟᱹ ᱫᱚᱦᱚ ᱦᱩᱭᱠᱟᱱᱟ᱾",
+       "cascadeprotected": "ᱱᱚᱣᱟ ᱥᱟᱦᱴᱟ ᱫᱚ ᱥᱟᱯᱲᱟᱣ ᱠᱷᱚᱱ ᱨᱩᱠᱷᱤᱭᱟᱹᱨᱮ ᱢᱮᱱᱟᱜ-ᱟ, ᱠᱟᱨᱚᱱ ᱱᱚᱣᱟ ᱥᱟᱦᱴᱟ ᱫᱚ ᱞᱟᱛᱟᱨ ᱨᱮᱭᱟᱜ {{PLURAL:$1 ᱜᱟᱱ ᱥᱟᱦᱴᱟ ᱨᱮᱭᱟᱜ ᱜᱟᱱ ᱥᱟᱦᱴᱟ ᱨᱮᱭᱟᱜ}} ᱵᱷᱤᱛᱨᱤᱨᱮ, ᱚᱠᱟ ᱥᱟᱦᱴᱟ ᱫᱚ (cascading) ᱛᱮ ᱨᱩᱠᱷᱤᱭᱟᱹ ᱢᱮᱱᱟᱜ-ᱟ:\n$2",
        "namespaceprotected": "Amaḱ do sakamko joṛao lạgit́te ạidạri banuḱ tama '''$1''' ńutumjayga.",
        "ns-specialprotected": "Asokay teaḱ sakamkodo baṅ oltoṅgea.",
        "exception-nologin": "ᱵᱟᱢ ᱵᱚᱞᱚ ᱟᱠᱟᱱᱟ",
        "createacct-emailoptional": "ᱤᱢᱮᱞ ᱴᱷᱤᱠᱱᱟ (ᱟᱢᱠᱩᱥᱤ)",
        "createacct-email-ph": "ᱟᱢᱟᱜ ᱤᱢᱮᱞ ᱵᱩᱴᱟᱹ ᱟᱫᱮᱨᱢᱮ",
        "createacct-another-email-ph": "ᱤᱢᱮᱞ ᱵᱩᱴᱟᱹ ᱟᱫᱮᱨᱢᱮ",
-       "createaccountmail": "E-mail hotete",
+       "createaccountmail": "E-mail ᱦᱚᱛᱮᱛᱮ",
        "createacct-realname": "ᱥᱚᱛ ᱧᱩᱛᱩᱢ (ᱚᱯᱥᱱᱟᱞ)",
        "createacct-reason": "ᱚᱡᱮ",
        "createacct-reason-ph": "ᱪᱮᱫᱟᱜ ᱟᱢ ᱮᱴᱟᱜ ᱦᱤᱥᱟᱹᱵᱮᱢ ᱛᱮᱭᱟᱨᱫᱟ",
        "nocookieslogin": "{{SITENAME}} re kuki hotete beoharićaḱ bhitri boloḱ do hoyoḱa. Amaḱ sendrare kuki bondo menaḱa. Kuki cạlu kate arhõ kurumuṭuimẽ.",
        "nocookiesfornew": "Beoharićaḱ ekaunṭ do baṅ tear akana, Cedaḱ je noa ńamoḱ jaega babote ale do bale uruma.\nAle do baḍae ocolem amaḱ kuki doe kạmikana, sakam do arhõ rakaṕ lạgit́te kurumuṭuemẽ.",
        "noname": "Am do asol beoharićaḱ ńutum ṭhikte bam emakada.",
-       "loginsuccesstitle": "Bhitri boloḱ do moctege puraoena",
+       "loginsuccesstitle": "ᱵᱷᱤᱛᱨᱤ ᱵᱚᱠᱟᱜ ᱫᱚ ᱢᱚᱡᱽᱛᱮᱜᱮ ᱯᱩᱨᱟᱹᱣᱱᱟ",
        "loginsuccess": "'''Am do nitge \"$1\" ńutumte {{SITENAME}} rem bolo akana.'''",
-       "nosuchuser": "\"$1\" ńutuman jahan beoharić bạnuea.\nBeoharićaḱ ńutum do bukṛogea.\nAmaḱ bananko ńelmẽ, se [[Special:Userlogin/signup nãwã mit́ṭen ekaunṭ tearmẽ]].",
+       "nosuchuser": "\"$1\" ᱧᱩᱛᱩᱢᱟᱱ ᱡᱟᱸᱦᱟᱱ ᱵᱮᱵᱷᱟᱨᱤᱡ ᱵᱟᱹᱱᱩᱭᱟ᱾ ᱵᱮᱵᱷᱟᱨᱤᱡ ᱧᱩᱛᱩᱢ ᱫᱚ ᱵᱩᱠᱨᱩᱜᱮᱭᱟ᱾ ᱟᱢᱟᱜ ᱵᱟᱱᱟᱱᱠᱩ  ᱧᱮᱞᱢᱮ, ᱥᱮ [[Special:CreateAccount|ᱱᱟᱣᱟ ᱢᱤᱫᱴᱮᱱ ᱦᱤᱫᱟᱹᱵ ᱠᱷᱟᱛᱟ ᱛᱮᱭᱟᱨᱢᱮ]]᱾",
        "nosuchusershort": "\"$1\" ńutuman jahãe beoharko do banuḱkoa. Ńutum reaḱ banan biḍaomẽ.",
        "nouserspecified": "Am do pusṭaote laṛcaṛićaḱ ńutum em hoyoḱtama.",
        "login-userblocked": "Nui laṛcaṛic doe esetgea. bhitri boloḱ ạidạri bań emoḱ kana.",
-       "wrongpassword": "Bań milaoaḱ oku nambar em hoyakana.\nDaya kate arhõ mitdhom kurumuṭuyme.",
+       "wrongpassword": "ᱵᱮᱷᱟᱨᱤᱭᱟᱜ ᱧᱩᱛᱩᱢ ᱥᱮ ᱩᱠᱩ ᱱᱟᱢᱵᱟᱨ ᱵᱟᱝ ᱢᱤᱞᱟᱹᱜ ᱠᱟᱱᱟ᱾ ᱫᱚᱲᱦᱟᱛᱮ ᱠᱩᱨᱩᱢᱩᱴᱩᱭᱢᱮ᱾",
        "wrongpasswordempty": "Em hoyen oku nambar do cetge banuḱa.\nDaya katet́ arhõ kurumuṭuyme.",
        "passwordtooshort": "Uku nambar do {{PLURAL:$1 1 horop reaḱ $1 horop reaḱ}} mudre hoyoḱ jạruṛa.",
        "password-name-match": "Amaḱ oku nambar do amaḱ ńutum khon eṭaḱ hoyoḱ jạruṛtama.",
        "noemail": "\"$1\" beoharić lạgit́te do jahan e-mail ṭhikana rukhiyạ doho bạnuḱa.",
        "noemailcreate": "Am do mitṭen jewet e-mail ṭhikạna em jaruṛ menaḱtama.",
        "passwordsent": "\"$1\" ṭhikạnate resṭariyen e-mail lạgit́te mitṭen oku nambar em hoyena.\nDaya kate ńam porte arhõ bhitri boloḱme.",
-       "blocked-mailpassword": "Amaḱ IP ṭhikạna khon sompadon do bondo menaḱa, Onate noa ṭhikạna baṅ beohar kate uku nambar ruạṛ baṅ hoyoḱa.",
+       "blocked-mailpassword": "ᱟᱢᱟᱜ IP ᱴᱷᱤᱠᱟᱹᱱᱟ ᱠᱷᱚᱱ ᱥᱟᱯᱲᱟᱣ ᱰᱚᱱᱫᱷ ᱜᱮᱭᱟ᱾ ᱚᱱᱟᱛᱮ ᱱᱚᱣᱟ ᱴᱷᱤᱠᱟᱹᱱᱟ ᱵᱟᱝ ᱵᱮᱵᱷᱟᱨ ᱠᱟᱛᱮ ᱩᱠᱩ ᱱᱟᱢᱵᱟᱨ ᱨᱩᱣᱟᱹᱲ ᱵᱟᱝ ᱦᱩᱭᱩᱜ-ᱟ᱾",
        "mailerror": "E-mail kulte eṭkẽṭõrẽ: $1",
-       "emailauthenticated": "Amaḱ e-mail ṭhikạna do $2 tạrikh reaḱ $3 re jạhirena.",
+       "emailauthenticated": "ᱟᱢᱟᱜ e-mail ᱴᱷᱤᱠᱟᱹᱱᱟ ᱫᱚ $2 ᱛᱟᱹᱨᱤᱠᱷ ᱨᱮᱭᱟᱜ $3 ᱨᱮ ᱡᱟᱹᱦᱤᱨᱮᱱᱟ᱾",
        "emailnotauthenticated": "Amaḱ e-mail reaḱ ṭhikạna do <strong> nit hõ baṅ jacay akana</strong> latar reaḱ features lạgit́te jahan e-mail do baṅkuloḱa.",
        "noemailprefs": "Noa features ko kạmie ocoy lạgit́te mit́ṭen e-mail ṭhikạna dohoe hoyoḱa.",
        "emailconfirmlink": "Amaḱ e-mail ṭhikana do sạriyme.",
index a147e4b..6bd59b7 100644 (file)
        "listingcontinuesabbrev": "جاري.",
        "index-category": "ڏسڻيل صفحا",
        "noindex-category": "غيرڏسڻيل صفحا",
-       "broken-file-category": "Ù\81ائÙ\8aÙ\84 Ø¬Ù\8a Ù½Ù½ل ڳنڍڻن وارا صفحا",
+       "broken-file-category": "ٽٽÙ\84 Ù\81ائل ڳنڍڻن وارا صفحا",
        "about": "بابت",
        "article": "موادي صفحو",
        "newwindow": "(نئين دريءَ ۾ کلندو)",
        "prefs-email": "برقٽپال چارا",
        "prefs-rendering": "حليو",
        "saveprefs": "سانڍيو",
+       "restoreprefs": "شروعاتي ترتيبون واپس ڪيو (سمورن خانن ۾)",
        "prefs-editing": "سنوارڻ",
        "searchresultshead": "ڳولا",
        "stub-threshold-sample-link": "نمونو",
        "timezoneregion-pacific": "ماٺو ساگر",
        "allowemail": "ٻين واپرائيندڙن کي مون ڏانھن برقٽپال ڪرڻ جي اجازت ڏيو",
        "email-allow-new-users-label": "نوان واپرائيندڙ برق ٽپال موڪلين",
+       "email-blacklist-label": "هنن واپرائندڙن کي مون ڏانهن برقٽپال ڪرڻ جي اجازت نه ڏيو:",
        "prefs-searchoptions": "ڳولا",
        "prefs-namespaces": "نانءُپولار",
        "default": "ڏنل",
        "email": "برقٽپال",
        "prefs-help-realname": "اصل نالو اختياري آهي.\nجيڪڏهن توهان اصل نالو ڄاڻائڻ جو فيصلو ٿا ڪريو، تہ اهو توهان کي توهان جي ڪم جي مڃتا ڏيڻ لاءِ ڪم آندو ويندو.",
        "prefs-help-email": "برقٽپال ڄاڻائڻ اختياري آهي، پر جڏهن توهان ڳجھولفظ وسري ويندا آهيو، تڏهن ان جو استعمال توهان کي نئون ڳجھولفظ ڏيڻ لاءِ استعمال ڪيو ويندو آهي.",
+       "prefs-help-email-others": "اوهان چونڊ ڪري سگھو ٿا ته اوهان جي ذاتي يا بحث صفحي تي موجود ڳنڍڻي ذريعي ٻيو ڪو واپرائيندڙ اوهان کي برقٽپال ڪري سگھي ٿو يا نه.\nاوهان جو برقٽپال پتو ٻين پاران رابطي ڪرڻ وقت ڳجهو رکيو ويندو.",
        "prefs-help-email-required": "برقٽپال پتو گھربل آهي.",
        "prefs-info": "بنيادي ڄاڻ",
        "prefs-i18n": "بين‌الاقوامڪاري",
        "rcfilters-days-show-days": "$1 {{PLURAL:$1|ڏينھُن|ڏينھَن}}",
        "rcfilters-highlighted-filters-list": "نمايان-ٿيل:$1",
        "rcfilters-quickfilters": "سانڍيل ڇاڻيون",
+       "rcfilters-quickfilters-placeholder-title": "اڃان ڪا به ڇاڻي سانڍيل ناهي",
        "rcfilters-savedqueries-defaultlabel": "سانڍيل ڇاڻيون",
        "rcfilters-savedqueries-rename": "ٻيھر نالو ڏيو",
        "rcfilters-savedqueries-new-name-label": "نالو",
        "block": "واپرائيندڙ کي بندشيو",
        "unblock": "واپرائيندڙ کي اڻبندشيو",
        "blockip": "{{GENDER:$1|واپرائيندڙ}} تي بندش وجھو",
+       "ipaddressorusername": "آئي پي پتو يا واپرائيندڙ-نانءُ:",
        "ipbexpiry": "اختتام:",
        "ipbreason": "سبب:",
        "ipbother": "ٻيو وقت:",
        "ipb-confirm": "بندش جي پڪ ڪريو",
        "badipaddress": "ناقابلڪار آءِ پي پتو",
        "blockipsuccesssub": "بندش ڪامياب ٿي",
+       "ipb-blockingself": "اوهان پنهنجي پاڻ تي بندش وجهي رهيا آهيو! ڇا اوهان اهو ڪرڻ پسند ڪندو؟",
        "unblockip": "واپرائيندڙ کي اڻبندشيو",
        "ipusubmit": "اها بندش هٽايو",
        "unblocked-range": "$1 تان بندش هٽي چڪي آهي.",
        "file-info-gif-frames": "$1 {{PLURAL:$1|فريم|فريمَ}}",
        "file-info-png-frames": "$1 {{PLURAL:$1|فريم|فريمَ}}",
        "newimages": "نون فائيلن جي گيلري",
+       "newimages-user": "آئي پي پتو يا واپرائيندڙ-نانءُ",
        "noimages": "ڏسڻ لاءِ ڪجھہ ناهي.",
        "ilsubmit": "ڳوليو",
        "bydate": "تاريخوار",
        "logentry-delete-revision": "$1 $3: $4 صفحي تي {{PLURAL:$5|ھڪ مسودي|$5 مسودن}} جي ظاھريت {{GENDER:$2|تبديل ڪئي}}",
        "revdelete-content-hid": "مواد لڪيل",
        "revdelete-uname-hid": "واپرائيندڙ-نانءُ لڪل",
+       "logentry-block-block": "$1، {{GENDER:$4|$3}} تي $5 وقت جي خاتمي تائين {{GENDER:$2|بندش هئي آهي}} $6",
        "logentry-move-move": "$1 {{GENDER:$2|چوريو}} صفحو $3 ڏانهن $4",
        "logentry-move-move-noredirect": "$1 $3 صفحي کي $4 ڏانھن {{GENDER:$2|چوريو}} سواءِ ڪو ريڊائريڪٽ ڇڏيندي",
        "logentry-move-move_redir": "$1 $3 کي $4 ڏانھن ريڊائريڪٽ مٿان {{GENDER:$2|چوريو}}",
        "mw-widgets-mediasearch-noresults": "ڪي-بہ نتيجا نہ لڌا.",
        "mw-widgets-titleinput-description-new-page": "اڃا اهو صفحو وجود نہ ٿو رکي",
        "mw-widgets-titleinput-description-redirect": "$1 ڏانهن چوريل",
+       "mw-widgets-usersmultiselect-placeholder": "وڌيڪ شامل ڪيو...",
        "date-range-from": "تاريخ کان:",
        "date-range-to": "تاريخ تائين:",
        "log-action-filter-all": "سڀ"
index a3d729e..f30b990 100644 (file)
        "diff-paragraph-moved-toold": "Odstavek je bil premaknjen. Kliknite, da skočite na staro nahajališče.",
        "difference-missing-revision": "{{PLURAL:$2|Ene redakcije|$2 redakcij}} razlike ($1) {{PLURAL:$2|nisem}} našel.\n\nPo navadi se to zgodi, ko sledite zastareli povezavi na razliko redakcij strani, ki jo je nekdo izbrisal.\nPodrobnosti lahko najdete v [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} dnevniku brisanja].",
        "searchresults": "Izid iskanja",
+       "search-filter-title-prefix": "Išči samo na straneh, katerih naslovi se začnejo z \"$1\"",
+       "search-filter-title-prefix-reset": "Išči vse strani",
        "searchresults-title": "Zadetki za povpraševanje »$1«",
        "titlematches": "Ujemanje z naslovom članka",
        "textmatches": "Ujemanje z besedilom članka",
        "prefs-rc": "Zadnje spremembe",
        "prefs-watchlist": "Spisek nadzorov",
        "prefs-editwatchlist": "Uredi spisek nadzorov",
-       "prefs-editwatchlist-label": "Uredite vnose na svojem spisku nadzorov:",
+       "prefs-editwatchlist-label": "Uredi vnose na svojem spisku nadzorov:",
        "prefs-editwatchlist-edit": "Preglejte in odstranite naslove s svojega spiska nadzorov",
        "prefs-editwatchlist-raw": "Uredite gol spisek nadzorov",
        "prefs-editwatchlist-clear": "Počistite svoj spisek nadzorov",
        "http-timed-out": "Zahteva HTTP je potekla.",
        "http-curl-error": "Napaka pri doseganju URL: $1",
        "http-bad-status": "Med zahtevo HTTP je prišlo do težave: $1 $2",
+       "http-internal-error": "Notranja napaka HTTP.",
        "upload-curl-error6": "Ni možno doseči URL",
        "upload-curl-error6-text": "Navedenega naslova URL ni mogoče doseči.\nProsimo, ponovno preverite pravilnost URL-a in delovanje strani.",
        "upload-curl-error28": "Časovna prekinitev nalaganja",
        "speciallogtitlelabel": "Cilj (naslov ali {{ns:user}}:uporabniškoime za uporabnika):",
        "log": "Dnevniki",
        "logeventslist-submit": "Prikaži",
-       "logeventslist-more-filters": "Več filtrov:",
+       "logeventslist-more-filters": "Prikaži dodatne dnevnike:",
        "logeventslist-patrol-log": "Dnevnik patrulje",
        "logeventslist-tag-log": "Dnevnik oznak",
        "all-logs-page": "Vsi javni dnevniki",
index c0515a5..03df866 100644 (file)
@@ -38,7 +38,8 @@
                        "Acamicamacaraca",
                        "BokicaK",
                        "BadDog",
-                       "Fitoschido"
+                       "Fitoschido",
+                       "Stalker"
                ]
        },
        "tog-underline": "Подвлачење веза:",
        "youhavenewmessagesmulti": "Имате нове поруке на $1",
        "editsection": "уреди",
        "editold": "уреди",
-       "viewsourceold": "изворни код",
+       "viewsourceold": "изворник",
        "editlink": "уреди",
-       "viewsourcelink": "изворни код",
+       "viewsourcelink": "изворник",
        "editsectionhint": "Уредите одељак „$1“",
        "toc": "Садржај",
        "showtoc": "прикажи",
        "perfcached": "Следећи подаци су кеширани и можда нису ажурирани. У кешу {{PLURAL:$1|је доступан највише један резултат|су доступна највише $1 резултата|је доступно највише $1 резултата}}.",
        "perfcachedts": "Следећи подаци су кеширани и последњи пут ажурирани на датум $2 у $3 ч. У кешу {{PLURAL:$4|је доступан највише један резултат|су доступна највише $4 резултата|је доступно највише $4 резултата}}.",
        "querypage-no-updates": "Ажурирање ове странице је тренутно онемогућено.\nПодаци који се овде налазе могу бити застарели.",
-       "viewsource": "Изворни код",
-       "viewsource-title": "Изворни код за страницу $1",
+       "viewsource": "Изворник",
+       "viewsource-title": "Изворник странице $1",
        "actionthrottled": "Радња је успорена",
        "actionthrottledtext": "У циљу борбе против непожељних порука, ограничене су вам измене у одређеном времену, а управо сте прешли то ограничење. Покушајте поново за неколико минута.",
        "protectedpagetext": "Ова страница је закључана за измене и друге радње.",
        "viewsourcetext": "Можете да читате и копирате изворник ове странице.",
-       "viewyourtext": "Можете да погледате и копирате изворни код <strong>Ваших измена</strong> на овој страници.",
+       "viewyourtext": "Можете да погледате и копирате изворник <strong>Ваших измена</strong> на овој страници.",
        "protectedinterface": "Ова страница садржи текст интерфејса за софтвер на овом викију и заштићена је ради спречавања злоупотребе.\nДа бисте додали или изменили преводе било којег викија, посетите [https://translatewiki.net/ translatewiki.net], пројекат за локализацију Медијавикија.",
        "editinginterface": "<strong>Упозорење:</strong> уређујете страницу која се користи за приказивање текста корисничког окружења.\nИзмене на овој страници ће утицати на све кориснике овог викија.",
        "translateinterface": "Да додате или промените преводе за све викије, посетите [https://translatewiki.net/ Транслејтвики], пројекат за локализацију Медијавикија.",
        "botpasswords-label-resetpassword": "Ресетуј лозинку",
        "botpasswords-label-grants": "Применљиве дозволе:",
        "botpasswords-label-grants-column": "Одобрено",
-       "botpasswords-bad-appid": "„$1” није валидан назив бота.",
+       "botpasswords-bad-appid": "Име бота „$1” није валидно.",
        "botpasswords-insert-failed": "Неуспешно додавање бота \"$1\". Да ли је већ додат?",
        "botpasswords-update-failed": "Није могуће ажурирати бота \"$1\". Да ли је обрисан?",
        "botpasswords-created-title": "Направљена лозинка бота",
        "powersearch-togglelabel": "Изабери:",
        "powersearch-toggleall": "Све",
        "powersearch-togglenone": "Ништа",
-       "powersearch-remember": "Ð\97апамÑ\82и Ð¼Ð¾Ñ\98 Ð¸Ð·Ð±Ð¾Ñ\80 Ð·Ð° Ð±Ñ\83дÑ\83Ñ\9bе Ð¿Ñ\80еÑ\82Ñ\80аге",
+       "powersearch-remember": "Запамти избор за будуће претраге",
        "search-external": "Спољашња претрага",
        "searchdisabled": "Претрага је онемогућена.\nУ међувремену можете тражити преко Гугла.\nУпамтите да његови пописи овог викија могу бити застарели.",
        "search-error": "Дошло је до грешке приликом претраге: $1",
        "img-auth-accessdenied": "Приступ је одбијен",
        "img-auth-nopathinfo": "Недостаје PATH_INFO.\nВаш сервер није подешен да прослеђује овакве податке.\nМожда је заснован на CGI-ју који не подржава img_auth.\nПогледајте https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization?uselang=sr-ec.",
        "img-auth-notindir": "Захтевана путања није у подешеној фасцикли за отпремање.",
-       "img-auth-badtitle": "Не могу да створим исправан наслов за „$1“.",
+       "img-auth-badtitle": "Не могу да направим ваљан наслов за „$1“.",
        "img-auth-nologinnWL": "Нисте пријављени и „$1” није на списку дозвољених.",
        "img-auth-nofile": "Датотека „$1“ не постоји.",
        "img-auth-isdir": "Покушавате да приступите фасцикли „$1“.\nДозвољен је само приступ датотекама.",
        "speciallogtitlelabel": "Циљ (наслов или {{ns:user}}:корисничко име):",
        "log": "Дневници",
        "logeventslist-submit": "Прикажи",
+       "logeventslist-patrol-log": "Дневник патролирања",
+       "logeventslist-tag-log": "Дневник ознака",
        "all-logs-page": "Сви јавни дневници",
        "alllogstext": "Скупни приказ свих доступних историја овог викија.\nМожете сузити приказ одабирући врсту историје, корисничког имена или тражене странице.",
        "logempty": "Нема пронађених уноса у дневнику.",
        "allinnamespace": "Све странице (именски простор $1)",
        "allpagessubmit": "Иди",
        "allpagesprefix": "Прикажи странице с префиксом:",
-       "allpagesbadtitle": "Ð\9dаведени Ð½Ð°Ð·Ð¸Ð² Ñ\81Ñ\82Ñ\80аниÑ\86е Ð½Ð¸Ñ\98е Ð¸Ñ\81пÑ\80аван Ð¸Ð»Ð¸ Ñ\81адÑ\80жи Ð¼ÐµÑ\92Ñ\83Ñ\98езиÑ\87ки Ð¸Ð»Ð¸ Ð¼ÐµÑ\92Ñ\83вики Ð¿Ñ\80еÑ\84икÑ\81.\nÐ\9cожда Ñ\81адÑ\80жи Ð·Ð½Ð°ÐºÐ¾Ð²Ðµ који се не могу користити у насловима.",
+       "allpagesbadtitle": "Ð\9dаведени Ð½Ð°Ð·Ð¸Ð² Ñ\81Ñ\82Ñ\80аниÑ\86е Ð½Ð¸Ñ\98е Ð²Ð°Ñ\99ан Ð¸Ð»Ð¸ Ñ\81адÑ\80жи Ð¼ÐµÑ\92Ñ\83Ñ\98езиÑ\87ки Ð¸Ð»Ð¸ Ð¼ÐµÑ\92Ñ\83вики Ð¿Ñ\80еÑ\84икÑ\81.\nÐ\9cожда Ñ\81адÑ\80жи Ñ\98едан Ð¸Ð»Ð¸ Ð²Ð¸Ñ\88е Ð·Ð½Ð°ÐºÐ¾Ð²Ð° који се не могу користити у насловима.",
        "allpages-bad-ns": "{{SITENAME}} нема именски простор „$1“.",
        "allpages-hide-redirects": "Сакриј преусмерења",
        "cachedspecial-viewing-cached-ttl": "Гледате кеширану верзију ове странице, која може бити стара и до $1.",
        "trackingcategories-nodesc": "Опис није доступан.",
        "trackingcategories-disabled": "Категорија је онемогућена",
        "mailnologin": "Нема адресе за слање",
-       "mailnologintext": "Ð\9cоÑ\80аÑ\82е Ð±Ð¸Ñ\82и [[Special:UserLogin|пÑ\80иÑ\98авÑ\99ени]] Ð¸ Ð¸Ð¼Ð°Ñ\82и Ð¸Ñ\81пÑ\80аван имејл адресу у [[Special:Preferences|подешавањима]] да бисте слали имејлове другим корисницима.",
+       "mailnologintext": "Ð\9cоÑ\80аÑ\82е Ð±Ð¸Ñ\82и [[Special:UserLogin|пÑ\80иÑ\98авÑ\99ени]] Ð¸ Ð¸Ð¼Ð°Ñ\82и Ð²Ð°Ñ\99анÑ\83 имејл адресу у [[Special:Preferences|подешавањима]] да бисте слали имејлове другим корисницима.",
        "emailuser": "Пошаљи имејл",
        "emailuser-title-target": "Слање имејла {{GENDER:$1|кориснику|корисници}}",
        "emailuser-title-notarget": "Слање имејла кориснику",
        "dellogpage": "Дневник брисања",
        "dellogpagetext": "Испод је списак последњих брисања.",
        "deletionlog": "дневник брисања",
+       "log-name-create": "Дневник прављења страница",
+       "log-description-create": "Испод је списак скорашњих прављења страница.",
        "logentry-create-create": "$1 је {{GENDER:$2|направио|направила}} страницу $3",
        "reverted": "Враћено на ранију измену",
        "deletecomment": "Разлог:",
        "cantrollback": "Не могу да вратим измену.\nПоследњи аутор је уједно и једини.",
        "alreadyrolled": "Враћање последње измене странице [[:$1]] од стране {{GENDER:$2|корисника|кориснице|корисника}} [[User:$2|$2]] ([[User talk:$2|разговор]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]) није успело; неко други је у међувремену изменио или вратио страницу.\n\nПоследњу измену је {{GENDER:$3|направио|направила|направио}} [[User:$3|$3]] ([[User talk:$3|разговор]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
        "editcomment": "Опис измене: <em>$1</em>.",
-       "revertpage": "Враћене измене {{GENDER:$2|корисника|кориснице}} [[Special:Contribs/$2|$2]] ([[User talk:$2|разговор]]) на последњу измену {{GENDER:$1|корисника|кориснице}} [[User:$1|$1]]",
+       "revertpage": "Враћене измене {{GENDER:$2|корисника|кориснице}} [[Special:Contributions/$2|$2]] ([[User talk:$2|разговор]]) на последњу измену {{GENDER:$1|корисника|кориснице}} [[User:$1|$1]]",
        "revertpage-nouser": "Измене скривеног корисника су враћене на последњу измену {{GENDER:$1|корисника|кориснице}} [[User:$1|$1]]",
        "rollback-success": "Измене {{GENDER:$1|корисника|кориснице}} {{GENDER:$3|$1}} су враћене на последњу измену {{GENDER:$2|корисника|кориснице}} {{GENDER:$4|$2}}.",
        "rollback-success-notify": "Враћене измене корисника $1;\nвраћено на последњу измену корисника $2. [$3 Прикажи измене]",
        "uctop": "(тренутна)",
        "month": "од месеца (и раније):",
        "year": "од године (и раније):",
+       "date": "Од датума (и раније):",
        "sp-contributions-newbies": "Прикажи само доприносе нових корисника",
        "sp-contributions-newbies-sub": "За нове кориснике",
        "sp-contributions-newbies-title": "Доприноси нових корисника",
        "whatlinkshere": "Шта води овде",
        "whatlinkshere-title": "Странице које су повезане са „$1”",
        "whatlinkshere-page": "Страница:",
-       "linkshere": "Следеће странице имају везу до <strong>$1</strong>:",
+       "linkshere": "Следеће странице имају везу до <strong>$2</strong>:",
        "nolinkshere": "Ниједна страница није повезана са: <strong>$2</strong>.",
        "nolinkshere-ns": "Ниједна страница не води до '''$2''' у изабраном именском простору.",
        "isredirect": "преусмерење",
        "tooltip-ca-talk": "Разговор о страници са садржајем",
        "tooltip-ca-edit": "Уредите ову страницу",
        "tooltip-ca-addsection": "Започните нови одељак",
-       "tooltip-ca-viewsource": "Ова страница је закључана. \nМожете да погледате њен изворни код",
+       "tooltip-ca-viewsource": "Ова страница је закључана. \nМожете да погледате њен изворник",
        "tooltip-ca-history": "Претходне измене ове странице",
        "tooltip-ca-protect": "Заштитите ову страницу",
        "tooltip-ca-unprotect": "Промени заштиту ове странице",
        "exif-keywords": "Кључне речи",
        "exif-worldregioncreated": "Област света где је сликана фотографија",
        "exif-countrycreated": "Земља где је сликана фотографија",
-       "exif-countrycodecreated": "Кôд земље где је слика направљена",
+       "exif-countrycodecreated": "Код земље где је слика направљена",
        "exif-provinceorstatecreated": "Покрајина или држава где је сликана фотографија",
        "exif-citycreated": "Град где је сликана фотографија",
        "exif-sublocationcreated": "Област града где је сликана фотографија",
        "log-description-pagelang": "Ово је дневник измена у језицима страница.",
        "logentry-pagelang-pagelang": "$1 је {{GENDER:$2|променио|променила}} језик странице $3 из $4 у $5.",
        "default-skin-not-found-row-enabled": "* <code>$1</code> / $2 (омогућена)",
+       "default-skin-not-found-row-disabled": "* <code>$1</code> / $2 (<strong>онемогућена</strong>)",
        "mediastatistics": "Статистика датотека",
        "mediastatistics-summary": "Статистике о типовима послатих датотека. Овде су урачунате само најновије верзије датотека. Старе или обрисане верзије нису урачунате.",
        "mediastatistics-nbytes": "{{PLURAL:$1|$1 бајт|$1 бајта|$1 бајтова}} ($2; $3%)",
        "headline-anchor-title": "Веза до овог одељка",
        "special-characters-group-latin": "Латиница",
        "special-characters-group-latinextended": "Проширена латиница",
-       "special-characters-group-ipa": "Ð\98Ð\9fА",
+       "special-characters-group-ipa": "Ð\9cФА",
        "special-characters-group-symbols": "Симболи",
        "special-characters-group-greek": "Грчки",
        "special-characters-group-greekextended": "Проширени грчки",
        "log-action-filter-newusers-create": "отворио анониман корисник",
        "log-action-filter-newusers-create2": "отворио регистрован корисник",
        "log-action-filter-newusers-autocreate": "аутоматски отворен",
+       "log-action-filter-newusers-byemail": "отварање лозинком посланом на имејлу",
        "log-action-filter-patrol-patrol": "ручно",
        "log-action-filter-patrol-autopatrol": "аутоматско",
        "log-action-filter-protect-protect": "закључавање",
        "undelete-cantcreate": "Не можете повратити ову страницу јер нема постојеће странице са овим именом и немате дозволу да направите ову страницу.",
        "pagedata-title": "Подаци странице",
        "pagedata-not-acceptable": "Није пронађен одговарајући облик. Подржане MIME-врсте: $1",
-       "pagedata-bad-title": "Невалидан наслов: $1."
+       "pagedata-bad-title": "Невалидан наслов: $1.",
+       "passwordpolicies": "Правила за лозинке",
+       "passwordpolicies-group": "Група",
+       "passwordpolicies-policies": "Правила",
+       "passwordpolicies-policy-minimalpasswordlength": "Лозинка мора да има најмање {{PLURAL:$1|један знак|$1 знака|$1 знакова}}",
+       "passwordpolicies-policy-passwordcannotmatchusername": "Лозинка не може да буде иста као корисничко име",
+       "passwordpolicies-policy-passwordcannotmatchblacklist": "Лозинка се не може подударати са лозинкама на црној листи",
+       "passwordpolicies-policy-maximalpasswordlength": "Лозинка мора да буде краћа од $1 {{PLURAL:$1|знака|знакова}}",
+       "passwordpolicies-policy-passwordcannotbepopular": "Лозинка не може да буде {{PLURAL:$1|популарна лозинка|на списку $1 популарних лозинки}}"
 }
index 1b2ccc9..57b789a 100644 (file)
        "diff-paragraph-moved-toold": "Avsnittet flyttades. Klicka för att hoppa till den gamla platsen.",
        "difference-missing-revision": "Det gick inte att hitta {{PLURAL:$2|en version|$2 versioner}} av den här differensen ($1).\n\nDetta beror oftast på att du har försökt följa en utgången difflänk till en sida som har raderats.\nMer detaljerad information finns i [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} raderingsloggen].",
        "searchresults": "Sökresultat",
+       "search-filter-title-prefix": "Sök endast på sidor vars titel börjar med \"$1\"",
+       "search-filter-title-prefix-reset": "Sök på alla sidor",
        "searchresults-title": "Sökresultat för \"$1\"",
        "titlematches": "Träffar i sidtitlar",
        "textmatches": "Artikeltexter som matchar sökningen",
        "speciallogtitlelabel": "Mål (titel eller {{ns:user}}:användarnamn för användare):",
        "log": "Loggar",
        "logeventslist-submit": "Visa",
-       "logeventslist-more-filters": "Fler filter:",
+       "logeventslist-more-filters": "Visa ytterligare loggar:",
        "logeventslist-patrol-log": "Patrulleringslogg",
        "logeventslist-tag-log": "Märkeslogg",
        "all-logs-page": "Alla offentliga loggar",
index 38f8e66..083df6b 100644 (file)
@@ -19,7 +19,8 @@
                        "Kwisha",
                        "Macofe",
                        "Muddyb",
-                       "Fitoschido"
+                       "Fitoschido",
+                       "Rance"
                ]
        },
        "tog-underline": "Wekea mstari viungo:",
        "editundo": "tengua",
        "diff-empty": "(Hakuna tofauti)",
        "searchresults": "Matokeo ya utafutaji",
+       "search-filter-title-prefix": "Kutafuta katika kurasa zenye kichwa kianzacho na \"$1\" pekee",
+       "search-filter-title-prefix-reset": "Tafuta kurasa zote",
        "searchresults-title": "Matokeo ya utafutaji kwa ajili ya \"$1\"",
        "titlematches": "Kurasa zinazo majina yenye maneno ya ulizo",
        "textmatches": "Kurasa zinazo maandishi yenye maneno ya ulizo",
        "specialloguserlabel": "Mtendaji:",
        "speciallogtitlelabel": "Kusudio (jina la ukurasa au mtumiaji):",
        "log": "Kumbukumbu",
+       "logeventslist-more-filters": "Onyesha taarifa za ziada",
        "all-logs-page": "Kumbukumbu zote zilizo wazi",
        "alllogstext": "Hapa panaonyeshwa kumbukumbu zote za {{SITENAME}} kwa pamoja.\nUnaweza kuona baadhi yao tu kwa kuchagua aina fulani ya kumbukumbu, jina la mtumiaji fulani (zingatia herufi kubwa na ndogo), au jina la ukurasa fulani (zingatia herufi kubwa na ndogo).",
        "logempty": "Vitu vyenye vipengele hivi havipo kwenye kumbukumbu.",
index 2ef8aba..7542577 100644 (file)
@@ -56,7 +56,7 @@
        "tog-shownumberswatching": "వీక్షకుల సంఖ్యను చూపు",
        "tog-oldsig": "మీ ప్రస్తుత సంతకం:",
        "tog-fancysig": "సంతకాన్ని వికీపాఠ్యంగా తీసుకో (ఆటోమెటిక్‌ లింకు లేకుండా)",
-       "tog-uselivepreview": "తాà°\9cà°¾ à°®à±\81à°¨à±\81à°\9cà±\82à°ªà±\81à°¨à±\81 à°µà°¾à°¡ు",
+       "tog-uselivepreview": "à°ªà±\87à°\9cà±\80ని à°®à°³à±\8dà°³à±\80 à°²à±\8bà°¡à±\81 à°\9aà±\86à°¯à±\8dà°¯à°\95à±\81à°\82à°¡à°¾ à°®à±\81à°¨à±\81à°\9cà±\82à°ªà±\81లనà±\81 à°\9aà±\82పిà°\82à°\9aు",
        "tog-forceeditsummary": "దిద్దుబాటు సారాంశం ఖాళీగా ఉంటే ఆ విషయాన్ని నాకు సూచించు",
        "tog-watchlisthideown": "నా మార్పులను వీక్షణా జాబితాలో చూపించొద్దు",
        "tog-watchlisthidebots": "బాట్లు చేసిన మార్పులను నా వీక్షణా జాబితాలో చూపించొద్దు",
        "mypage": "పుట",
        "mytalk": "చర్చ",
        "anontalk": "చర్చ",
-       "navigation": "మారà±\8dà°\97దరà±\8dà°¶à°\95à°\82",
+       "navigation": "మారà±\8dà°\97à°¸à±\82à°\9aà±\80",
        "and": "&#32;మరియు",
        "faq": "తరచూ అడిగే ప్రశ్నలు",
        "actions": "పనులు",
        "print": "ముద్రించు",
        "view": "చూపు",
        "view-foreign": "$1 లో చూడండి",
-       "edit": "సవరిà°\82చు",
+       "edit": "మారà±\8dచు",
        "edit-local": "స్థానిక వివరణని మార్చు",
        "create": "సృష్టించు",
        "create-local": "స్థానిక వివరణను చేర్చు",
        "lastmodifiedat": "ఈ పేజీలో చివరి మార్పు $1న $2కు జరిగింది.",
        "viewcount": "ఈ పేజీ {{PLURAL:$1|ఒక్క సారి|$1 సార్లు}} దర్శించబడింది.",
        "protectedpage": "సంరక్షణలోని పేజీ",
-       "jumpto": "à°\87à°\95à±\8dà°\95à°¡à°¿à°\95à°¿ à°\97à±\86à°\82à°¤ు:",
+       "jumpto": "à°\87à°\95à±\8dà°\95à°¡à°¿à°\95à°¿ à°¦à±\82à°\95ు:",
        "jumptonavigation": "మార్గసూచీ",
        "jumptosearch": "వెతుకు",
        "view-pool-error": "క్షమించండి, ప్రస్తుతం సర్వర్లన్నీ ఓవర్‌లోడ్ అయిఉన్నాయి.\nచాలామంది వాడుకరులు ఈ పేజీని చూస్తున్నారు.\nఈ పేజీని వీక్షించడానికి కొద్దిసేపు నిరీక్షించండి.\n\n$1",
        "wrongpasswordempty": "ఖాళీ సంకేతపదం ఇచ్చారు. మళ్ళీ ప్రయత్నించండి.",
        "passwordtooshort": "సంకేతపదం కనీసం {{PLURAL:$1|1 అక్షరం|$1 అక్షరాల}} నిడివి ఉండాలి.",
        "passwordtoolong": "సంకేతపదం పొడవు {{PLURAL:$1|1 అక్షరం|$1 అక్షరాల}} కన్నా ఎక్కువ ఉండకూడదు.",
-       "passwordtoopopular": "మామà±\82à°²à±\81à°\97à°¾ à°µà°¾à°¡à±\87 à°¸à°\82à°\95à±\87తపదాలనà±\81 à°µà°¾à°¡à±\87 à°µà±\80à°²à±\8dà°²à±\87à°¦à±\81. à°®à°°à°¿à°\82à°¤ à°µà°¿à°¶à°¿ష్టమైన సంకేతపదాన్ని ఎంచుకోండి.",
+       "passwordtoopopular": "సాధారణà°\82à°\97à°¾ à°µà°¾à°¡à±\87 à°¸à°\82à°\95à±\87తపదాలనà±\81 à°µà°¾à°¡à±\87 à°µà±\80à°²à±\8dà°²à±\87à°¦à±\81. à°\8aహిà°\82à°\9aడానిà°\95à°¿ à°\95ష్టమైన సంకేతపదాన్ని ఎంచుకోండి.",
        "password-name-match": "మీ సంకేతపదం మీ వాడుకరిపేరుకి భిన్నంగా ఉండాలి.",
        "password-login-forbidden": "ఈ వాడుకరిపేరు మరియు సంకేతపదాలను ఉపయోగించడం నిషిద్ధం.",
        "mailmypassword": "సంకేతపదాన్ని మార్చు",
        "resetpass-submit-loggedin": "సంకేతపదాన్ని మార్చు",
        "resetpass-submit-cancel": "రద్దుచేయి",
        "resetpass-wrong-oldpass": "తప్పుడు తాత్కాలిక లేదా ప్రస్తుత సంకేతపదం.\nమీరు మీ సంకేతపదాన్ని ఇప్పటికే మార్చుకొని ఉండవచ్చు లేదా కొత్త తాత్కాలిక సంకేతపదం కోసం అభ్యర్థించి ఉండవచ్చు.",
-       "resetpass-recycled": "à°®à±\80 à°ªà±\8dà°°à°¸à±\8dà°¤à±\81à°¤ à°¸à°\82à°\95à±\87తపదానà±\8dని à°µà±\87à°°à±\87 à°¸à°\82à°\95à±\87తపదà°\82à°¤à±\8b à°®à°¾à°°à±\8dà°\9aà±\81à°\95à±\8bà°\82à°¡à°¿",
+       "resetpass-recycled": "à°ªà±\8dà°°à°¸à±\8dà°¤à±\81à°¤ à°¸à°\82à°\95à±\87తపదà°\82 à°\95à°\82à°\9fà±\87 à°­à°¿à°¨à±\8dనమà±\88à°¨ à°¦à°¾à°¨à°¿à°¤à±\8b à°¸à°\82à°\95à±\87తపదానà±\8dని à°®à°¾à°°à±\8dà°\9aà±\81à°\95à±\8bà°\82à°¡à°¿.",
        "resetpass-temp-emailed": "మీరు మీ ఈమెయిలుకు పంపించిన తాత్కాలిక కోడుతో లాగినయ్యారు. లాగిన్ను పూర్తి చేసేందుకు, ఇక్కడ మీరు తప్పనిసరిగా సంకేతపదం మార్చుకోవాలి:",
        "resetpass-temp-password": "తాత్కాలిక సంకేతపదం:",
        "resetpass-abort-generic": "ఓ పొడిగింత (ఎక్స్టెన్‍షన్) సంకేతపదం మార్పిడిని ఆపేసింది.",
        "resetpass-expired": "మీ సంకేతపదానికి కాలం చెల్లింది. కొత్త సంకేతపదం ఇచ్చి లాగినవండి.",
-       "resetpass-expired-soft": "à°®à±\80 à°¸à°\82à°\95à±\87తపదానిà°\95à°¿ à°\95ాలà°\82 à°\9aà±\86à°²à±\8dలిà°\82ది, à°\95ాబà°\9fà±\8dà°\9fà°¿ à°\95à±\8aà°¤à±\8dతది à°\87à°µà±\8dవాలి. à°\95à±\8aà°¤à±\8dతది à°\87à°ªà±\8dà°ªà±\81à°¡à±\87 à°\87à°µà±\8dà°µà°\82à°¡à°¿ à°²à±\87దా \"{{int:authprovider-resetpass-skip-label}}\" à°¨à±\8aà°\95à±\8dà°\95à°¿, à°¤à°°à±\81వాత à°®à°¾à°°à±\8dà°\9aà±\81à°\95à±\8bà°\82à°¡à°¿.",
-       "resetpass-validity-soft": "à°®à±\80 à°¸à°\82à°\95à±\87తపదానిà°\95à°¿ à°\95ాలà°\82 à°\9aà±\86à°²à±\8dలిà°\82ది:$1\nà°\95à±\8aà°¤à±\8dతది à°\87à°ªà±\8dà°ªà±\81à°¡à±\87 à°\8eà°\82à°\9aà±\81à°\95à±\8bà°\82à°¡à°¿, à°²à±\87దా \"{{int:authprovider-resetpass-skip-label}}\" à°¨à±\8aà°\95à±\8dà°\95à°¿, à°¤à°°à±\81వాత à°®à°¾à°°à±\8dà°\9aà±\81à°\95à±\8bà°\82à°¡à°¿.",
+       "resetpass-expired-soft": "à°®à±\80 à°¸à°\82à°\95à±\87తపదానిà°\95à°¿ à°\95ాలà°\82 à°\9aà±\86à°²à±\8dలిà°\82ది, à°\95ాబà°\9fà±\8dà°\9fà°¿ à°¦à°¾à°¨à±\8dని à°®à°¾à°°à±\8dà°\9aాలి. à°\95à±\8aà°¤à±\8dతదానà±\8dని à°\87à°ªà±\8dà°ªà±\81à°¡à±\87 à°\8eà°\82à°\9aà±\81à°\95à±\8bà°\82à°¡à°¿ à°²à±\87దా \"{{int:authprovider-resetpass-skip-label}}\" à°¨à±\8aà°\95à±\8dà°\95à°¿, à°¤à°°à±\81వాత à°®à°¾à°°à±\8dà°\9aà±\81à°\95à±\8bà°µà°\9aà±\8dà°\9aà±\81.",
+       "resetpass-validity-soft": "à°®à±\80 à°¸à°\82à°\95à±\87తపదà°\82 à°¸à°°à±\88నది à°\95ాదà±\81:$1\nà°\95à±\8aà°¤à±\8dతది à°\87à°ªà±\8dà°ªà±\81à°¡à±\87 à°\8eà°\82à°\9aà±\81à°\95à±\8bà°\82à°¡à°¿, à°²à±\87దా \"{{int:authprovider-resetpass-skip-label}}\" à°¨à±\8aà°\95à±\8dà°\95à°¿, à°¤à°°à±\81వాత à°®à°¾à°°à±\8dà°\9aà±\81à°\95à±\8bà°µà°\9aà±\8dà°\9aà±\81.",
        "passwordreset": "సంకేతపదాన్ని మార్చుకోండి",
        "passwordreset-text-one": "ఈమెయిలు ద్వారా తాత్కాలిక సంకేతపదాన్ని పొందేందుకు ఈ ఫారమును నింపండి.",
        "passwordreset-text-many": "{{PLURAL:$1|ఈమెయిలు ద్వారా తాత్కాలిక సంకేతపదాన్ని పొందేందుకు ఏదో ఒక ఫీల్డును నింపండి.}}",
        "subject-preview": "విషయపు మునుజూపు:",
        "previewerrortext": "మీ మార్పులు మునుజూపు చూపడంలో లోపం దొర్లింది.",
        "blockedtitle": "వాడుకరి నిరోధించబడ్డారు",
-       "blockedtext": "<strong>మీ వాడుకరి పేరు లేదా ఐ.పీ. చిరునామా నిరోధించబడింది.</strong>\n\nనిరోధించినది $1.\nఅందుకు ఇచ్చిన కారణం: <em>$2</em>.\n\n* నిరోధం మొదలైన సమయం: $8\n* నిరోధించిన కాలం: $6\n* నిరోధానికి గురైనవారు: $7\n\nఈ నిరోధంపై చర్చించేందుకు మీరు $1 ను గాని, మరెవరైనా [[{{MediaWiki:Grouppage-sysop}}|నిర్వాహకులను]] గాని సంప్రదించవచ్చు.\nమీ [[Special:Preferences|ఖాతా అభిరుచులలో]] సరైన ఈ-మెయిలు చిరునామా ఇచ్చివుండకపోయినా లేదా మిమ్మల్ని  'ఈ వాడుకరికి ఈ-మెయిలు పంపు' సౌలభ్యాన్ని వాడుకోవడం నుండి నిరోధించివున్నా మీరు ఈమెయిలు ద్వారా సంప్రదించలేరు.\nమీ ప్రస్తుత ఐ.పీ. చిరునామా $3, మరియు నిరోధపు ID #$5.\nమీ సంప్రదింపులన్నిటిలోనూ వీటిని పేర్కొనండి.",
-       "autoblockedtext": "మీ ఐపీ చిరునామా ఆటోమాటిగ్గా నిరోధించబడింది. ఎందుకంటే ఇదే ఐపీ చిరునామాని ఓ నిరోధిత వాడుకరి ఉపయోగించారు. ఆ వాడుకరిని $1 నిరోధించారు.\nఅందుకు ఇచ్చిన కారణం ఇదీ:\n\n:<em>$2</em>\n\n* నిరోధం మొదలైన సమయం: $8\n* నిరోధించిన కాలం: $6\n* ఉద్దేశించిన నిరోధిత వాడుకరి: $7\n\nఈ నిరోధం గురించి చర్చించేందుకు మీరు $1 ను గానీ, లేదా ఇతర [[{{MediaWiki:Grouppage-sysop}}|నిర్వాహకులను]] గానీ సంప్రదించండి.\n\nమీ [[Special:Preferences|అభిరుచులలో]] సరైన ఈమెయిలు ఐడీని ఇచ్చి ఉంటే తప్ప, మీరు \"ఈ సభ్యునికి మెయిలు పంపు\" అనే అంశాన్ని వాడజాలరని గమనించండి. ఆ సౌలభ్యాన్ని వాడటం నుండి మిమ్మల్ని నిరోధించలేదు.\n\nమీ ప్రస్తుత ఐపీ చిరునామా $3, మరియు నిరోధపు ఐడీ: $5.\nమీ సంప్రదింపులన్నిటిలోను అన్ని పై వివరాలను ఉదహరించండి.",
+       "blockedtext": "<strong>మీ వాడుకరిపేరును లేదా ఐ.పి. చిరునామాను నిరోధించారు.</strong>\n\nనిరోధించినవారు $1.\nఅందుకు వారు ఇచ్చిన కారణం: <em>$2</em>.\n\n* నిరోధం మొదలైన సమయం: $8\n* నిరోధించిన కాలం: $6\n* నిరోధానికి గురైనవారు: $7\n\nఈ నిరోధంపై చర్చించేందుకు మీరు $1 ను గాని, మరెవరైనా [[{{MediaWiki:Grouppage-sysop}}|నిర్వాహకుని]] గాని సంప్రదించవచ్చు.\nమీ [[Special:Preferences|ఖాతా అభిరుచులలో]] సరైన ఈ-మెయిలు చిరునామా ఇచ్చివుండకపోయినా లేదా మిమ్మల్ని  'ఈ వాడుకరికి ఈ-మెయిలు పంపు' సౌలభ్యాన్ని వాడుకోవడం నుండి నిరోధించివున్నా మీరు ఈమెయిలు ద్వారా సంప్రదించలేరు.\nమీ ప్రస్తుత ఐ.పీ. చిరునామా $3, మరియు నిరోధపు ID #$5.\nమీ సంప్రదింపులన్నిటిలోనూ పై వివరాలను పేర్కొనండి.",
+       "autoblockedtext": "మీ ఐపీ చిరునామా ఆటోమాటిగ్గా నిరోధించబడింది. ఎందుకంటే ఇదే ఐపీ చిరునామాని ఓ నిరోధిత వాడుకరి ఉపయోగించారు. ఆ వాడుకరిని $1 నిరోధించారు.\nఅందుకు ఇచ్చిన కారణం ఇదీ:\n\n:<em>$2</em>\n\n* నిరోధం మొదలైన సమయం: $8\n* నిరోధించిన కాలం: $6\n* ఉద్దేశించిన నిరోధిత వాడుకరి: $7\n\nఈ నిరోధం గురించి చర్చించేందుకు మీరు $1 ను గానీ, లేదా ఇతర [[{{MediaWiki:Grouppage-sysop}}|నిర్వాహకులను]] గానీ సంప్రదించండి.\n\nమీ [[Special:Preferences|అభిరుచులలో]] సరైన ఈమెయిలు ఐడీని ఇచ్చి ఉంటే తప్ప, మీరు \"ఈ వాడుకరికి ఈమెయిలు పంపు\" అనే అంశాన్ని వాడజాలరని గమనించండి. ఆ సౌలభ్యాన్ని వాడటం నుండి మిమ్మల్ని నిరోధించలేదు.\n\nమీ ప్రస్తుత ఐపీ చిరునామా $3, మరియు నిరోధపు ఐడీ: $5.\nమీ సంప్రదింపులన్నిటిలోను అన్ని పై వివరాలను ఉదహరించండి.",
+       "systemblockedtext": "మీడియావికీ మీ వాడుకరిపేరు లేదా ఐపీ అడ్రసును ఆటోమాటిగ్గా నిరోధించింది.\nఅందుకు ఇచ్చిన కారణం:\n\n:<em>$2</em>\n\n* నిరోధం మొదలైన సమయం: $8\n* నిరోధం ముగిసే సమయం: $6\n* నిరోధానికి గురైనవారు: $7\n\nమీ ప్రస్తుత ఐపీ అడ్రసు $3.\nమీ సంప్రదింపులన్నిటిలోనూ పై వివరాలను పేర్కొనండి.",
        "blockednoreason": "కారణమేమీ ఇవ్వలేదు",
        "whitelistedittext": "పుటలలో మార్పులు చెయ్యడానికి $1.",
        "confirmedittext": "పేజీల్లో మార్పులు చేసేముందు మీ ఈమెయిలు చిరునామాను ధృవీకరించాలి. [[Special:Preferences|మీ అభిరుచుల]]లో మీ ఈమెయిలు చిరునామా రాసి, ధృవీకరించండి.",
        "permissionserrorstext-withaction": "ఈ క్రింది {{PLURAL:$1|కారణం|కారణాల}} వల్ల, $2 అనుమతి మీకు లేదు:",
        "recreate-moveddeleted-warn": "<strong>హెచ్చరిక: ఇంతకు మునుపు ఒకసారి తొలగించిన పేజీని మళ్లీ సృష్టిద్దామని మీరు ప్రయత్నిస్తున్నారు.</strong>\n\nఈ పేజీపై మార్పులు చేసేముందు, అవి ఇక్కడ ఉండతగినవేనా కాదా అని ఒకసారి ఆలోచించండి.\nమీ సౌలభ్యం కొరకు ఈ పేజీ యొక్క తొలగింపు మరియు తరలింపు చిట్టా ఇక్కడ ఇచ్చాము:",
        "moveddeleted-notice": "ఈ పేజీని తొలగించారు.\nఈ పేజీ యొక్క తొలగింపు, సంరక్షణ, తరలింపు చిట్టాను క్రింద ఇచ్చాం.",
+       "moveddeleted-notice-recent": "సారీ, ఈ పేజీని ఈమధ్యే తొలగించారు (గత 24 గంటల్లో).\nఈ పేజీకి సంబంధించిన తొలగింపు, సంరక్షణ, తరలింపు లాగ్‌లను కింద ఇచ్చాం.",
        "log-fulllog": "పూర్తి చిట్టాని చూడండి",
        "edit-hook-aborted": "కొక్కెం ఈ మార్పుని విచ్ఛిన్నం చేసింది.\nఅది ఎటువంటి వివరణా ఇవ్వలేదు.",
        "edit-gone-missing": "పేజీని తాజాకరించలేకపోయాం.\nదాన్ని తొలగించినట్టున్నారు.",
        "content-model-css": "CSS",
        "content-json-empty-object": "ఖాళీ అంశం",
        "content-json-empty-array": "ఖాళీ అరే",
+       "duplicate-args-warning": "<strong>హెచ్చరిక:</strong> [[:$1]], \"$3\" పరామితికి ఒకటి కంటే ఎక్కువ విలువలు ఇచ్చి [[:$2]] ను పిలుస్తోంది. చిట్టచివరిగా ఇచ్చిన విలువను మాత్రమే వాడుతాం.",
+       "duplicate-args-category-desc": "ఈ పేజీ డూప్లికేట్ ఆర్గ్యుమెంట్లను ఇచ్చి మూసలను పిలుస్తోంది, ఇలా: <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code> లేదా ఇలా: <code><nowiki>{{foo|bar|1=baz}}</nowiki></code>.",
        "expensive-parserfunction-warning": "<strong>హెచ్చరిక:</strong> ఈ పేజీలో ఖరీదైన పార్సరు పిలుపులు చాలా ఉన్నాయి.\n\nపార్సరు {{PLURAL:$2|పిలుపు|పిలుపులు}} $2 కంటే తక్కువ ఉండాలి,  ప్రస్తుతం {{PLURAL:$1|$1 పిలుపు ఉంది|$1  పిలుపులు ఉన్నాయి}}.",
        "expensive-parserfunction-category": "పార్సరు సందేశాలు అధికంగా ఉన్న పేజీలు",
        "post-expand-template-inclusion-warning": "<strong>హెచ్చరిక:</strong> మూస ఇముడ్పు సైజు చాలా పెద్దదిగా ఉంది.\nకొన్ని మూసలు ఇమడ్చబడవు.",
        "revdelete-submit": "ఎంచుకున్న {{PLURAL:$1|కూర్పుకు|కూర్పులకు}} ఆపాదించు",
        "revdelete-success": "కూర్పు కనబడే విధానాన్ని జయప్రదంగా తాజాకరించాం.",
        "revdelete-failure": "కూర్పు కనబడే పద్ధతిని తాజాపరచలేకపోయాం:\n$1",
-       "logdelete-success": "à°\98à°\9fà°¨ à°\95నబడà±\87 à°µà°¿à°§à°¾à°¨à°¾à°¨à±\8dని à°\9cయపà±\8dà°°à°¦à°\82à°\97à°¾ à°\85మరà±\8dà°\9aాం.",
+       "logdelete-success": "లాà°\97à±\8d à°\95నబడà±\87 à°µà°¿à°§à°¾à°¨à°¾à°¨à±\8dని à°¸à±\86à°\9fà±\8d à°\9aà±\87à°¸ాం.",
        "logdelete-failure": "'''చిట్టా కనబడే పద్ధతిని అమర్చలేకపోయాం:'''\n$1",
        "revdel-restore": "దృశ్యతని మార్చు",
        "pagehist": "పేజీ చరిత్ర",
        "stub-threshold-disabled": "అచేతనం",
        "recentchangesdays": "ఇటీవలి మార్పులు లో చూపించవలసిన రోజులు:",
        "recentchangesdays-max": "గరిష్ఠంగా $1 {{PLURAL:$1|రోజు|రోజులు}}",
-       "recentchangescount": "అప్రమేయంగా చూపించాల్సిన దిద్దుబాట్ల సంఖ్య:",
+       "recentchangescount": "à°\87à°\9fà±\80వలి à°®à°¾à°°à±\8dà°ªà±\81లలà±\8b, à°ªà±\87à°\9cà±\80 à°\9aà°°à°¿à°¤à±\8dరలలà±\8b, à°\9aà°¿à°\9fà±\8dà°\9fాలలà±\8b à°\85à°ªà±\8dà°°à°®à±\87à°¯à°\82à°\97à°¾ à°\9aà±\82పిà°\82à°\9aాలà±\8dసిన à°¦à°¿à°¦à±\8dà°¦à±\81బాà°\9fà±\8dà°² à°¸à°\82à°\96à±\8dà°¯:",
        "prefs-help-recentchangescount": "గరిష్ఠ సంఖ్య: 1000",
        "prefs-help-watchlist-token2": "మీ వీక్షణజాబితా యొక్క జాలవడ్డింపుకు చెందిన రహస్య తాళమిది.\nఈ తాళం తెలిసిన ఎవరైనా మీ వీక్షణజాబితాను చదవగలుగుతారు. అందుచేత దీన్ని ఎవరికీ ఇవ్వకండి.\nఅవసరమైతే [[Special:ResetTokens|దాన్ని మార్చుకోవచ్చు]].",
        "savedprefs": "మీ అభిరుచులను భద్రపరిచాం.",
        "right-viewsuppressed": "ఏ వాడుకరి నుండైనా వీక్షణ సంస్కరణలు దాయబడ్డాయి",
        "right-suppressionlog": "గోప్యంగా ఉన్న లాగ్‌లను చూడడం",
        "right-block": "దిద్దుబాటు చెయ్యకుండా ఇతర వాడుకరులను నిరోధించగలగడం",
-       "right-blockemail": "à°\88à°®à±\86యిలà±\81 à°ªà°\82à°ªà°\95à±\81à°\82à°¡à°¾ à°¸à°­à±\8dà°¯à±\81ని నిరోధించు",
-       "right-hideuser": "à°ªà±\8dà°°à°\9cà°²à°\95à±\81 à°\95నబడà°\95à±\81à°\82à°¡à°¾ à°\9aà±\87సి, à°¸à°­à±\8dయనామానà±\8dని నిరోధించు",
+       "right-blockemail": "à°\88à°®à±\86యిలà±\81 à°ªà°\82à°ªà°\95à±\81à°\82à°¡à°¾ à°µà°¾à°¡à±\81à°\95à°°à°¿ని నిరోధించు",
+       "right-hideuser": "బయà°\9fà°¿à°\95à°¿ à°\95నబడà°\95à±\81à°\82à°¡à°¾ à°\9aà±\87సి, à°µà°¾à°¡à±\81à°\95à°°à°¿à°ªà±\87à°°à±\81à°¨à±\81 నిరోధించు",
        "right-ipblock-exempt": "ఐపీ నిరోధాలు, ఆటో నిరోధాలు, శ్రేణి నిరోధాలను తప్పించు",
        "right-unblockself": "స్వీయ అనిరోధం",
        "right-protect": "సంరక్షణ స్థాయిలను మార్చు, కాస్కేడ్-రక్షిత పేజీలలో దిద్దుబాటు చెయ్యి",
        "minoreditletter": "చి",
        "newpageletter": "కొ",
        "boteditletter": "బా",
-       "number_of_watching_users_pageview": "[à°µà±\80à°\95à±\8dà°·à°¿à°¸à±\8dà°¤à±\81à°¨à±\8dà°¨ à°¸à°­à±\8dà°¯ులు: {{PLURAL:$1|ఒక్కరు|$1}}]",
+       "number_of_watching_users_pageview": "[à°µà±\80à°\95à±\8dà°·à°¿à°¸à±\8dà°¤à±\81à°¨à±\8dà°¨ à°µà°¾à°¡à±\81à°\95à°°ులు: {{PLURAL:$1|ఒక్కరు|$1}}]",
        "rc-change-size-new": "మార్పు తర్వాత $1 {{PLURAL:$1|బైటు|బైట్లు}}",
        "newsectionsummary": "/* $1 */ కొత్త విభాగం",
        "rc-enhanced-expand": "వివరాలను చూపించు",
        "unusedimagestext": "ఈ క్రింది ఫైళ్ళు ఉన్నాయి కానీ వాటిని ఏ పేజీలోనూ ఉపయోగించట్లేదు.\nఇతర వెబ్ సైట్లు సూటి URL ద్వారా ఇక్కడి ఫైళ్ళకు లింకు ఇవ్వవచ్చు, మరియు ఆవిధంగా క్రియాశీలంగా వాడుకలో ఉన్నప్పటికీ అటువంటివి ఈ జాబితాలో చేరి ఉండవచ్చునని గమనించండి.",
        "unusedcategoriestext": "కింది వర్గాలకు పేజీలైతే ఉన్నాయి గానీ, వీటిని వ్యాసాలు గానీ, ఇతర వర్గాలు గానీ ఉపయోగించడం లేదు.",
        "notargettitle": "గమ్యం లేదు",
-       "notargettext": "à°\88 à°ªà°¨à°¿ à°\8f à°ªà±\87à°\9cà±\80 à°²à±\87దా à°¸à°­à±\8dà°¯à±\81à°¨ిపై జరగాలనే గమ్యాన్ని మీరు సూచించలేదు.",
+       "notargettext": "à°\88 à°ªà°¨à°¿ à°\8f à°ªà±\87à°\9cà±\80 à°²à±\87దా à°µà°¾à°¡à±\81à°\95à°°ిపై జరగాలనే గమ్యాన్ని మీరు సూచించలేదు.",
        "nopagetitle": "అలాంటి పేజీ లేదు",
        "nopagetext": "మీరు అడిగిన పేజీ లేదు",
        "pager-newer-n": "{{PLURAL:$1|1 కొత్తది|$1 కొత్తవి}}",
        "activeusers-intro": "ఇది గత $1 {{PLURAL:$1|రోజులో|రోజులలో}} ఏదైనా కార్యకలాపం చేసిన వాడుకరుల జాబితా.",
        "activeusers-count": "గడచిన {{PLURAL:$3|ఒక రోజు|$3 రోజుల}}లో $1 {{PLURAL:$1|చర్య|చర్యలు}}",
        "activeusers-from": "వాడుకరులను ఇక్కడ నుండి చూపించు:",
+       "activeusers-groups": "ఈ గుంపులకు చెందిన వాడుకరులను చూపించు:",
+       "activeusers-excludegroups": "ఈ గుంపులకు చెందిన వాడుకరులను చూపించవద్దు:",
        "activeusers-noresult": "వాడుకరులెవరూ లేరు.",
        "activeusers-submit": "చేతనంగా ఉన్న వాడుకరులను చూపించు",
        "listgrouprights": "వాడుకరి గుంపుల హక్కులు",
        "dellogpage": "తొలగింపుల చిట్టా",
        "dellogpagetext": "ఇది ఇటీవలి తుడిచివేతల జాబితా.",
        "deletionlog": "తొలగింపుల చిట్టా",
+       "log-name-create": "పేజీల సృష్టి చిట్టా",
+       "log-description-create": "ఇటీవల సృష్టించిన కొత్త పేజీల జాబితా ఇది.",
        "logentry-create-create": "$3 పేజీని $1 {{GENDER:$2|సృష్టించారు}}",
        "reverted": "పాత కూర్పుకు తీసుకువెళ్ళాం.",
        "deletecomment": "కారణం:",
        "delete-toobig": "ఈ పేజీకి $1 {{PLURAL:$1|కూర్పుకు|కూర్పులకు}} మించిన, చాలా పెద్ద దిద్దుబాటు చరితం ఉంది. {{SITENAME}}కు అడ్డంకులు కలగడాన్ని నివారించేందుకు గాను, అలాంటి పెద్ద పేజీల తొలగింపును నియంత్రించాం.",
        "delete-warning-toobig": "ఈ పేజీకి $1 {{PLURAL:$1|కూర్పుకు|కూర్పులకు}} మించిన, చాలా పెద్ద దిద్దుబాటు చరితం ఉంది. దాన్ని తొలగిస్తే {{SITENAME}}కి చెందిన డేటాబేసు కార్యాలకు ఆటంకం కలగొచ్చు; అప్రమత్తతో ముందుకుసాగండి.",
        "deleteprotected": "ఈ పేజీ సంరక్షణలో ఉంది కనుక మీరు తొలగించలేరు.",
-       "deleting-backlinks-warning": "'''హెచ్చరిక:''' మీరు తొలగించబోతున్న పేజీకి [[Special:WhatLinksHere/{{FULLPAGENAME}}|ఇతర పేజీల]] నుండి లింకులు ఉన్నాయి లేదా ఇక్కడ నుండి ట్రాన్స్‍క్లూడు అవుతున్నాయి.",
+       "deleting-backlinks-warning": "<strong>హెచ్చరిక:</strong> మీరు తొలగించబోతున్న పేజీకి [[Special:WhatLinksHere/{{FULLPAGENAME}}|ఇతర పేజీల]] నుండి లింకులు ఉన్నాయి. లేదా ఇతర పేజీల్లో అది ట్రాన్స్‍క్లూడు అవుతోంది.",
        "rollback": "దిద్దుబాట్లను రద్దుచేయి",
        "rollbacklink": "రద్దుచేయి",
        "rollbacklinkcount": "$1 {{PLURAL:$1|మార్పును|మార్పులను}} రద్దుచేయి",
        "revertpage-nouser": "దాచబడిన వాడుకరి చేసిన మార్పులను [[User:$1|$1]] యొక్క చివరి కూర్పుకి తిప్పికొట్టారు",
        "rollback-success": "$1 చేసిన దిద్దుబాట్లను వెనక్కు తీసుకెళ్ళాం; తిరిగి $2 చేసిన చివరి కూర్పుకు మార్చాం.",
        "sessionfailure-title": "సెషను వైఫల్యం",
-       "sessionfailure": "మీ ప్రవేశపు సెషనుతో ఏదో సమస్య ఉన్నట్లుంది;\nసెషను హైజాకు కాకుండా ఈ చర్యను రద్దు చేసాం.\n\"back\" కొట్టి, ఎక్కడి నుండి వచ్చారో ఆ పేజీని మళ్ళీ లోడు చేసి, తిరిగి ప్రయత్నించండి.",
+       "sessionfailure": "మీ లాగిన్ సెషనుతో ఏదో సమస్య ఉన్నట్లుంది;\nసెషను హైజాకు కాకుండా ఈ చర్యను రద్దు చేసాం.\nఫారమును తిరిగి సమర్పించండి.",
+       "changecontentmodel": "పేజీ కంటెంటు మోడలును మార్చు",
        "changecontentmodel-legend": "కంటెంటు మోడల్‌ మార్పు",
        "changecontentmodel-title-label": "పేజీ శీర్షిక",
        "changecontentmodel-model-label": "కొత్త కంటెంటు మోడల్",
        "undeletehistorynoadmin": "ఈ పుటని తొలగించివున్నారు.\nతొలగింపునకు కారణం, తొలగింపునకు క్రితం ఈ పుటకి మార్పులు చేసిన వాడుకరుల వివరాలతో సహా, ఈ కింద సారాంశంలో చూపబడింది.\nతొలగించిన కూర్పులలోని వాస్తవ పాఠ్యం నిర్వాహకులకు మాత్రమే అందుబాటులో ఉంటుంది.",
        "undelete-revision": "$1 యొక్క తొలగించబడిన కూర్పు (చివరగా $4 నాడు, $5కి $3 మార్చారు):",
        "undeleterevision-missing": "తప్పుడు లేదా తప్పిపోయిన కూర్పు. మీరు నొక్కింది తప్పుడు లింకు కావచ్చు, లేదా భాండాగారం నుండి కూర్పు పునఃస్థాపించబడి లేదా తొలగించబడి ఉండవచ్చు.",
+       "undeleterevision-duplicate-revid": "{{PLURAL:$1|ఒక కూర్పు|$1 కూర్పుల}}ను పునస్థాపించలేకపోయాం. ఎందుకంటే {{PLURAL:$1|దాని|వాటి}} <code>rev_id</code> ఈసరికే వినియోగంలో ఉంది.",
        "undelete-nodiff": "గత కూర్పులేమీ లేవు.",
        "undeletebtn": "పునఃస్థాపించు",
        "undeletelink": "చూడండి/పునస్థాపించండి",
        "undelete-search-title": "తొలగించిన పేజీల అన్వేషణ",
        "undelete-search-box": "తొలగించిన పేజీలను వెతుకు",
        "undelete-search-prefix": "దీనితో మొదలయ్యే పేజీలు చూపించు:",
+       "undelete-search-full": "ఇవి ఉన్న పేజీ శీర్షికలను చూపించు:",
        "undelete-search-submit": "వెతుకు",
        "undelete-no-results": "తొలగింపు సంగ్రహాల్లో దీనిని పోలిన పేజీలు లేవు.",
        "undelete-filename-mismatch": "$1 టైమ్‌స్టాంపు కలిగిన ఫైలుకూర్పు తొలగింపును రద్దు చెయ్యలేకపోయాం: ఫైలుపేరు సరిపోలలేదు",
        "ipbcreateaccount": "ఖాతా సృష్టిని నివారించు",
        "ipbemailban": "వాడుకరిని ఈ-మెయిల్ చెయ్యకుండా నివారించు",
        "ipbenableautoblock": "ఈ వాడుకరి వాడిన చివరి ఐపీ అడ్రసును, అలాగే ఆ తరువాత వాడే అడ్రసులను కూడా ఆటోమాటిగ్గా నిరోధించు",
-       "ipbsubmit": "à°\88 à°¸à°­à±\8dà°¯à±\81ని నిరోధించు",
+       "ipbsubmit": "à°\88 à°µà°¾à°¡à±\81à°\95à°°à°¿ని నిరోధించు",
        "ipbother": "వేరే గడువు",
        "ipboptions": "2 గంటలు:2 hours,ఒక రోజు:1 day,3 రోజులు:3 days,ఒక వారం:1 week,2 వారాలు:2 weeks,ఒక నెల:1 month,3 నెలలు:3 months,6 నెలలు:6 months,ఒక సంవత్సరం:1 year,ఎప్పటికీ:infinite",
        "ipbhidename": "మార్పులు మరియు జాబితాల నుండి ఈ వాడుకరిపేరుని దాచు",
-       "ipbwatchuser": "à°\88 à°¸à°­à±\8dà°¯à±\81ని à°¸à°­à±\8dà°¯à±\81à°¨ి పేజీ, చర్చాపేజీలను వీక్షణలో ఉంచు",
+       "ipbwatchuser": "à°\88 à°µà°¾à°¡à±\81à°\95à°°à°¿ à°µà°¾à°¡à±\81à°\95à°°ి పేజీ, చర్చాపేజీలను వీక్షణలో ఉంచు",
        "ipb-disableusertalk": "నిరోధంలో ఉండగా ఈ వాడుకరి తన స్వంత చర్చ పేజీలో మార్పుచేర్పులు చెయ్యకుండా నిరోధించు",
        "ipb-change-block": "ఈ అమరికలతో వాడుకరిని పునర్నిరోధించు",
        "ipb-confirm": "నిరోధాన్ని ధృవపరచండి",
        "ipb-blocklist-contribs": "{{GENDER:$1|$1}} మార్పులు-చేర్పులు",
        "ipb-blocklist-duration-left": "ఇంకా $1 మిగిలి ఉంది.",
        "unblockip": "వాడుకరిపై నిరోధాన్ని తొలగించు",
-       "unblockiptext": "à°\95à°¿à°\82ది à°«à°¾à°°à°\82 à°\89పయà±\8bà°\97à°¿à°\82à°\9aà°¿, à°¨à°¿à°°à±\8bధిà°\82à°\9aబడిన à°\90.à°ªà±\80. à°\9aà°¿à°°à±\81నామా à°²à±\87దా à°¸à°­à±\8dà°¯à±\81à°¨ికి తిరిగి రచనలు చేసే అధికారం ఇవ్వవచ్చు.",
+       "unblockiptext": "à°\95à°¿à°\82ది à°«à°¾à°°à°\82 à°\89పయà±\8bà°\97à°¿à°\82à°\9aà°¿, à°¨à°¿à°°à±\8bధిà°\82à°\9aబడిన à°\90.à°ªà±\80. à°\9aà°¿à°°à±\81నామా à°²à±\87దా à°µà°¾à°¡à±\81à°\95à°°ికి తిరిగి రచనలు చేసే అధికారం ఇవ్వవచ్చు.",
        "ipusubmit": "ఈ నిరోధాన్ని తొలగించు",
        "unblocked": "[[User:$1|$1]]పై నిరోధం తొలగించబడింది",
        "unblocked-range": "$1 పై నిరోధాన్ని తీసేసాం",
        "emailblock": "ఈ-మెయిలుని నిరోధించాం",
        "blocklist-nousertalk": "తమ చర్చాపేజీని మార్చలేరు",
        "ipblocklist-empty": "నిరోధపు జాబితా ఖాళీగా ఉంది.",
-       "ipblocklist-no-results": "à°®à±\80à°°à°¡à°¿à°\97à°¿à°¨ à°\90à°ªà±\80 à°\85à°¡à±\8dà°°à°¸à±\81 à°²à±\87దా à°¸à°­à±\8dయనామానà±\8dని నిరోధించలేదు.",
+       "ipblocklist-no-results": "à°®à±\80à°°à°¡à°¿à°\97à°¿à°¨ à°\90à°ªà±\80 à°\85à°¡à±\8dà°°à°¸à±\81 à°²à±\87దా à°µà°¾à°¡à±\81à°\95à°°à°¿à°ªà±\87à°°à±\81à°¨à±\81 నిరోధించలేదు.",
        "blocklink": "నిరోధించు",
        "unblocklink": "నిరోధాన్ని ఎత్తివేయి",
        "change-blocklink": "నిరోధాన్ని మార్చండి",
        "ipbnounblockself": "మిమ్మల్ని మీరే అనిరోధించుకునే అనుమతి మీకు లేదు",
        "lockdb": "డాటాబేసును లాక్‌ చెయ్యి",
        "unlockdb": "డాటాబేసుకి తాళంతియ్యి",
-       "lockdbtext": "à°¡à°¾à°\9fాబà±\87à°¸à±\81à°¨à±\81 à°²à°¾à°\95à±\8dâ\80\8c à°\9aà±\86à°¯à±\8dయడà°\82 à°µà°²à°¨ à°¸à°­à±\8dà°¯ులు పేజీలు మార్చడం, అభిరుచులు మార్చడం, వీక్షణ జాబితాను మార్చడం వంటి డాటాబేసు ఆధారిత పనులు చెయ్యలేరు. మీరు చెయ్యదలచినది ఇదేనని, మీ పని కాగానే తిరిగి డాటాబేసును ప్రారంభిస్తాననీ ధృవీకరించండి.",
+       "lockdbtext": "à°¡à°¾à°\9fాబà±\87à°¸à±\81à°¨à±\81 à°²à°¾à°\95à±\8dâ\80\8c à°\9aà±\86à°¯à±\8dయడà°\82 à°µà°²à°¨ à°µà°¾à°¡à±\81à°\95à°°ులు పేజీలు మార్చడం, అభిరుచులు మార్చడం, వీక్షణ జాబితాను మార్చడం వంటి డాటాబేసు ఆధారిత పనులు చెయ్యలేరు. మీరు చెయ్యదలచినది ఇదేనని, మీ పని కాగానే తిరిగి డాటాబేసును ప్రారంభిస్తాననీ ధృవీకరించండి.",
        "unlockdbtext": "డేటాబేసుకు తాళం తీసేసిన తరువాత, వాడుకరులందరూ పేజీలను మార్చటం మొదలు పెట్టగలరు,\nతమ అభిరుచులను మార్చుకోగలరు, వీక్షణా జాబితాకు పేజీలను కలుపుకోగలరు తీసివేయనూగలరు,\nఅంతేకాక డేటాబేసులో మార్పులు చేయగలిగే ఇంకొన్ని పనులు కూడా చేయవచ్చు.\nమీరు చేయదలుచుకుంది ఇదేనాకాదా అని ఒకసారి నిర్ధారించండి.",
        "lockconfirm": "అవును, డేటాబేసును లాకు చెయ్యాలని నిజంగానే అనుకుంటున్నాను.",
        "unlockconfirm": "అవును, నేను నిజంగానే డాటాబేసుకి తాళం తియ్యాలనుకుంటున్నాను.",
        "delete_and_move_text": "గమ్యపు పేజీ \"[[:$1]]\" ఇప్పటికే ఉనికిలో ఉంది. \nప్రస్తుత తరలింపుకు వీలుగా దాన్ని తొలగించేయమంటారా?",
        "delete_and_move_confirm": "అవును, పేజీని తొలగించు",
        "delete_and_move_reason": "\"[[$1]]\"ను తరలించడానికి వీలుగా తొలగించారు",
-       "selfmove": "మూలం, గమ్యం పేర్లు ఒకటే; పేజీని దాని పైకే తరలించడం కుదరదు.",
+       "selfmove": " శీర్షిక ఒకటే;\nపేజీని దాని పైకే తరలించడం కుదరదు.",
        "immobile-source-namespace": "\"$1\" పేరుబరిలోని పేజీలను తరలించలేరు",
        "immobile-target-namespace": "\"$1\" పేరుబరిలోనికి పేజీలను తరలించలేరు",
        "immobile-target-namespace-iw": "పేజీని తరలించడానికి అంతర్వికీ లింకు సరైన లక్ష్యం కాదు.",
        "fix-double-redirects": "పాత పేజీని సూచిస్తున్న దారిమార్పులను తాజాకరించు",
        "move-leave-redirect": "పాత పేజీని దారిమార్పుగా ఉంచు",
        "protectedpagemovewarning": "'''హెచ్చరిక:''' ఈ పేజీని సంరక్షించారు కనుక నిర్వాహక హక్కులు కలిగిన వాడుకరులు మాత్రమే దీన్ని తరలించగలరు.\nమీ సమాచారం కోసం చివరి చిట్టా పద్దుని ఇక్కడ ఇస్తున్నాం:",
-       "semiprotectedpagemovewarning": "'''గమనిక:''' ఈ పేజీని సంరక్షించారు కనుక నమోదైన వాడుకరులు మాత్రమే దీన్ని తరలించగలరు.\nమీ సమాచారం కోసం చివరి చిట్టా పద్దుని ఇక్కడ ఇస్తున్నాం:",
+       "semiprotectedpagemovewarning": "<strong>గమనిక:</strong> ఈ పేజీని సంరక్షించారు కనుక ఆటోకన్ఫర్మ్‌డ్ వాడుకరులు మాత్రమే దీన్ని తరలించగలరు.\nమీ సమాచారం కోసం చివరి చిట్టా పద్దుని ఇక్కడ ఇస్తున్నాం:",
        "move-over-sharedrepo": "[[:$1]] సామూహిక నిక్షేపంలో ఉంది. ఈ పేరుకు మరొక ఫైలును తరలిస్తే అది ఆ సామూహిక ఫైలును ఓవర్‌రైడు చేస్తుంది.",
        "file-exists-sharedrepo": "ఎంచుకున్న ఫైలు పేరు ఇప్పటికే సామాన్య భాండాగారంలో వాడుకలో ఉంది.\nదయచేసి మరొక పేరుని ఎంచుకోండి.",
        "export": "పేజీల ఎగుమతి",
        "pageinfo-robot-index": "అనుమతించబడింది",
        "pageinfo-robot-noindex": "అనుమతించబడలేదు",
        "pageinfo-watchers": "పేజీ గమనింపుదారుల సంఖ్య",
+       "pageinfo-visiting-watchers": "ఈ పేజీలో ఇటీవల జరిగిన దిద్దుబాట్లను చూసిన వీక్షకుల సంఖ్య",
        "pageinfo-few-watchers": "$1 {{PLURAL:$1|వీక్షకుడి|వీక్షకుల}} కంటే తక్కువ",
        "pageinfo-redirects-name": "ఈ పేజీకి ఉన్న దారిమార్పుల సంఖ్య",
        "pageinfo-subpages-name": "ఈ పేజీకి ఉన్న ఉపపేజీల సంఖ్య",
        "pageinfo-protect-cascading-yes": "అవును",
        "pageinfo-protect-cascading-from": "సంరక్షణ ఇక్కడినుంచి వ్యాపిస్తుంది",
        "pageinfo-category-info": "వర్గపు సమాచారం",
-       "pageinfo-category-total": "à°®à±\8aà°¤à±\8dà°¤à°\82 à°¸à°­à±\8dà°¯ుల సంఖ్య",
+       "pageinfo-category-total": "à°®à±\8aà°¤à±\8dà°¤à°\82 à°µà°¾à°¡à±\81à°\95à°°ుల సంఖ్య",
        "pageinfo-category-pages": "పేజీల సంఖ్య",
        "pageinfo-category-subcats": "ఉపవర్గాల సంఖ్య",
        "pageinfo-category-files": "దస్త్రాల సంఖ్య",
        "tags-delete-explanation-initial": "మీరు \"$1\" ట్యాగును డేటాబేసు నుండి తొలగించబోతున్నారు.",
        "tags-delete-reason": "కారణం:",
        "tags-delete-not-found": "\"$1\" అనే ట్యాగు లేదు.",
+       "tags-activate-question": "\"$1\" ట్యాగును మీరు చేతనం చెయ్యబోతున్నారు.",
        "tags-activate-reason": "కారణం:",
+       "tags-activate-not-allowed": "\"$1\" ట్యాగును చేతనం చెయ్యడం కుదరలేదు.",
        "tags-activate-not-found": "\"$1\" అనే ట్యాగు లేదు.",
        "tags-activate-submit": "చేతనంచేయి",
        "tags-deactivate-title": "ట్యాగును అచేతనం చేయి",
        "tags-deactivate-not-allowed": "\"$1\" ట్యాగును అచేతనం చేయడం సాధ్యం కాదు.",
        "tags-deactivate-submit": "అచేతనం చేయి",
        "tags-apply-no-permission": "మీ మార్పులతో పాటు వాటికి ట్యాగులను ఆపాదించే అనుమతి మీకు లేదు.",
+       "tags-apply-blocked": "{{GENDER:$1|మీరు}} నిరోధంలో ఉండగా మీ మార్పులతో పాటు మార్పు ట్యాగులను అమలు చెయ్యడం కుదరదు.",
        "tags-apply-not-allowed-one": "\"$1\" ట్యాగును మానవీయంగా ఆపాదించలేరు.",
-       "tags-apply-not-allowed-multi": "ఈ {{PLURAL:$2|ట్యాగును|ట్యాగులను}} మానవీయంగా ఆపాదించడానికి అనుమతించరు:",
+       "tags-apply-not-allowed-multi": "ఈ {{PLURAL:$2|ట్యాగును|ట్యాగులను}} మానవీయంగా ఆపాదించడానికి అనుమతి లేదు:$1",
+       "tags-update-no-permission": "కూర్పులు లేదా చిట్టా లోని అంశాలకు మార్పు ట్యాగులను మార్చే, తీసేసే అనుమతి మీకు లేదు",
+       "tags-update-blocked": "{{GENDER:$1|మీరు}} నిరోధంలో ఉండగా మీ మార్పులతో పాటు మార్పు ట్యాగులను చేర్చడం, తీసెయ్యడం కుదరదు.",
+       "tags-update-add-not-allowed-one": "\"$1\" ట్యాగును మానవికంగా చేర్చే అనుమతి లేదు.",
+       "tags-update-add-not-allowed-multi": "కింది {{PLURAL:$2|ట్యాగును|ట్యాగులను}} మానవికంగా చేర్చే అనుమతి లేదు: $1",
+       "tags-update-remove-not-allowed-one": "\"$1\" ట్యాగును తీసేసే అనుమతి లేదు.",
+       "tags-update-remove-not-allowed-multi": "కింది {{PLURAL:$2|ట్యాగును|ట్యాగులను}} మానవికంగా తీసేసే అనుమతి లేదు: $1",
        "tags-edit-title": "ట్యాగులను సవరించు",
        "tags-edit-manage-link": "ట్యాగులను నిర్వహించండి",
+       "tags-edit-revision-selected": "[[:$2]] కు చెందిన {{PLURAL:$1|ఎంచుకున్న కూర్పు|ఎంచుకున్న కూర్పులు}}:",
        "tags-edit-existing-tags": "ప్రస్తుత ట్యాగులు:",
        "tags-edit-existing-tags-none": "<em>ఏమీలేవు</em>",
        "tags-edit-new-tags": "కొత్త ట్యాగులు:",
        "restrictionsfield-badip": "అసంబద్ధమైన ఐపీ అడ్రసు లేదా శ్రేణి: $1",
        "restrictionsfield-label": "అనుమతించబడ్డ ఐపీ శ్రేణులు:",
        "restrictionsfield-help": "వరుసకొక్క ఐపీ అడ్రసు లేదా CIDR శ్రేణి. ప్రతీ ఒక్కదాన్నీ చేతనం చేసేందుకు, వాడండి:<pre>0.0.0.0/0\n::/0</pre>",
+       "edit-error-short": "లోపం: $1",
+       "edit-error-long": "లోపాలు:\n\n$1",
        "revid": "కూర్పు $1",
        "pageid": "పేజీ ఐడీ $1",
+       "gotointerwiki": "{{SITENAME}} ను వీడిపోతున్నారు",
+       "gotointerwiki-invalid": "ఇచ్చిన శీర్షిక సరైనది కాదు.",
+       "gotointerwiki-external": "మీరు {{SITENAME}} ను వదలి [[$2]] కు వెళ్తున్నారు. అది వేరే సైటు.\n\n'''[$1 $1 కు తీసుకుపో]'''",
+       "undelete-cantedit": "ఈ పేజీ తొలగింపును రద్దు చెయ్యలేరు. ఎందుకంటే ఈ పేజీలో మార్పుచేర్పులు చేసే అనుమతి మీకు లేదు.",
+       "undelete-cantcreate": "ఈ పేజీ తొలగింపును రద్దు చెయ్యలేరు. ఎందుకంటే ఈ పేరుతో పేజీ లేదు, దాన్ని సృష్టించే అనుమతి మీకు లేదు.",
+       "pagedata-title": "పేజీ డేటా",
        "pagedata-bad-title": "చెల్లని శీర్షిక: $1.",
        "passwordpolicies": "సంకేతపదపు విధానాలు",
+       "passwordpolicies-summary": "ఈ వికీలో నిర్వ్చించిన వాడుకరి గుంపులకు వర్తించే సంకేతపద విధానాల జాబితా ఇది.",
        "passwordpolicies-group": "సమూహం",
-       "passwordpolicies-policies": "విధానాలు"
+       "passwordpolicies-policies": "విధానాలు",
+       "passwordpolicies-policy-minimalpasswordlength": "సంకేతపదం కనీసం $1 {{PLURAL:$1|క్యారెక్టరు|క్యారెక్టర్ల}} పొడవు ఉండాలి",
+       "passwordpolicies-policy-minimumpasswordlengthtologin": "లాగినవ్వాలంటే సంకేతపదం కనీసం $1 {{PLURAL:$1|క్యారెక్టరు|క్యారెక్టర్ల}} పొడవు ఉండాలి",
+       "passwordpolicies-policy-passwordcannotmatchusername": "సంకేతపదం, వాడుకరిపేరూ ఒకటే ఉండే వీల్లేదు.",
+       "passwordpolicies-policy-passwordcannotmatchblacklist": "సంకేతపదం నిషేధించిన సంకేతపదాలతో సరిపోలరాదు",
+       "passwordpolicies-policy-maximalpasswordlength": "సంకేతపదం $1 {{PLURAL:$1|క్యారెక్టరు|క్యారెక్టర్ల}} పొడవు కంటే తక్కువ ఉండాలి",
+       "passwordpolicies-policy-passwordcannotbepopular": "సంకేతపదం {{PLURAL:$1|ప్రజాదరణ పొందిన సంకేతపదం కారాదు|ప్రజాదరణ పొందిన $1 సంకేతపదాల్లో ఉండరాదు}}"
 }
index 7e30056..a5ef7a7 100644 (file)
        "cascadeprotectedwarning": "'''Babala:''' Ikinandado ang pahinang ito upang tanging mga tagagamit na may mga karapatang pang-''sysop'' lamang ang makapagbago nito, dahil kabilang ito sa sumusunod na mga {{PLURAL:$1|pahinang|mga pahinang}} may baita-baitang na panananggalang:",
        "titleprotectedwarning": "'''Babala:  Ikinandado ang pahinang ito upang [[Special:ListGroupRights|partikular na mga karapatan]] ang kakailanganin upang mailikha ito.'''\nAng pinakahuling entrada sa talaan ay ibinigay sa baba para sa inyong pagsasangguni:",
        "templatesused": "{{PLURAL:$1|Padron|Mga padron}} na ginagamit sa pahinang ito:",
-       "templatesusedpreview": "{{PLURAL:$1|Suleras|Mga suleras}} na ginagamit sa paunang-tinging ito:",
+       "templatesusedpreview": "{{PLURAL:$1|Padrong|Mga padrong}} ginagamit sa paunang-tinging ito:",
        "templatesusedsection": "{{PLURAL:$1|Suleras|Mga suleras}} na ginamit sa seksyong ito:",
        "template-protected": "(nakasanggalang)",
        "template-semiprotected": "(bahagyang nakasanggalang)",
        "compareselectedversions": "Paghambingin ang mga napiling bersyon",
        "showhideselectedversions": "Ipakita/itago ang napiling mga bersyon",
        "editundo": "ibalik",
+       "diff-empty": "(Walang pagkakaiba)",
        "diff-multi-manyusers": "({{PLURAL:$1|Isang panggitnang pagbabago|$1 panggitnang mga pagbabago}} ng {{PLURAL:$2|isang tagagamit|$2 mga tagagamit}} ang hindi ipinapakikita.)",
        "difference-missing-revision": "Hindi natagpuan ang {{PLURAL:$2|isang rebisyon|$2 mga rebisyon}} ng kaibahang ($1) ito.\n\nKaraniwang itong isinanhi ng pagsunod sa isang wala na sa panahong kawing sa pagkakaiba na papunta sa isang pahinang nabura na.\nMatatagpuan ang mga detalye sa loob ng [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} talaan ng pagbura].",
        "searchresults": "Kinalabasan/Resulta ng paghahanap",
        "whatlinkshere-hideredirs": "$1 ang mga pagpapapunta sa ibang pahina",
        "whatlinkshere-hidetrans": "$1 ang mga paglipat-sali (transklusyon)",
        "whatlinkshere-hidelinks": "$1 ang mga kawing",
-       "whatlinkshere-hideimages": "$1 kawing ng/sa talaksan",
+       "whatlinkshere-hideimages": "$1 kawing sa talaksan",
        "whatlinkshere-filters": "Mga pansala",
        "autoblockid": "Kusang harangin ang #$1",
        "block": "Harangin ang tagagamit",
        "pageinfo-default-sort": "Likas na nakatakdang susi ng pag-uuri",
        "pageinfo-length": "Haba ng pahina (na nasa mga byte)",
        "pageinfo-article-id": "ID ng pahina",
-       "pageinfo-robot-policy": "Katayuan ng makinang panghanap",
-       "pageinfo-robot-index": "Matataluntunan",
+       "pageinfo-language": "Wika ng nilalaman ng pahina",
+       "pageinfo-content-model": "Modelo ng nilalaman ng pahina",
+       "pageinfo-robot-policy": "Pagtatalatuntunang gawa ng robot",
+       "pageinfo-robot-index": "Pinapayagan",
        "pageinfo-robot-noindex": "Hindi pinayagan",
        "pageinfo-watchers": "Bilang ng mga nagbabantay ng pahina",
-       "pageinfo-redirects-name": "Napapapunta sa pahinang ito",
+       "pageinfo-redirects-name": "Bilang ng mga pagkarga sa pahinang ito",
        "pageinfo-redirects-value": "$1",
        "pageinfo-subpages-name": "Mga kabahaging pahina ng pahinang ito",
        "pageinfo-subpages-value": "$1 ($2 {{PLURAL:$2|pagpapapunta sa ibang pahina|mga pagpapapunta sa ibang pahina}}; $3 {{PLURAL:$3|hindi pagpapapunta sa ibang pahina|mga hindi pagpapapunta sa ibang pahina}})",
        "pageinfo-authors": "Kabuuang bilang ng magkakabukod na mga may-akda",
        "pageinfo-recent-edits": "Kamakailang bilang ng mga pamamatnugot (sa loob ng huling $1)",
        "pageinfo-recent-authors": "Kamakailang bilang ng magkakabukod na mga may-akda",
-       "pageinfo-magic-words": "{{PLURAL:$1|Salita|Mga salita}}ng mahiwaga ($1)",
+       "pageinfo-magic-words": "{{PLURAL:$1|Salitang|Mga salitang}} mahiwaga ($1)",
        "pageinfo-hidden-categories": "Nakatagong {{PLURAL:$1|kategorya|mga kategorya}} ($1)",
-       "pageinfo-templates": "{{PLURAL:$1|Suleras|Mga suleras}} ($1) na nasa transklusyon (kasama sa maraming mga lugar)",
+       "pageinfo-templates": "{{PLURAL:$1|Padrong nasa transklusyon|Mga padrong nasa transklusyon}} ($1)",
        "pageinfo-toolboxlink": "Impormasyon ng pahina",
+       "pageinfo-contentpage": "Binibilang bilang pahina ng nilalaman",
+       "pageinfo-contentpage-yes": "Oo",
        "markaspatrolleddiff": "Tatakan bilang napatrolya na",
        "markaspatrolledtext": "Tatakan ang pahinang ito bilang napatrolya na",
        "markedaspatrolled": "Tatakan bilang napatrolya na",
        "version-entrypoints-header-url": "URL",
        "version-entrypoints-articlepath": "[https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:$wgArticlePath Landas ng artikulo]",
        "version-entrypoints-scriptpath": "[https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:$wgScriptPath Landas ng panitik]",
+       "redirect-revision": "Rebisyon ng pahina",
+       "redirect-file": "Pangalan ng talaksan",
        "fileduplicatesearch": "Maghanap ng kaparehong mga talaksan",
        "fileduplicatesearch-summary": "Maghanap ng mga kaparehong mga talaksan sa baba ng kanyang halaga ng ''hash''.",
        "fileduplicatesearch-filename": "Pangalan ng file:",
        "tags-source-header": "Pinagmulan",
        "tags-hitcount-header": "Natatakang mga pagbabago",
        "tags-actions-header": "Mga hakbang",
+       "tags-active-yes": "Oo",
+       "tags-active-no": "Hindi",
        "tags-source-none": "Hindi na ginagamit pa",
        "tags-edit": "baguhin",
        "tags-delete": "Burahin",
        "revdelete-unrestricted": "tinanggal ang mga pagbabawal para sa mga tagapangasiwa",
        "logentry-move-move": "{{GENDER:$2|Inilipat}} ni $1 ang pahinang $3 papunta sa $4",
        "logentry-move-move-noredirect": "{{GENDER:$2|Inilipat}} ni $1 ang pahinang $3 papunta sa $4 na hindi nag-iiwan ng ibang kapupuntahan",
-       "logentry-move-move_redir": "Inilipat ni $1 ang pahinang $3 papunta sa $4 na nasa ibayo ng ibang kapupuntahan",
+       "logentry-move-move_redir": "{{GENDER:$2|Inilipat}} ni $1 ang pahinang \"$3\" sa \"$4\" sa ibayo ng pagkarga",
        "logentry-move-move_redir-noredirect": "Inilipat ni $1 ang pahinang $3 papunta sa $4 sa ibabaw ng isang pagpapunta sa iba na hindi nag-iiwan ng isang pagpapapunta sa iba",
        "logentry-patrol-patrol": "Minarkahan ni $1 ang rebisyong $4 ng pahinang $3 bilang napatrolya na",
        "logentry-patrol-patrol-auto": "Kusang minarkahan ni $1 ang rebisyong $4 ng pahinang $3 bilang napatrolya na",
index 3f492b3..196f1f0 100644 (file)
        "tog-watchlisthideminor": "Küçük düzenlemeleri izleme listesinde gizle",
        "tog-watchlisthideliu": "Oturum açmış kullanıcıların düzenlemelerini izleme listesinde gizle",
        "tog-watchlistreloadautomatically": "Her süzgeç değişikliği olduğunda izleme listesini otomatik olarak yeniden yükle (JavaScript gerekir)",
+       "tog-watchlistunwatchlinks": "Değişiklik içeren izleme sayfalarına doğrudan izle/izleme işareti ({{int:Watchlist-unwatch}}/{{int:Watchlist-unwatch-undo}}) ekle (Geçiş işlevi için JavaScript gereklidir)",
        "tog-watchlisthideanons": "Anonim kullanıcıların düzenlemelerini izleme listesinde gizle",
        "tog-watchlisthidepatrolled": "Devriyenin gördüğü düzenlemeleri izleme listesinde gizle",
        "tog-watchlisthidecategorization": "Sayfa kategorilendirmesini gizle",
        "converter-manual-rule-error": "Elle yapılandırma dil dönüşüm kuralı hatası tespit edildi",
        "undo-success": "Bu değişiklik geri alınabilir. Lütfen aşağıdaki karşılaştırmayı kontrol edin, gerçekten bu değişikliği yapmak istediğinizden emin olun ve sayfayı kaydederek bir önceki değişikliği geriye alın.",
        "undo-failure": "Değişikliklerin çakışması nedeniyle geri alma işlemi başarısız oldu.",
+       "undo-main-slot-only": "Düzenleme, ana alanın dışındaki içeriği içerdiği için geri alınamaz.",
        "undo-norev": "Değişiklik geri alınamaz çünkü ya silinmiş ya da varolmamaktadır.",
        "undo-nochange": "Düzeltme zaten geri alınmış.",
        "undo-summary": "$1 değişikliği [[Special:Contributions/$2|$2]] ([[User talk:$2|mesaj]]) tarafından geri alındı.",
        "diff-multi-sameuser": "(Aynı kullanıcının {{PLURAL:$1|aradaki bir diğer değişikliği|aradaki diğer $1 değişikliği}} gösterilmiyor)",
        "diff-multi-otherusers": "({{PLURAL:$2|Bir diğer kullanıcıdan|$2 kullanıcıdan}} {{PLURAL:$1|bir ara revizyon|$1 ara revizyon}} gösterilmiyor)",
        "diff-multi-manyusers": "($2 kullancıdan fazla {{PLURAL:$2|kullanıcı|kullanıcı}} tarafından yapılan {{PLURAL:$1|bir ara revizyon|$1 ara revizyon}} gösterilmiyor)",
+       "diff-paragraph-moved-tonew": "Paragraf taşındı. Yeni yere gitmek için tıklayın.",
+       "diff-paragraph-moved-toold": "Paragraf taşındı. Eski yere gitmek için tıklayın.",
        "difference-missing-revision": "Bu farkın {{PLURAL:$2|bir revizyonu|$2 revizyonu}} ($1) {{PLURAL:$2|bulunamadı|bulunamadı}}.\n\nBu genellikle, silinen bir sayfaya olan eski tarihli bir fark sayfasına bağlantılardan dolayı olur.\nAyrıntılar [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} silme günlüğünde] bulunabilir.",
        "searchresults": "Arama sonuçları",
+       "search-filter-title-prefix": "Sadece başlığı \"$1\" ile başlayan sayfalarda arama",
+       "search-filter-title-prefix-reset": "Tüm sayfalarda ara",
        "searchresults-title": "\"$1\" için arama sonuçları",
        "titlematches": "Madde adı eşleşiyor",
        "textmatches": "Sayfa metni eşleşiyor",
        "prefs-watchlist-edits": "İzleme listesinde gösterilecek en fazla değişiklik sayısı:",
        "prefs-watchlist-edits-max": "En fazla sayı: 1000",
        "prefs-watchlist-token": "İzleme listesi anahtarı:",
+       "prefs-watchlist-managetokens": "Anahtarları yönet",
        "prefs-misc": "Diğer ayarlar",
        "prefs-resetpass": "Parolayı değiştir",
        "prefs-changeemail": "E-posta adresini değiştir veya kaldır",
        "recentchangescount": "Son değişiliklerde, sayfa geçmişlerinde ve günlüklerde varsayılan olarak gösterilecek değişiklik sayısı:",
        "prefs-help-recentchangescount": "Azami sayı: 1000",
        "prefs-help-watchlist-token2": "Bu izleme listenizin gizli anahtarıdır.\nAnahtarı bilen herkes izleme listenizi görebilir, bu nedenle kimseyle paylaşmayın.\nEğer isterseniz, [[Special:ResetTokens|sıfırlayabilirsiniz]].",
+       "prefs-help-tokenmanagement": "Hesabınızın izleme listenizdeki web akışına erişebilecek gizli anahtarını görebilir ve sıfırlayabilirsiniz. Anahtarı bilen herkes izleme listenizi okuyabilecek, bu yüzden paylaşmayın.",
        "savedprefs": "Tercihleriniz kaydedildi.",
        "savedrights": "{{GENDER:$1|$1}} için kullanıcı grupları kaydedildi.",
        "timezonelegend": "Zaman dilimi:",
        "timezoneregion-indian": "Hint Okyanusu",
        "timezoneregion-pacific": "Pasifik Okyanusu",
        "allowemail": "Diğer kullanıcıların bana e-posta gönderebilmesine izin ver",
+       "email-allow-new-users-label": "Yepyeni kullanıcılardan e-postalara izin ver",
        "email-blacklist-label": "Şu kullanıcıların bana e-posta göndermesine izin verme:",
        "prefs-searchoptions": "Arama",
        "prefs-namespaces": "İsim alanları",
        "default": "varsayılan",
        "prefs-files": "Dosyalar",
        "prefs-custom-css": "Özel CSS",
+       "prefs-custom-json": "Özel JSON",
        "prefs-custom-js": "Özel JS",
        "prefs-common-config": "Tüm temalar için paylaşılan CSS/JSON/Javascript:",
        "prefs-reset-intro": "Bu sayfayı tercihlerinizi site varsayılanına döndürmek için kullanabilirsiniz. Bu geri alınamaz.",
        "prefs-dateformat": "Tarih biçemi",
        "prefs-timeoffset": "Saat farkı",
        "prefs-advancedediting": "Genel seçenekler",
+       "prefs-developertools": "Geliştirici araçları",
        "prefs-editor": "Editör",
        "prefs-preview": "Önizleme",
        "prefs-advancedrc": "Gelişmiş seçenekler",
+       "prefs-opt-out": "İyileştirmeleri devre dışı bırak",
        "prefs-advancedrendering": "Gelişmiş seçenekler",
        "prefs-advancedsearchoptions": "Gelişmiş seçenekler",
        "prefs-advancedwatchlist": "Gelişmiş seçenekler",
        "userrights-user-editname": "Kullanıcı adı giriniz:",
        "editusergroup": "Kullanıcı gruplarını gör",
        "editinguser": "<strong>'''[[User:$1|$1]]'''</strong> $2 kullanıcısının yetkileri değiştiriliyor",
+       "viewinguserrights": "<strong>[[User:$1|$1]]</strong> $2 kullanıcısının {{GENDER:$1|kullanıcı}} haklarını görüntüleme",
        "userrights-editusergroup": "{{GENDER:$1|Kullanıcı}} gruplarını düzenle",
        "userrights-viewusergroup": "{{GENDER:$1|Kullanıcı}} gruplarını gör",
        "saveusergroups": "{{GENDER:$1|Kullanıcı}} gruplarını kaydet",
        "userrights-expiry-existing": "Mevcut bitiş süresi: $2 $3",
        "userrights-expiry-othertime": "Diğer süre:",
        "userrights-expiry-options": "1 gün:1 day,1 hafta:1 week,1 ay:1 month,3 ay:3 months,6 ay:6 months,1 yıl:1 year",
+       "userrights-invalid-expiry": "\"$1\" grubunun süre sonu geçersiz.",
        "userrights-expiry-in-past": "\"$1\" grubunun bitiş süresi geçmişte.",
+       "userrights-cannot-shorten-expiry": "Üyelik süresinin sona ermesini \"$1\" grubunda ileriye taşıyamazsınız. Yalnızca bu grubu ekleme ve kaldırma izni olan kullanıcılar, sürenin dolduğu süreyi getirebilir.",
        "userrights-conflict": "Kullanıcı hakları değişikliklerinde çakışma! Lütfen değişikliklerinizi gözden geçirin ve onaylayın.",
        "group": "Grup:",
        "group-user": "Kullanıcılar",
        "right-editcontentmodel": "Sayfanın içerik modelini düzenle",
        "right-editinterface": "Kullanıcı arayüzünü değiştirmek",
        "right-editusercss": "Diğer kullanıcıların CSS dosyalarında değişiklik yap",
+       "right-edituserjson": "Diğer kullanıcıların JSON dosyalarını düzenle",
        "right-edituserjs": "Diğer kullanıcıların JS dosyalarında değişiklik yap",
        "right-editmyusercss": "Kendi kullanıcı CSS dosyaları düzenle",
+       "right-editmyuserjson": "Kendi kullanıcı JSON dosyalarını düzenle",
        "right-editmyuserjs": "Kendi kullanıcı JavaScript dosyalarını düzenle",
        "right-viewmywatchlist": "Kendi izleme listeni gör",
        "right-editmywatchlist": "Kendi izleme listeni düzenle. Not, bazı eylemler bu yetki olmadan da sayfa ekleyebilir.",
        "right-managechangetags": "[[Special:Tags|Etiket]] oluşturma ve (de)aktive etme",
        "right-applychangetags": "Değişiklikleriyle beraber [[Special:Tags|etiketleri]] uygula",
        "right-changetags": "Tekil sürümler ve günlük kayıtlarına rastgele [[Special:Tags|etiket]] ekleme veya çıkarma",
+       "right-deletechangetags": "Veritabanından [[Special:Tags|etiketleri]] silin",
+       "grant-generic": "\"$1\" hak paketi",
+       "grant-group-page-interaction": "Sayfalarla etkileşim kur",
+       "grant-group-file-interaction": "Medya ile etkileşim kur",
+       "grant-group-watchlist-interaction": "İzleme listenle etkileşim kur",
        "grant-group-email": "E-posta gönder",
+       "grant-group-customization": "Özelleştirme ve tercihler",
+       "grant-group-private-information": "Sizinle ilgili özel verilere erişme",
        "grant-group-other": "Çeşitli aktivite",
+       "grant-blockusers": "Kullanıcıları engelle ve engeli kaldır",
+       "grant-createaccount": "Hesap oluştur",
        "grant-createeditmovepage": "Sayfaları oluşturma, düzenleme ve taşıma",
+       "grant-delete": "Sayfaları, sürümleri ve günlük girdileri sil",
+       "grant-editinterface": "MediaWiki alanadını ve kullanıcı CSS/JSON/JavaScript'ini düzenle",
        "grant-editmycssjs": "Kullanıcı CSS/JSON/JavaScript'ini düzenle",
        "grant-editmyoptions": "Kullanıcı tercihlerini Düzenle",
        "grant-editmywatchlist": "İzleme listeni düzenle",
+       "grant-editpage": "Mevcut sayfaları düzenle",
        "grant-editprotected": "Korumalı sayfaları Düzenle",
+       "grant-highvolume": "Yüksek hacimli düzenleme",
+       "grant-oversight": "Kullanıcıları gizle ve sürümleri durdur",
        "grant-patrol": "Sayfadaki değişiklikleri incele",
+       "grant-privateinfo": "Özel bilgilere eriş",
+       "grant-protect": "Sayfaları koru ve korumasını kaldır",
        "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",
        "recentchanges-legend": "Son değişiklikler seçenekleri",
        "recentchanges-summary": "Vikide yapılan en son değişiklikleri bu sayfadan izleyin.",
        "recentchanges-noresult": "Belirtilen kriterlere uyan herhangi bir değişiklik bulunamadı.",
+       "recentchanges-timeout": "Bu arama zaman aşımına uğradı. Farklı arama parametrelerini denemek isteyebilirsiniz.",
+       "recentchanges-network": "Teknik bir hatadan dolayı sonuç yüklenemedi. Lütfen sayfayı yenilemeyi deneyin.",
+       "recentchanges-notargetpage": "Bu sayfa ile ilgili değişiklikleri görmek için yukarıdaya bir sayfa adı girin.",
        "recentchanges-feed-description": "Bu beslemede, viki'de yapılan en son değişiklikleri takip edin.",
        "recentchanges-label-newpage": "Bu değişiklikle yeni bir sayfa oluşturuldu",
        "recentchanges-label-minor": "Bu küçük bir değişiklik",
        "recentchanges-legend-heading": "<strong>Gösterge:</strong>",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (ayrıca [[Special:NewPages|yeni sayfalar listesine]] bakınız)",
        "recentchanges-submit": "Göster",
+       "rcfilters-tag-remove": "'$1' kaldır",
        "rcfilters-legend-heading": "<strong>Kısaltmalar listesi:</strong>",
        "rcfilters-other-review-tools": "Diğer inceleme araçları",
        "rcfilters-group-results-by-page": "Sayfalandırılmış grup sonuçları",
        "rcfilters-activefilters": "Etkin süzgeçler",
+       "rcfilters-activefilters-hide": "Gizle",
+       "rcfilters-activefilters-show": "Göster",
+       "rcfilters-activefilters-hide-tooltip": "Etkin Filtreler alanını gizle",
+       "rcfilters-activefilters-show-tooltip": "Etkin Filtreler alanını göster",
        "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-hours-title": "Son saatler",
        "rcfilters-days-show-days": "$1 gün",
        "rcfilters-days-show-hours": "$1 saat",
+       "rcfilters-highlighted-filters-list": "Vurgulanan: $1",
        "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-savedqueries-new-name-label": "Ad",
        "rcfilters-savedqueries-new-name-placeholder": "Süzgecin amacını tanımlayın",
        "rcfilters-savedqueries-apply-label": "Süzgeç oluştur",
+       "rcfilters-savedqueries-apply-and-setdefault-label": "Varsayılan filtre oluştur",
+       "rcfilters-savedqueries-cancel-label": "İptal",
        "rcfilters-savedqueries-add-new-title": "Mevcut süzgeç ayarlarını kaydet",
+       "rcfilters-savedqueries-already-saved": "Bu filtreler zaten kaydedildi. Yeni bir Kayıtlı Filtre oluşturmak için ayarlarınızı değiştirin.",
        "rcfilters-restore-default-filters": "Varsayılan süzgeçleri geri getir",
        "rcfilters-clear-all-filters": "Tüm süzgeçleri temizle",
        "rcfilters-show-new-changes": "Yeni değişiklikleri görüntüle",
        "rcfilters-highlightmenu-help": "Bu özelliği vurgulamak için bir renk seçin",
        "rcfilters-filterlist-noresults": "Süzgeç bulunamadı",
        "rcfilters-noresults-conflict": "Arama kriterleri çelişkili olduğu için hiçbir sonuç bulunamadı",
+       "rcfilters-state-message-subset": "Buu filtrenin etkisi yok çünkü sonuçları aşağıdaki, daha geniş {{PLURAL:$2|filtreye|filtrelere}} dahil edilmiştir (ayırt etmek için vurgulamayı deneyin): $1",
+       "rcfilters-state-message-fullcoverage": "Bu gruptaki tüm filtreleri seçmek, hiçbirini seçmemekle aynıdır; bu nedenle bu filtrenin etkisi yoktur. Grup şunları içerir: $1",
        "rcfilters-filtergroup-authorship": "Katkı sahipliği",
        "rcfilters-filter-editsbyself-label": "Senin değişiklikleriniz",
        "rcfilters-filter-editsbyself-description": "Kendi katkılarınız.",
        "rcfilters-filter-humans-label": "İnsan (bot değil)",
        "rcfilters-filter-humans-description": "İnsan editörler tarafından yapılan düzenlemeler.",
        "rcfilters-filtergroup-reviewstatus": "İnceleme durumu",
+       "rcfilters-filter-reviewstatus-unpatrolled-description": "Devriye onayından el ile ya da otomatik olarak geçmemiş düzenlemeler.",
        "rcfilters-filter-reviewstatus-unpatrolled-label": "Devriye onayından geçmemiş",
+       "rcfilters-filter-reviewstatus-manual-description": "Devriye onayından el ile geçmiş düzenlemeler.",
        "rcfilters-filtergroup-significance": "Önem",
        "rcfilters-filter-minor-label": "Küçük düzenlemeler",
        "rcfilters-filter-minor-description": "Yazarın küçük olarak etiketlediği düzenlemeler.",
        "rcfilters-filter-categorization-description": "Kategorilere eklenen veya kaldırılan sayfaların kayıtları.",
        "rcfilters-filter-logactions-label": "Günlüğü tutulan işlemler",
        "rcfilters-filter-logactions-description": "Hizmetli işlemleri, hesap oluşturmalar, sayfa silmeler, yüklemeler...",
+       "rcfilters-filtergroup-lastRevision": "En son sürümler",
        "rcfilters-filter-lastrevision-label": "Son revizyon",
+       "rcfilters-filter-lastrevision-description": "Bir sayfadaki en yeni değişiklik.",
        "rcfilters-filter-previousrevision-label": "Son revizyon değil",
        "rcfilters-filter-excluded": "Hariç",
        "rcfilters-exclude-button-off": "Seçileni hariç tut",
        "rcfilters-liveupdates-button-title-off": "Yeni değişiklikleri yapıldıkları anda görüntüleyin",
        "rcfilters-watchlist-markseen-button": "Tüm değişiklikleri görüldü olarak işaretle",
        "rcfilters-watchlist-edit-watchlist-button": "İzlenen sayfaların listesini düzenle",
+       "rcfilters-watchlist-showupdated": "Gerçekleştirilen değişikliklerden bu yana ziyaret etmediğiniz sayfalarda yapılan değişiklikler <strong>koyu</strong> renktedir.",
+       "rcfilters-preference-label": "Son Değişikliklerin geliştirilmiş sürümünü gizle",
+       "rcfilters-watchlist-preference-label": "İzleme Listesinin geliştirilmiş sürümünü gizle",
        "rcfilters-target-page-placeholder": "Bir sayfa (ya da kategori) adı girin",
        "rcnotefrom": "<strong>$3, $4</strong> tarihinden itibaren yapılan {{PLURAL:$5|değişiklik|değişiklik}} aşağıdadır (<strong>$1</strong> tarhine kadar olanlar gösterilmektedir).",
        "rclistfrom": "$3 $2 tarihinden itibaren yeni değişiklikleri göster",
        "apisandbox-submit": "İstek yap",
        "apisandbox-reset": "Temizle",
        "apisandbox-retry": "Tekrar dene",
+       "apisandbox-no-parameters": "Bu API modülünde parametre yok.",
        "apisandbox-helpurls": "Yardım bağlantıları",
        "apisandbox-examples": "Örnekler",
+       "apisandbox-dynamic-parameters": "Ek parametreler",
        "apisandbox-dynamic-parameters-add-label": "Parametre ekle:",
        "apisandbox-dynamic-parameters-add-placeholder": "Parametre adı",
+       "apisandbox-deprecated-parameters": "Onaylanmamış parametreler",
+       "apisandbox-fetch-token": "Anahtarı otomatik olarak doldur",
        "apisandbox-add-multi": "Ekle",
        "apisandbox-submit-invalid-fields-title": "Bazı alanlar geçersiz",
+       "apisandbox-submit-invalid-fields-message": "Lütfen işaretli alanları düzeltin ve tekrar deneyin.",
        "apisandbox-results": "Sonuçlar",
+       "apisandbox-sending-request": "API isteği gönderiliyor...",
+       "apisandbox-loading-results": "API sonuçları alınıyor...",
+       "apisandbox-results-error": "API sorgusu yanıtı yüklenirken bir hata oluştu: $1.",
        "apisandbox-request-url-label": "İstek URL:",
        "apisandbox-request-time": "İstek zamanı: $1",
        "apisandbox-continue": "Devam et",
        "newimages-legend": "Filtre",
        "newimages-label": "Dosya adı (ya da bir parçası):",
        "newimages-showbots": "Bot yüklemelerini göster",
+       "newimages-hidepatrolled": "Denetlenmiş yüklemeleri gizle",
        "noimages": "Görecek bir şey yok.",
        "ilsubmit": "Ara",
        "bydate": "kronolojik sırayla",
index 6b9decc..e43963d 100644 (file)
        "diff-paragraph-moved-toold": "Абзац було переміщено. Натисніть щоб перестрибнути до старого розташування.",
        "difference-missing-revision": "{{PLURAL:$2|$2 версія|$2 версії|$2 версій}} для цього порівняння ($1) не {{PLURAL:$2|1=знайдена|знайдені}}.\n\nІмовірно, ви перейшли за застарілим посиланням на порівняння версій вилученої сторінки.\nПодробиці можна дізнатися з [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} журналу вилучень].",
        "searchresults": "Результати пошуку",
+       "search-filter-title-prefix": "Лише пошук в сторінках, назви яких починаються з \"$1\"",
+       "search-filter-title-prefix-reset": "Пошук по всіх сторінках",
        "searchresults-title": "Результати пошуку для «$1»",
        "titlematches": "Збіги в назвах сторінок",
        "textmatches": "Збіги в текстах сторінок",
        "grouppage-suppress": "{{ns:project}}:Подавлювачі",
        "right-read": "перегляд сторінок",
        "right-edit": "редагування сторінок",
-       "right-createpage": "створення сторінок (але не обговорень)",
+       "right-createpage": "створення сторінок (які не є обговореннями)",
        "right-createtalk": "створення обговорень сторінок",
        "right-createaccount": "створення нових облікових записів",
        "right-autocreateaccount": "Автоматичний вхід в систему із зовнішнього облікового запису користувача",
        "right-markbotedits": "позначення відкинутих редагувань як редагування бота",
        "right-noratelimit": "нема обмежень за швидкістю",
        "right-import": "імпорт сторінок з інших вікі",
-       "right-importupload": "Імпорт сторінок через завантаження файлів",
+       "right-importupload": "імпорт сторінок через завантаження файлів",
        "right-patrol": "позначення редагувань патрульованими",
        "right-autopatrol": "автоматичне позначення редагувань патрульованими",
        "right-patrolmarks": "Перегляд патрульованих сторінок у нових редагуваннях",
        "speciallogtitlelabel": "Ціль (назва сторінки або {{ns:user}}:Ім'я_користувача):",
        "log": "Журнали",
        "logeventslist-submit": "Показати",
-       "logeventslist-more-filters": "Ð\91Ñ\96лÑ\8cÑ\88е Ñ\84Ñ\96лÑ\8cÑ\82Ñ\80Ñ\96в:",
+       "logeventslist-more-filters": "Ð\9fоказаÑ\82и Ð´Ð¾Ð´Ð°Ñ\82ковÑ\96 Ð¶Ñ\83Ñ\80нали:",
        "logeventslist-patrol-log": "Журнал патрулювання",
        "logeventslist-tag-log": "Журнал міток",
        "all-logs-page": "Усі публічні журнали",
index be1bc38..20af4c0 100644 (file)
        "deletepage": "حذف کریں",
        "confirm": "یقین",
        "excontent": "'$1':مواد تھا",
-       "excontentauthor": "حذف شدہ مواد: «$1» اور صرف «[[Special:Contributions/$2|$2]]» ([[User talk:$2|تبادلۂ خیال]]) نے اس میں ترمیم کی",
+       "excontentauthor": "مواد «$1» تھا اور صرف «[[Special:Contributions/$2|$2]]» ([[User talk:$2|تبادلۂ خیال]]) نے اس میں ترمیم کی تھی",
        "exbeforeblank": "خالی کرنے سے قبل موجود مواد: «$1»",
        "delete-confirm": "حذف ''$1''",
        "delete-legend": "حذف",
index 77e9851..a40a8ff 100644 (file)
        "diff-paragraph-moved-toold": "Đoạn văn được chuyển từ vị trí khác. Nhấn chuột để nhảy tới vị trí cũ.",
        "difference-missing-revision": "Không tìm thấy {{PLURAL:$2|một phiên bản|$2 phiên bản}} trong khác biệt này ($1).\n\nLỗi này thường xuất hiện đối khi theo dõi liên kết lỗi thời đến khác biệt giữa các bản của trang đã bị xóa.\nXem chi tiết trong [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} nhật trình xóa].",
        "searchresults": "Kết quả tìm kiếm",
+       "search-filter-title-prefix": "Chỉ có tìm trong các trang có tên bắt đầu với “$1”",
+       "search-filter-title-prefix-reset": "Tìm kiếm tất cả các trang",
        "searchresults-title": "Kết quả tìm kiếm “$1”",
        "titlematches": "Đề mục tương tự",
        "textmatches": "Câu chữ tương tự",
        "prefs-editor": "Trình soạn",
        "prefs-preview": "Xem trước",
        "prefs-advancedrc": "Tùy chọn nâng cao",
+       "prefs-opt-out": "Quyết định không sử dụng các cải thiện",
        "prefs-advancedrendering": "Tùy chọn nâng cao",
        "prefs-advancedsearchoptions": "Tùy chọn nâng cao",
        "prefs-advancedwatchlist": "Tùy chọn nâng cao",
        "rcfilters-activefilters": "Bộ lọc hiện hành",
        "rcfilters-activefilters-hide": "Ẩn",
        "rcfilters-activefilters-show": "Hiện",
+       "rcfilters-activefilters-hide-tooltip": "Ẩn phần Bộ lọc kích hoạt",
+       "rcfilters-activefilters-show-tooltip": "Hiện phần Bộ lọc kích hoạt",
        "rcfilters-advancedfilters": "Bộ lọc nâng cao",
        "rcfilters-limit-title": "Số kết quả hiển thị",
        "rcfilters-limit-and-date-label": "$1 thay đổi, $2",
        "rcfilters-savedqueries-apply-and-setdefault-label": "Tạo bộ lọc mặc định",
        "rcfilters-savedqueries-cancel-label": "Hủy bỏ",
        "rcfilters-savedqueries-add-new-title": "Lưu thiết lập bộ lọc hiện tại",
+       "rcfilters-savedqueries-already-saved": "Đã lưu các bộ lọc này rồi. Thay đổi tùy chọn để tạo một Bộ lọc đã lưu mới.",
        "rcfilters-restore-default-filters": "Mặc định lại các bộ lọc",
        "rcfilters-clear-all-filters": "Xóa sạch các bộ lọc",
        "rcfilters-show-new-changes": "Xem các thay đổi mới nhất",
        "rcfilters-watchlist-edit-watchlist-button": "Sửa danh sách trang theo dõi",
        "rcfilters-watchlist-showupdated": "Thay đổi mới trên các trang kể lần cuối bạn xem trang được in <strong>đậm</strong> và có dấu tô màu.",
        "rcfilters-preference-label": "Ẩn phiên bản cải tiến của trang Thay đổi Gần đây",
+       "rcfilters-preference-help": "Hủy bỏ thiết kế lại giao diện năm 2017 và tất cả các công cụ được thêm từ lúc đó trở về nay.",
+       "rcfilters-watchlist-preference-label": "Ẩn danh sách theo dõi cải thiện",
+       "rcfilters-watchlist-preference-help": "Hủy bỏ thiết kế lại giao diện năm 2017 và tất cả các công cụ được thêm từ lúc đó trở về nay.",
        "rcfilters-filter-showlinkedfrom-label": "Xem các thay đổi tại trang có liên kết từ",
        "rcfilters-filter-showlinkedfrom-option-label": "<strong>Trang có liên kết từ</strong> trang được chọn",
        "rcfilters-filter-showlinkedto-label": "Xem các thay đổi tại trang có liên kết đến",
        "uploadbtn": "Tải tập tin lên",
        "reuploaddesc": "Hủy tác vụ tải và quay lại mẫu tải tập tin lên",
        "upload-tryagain": "Lưu miêu tả tập tin được sửa đổi",
+       "upload-tryagain-nostash": "Gửi lại tập tin được tải lên lại và miêu tả được sửa đổi",
        "uploadnologin": "Chưa đăng nhập",
        "uploadnologintext": "Bạn phải $1 để tải tập tin lên.",
        "upload_directory_missing": "Thư mục tải lên ($1) không có hoặc máy chủ web không thể tạo được.",
        "uploadstash-bad-path-invalid": "Đường dẫn không hợp lệ.",
        "uploadstash-bad-path-unknown-type": "Loại không xác định \"$1\".",
        "uploadstash-bad-path-unrecognized-thumb-name": "Tên hình thu nhỏ không nhận dạng được.",
+       "uploadstash-bad-path-no-handler": "Không tìm thấy trình xử lý kiểu MIME $1 của tập tin $2.",
        "uploadstash-bad-path-bad-format": "Chìa khóa “$1” không tuân theo định dạng.",
        "uploadstash-file-not-found-no-thumb": "Không thể tải hình thu nhỏ.",
+       "uploadstash-file-not-found-no-local-path": "Không tìm thấy đường dẫn trên máy cho hình nhỏ.",
        "uploadstash-file-not-found-no-object": "Không tạo được đối tượng tập tin cục bộ cho hình thu nhỏ.",
        "uploadstash-file-not-found-no-remote-thumb": "Nạp hình thu nhỏ thất bại: $1\nURL = $2",
        "uploadstash-file-not-found-missing-content-type": "Thiếu đầu đề kiểu-nội-dung (content-type).",
+       "uploadstash-file-not-found-not-exists": "Không tìm thấy đường dẫn hoặc không phải là tập tin thuần túy.",
+       "uploadstash-file-too-large": "Không thể phục vụ tập tin lớn hơn $1 byte.",
        "uploadstash-not-logged-in": "Người dùng chưa đăng nhập, các tập tin phải do người dùng đã đăng nhập tải lên.",
+       "uploadstash-wrong-owner": "Tập tin này ($1) không phải do người dùng hiện tại tải lên.",
        "uploadstash-no-such-key": "Khóa không tồn tại ($1), không thể xóa.",
+       "uploadstash-no-extension": "Phần mở rộng không có giá trị.",
        "uploadstash-zero-length": "Tập tin có dung lượng bằng không.",
        "invalid-chunk-offset": "Khúc lệch (chunk offset) không hợp lệ",
        "img-auth-accessdenied": "Không cho phép truy cập",
        "http-timed-out": "Hết thời gian yêu cầu HTTP.",
        "http-curl-error": "Có lỗi khi truy xuất URL: $1",
        "http-bad-status": "Có vấn đề khi yêu cầu HTTP: $1 $2",
+       "http-internal-error": "Lỗi nội bộ HTTP.",
        "upload-curl-error6": "Không thể truy cập URL",
        "upload-curl-error6-text": "Không thể truy cập URL mà bạn đưa vào. Xin hãy kiểm tra xem URL có đúng không và website vẫn còn hoạt động.",
        "upload-curl-error28": "Quá thời gian tải lên cho phép",
        "speciallogtitlelabel": "Mục tiêu (tiêu đề hoặc {{ns:user}}:Tên-người-dùng đối với người dùng):",
        "log": "Nhật trình",
        "logeventslist-submit": "Xem",
+       "logeventslist-more-filters": "Hiện thêm nhật trình:",
+       "logeventslist-patrol-log": "Nhật trình tuần tra",
+       "logeventslist-tag-log": "Nhật trình thẻ đánh dấu",
        "all-logs-page": "Tất cả các nhật trình công khai",
        "alllogstext": "Hiển thị tất cả các nhật trình đang có của {{SITENAME}} chung với nhau.\nBạn có thể thu hẹp kết quả bằng cách chọn loại nhật trình, tên thành viên (phân biệt chữ hoa-chữ thường), hoặc các trang bị ảnh hưởng (cũng phân biệt chữ hoa-chữ thường).",
        "logempty": "Không có mục nào khớp với từ khóa.",
        "uctop": "(hiện tại)",
        "month": "Từ tháng (trở về trước):",
        "year": "Từ năm (trở về trước):",
+       "date": "Từ ngày (trở về trước):",
        "sp-contributions-newbies": "Chỉ hiển thị đóng góp của tài khoản mới",
        "sp-contributions-newbies-sub": "Các thành viên mới",
        "sp-contributions-newbies-title": "Đóng góp của các thành viên mới",
        "sp-contributions-newonly": "Chỉ hiện các sửa đổi tạo trang",
        "sp-contributions-hideminor": "Ẩn các sửa đổi nhỏ",
        "sp-contributions-submit": "Tìm kiếm",
+       "sp-contributions-outofrange": "Không thể hiển thị kết quả cho dải địa chỉ IP quá giới hạn CIDR là /$1.",
        "whatlinkshere": "Các liên kết đến đây",
        "whatlinkshere-title": "Các trang liên kết đến “$1”",
        "whatlinkshere-page": "Trang:",
        "ipb_blocked_as_range": "Lỗi: Địa chỉ IP $1 không bị cấm trực tiếp và do đó không thể bỏ cấm. Tuy nhiên, nó bị cấm do là một bộ phận của dải IP $2, bạn có thể bỏ cấm dải này.",
        "ip_range_invalid": "Dải IP không hợp lệ.",
        "ip_range_toolarge": "Không được phép cấm dải IP lớn hơn /$1.",
+       "ip_range_exceeded": "Dải địa chỉ IP này vượt quá dải tối đa. Dải được cho phép: /$1.",
+       "ip_range_toolow": "Không cho phép dải địa chỉ IP trên thực tế.",
        "proxyblocker": "Cấm proxy",
        "proxyblockreason": "Địa chỉ IP của bạn đã bị cấm vì là proxy mở. Xin hãy liên hệ nhà cung cấp dịch vụ Internet hoặc bộ phận hỗ trợ kỹ thuật của bạn và thông báo với họ về vấn đề an ninh nghiêm trọng này.",
        "sorbsreason": "Địa chỉ IP của bạn bị liệt kê là một proxy mở trong DNSBL mà {{SITENAME}} đang sử dụng.",
index 83c33d3..9cf7059 100644 (file)
        "previewerrortext": "預覽你嘅修改嗰陣出錯。",
        "blockedtitle": "用戶已經封鎖",
        "blockedtext": "<strong>你嘅用戶名或者IP地址已經俾人封咗。</strong>\n\n呢次封鎖係由$1所封嘅。\n俾咗嘅原因係<em>$2</em>。\n\n* 呢次封鎖嘅開始時間係:$8\n* 呢次封鎖嘅到期時間係:$6\n* 被封鎖者:$7\n\n你可以聯絡 $1 或者其他嘅[[{{MediaWiki:Grouppage-sysop}}|管理員]]討論呢次封鎖。\n除非你已經響你嘅[[Special:Preferences|戶口喜好設定]]入面設定咗有效嘅電郵地址,否則你係唔可以用「{{int:emailuser}}」嘅功能。當設定咗一個有效嘅電郵地址之後,呢個功能係唔會被封鎖嘅。\n你現時嘅IP地址係 $3 ,而個封鎖ID係 #$5。\n請喺你嘅查詢都註明以上封鎖嘅資料。",
-       "autoblockedtext": "你嘅IP地址已經被自動封鎖,由於之前嘅另一位用戶係畀$1封咗。\n而封鎖嘅原因係:\n\n:''$2''\n\n* 呢次封鎖嘅開始時間係:$8\n* 呢次封鎖嘅到期時間係:$6\n* 對於被封鎖者:$7\n\n你可以聯絡 $1 或者其他嘅[[{{MediaWiki:Grouppage-sysop}}|管理員]],討論呢次封鎖。\n\n除非你已經響你嘅[[Special:Preferences|戶口喜好設定]]入面設定咗有效嘅電郵地址,否則你係唔可以用「電郵呢個用戶」嘅功能。當設定咗一個有效嘅電郵地址之後,呢個功能係唔會封鎖嘅。\n\n你現時用緊嘅 IP 地址係 $3,個封鎖 ID 係 #$5。 請喺你嘅查詢都註明呢個封鎖上面嘅資料。",
+       "autoblockedtext": "你嘅IP地址已經被自動封鎖,原因係之前嘅另一位用戶係畀$1封咗。\n而封鎖嘅原因係:\n\n:''$2''\n\n* 呢次封鎖嘅開始時間係:$8\n* 呢次封鎖嘅到期時間係:$6\n* 對於被封鎖者:$7\n\n你可以聯絡 $1 或者其他嘅[[{{MediaWiki:Grouppage-sysop}}|管理員]],討論呢次封鎖。\n\n除非你已經響你嘅[[Special:Preferences|戶口喜好設定]]入面設定咗有效嘅電郵地址,否則你係唔可以用「電郵呢個用戶」嘅功能。當設定咗一個有效嘅電郵地址之後,呢個功能係唔會封鎖嘅。\n\n你現時用緊嘅 IP 地址係 $3,個封鎖 ID 係 #$5。 請喺你嘅查詢都註明呢個封鎖上面嘅資料。",
        "systemblockedtext": "你嘅用戶名或者IP地址已經俾MediaWiki自動封鎖。\n封鎖原因係:\n\n:<em>$2</em>\n\n* 開始時間:$8\n* 到期時間:$6\n* 目標用戶:$7\n\n你而家嘅IP地址係$3。\n當你作出任何查詢嘅時候,請包含以上所有資料。",
        "blockednoreason": "無原因畀低",
        "whitelistedittext": "你需要$1去編輯呢頁。",
        "rcfilters-other-review-tools": "第啲檢閱架撐",
        "rcfilters-group-results-by-page": "將相同頁面嘅結果夾埋",
        "rcfilters-activefilters": "用緊嘅篩選條件",
+       "rcfilters-activefilters-hide": "隱藏",
+       "rcfilters-activefilters-show": "顯示",
        "rcfilters-advancedfilters": "進階嘅篩選條件",
        "rcfilters-limit-title": "顯示幾多結果",
        "rcfilters-limit-and-date-label": "$1次{{PLURAL:$1|改動}},$2",
        "rcfilters-filter-humans-description": "真人做嘅編輯",
        "rcfilters-filtergroup-reviewstatus": "巡查狀態",
        "rcfilters-filter-reviewstatus-unpatrolled-label": "未巡查",
+       "rcfilters-filter-reviewstatus-auto-label": "已經自動巡查咗",
        "rcfilters-filtergroup-significance": "顯著度",
        "rcfilters-filter-minor-label": "細編輯",
        "rcfilters-filter-minor-description": "作者話程度細嘅修改。",
index 601e427..9cc2440 100644 (file)
        },
        "tog-underline": "ⵉⵣⵔⵉⵔⴳ ⴷⴷⵓ ⵓⵙⵖⵏ",
        "tog-hideminor": "ⵙⵙⵏⵜⵍ ⵜⵉⵙⵏⴼⵉⵍⵉⵏ ⵜⵉⵎⵥⵥⴰⵏⵉⵏ ⵙⴳ ⵉⵙⵏⴼⵍⵏ ⵉⵏⴳⴳⵓⵔⴰ",
+       "tog-hidepatrolled": "ⵙⵙⵏⵜⵍ ⵉⵙⵏⵉⴼⵉⵍⵏ ⵎⵉ ⵉⵜⵜⵓⴳⴰ ⵓⵣⵣⵔⴰⵢ ⴳ ⵉⵙⵏⵉⴼⵉⵍⵏ ⵉⵎⴳⴳⵓⵔⴰ",
+       "tog-newpageshidepatrolled": "ⵙⵙⵏⵜⵍ ⵜⴰⵙⵏⵉⵡⵉⵏ ⵎⵉ ⵉⵜⵜⵓⴳⴰ ⵓⵣⵣⵔⴰⵢ ⴳ ⵜⵍⴳⴰⵎⵜ ⵏ ⵜⴰⵙⵏⵉⵡⵉⵏ ⵜⵉⵎⴰⵢⵏⵓⵜⵉⵏ",
        "tog-hidecategorization": "ⵙⵙⵏⵜⵍ ⴰⵙⵎⵉⵍ ⵏ ⵜⴰⵙⵏⵉⵡⵉⵏ",
+       "tog-extendwatchlist": "ⵙⵙⵉⵔⵉⵡ ⵜⴰⵍⴳⴰⵎⵜ ⵏ ⵓⴹⴼⴼⵓⵔ ⴰⴼⴰⴷ ⴰⴷ ⵜⵙⴽⵏⴷ ⵉⵙⵏⵉⴼⵉⵍⵏ ⴰⴽⴽ ⵓⵔ ⴷ ⵖⴰⵙ ⵉⵎⴳⴳⵓⵔⴰ",
+       "tog-usenewrc": "ⵙⵎⵓⵏ ⵉⵙⵏⴼⵍⵏ ⴷ ⵜⴰⵙⵏⴰ ⵏⵙⵏ ⴳ ⵉⵙⵏⴼⵍⵏ ⵉⵎⴳⴳⵓⵔⴰ ⴷ ⵜⴰⵍⴳⴰⵎⵜ ⵏ ⵓⴹⴼⵓⵔ",
+       "tog-numberheadings": "ⴰⵙⵓⵟⵟⵏ ⴰⵡⵓⵔⵎⴰⵏ ⵏ ⵉⵣⵡⵍⴰⵏ",
+       "tog-showtoolbar": "ⵙⴽⵏ ⵜⴰⵙⵙⴼⵉⴼⵜ ⵏ ⵉⵎⴰⵙⵙⵏ ⵏ ⵓⵥⵔⴰⴳ",
+       "tog-editondblclick": "ⵙⵏⴼⵍ ⵜⴰⵙⵏⵉⵡⵉⵏ ⵙ ⵙⵉⵏ ⵉⴽⵍⵉⴽⵉⵜⵏ",
+       "tog-editsectiononrightclick": "ⵙⵙⵔⵎⴷ ⴰⵙⵏⴼⵍ ⵏ ⵜⵣⵓⵏⵉⵡⵉⵏ ⵙ ⵢⴰⵏ ⵓⴽⵍⵉⴽⵉ ⵖⴼ ⵉⵣⵡⵍⴰⵏ ⵏ ⵜⵣⵓⵏⵉ",
+       "tog-watchcreations": "ⵔⵏⵓ ⵖⵔ ⵜⴳⵍⴰⵎⵜ ⵉⵏⵓ ⵏ ⵓⴹⴼⴼⵓⵔ ⵜⴰⵙⵏⵉⵡⵉⵏ ⵏⵏⴰ ⵙⴽⵔⵖ ⴷ ⵉⴼⵓⵢⵍⴰ ⵏⵏⴰ ⴷ ⵙⴽⵛⵎⵖ",
+       "tog-watchdefault": "ⵔⵏⵓ ⵉ ⵜⵍⴳⴰⵎⵜ ⵉⵏⵓ ⵏ ⵓⴹⴼⴼⵓⵔ ⵜⴰⵙⵏⵉⵡⵉⵏ ⴷ ⵉⴼⵓⵢⵍⴰ ⵏⵏⴰ ⵙⵏⴼⴰⵍⵖ",
+       "tog-watchmoves": "ⵔⵏⵓ ⵜⴰⵙⵏⵉⵡⵉⵏ ⴷ ⵉⴼⵓⵢⵍⴰ ⵏⵏⴰ ⵙⵎⵓⵜⵜⵉⵖ ⵉ ⵜⵍⴳⴰⵎⵜ ⵉⵏⵓ ⵏ ⵓⴹⴼⴼⵓⵔ",
+       "tog-watchdeletion": "ⵔⵏⵓ ⵉ ⵜⴰⵙⵏⴰ ⵉⵏⵓ ⵏ ⵓⴹⴼⴼⵓⵔ ⵜⴰⵙⵏⵉⵡⵉⵏ ⴷ ⵉⴼⵓⵢⵍⴰ ⵏⵏⴰ ⴽⴽⵙⵖ",
+       "tog-watchuploads": "ⵔⵏⵓ ⵉⴼⵓⵢⵍⴰ ⵉⵎⴰⵢⵏⵓⵜⵏ ⵏⵏⴰ ⴷ ⵙⴽⵛⵎⵖ ⵉ ⵜⵍⴳⴰⵎⵜ ⵉⵏⵓ ⵏ ⵓⴹⴼⴼⵓⵔ",
+       "tog-watchrollback": "ⵔⵏⵓ ⵜⴰⵙⵏⵉⵡⵉⵏ ⵍⵍⵉ ⵅⴼ ⵙⴽⵔⵖ ⴰⵡⵔⵔⵉ ⵉ ⵜⵍⴳⴰⵎⵜ ⵏ ⵓⴹⴼⴼⵓⵔ ⵉⵏⵓ",
        "tog-minordefault": "ⵕⵛⵎ ⵎⴰⵕⵕⴰ ⵉⵙⵏⴼⵍⵏ ⵎⵥⵥⵉⵢⵏⵉⵏ ⵙ ⵓⵡⵏⵓⵍ",
+       "tog-previewontop": "ⵙⴽⵏ ⴰⴱⵔⵉⴼⵢⵓ ⵉⴳⴳⵉ ⵏ ⵓⴷⵖⴰⵔ ⵏ ⵓⵙⵏⴼⵍ",
+       "tog-previewonfirst": "ⵙⴽⵏ ⴰⴱⵔⵉⴱⵢⵓ ⴳ ⵓⵙⵏⴼⵍ ⵉⵣⵡⴰⵔⵏ",
+       "tog-enotifwatchlistpages": "ⴰⵣⵏ ⵢⵉ ⵏⵏ ⴳ ⵉⵎⴰⵢⵍ ⴰⴽⵓⴷ ⵏⵏⴰ ⵜⵡⴰⵙⵙⵏⴼⵍ ⴽⵔⴰ ⵏ ⵜⴰⵙⵏⴰ ⵏⵖ ⴰⴼⴰⵢⵍⵓ ⵉⵍⵍⴰⵏ ⴳ ⵜⵍⴳⴰⵎⵜ ⵏ ⵓⴹⴼⴼⵓⵔ ⵉⵏⵓ",
+       "tog-enotifusertalkpages": "ⴰⵣⵏ ⵢⵢⵉ ⵏⵏ ⵉⵎⴰⵢⵍ ⴰⴽⵓⴷ ⵏⵏⴰ ⵜⵡⴰⵙⵙⵏⴼⵍ ⵜⴰⵙⵏⴰ ⵏ ⵓⵎⵙⴰⵡⴰⵍ ⵉⵏⵓ",
+       "tog-enotifminoredits": "ⴰⵣⵏ ⵢⵢⵉ ⵏⵏ ⴰⵍⵜⵓ ⵉⵎⴰⵢⵍ ⵅⴼ ⵉⵙⵏⴼⵍⵏ ⵉⵎⵣⵢⴰⵏⵏ ⵏ ⵜⴰⵙⵏⵉⵡⵉⵏ ⴷ ⵉⴼⴰⵢⵍⵓⵜⵏ",
+       "tog-watchlisthideown": "ⵙⵙⵏⵜⵍ ⵉⵙⵏⵉⴼⵉⵍⵏ ⵉⵏⵓ ⵙⴳ ⵜⵍⴳⴰⵎⵜ ⵏ ⵓⴹⴼⴼⵓⵔ",
+       "tog-watchlisthidebots": "ⵙⵙⵏⵜⵍ ⵉⵙⵏⵉⴼⵉⵍⵏ ⵏ ⵉⵔⵓⴱⵓⵜⵏ ⵙⴳ ⵜⵍⴳⴰⵎⵜ ⵏ ⵓⴹⴼⴼⵓⵔ",
+       "tog-watchlisthideminor": "ⵙⵙⵏⵜⵍ ⵉⵙⵏⵉⴼⵉⵍⵏ ⵉⵎⵥⵢⴰⵏⴻⵏ ⵙⴳ ⵜⵍⴳⴰⵎⵜ ⵏ ⵓⴹⴼⴼⵓⵔ",
        "tog-ccmeonemails": "ⴰⵣⵏ ⵉⵢⵉ ⴷ ⵜⵓⵏⵖⵉⵍⵉⵏ ⵏ ⵉⵎⴰⵢⵍⵏ ⵏⵏⴰ ⵓⵣⵏⵖ ⵉ ⵉⵎⵙⵙⵎⵔⵙⵏ ⵢⴰⴹⵏ",
        "tog-diffonly": "ⴰⴷ ⵓⵔ ⵜⵙⵙⴽⴷ ⵜⵓⵎⴰⵢⵜ ⵏ ⵜⴰⵙⵏⴰ ⴷⴷⵓ ⵉⵎⵣⴰⵔⴰⵢⵏ",
        "tog-showhiddencats": "ⵙⴽⵏ ⵜⴰⴳⴳⴰⵢⵉⵏ ⵉⵜⵜⵓⵃⴹⴰⵏ",
+       "underline-always": "ⴽⵓ ⴰⵙⵙ",
        "underline-never": "ⵓⵙⴰⵔ",
        "sunday": "ⴰⵙⴰⵎⴰⵙ",
        "monday": "ⴰⵢⵏⴰⵙ",
@@ -35,7 +58,7 @@
        "sat": "ⴰⵙⴹ",
        "january": "ⵉⵏⵏⴰⵢⵔ",
        "february": "ⴱⵕⴰⵢⵕ",
-       "march": "âµ\8eâ´°âµ\95âµ\9a",
+       "march": "âµ\8eâ´°âµ\94âµ\99",
        "april": "ⵉⴱⵔⵉⵔ",
        "may_long": "ⵎⴰⵢⵢⵓ",
        "june": "ⵢⵓⵏⵢⵓ",
@@ -59,7 +82,7 @@
        "december-gen": "ⴷⵓⵊⴰⵏⴱⵉⵔ",
        "jan": "ⵉⵏⵏ",
        "feb": "ⴱⵕⴰ",
-       "mar": "âµ\8eâ´°âµ\95",
+       "mar": "âµ\8eâ´°âµ\94",
        "apr": "ⴱⵕⴰ",
        "may": "ⵎⴰⵢ",
        "jun": "ⵢⵓⵏ",
        "navigation": "ⴰⵙⵜⴰⵔⴰ",
        "and": "&#32;ⴷ",
        "faq": "FAQ",
+       "actions": "ⵉⴳⵉⵜⵏ",
        "namespaces": "ⵜⵉⵔⵉⵡⵉⵏ ⵏ ⵉⵙⵎⴰⵡⵏ",
        "variants": "ⵜⵉⵎⵣⴰⵔⴰⵢⵉⵏ",
        "navigation-heading": "ⵓⵎⵓⵖ ⵏ ⵓⵙⵙⴰⵔⴰ",
        "nosuchspecialpage": "ⴰⵡⴷ ⵢⴰⵜ ⵜⴰⵙⵏⴰ ⵉⵥⵍⵉⵏ.",
        "nospecialpagetext": "<strong>ⵜⴻⵜⵜⵔⴷ ⵢⴰⵜ ⵜⴰⵙⵏⴰ ⵉⵥⵍⵉⵏ ⵓⵔ ⵉⴱⵓⵏⵉⵏ</strong>\n\nⵢⴰⵜ ⵜⵍⴳⴰⵎⵜ ⵏ ⵜⴰⵙⵏⵉⵡⵉⵏ ⵉⵥⵍⵉⵏ ⵔⴰⴷ ⵜⵜ ⵜⴰⴼⴷ ⴳ [[Special:SpecialPages|{{int:specialpages}}]].",
        "error": "ⵜⴰⵣⴳⵍⵜ",
+       "databaseerror-function": "ⵜⴰⵙⵖⵏⵜ: $1",
        "databaseerror-error": "ⵜⴰⵣⴳⵍⵜ: $1",
        "badtitle": "ⴳⴰⵔ ⴰⵣⵡⵍ",
        "badtitletext": "ⴰⵣⵡⵍ ⵏ ⵜⴰⵙⵏⴰ ⵉⵜⵜⵡⴰⵜⵜⴰⵔⵏ ⵢⴰ ⵓⵔ ⵉⵏⵉⵎ ⵢⴰ ⵢⵓⵔⴰ ⵢⴰ ⵓⵔ ⵢⵉⵍⵉⵖ ⵎⵍⵉⵃ ⵎⴽ ⵉⴳⴰ ⵢⴰⵏ ⵓⵣⵡⵍ ⵉⵙⵎⴰⵏⴻⵏ ⵙⵏⴰⵜ ⵜⵓⵜⵍⴰⵢⵉⵏ ⵏⵖ ⵙⵉⵏ ⵢⵉⵙⵏⴼⴰⵔⵏ. ⵄⵏⵉⵖ ⴷⵉⴳⵙ ⵢⴰⵏ ⵏⵖ ⵎⵏⵏⴰⵡ ⵏ ⵢⵉⵙⴽⴽⵉⵍⵏ ⵙ ⵓⵔ ⵉⵙⵙⵉⵏ ⴰⴷ ⵜⵜⵓⵙⵎⵔⵙⵏ ⴳ ⵢⵉⵣⵡⵍⴰⵏ.",
        "noarticletext": "ⵓⵔ ⵉⵍⵍⵉ ⴽⵔⴰ ⵏ ⵓⴹⵔⵉⵙ ⴳ ⵜⴰⵙⵏⴰ ⴰⴷ ⵖⵉⵍⴰ. \nⵜⵣⵎⵔⴷ ⴰⴷ [[Special:Search/{{PAGENAME}}|ⵜⵔⵣⵓⴷ ⵖⴼ ⵓⵣⵡⵍ ⵏⵏⵙ]] ⴳ ⵜⴰⵙⵏⵉⵡⵉⵏ ⵢⴰⴹⵏ, <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} ⵔⵣⵓ ⵖⴼ logs ⵖⵔⵙ ⵉⵇⵇⵏⴻⵏ],\nⵏⵖ [{{fullurl:{{FULLPAGENAME}}|action=edit}} ⵙⵏⵓⵍⴼⵓ ⵜⴰⵙⵏⴰ]</span>.",
        "noarticletext-nopermission": "ⴷⵖⵉ ⵓⵔ ⵉⵍⵍⵉ ⴰⵡⴷ ⴽⵔⴰ ⵏ ⵓⴹⵔⵉⵚ ⴳ ⵜⴰⵙⵏⴰ ⴰ.\nⵜⵣⵎⵔⴷ ⴰⴷ [[Special:Search/{{PAGENAME}}|ⵜⵔⵣⵓⴷ ⵖⴼ ⵓⵣⵡⵍ ⵏⵏⵙ]] ⴳ ⵜⴰⵙⵏⵉⵡⵉⵏ ⵢⴰⴹⵏⵉⵏ, ⵏⵖ <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} ⵔⵣⵓ ⵖⴼ ⵉⵣⵎⵎⴻⵎⵏ ⵉⵣⴷⵉⵏ]</span>, ⵎⴰⵛⴰ ⵓⵔ ⴷⴰⵔⴽ ⵜⵓⵔⴰⴳⵜ ⴰⴷ ⵜⵙⵏⵓⵍⴼⵓⴷ ⵜⴰⵙⵏⴰ ⴰ.",
        "userpage-userdoesnotexist-view": "ⴰⵎⵉⴹⴰⵏ ⵏ ⵓⵎⵙⵙⵎⵔⵙ $1 ⵓⵔ ⵉⵜⵜⵓⵣⵎⵎⴻⵎ.",
+       "clearyourcache": "<strong>ⵜⴰⵎⴰⵡⵜ :</strong> ⴷⴼⴼⵉⵔ ⴰⴷ ⵜⵙⴽⵍⵙⵍ ⵉⵙⵏⵉⴼⵉⵍⵏ ⵏⵏⴽ (ⵎ), ⵄⵏⵉⵖ ⵉⵇⵇⴰⵏ ⴷ ⴰⴷ ⵜⵙⵙⴽⵜⵔⴷ ⵜⵉⵏⵏⵓⵜⵍⴰ ⵏ ⵓⵎⵙⵜⴰⵔⴰ ⴰⴼⴰⴷ ⴰ ⵜⵥⵔⴷ ⵉⵙⵏⵉⴼⵉⵍⵏ.\n* <strong>ⴼⴰⵔⴼⵓⴽⵙ / ⵙⴰⴼⴰⵔⵉ :</strong> ⴰⵊⵊ ⴰⴹⴰⴹ ⵖⴼ ⵜⵙⴰⵔⵓⵜ  <em>Maj</em> (<em>Shift</em>) ⵜⴰⴷⵔⴷ ⵉ ⵜⵙⴰⵔⵓⵜ <em>Actualiser</em> ⵏⵖ ⴰⴷⴷ ⵉ <em>Ctrl-F5</em> ⵏⵖ <em>Ctrl-R</em> (<em>⌘-R</em> ⵖⴼ Mac) \n* <strong>ⴳⵓⴳⵍ ⴽⵔⵓⵎ :</strong> ⴰⴷⴷ ⵉ <em>Ctrl-Maj-R</em> (<em>⌘-Shift-R</em> ⴳ Mac) \n* <strong>ⴰⵏⵜⵉⵔⵏⵉⵜ ⵉⴽⵙⴱⵍⵓⵔⴻⵔ :</strong> ⴰⵊⵊ ⴰⴹⴰⴹ ⵖⴼ ⵜⵙⴰⵔⵓⵜ <em>Ctrl</em> ⵜⴰⴽⵍⴷ ⵜⵙⴰⵔⵓⵜ <em>Actualiser</em> ⵏⵖ ⵜⵓⴽⵍⴷ <em>Ctrl-F5</em> \n* <strong>ⵓⴱⵉⵔⴰ :</strong> ⴷⴷⵓ ⵖⵔ <em>ⵓⵎⵓⵖ → ⵜⵉⵙⵖⴰⵍ</em> (<em>ⵓⴱⵉⵔⴰ → ⵉⵙⵏⵢⵉⴼⵏ</em> ⵖⴼ Mac) ⵙⴰⴽⵉⵏ <em>ⵜⵓⵥⵍⵉⵢⵉⵏ ⴷ ⵜⵖⵍⵙⵜ → ⵙⴼⴹ ⵉⵙⴼⴽⴰ ⵏ ⵓⵙⴼⵓⴽⴽⵔ → ⵜⵓⴳⵏⵉⵡⵉⵏ ⴷ ⵉⴼⵓⵢⵍⴰ ⵉⵍⵍⴰⵏ ⴳ ⵜⵉⵏⵏⵓⵜⵍⴰ</em>.",
        "previewnote": "<strong>ⴽⵜⵉ ⴷ ⵎⴰⵙ ⵓⵔ ⵜⴳⵉ ⵖⴰⵙ ⴰⵣⵔⵙⴽⴰⵏ.</strong>\nⵉⵙⵏⵉⴼⵉⵍⵏ ⵏⵏⴽ ⵓⵔ ⵜⴰ ⵜⵜⵓⵙⴽⵍⴰⵙⵏ!",
        "continue-editing": "ⴷⴷⵓ ⵙ ⴰⵏⵙⴰ ⵏ ⵓⵙⵏⴼⵍ",
        "editing": "ⴰⵙⵏⴼⵍ ⵏ $1",
        "hiddencategories": "ⵜⴰⵙⵏⴰ ⴰ ⴷ ⴰⴳⵎⴰⵎ ⵏ {{PLURAL:$1|1 ⵏⵜⵍ ⴰⵙⵎⵉⵍ|$1 ⵏⵜⵍ ⵉⵙⵎⵉⵍⵏ}}:",
        "permissionserrors": "ⴰⵙⵙⵓⵔⴳ ⵉⵣⴳⵜ",
        "permissionserrorstext-withaction": "ⵓⵔ ⴷⴰⵔⴽ ⵜⵓⵔⴰⴳⵜ ⴰⴼⴰⴷ ⴰⴷ $2, ⵙ {{PLURAL:$1|reason|reasons}}:",
+       "recreate-moveddeleted-warn": "<strong>ⴰⵡⵢ ⵉⵜⵏⴰⵏ : ⵀⴰⵜ ⴰⵔ ⵜⵙⴽⴰⵔⴷ ⵢⴰⵜ ⵜⴰⵙⵏⴰ ⵏⵏⴰ ⵜⵜ ⵏⵏ ⵉⴽⴽⴰⵏ ⵜⴻⵜⵜⵡⴰⴽⴽⵙ.</strong>\n\nⵏⵥⵉ ⴳ ⵉⵙ ⵉⵙⵙⵏ ⴰⴷ ⵜⵙⵎⴷⴻⴷ ⴳ ⵉⵙⵏⵉⴼⵉⵍⵏ ⵖⴼ ⵜⴰⵙⵏⴰ ⴰⴷ. \nⵜⵉⵎⵙⵙⴽⵜⵉⵜⵉⵏ ⵏ ⵜⵓⴽⴽⵙⵉⵡⵉⵏ ⴷ ⵉⵙⵎⵓⵜⵜⵓⵢⵏ ⵏ ⵜⴰⵙⵏⴰ ⴰⴷ, ⵍⵍⴰⵏⵜ ⴷⴰ ⵉ ⵓⵙⵏⵖⵎⵙ :",
        "moveddeleted-notice": "ⵜⴰⵙⵏⴰ ⴰⴷ ⵜⴻⵜⵜⵡⴰⴽⴽⵙ.\nⵜⵉⵎⵙⵙⴽⵜⵉⵜⵉⵏ ⵏ ⵜⵓⴽⴽⵙⵉⵡⵉⵏ, ⵉⴼⵔⴰⴳⵏ ⴷ ⵉⵙⵎⵓⵜⵜⵓⵢⵏ ⵉⵜⵜⵓⴳⴰⵏ ⵉ ⵜⴰⵙⵏⴰ ⵜⵜⵓⵙⴽⴰⵏⴻⵏ ⴳ ⵢⵉⵣⴷⴰⵔ ⴰⵎ ⵉⵙⴰⵖⵓⵍⵏ.",
        "content-model-wikitext": "wikitext",
        "content-model-javascript": "JavaScript",
        "cur": "ⵎⵔⵏ",
        "last": "ⵓⵣⵡⵔ",
        "page_last": "ⴰⵎⴳⴳⴰⵔⵓ",
+       "histlegend": "ⴰⵙⵜⴰⵢ ⵏ ⵓⵎⵣⴰⵔⴰⵢ : ⵔⵛⵎ ⵜⴰⵏⴽⵓⵍⵉⵏ ⵏ ⵜⵓⵏⵖⵉⵍⵉⵏ ⵏⵏⴰ ⵜⵔⵉⴷ ⴰⴷ ⵜⵙⵎⵣⴰⵣⴰⵍⴷ ⵜⴰⴷⵔⴷ ⵉ ⴰⴽⵎ ⵏⵖ ⵉ ⵜⵙⴰⵔⵓⵜ ⵏ ⵢⵉⵣⴷⴰⵔ.<br />\nⵜⴰⴱⴰⴷⵓⵜ : <strong>({{int:cur}})</strong> = ⴰⵎⵣⴰⵔⴰⵢ ⴰⴽⴷ ⵜⵓⵏⵖⵉⵍⵜ ⵜⴰⵎⴳⴳⴰⵔⵓⵜ, <strong>({{int:last}})</strong> = ⴰⵎⵣⴰⵔⴰⵢ ⴰⴽⴷ ⵜⵓⵏⵖⵉⵍⵜ ⵉⵣⵔⵉⵏ, <strong>{{int:minoreditletter}}</strong> = ⵉⵙⵏⵉⴼⵉⵍⵏ ⵉⵎⵥⵥⵉⵢⵏ.",
        "history-fieldset-title": "ⵔⵣⵓ ⵖⴼ ⵉⵣⵣⵔⴰⵢⵏ",
        "histfirst": "ⴰⵇⴱⵓⵔ",
        "histlast": "ⴰⵎⴰⵢⵏⵓ",
        "editundo": "ⵙⵔ",
        "diff-empty": "(ⵓⵔ ⵉⵍⵍⵉ ⵓⵎⵣⴰⵔⴰⵢ)",
        "diff-multi-sameuser": "({{PLURAL:$1|ⴽⵔⴰ ⵏ ⵓⵣⵣⵔⴰⵢ ⵉⵏⵏⵓⵎⵎⵙⵏ ⵉⵙⴽⵔ ⵓⵏⵙⵙⵎⵔⵙ ⵉⴳⴰⵏ ⵢⴰⵏ ⵓⵔ ⵜⴻⵜⵜⵓⵙⴽⴰⵏ|$1 ⴽⵔⴰ ⵏ ⵉⵣⵣⵔⴰⵢⵏ ⵉⵏⵏⵓⵎⵎⵙⵏ ⵉⵙⴽⵔ ⵓⵏⵙⵙⵎⵔⵙ ⵉⴳⴰⵏ ⵢⴰⵏ ⵓⵔ ⵜⵜⵓⵙⴽⴰⵏⴻⵏⵜ}})",
+       "diff-multi-otherusers": "({{PLURAL:$1|ⵢⴰⵏ ⵓⵣⵣⵔⴰⵢ ⴰⵎⵏⵏⵓⵎⵎⵙ|$1 ⵏ ⵉⵣⵣⵔⴰⵢⵏ ⵉⵎⵏⵏⵓⵎⵎⵙⵏ}} ⵏ {{PLURAL:$2|ⵢⴰⵏ ⵓⵏⵙⵙⵎⵔⵙ ⵢⴰⴹⵏ|$2 ⵏ ⵉⵏⵙⵙⵎⵔⵙⵏ}} ⵓⵔ ⵉⵜⵜⵓⵙⴽⴰⵏⴻⵏ).",
        "searchresults": "ⵜⵉⵢⴰⴼⵓⵜⵉⵏ ⵏ ⵓⵔⵣⵣⵓ",
        "searchresults-title": "ⵜⵉⵢⴰⴼⵓⵜⵉⵏ ⵏ ⵓⵔⵣⵣⵓ ⵖⴼ \"$1\"",
        "prevn": "{{PLURAL:$1|$1}} ⵉⵎⵣⵡⵓⵔⴰ",
        "unwatch": "ⴽⴽⵙ ⴰⴹⴼⴼⵓⵔ",
        "watchlist-details": "{{PLURAL:$1|$1 ⵜⴰⵙⵏⴰ|$1 ⵏ ⵜⴰⵙⵏⵉⵡⵉⵏ}} ⴳ ⵜⵍⴳⴰⵎⵜ ⵏ ⵓⴹⴼⴼⵓⵔ ⵏⵏⴽ (ⴰⴽⴷ ⵜⴰⵙⵏⵉⵡⵉⵏ ⵏ ⵓⵎⵙⴰⵡⴰⵍ).",
        "wlheader-showupdated": "ⵜⴰⵙⵏⵉⵡⵉⵏ ⵏⵏⴰ ⵉⵜⵜⵓⵙⵏⴼⵍⵏ ⵙⴳ ⵜⵔⵣⴼⵜ ⵏⵏⴳ ⵜⴰⵎⴳⴳⴰⵔⵓⵜ ⵜⵜⵓⵙⴽⴰⵏⴻⵏⵜ ⵙ  <strong>ⴰⵣⵓⵔⴰⵔ</strong>.",
+       "wlnote": "ⴳ ⵓⴼⵍⵍⴰ, {{PLURAL:$1|ⵢⵓⵎⴰⵏ ⵓⵙⵏⴼⵍ ⴰⵎⴳⴳⴰⵔⵓ ⵉⵜⵜⵡⴰⵙⴽⴰⵔⵏ|ⵓⵎⴰⵏⴻⵏ <strong>$1</strong> ⵏ ⵉⵙⵏⵉⴼⵉⵍⵏ ⵉⵎⴳⴳⵓⵔⴰ ⵉⵜⵜⵢⴰⵙⴽⴰⵔⵏ}} {{PLURAL:$2|ⵢⴰⵜ ⵜⵙⵔⴰⴳⵜ ⴰⵢⴰ|ⵢⴰⵏ <strong>$2</strong> ⵏ ⵜⵙⵔⴰⴳⵉⵏ ⴰⵢⴰ}}, ⴰⵔ $3, $4.",
        "wlshowlast": "ⵙⴽⵏ $1 ⵜⴰⵙⵔⴰⴳⵉⵏ $2 ⵓⵙⵙⴰⵏ ⵉⵎⴳⴳⵓⵔⴰ",
        "watchlist-options": "ⵜⵉⴷⵖⵔⵉⵏ ⵏ ⵜⵍⴳⴰⵎⵜ ⵏ ⵓⴹⴼⴼⵓⵔ",
        "enotif_reset": "ⴷⵔⵣ ⵜⴰⵙⵏⵉⵡⵉⵏ ⴰⴽⴽ ⵏⵏⴰ ⵜⵔⵣⴼⴷ",
        "pageinfo-recent-authors": "ⵓⵟⵟⵓⵏ ⵏ ⵉⵎⴰⵔⴰⵜⵏ ⵉⵎⴳⴳⵓⵔⴰ ⵉⵎⵢⴰⵍⵍⴰⵏ",
        "pageinfo-magic-words": "ⵉⵎⴽⵓⵔⴰⵔⵏ {{PLURAL:$1|ⵜⴰⴳⵓⵔⵉ|ⵜⵉⴳⵓⵔⵉⵡⵉⵏ}} ($1)",
        "pageinfo-hidden-categories": "ⵏⵜⵍ {{PLURAL:$1|ⴰⵙⵎⵉⵍ|ⵉⵙⵎⵉⵍⵏ}}($1)",
+       "pageinfo-templates": "{{PLURAL:$1|ⵡⴰⵍⴱⵓⴹ ⵢⴰⵎⵓⵏ|ⵏ ⵡⴰⵍⴱⵓⴹⵏ ⵢⴰⵎⵓⵏ}} ($1)",
        "pageinfo-toolboxlink": "ⴰⵏⵖⵎⵉⵙ ⵖⴼ ⵜⴰⵙⵏⴰ",
        "pageinfo-contentpage": "ⵉⵜⵜⵓⵙⵉⴹⵏ ⴰⵎ ⵜⴰⵙⵏⴰ ⵏ ⵜⵓⵎⴰⵢⵜ",
        "pageinfo-contentpage-yes": "ⵢⴰⵀ",
        "pageinfo-protect-cascading-yes": "ⵢⴰⵀ",
+       "patrol-log-page": "ⵜⴰⵎⵙⵙⴽⵜⵉⵜ ⵏ ⵓⵣⵣⵔⴰⵢ",
        "previousdiff": "ⴰⵙⵏⴼⵍ ⴰⵎⴳⴳⴰⵔⵓ",
        "nextdiff": "ⴰⵙⵏⴼⵍ ⴰⵎⴰⵢⵏⵓ",
        "widthheightpage": "$1 × $2, $3 {{PLURAL:$3|ⵜⴰⵙⵏⴰ|ⵜⴰⵙⵏⵉⵡⵉⵏ}}",
        "htmlform-yes": "ⵢⴰⵀ",
        "logentry-delete-delete": "$1 {{GENDER:$2|ⵉⴽⴽⵙ|ⵜⴽⴽⵙ}} ⵜⴰⵙⵏⴰ $3",
        "logentry-delete-restore": "$1 {{GENDER:$2|ⵉⵔⵓⵔ ⴷ|ⵜⵔⵓⵔ ⴷ}} ⵜⴰⵙⵏⴰ $3 ($4)",
+       "logentry-delete-revision": "$1 {{GENDER:$2|ⵉⵙⵏⴼⵍ}} ⴰⵍⵍⴼⵓⵙ ⵏ ⵡⵓⵎⴰⵏ {{PLURAL:$5|ⵏ ⵢⴰⵏ ⵓⵣⵣⵔⴰⵢ|ⵏ $5 ⵏ ⵉⵣⵣⵔⴰⵢⵏ}} ⴳ ⵜⴰⵙⵏⴰ $3 : $4",
        "revdelete-content-hid": "ⵜⴻⵜⵜⵓⵏⵜⴰⵍ ⵜⵓⵎⴰⵢⵜ",
        "logentry-move-move": "$1 {{GENDER:$2|ⵉⵙⵎⵓⵜⵜⵉ|ⵜⵙⵎⵓⵜⵜⵉ}} ⵜⴰⵙⵏⴰ ⵙⴳ $3 ⵖⵔ $4",
        "logentry-move-move-noredirect": "{{GENDER:$2|ⵉⵙⵎⵓⵜⵜⵉ}} $1 ⵜⴰⵙⵏⴰ $3 ⵖⵔ $4 ⵎⵉⵏ ⴰⴷ ⵉⴼⵍ redirect",
index 769fa02..1d2dad0 100644 (file)
@@ -99,7 +99,9 @@
                        "WhitePhosphorus",
                        "A2093064",
                        "EagerLin",
-                       "Deathkon"
+                       "Deathkon",
+                       "RyRubyy",
+                       "Wxyveronica"
                ]
        },
        "tog-underline": "链接下划线:",
        "noemailcreate": "您需要提供一个有效的电子邮件地址",
        "passwordsent": "用户\"$1\"的新密码已经寄往所登记的电子邮件地址。\n请在收到后再登录。",
        "blocked-mailpassword": "您的IP地址被禁止编辑,为预防滥用,也不允许从该IP地址使用密码恢复功能。",
-       "eauthentsent": "一封确认信已经发送至您设定的邮件地址。\n在任何其他邮件发送至您的账户前,您将不得不根据邮件中的指示,确认那个账户确实是您的。",
+       "eauthentsent": "一封确认信已经发送至您设定的邮件地址。\n在任何其他邮件发送至您的账户前,您将需要根据邮件中的指示,确认那个账户确实是您的。",
        "throttled-mailpassword": "密码提醒已在最近$1小时内发送。为了安全起见,在每$1小时内只能发送一个密码提醒。",
        "mailerror": "发送邮件错误:$1",
        "acct_creation_throttle_hit": "使用您的IP地址访问本wiki的访客在过去$2中创建了{{PLURAL:$1|$1个账户}},达到了这段时间所允许的最大值。因此,使用该IP地址的访客现在不能再创建账户。",
        "http-timed-out": "HTTP请求已过时。",
        "http-curl-error": "撷取URL时出错:$1",
        "http-bad-status": "进行HTTP请求时出现问题:$1 $2",
+       "http-internal-error": "HTTP内部错误。",
        "upload-curl-error6": "无法访问URL",
        "upload-curl-error6-text": "无法访问提供的URL。请检查该URL是否正确,及其网站是否在线。",
        "upload-curl-error28": "上传超时",
index 51bb659..b649940 100644 (file)
        "http-timed-out": "HTTP 請求已逾時。",
        "http-curl-error": "擷取 URL 時錯誤:$1",
        "http-bad-status": "進行 HTTP 請求發生問題:$1 $2",
+       "http-internal-error": "HTTP 內部錯誤。",
        "upload-curl-error6": "無法連線至 URL",
        "upload-curl-error6-text": "無法連線至指定的 URL 。\n請重新檢查 URL 是否正確,且確認網站是否正常運作。",
        "upload-curl-error28": "上傳逾時",
        "undeletehistory": "若您還原該頁面,所有的修訂歷史也會一併還原。\n若刪除之後已有使用相同名稱建立的新頁面,還原的修訂歷史會出現在此頁面之前的歷史中。",
        "undeleterevdel": "若最新頁面或檔案修訂被部份刪除,將無法執行取消刪除的動作。\n這種情況您必須取消勾選或取消隱藏已刪除的最新修訂。",
        "undeletehistorynoadmin": "已刪除此頁面。\n以下摘要顯示刪除原因與刪除前所有編輯過此頁面的使用者詳細資料。\n這些已刪除的實際文字修訂僅對管理員可用。",
-       "undelete-revision": "被 $3 刪除的 $1 (於 $4 $5) 修訂:",
+       "undelete-revision": "$1由$3(於$4 $5)所編輯的已刪除修訂版本",
        "undeleterevision-missing": "無效或遺失的修訂。\n您可能使用了錯誤的連結,或該修訂已從封存中還原或刪除。",
        "undeleterevision-duplicate-revid": "無法還原 {{PLURAL:$1|1 個修訂|$1 修訂}},因{{PLURAL:$1|修訂的|修訂的}} <code>rev_id</code> 已在使用中。",
        "undelete-nodiff": "查無先前的修訂。",
index 9955e95..4b15837 100644 (file)
@@ -15,7 +15,8 @@
                        "LNDDYL",
                        "Liuxinyu970226",
                        "Quest for Truth",
-                       "Wxyveronica"
+                       "Wxyveronica",
+                       "和平至上"
                ]
        },
        "tog-watchlisthidebots": "隱藏監視清單中機械人的編輯",
        "tooltip-t-specialpages": "特殊頁面總清單",
        "tooltip-t-print": "這個頁面的可打印版本",
        "tooltip-ca-nstab-user": "檢視用戶頁面",
-       "pageinfo-toolboxlink": "頁面咨詢",
+       "pageinfo-toolboxlink": "頁面資訊",
        "newimages-showbots": "顯示機械人上傳的檔案",
        "exif-rowsperstrip": "每帶行數",
        "redirect-user": "用戶 ID",
index 3f52c88..4ae1d9d 100644 (file)
@@ -8,8 +8,6 @@
  *
  */
 
-$fallback = 'bn';
-
 $namespaceNames = [
        NS_MEDIA            => 'ᱢᱤᱰᱤᱭᱟ',
        NS_SPECIAL          => 'ᱟᱥᱚᱠᱟᱭ',
index d3efca6..2072d7b 100644 (file)
@@ -31,7 +31,7 @@ require_once __DIR__ . '/Maintenance.php';
  * @ingroup Maintenance
  */
 class CreateAndPromote extends Maintenance {
-       private static $permitRoles = [ 'sysop', 'bureaucrat', 'bot' ];
+       private static $permitRoles = [ 'sysop', 'bureaucrat', 'interface-admin', 'bot' ];
 
        public function __construct() {
                parent::__construct();
index af47d65..fad35cb 100644 (file)
@@ -28,6 +28,11 @@ require_once __DIR__ . '/../Maintenance.php';
  *
  * This data file is used after normalizing to NFC.
  *
+ * Example usage:
+ *
+ *    curl 'https://unicode.org/Public/6.0.0/ucd/UnicodeData.txt' > /tmp/UnicodeData.txt
+ *    php generateNormalizerDataAr.php --unicode-data-file /tmp/UnicodeData.txt
+ *
  * @ingroup MaintenanceLanguage
  */
 class GenerateNormalizerDataAr extends Maintenance {
index 6d0882a..1b575bb 100644 (file)
@@ -165,6 +165,12 @@ class MysqlMaintenance extends Maintenance {
 
                $args = array_merge( $args, $this->mArgs );
 
+               // Ignore SIGINT if possible, otherwise the wrapper terminates when the user presses
+               // ctrl-C to kill a query.
+               if ( function_exists( 'pcntl_signal' ) ) {
+                       pcntl_signal( SIGINT, SIG_IGN );
+               }
+
                $pipes = [];
                $proc = proc_open( Shell::escape( $args ), $desc, $pipes );
                if ( $proc === false ) {
diff --git a/maintenance/resetAuthenticationThrottle.php b/maintenance/resetAuthenticationThrottle.php
new file mode 100644 (file)
index 0000000..9760c42
--- /dev/null
@@ -0,0 +1,152 @@
+<?php
+/**
+ * Reset login/signup throttling for a specified user and/or IP.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup Maintenance
+ */
+
+use MediaWiki\Auth\AuthManager;
+use MediaWiki\Auth\Throttler;
+use MediaWiki\Logger\LoggerFactory;
+
+require_once __DIR__ . '/Maintenance.php';
+
+/**
+ * Reset login/signup throttling for a specified user and/or IP.
+ *
+ * @ingroup Maintenance
+ * @since 1.32
+ */
+class ResetAuthenticationThrottle extends Maintenance {
+
+       public function __construct() {
+               parent::__construct();
+               $this->addDescription( 'Reset login/signup throttling for a specified user and/or IP. '
+                       . "\n\n"
+                       . 'When resetting signup only, provide the IP. When resetting login (or both), provide '
+                       . 'both username (as entered in login screen) and IP. An easy way to obtain them is '
+                       . "the 'throttler' log channel." );
+               $this->addOption( 'login', 'Reset login throttle' );
+               $this->addOption( 'signup', 'Reset account creation throttle' );
+               $this->addOption( 'user', 'Username to reset', false, true );
+               $this->addOption( 'ip', 'IP to reset', false, true );
+       }
+
+       public function execute() {
+               $forLogin = (bool)$this->getOption( 'login' );
+               $forSignup = (bool)$this->getOption( 'signup' );
+               $username = $this->getOption( 'user' );
+               $ip = $this->getOption( 'ip' );
+
+               if ( !$forLogin && !$forSignup ) {
+                       $this->fatalError( 'At least one of --login and --signup is required!' );
+               } elseif ( $forLogin && ( $ip === null || $username === null ) ) {
+                       $this->fatalError( '--usename and --ip are both required when using --login!' );
+               } elseif ( $forSignup && $ip === null ) {
+                       $this->fatalError( '--ip is required when using --signup!' );
+               } elseif ( $ip !== null && !IP::isValid( $ip ) ) {
+                       $this->fatalError( "Not a valid IP: $ip" );
+               }
+
+               if ( $forLogin ) {
+                       $this->clearLoginThrottle( $username, $ip );
+               }
+               if ( $forSignup ) {
+                       $this->clearSignupThrottle( $ip );
+               }
+
+               LoggerFactory::getInstance( 'throttler' )->notice( 'Manually cleared {type} throttle', [
+                       'type' => implode( ' and ', array_filter( [
+                               $forLogin ? 'login' : null,
+                               $forSignup ? 'signup' : null,
+                       ] ) ),
+                       'username' => $username,
+                       'ipKey' => $ip,
+               ] );
+       }
+
+       /**
+        * @param string|null $rawUsername
+        * @param string|null $ip
+        */
+       protected function clearLoginThrottle( $rawUsername, $ip ) {
+               $this->output( 'Clearing login throttle... ' );
+
+               $passwordAttemptThrottle = $this->getConfig()->get( 'PasswordAttemptThrottle' );
+               if ( !$passwordAttemptThrottle ) {
+                       $this->output( "none set\n" );
+                       return;
+               }
+
+               $throttler = new Throttler( $passwordAttemptThrottle, [
+                       'type' => 'password',
+                       'cache' => ObjectCache::getLocalClusterInstance(),
+               ] );
+               if ( $rawUsername !== null ) {
+                       $usernames = AuthManager::singleton()->normalizeUsername( $rawUsername );
+                       if ( !$usernames ) {
+                               $this->fatalError( "Not a valid username: $rawUsername" );
+                       }
+               } else {
+                       $usernames = [ null ];
+               }
+               foreach ( $usernames as $username ) {
+                       $throttler->clear( $username, $ip );
+               }
+
+               $botPasswordThrottler = new Throttler( $passwordAttemptThrottle, [
+                       'type' => 'botpassword',
+                       'cache' => ObjectCache::getLocalClusterInstance(),
+               ] );
+               $botPasswordThrottler->clear( $username, $ip );
+
+               $this->output( "done\n" );
+       }
+
+       /**
+        * @param string $ip
+        */
+       protected function clearSignupThrottle( $ip ) {
+               $this->output( 'Clearing signup throttle... ' );
+
+               $accountCreationThrottle = $this->getConfig()->get( 'AccountCreationThrottle' );
+               if ( !is_array( $accountCreationThrottle ) ) {
+                       $accountCreationThrottle = [ [
+                               'count' => $accountCreationThrottle,
+                               'seconds' => 86400,
+                       ] ];
+               }
+               if ( !$accountCreationThrottle ) {
+                       $this->output( "none set\n" );
+                       return;
+               }
+               $throttler = new Throttler( $accountCreationThrottle, [
+                       'type' => 'acctcreate',
+                       'cache' => ObjectCache::getLocalClusterInstance(),
+               ] );
+
+               $throttler->clear( null, $ip );
+
+               $this->output( "done\n" );
+       }
+
+}
+
+$maintClass = ResetAuthenticationThrottle::class;
+require_once RUN_MAINTENANCE_IF_MAIN;
index fc24035..c591c7c 100644 (file)
@@ -2771,6 +2771,25 @@ return [
                'raw' => true,
        ],
 
+       /* EasyDeflate */
+
+       'easy-deflate.core' => [
+               'scripts' => [ 'resources/lib/easy-deflate/easydeflate.js' ],
+               'targets' => [ 'desktop', 'mobile' ],
+       ],
+
+       'easy-deflate.deflate' => [
+               'scripts' => [ 'resources/lib/easy-deflate/deflate.js' ],
+               'dependencies' => [ 'easy-deflate.core' ],
+               'targets' => [ 'desktop', 'mobile' ],
+       ],
+
+       'easy-deflate.inflate' => [
+               'scripts' => [ 'resources/lib/easy-deflate/inflate.js' ],
+               'dependencies' => [ 'easy-deflate.core' ],
+               'targets' => [ 'desktop', 'mobile' ],
+       ],
+
        /* OOjs */
        'oojs' => [
                'scripts' => [
diff --git a/resources/lib/easy-deflate/README.md b/resources/lib/easy-deflate/README.md
new file mode 100644 (file)
index 0000000..9b3d07e
--- /dev/null
@@ -0,0 +1,38 @@
+Modified version of Easy-Deflate https://github.com/Jacob-Christian-Munch-Andersen/Easy-Deflate
+
+This version: https://github.com/edg2s/Easy-Deflate
+
+* Added semi-colons to easydeflate.js so it can be minified
+* Namespaced functions inside global EasyDeflate object
+* Base64 lib replaced with one with detailed license info
+
+Modifications by Ed Sanders, Public Domain.
+
+Easy-Deflate
+============
+
+Library for compressing and decompressing strings in JavaScript, feature full Unicode support and is compatible with most browsers.
+
+Use:
+====
+Copy the script inclusion from demo.html.<br>
+Call EasyDeflate.deflate(foo) in order to compress a string.<br>
+Call EasyDeflate.inflate(bar) in order to decompress a string compressed in this manner.<br>
+Both functions return a string, or null in case of illegal input.
+
+The compression works by first UTF-8 encoding the input, then compressing it to a raw deflate stream. The stream is then base64 encoded, and finally the identifier "rawdeflate," is prepended.
+
+Credits:
+========
+Gildas Lormeau made the JavaScript conversion of a Deflate utility: https://github.com/gildas-lormeau/zip.js<br>
+Jacob Christian Munch-Andersen made this package in order to make simple use easier and compatible with older browsers.
+
+The following shims are included:<br>
+es5-shim by Kristopher Michael Kowal https://github.com/kriskowal/es5-shim<br>
+JSON 3 by Kit Cambridge http://bestiejs.github.com/json3/<br>
+Typed arrays light shim by Jacob Christian Munch-Andersen https://github.com/Jacob-Christian-Munch-Andersen/Typed-arrays-light-shim<br>
+<s>base64 by Yaffle https://gist.github.com/1284012</s>
+
+License:
+========
+Main packages come with a BSD licence, the shims, except for base64 that include no license text, each has a permissive license.
diff --git a/resources/lib/easy-deflate/deflate.js b/resources/lib/easy-deflate/deflate.js
new file mode 100644 (file)
index 0000000..665bfb1
--- /dev/null
@@ -0,0 +1,2088 @@
+/*
+ Copyright (c) 2013 Gildas Lormeau. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright 
+ notice, this list of conditions and the following disclaimer in 
+ the documentation and/or other materials provided with the distribution.
+
+ 3. The names of the authors may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+ INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * This program is based on JZlib 1.0.2 ymnk, JCraft,Inc.
+ * JZlib is based on zlib-1.1.3, so all credit should go authors
+ * Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu)
+ * and contributors of zlib.
+ */
+
+(function(obj) {
+
+       // Global
+
+       var MAX_BITS = 15;
+       var D_CODES = 30;
+       var BL_CODES = 19;
+
+       var LENGTH_CODES = 29;
+       var LITERALS = 256;
+       var L_CODES = (LITERALS + 1 + LENGTH_CODES);
+       var HEAP_SIZE = (2 * L_CODES + 1);
+
+       var END_BLOCK = 256;
+
+       // Bit length codes must not exceed MAX_BL_BITS bits
+       var MAX_BL_BITS = 7;
+
+       // repeat previous bit length 3-6 times (2 bits of repeat count)
+       var REP_3_6 = 16;
+
+       // repeat a zero length 3-10 times (3 bits of repeat count)
+       var REPZ_3_10 = 17;
+
+       // repeat a zero length 11-138 times (7 bits of repeat count)
+       var REPZ_11_138 = 18;
+
+       // The lengths of the bit length codes are sent in order of decreasing
+       // probability, to avoid transmitting the lengths for unused bit
+       // length codes.
+
+       var Buf_size = 8 * 2;
+
+       // JZlib version : "1.0.2"
+       var Z_DEFAULT_COMPRESSION = -1;
+
+       // compression strategy
+       var Z_FILTERED = 1;
+       var Z_HUFFMAN_ONLY = 2;
+       var Z_DEFAULT_STRATEGY = 0;
+
+       var Z_NO_FLUSH = 0;
+       var Z_PARTIAL_FLUSH = 1;
+       var Z_FULL_FLUSH = 3;
+       var Z_FINISH = 4;
+
+       var Z_OK = 0;
+       var Z_STREAM_END = 1;
+       var Z_NEED_DICT = 2;
+       var Z_STREAM_ERROR = -2;
+       var Z_DATA_ERROR = -3;
+       var Z_BUF_ERROR = -5;
+
+       // Tree
+
+       // see definition of array dist_code below
+       var _dist_code = [ 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+                       10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
+                       12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+                       13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+                       14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+                       14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+                       15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 16, 17, 18, 18, 19, 19,
+                       20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+                       24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+                       26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
+                       27, 27, 27, 27, 27, 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+                       28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 29,
+                       29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
+                       29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29 ];
+
+       function Tree() {
+               var that = this;
+
+               // dyn_tree; // the dynamic tree
+               // max_code; // largest code with non zero frequency
+               // stat_desc; // the corresponding static tree
+
+               // Compute the optimal bit lengths for a tree and update the total bit
+               // length
+               // for the current block.
+               // IN assertion: the fields freq and dad are set, heap[heap_max] and
+               // above are the tree nodes sorted by increasing frequency.
+               // OUT assertions: the field len is set to the optimal bit length, the
+               // array bl_count contains the frequencies for each bit length.
+               // The length opt_len is updated; static_len is also updated if stree is
+               // not null.
+               function gen_bitlen(s) {
+                       var tree = that.dyn_tree;
+                       var stree = that.stat_desc.static_tree;
+                       var extra = that.stat_desc.extra_bits;
+                       var base = that.stat_desc.extra_base;
+                       var max_length = that.stat_desc.max_length;
+                       var h; // heap index
+                       var n, m; // iterate over the tree elements
+                       var bits; // bit length
+                       var xbits; // extra bits
+                       var f; // frequency
+                       var overflow = 0; // number of elements with bit length too large
+
+                       for (bits = 0; bits <= MAX_BITS; bits++)
+                               s.bl_count[bits] = 0;
+
+                       // In a first pass, compute the optimal bit lengths (which may
+                       // overflow in the case of the bit length tree).
+                       tree[s.heap[s.heap_max] * 2 + 1] = 0; // root of the heap
+
+                       for (h = s.heap_max + 1; h < HEAP_SIZE; h++) {
+                               n = s.heap[h];
+                               bits = tree[tree[n * 2 + 1] * 2 + 1] + 1;
+                               if (bits > max_length) {
+                                       bits = max_length;
+                                       overflow++;
+                               }
+                               tree[n * 2 + 1] = bits;
+                               // We overwrite tree[n*2+1] which is no longer needed
+
+                               if (n > that.max_code)
+                                       continue; // not a leaf node
+
+                               s.bl_count[bits]++;
+                               xbits = 0;
+                               if (n >= base)
+                                       xbits = extra[n - base];
+                               f = tree[n * 2];
+                               s.opt_len += f * (bits + xbits);
+                               if (stree)
+                                       s.static_len += f * (stree[n * 2 + 1] + xbits);
+                       }
+                       if (overflow === 0)
+                               return;
+
+                       // This happens for example on obj2 and pic of the Calgary corpus
+                       // Find the first bit length which could increase:
+                       do {
+                               bits = max_length - 1;
+                               while (s.bl_count[bits] === 0)
+                                       bits--;
+                               s.bl_count[bits]--; // move one leaf down the tree
+                               s.bl_count[bits + 1] += 2; // move one overflow item as its brother
+                               s.bl_count[max_length]--;
+                               // The brother of the overflow item also moves one step up,
+                               // but this does not affect bl_count[max_length]
+                               overflow -= 2;
+                       } while (overflow > 0);
+
+                       for (bits = max_length; bits !== 0; bits--) {
+                               n = s.bl_count[bits];
+                               while (n !== 0) {
+                                       m = s.heap[--h];
+                                       if (m > that.max_code)
+                                               continue;
+                                       if (tree[m * 2 + 1] != bits) {
+                                               s.opt_len += (bits - tree[m * 2 + 1]) * tree[m * 2];
+                                               tree[m * 2 + 1] = bits;
+                                       }
+                                       n--;
+                               }
+                       }
+               }
+
+               // Reverse the first len bits of a code, using straightforward code (a
+               // faster
+               // method would use a table)
+               // IN assertion: 1 <= len <= 15
+               function bi_reverse(code, // the value to invert
+               len // its bit length
+               ) {
+                       var res = 0;
+                       do {
+                               res |= code & 1;
+                               code >>>= 1;
+                               res <<= 1;
+                       } while (--len > 0);
+                       return res >>> 1;
+               }
+
+               // Generate the codes for a given tree and bit counts (which need not be
+               // optimal).
+               // IN assertion: the array bl_count contains the bit length statistics for
+               // the given tree and the field len is set for all tree elements.
+               // OUT assertion: the field code is set for all tree elements of non
+               // zero code length.
+               function gen_codes(tree, // the tree to decorate
+               max_code, // largest code with non zero frequency
+               bl_count // number of codes at each bit length
+               ) {
+                       var next_code = []; // next code value for each
+                       // bit length
+                       var code = 0; // running code value
+                       var bits; // bit index
+                       var n; // code index
+                       var len;
+
+                       // The distribution counts are first used to generate the code values
+                       // without bit reversal.
+                       for (bits = 1; bits <= MAX_BITS; bits++) {
+                               next_code[bits] = code = ((code + bl_count[bits - 1]) << 1);
+                       }
+
+                       // Check that the bit counts in bl_count are consistent. The last code
+                       // must be all ones.
+                       // Assert (code + bl_count[MAX_BITS]-1 == (1<<MAX_BITS)-1,
+                       // "inconsistent bit counts");
+                       // Tracev((stderr,"\ngen_codes: max_code %d ", max_code));
+
+                       for (n = 0; n <= max_code; n++) {
+                               len = tree[n * 2 + 1];
+                               if (len === 0)
+                                       continue;
+                               // Now reverse the bits
+                               tree[n * 2] = bi_reverse(next_code[len]++, len);
+                       }
+               }
+
+               // Construct one Huffman tree and assigns the code bit strings and lengths.
+               // Update the total bit length for the current block.
+               // IN assertion: the field freq is set for all tree elements.
+               // OUT assertions: the fields len and code are set to the optimal bit length
+               // and corresponding code. The length opt_len is updated; static_len is
+               // also updated if stree is not null. The field max_code is set.
+               that.build_tree = function(s) {
+                       var tree = that.dyn_tree;
+                       var stree = that.stat_desc.static_tree;
+                       var elems = that.stat_desc.elems;
+                       var n, m; // iterate over heap elements
+                       var max_code = -1; // largest code with non zero frequency
+                       var node; // new node being created
+
+                       // Construct the initial heap, with least frequent element in
+                       // heap[1]. The sons of heap[n] are heap[2*n] and heap[2*n+1].
+                       // heap[0] is not used.
+                       s.heap_len = 0;
+                       s.heap_max = HEAP_SIZE;
+
+                       for (n = 0; n < elems; n++) {
+                               if (tree[n * 2] !== 0) {
+                                       s.heap[++s.heap_len] = max_code = n;
+                                       s.depth[n] = 0;
+                               } else {
+                                       tree[n * 2 + 1] = 0;
+                               }
+                       }
+
+                       // The pkzip format requires that at least one distance code exists,
+                       // and that at least one bit should be sent even if there is only one
+                       // possible code. So to avoid special checks later on we force at least
+                       // two codes of non zero frequency.
+                       while (s.heap_len < 2) {
+                               node = s.heap[++s.heap_len] = max_code < 2 ? ++max_code : 0;
+                               tree[node * 2] = 1;
+                               s.depth[node] = 0;
+                               s.opt_len--;
+                               if (stree)
+                                       s.static_len -= stree[node * 2 + 1];
+                               // node is 0 or 1 so it does not have extra bits
+                       }
+                       that.max_code = max_code;
+
+                       // The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree,
+                       // establish sub-heaps of increasing lengths:
+
+                       for (n = Math.floor(s.heap_len / 2); n >= 1; n--)
+                               s.pqdownheap(tree, n);
+
+                       // Construct the Huffman tree by repeatedly combining the least two
+                       // frequent nodes.
+
+                       node = elems; // next internal node of the tree
+                       do {
+                               // n = node of least frequency
+                               n = s.heap[1];
+                               s.heap[1] = s.heap[s.heap_len--];
+                               s.pqdownheap(tree, 1);
+                               m = s.heap[1]; // m = node of next least frequency
+
+                               s.heap[--s.heap_max] = n; // keep the nodes sorted by frequency
+                               s.heap[--s.heap_max] = m;
+
+                               // Create a new node father of n and m
+                               tree[node * 2] = (tree[n * 2] + tree[m * 2]);
+                               s.depth[node] = Math.max(s.depth[n], s.depth[m]) + 1;
+                               tree[n * 2 + 1] = tree[m * 2 + 1] = node;
+
+                               // and insert the new node in the heap
+                               s.heap[1] = node++;
+                               s.pqdownheap(tree, 1);
+                       } while (s.heap_len >= 2);
+
+                       s.heap[--s.heap_max] = s.heap[1];
+
+                       // At this point, the fields freq and dad are set. We can now
+                       // generate the bit lengths.
+
+                       gen_bitlen(s);
+
+                       // The field len is now set, we can generate the bit codes
+                       gen_codes(tree, that.max_code, s.bl_count);
+               };
+
+       }
+
+       Tree._length_code = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16,
+                       16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20,
+                       20, 20, 20, 20, 20, 20, 20, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
+                       22, 22, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+                       24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+                       25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+                       26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28 ];
+
+       Tree.base_length = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 0 ];
+
+       Tree.base_dist = [ 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384,
+                       24576 ];
+
+       // Mapping from a distance to a distance code. dist is the distance - 1 and
+       // must not have side effects. _dist_code[256] and _dist_code[257] are never
+       // used.
+       Tree.d_code = function(dist) {
+               return ((dist) < 256 ? _dist_code[dist] : _dist_code[256 + ((dist) >>> 7)]);
+       };
+
+       // extra bits for each length code
+       Tree.extra_lbits = [ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 ];
+
+       // extra bits for each distance code
+       Tree.extra_dbits = [ 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13 ];
+
+       // extra bits for each bit length code
+       Tree.extra_blbits = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 7 ];
+
+       Tree.bl_order = [ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 ];
+
+       // StaticTree
+
+       function StaticTree(static_tree, extra_bits, extra_base, elems, max_length) {
+               var that = this;
+               that.static_tree = static_tree;
+               that.extra_bits = extra_bits;
+               that.extra_base = extra_base;
+               that.elems = elems;
+               that.max_length = max_length;
+       }
+
+       StaticTree.static_ltree = [ 12, 8, 140, 8, 76, 8, 204, 8, 44, 8, 172, 8, 108, 8, 236, 8, 28, 8, 156, 8, 92, 8, 220, 8, 60, 8, 188, 8, 124, 8, 252, 8, 2, 8,
+                       130, 8, 66, 8, 194, 8, 34, 8, 162, 8, 98, 8, 226, 8, 18, 8, 146, 8, 82, 8, 210, 8, 50, 8, 178, 8, 114, 8, 242, 8, 10, 8, 138, 8, 74, 8, 202, 8, 42,
+                       8, 170, 8, 106, 8, 234, 8, 26, 8, 154, 8, 90, 8, 218, 8, 58, 8, 186, 8, 122, 8, 250, 8, 6, 8, 134, 8, 70, 8, 198, 8, 38, 8, 166, 8, 102, 8, 230, 8,
+                       22, 8, 150, 8, 86, 8, 214, 8, 54, 8, 182, 8, 118, 8, 246, 8, 14, 8, 142, 8, 78, 8, 206, 8, 46, 8, 174, 8, 110, 8, 238, 8, 30, 8, 158, 8, 94, 8,
+                       222, 8, 62, 8, 190, 8, 126, 8, 254, 8, 1, 8, 129, 8, 65, 8, 193, 8, 33, 8, 161, 8, 97, 8, 225, 8, 17, 8, 145, 8, 81, 8, 209, 8, 49, 8, 177, 8, 113,
+                       8, 241, 8, 9, 8, 137, 8, 73, 8, 201, 8, 41, 8, 169, 8, 105, 8, 233, 8, 25, 8, 153, 8, 89, 8, 217, 8, 57, 8, 185, 8, 121, 8, 249, 8, 5, 8, 133, 8,
+                       69, 8, 197, 8, 37, 8, 165, 8, 101, 8, 229, 8, 21, 8, 149, 8, 85, 8, 213, 8, 53, 8, 181, 8, 117, 8, 245, 8, 13, 8, 141, 8, 77, 8, 205, 8, 45, 8,
+                       173, 8, 109, 8, 237, 8, 29, 8, 157, 8, 93, 8, 221, 8, 61, 8, 189, 8, 125, 8, 253, 8, 19, 9, 275, 9, 147, 9, 403, 9, 83, 9, 339, 9, 211, 9, 467, 9,
+                       51, 9, 307, 9, 179, 9, 435, 9, 115, 9, 371, 9, 243, 9, 499, 9, 11, 9, 267, 9, 139, 9, 395, 9, 75, 9, 331, 9, 203, 9, 459, 9, 43, 9, 299, 9, 171, 9,
+                       427, 9, 107, 9, 363, 9, 235, 9, 491, 9, 27, 9, 283, 9, 155, 9, 411, 9, 91, 9, 347, 9, 219, 9, 475, 9, 59, 9, 315, 9, 187, 9, 443, 9, 123, 9, 379,
+                       9, 251, 9, 507, 9, 7, 9, 263, 9, 135, 9, 391, 9, 71, 9, 327, 9, 199, 9, 455, 9, 39, 9, 295, 9, 167, 9, 423, 9, 103, 9, 359, 9, 231, 9, 487, 9, 23,
+                       9, 279, 9, 151, 9, 407, 9, 87, 9, 343, 9, 215, 9, 471, 9, 55, 9, 311, 9, 183, 9, 439, 9, 119, 9, 375, 9, 247, 9, 503, 9, 15, 9, 271, 9, 143, 9,
+                       399, 9, 79, 9, 335, 9, 207, 9, 463, 9, 47, 9, 303, 9, 175, 9, 431, 9, 111, 9, 367, 9, 239, 9, 495, 9, 31, 9, 287, 9, 159, 9, 415, 9, 95, 9, 351, 9,
+                       223, 9, 479, 9, 63, 9, 319, 9, 191, 9, 447, 9, 127, 9, 383, 9, 255, 9, 511, 9, 0, 7, 64, 7, 32, 7, 96, 7, 16, 7, 80, 7, 48, 7, 112, 7, 8, 7, 72, 7,
+                       40, 7, 104, 7, 24, 7, 88, 7, 56, 7, 120, 7, 4, 7, 68, 7, 36, 7, 100, 7, 20, 7, 84, 7, 52, 7, 116, 7, 3, 8, 131, 8, 67, 8, 195, 8, 35, 8, 163, 8,
+                       99, 8, 227, 8 ];
+
+       StaticTree.static_dtree = [ 0, 5, 16, 5, 8, 5, 24, 5, 4, 5, 20, 5, 12, 5, 28, 5, 2, 5, 18, 5, 10, 5, 26, 5, 6, 5, 22, 5, 14, 5, 30, 5, 1, 5, 17, 5, 9, 5,
+                       25, 5, 5, 5, 21, 5, 13, 5, 29, 5, 3, 5, 19, 5, 11, 5, 27, 5, 7, 5, 23, 5 ];
+
+       StaticTree.static_l_desc = new StaticTree(StaticTree.static_ltree, Tree.extra_lbits, LITERALS + 1, L_CODES, MAX_BITS);
+
+       StaticTree.static_d_desc = new StaticTree(StaticTree.static_dtree, Tree.extra_dbits, 0, D_CODES, MAX_BITS);
+
+       StaticTree.static_bl_desc = new StaticTree(null, Tree.extra_blbits, 0, BL_CODES, MAX_BL_BITS);
+
+       // Deflate
+
+       var MAX_MEM_LEVEL = 9;
+       var DEF_MEM_LEVEL = 8;
+
+       function Config(good_length, max_lazy, nice_length, max_chain, func) {
+               var that = this;
+               that.good_length = good_length;
+               that.max_lazy = max_lazy;
+               that.nice_length = nice_length;
+               that.max_chain = max_chain;
+               that.func = func;
+       }
+
+       var STORED = 0;
+       var FAST = 1;
+       var SLOW = 2;
+       var config_table = [ new Config(0, 0, 0, 0, STORED), new Config(4, 4, 8, 4, FAST), new Config(4, 5, 16, 8, FAST), new Config(4, 6, 32, 32, FAST),
+                       new Config(4, 4, 16, 16, SLOW), new Config(8, 16, 32, 32, SLOW), new Config(8, 16, 128, 128, SLOW), new Config(8, 32, 128, 256, SLOW),
+                       new Config(32, 128, 258, 1024, SLOW), new Config(32, 258, 258, 4096, SLOW) ];
+
+       var z_errmsg = [ "need dictionary", // Z_NEED_DICT
+       // 2
+       "stream end", // Z_STREAM_END 1
+       "", // Z_OK 0
+       "", // Z_ERRNO (-1)
+       "stream error", // Z_STREAM_ERROR (-2)
+       "data error", // Z_DATA_ERROR (-3)
+       "", // Z_MEM_ERROR (-4)
+       "buffer error", // Z_BUF_ERROR (-5)
+       "",// Z_VERSION_ERROR (-6)
+       "" ];
+
+       // block not completed, need more input or more output
+       var NeedMore = 0;
+
+       // block flush performed
+       var BlockDone = 1;
+
+       // finish started, need only more output at next deflate
+       var FinishStarted = 2;
+
+       // finish done, accept no more input or output
+       var FinishDone = 3;
+
+       // preset dictionary flag in zlib header
+       var PRESET_DICT = 0x20;
+
+       var INIT_STATE = 42;
+       var BUSY_STATE = 113;
+       var FINISH_STATE = 666;
+
+       // The deflate compression method
+       var Z_DEFLATED = 8;
+
+       var STORED_BLOCK = 0;
+       var STATIC_TREES = 1;
+       var DYN_TREES = 2;
+
+       var MIN_MATCH = 3;
+       var MAX_MATCH = 258;
+       var MIN_LOOKAHEAD = (MAX_MATCH + MIN_MATCH + 1);
+
+       function smaller(tree, n, m, depth) {
+               var tn2 = tree[n * 2];
+               var tm2 = tree[m * 2];
+               return (tn2 < tm2 || (tn2 == tm2 && depth[n] <= depth[m]));
+       }
+
+       function Deflate() {
+
+               var that = this;
+               var strm; // pointer back to this zlib stream
+               var status; // as the name implies
+               // pending_buf; // output still pending
+               var pending_buf_size; // size of pending_buf
+               // pending_out; // next pending byte to output to the stream
+               // pending; // nb of bytes in the pending buffer
+               var method; // STORED (for zip only) or DEFLATED
+               var last_flush; // value of flush param for previous deflate call
+
+               var w_size; // LZ77 window size (32K by default)
+               var w_bits; // log2(w_size) (8..16)
+               var w_mask; // w_size - 1
+
+               var window;
+               // Sliding window. Input bytes are read into the second half of the window,
+               // and move to the first half later to keep a dictionary of at least wSize
+               // bytes. With this organization, matches are limited to a distance of
+               // wSize-MAX_MATCH bytes, but this ensures that IO is always
+               // performed with a length multiple of the block size. Also, it limits
+               // the window size to 64K, which is quite useful on MSDOS.
+               // To do: use the user input buffer as sliding window.
+
+               var window_size;
+               // Actual size of window: 2*wSize, except when the user input buffer
+               // is directly used as sliding window.
+
+               var prev;
+               // Link to older string with same hash index. To limit the size of this
+               // array to 64K, this link is maintained only for the last 32K strings.
+               // An index in this array is thus a window index modulo 32K.
+
+               var head; // Heads of the hash chains or NIL.
+
+               var ins_h; // hash index of string to be inserted
+               var hash_size; // number of elements in hash table
+               var hash_bits; // log2(hash_size)
+               var hash_mask; // hash_size-1
+
+               // Number of bits by which ins_h must be shifted at each input
+               // step. It must be such that after MIN_MATCH steps, the oldest
+               // byte no longer takes part in the hash key, that is:
+               // hash_shift * MIN_MATCH >= hash_bits
+               var hash_shift;
+
+               // Window position at the beginning of the current output block. Gets
+               // negative when the window is moved backwards.
+
+               var block_start;
+
+               var match_length; // length of best match
+               var prev_match; // previous match
+               var match_available; // set if previous match exists
+               var strstart; // start of string to insert
+               var match_start; // start of matching string
+               var lookahead; // number of valid bytes ahead in window
+
+               // Length of the best match at previous step. Matches not greater than this
+               // are discarded. This is used in the lazy match evaluation.
+               var prev_length;
+
+               // To speed up deflation, hash chains are never searched beyond this
+               // length. A higher limit improves compression ratio but degrades the speed.
+               var max_chain_length;
+
+               // Attempt to find a better match only when the current match is strictly
+               // smaller than this value. This mechanism is used only for compression
+               // levels >= 4.
+               var max_lazy_match;
+
+               // Insert new strings in the hash table only if the match length is not
+               // greater than this length. This saves time but degrades compression.
+               // max_insert_length is used only for compression levels <= 3.
+
+               var level; // compression level (1..9)
+               var strategy; // favor or force Huffman coding
+
+               // Use a faster search when the previous match is longer than this
+               var good_match;
+
+               // Stop searching when current match exceeds this
+               var nice_match;
+
+               var dyn_ltree; // literal and length tree
+               var dyn_dtree; // distance tree
+               var bl_tree; // Huffman tree for bit lengths
+
+               var l_desc = new Tree(); // desc for literal tree
+               var d_desc = new Tree(); // desc for distance tree
+               var bl_desc = new Tree(); // desc for bit length tree
+
+               // that.heap_len; // number of elements in the heap
+               // that.heap_max; // element of largest frequency
+               // The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used.
+               // The same heap array is used to build all trees.
+
+               // Depth of each subtree used as tie breaker for trees of equal frequency
+               that.depth = [];
+
+               var l_buf; // index for literals or lengths */
+
+               // Size of match buffer for literals/lengths. There are 4 reasons for
+               // limiting lit_bufsize to 64K:
+               // - frequencies can be kept in 16 bit counters
+               // - if compression is not successful for the first block, all input
+               // data is still in the window so we can still emit a stored block even
+               // when input comes from standard input. (This can also be done for
+               // all blocks if lit_bufsize is not greater than 32K.)
+               // - if compression is not successful for a file smaller than 64K, we can
+               // even emit a stored file instead of a stored block (saving 5 bytes).
+               // This is applicable only for zip (not gzip or zlib).
+               // - creating new Huffman trees less frequently may not provide fast
+               // adaptation to changes in the input data statistics. (Take for
+               // example a binary file with poorly compressible code followed by
+               // a highly compressible string table.) Smaller buffer sizes give
+               // fast adaptation but have of course the overhead of transmitting
+               // trees more frequently.
+               // - I can't count above 4
+               var lit_bufsize;
+
+               var last_lit; // running index in l_buf
+
+               // Buffer for distances. To simplify the code, d_buf and l_buf have
+               // the same number of elements. To use different lengths, an extra flag
+               // array would be necessary.
+
+               var d_buf; // index of pendig_buf
+
+               // that.opt_len; // bit length of current block with optimal trees
+               // that.static_len; // bit length of current block with static trees
+               var matches; // number of string matches in current block
+               var last_eob_len; // bit length of EOB code for last block
+
+               // Output buffer. bits are inserted starting at the bottom (least
+               // significant bits).
+               var bi_buf;
+
+               // Number of valid bits in bi_buf. All bits above the last valid bit
+               // are always zero.
+               var bi_valid;
+
+               // number of codes at each bit length for an optimal tree
+               that.bl_count = [];
+
+               // heap used to build the Huffman trees
+               that.heap = [];
+
+               dyn_ltree = [];
+               dyn_dtree = [];
+               bl_tree = [];
+
+               function lm_init() {
+                       var i;
+                       window_size = 2 * w_size;
+
+                       head[hash_size - 1] = 0;
+                       for (i = 0; i < hash_size - 1; i++) {
+                               head[i] = 0;
+                       }
+
+                       // Set the default configuration parameters:
+                       max_lazy_match = config_table[level].max_lazy;
+                       good_match = config_table[level].good_length;
+                       nice_match = config_table[level].nice_length;
+                       max_chain_length = config_table[level].max_chain;
+
+                       strstart = 0;
+                       block_start = 0;
+                       lookahead = 0;
+                       match_length = prev_length = MIN_MATCH - 1;
+                       match_available = 0;
+                       ins_h = 0;
+               }
+
+               function init_block() {
+                       var i;
+                       // Initialize the trees.
+                       for (i = 0; i < L_CODES; i++)
+                               dyn_ltree[i * 2] = 0;
+                       for (i = 0; i < D_CODES; i++)
+                               dyn_dtree[i * 2] = 0;
+                       for (i = 0; i < BL_CODES; i++)
+                               bl_tree[i * 2] = 0;
+
+                       dyn_ltree[END_BLOCK * 2] = 1;
+                       that.opt_len = that.static_len = 0;
+                       last_lit = matches = 0;
+               }
+
+               // Initialize the tree data structures for a new zlib stream.
+               function tr_init() {
+
+                       l_desc.dyn_tree = dyn_ltree;
+                       l_desc.stat_desc = StaticTree.static_l_desc;
+
+                       d_desc.dyn_tree = dyn_dtree;
+                       d_desc.stat_desc = StaticTree.static_d_desc;
+
+                       bl_desc.dyn_tree = bl_tree;
+                       bl_desc.stat_desc = StaticTree.static_bl_desc;
+
+                       bi_buf = 0;
+                       bi_valid = 0;
+                       last_eob_len = 8; // enough lookahead for inflate
+
+                       // Initialize the first block of the first file:
+                       init_block();
+               }
+
+               // Restore the heap property by moving down the tree starting at node k,
+               // exchanging a node with the smallest of its two sons if necessary,
+               // stopping
+               // when the heap property is re-established (each father smaller than its
+               // two sons).
+               that.pqdownheap = function(tree, // the tree to restore
+               k // node to move down
+               ) {
+                       var heap = that.heap;
+                       var v = heap[k];
+                       var j = k << 1; // left son of k
+                       while (j <= that.heap_len) {
+                               // Set j to the smallest of the two sons:
+                               if (j < that.heap_len && smaller(tree, heap[j + 1], heap[j], that.depth)) {
+                                       j++;
+                               }
+                               // Exit if v is smaller than both sons
+                               if (smaller(tree, v, heap[j], that.depth))
+                                       break;
+
+                               // Exchange v with the smallest son
+                               heap[k] = heap[j];
+                               k = j;
+                               // And continue down the tree, setting j to the left son of k
+                               j <<= 1;
+                       }
+                       heap[k] = v;
+               };
+
+               // Scan a literal or distance tree to determine the frequencies of the codes
+               // in the bit length tree.
+               function scan_tree(tree,// the tree to be scanned
+               max_code // and its largest code of non zero frequency
+               ) {
+                       var n; // iterates over all tree elements
+                       var prevlen = -1; // last emitted length
+                       var curlen; // length of current code
+                       var nextlen = tree[0 * 2 + 1]; // length of next code
+                       var count = 0; // repeat count of the current code
+                       var max_count = 7; // max repeat count
+                       var min_count = 4; // min repeat count
+
+                       if (nextlen === 0) {
+                               max_count = 138;
+                               min_count = 3;
+                       }
+                       tree[(max_code + 1) * 2 + 1] = 0xffff; // guard
+
+                       for (n = 0; n <= max_code; n++) {
+                               curlen = nextlen;
+                               nextlen = tree[(n + 1) * 2 + 1];
+                               if (++count < max_count && curlen == nextlen) {
+                                       continue;
+                               } else if (count < min_count) {
+                                       bl_tree[curlen * 2] += count;
+                               } else if (curlen !== 0) {
+                                       if (curlen != prevlen)
+                                               bl_tree[curlen * 2]++;
+                                       bl_tree[REP_3_6 * 2]++;
+                               } else if (count <= 10) {
+                                       bl_tree[REPZ_3_10 * 2]++;
+                               } else {
+                                       bl_tree[REPZ_11_138 * 2]++;
+                               }
+                               count = 0;
+                               prevlen = curlen;
+                               if (nextlen === 0) {
+                                       max_count = 138;
+                                       min_count = 3;
+                               } else if (curlen == nextlen) {
+                                       max_count = 6;
+                                       min_count = 3;
+                               } else {
+                                       max_count = 7;
+                                       min_count = 4;
+                               }
+                       }
+               }
+
+               // Construct the Huffman tree for the bit lengths and return the index in
+               // bl_order of the last bit length code to send.
+               function build_bl_tree() {
+                       var max_blindex; // index of last bit length code of non zero freq
+
+                       // Determine the bit length frequencies for literal and distance trees
+                       scan_tree(dyn_ltree, l_desc.max_code);
+                       scan_tree(dyn_dtree, d_desc.max_code);
+
+                       // Build the bit length tree:
+                       bl_desc.build_tree(that);
+                       // opt_len now includes the length of the tree representations, except
+                       // the lengths of the bit lengths codes and the 5+5+4 bits for the
+                       // counts.
+
+                       // Determine the number of bit length codes to send. The pkzip format
+                       // requires that at least 4 bit length codes be sent. (appnote.txt says
+                       // 3 but the actual value used is 4.)
+                       for (max_blindex = BL_CODES - 1; max_blindex >= 3; max_blindex--) {
+                               if (bl_tree[Tree.bl_order[max_blindex] * 2 + 1] !== 0)
+                                       break;
+                       }
+                       // Update opt_len to include the bit length tree and counts
+                       that.opt_len += 3 * (max_blindex + 1) + 5 + 5 + 4;
+
+                       return max_blindex;
+               }
+
+               // Output a byte on the stream.
+               // IN assertion: there is enough room in pending_buf.
+               function put_byte(p) {
+                       that.pending_buf[that.pending++] = p;
+               }
+
+               function put_short(w) {
+                       put_byte(w & 0xff);
+                       put_byte((w >>> 8) & 0xff);
+               }
+
+               function putShortMSB(b) {
+                       put_byte((b >> 8) & 0xff);
+                       put_byte((b & 0xff) & 0xff);
+               }
+
+               function send_bits(value, length) {
+                       var val, len = length;
+                       if (bi_valid > Buf_size - len) {
+                               val = value;
+                               // bi_buf |= (val << bi_valid);
+                               bi_buf |= ((val << bi_valid) & 0xffff);
+                               put_short(bi_buf);
+                               bi_buf = val >>> (Buf_size - bi_valid);
+                               bi_valid += len - Buf_size;
+                       } else {
+                               // bi_buf |= (value) << bi_valid;
+                               bi_buf |= (((value) << bi_valid) & 0xffff);
+                               bi_valid += len;
+                       }
+               }
+
+               function send_code(c, tree) {
+                       var c2 = c * 2;
+                       send_bits(tree[c2] & 0xffff, tree[c2 + 1] & 0xffff);
+               }
+
+               // Send a literal or distance tree in compressed form, using the codes in
+               // bl_tree.
+               function send_tree(tree,// the tree to be sent
+               max_code // and its largest code of non zero frequency
+               ) {
+                       var n; // iterates over all tree elements
+                       var prevlen = -1; // last emitted length
+                       var curlen; // length of current code
+                       var nextlen = tree[0 * 2 + 1]; // length of next code
+                       var count = 0; // repeat count of the current code
+                       var max_count = 7; // max repeat count
+                       var min_count = 4; // min repeat count
+
+                       if (nextlen === 0) {
+                               max_count = 138;
+                               min_count = 3;
+                       }
+
+                       for (n = 0; n <= max_code; n++) {
+                               curlen = nextlen;
+                               nextlen = tree[(n + 1) * 2 + 1];
+                               if (++count < max_count && curlen == nextlen) {
+                                       continue;
+                               } else if (count < min_count) {
+                                       do {
+                                               send_code(curlen, bl_tree);
+                                       } while (--count !== 0);
+                               } else if (curlen !== 0) {
+                                       if (curlen != prevlen) {
+                                               send_code(curlen, bl_tree);
+                                               count--;
+                                       }
+                                       send_code(REP_3_6, bl_tree);
+                                       send_bits(count - 3, 2);
+                               } else if (count <= 10) {
+                                       send_code(REPZ_3_10, bl_tree);
+                                       send_bits(count - 3, 3);
+                               } else {
+                                       send_code(REPZ_11_138, bl_tree);
+                                       send_bits(count - 11, 7);
+                               }
+                               count = 0;
+                               prevlen = curlen;
+                               if (nextlen === 0) {
+                                       max_count = 138;
+                                       min_count = 3;
+                               } else if (curlen == nextlen) {
+                                       max_count = 6;
+                                       min_count = 3;
+                               } else {
+                                       max_count = 7;
+                                       min_count = 4;
+                               }
+                       }
+               }
+
+               // Send the header for a block using dynamic Huffman trees: the counts, the
+               // lengths of the bit length codes, the literal tree and the distance tree.
+               // IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4.
+               function send_all_trees(lcodes, dcodes, blcodes) {
+                       var rank; // index in bl_order
+
+                       send_bits(lcodes - 257, 5); // not +255 as stated in appnote.txt
+                       send_bits(dcodes - 1, 5);
+                       send_bits(blcodes - 4, 4); // not -3 as stated in appnote.txt
+                       for (rank = 0; rank < blcodes; rank++) {
+                               send_bits(bl_tree[Tree.bl_order[rank] * 2 + 1], 3);
+                       }
+                       send_tree(dyn_ltree, lcodes - 1); // literal tree
+                       send_tree(dyn_dtree, dcodes - 1); // distance tree
+               }
+
+               // Flush the bit buffer, keeping at most 7 bits in it.
+               function bi_flush() {
+                       if (bi_valid == 16) {
+                               put_short(bi_buf);
+                               bi_buf = 0;
+                               bi_valid = 0;
+                       } else if (bi_valid >= 8) {
+                               put_byte(bi_buf & 0xff);
+                               bi_buf >>>= 8;
+                               bi_valid -= 8;
+                       }
+               }
+
+               // Send one empty static block to give enough lookahead for inflate.
+               // This takes 10 bits, of which 7 may remain in the bit buffer.
+               // The current inflate code requires 9 bits of lookahead. If the
+               // last two codes for the previous block (real code plus EOB) were coded
+               // on 5 bits or less, inflate may have only 5+3 bits of lookahead to decode
+               // the last real code. In this case we send two empty static blocks instead
+               // of one. (There are no problems if the previous block is stored or fixed.)
+               // To simplify the code, we assume the worst case of last real code encoded
+               // on one bit only.
+               function _tr_align() {
+                       send_bits(STATIC_TREES << 1, 3);
+                       send_code(END_BLOCK, StaticTree.static_ltree);
+
+                       bi_flush();
+
+                       // Of the 10 bits for the empty block, we have already sent
+                       // (10 - bi_valid) bits. The lookahead for the last real code (before
+                       // the EOB of the previous block) was thus at least one plus the length
+                       // of the EOB plus what we have just sent of the empty static block.
+                       if (1 + last_eob_len + 10 - bi_valid < 9) {
+                               send_bits(STATIC_TREES << 1, 3);
+                               send_code(END_BLOCK, StaticTree.static_ltree);
+                               bi_flush();
+                       }
+                       last_eob_len = 7;
+               }
+
+               // Save the match info and tally the frequency counts. Return true if
+               // the current block must be flushed.
+               function _tr_tally(dist, // distance of matched string
+               lc // match length-MIN_MATCH or unmatched char (if dist==0)
+               ) {
+                       var out_length, in_length, dcode;
+                       that.pending_buf[d_buf + last_lit * 2] = (dist >>> 8) & 0xff;
+                       that.pending_buf[d_buf + last_lit * 2 + 1] = dist & 0xff;
+
+                       that.pending_buf[l_buf + last_lit] = lc & 0xff;
+                       last_lit++;
+
+                       if (dist === 0) {
+                               // lc is the unmatched char
+                               dyn_ltree[lc * 2]++;
+                       } else {
+                               matches++;
+                               // Here, lc is the match length - MIN_MATCH
+                               dist--; // dist = match distance - 1
+                               dyn_ltree[(Tree._length_code[lc] + LITERALS + 1) * 2]++;
+                               dyn_dtree[Tree.d_code(dist) * 2]++;
+                       }
+
+                       if ((last_lit & 0x1fff) === 0 && level > 2) {
+                               // Compute an upper bound for the compressed length
+                               out_length = last_lit * 8;
+                               in_length = strstart - block_start;
+                               for (dcode = 0; dcode < D_CODES; dcode++) {
+                                       out_length += dyn_dtree[dcode * 2] * (5 + Tree.extra_dbits[dcode]);
+                               }
+                               out_length >>>= 3;
+                               if ((matches < Math.floor(last_lit / 2)) && out_length < Math.floor(in_length / 2))
+                                       return true;
+                       }
+
+                       return (last_lit == lit_bufsize - 1);
+                       // We avoid equality with lit_bufsize because of wraparound at 64K
+                       // on 16 bit machines and because stored blocks are restricted to
+                       // 64K-1 bytes.
+               }
+
+               // Send the block data compressed using the given Huffman trees
+               function compress_block(ltree, dtree) {
+                       var dist; // distance of matched string
+                       var lc; // match length or unmatched char (if dist === 0)
+                       var lx = 0; // running index in l_buf
+                       var code; // the code to send
+                       var extra; // number of extra bits to send
+
+                       if (last_lit !== 0) {
+                               do {
+                                       dist = ((that.pending_buf[d_buf + lx * 2] << 8) & 0xff00) | (that.pending_buf[d_buf + lx * 2 + 1] & 0xff);
+                                       lc = (that.pending_buf[l_buf + lx]) & 0xff;
+                                       lx++;
+
+                                       if (dist === 0) {
+                                               send_code(lc, ltree); // send a literal byte
+                                       } else {
+                                               // Here, lc is the match length - MIN_MATCH
+                                               code = Tree._length_code[lc];
+
+                                               send_code(code + LITERALS + 1, ltree); // send the length
+                                               // code
+                                               extra = Tree.extra_lbits[code];
+                                               if (extra !== 0) {
+                                                       lc -= Tree.base_length[code];
+                                                       send_bits(lc, extra); // send the extra length bits
+                                               }
+                                               dist--; // dist is now the match distance - 1
+                                               code = Tree.d_code(dist);
+
+                                               send_code(code, dtree); // send the distance code
+                                               extra = Tree.extra_dbits[code];
+                                               if (extra !== 0) {
+                                                       dist -= Tree.base_dist[code];
+                                                       send_bits(dist, extra); // send the extra distance bits
+                                               }
+                                       } // literal or match pair ?
+
+                                       // Check that the overlay between pending_buf and d_buf+l_buf is
+                                       // ok:
+                               } while (lx < last_lit);
+                       }
+
+                       send_code(END_BLOCK, ltree);
+                       last_eob_len = ltree[END_BLOCK * 2 + 1];
+               }
+
+               // Flush the bit buffer and align the output on a byte boundary
+               function bi_windup() {
+                       if (bi_valid > 8) {
+                               put_short(bi_buf);
+                       } else if (bi_valid > 0) {
+                               put_byte(bi_buf & 0xff);
+                       }
+                       bi_buf = 0;
+                       bi_valid = 0;
+               }
+
+               // Copy a stored block, storing first the length and its
+               // one's complement if requested.
+               function copy_block(buf, // the input data
+               len, // its length
+               header // true if block header must be written
+               ) {
+                       bi_windup(); // align on byte boundary
+                       last_eob_len = 8; // enough lookahead for inflate
+
+                       if (header) {
+                               put_short(len);
+                               put_short(~len);
+                       }
+
+                       that.pending_buf.set(window.subarray(buf, buf + len), that.pending);
+                       that.pending += len;
+               }
+
+               // Send a stored block
+               function _tr_stored_block(buf, // input block
+               stored_len, // length of input block
+               eof // true if this is the last block for a file
+               ) {
+                       send_bits((STORED_BLOCK << 1) + (eof ? 1 : 0), 3); // send block type
+                       copy_block(buf, stored_len, true); // with header
+               }
+
+               // Determine the best encoding for the current block: dynamic trees, static
+               // trees or store, and output the encoded block to the zip file.
+               function _tr_flush_block(buf, // input block, or NULL if too old
+               stored_len, // length of input block
+               eof // true if this is the last block for a file
+               ) {
+                       var opt_lenb, static_lenb;// opt_len and static_len in bytes
+                       var max_blindex = 0; // index of last bit length code of non zero freq
+
+                       // Build the Huffman trees unless a stored block is forced
+                       if (level > 0) {
+                               // Construct the literal and distance trees
+                               l_desc.build_tree(that);
+
+                               d_desc.build_tree(that);
+
+                               // At this point, opt_len and static_len are the total bit lengths
+                               // of
+                               // the compressed block data, excluding the tree representations.
+
+                               // Build the bit length tree for the above two trees, and get the
+                               // index
+                               // in bl_order of the last bit length code to send.
+                               max_blindex = build_bl_tree();
+
+                               // Determine the best encoding. Compute first the block length in
+                               // bytes
+                               opt_lenb = (that.opt_len + 3 + 7) >>> 3;
+                               static_lenb = (that.static_len + 3 + 7) >>> 3;
+
+                               if (static_lenb <= opt_lenb)
+                                       opt_lenb = static_lenb;
+                       } else {
+                               opt_lenb = static_lenb = stored_len + 5; // force a stored block
+                       }
+
+                       if ((stored_len + 4 <= opt_lenb) && buf != -1) {
+                               // 4: two words for the lengths
+                               // The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE.
+                               // Otherwise we can't have processed more than WSIZE input bytes
+                               // since
+                               // the last block flush, because compression would have been
+                               // successful. If LIT_BUFSIZE <= WSIZE, it is never too late to
+                               // transform a block into a stored block.
+                               _tr_stored_block(buf, stored_len, eof);
+                       } else if (static_lenb == opt_lenb) {
+                               send_bits((STATIC_TREES << 1) + (eof ? 1 : 0), 3);
+                               compress_block(StaticTree.static_ltree, StaticTree.static_dtree);
+                       } else {
+                               send_bits((DYN_TREES << 1) + (eof ? 1 : 0), 3);
+                               send_all_trees(l_desc.max_code + 1, d_desc.max_code + 1, max_blindex + 1);
+                               compress_block(dyn_ltree, dyn_dtree);
+                       }
+
+                       // The above check is made mod 2^32, for files larger than 512 MB
+                       // and uLong implemented on 32 bits.
+
+                       init_block();
+
+                       if (eof) {
+                               bi_windup();
+                       }
+               }
+
+               function flush_block_only(eof) {
+                       _tr_flush_block(block_start >= 0 ? block_start : -1, strstart - block_start, eof);
+                       block_start = strstart;
+                       strm.flush_pending();
+               }
+
+               // Fill the window when the lookahead becomes insufficient.
+               // Updates strstart and lookahead.
+               //
+               // IN assertion: lookahead < MIN_LOOKAHEAD
+               // OUT assertions: strstart <= window_size-MIN_LOOKAHEAD
+               // At least one byte has been read, or avail_in === 0; reads are
+               // performed for at least two bytes (required for the zip translate_eol
+               // option -- not supported here).
+               function fill_window() {
+                       var n, m;
+                       var p;
+                       var more; // Amount of free space at the end of the window.
+
+                       do {
+                               more = (window_size - lookahead - strstart);
+
+                               // Deal with !@#$% 64K limit:
+                               if (more === 0 && strstart === 0 && lookahead === 0) {
+                                       more = w_size;
+                               } else if (more == -1) {
+                                       // Very unlikely, but possible on 16 bit machine if strstart ==
+                                       // 0
+                                       // and lookahead == 1 (input done one byte at time)
+                                       more--;
+
+                                       // If the window is almost full and there is insufficient
+                                       // lookahead,
+                                       // move the upper half to the lower one to make room in the
+                                       // upper half.
+                               } else if (strstart >= w_size + w_size - MIN_LOOKAHEAD) {
+                                       window.set(window.subarray(w_size, w_size + w_size), 0);
+
+                                       match_start -= w_size;
+                                       strstart -= w_size; // we now have strstart >= MAX_DIST
+                                       block_start -= w_size;
+
+                                       // Slide the hash table (could be avoided with 32 bit values
+                                       // at the expense of memory usage). We slide even when level ==
+                                       // 0
+                                       // to keep the hash table consistent if we switch back to level
+                                       // > 0
+                                       // later. (Using level 0 permanently is not an optimal usage of
+                                       // zlib, so we don't care about this pathological case.)
+
+                                       n = hash_size;
+                                       p = n;
+                                       do {
+                                               m = (head[--p] & 0xffff);
+                                               head[p] = (m >= w_size ? m - w_size : 0);
+                                       } while (--n !== 0);
+
+                                       n = w_size;
+                                       p = n;
+                                       do {
+                                               m = (prev[--p] & 0xffff);
+                                               prev[p] = (m >= w_size ? m - w_size : 0);
+                                               // If n is not on any hash chain, prev[n] is garbage but
+                                               // its value will never be used.
+                                       } while (--n !== 0);
+                                       more += w_size;
+                               }
+
+                               if (strm.avail_in === 0)
+                                       return;
+
+                               // If there was no sliding:
+                               // strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 &&
+                               // more == window_size - lookahead - strstart
+                               // => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1)
+                               // => more >= window_size - 2*WSIZE + 2
+                               // In the BIG_MEM or MMAP case (not yet supported),
+                               // window_size == input_size + MIN_LOOKAHEAD &&
+                               // strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD.
+                               // Otherwise, window_size == 2*WSIZE so more >= 2.
+                               // If there was sliding, more >= WSIZE. So in all cases, more >= 2.
+
+                               n = strm.read_buf(window, strstart + lookahead, more);
+                               lookahead += n;
+
+                               // Initialize the hash value now that we have some input:
+                               if (lookahead >= MIN_MATCH) {
+                                       ins_h = window[strstart] & 0xff;
+                                       ins_h = (((ins_h) << hash_shift) ^ (window[strstart + 1] & 0xff)) & hash_mask;
+                               }
+                               // If the whole input has less than MIN_MATCH bytes, ins_h is
+                               // garbage,
+                               // but this is not important since only literal bytes will be
+                               // emitted.
+                       } while (lookahead < MIN_LOOKAHEAD && strm.avail_in !== 0);
+               }
+
+               // Copy without compression as much as possible from the input stream,
+               // return
+               // the current block state.
+               // This function does not insert new strings in the dictionary since
+               // uncompressible data is probably not useful. This function is used
+               // only for the level=0 compression option.
+               // NOTE: this function should be optimized to avoid extra copying from
+               // window to pending_buf.
+               function deflate_stored(flush) {
+                       // Stored blocks are limited to 0xffff bytes, pending_buf is limited
+                       // to pending_buf_size, and each stored block has a 5 byte header:
+
+                       var max_block_size = 0xffff;
+                       var max_start;
+
+                       if (max_block_size > pending_buf_size - 5) {
+                               max_block_size = pending_buf_size - 5;
+                       }
+
+                       // Copy as much as possible from input to output:
+                       while (true) {
+                               // Fill the window as much as possible:
+                               if (lookahead <= 1) {
+                                       fill_window();
+                                       if (lookahead === 0 && flush == Z_NO_FLUSH)
+                                               return NeedMore;
+                                       if (lookahead === 0)
+                                               break; // flush the current block
+                               }
+
+                               strstart += lookahead;
+                               lookahead = 0;
+
+                               // Emit a stored block if pending_buf will be full:
+                               max_start = block_start + max_block_size;
+                               if (strstart === 0 || strstart >= max_start) {
+                                       // strstart === 0 is possible when wraparound on 16-bit machine
+                                       lookahead = (strstart - max_start);
+                                       strstart = max_start;
+
+                                       flush_block_only(false);
+                                       if (strm.avail_out === 0)
+                                               return NeedMore;
+
+                               }
+
+                               // Flush if we may have to slide, otherwise block_start may become
+                               // negative and the data will be gone:
+                               if (strstart - block_start >= w_size - MIN_LOOKAHEAD) {
+                                       flush_block_only(false);
+                                       if (strm.avail_out === 0)
+                                               return NeedMore;
+                               }
+                       }
+
+                       flush_block_only(flush == Z_FINISH);
+                       if (strm.avail_out === 0)
+                               return (flush == Z_FINISH) ? FinishStarted : NeedMore;
+
+                       return flush == Z_FINISH ? FinishDone : BlockDone;
+               }
+
+               function longest_match(cur_match) {
+                       var chain_length = max_chain_length; // max hash chain length
+                       var scan = strstart; // current string
+                       var match; // matched string
+                       var len; // length of current match
+                       var best_len = prev_length; // best match length so far
+                       var limit = strstart > (w_size - MIN_LOOKAHEAD) ? strstart - (w_size - MIN_LOOKAHEAD) : 0;
+                       var _nice_match = nice_match;
+
+                       // Stop when cur_match becomes <= limit. To simplify the code,
+                       // we prevent matches with the string of window index 0.
+
+                       var wmask = w_mask;
+
+                       var strend = strstart + MAX_MATCH;
+                       var scan_end1 = window[scan + best_len - 1];
+                       var scan_end = window[scan + best_len];
+
+                       // The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of
+                       // 16.
+                       // It is easy to get rid of this optimization if necessary.
+
+                       // Do not waste too much time if we already have a good match:
+                       if (prev_length >= good_match) {
+                               chain_length >>= 2;
+                       }
+
+                       // Do not look for matches beyond the end of the input. This is
+                       // necessary
+                       // to make deflate deterministic.
+                       if (_nice_match > lookahead)
+                               _nice_match = lookahead;
+
+                       do {
+                               match = cur_match;
+
+                               // Skip to next match if the match length cannot increase
+                               // or if the match length is less than 2:
+                               if (window[match + best_len] != scan_end || window[match + best_len - 1] != scan_end1 || window[match] != window[scan]
+                                               || window[++match] != window[scan + 1])
+                                       continue;
+
+                               // The check at best_len-1 can be removed because it will be made
+                               // again later. (This heuristic is not always a win.)
+                               // It is not necessary to compare scan[2] and match[2] since they
+                               // are always equal when the other bytes match, given that
+                               // the hash keys are equal and that HASH_BITS >= 8.
+                               scan += 2;
+                               match++;
+
+                               // We check for insufficient lookahead only every 8th comparison;
+                               // the 256th check will be made at strstart+258.
+                               do {
+                               } while (window[++scan] == window[++match] && window[++scan] == window[++match] && window[++scan] == window[++match]
+                                               && window[++scan] == window[++match] && window[++scan] == window[++match] && window[++scan] == window[++match]
+                                               && window[++scan] == window[++match] && window[++scan] == window[++match] && scan < strend);
+
+                               len = MAX_MATCH - (strend - scan);
+                               scan = strend - MAX_MATCH;
+
+                               if (len > best_len) {
+                                       match_start = cur_match;
+                                       best_len = len;
+                                       if (len >= _nice_match)
+                                               break;
+                                       scan_end1 = window[scan + best_len - 1];
+                                       scan_end = window[scan + best_len];
+                               }
+
+                       } while ((cur_match = (prev[cur_match & wmask] & 0xffff)) > limit && --chain_length !== 0);
+
+                       if (best_len <= lookahead)
+                               return best_len;
+                       return lookahead;
+               }
+
+               // Compress as much as possible from the input stream, return the current
+               // block state.
+               // This function does not perform lazy evaluation of matches and inserts
+               // new strings in the dictionary only for unmatched strings or for short
+               // matches. It is used only for the fast compression options.
+               function deflate_fast(flush) {
+                       // short hash_head = 0; // head of the hash chain
+                       var hash_head = 0; // head of the hash chain
+                       var bflush; // set if current block must be flushed
+
+                       while (true) {
+                               // Make sure that we always have enough lookahead, except
+                               // at the end of the input file. We need MAX_MATCH bytes
+                               // for the next match, plus MIN_MATCH bytes to insert the
+                               // string following the next match.
+                               if (lookahead < MIN_LOOKAHEAD) {
+                                       fill_window();
+                                       if (lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) {
+                                               return NeedMore;
+                                       }
+                                       if (lookahead === 0)
+                                               break; // flush the current block
+                               }
+
+                               // Insert the string window[strstart .. strstart+2] in the
+                               // dictionary, and set hash_head to the head of the hash chain:
+                               if (lookahead >= MIN_MATCH) {
+                                       ins_h = (((ins_h) << hash_shift) ^ (window[(strstart) + (MIN_MATCH - 1)] & 0xff)) & hash_mask;
+
+                                       // prev[strstart&w_mask]=hash_head=head[ins_h];
+                                       hash_head = (head[ins_h] & 0xffff);
+                                       prev[strstart & w_mask] = head[ins_h];
+                                       head[ins_h] = strstart;
+                               }
+
+                               // Find the longest match, discarding those <= prev_length.
+                               // At this point we have always match_length < MIN_MATCH
+
+                               if (hash_head !== 0 && ((strstart - hash_head) & 0xffff) <= w_size - MIN_LOOKAHEAD) {
+                                       // To simplify the code, we prevent matches with the string
+                                       // of window index 0 (in particular we have to avoid a match
+                                       // of the string with itself at the start of the input file).
+                                       if (strategy != Z_HUFFMAN_ONLY) {
+                                               match_length = longest_match(hash_head);
+                                       }
+                                       // longest_match() sets match_start
+                               }
+                               if (match_length >= MIN_MATCH) {
+                                       // check_match(strstart, match_start, match_length);
+
+                                       bflush = _tr_tally(strstart - match_start, match_length - MIN_MATCH);
+
+                                       lookahead -= match_length;
+
+                                       // Insert new strings in the hash table only if the match length
+                                       // is not too large. This saves time but degrades compression.
+                                       if (match_length <= max_lazy_match && lookahead >= MIN_MATCH) {
+                                               match_length--; // string at strstart already in hash table
+                                               do {
+                                                       strstart++;
+
+                                                       ins_h = ((ins_h << hash_shift) ^ (window[(strstart) + (MIN_MATCH - 1)] & 0xff)) & hash_mask;
+                                                       // prev[strstart&w_mask]=hash_head=head[ins_h];
+                                                       hash_head = (head[ins_h] & 0xffff);
+                                                       prev[strstart & w_mask] = head[ins_h];
+                                                       head[ins_h] = strstart;
+
+                                                       // strstart never exceeds WSIZE-MAX_MATCH, so there are
+                                                       // always MIN_MATCH bytes ahead.
+                                               } while (--match_length !== 0);
+                                               strstart++;
+                                       } else {
+                                               strstart += match_length;
+                                               match_length = 0;
+                                               ins_h = window[strstart] & 0xff;
+
+                                               ins_h = (((ins_h) << hash_shift) ^ (window[strstart + 1] & 0xff)) & hash_mask;
+                                               // If lookahead < MIN_MATCH, ins_h is garbage, but it does
+                                               // not
+                                               // matter since it will be recomputed at next deflate call.
+                                       }
+                               } else {
+                                       // No match, output a literal byte
+
+                                       bflush = _tr_tally(0, window[strstart] & 0xff);
+                                       lookahead--;
+                                       strstart++;
+                               }
+                               if (bflush) {
+
+                                       flush_block_only(false);
+                                       if (strm.avail_out === 0)
+                                               return NeedMore;
+                               }
+                       }
+
+                       flush_block_only(flush == Z_FINISH);
+                       if (strm.avail_out === 0) {
+                               if (flush == Z_FINISH)
+                                       return FinishStarted;
+                               else
+                                       return NeedMore;
+                       }
+                       return flush == Z_FINISH ? FinishDone : BlockDone;
+               }
+
+               // Same as above, but achieves better compression. We use a lazy
+               // evaluation for matches: a match is finally adopted only if there is
+               // no better match at the next window position.
+               function deflate_slow(flush) {
+                       // short hash_head = 0; // head of hash chain
+                       var hash_head = 0; // head of hash chain
+                       var bflush; // set if current block must be flushed
+                       var max_insert;
+
+                       // Process the input block.
+                       while (true) {
+                               // Make sure that we always have enough lookahead, except
+                               // at the end of the input file. We need MAX_MATCH bytes
+                               // for the next match, plus MIN_MATCH bytes to insert the
+                               // string following the next match.
+
+                               if (lookahead < MIN_LOOKAHEAD) {
+                                       fill_window();
+                                       if (lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) {
+                                               return NeedMore;
+                                       }
+                                       if (lookahead === 0)
+                                               break; // flush the current block
+                               }
+
+                               // Insert the string window[strstart .. strstart+2] in the
+                               // dictionary, and set hash_head to the head of the hash chain:
+
+                               if (lookahead >= MIN_MATCH) {
+                                       ins_h = (((ins_h) << hash_shift) ^ (window[(strstart) + (MIN_MATCH - 1)] & 0xff)) & hash_mask;
+                                       // prev[strstart&w_mask]=hash_head=head[ins_h];
+                                       hash_head = (head[ins_h] & 0xffff);
+                                       prev[strstart & w_mask] = head[ins_h];
+                                       head[ins_h] = strstart;
+                               }
+
+                               // Find the longest match, discarding those <= prev_length.
+                               prev_length = match_length;
+                               prev_match = match_start;
+                               match_length = MIN_MATCH - 1;
+
+                               if (hash_head !== 0 && prev_length < max_lazy_match && ((strstart - hash_head) & 0xffff) <= w_size - MIN_LOOKAHEAD) {
+                                       // To simplify the code, we prevent matches with the string
+                                       // of window index 0 (in particular we have to avoid a match
+                                       // of the string with itself at the start of the input file).
+
+                                       if (strategy != Z_HUFFMAN_ONLY) {
+                                               match_length = longest_match(hash_head);
+                                       }
+                                       // longest_match() sets match_start
+
+                                       if (match_length <= 5 && (strategy == Z_FILTERED || (match_length == MIN_MATCH && strstart - match_start > 4096))) {
+
+                                               // If prev_match is also MIN_MATCH, match_start is garbage
+                                               // but we will ignore the current match anyway.
+                                               match_length = MIN_MATCH - 1;
+                                       }
+                               }
+
+                               // If there was a match at the previous step and the current
+                               // match is not better, output the previous match:
+                               if (prev_length >= MIN_MATCH && match_length <= prev_length) {
+                                       max_insert = strstart + lookahead - MIN_MATCH;
+                                       // Do not insert strings in hash table beyond this.
+
+                                       // check_match(strstart-1, prev_match, prev_length);
+
+                                       bflush = _tr_tally(strstart - 1 - prev_match, prev_length - MIN_MATCH);
+
+                                       // Insert in hash table all strings up to the end of the match.
+                                       // strstart-1 and strstart are already inserted. If there is not
+                                       // enough lookahead, the last two strings are not inserted in
+                                       // the hash table.
+                                       lookahead -= prev_length - 1;
+                                       prev_length -= 2;
+                                       do {
+                                               if (++strstart <= max_insert) {
+                                                       ins_h = (((ins_h) << hash_shift) ^ (window[(strstart) + (MIN_MATCH - 1)] & 0xff)) & hash_mask;
+                                                       // prev[strstart&w_mask]=hash_head=head[ins_h];
+                                                       hash_head = (head[ins_h] & 0xffff);
+                                                       prev[strstart & w_mask] = head[ins_h];
+                                                       head[ins_h] = strstart;
+                                               }
+                                       } while (--prev_length !== 0);
+                                       match_available = 0;
+                                       match_length = MIN_MATCH - 1;
+                                       strstart++;
+
+                                       if (bflush) {
+                                               flush_block_only(false);
+                                               if (strm.avail_out === 0)
+                                                       return NeedMore;
+                                       }
+                               } else if (match_available !== 0) {
+
+                                       // If there was no match at the previous position, output a
+                                       // single literal. If there was a match but the current match
+                                       // is longer, truncate the previous match to a single literal.
+
+                                       bflush = _tr_tally(0, window[strstart - 1] & 0xff);
+
+                                       if (bflush) {
+                                               flush_block_only(false);
+                                       }
+                                       strstart++;
+                                       lookahead--;
+                                       if (strm.avail_out === 0)
+                                               return NeedMore;
+                               } else {
+                                       // There is no previous match to compare with, wait for
+                                       // the next step to decide.
+
+                                       match_available = 1;
+                                       strstart++;
+                                       lookahead--;
+                               }
+                       }
+
+                       if (match_available !== 0) {
+                               bflush = _tr_tally(0, window[strstart - 1] & 0xff);
+                               match_available = 0;
+                       }
+                       flush_block_only(flush == Z_FINISH);
+
+                       if (strm.avail_out === 0) {
+                               if (flush == Z_FINISH)
+                                       return FinishStarted;
+                               else
+                                       return NeedMore;
+                       }
+
+                       return flush == Z_FINISH ? FinishDone : BlockDone;
+               }
+
+               function deflateReset(strm) {
+                       strm.total_in = strm.total_out = 0;
+                       strm.msg = null; //
+                       
+                       that.pending = 0;
+                       that.pending_out = 0;
+
+                       status = BUSY_STATE;
+
+                       last_flush = Z_NO_FLUSH;
+
+                       tr_init();
+                       lm_init();
+                       return Z_OK;
+               }
+
+               that.deflateInit = function(strm, _level, bits, _method, memLevel, _strategy) {
+                       if (!_method)
+                               _method = Z_DEFLATED;
+                       if (!memLevel)
+                               memLevel = DEF_MEM_LEVEL;
+                       if (!_strategy)
+                               _strategy = Z_DEFAULT_STRATEGY;
+
+                       // byte[] my_version=ZLIB_VERSION;
+
+                       //
+                       // if (!version || version[0] != my_version[0]
+                       // || stream_size != sizeof(z_stream)) {
+                       // return Z_VERSION_ERROR;
+                       // }
+
+                       strm.msg = null;
+
+                       if (_level == Z_DEFAULT_COMPRESSION)
+                               _level = 6;
+
+                       if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || _method != Z_DEFLATED || bits < 9 || bits > 15 || _level < 0 || _level > 9 || _strategy < 0
+                                       || _strategy > Z_HUFFMAN_ONLY) {
+                               return Z_STREAM_ERROR;
+                       }
+
+                       strm.dstate = that;
+
+                       w_bits = bits;
+                       w_size = 1 << w_bits;
+                       w_mask = w_size - 1;
+
+                       hash_bits = memLevel + 7;
+                       hash_size = 1 << hash_bits;
+                       hash_mask = hash_size - 1;
+                       hash_shift = Math.floor((hash_bits + MIN_MATCH - 1) / MIN_MATCH);
+
+                       window = new Uint8Array(w_size * 2);
+                       prev = [];
+                       head = [];
+
+                       lit_bufsize = 1 << (memLevel + 6); // 16K elements by default
+
+                       // We overlay pending_buf and d_buf+l_buf. This works since the average
+                       // output size for (length,distance) codes is <= 24 bits.
+                       that.pending_buf = new Uint8Array(lit_bufsize * 4);
+                       pending_buf_size = lit_bufsize * 4;
+
+                       d_buf = Math.floor(lit_bufsize / 2);
+                       l_buf = (1 + 2) * lit_bufsize;
+
+                       level = _level;
+
+                       strategy = _strategy;
+                       method = _method & 0xff;
+
+                       return deflateReset(strm);
+               };
+
+               that.deflateEnd = function() {
+                       if (status != INIT_STATE && status != BUSY_STATE && status != FINISH_STATE) {
+                               return Z_STREAM_ERROR;
+                       }
+                       // Deallocate in reverse order of allocations:
+                       that.pending_buf = null;
+                       head = null;
+                       prev = null;
+                       window = null;
+                       // free
+                       that.dstate = null;
+                       return status == BUSY_STATE ? Z_DATA_ERROR : Z_OK;
+               };
+
+               that.deflateParams = function(strm, _level, _strategy) {
+                       var err = Z_OK;
+
+                       if (_level == Z_DEFAULT_COMPRESSION) {
+                               _level = 6;
+                       }
+                       if (_level < 0 || _level > 9 || _strategy < 0 || _strategy > Z_HUFFMAN_ONLY) {
+                               return Z_STREAM_ERROR;
+                       }
+
+                       if (config_table[level].func != config_table[_level].func && strm.total_in !== 0) {
+                               // Flush the last buffer:
+                               err = strm.deflate(Z_PARTIAL_FLUSH);
+                       }
+
+                       if (level != _level) {
+                               level = _level;
+                               max_lazy_match = config_table[level].max_lazy;
+                               good_match = config_table[level].good_length;
+                               nice_match = config_table[level].nice_length;
+                               max_chain_length = config_table[level].max_chain;
+                       }
+                       strategy = _strategy;
+                       return err;
+               };
+
+               that.deflateSetDictionary = function(strm, dictionary, dictLength) {
+                       var length = dictLength;
+                       var n, index = 0;
+
+                       if (!dictionary || status != INIT_STATE)
+                               return Z_STREAM_ERROR;
+
+                       if (length < MIN_MATCH)
+                               return Z_OK;
+                       if (length > w_size - MIN_LOOKAHEAD) {
+                               length = w_size - MIN_LOOKAHEAD;
+                               index = dictLength - length; // use the tail of the dictionary
+                       }
+                       window.set(dictionary.subarray(index, index + length), 0);
+
+                       strstart = length;
+                       block_start = length;
+
+                       // Insert all strings in the hash table (except for the last two bytes).
+                       // s->lookahead stays null, so s->ins_h will be recomputed at the next
+                       // call of fill_window.
+
+                       ins_h = window[0] & 0xff;
+                       ins_h = (((ins_h) << hash_shift) ^ (window[1] & 0xff)) & hash_mask;
+
+                       for (n = 0; n <= length - MIN_MATCH; n++) {
+                               ins_h = (((ins_h) << hash_shift) ^ (window[(n) + (MIN_MATCH - 1)] & 0xff)) & hash_mask;
+                               prev[n & w_mask] = head[ins_h];
+                               head[ins_h] = n;
+                       }
+                       return Z_OK;
+               };
+
+               that.deflate = function(_strm, flush) {
+                       var i, header, level_flags, old_flush, bstate;
+
+                       if (flush > Z_FINISH || flush < 0) {
+                               return Z_STREAM_ERROR;
+                       }
+
+                       if (!_strm.next_out || (!_strm.next_in && _strm.avail_in !== 0) || (status == FINISH_STATE && flush != Z_FINISH)) {
+                               _strm.msg = z_errmsg[Z_NEED_DICT - (Z_STREAM_ERROR)];
+                               return Z_STREAM_ERROR;
+                       }
+                       if (_strm.avail_out === 0) {
+                               _strm.msg = z_errmsg[Z_NEED_DICT - (Z_BUF_ERROR)];
+                               return Z_BUF_ERROR;
+                       }
+
+                       strm = _strm; // just in case
+                       old_flush = last_flush;
+                       last_flush = flush;
+
+                       // Write the zlib header
+                       if (status == INIT_STATE) {
+                               header = (Z_DEFLATED + ((w_bits - 8) << 4)) << 8;
+                               level_flags = ((level - 1) & 0xff) >> 1;
+
+                               if (level_flags > 3)
+                                       level_flags = 3;
+                               header |= (level_flags << 6);
+                               if (strstart !== 0)
+                                       header |= PRESET_DICT;
+                               header += 31 - (header % 31);
+
+                               status = BUSY_STATE;
+                               putShortMSB(header);
+                       }
+
+                       // Flush as much pending output as possible
+                       if (that.pending !== 0) {
+                               strm.flush_pending();
+                               if (strm.avail_out === 0) {
+                                       // console.log(" avail_out==0");
+                                       // Since avail_out is 0, deflate will be called again with
+                                       // more output space, but possibly with both pending and
+                                       // avail_in equal to zero. There won't be anything to do,
+                                       // but this is not an error situation so make sure we
+                                       // return OK instead of BUF_ERROR at next call of deflate:
+                                       last_flush = -1;
+                                       return Z_OK;
+                               }
+
+                               // Make sure there is something to do and avoid duplicate
+                               // consecutive
+                               // flushes. For repeated and useless calls with Z_FINISH, we keep
+                               // returning Z_STREAM_END instead of Z_BUFF_ERROR.
+                       } else if (strm.avail_in === 0 && flush <= old_flush && flush != Z_FINISH) {
+                               strm.msg = z_errmsg[Z_NEED_DICT - (Z_BUF_ERROR)];
+                               return Z_BUF_ERROR;
+                       }
+
+                       // User must not provide more input after the first FINISH:
+                       if (status == FINISH_STATE && strm.avail_in !== 0) {
+                               _strm.msg = z_errmsg[Z_NEED_DICT - (Z_BUF_ERROR)];
+                               return Z_BUF_ERROR;
+                       }
+
+                       // Start a new block or continue the current one.
+                       if (strm.avail_in !== 0 || lookahead !== 0 || (flush != Z_NO_FLUSH && status != FINISH_STATE)) {
+                               bstate = -1;
+                               switch (config_table[level].func) {
+                               case STORED:
+                                       bstate = deflate_stored(flush);
+                                       break;
+                               case FAST:
+                                       bstate = deflate_fast(flush);
+                                       break;
+                               case SLOW:
+                                       bstate = deflate_slow(flush);
+                                       break;
+                               default:
+                               }
+
+                               if (bstate == FinishStarted || bstate == FinishDone) {
+                                       status = FINISH_STATE;
+                               }
+                               if (bstate == NeedMore || bstate == FinishStarted) {
+                                       if (strm.avail_out === 0) {
+                                               last_flush = -1; // avoid BUF_ERROR next call, see above
+                                       }
+                                       return Z_OK;
+                                       // If flush != Z_NO_FLUSH && avail_out === 0, the next call
+                                       // of deflate should use the same flush parameter to make sure
+                                       // that the flush is complete. So we don't have to output an
+                                       // empty block here, this will be done at next call. This also
+                                       // ensures that for a very small output buffer, we emit at most
+                                       // one empty block.
+                               }
+
+                               if (bstate == BlockDone) {
+                                       if (flush == Z_PARTIAL_FLUSH) {
+                                               _tr_align();
+                                       } else { // FULL_FLUSH or SYNC_FLUSH
+                                               _tr_stored_block(0, 0, false);
+                                               // For a full flush, this empty block will be recognized
+                                               // as a special marker by inflate_sync().
+                                               if (flush == Z_FULL_FLUSH) {
+                                                       // state.head[s.hash_size-1]=0;
+                                                       for (i = 0; i < hash_size/*-1*/; i++)
+                                                               // forget history
+                                                               head[i] = 0;
+                                               }
+                                       }
+                                       strm.flush_pending();
+                                       if (strm.avail_out === 0) {
+                                               last_flush = -1; // avoid BUF_ERROR at next call, see above
+                                               return Z_OK;
+                                       }
+                               }
+                       }
+
+                       if (flush != Z_FINISH)
+                               return Z_OK;
+                       return Z_STREAM_END;
+               };
+       }
+
+       // ZStream
+
+       function ZStream() {
+               var that = this;
+               that.next_in_index = 0;
+               that.next_out_index = 0;
+               // that.next_in; // next input byte
+               that.avail_in = 0; // number of bytes available at next_in
+               that.total_in = 0; // total nb of input bytes read so far
+               // that.next_out; // next output byte should be put there
+               that.avail_out = 0; // remaining free space at next_out
+               that.total_out = 0; // total nb of bytes output so far
+               // that.msg;
+               // that.dstate;
+       }
+
+       ZStream.prototype = {
+               deflateInit : function(level, bits) {
+                       var that = this;
+                       that.dstate = new Deflate();
+                       if (!bits)
+                               bits = MAX_BITS;
+                       return that.dstate.deflateInit(that, level, bits);
+               },
+
+               deflate : function(flush) {
+                       var that = this;
+                       if (!that.dstate) {
+                               return Z_STREAM_ERROR;
+                       }
+                       return that.dstate.deflate(that, flush);
+               },
+
+               deflateEnd : function() {
+                       var that = this;
+                       if (!that.dstate)
+                               return Z_STREAM_ERROR;
+                       var ret = that.dstate.deflateEnd();
+                       that.dstate = null;
+                       return ret;
+               },
+
+               deflateParams : function(level, strategy) {
+                       var that = this;
+                       if (!that.dstate)
+                               return Z_STREAM_ERROR;
+                       return that.dstate.deflateParams(that, level, strategy);
+               },
+
+               deflateSetDictionary : function(dictionary, dictLength) {
+                       var that = this;
+                       if (!that.dstate)
+                               return Z_STREAM_ERROR;
+                       return that.dstate.deflateSetDictionary(that, dictionary, dictLength);
+               },
+
+               // Read a new buffer from the current input stream, update the
+               // total number of bytes read. All deflate() input goes through
+               // this function so some applications may wish to modify it to avoid
+               // allocating a large strm->next_in buffer and copying from it.
+               // (See also flush_pending()).
+               read_buf : function(buf, start, size) {
+                       var that = this;
+                       var len = that.avail_in;
+                       if (len > size)
+                               len = size;
+                       if (len === 0)
+                               return 0;
+                       that.avail_in -= len;
+                       buf.set(that.next_in.subarray(that.next_in_index, that.next_in_index + len), start);
+                       that.next_in_index += len;
+                       that.total_in += len;
+                       return len;
+               },
+
+               // Flush as much pending output as possible. All deflate() output goes
+               // through this function so some applications may wish to modify it
+               // to avoid allocating a large strm->next_out buffer and copying into it.
+               // (See also read_buf()).
+               flush_pending : function() {
+                       var that = this;
+                       var len = that.dstate.pending;
+
+                       if (len > that.avail_out)
+                               len = that.avail_out;
+                       if (len === 0)
+                               return;
+
+                       // if (that.dstate.pending_buf.length <= that.dstate.pending_out || that.next_out.length <= that.next_out_index
+                       // || that.dstate.pending_buf.length < (that.dstate.pending_out + len) || that.next_out.length < (that.next_out_index +
+                       // len)) {
+                       // console.log(that.dstate.pending_buf.length + ", " + that.dstate.pending_out + ", " + that.next_out.length + ", " +
+                       // that.next_out_index + ", " + len);
+                       // console.log("avail_out=" + that.avail_out);
+                       // }
+
+                       that.next_out.set(that.dstate.pending_buf.subarray(that.dstate.pending_out, that.dstate.pending_out + len), that.next_out_index);
+
+                       that.next_out_index += len;
+                       that.dstate.pending_out += len;
+                       that.total_out += len;
+                       that.avail_out -= len;
+                       that.dstate.pending -= len;
+                       if (that.dstate.pending === 0) {
+                               that.dstate.pending_out = 0;
+                       }
+               }
+       };
+
+       // Deflater
+
+       function Deflater(level) {
+               var that = this;
+               var z = new ZStream();
+               var bufsize = 512;
+               var flush = Z_NO_FLUSH;
+               var buf = new Uint8Array(bufsize);
+
+               if (typeof level == "undefined")
+                       level = Z_DEFAULT_COMPRESSION;
+               z.deflateInit(level);
+               z.next_out = buf;
+
+               that.append = function(data, onprogress) {
+                       var err, buffers = [], lastIndex = 0, bufferIndex = 0, bufferSize = 0, array;
+                       if (!data.length)
+                               return;
+                       z.next_in_index = 0;
+                       z.next_in = data;
+                       z.avail_in = data.length;
+                       do {
+                               z.next_out_index = 0;
+                               z.avail_out = bufsize;
+                               err = z.deflate(flush);
+                               if (err != Z_OK)
+                                       throw "deflating: " + z.msg;
+                               if (z.next_out_index)
+                                       if (z.next_out_index == bufsize)
+                                               buffers.push(new Uint8Array(buf));
+                                       else
+                                               buffers.push(new Uint8Array(buf.subarray(0, z.next_out_index)));
+                               bufferSize += z.next_out_index;
+                               if (onprogress && z.next_in_index > 0 && z.next_in_index != lastIndex) {
+                                       onprogress(z.next_in_index);
+                                       lastIndex = z.next_in_index;
+                               }
+                       } while (z.avail_in > 0 || z.avail_out === 0);
+                       array = new Uint8Array(bufferSize);
+                       buffers.forEach(function(chunk) {
+                               array.set(chunk, bufferIndex);
+                               bufferIndex += chunk.length;
+                       });
+                       return array;
+               };
+               that.flush = function() {
+                       var err, buffers = [], bufferIndex = 0, bufferSize = 0, array;
+                       do {
+                               z.next_out_index = 0;
+                               z.avail_out = bufsize;
+                               err = z.deflate(Z_FINISH);
+                               if (err != Z_STREAM_END && err != Z_OK)
+                                       throw "deflating: " + z.msg;
+                               if (bufsize - z.avail_out > 0)
+                                       buffers.push(new Uint8Array(buf.subarray(0, z.next_out_index)));
+                               bufferSize += z.next_out_index;
+                       } while (z.avail_in > 0 || z.avail_out === 0);
+                       z.deflateEnd();
+                       array = new Uint8Array(bufferSize);
+                       buffers.forEach(function(chunk) {
+                               array.set(chunk, bufferIndex);
+                               bufferIndex += chunk.length;
+                       });
+                       return array;
+               };
+       }
+
+       var deflater;
+
+       if (obj.zip)
+               obj.zip.Deflater = Deflater;
+       else {
+               deflater = new Deflater();
+               obj.addEventListener("message", function(event) {
+                       var message = event.data;
+                       if (message.init) {
+                               deflater = new Deflater(message.level);
+                               obj.postMessage({
+                                       oninit : true
+                               });
+                       }
+                       if (message.append)
+                               obj.postMessage({
+                                       onappend : true,
+                                       data : deflater.append(message.data, function(current) {
+                                               obj.postMessage({
+                                                       progress : true,
+                                                       current : current
+                                               });
+                                       })
+                               });
+                       if (message.flush)
+                               obj.postMessage({
+                                       onflush : true,
+                                       data : deflater.flush()
+                               });
+               }, false);
+       }
+
+})(EasyDeflate);
diff --git a/resources/lib/easy-deflate/easydeflate.js b/resources/lib/easy-deflate/easydeflate.js
new file mode 100644 (file)
index 0000000..342229e
--- /dev/null
@@ -0,0 +1,219 @@
+/**\r
+Copyright (c) 2013, Specialisterne.\r
+http://specialisterne.com/dk/\r
+All rights reserved.\r
+Authors:\r
+Jacob Christian Munch-Andersen\r
+\r
+Redistribution and use in source and binary forms, with or without\r
+modification, are permitted provided that the following conditions are met:\r
+\r
+1. Redistributions of source code must retain the above copyright notice, this\r
+   list of conditions and the following disclaimer.\r
+2. Redistributions in binary form must reproduce the above copyright notice,\r
+   this list of conditions and the following disclaimer in the documentation\r
+   and/or other materials provided with the distribution.\r
+\r
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND\r
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\r
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\r
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\r
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\r
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\r
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\r
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\r
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\r
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
+**/\r
+// For information and latest version see: https://github.com/Jacob-Christian-Munch-Andersen/Easy-Deflate\r
+(function(){\r
+\r
+var zip={};\r
+function UTF8encode(str){\r
+       var out=[];\r
+       var a;\r
+       var c,c2;\r
+       for(a=0;a<str.length;a++){\r
+               c=str.charCodeAt(a);\r
+               if(c<128){\r
+                       out.push(c);\r
+               }\r
+               else if(c<2048){\r
+                       out.push((c >> 6)+192);\r
+                       out.push((c & 63)+128);\r
+               }\r
+               else if(c<65536){\r
+                       if(c>=0xD800 && c<0xDC00){\r
+                               a++;\r
+                               if(a>=str.length){\r
+                                       return null;\r
+                               }\r
+                               c2=str.charCodeAt(a);\r
+                               if(c2>=0xDC00 && c2<0xE000){\r
+                                       c=65536+(c-0xD800)*1024+c2-0xDC00;\r
+                                       out.push((c >> 18)+240);\r
+                                       out.push(((c >> 12) & 63)+128);\r
+                                       out.push(((c >> 6) & 63)+128);\r
+                                       out.push((c & 63)+128);\r
+                               }\r
+                               else{\r
+                                       return null;\r
+                               }\r
+                       }\r
+                       else if(c>=0xDC00 && c<0xE000){\r
+                               return null;\r
+                       }\r
+                       else{\r
+                               out.push((c >> 12)+224);\r
+                               out.push(((c >> 6) & 63)+128);\r
+                               out.push((c & 63)+128);\r
+                       }\r
+               }\r
+               else{\r
+                       return null;\r
+               }\r
+       }\r
+       return new Uint8Array(out);\r
+}\r
+function UTF8decodeA(arrarr){\r
+       var result="";\r
+       var intermediate;\r
+       var minvalue;\r
+       var missing=0;\r
+       var a,b;\r
+       var arr;\r
+       var c;\r
+       var lower,upper;\r
+       for(a=0;a<arrarr.length;a++){\r
+               arr=arrarr[a];\r
+               for(b=0;b<arr.length;b++){\r
+                       c=arr[b];\r
+                       if(missing){\r
+                               if(c>127 && c<192){\r
+                                       intermediate=intermediate*64+c-128;\r
+                                       missing--;\r
+                                       if(!missing){\r
+                                               if(intermediate>=minvalue){\r
+                                                       if(intermediate>=65536){\r
+                                                               if(intermediate>0x10FFFF){\r
+                                                                       return null;\r
+                                                               }\r
+                                                               upper=(intermediate-65536)>>10;\r
+                                                               lower=intermediate%1024;\r
+                                                               result+=String.fromCharCode(upper+0xD800,lower+0xDC00);\r
+                                                       }\r
+                                                       else{\r
+                                                               result+=String.fromCharCode(intermediate);\r
+                                                       }\r
+                                               }\r
+                                               else{\r
+                                                       return null;\r
+                                               }\r
+                                       }\r
+                               }\r
+                               else{\r
+                                       return null;\r
+                               }\r
+                       }\r
+                       else if(c<128){\r
+                               result+=String.fromCharCode(c);\r
+                       }\r
+                       else if(c>191 && c<248){\r
+                               if(c<224){\r
+                                       intermediate=c-192;\r
+                                       minvalue=128;\r
+                                       missing=1;\r
+                               }\r
+                               else if(c<240){\r
+                                       intermediate=c-224;\r
+                                       minvalue=2048;\r
+                                       missing=2;\r
+                               }\r
+                               else{\r
+                                       intermediate=c-240;\r
+                                       minvalue=65536;\r
+                                       missing=3;\r
+                               }\r
+                       }\r
+                       else{\r
+                               return null;\r
+                       }\r
+               }\r
+       }\r
+       if(missing){\r
+               return null;\r
+       }\r
+       return result;\r
+}\r
+function deflate(str){\r
+       var a,c;\r
+       var readlen=50000;\r
+       var resulta=[];\r
+       var results="";\r
+       var b,d;\r
+       var zipper=new zip.Deflater(9);\r
+       for(a=0;a<str.length;a+=readlen){\r
+               d=UTF8encode(str.substr(a,readlen));\r
+               if(d===null){ //This error may be due to a 4 byte charachter being split, retry with a string that is 1 longer to fix it.\r
+                       d=UTF8encode(str.substr(a,readlen+1));\r
+                       a+=1;\r
+                       if(d===null){\r
+                               return null;\r
+                       }\r
+               }\r
+               b=zipper.append(d);\r
+               if(b.length!==0){\r
+                       resulta.push(b);\r
+               }\r
+       }\r
+       b=zipper.flush();\r
+       if(b.length!==0){\r
+               resulta.push(b);\r
+       }\r
+       for(a=0;a<resulta.length;a++){\r
+               for(c=0;c<resulta[a].length;c++){\r
+                       results+=String.fromCharCode(resulta[a][c]);\r
+               }\r
+       }\r
+       return "rawdeflate,"+btoa(results);\r
+}\r
+function inflate(dfl){\r
+       var unzipper=new zip.Inflater();\r
+       var resulta=[];\r
+       var dfls;\r
+       var a,c;\r
+       var b,d;\r
+       if(dfl.slice(0,11)!="rawdeflate,"){\r
+               return null;\r
+       }\r
+       try{\r
+               dfls=atob(dfl.slice(11));\r
+       }\r
+       catch(e){\r
+               return null;\r
+       }\r
+       try{\r
+               for(a=0;a<dfls.length;a+=50000){\r
+                       b=new Uint8Array(Math.min(50000,dfls.length-a));\r
+                       for(c=0;c<b.length;c++){\r
+                               b[c]=dfls.charCodeAt(c+a);\r
+                       }\r
+                       d=unzipper.append(b);\r
+                       if(d.length){\r
+                               resulta.push(d);\r
+                       }\r
+               }\r
+               return UTF8decodeA(resulta);\r
+       }\r
+       catch(e){\r
+               return null;\r
+       }\r
+}\r
+\r
+window.EasyDeflate = {\r
+       'zip': zip,\r
+       'inflate': inflate,\r
+       'deflate': deflate\r
+};\r
+\r
+})();
\ No newline at end of file
diff --git a/resources/lib/easy-deflate/inflate.js b/resources/lib/easy-deflate/inflate.js
new file mode 100644 (file)
index 0000000..22f83fc
--- /dev/null
@@ -0,0 +1,2163 @@
+/*
+ Copyright (c) 2013 Gildas Lormeau. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright 
+ notice, this list of conditions and the following disclaimer in 
+ the documentation and/or other materials provided with the distribution.
+
+ 3. The names of the authors may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+ INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * This program is based on JZlib 1.0.2 ymnk, JCraft,Inc.
+ * JZlib is based on zlib-1.1.3, so all credit should go authors
+ * Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu)
+ * and contributors of zlib.
+ */
+
+(function(obj) {
+
+       // Global
+       var MAX_BITS = 15;
+
+       var Z_OK = 0;
+       var Z_STREAM_END = 1;
+       var Z_NEED_DICT = 2;
+       var Z_STREAM_ERROR = -2;
+       var Z_DATA_ERROR = -3;
+       var Z_MEM_ERROR = -4;
+       var Z_BUF_ERROR = -5;
+
+       var inflate_mask = [ 0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000f, 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, 0x000001ff, 0x000003ff,
+                       0x000007ff, 0x00000fff, 0x00001fff, 0x00003fff, 0x00007fff, 0x0000ffff ];
+
+       var MANY = 1440;
+
+       // JZlib version : "1.0.2"
+       var Z_NO_FLUSH = 0;
+       var Z_FINISH = 4;
+
+       // InfTree
+       var fixed_bl = 9;
+       var fixed_bd = 5;
+
+       var fixed_tl = [ 96, 7, 256, 0, 8, 80, 0, 8, 16, 84, 8, 115, 82, 7, 31, 0, 8, 112, 0, 8, 48, 0, 9, 192, 80, 7, 10, 0, 8, 96, 0, 8, 32, 0, 9, 160, 0, 8, 0,
+                       0, 8, 128, 0, 8, 64, 0, 9, 224, 80, 7, 6, 0, 8, 88, 0, 8, 24, 0, 9, 144, 83, 7, 59, 0, 8, 120, 0, 8, 56, 0, 9, 208, 81, 7, 17, 0, 8, 104, 0, 8, 40,
+                       0, 9, 176, 0, 8, 8, 0, 8, 136, 0, 8, 72, 0, 9, 240, 80, 7, 4, 0, 8, 84, 0, 8, 20, 85, 8, 227, 83, 7, 43, 0, 8, 116, 0, 8, 52, 0, 9, 200, 81, 7, 13,
+                       0, 8, 100, 0, 8, 36, 0, 9, 168, 0, 8, 4, 0, 8, 132, 0, 8, 68, 0, 9, 232, 80, 7, 8, 0, 8, 92, 0, 8, 28, 0, 9, 152, 84, 7, 83, 0, 8, 124, 0, 8, 60,
+                       0, 9, 216, 82, 7, 23, 0, 8, 108, 0, 8, 44, 0, 9, 184, 0, 8, 12, 0, 8, 140, 0, 8, 76, 0, 9, 248, 80, 7, 3, 0, 8, 82, 0, 8, 18, 85, 8, 163, 83, 7,
+                       35, 0, 8, 114, 0, 8, 50, 0, 9, 196, 81, 7, 11, 0, 8, 98, 0, 8, 34, 0, 9, 164, 0, 8, 2, 0, 8, 130, 0, 8, 66, 0, 9, 228, 80, 7, 7, 0, 8, 90, 0, 8,
+                       26, 0, 9, 148, 84, 7, 67, 0, 8, 122, 0, 8, 58, 0, 9, 212, 82, 7, 19, 0, 8, 106, 0, 8, 42, 0, 9, 180, 0, 8, 10, 0, 8, 138, 0, 8, 74, 0, 9, 244, 80,
+                       7, 5, 0, 8, 86, 0, 8, 22, 192, 8, 0, 83, 7, 51, 0, 8, 118, 0, 8, 54, 0, 9, 204, 81, 7, 15, 0, 8, 102, 0, 8, 38, 0, 9, 172, 0, 8, 6, 0, 8, 134, 0,
+                       8, 70, 0, 9, 236, 80, 7, 9, 0, 8, 94, 0, 8, 30, 0, 9, 156, 84, 7, 99, 0, 8, 126, 0, 8, 62, 0, 9, 220, 82, 7, 27, 0, 8, 110, 0, 8, 46, 0, 9, 188, 0,
+                       8, 14, 0, 8, 142, 0, 8, 78, 0, 9, 252, 96, 7, 256, 0, 8, 81, 0, 8, 17, 85, 8, 131, 82, 7, 31, 0, 8, 113, 0, 8, 49, 0, 9, 194, 80, 7, 10, 0, 8, 97,
+                       0, 8, 33, 0, 9, 162, 0, 8, 1, 0, 8, 129, 0, 8, 65, 0, 9, 226, 80, 7, 6, 0, 8, 89, 0, 8, 25, 0, 9, 146, 83, 7, 59, 0, 8, 121, 0, 8, 57, 0, 9, 210,
+                       81, 7, 17, 0, 8, 105, 0, 8, 41, 0, 9, 178, 0, 8, 9, 0, 8, 137, 0, 8, 73, 0, 9, 242, 80, 7, 4, 0, 8, 85, 0, 8, 21, 80, 8, 258, 83, 7, 43, 0, 8, 117,
+                       0, 8, 53, 0, 9, 202, 81, 7, 13, 0, 8, 101, 0, 8, 37, 0, 9, 170, 0, 8, 5, 0, 8, 133, 0, 8, 69, 0, 9, 234, 80, 7, 8, 0, 8, 93, 0, 8, 29, 0, 9, 154,
+                       84, 7, 83, 0, 8, 125, 0, 8, 61, 0, 9, 218, 82, 7, 23, 0, 8, 109, 0, 8, 45, 0, 9, 186, 0, 8, 13, 0, 8, 141, 0, 8, 77, 0, 9, 250, 80, 7, 3, 0, 8, 83,
+                       0, 8, 19, 85, 8, 195, 83, 7, 35, 0, 8, 115, 0, 8, 51, 0, 9, 198, 81, 7, 11, 0, 8, 99, 0, 8, 35, 0, 9, 166, 0, 8, 3, 0, 8, 131, 0, 8, 67, 0, 9, 230,
+                       80, 7, 7, 0, 8, 91, 0, 8, 27, 0, 9, 150, 84, 7, 67, 0, 8, 123, 0, 8, 59, 0, 9, 214, 82, 7, 19, 0, 8, 107, 0, 8, 43, 0, 9, 182, 0, 8, 11, 0, 8, 139,
+                       0, 8, 75, 0, 9, 246, 80, 7, 5, 0, 8, 87, 0, 8, 23, 192, 8, 0, 83, 7, 51, 0, 8, 119, 0, 8, 55, 0, 9, 206, 81, 7, 15, 0, 8, 103, 0, 8, 39, 0, 9, 174,
+                       0, 8, 7, 0, 8, 135, 0, 8, 71, 0, 9, 238, 80, 7, 9, 0, 8, 95, 0, 8, 31, 0, 9, 158, 84, 7, 99, 0, 8, 127, 0, 8, 63, 0, 9, 222, 82, 7, 27, 0, 8, 111,
+                       0, 8, 47, 0, 9, 190, 0, 8, 15, 0, 8, 143, 0, 8, 79, 0, 9, 254, 96, 7, 256, 0, 8, 80, 0, 8, 16, 84, 8, 115, 82, 7, 31, 0, 8, 112, 0, 8, 48, 0, 9,
+                       193, 80, 7, 10, 0, 8, 96, 0, 8, 32, 0, 9, 161, 0, 8, 0, 0, 8, 128, 0, 8, 64, 0, 9, 225, 80, 7, 6, 0, 8, 88, 0, 8, 24, 0, 9, 145, 83, 7, 59, 0, 8,
+                       120, 0, 8, 56, 0, 9, 209, 81, 7, 17, 0, 8, 104, 0, 8, 40, 0, 9, 177, 0, 8, 8, 0, 8, 136, 0, 8, 72, 0, 9, 241, 80, 7, 4, 0, 8, 84, 0, 8, 20, 85, 8,
+                       227, 83, 7, 43, 0, 8, 116, 0, 8, 52, 0, 9, 201, 81, 7, 13, 0, 8, 100, 0, 8, 36, 0, 9, 169, 0, 8, 4, 0, 8, 132, 0, 8, 68, 0, 9, 233, 80, 7, 8, 0, 8,
+                       92, 0, 8, 28, 0, 9, 153, 84, 7, 83, 0, 8, 124, 0, 8, 60, 0, 9, 217, 82, 7, 23, 0, 8, 108, 0, 8, 44, 0, 9, 185, 0, 8, 12, 0, 8, 140, 0, 8, 76, 0, 9,
+                       249, 80, 7, 3, 0, 8, 82, 0, 8, 18, 85, 8, 163, 83, 7, 35, 0, 8, 114, 0, 8, 50, 0, 9, 197, 81, 7, 11, 0, 8, 98, 0, 8, 34, 0, 9, 165, 0, 8, 2, 0, 8,
+                       130, 0, 8, 66, 0, 9, 229, 80, 7, 7, 0, 8, 90, 0, 8, 26, 0, 9, 149, 84, 7, 67, 0, 8, 122, 0, 8, 58, 0, 9, 213, 82, 7, 19, 0, 8, 106, 0, 8, 42, 0, 9,
+                       181, 0, 8, 10, 0, 8, 138, 0, 8, 74, 0, 9, 245, 80, 7, 5, 0, 8, 86, 0, 8, 22, 192, 8, 0, 83, 7, 51, 0, 8, 118, 0, 8, 54, 0, 9, 205, 81, 7, 15, 0, 8,
+                       102, 0, 8, 38, 0, 9, 173, 0, 8, 6, 0, 8, 134, 0, 8, 70, 0, 9, 237, 80, 7, 9, 0, 8, 94, 0, 8, 30, 0, 9, 157, 84, 7, 99, 0, 8, 126, 0, 8, 62, 0, 9,
+                       221, 82, 7, 27, 0, 8, 110, 0, 8, 46, 0, 9, 189, 0, 8, 14, 0, 8, 142, 0, 8, 78, 0, 9, 253, 96, 7, 256, 0, 8, 81, 0, 8, 17, 85, 8, 131, 82, 7, 31, 0,
+                       8, 113, 0, 8, 49, 0, 9, 195, 80, 7, 10, 0, 8, 97, 0, 8, 33, 0, 9, 163, 0, 8, 1, 0, 8, 129, 0, 8, 65, 0, 9, 227, 80, 7, 6, 0, 8, 89, 0, 8, 25, 0, 9,
+                       147, 83, 7, 59, 0, 8, 121, 0, 8, 57, 0, 9, 211, 81, 7, 17, 0, 8, 105, 0, 8, 41, 0, 9, 179, 0, 8, 9, 0, 8, 137, 0, 8, 73, 0, 9, 243, 80, 7, 4, 0, 8,
+                       85, 0, 8, 21, 80, 8, 258, 83, 7, 43, 0, 8, 117, 0, 8, 53, 0, 9, 203, 81, 7, 13, 0, 8, 101, 0, 8, 37, 0, 9, 171, 0, 8, 5, 0, 8, 133, 0, 8, 69, 0, 9,
+                       235, 80, 7, 8, 0, 8, 93, 0, 8, 29, 0, 9, 155, 84, 7, 83, 0, 8, 125, 0, 8, 61, 0, 9, 219, 82, 7, 23, 0, 8, 109, 0, 8, 45, 0, 9, 187, 0, 8, 13, 0, 8,
+                       141, 0, 8, 77, 0, 9, 251, 80, 7, 3, 0, 8, 83, 0, 8, 19, 85, 8, 195, 83, 7, 35, 0, 8, 115, 0, 8, 51, 0, 9, 199, 81, 7, 11, 0, 8, 99, 0, 8, 35, 0, 9,
+                       167, 0, 8, 3, 0, 8, 131, 0, 8, 67, 0, 9, 231, 80, 7, 7, 0, 8, 91, 0, 8, 27, 0, 9, 151, 84, 7, 67, 0, 8, 123, 0, 8, 59, 0, 9, 215, 82, 7, 19, 0, 8,
+                       107, 0, 8, 43, 0, 9, 183, 0, 8, 11, 0, 8, 139, 0, 8, 75, 0, 9, 247, 80, 7, 5, 0, 8, 87, 0, 8, 23, 192, 8, 0, 83, 7, 51, 0, 8, 119, 0, 8, 55, 0, 9,
+                       207, 81, 7, 15, 0, 8, 103, 0, 8, 39, 0, 9, 175, 0, 8, 7, 0, 8, 135, 0, 8, 71, 0, 9, 239, 80, 7, 9, 0, 8, 95, 0, 8, 31, 0, 9, 159, 84, 7, 99, 0, 8,
+                       127, 0, 8, 63, 0, 9, 223, 82, 7, 27, 0, 8, 111, 0, 8, 47, 0, 9, 191, 0, 8, 15, 0, 8, 143, 0, 8, 79, 0, 9, 255 ];
+       var fixed_td = [ 80, 5, 1, 87, 5, 257, 83, 5, 17, 91, 5, 4097, 81, 5, 5, 89, 5, 1025, 85, 5, 65, 93, 5, 16385, 80, 5, 3, 88, 5, 513, 84, 5, 33, 92, 5,
+                       8193, 82, 5, 9, 90, 5, 2049, 86, 5, 129, 192, 5, 24577, 80, 5, 2, 87, 5, 385, 83, 5, 25, 91, 5, 6145, 81, 5, 7, 89, 5, 1537, 85, 5, 97, 93, 5,
+                       24577, 80, 5, 4, 88, 5, 769, 84, 5, 49, 92, 5, 12289, 82, 5, 13, 90, 5, 3073, 86, 5, 193, 192, 5, 24577 ];
+
+       // Tables for deflate from PKZIP's appnote.txt.
+       var cplens = [ // Copy lengths for literal codes 257..285
+       3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 ];
+
+       // see note #13 above about 258
+       var cplext = [ // Extra bits for literal codes 257..285
+       0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 112, 112 // 112==invalid
+       ];
+
+       var cpdist = [ // Copy offsets for distance codes 0..29
+       1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577 ];
+
+       var cpdext = [ // Extra bits for distance codes
+       0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13 ];
+
+       // If BMAX needs to be larger than 16, then h and x[] should be uLong.
+       var BMAX = 15; // maximum bit length of any code
+
+       function InfTree() {
+               var that = this;
+
+               var hn; // hufts used in space
+               var v; // work area for huft_build
+               var c; // bit length count table
+               var r; // table entry for structure assignment
+               var u; // table stack
+               var x; // bit offsets, then code stack
+
+               function huft_build(b, // code lengths in bits (all assumed <=
+               // BMAX)
+               bindex, n, // number of codes (assumed <= 288)
+               s, // number of simple-valued codes (0..s-1)
+               d, // list of base values for non-simple codes
+               e, // list of extra bits for non-simple codes
+               t, // result: starting table
+               m, // maximum lookup bits, returns actual
+               hp,// space for trees
+               hn,// hufts used in space
+               v // working area: values in order of bit length
+               ) {
+                       // Given a list of code lengths and a maximum table size, make a set of
+                       // tables to decode that set of codes. Return Z_OK on success,
+                       // Z_BUF_ERROR
+                       // if the given code set is incomplete (the tables are still built in
+                       // this
+                       // case), Z_DATA_ERROR if the input is invalid (an over-subscribed set
+                       // of
+                       // lengths), or Z_MEM_ERROR if not enough memory.
+
+                       var a; // counter for codes of length k
+                       var f; // i repeats in table every f entries
+                       var g; // maximum code length
+                       var h; // table level
+                       var i; // counter, current code
+                       var j; // counter
+                       var k; // number of bits in current code
+                       var l; // bits per table (returned in m)
+                       var mask; // (1 << w) - 1, to avoid cc -O bug on HP
+                       var p; // pointer into c[], b[], or v[]
+                       var q; // points to current table
+                       var w; // bits before this table == (l * h)
+                       var xp; // pointer into x
+                       var y; // number of dummy codes added
+                       var z; // number of entries in current table
+
+                       // Generate counts for each bit length
+
+                       p = 0;
+                       i = n;
+                       do {
+                               c[b[bindex + p]]++;
+                               p++;
+                               i--; // assume all entries <= BMAX
+                       } while (i !== 0);
+
+                       if (c[0] == n) { // null input--all zero length codes
+                               t[0] = -1;
+                               m[0] = 0;
+                               return Z_OK;
+                       }
+
+                       // Find minimum and maximum length, bound *m by those
+                       l = m[0];
+                       for (j = 1; j <= BMAX; j++)
+                               if (c[j] !== 0)
+                                       break;
+                       k = j; // minimum code length
+                       if (l < j) {
+                               l = j;
+                       }
+                       for (i = BMAX; i !== 0; i--) {
+                               if (c[i] !== 0)
+                                       break;
+                       }
+                       g = i; // maximum code length
+                       if (l > i) {
+                               l = i;
+                       }
+                       m[0] = l;
+
+                       // Adjust last length count to fill out codes, if needed
+                       for (y = 1 << j; j < i; j++, y <<= 1) {
+                               if ((y -= c[j]) < 0) {
+                                       return Z_DATA_ERROR;
+                               }
+                       }
+                       if ((y -= c[i]) < 0) {
+                               return Z_DATA_ERROR;
+                       }
+                       c[i] += y;
+
+                       // Generate starting offsets into the value table for each length
+                       x[1] = j = 0;
+                       p = 1;
+                       xp = 2;
+                       while (--i !== 0) { // note that i == g from above
+                               x[xp] = (j += c[p]);
+                               xp++;
+                               p++;
+                       }
+
+                       // Make a table of values in order of bit lengths
+                       i = 0;
+                       p = 0;
+                       do {
+                               if ((j = b[bindex + p]) !== 0) {
+                                       v[x[j]++] = i;
+                               }
+                               p++;
+                       } while (++i < n);
+                       n = x[g]; // set n to length of v
+
+                       // Generate the Huffman codes and for each, make the table entries
+                       x[0] = i = 0; // first Huffman code is zero
+                       p = 0; // grab values in bit order
+                       h = -1; // no tables yet--level -1
+                       w = -l; // bits decoded == (l * h)
+                       u[0] = 0; // just to keep compilers happy
+                       q = 0; // ditto
+                       z = 0; // ditto
+
+                       // go through the bit lengths (k already is bits in shortest code)
+                       for (; k <= g; k++) {
+                               a = c[k];
+                               while (a-- !== 0) {
+                                       // here i is the Huffman code of length k bits for value *p
+                                       // make tables up to required level
+                                       while (k > w + l) {
+                                               h++;
+                                               w += l; // previous table always l bits
+                                               // compute minimum size table less than or equal to l bits
+                                               z = g - w;
+                                               z = (z > l) ? l : z; // table size upper limit
+                                               if ((f = 1 << (j = k - w)) > a + 1) { // try a k-w bit table
+                                                       // too few codes for
+                                                       // k-w bit table
+                                                       f -= a + 1; // deduct codes from patterns left
+                                                       xp = k;
+                                                       if (j < z) {
+                                                               while (++j < z) { // try smaller tables up to z bits
+                                                                       if ((f <<= 1) <= c[++xp])
+                                                                               break; // enough codes to use up j bits
+                                                                       f -= c[xp]; // else deduct codes from patterns
+                                                               }
+                                                       }
+                                               }
+                                               z = 1 << j; // table entries for j-bit table
+
+                                               // allocate new table
+                                               if (hn[0] + z > MANY) { // (note: doesn't matter for fixed)
+                                                       return Z_DATA_ERROR; // overflow of MANY
+                                               }
+                                               u[h] = q = /* hp+ */hn[0]; // DEBUG
+                                               hn[0] += z;
+
+                                               // connect to last table, if there is one
+                                               if (h !== 0) {
+                                                       x[h] = i; // save pattern for backing up
+                                                       r[0] = /* (byte) */j; // bits in this table
+                                                       r[1] = /* (byte) */l; // bits to dump before this table
+                                                       j = i >>> (w - l);
+                                                       r[2] = /* (int) */(q - u[h - 1] - j); // offset to this table
+                                                       hp.set(r, (u[h - 1] + j) * 3);
+                                                       // to
+                                                       // last
+                                                       // table
+                                               } else {
+                                                       t[0] = q; // first table is returned result
+                                               }
+                                       }
+
+                                       // set up table entry in r
+                                       r[1] = /* (byte) */(k - w);
+                                       if (p >= n) {
+                                               r[0] = 128 + 64; // out of values--invalid code
+                                       } else if (v[p] < s) {
+                                               r[0] = /* (byte) */(v[p] < 256 ? 0 : 32 + 64); // 256 is
+                                               // end-of-block
+                                               r[2] = v[p++]; // simple code is just the value
+                                       } else {
+                                               r[0] = /* (byte) */(e[v[p] - s] + 16 + 64); // non-simple--look
+                                               // up in lists
+                                               r[2] = d[v[p++] - s];
+                                       }
+
+                                       // fill code-like entries with r
+                                       f = 1 << (k - w);
+                                       for (j = i >>> w; j < z; j += f) {
+                                               hp.set(r, (q + j) * 3);
+                                       }
+
+                                       // backwards increment the k-bit code i
+                                       for (j = 1 << (k - 1); (i & j) !== 0; j >>>= 1) {
+                                               i ^= j;
+                                       }
+                                       i ^= j;
+
+                                       // backup over finished tables
+                                       mask = (1 << w) - 1; // needed on HP, cc -O bug
+                                       while ((i & mask) != x[h]) {
+                                               h--; // don't need to update q
+                                               w -= l;
+                                               mask = (1 << w) - 1;
+                                       }
+                               }
+                       }
+                       // Return Z_BUF_ERROR if we were given an incomplete table
+                       return y !== 0 && g != 1 ? Z_BUF_ERROR : Z_OK;
+               }
+
+               function initWorkArea(vsize) {
+                       var i;
+                       if (!hn) {
+                               hn = []; // []; //new Array(1);
+                               v = []; // new Array(vsize);
+                               c = new Int32Array(BMAX + 1); // new Array(BMAX + 1);
+                               r = []; // new Array(3);
+                               u = new Int32Array(BMAX); // new Array(BMAX);
+                               x = new Int32Array(BMAX + 1); // new Array(BMAX + 1);
+                       }
+                       if (v.length < vsize) {
+                               v = []; // new Array(vsize);
+                       }
+                       for (i = 0; i < vsize; i++) {
+                               v[i] = 0;
+                       }
+                       for (i = 0; i < BMAX + 1; i++) {
+                               c[i] = 0;
+                       }
+                       for (i = 0; i < 3; i++) {
+                               r[i] = 0;
+                       }
+                       // for(int i=0; i<BMAX; i++){u[i]=0;}
+                       u.set(c.subarray(0, BMAX), 0);
+                       // for(int i=0; i<BMAX+1; i++){x[i]=0;}
+                       x.set(c.subarray(0, BMAX + 1), 0);
+               }
+
+               that.inflate_trees_bits = function(c, // 19 code lengths
+               bb, // bits tree desired/actual depth
+               tb, // bits tree result
+               hp, // space for trees
+               z // for messages
+               ) {
+                       var result;
+                       initWorkArea(19);
+                       hn[0] = 0;
+                       result = huft_build(c, 0, 19, 19, null, null, tb, bb, hp, hn, v);
+
+                       if (result == Z_DATA_ERROR) {
+                               z.msg = "oversubscribed dynamic bit lengths tree";
+                       } else if (result == Z_BUF_ERROR || bb[0] === 0) {
+                               z.msg = "incomplete dynamic bit lengths tree";
+                               result = Z_DATA_ERROR;
+                       }
+                       return result;
+               };
+
+               that.inflate_trees_dynamic = function(nl, // number of literal/length codes
+               nd, // number of distance codes
+               c, // that many (total) code lengths
+               bl, // literal desired/actual bit depth
+               bd, // distance desired/actual bit depth
+               tl, // literal/length tree result
+               td, // distance tree result
+               hp, // space for trees
+               z // for messages
+               ) {
+                       var result;
+
+                       // build literal/length tree
+                       initWorkArea(288);
+                       hn[0] = 0;
+                       result = huft_build(c, 0, nl, 257, cplens, cplext, tl, bl, hp, hn, v);
+                       if (result != Z_OK || bl[0] === 0) {
+                               if (result == Z_DATA_ERROR) {
+                                       z.msg = "oversubscribed literal/length tree";
+                               } else if (result != Z_MEM_ERROR) {
+                                       z.msg = "incomplete literal/length tree";
+                                       result = Z_DATA_ERROR;
+                               }
+                               return result;
+                       }
+
+                       // build distance tree
+                       initWorkArea(288);
+                       result = huft_build(c, nl, nd, 0, cpdist, cpdext, td, bd, hp, hn, v);
+
+                       if (result != Z_OK || (bd[0] === 0 && nl > 257)) {
+                               if (result == Z_DATA_ERROR) {
+                                       z.msg = "oversubscribed distance tree";
+                               } else if (result == Z_BUF_ERROR) {
+                                       z.msg = "incomplete distance tree";
+                                       result = Z_DATA_ERROR;
+                               } else if (result != Z_MEM_ERROR) {
+                                       z.msg = "empty distance tree with lengths";
+                                       result = Z_DATA_ERROR;
+                               }
+                               return result;
+                       }
+
+                       return Z_OK;
+               };
+
+       }
+
+       InfTree.inflate_trees_fixed = function(bl, // literal desired/actual bit depth
+       bd, // distance desired/actual bit depth
+       tl,// literal/length tree result
+       td// distance tree result
+       ) {
+               bl[0] = fixed_bl;
+               bd[0] = fixed_bd;
+               tl[0] = fixed_tl;
+               td[0] = fixed_td;
+               return Z_OK;
+       };
+
+       // InfCodes
+
+       // waiting for "i:"=input,
+       // "o:"=output,
+       // "x:"=nothing
+       var START = 0; // x: set up for LEN
+       var LEN = 1; // i: get length/literal/eob next
+       var LENEXT = 2; // i: getting length extra (have base)
+       var DIST = 3; // i: get distance next
+       var DISTEXT = 4;// i: getting distance extra
+       var COPY = 5; // o: copying bytes in window, waiting
+       // for space
+       var LIT = 6; // o: got literal, waiting for output
+       // space
+       var WASH = 7; // o: got eob, possibly still output
+       // waiting
+       var END = 8; // x: got eob and all data flushed
+       var BADCODE = 9;// x: got error
+
+       function InfCodes() {
+               var that = this;
+
+               var mode; // current inflate_codes mode
+
+               // mode dependent information
+               var len = 0;
+
+               var tree; // pointer into tree
+               var tree_index = 0;
+               var need = 0; // bits needed
+
+               var lit = 0;
+
+               // if EXT or COPY, where and how much
+               var get = 0; // bits to get for extra
+               var dist = 0; // distance back to copy from
+
+               var lbits = 0; // ltree bits decoded per branch
+               var dbits = 0; // dtree bits decoder per branch
+               var ltree; // literal/length/eob tree
+               var ltree_index = 0; // literal/length/eob tree
+               var dtree; // distance tree
+               var dtree_index = 0; // distance tree
+
+               // Called with number of bytes left to write in window at least 258
+               // (the maximum string length) and number of input bytes available
+               // at least ten. The ten bytes are six bytes for the longest length/
+               // distance pair plus four bytes for overloading the bit buffer.
+
+               function inflate_fast(bl, bd, tl, tl_index, td, td_index, s, z) {
+                       var t; // temporary pointer
+                       var tp; // temporary pointer
+                       var tp_index; // temporary pointer
+                       var e; // extra bits or operation
+                       var b; // bit buffer
+                       var k; // bits in bit buffer
+                       var p; // input data pointer
+                       var n; // bytes available there
+                       var q; // output window write pointer
+                       var m; // bytes to end of window or read pointer
+                       var ml; // mask for literal/length tree
+                       var md; // mask for distance tree
+                       var c; // bytes to copy
+                       var d; // distance back to copy from
+                       var r; // copy source pointer
+
+                       var tp_index_t_3; // (tp_index+t)*3
+
+                       // load input, output, bit values
+                       p = z.next_in_index;
+                       n = z.avail_in;
+                       b = s.bitb;
+                       k = s.bitk;
+                       q = s.write;
+                       m = q < s.read ? s.read - q - 1 : s.end - q;
+
+                       // initialize masks
+                       ml = inflate_mask[bl];
+                       md = inflate_mask[bd];
+
+                       // do until not enough input or output space for fast loop
+                       do { // assume called with m >= 258 && n >= 10
+                               // get literal/length code
+                               while (k < (20)) { // max bits for literal/length code
+                                       n--;
+                                       b |= (z.read_byte(p++) & 0xff) << k;
+                                       k += 8;
+                               }
+
+                               t = b & ml;
+                               tp = tl;
+                               tp_index = tl_index;
+                               tp_index_t_3 = (tp_index + t) * 3;
+                               if ((e = tp[tp_index_t_3]) === 0) {
+                                       b >>= (tp[tp_index_t_3 + 1]);
+                                       k -= (tp[tp_index_t_3 + 1]);
+
+                                       s.window[q++] = /* (byte) */tp[tp_index_t_3 + 2];
+                                       m--;
+                                       continue;
+                               }
+                               do {
+
+                                       b >>= (tp[tp_index_t_3 + 1]);
+                                       k -= (tp[tp_index_t_3 + 1]);
+
+                                       if ((e & 16) !== 0) {
+                                               e &= 15;
+                                               c = tp[tp_index_t_3 + 2] + (/* (int) */b & inflate_mask[e]);
+
+                                               b >>= e;
+                                               k -= e;
+
+                                               // decode distance base of block to copy
+                                               while (k < (15)) { // max bits for distance code
+                                                       n--;
+                                                       b |= (z.read_byte(p++) & 0xff) << k;
+                                                       k += 8;
+                                               }
+
+                                               t = b & md;
+                                               tp = td;
+                                               tp_index = td_index;
+                                               tp_index_t_3 = (tp_index + t) * 3;
+                                               e = tp[tp_index_t_3];
+
+                                               do {
+
+                                                       b >>= (tp[tp_index_t_3 + 1]);
+                                                       k -= (tp[tp_index_t_3 + 1]);
+
+                                                       if ((e & 16) !== 0) {
+                                                               // get extra bits to add to distance base
+                                                               e &= 15;
+                                                               while (k < (e)) { // get extra bits (up to 13)
+                                                                       n--;
+                                                                       b |= (z.read_byte(p++) & 0xff) << k;
+                                                                       k += 8;
+                                                               }
+
+                                                               d = tp[tp_index_t_3 + 2] + (b & inflate_mask[e]);
+
+                                                               b >>= (e);
+                                                               k -= (e);
+
+                                                               // do the copy
+                                                               m -= c;
+                                                               if (q >= d) { // offset before dest
+                                                                       // just copy
+                                                                       r = q - d;
+                                                                       if (q - r > 0 && 2 > (q - r)) {
+                                                                               s.window[q++] = s.window[r++]; // minimum
+                                                                               // count is
+                                                                               // three,
+                                                                               s.window[q++] = s.window[r++]; // so unroll
+                                                                               // loop a
+                                                                               // little
+                                                                               c -= 2;
+                                                                       } else {
+                                                                               s.window.set(s.window.subarray(r, r + 2), q);
+                                                                               q += 2;
+                                                                               r += 2;
+                                                                               c -= 2;
+                                                                       }
+                                                               } else { // else offset after destination
+                                                                       r = q - d;
+                                                                       do {
+                                                                               r += s.end; // force pointer in window
+                                                                       } while (r < 0); // covers invalid distances
+                                                                       e = s.end - r;
+                                                                       if (c > e) { // if source crosses,
+                                                                               c -= e; // wrapped copy
+                                                                               if (q - r > 0 && e > (q - r)) {
+                                                                                       do {
+                                                                                               s.window[q++] = s.window[r++];
+                                                                                       } while (--e !== 0);
+                                                                               } else {
+                                                                                       s.window.set(s.window.subarray(r, r + e), q);
+                                                                                       q += e;
+                                                                                       r += e;
+                                                                                       e = 0;
+                                                                               }
+                                                                               r = 0; // copy rest from start of window
+                                                                       }
+
+                                                               }
+
+                                                               // copy all or what's left
+                                                               if (q - r > 0 && c > (q - r)) {
+                                                                       do {
+                                                                               s.window[q++] = s.window[r++];
+                                                                       } while (--c !== 0);
+                                                               } else {
+                                                                       s.window.set(s.window.subarray(r, r + c), q);
+                                                                       q += c;
+                                                                       r += c;
+                                                                       c = 0;
+                                                               }
+                                                               break;
+                                                       } else if ((e & 64) === 0) {
+                                                               t += tp[tp_index_t_3 + 2];
+                                                               t += (b & inflate_mask[e]);
+                                                               tp_index_t_3 = (tp_index + t) * 3;
+                                                               e = tp[tp_index_t_3];
+                                                       } else {
+                                                               z.msg = "invalid distance code";
+
+                                                               c = z.avail_in - n;
+                                                               c = (k >> 3) < c ? k >> 3 : c;
+                                                               n += c;
+                                                               p -= c;
+                                                               k -= c << 3;
+
+                                                               s.bitb = b;
+                                                               s.bitk = k;
+                                                               z.avail_in = n;
+                                                               z.total_in += p - z.next_in_index;
+                                                               z.next_in_index = p;
+                                                               s.write = q;
+
+                                                               return Z_DATA_ERROR;
+                                                       }
+                                               } while (true);
+                                               break;
+                                       }
+
+                                       if ((e & 64) === 0) {
+                                               t += tp[tp_index_t_3 + 2];
+                                               t += (b & inflate_mask[e]);
+                                               tp_index_t_3 = (tp_index + t) * 3;
+                                               if ((e = tp[tp_index_t_3]) === 0) {
+
+                                                       b >>= (tp[tp_index_t_3 + 1]);
+                                                       k -= (tp[tp_index_t_3 + 1]);
+
+                                                       s.window[q++] = /* (byte) */tp[tp_index_t_3 + 2];
+                                                       m--;
+                                                       break;
+                                               }
+                                       } else if ((e & 32) !== 0) {
+
+                                               c = z.avail_in - n;
+                                               c = (k >> 3) < c ? k >> 3 : c;
+                                               n += c;
+                                               p -= c;
+                                               k -= c << 3;
+
+                                               s.bitb = b;
+                                               s.bitk = k;
+                                               z.avail_in = n;
+                                               z.total_in += p - z.next_in_index;
+                                               z.next_in_index = p;
+                                               s.write = q;
+
+                                               return Z_STREAM_END;
+                                       } else {
+                                               z.msg = "invalid literal/length code";
+
+                                               c = z.avail_in - n;
+                                               c = (k >> 3) < c ? k >> 3 : c;
+                                               n += c;
+                                               p -= c;
+                                               k -= c << 3;
+
+                                               s.bitb = b;
+                                               s.bitk = k;
+                                               z.avail_in = n;
+                                               z.total_in += p - z.next_in_index;
+                                               z.next_in_index = p;
+                                               s.write = q;
+
+                                               return Z_DATA_ERROR;
+                                       }
+                               } while (true);
+                       } while (m >= 258 && n >= 10);
+
+                       // not enough input or output--restore pointers and return
+                       c = z.avail_in - n;
+                       c = (k >> 3) < c ? k >> 3 : c;
+                       n += c;
+                       p -= c;
+                       k -= c << 3;
+
+                       s.bitb = b;
+                       s.bitk = k;
+                       z.avail_in = n;
+                       z.total_in += p - z.next_in_index;
+                       z.next_in_index = p;
+                       s.write = q;
+
+                       return Z_OK;
+               }
+
+               that.init = function(bl, bd, tl, tl_index, td, td_index) {
+                       mode = START;
+                       lbits = /* (byte) */bl;
+                       dbits = /* (byte) */bd;
+                       ltree = tl;
+                       ltree_index = tl_index;
+                       dtree = td;
+                       dtree_index = td_index;
+                       tree = null;
+               };
+
+               that.proc = function(s, z, r) {
+                       var j; // temporary storage
+                       var tindex; // temporary pointer
+                       var e; // extra bits or operation
+                       var b = 0; // bit buffer
+                       var k = 0; // bits in bit buffer
+                       var p = 0; // input data pointer
+                       var n; // bytes available there
+                       var q; // output window write pointer
+                       var m; // bytes to end of window or read pointer
+                       var f; // pointer to copy strings from
+
+                       // copy input/output information to locals (UPDATE macro restores)
+                       p = z.next_in_index;
+                       n = z.avail_in;
+                       b = s.bitb;
+                       k = s.bitk;
+                       q = s.write;
+                       m = q < s.read ? s.read - q - 1 : s.end - q;
+
+                       // process input and output based on current state
+                       while (true) {
+                               switch (mode) {
+                               // waiting for "i:"=input, "o:"=output, "x:"=nothing
+                               case START: // x: set up for LEN
+                                       if (m >= 258 && n >= 10) {
+
+                                               s.bitb = b;
+                                               s.bitk = k;
+                                               z.avail_in = n;
+                                               z.total_in += p - z.next_in_index;
+                                               z.next_in_index = p;
+                                               s.write = q;
+                                               r = inflate_fast(lbits, dbits, ltree, ltree_index, dtree, dtree_index, s, z);
+
+                                               p = z.next_in_index;
+                                               n = z.avail_in;
+                                               b = s.bitb;
+                                               k = s.bitk;
+                                               q = s.write;
+                                               m = q < s.read ? s.read - q - 1 : s.end - q;
+
+                                               if (r != Z_OK) {
+                                                       mode = r == Z_STREAM_END ? WASH : BADCODE;
+                                                       break;
+                                               }
+                                       }
+                                       need = lbits;
+                                       tree = ltree;
+                                       tree_index = ltree_index;
+
+                                       mode = LEN;
+                               case LEN: // i: get length/literal/eob next
+                                       j = need;
+
+                                       while (k < (j)) {
+                                               if (n !== 0)
+                                                       r = Z_OK;
+                                               else {
+
+                                                       s.bitb = b;
+                                                       s.bitk = k;
+                                                       z.avail_in = n;
+                                                       z.total_in += p - z.next_in_index;
+                                                       z.next_in_index = p;
+                                                       s.write = q;
+                                                       return s.inflate_flush(z, r);
+                                               }
+                                               n--;
+                                               b |= (z.read_byte(p++) & 0xff) << k;
+                                               k += 8;
+                                       }
+
+                                       tindex = (tree_index + (b & inflate_mask[j])) * 3;
+
+                                       b >>>= (tree[tindex + 1]);
+                                       k -= (tree[tindex + 1]);
+
+                                       e = tree[tindex];
+
+                                       if (e === 0) { // literal
+                                               lit = tree[tindex + 2];
+                                               mode = LIT;
+                                               break;
+                                       }
+                                       if ((e & 16) !== 0) { // length
+                                               get = e & 15;
+                                               len = tree[tindex + 2];
+                                               mode = LENEXT;
+                                               break;
+                                       }
+                                       if ((e & 64) === 0) { // next table
+                                               need = e;
+                                               tree_index = tindex / 3 + tree[tindex + 2];
+                                               break;
+                                       }
+                                       if ((e & 32) !== 0) { // end of block
+                                               mode = WASH;
+                                               break;
+                                       }
+                                       mode = BADCODE; // invalid code
+                                       z.msg = "invalid literal/length code";
+                                       r = Z_DATA_ERROR;
+
+                                       s.bitb = b;
+                                       s.bitk = k;
+                                       z.avail_in = n;
+                                       z.total_in += p - z.next_in_index;
+                                       z.next_in_index = p;
+                                       s.write = q;
+                                       return s.inflate_flush(z, r);
+
+                               case LENEXT: // i: getting length extra (have base)
+                                       j = get;
+
+                                       while (k < (j)) {
+                                               if (n !== 0)
+                                                       r = Z_OK;
+                                               else {
+
+                                                       s.bitb = b;
+                                                       s.bitk = k;
+                                                       z.avail_in = n;
+                                                       z.total_in += p - z.next_in_index;
+                                                       z.next_in_index = p;
+                                                       s.write = q;
+                                                       return s.inflate_flush(z, r);
+                                               }
+                                               n--;
+                                               b |= (z.read_byte(p++) & 0xff) << k;
+                                               k += 8;
+                                       }
+
+                                       len += (b & inflate_mask[j]);
+
+                                       b >>= j;
+                                       k -= j;
+
+                                       need = dbits;
+                                       tree = dtree;
+                                       tree_index = dtree_index;
+                                       mode = DIST;
+                               case DIST: // i: get distance next
+                                       j = need;
+
+                                       while (k < (j)) {
+                                               if (n !== 0)
+                                                       r = Z_OK;
+                                               else {
+
+                                                       s.bitb = b;
+                                                       s.bitk = k;
+                                                       z.avail_in = n;
+                                                       z.total_in += p - z.next_in_index;
+                                                       z.next_in_index = p;
+                                                       s.write = q;
+                                                       return s.inflate_flush(z, r);
+                                               }
+                                               n--;
+                                               b |= (z.read_byte(p++) & 0xff) << k;
+                                               k += 8;
+                                       }
+
+                                       tindex = (tree_index + (b & inflate_mask[j])) * 3;
+
+                                       b >>= tree[tindex + 1];
+                                       k -= tree[tindex + 1];
+
+                                       e = (tree[tindex]);
+                                       if ((e & 16) !== 0) { // distance
+                                               get = e & 15;
+                                               dist = tree[tindex + 2];
+                                               mode = DISTEXT;
+                                               break;
+                                       }
+                                       if ((e & 64) === 0) { // next table
+                                               need = e;
+                                               tree_index = tindex / 3 + tree[tindex + 2];
+                                               break;
+                                       }
+                                       mode = BADCODE; // invalid code
+                                       z.msg = "invalid distance code";
+                                       r = Z_DATA_ERROR;
+
+                                       s.bitb = b;
+                                       s.bitk = k;
+                                       z.avail_in = n;
+                                       z.total_in += p - z.next_in_index;
+                                       z.next_in_index = p;
+                                       s.write = q;
+                                       return s.inflate_flush(z, r);
+
+                               case DISTEXT: // i: getting distance extra
+                                       j = get;
+
+                                       while (k < (j)) {
+                                               if (n !== 0)
+                                                       r = Z_OK;
+                                               else {
+
+                                                       s.bitb = b;
+                                                       s.bitk = k;
+                                                       z.avail_in = n;
+                                                       z.total_in += p - z.next_in_index;
+                                                       z.next_in_index = p;
+                                                       s.write = q;
+                                                       return s.inflate_flush(z, r);
+                                               }
+                                               n--;
+                                               b |= (z.read_byte(p++) & 0xff) << k;
+                                               k += 8;
+                                       }
+
+                                       dist += (b & inflate_mask[j]);
+
+                                       b >>= j;
+                                       k -= j;
+
+                                       mode = COPY;
+                               case COPY: // o: copying bytes in window, waiting for space
+                                       f = q - dist;
+                                       while (f < 0) { // modulo window size-"while" instead
+                                               f += s.end; // of "if" handles invalid distances
+                                       }
+                                       while (len !== 0) {
+
+                                               if (m === 0) {
+                                                       if (q == s.end && s.read !== 0) {
+                                                               q = 0;
+                                                               m = q < s.read ? s.read - q - 1 : s.end - q;
+                                                       }
+                                                       if (m === 0) {
+                                                               s.write = q;
+                                                               r = s.inflate_flush(z, r);
+                                                               q = s.write;
+                                                               m = q < s.read ? s.read - q - 1 : s.end - q;
+
+                                                               if (q == s.end && s.read !== 0) {
+                                                                       q = 0;
+                                                                       m = q < s.read ? s.read - q - 1 : s.end - q;
+                                                               }
+
+                                                               if (m === 0) {
+                                                                       s.bitb = b;
+                                                                       s.bitk = k;
+                                                                       z.avail_in = n;
+                                                                       z.total_in += p - z.next_in_index;
+                                                                       z.next_in_index = p;
+                                                                       s.write = q;
+                                                                       return s.inflate_flush(z, r);
+                                                               }
+                                                       }
+                                               }
+
+                                               s.window[q++] = s.window[f++];
+                                               m--;
+
+                                               if (f == s.end)
+                                                       f = 0;
+                                               len--;
+                                       }
+                                       mode = START;
+                                       break;
+                               case LIT: // o: got literal, waiting for output space
+                                       if (m === 0) {
+                                               if (q == s.end && s.read !== 0) {
+                                                       q = 0;
+                                                       m = q < s.read ? s.read - q - 1 : s.end - q;
+                                               }
+                                               if (m === 0) {
+                                                       s.write = q;
+                                                       r = s.inflate_flush(z, r);
+                                                       q = s.write;
+                                                       m = q < s.read ? s.read - q - 1 : s.end - q;
+
+                                                       if (q == s.end && s.read !== 0) {
+                                                               q = 0;
+                                                               m = q < s.read ? s.read - q - 1 : s.end - q;
+                                                       }
+                                                       if (m === 0) {
+                                                               s.bitb = b;
+                                                               s.bitk = k;
+                                                               z.avail_in = n;
+                                                               z.total_in += p - z.next_in_index;
+                                                               z.next_in_index = p;
+                                                               s.write = q;
+                                                               return s.inflate_flush(z, r);
+                                                       }
+                                               }
+                                       }
+                                       r = Z_OK;
+
+                                       s.window[q++] = /* (byte) */lit;
+                                       m--;
+
+                                       mode = START;
+                                       break;
+                               case WASH: // o: got eob, possibly more output
+                                       if (k > 7) { // return unused byte, if any
+                                               k -= 8;
+                                               n++;
+                                               p--; // can always return one
+                                       }
+
+                                       s.write = q;
+                                       r = s.inflate_flush(z, r);
+                                       q = s.write;
+                                       m = q < s.read ? s.read - q - 1 : s.end - q;
+
+                                       if (s.read != s.write) {
+                                               s.bitb = b;
+                                               s.bitk = k;
+                                               z.avail_in = n;
+                                               z.total_in += p - z.next_in_index;
+                                               z.next_in_index = p;
+                                               s.write = q;
+                                               return s.inflate_flush(z, r);
+                                       }
+                                       mode = END;
+                               case END:
+                                       r = Z_STREAM_END;
+                                       s.bitb = b;
+                                       s.bitk = k;
+                                       z.avail_in = n;
+                                       z.total_in += p - z.next_in_index;
+                                       z.next_in_index = p;
+                                       s.write = q;
+                                       return s.inflate_flush(z, r);
+
+                               case BADCODE: // x: got error
+
+                                       r = Z_DATA_ERROR;
+
+                                       s.bitb = b;
+                                       s.bitk = k;
+                                       z.avail_in = n;
+                                       z.total_in += p - z.next_in_index;
+                                       z.next_in_index = p;
+                                       s.write = q;
+                                       return s.inflate_flush(z, r);
+
+                               default:
+                                       r = Z_STREAM_ERROR;
+
+                                       s.bitb = b;
+                                       s.bitk = k;
+                                       z.avail_in = n;
+                                       z.total_in += p - z.next_in_index;
+                                       z.next_in_index = p;
+                                       s.write = q;
+                                       return s.inflate_flush(z, r);
+                               }
+                       }
+               };
+
+               that.free = function() {
+                       // ZFREE(z, c);
+               };
+
+       }
+
+       // InfBlocks
+
+       // Table for deflate from PKZIP's appnote.txt.
+       var border = [ // Order of the bit length code lengths
+       16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 ];
+
+       var TYPE = 0; // get type bits (3, including end bit)
+       var LENS = 1; // get lengths for stored
+       var STORED = 2;// processing stored block
+       var TABLE = 3; // get table lengths
+       var BTREE = 4; // get bit lengths tree for a dynamic
+       // block
+       var DTREE = 5; // get length, distance trees for a
+       // dynamic block
+       var CODES = 6; // processing fixed or dynamic block
+       var DRY = 7; // output remaining window bytes
+       var DONELOCKS = 8; // finished last block, done
+       var BADBLOCKS = 9; // ot a data error--stuck here
+
+       function InfBlocks(z, w) {
+               var that = this;
+
+               var mode = TYPE; // current inflate_block mode
+
+               var left = 0; // if STORED, bytes left to copy
+
+               var table = 0; // table lengths (14 bits)
+               var index = 0; // index into blens (or border)
+               var blens; // bit lengths of codes
+               var bb = [ 0 ]; // bit length tree depth
+               var tb = [ 0 ]; // bit length decoding tree
+
+               var codes = new InfCodes(); // if CODES, current state
+
+               var last = 0; // true if this block is the last block
+
+               var hufts = new Int32Array(MANY * 3); // single malloc for tree space
+               var check = 0; // check on output
+               var inftree = new InfTree();
+
+               that.bitk = 0; // bits in bit buffer
+               that.bitb = 0; // bit buffer
+               that.window = new Uint8Array(w); // sliding window
+               that.end = w; // one byte after sliding window
+               that.read = 0; // window read pointer
+               that.write = 0; // window write pointer
+
+               that.reset = function(z, c) {
+                       if (c)
+                               c[0] = check;
+                       // if (mode == BTREE || mode == DTREE) {
+                       // }
+                       if (mode == CODES) {
+                               codes.free(z);
+                       }
+                       mode = TYPE;
+                       that.bitk = 0;
+                       that.bitb = 0;
+                       that.read = that.write = 0;
+               };
+
+               that.reset(z, null);
+
+               // copy as much as possible from the sliding window to the output area
+               that.inflate_flush = function(z, r) {
+                       var n;
+                       var p;
+                       var q;
+
+                       // local copies of source and destination pointers
+                       p = z.next_out_index;
+                       q = that.read;
+
+                       // compute number of bytes to copy as far as end of window
+                       n = /* (int) */((q <= that.write ? that.write : that.end) - q);
+                       if (n > z.avail_out)
+                               n = z.avail_out;
+                       if (n !== 0 && r == Z_BUF_ERROR)
+                               r = Z_OK;
+
+                       // update counters
+                       z.avail_out -= n;
+                       z.total_out += n;
+
+                       // copy as far as end of window
+                       z.next_out.set(that.window.subarray(q, q + n), p);
+                       p += n;
+                       q += n;
+
+                       // see if more to copy at beginning of window
+                       if (q == that.end) {
+                               // wrap pointers
+                               q = 0;
+                               if (that.write == that.end)
+                                       that.write = 0;
+
+                               // compute bytes to copy
+                               n = that.write - q;
+                               if (n > z.avail_out)
+                                       n = z.avail_out;
+                               if (n !== 0 && r == Z_BUF_ERROR)
+                                       r = Z_OK;
+
+                               // update counters
+                               z.avail_out -= n;
+                               z.total_out += n;
+
+                               // copy
+                               z.next_out.set(that.window.subarray(q, q + n), p);
+                               p += n;
+                               q += n;
+                       }
+
+                       // update pointers
+                       z.next_out_index = p;
+                       that.read = q;
+
+                       // done
+                       return r;
+               };
+
+               that.proc = function(z, r) {
+                       var t; // temporary storage
+                       var b; // bit buffer
+                       var k; // bits in bit buffer
+                       var p; // input data pointer
+                       var n; // bytes available there
+                       var q; // output window write pointer
+                       var m; // bytes to end of window or read pointer
+
+                       var i;
+
+                       // copy input/output information to locals (UPDATE macro restores)
+                       // {
+                       p = z.next_in_index;
+                       n = z.avail_in;
+                       b = that.bitb;
+                       k = that.bitk;
+                       // }
+                       // {
+                       q = that.write;
+                       m = /* (int) */(q < that.read ? that.read - q - 1 : that.end - q);
+                       // }
+
+                       // process input based on current state
+                       // DEBUG dtree
+                       while (true) {
+                               switch (mode) {
+                               case TYPE:
+
+                                       while (k < (3)) {
+                                               if (n !== 0) {
+                                                       r = Z_OK;
+                                               } else {
+                                                       that.bitb = b;
+                                                       that.bitk = k;
+                                                       z.avail_in = n;
+                                                       z.total_in += p - z.next_in_index;
+                                                       z.next_in_index = p;
+                                                       that.write = q;
+                                                       return that.inflate_flush(z, r);
+                                               }
+                                               n--;
+                                               b |= (z.read_byte(p++) & 0xff) << k;
+                                               k += 8;
+                                       }
+                                       t = /* (int) */(b & 7);
+                                       last = t & 1;
+
+                                       switch (t >>> 1) {
+                                       case 0: // stored
+                                               // {
+                                               b >>>= (3);
+                                               k -= (3);
+                                               // }
+                                               t = k & 7; // go to byte boundary
+
+                                               // {
+                                               b >>>= (t);
+                                               k -= (t);
+                                               // }
+                                               mode = LENS; // get length of stored block
+                                               break;
+                                       case 1: // fixed
+                                               // {
+                                               var bl = []; // new Array(1);
+                                               var bd = []; // new Array(1);
+                                               var tl = [ [] ]; // new Array(1);
+                                               var td = [ [] ]; // new Array(1);
+
+                                               InfTree.inflate_trees_fixed(bl, bd, tl, td);
+                                               codes.init(bl[0], bd[0], tl[0], 0, td[0], 0);
+                                               // }
+
+                                               // {
+                                               b >>>= (3);
+                                               k -= (3);
+                                               // }
+
+                                               mode = CODES;
+                                               break;
+                                       case 2: // dynamic
+
+                                               // {
+                                               b >>>= (3);
+                                               k -= (3);
+                                               // }
+
+                                               mode = TABLE;
+                                               break;
+                                       case 3: // illegal
+
+                                               // {
+                                               b >>>= (3);
+                                               k -= (3);
+                                               // }
+                                               mode = BADBLOCKS;
+                                               z.msg = "invalid block type";
+                                               r = Z_DATA_ERROR;
+
+                                               that.bitb = b;
+                                               that.bitk = k;
+                                               z.avail_in = n;
+                                               z.total_in += p - z.next_in_index;
+                                               z.next_in_index = p;
+                                               that.write = q;
+                                               return that.inflate_flush(z, r);
+                                       }
+                                       break;
+                               case LENS:
+
+                                       while (k < (32)) {
+                                               if (n !== 0) {
+                                                       r = Z_OK;
+                                               } else {
+                                                       that.bitb = b;
+                                                       that.bitk = k;
+                                                       z.avail_in = n;
+                                                       z.total_in += p - z.next_in_index;
+                                                       z.next_in_index = p;
+                                                       that.write = q;
+                                                       return that.inflate_flush(z, r);
+                                               }
+                                               n--;
+                                               b |= (z.read_byte(p++) & 0xff) << k;
+                                               k += 8;
+                                       }
+
+                                       if ((((~b) >>> 16) & 0xffff) != (b & 0xffff)) {
+                                               mode = BADBLOCKS;
+                                               z.msg = "invalid stored block lengths";
+                                               r = Z_DATA_ERROR;
+
+                                               that.bitb = b;
+                                               that.bitk = k;
+                                               z.avail_in = n;
+                                               z.total_in += p - z.next_in_index;
+                                               z.next_in_index = p;
+                                               that.write = q;
+                                               return that.inflate_flush(z, r);
+                                       }
+                                       left = (b & 0xffff);
+                                       b = k = 0; // dump bits
+                                       mode = left !== 0 ? STORED : (last !== 0 ? DRY : TYPE);
+                                       break;
+                               case STORED:
+                                       if (n === 0) {
+                                               that.bitb = b;
+                                               that.bitk = k;
+                                               z.avail_in = n;
+                                               z.total_in += p - z.next_in_index;
+                                               z.next_in_index = p;
+                                               that.write = q;
+                                               return that.inflate_flush(z, r);
+                                       }
+
+                                       if (m === 0) {
+                                               if (q == that.end && that.read !== 0) {
+                                                       q = 0;
+                                                       m = /* (int) */(q < that.read ? that.read - q - 1 : that.end - q);
+                                               }
+                                               if (m === 0) {
+                                                       that.write = q;
+                                                       r = that.inflate_flush(z, r);
+                                                       q = that.write;
+                                                       m = /* (int) */(q < that.read ? that.read - q - 1 : that.end - q);
+                                                       if (q == that.end && that.read !== 0) {
+                                                               q = 0;
+                                                               m = /* (int) */(q < that.read ? that.read - q - 1 : that.end - q);
+                                                       }
+                                                       if (m === 0) {
+                                                               that.bitb = b;
+                                                               that.bitk = k;
+                                                               z.avail_in = n;
+                                                               z.total_in += p - z.next_in_index;
+                                                               z.next_in_index = p;
+                                                               that.write = q;
+                                                               return that.inflate_flush(z, r);
+                                                       }
+                                               }
+                                       }
+                                       r = Z_OK;
+
+                                       t = left;
+                                       if (t > n)
+                                               t = n;
+                                       if (t > m)
+                                               t = m;
+                                       that.window.set(z.read_buf(p, t), q);
+                                       p += t;
+                                       n -= t;
+                                       q += t;
+                                       m -= t;
+                                       if ((left -= t) !== 0)
+                                               break;
+                                       mode = last !== 0 ? DRY : TYPE;
+                                       break;
+                               case TABLE:
+
+                                       while (k < (14)) {
+                                               if (n !== 0) {
+                                                       r = Z_OK;
+                                               } else {
+                                                       that.bitb = b;
+                                                       that.bitk = k;
+                                                       z.avail_in = n;
+                                                       z.total_in += p - z.next_in_index;
+                                                       z.next_in_index = p;
+                                                       that.write = q;
+                                                       return that.inflate_flush(z, r);
+                                               }
+
+                                               n--;
+                                               b |= (z.read_byte(p++) & 0xff) << k;
+                                               k += 8;
+                                       }
+
+                                       table = t = (b & 0x3fff);
+                                       if ((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29) {
+                                               mode = BADBLOCKS;
+                                               z.msg = "too many length or distance symbols";
+                                               r = Z_DATA_ERROR;
+
+                                               that.bitb = b;
+                                               that.bitk = k;
+                                               z.avail_in = n;
+                                               z.total_in += p - z.next_in_index;
+                                               z.next_in_index = p;
+                                               that.write = q;
+                                               return that.inflate_flush(z, r);
+                                       }
+                                       t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f);
+                                       if (!blens || blens.length < t) {
+                                               blens = []; // new Array(t);
+                                       } else {
+                                               for (i = 0; i < t; i++) {
+                                                       blens[i] = 0;
+                                               }
+                                       }
+
+                                       // {
+                                       b >>>= (14);
+                                       k -= (14);
+                                       // }
+
+                                       index = 0;
+                                       mode = BTREE;
+                               case BTREE:
+                                       while (index < 4 + (table >>> 10)) {
+                                               while (k < (3)) {
+                                                       if (n !== 0) {
+                                                               r = Z_OK;
+                                                       } else {
+                                                               that.bitb = b;
+                                                               that.bitk = k;
+                                                               z.avail_in = n;
+                                                               z.total_in += p - z.next_in_index;
+                                                               z.next_in_index = p;
+                                                               that.write = q;
+                                                               return that.inflate_flush(z, r);
+                                                       }
+                                                       n--;
+                                                       b |= (z.read_byte(p++) & 0xff) << k;
+                                                       k += 8;
+                                               }
+
+                                               blens[border[index++]] = b & 7;
+
+                                               // {
+                                               b >>>= (3);
+                                               k -= (3);
+                                               // }
+                                       }
+
+                                       while (index < 19) {
+                                               blens[border[index++]] = 0;
+                                       }
+
+                                       bb[0] = 7;
+                                       t = inftree.inflate_trees_bits(blens, bb, tb, hufts, z);
+                                       if (t != Z_OK) {
+                                               r = t;
+                                               if (r == Z_DATA_ERROR) {
+                                                       blens = null;
+                                                       mode = BADBLOCKS;
+                                               }
+
+                                               that.bitb = b;
+                                               that.bitk = k;
+                                               z.avail_in = n;
+                                               z.total_in += p - z.next_in_index;
+                                               z.next_in_index = p;
+                                               that.write = q;
+                                               return that.inflate_flush(z, r);
+                                       }
+
+                                       index = 0;
+                                       mode = DTREE;
+                               case DTREE:
+                                       while (true) {
+                                               t = table;
+                                               if (!(index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f))) {
+                                                       break;
+                                               }
+
+                                               var j, c;
+
+                                               t = bb[0];
+
+                                               while (k < (t)) {
+                                                       if (n !== 0) {
+                                                               r = Z_OK;
+                                                       } else {
+                                                               that.bitb = b;
+                                                               that.bitk = k;
+                                                               z.avail_in = n;
+                                                               z.total_in += p - z.next_in_index;
+                                                               z.next_in_index = p;
+                                                               that.write = q;
+                                                               return that.inflate_flush(z, r);
+                                                       }
+                                                       n--;
+                                                       b |= (z.read_byte(p++) & 0xff) << k;
+                                                       k += 8;
+                                               }
+
+                                               // if (tb[0] == -1) {
+                                               // System.err.println("null...");
+                                               // }
+
+                                               t = hufts[(tb[0] + (b & inflate_mask[t])) * 3 + 1];
+                                               c = hufts[(tb[0] + (b & inflate_mask[t])) * 3 + 2];
+
+                                               if (c < 16) {
+                                                       b >>>= (t);
+                                                       k -= (t);
+                                                       blens[index++] = c;
+                                               } else { // c == 16..18
+                                                       i = c == 18 ? 7 : c - 14;
+                                                       j = c == 18 ? 11 : 3;
+
+                                                       while (k < (t + i)) {
+                                                               if (n !== 0) {
+                                                                       r = Z_OK;
+                                                               } else {
+                                                                       that.bitb = b;
+                                                                       that.bitk = k;
+                                                                       z.avail_in = n;
+                                                                       z.total_in += p - z.next_in_index;
+                                                                       z.next_in_index = p;
+                                                                       that.write = q;
+                                                                       return that.inflate_flush(z, r);
+                                                               }
+                                                               n--;
+                                                               b |= (z.read_byte(p++) & 0xff) << k;
+                                                               k += 8;
+                                                       }
+
+                                                       b >>>= (t);
+                                                       k -= (t);
+
+                                                       j += (b & inflate_mask[i]);
+
+                                                       b >>>= (i);
+                                                       k -= (i);
+
+                                                       i = index;
+                                                       t = table;
+                                                       if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) || (c == 16 && i < 1)) {
+                                                               blens = null;
+                                                               mode = BADBLOCKS;
+                                                               z.msg = "invalid bit length repeat";
+                                                               r = Z_DATA_ERROR;
+
+                                                               that.bitb = b;
+                                                               that.bitk = k;
+                                                               z.avail_in = n;
+                                                               z.total_in += p - z.next_in_index;
+                                                               z.next_in_index = p;
+                                                               that.write = q;
+                                                               return that.inflate_flush(z, r);
+                                                       }
+
+                                                       c = c == 16 ? blens[i - 1] : 0;
+                                                       do {
+                                                               blens[i++] = c;
+                                                       } while (--j !== 0);
+                                                       index = i;
+                                               }
+                                       }
+
+                                       tb[0] = -1;
+                                       // {
+                                       var bl_ = []; // new Array(1);
+                                       var bd_ = []; // new Array(1);
+                                       var tl_ = []; // new Array(1);
+                                       var td_ = []; // new Array(1);
+                                       bl_[0] = 9; // must be <= 9 for lookahead assumptions
+                                       bd_[0] = 6; // must be <= 9 for lookahead assumptions
+
+                                       t = table;
+                                       t = inftree.inflate_trees_dynamic(257 + (t & 0x1f), 1 + ((t >> 5) & 0x1f), blens, bl_, bd_, tl_, td_, hufts, z);
+
+                                       if (t != Z_OK) {
+                                               if (t == Z_DATA_ERROR) {
+                                                       blens = null;
+                                                       mode = BADBLOCKS;
+                                               }
+                                               r = t;
+
+                                               that.bitb = b;
+                                               that.bitk = k;
+                                               z.avail_in = n;
+                                               z.total_in += p - z.next_in_index;
+                                               z.next_in_index = p;
+                                               that.write = q;
+                                               return that.inflate_flush(z, r);
+                                       }
+                                       codes.init(bl_[0], bd_[0], hufts, tl_[0], hufts, td_[0]);
+                                       // }
+                                       mode = CODES;
+                               case CODES:
+                                       that.bitb = b;
+                                       that.bitk = k;
+                                       z.avail_in = n;
+                                       z.total_in += p - z.next_in_index;
+                                       z.next_in_index = p;
+                                       that.write = q;
+
+                                       if ((r = codes.proc(that, z, r)) != Z_STREAM_END) {
+                                               return that.inflate_flush(z, r);
+                                       }
+                                       r = Z_OK;
+                                       codes.free(z);
+
+                                       p = z.next_in_index;
+                                       n = z.avail_in;
+                                       b = that.bitb;
+                                       k = that.bitk;
+                                       q = that.write;
+                                       m = /* (int) */(q < that.read ? that.read - q - 1 : that.end - q);
+
+                                       if (last === 0) {
+                                               mode = TYPE;
+                                               break;
+                                       }
+                                       mode = DRY;
+                               case DRY:
+                                       that.write = q;
+                                       r = that.inflate_flush(z, r);
+                                       q = that.write;
+                                       m = /* (int) */(q < that.read ? that.read - q - 1 : that.end - q);
+                                       if (that.read != that.write) {
+                                               that.bitb = b;
+                                               that.bitk = k;
+                                               z.avail_in = n;
+                                               z.total_in += p - z.next_in_index;
+                                               z.next_in_index = p;
+                                               that.write = q;
+                                               return that.inflate_flush(z, r);
+                                       }
+                                       mode = DONELOCKS;
+                               case DONELOCKS:
+                                       r = Z_STREAM_END;
+
+                                       that.bitb = b;
+                                       that.bitk = k;
+                                       z.avail_in = n;
+                                       z.total_in += p - z.next_in_index;
+                                       z.next_in_index = p;
+                                       that.write = q;
+                                       return that.inflate_flush(z, r);
+                               case BADBLOCKS:
+                                       r = Z_DATA_ERROR;
+
+                                       that.bitb = b;
+                                       that.bitk = k;
+                                       z.avail_in = n;
+                                       z.total_in += p - z.next_in_index;
+                                       z.next_in_index = p;
+                                       that.write = q;
+                                       return that.inflate_flush(z, r);
+
+                               default:
+                                       r = Z_STREAM_ERROR;
+
+                                       that.bitb = b;
+                                       that.bitk = k;
+                                       z.avail_in = n;
+                                       z.total_in += p - z.next_in_index;
+                                       z.next_in_index = p;
+                                       that.write = q;
+                                       return that.inflate_flush(z, r);
+                               }
+                       }
+               };
+
+               that.free = function(z) {
+                       that.reset(z, null);
+                       that.window = null;
+                       hufts = null;
+                       // ZFREE(z, s);
+               };
+
+               that.set_dictionary = function(d, start, n) {
+                       that.window.set(d.subarray(start, start + n), 0);
+                       that.read = that.write = n;
+               };
+
+               // Returns true if inflate is currently at the end of a block generated
+               // by Z_SYNC_FLUSH or Z_FULL_FLUSH.
+               that.sync_point = function() {
+                       return mode == LENS ? 1 : 0;
+               };
+
+       }
+
+       // Inflate
+
+       // preset dictionary flag in zlib header
+       var PRESET_DICT = 0x20;
+
+       var Z_DEFLATED = 8;
+
+       var METHOD = 0; // waiting for method byte
+       var FLAG = 1; // waiting for flag byte
+       var DICT4 = 2; // four dictionary check bytes to go
+       var DICT3 = 3; // three dictionary check bytes to go
+       var DICT2 = 4; // two dictionary check bytes to go
+       var DICT1 = 5; // one dictionary check byte to go
+       var DICT0 = 6; // waiting for inflateSetDictionary
+       var BLOCKS = 7; // decompressing blocks
+       var DONE = 12; // finished check, done
+       var BAD = 13; // got an error--stay here
+
+       var mark = [ 0, 0, 0xff, 0xff ];
+
+       function Inflate() {
+               var that = this;
+
+               that.mode = 0; // current inflate mode
+
+               // mode dependent information
+               that.method = 0; // if FLAGS, method byte
+
+               // if CHECK, check values to compare
+               that.was = [ 0 ]; // new Array(1); // computed check value
+               that.need = 0; // stream check value
+
+               // if BAD, inflateSync's marker bytes count
+               that.marker = 0;
+
+               // mode independent information
+               that.wbits = 0; // log2(window size) (8..15, defaults to 15)
+
+               // this.blocks; // current inflate_blocks state
+
+               function inflateReset(z) {
+                       if (!z || !z.istate)
+                               return Z_STREAM_ERROR;
+
+                       z.total_in = z.total_out = 0;
+                       z.msg = null;
+                       z.istate.mode = BLOCKS;
+                       z.istate.blocks.reset(z, null);
+                       return Z_OK;
+               }
+
+               that.inflateEnd = function(z) {
+                       if (that.blocks)
+                               that.blocks.free(z);
+                       that.blocks = null;
+                       // ZFREE(z, z->state);
+                       return Z_OK;
+               };
+
+               that.inflateInit = function(z, w) {
+                       z.msg = null;
+                       that.blocks = null;
+
+                       // set window size
+                       if (w < 8 || w > 15) {
+                               that.inflateEnd(z);
+                               return Z_STREAM_ERROR;
+                       }
+                       that.wbits = w;
+
+                       z.istate.blocks = new InfBlocks(z, 1 << w);
+
+                       // reset state
+                       inflateReset(z);
+                       return Z_OK;
+               };
+
+               that.inflate = function(z, f) {
+                       var r;
+                       var b;
+
+                       if (!z || !z.istate || !z.next_in)
+                               return Z_STREAM_ERROR;
+                       f = f == Z_FINISH ? Z_BUF_ERROR : Z_OK;
+                       r = Z_BUF_ERROR;
+                       while (true) {
+                               // System.out.println("mode: "+z.istate.mode);
+                               switch (z.istate.mode) {
+                               case METHOD:
+
+                                       if (z.avail_in === 0)
+                                               return r;
+                                       r = f;
+
+                                       z.avail_in--;
+                                       z.total_in++;
+                                       if (((z.istate.method = z.read_byte(z.next_in_index++)) & 0xf) != Z_DEFLATED) {
+                                               z.istate.mode = BAD;
+                                               z.msg = "unknown compression method";
+                                               z.istate.marker = 5; // can't try inflateSync
+                                               break;
+                                       }
+                                       if ((z.istate.method >> 4) + 8 > z.istate.wbits) {
+                                               z.istate.mode = BAD;
+                                               z.msg = "invalid window size";
+                                               z.istate.marker = 5; // can't try inflateSync
+                                               break;
+                                       }
+                                       z.istate.mode = FLAG;
+                               case FLAG:
+
+                                       if (z.avail_in === 0)
+                                               return r;
+                                       r = f;
+
+                                       z.avail_in--;
+                                       z.total_in++;
+                                       b = (z.read_byte(z.next_in_index++)) & 0xff;
+
+                                       if ((((z.istate.method << 8) + b) % 31) !== 0) {
+                                               z.istate.mode = BAD;
+                                               z.msg = "incorrect header check";
+                                               z.istate.marker = 5; // can't try inflateSync
+                                               break;
+                                       }
+
+                                       if ((b & PRESET_DICT) === 0) {
+                                               z.istate.mode = BLOCKS;
+                                               break;
+                                       }
+                                       z.istate.mode = DICT4;
+                               case DICT4:
+
+                                       if (z.avail_in === 0)
+                                               return r;
+                                       r = f;
+
+                                       z.avail_in--;
+                                       z.total_in++;
+                                       z.istate.need = ((z.read_byte(z.next_in_index++) & 0xff) << 24) & 0xff000000;
+                                       z.istate.mode = DICT3;
+                               case DICT3:
+
+                                       if (z.avail_in === 0)
+                                               return r;
+                                       r = f;
+
+                                       z.avail_in--;
+                                       z.total_in++;
+                                       z.istate.need += ((z.read_byte(z.next_in_index++) & 0xff) << 16) & 0xff0000;
+                                       z.istate.mode = DICT2;
+                               case DICT2:
+
+                                       if (z.avail_in === 0)
+                                               return r;
+                                       r = f;
+
+                                       z.avail_in--;
+                                       z.total_in++;
+                                       z.istate.need += ((z.read_byte(z.next_in_index++) & 0xff) << 8) & 0xff00;
+                                       z.istate.mode = DICT1;
+                               case DICT1:
+
+                                       if (z.avail_in === 0)
+                                               return r;
+                                       r = f;
+
+                                       z.avail_in--;
+                                       z.total_in++;
+                                       z.istate.need += (z.read_byte(z.next_in_index++) & 0xff);
+                                       z.istate.mode = DICT0;
+                                       return Z_NEED_DICT;
+                               case DICT0:
+                                       z.istate.mode = BAD;
+                                       z.msg = "need dictionary";
+                                       z.istate.marker = 0; // can try inflateSync
+                                       return Z_STREAM_ERROR;
+                               case BLOCKS:
+
+                                       r = z.istate.blocks.proc(z, r);
+                                       if (r == Z_DATA_ERROR) {
+                                               z.istate.mode = BAD;
+                                               z.istate.marker = 0; // can try inflateSync
+                                               break;
+                                       }
+                                       if (r == Z_OK) {
+                                               r = f;
+                                       }
+                                       if (r != Z_STREAM_END) {
+                                               return r;
+                                       }
+                                       r = f;
+                                       z.istate.blocks.reset(z, z.istate.was);
+                                       z.istate.mode = DONE;
+                               case DONE:
+                                       return Z_STREAM_END;
+                               case BAD:
+                                       return Z_DATA_ERROR;
+                               default:
+                                       return Z_STREAM_ERROR;
+                               }
+                       }
+               };
+
+               that.inflateSetDictionary = function(z, dictionary, dictLength) {
+                       var index = 0;
+                       var length = dictLength;
+                       if (!z || !z.istate || z.istate.mode != DICT0)
+                               return Z_STREAM_ERROR;
+
+                       if (length >= (1 << z.istate.wbits)) {
+                               length = (1 << z.istate.wbits) - 1;
+                               index = dictLength - length;
+                       }
+                       z.istate.blocks.set_dictionary(dictionary, index, length);
+                       z.istate.mode = BLOCKS;
+                       return Z_OK;
+               };
+
+               that.inflateSync = function(z) {
+                       var n; // number of bytes to look at
+                       var p; // pointer to bytes
+                       var m; // number of marker bytes found in a row
+                       var r, w; // temporaries to save total_in and total_out
+
+                       // set up
+                       if (!z || !z.istate)
+                               return Z_STREAM_ERROR;
+                       if (z.istate.mode != BAD) {
+                               z.istate.mode = BAD;
+                               z.istate.marker = 0;
+                       }
+                       if ((n = z.avail_in) === 0)
+                               return Z_BUF_ERROR;
+                       p = z.next_in_index;
+                       m = z.istate.marker;
+
+                       // search
+                       while (n !== 0 && m < 4) {
+                               if (z.read_byte(p) == mark[m]) {
+                                       m++;
+                               } else if (z.read_byte(p) !== 0) {
+                                       m = 0;
+                               } else {
+                                       m = 4 - m;
+                               }
+                               p++;
+                               n--;
+                       }
+
+                       // restore
+                       z.total_in += p - z.next_in_index;
+                       z.next_in_index = p;
+                       z.avail_in = n;
+                       z.istate.marker = m;
+
+                       // return no joy or set up to restart on a new block
+                       if (m != 4) {
+                               return Z_DATA_ERROR;
+                       }
+                       r = z.total_in;
+                       w = z.total_out;
+                       inflateReset(z);
+                       z.total_in = r;
+                       z.total_out = w;
+                       z.istate.mode = BLOCKS;
+                       return Z_OK;
+               };
+
+               // Returns true if inflate is currently at the end of a block generated
+               // by Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP
+               // implementation to provide an additional safety check. PPP uses
+               // Z_SYNC_FLUSH
+               // but removes the length bytes of the resulting empty stored block. When
+               // decompressing, PPP checks that at the end of input packet, inflate is
+               // waiting for these length bytes.
+               that.inflateSyncPoint = function(z) {
+                       if (!z || !z.istate || !z.istate.blocks)
+                               return Z_STREAM_ERROR;
+                       return z.istate.blocks.sync_point();
+               };
+       }
+
+       // ZStream
+
+       function ZStream() {
+       }
+
+       ZStream.prototype = {
+               inflateInit : function(bits) {
+                       var that = this;
+                       that.istate = new Inflate();
+                       if (!bits)
+                               bits = MAX_BITS;
+                       return that.istate.inflateInit(that, bits);
+               },
+
+               inflate : function(f) {
+                       var that = this;
+                       if (!that.istate)
+                               return Z_STREAM_ERROR;
+                       return that.istate.inflate(that, f);
+               },
+
+               inflateEnd : function() {
+                       var that = this;
+                       if (!that.istate)
+                               return Z_STREAM_ERROR;
+                       var ret = that.istate.inflateEnd(that);
+                       that.istate = null;
+                       return ret;
+               },
+
+               inflateSync : function() {
+                       var that = this;
+                       if (!that.istate)
+                               return Z_STREAM_ERROR;
+                       return that.istate.inflateSync(that);
+               },
+               inflateSetDictionary : function(dictionary, dictLength) {
+                       var that = this;
+                       if (!that.istate)
+                               return Z_STREAM_ERROR;
+                       return that.istate.inflateSetDictionary(that, dictionary, dictLength);
+               },
+               read_byte : function(start) {
+                       var that = this;
+                       return that.next_in.subarray(start, start + 1)[0];
+               },
+               read_buf : function(start, size) {
+                       var that = this;
+                       return that.next_in.subarray(start, start + size);
+               }
+       };
+
+       // Inflater
+
+       function Inflater() {
+               var that = this;
+               var z = new ZStream();
+               var bufsize = 512;
+               var flush = Z_NO_FLUSH;
+               var buf = new Uint8Array(bufsize);
+               var nomoreinput = false;
+
+               z.inflateInit();
+               z.next_out = buf;
+
+               that.append = function(data, onprogress) {
+                       var err, buffers = [], lastIndex = 0, bufferIndex = 0, bufferSize = 0, array;
+                       if (data.length === 0)
+                               return;
+                       z.next_in_index = 0;
+                       z.next_in = data;
+                       z.avail_in = data.length;
+                       do {
+                               z.next_out_index = 0;
+                               z.avail_out = bufsize;
+                               if ((z.avail_in === 0) && (!nomoreinput)) { // if buffer is empty and more input is available, refill it
+                                       z.next_in_index = 0;
+                                       nomoreinput = true;
+                               }
+                               err = z.inflate(flush);
+                               if (nomoreinput && (err == Z_BUF_ERROR))
+                                       return -1;
+                               if (err != Z_OK && err != Z_STREAM_END)
+                                       throw "inflating: " + z.msg;
+                               if ((nomoreinput || err == Z_STREAM_END) && (z.avail_in == data.length))
+                                       return -1;
+                               if (z.next_out_index)
+                                       if (z.next_out_index == bufsize)
+                                               buffers.push(new Uint8Array(buf));
+                                       else
+                                               buffers.push(new Uint8Array(buf.subarray(0, z.next_out_index)));
+                               bufferSize += z.next_out_index;
+                               if (onprogress && z.next_in_index > 0 && z.next_in_index != lastIndex) {
+                                       onprogress(z.next_in_index);
+                                       lastIndex = z.next_in_index;
+                               }
+                       } while (z.avail_in > 0 || z.avail_out === 0);
+                       array = new Uint8Array(bufferSize);
+                       buffers.forEach(function(chunk) {
+                               array.set(chunk, bufferIndex);
+                               bufferIndex += chunk.length;
+                       });
+                       return array;
+               };
+               that.flush = function() {
+                       z.inflateEnd();
+               };
+       }
+
+       var inflater;
+
+       if (obj.zip)
+               obj.zip.Inflater = Inflater;
+       else {
+               inflater = new Inflater();
+               obj.addEventListener("message", function(event) {
+                       var message = event.data;
+
+                       if (message.append)
+                               obj.postMessage({
+                                       onappend : true,
+                                       data : inflater.append(message.data, function(current) {
+                                               obj.postMessage({
+                                                       progress : true,
+                                                       current : current
+                                               });
+                                       })
+                               });
+                       if (message.flush) {
+                               inflater.flush();
+                               obj.postMessage({
+                                       onflush : true
+                               });
+                       }
+               }, false);
+       }
+
+})(EasyDeflate);
index 83525f2..34add28 100644 (file)
@@ -37,8 +37,6 @@
                 *  - `pluralRules`
                 *  - `digitGroupingPattern`
                 *  - `fallbackLanguages`
-                *  - `bcp47Map`
-                *  - `languageNames`
                 *
                 * @property
                 */
index 808f347..45863a3 100644 (file)
                },
 
                /**
-                * Formats language tags according the BCP 47 standard.
+                * Formats language tags according the BCP47 standard.
                 * See LanguageCode::bcp47 for the PHP implementation.
                 *
                 * @param {string} languageTag Well-formed language tag
                 * @return {string}
                 */
                bcp47: function ( languageTag ) {
-                       var bcp47Map,
-                               formatted,
-                               segments,
+                       var formatted,
                                isFirstSegment = true,
-                               isPrivate = false;
+                               isPrivate = false,
+                               segments = languageTag.split( '-' );
 
-                       languageTag = languageTag.toLowerCase();
-
-                       bcp47Map = mw.language.getData( mw.config.get( 'wgUserLanguage' ), 'bcp47Map' );
-                       if ( bcp47Map && Object.prototype.hasOwnProperty.call( bcp47Map, languageTag ) ) {
-                               languageTag = bcp47Map[ languageTag ];
-                       }
-
-                       segments = languageTag.split( '-' );
                        formatted = segments.map( function ( segment ) {
                                var newSegment;
 
index ad0bc4a..1cc48e2 100644 (file)
                        display: block;
                        padding-top: 1em;
                }
+
+               label {
+                       // Workaround for Chrome browser bug (T199932)
+                       // Override padding rule from FieldLayout
+                       padding-left: 0 !important; /* stylelint-disable-line declaration-no-important */
+               }
        }
 
        .mw-rcfilters-ui-cell {
index 06d789b..0cb042a 100644 (file)
@@ -109,6 +109,10 @@ $wgAutoloadClasses += [
        # tests/phpunit/includes/db
        'DatabaseTestHelper' => "$testDir/phpunit/includes/db/DatabaseTestHelper.php",
 
+       # tests/phpunit/includes/debug
+       'TestDeprecatedClass' => "$testDir/phpunit/includes/debug/TestDeprecatedClass.php",
+       'TestDeprecatedSubclass' => "$testDir/phpunit/includes/debug/TestDeprecatedSubclass.php",
+
        # tests/phpunit/includes/diff
        'FakeDiffOp' => "$testDir/phpunit/includes/diff/FakeDiffOp.php",
 
index ceee432..5eb91c3 100644 (file)
@@ -116,6 +116,30 @@ class ParserTestRunner {
         */
        private $normalizationFunctions = [];
 
+       /**
+        * Run disabled parser tests
+        * @var bool
+        */
+       private $runDisabled;
+
+       /**
+        * Run tests intended only for parsoid
+        * @var bool
+        */
+       private $runParsoid;
+
+       /**
+        * Disable parse on article insertion
+        * @var bool
+        */
+       private $disableSaveParse;
+
+       /**
+        * Reuse upload directory
+        * @var bool
+        */
+       private $keepUploads;
+
        /**
         * @param TestRecorder $recorder
         * @param array $options
@@ -148,6 +172,8 @@ class ParserTestRunner {
                $this->runDisabled = !empty( $options['run-disabled'] );
                $this->runParsoid = !empty( $options['run-parsoid'] );
 
+               $this->disableSaveParse = !empty( $options['disable-save-parse'] );
+
                $this->tidySupport = new TidySupport( !empty( $options['use-tidy-config'] ) );
                if ( !$this->tidySupport->isEnabled() ) {
                        $this->recorder->warning(
@@ -1184,7 +1210,8 @@ class ParserTestRunner {
                        'site_stats', 'ipblocks', 'image', 'oldimage',
                        'recentchanges', 'watchlist', 'interwiki', 'logging', 'log_search',
                        'querycache', 'objectcache', 'job', 'l10n_cache', 'redirect', 'querycachetwo',
-                       'archive', 'user_groups', 'page_props', 'category'
+                       'archive', 'user_groups', 'page_props', 'category',
+                       'slots', 'content', 'slot_roles', 'content_models',
                ];
 
                if ( $wgCommentTableSchemaMigrationStage >= MIGRATION_WRITE_BOTH ) {
@@ -1223,7 +1250,7 @@ class ParserTestRunner {
         * For best performance, call this once only for all tests. However, it can
         * be called at the start of each test if more isolation is desired.
         *
-        * @todo: This is basically an unrefactored copy of
+        * @todo This is basically an unrefactored copy of
         * MediaWikiTestCase::setupAllTestDBs. They should be factored out somehow.
         *
         * Do not call this function from a MediaWikiTestCase subclass, since
@@ -1451,7 +1478,6 @@ class ParserTestRunner {
                $this->checkSetupDone( 'setupDatabase' );
 
                $this->dbClone->destroy();
-               $this->databaseSetupDone = false;
 
                if ( $this->useTemporaryTables ) {
                        if ( $this->db->getType() == 'sqlite' ) {
@@ -1651,11 +1677,15 @@ class ParserTestRunner {
                        );
                }
 
-               // Use mock parser, to make debugging of actual parser tests simpler.
+               // Optionally use mock parser, to make debugging of actual parser tests simpler.
                // But initialise the MessageCache clone first, don't let MessageCache
                // get a reference to the mock object.
-               MessageCache::singleton()->getParser();
-               $restore = $this->executeSetupSnippets( [ 'wgParser' => new ParserTestMockParser ] );
+               if ( $this->disableSaveParse ) {
+                       MessageCache::singleton()->getParser();
+                       $restore = $this->executeSetupSnippets( [ 'wgParser' => new ParserTestMockParser ] );
+               } else {
+                       $restore = false;
+               }
                try {
                        $status = $page->doEditContent(
                                $newContent,
@@ -1663,7 +1693,9 @@ class ParserTestRunner {
                                EDIT_NEW | EDIT_INTERNAL
                        );
                } finally {
-                       $restore();
+                       if ( $restore ) {
+                               $restore();
+                       }
                }
 
                if ( !$status->isOK() ) {
index 6a423d5..e1d943f 100644 (file)
@@ -62,6 +62,8 @@ class ParserTestsMaintenance extends Maintenance {
                        'be used.', false, true );
                $this->addOption( 'run-disabled', 'run disabled tests' );
                $this->addOption( 'run-parsoid', 'run parsoid tests (normally disabled)' );
+               $this->addOption( 'disable-save-parse', 'Don\'t run the parser when ' .
+                       'inserting articles into the database' );
                $this->addOption( 'dwdiff', 'Use dwdiff to display diff output' );
                $this->addOption( 'mark-ws', 'Mark whitespace in diffs by replacing it with symbols' );
                $this->addOption( 'norm', 'Apply a comma-separated list of normalization functions to ' .
@@ -180,6 +182,7 @@ class ParserTestsMaintenance extends Maintenance {
                        'keep-uploads' => $this->hasOption( 'keep-uploads' ),
                        'run-disabled' => $this->hasOption( 'run-disabled' ),
                        'run-parsoid' => $this->hasOption( 'run-parsoid' ),
+                       'disable-save-parse' => $this->hasOption( 'disable-save-parse' ),
                        'use-tidy-config' => $this->hasOption( 'use-tidy-config' ),
                        'file-backend' => $this->getOption( 'file-backend' ),
                        'upload-dir' => $this->getOption( 'upload-dir' ),
index 8f0826b..036b618 100644 (file)
@@ -16,11 +16,12 @@ class EditPageTest extends MediaWikiLangTestCase {
 
                parent::setUp();
 
+               $this->setContentLang( $wgContLang );
+
                $this->setMwGlobals( [
                        'wgExtraNamespaces' => $wgExtraNamespaces,
                        'wgNamespaceContentModels' => $wgNamespaceContentModels,
                        'wgContentHandlers' => $wgContentHandlers,
-                       'wgContLang' => $wgContLang,
                ] );
 
                $wgExtraNamespaces[12312] = 'Dummy';
index 75ebd31..164c83c 100644 (file)
@@ -17,7 +17,7 @@ class ExtraParserTest extends MediaWikiTestCase {
 
                $contLang = Language::factory( 'en' );
                $this->setMwGlobals( [
-                       'wgShowDBErrorBacktrace' => true,
+                       'wgShowExceptionDetails' => true,
                        'wgCleanSignatures' => true,
                ] );
                $this->setUserLang( 'en' );
diff --git a/tests/phpunit/includes/GlobalFunctions/WfExpandUrlTest.php b/tests/phpunit/includes/GlobalFunctions/WfExpandUrlTest.php
new file mode 100644 (file)
index 0000000..f008830
--- /dev/null
@@ -0,0 +1,153 @@
+<?php
+/**
+ * @group GlobalFunctions
+ * @covers ::wfExpandUrl
+ */
+class WfExpandUrlTest extends MediaWikiTestCase {
+       /**
+        * @dataProvider provideExpandableUrls
+        */
+       public function testWfExpandUrl( $fullUrl, $shortUrl, $defaultProto,
+               $server, $canServer, $httpsMode, $httpsPort, $message
+       ) {
+               // Fake $wgServer, $wgCanonicalServer and $wgRequest->getProtocol()
+               // fake edit to fake globals
+               $this->setMwGlobals( [
+                       'wgServer' => $server,
+                       'wgCanonicalServer' => $canServer,
+                       'wgRequest' => new FauxRequest( [], false, null, $httpsMode ? 'https' : 'http' ),
+                       'wgHttpsPort' => $httpsPort
+               ] );
+
+               $this->assertEquals( $fullUrl, wfExpandUrl( $shortUrl, $defaultProto ), $message );
+       }
+
+       /**
+        * Provider of URL examples for testing wfExpandUrl()
+        *
+        * @return array
+        */
+       public static function provideExpandableUrls() {
+               $modes = [ 'http', 'https' ];
+               $servers = [
+                       'http' => 'http://example.com',
+                       'https' => 'https://example.com',
+                       'protocol-relative' => '//example.com'
+               ];
+               $defaultProtos = [
+                       'http' => PROTO_HTTP,
+                       'https' => PROTO_HTTPS,
+                       'protocol-relative' => PROTO_RELATIVE,
+                       'current' => PROTO_CURRENT,
+                       'canonical' => PROTO_CANONICAL
+               ];
+
+               $retval = [];
+               foreach ( $modes as $mode ) {
+                       $httpsMode = $mode == 'https';
+                       foreach ( $servers as $serverDesc => $server ) {
+                               foreach ( $modes as $canServerMode ) {
+                                       $canServer = "$canServerMode://example2.com";
+                                       foreach ( $defaultProtos as $protoDesc => $defaultProto ) {
+                                               $retval[] = [
+                                                       'http://example.com', 'http://example.com',
+                                                       $defaultProto, $server, $canServer, $httpsMode, 443,
+                                                       "Testing fully qualified http URLs (no need to expand) "
+                                                               . "(defaultProto: $protoDesc , wgServer: $server, "
+                                                               . "wgCanonicalServer: $canServer, current request protocol: $mode )"
+                                               ];
+                                               $retval[] = [
+                                                       'https://example.com', 'https://example.com',
+                                                       $defaultProto, $server, $canServer, $httpsMode, 443,
+                                                       "Testing fully qualified https URLs (no need to expand) "
+                                                               . "(defaultProto: $protoDesc , wgServer: $server, "
+                                                               . "wgCanonicalServer: $canServer, current request protocol: $mode )"
+                                               ];
+                                               # Would be nice to support this, see fixme on wfExpandUrl()
+                                               $retval[] = [
+                                                       "wiki/FooBar", 'wiki/FooBar',
+                                                       $defaultProto, $server, $canServer, $httpsMode, 443,
+                                                       "Test non-expandable relative URLs (defaultProto: $protoDesc, "
+                                                               . "wgServer: $server, wgCanonicalServer: $canServer, "
+                                                               . "current request protocol: $mode )"
+                                               ];
+
+                                               // Determine expected protocol
+                                               if ( $protoDesc == 'protocol-relative' ) {
+                                                       $p = '';
+                                               } elseif ( $protoDesc == 'current' ) {
+                                                       $p = "$mode:";
+                                               } elseif ( $protoDesc == 'canonical' ) {
+                                                       $p = "$canServerMode:";
+                                               } else {
+                                                       $p = $protoDesc . ':';
+                                               }
+                                               // Determine expected server name
+                                               if ( $protoDesc == 'canonical' ) {
+                                                       $srv = $canServer;
+                                               } elseif ( $serverDesc == 'protocol-relative' ) {
+                                                       $srv = $p . $server;
+                                               } else {
+                                                       $srv = $server;
+                                               }
+
+                                               $retval[] = [
+                                                       "$p//wikipedia.org", '//wikipedia.org',
+                                                       $defaultProto, $server, $canServer, $httpsMode, 443,
+                                                       "Test protocol-relative URL (defaultProto: $protoDesc, "
+                                                               . "wgServer: $server, wgCanonicalServer: $canServer, "
+                                                               . "current request protocol: $mode )"
+                                               ];
+                                               $retval[] = [
+                                                       "$srv/wiki/FooBar",
+                                                       '/wiki/FooBar',
+                                                       $defaultProto,
+                                                       $server,
+                                                       $canServer,
+                                                       $httpsMode,
+                                                       443,
+                                                       "Testing expanding URL beginning with / (defaultProto: $protoDesc, "
+                                                               . "wgServer: $server, wgCanonicalServer: $canServer, "
+                                                               . "current request protocol: $mode )"
+                                               ];
+                                       }
+                               }
+                       }
+               }
+
+               // Don't add HTTPS port to foreign URLs
+               $retval[] = [
+                       'https://foreign.example.com/foo',
+                       'https://foreign.example.com/foo',
+                       PROTO_HTTPS,
+                       '//wiki.example.com',
+                       'http://wiki.example.com',
+                       'https',
+                       111,
+                       "Don't add HTTPS port to foreign URLs"
+               ];
+               $retval[] = [
+                       'https://foreign.example.com:222/foo',
+                       'https://foreign.example.com:222/foo',
+                       PROTO_HTTPS,
+                       '//wiki.example.com',
+                       'http://wiki.example.com',
+                       'https',
+                       111,
+                       "Don't overwrite HTTPS port of foreign URLs"
+               ];
+               // Do add HTTPS port to local URLs
+               $retval[] = [
+                       'https://wiki.example.com:111/foo',
+                       '/foo',
+                       PROTO_HTTPS,
+                       '//wiki.example.com',
+                       'http://wiki.example.com',
+                       'https',
+                       111,
+                       "Do add HTTPS port to protocol-relative URLs"
+               ];
+
+               return $retval;
+       }
+}
diff --git a/tests/phpunit/includes/GlobalFunctions/wfExpandUrlTest.php b/tests/phpunit/includes/GlobalFunctions/wfExpandUrlTest.php
deleted file mode 100644 (file)
index 1cd320f..0000000
+++ /dev/null
@@ -1,117 +0,0 @@
-<?php
-/**
- * @group GlobalFunctions
- * @covers ::wfExpandUrl
- */
-class WfExpandUrlTest extends MediaWikiTestCase {
-       /**
-        * @dataProvider provideExpandableUrls
-        */
-       public function testWfExpandUrl( $fullUrl, $shortUrl, $defaultProto,
-               $server, $canServer, $httpsMode, $message
-       ) {
-               // Fake $wgServer, $wgCanonicalServer and $wgRequest->getProtocol()
-               $this->setMwGlobals( [
-                       'wgServer' => $server,
-                       'wgCanonicalServer' => $canServer,
-                       'wgRequest' => new FauxRequest( [], false, null, $httpsMode ? 'https' : 'http' )
-               ] );
-
-               $this->assertEquals( $fullUrl, wfExpandUrl( $shortUrl, $defaultProto ), $message );
-       }
-
-       /**
-        * Provider of URL examples for testing wfExpandUrl()
-        *
-        * @return array
-        */
-       public static function provideExpandableUrls() {
-               $modes = [ 'http', 'https' ];
-               $servers = [
-                       'http' => 'http://example.com',
-                       'https' => 'https://example.com',
-                       'protocol-relative' => '//example.com'
-               ];
-               $defaultProtos = [
-                       'http' => PROTO_HTTP,
-                       'https' => PROTO_HTTPS,
-                       'protocol-relative' => PROTO_RELATIVE,
-                       'current' => PROTO_CURRENT,
-                       'canonical' => PROTO_CANONICAL
-               ];
-
-               $retval = [];
-               foreach ( $modes as $mode ) {
-                       $httpsMode = $mode == 'https';
-                       foreach ( $servers as $serverDesc => $server ) {
-                               foreach ( $modes as $canServerMode ) {
-                                       $canServer = "$canServerMode://example2.com";
-                                       foreach ( $defaultProtos as $protoDesc => $defaultProto ) {
-                                               $retval[] = [
-                                                       'http://example.com', 'http://example.com',
-                                                       $defaultProto, $server, $canServer, $httpsMode,
-                                                       "Testing fully qualified http URLs (no need to expand) "
-                                                               . "(defaultProto: $protoDesc , wgServer: $server, "
-                                                               . "wgCanonicalServer: $canServer, current request protocol: $mode )"
-                                               ];
-                                               $retval[] = [
-                                                       'https://example.com', 'https://example.com',
-                                                       $defaultProto, $server, $canServer, $httpsMode,
-                                                       "Testing fully qualified https URLs (no need to expand) "
-                                                               . "(defaultProto: $protoDesc , wgServer: $server, "
-                                                               . "wgCanonicalServer: $canServer, current request protocol: $mode )"
-                                               ];
-                                               # Would be nice to support this, see fixme on wfExpandUrl()
-                                               $retval[] = [
-                                                       "wiki/FooBar", 'wiki/FooBar',
-                                                       $defaultProto, $server, $canServer, $httpsMode,
-                                                       "Test non-expandable relative URLs (defaultProto: $protoDesc, "
-                                                               . "wgServer: $server, wgCanonicalServer: $canServer, "
-                                                               . "current request protocol: $mode )"
-                                               ];
-
-                                               // Determine expected protocol
-                                               if ( $protoDesc == 'protocol-relative' ) {
-                                                       $p = '';
-                                               } elseif ( $protoDesc == 'current' ) {
-                                                       $p = "$mode:";
-                                               } elseif ( $protoDesc == 'canonical' ) {
-                                                       $p = "$canServerMode:";
-                                               } else {
-                                                       $p = $protoDesc . ':';
-                                               }
-                                               // Determine expected server name
-                                               if ( $protoDesc == 'canonical' ) {
-                                                       $srv = $canServer;
-                                               } elseif ( $serverDesc == 'protocol-relative' ) {
-                                                       $srv = $p . $server;
-                                               } else {
-                                                       $srv = $server;
-                                               }
-
-                                               $retval[] = [
-                                                       "$p//wikipedia.org", '//wikipedia.org',
-                                                       $defaultProto, $server, $canServer, $httpsMode,
-                                                       "Test protocol-relative URL (defaultProto: $protoDesc, "
-                                                               . "wgServer: $server, wgCanonicalServer: $canServer, "
-                                                               . "current request protocol: $mode )"
-                                               ];
-                                               $retval[] = [
-                                                       "$srv/wiki/FooBar",
-                                                       '/wiki/FooBar',
-                                                       $defaultProto,
-                                                       $server,
-                                                       $canServer,
-                                                       $httpsMode,
-                                                       "Testing expanding URL beginning with / (defaultProto: $protoDesc, "
-                                                               . "wgServer: $server, wgCanonicalServer: $canServer, "
-                                                               . "current request protocol: $mode )"
-                                               ];
-                                       }
-                               }
-                       }
-               }
-
-               return $retval;
-       }
-}
index 093cb07..7763aff 100644 (file)
@@ -364,6 +364,7 @@ class MediaWikiServicesTest extends MediaWikiTestCase {
                        'ExternalStoreFactory' => [ 'ExternalStoreFactory', ExternalStoreFactory::class ],
                        'PreferencesFactory' => [ 'PreferencesFactory', PreferencesFactory::class ],
                        'ActorMigration' => [ 'ActorMigration', ActorMigration::class ],
+                       'ConfigRepository' => [ 'ConfigRepository', \MediaWiki\Config\ConfigRepository::class ],
                ];
        }
 
index 7d7e637..d79d2cf 100644 (file)
@@ -1,6 +1,8 @@
 <?php
 
 class MediaWikiTest extends MediaWikiTestCase {
+       private $oldServer, $oldGet, $oldPost;
+
        protected function setUp() {
                parent::setUp();
 
@@ -11,6 +13,18 @@ class MediaWikiTest extends MediaWikiTestCase {
                        'wgArticlePath' => '/wiki/$1',
                        'wgActionPaths' => [],
                ] );
+
+               // phpcs:disable MediaWiki.Usage.SuperGlobalsUsage.SuperGlobals
+               $this->oldServer = $_SERVER;
+               $this->oldGet = $_GET;
+               $this->oldPost = $_POST;
+       }
+
+       protected function tearDown() {
+               parent::tearDown();
+               $_SERVER = $this->oldServer;
+               $_GET = $this->oldGet;
+               $_POST = $this->oldPost;
        }
 
        public static function provideTryNormaliseRedirect() {
@@ -113,6 +127,13 @@ class MediaWikiTest extends MediaWikiTestCase {
                                'title' => 'Foo_Bar',
                                'redirect' => false,
                        ],
+                       [
+                               // Path with double slash prefix (T100782)
+                               'url' => 'http://example.org//wiki/Double_slash',
+                               'query' => [],
+                               'title' => 'Double_slash',
+                               'redirect' => false,
+                       ],
                ];
        }
 
@@ -122,11 +143,13 @@ class MediaWikiTest extends MediaWikiTestCase {
         */
        public function testTryNormaliseRedirect( $url, $query, $title, $expectedRedirect = false ) {
                // Set SERVER because interpolateTitle() doesn't use getRequestURL(),
-               // whereas tryNormaliseRedirect does().
+               // whereas tryNormaliseRedirect does(). Also, using WebRequest allows
+               // us to test some quirks in that class.
                $_SERVER['REQUEST_URI'] = $url;
+               $_POST = [];
+               $_GET = $query;
+               $req = new WebRequest;
 
-               $req = new FauxRequest( $query );
-               $req->setRequestURL( $url );
                // This adds a virtual 'title' query parameter. Normally called from Setup.php
                $req->interpolateTitle();
 
diff --git a/tests/phpunit/includes/MultiHttpClientTest.php b/tests/phpunit/includes/MultiHttpClientTest.php
new file mode 100644 (file)
index 0000000..7073b71
--- /dev/null
@@ -0,0 +1,168 @@
+<?php
+
+/**
+ * Tests for MultiHttpClient
+ *
+ * The urls herein are not actually called, because we mock the return results.
+ *
+ * @covers MultiHttpClient
+ */
+class MultiHttpClientTest extends MediaWikiTestCase {
+       protected $client;
+
+       protected function setUp() {
+               parent::setUp();
+               $client = $this->getMockBuilder( MultiHttpClient::class )
+                       ->setConstructorArgs( [ [] ] )
+                       ->setMethods( [ 'isCurlEnabled' ] )->getMock();
+               $client->method( 'isCurlEnabled' )->willReturn( false );
+               $this->client = $client;
+       }
+
+       private function getHttpRequest( $statusValue, $statusCode, $headers = [] ) {
+               $httpRequest = $this->getMockBuilder( PhpHttpRequest::class )
+                       ->setConstructorArgs( [ '', [] ] )
+                       ->getMock();
+               $httpRequest->expects( $this->any() )
+                       ->method( 'execute' )
+                       ->willReturn( Status::wrap( $statusValue ) );
+               $httpRequest->expects( $this->any() )
+                       ->method( 'getResponseHeaders' )
+                       ->willReturn( $headers );
+               $httpRequest->expects( $this->any() )
+                               ->method( 'getStatus' )
+                               ->willReturn( $statusCode );
+               return $httpRequest;
+       }
+
+       private function mockHttpRequestFactory( $httpRequest ) {
+               $factory = $this->getMockBuilder( MediaWiki\Http\HttpRequestFactory::class )
+                       ->getMock();
+               $factory->expects( $this->any() )
+                       ->method( 'create' )
+                       ->willReturn( $httpRequest );
+               return $factory;
+       }
+
+       /**
+        * Test call of a single url that should succeed
+        */
+       public function testMultiHttpClientSingleSuccess() {
+               // Mock success
+               $httpRequest = $this->getHttpRequest( StatusValue::newGood( 200 ), 200 );
+               $this->setService( 'HttpRequestFactory', $this->mockHttpRequestFactory( $httpRequest ) );
+
+               list( $rcode, $rdesc, /* $rhdrs */, $rbody, $rerr ) = $this->client->run( [
+                       'method' => 'GET',
+                       'url' => "http://example.test",
+               ] );
+
+               $this->assertEquals( 200, $rcode );
+       }
+
+       /**
+        * Test call of a single url that should not exist, and therefore fail
+        */
+       public function testMultiHttpClientSingleFailure() {
+               // Mock an invalid tld
+               $httpRequest = $this->getHttpRequest(
+                       StatusValue::newFatal( 'http-invalid-url', 'http://www.example.test' ), 0 );
+               $this->setService( 'HttpRequestFactory', $this->mockHttpRequestFactory( $httpRequest ) );
+
+               list( $rcode, $rdesc, /* $rhdrs */, $rbody, $rerr ) = $this->client->run( [
+                       'method' => 'GET',
+                       'url' => "http://www.example.test",
+               ] );
+
+               $failure = $rcode < 200 || $rcode >= 400;
+               $this->assertTrue( $failure );
+       }
+
+       /**
+        * Test call of multiple urls that should all succeed
+        */
+       public function testMultiHttpClientMultipleSuccess() {
+               // Mock success
+               $httpRequest = $this->getHttpRequest( StatusValue::newGood( 200 ), 200 );
+               $this->setService( 'HttpRequestFactory', $this->mockHttpRequestFactory( $httpRequest ) );
+
+               $reqs = [
+                       [
+                               'method' => 'GET',
+                               'url' => 'http://example.test',
+                       ],
+                       [
+                               'method' => 'GET',
+                               'url' => 'https://get.test',
+                       ],
+               ];
+               $responses = $this->client->runMulti( $reqs );
+               foreach ( $responses as $response ) {
+                       list( $rcode, $rdesc, /* $rhdrs */, $rbody, $rerr ) = $response['response'];
+                       $this->assertEquals( 200, $rcode );
+               }
+       }
+
+       /**
+        * Test call of multiple urls that should all fail
+        */
+       public function testMultiHttpClientMultipleFailure() {
+               // Mock page not found
+               $httpRequest = $this->getHttpRequest(
+                       StatusValue::newFatal( "http-bad-status", 404, 'Not Found' ), 404 );
+               $this->setService( 'HttpRequestFactory', $this->mockHttpRequestFactory( $httpRequest ) );
+
+               $reqs = [
+                       [
+                               'method' => 'GET',
+                               'url' => 'http://example.test/12345',
+                       ],
+                       [
+                               'method' => 'GET',
+                               'url' => 'http://example.test/67890' ,
+                       ]
+               ];
+               $responses = $this->client->runMulti( $reqs );
+               foreach ( $responses as $response ) {
+                       list( $rcode, $rdesc, /* $rhdrs */, $rbody, $rerr ) = $response['response'];
+                       $failure = $rcode < 200 || $rcode >= 400;
+                       $this->assertTrue( $failure );
+               }
+       }
+
+       /**
+        * Test of response header handling
+        */
+       public function testMultiHttpClientHeaders() {
+               // Represenative headers for typical requests, per MWHttpRequest::getResponseHeaders()
+               $headers = [
+                       'content-type' => [
+                               'text/html; charset=utf-8',
+                       ],
+                       'date' => [
+                               'Wed, 18 Jul 2018 14:52:41 GMT',
+                       ],
+                       'set-cookie' => [
+                               'COUNTRY=NAe6; expires=Wed, 25-Jul-2018 14:52:41 GMT; path=/; domain=.example.test',
+                               'LAST_NEWS=1531925562; expires=Thu, 18-Jul-2019 14:52:41 GMT; path=/; domain=.example.test',
+                       ]
+               ];
+
+               // Mock success with specific headers
+               $httpRequest = $this->getHttpRequest( StatusValue::newGood( 200 ), 200, $headers );
+               $this->setService( 'HttpRequestFactory', $this->mockHttpRequestFactory( $httpRequest ) );
+
+               list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $this->client->run( [
+                       'method' => 'GET',
+                       'url' => 'http://example.test',
+               ] );
+
+               $this->assertEquals( 200, $rcode );
+               $this->assertEquals( count( $headers ), count( $rhdrs ) );
+               foreach ( $headers as $name => $values ) {
+                       $value = implode( ', ', $values );
+                       $this->assertArrayHasKey( $name, $rhdrs );
+                       $this->assertEquals( $value, $rhdrs[$name] );
+               }
+       }
+}
index 15c4791..d891675 100644 (file)
@@ -145,6 +145,58 @@ class PathRouterTest extends MediaWikiTestCase {
                                [ 'title' => "Title_With Space" ]
                        ],
 
+                       // Double slash and dot expansion
+                       'Double slash in prefix' => [
+                               '/wiki/$1',
+                               '//wiki/Foo',
+                               [ 'title' => 'Foo' ]
+                       ],
+                       'Double slash at start of $1' => [
+                               '/wiki/$1',
+                               '/wiki//Foo',
+                               [ 'title' => '/Foo' ]
+                       ],
+                       'Double slash in middle of $1' => [
+                               '/wiki/$1',
+                               '/wiki/.hack//SIGN',
+                               [ 'title' => '.hack//SIGN' ]
+                       ],
+                       'Dots removed 1' => [
+                               '/wiki/$1',
+                               '/x/../wiki/Foo',
+                               [ 'title' => 'Foo' ]
+                       ],
+                       'Dots removed 2' => [
+                               '/wiki/$1',
+                               '/./wiki/Foo',
+                               [ 'title' => 'Foo' ]
+                       ],
+                       'Dots retained 1' => [
+                               '/wiki/$1',
+                               '/wiki/../wiki/Foo',
+                               [ 'title' => '../wiki/Foo' ]
+                       ],
+                       'Dots retained 2' => [
+                               '/wiki/$1',
+                               '/wiki/./Foo',
+                               [ 'title' => './Foo' ]
+                       ],
+                       'Triple slash' => [
+                               '/wiki/$1',
+                               '///wiki/Foo',
+                               [ 'title' => 'Foo' ]
+                       ],
+                       // '..' only traverses one slash, see e.g. RFC 3986
+                       'Dots traversing double slash 1' => [
+                               '/wiki/$1',
+                               '/a//b/../../wiki/Foo',
+                               []
+                       ],
+                       'Dots traversing double slash 2' => [
+                               '/wiki/$1',
+                               '/a//b/../../../wiki/Foo',
+                               [ 'title' => 'Foo' ]
+                       ],
                ];
 
                // Make sure the router doesn't break on special characters like $ used in regexp replacements
index 4747466..22495d0 100644 (file)
@@ -16,9 +16,8 @@ class SampleTest extends MediaWikiLangTestCase {
 
                // This sets the globals and will restore them automatically
                // after each test.
+               $this->setContentLang( 'en' );
                $this->setMwGlobals( [
-                       'wgContLang' => Language::factory( 'en' ),
-                       'wgLanguageCode' => 'en',
                        'wgCapitalLinks' => true,
                ] );
        }
index 4f65ae9..866848b 100644 (file)
@@ -256,7 +256,6 @@ class ApiBaseTest extends ApiTestCase {
        }
 
        /**
-        * @dataProvider provideGetParameterFromSettings
         * @param string|null $input
         * @param array $paramSettings
         * @param mixed $expected
@@ -264,13 +263,20 @@ class ApiBaseTest extends ApiTestCase {
         *   'parseLimits': true|false
         *   'apihighlimits': true|false
         *   'internalmode': true|false
+        *   'prefix': true|false
         * @param string[] $warnings
         */
-       public function testGetParameterFromSettings(
+       private function doGetParameterFromSettings(
                $input, $paramSettings, $expected, $warnings, $options = []
        ) {
                $mock = new MockApi();
                $wrapper = TestingAccessWrapper::newFromObject( $mock );
+               if ( $options['prefix'] ) {
+                       $wrapper->mModulePrefix = 'my';
+                       $paramName = 'Param';
+               } else {
+                       $paramName = 'myParam';
+               }
 
                $context = new DerivativeContext( $mock );
                $context->setRequest( new FauxRequest(
@@ -298,14 +304,14 @@ class ApiBaseTest extends ApiTestCase {
 
                if ( $expected instanceof Exception ) {
                        try {
-                               $wrapper->getParameterFromSettings( 'myParam', $paramSettings,
+                               $wrapper->getParameterFromSettings( $paramName, $paramSettings,
                                        $parseLimits );
                                $this->fail( 'No exception thrown' );
                        } catch ( Exception $ex ) {
                                $this->assertEquals( $expected, $ex );
                        }
                } else {
-                       $result = $wrapper->getParameterFromSettings( 'myParam',
+                       $result = $wrapper->getParameterFromSettings( $paramName,
                                $paramSettings, $parseLimits );
                        if ( isset( $paramSettings[ApiBase::PARAM_TYPE] ) &&
                                $paramSettings[ApiBase::PARAM_TYPE] === 'timestamp' &&
@@ -339,6 +345,28 @@ class ApiBaseTest extends ApiTestCase {
                }
        }
 
+       /**
+        * @dataProvider provideGetParameterFromSettings
+        * @see self::doGetParameterFromSettings()
+        */
+       public function testGetParameterFromSettings_noprefix(
+               $input, $paramSettings, $expected, $warnings, $options = []
+       ) {
+               $options['prefix'] = false;
+               $this->doGetParameterFromSettings( $input, $paramSettings, $expected, $warnings, $options );
+       }
+
+       /**
+        * @dataProvider provideGetParameterFromSettings
+        * @see self::doGetParameterFromSettings()
+        */
+       public function testGetParameterFromSettings_prefix(
+               $input, $paramSettings, $expected, $warnings, $options = []
+       ) {
+               $options['prefix'] = true;
+               $this->doGetParameterFromSettings( $input, $paramSettings, $expected, $warnings, $options );
+       }
+
        public static function provideGetParameterFromSettings() {
                $warnings = [
                        [ 'apiwarn-badutf8', 'myParam' ],
index 3909b30..4febb46 100644 (file)
@@ -18,11 +18,12 @@ class ApiEditPageTest extends ApiTestCase {
 
                parent::setUp();
 
+               $this->setContentLang( $wgContLang );
+
                $this->setMwGlobals( [
                        'wgExtraNamespaces' => $wgExtraNamespaces,
                        'wgNamespaceContentModels' => $wgNamespaceContentModels,
                        'wgContentHandlers' => $wgContentHandlers,
-                       'wgContLang' => $wgContLang,
                ] );
 
                $wgExtraNamespaces[12312] = 'Dummy';
index f06d97e..20d758e 100644 (file)
@@ -967,8 +967,7 @@ class ApiMainTest extends ApiTestCase {
                $context->setLanguage( 'en' );
                $context->setConfig( new MultiConfig( [
                        new HashConfig( [
-                               'ShowHostnames' => true, 'ShowSQLErrors' => false,
-                               'ShowExceptionDetails' => true, 'ShowDBErrorBacktrace' => true,
+                               'ShowHostnames' => true, 'ShowExceptionDetails' => true,
                        ] ),
                        $context->getConfig()
                ] ) );
@@ -1052,7 +1051,8 @@ class ApiMainTest extends ApiTestCase {
                                                [ 'code' => 'existing-error', 'text' => 'existing error', 'module' => 'main' ],
                                                [
                                                        'code' => 'internal_api_error_DBQueryError',
-                                                       'text' => "[$reqId] Database query error.",
+                                                       'text' => "[$reqId] Exception caught: A database query error has occurred. " .
+                                                               "This may indicate a bug in the software.",
                                                ]
                                        ],
                                        'trace' => $dbtrace,
@@ -1098,4 +1098,21 @@ class ApiMainTest extends ApiTestCase {
                        ],
                ];
        }
+
+       public function testPrinterParameterValidationError() {
+               $api = $this->getNonInternalApiMain( [
+                       'action' => 'query', 'meta' => 'siteinfo', 'format' => 'json', 'formatversion' => 'bogus',
+               ] );
+
+               ob_start();
+               $api->execute();
+               $txt = ob_get_clean();
+
+               // Test that the actual output is valid JSON, not just the format of the ApiResult.
+               $data = FormatJson::decode( $txt, true );
+               $this->assertInternalType( 'array', $data );
+               $this->assertArrayHasKey( 'error', $data );
+               $this->assertArrayHasKey( 'code', $data['error'] );
+               $this->assertSame( 'unknown_formatversion', $data['error']['code'] );
+       }
 }
index d4aa805..03359f8 100644 (file)
@@ -10,6 +10,13 @@ class ApiFormatBaseTest extends ApiFormatTestBase {
 
        protected $printerName = 'mockbase';
 
+       protected function setUp() {
+               parent::setUp();
+               $this->setMwGlobals( [
+                       'wgServer' => 'http://example.org'
+               ] );
+       }
+
        public function getMockFormatter( ApiMain $main = null, $format, $methods = [] ) {
                if ( $main === null ) {
                        $context = new RequestContext;
@@ -352,7 +359,7 @@ class ApiFormatBaseTest extends ApiFormatTestBase {
        public function testHtmlHeader( $post, $registerNonHtml, $expect ) {
                $context = new RequestContext;
                $request = new FauxRequest( [ 'a' => 1, 'b' => 2 ], $post );
-               $request->setRequestURL( 'http://example.org/wx/api.php' );
+               $request->setRequestURL( '/wx/api.php' );
                $context->setRequest( $request );
                $context->setLanguage( 'qqx' );
                $main = new ApiMain( $context );
index d481eb4..d14ad59 100644 (file)
@@ -594,7 +594,7 @@ class AuthManagerTest extends \MediaWikiTestCase {
                $context = \RequestContext::getMain();
                $reset = new ScopedCallback( [ $context, 'setLanguage' ], [ $context->getLanguage() ] );
                $context->setLanguage( 'de' );
-               $this->setMwGlobals( 'wgContLang', \Language::factory( 'zh' ) );
+               $this->setContentLang( 'zh' );
 
                $user = \User::newFromName( self::usernameForCreation() );
                $user->addToDatabase();
@@ -614,7 +614,7 @@ class AuthManagerTest extends \MediaWikiTestCase {
                $this->assertSame( 'de', $user->getOption( 'language' ) );
                $this->assertSame( 'zh', $user->getOption( 'variant' ) );
 
-               $this->setMwGlobals( 'wgContLang', \Language::factory( 'fr' ) );
+               $this->setContentLang( 'fr' );
 
                $user = \User::newFromName( self::usernameForCreation() );
                $user->addToDatabase();
index 9a1c90f..7c63105 100644 (file)
@@ -335,7 +335,7 @@ class ContentHandlerTest extends MediaWikiTestCase {
         * page.
         */
        public function testGetAutosummary() {
-               $this->setMwGlobals( 'wgContLang', Language::factory( 'en' ) );
+               $this->setContentLang( 'en' );
 
                $content = new DummyContentHandlerForTesting( CONTENT_MODEL_WIKITEXT );
                $title = Title::newFromText( 'Help:Test' );
diff --git a/tests/phpunit/includes/debug/DeprecationHelperTest.php b/tests/phpunit/includes/debug/DeprecationHelperTest.php
new file mode 100644 (file)
index 0000000..55e5bbf
--- /dev/null
@@ -0,0 +1,163 @@
+<?php
+
+use Wikimedia\TestingAccessWrapper;
+
+/**
+ * @covers DeprecationHelper
+ */
+class DeprecationHelperTest extends MediaWikiTestCase {
+
+       /** @var TestDeprecatedClass */
+       private $testClass;
+
+       /** @var TestDeprecatedSubclass */
+       private $testSubclass;
+
+       public function setUp() {
+               parent::setUp();
+               $this->testClass = new TestDeprecatedClass();
+               $this->testSubclass = new TestDeprecatedSubclass();
+               $this->setMwGlobals( 'wgDevelopmentWarnings', false );
+       }
+
+       public function tearDown() {
+               parent::tearDown();
+               MWDebug::clearLog();
+       }
+
+       /**
+        * @dataProvider provideGet
+        */
+       public function testGet( $propName, $expectedLevel, $expectedMessage ) {
+               if ( $expectedLevel ) {
+                       $this->assertErrorTriggered( function () use ( $propName ) {
+                               $this->assertSame( null, $this->testClass->$propName );
+                       }, $expectedLevel, $expectedMessage );
+               } else {
+                       $this->assertDeprecationWarningIssued( function () use ( $propName ) {
+                               $this->assertSame( 1, $this->testClass->$propName );
+                       } );
+               }
+       }
+
+       public function provideGet() {
+               return [
+                       [ 'protectedDeprecated', null, null ],
+                       [ 'protectedNonDeprecated', E_USER_ERROR,
+                               'Cannot access non-public property TestDeprecatedClass::$protectedNonDeprecated' ],
+                       [ 'privateDeprecated', null, null ],
+                       [ 'privateNonDeprecated', E_USER_ERROR,
+                         'Cannot access non-public property TestDeprecatedClass::$privateNonDeprecated' ],
+                       [ 'nonExistent', E_USER_NOTICE, 'Undefined property: TestDeprecatedClass::$nonExistent' ],
+               ];
+       }
+
+       /**
+        * @dataProvider provideSet
+        */
+       public function testSet( $propName, $expectedLevel, $expectedMessage ) {
+               $this->assertPropertySame( 1, $this->testClass, $propName );
+               if ( $expectedLevel ) {
+                       $this->assertErrorTriggered( function () use ( $propName ) {
+                               $this->testClass->$propName = 0;
+                               $this->assertPropertySame( 1, $this->testClass, $propName );
+                       }, $expectedLevel, $expectedMessage );
+               } else {
+                       if ( $propName === 'nonExistent' ) {
+                               $this->testClass->$propName = 0;
+                       } else {
+                               $this->assertDeprecationWarningIssued( function () use ( $propName ) {
+                                       $this->testClass->$propName = 0;
+                               } );
+                       }
+                       $this->assertPropertySame( 0, $this->testClass, $propName );
+               }
+       }
+
+       public function provideSet() {
+               return [
+                       [ 'protectedDeprecated', null, null ],
+                       [ 'protectedNonDeprecated', E_USER_ERROR,
+                         'Cannot access non-public property TestDeprecatedClass::$protectedNonDeprecated' ],
+                       [ 'privateDeprecated', null, null ],
+                       [ 'privateNonDeprecated', E_USER_ERROR,
+                         'Cannot access non-public property TestDeprecatedClass::$privateNonDeprecated' ],
+                       [ 'nonExistent', null, null ],
+               ];
+       }
+
+       public function testInternalGet() {
+               $this->assertSame( [
+                       'prod' => 1,
+                       'prond' => 1,
+                       'prid' => 1,
+                       'prind' => 1,
+               ], $this->testClass->getThings() );
+       }
+
+       public function testInternalSet() {
+               $this->testClass->setThings( 2, 2, 2, 2 );
+               $wrapper = TestingAccessWrapper::newFromObject( $this->testClass );
+               $this->assertSame( 2, $wrapper->protectedDeprecated );
+               $this->assertSame( 2, $wrapper->protectedNonDeprecated );
+               $this->assertSame( 2, $wrapper->privateDeprecated );
+               $this->assertSame( 2, $wrapper->privateNonDeprecated );
+       }
+
+       public function testSubclassGetSet() {
+               $this->assertDeprecationWarningIssued( function () {
+                       $this->assertSame( 1, $this->testSubclass->getDeprecatedPrivateParentProperty() );
+               } );
+               $this->assertDeprecationWarningIssued( function () {
+                       $this->testSubclass->setDeprecatedPrivateParentProperty( 0 );
+               } );
+               $wrapper = TestingAccessWrapper::newFromObject( $this->testSubclass );
+               $this->assertSame( 0, $wrapper->privateDeprecated );
+
+               $fullName = 'TestDeprecatedClass::$privateNonDeprecated';
+               $this->assertErrorTriggered( function () {
+                       $this->assertSame( null, $this->testSubclass->getNonDeprecatedPrivateParentProperty() );
+               }, E_USER_ERROR, "Cannot access non-public property $fullName" );
+               $this->assertErrorTriggered( function () {
+                       $this->testSubclass->setNonDeprecatedPrivateParentProperty( 0 );
+                       $wrapper = TestingAccessWrapper::newFromObject( $this->testSubclass );
+                       $this->assertSame( 1, $wrapper->privateNonDeprecated );
+               }, E_USER_ERROR, "Cannot access non-public property $fullName" );
+       }
+
+       protected function assertErrorTriggered( callable $callback, $level, $message ) {
+               $actualLevel = $actualMessage = null;
+               set_error_handler( function ( $errorCode, $errorStr ) use ( &$actualLevel, &$actualMessage ) {
+                       $actualLevel = $errorCode;
+                       $actualMessage = $errorStr;
+               } );
+               $callback();
+               restore_error_handler();
+               $this->assertSame( $level, $actualLevel );
+               $this->assertSame( $message, $actualMessage );
+       }
+
+       protected function assertPropertySame( $expected, $object, $propName ) {
+               try {
+                       $this->assertSame( $expected, TestingAccessWrapper::newFromObject( $object )->$propName );
+               } catch ( ReflectionException $e ) {
+                       if ( !preg_match( "/Property (TestDeprecated(Class|Subclass)::)?$propName does not exist/",
+                               $e->getMessage() )
+                       ) {
+                               throw $e;
+                       }
+                       // property_exists accepts monkey-patching, Reflection / TestingAccessWrapper doesn't
+                       if ( property_exists( $object, $propName ) ) {
+                               $this->assertSame( $expected, $object->$propName );
+                       }
+               }
+       }
+
+       protected function assertDeprecationWarningIssued( callable $callback ) {
+               MWDebug::clearLog();
+               $callback();
+               $wrapper = TestingAccessWrapper::newFromClass( MWDebug::class );
+               $this->assertNotEmpty( $wrapper->deprecationWarnings );
+       }
+
+}
diff --git a/tests/phpunit/includes/debug/TestDeprecatedClass.php b/tests/phpunit/includes/debug/TestDeprecatedClass.php
new file mode 100644 (file)
index 0000000..5a9e645
--- /dev/null
@@ -0,0 +1,35 @@
+<?php
+
+class TestDeprecatedClass {
+
+       use DeprecationHelper;
+
+       protected $protectedDeprecated = 1;
+       protected $protectedNonDeprecated = 1;
+       private $privateDeprecated = 1;
+       private $privateNonDeprecated = 1;
+
+       public function __construct() {
+               $this->deprecatedPublicProperties = [
+                       'protectedDeprecated' => '1.23',
+                       'privateDeprecated' => '1.24',
+               ];
+       }
+
+       public function setThings( $prod, $prond, $prid, $prind ) {
+               $this->protectedDeprecated = $prod;
+               $this->protectedNonDeprecated = $prond;
+               $this->privateDeprecated = $prid;
+               $this->privateNonDeprecated = $prind;
+       }
+
+       public function getThings() {
+               return [
+                       'prod' => $this->protectedDeprecated,
+                       'prond' => $this->protectedNonDeprecated,
+                       'prid' => $this->privateDeprecated,
+                       'prind' => $this->privateNonDeprecated,
+               ];
+       }
+
+}
diff --git a/tests/phpunit/includes/debug/TestDeprecatedSubclass.php b/tests/phpunit/includes/debug/TestDeprecatedSubclass.php
new file mode 100644 (file)
index 0000000..0b6c8cf
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+
+class TestDeprecatedSubclass extends TestDeprecatedClass {
+
+       public function getDeprecatedPrivateParentProperty() {
+               return $this->privateDeprecated;
+       }
+
+       public function setDeprecatedPrivateParentProperty( $value ) {
+               $this->privateDeprecated = $value;
+       }
+
+       public function getNondeprecatedPrivateParentProperty() {
+               return $this->privateNonDeprecated;
+       }
+
+       public function setNondeprecatedPrivateParentProperty( $value ) {
+               $this->privateNonDeprecated = $value;
+       }
+
+}
index 06772e5..d7dc411 100644 (file)
@@ -5,7 +5,6 @@
  *
  * @license GPL-2.0-or-later
  * @author Gergő Tisza
- * @author Thiemo Mättig
  */
 class HTMLFormTest extends MediaWikiTestCase {
 
index c711291..04aecc9 100644 (file)
@@ -19,6 +19,30 @@ class CSSMinTest extends MediaWikiTestCase {
                ] );
        }
 
+       /**
+        * @dataProvider providesReferencedFiles
+        * @covers CSSMin::getLocalFileReferences
+        */
+       public function testGetLocalFileReferences( $input, $expected ) {
+               $output = CSSMin::getLocalFileReferences( $input, '/' );
+               $this->assertEquals(
+                       $expected,
+                       $output,
+                       'getLocalFileReferences() must find the local file properly'
+               );
+       }
+
+       public static function providesReferencedFiles() {
+               // input, array of expected local file names
+               return [
+                       [ 'url("//example.org")', [] ],
+                       [ 'url("https://example.org")', [] ],
+                       [ 'url("#default#")', [] ],
+                       [ 'url("WikiFont-Glyphs.svg#wikiglyph")', [ '/WikiFont-Glyphs.svg' ] ],
+                       [ 'url("#some-anchor")', [] ],
+               ];
+       }
+
        /**
         * @dataProvider provideSerializeStringValue
         * @covers CSSMin::serializeStringValue
@@ -292,6 +316,16 @@ class CSSMinTest extends MediaWikiTestCase {
                                [ 'foo { behavior: url(#default#bar); }', false, '/w/', false ],
                                'foo { behavior: url("#default#bar"); }',
                        ],
+                       [
+                               'Keeps anchors',
+                               [ 'url(#other)', false, '/', false ],
+                               'url("#other")'
+                       ],
+                       [
+                               'Keeps anchors after a path',
+                               [ 'url(images/file.svg#id)', false, '/', false ],
+                               'url("/images/file.svg#id")'
+                       ],
                ];
        }
 
diff --git a/tests/phpunit/includes/libs/EasyDeflateTest.php b/tests/phpunit/includes/libs/EasyDeflateTest.php
new file mode 100644 (file)
index 0000000..da39d48
--- /dev/null
@@ -0,0 +1,64 @@
+<?php
+/**
+ * Copyright (C) 2018 Kunal Mehta <legoktm@member.fsf.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+/**
+ * @covers EasyDeflate
+ */
+class EasyDeflateTest extends PHPUnit\Framework\TestCase {
+
+       public function provideIsDeflated() {
+               return [
+                       [ 'rawdeflate,S8vPT0osAgA=', true ],
+                       [ 'abcdefghijklmnopqrstuvwxyz', false ],
+               ];
+       }
+
+       /**
+        * @dataProvider provideIsDeflated
+        */
+       public function testIsDeflated( $data, $expected ) {
+               $actual = EasyDeflate::isDeflated( $data );
+               $this->assertSame( $expected, $actual );
+       }
+
+       public function provideInflate() {
+               return [
+                       [ 'rawdeflate,S8vPT0osAgA=', true, 'foobar' ],
+                       // Fails base64_decode
+                       [ 'rawdeflate,🌻', false, 'easydeflate-invaliddeflate' ],
+                       // Fails gzinflate
+                       [ 'rawdeflate,S8vPT0dfdAgB=', false, 'easydeflate-invaliddeflate' ],
+               ];
+       }
+
+       /**
+        * @dataProvider provideInflate
+        */
+       public function testInflate( $data, $ok, $value ) {
+               $actual = EasyDeflate::inflate( $data );
+               if ( $ok ) {
+                       $this->assertTrue( $actual->isOK() );
+                       $this->assertSame( $value, $actual->getValue() );
+               } else {
+                       $this->assertFalse( $actual->isOK() );
+                       $this->assertTrue( $actual->hasMessage( $value ) );
+               }
+       }
+}
index 86b496e..02a4a25 100644 (file)
@@ -38,10 +38,7 @@ class MagicVariableTest extends MediaWikiTestCase {
                parent::setUp();
 
                $contLang = Language::factory( 'en' );
-               $this->setMwGlobals( [
-                       'wgLanguageCode' => 'en',
-                       'wgContLang' => $contLang,
-               ] );
+               $this->setContentLang( $contLang );
 
                $this->testParser = new Parser();
                $this->testParser->Options( ParserOptions::newFromUserAndLang( new User, $contLang ) );
index 48205f4..29f1c8c 100644 (file)
@@ -47,10 +47,10 @@ class ParserOptionsTest extends MediaWikiTestCase {
                $wgLang = Language::factory( 'fr' );
                $wgContLang = Language::factory( 'qqx' );
 
+               $this->setContentLang( $wgContLang );
                $this->setMwGlobals( [
                        'wgUser' => $wgUser,
                        'wgLang' => $wgLang,
-                       'wgContLang' => $wgContLang,
                ] );
 
                $user = $this->getMutableTestUser()->getUser();
index 42cbc2c..60b01b8 100644 (file)
@@ -102,7 +102,7 @@ class FiltersTest extends MediaWikiTestCase {
                        [ '', '' ],
                        [ "\n", '' ],
                        [ '1', 'Foo' ],
-                       [ "\n1\n\n2\666\n", "Foo\nBar" ],
+                       [ "\n1\n\n2\377\n", "Foo\nBar" ],
                        [ "666\n667", '' ],
                ];
        }
index 9ac546d..784fed0 100644 (file)
@@ -105,7 +105,7 @@ class SpecialPageFactoryTest extends MediaWikiTestCase {
         * @covers SpecialPageFactory::resolveAlias
         */
        public function testResolveAlias() {
-               $this->setMwGlobals( 'wgContLang', Language::factory( 'de' ) );
+               $this->setContentLang( 'de' );
                SpecialPageFactory::resetList();
 
                list( $name, $param ) = SpecialPageFactory::resolveAlias( 'Spezialseiten/Foo' );
@@ -117,7 +117,7 @@ class SpecialPageFactoryTest extends MediaWikiTestCase {
         * @covers SpecialPageFactory::getLocalNameFor
         */
        public function testGetLocalNameFor() {
-               $this->setMwGlobals( 'wgContLang', Language::factory( 'de' ) );
+               $this->setContentLang( 'de' );
                SpecialPageFactory::resetList();
 
                $name = SpecialPageFactory::getLocalNameFor( 'Specialpages', 'Foo' );
@@ -128,7 +128,7 @@ class SpecialPageFactoryTest extends MediaWikiTestCase {
         * @covers SpecialPageFactory::getTitleForAlias
         */
        public function testGetTitleForAlias() {
-               $this->setMwGlobals( 'wgContLang', Language::factory( 'de' ) );
+               $this->setContentLang( 'de' );
                SpecialPageFactory::resetList();
 
                $title = SpecialPageFactory::getTitleForAlias( 'Specialpages/Foo' );
@@ -145,7 +145,7 @@ class SpecialPageFactoryTest extends MediaWikiTestCase {
                global $wgContLang;
                $lang = clone $wgContLang;
                $lang->mExtendedSpecialPageAliases = $aliasesList;
-               $this->setMwGlobals( 'wgContLang', $lang );
+               $this->setContentLang( $lang );
                $this->setMwGlobals( 'wgSpecialPages',
                        array_combine( array_keys( $aliasesList ), array_keys( $aliasesList ) )
                );
index 2ad3972..2eddb01 100644 (file)
@@ -12,9 +12,9 @@ class SpecialPageTest extends MediaWikiTestCase {
        protected function setUp() {
                parent::setUp();
 
+               $this->setContentLang( 'en' );
                $this->setMwGlobals( [
                        'wgScript' => '/index.php',
-                       'wgContLang' => Language::factory( 'en' )
                ] );
        }
 
index d711bac..71e52e8 100644 (file)
@@ -29,9 +29,8 @@ class NaiveImportTitleFactoryTest extends MediaWikiTestCase {
        protected function setUp() {
                parent::setUp();
 
+               $this->setContentLang( 'en' );
                $this->setMwGlobals( [
-                       'wgLanguageCode' => 'en',
-                       'wgContLang' => Language::factory( 'en' ),
                        'wgExtraNamespaces' => [ 100 => 'Portal' ],
                ] );
        }
index 9b6ac93..ae87276 100644 (file)
@@ -29,10 +29,7 @@ class NamespaceImportTitleFactoryTest extends MediaWikiTestCase {
        protected function setUp() {
                parent::setUp();
 
-               $this->setMwGlobals( [
-                       'wgLanguageCode' => 'en',
-                       'wgContLang' => Language::factory( 'en' ),
-               ] );
+               $this->setContentLang( 'en' );
        }
 
        public function basicProvider() {
index 008cf5d..7422bdb 100644 (file)
@@ -29,9 +29,8 @@ class SubpageImportTitleFactoryTest extends MediaWikiTestCase {
        protected function setUp() {
                parent::setUp();
 
+               $this->setContentLang( 'en' );
                $this->setMwGlobals( [
-                       'wgLanguageCode' => 'en',
-                       'wgContLang' => Language::factory( 'en' ),
                        'wgNamespacesWithSubpages' => [ 0 => false, 2 => true ],
                ] );
        }
index 294bd80..169a7c5 100644 (file)
@@ -235,36 +235,6 @@ class UserTest extends MediaWikiTestCase {
                ];
        }
 
-       /**
-        * Test, if for all rights a right- message exist,
-        * which is used on Special:ListGroupRights as help text
-        * Extensions and core
-        *
-        * @coversNothing
-        */
-       public function testAllRightsWithMessage() {
-               // Getting all user rights, for core: User::$mCoreRights, for extensions: $wgAvailableRights
-               $allRights = User::getAllRights();
-               $allMessageKeys = Language::getMessageKeysFor( 'en' );
-
-               $rightsWithMessage = [];
-               foreach ( $allMessageKeys as $message ) {
-                       // === 0: must be at beginning of string (position 0)
-                       if ( strpos( $message, 'right-' ) === 0 ) {
-                               $rightsWithMessage[] = substr( $message, strlen( 'right-' ) );
-                       }
-               }
-
-               sort( $allRights );
-               sort( $rightsWithMessage );
-
-               $this->assertEquals(
-                       $allRights,
-                       $rightsWithMessage,
-                       'Each user rights (core/extensions) has a corresponding right- message.'
-               );
-       }
-
        /**
         * Test User::editCount
         * @group medium
index f14d2ce..544a063 100644 (file)
@@ -54,18 +54,14 @@ class LanguageCodeTest extends PHPUnit\Framework\TestCase {
         * @dataProvider provideLanguageCodes()
         */
        public function testBcp47( $code, $expected ) {
-               $this->assertEquals( $expected, LanguageCode::bcp47( $code ),
-                       "Applying BCP 47 standard to '$code'"
-               );
-
                $code = strtolower( $code );
                $this->assertEquals( $expected, LanguageCode::bcp47( $code ),
-                       "Applying BCP 47 standard to lower case '$code'"
+                       "Applying BCP47 standard to lower case '$code'"
                );
 
                $code = strtoupper( $code );
                $this->assertEquals( $expected, LanguageCode::bcp47( $code ),
-                       "Applying BCP 47 standard to upper case '$code'"
+                       "Applying BCP47 standard to upper case '$code'"
                );
        }
 
@@ -159,41 +155,6 @@ class LanguageCodeTest extends PHPUnit\Framework\TestCase {
                        // de-419-DE
                        // a-DE
                        // ar-a-aaa-b-bbb-a-ccc
-
-                       # Non-standard and deprecated language codes used by MediaWiki
-                       [ 'als', 'gsw' ],
-                       [ 'bat-smg', 'sgs' ],
-                       [ 'be-x-old', 'be-tarask' ],
-                       [ 'fiu-vro', 'vro' ],
-                       [ 'roa-rup', 'rup' ],
-                       [ 'zh-classical', 'lzh' ],
-                       [ 'zh-min-nan', 'nan' ],
-                       [ 'zh-yue', 'yue' ],
-                       [ 'cbk-zam', 'cbk' ],
-                       [ 'de-formal', 'de-x-formal' ],
-                       [ 'eml', 'egl' ],
-                       [ 'en-rtl', 'en-x-rtl' ],
-                       [ 'es-formal', 'es-x-formal' ],
-                       [ 'hu-formal', 'hu-x-formal' ],
-                       [ 'kk-Arab', 'kk-Arab' ],
-                       [ 'kk-Cyrl', 'kk-Cyrl' ],
-                       [ 'kk-Latn', 'kk-Latn' ],
-                       [ 'map-bms', 'jv-x-bms' ],
-                       [ 'mo', 'ro-MD' ],
-                       [ 'nrm', 'nrf' ],
-                       [ 'nl-informal', 'nl-x-informal' ],
-                       [ 'roa-tara', 'nap-x-tara' ],
-                       [ 'simple', 'en-simple' ],
-                       [ 'sr-ec', 'sr-Cyrl' ],
-                       [ 'sr-el', 'sr-Latn' ],
-                       [ 'zh-cn', 'zh-Hans-CN' ],
-                       [ 'zh-sg', 'zh-Hans-SG' ],
-                       [ 'zh-my', 'zh-Hans-MY' ],
-                       [ 'zh-tw', 'zh-Hant-TW' ],
-                       [ 'zh-hk', 'zh-Hant-HK' ],
-                       [ 'zh-mo', 'zh-Hant-MO' ],
-                       [ 'zh-hans', 'zh-Hans' ],
-                       [ 'zh-hant', 'zh-Hant' ],
                ];
        }
 
index b5db2ec..c97bdaf 100644 (file)
@@ -9,9 +9,9 @@ class LanguageConverterTest extends MediaWikiLangTestCase {
        protected function setUp() {
                parent::setUp();
 
+               $this->setContentLang( 'tg' );
+
                $this->setMwGlobals( [
-                       'wgContLang' => Language::factory( 'tg' ),
-                       'wgLanguageCode' => 'tg',
                        'wgDefaultLanguageVariant' => false,
                        'wgRequest' => new FauxRequest( [] ),
                        'wgUser' => new User,
@@ -20,9 +20,7 @@ class LanguageConverterTest extends MediaWikiLangTestCase {
                $this->lang = new LanguageToTest();
                $this->lc = new TestConverter(
                        $this->lang, 'tg',
-                       # Adding 'sgs' as a variant to ensure we handle deprecated codes
-                       # adding 'simple' as a variant to ensure we handle non BCP 47 codes
-                       [ 'tg', 'tg-latn', 'sgs', 'simple' ]
+                       [ 'tg', 'tg-latn' ]
                );
        }
 
@@ -40,39 +38,6 @@ class LanguageConverterTest extends MediaWikiLangTestCase {
                $this->assertEquals( 'tg', $this->lc->getPreferredVariant() );
        }
 
-       /**
-        * @covers LanguageConverter::getPreferredVariant
-        * @covers LanguageConverter::getURLVariant
-        */
-       public function testGetPreferredVariantUrl() {
-               global $wgRequest;
-               $wgRequest->setVal( 'variant', 'tg-latn' );
-
-               $this->assertEquals( 'tg-latn', $this->lc->getPreferredVariant() );
-       }
-
-       /**
-        * @covers LanguageConverter::getPreferredVariant
-        * @covers LanguageConverter::getURLVariant
-        */
-       public function testGetPreferredVariantUrlDeprecated() {
-               global $wgRequest;
-               $wgRequest->setVal( 'variant', 'bat-smg' );
-
-               $this->assertEquals( 'sgs', $this->lc->getPreferredVariant() );
-       }
-
-       /**
-        * @covers LanguageConverter::getPreferredVariant
-        * @covers LanguageConverter::getURLVariant
-        */
-       public function testGetPreferredVariantUrlBCP47() {
-               global $wgRequest;
-               $wgRequest->setVal( 'variant', 'en-simple' );
-
-               $this->assertEquals( 'simple', $this->lc->getPreferredVariant() );
-       }
-
        /**
         * @covers LanguageConverter::getPreferredVariant
         * @covers LanguageConverter::getHeaderVariant
@@ -84,17 +49,6 @@ class LanguageConverterTest extends MediaWikiLangTestCase {
                $this->assertEquals( 'tg-latn', $this->lc->getPreferredVariant() );
        }
 
-       /**
-        * @covers LanguageConverter::getPreferredVariant
-        * @covers LanguageConverter::getHeaderVariant
-        */
-       public function testGetPreferredVariantHeadersBCP47() {
-               global $wgRequest;
-               $wgRequest->setHeader( 'Accept-Language', 'en-simple' );
-
-               $this->assertEquals( 'simple', $this->lc->getPreferredVariant() );
-       }
-
        /**
         * @covers LanguageConverter::getPreferredVariant
         * @covers LanguageConverter::getHeaderVariant
@@ -144,38 +98,6 @@ class LanguageConverterTest extends MediaWikiLangTestCase {
                $this->assertEquals( 'tg-latn', $this->lc->getPreferredVariant() );
        }
 
-       /**
-        * @covers LanguageConverter::getPreferredVariant
-        */
-       public function testGetPreferredVariantUserOptionDeprecated() {
-               global $wgUser;
-
-               $wgUser = new User;
-               $wgUser->load(); // from 'defaults'
-               $wgUser->mId = 1;
-               $wgUser->mDataLoaded = true;
-               $wgUser->mOptionsLoaded = true;
-               $wgUser->setOption( 'variant', 'bat-smg' );
-
-               $this->assertEquals( 'sgs', $this->lc->getPreferredVariant() );
-       }
-
-       /**
-        * @covers LanguageConverter::getPreferredVariant
-        */
-       public function testGetPreferredVariantUserOptionBCP47() {
-               global $wgUser;
-
-               $wgUser = new User;
-               $wgUser->load(); // from 'defaults'
-               $wgUser->mId = 1;
-               $wgUser->mDataLoaded = true;
-               $wgUser->mOptionsLoaded = true;
-               $wgUser->setOption( 'variant', 'en-simple' );
-
-               $this->assertEquals( 'simple', $this->lc->getPreferredVariant() );
-       }
-
        /**
         * @covers LanguageConverter::getPreferredVariant
         * @covers LanguageConverter::getUserVariant
@@ -194,42 +116,6 @@ class LanguageConverterTest extends MediaWikiLangTestCase {
                $this->assertEquals( 'tg-latn', $this->lc->getPreferredVariant() );
        }
 
-       /**
-        * @covers LanguageConverter::getPreferredVariant
-        * @covers LanguageConverter::getUserVariant
-        */
-       public function testGetPreferredVariantUserOptionForForeignLanguageDeprecated() {
-               global $wgContLang, $wgUser;
-
-               $wgContLang = Language::factory( 'en' );
-               $wgUser = new User;
-               $wgUser->load(); // from 'defaults'
-               $wgUser->mId = 1;
-               $wgUser->mDataLoaded = true;
-               $wgUser->mOptionsLoaded = true;
-               $wgUser->setOption( 'variant-tg', 'bat-smg' );
-
-               $this->assertEquals( 'sgs', $this->lc->getPreferredVariant() );
-       }
-
-       /**
-        * @covers LanguageConverter::getPreferredVariant
-        * @covers LanguageConverter::getUserVariant
-        */
-       public function testGetPreferredVariantUserOptionForForeignLanguageBCP47() {
-               global $wgContLang, $wgUser;
-
-               $wgContLang = Language::factory( 'en' );
-               $wgUser = new User;
-               $wgUser->load(); // from 'defaults'
-               $wgUser->mId = 1;
-               $wgUser->mDataLoaded = true;
-               $wgUser->mOptionsLoaded = true;
-               $wgUser->setOption( 'variant-tg', 'en-simple' );
-
-               $this->assertEquals( 'simple', $this->lc->getPreferredVariant() );
-       }
-
        /**
         * @covers LanguageConverter::getPreferredVariant
         * @covers LanguageConverter::getUserVariant
@@ -259,26 +145,6 @@ class LanguageConverterTest extends MediaWikiLangTestCase {
                $this->assertEquals( 'tg-latn', $this->lc->getPreferredVariant() );
        }
 
-       /**
-        * @covers LanguageConverter::getPreferredVariant
-        */
-       public function testGetPreferredVariantDefaultLanguageVariantDeprecated() {
-               global $wgDefaultLanguageVariant;
-
-               $wgDefaultLanguageVariant = 'bat-smg';
-               $this->assertEquals( 'sgs', $this->lc->getPreferredVariant() );
-       }
-
-       /**
-        * @covers LanguageConverter::getPreferredVariant
-        */
-       public function testGetPreferredVariantDefaultLanguageVariantBCP47() {
-               global $wgDefaultLanguageVariant;
-
-               $wgDefaultLanguageVariant = 'en-simple';
-               $this->assertEquals( 'simple', $this->lc->getPreferredVariant() );
-       }
-
        /**
         * @covers LanguageConverter::getPreferredVariant
         * @covers LanguageConverter::getURLVariant
@@ -326,8 +192,6 @@ class TestConverter extends LanguageConverter {
 
        function loadDefaultTables() {
                $this->mTables = [
-                       'sgs' => new ReplacementArray(),
-                       'simple' => new ReplacementArray(),
                        'tg-latn' => new ReplacementArray( $this->table ),
                        'tg' => new ReplacementArray()
                ];
index 51a1ed6..000b50f 100644 (file)
@@ -3,7 +3,6 @@
 namespace MediaWiki\Tests\Maintenance;
 
 use DumpBackup;
-use Language;
 use Title;
 use WikiExporter;
 use WikiPage;
@@ -30,10 +29,7 @@ class BackupDumperPageTest extends DumpTestCase {
 
        function addDBData() {
                // be sure, titles created here using english namespace names
-               $this->setMwGlobals( [
-                       'wgLanguageCode' => 'en',
-                       'wgContLang' => Language::factory( 'en' ),
-               ] );
+               $this->setContentLang( 'en' );
 
                $this->tablesUsed[] = 'page';
                $this->tablesUsed[] = 'revision';
index ea132e9..a7d947e 100644 (file)
@@ -53,4 +53,36 @@ class AvailableRightsTest extends PHPUnit\Framework\TestCase {
                        'https://www.mediawiki.org/wiki/Manual:User_rights#Adding_new_rights'
                );
        }
+
+       /**
+        * Test, if for all rights a right- message exist,
+        * which is used on Special:ListGroupRights as help text
+        * Extensions and core
+        *
+        * @coversNothing
+        */
+       public function testAllRightsWithMessage() {
+               // Getting all user rights, for core: User::$mCoreRights, for extensions: $wgAvailableRights
+               $allRights = User::getAllRights();
+               $allMessageKeys = Language::getMessageKeysFor( 'en' );
+
+               $rightsWithMessage = [];
+               foreach ( $allMessageKeys as $message ) {
+                       // === 0: must be at beginning of string (position 0)
+                       if ( strpos( $message, 'right-' ) === 0 ) {
+                               $rightsWithMessage[] = substr( $message, strlen( 'right-' ) );
+                       }
+               }
+
+               $missing = array_diff(
+                       $allRights,
+                       $rightsWithMessage
+               );
+
+               $this->assertEquals(
+                       [],
+                       $missing,
+                       'Each user rights (core/extensions) has a corresponding right- message.'
+               );
+       }
 }
index af5433a..9ea3c11 100644 (file)
                // # Tags that use extensions
                [ 'en-us-u-islamcal', 'en-US-u-islamcal' ],
                [ 'zh-cn-a-myext-x-private', 'zh-CN-a-myext-x-private' ],
-               [ 'en-a-myext-b-another', 'en-a-myext-b-another' ],
+               [ 'en-a-myext-b-another', 'en-a-myext-b-another' ]
 
                // # Invalid:
                // de-419-DE
                // a-DE
                // ar-a-aaa-b-bbb-a-ccc
-
-               // Non-standard and deprecated language codes used by MediaWiki
-               [ 'als', 'gsw' ],
-               [ 'bat-smg', 'sgs' ],
-               [ 'be-x-old', 'be-tarask' ],
-               [ 'fiu-vro', 'vro' ],
-               [ 'roa-rup', 'rup' ],
-               [ 'zh-classical', 'lzh' ],
-               [ 'zh-min-nan', 'nan' ],
-               [ 'zh-yue', 'yue' ],
-               [ 'cbk-zam', 'cbk' ],
-               [ 'de-formal', 'de-x-formal' ],
-               [ 'eml', 'egl' ],
-               [ 'en-rtl', 'en-x-rtl' ],
-               [ 'es-formal', 'es-x-formal' ],
-               [ 'hu-formal', 'hu-x-formal' ],
-               [ 'kk-Arab', 'kk-Arab' ],
-               [ 'kk-Cyrl', 'kk-Cyrl' ],
-               [ 'kk-Latn', 'kk-Latn' ],
-               [ 'map-bms', 'jv-x-bms' ],
-               [ 'mo', 'ro-MD' ],
-               [ 'nrm', 'nrf' ],
-               [ 'nl-informal', 'nl-x-informal' ],
-               [ 'roa-tara', 'nap-x-tara' ],
-               [ 'simple', 'en-simple' ],
-               [ 'sr-ec', 'sr-Cyrl' ],
-               [ 'sr-el', 'sr-Latn' ],
-               [ 'zh-cn', 'zh-Hans-CN' ],
-               [ 'zh-sg', 'zh-Hans-SG' ],
-               [ 'zh-my', 'zh-Hans-MY' ],
-               [ 'zh-tw', 'zh-Hant-TW' ],
-               [ 'zh-hk', 'zh-Hant-HK' ],
-               [ 'zh-mo', 'zh-Hant-MO' ],
-               [ 'zh-hans', 'zh-Hans' ],
-               [ 'zh-hant', 'zh-Hant' ]
        ];
 
        QUnit.test( 'mw.language.bcp47', function ( assert ) {
-               mw.language.data = this.liveLangData;
                bcp47Tests.forEach( function ( data ) {
                        var input = data[ 0 ],
                                expected = data[ 1 ];
                        assert.strictEqual( mw.language.bcp47( input ), expected );
-                       assert.strictEqual( mw.language.bcp47( input.toLowerCase() ), expected );
-                       assert.strictEqual( mw.language.bcp47( input.toUpperCase() ), expected );
                } );
        } );
 }( mediaWiki, jQuery ) );