Merge "Add wfWaitForSlaves() call in DatabaseUpdater::runUpdates()"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Sat, 26 Sep 2015 17:35:07 +0000 (17:35 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Sat, 26 Sep 2015 17:35:07 +0000 (17:35 +0000)
302 files changed:
RELEASE-NOTES-1.26
UPGRADE
autoload.php
composer.json
docs/hooks.txt
includes/DefaultSettings.php
includes/Feed.php
includes/GlobalFunctions.php
includes/HtmlFormatter.php
includes/HttpFunctions.php
includes/Import.php
includes/Linker.php
includes/MWTimestamp.php
includes/User.php
includes/WebRequest.php
includes/WebStart.php
includes/actions/RevertAction.php
includes/actions/RollbackAction.php
includes/api/ApiBase.php
includes/api/ApiFormatPhp.php
includes/api/ApiImport.php
includes/api/ApiQueryAllDeletedRevisions.php
includes/api/ApiQueryDeletedRevisions.php
includes/api/i18n/en.json
includes/api/i18n/es.json
includes/api/i18n/gl.json
includes/api/i18n/it.json
includes/api/i18n/ko.json
includes/api/i18n/lt.json [new file with mode: 0644]
includes/api/i18n/pl.json
includes/api/i18n/ru.json
includes/api/i18n/sq.json
includes/api/i18n/zh-hans.json
includes/cache/LinkCache.php
includes/cache/LocalisationCache.php
includes/changes/EnhancedChangesList.php
includes/clientpool/RedisConnectionPool.php
includes/context/RequestContext.php
includes/db/DatabaseMysqlBase.php
includes/db/DatabaseSqlite.php
includes/db/LBFactory.php [deleted file]
includes/db/LBFactoryMulti.php [deleted file]
includes/db/LBFactorySingle.php [deleted file]
includes/db/LoadBalancer.php [deleted file]
includes/db/LoadMonitor.php [deleted file]
includes/db/LoadMonitorMySQL.php [deleted file]
includes/db/loadbalancer/LBFactory.php [new file with mode: 0644]
includes/db/loadbalancer/LBFactoryFake.php [new file with mode: 0644]
includes/db/loadbalancer/LBFactoryMulti.php [new file with mode: 0644]
includes/db/loadbalancer/LBFactorySimple.php [new file with mode: 0644]
includes/db/loadbalancer/LBFactorySingle.php [new file with mode: 0644]
includes/db/loadbalancer/LoadBalancer.php [new file with mode: 0644]
includes/db/loadbalancer/LoadMonitor.php [new file with mode: 0644]
includes/db/loadbalancer/LoadMonitorMySQL.php [new file with mode: 0644]
includes/debug/logger/monolog/BufferHandler.php
includes/debug/logger/monolog/KafkaHandler.php
includes/deferred/DataUpdate.php
includes/deferred/LinksDeletionUpdate.php
includes/diff/TableDiffFormatter.php
includes/exception/MWExceptionHandler.php
includes/filerepo/file/LocalFile.php
includes/gallery/TraditionalImageGallery.php
includes/htmlform/HTMLForm.php
includes/htmlform/HTMLFormField.php
includes/htmlform/HTMLHiddenField.php
includes/htmlform/OOUIHTMLForm.php
includes/installer/Installer.php
includes/installer/i18n/lt.json
includes/installer/i18n/nah.json
includes/installer/i18n/nap.json
includes/installer/i18n/ru.json
includes/installer/i18n/sq.json
includes/libs/CSSMin.php
includes/libs/HttpStatus.php
includes/libs/ReplacementArray.php
includes/libs/objectcache/WANObjectCache.php
includes/libs/virtualrest/ParsoidVirtualRESTService.php
includes/logging/LogFormatter.php
includes/logging/LogPage.php
includes/logging/ProtectLogFormatter.php
includes/media/Bitmap.php
includes/media/ExifBitmap.php
includes/media/FormatMetadata.php
includes/media/GIF.php
includes/media/MediaHandler.php
includes/media/PNG.php
includes/media/XMP.php
includes/objectcache/MultiWriteBagOStuff.php
includes/page/ImagePage.php
includes/page/WikiPage.php
includes/parser/Parser.php
includes/profiler/ProfilerFunctions.php
includes/registration/ExtensionRegistry.php
includes/resourceloader/ResourceLoader.php
includes/resourceloader/ResourceLoaderEditToolbarModule.php
includes/resourceloader/ResourceLoaderFileModule.php
includes/resourceloader/ResourceLoaderModule.php
includes/search/SearchHighlighter.php
includes/site/MediaWikiSite.php
includes/specials/SpecialImport.php
includes/specials/SpecialListfiles.php
includes/specials/SpecialMovepage.php
includes/specials/SpecialNewpages.php
includes/specials/SpecialSearch.php
includes/specials/SpecialUserlogin.php
includes/tidy/Html5Depurate.php
includes/tidy/RaggettWrapper.php
includes/tidy/TidyDriverBase.php
includes/upload/UploadFromUrl.php
includes/utils/AvroValidator.php
includes/utils/FileContentsHasher.php [new file with mode: 0644]
languages/Language.php
languages/Names.php
languages/i18n/ady-cyrl.json
languages/i18n/af.json
languages/i18n/an.json
languages/i18n/ar.json
languages/i18n/as.json
languages/i18n/ast.json
languages/i18n/awa.json
languages/i18n/azb.json
languages/i18n/bcl.json
languages/i18n/be-tarask.json
languages/i18n/be.json
languages/i18n/bg.json
languages/i18n/bho.json
languages/i18n/bn.json
languages/i18n/br.json
languages/i18n/bs.json
languages/i18n/ca.json
languages/i18n/cs.json
languages/i18n/cy.json
languages/i18n/da.json
languages/i18n/de.json
languages/i18n/diq.json
languages/i18n/dty.json
languages/i18n/el.json
languages/i18n/en.json
languages/i18n/eo.json
languages/i18n/es.json
languages/i18n/et.json
languages/i18n/eu.json
languages/i18n/fa.json
languages/i18n/fi.json
languages/i18n/fr.json
languages/i18n/frp.json
languages/i18n/frr.json
languages/i18n/gd.json
languages/i18n/gl.json
languages/i18n/gsw.json
languages/i18n/gu.json
languages/i18n/he.json
languages/i18n/hrx.json
languages/i18n/hu.json
languages/i18n/ia.json
languages/i18n/ilo.json
languages/i18n/is.json
languages/i18n/it.json
languages/i18n/ja.json
languages/i18n/jut.json
languages/i18n/jv.json
languages/i18n/ka.json
languages/i18n/kab.json
languages/i18n/kk-cyrl.json
languages/i18n/km.json
languages/i18n/ko.json
languages/i18n/ksh.json
languages/i18n/lb.json
languages/i18n/lt.json
languages/i18n/lv.json
languages/i18n/mg.json
languages/i18n/mk.json
languages/i18n/ml.json
languages/i18n/ms.json
languages/i18n/mzn.json
languages/i18n/nah.json
languages/i18n/nap.json
languages/i18n/nb.json
languages/i18n/nds-nl.json
languages/i18n/ne.json
languages/i18n/nl.json
languages/i18n/nn.json
languages/i18n/oc.json
languages/i18n/olo.json
languages/i18n/or.json
languages/i18n/pa.json
languages/i18n/pl.json
languages/i18n/pms.json
languages/i18n/pnb.json
languages/i18n/ps.json
languages/i18n/pt-br.json
languages/i18n/pt.json
languages/i18n/qqq.json
languages/i18n/qu.json
languages/i18n/rm.json
languages/i18n/ro.json
languages/i18n/roa-tara.json
languages/i18n/ru.json
languages/i18n/rue.json
languages/i18n/sa.json
languages/i18n/sah.json
languages/i18n/scn.json
languages/i18n/sco.json
languages/i18n/ses.json
languages/i18n/sh.json
languages/i18n/si.json
languages/i18n/sk.json
languages/i18n/sl.json
languages/i18n/sq.json
languages/i18n/sr-ec.json
languages/i18n/sr-el.json
languages/i18n/sv.json
languages/i18n/ta.json
languages/i18n/te.json
languages/i18n/th.json
languages/i18n/tl.json
languages/i18n/tr.json
languages/i18n/tt-cyrl.json
languages/i18n/uk.json
languages/i18n/vec.json
languages/i18n/vi.json
languages/i18n/wa.json
languages/i18n/xmf.json
languages/i18n/yi.json
languages/i18n/yo.json
languages/i18n/yue.json
languages/i18n/zh-hans.json
languages/i18n/zh-hant.json
languages/messages/MessagesAn.php
languages/messages/MessagesDty.php
languages/messages/MessagesOlo.php [new file with mode: 0644]
languages/utils/CLDRPluralRuleConverter.php [deleted file]
languages/utils/CLDRPluralRuleConverterExpression.php [deleted file]
languages/utils/CLDRPluralRuleConverterFragment.php [deleted file]
languages/utils/CLDRPluralRuleConverterOperator.php [deleted file]
languages/utils/CLDRPluralRuleError.php [deleted file]
languages/utils/CLDRPluralRuleEvaluator.php [deleted file]
languages/utils/CLDRPluralRuleEvaluatorRange.php [deleted file]
maintenance/archives/patch-archive_ar_revid.sql
maintenance/archives/patch-backlinkindexes.sql
maintenance/archives/patch-categorylinksindex.sql
maintenance/cleanupPreferences.php
maintenance/populateContentModel.php
phpcs.xml
resources/Resources.php
resources/src/jquery/jquery.tablesorter.js
resources/src/mediawiki.action/mediawiki.action.edit.preview.js
resources/src/mediawiki.special/mediawiki.special.upload.js
resources/src/mediawiki.widgets/mw.widgets.CategorySelector.js
resources/src/mediawiki.widgets/mw.widgets.TitleInputWidget.css [deleted file]
resources/src/mediawiki.widgets/mw.widgets.TitleInputWidget.js
resources/src/mediawiki.widgets/mw.widgets.TitleOptionWidget.js
resources/src/mediawiki.widgets/mw.widgets.TitleSearchWidget.js [new file with mode: 0644]
resources/src/mediawiki.widgets/mw.widgets.TitleWidget.js [new file with mode: 0644]
resources/src/mediawiki.widgets/mw.widgets.TitleWidget.less [new file with mode: 0644]
resources/src/mediawiki/mediawiki.ForeignStructuredUpload.js
resources/src/mediawiki/mediawiki.Upload.BookletLayout.js [new file with mode: 0644]
resources/src/mediawiki/mediawiki.Upload.Dialog.js
tests/parser/parserTests.txt
tests/phpunit/MediaWikiTestCase.php
tests/phpunit/data/filecontentshasher/hash.svg [new file with mode: 0644]
tests/phpunit/data/filecontentshasher/primes.txt [new file with mode: 0644]
tests/phpunit/includes/ConsecutiveParametersMatcher.php [deleted file]
tests/phpunit/includes/GlobalFunctions/GlobalTest.php
tests/phpunit/includes/HtmlFormatterTest.php
tests/phpunit/includes/ImportTest.php
tests/phpunit/includes/OutputPageTest.php
tests/phpunit/includes/StatusTest.php
tests/phpunit/includes/WikiMapTest.php
tests/phpunit/includes/actions/ActionTest.php
tests/phpunit/includes/api/ApiBlockTest.php
tests/phpunit/includes/api/ApiUploadTest.php
tests/phpunit/includes/api/format/ApiFormatPhpTest.php
tests/phpunit/includes/changes/RecentChangeTest.php
tests/phpunit/includes/content/CssContentTest.php
tests/phpunit/includes/content/JavaScriptContentTest.php
tests/phpunit/includes/debug/logger/monolog/KafkaHandlerTest.php
tests/phpunit/includes/filebackend/SwiftFileBackendTest.php
tests/phpunit/includes/filerepo/FileBackendDBRepoWrapperTest.php
tests/phpunit/includes/filerepo/MigrateFileRepoLayoutTest.php
tests/phpunit/includes/logging/LogFormatterTest.php
tests/phpunit/includes/logging/LogFormatterTestCase.php
tests/phpunit/includes/logging/ProtectLogFormatterTest.php
tests/phpunit/includes/media/GIFTest.php
tests/phpunit/includes/media/PNGTest.php
tests/phpunit/includes/media/WebPTest.php
tests/phpunit/includes/parser/PreprocessorTest.php
tests/phpunit/includes/phpunit/ConsecutiveParametersMatcher.php [new file with mode: 0644]
tests/phpunit/includes/phpunit/LICENSE [new file with mode: 0644]
tests/phpunit/includes/phpunit/README [new file with mode: 0644]
tests/phpunit/includes/specials/SpecialSearchTest.php
tests/phpunit/includes/utils/AvroValidatorTest.php
tests/phpunit/includes/utils/BatchRowUpdateTest.php
tests/phpunit/includes/utils/FileContentsHasherTest.php [new file with mode: 0644]
tests/phpunit/languages/utils/CLDRPluralRuleEvaluatorTest.php [deleted file]
tests/phpunit/maintenance/MaintenanceTest.php
tests/phpunit/phpunit.php
tests/phpunit/suite.xml
tests/qunit/suites/resources/jquery/jquery.tablesorter.parsers.test.js
tests/qunit/suites/resources/jquery/jquery.tablesorter.test.js
tests/qunit/suites/resources/mediawiki/mediawiki.jqueryMsg.test.js
tests/qunit/suites/resources/mediawiki/mediawiki.test.js

index 47c751e..aab1b42 100644 (file)
@@ -41,6 +41,10 @@ production.
 * The UserRights hook is deprecated in favor of the new UserGroupsChanged hook.
 * AuthPlugin::initUser() and AuthPlugin::updateUser() should no longer replace
   the passed User object.
+* $wgBlockAllowsUTEdit is now set to true by default. This allows
+  blocked users to edit their talk pages unless explicitly disabled
+  when they are being blocked.
+* CLDRPluralRule* classes have been replaced with wikimedia/cldr-plural-rule-parser.
 
 === New features in 1.26 ===
 * (T51506) Now action=info gives estimates of actual watchers for a page.
@@ -70,6 +74,9 @@ production.
 * Caches that need purging ability now use the WANObjectCache interface.
   This corresponds to a new $wgMainWANCache setting, which defaults to using
   the $wgMainCacheType settings.
+* Added MWTimestamp::getTimezoneString() which returns the localized timezone
+  string, if available. To localize this string, see the comments of
+  $wgLocaltimezone in includes/DefaultSettings.php.
 * Callers needing fast light-weight data stores use $wgMainStash to select
   the store type from $wgObjectCaches. The default is the local database.
 * Interface message overrides in the MediaWiki namespace will now be cached in
@@ -84,6 +91,9 @@ production.
   page. During the deprecation period, the styles will only be loaded on pages
   which contain 'mw-ui-button' in their HTML. Starting in 1.28, the styles will
   only be loaded if explicitly required.
+* If search returns zero results and current search engine has a "did you mean"
+  suggestion, results for suggestion will be shown. Can be disabled by setting
+  $wgSearchRunSuggestedQuery to false.
 
 ==== External libraries ====
 * Update es5-shim from v4.0.0 to v4.1.5.
@@ -92,6 +102,7 @@ production.
 * Upgrade jQuery Client from v1.0.0 to v2.0.0.
 * Added mediawiki/at-ease 1.0.0.
 * Update QUnit from v1.17.1 to v1.18.0.
+* Added wikimedia/cldr-plural-rule-parser 1.0.0
 
 === Bug fixes in 1.26 ===
 * (T53283) load.php sometimes sends 304 response without full headers
@@ -145,6 +156,8 @@ changes to languages because of Phabricator reports.
 ** dty (डोटेली/Doteli), thanks to translators जनक राज भट्ट, बिप्लब आनन्द,
    मेश सिंह बोहरा, and राम प्रसाद जोशी
 ** luz (لئری دوٙمینی / Southern Luri)
+** olo (Livvinкarjala / Livvi-Karelian), thanks to translators Denö, Hiloin Natoi,
+   Ilja.mos, and Mashoi7
 
 === Other changes in 1.26 ===
 * ChangeTags::tagDescription() will return false if the interface message
@@ -198,9 +211,15 @@ changes to languages because of Phabricator reports.
 * Watchlist tokens, SpecialResetTokens, and User::getTokenFromOption()
   are deprecated. Applications using those can work via the OAuth
   extension instead. New tokens types should not be added.
+* (T36948) The default file revert message's timestamp is now in $wgLocaltimezone,
+  instead of UTC.
 * DatabaseBase::errorCount() was removed (unused).
 * $wgDeferredUpdateList was removed.
 * DeferredUpdates::addHTMLCacheUpdate() was removed.
+* The default name of the 'suppress' group page has been changed from
+  'Project:Oversight' to 'Project:Suppress'.
+* (T84937) Free external links ("autolinked" urls) will now be terminated
+  by &nbsp; and HTML entity encodings of &nbsp, <, and >.
 
 == Compatibility ==
 
diff --git a/UPGRADE b/UPGRADE
index 1ff98cd..088701a 100644 (file)
--- a/UPGRADE
+++ b/UPGRADE
@@ -5,7 +5,7 @@ specific problems, check
 * the documentation at https://www.mediawiki.org
 * the mediawiki-l mailing list archive at
   http://lists.wikimedia.org/pipermail/mediawiki-l/
-* the bug tracker at https://bugzilla.wikimedia.org
+* the bug tracker at https://phabricator.wikimedia.org
 
 for information and workarounds to common issues.
 
index 4bed014..f1b0a6c 100644 (file)
@@ -184,13 +184,6 @@ $wgAutoloadLocalClasses = array(
        'BmpHandler' => __DIR__ . '/includes/media/BMP.php',
        'BrokenRedirectsPage' => __DIR__ . '/includes/specials/SpecialBrokenRedirects.php',
        'BufferingStatsdDataFactory' => __DIR__ . '/includes/libs/BufferingStatsdDataFactory.php',
-       'CLDRPluralRuleConverter' => __DIR__ . '/languages/utils/CLDRPluralRuleConverter.php',
-       'CLDRPluralRuleConverterExpression' => __DIR__ . '/languages/utils/CLDRPluralRuleConverterExpression.php',
-       'CLDRPluralRuleConverterFragment' => __DIR__ . '/languages/utils/CLDRPluralRuleConverterFragment.php',
-       'CLDRPluralRuleConverterOperator' => __DIR__ . '/languages/utils/CLDRPluralRuleConverterOperator.php',
-       'CLDRPluralRuleError' => __DIR__ . '/languages/utils/CLDRPluralRuleError.php',
-       'CLDRPluralRuleEvaluator' => __DIR__ . '/languages/utils/CLDRPluralRuleEvaluator.php',
-       'CLDRPluralRuleEvaluatorRange' => __DIR__ . '/languages/utils/CLDRPluralRuleEvaluatorRange.php',
        'CLIParser' => __DIR__ . '/maintenance/parse.php',
        'CSSMin' => __DIR__ . '/includes/libs/CSSMin.php',
        'CacheDependency' => __DIR__ . '/includes/cache/CacheDependency.php',
@@ -283,7 +276,7 @@ $wgAutoloadLocalClasses = array(
        'CsvStatsOutput' => __DIR__ . '/maintenance/language/StatOutputs.php',
        'CurlHttpRequest' => __DIR__ . '/includes/HttpFunctions.php',
        'DBAccessBase' => __DIR__ . '/includes/dao/DBAccessBase.php',
-       'DBAccessError' => __DIR__ . '/includes/db/LBFactory.php',
+       'DBAccessError' => __DIR__ . '/includes/db/loadbalancer/LBFactory.php',
        'DBAccessObjectUtils' => __DIR__ . '/includes/dao/DBAccessObjectUtils.php',
        'DBConnRef' => __DIR__ . '/includes/db/DBConnRef.php',
        'DBConnectionError' => __DIR__ . '/includes/db/DatabaseError.php',
@@ -384,6 +377,7 @@ $wgAutoloadLocalClasses = array(
        'EnhancedChangesList' => __DIR__ . '/includes/changes/EnhancedChangesList.php',
        'EnotifNotifyJob' => __DIR__ . '/includes/jobqueue/jobs/EnotifNotifyJob.php',
        'EnqueueJob' => __DIR__ . '/includes/jobqueue/jobs/EnqueueJob.php',
+       'EnqueueableDataUpdate' => __DIR__ . '/includes/deferred/DataUpdate.php',
        'EraseArchivedFile' => __DIR__ . '/maintenance/eraseArchivedFile.php',
        'ErrorPageError' => __DIR__ . '/includes/exception/ErrorPageError.php',
        'EventRelayer' => __DIR__ . '/includes/libs/eventrelayer/EventRelayer.php',
@@ -436,6 +430,7 @@ $wgAutoloadLocalClasses = array(
        'FileBackendStoreShardListIterator' => __DIR__ . '/includes/filebackend/FileBackendStore.php',
        'FileBasedSiteLookup' => __DIR__ . '/includes/site/FileBasedSiteLookup.php',
        'FileCacheBase' => __DIR__ . '/includes/cache/FileCacheBase.php',
+       'FileContentsHasher' => __DIR__ . '/includes/utils/FileContentsHasher.php',
        'FileDeleteForm' => __DIR__ . '/includes/FileDeleteForm.php',
        'FileDependency' => __DIR__ . '/includes/cache/CacheDependency.php',
        'FileDuplicateSearchPage' => __DIR__ . '/includes/specials/SpecialFileDuplicateSearch.php',
@@ -609,11 +604,11 @@ $wgAutoloadLocalClasses = array(
        'JsonContentHandler' => __DIR__ . '/includes/content/JsonContentHandler.php',
        'KkConverter' => __DIR__ . '/languages/classes/LanguageKk.php',
        'KuConverter' => __DIR__ . '/languages/classes/LanguageKu.php',
-       'LBFactory' => __DIR__ . '/includes/db/LBFactory.php',
-       'LBFactoryFake' => __DIR__ . '/includes/db/LBFactory.php',
-       'LBFactoryMulti' => __DIR__ . '/includes/db/LBFactoryMulti.php',
-       'LBFactorySimple' => __DIR__ . '/includes/db/LBFactory.php',
-       'LBFactorySingle' => __DIR__ . '/includes/db/LBFactorySingle.php',
+       'LBFactory' => __DIR__ . '/includes/db/loadbalancer/LBFactory.php',
+       'LBFactoryFake' => __DIR__ . '/includes/db/loadbalancer/LBFactoryFake.php',
+       'LBFactoryMulti' => __DIR__ . '/includes/db/loadbalancer/LBFactoryMulti.php',
+       'LBFactorySimple' => __DIR__ . '/includes/db/loadbalancer/LBFactorySimple.php',
+       'LBFactorySingle' => __DIR__ . '/includes/db/loadbalancer/LBFactorySingle.php',
        'LCStore' => __DIR__ . '/includes/cache/LocalisationCache.php',
        'LCStoreCDB' => __DIR__ . '/includes/cache/LocalisationCache.php',
        'LCStoreDB' => __DIR__ . '/includes/cache/LocalisationCache.php',
@@ -684,11 +679,11 @@ $wgAutoloadLocalClasses = array(
        'ListDuplicatedFilesPage' => __DIR__ . '/includes/specials/SpecialListDuplicatedFiles.php',
        'ListVariants' => __DIR__ . '/maintenance/language/listVariants.php',
        'ListredirectsPage' => __DIR__ . '/includes/specials/SpecialListredirects.php',
-       'LoadBalancer' => __DIR__ . '/includes/db/LoadBalancer.php',
-       'LoadBalancerSingle' => __DIR__ . '/includes/db/LBFactorySingle.php',
-       'LoadMonitor' => __DIR__ . '/includes/db/LoadMonitor.php',
-       'LoadMonitorMySQL' => __DIR__ . '/includes/db/LoadMonitorMySQL.php',
-       'LoadMonitorNull' => __DIR__ . '/includes/db/LoadMonitor.php',
+       'LoadBalancer' => __DIR__ . '/includes/db/loadbalancer/LoadBalancer.php',
+       'LoadBalancerSingle' => __DIR__ . '/includes/db/loadbalancer/LBFactorySingle.php',
+       'LoadMonitor' => __DIR__ . '/includes/db/loadbalancer/LoadMonitor.php',
+       'LoadMonitorMySQL' => __DIR__ . '/includes/db/loadbalancer/LoadMonitorMySQL.php',
+       'LoadMonitorNull' => __DIR__ . '/includes/db/loadbalancer/LoadMonitor.php',
        'LocalFile' => __DIR__ . '/includes/filerepo/file/LocalFile.php',
        'LocalFileDeleteBatch' => __DIR__ . '/includes/filerepo/file/LocalFile.php',
        'LocalFileMoveBatch' => __DIR__ . '/includes/filerepo/file/LocalFile.php',
index e41b6c7..6153045 100644 (file)
                "cssjanus/cssjanus": "1.1.1",
                "ext-iconv": "*",
                "liuggio/statsd-php-client": "1.0.16",
-               "oyejorge/less.php": "1.7.0.5",
+               "oyejorge/less.php": "1.7.0.8",
                "mediawiki/at-ease": "1.1.0",
                "oojs/oojs-ui": "0.12.9",
                "php": ">=5.3.3",
                "psr/log": "1.0.0",
                "wikimedia/assert": "0.2.2",
                "wikimedia/cdb": "1.3.0",
+               "wikimedia/cldr-plural-rule-parser": "1.0.0",
                "wikimedia/composer-merge-plugin": "1.2.1",
                "wikimedia/ip-set": "1.0.1",
                "wikimedia/utfnormal": "1.0.3",
index 866e891..2d268b8 100644 (file)
@@ -641,16 +641,14 @@ $popts: parser options to be used for pre-save transformation
 'ArticleProtect': Before an article is protected.
 $wikiPage: the WikiPage being protected
 $user: the user doing the protection
-$protect: boolean whether this is a protect or an unprotect
+$protect: Set of restriction keys
 $reason: Reason for protect
-$moveonly: boolean whether this is for move only or not
 
 'ArticleProtectComplete': After an article is protected.
 $wikiPage: the WikiPage that was protected
 $user: the user who did the protection
-$protect: boolean whether it was a protect or an unprotect
+$protect: Set of restriction keys
 $reason: Reason for protect
-$moveonly: boolean whether it was for move only or not
 
 'ArticlePurge': Before executing "&action=purge".
 $wikiPage: WikiPage (object) to purge
@@ -1581,9 +1579,10 @@ $imagePage: ImagePage object ($this)
 &$html: HTML for the hook to add
 
 'ImagePageFileHistoryLine': Called when a file history line is constructed.
+$imagePage: ImagePage object ($this)
 $file: the file
-$line: the HTML of the history line
-$css: the line CSS class
+&$line: the HTML of the history line
+&$css: the line CSS class
 
 'ImagePageFindFile': Called when fetching the file associated with an image
 page.
@@ -1633,6 +1632,11 @@ Return false to stop further processing of the tag
 $reader: XMLReader object
 $revisionInfo: Array of information
 
+'ImportSources': Called when reading from the $wgImportSources configuration
+variable. Can be used to lazy-load the import sources list.
+&$importSources: The value of $wgImportSources. Modify as necessary. See the
+comment in DefaultSettings.php for the detail of how to structure this array.
+
 'InfoAction': When building information to display on the action=info page.
 $context: IContextSource object
 &$pageInfo: Array of information
@@ -2164,9 +2168,10 @@ $article: the article that the history is loading for
 $context: RequestContext object
 
 'PageHistoryLineEnding': Right before the end <li> is added to a history line.
-$row: the revision row for this line
-$s: the string representing this parsed line
-$classes: array containing the <li> element classes
+$historyAction: the action object
+&$row: the revision row for this line
+&$s: the string representing this parsed line
+&$classes: array containing the <li> element classes
 
 'PageHistoryPager::doBatchLookups': Called after the pager query was run, before
 any output is generated, to allow batch lookups for prefetching information
@@ -2670,8 +2675,10 @@ $out: OutputPage object
 
 'SkinTemplateBuildNavUrlsNav_urlsAfterPermalink': After creating the "permanent
 link" tab.
-$sktemplate: SkinTemplate object
-$nav_urls: array of tabs
+&$sktemplate: SkinTemplate object
+&$nav_urls: array of tabs
+&$revid: The revision id of the permanent link
+&$revid2: The revision id of the permanent link, second time
 
 'SkinTemplateGetLanguageLink': After building the data for a language link from
 which the actual html is constructed.
index 37429b9..f58b339 100644 (file)
@@ -4573,7 +4573,7 @@ $wgAutoblockExpiry = 86400;
 /**
  * Set this to true to allow blocked users to edit their own user talk page.
  */
-$wgBlockAllowsUTEdit = false;
+$wgBlockAllowsUTEdit = true;
 
 /**
  * Allow sysops to ban users from accessing Emailuser
@@ -6363,8 +6363,8 @@ $wgShowCreditsIfMax = true;
 
 /**
  * List of interwiki prefixes for wikis we'll accept as sources for
- * Special:Import (for sysops). Since complete page history can be imported,
- * these should be 'trusted'.
+ * Special:Import and API action=import. Since complete page history can be
+ * imported, these should be 'trusted'.
  *
  * This can either be a regular array, or an associative map specifying
  * subprojects on the interwiki map of the target wiki, or a mix of the two,
@@ -6377,6 +6377,9 @@ $wgShowCreditsIfMax = true;
  *     );
  * @endcode
  *
+ * If you have a very complex import sources setup, you can lazy-load it using
+ * the ImportSources hook.
+ *
  * If a user has the 'import' permission but not the 'importupload' permission,
  * they will only be able to run imports through this transwiki interface.
  */
@@ -6931,11 +6934,7 @@ $wgLogHeaders = array(
  *
  * Extensions with custom log types may add to this array.
  */
-$wgLogActions = array(
-       'protect/modify' => 'modifiedarticleprotection',
-       'protect/protect' => 'protectedarticle',
-       'protect/unprotect' => 'unprotectedarticle',
-);
+$wgLogActions = array();
 
 /**
  * The same as above, but here values are names of classes,
@@ -6962,7 +6961,10 @@ $wgLogActionsHandlers = array(
        'move/move' => 'MoveLogFormatter',
        'move/move_redir' => 'MoveLogFormatter',
        'patrol/patrol' => 'PatrolLogFormatter',
+       'protect/modify' => 'ProtectLogFormatter',
        'protect/move_prot' => 'ProtectLogFormatter',
+       'protect/protect' => 'ProtectLogFormatter',
+       'protect/unprotect' => 'ProtectLogFormatter',
        'rights/autopromote' => 'RightsLogFormatter',
        'rights/rights' => 'RightsLogFormatter',
        'suppress/block' => 'BlockLogFormatter',
@@ -7470,7 +7472,7 @@ $wgRunJobsAsync = true;
 /**
  * Number of rows to update per job
  */
-$wgUpdateRowsPerJob = 500;
+$wgUpdateRowsPerJob = 300;
 
 /**
  * Number of rows to update per query
@@ -7704,14 +7706,13 @@ $wgVirtualRestConfig = array(
 );
 
 /**
- * Controls the percentage of zero-result search queries with suggestions that
- * run the suggestion automatically. Must be a number between 0 and 1.  This
- * can be lowered to reduce query volume at the expense of result quality.
+ * Controls whether zero-result search queries with suggestions should display results for
+ * these suggestions.
  *
- * @var float
+ * @var bool
  * @since 1.26
  */
-$wgSearchRunSuggestedQueryPercent = 1;
+$wgSearchRunSuggestedQuery = true;
 
 /**
  * For really cool vim folding this needs to be at the end:
index 600b136..2133dae 100644 (file)
@@ -141,7 +141,7 @@ class FeedItem {
         */
        public function getLanguage() {
                global $wgLanguageCode;
-               return $wgLanguageCode;
+               return wfBCP47( $wgLanguageCode );
        }
 
        /**
index 486926f..8f70120 100644 (file)
@@ -635,7 +635,7 @@ function wfExpandUrl( $url, $defaultProto = PROTO_CURRENT ) {
        $bits = wfParseUrl( $url );
 
        // ensure proper port for HTTPS arrives in URL
-       // https://bugzilla.wikimedia.org/show_bug.cgi?id=65184
+       // https://phabricator.wikimedia.org/T67184
        if ( $defaultProto === PROTO_HTTPS && $wgHttpsPort != 443 ) {
                $bits['port'] = $wgHttpsPort;
        }
@@ -4072,8 +4072,6 @@ function wfUnpack( $format, $data, $length = false ) {
  * @return bool
  */
 function wfIsBadImage( $name, $contextTitle = false, $blacklist = null ) {
-       static $badImageCache = null; // based on bad_image_list msg
-
        # Handle redirects
        $redirectTitle = RepoGroup::singleton()->checkRedirect( Title::makeTitle( NS_FILE, $name ) );
        if ( $redirectTitle ) {
@@ -4086,10 +4084,11 @@ function wfIsBadImage( $name, $contextTitle = false, $blacklist = null ) {
                return $bad;
        }
 
-       $cacheable = ( $blacklist === null );
-       if ( $cacheable && $badImageCache !== null ) {
-               $badImages = $badImageCache;
-       } else { // cache miss
+       $cache = ObjectCache::newAccelerator( 'hash' );
+       $key = wfMemcKey( 'bad-image-list', ( $blacklist === null ) ? 'default' : md5( $blacklist ) );
+       $badImages = $cache->get( $key );
+
+       if ( $badImages === false ) { // cache miss
                if ( $blacklist === null ) {
                        $blacklist = wfMessage( 'bad_image_list' )->inContentLanguage()->plain(); // site list
                }
@@ -4125,13 +4124,12 @@ function wfIsBadImage( $name, $contextTitle = false, $blacklist = null ) {
                                $badImages[$imageDBkey] = $exceptions;
                        }
                }
-               if ( $cacheable ) {
-                       $badImageCache = $badImages;
-               }
+               $cache->set( $key, $badImages, 60 );
        }
 
        $contextKey = $contextTitle ? $contextTitle->getPrefixedDBkey() : false;
        $bad = isset( $badImages[$name] ) && !isset( $badImages[$name][$contextKey] );
+
        return $bad;
 }
 
index 221cefb..83db268 100644 (file)
@@ -75,7 +75,7 @@ class HtmlFormatter {
 
                        // Workaround for bug that caused spaces before references
                        // to disappear during processing:
-                       // https://bugzilla.wikimedia.org/show_bug.cgi?id=53086
+                       // https://phabricator.wikimedia.org/T55086
                        //
                        // Please replace with a better fix if one can be found.
                        $html = str_replace( ' <', '&#32;<', $html );
index bc5a957..5d46b5e 100644 (file)
@@ -838,17 +838,19 @@ class CurlHttpRequest extends MWHttpRequest {
         * @return bool
         */
        public function canFollowRedirects() {
-               if ( strval( ini_get( 'open_basedir' ) ) !== '' || wfIniGetBool( 'safe_mode' ) ) {
-                       wfDebug( "Cannot follow redirects in safe mode\n" );
-                       return false;
-               }
-
                $curlVersionInfo = curl_version();
                if ( $curlVersionInfo['version_number'] < 0x071304 ) {
                        wfDebug( "Cannot follow redirects with libcurl < 7.19.4 due to CVE-2009-0037\n" );
                        return false;
                }
 
+               if ( version_compare( PHP_VERSION, '5.6.0', '<' ) ) {
+                       if ( strval( ini_get( 'open_basedir' ) ) !== '' || wfIniGetBool( 'safe_mode' ) ) {
+                               wfDebug( "Cannot follow redirects in safe mode\n" );
+                               return false;
+                       }
+               }
+
                return true;
        }
 }
index 6a0bfd0..db4a6b2 100644 (file)
@@ -728,13 +728,14 @@ class WikiImporter {
                                        $title = $this->processTitle( $pageInfo['title'],
                                                isset( $pageInfo['ns'] ) ? $pageInfo['ns'] : null );
 
-                                       if ( !$title ) {
+                                       // $title is either an array of two titles or false.
+                                       if ( is_array( $title ) ) {
+                                               $this->pageCallback( $title );
+                                               list( $pageInfo['_title'], $foreignTitle ) = $title;
+                                       } else {
                                                $badTitle = true;
                                                $skip = true;
                                        }
-
-                                       $this->pageCallback( $title );
-                                       list( $pageInfo['_title'], $foreignTitle ) = $title;
                                }
 
                                if ( $title ) {
@@ -750,10 +751,17 @@ class WikiImporter {
                        }
                }
 
-               $this->pageOutCallback( $pageInfo['_title'], $foreignTitle,
+               // @note $pageInfo is only set if a valid $title is processed above with
+               //       no error. If we have a valid $title, then pageCallback is called
+               //       above, $pageInfo['title'] is set and we do pageOutCallback here.
+               //       If $pageInfo['_title'] is not set, then $foreignTitle is also not
+               //       set since they both come from $title above.
+               if ( array_key_exists( '_title', $pageInfo ) ) {
+                       $this->pageOutCallback( $pageInfo['_title'], $foreignTitle,
                                        $pageInfo['revisionCount'],
                                        $pageInfo['successfulRevisionCount'],
                                        $pageInfo );
+               }
        }
 
        /**
index 9b5ff27..2e33bd1 100644 (file)
@@ -1274,14 +1274,17 @@ class Linker {
         * temporarily to a value pass. Should be adjusted further. --brion
         *
         * @param string $comment
-        * @param Title|null $title Title object (to generate link to the section in autocomment) or null
+        * @param Title|null $title Title object (to generate link to the section in autocomment)
+        *  or null
         * @param bool $local Whether section links should refer to local page
-        * @param string|null $wikiId Id (as used by WikiMap) of the wiki to generate links to. For use with external changes.
+        * @param string|null $wikiId Id (as used by WikiMap) of the wiki to generate links to.
+        *  For use with external changes.
         *
         * @return mixed|string
         */
-       public static function formatComment( $comment, $title = null, $local = false, $wikiId = null ) {
-
+       public static function formatComment(
+               $comment, $title = null, $local = false, $wikiId = null
+       ) {
                # Sanitize text a bit:
                $comment = str_replace( "\n", " ", $comment );
                # Allow HTML entities (for bug 13815)
@@ -1306,11 +1309,14 @@ class Linker {
         * @param string $comment Comment text
         * @param Title|null $title An optional title object used to links to sections
         * @param bool $local Whether section links should refer to local page
-        * @param string|null $wikiId Id of the wiki to link to (if not the local wiki), as used by WikiMap.
+        * @param string|null $wikiId Id of the wiki to link to (if not the local wiki),
+        *  as used by WikiMap.
         *
         * @return string Formatted comment (wikitext)
         */
-       private static function formatAutocomments( $comment, $title = null, $local = false, $wikiId = null ) {
+       private static function formatAutocomments(
+               $comment, $title = null, $local = false, $wikiId = null
+       ) {
                // @todo $append here is something of a hack to preserve the status
                // quo. Someone who knows more about bidi and such should decide
                // (1) what sane rendering even *is* for an LTR edit summary on an RTL
@@ -1334,7 +1340,12 @@ class Linker {
                                $auto = $match[2];
                                $post = $match[3] !== '';
                                $comment = null;
-                               Hooks::run( 'FormatAutocomments', array( &$comment, $pre, $auto, $post, $title, $local, $wikiId ) );
+
+                               Hooks::run(
+                                       'FormatAutocomments',
+                                       array( &$comment, $pre, $auto, $post, $title, $local, $wikiId )
+                               );
+
                                if ( $comment === null ) {
                                        $link = '';
                                        if ( $title ) {
@@ -1386,7 +1397,8 @@ class Linker {
         * @param string $comment Text to format links in
         * @param Title|null $title An optional title object used to links to sections
         * @param bool $local Whether section links should refer to local page
-        * @param string|null $wikiId Id of the wiki to link to (if not the local wiki), as used by WikiMap.
+        * @param string|null $wikiId Id of the wiki to link to (if not the local wiki),
+        *  as used by WikiMap.
         *
         * @return string
         */
@@ -1489,18 +1501,25 @@ class Linker {
         *
         * @param Title $title
         * @param string $text
-        * @param string|null $wikiId Id of the wiki to link to (if not the local wiki), as used by WikiMap.
+        * @param string|null $wikiId Id of the wiki to link to (if not the local wiki),
+        *  as used by WikiMap.
         * @param string|string[] $options See the $options parameter in Linker::link.
         *
         * @return string HTML link
         */
-       public static function makeCommentLink( Title $title, $text, $wikiId = null, $options = array() ) {
+       public static function makeCommentLink(
+               Title $title, $text, $wikiId = null, $options = array()
+       ) {
                if ( $wikiId !== null && !$title->isExternal() ) {
                        $link = Linker::makeExternalLink(
-                                       WikiMap::getForeignURL( $wikiId, $title->getPrefixedText(), $title->getFragment() ),
-                                       $text,
-                                       /* escape = */ false // Already escaped
-                               );
+                               WikiMap::getForeignURL(
+                                       $wikiId,
+                                       $title->getPrefixedText(),
+                                       $title->getFragment()
+                               ),
+                               $text,
+                               /* escape = */ false // Already escaped
+                       );
                } else {
                        $link = Linker::link( $title, $text, array(), array(), $options );
                }
@@ -1594,11 +1613,14 @@ class Linker {
         * @param string $comment
         * @param Title|null $title Title object (to generate link to section in autocomment) or null
         * @param bool $local Whether section links should refer to local page
-        * @param string|null $wikiId Id (as used by WikiMap) of the wiki to generate links to. For use with external changes.
+        * @param string|null $wikiId Id (as used by WikiMap) of the wiki to generate links to.
+        *  For use with external changes.
         *
         * @return string
         */
-       public static function commentBlock( $comment, $title = null, $local = false, $wikiId = null ) {
+       public static function commentBlock(
+               $comment, $title = null, $local = false, $wikiId = null
+       ) {
                // '*' used to be the comment inserted by the software way back
                // in antiquity in case none was provided, here for backwards
                // compatibility, acc. to brion -ævar
index d28f88e..6f3be73 100644 (file)
@@ -367,6 +367,26 @@ class MWTimestamp {
                return $this->timestamp->getTimezone();
        }
 
+       /**
+        * Get the localized timezone message, if available.
+        *
+        * Premade translations are not shipped as format() may return whatever the
+        * system uses, localized or not, so translation must be done through wiki.
+        *
+        * @since 1.25
+        * @return Message The localized timezone message
+        */
+       public function getTimezoneMessage() {
+               $tzMsg = $this->format( 'T' );  // might vary on DST changeover!
+               $key = 'timezone-' . strtolower( trim( $tzMsg ) );
+               $msg = wfMessage( $key );
+               if ( $msg->exists() ) {
+                       return $msg;
+               } else {
+                       return new RawMessage( $tzMsg );
+               }
+       }
+
        /**
         * Format the timestamp in a given format.
         *
index 22c90cd..d57dfaa 100644 (file)
@@ -1694,6 +1694,7 @@ class User implements IDBAccessObject {
                        foreach ( (array)$bases as $base ) {
                                // Make hostname
                                // If we have an access key, use that too (ProjectHoneypot, etc.)
+                               $basename = $base;
                                if ( is_array( $base ) ) {
                                        if ( count( $base ) >= 2 ) {
                                                // Access key is 1, base URL is 0
@@ -1701,6 +1702,7 @@ class User implements IDBAccessObject {
                                        } else {
                                                $host = "$ipReversed.{$base[0]}";
                                        }
+                                       $basename = $base[0];
                                } else {
                                        $host = "$ipReversed.$base";
                                }
@@ -1709,11 +1711,11 @@ class User implements IDBAccessObject {
                                $ipList = gethostbynamel( $host );
 
                                if ( $ipList ) {
-                                       wfDebugLog( 'dnsblacklist', "Hostname $host is {$ipList[0]}, it's a proxy says $base!" );
+                                       wfDebugLog( 'dnsblacklist', "Hostname $host is {$ipList[0]}, it's a proxy says $basename!" );
                                        $found = true;
                                        break;
                                } else {
-                                       wfDebugLog( 'dnsblacklist', "Requested $host, not found in $base." );
+                                       wfDebugLog( 'dnsblacklist', "Requested $host, not found in $basename." );
                                }
                        }
                }
index b4b8be9..f402f3b 100644 (file)
@@ -315,7 +315,9 @@ class WebRequest {
                        }
                } else {
                        global $wgContLang;
-                       $data = isset( $wgContLang ) ? $wgContLang->normalize( $data ) : UtfNormal\Validator::cleanUp( $data );
+                       $data = isset( $wgContLang ) ?
+                               $wgContLang->normalize( $data ) :
+                               UtfNormal\Validator::cleanUp( $data );
                }
                return $data;
        }
@@ -754,7 +756,8 @@ class WebRequest {
         * Appends or replaces value of query variables.
         *
         * @param array $array Array of values to replace/add to query
-        * @param bool $onlyquery Whether to only return the query string and not the complete URL [deprecated]
+        * @param bool $onlyquery Whether to only return the query string
+        *  and not the complete URL [deprecated]
         * @return string
         */
        public function appendQueryArray( $array, $onlyquery = true ) {
index f5a4f93..b095577 100644 (file)
 # Die if register_globals is enabled (PHP <=5.3)
 # This must be done before any globals are set by the code
 if ( ini_get( 'register_globals' ) ) {
-       die( 'MediaWiki does not support installations where register_globals is enabled. '
-               . 'Please see <a href="https://www.mediawiki.org/wiki/register_globals">mediawiki.org</a> '
+       die( 'MediaWiki does not support installations where register_globals is enabled. Please see '
+               . '<a href="https://www.mediawiki.org/wiki/register_globals">mediawiki.org</a> '
                . 'for help on how to disable it.' );
 }
 
 if ( function_exists( 'get_magic_quotes_gpc' ) && get_magic_quotes_gpc() ) {
-       die( 'MediaWiki does not function when magic quotes are enabled. '
-               . 'Please see the <a href="https://php.net/manual/security.magicquotes.disabling.php">PHP Manual</a> '
+       die( 'MediaWiki does not function when magic quotes are enabled. Please see the '
+               . '<a href="https://php.net/manual/security.magicquotes.disabling.php">PHP Manual</a> '
                . 'for help on how to disable magic quotes.' );
 }
 
index c7f3346..4885a31 100644 (file)
@@ -82,8 +82,11 @@ class RevertAction extends FormAction {
                $lang = $this->getLanguage();
                $userDate = $lang->userDate( $timestamp, $user );
                $userTime = $lang->userTime( $timestamp, $user );
-               $siteDate = $wgContLang->date( $timestamp, false, false );
-               $siteTime = $wgContLang->time( $timestamp, false, false );
+               $siteTs = MWTimestamp::getLocalInstance( $timestamp );
+               $ts = $siteTs->format( 'YmdHis' );
+               $siteDate = $wgContLang->date( $ts, false, false );
+               $siteTime = $wgContLang->time( $ts, false, false );
+               $tzMsg = $siteTs->getTimezoneMessage()->inContentLanguage()->text();
 
                return array(
                        'intro' => array(
@@ -100,8 +103,8 @@ class RevertAction extends FormAction {
                        'comment' => array(
                                'type' => 'text',
                                'label-message' => 'filerevert-comment',
-                               'default' => $this->msg( 'filerevert-defaultcomment', $siteDate, $siteTime
-                                       )->inContentLanguage()->text()
+                               'default' => $this->msg( 'filerevert-defaultcomment', $siteDate, $siteTime,
+                                       $tzMsg )->inContentLanguage()->text()
                        )
                );
        }
index 93669cf..70d7d16 100644 (file)
@@ -109,7 +109,7 @@ class RollbackAction extends FormlessAction {
                $this->getOutput()->returnToMain( false, $this->getTitle() );
 
                if ( !$request->getBool( 'hidediff', false ) &&
-                       !$this->getUser()->getBoolOption( 'norollbackdiff', false )
+                       !$this->getUser()->getBoolOption( 'norollbackdiff' )
                ) {
                        $contentHandler = $current->getContentHandler();
                        $de = $contentHandler->createDifferenceEngine(
index d53797b..7743384 100644 (file)
@@ -206,7 +206,8 @@ abstract class ApiBase extends ContextSource {
                                // Fix up the ugly "even numbered elements are description, odd
                                // numbered elemts are the link" format (see doc for self::getExamples)
                                $tmp = array();
-                               for ( $i = 0; $i < count( $examples ); $i += 2 ) {
+                               $examplesCount = count( $examples );
+                               for ( $i = 0; $i < $examplesCount; $i += 2 ) {
                                        $tmp[$examples[$i + 1]] = $examples[$i];
                                }
                                $examples = $tmp;
index 6420a5b..df9d581 100644 (file)
@@ -69,7 +69,7 @@ class ApiFormatPhp extends ApiFormatBase {
                ) {
                        $this->dieUsage(
                                'This response cannot be represented using format=php. ' .
-                               'See https://bugzilla.wikimedia.org/show_bug.cgi?id=66776',
+                               'See https://phabricator.wikimedia.org/T68776',
                                'internalerror'
                        );
                }
index 735cc7f..a6aae4b 100644 (file)
@@ -92,6 +92,30 @@ class ApiImport extends ApiBase {
                $result->addValue( null, $this->getModuleName(), $resultData );
        }
 
+       /**
+        * Returns a list of interwiki prefixes corresponding to each defined import
+        * source.
+        *
+        * @return array
+        * @since 1.26
+        */
+       public function getAllowedImportSources() {
+               $importSources = $this->getConfig()->get( 'ImportSources' );
+               Hooks::run( 'ImportSources', array( &$importSources ) );
+
+               $result = array();
+               foreach ( $importSources as $key => $value ) {
+                       if ( is_int( $key ) ) {
+                               $result[] = $value;
+                       } else {
+                               foreach ( $value as $subproject ) {
+                                       $result[] = "$key:$subproject";
+                               }
+                       }
+               }
+               return $result;
+       }
+
        public function mustBePosted() {
                return true;
        }
@@ -107,7 +131,7 @@ class ApiImport extends ApiBase {
                                ApiBase::PARAM_TYPE => 'upload',
                        ),
                        'interwikisource' => array(
-                               ApiBase::PARAM_TYPE => $this->getConfig()->get( 'ImportSources' ),
+                               ApiBase::PARAM_TYPE => $this->getAllowedImportSources(),
                        ),
                        'interwikipage' => null,
                        'fullhistory' => false,
index 172ee99..4f7984e 100644 (file)
@@ -54,7 +54,6 @@ class ApiQueryAllDeletedRevisions extends ApiQueryRevisionsBase {
                $params = $this->extractRequestParams( false );
 
                $result = $this->getResult();
-               $pageSet = $this->getPageSet();
 
                // This module operates in two modes:
                // 'user': List deleted revs by a certain user
index bf48b19..fa1dfc2 100644 (file)
@@ -46,7 +46,6 @@ class ApiQueryDeletedRevisions extends ApiQueryRevisionsBase {
                        );
                }
 
-               $result = $this->getResult();
                $pageSet = $this->getPageSet();
                $pageMap = $pageSet->getGoodAndMissingTitlesByNamespace();
                $pageCount = count( $pageSet->getGoodAndMissingTitles() );
index 78f0568..849cef8 100644 (file)
        "apihelp-parse-param-pst": "Do a pre-save transform on the input before parsing it. Only valid when used with text.",
        "apihelp-parse-param-onlypst": "Do a pre-save transform (PST) on the input, but don't parse it. Returns the same wikitext, after a PST has been applied. Only valid when used with <var>$1text</var>.",
        "apihelp-parse-param-effectivelanglinks": "Includes language links supplied by extensions (for use with <kbd>$1prop=langlinks</kbd>).",
-       "apihelp-parse-param-section": "Only retrieve the content of this section number or when <kbd>new</kbd> generate a new section.\n\n<kbd>new</kbd> section is only honored when specifying <var>text</var>.",
+       "apihelp-parse-param-section": "Only parse the content of this section number.\n\nWhen <kbd>new</kbd>, parse <var>$1text</var> and <var>$1sectiontitle</var> as if adding a new section to the page.\n\n<kbd>new</kbd> is allowed only when specifying <var>text</var>.",
        "apihelp-parse-param-sectiontitle": "New section title when <var>section</var> is <kbd>new</kbd>.\n\nUnlike page editing, this does not fall back to <var>summary</var> when omitted or empty.",
        "apihelp-parse-param-disablelimitreport": "Omit the limit report (\"NewPP limit report\") from the parser output.",
        "apihelp-parse-param-disablepp": "Use <var>$1disablelimitreport</var> instead.",
index a9c04d0..1163680 100644 (file)
        "apihelp-stashedit-param-section": "Número de la sección. <kbd>0</kbd> para una sección superior, <kbd>new</kbd> para una sección nueva.",
        "apihelp-stashedit-param-sectiontitle": "El título de una sección nueva.",
        "apihelp-stashedit-param-text": "Contenido de la página.",
+       "apihelp-stashedit-param-contentmodel": "Modelo del contenido nuevo.",
        "apihelp-stashedit-param-contentformat": "Formato de serialización de contenido utilizado para el texto de entrada.",
        "apihelp-stashedit-param-baserevid": "Identificador de la revisión de base.",
        "apihelp-tag-param-logid": "Uno o más identificadores de entradas del registro a los que agregar o eliminar la etiqueta.",
index 6b6345d..1d06cd8 100644 (file)
        "apihelp-setnotificationtimestamp-example-page": "Restaurar o estado de notificación para a <kbd>Páxina Principal</kbd>.",
        "apihelp-setnotificationtimestamp-example-pagetimestamp": "Fixar o selo de tempo de notificación para a <kbd>Main page</kbd> de forma que todas as edicións dende o 1 se xaneiro de 2012 queden sen revisar.",
        "apihelp-setnotificationtimestamp-example-allpages": "Restaurar o estado de notificación para as páxinas no espazo de nomes de <kbd>{{ns:user}}</kbd>.",
+       "apihelp-stashedit-param-text": "Contido da páxina.",
+       "apihelp-stashedit-param-contentmodel": "Modelo de contido para o novo contido.",
+       "apihelp-stashedit-param-contentformat": "Formato de serialización de contido utilizado para o texto de entrada.",
+       "apihelp-stashedit-param-baserevid": "Identificador da revisión da revisión de base.",
        "apihelp-tag-description": "Engadir ou eliminar etiquetas de cambio de revisións individuais ou entradas de rexistro.",
        "apihelp-tag-param-rcid": "Identificadores de un ou máis cambios recentes nos que engadir ou eliminar a etiqueta.",
        "apihelp-tag-param-revid": "Identificadores de unha ou máis revisións nas que engadir ou eliminar a etiqueta.",
index 90be9e2..f4c3c56 100644 (file)
        "api-help-parameters": "{{PLURAL:$1|Parametro|Parametri}}:",
        "api-help-param-deprecated": "Deprecato.",
        "api-help-param-required": "Questo parametro è obbligatorio.",
+       "api-help-param-list": "{{PLURAL:$1|1=Uno dei seguenti valori|2=Valori (separati da <kbd>{{!}}</kbd>)}}: $2",
        "api-help-param-multi-max": "Il numero massimo di valori è {{PLURAL:$1|$1}} ({{PLURAL:$2|$2}} per i bot).",
        "api-help-param-default": "Predefinito: $1",
        "api-help-param-default-empty": "Predefinito: <span class=\"apihelp-empty\">(vuoto)</span>",
index ab018c9..8750b8f 100644 (file)
@@ -75,6 +75,7 @@
        "apihelp-options-param-reset": "사이트 기본으로 설정 초기화",
        "apihelp-options-example-reset": "모든 설정 초기화",
        "apihelp-protect-example-protect": "문서 보호",
+       "apihelp-query+allmessages-example-ipb": "<kbd>ipb-</kbd>로 시작하는 메시지를 보입니다.",
        "apihelp-query+pageswithprop-param-limit": "나타낼 문서의 최대 수입니다.",
        "apihelp-query+pageswithprop-param-dir": "정렬 순서",
        "apihelp-query+prefixsearch-param-search": "문자열 검색",
diff --git a/includes/api/i18n/lt.json b/includes/api/i18n/lt.json
new file mode 100644 (file)
index 0000000..e5edf2a
--- /dev/null
@@ -0,0 +1,10 @@
+{
+       "@metadata": {
+               "authors": [
+                       "Zygimantus"
+               ]
+       },
+       "apihelp-stashedit-param-title": "Puslapio pavadinimas buvo redaguotas.",
+       "apihelp-stashedit-param-sectiontitle": "Naujo skyriaus pavadinimas.",
+       "apihelp-stashedit-param-text": "Puslapio turinys."
+}
index a64e8e2..6d88ca5 100644 (file)
        "apihelp-query+alldeletedrevisions-param-user": "Pokazuj tylko zmiany dokonane przez tego użytkownika.",
        "apihelp-query+alldeletedrevisions-param-excludeuser": "Nie pokazuj zmian dokonanych przez tego użytkownika.",
        "apihelp-query+alldeletedrevisions-param-namespace": "Listuj tylko strony z tej przestrzeni nazw.",
-       "apihelp-query+allfileusages-param-limit": "Łączna ilość obiektów do zwrócenia.",
+       "apihelp-query+allfileusages-param-limit": "Łączna liczba obiektów do zwrócenia.",
        "apihelp-query+allfileusages-example-unique": "Lista unikatowych tytułów plików.",
        "apihelp-query+allimages-param-sort": "Sortowanie według właściwości.",
        "apihelp-query+allimages-example-recent": "Pokaż listę ostatnio przesłanych plików, podobnie do [[Special:NewFiles]].",
        "apihelp-query+allimages-example-mimetypes": "Pokaż listę plików z typem MIME <kbd>image/png</kbd> lub <kbd>image/gif</kbd>",
        "apihelp-query+alllinks-param-namespace": "Przestrzeń nazw do emulacji.",
-       "apihelp-query+alllinks-param-limit": "Łączna ilość obiektów do zwrócenia.",
+       "apihelp-query+alllinks-param-limit": "Łączna liczba obiektów do zwrócenia.",
        "apihelp-query+alllinks-example-unique": "Lista unikatowych tytułów plików.",
        "apihelp-query+allmessages-param-prop": "Właściwości do odczytu.",
        "apihelp-query+allmessages-param-prefix": "Zwróć wiadomości z tym prefixem.",
        "apihelp-query+allpages-param-prtype": "Ogranicz tylko do zabezpieczonych stron.",
-       "apihelp-query+allpages-param-limit": "Ilość stron do zwrócenia.",
+       "apihelp-query+allpages-param-limit": "Liczba stron do zwrócenia.",
        "apihelp-query+allpages-example-B": "Pokaż listę stron rozpoczynających się na literę <kbd>B</kbd>.",
        "apihelp-query+allredirects-description": "Lista wszystkich przekierowań do przestrzeni nazw.",
        "apihelp-query+allredirects-param-namespace": "Przestrzeń nazw do emulacji.",
-       "apihelp-query+allredirects-param-limit": "Łączna ilość obiektów do zwrócenia.",
+       "apihelp-query+allredirects-param-limit": "Łączna liczba obiektów do zwrócenia.",
        "apihelp-query+alltransclusions-param-namespace": "Przestrzeń nazw do emulacji.",
        "apihelp-query+allusers-param-witheditsonly": "Tylko użytkownicy, którzy edytowali.",
        "apihelp-query+backlinks-param-namespace": "Przestrzeń nazw do emulacji.",
        "apihelp-query+blocks-param-users": "Lista użytkowników do wyszukania (opcjonalne).",
        "apihelp-query+blocks-param-limit": "Maksymalna liczba blokad do wylistowania.",
        "apihelp-query+blocks-example-simple": "Listuj blokady.",
-       "apihelp-query+categories-param-limit": "Ilość kategorii do zwrócenia.",
+       "apihelp-query+categories-param-limit": "Liczba kategorii do zwrócenia.",
        "apihelp-query+categorymembers-description": "Wszystkie strony w danej kategorii.",
        "apihelp-query+categorymembers-param-limit": "Maksymalna liczba zwracanych wyników.",
        "apihelp-query+categorymembers-param-sort": "Sortowanie według właściwości.",
        "apihelp-query+deletedrevs-param-user": "Listuj tylko zmiany dokonane przez tego użytkownika.",
        "apihelp-query+deletedrevs-param-excludeuser": "Nie listuj zmian dokonanych przez tego użytkownika.",
        "apihelp-query+deletedrevs-param-namespace": "Listuj tylko strony z tej przestrzeni nazw.",
-       "apihelp-query+deletedrevs-param-limit": "Maksymalna ilość zmian do wylistowania.",
+       "apihelp-query+deletedrevs-param-limit": "Maksymalna liczba zmian do wylistowania.",
        "apihelp-query+duplicatefiles-example-generated": "Szukaj duplikatów wszystkich plików.",
        "apihelp-query+embeddedin-param-filterredir": "Jaki filtrować przekierowania.",
-       "apihelp-query+extlinks-param-limit": "Ilość linków do zwrócenia.",
-       "apihelp-query+exturlusage-param-limit": "Ilość stron do zwrócenia.",
+       "apihelp-query+extlinks-param-limit": "Liczba linków do zwrócenia.",
+       "apihelp-query+exturlusage-param-limit": "Liczba stron do zwrócenia.",
        "apihelp-query+filearchive-paramvalue-prop-dimensions": "Alias rozmiaru.",
        "apihelp-query+filearchive-example-simple": "Pokaż listę wszystkich usuniętych plików.",
        "apihelp-query+filerepoinfo-example-simple": "Uzyskaj informacje na temat repozytoriów plików.",
        "apihelp-query+imageinfo-paramvalue-prop-sha1": "Dodaj sumę kontrolną SHA-1 dla tego pliku.",
        "apihelp-query+imageinfo-paramvalue-prop-mime": "Dodaje typ MIME pliku.",
        "apihelp-query+imageinfo-param-urlheight": "Podobne do $1urlwidth.",
-       "apihelp-query+images-param-limit": "Ilość plików do zwrócenia.",
+       "apihelp-query+images-param-limit": "Liczba plików do zwrócenia.",
        "apihelp-query+info-description": "Pokaż podstawowe informacje o stronie.",
        "apihelp-query+info-paramvalue-prop-watchers": "Liczba obserwujących, jeśli jest to dozwolone.",
        "apihelp-query+info-paramvalue-prop-readable": "Czy użytkownik może przeczytać tę stronę.",
        "apihelp-query+iwbacklinks-param-prefix": "Prefix interwiki.",
-       "apihelp-query+iwbacklinks-param-limit": "Łączna ilość stron do zwrócenia.",
+       "apihelp-query+iwbacklinks-param-limit": "Łączna liczba stron do zwrócenia.",
        "apihelp-query+iwlinks-paramvalue-prop-url": "Dodaje pełny adres URL.",
-       "apihelp-query+links-param-limit": "Ilość linków do zwrócenia.",
+       "apihelp-query+links-param-limit": "Liczba linków do zwrócenia.",
        "apihelp-query+linkshere-paramvalue-prop-title": "Nazwa każdej strony.",
-       "apihelp-query+linkshere-param-limit": "Ilość do zwrócenia.",
+       "apihelp-query+linkshere-param-limit": "Liczba do zwrócenia.",
        "apihelp-query+logevents-description": "Pobierz eventy z logu.",
        "apihelp-query+logevents-example-simple": "Lista ostatnich zarejestrowanych zdarzeń.",
        "apihelp-query+pageswithprop-example-generator": "Pobierz dodatkowe informacje o pierwszych 10 stronach wykorzystując <code>_&#95;NOTOC_&#95;</code>.",
index 56b3f5b..9acb1b5 100644 (file)
@@ -9,7 +9,8 @@
                        "Дмитрий",
                        "WindEwriX",
                        "Ochilov",
-                       "Nzeemin"
+                       "Nzeemin",
+                       "INS Pirat"
                ]
        },
        "apihelp-main-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:API:Main_page|Документация]]\n* [[mw:API:FAQ|ЧаВО]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api Почтовая рассылка]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce Новости API]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Ошибки и запросы]\n</div>\n<strong>Статус:</strong> Все отображаемые на этой странице функции должны работать, однако API находится в статусе активной разработки, и может измениться в любой момент. Подпишитесь на  [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ почтовую рассылку mediawiki-api-announce], чтобы быть в курсе обновлений.\n\n<strong>Ошибочные запросы:</strong> Если API получает запрос с ошибкой, вернётся заголовок HTTP с ключом \"MediaWiki-API-Error\", после чего значение заголовка и код ошибки будут отправлены обратно и установлены в то же значение. Более подробную информацию см. [[mw:API:Errors_and_warnings|API: Ошибки и предупреждения]].",
@@ -27,6 +28,9 @@
        "apihelp-block-param-reason": "Причина блокировки.",
        "apihelp-block-param-anononly": "Блокировать только анонимных пользователей (т. е. запретить анонимные правки для этого IP-адреса).",
        "apihelp-block-param-nocreate": "Запретить создание учётных записей.",
+       "apihelp-block-param-autoblock": "Автоматически блокировать последний использованный IP-адрес и все последующие, с которых будут совершаться попытки авторизации.",
+       "apihelp-block-param-hidename": "Скрыть имя участника из журнала блокировок. (Требуется право <code>hideuser</code>).",
+       "apihelp-block-param-reblock": "Если участник уже заблокирован, перезаписать существующую блокировку.",
        "apihelp-block-param-watchuser": "Следить за страницей пользователя или IP-участника и страницей обсуждения.",
        "apihelp-checktoken-param-type": "Тип маркера проходит тестирование.",
        "apihelp-checktoken-param-token": "токен для проверки",
        "apihelp-opensearch-param-search": "Строка поиска.",
        "apihelp-opensearch-param-limit": "Максимальное число возвращаемых результатов.",
        "apihelp-opensearch-param-namespace": "Пространства имён для поиска.",
+       "apihelp-opensearch-param-format": "Формат вывода.",
        "apihelp-options-example-reset": "Сбросить все настройки.",
+       "apihelp-paraminfo-description": "Получить информацию о модулях API.",
        "apihelp-paraminfo-param-helpformat": "Формат строк справки.",
        "apihelp-parse-example-page": "анализ страницы",
        "apihelp-parse-example-text": "Анализ wikitext.",
+       "apihelp-protect-description": "Изменить уровень защиты страницы.",
        "apihelp-protect-example-protect": "Защитить страницу.",
        "apihelp-purge-param-forcelinkupdate": "Обновление связей таблиц.",
        "apihelp-query-param-list": "Какие списки использовать",
        "apihelp-query+transcludedin-param-limit": "Сколько возвращать",
        "apihelp-query+usercontribs-description": "Получить все правки пользователя",
        "apihelp-revisiondelete-description": "удалить и восстановить редакции",
+       "apihelp-stashedit-param-sectiontitle": "Заголовок нового раздела.",
        "apihelp-unblock-description": "Разблокировать пользователя.",
        "apihelp-unblock-param-reason": "Причина разблокировки",
        "apihelp-unblock-example-id": "Разблокировать блок с идентификатором #<kbd>105</kbd>.",
        "api-help-license-unknown": "Лицензия: <span class=\"apihelp-unknown\">unknown</span>",
        "api-help-parameters": "Параметр{{PLURAL:$1||ы}}:",
        "api-help-param-deprecated": "Устаревший.",
-       "api-help-param-required": "Этот параметр является обязательным.",
+       "api-help-param-required": "Это обязательный параметр.",
        "api-help-datatypes-header": "Типы данных",
        "api-help-param-type-limit": "Тип: целое число или <kbd>max</kbd>",
        "api-help-param-type-integer": "Тип: {{PLURAL:$1|1=integer|2=list of integers}}",
index 34d55af..ed3d9e1 100644 (file)
@@ -1,12 +1,13 @@
 {
        "@metadata": {
                "authors": [
-                       "Ammartivari"
+                       "Ammartivari",
+                       "Kosovastar"
                ]
        },
        "apihelp-block-param-reason": "Arsyeja për bllokim.",
        "apihelp-move-param-reason": "Arsyeja për riemërtim.",
        "apihelp-tag-param-reason": "Arsyeja për ndërrimin.",
        "apihelp-unblock-description": "Zhblloko një përdorues.",
-       "apihelp-userrights-description": "Ndërro anëtarësinë e grupit e një përdoruesit."
+       "apihelp-userrights-description": "Ndërro anëtarësinë e grupit  një përdoruesit."
 }
index dd3fb8f..2a5f805 100644 (file)
        "apihelp-setnotificationtimestamp-example-page": "重置用于<kbd>Main page</kbd>的通知状态。",
        "apihelp-setnotificationtimestamp-example-pagetimestamp": "设置<kbd>Main page</kbd>的通知时间戳,这样所有从2012年1月1日起的编辑都会是未复核的。",
        "apihelp-setnotificationtimestamp-example-allpages": "重置在<kbd>{{ns:user}}</kbd>名字空间中的页面的通知状态。",
+       "apihelp-stashedit-param-title": "已开始编辑的页面标题。",
+       "apihelp-stashedit-param-text": "页面内容。",
+       "apihelp-stashedit-param-contentmodel": "新内容的内容模型。",
        "apihelp-tag-description": "从个别修订或日志记录中添加或移除更改标签。",
        "apihelp-tag-param-rcid": "要添加或移除标签的一个或更多的最近更改ID。",
        "apihelp-tag-param-revid": "要添加或移除标签的一个或更多的修订ID。",
index 56c9256..3db84a6 100644 (file)
  * @ingroup Cache
  */
 class LinkCache {
-       // Increment $mClassVer whenever old serialized versions of this class
-       // becomes incompatible with the new version.
-       private $mClassVer = 5;
-
        /**
         * @var MapCacheLRU
         */
index 276e84a..f5b2350 100644 (file)
@@ -23,6 +23,7 @@
 use Cdb\Exception as CdbException;
 use Cdb\Reader as CdbReader;
 use Cdb\Writer as CdbWriter;
+use CLDRPluralRuleParser\Evaluator;
 
 /**
  * Class for caching the contents of localisation files, Messages*.php
@@ -576,7 +577,7 @@ class LocalisationCache {
                        return null;
                }
                try {
-                       $compiledRules = CLDRPluralRuleEvaluator::compile( $rules );
+                       $compiledRules = Evaluator::compile( $rules );
                } catch ( CLDRPluralRuleError $e ) {
                        wfDebugLog( 'l10n', $e->getMessage() );
 
index d912e3a..1dcb7ae 100644 (file)
@@ -283,6 +283,10 @@ class EnhancedChangesList extends ChangesList {
                // Further down are some assumptions that $block is a 0-indexed array
                // with (count-1) as last key. Let's make sure it is.
                $block = array_values( $block );
+               if ( empty( $block ) ) {
+                       // if we can't show anything, don't display this block altogether
+                       return '';
+               }
 
                $r .= $this->getLogText( $block, $queryParams, $allLogs, $isnew, $namehidden );
 
@@ -453,6 +457,10 @@ class EnhancedChangesList extends ChangesList {
         * @return string
         */
        protected function getLogText( $block, $queryParams, $allLogs, $isnew, $namehidden ) {
+               if ( empty( $block ) ) {
+                       return '';
+               }
+
                # Changes message
                static $nchanges = array();
                static $sinceLastVisitMsg = array();
index ec0573e..8a88fab 100644 (file)
@@ -442,7 +442,9 @@ class RedisConnRef {
         * @param Redis $conn
         * @param LoggerInterface $logger
         */
-       public function __construct( RedisConnectionPool $pool, $server, Redis $conn, LoggerInterface $logger ) {
+       public function __construct(
+               RedisConnectionPool $pool, $server, Redis $conn, LoggerInterface $logger
+       ) {
                $this->pool = $pool;
                $this->server = $server;
                $this->conn = $conn;
index 93adde1..42a2aee 100644 (file)
@@ -159,7 +159,10 @@ class RequestContext implements IContextSource, MutableContext {
                if ( $this->title === null ) {
                        global $wgTitle; # fallback to $wg till we can improve this
                        $this->title = $wgTitle;
-                       wfDebugLog( 'GlobalTitleFail', __METHOD__ . ' called by ' . wfGetAllCallers( 5 ) . ' with no title set.' );
+                       wfDebugLog(
+                               'GlobalTitleFail',
+                               __METHOD__ . ' called by ' . wfGetAllCallers( 5 ) . ' with no title set.'
+                       );
                }
 
                return $this->title;
index be34242..160c354 100644 (file)
@@ -138,7 +138,7 @@ abstract class DatabaseMysqlBase extends DatabaseBase {
 
                if ( $set ) {
                        // Use doQuery() to avoid opening implicit transactions (DBO_TRX)
-                       $success = $this->doQuery( 'SET ' . implode( ', ', $set ), __METHOD__ );
+                       $success = $this->doQuery( 'SET ' . implode( ', ', $set ) );
                        if ( !$success ) {
                                wfLogDBError(
                                        'Error setting MySQL variables on server {db_server} (check $wgSQLMode)',
@@ -302,7 +302,7 @@ abstract class DatabaseMysqlBase extends DatabaseBase {
                // We are not checking for any errors here, since
                // these are no errors mysql_num_rows can cause.
                // See http://dev.mysql.com/doc/refman/5.0/en/mysql-fetch-row.html.
-               // See https://bugzilla.wikimedia.org/42430
+               // See https://phabricator.wikimedia.org/T44430
                return $n;
        }
 
index 656547b..7ece56d 100644 (file)
@@ -806,7 +806,7 @@ class DatabaseSqlite extends DatabaseBase {
                        // https://bugs.php.net/bug.php?id=62361
                        // There is an additional bug regarding sorting this data after insert
                        // on older versions of sqlite shipped with ubuntu 12.04
-                       // https://bugzilla.wikimedia.org/show_bug.cgi?id=72367
+                       // https://phabricator.wikimedia.org/T74367
                        wfDebugLog( __CLASS__, __FUNCTION__ . ': Quoting value containing null byte. For consistency all binary data should have been first processed with self::encodeBlob()' );
                        return "x'" . bin2hex( $s ) . "'";
                } else {
diff --git a/includes/db/LBFactory.php b/includes/db/LBFactory.php
deleted file mode 100644 (file)
index cf522b2..0000000
+++ /dev/null
@@ -1,387 +0,0 @@
-<?php
-/**
- * Generator of database load balancing objects.
- *
- * 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 Database
- */
-
-/**
- * An interface for generating database load balancers
- * @ingroup Database
- */
-abstract class LBFactory {
-       /** @var LBFactory */
-       private static $instance;
-
-       /**
-        * Disables all access to the load balancer, will cause all database access
-        * to throw a DBAccessError
-        */
-       public static function disableBackend() {
-               global $wgLBFactoryConf;
-               self::$instance = new LBFactoryFake( $wgLBFactoryConf );
-       }
-
-       /**
-        * Get an LBFactory instance
-        *
-        * @return LBFactory
-        */
-       public static function singleton() {
-               global $wgLBFactoryConf;
-
-               if ( is_null( self::$instance ) ) {
-                       $class = self::getLBFactoryClass( $wgLBFactoryConf );
-
-                       self::$instance = new $class( $wgLBFactoryConf );
-               }
-
-               return self::$instance;
-       }
-
-       /**
-        * Returns the LBFactory class to use and the load balancer configuration.
-        *
-        * @param array $config (e.g. $wgLBFactoryConf)
-        * @return string Class name
-        */
-       public static function getLBFactoryClass( array $config ) {
-               // For configuration backward compatibility after removing
-               // underscores from class names in MediaWiki 1.23.
-               $bcClasses = array(
-                       'LBFactory_Simple' => 'LBFactorySimple',
-                       'LBFactory_Single' => 'LBFactorySingle',
-                       'LBFactory_Multi' => 'LBFactoryMulti',
-                       'LBFactory_Fake' => 'LBFactoryFake',
-               );
-
-               $class = $config['class'];
-
-               if ( isset( $bcClasses[$class] ) ) {
-                       $class = $bcClasses[$class];
-                       wfDeprecated(
-                               '$wgLBFactoryConf must be updated. See RELEASE-NOTES for details',
-                               '1.23'
-                       );
-               }
-
-               return $class;
-       }
-
-       /**
-        * Shut down, close connections and destroy the cached instance.
-        */
-       public static function destroyInstance() {
-               if ( self::$instance ) {
-                       self::$instance->shutdown();
-                       self::$instance->forEachLBCallMethod( 'closeAll' );
-                       self::$instance = null;
-               }
-       }
-
-       /**
-        * Set the instance to be the given object
-        *
-        * @param LBFactory $instance
-        */
-       public static function setInstance( $instance ) {
-               self::destroyInstance();
-               self::$instance = $instance;
-       }
-
-       /**
-        * Construct a factory based on a configuration array (typically from $wgLBFactoryConf)
-        * @param array $conf
-        */
-       abstract public function __construct( array $conf );
-
-       /**
-        * Create a new load balancer object. The resulting object will be untracked,
-        * not chronology-protected, and the caller is responsible for cleaning it up.
-        *
-        * @param bool|string $wiki Wiki ID, or false for the current wiki
-        * @return LoadBalancer
-        */
-       abstract public function newMainLB( $wiki = false );
-
-       /**
-        * Get a cached (tracked) load balancer object.
-        *
-        * @param bool|string $wiki Wiki ID, or false for the current wiki
-        * @return LoadBalancer
-        */
-       abstract public function getMainLB( $wiki = false );
-
-       /**
-        * Create a new load balancer for external storage. The resulting object will be
-        * untracked, not chronology-protected, and the caller is responsible for
-        * cleaning it up.
-        *
-        * @param string $cluster External storage cluster, or false for core
-        * @param bool|string $wiki Wiki ID, or false for the current wiki
-        * @return LoadBalancer
-        */
-       abstract protected function newExternalLB( $cluster, $wiki = false );
-
-       /**
-        * Get a cached (tracked) load balancer for external storage
-        *
-        * @param string $cluster External storage cluster, or false for core
-        * @param bool|string $wiki Wiki ID, or false for the current wiki
-        * @return LoadBalancer
-        */
-       abstract public function &getExternalLB( $cluster, $wiki = false );
-
-       /**
-        * Execute a function for each tracked load balancer
-        * The callback is called with the load balancer as the first parameter,
-        * and $params passed as the subsequent parameters.
-        *
-        * @param callable $callback
-        * @param array $params
-        */
-       abstract public function forEachLB( $callback, array $params = array() );
-
-       /**
-        * Prepare all tracked load balancers for shutdown
-        * STUB
-        */
-       public function shutdown() {
-       }
-
-       /**
-        * Call a method of each tracked load balancer
-        *
-        * @param string $methodName
-        * @param array $args
-        */
-       private function forEachLBCallMethod( $methodName, array $args = array() ) {
-               $this->forEachLB( function ( LoadBalancer $loadBalancer, $methodName, array $args ) {
-                       call_user_func_array( array( $loadBalancer, $methodName ), $args );
-               }, array( $methodName, $args ) );
-       }
-
-       /**
-        * Commit on all connections. Done for two reasons:
-        * 1. To commit changes to the masters.
-        * 2. To release the snapshot on all connections, master and slave.
-        */
-       public function commitAll() {
-               $this->forEachLBCallMethod( 'commitAll' );
-       }
-
-       /**
-        * Commit changes on all master connections
-        */
-       public function commitMasterChanges() {
-               $this->forEachLBCallMethod( 'commitMasterChanges' );
-       }
-
-       /**
-        * Rollback changes on all master connections
-        * @since 1.23
-        */
-       public function rollbackMasterChanges() {
-               $this->forEachLBCallMethod( 'rollbackMasterChanges' );
-       }
-
-       /**
-        * Detemine if any master connection has pending changes.
-        * @since 1.23
-        * @return bool
-        */
-       public function hasMasterChanges() {
-               $ret = false;
-               $this->forEachLB( function ( LoadBalancer $lb ) use ( &$ret ) {
-                       $ret = $ret || $lb->hasMasterChanges();
-               } );
-               return $ret;
-       }
-}
-
-/**
- * A simple single-master LBFactory that gets its configuration from the b/c globals
- */
-class LBFactorySimple extends LBFactory {
-       /** @var LoadBalancer */
-       private $mainLB;
-
-       /** @var LoadBalancer[] */
-       private $extLBs = array();
-
-       /** @var ChronologyProtector */
-       private $chronProt;
-
-       public function __construct( array $conf ) {
-               $this->chronProt = new ChronologyProtector;
-       }
-
-       /**
-        * @param bool|string $wiki
-        * @return LoadBalancer
-        */
-       public function newMainLB( $wiki = false ) {
-               global $wgDBservers;
-               if ( $wgDBservers ) {
-                       $servers = $wgDBservers;
-               } else {
-                       global $wgDBserver, $wgDBuser, $wgDBpassword, $wgDBname, $wgDBtype, $wgDebugDumpSql;
-                       global $wgDBssl, $wgDBcompress;
-
-                       $flags = DBO_DEFAULT;
-                       if ( $wgDebugDumpSql ) {
-                               $flags |= DBO_DEBUG;
-                       }
-                       if ( $wgDBssl ) {
-                               $flags |= DBO_SSL;
-                       }
-                       if ( $wgDBcompress ) {
-                               $flags |= DBO_COMPRESS;
-                       }
-
-                       $servers = array( array(
-                               'host' => $wgDBserver,
-                               'user' => $wgDBuser,
-                               'password' => $wgDBpassword,
-                               'dbname' => $wgDBname,
-                               'type' => $wgDBtype,
-                               'load' => 1,
-                               'flags' => $flags
-                       ) );
-               }
-
-               return new LoadBalancer( array(
-                       'servers' => $servers,
-               ) );
-       }
-
-       /**
-        * @param bool|string $wiki
-        * @return LoadBalancer
-        */
-       public function getMainLB( $wiki = false ) {
-               if ( !isset( $this->mainLB ) ) {
-                       $this->mainLB = $this->newMainLB( $wiki );
-                       $this->mainLB->parentInfo( array( 'id' => 'main' ) );
-                       $this->chronProt->initLB( $this->mainLB );
-               }
-
-               return $this->mainLB;
-       }
-
-       /**
-        * @throws MWException
-        * @param string $cluster
-        * @param bool|string $wiki
-        * @return LoadBalancer
-        */
-       protected function newExternalLB( $cluster, $wiki = false ) {
-               global $wgExternalServers;
-               if ( !isset( $wgExternalServers[$cluster] ) ) {
-                       throw new MWException( __METHOD__ . ": Unknown cluster \"$cluster\"" );
-               }
-
-               return new LoadBalancer( array(
-                       'servers' => $wgExternalServers[$cluster]
-               ) );
-       }
-
-       /**
-        * @param string $cluster
-        * @param bool|string $wiki
-        * @return array
-        */
-       public function &getExternalLB( $cluster, $wiki = false ) {
-               if ( !isset( $this->extLBs[$cluster] ) ) {
-                       $this->extLBs[$cluster] = $this->newExternalLB( $cluster, $wiki );
-                       $this->extLBs[$cluster]->parentInfo( array( 'id' => "ext-$cluster" ) );
-                       $this->chronProt->initLB( $this->extLBs[$cluster] );
-               }
-
-               return $this->extLBs[$cluster];
-       }
-
-       /**
-        * Execute a function for each tracked load balancer
-        * The callback is called with the load balancer as the first parameter,
-        * and $params passed as the subsequent parameters.
-        *
-        * @param callable $callback
-        * @param array $params
-        */
-       public function forEachLB( $callback, array $params = array() ) {
-               if ( isset( $this->mainLB ) ) {
-                       call_user_func_array( $callback, array_merge( array( $this->mainLB ), $params ) );
-               }
-               foreach ( $this->extLBs as $lb ) {
-                       call_user_func_array( $callback, array_merge( array( $lb ), $params ) );
-               }
-       }
-
-       public function shutdown() {
-               if ( $this->mainLB ) {
-                       $this->chronProt->shutdownLB( $this->mainLB );
-               }
-               foreach ( $this->extLBs as $extLB ) {
-                       $this->chronProt->shutdownLB( $extLB );
-               }
-               $this->chronProt->shutdown();
-               $this->commitMasterChanges();
-       }
-}
-
-/**
- * LBFactory class that throws an error on any attempt to use it.
- * This will typically be done via wfGetDB().
- * Call LBFactory::disableBackend() to start using this, and
- * LBFactory::enableBackend() to return to normal behavior
- */
-class LBFactoryFake extends LBFactory {
-       public function __construct( array $conf ) {
-       }
-
-       public function newMainLB( $wiki = false ) {
-               throw new DBAccessError;
-       }
-
-       public function getMainLB( $wiki = false ) {
-               throw new DBAccessError;
-       }
-
-       protected function newExternalLB( $cluster, $wiki = false ) {
-               throw new DBAccessError;
-       }
-
-       public function &getExternalLB( $cluster, $wiki = false ) {
-               throw new DBAccessError;
-       }
-
-       public function forEachLB( $callback, array $params = array() ) {
-       }
-}
-
-/**
- * Exception class for attempted DB access
- */
-class DBAccessError extends MWException {
-       public function __construct() {
-               parent::__construct( "Mediawiki tried to access the database via wfGetDB(). " .
-                       "This is not allowed." );
-       }
-}
diff --git a/includes/db/LBFactoryMulti.php b/includes/db/LBFactoryMulti.php
deleted file mode 100644 (file)
index 92fbccd..0000000
+++ /dev/null
@@ -1,399 +0,0 @@
-<?php
-/**
- * Advanced generator of database load balancing objects for wiki farms.
- *
- * 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 Database
- */
-
-/**
- * A multi-wiki, multi-master factory for Wikimedia and similar installations.
- * Ignores the old configuration globals
- *
- * Configuration:
- *     sectionsByDB                A map of database names to section names.
- *
- *     sectionLoads                A 2-d map. For each section, gives a map of server names to
- *                                 load ratios. For example:
- *                                 array(
- *                                     'section1' => array(
- *                                         'db1' => 100,
- *                                         'db2' => 100
- *                                     )
- *                                 )
- *
- *     serverTemplate              A server info associative array as documented for $wgDBservers.
- *                                 The host, hostName and load entries will be overridden.
- *
- *     groupLoadsBySection         A 3-d map giving server load ratios for each section and group.
- *                                 For example:
- *                                 array(
- *                                     'section1' => array(
- *                                         'group1' => array(
- *                                             'db1' => 100,
- *                                             'db2' => 100
- *                                         )
- *                                     )
- *                                 )
- *
- *     groupLoadsByDB              A 3-d map giving server load ratios by DB name.
- *
- *     hostsByName                 A map of hostname to IP address.
- *
- *     externalLoads               A map of external storage cluster name to server load map.
- *
- *     externalTemplateOverrides   A set of server info keys overriding serverTemplate for external
- *                                 storage.
- *
- *     templateOverridesByServer   A 2-d map overriding serverTemplate and
- *                                 externalTemplateOverrides on a server-by-server basis. Applies
- *                                 to both core and external storage.
- *
- *     templateOverridesByCluster  A 2-d map overriding the server info by external storage cluster.
- *
- *     masterTemplateOverrides     An override array for all master servers.
- *
- *     readOnlyBySection           A map of section name to read-only message.
- *                                 Missing or false for read/write.
- *
- * @ingroup Database
- */
-class LBFactoryMulti extends LBFactory {
-       // Required settings
-
-       /** @var array A map of database names to section names */
-       private $sectionsByDB;
-
-       /**
-        * @var array A 2-d map. For each section, gives a map of server names to
-        * load ratios
-        */
-       private $sectionLoads;
-
-       /**
-        * @var array A server info associative array as documented for
-        * $wgDBservers. The host, hostName and load entries will be
-        * overridden
-        */
-       private $serverTemplate;
-
-       // Optional settings
-
-       /** @var array A 3-d map giving server load ratios for each section and group */
-       private $groupLoadsBySection = array();
-
-       /** @var array A 3-d map giving server load ratios by DB name */
-       private $groupLoadsByDB = array();
-
-       /** @var array A map of hostname to IP address */
-       private $hostsByName = array();
-
-       /** @var array A map of external storage cluster name to server load map */
-       private $externalLoads = array();
-
-       /**
-        * @var array A set of server info keys overriding serverTemplate for
-        * external storage
-        */
-       private $externalTemplateOverrides;
-
-       /**
-        * @var array A 2-d map overriding serverTemplate and
-        * externalTemplateOverrides on a server-by-server basis. Applies to both
-        * core and external storage
-        */
-       private $templateOverridesByServer;
-
-       /** @var array A 2-d map overriding the server info by external storage cluster */
-       private $templateOverridesByCluster;
-
-       /** @var array An override array for all master servers */
-       private $masterTemplateOverrides;
-
-       /**
-        * @var array|bool A map of section name to read-only message. Missing or
-        * false for read/write
-        */
-       private $readOnlyBySection = array();
-
-       // Other stuff
-
-       /** @var array Load balancer factory configuration */
-       private $conf;
-
-       /** @var LoadBalancer[] */
-       private $mainLBs = array();
-
-       /** @var LoadBalancer[] */
-       private $extLBs = array();
-
-       /** @var string */
-       private $lastWiki;
-
-       /** @var string */
-       private $lastSection;
-
-       /**
-        * @param array $conf
-        * @throws MWException
-        */
-       public function __construct( array $conf ) {
-               $this->chronProt = new ChronologyProtector;
-               $this->conf = $conf;
-               $required = array( 'sectionsByDB', 'sectionLoads', 'serverTemplate' );
-               $optional = array( 'groupLoadsBySection', 'groupLoadsByDB', 'hostsByName',
-                       'externalLoads', 'externalTemplateOverrides', 'templateOverridesByServer',
-                       'templateOverridesByCluster', 'masterTemplateOverrides',
-                       'readOnlyBySection' );
-
-               foreach ( $required as $key ) {
-                       if ( !isset( $conf[$key] ) ) {
-                               throw new MWException( __CLASS__ . ": $key is required in configuration" );
-                       }
-                       $this->$key = $conf[$key];
-               }
-
-               foreach ( $optional as $key ) {
-                       if ( isset( $conf[$key] ) ) {
-                               $this->$key = $conf[$key];
-                       }
-               }
-
-               // Check for read-only mode
-               $section = $this->getSectionForWiki();
-               if ( !empty( $this->readOnlyBySection[$section] ) ) {
-                       global $wgReadOnly;
-                       $wgReadOnly = $this->readOnlyBySection[$section];
-               }
-       }
-
-       /**
-        * @param bool|string $wiki
-        * @return string
-        */
-       private function getSectionForWiki( $wiki = false ) {
-               if ( $this->lastWiki === $wiki ) {
-                       return $this->lastSection;
-               }
-               list( $dbName, ) = $this->getDBNameAndPrefix( $wiki );
-               if ( isset( $this->sectionsByDB[$dbName] ) ) {
-                       $section = $this->sectionsByDB[$dbName];
-               } else {
-                       $section = 'DEFAULT';
-               }
-               $this->lastSection = $section;
-               $this->lastWiki = $wiki;
-
-               return $section;
-       }
-
-       /**
-        * @param bool|string $wiki
-        * @return LoadBalancer
-        */
-       public function newMainLB( $wiki = false ) {
-               list( $dbName, ) = $this->getDBNameAndPrefix( $wiki );
-               $section = $this->getSectionForWiki( $wiki );
-               $groupLoads = array();
-               if ( isset( $this->groupLoadsByDB[$dbName] ) ) {
-                       $groupLoads = $this->groupLoadsByDB[$dbName];
-               }
-
-               if ( isset( $this->groupLoadsBySection[$section] ) ) {
-                       $groupLoads = array_merge_recursive( $groupLoads, $this->groupLoadsBySection[$section] );
-               }
-
-               return $this->newLoadBalancer(
-                       $this->serverTemplate,
-                       $this->sectionLoads[$section],
-                       $groupLoads
-               );
-       }
-
-       /**
-        * @param bool|string $wiki
-        * @return LoadBalancer
-        */
-       public function getMainLB( $wiki = false ) {
-               $section = $this->getSectionForWiki( $wiki );
-               if ( !isset( $this->mainLBs[$section] ) ) {
-                       $lb = $this->newMainLB( $wiki );
-                       $lb->parentInfo( array( 'id' => "main-$section" ) );
-                       $this->chronProt->initLB( $lb );
-                       $this->mainLBs[$section] = $lb;
-               }
-
-               return $this->mainLBs[$section];
-       }
-
-       /**
-        * @param string $cluster
-        * @param bool|string $wiki
-        * @throws MWException
-        * @return LoadBalancer
-        */
-       protected function newExternalLB( $cluster, $wiki = false ) {
-               if ( !isset( $this->externalLoads[$cluster] ) ) {
-                       throw new MWException( __METHOD__ . ": Unknown cluster \"$cluster\"" );
-               }
-               $template = $this->serverTemplate;
-               if ( isset( $this->externalTemplateOverrides ) ) {
-                       $template = $this->externalTemplateOverrides + $template;
-               }
-               if ( isset( $this->templateOverridesByCluster[$cluster] ) ) {
-                       $template = $this->templateOverridesByCluster[$cluster] + $template;
-               }
-
-               return $this->newLoadBalancer( $template, $this->externalLoads[$cluster], array() );
-       }
-
-       /**
-        * @param string $cluster External storage cluster, or false for core
-        * @param bool|string $wiki Wiki ID, or false for the current wiki
-        * @return LoadBalancer
-        */
-       public function &getExternalLB( $cluster, $wiki = false ) {
-               if ( !isset( $this->extLBs[$cluster] ) ) {
-                       $this->extLBs[$cluster] = $this->newExternalLB( $cluster, $wiki );
-                       $this->extLBs[$cluster]->parentInfo( array( 'id' => "ext-$cluster" ) );
-                       $this->chronProt->initLB( $this->extLBs[$cluster] );
-               }
-
-               return $this->extLBs[$cluster];
-       }
-
-       /**
-        * Make a new load balancer object based on template and load array
-        *
-        * @param array $template
-        * @param array $loads
-        * @param array $groupLoads
-        * @return LoadBalancer
-        */
-       private function newLoadBalancer( $template, $loads, $groupLoads ) {
-               $servers = $this->makeServerArray( $template, $loads, $groupLoads );
-               $lb = new LoadBalancer( array(
-                       'servers' => $servers,
-               ) );
-
-               return $lb;
-       }
-
-       /**
-        * Make a server array as expected by LoadBalancer::__construct, using a template and load array
-        *
-        * @param array $template
-        * @param array $loads
-        * @param array $groupLoads
-        * @return array
-        */
-       private function makeServerArray( $template, $loads, $groupLoads ) {
-               $servers = array();
-               $master = true;
-               $groupLoadsByServer = $this->reindexGroupLoads( $groupLoads );
-               foreach ( $groupLoadsByServer as $server => $stuff ) {
-                       if ( !isset( $loads[$server] ) ) {
-                               $loads[$server] = 0;
-                       }
-               }
-               foreach ( $loads as $serverName => $load ) {
-                       $serverInfo = $template;
-                       if ( $master ) {
-                               $serverInfo['master'] = true;
-                               if ( isset( $this->masterTemplateOverrides ) ) {
-                                       $serverInfo = $this->masterTemplateOverrides + $serverInfo;
-                               }
-                               $master = false;
-                       }
-                       if ( isset( $this->templateOverridesByServer[$serverName] ) ) {
-                               $serverInfo = $this->templateOverridesByServer[$serverName] + $serverInfo;
-                       }
-                       if ( isset( $groupLoadsByServer[$serverName] ) ) {
-                               $serverInfo['groupLoads'] = $groupLoadsByServer[$serverName];
-                       }
-                       if ( isset( $this->hostsByName[$serverName] ) ) {
-                               $serverInfo['host'] = $this->hostsByName[$serverName];
-                       } else {
-                               $serverInfo['host'] = $serverName;
-                       }
-                       $serverInfo['hostName'] = $serverName;
-                       $serverInfo['load'] = $load;
-                       $servers[] = $serverInfo;
-               }
-
-               return $servers;
-       }
-
-       /**
-        * Take a group load array indexed by group then server, and reindex it by server then group
-        * @param array $groupLoads
-        * @return array
-        */
-       private function reindexGroupLoads( $groupLoads ) {
-               $reindexed = array();
-               foreach ( $groupLoads as $group => $loads ) {
-                       foreach ( $loads as $server => $load ) {
-                               $reindexed[$server][$group] = $load;
-                       }
-               }
-
-               return $reindexed;
-       }
-
-       /**
-        * Get the database name and prefix based on the wiki ID
-        * @param bool|string $wiki
-        * @return array
-        */
-       private function getDBNameAndPrefix( $wiki = false ) {
-               if ( $wiki === false ) {
-                       global $wgDBname, $wgDBprefix;
-
-                       return array( $wgDBname, $wgDBprefix );
-               } else {
-                       return wfSplitWikiID( $wiki );
-               }
-       }
-
-       /**
-        * Execute a function for each tracked load balancer
-        * The callback is called with the load balancer as the first parameter,
-        * and $params passed as the subsequent parameters.
-        * @param callable $callback
-        * @param array $params
-        */
-       public function forEachLB( $callback, array $params = array() ) {
-               foreach ( $this->mainLBs as $lb ) {
-                       call_user_func_array( $callback, array_merge( array( $lb ), $params ) );
-               }
-               foreach ( $this->extLBs as $lb ) {
-                       call_user_func_array( $callback, array_merge( array( $lb ), $params ) );
-               }
-       }
-
-       public function shutdown() {
-               foreach ( $this->mainLBs as $lb ) {
-                       $this->chronProt->shutdownLB( $lb );
-               }
-               foreach ( $this->extLBs as $extLB ) {
-                       $this->chronProt->shutdownLB( $extLB );
-               }
-               $this->chronProt->shutdown();
-               $this->commitMasterChanges();
-       }
-}
diff --git a/includes/db/LBFactorySingle.php b/includes/db/LBFactorySingle.php
deleted file mode 100644 (file)
index a41dadf..0000000
+++ /dev/null
@@ -1,112 +0,0 @@
-<?php
-/**
- * Simple generator of database connections that always returns the same object.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- * @ingroup Database
- */
-
-/**
- * An LBFactory class that always returns a single database object.
- */
-class LBFactorySingle extends LBFactory {
-       /** @var LoadBalancerSingle */
-       private $lb;
-
-       /**
-        * @param array $conf An associative array with one member:
-        *  - connection: The DatabaseBase connection object
-        */
-       public function __construct( array $conf ) {
-               $this->lb = new LoadBalancerSingle( $conf );
-       }
-
-       /**
-        * @param bool|string $wiki
-        * @return LoadBalancerSingle
-        */
-       public function newMainLB( $wiki = false ) {
-               return $this->lb;
-       }
-
-       /**
-        * @param bool|string $wiki
-        * @return LoadBalancerSingle
-        */
-       public function getMainLB( $wiki = false ) {
-               return $this->lb;
-       }
-
-       /**
-        * @param string $cluster External storage cluster, or false for core
-        * @param bool|string $wiki Wiki ID, or false for the current wiki
-        * @return LoadBalancerSingle
-        */
-       protected function newExternalLB( $cluster, $wiki = false ) {
-               return $this->lb;
-       }
-
-       /**
-        * @param string $cluster External storage cluster, or false for core
-        * @param bool|string $wiki Wiki ID, or false for the current wiki
-        * @return LoadBalancerSingle
-        */
-       public function &getExternalLB( $cluster, $wiki = false ) {
-               return $this->lb;
-       }
-
-       /**
-        * @param string|callable $callback
-        * @param array $params
-        */
-       public function forEachLB( $callback, array $params = array() ) {
-               call_user_func_array( $callback, array_merge( array( $this->lb ), $params ) );
-       }
-}
-
-/**
- * Helper class for LBFactorySingle.
- */
-class LoadBalancerSingle extends LoadBalancer {
-       /** @var DatabaseBase */
-       private $db;
-
-       /**
-        * @param array $params
-        */
-       public function __construct( array $params ) {
-               $this->db = $params['connection'];
-               parent::__construct( array( 'servers' => array( array(
-                       'type' => $this->db->getType(),
-                       'host' => $this->db->getServer(),
-                       'dbname' => $this->db->getDBname(),
-                       'load' => 1,
-               ) ) ) );
-       }
-
-       /**
-        *
-        * @param string $server
-        * @param bool $dbNameOverride
-        *
-        * @return DatabaseBase
-        */
-       protected function reallyOpenConnection( $server, $dbNameOverride = false ) {
-               return $this->db;
-       }
-}
diff --git a/includes/db/LoadBalancer.php b/includes/db/LoadBalancer.php
deleted file mode 100644 (file)
index 52dca08..0000000
+++ /dev/null
@@ -1,1273 +0,0 @@
-<?php
-/**
- * Database load balancing.
- *
- * 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 Database
- */
-
-/**
- * Database load balancing object
- *
- * @todo document
- * @ingroup Database
- */
-class LoadBalancer {
-       /** @var array[] Map of (server index => server config array) */
-       private $mServers;
-       /** @var array[] Map of (local/foreignUsed/foreignFree => server index => DatabaseBase array) */
-       private $mConns;
-       /** @var array Map of (server index => weight) */
-       private $mLoads;
-       /** @var array[] Map of (group => server index => weight) */
-       private $mGroupLoads;
-       /** @var bool Whether to disregard slave lag as a factor in slave selection */
-       private $mAllowLagged;
-       /** @var integer Seconds to spend waiting on slave lag to resolve */
-       private $mWaitTimeout;
-
-       /** @var array LBFactory information */
-       private $mParentInfo;
-       /** @var string The LoadMonitor subclass name */
-       private $mLoadMonitorClass;
-       /** @var LoadMonitor */
-       private $mLoadMonitor;
-
-       /** @var bool|DatabaseBase Database connection that caused a problem */
-       private $mErrorConnection;
-       /** @var integer The generic (not query grouped) slave index (of $mServers) */
-       private $mReadIndex;
-       /** @var bool|DBMasterPos False if not set */
-       private $mWaitForPos;
-       /** @var bool Whether the generic reader fell back to a lagged slave */
-       private $mLaggedSlaveMode;
-       /** @var string The last DB selection or connection error */
-       private $mLastError = 'Unknown error';
-       /** @var integer Total connections opened */
-       private $connsOpened = 0;
-
-       /** @var integer Warn when this many connection are held */
-       const CONN_HELD_WARN_THRESHOLD = 10;
-
-       /**
-        * @param array $params Array with keys:
-        *   servers           Required. Array of server info structures.
-        *   loadMonitor       Name of a class used to fetch server lag and load.
-        * @throws MWException
-        */
-       public function __construct( array $params ) {
-               if ( !isset( $params['servers'] ) ) {
-                       throw new MWException( __CLASS__ . ': missing servers parameter' );
-               }
-               $this->mServers = $params['servers'];
-               $this->mWaitTimeout = 10;
-
-               $this->mReadIndex = -1;
-               $this->mWriteIndex = -1;
-               $this->mConns = array(
-                       'local' => array(),
-                       'foreignUsed' => array(),
-                       'foreignFree' => array() );
-               $this->mLoads = array();
-               $this->mWaitForPos = false;
-               $this->mLaggedSlaveMode = false;
-               $this->mErrorConnection = false;
-               $this->mAllowLagged = false;
-
-               if ( isset( $params['loadMonitor'] ) ) {
-                       $this->mLoadMonitorClass = $params['loadMonitor'];
-               } else {
-                       $master = reset( $params['servers'] );
-                       if ( isset( $master['type'] ) && $master['type'] === 'mysql' ) {
-                               $this->mLoadMonitorClass = 'LoadMonitorMySQL';
-                       } else {
-                               $this->mLoadMonitorClass = 'LoadMonitorNull';
-                       }
-               }
-
-               foreach ( $params['servers'] as $i => $server ) {
-                       $this->mLoads[$i] = $server['load'];
-                       if ( isset( $server['groupLoads'] ) ) {
-                               foreach ( $server['groupLoads'] as $group => $ratio ) {
-                                       if ( !isset( $this->mGroupLoads[$group] ) ) {
-                                               $this->mGroupLoads[$group] = array();
-                                       }
-                                       $this->mGroupLoads[$group][$i] = $ratio;
-                               }
-                       }
-               }
-       }
-
-       /**
-        * Get a LoadMonitor instance
-        *
-        * @return LoadMonitor
-        */
-       private function getLoadMonitor() {
-               if ( !isset( $this->mLoadMonitor ) ) {
-                       $class = $this->mLoadMonitorClass;
-                       $this->mLoadMonitor = new $class( $this );
-               }
-
-               return $this->mLoadMonitor;
-       }
-
-       /**
-        * Get or set arbitrary data used by the parent object, usually an LBFactory
-        * @param mixed $x
-        * @return mixed
-        */
-       public function parentInfo( $x = null ) {
-               return wfSetVar( $this->mParentInfo, $x );
-       }
-
-       /**
-        * Given an array of non-normalised probabilities, this function will select
-        * an element and return the appropriate key
-        *
-        * @deprecated since 1.21, use ArrayUtils::pickRandom()
-        *
-        * @param array $weights
-        * @return bool|int|string
-        */
-       public function pickRandom( array $weights ) {
-               return ArrayUtils::pickRandom( $weights );
-       }
-
-       /**
-        * @param array $loads
-        * @param bool|string $wiki Wiki to get non-lagged for
-        * @param float $maxLag Restrict the maximum allowed lag to this many seconds
-        * @return bool|int|string
-        */
-       private function getRandomNonLagged( array $loads, $wiki = false, $maxLag = INF ) {
-               $lags = $this->getLagTimes( $wiki );
-
-               # Unset excessively lagged servers
-               foreach ( $lags as $i => $lag ) {
-                       if ( $i != 0 ) {
-                               $maxServerLag = $maxLag;
-                               if ( isset( $this->mServers[$i]['max lag'] ) ) {
-                                       $maxServerLag = min( $maxServerLag, $this->mServers[$i]['max lag'] );
-                               }
-                               if ( $lag === false ) {
-                                       wfDebugLog( 'replication', "Server #$i is not replicating" );
-                                       unset( $loads[$i] );
-                               } elseif ( $lag > $maxServerLag ) {
-                                       wfDebugLog( 'replication', "Server #$i is excessively lagged ($lag seconds)" );
-                                       unset( $loads[$i] );
-                               }
-                       }
-               }
-
-               # Find out if all the slaves with non-zero load are lagged
-               $sum = 0;
-               foreach ( $loads as $load ) {
-                       $sum += $load;
-               }
-               if ( $sum == 0 ) {
-                       # No appropriate DB servers except maybe the master and some slaves with zero load
-                       # Do NOT use the master
-                       # Instead, this function will return false, triggering read-only mode,
-                       # and a lagged slave will be used instead.
-                       return false;
-               }
-
-               if ( count( $loads ) == 0 ) {
-                       return false;
-               }
-
-               #wfDebugLog( 'connect', var_export( $loads, true ) );
-
-               # Return a random representative of the remainder
-               return ArrayUtils::pickRandom( $loads );
-       }
-
-       /**
-        * Get the index of the reader connection, which may be a slave
-        * This takes into account load ratios and lag times. It should
-        * always return a consistent index during a given invocation
-        *
-        * Side effect: opens connections to databases
-        * @param string|bool $group Query group, or false for the generic reader
-        * @param string|bool $wiki Wiki ID, or false for the current wiki
-        * @throws MWException
-        * @return bool|int|string
-        */
-       public function getReaderIndex( $group = false, $wiki = false ) {
-               global $wgDBtype;
-
-               # @todo FIXME: For now, only go through all this for mysql databases
-               if ( $wgDBtype != 'mysql' ) {
-                       return $this->getWriterIndex();
-               }
-
-               if ( count( $this->mServers ) == 1 ) {
-                       # Skip the load balancing if there's only one server
-                       return 0;
-               } elseif ( $group === false && $this->mReadIndex >= 0 ) {
-                       # Shortcut if generic reader exists already
-                       return $this->mReadIndex;
-               }
-
-               # Find the relevant load array
-               if ( $group !== false ) {
-                       if ( isset( $this->mGroupLoads[$group] ) ) {
-                               $nonErrorLoads = $this->mGroupLoads[$group];
-                       } else {
-                               # No loads for this group, return false and the caller can use some other group
-                               wfDebug( __METHOD__ . ": no loads for group $group\n" );
-
-                               return false;
-                       }
-               } else {
-                       $nonErrorLoads = $this->mLoads;
-               }
-
-               if ( !count( $nonErrorLoads ) ) {
-                       throw new MWException( "Empty server array given to LoadBalancer" );
-               }
-
-               # Scale the configured load ratios according to the dynamic load (if the load monitor supports it)
-               $this->getLoadMonitor()->scaleLoads( $nonErrorLoads, $group, $wiki );
-
-               $laggedSlaveMode = false;
-
-               # No server found yet
-               $i = false;
-               # First try quickly looking through the available servers for a server that
-               # meets our criteria
-               $currentLoads = $nonErrorLoads;
-               while ( count( $currentLoads ) ) {
-                       if ( $this->mAllowLagged || $laggedSlaveMode ) {
-                               $i = ArrayUtils::pickRandom( $currentLoads );
-                       } else {
-                               $i = false;
-                               if ( $this->mWaitForPos && $this->mWaitForPos->asOfTime() ) {
-                                       # ChronologyProtecter causes mWaitForPos to be set via sessions.
-                                       # This triggers doWait() after connect, so it's especially good to
-                                       # avoid lagged servers so as to avoid just blocking in that method.
-                                       $ago = microtime( true ) - $this->mWaitForPos->asOfTime();
-                                       # Aim for <= 1 second of waiting (being too picky can backfire)
-                                       $i = $this->getRandomNonLagged( $currentLoads, $wiki, $ago + 1 );
-                               }
-                               if ( $i === false ) {
-                                       # Any server with less lag than it's 'max lag' param is preferable
-                                       $i = $this->getRandomNonLagged( $currentLoads, $wiki );
-                               }
-                               if ( $i === false && count( $currentLoads ) != 0 ) {
-                                       # All slaves lagged. Switch to read-only mode
-                                       wfDebugLog( 'replication', "All slaves lagged. Switch to read-only mode" );
-                                       $i = ArrayUtils::pickRandom( $currentLoads );
-                                       $laggedSlaveMode = true;
-                               }
-                       }
-
-                       if ( $i === false ) {
-                               # pickRandom() returned false
-                               # This is permanent and means the configuration or the load monitor
-                               # wants us to return false.
-                               wfDebugLog( 'connect', __METHOD__ . ": pickRandom() returned false" );
-
-                               return false;
-                       }
-
-                       $serverName = $this->getServerName( $i );
-                       wfDebugLog( 'connect', __METHOD__ . ": Using reader #$i: $serverName..." );
-
-                       $conn = $this->openConnection( $i, $wiki );
-                       if ( !$conn ) {
-                               wfDebugLog( 'connect', __METHOD__ . ": Failed connecting to $i/$wiki" );
-                               unset( $nonErrorLoads[$i] );
-                               unset( $currentLoads[$i] );
-                               $i = false;
-                               continue;
-                       }
-
-                       // Decrement reference counter, we are finished with this connection.
-                       // It will be incremented for the caller later.
-                       if ( $wiki !== false ) {
-                               $this->reuseConnection( $conn );
-                       }
-
-                       # Return this server
-                       break;
-               }
-
-               # If all servers were down, quit now
-               if ( !count( $nonErrorLoads ) ) {
-                       wfDebugLog( 'connect', "All servers down" );
-               }
-
-               if ( $i !== false ) {
-                       # Slave connection successful
-                       # Wait for the session master pos for a short time
-                       if ( $this->mWaitForPos && $i > 0 ) {
-                               if ( !$this->doWait( $i ) ) {
-                                       $this->mServers[$i]['slave pos'] = $conn->getSlavePos();
-                               }
-                       }
-                       if ( $this->mReadIndex <= 0 && $this->mLoads[$i] > 0 && $group === false ) {
-                               $this->mReadIndex = $i;
-                               # Record if the generic reader index is in "lagged slave" mode
-                               if ( $laggedSlaveMode ) {
-                                       $this->mLaggedSlaveMode = true;
-                               }
-                       }
-                       $serverName = $this->getServerName( $i );
-                       wfDebug( __METHOD__ . ": using server $serverName for group '$group'\n" );
-               }
-
-               return $i;
-       }
-
-       /**
-        * Set the master wait position
-        * If a DB_SLAVE connection has been opened already, waits
-        * Otherwise sets a variable telling it to wait if such a connection is opened
-        * @param DBMasterPos $pos
-        */
-       public function waitFor( $pos ) {
-               $this->mWaitForPos = $pos;
-               $i = $this->mReadIndex;
-
-               if ( $i > 0 ) {
-                       if ( !$this->doWait( $i ) ) {
-                               $this->mServers[$i]['slave pos'] = $this->getAnyOpenConnection( $i )->getSlavePos();
-                               $this->mLaggedSlaveMode = true;
-                       }
-               }
-       }
-
-       /**
-        * Set the master wait position and wait for a "generic" slave to catch up to it
-        *
-        * This can be used a faster proxy for waitForAll()
-        *
-        * @param DBMasterPos $pos
-        * @param int $timeout Max seconds to wait; default is mWaitTimeout
-        * @return bool Success (able to connect and no timeouts reached)
-        * @since 1.26
-        */
-       public function waitForOne( $pos, $timeout = null ) {
-               $this->mWaitForPos = $pos;
-
-               $i = $this->mReadIndex;
-               if ( $i <= 0 ) {
-                       // Pick a generic slave if there isn't one yet
-                       $readLoads = $this->mLoads;
-                       unset( $readLoads[$this->getWriterIndex()] ); // slaves only
-                       $readLoads = array_filter( $readLoads ); // with non-zero load
-                       $i = ArrayUtils::pickRandom( $readLoads );
-               }
-
-               if ( $i > 0 ) {
-                       $ok = $this->doWait( $i, true, $timeout );
-               } else {
-                       $ok = true; // no applicable loads
-               }
-
-               return $ok;
-       }
-
-       /**
-        * Set the master wait position and wait for ALL slaves to catch up to it
-        * @param DBMasterPos $pos
-        * @param int $timeout Max seconds to wait; default is mWaitTimeout
-        * @return bool Success (able to connect and no timeouts reached)
-        */
-       public function waitForAll( $pos, $timeout = null ) {
-               $this->mWaitForPos = $pos;
-               $serverCount = count( $this->mServers );
-
-               $ok = true;
-               for ( $i = 1; $i < $serverCount; $i++ ) {
-                       if ( $this->mLoads[$i] > 0 ) {
-                               $ok = $this->doWait( $i, true, $timeout ) && $ok;
-                       }
-               }
-
-               return $ok;
-       }
-
-       /**
-        * Get any open connection to a given server index, local or foreign
-        * Returns false if there is no connection open
-        *
-        * @param int $i
-        * @return DatabaseBase|bool False on failure
-        */
-       public function getAnyOpenConnection( $i ) {
-               foreach ( $this->mConns as $conns ) {
-                       if ( !empty( $conns[$i] ) ) {
-                               return reset( $conns[$i] );
-                       }
-               }
-
-               return false;
-       }
-
-       /**
-        * Wait for a given slave to catch up to the master pos stored in $this
-        * @param int $index Server index
-        * @param bool $open Check the server even if a new connection has to be made
-        * @param int $timeout Max seconds to wait; default is mWaitTimeout
-        * @return bool
-        */
-       protected function doWait( $index, $open = false, $timeout = null ) {
-               $close = false; // close the connection afterwards
-
-               # Find a connection to wait on, creating one if needed and allowed
-               $conn = $this->getAnyOpenConnection( $index );
-               if ( !$conn ) {
-                       if ( !$open ) {
-                               wfDebug( __METHOD__ . ": no connection open\n" );
-
-                               return false;
-                       } else {
-                               $conn = $this->openConnection( $index, '' );
-                               if ( !$conn ) {
-                                       wfDebug( __METHOD__ . ": failed to open connection\n" );
-
-                                       return false;
-                               }
-                               // Avoid connection spam in waitForAll() when connections
-                               // are made just for the sake of doing this lag check.
-                               $close = true;
-                       }
-               }
-
-               wfDebug( __METHOD__ . ": Waiting for slave #$index to catch up...\n" );
-               $timeout = $timeout ?: $this->mWaitTimeout;
-               $result = $conn->masterPosWait( $this->mWaitForPos, $timeout );
-
-               if ( $result == -1 || is_null( $result ) ) {
-                       # Timed out waiting for slave, use master instead
-                       $server = $server = $this->getServerName( $index );
-                       $msg = __METHOD__ . ": Timed out waiting on $server pos {$this->mWaitForPos}";
-                       wfDebug( "$msg\n" );
-                       wfDebugLog( 'DBPerformance', "$msg:\n" . wfBacktrace( true ) );
-                       $ok = false;
-               } else {
-                       wfDebug( __METHOD__ . ": Done\n" );
-                       $ok = true;
-               }
-
-               if ( $close ) {
-                       $this->closeConnection( $conn );
-               }
-
-               return $ok;
-       }
-
-       /**
-        * Get a connection by index
-        * This is the main entry point for this class.
-        *
-        * @param int $i Server index
-        * @param array|string|bool $groups Query group(s), or false for the generic reader
-        * @param string|bool $wiki Wiki ID, or false for the current wiki
-        *
-        * @throws MWException
-        * @return DatabaseBase
-        */
-       public function getConnection( $i, $groups = array(), $wiki = false ) {
-               if ( $i === null || $i === false ) {
-                       throw new MWException( 'Attempt to call ' . __METHOD__ .
-                               ' with invalid server index' );
-               }
-
-               if ( $wiki === wfWikiID() ) {
-                       $wiki = false;
-               }
-
-               $groups = ( $groups === false || $groups === array() )
-                       ? array( false ) // check one "group": the generic pool
-                       : (array)$groups;
-
-               $masterOnly = ( $i == DB_MASTER || $i == $this->getWriterIndex() );
-               $oldConnsOpened = $this->connsOpened; // connections open now
-
-               if ( $i == DB_MASTER ) {
-                       $i = $this->getWriterIndex();
-               } else {
-                       # Try to find an available server in any the query groups (in order)
-                       foreach ( $groups as $group ) {
-                               $groupIndex = $this->getReaderIndex( $group, $wiki );
-                               if ( $groupIndex !== false ) {
-                                       $i = $groupIndex;
-                                       break;
-                               }
-                       }
-               }
-
-               # Operation-based index
-               if ( $i == DB_SLAVE ) {
-                       $this->mLastError = 'Unknown error'; // reset error string
-                       # Try the general server pool if $groups are unavailable.
-                       $i = in_array( false, $groups, true )
-                               ? false // don't bother with this if that is what was tried above
-                               : $this->getReaderIndex( false, $wiki );
-                       # Couldn't find a working server in getReaderIndex()?
-                       if ( $i === false ) {
-                               $this->mLastError = 'No working slave server: ' . $this->mLastError;
-
-                               return $this->reportConnectionError();
-                       }
-               }
-
-               # Now we have an explicit index into the servers array
-               $conn = $this->openConnection( $i, $wiki );
-               if ( !$conn ) {
-                       return $this->reportConnectionError();
-               }
-
-               # Profile any new connections that happen
-               if ( $this->connsOpened > $oldConnsOpened ) {
-                       $host = $conn->getServer();
-                       $dbname = $conn->getDBname();
-                       $trxProf = Profiler::instance()->getTransactionProfiler();
-                       $trxProf->recordConnection( $host, $dbname, $masterOnly );
-               }
-
-               return $conn;
-       }
-
-       /**
-        * Mark a foreign connection as being available for reuse under a different
-        * DB name or prefix. This mechanism is reference-counted, and must be called
-        * the same number of times as getConnection() to work.
-        *
-        * @param DatabaseBase $conn
-        * @throws MWException
-        */
-       public function reuseConnection( $conn ) {
-               $serverIndex = $conn->getLBInfo( 'serverIndex' );
-               $refCount = $conn->getLBInfo( 'foreignPoolRefCount' );
-               if ( $serverIndex === null || $refCount === null ) {
-                       wfDebug( __METHOD__ . ": this connection was not opened as a foreign connection\n" );
-
-                       /**
-                        * This can happen in code like:
-                        *   foreach ( $dbs as $db ) {
-                        *     $conn = $lb->getConnection( DB_SLAVE, array(), $db );
-                        *     ...
-                        *     $lb->reuseConnection( $conn );
-                        *   }
-                        * When a connection to the local DB is opened in this way, reuseConnection()
-                        * should be ignored
-                        */
-
-                       return;
-               }
-
-               $dbName = $conn->getDBname();
-               $prefix = $conn->tablePrefix();
-               if ( strval( $prefix ) !== '' ) {
-                       $wiki = "$dbName-$prefix";
-               } else {
-                       $wiki = $dbName;
-               }
-               if ( $this->mConns['foreignUsed'][$serverIndex][$wiki] !== $conn ) {
-                       throw new MWException( __METHOD__ . ": connection not found, has " .
-                               "the connection been freed already?" );
-               }
-               $conn->setLBInfo( 'foreignPoolRefCount', --$refCount );
-               if ( $refCount <= 0 ) {
-                       $this->mConns['foreignFree'][$serverIndex][$wiki] = $conn;
-                       unset( $this->mConns['foreignUsed'][$serverIndex][$wiki] );
-                       wfDebug( __METHOD__ . ": freed connection $serverIndex/$wiki\n" );
-               } else {
-                       wfDebug( __METHOD__ . ": reference count for $serverIndex/$wiki reduced to $refCount\n" );
-               }
-       }
-
-       /**
-        * Get a database connection handle reference
-        *
-        * The handle's methods wrap simply wrap those of a DatabaseBase handle
-        *
-        * @see LoadBalancer::getConnection() for parameter information
-        *
-        * @param int $db
-        * @param array|string|bool $groups Query group(s), or false for the generic reader
-        * @param string|bool $wiki Wiki ID, or false for the current wiki
-        * @return DBConnRef
-        */
-       public function getConnectionRef( $db, $groups = array(), $wiki = false ) {
-               return new DBConnRef( $this, $this->getConnection( $db, $groups, $wiki ) );
-       }
-
-       /**
-        * Get a database connection handle reference without connecting yet
-        *
-        * The handle's methods wrap simply wrap those of a DatabaseBase handle
-        *
-        * @see LoadBalancer::getConnection() for parameter information
-        *
-        * @param int $db
-        * @param array|string|bool $groups Query group(s), or false for the generic reader
-        * @param string|bool $wiki Wiki ID, or false for the current wiki
-        * @return DBConnRef
-        */
-       public function getLazyConnectionRef( $db, $groups = array(), $wiki = false ) {
-               return new DBConnRef( $this, array( $db, $groups, $wiki ) );
-       }
-
-       /**
-        * Open a connection to the server given by the specified index
-        * Index must be an actual index into the array.
-        * If the server is already open, returns it.
-        *
-        * On error, returns false, and the connection which caused the
-        * error will be available via $this->mErrorConnection.
-        *
-        * @param int $i Server index
-        * @param string|bool $wiki Wiki ID, or false for the current wiki
-        * @return DatabaseBase
-        *
-        * @access private
-        */
-       public function openConnection( $i, $wiki = false ) {
-               if ( $wiki !== false ) {
-                       $conn = $this->openForeignConnection( $i, $wiki );
-               } elseif ( isset( $this->mConns['local'][$i][0] ) ) {
-                       $conn = $this->mConns['local'][$i][0];
-               } else {
-                       $server = $this->mServers[$i];
-                       $server['serverIndex'] = $i;
-                       $conn = $this->reallyOpenConnection( $server, false );
-                       $serverName = $this->getServerName( $i );
-                       if ( $conn->isOpen() ) {
-                               wfDebug( "Connected to database $i at $serverName\n" );
-                               $this->mConns['local'][$i][0] = $conn;
-                       } else {
-                               wfDebug( "Failed to connect to database $i at $serverName\n" );
-                               $this->mErrorConnection = $conn;
-                               $conn = false;
-                       }
-               }
-
-               if ( $conn && !$conn->isOpen() ) {
-                       // Connection was made but later unrecoverably lost for some reason.
-                       // Do not return a handle that will just throw exceptions on use,
-                       // but let the calling code (e.g. getReaderIndex) try another server.
-                       // See DatabaseMyslBase::ping() for how this can happen.
-                       $this->mErrorConnection = $conn;
-                       $conn = false;
-               }
-
-               return $conn;
-       }
-
-       /**
-        * Open a connection to a foreign DB, or return one if it is already open.
-        *
-        * Increments a reference count on the returned connection which locks the
-        * connection to the requested wiki. This reference count can be
-        * decremented by calling reuseConnection().
-        *
-        * If a connection is open to the appropriate server already, but with the wrong
-        * database, it will be switched to the right database and returned, as long as
-        * it has been freed first with reuseConnection().
-        *
-        * On error, returns false, and the connection which caused the
-        * error will be available via $this->mErrorConnection.
-        *
-        * @param int $i Server index
-        * @param string $wiki Wiki ID to open
-        * @return DatabaseBase
-        */
-       private function openForeignConnection( $i, $wiki ) {
-               list( $dbName, $prefix ) = wfSplitWikiID( $wiki );
-               if ( isset( $this->mConns['foreignUsed'][$i][$wiki] ) ) {
-                       // Reuse an already-used connection
-                       $conn = $this->mConns['foreignUsed'][$i][$wiki];
-                       wfDebug( __METHOD__ . ": reusing connection $i/$wiki\n" );
-               } elseif ( isset( $this->mConns['foreignFree'][$i][$wiki] ) ) {
-                       // Reuse a free connection for the same wiki
-                       $conn = $this->mConns['foreignFree'][$i][$wiki];
-                       unset( $this->mConns['foreignFree'][$i][$wiki] );
-                       $this->mConns['foreignUsed'][$i][$wiki] = $conn;
-                       wfDebug( __METHOD__ . ": reusing free connection $i/$wiki\n" );
-               } elseif ( !empty( $this->mConns['foreignFree'][$i] ) ) {
-                       // Reuse a connection from another wiki
-                       $conn = reset( $this->mConns['foreignFree'][$i] );
-                       $oldWiki = key( $this->mConns['foreignFree'][$i] );
-
-                       // The empty string as a DB name means "don't care".
-                       // DatabaseMysqlBase::open() already handle this on connection.
-                       if ( $dbName !== '' && !$conn->selectDB( $dbName ) ) {
-                               $this->mLastError = "Error selecting database $dbName on server " .
-                                       $conn->getServer() . " from client host " . wfHostname() . "\n";
-                               $this->mErrorConnection = $conn;
-                               $conn = false;
-                       } else {
-                               $conn->tablePrefix( $prefix );
-                               unset( $this->mConns['foreignFree'][$i][$oldWiki] );
-                               $this->mConns['foreignUsed'][$i][$wiki] = $conn;
-                               wfDebug( __METHOD__ . ": reusing free connection from $oldWiki for $wiki\n" );
-                       }
-               } else {
-                       // Open a new connection
-                       $server = $this->mServers[$i];
-                       $server['serverIndex'] = $i;
-                       $server['foreignPoolRefCount'] = 0;
-                       $server['foreign'] = true;
-                       $conn = $this->reallyOpenConnection( $server, $dbName );
-                       if ( !$conn->isOpen() ) {
-                               wfDebug( __METHOD__ . ": error opening connection for $i/$wiki\n" );
-                               $this->mErrorConnection = $conn;
-                               $conn = false;
-                       } else {
-                               $conn->tablePrefix( $prefix );
-                               $this->mConns['foreignUsed'][$i][$wiki] = $conn;
-                               wfDebug( __METHOD__ . ": opened new connection for $i/$wiki\n" );
-                       }
-               }
-
-               // Increment reference count
-               if ( $conn ) {
-                       $refCount = $conn->getLBInfo( 'foreignPoolRefCount' );
-                       $conn->setLBInfo( 'foreignPoolRefCount', $refCount + 1 );
-               }
-
-               return $conn;
-       }
-
-       /**
-        * Test if the specified index represents an open connection
-        *
-        * @param int $index Server index
-        * @access private
-        * @return bool
-        */
-       private function isOpen( $index ) {
-               if ( !is_integer( $index ) ) {
-                       return false;
-               }
-
-               return (bool)$this->getAnyOpenConnection( $index );
-       }
-
-       /**
-        * Really opens a connection. Uncached.
-        * Returns a Database object whether or not the connection was successful.
-        * @access private
-        *
-        * @param array $server
-        * @param bool $dbNameOverride
-        * @throws MWException
-        * @return DatabaseBase
-        */
-       protected function reallyOpenConnection( $server, $dbNameOverride = false ) {
-               if ( !is_array( $server ) ) {
-                       throw new MWException( 'You must update your load-balancing configuration. ' .
-                               'See DefaultSettings.php entry for $wgDBservers.' );
-               }
-
-               if ( $dbNameOverride !== false ) {
-                       $server['dbname'] = $dbNameOverride;
-               }
-
-               // Log when many connection are made on requests
-               if ( ++$this->connsOpened >= self::CONN_HELD_WARN_THRESHOLD ) {
-                       $masterAddr = $this->getServerName( 0 );
-                       wfDebugLog( 'DBPerformance', __METHOD__ . ": " .
-                               "{$this->connsOpened}+ connections made (master=$masterAddr)\n" .
-                               wfBacktrace( true ) );
-               }
-
-               # Create object
-               try {
-                       $db = DatabaseBase::factory( $server['type'], $server );
-               } catch ( DBConnectionError $e ) {
-                       // FIXME: This is probably the ugliest thing I have ever done to
-                       // PHP. I'm half-expecting it to segfault, just out of disgust. -- TS
-                       $db = $e->db;
-               }
-
-               $db->setLBInfo( $server );
-               if ( isset( $server['fakeSlaveLag'] ) ) {
-                       $db->setFakeSlaveLag( $server['fakeSlaveLag'] );
-               }
-               if ( isset( $server['fakeMaster'] ) ) {
-                       $db->setFakeMaster( true );
-               }
-
-               return $db;
-       }
-
-       /**
-        * @throws DBConnectionError
-        * @return bool
-        */
-       private function reportConnectionError() {
-               $conn = $this->mErrorConnection; // The connection which caused the error
-               $context = array(
-                       'method' => __METHOD__,
-                       'last_error' => $this->mLastError,
-               );
-
-               if ( !is_object( $conn ) ) {
-                       // No last connection, probably due to all servers being too busy
-                       wfLogDBError(
-                               "LB failure with no last connection. Connection error: {last_error}",
-                               $context
-                       );
-
-                       // If all servers were busy, mLastError will contain something sensible
-                       throw new DBConnectionError( null, $this->mLastError );
-               } else {
-                       $context['db_server'] = $conn->getProperty( 'mServer' );
-                       wfLogDBError(
-                               "Connection error: {last_error} ({db_server})",
-                               $context
-                       );
-                       $conn->reportConnectionError( "{$this->mLastError} ({$context['db_server']})" ); // throws DBConnectionError
-               }
-
-               return false; /* not reached */
-       }
-
-       /**
-        * @return int
-        * @since 1.26
-        */
-       public function getWriterIndex() {
-               return 0;
-       }
-
-       /**
-        * Returns true if the specified index is a valid server index
-        *
-        * @param string $i
-        * @return bool
-        */
-       public function haveIndex( $i ) {
-               return array_key_exists( $i, $this->mServers );
-       }
-
-       /**
-        * Returns true if the specified index is valid and has non-zero load
-        *
-        * @param string $i
-        * @return bool
-        */
-       public function isNonZeroLoad( $i ) {
-               return array_key_exists( $i, $this->mServers ) && $this->mLoads[$i] != 0;
-       }
-
-       /**
-        * Get the number of defined servers (not the number of open connections)
-        *
-        * @return int
-        */
-       public function getServerCount() {
-               return count( $this->mServers );
-       }
-
-       /**
-        * Get the host name or IP address of the server with the specified index
-        * Prefer a readable name if available.
-        * @param string $i
-        * @return string
-        */
-       public function getServerName( $i ) {
-               if ( isset( $this->mServers[$i]['hostName'] ) ) {
-                       $name = $this->mServers[$i]['hostName'];
-               } elseif ( isset( $this->mServers[$i]['host'] ) ) {
-                       $name = $this->mServers[$i]['host'];
-               } else {
-                       $name = '';
-               }
-
-               return ( $name != '' ) ? $name : 'localhost';
-       }
-
-       /**
-        * Return the server info structure for a given index, or false if the index is invalid.
-        * @param int $i
-        * @return array|bool
-        */
-       public function getServerInfo( $i ) {
-               if ( isset( $this->mServers[$i] ) ) {
-                       return $this->mServers[$i];
-               } else {
-                       return false;
-               }
-       }
-
-       /**
-        * Sets the server info structure for the given index. Entry at index $i
-        * is created if it doesn't exist
-        * @param int $i
-        * @param array $serverInfo
-        */
-       public function setServerInfo( $i, array $serverInfo ) {
-               $this->mServers[$i] = $serverInfo;
-       }
-
-       /**
-        * Get the current master position for chronology control purposes
-        * @return mixed
-        */
-       public function getMasterPos() {
-               # If this entire request was served from a slave without opening a connection to the
-               # master (however unlikely that may be), then we can fetch the position from the slave.
-               $masterConn = $this->getAnyOpenConnection( 0 );
-               if ( !$masterConn ) {
-                       $serverCount = count( $this->mServers );
-                       for ( $i = 1; $i < $serverCount; $i++ ) {
-                               $conn = $this->getAnyOpenConnection( $i );
-                               if ( $conn ) {
-                                       wfDebug( "Master pos fetched from slave\n" );
-
-                                       return $conn->getSlavePos();
-                               }
-                       }
-               } else {
-                       wfDebug( "Master pos fetched from master\n" );
-
-                       return $masterConn->getMasterPos();
-               }
-
-               return false;
-       }
-
-       /**
-        * Close all open connections
-        */
-       public function closeAll() {
-               foreach ( $this->mConns as $conns2 ) {
-                       foreach ( $conns2 as $conns3 ) {
-                               /** @var DatabaseBase $conn */
-                               foreach ( $conns3 as $conn ) {
-                                       $conn->close();
-                               }
-                       }
-               }
-               $this->mConns = array(
-                       'local' => array(),
-                       'foreignFree' => array(),
-                       'foreignUsed' => array(),
-               );
-               $this->connsOpened = 0;
-       }
-
-       /**
-        * Close a connection
-        * Using this function makes sure the LoadBalancer knows the connection is closed.
-        * If you use $conn->close() directly, the load balancer won't update its state.
-        * @param DatabaseBase $conn
-        */
-       public function closeConnection( $conn ) {
-               $done = false;
-               foreach ( $this->mConns as $i1 => $conns2 ) {
-                       foreach ( $conns2 as $i2 => $conns3 ) {
-                               foreach ( $conns3 as $i3 => $candidateConn ) {
-                                       if ( $conn === $candidateConn ) {
-                                               $conn->close();
-                                               unset( $this->mConns[$i1][$i2][$i3] );
-                                               --$this->connsOpened;
-                                               $done = true;
-                                               break;
-                                       }
-                               }
-                       }
-               }
-               if ( !$done ) {
-                       $conn->close();
-               }
-       }
-
-       /**
-        * Commit transactions on all open connections
-        */
-       public function commitAll() {
-               foreach ( $this->mConns as $conns2 ) {
-                       foreach ( $conns2 as $conns3 ) {
-                               /** @var DatabaseBase[] $conns3 */
-                               foreach ( $conns3 as $conn ) {
-                                       if ( $conn->trxLevel() ) {
-                                               $conn->commit( __METHOD__, 'flush' );
-                                       }
-                               }
-                       }
-               }
-       }
-
-       /**
-        *  Issue COMMIT only on master, only if queries were done on connection
-        */
-       public function commitMasterChanges() {
-               $masterIndex = $this->getWriterIndex();
-               foreach ( $this->mConns as $conns2 ) {
-                       if ( empty( $conns2[$masterIndex] ) ) {
-                               continue;
-                       }
-                       /** @var DatabaseBase $conn */
-                       foreach ( $conns2[$masterIndex] as $conn ) {
-                               if ( $conn->trxLevel() && $conn->writesOrCallbacksPending() ) {
-                                       $conn->commit( __METHOD__, 'flush' );
-                               }
-                       }
-               }
-       }
-
-       /**
-        * Issue ROLLBACK only on master, only if queries were done on connection
-        * @since 1.23
-        */
-       public function rollbackMasterChanges() {
-               $failedServers = array();
-
-               $masterIndex = $this->getWriterIndex();
-               foreach ( $this->mConns as $conns2 ) {
-                       if ( empty( $conns2[$masterIndex] ) ) {
-                               continue;
-                       }
-                       /** @var DatabaseBase $conn */
-                       foreach ( $conns2[$masterIndex] as $conn ) {
-                               if ( $conn->trxLevel() && $conn->writesOrCallbacksPending() ) {
-                                       try {
-                                               $conn->rollback( __METHOD__, 'flush' );
-                                       } catch ( DBError $e ) {
-                                               MWExceptionHandler::logException( $e );
-                                               $failedServers[] = $conn->getServer();
-                                       }
-                               }
-                       }
-               }
-
-               if ( $failedServers ) {
-                       throw new DBExpectedError( null, "Rollback failed on server(s) " .
-                               implode( ', ', array_unique( $failedServers ) ) );
-               }
-       }
-
-       /**
-        * @return bool Whether a master connection is already open
-        * @since 1.24
-        */
-       public function hasMasterConnection() {
-               return $this->isOpen( $this->getWriterIndex() );
-       }
-
-       /**
-        * Determine if there are pending changes in a transaction by this thread
-        * @since 1.23
-        * @return bool
-        */
-       public function hasMasterChanges() {
-               $masterIndex = $this->getWriterIndex();
-               foreach ( $this->mConns as $conns2 ) {
-                       if ( empty( $conns2[$masterIndex] ) ) {
-                               continue;
-                       }
-                       /** @var DatabaseBase $conn */
-                       foreach ( $conns2[$masterIndex] as $conn ) {
-                               if ( $conn->trxLevel() && $conn->writesOrCallbacksPending() ) {
-                                       return true;
-                               }
-                       }
-               }
-               return false;
-       }
-
-       /**
-        * Get the timestamp of the latest write query done by this thread
-        * @since 1.25
-        * @return float|bool UNIX timestamp or false
-        */
-       public function lastMasterChangeTimestamp() {
-               $lastTime = false;
-               $masterIndex = $this->getWriterIndex();
-               foreach ( $this->mConns as $conns2 ) {
-                       if ( empty( $conns2[$masterIndex] ) ) {
-                               continue;
-                       }
-                       /** @var DatabaseBase $conn */
-                       foreach ( $conns2[$masterIndex] as $conn ) {
-                               $lastTime = max( $lastTime, $conn->lastDoneWrites() );
-                       }
-               }
-               return $lastTime;
-       }
-
-       /**
-        * Check if this load balancer object had any recent or still
-        * pending writes issued against it by this PHP thread
-        *
-        * @param float $age How many seconds ago is "recent" [defaults to mWaitTimeout]
-        * @return bool
-        * @since 1.25
-        */
-       public function hasOrMadeRecentMasterChanges( $age = null ) {
-               $age = ( $age === null ) ? $this->mWaitTimeout : $age;
-
-               return ( $this->hasMasterChanges()
-                       || $this->lastMasterChangeTimestamp() > microtime( true ) - $age );
-       }
-
-       /**
-        * @param mixed $value
-        * @return mixed
-        */
-       public function waitTimeout( $value = null ) {
-               return wfSetVar( $this->mWaitTimeout, $value );
-       }
-
-       /**
-        * @return bool Whether the generic connection for reads is highly "lagged"
-        */
-       public function getLaggedSlaveMode() {
-               # Get a generic reader connection
-               $this->getConnection( DB_SLAVE );
-
-               return $this->mLaggedSlaveMode;
-       }
-
-       /**
-        * Disables/enables lag checks
-        * @param null|bool $mode
-        * @return bool
-        */
-       public function allowLagged( $mode = null ) {
-               if ( $mode === null ) {
-                       return $this->mAllowLagged;
-               }
-               $this->mAllowLagged = $mode;
-
-               return $this->mAllowLagged;
-       }
-
-       /**
-        * @return bool
-        */
-       public function pingAll() {
-               $success = true;
-               foreach ( $this->mConns as $conns2 ) {
-                       foreach ( $conns2 as $conns3 ) {
-                               /** @var DatabaseBase[] $conns3 */
-                               foreach ( $conns3 as $conn ) {
-                                       if ( !$conn->ping() ) {
-                                               $success = false;
-                                       }
-                               }
-                       }
-               }
-
-               return $success;
-       }
-
-       /**
-        * Call a function with each open connection object
-        * @param callable $callback
-        * @param array $params
-        */
-       public function forEachOpenConnection( $callback, array $params = array() ) {
-               foreach ( $this->mConns as $conns2 ) {
-                       foreach ( $conns2 as $conns3 ) {
-                               foreach ( $conns3 as $conn ) {
-                                       $mergedParams = array_merge( array( $conn ), $params );
-                                       call_user_func_array( $callback, $mergedParams );
-                               }
-                       }
-               }
-       }
-
-       /**
-        * Get the hostname and lag time of the most-lagged slave
-        *
-        * This is useful for maintenance scripts that need to throttle their updates.
-        * May attempt to open connections to slaves on the default DB. If there is
-        * no lag, the maximum lag will be reported as -1.
-        *
-        * @param bool|string $wiki Wiki ID, or false for the default database
-        * @return array ( host, max lag, index of max lagged host )
-        */
-       public function getMaxLag( $wiki = false ) {
-               $maxLag = -1;
-               $host = '';
-               $maxIndex = 0;
-
-               if ( $this->getServerCount() <= 1 ) {
-                       return array( $host, $maxLag, $maxIndex ); // no replication = no lag
-               }
-
-               $lagTimes = $this->getLagTimes( $wiki );
-               foreach ( $lagTimes as $i => $lag ) {
-                       if ( $lag > $maxLag ) {
-                               $maxLag = $lag;
-                               $host = $this->mServers[$i]['host'];
-                               $maxIndex = $i;
-                       }
-               }
-
-               return array( $host, $maxLag, $maxIndex );
-       }
-
-       /**
-        * Get lag time for each server
-        *
-        * Results are cached for a short time in memcached/process cache
-        *
-        * @param string|bool $wiki
-        * @return int[] Map of (server index => seconds)
-        */
-       public function getLagTimes( $wiki = false ) {
-               if ( $this->getServerCount() <= 1 ) {
-                       return array( 0 => 0 ); // no replication = no lag
-               }
-
-               # Send the request to the load monitor
-               return $this->getLoadMonitor()->getLagTimes( array_keys( $this->mServers ), $wiki );
-       }
-
-       /**
-        * Get the lag in seconds for a given connection, or zero if this load
-        * balancer does not have replication enabled.
-        *
-        * This should be used in preference to Database::getLag() in cases where
-        * replication may not be in use, since there is no way to determine if
-        * replication is in use at the connection level without running
-        * potentially restricted queries such as SHOW SLAVE STATUS. Using this
-        * function instead of Database::getLag() avoids a fatal error in this
-        * case on many installations.
-        *
-        * @param DatabaseBase $conn
-        * @return int
-        */
-       public function safeGetLag( $conn ) {
-               if ( $this->getServerCount() == 1 ) {
-                       return 0;
-               } else {
-                       return $conn->getLag();
-               }
-       }
-
-       /**
-        * Clear the cache for slag lag delay times
-        *
-        * This is only used for testing
-        */
-       public function clearLagTimeCache() {
-               $this->getLoadMonitor()->clearCaches();
-       }
-}
diff --git a/includes/db/LoadMonitor.php b/includes/db/LoadMonitor.php
deleted file mode 100644 (file)
index 4975ea1..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-<?php
-/**
- * Database load monitoring.
- *
- * 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 Database
- */
-
-/**
- * An interface for database load monitoring
- *
- * @ingroup Database
- */
-interface LoadMonitor {
-       /**
-        * Construct a new LoadMonitor with a given LoadBalancer parent
-        *
-        * @param LoadBalancer $parent
-        */
-       public function __construct( $parent );
-
-       /**
-        * Perform pre-connection load ratio adjustment.
-        * @param array $loads
-        * @param string|bool $group The selected query group. Default: false
-        * @param string|bool $wiki Default: false
-        */
-       public function scaleLoads( &$loads, $group = false, $wiki = false );
-
-       /**
-        * Return an estimate of replication lag for each server
-        *
-        * @param array $serverIndexes
-        * @param string $wiki
-        *
-        * @return array Map of (server index => seconds)
-        */
-       public function getLagTimes( $serverIndexes, $wiki );
-}
-
-class LoadMonitorNull implements LoadMonitor {
-       public function __construct( $parent ) {
-       }
-
-       public function scaleLoads( &$loads, $group = false, $wiki = false ) {
-       }
-
-       public function getLagTimes( $serverIndexes, $wiki ) {
-               return array_fill_keys( $serverIndexes, 0 );
-       }
-}
diff --git a/includes/db/LoadMonitorMySQL.php b/includes/db/LoadMonitorMySQL.php
deleted file mode 100644 (file)
index 3008419..0000000
+++ /dev/null
@@ -1,124 +0,0 @@
-<?php
-/**
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- * @ingroup Database
- */
-
-/**
- * Basic MySQL load monitor with no external dependencies
- * Uses memcached to cache the replication lag for a short time
- *
- * @ingroup Database
- */
-class LoadMonitorMySQL implements LoadMonitor {
-       /** @var LoadBalancer */
-       public $parent;
-       /** @var BagOStuff */
-       protected $srvCache;
-       /** @var BagOStuff */
-       protected $mainCache;
-
-       public function __construct( $parent ) {
-               $this->parent = $parent;
-
-               $this->srvCache = ObjectCache::newAccelerator( 'hash' );
-               $this->mainCache = wfGetMainCache();
-       }
-
-       public function scaleLoads( &$loads, $group = false, $wiki = false ) {
-       }
-
-       public function getLagTimes( $serverIndexes, $wiki ) {
-               if ( count( $serverIndexes ) == 1 && reset( $serverIndexes ) == 0 ) {
-                       # Single server only, just return zero without caching
-                       return array( 0 => 0 );
-               }
-
-               $key = $this->getLagTimeCacheKey();
-               # Randomize TTLs to reduce stampedes (4.0 - 5.0 sec)
-               $ttl = mt_rand( 4e6, 5e6 ) / 1e6;
-               # Keep keys around longer as fallbacks
-               $staleTTL = 60;
-
-               # (a) Check the local APC cache
-               $value = $this->srvCache->get( $key );
-               if ( $value && $value['timestamp'] > ( microtime( true ) - $ttl ) ) {
-                       wfDebugLog( 'replication',  __FUNCTION__ . ": got lag times ($key) from local cache" );
-                       return $value['lagTimes']; // cache hit
-               }
-               $staleValue = $value ?: false;
-
-               # (b) Check the shared cache and backfill APC
-               $value = $this->mainCache->get( $key );
-               if ( $value && $value['timestamp'] > ( microtime( true ) - $ttl ) ) {
-                       $this->srvCache->set( $key, $value, $staleTTL );
-                       wfDebugLog( 'replication',  __FUNCTION__ . ": got lag times ($key) from main cache" );
-
-                       return $value['lagTimes']; // cache hit
-               }
-               $staleValue = $value ?: $staleValue;
-
-               # (c) Cache key missing or expired; regenerate and backfill
-               if ( $this->mainCache->lock( $key, 0, 10 ) ) {
-                       # Let this process alone update the cache value
-                       $cache = $this->mainCache;
-                       /** @noinspection PhpUnusedLocalVariableInspection */
-                       $unlocker = new ScopedCallback( function () use ( $cache, $key ) {
-                               $cache->unlock( $key );
-                       } );
-               } elseif ( $staleValue ) {
-                       # Could not acquire lock but an old cache exists, so use it
-                       return $staleValue['lagTimes'];
-               }
-
-               $lagTimes = array();
-               foreach ( $serverIndexes as $i ) {
-                       if ( $i == 0 ) { # Master
-                               $lagTimes[$i] = 0;
-                       } elseif ( false !== ( $conn = $this->parent->getAnyOpenConnection( $i ) ) ) {
-                               $lagTimes[$i] = $conn->getLag();
-                       } elseif ( false !== ( $conn = $this->parent->openConnection( $i, $wiki ) ) ) {
-                               $lagTimes[$i] = $conn->getLag();
-                               # Close the connection to avoid sleeper connections piling up.
-                               # Note that the caller will pick one of these DBs and reconnect,
-                               # which is slightly inefficient, but this only matters for the lag
-                               # time cache miss cache, which is far less common that cache hits.
-                               $this->parent->closeConnection( $conn );
-                       }
-               }
-
-               # Add a timestamp key so we know when it was cached
-               $value = array( 'lagTimes' => $lagTimes, 'timestamp' => microtime( true ) );
-               $this->mainCache->set( $key, $value, $staleTTL );
-               $this->srvCache->set( $key, $value, $staleTTL );
-               wfDebugLog( 'replication',  __FUNCTION__ . ": re-calculated lag times ($key)" );
-
-               return $value['lagTimes'];
-       }
-
-       public function clearCaches() {
-               $key = $this->getLagTimeCacheKey();
-               $this->srvCache->delete( $key );
-               $this->mainCache->delete( $key );
-       }
-
-       private function getLagTimeCacheKey() {
-               # Lag is per-server, not per-DB, so key on the master DB name
-               return wfGlobalCacheKey( 'lag-times', $this->parent->getServerName( 0 ) );
-       }
-}
diff --git a/includes/db/loadbalancer/LBFactory.php b/includes/db/loadbalancer/LBFactory.php
new file mode 100644 (file)
index 0000000..da0fe44
--- /dev/null
@@ -0,0 +1,226 @@
+<?php
+/**
+ * Generator of database load balancing objects.
+ *
+ * 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 Database
+ */
+
+/**
+ * An interface for generating database load balancers
+ * @ingroup Database
+ */
+abstract class LBFactory {
+       /** @var LBFactory */
+       private static $instance;
+
+       /**
+        * Disables all access to the load balancer, will cause all database access
+        * to throw a DBAccessError
+        */
+       public static function disableBackend() {
+               global $wgLBFactoryConf;
+               self::$instance = new LBFactoryFake( $wgLBFactoryConf );
+       }
+
+       /**
+        * Get an LBFactory instance
+        *
+        * @return LBFactory
+        */
+       public static function singleton() {
+               global $wgLBFactoryConf;
+
+               if ( is_null( self::$instance ) ) {
+                       $class = self::getLBFactoryClass( $wgLBFactoryConf );
+
+                       self::$instance = new $class( $wgLBFactoryConf );
+               }
+
+               return self::$instance;
+       }
+
+       /**
+        * Returns the LBFactory class to use and the load balancer configuration.
+        *
+        * @param array $config (e.g. $wgLBFactoryConf)
+        * @return string Class name
+        */
+       public static function getLBFactoryClass( array $config ) {
+               // For configuration backward compatibility after removing
+               // underscores from class names in MediaWiki 1.23.
+               $bcClasses = array(
+                       'LBFactory_Simple' => 'LBFactorySimple',
+                       'LBFactory_Single' => 'LBFactorySingle',
+                       'LBFactory_Multi' => 'LBFactoryMulti',
+                       'LBFactory_Fake' => 'LBFactoryFake',
+               );
+
+               $class = $config['class'];
+
+               if ( isset( $bcClasses[$class] ) ) {
+                       $class = $bcClasses[$class];
+                       wfDeprecated(
+                               '$wgLBFactoryConf must be updated. See RELEASE-NOTES for details',
+                               '1.23'
+                       );
+               }
+
+               return $class;
+       }
+
+       /**
+        * Shut down, close connections and destroy the cached instance.
+        */
+       public static function destroyInstance() {
+               if ( self::$instance ) {
+                       self::$instance->shutdown();
+                       self::$instance->forEachLBCallMethod( 'closeAll' );
+                       self::$instance = null;
+               }
+       }
+
+       /**
+        * Set the instance to be the given object
+        *
+        * @param LBFactory $instance
+        */
+       public static function setInstance( $instance ) {
+               self::destroyInstance();
+               self::$instance = $instance;
+       }
+
+       /**
+        * Construct a factory based on a configuration array (typically from $wgLBFactoryConf)
+        * @param array $conf
+        */
+       abstract public function __construct( array $conf );
+
+       /**
+        * Create a new load balancer object. The resulting object will be untracked,
+        * not chronology-protected, and the caller is responsible for cleaning it up.
+        *
+        * @param bool|string $wiki Wiki ID, or false for the current wiki
+        * @return LoadBalancer
+        */
+       abstract public function newMainLB( $wiki = false );
+
+       /**
+        * Get a cached (tracked) load balancer object.
+        *
+        * @param bool|string $wiki Wiki ID, or false for the current wiki
+        * @return LoadBalancer
+        */
+       abstract public function getMainLB( $wiki = false );
+
+       /**
+        * Create a new load balancer for external storage. The resulting object will be
+        * untracked, not chronology-protected, and the caller is responsible for
+        * cleaning it up.
+        *
+        * @param string $cluster External storage cluster, or false for core
+        * @param bool|string $wiki Wiki ID, or false for the current wiki
+        * @return LoadBalancer
+        */
+       abstract protected function newExternalLB( $cluster, $wiki = false );
+
+       /**
+        * Get a cached (tracked) load balancer for external storage
+        *
+        * @param string $cluster External storage cluster, or false for core
+        * @param bool|string $wiki Wiki ID, or false for the current wiki
+        * @return LoadBalancer
+        */
+       abstract public function &getExternalLB( $cluster, $wiki = false );
+
+       /**
+        * Execute a function for each tracked load balancer
+        * The callback is called with the load balancer as the first parameter,
+        * and $params passed as the subsequent parameters.
+        *
+        * @param callable $callback
+        * @param array $params
+        */
+       abstract public function forEachLB( $callback, array $params = array() );
+
+       /**
+        * Prepare all tracked load balancers for shutdown
+        * STUB
+        */
+       public function shutdown() {
+       }
+
+       /**
+        * Call a method of each tracked load balancer
+        *
+        * @param string $methodName
+        * @param array $args
+        */
+       private function forEachLBCallMethod( $methodName, array $args = array() ) {
+               $this->forEachLB( function ( LoadBalancer $loadBalancer, $methodName, array $args ) {
+                       call_user_func_array( array( $loadBalancer, $methodName ), $args );
+               }, array( $methodName, $args ) );
+       }
+
+       /**
+        * Commit on all connections. Done for two reasons:
+        * 1. To commit changes to the masters.
+        * 2. To release the snapshot on all connections, master and slave.
+        */
+       public function commitAll() {
+               $this->forEachLBCallMethod( 'commitAll' );
+       }
+
+       /**
+        * Commit changes on all master connections
+        */
+       public function commitMasterChanges() {
+               $this->forEachLBCallMethod( 'commitMasterChanges' );
+       }
+
+       /**
+        * Rollback changes on all master connections
+        * @since 1.23
+        */
+       public function rollbackMasterChanges() {
+               $this->forEachLBCallMethod( 'rollbackMasterChanges' );
+       }
+
+       /**
+        * Detemine if any master connection has pending changes.
+        * @since 1.23
+        * @return bool
+        */
+       public function hasMasterChanges() {
+               $ret = false;
+               $this->forEachLB( function ( LoadBalancer $lb ) use ( &$ret ) {
+                       $ret = $ret || $lb->hasMasterChanges();
+               } );
+               return $ret;
+       }
+}
+
+/**
+ * Exception class for attempted DB access
+ */
+class DBAccessError extends MWException {
+       public function __construct() {
+               parent::__construct( "Mediawiki tried to access the database via wfGetDB(). " .
+                       "This is not allowed." );
+       }
+}
diff --git a/includes/db/loadbalancer/LBFactoryFake.php b/includes/db/loadbalancer/LBFactoryFake.php
new file mode 100644 (file)
index 0000000..d8becf5
--- /dev/null
@@ -0,0 +1,52 @@
+<?php
+/**
+ * Generator of database load balancing objects.
+ *
+ * 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 Database
+ */
+
+/**
+ * LBFactory class that throws an error on any attempt to use it.
+ * This will typically be done via wfGetDB().
+ * Call LBFactory::disableBackend() to start using this, and
+ * LBFactory::enableBackend() to return to normal behavior
+ */
+class LBFactoryFake extends LBFactory {
+       public function __construct( array $conf ) {
+       }
+
+       public function newMainLB( $wiki = false ) {
+               throw new DBAccessError;
+       }
+
+       public function getMainLB( $wiki = false ) {
+               throw new DBAccessError;
+       }
+
+       protected function newExternalLB( $cluster, $wiki = false ) {
+               throw new DBAccessError;
+       }
+
+       public function &getExternalLB( $cluster, $wiki = false ) {
+               throw new DBAccessError;
+       }
+
+       public function forEachLB( $callback, array $params = array() ) {
+       }
+}
diff --git a/includes/db/loadbalancer/LBFactoryMulti.php b/includes/db/loadbalancer/LBFactoryMulti.php
new file mode 100644 (file)
index 0000000..92fbccd
--- /dev/null
@@ -0,0 +1,399 @@
+<?php
+/**
+ * Advanced generator of database load balancing objects for wiki farms.
+ *
+ * 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 Database
+ */
+
+/**
+ * A multi-wiki, multi-master factory for Wikimedia and similar installations.
+ * Ignores the old configuration globals
+ *
+ * Configuration:
+ *     sectionsByDB                A map of database names to section names.
+ *
+ *     sectionLoads                A 2-d map. For each section, gives a map of server names to
+ *                                 load ratios. For example:
+ *                                 array(
+ *                                     'section1' => array(
+ *                                         'db1' => 100,
+ *                                         'db2' => 100
+ *                                     )
+ *                                 )
+ *
+ *     serverTemplate              A server info associative array as documented for $wgDBservers.
+ *                                 The host, hostName and load entries will be overridden.
+ *
+ *     groupLoadsBySection         A 3-d map giving server load ratios for each section and group.
+ *                                 For example:
+ *                                 array(
+ *                                     'section1' => array(
+ *                                         'group1' => array(
+ *                                             'db1' => 100,
+ *                                             'db2' => 100
+ *                                         )
+ *                                     )
+ *                                 )
+ *
+ *     groupLoadsByDB              A 3-d map giving server load ratios by DB name.
+ *
+ *     hostsByName                 A map of hostname to IP address.
+ *
+ *     externalLoads               A map of external storage cluster name to server load map.
+ *
+ *     externalTemplateOverrides   A set of server info keys overriding serverTemplate for external
+ *                                 storage.
+ *
+ *     templateOverridesByServer   A 2-d map overriding serverTemplate and
+ *                                 externalTemplateOverrides on a server-by-server basis. Applies
+ *                                 to both core and external storage.
+ *
+ *     templateOverridesByCluster  A 2-d map overriding the server info by external storage cluster.
+ *
+ *     masterTemplateOverrides     An override array for all master servers.
+ *
+ *     readOnlyBySection           A map of section name to read-only message.
+ *                                 Missing or false for read/write.
+ *
+ * @ingroup Database
+ */
+class LBFactoryMulti extends LBFactory {
+       // Required settings
+
+       /** @var array A map of database names to section names */
+       private $sectionsByDB;
+
+       /**
+        * @var array A 2-d map. For each section, gives a map of server names to
+        * load ratios
+        */
+       private $sectionLoads;
+
+       /**
+        * @var array A server info associative array as documented for
+        * $wgDBservers. The host, hostName and load entries will be
+        * overridden
+        */
+       private $serverTemplate;
+
+       // Optional settings
+
+       /** @var array A 3-d map giving server load ratios for each section and group */
+       private $groupLoadsBySection = array();
+
+       /** @var array A 3-d map giving server load ratios by DB name */
+       private $groupLoadsByDB = array();
+
+       /** @var array A map of hostname to IP address */
+       private $hostsByName = array();
+
+       /** @var array A map of external storage cluster name to server load map */
+       private $externalLoads = array();
+
+       /**
+        * @var array A set of server info keys overriding serverTemplate for
+        * external storage
+        */
+       private $externalTemplateOverrides;
+
+       /**
+        * @var array A 2-d map overriding serverTemplate and
+        * externalTemplateOverrides on a server-by-server basis. Applies to both
+        * core and external storage
+        */
+       private $templateOverridesByServer;
+
+       /** @var array A 2-d map overriding the server info by external storage cluster */
+       private $templateOverridesByCluster;
+
+       /** @var array An override array for all master servers */
+       private $masterTemplateOverrides;
+
+       /**
+        * @var array|bool A map of section name to read-only message. Missing or
+        * false for read/write
+        */
+       private $readOnlyBySection = array();
+
+       // Other stuff
+
+       /** @var array Load balancer factory configuration */
+       private $conf;
+
+       /** @var LoadBalancer[] */
+       private $mainLBs = array();
+
+       /** @var LoadBalancer[] */
+       private $extLBs = array();
+
+       /** @var string */
+       private $lastWiki;
+
+       /** @var string */
+       private $lastSection;
+
+       /**
+        * @param array $conf
+        * @throws MWException
+        */
+       public function __construct( array $conf ) {
+               $this->chronProt = new ChronologyProtector;
+               $this->conf = $conf;
+               $required = array( 'sectionsByDB', 'sectionLoads', 'serverTemplate' );
+               $optional = array( 'groupLoadsBySection', 'groupLoadsByDB', 'hostsByName',
+                       'externalLoads', 'externalTemplateOverrides', 'templateOverridesByServer',
+                       'templateOverridesByCluster', 'masterTemplateOverrides',
+                       'readOnlyBySection' );
+
+               foreach ( $required as $key ) {
+                       if ( !isset( $conf[$key] ) ) {
+                               throw new MWException( __CLASS__ . ": $key is required in configuration" );
+                       }
+                       $this->$key = $conf[$key];
+               }
+
+               foreach ( $optional as $key ) {
+                       if ( isset( $conf[$key] ) ) {
+                               $this->$key = $conf[$key];
+                       }
+               }
+
+               // Check for read-only mode
+               $section = $this->getSectionForWiki();
+               if ( !empty( $this->readOnlyBySection[$section] ) ) {
+                       global $wgReadOnly;
+                       $wgReadOnly = $this->readOnlyBySection[$section];
+               }
+       }
+
+       /**
+        * @param bool|string $wiki
+        * @return string
+        */
+       private function getSectionForWiki( $wiki = false ) {
+               if ( $this->lastWiki === $wiki ) {
+                       return $this->lastSection;
+               }
+               list( $dbName, ) = $this->getDBNameAndPrefix( $wiki );
+               if ( isset( $this->sectionsByDB[$dbName] ) ) {
+                       $section = $this->sectionsByDB[$dbName];
+               } else {
+                       $section = 'DEFAULT';
+               }
+               $this->lastSection = $section;
+               $this->lastWiki = $wiki;
+
+               return $section;
+       }
+
+       /**
+        * @param bool|string $wiki
+        * @return LoadBalancer
+        */
+       public function newMainLB( $wiki = false ) {
+               list( $dbName, ) = $this->getDBNameAndPrefix( $wiki );
+               $section = $this->getSectionForWiki( $wiki );
+               $groupLoads = array();
+               if ( isset( $this->groupLoadsByDB[$dbName] ) ) {
+                       $groupLoads = $this->groupLoadsByDB[$dbName];
+               }
+
+               if ( isset( $this->groupLoadsBySection[$section] ) ) {
+                       $groupLoads = array_merge_recursive( $groupLoads, $this->groupLoadsBySection[$section] );
+               }
+
+               return $this->newLoadBalancer(
+                       $this->serverTemplate,
+                       $this->sectionLoads[$section],
+                       $groupLoads
+               );
+       }
+
+       /**
+        * @param bool|string $wiki
+        * @return LoadBalancer
+        */
+       public function getMainLB( $wiki = false ) {
+               $section = $this->getSectionForWiki( $wiki );
+               if ( !isset( $this->mainLBs[$section] ) ) {
+                       $lb = $this->newMainLB( $wiki );
+                       $lb->parentInfo( array( 'id' => "main-$section" ) );
+                       $this->chronProt->initLB( $lb );
+                       $this->mainLBs[$section] = $lb;
+               }
+
+               return $this->mainLBs[$section];
+       }
+
+       /**
+        * @param string $cluster
+        * @param bool|string $wiki
+        * @throws MWException
+        * @return LoadBalancer
+        */
+       protected function newExternalLB( $cluster, $wiki = false ) {
+               if ( !isset( $this->externalLoads[$cluster] ) ) {
+                       throw new MWException( __METHOD__ . ": Unknown cluster \"$cluster\"" );
+               }
+               $template = $this->serverTemplate;
+               if ( isset( $this->externalTemplateOverrides ) ) {
+                       $template = $this->externalTemplateOverrides + $template;
+               }
+               if ( isset( $this->templateOverridesByCluster[$cluster] ) ) {
+                       $template = $this->templateOverridesByCluster[$cluster] + $template;
+               }
+
+               return $this->newLoadBalancer( $template, $this->externalLoads[$cluster], array() );
+       }
+
+       /**
+        * @param string $cluster External storage cluster, or false for core
+        * @param bool|string $wiki Wiki ID, or false for the current wiki
+        * @return LoadBalancer
+        */
+       public function &getExternalLB( $cluster, $wiki = false ) {
+               if ( !isset( $this->extLBs[$cluster] ) ) {
+                       $this->extLBs[$cluster] = $this->newExternalLB( $cluster, $wiki );
+                       $this->extLBs[$cluster]->parentInfo( array( 'id' => "ext-$cluster" ) );
+                       $this->chronProt->initLB( $this->extLBs[$cluster] );
+               }
+
+               return $this->extLBs[$cluster];
+       }
+
+       /**
+        * Make a new load balancer object based on template and load array
+        *
+        * @param array $template
+        * @param array $loads
+        * @param array $groupLoads
+        * @return LoadBalancer
+        */
+       private function newLoadBalancer( $template, $loads, $groupLoads ) {
+               $servers = $this->makeServerArray( $template, $loads, $groupLoads );
+               $lb = new LoadBalancer( array(
+                       'servers' => $servers,
+               ) );
+
+               return $lb;
+       }
+
+       /**
+        * Make a server array as expected by LoadBalancer::__construct, using a template and load array
+        *
+        * @param array $template
+        * @param array $loads
+        * @param array $groupLoads
+        * @return array
+        */
+       private function makeServerArray( $template, $loads, $groupLoads ) {
+               $servers = array();
+               $master = true;
+               $groupLoadsByServer = $this->reindexGroupLoads( $groupLoads );
+               foreach ( $groupLoadsByServer as $server => $stuff ) {
+                       if ( !isset( $loads[$server] ) ) {
+                               $loads[$server] = 0;
+                       }
+               }
+               foreach ( $loads as $serverName => $load ) {
+                       $serverInfo = $template;
+                       if ( $master ) {
+                               $serverInfo['master'] = true;
+                               if ( isset( $this->masterTemplateOverrides ) ) {
+                                       $serverInfo = $this->masterTemplateOverrides + $serverInfo;
+                               }
+                               $master = false;
+                       }
+                       if ( isset( $this->templateOverridesByServer[$serverName] ) ) {
+                               $serverInfo = $this->templateOverridesByServer[$serverName] + $serverInfo;
+                       }
+                       if ( isset( $groupLoadsByServer[$serverName] ) ) {
+                               $serverInfo['groupLoads'] = $groupLoadsByServer[$serverName];
+                       }
+                       if ( isset( $this->hostsByName[$serverName] ) ) {
+                               $serverInfo['host'] = $this->hostsByName[$serverName];
+                       } else {
+                               $serverInfo['host'] = $serverName;
+                       }
+                       $serverInfo['hostName'] = $serverName;
+                       $serverInfo['load'] = $load;
+                       $servers[] = $serverInfo;
+               }
+
+               return $servers;
+       }
+
+       /**
+        * Take a group load array indexed by group then server, and reindex it by server then group
+        * @param array $groupLoads
+        * @return array
+        */
+       private function reindexGroupLoads( $groupLoads ) {
+               $reindexed = array();
+               foreach ( $groupLoads as $group => $loads ) {
+                       foreach ( $loads as $server => $load ) {
+                               $reindexed[$server][$group] = $load;
+                       }
+               }
+
+               return $reindexed;
+       }
+
+       /**
+        * Get the database name and prefix based on the wiki ID
+        * @param bool|string $wiki
+        * @return array
+        */
+       private function getDBNameAndPrefix( $wiki = false ) {
+               if ( $wiki === false ) {
+                       global $wgDBname, $wgDBprefix;
+
+                       return array( $wgDBname, $wgDBprefix );
+               } else {
+                       return wfSplitWikiID( $wiki );
+               }
+       }
+
+       /**
+        * Execute a function for each tracked load balancer
+        * The callback is called with the load balancer as the first parameter,
+        * and $params passed as the subsequent parameters.
+        * @param callable $callback
+        * @param array $params
+        */
+       public function forEachLB( $callback, array $params = array() ) {
+               foreach ( $this->mainLBs as $lb ) {
+                       call_user_func_array( $callback, array_merge( array( $lb ), $params ) );
+               }
+               foreach ( $this->extLBs as $lb ) {
+                       call_user_func_array( $callback, array_merge( array( $lb ), $params ) );
+               }
+       }
+
+       public function shutdown() {
+               foreach ( $this->mainLBs as $lb ) {
+                       $this->chronProt->shutdownLB( $lb );
+               }
+               foreach ( $this->extLBs as $extLB ) {
+                       $this->chronProt->shutdownLB( $extLB );
+               }
+               $this->chronProt->shutdown();
+               $this->commitMasterChanges();
+       }
+}
diff --git a/includes/db/loadbalancer/LBFactorySimple.php b/includes/db/loadbalancer/LBFactorySimple.php
new file mode 100644 (file)
index 0000000..23cdbc6
--- /dev/null
@@ -0,0 +1,153 @@
+<?php
+/**
+ * Generator of database load balancing objects.
+ *
+ * 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 Database
+ */
+
+/**
+ * A simple single-master LBFactory that gets its configuration from the b/c globals
+ */
+class LBFactorySimple extends LBFactory {
+       /** @var LoadBalancer */
+       private $mainLB;
+
+       /** @var LoadBalancer[] */
+       private $extLBs = array();
+
+       /** @var ChronologyProtector */
+       private $chronProt;
+
+       public function __construct( array $conf ) {
+               $this->chronProt = new ChronologyProtector;
+       }
+
+       /**
+        * @param bool|string $wiki
+        * @return LoadBalancer
+        */
+       public function newMainLB( $wiki = false ) {
+               global $wgDBservers;
+               if ( $wgDBservers ) {
+                       $servers = $wgDBservers;
+               } else {
+                       global $wgDBserver, $wgDBuser, $wgDBpassword, $wgDBname, $wgDBtype, $wgDebugDumpSql;
+                       global $wgDBssl, $wgDBcompress;
+
+                       $flags = DBO_DEFAULT;
+                       if ( $wgDebugDumpSql ) {
+                               $flags |= DBO_DEBUG;
+                       }
+                       if ( $wgDBssl ) {
+                               $flags |= DBO_SSL;
+                       }
+                       if ( $wgDBcompress ) {
+                               $flags |= DBO_COMPRESS;
+                       }
+
+                       $servers = array( array(
+                               'host' => $wgDBserver,
+                               'user' => $wgDBuser,
+                               'password' => $wgDBpassword,
+                               'dbname' => $wgDBname,
+                               'type' => $wgDBtype,
+                               'load' => 1,
+                               'flags' => $flags
+                       ) );
+               }
+
+               return new LoadBalancer( array(
+                       'servers' => $servers,
+               ) );
+       }
+
+       /**
+        * @param bool|string $wiki
+        * @return LoadBalancer
+        */
+       public function getMainLB( $wiki = false ) {
+               if ( !isset( $this->mainLB ) ) {
+                       $this->mainLB = $this->newMainLB( $wiki );
+                       $this->mainLB->parentInfo( array( 'id' => 'main' ) );
+                       $this->chronProt->initLB( $this->mainLB );
+               }
+
+               return $this->mainLB;
+       }
+
+       /**
+        * @throws MWException
+        * @param string $cluster
+        * @param bool|string $wiki
+        * @return LoadBalancer
+        */
+       protected function newExternalLB( $cluster, $wiki = false ) {
+               global $wgExternalServers;
+               if ( !isset( $wgExternalServers[$cluster] ) ) {
+                       throw new MWException( __METHOD__ . ": Unknown cluster \"$cluster\"" );
+               }
+
+               return new LoadBalancer( array(
+                       'servers' => $wgExternalServers[$cluster]
+               ) );
+       }
+
+       /**
+        * @param string $cluster
+        * @param bool|string $wiki
+        * @return array
+        */
+       public function &getExternalLB( $cluster, $wiki = false ) {
+               if ( !isset( $this->extLBs[$cluster] ) ) {
+                       $this->extLBs[$cluster] = $this->newExternalLB( $cluster, $wiki );
+                       $this->extLBs[$cluster]->parentInfo( array( 'id' => "ext-$cluster" ) );
+                       $this->chronProt->initLB( $this->extLBs[$cluster] );
+               }
+
+               return $this->extLBs[$cluster];
+       }
+
+       /**
+        * Execute a function for each tracked load balancer
+        * The callback is called with the load balancer as the first parameter,
+        * and $params passed as the subsequent parameters.
+        *
+        * @param callable $callback
+        * @param array $params
+        */
+       public function forEachLB( $callback, array $params = array() ) {
+               if ( isset( $this->mainLB ) ) {
+                       call_user_func_array( $callback, array_merge( array( $this->mainLB ), $params ) );
+               }
+               foreach ( $this->extLBs as $lb ) {
+                       call_user_func_array( $callback, array_merge( array( $lb ), $params ) );
+               }
+       }
+
+       public function shutdown() {
+               if ( $this->mainLB ) {
+                       $this->chronProt->shutdownLB( $this->mainLB );
+               }
+               foreach ( $this->extLBs as $extLB ) {
+                       $this->chronProt->shutdownLB( $extLB );
+               }
+               $this->chronProt->shutdown();
+               $this->commitMasterChanges();
+       }
+}
diff --git a/includes/db/loadbalancer/LBFactorySingle.php b/includes/db/loadbalancer/LBFactorySingle.php
new file mode 100644 (file)
index 0000000..a41dadf
--- /dev/null
@@ -0,0 +1,112 @@
+<?php
+/**
+ * Simple generator of database connections that always returns the same object.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup Database
+ */
+
+/**
+ * An LBFactory class that always returns a single database object.
+ */
+class LBFactorySingle extends LBFactory {
+       /** @var LoadBalancerSingle */
+       private $lb;
+
+       /**
+        * @param array $conf An associative array with one member:
+        *  - connection: The DatabaseBase connection object
+        */
+       public function __construct( array $conf ) {
+               $this->lb = new LoadBalancerSingle( $conf );
+       }
+
+       /**
+        * @param bool|string $wiki
+        * @return LoadBalancerSingle
+        */
+       public function newMainLB( $wiki = false ) {
+               return $this->lb;
+       }
+
+       /**
+        * @param bool|string $wiki
+        * @return LoadBalancerSingle
+        */
+       public function getMainLB( $wiki = false ) {
+               return $this->lb;
+       }
+
+       /**
+        * @param string $cluster External storage cluster, or false for core
+        * @param bool|string $wiki Wiki ID, or false for the current wiki
+        * @return LoadBalancerSingle
+        */
+       protected function newExternalLB( $cluster, $wiki = false ) {
+               return $this->lb;
+       }
+
+       /**
+        * @param string $cluster External storage cluster, or false for core
+        * @param bool|string $wiki Wiki ID, or false for the current wiki
+        * @return LoadBalancerSingle
+        */
+       public function &getExternalLB( $cluster, $wiki = false ) {
+               return $this->lb;
+       }
+
+       /**
+        * @param string|callable $callback
+        * @param array $params
+        */
+       public function forEachLB( $callback, array $params = array() ) {
+               call_user_func_array( $callback, array_merge( array( $this->lb ), $params ) );
+       }
+}
+
+/**
+ * Helper class for LBFactorySingle.
+ */
+class LoadBalancerSingle extends LoadBalancer {
+       /** @var DatabaseBase */
+       private $db;
+
+       /**
+        * @param array $params
+        */
+       public function __construct( array $params ) {
+               $this->db = $params['connection'];
+               parent::__construct( array( 'servers' => array( array(
+                       'type' => $this->db->getType(),
+                       'host' => $this->db->getServer(),
+                       'dbname' => $this->db->getDBname(),
+                       'load' => 1,
+               ) ) ) );
+       }
+
+       /**
+        *
+        * @param string $server
+        * @param bool $dbNameOverride
+        *
+        * @return DatabaseBase
+        */
+       protected function reallyOpenConnection( $server, $dbNameOverride = false ) {
+               return $this->db;
+       }
+}
diff --git a/includes/db/loadbalancer/LoadBalancer.php b/includes/db/loadbalancer/LoadBalancer.php
new file mode 100644 (file)
index 0000000..52dca08
--- /dev/null
@@ -0,0 +1,1273 @@
+<?php
+/**
+ * Database load balancing.
+ *
+ * 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 Database
+ */
+
+/**
+ * Database load balancing object
+ *
+ * @todo document
+ * @ingroup Database
+ */
+class LoadBalancer {
+       /** @var array[] Map of (server index => server config array) */
+       private $mServers;
+       /** @var array[] Map of (local/foreignUsed/foreignFree => server index => DatabaseBase array) */
+       private $mConns;
+       /** @var array Map of (server index => weight) */
+       private $mLoads;
+       /** @var array[] Map of (group => server index => weight) */
+       private $mGroupLoads;
+       /** @var bool Whether to disregard slave lag as a factor in slave selection */
+       private $mAllowLagged;
+       /** @var integer Seconds to spend waiting on slave lag to resolve */
+       private $mWaitTimeout;
+
+       /** @var array LBFactory information */
+       private $mParentInfo;
+       /** @var string The LoadMonitor subclass name */
+       private $mLoadMonitorClass;
+       /** @var LoadMonitor */
+       private $mLoadMonitor;
+
+       /** @var bool|DatabaseBase Database connection that caused a problem */
+       private $mErrorConnection;
+       /** @var integer The generic (not query grouped) slave index (of $mServers) */
+       private $mReadIndex;
+       /** @var bool|DBMasterPos False if not set */
+       private $mWaitForPos;
+       /** @var bool Whether the generic reader fell back to a lagged slave */
+       private $mLaggedSlaveMode;
+       /** @var string The last DB selection or connection error */
+       private $mLastError = 'Unknown error';
+       /** @var integer Total connections opened */
+       private $connsOpened = 0;
+
+       /** @var integer Warn when this many connection are held */
+       const CONN_HELD_WARN_THRESHOLD = 10;
+
+       /**
+        * @param array $params Array with keys:
+        *   servers           Required. Array of server info structures.
+        *   loadMonitor       Name of a class used to fetch server lag and load.
+        * @throws MWException
+        */
+       public function __construct( array $params ) {
+               if ( !isset( $params['servers'] ) ) {
+                       throw new MWException( __CLASS__ . ': missing servers parameter' );
+               }
+               $this->mServers = $params['servers'];
+               $this->mWaitTimeout = 10;
+
+               $this->mReadIndex = -1;
+               $this->mWriteIndex = -1;
+               $this->mConns = array(
+                       'local' => array(),
+                       'foreignUsed' => array(),
+                       'foreignFree' => array() );
+               $this->mLoads = array();
+               $this->mWaitForPos = false;
+               $this->mLaggedSlaveMode = false;
+               $this->mErrorConnection = false;
+               $this->mAllowLagged = false;
+
+               if ( isset( $params['loadMonitor'] ) ) {
+                       $this->mLoadMonitorClass = $params['loadMonitor'];
+               } else {
+                       $master = reset( $params['servers'] );
+                       if ( isset( $master['type'] ) && $master['type'] === 'mysql' ) {
+                               $this->mLoadMonitorClass = 'LoadMonitorMySQL';
+                       } else {
+                               $this->mLoadMonitorClass = 'LoadMonitorNull';
+                       }
+               }
+
+               foreach ( $params['servers'] as $i => $server ) {
+                       $this->mLoads[$i] = $server['load'];
+                       if ( isset( $server['groupLoads'] ) ) {
+                               foreach ( $server['groupLoads'] as $group => $ratio ) {
+                                       if ( !isset( $this->mGroupLoads[$group] ) ) {
+                                               $this->mGroupLoads[$group] = array();
+                                       }
+                                       $this->mGroupLoads[$group][$i] = $ratio;
+                               }
+                       }
+               }
+       }
+
+       /**
+        * Get a LoadMonitor instance
+        *
+        * @return LoadMonitor
+        */
+       private function getLoadMonitor() {
+               if ( !isset( $this->mLoadMonitor ) ) {
+                       $class = $this->mLoadMonitorClass;
+                       $this->mLoadMonitor = new $class( $this );
+               }
+
+               return $this->mLoadMonitor;
+       }
+
+       /**
+        * Get or set arbitrary data used by the parent object, usually an LBFactory
+        * @param mixed $x
+        * @return mixed
+        */
+       public function parentInfo( $x = null ) {
+               return wfSetVar( $this->mParentInfo, $x );
+       }
+
+       /**
+        * Given an array of non-normalised probabilities, this function will select
+        * an element and return the appropriate key
+        *
+        * @deprecated since 1.21, use ArrayUtils::pickRandom()
+        *
+        * @param array $weights
+        * @return bool|int|string
+        */
+       public function pickRandom( array $weights ) {
+               return ArrayUtils::pickRandom( $weights );
+       }
+
+       /**
+        * @param array $loads
+        * @param bool|string $wiki Wiki to get non-lagged for
+        * @param float $maxLag Restrict the maximum allowed lag to this many seconds
+        * @return bool|int|string
+        */
+       private function getRandomNonLagged( array $loads, $wiki = false, $maxLag = INF ) {
+               $lags = $this->getLagTimes( $wiki );
+
+               # Unset excessively lagged servers
+               foreach ( $lags as $i => $lag ) {
+                       if ( $i != 0 ) {
+                               $maxServerLag = $maxLag;
+                               if ( isset( $this->mServers[$i]['max lag'] ) ) {
+                                       $maxServerLag = min( $maxServerLag, $this->mServers[$i]['max lag'] );
+                               }
+                               if ( $lag === false ) {
+                                       wfDebugLog( 'replication', "Server #$i is not replicating" );
+                                       unset( $loads[$i] );
+                               } elseif ( $lag > $maxServerLag ) {
+                                       wfDebugLog( 'replication', "Server #$i is excessively lagged ($lag seconds)" );
+                                       unset( $loads[$i] );
+                               }
+                       }
+               }
+
+               # Find out if all the slaves with non-zero load are lagged
+               $sum = 0;
+               foreach ( $loads as $load ) {
+                       $sum += $load;
+               }
+               if ( $sum == 0 ) {
+                       # No appropriate DB servers except maybe the master and some slaves with zero load
+                       # Do NOT use the master
+                       # Instead, this function will return false, triggering read-only mode,
+                       # and a lagged slave will be used instead.
+                       return false;
+               }
+
+               if ( count( $loads ) == 0 ) {
+                       return false;
+               }
+
+               #wfDebugLog( 'connect', var_export( $loads, true ) );
+
+               # Return a random representative of the remainder
+               return ArrayUtils::pickRandom( $loads );
+       }
+
+       /**
+        * Get the index of the reader connection, which may be a slave
+        * This takes into account load ratios and lag times. It should
+        * always return a consistent index during a given invocation
+        *
+        * Side effect: opens connections to databases
+        * @param string|bool $group Query group, or false for the generic reader
+        * @param string|bool $wiki Wiki ID, or false for the current wiki
+        * @throws MWException
+        * @return bool|int|string
+        */
+       public function getReaderIndex( $group = false, $wiki = false ) {
+               global $wgDBtype;
+
+               # @todo FIXME: For now, only go through all this for mysql databases
+               if ( $wgDBtype != 'mysql' ) {
+                       return $this->getWriterIndex();
+               }
+
+               if ( count( $this->mServers ) == 1 ) {
+                       # Skip the load balancing if there's only one server
+                       return 0;
+               } elseif ( $group === false && $this->mReadIndex >= 0 ) {
+                       # Shortcut if generic reader exists already
+                       return $this->mReadIndex;
+               }
+
+               # Find the relevant load array
+               if ( $group !== false ) {
+                       if ( isset( $this->mGroupLoads[$group] ) ) {
+                               $nonErrorLoads = $this->mGroupLoads[$group];
+                       } else {
+                               # No loads for this group, return false and the caller can use some other group
+                               wfDebug( __METHOD__ . ": no loads for group $group\n" );
+
+                               return false;
+                       }
+               } else {
+                       $nonErrorLoads = $this->mLoads;
+               }
+
+               if ( !count( $nonErrorLoads ) ) {
+                       throw new MWException( "Empty server array given to LoadBalancer" );
+               }
+
+               # Scale the configured load ratios according to the dynamic load (if the load monitor supports it)
+               $this->getLoadMonitor()->scaleLoads( $nonErrorLoads, $group, $wiki );
+
+               $laggedSlaveMode = false;
+
+               # No server found yet
+               $i = false;
+               # First try quickly looking through the available servers for a server that
+               # meets our criteria
+               $currentLoads = $nonErrorLoads;
+               while ( count( $currentLoads ) ) {
+                       if ( $this->mAllowLagged || $laggedSlaveMode ) {
+                               $i = ArrayUtils::pickRandom( $currentLoads );
+                       } else {
+                               $i = false;
+                               if ( $this->mWaitForPos && $this->mWaitForPos->asOfTime() ) {
+                                       # ChronologyProtecter causes mWaitForPos to be set via sessions.
+                                       # This triggers doWait() after connect, so it's especially good to
+                                       # avoid lagged servers so as to avoid just blocking in that method.
+                                       $ago = microtime( true ) - $this->mWaitForPos->asOfTime();
+                                       # Aim for <= 1 second of waiting (being too picky can backfire)
+                                       $i = $this->getRandomNonLagged( $currentLoads, $wiki, $ago + 1 );
+                               }
+                               if ( $i === false ) {
+                                       # Any server with less lag than it's 'max lag' param is preferable
+                                       $i = $this->getRandomNonLagged( $currentLoads, $wiki );
+                               }
+                               if ( $i === false && count( $currentLoads ) != 0 ) {
+                                       # All slaves lagged. Switch to read-only mode
+                                       wfDebugLog( 'replication', "All slaves lagged. Switch to read-only mode" );
+                                       $i = ArrayUtils::pickRandom( $currentLoads );
+                                       $laggedSlaveMode = true;
+                               }
+                       }
+
+                       if ( $i === false ) {
+                               # pickRandom() returned false
+                               # This is permanent and means the configuration or the load monitor
+                               # wants us to return false.
+                               wfDebugLog( 'connect', __METHOD__ . ": pickRandom() returned false" );
+
+                               return false;
+                       }
+
+                       $serverName = $this->getServerName( $i );
+                       wfDebugLog( 'connect', __METHOD__ . ": Using reader #$i: $serverName..." );
+
+                       $conn = $this->openConnection( $i, $wiki );
+                       if ( !$conn ) {
+                               wfDebugLog( 'connect', __METHOD__ . ": Failed connecting to $i/$wiki" );
+                               unset( $nonErrorLoads[$i] );
+                               unset( $currentLoads[$i] );
+                               $i = false;
+                               continue;
+                       }
+
+                       // Decrement reference counter, we are finished with this connection.
+                       // It will be incremented for the caller later.
+                       if ( $wiki !== false ) {
+                               $this->reuseConnection( $conn );
+                       }
+
+                       # Return this server
+                       break;
+               }
+
+               # If all servers were down, quit now
+               if ( !count( $nonErrorLoads ) ) {
+                       wfDebugLog( 'connect', "All servers down" );
+               }
+
+               if ( $i !== false ) {
+                       # Slave connection successful
+                       # Wait for the session master pos for a short time
+                       if ( $this->mWaitForPos && $i > 0 ) {
+                               if ( !$this->doWait( $i ) ) {
+                                       $this->mServers[$i]['slave pos'] = $conn->getSlavePos();
+                               }
+                       }
+                       if ( $this->mReadIndex <= 0 && $this->mLoads[$i] > 0 && $group === false ) {
+                               $this->mReadIndex = $i;
+                               # Record if the generic reader index is in "lagged slave" mode
+                               if ( $laggedSlaveMode ) {
+                                       $this->mLaggedSlaveMode = true;
+                               }
+                       }
+                       $serverName = $this->getServerName( $i );
+                       wfDebug( __METHOD__ . ": using server $serverName for group '$group'\n" );
+               }
+
+               return $i;
+       }
+
+       /**
+        * Set the master wait position
+        * If a DB_SLAVE connection has been opened already, waits
+        * Otherwise sets a variable telling it to wait if such a connection is opened
+        * @param DBMasterPos $pos
+        */
+       public function waitFor( $pos ) {
+               $this->mWaitForPos = $pos;
+               $i = $this->mReadIndex;
+
+               if ( $i > 0 ) {
+                       if ( !$this->doWait( $i ) ) {
+                               $this->mServers[$i]['slave pos'] = $this->getAnyOpenConnection( $i )->getSlavePos();
+                               $this->mLaggedSlaveMode = true;
+                       }
+               }
+       }
+
+       /**
+        * Set the master wait position and wait for a "generic" slave to catch up to it
+        *
+        * This can be used a faster proxy for waitForAll()
+        *
+        * @param DBMasterPos $pos
+        * @param int $timeout Max seconds to wait; default is mWaitTimeout
+        * @return bool Success (able to connect and no timeouts reached)
+        * @since 1.26
+        */
+       public function waitForOne( $pos, $timeout = null ) {
+               $this->mWaitForPos = $pos;
+
+               $i = $this->mReadIndex;
+               if ( $i <= 0 ) {
+                       // Pick a generic slave if there isn't one yet
+                       $readLoads = $this->mLoads;
+                       unset( $readLoads[$this->getWriterIndex()] ); // slaves only
+                       $readLoads = array_filter( $readLoads ); // with non-zero load
+                       $i = ArrayUtils::pickRandom( $readLoads );
+               }
+
+               if ( $i > 0 ) {
+                       $ok = $this->doWait( $i, true, $timeout );
+               } else {
+                       $ok = true; // no applicable loads
+               }
+
+               return $ok;
+       }
+
+       /**
+        * Set the master wait position and wait for ALL slaves to catch up to it
+        * @param DBMasterPos $pos
+        * @param int $timeout Max seconds to wait; default is mWaitTimeout
+        * @return bool Success (able to connect and no timeouts reached)
+        */
+       public function waitForAll( $pos, $timeout = null ) {
+               $this->mWaitForPos = $pos;
+               $serverCount = count( $this->mServers );
+
+               $ok = true;
+               for ( $i = 1; $i < $serverCount; $i++ ) {
+                       if ( $this->mLoads[$i] > 0 ) {
+                               $ok = $this->doWait( $i, true, $timeout ) && $ok;
+                       }
+               }
+
+               return $ok;
+       }
+
+       /**
+        * Get any open connection to a given server index, local or foreign
+        * Returns false if there is no connection open
+        *
+        * @param int $i
+        * @return DatabaseBase|bool False on failure
+        */
+       public function getAnyOpenConnection( $i ) {
+               foreach ( $this->mConns as $conns ) {
+                       if ( !empty( $conns[$i] ) ) {
+                               return reset( $conns[$i] );
+                       }
+               }
+
+               return false;
+       }
+
+       /**
+        * Wait for a given slave to catch up to the master pos stored in $this
+        * @param int $index Server index
+        * @param bool $open Check the server even if a new connection has to be made
+        * @param int $timeout Max seconds to wait; default is mWaitTimeout
+        * @return bool
+        */
+       protected function doWait( $index, $open = false, $timeout = null ) {
+               $close = false; // close the connection afterwards
+
+               # Find a connection to wait on, creating one if needed and allowed
+               $conn = $this->getAnyOpenConnection( $index );
+               if ( !$conn ) {
+                       if ( !$open ) {
+                               wfDebug( __METHOD__ . ": no connection open\n" );
+
+                               return false;
+                       } else {
+                               $conn = $this->openConnection( $index, '' );
+                               if ( !$conn ) {
+                                       wfDebug( __METHOD__ . ": failed to open connection\n" );
+
+                                       return false;
+                               }
+                               // Avoid connection spam in waitForAll() when connections
+                               // are made just for the sake of doing this lag check.
+                               $close = true;
+                       }
+               }
+
+               wfDebug( __METHOD__ . ": Waiting for slave #$index to catch up...\n" );
+               $timeout = $timeout ?: $this->mWaitTimeout;
+               $result = $conn->masterPosWait( $this->mWaitForPos, $timeout );
+
+               if ( $result == -1 || is_null( $result ) ) {
+                       # Timed out waiting for slave, use master instead
+                       $server = $server = $this->getServerName( $index );
+                       $msg = __METHOD__ . ": Timed out waiting on $server pos {$this->mWaitForPos}";
+                       wfDebug( "$msg\n" );
+                       wfDebugLog( 'DBPerformance', "$msg:\n" . wfBacktrace( true ) );
+                       $ok = false;
+               } else {
+                       wfDebug( __METHOD__ . ": Done\n" );
+                       $ok = true;
+               }
+
+               if ( $close ) {
+                       $this->closeConnection( $conn );
+               }
+
+               return $ok;
+       }
+
+       /**
+        * Get a connection by index
+        * This is the main entry point for this class.
+        *
+        * @param int $i Server index
+        * @param array|string|bool $groups Query group(s), or false for the generic reader
+        * @param string|bool $wiki Wiki ID, or false for the current wiki
+        *
+        * @throws MWException
+        * @return DatabaseBase
+        */
+       public function getConnection( $i, $groups = array(), $wiki = false ) {
+               if ( $i === null || $i === false ) {
+                       throw new MWException( 'Attempt to call ' . __METHOD__ .
+                               ' with invalid server index' );
+               }
+
+               if ( $wiki === wfWikiID() ) {
+                       $wiki = false;
+               }
+
+               $groups = ( $groups === false || $groups === array() )
+                       ? array( false ) // check one "group": the generic pool
+                       : (array)$groups;
+
+               $masterOnly = ( $i == DB_MASTER || $i == $this->getWriterIndex() );
+               $oldConnsOpened = $this->connsOpened; // connections open now
+
+               if ( $i == DB_MASTER ) {
+                       $i = $this->getWriterIndex();
+               } else {
+                       # Try to find an available server in any the query groups (in order)
+                       foreach ( $groups as $group ) {
+                               $groupIndex = $this->getReaderIndex( $group, $wiki );
+                               if ( $groupIndex !== false ) {
+                                       $i = $groupIndex;
+                                       break;
+                               }
+                       }
+               }
+
+               # Operation-based index
+               if ( $i == DB_SLAVE ) {
+                       $this->mLastError = 'Unknown error'; // reset error string
+                       # Try the general server pool if $groups are unavailable.
+                       $i = in_array( false, $groups, true )
+                               ? false // don't bother with this if that is what was tried above
+                               : $this->getReaderIndex( false, $wiki );
+                       # Couldn't find a working server in getReaderIndex()?
+                       if ( $i === false ) {
+                               $this->mLastError = 'No working slave server: ' . $this->mLastError;
+
+                               return $this->reportConnectionError();
+                       }
+               }
+
+               # Now we have an explicit index into the servers array
+               $conn = $this->openConnection( $i, $wiki );
+               if ( !$conn ) {
+                       return $this->reportConnectionError();
+               }
+
+               # Profile any new connections that happen
+               if ( $this->connsOpened > $oldConnsOpened ) {
+                       $host = $conn->getServer();
+                       $dbname = $conn->getDBname();
+                       $trxProf = Profiler::instance()->getTransactionProfiler();
+                       $trxProf->recordConnection( $host, $dbname, $masterOnly );
+               }
+
+               return $conn;
+       }
+
+       /**
+        * Mark a foreign connection as being available for reuse under a different
+        * DB name or prefix. This mechanism is reference-counted, and must be called
+        * the same number of times as getConnection() to work.
+        *
+        * @param DatabaseBase $conn
+        * @throws MWException
+        */
+       public function reuseConnection( $conn ) {
+               $serverIndex = $conn->getLBInfo( 'serverIndex' );
+               $refCount = $conn->getLBInfo( 'foreignPoolRefCount' );
+               if ( $serverIndex === null || $refCount === null ) {
+                       wfDebug( __METHOD__ . ": this connection was not opened as a foreign connection\n" );
+
+                       /**
+                        * This can happen in code like:
+                        *   foreach ( $dbs as $db ) {
+                        *     $conn = $lb->getConnection( DB_SLAVE, array(), $db );
+                        *     ...
+                        *     $lb->reuseConnection( $conn );
+                        *   }
+                        * When a connection to the local DB is opened in this way, reuseConnection()
+                        * should be ignored
+                        */
+
+                       return;
+               }
+
+               $dbName = $conn->getDBname();
+               $prefix = $conn->tablePrefix();
+               if ( strval( $prefix ) !== '' ) {
+                       $wiki = "$dbName-$prefix";
+               } else {
+                       $wiki = $dbName;
+               }
+               if ( $this->mConns['foreignUsed'][$serverIndex][$wiki] !== $conn ) {
+                       throw new MWException( __METHOD__ . ": connection not found, has " .
+                               "the connection been freed already?" );
+               }
+               $conn->setLBInfo( 'foreignPoolRefCount', --$refCount );
+               if ( $refCount <= 0 ) {
+                       $this->mConns['foreignFree'][$serverIndex][$wiki] = $conn;
+                       unset( $this->mConns['foreignUsed'][$serverIndex][$wiki] );
+                       wfDebug( __METHOD__ . ": freed connection $serverIndex/$wiki\n" );
+               } else {
+                       wfDebug( __METHOD__ . ": reference count for $serverIndex/$wiki reduced to $refCount\n" );
+               }
+       }
+
+       /**
+        * Get a database connection handle reference
+        *
+        * The handle's methods wrap simply wrap those of a DatabaseBase handle
+        *
+        * @see LoadBalancer::getConnection() for parameter information
+        *
+        * @param int $db
+        * @param array|string|bool $groups Query group(s), or false for the generic reader
+        * @param string|bool $wiki Wiki ID, or false for the current wiki
+        * @return DBConnRef
+        */
+       public function getConnectionRef( $db, $groups = array(), $wiki = false ) {
+               return new DBConnRef( $this, $this->getConnection( $db, $groups, $wiki ) );
+       }
+
+       /**
+        * Get a database connection handle reference without connecting yet
+        *
+        * The handle's methods wrap simply wrap those of a DatabaseBase handle
+        *
+        * @see LoadBalancer::getConnection() for parameter information
+        *
+        * @param int $db
+        * @param array|string|bool $groups Query group(s), or false for the generic reader
+        * @param string|bool $wiki Wiki ID, or false for the current wiki
+        * @return DBConnRef
+        */
+       public function getLazyConnectionRef( $db, $groups = array(), $wiki = false ) {
+               return new DBConnRef( $this, array( $db, $groups, $wiki ) );
+       }
+
+       /**
+        * Open a connection to the server given by the specified index
+        * Index must be an actual index into the array.
+        * If the server is already open, returns it.
+        *
+        * On error, returns false, and the connection which caused the
+        * error will be available via $this->mErrorConnection.
+        *
+        * @param int $i Server index
+        * @param string|bool $wiki Wiki ID, or false for the current wiki
+        * @return DatabaseBase
+        *
+        * @access private
+        */
+       public function openConnection( $i, $wiki = false ) {
+               if ( $wiki !== false ) {
+                       $conn = $this->openForeignConnection( $i, $wiki );
+               } elseif ( isset( $this->mConns['local'][$i][0] ) ) {
+                       $conn = $this->mConns['local'][$i][0];
+               } else {
+                       $server = $this->mServers[$i];
+                       $server['serverIndex'] = $i;
+                       $conn = $this->reallyOpenConnection( $server, false );
+                       $serverName = $this->getServerName( $i );
+                       if ( $conn->isOpen() ) {
+                               wfDebug( "Connected to database $i at $serverName\n" );
+                               $this->mConns['local'][$i][0] = $conn;
+                       } else {
+                               wfDebug( "Failed to connect to database $i at $serverName\n" );
+                               $this->mErrorConnection = $conn;
+                               $conn = false;
+                       }
+               }
+
+               if ( $conn && !$conn->isOpen() ) {
+                       // Connection was made but later unrecoverably lost for some reason.
+                       // Do not return a handle that will just throw exceptions on use,
+                       // but let the calling code (e.g. getReaderIndex) try another server.
+                       // See DatabaseMyslBase::ping() for how this can happen.
+                       $this->mErrorConnection = $conn;
+                       $conn = false;
+               }
+
+               return $conn;
+       }
+
+       /**
+        * Open a connection to a foreign DB, or return one if it is already open.
+        *
+        * Increments a reference count on the returned connection which locks the
+        * connection to the requested wiki. This reference count can be
+        * decremented by calling reuseConnection().
+        *
+        * If a connection is open to the appropriate server already, but with the wrong
+        * database, it will be switched to the right database and returned, as long as
+        * it has been freed first with reuseConnection().
+        *
+        * On error, returns false, and the connection which caused the
+        * error will be available via $this->mErrorConnection.
+        *
+        * @param int $i Server index
+        * @param string $wiki Wiki ID to open
+        * @return DatabaseBase
+        */
+       private function openForeignConnection( $i, $wiki ) {
+               list( $dbName, $prefix ) = wfSplitWikiID( $wiki );
+               if ( isset( $this->mConns['foreignUsed'][$i][$wiki] ) ) {
+                       // Reuse an already-used connection
+                       $conn = $this->mConns['foreignUsed'][$i][$wiki];
+                       wfDebug( __METHOD__ . ": reusing connection $i/$wiki\n" );
+               } elseif ( isset( $this->mConns['foreignFree'][$i][$wiki] ) ) {
+                       // Reuse a free connection for the same wiki
+                       $conn = $this->mConns['foreignFree'][$i][$wiki];
+                       unset( $this->mConns['foreignFree'][$i][$wiki] );
+                       $this->mConns['foreignUsed'][$i][$wiki] = $conn;
+                       wfDebug( __METHOD__ . ": reusing free connection $i/$wiki\n" );
+               } elseif ( !empty( $this->mConns['foreignFree'][$i] ) ) {
+                       // Reuse a connection from another wiki
+                       $conn = reset( $this->mConns['foreignFree'][$i] );
+                       $oldWiki = key( $this->mConns['foreignFree'][$i] );
+
+                       // The empty string as a DB name means "don't care".
+                       // DatabaseMysqlBase::open() already handle this on connection.
+                       if ( $dbName !== '' && !$conn->selectDB( $dbName ) ) {
+                               $this->mLastError = "Error selecting database $dbName on server " .
+                                       $conn->getServer() . " from client host " . wfHostname() . "\n";
+                               $this->mErrorConnection = $conn;
+                               $conn = false;
+                       } else {
+                               $conn->tablePrefix( $prefix );
+                               unset( $this->mConns['foreignFree'][$i][$oldWiki] );
+                               $this->mConns['foreignUsed'][$i][$wiki] = $conn;
+                               wfDebug( __METHOD__ . ": reusing free connection from $oldWiki for $wiki\n" );
+                       }
+               } else {
+                       // Open a new connection
+                       $server = $this->mServers[$i];
+                       $server['serverIndex'] = $i;
+                       $server['foreignPoolRefCount'] = 0;
+                       $server['foreign'] = true;
+                       $conn = $this->reallyOpenConnection( $server, $dbName );
+                       if ( !$conn->isOpen() ) {
+                               wfDebug( __METHOD__ . ": error opening connection for $i/$wiki\n" );
+                               $this->mErrorConnection = $conn;
+                               $conn = false;
+                       } else {
+                               $conn->tablePrefix( $prefix );
+                               $this->mConns['foreignUsed'][$i][$wiki] = $conn;
+                               wfDebug( __METHOD__ . ": opened new connection for $i/$wiki\n" );
+                       }
+               }
+
+               // Increment reference count
+               if ( $conn ) {
+                       $refCount = $conn->getLBInfo( 'foreignPoolRefCount' );
+                       $conn->setLBInfo( 'foreignPoolRefCount', $refCount + 1 );
+               }
+
+               return $conn;
+       }
+
+       /**
+        * Test if the specified index represents an open connection
+        *
+        * @param int $index Server index
+        * @access private
+        * @return bool
+        */
+       private function isOpen( $index ) {
+               if ( !is_integer( $index ) ) {
+                       return false;
+               }
+
+               return (bool)$this->getAnyOpenConnection( $index );
+       }
+
+       /**
+        * Really opens a connection. Uncached.
+        * Returns a Database object whether or not the connection was successful.
+        * @access private
+        *
+        * @param array $server
+        * @param bool $dbNameOverride
+        * @throws MWException
+        * @return DatabaseBase
+        */
+       protected function reallyOpenConnection( $server, $dbNameOverride = false ) {
+               if ( !is_array( $server ) ) {
+                       throw new MWException( 'You must update your load-balancing configuration. ' .
+                               'See DefaultSettings.php entry for $wgDBservers.' );
+               }
+
+               if ( $dbNameOverride !== false ) {
+                       $server['dbname'] = $dbNameOverride;
+               }
+
+               // Log when many connection are made on requests
+               if ( ++$this->connsOpened >= self::CONN_HELD_WARN_THRESHOLD ) {
+                       $masterAddr = $this->getServerName( 0 );
+                       wfDebugLog( 'DBPerformance', __METHOD__ . ": " .
+                               "{$this->connsOpened}+ connections made (master=$masterAddr)\n" .
+                               wfBacktrace( true ) );
+               }
+
+               # Create object
+               try {
+                       $db = DatabaseBase::factory( $server['type'], $server );
+               } catch ( DBConnectionError $e ) {
+                       // FIXME: This is probably the ugliest thing I have ever done to
+                       // PHP. I'm half-expecting it to segfault, just out of disgust. -- TS
+                       $db = $e->db;
+               }
+
+               $db->setLBInfo( $server );
+               if ( isset( $server['fakeSlaveLag'] ) ) {
+                       $db->setFakeSlaveLag( $server['fakeSlaveLag'] );
+               }
+               if ( isset( $server['fakeMaster'] ) ) {
+                       $db->setFakeMaster( true );
+               }
+
+               return $db;
+       }
+
+       /**
+        * @throws DBConnectionError
+        * @return bool
+        */
+       private function reportConnectionError() {
+               $conn = $this->mErrorConnection; // The connection which caused the error
+               $context = array(
+                       'method' => __METHOD__,
+                       'last_error' => $this->mLastError,
+               );
+
+               if ( !is_object( $conn ) ) {
+                       // No last connection, probably due to all servers being too busy
+                       wfLogDBError(
+                               "LB failure with no last connection. Connection error: {last_error}",
+                               $context
+                       );
+
+                       // If all servers were busy, mLastError will contain something sensible
+                       throw new DBConnectionError( null, $this->mLastError );
+               } else {
+                       $context['db_server'] = $conn->getProperty( 'mServer' );
+                       wfLogDBError(
+                               "Connection error: {last_error} ({db_server})",
+                               $context
+                       );
+                       $conn->reportConnectionError( "{$this->mLastError} ({$context['db_server']})" ); // throws DBConnectionError
+               }
+
+               return false; /* not reached */
+       }
+
+       /**
+        * @return int
+        * @since 1.26
+        */
+       public function getWriterIndex() {
+               return 0;
+       }
+
+       /**
+        * Returns true if the specified index is a valid server index
+        *
+        * @param string $i
+        * @return bool
+        */
+       public function haveIndex( $i ) {
+               return array_key_exists( $i, $this->mServers );
+       }
+
+       /**
+        * Returns true if the specified index is valid and has non-zero load
+        *
+        * @param string $i
+        * @return bool
+        */
+       public function isNonZeroLoad( $i ) {
+               return array_key_exists( $i, $this->mServers ) && $this->mLoads[$i] != 0;
+       }
+
+       /**
+        * Get the number of defined servers (not the number of open connections)
+        *
+        * @return int
+        */
+       public function getServerCount() {
+               return count( $this->mServers );
+       }
+
+       /**
+        * Get the host name or IP address of the server with the specified index
+        * Prefer a readable name if available.
+        * @param string $i
+        * @return string
+        */
+       public function getServerName( $i ) {
+               if ( isset( $this->mServers[$i]['hostName'] ) ) {
+                       $name = $this->mServers[$i]['hostName'];
+               } elseif ( isset( $this->mServers[$i]['host'] ) ) {
+                       $name = $this->mServers[$i]['host'];
+               } else {
+                       $name = '';
+               }
+
+               return ( $name != '' ) ? $name : 'localhost';
+       }
+
+       /**
+        * Return the server info structure for a given index, or false if the index is invalid.
+        * @param int $i
+        * @return array|bool
+        */
+       public function getServerInfo( $i ) {
+               if ( isset( $this->mServers[$i] ) ) {
+                       return $this->mServers[$i];
+               } else {
+                       return false;
+               }
+       }
+
+       /**
+        * Sets the server info structure for the given index. Entry at index $i
+        * is created if it doesn't exist
+        * @param int $i
+        * @param array $serverInfo
+        */
+       public function setServerInfo( $i, array $serverInfo ) {
+               $this->mServers[$i] = $serverInfo;
+       }
+
+       /**
+        * Get the current master position for chronology control purposes
+        * @return mixed
+        */
+       public function getMasterPos() {
+               # If this entire request was served from a slave without opening a connection to the
+               # master (however unlikely that may be), then we can fetch the position from the slave.
+               $masterConn = $this->getAnyOpenConnection( 0 );
+               if ( !$masterConn ) {
+                       $serverCount = count( $this->mServers );
+                       for ( $i = 1; $i < $serverCount; $i++ ) {
+                               $conn = $this->getAnyOpenConnection( $i );
+                               if ( $conn ) {
+                                       wfDebug( "Master pos fetched from slave\n" );
+
+                                       return $conn->getSlavePos();
+                               }
+                       }
+               } else {
+                       wfDebug( "Master pos fetched from master\n" );
+
+                       return $masterConn->getMasterPos();
+               }
+
+               return false;
+       }
+
+       /**
+        * Close all open connections
+        */
+       public function closeAll() {
+               foreach ( $this->mConns as $conns2 ) {
+                       foreach ( $conns2 as $conns3 ) {
+                               /** @var DatabaseBase $conn */
+                               foreach ( $conns3 as $conn ) {
+                                       $conn->close();
+                               }
+                       }
+               }
+               $this->mConns = array(
+                       'local' => array(),
+                       'foreignFree' => array(),
+                       'foreignUsed' => array(),
+               );
+               $this->connsOpened = 0;
+       }
+
+       /**
+        * Close a connection
+        * Using this function makes sure the LoadBalancer knows the connection is closed.
+        * If you use $conn->close() directly, the load balancer won't update its state.
+        * @param DatabaseBase $conn
+        */
+       public function closeConnection( $conn ) {
+               $done = false;
+               foreach ( $this->mConns as $i1 => $conns2 ) {
+                       foreach ( $conns2 as $i2 => $conns3 ) {
+                               foreach ( $conns3 as $i3 => $candidateConn ) {
+                                       if ( $conn === $candidateConn ) {
+                                               $conn->close();
+                                               unset( $this->mConns[$i1][$i2][$i3] );
+                                               --$this->connsOpened;
+                                               $done = true;
+                                               break;
+                                       }
+                               }
+                       }
+               }
+               if ( !$done ) {
+                       $conn->close();
+               }
+       }
+
+       /**
+        * Commit transactions on all open connections
+        */
+       public function commitAll() {
+               foreach ( $this->mConns as $conns2 ) {
+                       foreach ( $conns2 as $conns3 ) {
+                               /** @var DatabaseBase[] $conns3 */
+                               foreach ( $conns3 as $conn ) {
+                                       if ( $conn->trxLevel() ) {
+                                               $conn->commit( __METHOD__, 'flush' );
+                                       }
+                               }
+                       }
+               }
+       }
+
+       /**
+        *  Issue COMMIT only on master, only if queries were done on connection
+        */
+       public function commitMasterChanges() {
+               $masterIndex = $this->getWriterIndex();
+               foreach ( $this->mConns as $conns2 ) {
+                       if ( empty( $conns2[$masterIndex] ) ) {
+                               continue;
+                       }
+                       /** @var DatabaseBase $conn */
+                       foreach ( $conns2[$masterIndex] as $conn ) {
+                               if ( $conn->trxLevel() && $conn->writesOrCallbacksPending() ) {
+                                       $conn->commit( __METHOD__, 'flush' );
+                               }
+                       }
+               }
+       }
+
+       /**
+        * Issue ROLLBACK only on master, only if queries were done on connection
+        * @since 1.23
+        */
+       public function rollbackMasterChanges() {
+               $failedServers = array();
+
+               $masterIndex = $this->getWriterIndex();
+               foreach ( $this->mConns as $conns2 ) {
+                       if ( empty( $conns2[$masterIndex] ) ) {
+                               continue;
+                       }
+                       /** @var DatabaseBase $conn */
+                       foreach ( $conns2[$masterIndex] as $conn ) {
+                               if ( $conn->trxLevel() && $conn->writesOrCallbacksPending() ) {
+                                       try {
+                                               $conn->rollback( __METHOD__, 'flush' );
+                                       } catch ( DBError $e ) {
+                                               MWExceptionHandler::logException( $e );
+                                               $failedServers[] = $conn->getServer();
+                                       }
+                               }
+                       }
+               }
+
+               if ( $failedServers ) {
+                       throw new DBExpectedError( null, "Rollback failed on server(s) " .
+                               implode( ', ', array_unique( $failedServers ) ) );
+               }
+       }
+
+       /**
+        * @return bool Whether a master connection is already open
+        * @since 1.24
+        */
+       public function hasMasterConnection() {
+               return $this->isOpen( $this->getWriterIndex() );
+       }
+
+       /**
+        * Determine if there are pending changes in a transaction by this thread
+        * @since 1.23
+        * @return bool
+        */
+       public function hasMasterChanges() {
+               $masterIndex = $this->getWriterIndex();
+               foreach ( $this->mConns as $conns2 ) {
+                       if ( empty( $conns2[$masterIndex] ) ) {
+                               continue;
+                       }
+                       /** @var DatabaseBase $conn */
+                       foreach ( $conns2[$masterIndex] as $conn ) {
+                               if ( $conn->trxLevel() && $conn->writesOrCallbacksPending() ) {
+                                       return true;
+                               }
+                       }
+               }
+               return false;
+       }
+
+       /**
+        * Get the timestamp of the latest write query done by this thread
+        * @since 1.25
+        * @return float|bool UNIX timestamp or false
+        */
+       public function lastMasterChangeTimestamp() {
+               $lastTime = false;
+               $masterIndex = $this->getWriterIndex();
+               foreach ( $this->mConns as $conns2 ) {
+                       if ( empty( $conns2[$masterIndex] ) ) {
+                               continue;
+                       }
+                       /** @var DatabaseBase $conn */
+                       foreach ( $conns2[$masterIndex] as $conn ) {
+                               $lastTime = max( $lastTime, $conn->lastDoneWrites() );
+                       }
+               }
+               return $lastTime;
+       }
+
+       /**
+        * Check if this load balancer object had any recent or still
+        * pending writes issued against it by this PHP thread
+        *
+        * @param float $age How many seconds ago is "recent" [defaults to mWaitTimeout]
+        * @return bool
+        * @since 1.25
+        */
+       public function hasOrMadeRecentMasterChanges( $age = null ) {
+               $age = ( $age === null ) ? $this->mWaitTimeout : $age;
+
+               return ( $this->hasMasterChanges()
+                       || $this->lastMasterChangeTimestamp() > microtime( true ) - $age );
+       }
+
+       /**
+        * @param mixed $value
+        * @return mixed
+        */
+       public function waitTimeout( $value = null ) {
+               return wfSetVar( $this->mWaitTimeout, $value );
+       }
+
+       /**
+        * @return bool Whether the generic connection for reads is highly "lagged"
+        */
+       public function getLaggedSlaveMode() {
+               # Get a generic reader connection
+               $this->getConnection( DB_SLAVE );
+
+               return $this->mLaggedSlaveMode;
+       }
+
+       /**
+        * Disables/enables lag checks
+        * @param null|bool $mode
+        * @return bool
+        */
+       public function allowLagged( $mode = null ) {
+               if ( $mode === null ) {
+                       return $this->mAllowLagged;
+               }
+               $this->mAllowLagged = $mode;
+
+               return $this->mAllowLagged;
+       }
+
+       /**
+        * @return bool
+        */
+       public function pingAll() {
+               $success = true;
+               foreach ( $this->mConns as $conns2 ) {
+                       foreach ( $conns2 as $conns3 ) {
+                               /** @var DatabaseBase[] $conns3 */
+                               foreach ( $conns3 as $conn ) {
+                                       if ( !$conn->ping() ) {
+                                               $success = false;
+                                       }
+                               }
+                       }
+               }
+
+               return $success;
+       }
+
+       /**
+        * Call a function with each open connection object
+        * @param callable $callback
+        * @param array $params
+        */
+       public function forEachOpenConnection( $callback, array $params = array() ) {
+               foreach ( $this->mConns as $conns2 ) {
+                       foreach ( $conns2 as $conns3 ) {
+                               foreach ( $conns3 as $conn ) {
+                                       $mergedParams = array_merge( array( $conn ), $params );
+                                       call_user_func_array( $callback, $mergedParams );
+                               }
+                       }
+               }
+       }
+
+       /**
+        * Get the hostname and lag time of the most-lagged slave
+        *
+        * This is useful for maintenance scripts that need to throttle their updates.
+        * May attempt to open connections to slaves on the default DB. If there is
+        * no lag, the maximum lag will be reported as -1.
+        *
+        * @param bool|string $wiki Wiki ID, or false for the default database
+        * @return array ( host, max lag, index of max lagged host )
+        */
+       public function getMaxLag( $wiki = false ) {
+               $maxLag = -1;
+               $host = '';
+               $maxIndex = 0;
+
+               if ( $this->getServerCount() <= 1 ) {
+                       return array( $host, $maxLag, $maxIndex ); // no replication = no lag
+               }
+
+               $lagTimes = $this->getLagTimes( $wiki );
+               foreach ( $lagTimes as $i => $lag ) {
+                       if ( $lag > $maxLag ) {
+                               $maxLag = $lag;
+                               $host = $this->mServers[$i]['host'];
+                               $maxIndex = $i;
+                       }
+               }
+
+               return array( $host, $maxLag, $maxIndex );
+       }
+
+       /**
+        * Get lag time for each server
+        *
+        * Results are cached for a short time in memcached/process cache
+        *
+        * @param string|bool $wiki
+        * @return int[] Map of (server index => seconds)
+        */
+       public function getLagTimes( $wiki = false ) {
+               if ( $this->getServerCount() <= 1 ) {
+                       return array( 0 => 0 ); // no replication = no lag
+               }
+
+               # Send the request to the load monitor
+               return $this->getLoadMonitor()->getLagTimes( array_keys( $this->mServers ), $wiki );
+       }
+
+       /**
+        * Get the lag in seconds for a given connection, or zero if this load
+        * balancer does not have replication enabled.
+        *
+        * This should be used in preference to Database::getLag() in cases where
+        * replication may not be in use, since there is no way to determine if
+        * replication is in use at the connection level without running
+        * potentially restricted queries such as SHOW SLAVE STATUS. Using this
+        * function instead of Database::getLag() avoids a fatal error in this
+        * case on many installations.
+        *
+        * @param DatabaseBase $conn
+        * @return int
+        */
+       public function safeGetLag( $conn ) {
+               if ( $this->getServerCount() == 1 ) {
+                       return 0;
+               } else {
+                       return $conn->getLag();
+               }
+       }
+
+       /**
+        * Clear the cache for slag lag delay times
+        *
+        * This is only used for testing
+        */
+       public function clearLagTimeCache() {
+               $this->getLoadMonitor()->clearCaches();
+       }
+}
diff --git a/includes/db/loadbalancer/LoadMonitor.php b/includes/db/loadbalancer/LoadMonitor.php
new file mode 100644 (file)
index 0000000..4975ea1
--- /dev/null
@@ -0,0 +1,66 @@
+<?php
+/**
+ * Database load monitoring.
+ *
+ * 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 Database
+ */
+
+/**
+ * An interface for database load monitoring
+ *
+ * @ingroup Database
+ */
+interface LoadMonitor {
+       /**
+        * Construct a new LoadMonitor with a given LoadBalancer parent
+        *
+        * @param LoadBalancer $parent
+        */
+       public function __construct( $parent );
+
+       /**
+        * Perform pre-connection load ratio adjustment.
+        * @param array $loads
+        * @param string|bool $group The selected query group. Default: false
+        * @param string|bool $wiki Default: false
+        */
+       public function scaleLoads( &$loads, $group = false, $wiki = false );
+
+       /**
+        * Return an estimate of replication lag for each server
+        *
+        * @param array $serverIndexes
+        * @param string $wiki
+        *
+        * @return array Map of (server index => seconds)
+        */
+       public function getLagTimes( $serverIndexes, $wiki );
+}
+
+class LoadMonitorNull implements LoadMonitor {
+       public function __construct( $parent ) {
+       }
+
+       public function scaleLoads( &$loads, $group = false, $wiki = false ) {
+       }
+
+       public function getLagTimes( $serverIndexes, $wiki ) {
+               return array_fill_keys( $serverIndexes, 0 );
+       }
+}
diff --git a/includes/db/loadbalancer/LoadMonitorMySQL.php b/includes/db/loadbalancer/LoadMonitorMySQL.php
new file mode 100644 (file)
index 0000000..3008419
--- /dev/null
@@ -0,0 +1,124 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup Database
+ */
+
+/**
+ * Basic MySQL load monitor with no external dependencies
+ * Uses memcached to cache the replication lag for a short time
+ *
+ * @ingroup Database
+ */
+class LoadMonitorMySQL implements LoadMonitor {
+       /** @var LoadBalancer */
+       public $parent;
+       /** @var BagOStuff */
+       protected $srvCache;
+       /** @var BagOStuff */
+       protected $mainCache;
+
+       public function __construct( $parent ) {
+               $this->parent = $parent;
+
+               $this->srvCache = ObjectCache::newAccelerator( 'hash' );
+               $this->mainCache = wfGetMainCache();
+       }
+
+       public function scaleLoads( &$loads, $group = false, $wiki = false ) {
+       }
+
+       public function getLagTimes( $serverIndexes, $wiki ) {
+               if ( count( $serverIndexes ) == 1 && reset( $serverIndexes ) == 0 ) {
+                       # Single server only, just return zero without caching
+                       return array( 0 => 0 );
+               }
+
+               $key = $this->getLagTimeCacheKey();
+               # Randomize TTLs to reduce stampedes (4.0 - 5.0 sec)
+               $ttl = mt_rand( 4e6, 5e6 ) / 1e6;
+               # Keep keys around longer as fallbacks
+               $staleTTL = 60;
+
+               # (a) Check the local APC cache
+               $value = $this->srvCache->get( $key );
+               if ( $value && $value['timestamp'] > ( microtime( true ) - $ttl ) ) {
+                       wfDebugLog( 'replication',  __FUNCTION__ . ": got lag times ($key) from local cache" );
+                       return $value['lagTimes']; // cache hit
+               }
+               $staleValue = $value ?: false;
+
+               # (b) Check the shared cache and backfill APC
+               $value = $this->mainCache->get( $key );
+               if ( $value && $value['timestamp'] > ( microtime( true ) - $ttl ) ) {
+                       $this->srvCache->set( $key, $value, $staleTTL );
+                       wfDebugLog( 'replication',  __FUNCTION__ . ": got lag times ($key) from main cache" );
+
+                       return $value['lagTimes']; // cache hit
+               }
+               $staleValue = $value ?: $staleValue;
+
+               # (c) Cache key missing or expired; regenerate and backfill
+               if ( $this->mainCache->lock( $key, 0, 10 ) ) {
+                       # Let this process alone update the cache value
+                       $cache = $this->mainCache;
+                       /** @noinspection PhpUnusedLocalVariableInspection */
+                       $unlocker = new ScopedCallback( function () use ( $cache, $key ) {
+                               $cache->unlock( $key );
+                       } );
+               } elseif ( $staleValue ) {
+                       # Could not acquire lock but an old cache exists, so use it
+                       return $staleValue['lagTimes'];
+               }
+
+               $lagTimes = array();
+               foreach ( $serverIndexes as $i ) {
+                       if ( $i == 0 ) { # Master
+                               $lagTimes[$i] = 0;
+                       } elseif ( false !== ( $conn = $this->parent->getAnyOpenConnection( $i ) ) ) {
+                               $lagTimes[$i] = $conn->getLag();
+                       } elseif ( false !== ( $conn = $this->parent->openConnection( $i, $wiki ) ) ) {
+                               $lagTimes[$i] = $conn->getLag();
+                               # Close the connection to avoid sleeper connections piling up.
+                               # Note that the caller will pick one of these DBs and reconnect,
+                               # which is slightly inefficient, but this only matters for the lag
+                               # time cache miss cache, which is far less common that cache hits.
+                               $this->parent->closeConnection( $conn );
+                       }
+               }
+
+               # Add a timestamp key so we know when it was cached
+               $value = array( 'lagTimes' => $lagTimes, 'timestamp' => microtime( true ) );
+               $this->mainCache->set( $key, $value, $staleTTL );
+               $this->srvCache->set( $key, $value, $staleTTL );
+               wfDebugLog( 'replication',  __FUNCTION__ . ": re-calculated lag times ($key)" );
+
+               return $value['lagTimes'];
+       }
+
+       public function clearCaches() {
+               $key = $this->getLagTimeCacheKey();
+               $this->srvCache->delete( $key );
+               $this->mainCache->delete( $key );
+       }
+
+       private function getLagTimeCacheKey() {
+               # Lag is per-server, not per-DB, so key on the master DB name
+               return wfGlobalCacheKey( 'lag-times', $this->parent->getServerName( 0 ) );
+       }
+}
index 3ebd0b1..ea286b3 100644 (file)
@@ -37,7 +37,7 @@ class BufferHandler extends BaseBufferHandler {
         * {@inheritDoc}
         */
        public function handle( array $record ) {
-               if (!$this->initialized) {
+               if ( !$this->initialized ) {
                        DeferredUpdates::addCallableUpdate( array( $this, 'close' ) );
                        $this->initialized = true;
                }
index 59d7764..1583cd1 100644 (file)
@@ -133,7 +133,7 @@ class KafkaHandler extends AbstractProcessingHandler {
                                }
                        }
                        if ( $messages ) {
-                               $this->addMessages($channel, $messages);
+                               $this->addMessages( $channel, $messages );
                        }
                }
 
index ed12c60..9cbb62f 100644 (file)
  *       subclasses can override the beginTransaction() and commitTransaction() methods.
  */
 abstract class DataUpdate implements DeferrableUpdate {
-       /**
-        * Constructor
-        */
        public function __construct() {
-               noop
+               //noop
        }
 
        /**
@@ -73,22 +70,23 @@ abstract class DataUpdate implements DeferrableUpdate {
         * This allows for limited transactional logic across multiple backends for storing
         * secondary data.
         *
-        * @param array $updates A list of DataUpdate instances
+        * @param DataUpdate[] $updates A list of DataUpdate instances
+        * @param string $mode Use "enqueue" to use the job queue when possible [Default: run]
         * @throws Exception|null
         */
-       public static function runUpdates( $updates ) {
-               if ( empty( $updates ) ) {
-                       return; # nothing to do
+       public static function runUpdates( array $updates, $mode = 'run' ) {
+               if ( $mode === 'enqueue' ) {
+                       // When possible, push updates as jobs instead of calling doUpdate()
+                       $updates = self::enqueueUpdates( $updates );
+               }
+
+               if ( !count( $updates ) ) {
+                       return; // nothing to do
                }
 
                $open_transactions = array();
                $exception = null;
 
-               /**
-                * @var $update DataUpdate
-                * @var $trans DataUpdate
-                */
-
                try {
                        // begin transactions
                        foreach ( $updates as $update ) {
@@ -122,4 +120,37 @@ abstract class DataUpdate implements DeferrableUpdate {
                        throw $exception; // rethrow after cleanup
                }
        }
+
+       /**
+        * Enqueue jobs for every DataUpdate that support enqueueUpdate()
+        * and return the remaining DataUpdate objects (those that do not)
+        *
+        * @param DataUpdate[] $updates A list of DataUpdate instances
+        * @return DataUpdate[]
+        * @since 1.26
+        */
+       protected static function enqueueUpdates( array $updates ) {
+               $remaining = array();
+
+               foreach ( $updates as $update ) {
+                       if ( $update instanceof EnqueueableDataUpdate ) {
+                               $update->enqueueUpdate();
+                       } else {
+                               $remaining[] = $update;
+                       }
+               }
+
+               return $remaining;
+       }
 }
+
+/**
+ * @since 1.26
+ */
+interface EnqueueableDataUpdate {
+       /**
+        * Push the update into the job queue
+        */
+       public function enqueueUpdate();
+}
+
index bbdfcf1..7351e4c 100644 (file)
@@ -102,4 +102,5 @@ class LinksDeletionUpdate extends SqlDataUpdate {
                        }
                }
        }
-}
\ No newline at end of file
+}
+
index 83e04a5..4737f08 100644 (file)
@@ -62,8 +62,14 @@ class TableDiffFormatter extends DiffFormatter {
        protected function blockHeader( $xbeg, $xlen, $ybeg, $ylen ) {
                // '<!--LINE \d+ -->' get replaced by a localised line number
                // in DifferenceEngine::localiseLineNumbers
-               $r = '<tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l' . $xbeg . '" ><!--LINE ' . $xbeg . "--></td>\n" .
-                       '<td colspan="2" class="diff-lineno"><!--LINE ' . $ybeg . "--></td></tr>\n";
+               $r = '<tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l' .
+                       $xbeg .
+                       '" ><!--LINE ' .
+                       $xbeg .
+                       "--></td>\n" .
+                       '<td colspan="2" class="diff-lineno"><!--LINE ' .
+                       $ybeg .
+                       "--></td></tr>\n";
 
                return $r;
        }
index d25f1a8..4e50070 100644 (file)
@@ -485,6 +485,14 @@ TXT;
                return "[$id] $url   $type from line $line of $file: $message";
        }
 
+       public static function getPublicLogMessage( Exception $e ) {
+               $logId = self::getLogId( $e );
+               $type = get_class( $e );
+               return '[' . $logId . '] '
+                       . gmdate( 'Y-m-d H:i:s' ) . ': '
+                       . 'Fatal exception of type ' . $type;
+       }
+
        /**
         * Get a PSR-3 log event context from an Exception.
         *
index d2c37e6..4c9c2aa 100644 (file)
@@ -456,7 +456,7 @@ class LocalFile extends File {
        }
 
        /**
-        * @param array $row Row
+        * @param array|object $row
         * @param string $prefix
         * @throws MWException
         * @return array
index 0df6f84..0934f6a 100644 (file)
@@ -46,6 +46,7 @@ class TraditionalImageGallery extends ImageGalleryBase {
                        array( 'class' => 'gallery mw-gallery-' . $this->mMode ), $this->mAttribs );
 
                $modules = $this->getModules();
+               $modules[] = 'mediawiki.page.gallery.styles';
 
                if ( $this->mParser ) {
                        $this->mParser->getOutput()->addModules( $modules );
index 08fa0a9..2779d5a 100644 (file)
@@ -537,6 +537,12 @@ class HTMLForm extends ContextSource {
         *       params) or strings (message keys)
         */
        function trySubmit() {
+               $valid = true;
+               $hoistedErrors = array();
+               $hoistedErrors[] = isset( $this->mValidationErrorMessage )
+                       ? $this->mValidationErrorMessage
+                       : array( 'htmlform-invalid-input' );
+
                $this->mWasSubmitted = true;
 
                # Check for cancelled submission
@@ -558,15 +564,20 @@ class HTMLForm extends ContextSource {
                        if ( $field->isHidden( $this->mFieldData ) ) {
                                continue;
                        }
-                       if ( $field->validate(
-                                       $this->mFieldData[$fieldname],
-                                       $this->mFieldData )
-                               !== true
-                       ) {
-                               return isset( $this->mValidationErrorMessage )
-                                       ? $this->mValidationErrorMessage
-                                       : array( 'htmlform-invalid-input' );
+                       $res = $field->validate( $this->mFieldData[$fieldname], $this->mFieldData );
+                       if ( $res !== true ) {
+                               $valid = false;
+                               if ( $res !== false && !$field->canDisplayErrors() ) {
+                                       $hoistedErrors[] = array( 'rawmessage', $res );
+                               }
+                       }
+               }
+
+               if ( !$valid ) {
+                       if ( count( $hoistedErrors ) === 1 ) {
+                               $hoistedErrors = $hoistedErrors[0];
                        }
+                       return $hoistedErrors;
                }
 
                $callback = $this->mSubmitCallback;
index 13756e3..484b29a 100644 (file)
@@ -55,6 +55,15 @@ abstract class HTMLFormField {
                return false;
        }
 
+       /**
+        * True if this field type is able to display errors; false if validation errors need to be
+        * displayed in the main HTMLForm error area.
+        * @return bool
+        */
+       public function canDisplayErrors() {
+               return true;
+       }
+
        /**
         * Get a translated interface message
         *
index ffde915..e4695f7 100644 (file)
@@ -55,4 +55,8 @@ class HTMLHiddenField extends HTMLFormField {
        public function getInputHTML( $value ) {
                return '';
        }
+
+       public function canDisplayErrors() {
+               return false;
+       }
 }
index 84d40a1..f8aa0ac 100644 (file)
@@ -133,7 +133,7 @@ class OOUIHTMLForm extends HTMLForm {
        function getErrors( $err ) {
                if ( !$err ) {
                        $errors = array();
-               } else if ( $err instanceof Status ) {
+               } elseif ( $err instanceof Status ) {
                        if ( $err->isOK() ) {
                                $errors = array();
                        } else {
index 662469b..d98ca79 100644 (file)
@@ -534,13 +534,15 @@ abstract class Installer {
                // then some poorly-formed extensions try to call their own classes
                // after immediately registering them. We really need to get extension
                // registration out of the global scope and into a real format.
-               // @see https://bugzilla.wikimedia.org/67440
+               // @see https://phabricator.wikimedia.org/T69440
                global $wgAutoloadClasses;
                $wgAutoloadClasses = array();
 
+               // @codingStandardsIgnoreStart
                // LocalSettings.php should not call functions, except wfLoadSkin/wfLoadExtensions
                // Define the required globals here, to ensure, the functions can do it work correctly.
                global $wgExtensionDirectory, $wgStyleDirectory;
+               // @codingStandardsIgnoreEnd
 
                MediaWiki\suppressWarnings();
                $_lsExists = file_exists( "$IP/LocalSettings.php" );
@@ -1477,7 +1479,7 @@ abstract class Installer {
                 * want here is $wgHooks['LoadExtensionSchemaUpdates']. This won't work
                 * if the extension has hidden hook registration in $wgExtensionFunctions,
                 * but we're not opening that can of worms
-                * @see https://bugzilla.wikimedia.org/show_bug.cgi?id=26857
+                * @see https://phabricator.wikimedia.org/T28857
                 */
                global $wgAutoloadClasses;
                $wgAutoloadClasses = array();
index f7e4e67..078ff72 100644 (file)
@@ -2,14 +2,15 @@
        "@metadata": {
                "authors": [
                        "Eitvys200",
-                       "Mantak111"
+                       "Mantak111",
+                       "Zygimantus"
                ]
        },
        "config-desc": "MediaWiki diegimas",
        "config-title": "MediaWiki $1 diegimas",
        "config-information": "Informacija",
        "config-localsettings-key": "Naujinimo raktas:",
-       "config-localsettings-badkey": "Raktą, kurį pateikėte yra neteisingas.",
+       "config-localsettings-badkey": "Raktas, kurį pateikėte, yra neteisingas.",
        "config-your-language": "Jūsų kalba:",
        "config-wiki-language": "Viki kalba:",
        "config-back": "← Atgal",
        "config-page-install": "Įdiegti",
        "config-page-complete": "Baigta!",
        "config-page-restart": "Iš naujo paleiskite diegimą",
-       "config-page-readme": "Perskaityk manę",
+       "config-page-readme": "Skaityti daugiau",
        "config-page-copying": "Kopijuojama",
        "config-page-upgradedoc": "Atnaujinama",
-       "config-page-existingwiki": "Esamas wiki",
+       "config-page-existingwiki": "Esamas viki",
        "config-restart": "Taip, paleiskite jį iš naujo",
        "config-env-php": "PHP $1 yra įdiegtas.",
-       "config-env-php-toolow": "PHP $1 įdiegta.\nTačiau, MediaWiki reikia PHP $2 ar naujesnės.",
        "config-db-type": "Duomenų bazės tipas:",
        "config-db-host": "Duomenų bazės serveris:",
        "config-db-name": "Duomenų bazės pavadinimas:",
        "config-mssql-windowsauth": "Windows autentifikavimas",
        "config-site-name": "Viki pavadinimas:",
        "config-site-name-blank": "Įveskite svetainės pavadinimą.",
-       "config-project-namespace": "Projekto pavadinimas:",
+       "config-project-namespace": "Projekto vardų sritis:",
        "config-ns-generic": "Projektas",
        "config-ns-site-name": "Toks pat kaip viki pavadinimas: $1",
-       "config-ns-other-default": "ManoWiki",
+       "config-ns-other-default": "ManoViki",
        "config-admin-box": "Administratoriaus paskyra",
-       "config-admin-name": "Jūsų vardas:",
+       "config-admin-name": "Jūsų naudotojo vardas:",
        "config-admin-password": "Slaptažodis:",
        "config-admin-password-confirm": "Slaptažodis dar kartą:",
        "config-admin-name-blank": "Įveskite administratoriaus vartotojo vardą.",
@@ -67,9 +67,9 @@
        "config-optional-continue": "Paklausti daugiau klausimų.",
        "config-optional-skip": "Man jau nuobodu, tiesiog įdiekite viki.",
        "config-profile": "Vartotojo teisių paskyra:",
-       "config-profile-wiki": "Tradicinė viki",
+       "config-profile-wiki": "Atidaryti viki",
        "config-profile-private": "Privati viki",
-       "config-license-pd": "Viešas Domenas",
+       "config-license-pd": "Viešas domenas",
        "config-email-settings": "El. pašto nustatymai",
        "config-upload-enable": "Įgalinti failų įkėlimus",
        "config-logo": "Logotipo URL:",
@@ -81,7 +81,7 @@
        "config-install-tables": "Kuriamos lentelės",
        "config-install-stats": "Inicijuojamos statistikos",
        "config-install-keys": "Generuojami slapti raktai",
-       "config-install-done": "'''Sveikiname!'''\nJūs sėkmingai įdiegėte MediaWiki.\n\nĮdiegimo programa sukūrė <code>LocalSettings.php</code> failą.\nJame yra visos jūsų konfigūracijos.\n\nJums reikės atsisiųsti ir įdėti jį į savo wiki įdiegimo bazę (pačiame kataloge, kaip index.php). Atsisiuntimas turėtų prasidėti automatiškai.\n\nJei atsisiuntimas nebuvo pasiūlytas, arba jį atšaukėte, galite iš naujo atsisiųsti paspaudę žemiau esančią nuorodą:\n\n$3\n\n'''Pastaba:''' Jei jūs to nepadarysite dabar, tada šis sukurtas konfigūracijos failas nebus galimas vėliau, jei išeisite iš įdiegimo be atsisiuntimo.\n\nKai baigsite, jūs galėsite '''[$2 įeiti į savo wiki]'''.",
+       "config-install-done": "'''Sveikiname!'''\nJūs sėkmingai įdiegėte MediaWiki.\n\nĮdiegimo programa sukūrė <code>LocalSettings.php</code> failą.\nJame yra visos jūsų konfigūracijos.\n\nJums reikės atsisiųsti ir įdėti jį į savo wiki įdiegimo bazę (pačiame kataloge, kaip index.php). Atsisiuntimas turėtų prasidėti automatiškai.\n\nJei atsisiuntimas nebuvo pasiūlytas, arba jį atšaukėte, galite iš naujo atsisiųsti paspaudę žemiau esančią nuorodą:\n\n$3\n\n'''Pastaba:''' Jei jūs to nepadarysite dabar, tada šis sukurtas konfigūracijos failas nebus galimas vėliau, jei išeisite iš įdiegimo be atsisiuntimo.\n\nKai baigsite, jūs galėsite '''[$2 įeiti į savo viki]'''.",
        "config-download-localsettings": "Atsisiųsti <code>LocalSettings.php</code>",
        "config-help": "pagalba",
        "mainpagetext": "'''MediaWiki sėkmingai įdiegta.'''",
index 03ea59c..235ff29 100644 (file)
@@ -1,4 +1,9 @@
 {
-       "@metadata": [],
+       "@metadata": {
+               "authors": [
+                       "Taresi"
+               ]
+       },
+       "config-project-namespace": "Tlatequipanōlli ītōcātlacāuh:",
        "mainpagetext": "'''MediaHuiqui cualli ōmotlahtlāli.'''"
 }
index ab318e2..f518d15 100644 (file)
        "config-license-help": "Nu cuofeno 'e wiki pubbrece lassano 'e cuntribbute lloro cu na [http://freedomdefined.org/Definition licienza libbera]. Chesto aiutasse a crià nu senso 'e pruprietà spartuta dint'a communità e ncuraggiasse a cuntribbuiì a nu tèrmene luongo. Nun è generalmente necessario pe' nu wiki privato o aziendale.\n\nSi vulite ausà testi 'a Wikipedia, o vulite ca Wikipedia se pozza miette 'n grado d'accettà teste cupiate d' 'o wiki vuosto, avissev'a scegiere <strong>{{int:config-license-cc-by-sa}}</strong>.\n\nApprimma Wikipedia aveva ausato 'a GNU Free Documentation License. 'A GFDL è una licienza valida, ma è di difficile comprensiona e complica 'o riutilizzo 'e cuntenute.",
        "config-email-settings": "Mpustaziune email",
        "config-enable-email": "Premmette mmasciate elettroniche r'asciuta",
+       "config-enable-email-help": "Si vulite ca 'o sistema 'e mmasciate mail funziunasse, [http://www.php.net/manual/en/mail.configuration.php 'e mpustaziune PHP] s'avesser'a ffà bbuone.\nSi nun vulite 'a funziona mmasciata e-mail, allora stutate chiste llàn.",
        "config-email-user": "Premmette email utente-utente",
        "config-email-user-help": "Cunzente a ll'utente 'e mannà uno a ll'ato le mail si 'e teneno appicciate dint' 'e preferenze lloro.",
        "config-email-usertalk": "Premmette notifiche p' 'e paggene 'e chiacchiera utente",
        "config-email-usertalk-help": "Premmette ll'utente 'e ricevere notifiche p' 'e cagnamiente r' 'e paggene 'e chiacchiera lloro, si l'avessero appicciato dint' 'e preferenze lloro.",
        "config-email-watchlist": "Appiccia notifica 'osservati speciale",
+       "config-email-watchlist-help": "Premmettesse ll'utente 'e se piglià notifiche ncopp' 'e paggene cuntrullate lloro si tenessero appicciate chisto in'a le mpustaziune.",
        "config-email-auth": "Appiccia autenticaziona via email",
        "config-email-sender": "Innerizo email e ritorno:",
        "config-upload-settings": "Immaggene e upload",
index fa3512e..c4ccec6 100644 (file)
@@ -29,7 +29,7 @@
        "config-localsettings-key": "Ключ обновления:",
        "config-localsettings-badkey": "Вы указали неправильный ключ",
        "config-upgrade-key-missing": "Обнаружена существующая установленная копия MediaWiki.\nЧтобы обновить обнаруженную установку, пожалуйста, добавьте следующую строку в конец вашего файла <code>LocalSettings.php</code>:\n\n$1",
-       "config-localsettings-incomplete": "Похоже, что существующий файл <code>LocalSettings.php</code> не является полными.\nНе установлена переменная $1.\nПожалуйста, измените <code>LocalSettings.php</code> так, чтобы значение этой переменной было задано, затем нажмите «{{int:Config-continue}}».",
+       "config-localsettings-incomplete": "Похоже, что существующий файл <code>LocalSettings.php</code> неполон.\nНе установлена переменная $1.\nПожалуйста, измените <code>LocalSettings.php</code> так, чтобы значение этой переменной было задано, затем нажмите «{{int:Config-continue}}».",
        "config-localsettings-connection-error": "Произошла ошибка при подключении к базе данных с помощью настроек, указанных в <code>LocalSettings.php</code> или <code>AdminSettings.php</code>. Пожалуйста, исправьте эти настройки и повторите попытку.\n\n$1",
        "config-session-error": "Ошибка при запуске сессии: $1",
        "config-session-expired": "Ваша сессия истекла.\nСессии настроены на длительность $1.\nВы её можете увеличить, изменив <code>session.gc_maxlifetime</code> в php.ini.\nПерезапустите процесс установки.",
@@ -58,7 +58,7 @@
        "config-help-restart": "Вы хотите удалить все сохранённые данные, которые вы ввели, и запустить процесс установки заново?",
        "config-restart": "Да, начать заново",
        "config-welcome": "=== Проверка окружения ===\nБудут проведены базовые проверки с целью определить, подходит ли данная система для установки MediaWiki.\nНе забудьте включить эту информацию, если вам потребуется помощь для завершения установки.",
-       "config-copyright": "=== Авторские права и условия ===\n\n$1\n\nMediaWiki является свободным программным обеспечением, которое вы можете распространять и/или изменять в соответствии с условиями лицензии GNU General Public License, опубликованной фондом свободного программного обеспечения; второй версии, либо любой более поздней версии.\n\nMediaWiki распространяется в надежде, что она будет полезной, но '''без каких-либо гарантий''', даже без подразумеваемых гарантий '''коммерческой ценности''' или '''пригодности для определённой цели'''. См. лицензию GNU General Public License для более подробной информации.\n\nВы должны были получить <doclink href=Copying>копию GNU General Public License</doclink> вместе с этой программой, если нет, то напишите Free Software Foundation, Inc., по адресу: 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA или [http://www.gnu.org/licenses/old-licenses/gpl-2.0.html прочтите её онлайн].",
+       "config-copyright": "=== Авторские права и условия ===\n\n$1\n\nMediaWiki — свободное программное обеспечение, которое вы можете распространять и/или изменять в соответствии с условиями лицензии GNU General Public License, опубликованной фондом свободного программного обеспечения; второй версии, либо любой более поздней версии.\n\nMediaWiki распространяется в надежде, что она будет полезной, но <strong>без каких-либо гарантий</strong>, даже без подразумеваемых гарантий <strong>коммерческой ценности</strong> или <strong>пригодности для определённой цели</strong>. См. лицензию GNU General Public License для более подробной информации.\n\nВы должны были получить <doclink href=Copying>копию GNU General Public License</doclink> вместе с этой программой, если нет, то напишите Free Software Foundation, Inc., по адресу: 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA или [http://www.gnu.org/copyleft/gpl.html прочтите её онлайн].",
        "config-sidebar": "* [//www.mediawiki.org Сайт MediaWiki]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents/ru Справка для пользователей]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents/ru Справка для администраторов]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ/ru FAQ]\n----\n* <doclink href=Readme>Readme-файл</doclink>\n* <doclink href=ReleaseNotes>Информация о выпуске</doclink>\n* <doclink href=Copying>Лицензия</doclink>\n* <doclink href=UpgradeDoc>Обновление</doclink>",
        "config-env-good": "Проверка внешней среды была успешно проведена.\nВы можете установить MediaWiki.",
        "config-env-bad": "Была проведена проверка внешней среды.\nВы не можете установить MediaWiki.",
        "config-db-install-account": "Учётная запись для установки",
        "config-db-username": "Имя пользователя базы данных:",
        "config-db-password": "Пароль базы данных:",
-       "config-db-password-empty": "Пожалуйста, введите пароль для нового пользователя базы данных «$1».\nХотя и возможно создание пользователей без паролей, это небезопасно.",
-       "config-db-username-empty": "Вы должны ввести значение параметра «{{int:config-db-username}}».",
        "config-db-install-username": "Введите имя пользователя, которое будет использоваться для подключения к базе данных в процессе установки.\nЭто не имя пользователя MediaWiki, это имя пользователя для базы данных.",
        "config-db-install-password": "Введите пароль, который будет использоваться для подключения к базе данных в процессе установки.\nЭто не пароль пользователя MediaWiki, это пароль для базы данных.",
        "config-db-install-help": "Введите имя пользователя и пароль, которые будут использоваться для подключения к базе данных во время процесса установки.",
        "config-mysql-innodb": "InnoDB",
        "config-mysql-myisam": "MyISAM",
        "config-mysql-myisam-dep": "''' Внимание.''' Вы выбрали механизм MyISAM для хранения данных MySQL. Он не рекомендуется к использованию по следующим причинам:\n* он слабо поддерживает параллелизм из-за табличных блокировок;\n* более склонен к потере данных, по сравнению с другими механизмами;\n* код MediaWiki не всегда учитывает особенности MyISAM должным образом.\n\nЕсли ваша MySQL поддерживает InnoDB, настоятельно рекомендуется выбрать этот механизм.\nЕсли ваша MySQL не поддерживает InnoDB, возможно, настало время обновиться.",
-       "config-mysql-only-myisam-dep": "'''Предупреждение:''' MyISAM является единственной доступной системой хранения данных для MySQL на этом компьютере, и она не рекомендуется для использования с MediaWiki, потому что:\n * он слабо поддерживает параллелизм из-за блокировки таблиц\n * она больше других систем подвержена повреждению\n * кодовая база MediaWiki не всегда обрабатывает MyISAM так, как следует\n\nВаша MySQL не поддерживает InnoDB, так что, возможно, настало время для обновления.",
+       "config-mysql-only-myisam-dep": "<strong>Предупреждение:</strong> MyISAM — единственная доступная система хранения данных для MySQL на этом компьютере, и она не рекомендуется для использования совместно с MediaWiki, потому что:\n* слабо поддерживает параллелизм из-за блокировки таблиц\n* больше других систем подвержена повреждению\n* кодовая база MediaWiki не всегда обрабатывает MyISAM так, как следует\n\nВаша MySQL не поддерживает InnoDB, так что, возможно, настало время для обновления.",
        "config-mysql-engine-help": "'''InnoDB''' почти всегда предпочтительнее, так как он лучше справляется с параллельным доступом.\n\n'''MyISAM''' может оказаться быстрее для вики с одним пользователем или с минимальным количеством поступающих правок, однако базы данных на нём портятся чаще, чем на InnoDB.",
        "config-mysql-charset": "Кодировка базы данных:",
        "config-mysql-binary": "Двоичный",
        "config-install-pg-plpgsql": "Проверка языка PL/pgSQL",
        "config-pg-no-plpgsql": "Вам необходимо установить поддержку языка PL/pgSQL для базы данных $1",
        "config-pg-no-create-privs": "Учётная запись, указанная для установки, не обладает достаточными привилегиями для создания учётной записи.",
-       "config-pg-not-in-role": "УказаннаÑ\8f Ñ\83Ñ\87Ñ\91Ñ\82наÑ\8f Ð·Ð°Ð¿Ð¸Ñ\81Ñ\8c Ð²ÐµÐ±-полÑ\8cзоваÑ\82елÑ\8f Ñ\83же Ñ\81Ñ\83Ñ\89еÑ\81Ñ\82вÑ\83еÑ\82.\nУказаннаÑ\8f Ð´Ð»Ñ\8f Ñ\83Ñ\81Ñ\82ановки Ñ\83Ñ\87Ñ\91Ñ\82наÑ\8f Ð·Ð°Ð¿Ð¸Ñ\81Ñ\8c Ð½Ðµ Ñ\8fвлÑ\8fеÑ\82Ñ\81Ñ\8f Ð·Ð°Ð¿Ð¸Ñ\81Ñ\8cÑ\8e Ñ\81Ñ\83пеÑ\80полÑ\8cзоваÑ\82елÑ\8f, Ð¸ Ð½Ðµ Ð¾Ñ\82ноÑ\81иÑ\82Ñ\81Ñ\8f Ðº Ñ\80оли Ð²ÐµÐ±-полÑ\8cзоваÑ\82елÑ\8f, Ð¿Ð¾Ñ\8dÑ\82омÑ\83 Ð½Ðµ Ð¿Ð¾Ð»Ñ\83Ñ\87аеÑ\82Ñ\81Ñ\8f Ñ\81оздаÑ\82Ñ\8c Ð¾Ð±Ñ\8aекÑ\82Ñ\8b, Ð¿Ñ\80инадлежаÑ\89ие Ð²ÐµÐ±-полÑ\8cзоваÑ\82елÑ\8e.\n\nMediaWiki Ð² Ð½Ð°Ñ\81Ñ\82оÑ\8fÑ\89ее Ð²Ñ\80емÑ\8f Ñ\82Ñ\80ебÑ\83еÑ\82, Ñ\87Ñ\82обÑ\8b Ð²Ð»Ð°Ð´ÐµÐ»Ñ\8cÑ\86ем Ñ\82аблиÑ\86 Ð±Ñ\8bл Ð²ÐµÐ±-полÑ\8cзоваÑ\82елÑ\8c. Ð\9fожалÑ\83йÑ\81Ñ\82а, Ñ\83кажиÑ\82е Ð´Ñ\80Ñ\83гое Ð¸Ð¼Ñ\8f Ñ\83Ñ\87Ñ\91Ñ\82ной Ð·Ð°Ð¿Ð¸Ñ\81и Ð´Ð»Ñ\8f Ð²ÐµÐ±, или нажмите кнопку «назад» и укажите пользователя с достаточными для установки правами.",
+       "config-pg-not-in-role": "УказаннаÑ\8f Ñ\83Ñ\87Ñ\91Ñ\82наÑ\8f Ð·Ð°Ð¿Ð¸Ñ\81Ñ\8c Ð²ÐµÐ±-полÑ\8cзоваÑ\82елÑ\8f Ñ\83же Ñ\81Ñ\83Ñ\89еÑ\81Ñ\82вÑ\83еÑ\82.\nÐ\92Ñ\8bбÑ\80аннаÑ\8f Ð²Ð°Ð¼Ð¸ Ð´Ð»Ñ\8f Ñ\83Ñ\81Ñ\82ановки Ñ\83Ñ\87Ñ\91Ñ\82наÑ\8f Ð·Ð°Ð¿Ð¸Ñ\81Ñ\8c Ð½Ðµ Ñ\8fвлÑ\8fеÑ\82Ñ\81Ñ\8f Ð·Ð°Ð¿Ð¸Ñ\81Ñ\8cÑ\8e Ñ\81Ñ\83пеÑ\80полÑ\8cзоваÑ\82елÑ\8f Ð¸ Ð½Ðµ Ð¾Ñ\82ноÑ\81иÑ\82Ñ\81Ñ\8f Ðº Ñ\80оли Ð²ÐµÐ±-полÑ\8cзоваÑ\82елÑ\8f; Ð¿Ð¾Ñ\8dÑ\82омÑ\83 Ð½Ðµ Ð¿Ð¾Ð»Ñ\83Ñ\87аеÑ\82Ñ\81Ñ\8f Ñ\81оздаÑ\82Ñ\8c Ð¾Ð±Ñ\8aекÑ\82Ñ\8b, Ð¿Ñ\80инадлежаÑ\89ие Ð²ÐµÐ±-полÑ\8cзоваÑ\82елÑ\8e.\n\nMediaWiki Ð² Ð½Ð°Ñ\81Ñ\82оÑ\8fÑ\89ее Ð²Ñ\80емÑ\8f Ñ\82Ñ\80ебÑ\83еÑ\82, Ñ\87Ñ\82обÑ\8b Ð²Ð»Ð°Ð´ÐµÐ»Ñ\8cÑ\86ем Ñ\82аблиÑ\86 Ð±Ñ\8bл Ð²ÐµÐ±-полÑ\8cзоваÑ\82елÑ\8c. Ð\9fожалÑ\83йÑ\81Ñ\82а, Ñ\83кажиÑ\82е Ð´Ñ\80Ñ\83гое Ð¸Ð¼Ñ\8f Ñ\83Ñ\87Ñ\91Ñ\82ной Ð·Ð°Ð¿Ð¸Ñ\81и Ð´Ð»Ñ\8f Ð²ÐµÐ±-полÑ\8cзоваÑ\82елÑ\8f, или нажмите кнопку «назад» и укажите пользователя с достаточными для установки правами.",
        "config-install-user": "Создание базы данных пользователей",
        "config-install-user-alreadyexists": "Участник «$1» уже существует",
        "config-install-user-create-failed": "Не получилось создать участника «$1»: $2",
index 7f0ad67..e49d671 100644 (file)
@@ -1,22 +1,23 @@
 {
        "@metadata": {
                "authors": [
-                       "Ammartivari"
+                       "Ammartivari",
+                       "Kosovastar"
                ]
        },
        "config-desc": "Instaluesi për MediaWiki",
        "config-title": "Instalimi MediaWiki $1",
-       "config-information": "Të dhëna",
-       "config-your-language": "Gjuha juaj:",
+       "config-information": "Informacion",
+       "config-your-language": "Gjuha juaj:",
        "config-your-language-help": "Zgjidhni një gjuhë për ta përdorur gjatë procesit të instalimit.",
-       "config-wiki-language": "Gjuha wiki:",
-       "config-wiki-language-help": "Zgjidhni gjuhën në të cilën wiki do të jetë kryesisht e shkruar.",
+       "config-wiki-language": "Gjuha e wikit:",
+       "config-wiki-language-help": "Zgjidhni gjuhën e cila do të mbizotërojë në shkrimin e wiki-t.",
        "config-back": "← Prapa",
        "config-continue": "Para →",
        "config-page-language": "Gjuha",
-       "config-page-welcome": "Mirë se vini në MediaWiki!",
-       "config-page-dbconnect": "Lidhuni me bazën e të dhënave",
-       "config-page-dbsettings": "Cilësimet e bazës së të dhënave",
+       "config-page-welcome": "Mirësevini në MediaWiki!",
+       "config-page-dbconnect": "Lidhu me bazën e të dhënave",
+       "config-page-dbsettings": "Parametrat e bazës së të dhënave",
        "config-page-name": "Emri",
        "config-page-options": "Opcionet",
        "config-page-install": "Instalo",
@@ -33,9 +34,9 @@
        "config-admin-box": "Llogari administruesi",
        "config-admin-name-blank": "Shkruani nofkën e një administruesi.",
        "config-admin-password-blank": "Shkruani një fjalëkalim për llogarinë e administruesit.",
-       "config-admin-email": "Email adresa:",
+       "config-admin-email": "Adresa e emailit:",
        "config-license-pd": "Domeni publik",
-       "config-logo": "URL-ja i logos:",
+       "config-logo": "URL e logos:",
        "config-install-tables": "Duke krijuar tabela",
        "config-help": "ndihmë",
        "mainpagetext": "'''MediaWiki software u instalua me sukses.'''",
index 5a8c4c7..c93206c 100644 (file)
@@ -180,6 +180,25 @@ class CSSMin {
                return false;
        }
 
+       /**
+        * Serialize a string (escape and quote) for use as a CSS string value.
+        * http://www.w3.org/TR/2013/WD-cssom-20131205/#serialize-a-string
+        *
+        * @param string $value
+        * @return string
+        * @throws Exception
+        */
+       public static function serializeStringValue( $value ) {
+               if ( strstr( $value, "\0" ) ) {
+                       throw new Exception( "Invalid character in CSS string" );
+               }
+               $value = strtr( $value, array( '\\' => '\\\\', '"' => '\\"' ) );
+               $value = preg_replace_callback( '/[\x01-\x1f\x7f-\x9f]/', function ( $match ) {
+                       return '\\' . base_convert( ord( $match[0] ), 10, 16 ) . ' ';
+               }, $value );
+               return '"' . $value . '"';
+       }
+
        /**
         * @param $file string
         * @return bool|string
index 442298a..3d7dee7 100644 (file)
@@ -102,7 +102,10 @@ class HttpStatus {
                }
 
                if ( $version === null ) {
-                       $version = isset( $_SERVER['SERVER_PROTOCOL'] ) && $_SERVER['SERVER_PROTOCOL'] === 'HTTP/1.0' ? '1.0' : '1.1';
+                       $version = isset( $_SERVER['SERVER_PROTOCOL'] ) &&
+                               $_SERVER['SERVER_PROTOCOL'] === 'HTTP/1.0' ?
+                                       '1.0' :
+                                       '1.1';
                }
 
                header( "HTTP/$version $code $message" );
index b6faa37..ea50a85 100644 (file)
@@ -112,7 +112,7 @@ class ReplacementArray {
         */
        public function replace( $subject ) {
                if (
-                       function_exists( 'fss_prep_replace' )  &&
+                       function_exists( 'fss_prep_replace' ) &&
                        version_compare( PHP_VERSION, '5.5.0' ) < 0
                ) {
                        if ( $this->fss === false ) {
index a382e1f..fb96269 100644 (file)
 /**
  * Multi-datacenter aware caching interface
  *
- * All operations go to the local cache, except the delete()
- * and touchCheckKey(), which broadcast to all clusters.
+ * All operations go to the local datacenter cache, except for delete(),
+ * touchCheckKey(), and resetCheckKey(), which broadcast to all clusters.
+ *
  * This class is intended for caching data from primary stores.
  * If the get() method does not return a value, then the caller
  * should query the new value and backfill the cache using set().
- * When the source data changes, the delete() method should be called.
- * Since delete() is expensive, it should be avoided. One can do so if:
+ * When the source data changes, a purge method should be called.
+ * Since purges are expensive, they should be avoided. One can do so if:
  *   - a) The object cached is immutable; or
  *   - b) Validity is checked against the source after get(); or
  *   - c) Using a modest TTL is reasonably correct and performant
- * Consider using getWithSetCallback() instead of the get()/set() cycle.
+ * The simplest purge method is delete().
  *
  * Instances of this class must be configured to point to a valid
  * PubSub endpoint, and there must be listeners on the cache servers
@@ -75,13 +76,15 @@ class WANObjectCache {
        const LOCK_TTL = 5;
        /** Default remaining TTL at which to consider pre-emptive regeneration */
        const LOW_TTL = 10;
-       /** Default TTL for temporarily caching tombstoned keys */
-       const TEMP_TTL = 5;
+       /** Default time-since-expiry on a miss that makes a key "hot" */
+       const LOCK_TSE = 1;
 
        /** Idiom for set()/getWithSetCallback() TTL */
        const TTL_NONE = 0;
        /** Idiom for getWithSetCallback() callbacks to avoid calling set() */
        const TTL_UNCACHEABLE = -1;
+       /** Idiom for getWithSetCallback() callbacks to 'lockTSE' logic */
+       const TSE_NONE = -1;
 
        /** Cache format version number */
        const VERSION = 1;
@@ -157,7 +160,8 @@ class WANObjectCache {
         *   - b) Keeping transaction duration shorter than delete() hold-off TTL
         * However, pre-snapshot values might still be seen due to delete() relay lag.
         *
-        * For keys that are hot/expensive, consider using getWithSetCallback() instead.
+        * Consider using getWithSetCallback() instead of get()/set() cycles.
+        * That method has cache slam avoiding features for hot/expensive keys.
         *
         * @param string $key Cache key
         * @param mixed $curTTL Approximate TTL left on the key if present [returned]
@@ -376,10 +380,10 @@ class WANObjectCache {
         * This is similar to touchCheckKey() in that keys using it via
         * getWithSetCallback() will be invalidated. The differences are:
         *   - a) The timestamp will be deleted from all caches and lazily
-        *      re-initialized when accessed (rather than set everywhere)
+        *        re-initialized when accessed (rather than set everywhere)
         *   - b) Thus, dependent keys will be known to be invalid, but not
-        *      for how long (they are treated as "just" purged), which
-        *      effects any lockTSE logic in getWithSetCallback()
+        *        for how long (they are treated as "just" purged), which
+        *        effects any lockTSE logic in getWithSetCallback()
         * The advantage is that this does not place high TTL keys on every cache
         * server, making it better for code that will cache many different keys
         * and either does not use lockTSE or uses a low enough TTL anyway.
@@ -418,7 +422,7 @@ class WANObjectCache {
         * to maintain "most recent X" values that come from time or sequence
         * based source data, provided that the "as of" id/time is tracked.
         *
-        * Usage of $checkKeys is similar to get()/getMulti(). However,
+        * Usage of $checkKeys is similar to get() and getMulti(). However,
         * rather than the caller having to inspect a "current time left"
         * variable (e.g. $curTTL, $curTTLs), a cache regeneration will be
         * triggered using the callback.
@@ -472,6 +476,7 @@ class WANObjectCache {
         *   - lowTTL  : consider pre-emptive updates when the current TTL (sec)
         *               of the key is less than this. It becomes more likely
         *               over time, becoming a certainty once the key is expired.
+        *               [Default: WANObjectCache::LOW_TTL seconds]
         *   - lockTSE : if the key is tombstoned or expired (by $checkKeys) less
         *               than this many seconds ago, then try to have a single
         *               thread handle cache regeneration at any given time.
@@ -479,17 +484,16 @@ class WANObjectCache {
         *               If, on miss, the time since expiration is low, the assumption
         *               is that the key is hot and that a stampede is worth avoiding.
         *               Setting this above WANObjectCache::HOLDOFF_TTL makes no difference.
-        *   - tempTTL : TTL of the temp key used to cache values while a key is tombstoned.
-        *               This avoids excessive regeneration of hot keys on delete() but may
-        *               result in stale values.
+        *               The higher this is set, the higher the worst-case staleness can be.
+        *               Use WANObjectCache::TSE_NONE to disable this logic.
+        *               [Default: WANObjectCache::TSE_NONE]
         * @return mixed Value to use for the key
         */
        final public function getWithSetCallback(
                $key, $callback, $ttl, array $checkKeys = array(), array $opts = array()
        ) {
                $lowTTL = isset( $opts['lowTTL'] ) ? $opts['lowTTL'] : min( self::LOW_TTL, $ttl );
-               $lockTSE = isset( $opts['lockTSE'] ) ? $opts['lockTSE'] : -1;
-               $tempTTL = isset( $opts['tempTTL'] ) ? $opts['tempTTL'] : self::TEMP_TTL;
+               $lockTSE = isset( $opts['lockTSE'] ) ? $opts['lockTSE'] : self::TSE_NONE;
 
                // Get the current key value
                $curTTL = null;
@@ -505,9 +509,14 @@ class WANObjectCache {
                $isTombstone = ( $curTTL !== null && $value === false );
                // Assume a key is hot if requested soon after invalidation
                $isHot = ( $curTTL !== null && $curTTL <= 0 && abs( $curTTL ) <= $lockTSE );
+               // Decide whether a single thread should handle regenerations.
+               // This avoids stampedes when $checkKeys are bumped and when preemptive
+               // renegerations take too long. It also reduces regenerations while $key
+               // is tombstoned. This balances cache freshness with avoiding DB load.
+               $useMutex = ( $isHot || ( $isTombstone && $lockTSE > 0 ) );
 
                $lockAcquired = false;
-               if ( $isHot ) {
+               if ( $useMutex ) {
                        // Acquire a cluster-local non-blocking lock
                        if ( $this->cache->lock( $key, 0, self::LOCK_TTL ) ) {
                                // Lock acquired; this thread should update the key
@@ -515,16 +524,14 @@ class WANObjectCache {
                        } elseif ( $value !== false ) {
                                // If it cannot be acquired; then the stale value can be used
                                return $value;
-                       }
-               }
-
-               if ( !$lockAcquired && ( $isTombstone || $isHot ) ) {
-                       // Use the stash value for tombstoned keys to reduce regeneration load.
-                       // For hot keys, either another thread has the lock or the lock failed;
-                       // use the stash value from the last thread that regenerated it.
-                       $value = $this->cache->get( self::STASH_KEY_PREFIX . $key );
-                       if ( $value !== false ) {
-                               return $value;
+                       } else {
+                               // Use the stash value for tombstoned keys to reduce regeneration load.
+                               // For hot keys, either another thread has the lock or the lock failed;
+                               // use the stash value from the last thread that regenerated it.
+                               $value = $this->cache->get( self::STASH_KEY_PREFIX . $key );
+                               if ( $value !== false ) {
+                                       return $value;
+                               }
                        }
                }
 
@@ -536,7 +543,8 @@ class WANObjectCache {
                $value = call_user_func_array( $callback, array( $cValue, &$ttl ) );
                // When delete() is called, writes are write-holed by the tombstone,
                // so use a special stash key to pass the new value around threads.
-               if ( $value !== false && ( $isHot || $isTombstone ) && $ttl >= 0 ) {
+               if ( $useMutex && $value !== false && $ttl >= 0 ) {
+                       $tempTTL = max( 1, (int)$lockTSE ); // set() expects seconds
                        $this->cache->set( self::STASH_KEY_PREFIX . $key, $value, $tempTTL );
                }
 
index 43dfab3..97ddccf 100644 (file)
@@ -34,7 +34,7 @@ class ParsoidVirtualRESTService extends VirtualRESTService {
         *   * body: array( 'wikitext' => ... ) or array( 'wikitext' => ..., 'bodyOnly' => true/false )
         *   * $title is optional
         *   * $revision is optional
-     *
+        *
         * There are also deprecated "v1" requests; see onParsoid1Request
         * for details.
         * @param array $params Key/value map
index f31a42a..1d31088 100644 (file)
@@ -263,7 +263,7 @@ class LogFormatter {
                                switch ( $entry->getSubtype() ) {
                                        case 'protect':
                                                $text = wfMessage( 'protectedarticle' )
-                                                       ->rawParams( $target . ' ' . $parameters[0] )->inContentLanguage()->escaped();
+                                                       ->rawParams( $target . ' ' . $parameters['4::description'] )->inContentLanguage()->escaped();
                                                break;
                                        case 'unprotect':
                                                $text = wfMessage( 'unprotectedarticle' )
@@ -271,7 +271,7 @@ class LogFormatter {
                                                break;
                                        case 'modify':
                                                $text = wfMessage( 'modifiedarticleprotection' )
-                                                       ->rawParams( $target . ' ' . $parameters[0] )->inContentLanguage()->escaped();
+                                                       ->rawParams( $target . ' ' . $parameters['4::description'] )->inContentLanguage()->escaped();
                                                break;
                                        case 'move_prot':
                                                $text = wfMessage( 'movedarticleprotection' )
@@ -932,32 +932,6 @@ class LegacyLogFormatter extends LogFormatter {
                $type = $this->entry->getType();
                $subtype = $this->entry->getSubtype();
 
-               if ( $type == 'protect'
-                       && ( $subtype == 'protect' || $subtype == 'modify' || $subtype == 'unprotect' )
-               ) {
-                       $links = array(
-                               Linker::link( $title,
-                                       $this->msg( 'hist' )->escaped(),
-                                       array(),
-                                       array(
-                                               'action' => 'history',
-                                               'offset' => $this->entry->getTimestamp()
-                                       )
-                               )
-                       );
-                       if ( $this->context->getUser()->isAllowed( 'protect' ) ) {
-                               $links[] = Linker::linkKnown(
-                                       $title,
-                                       $this->msg( 'protect_change' )->escaped(),
-                                       array(),
-                                       array( 'action' => 'protect' )
-                               );
-                       }
-
-                       return $this->msg( 'parentheses' )->rawParams(
-                               $this->context->getLanguage()->pipeList( $links ) )->escaped();
-               }
-
                // Do nothing. The implementation is handled by the hook modifiying the
                // passed-by-ref parameters. This also changes the default value so that
                // getComment() and getActionLinks() do not call them indefinitely.
index 82e5808..2e28917 100644 (file)
@@ -285,30 +285,10 @@ class LogPage {
                                        $rv = wfMessage( $wgLogActions[$key] )->rawParams( $titleLink )
                                                ->inLanguage( $langObj )->escaped();
                                } else {
-                                       $details = '';
                                        array_unshift( $params, $titleLink );
 
-                                       // Page protections
-                                       if ( $type == 'protect' && count( $params ) == 3 ) {
-                                               // Restrictions and expiries
-                                               if ( $skin ) {
-                                                       $details .= $wgLang->getDirMark() . htmlspecialchars( " {$params[1]}" );
-                                               } else {
-                                                       $details .= " {$params[1]}";
-                                               }
-
-                                               // Cascading flag...
-                                               if ( $params[2] ) {
-                                                       $text = wfMessage( 'protect-summary-cascade' )
-                                                               ->inLanguage( $langObj )->text();
-                                                       $details .= ' ';
-                                                       $details .= wfMessage( 'brackets', $text )->inLanguage( $langObj )->text();
-
-                                               }
-                                       }
-
                                        $rv = wfMessage( $wgLogActions[$key] )->rawParams( $params )
-                                                       ->inLanguage( $langObj )->escaped() . $details;
+                                                       ->inLanguage( $langObj )->escaped();
                                }
                        }
                } else {
index 5327e07..bd04f15 100644 (file)
@@ -37,11 +37,38 @@ class ProtectLogFormatter extends LogFormatter {
                return array();
        }
 
+       protected function getMessageKey() {
+               $key = parent::getMessageKey();
+               $params = $this->extractParameters();
+               if ( isset( $params[4] ) && $params[4] ) {
+                       // Messages: logentry-protect-protect-cascade, logentry-protect-modify-cascade
+                       $key .= '-cascade';
+               }
+
+               return $key;
+       }
+
        protected function getMessageParameters() {
                $params = parent::getMessageParameters();
 
                $subtype = $this->entry->getSubtype();
-               if ( $subtype === 'move_prot' ) {
+               if ( $subtype === 'protect' || $subtype === 'modify' ) {
+                       $rawParams = $this->entry->getParameters();
+                       if ( isset( $rawParams['details'] ) ) {
+                               $params[3] = $this->createProtectDescription( $rawParams['details'] );
+                       } elseif ( isset( $params[3] ) ) {
+                               // Old way of Restrictions and expiries
+                               $params[3] = $this->context->getLanguage()->getDirMark() . $params[3];
+                       } else {
+                               // Very old way (nothing set)
+                               $params[3] = '';
+                       }
+                       // Cascading flag
+                       if ( isset( $params[4] ) ) {
+                               // handled in getMessageKey
+                               unset( $params[4] );
+                       }
+               } elseif ( $subtype === 'move_prot' ) {
                        $oldname = $this->makePageLink( Title::newFromText( $params[3] ), array( 'redirect' => 'no' ) );
                        $params[3] = Message::rawParam( $oldname );
                }
@@ -49,15 +76,59 @@ class ProtectLogFormatter extends LogFormatter {
                return $params;
        }
 
+       public function getActionLinks() {
+               $subtype = $this->entry->getSubtype();
+               if ( $this->entry->isDeleted( LogPage::DELETED_ACTION ) // Action is hidden
+                       || $subtype === 'move_prot' // the move log entry has the right action link
+               ) {
+                       return '';
+               }
+
+               // Show history link for all changes after the protection
+               $title = $this->entry->getTarget();
+               $links = array(
+                       Linker::link( $title,
+                               $this->msg( 'hist' )->escaped(),
+                               array(),
+                               array(
+                                       'action' => 'history',
+                                       'offset' => $this->entry->getTimestamp(),
+                               )
+                       )
+               );
+
+               // Show change protection link
+               if ( $this->context->getUser()->isAllowed( 'protect' ) ) {
+                       $links[] = Linker::linkKnown(
+                               $title,
+                               $this->msg( 'protect_change' )->escaped(),
+                               array(),
+                               array( 'action' => 'protect' )
+                       );
+               }
+
+               return $this->msg( 'parentheses' )->rawParams(
+                       $this->context->getLanguage()->pipeList( $links ) )->escaped();
+       }
+
        protected function getParametersForApi() {
                $entry = $this->entry;
+               $subtype = $this->entry->getSubtype();
                $params = $entry->getParameters();
 
-               static $map = array(
-                       // param keys for move_prot sub type
-                       '4:title:oldtitle',
-                       '4::oldtitle' => '4:title:oldtitle',
-               );
+               $map = array();
+               if ( $subtype === 'protect' || $subtype === 'modify' ) {
+                       $map = array(
+                               '4::description',
+                               '5:bool:cascade',
+                               'details' => ':array:details',
+                       );
+               } elseif ( $subtype === 'move_prot' ) {
+                       $map = array(
+                               '4:title:oldtitle',
+                               '4::oldtitle' => '4:title:oldtitle',
+                       );
+               }
                foreach ( $map as $index => $key ) {
                        if ( isset( $params[$index] ) ) {
                                $params[$key] = $params[$index];
@@ -65,6 +136,78 @@ class ProtectLogFormatter extends LogFormatter {
                        }
                }
 
+               // Change string to explicit boolean
+               if ( isset( $params['5:bool:cascade'] ) && is_string( $params['5:bool:cascade'] ) ) {
+                       $params['5:bool:cascade'] = $params['5:bool:cascade'] === 'cascade';
+               }
+
                return $params;
        }
+
+       public function formatParametersForApi() {
+               global $wgContLang;
+
+               $ret = parent::formatParametersForApi();
+               if ( isset( $ret['details'] ) && is_array( $ret['details'] ) ) {
+                       foreach ( $ret['details'] as &$detail ) {
+                               if ( isset( $detail['expiry'] ) ) {
+                                       $detail['expiry'] = $wgContLang->formatExpiry( $detail['expiry'], TS_ISO_8601, 'infinite' );
+                               }
+                       }
+               }
+
+               return $ret;
+       }
+
+       /**
+        * Create the protect description to show in the log formatter
+        *
+        * @param array $details
+        * @return string
+        */
+       public function createProtectDescription( array $details ) {
+               $protectDescription = '';
+
+               foreach ( $details as $param ) {
+                       $expiryText = $this->formatExpiry( $param['expiry'] );
+
+                       // Messages: restriction-edit, restriction-move, restriction-create,
+                       // restriction-upload
+                       $action = $this->context->msg( 'restriction-' . $param['type'] )->escaped();
+
+                       $protectionLevel = $param['level'];
+                       // Messages: protect-level-autoconfirmed, protect-level-sysop
+                       $message = $this->context->msg( 'protect-level-' . $protectionLevel );
+                       if ( $message->isDisabled() ) {
+                               // Require "$1" permission
+                               $restrictions = $this->context->msg( "protect-fallback", $protectionLevel )->parse();
+                       } else {
+                               $restrictions = $message->escaped();
+                       }
+
+                       if ( $protectDescription !== '' ) {
+                               $protectDescription .= $this->context->msg( 'word-separator' )->escaped();
+                       }
+
+                       $protectDescription .= $this->context->msg( 'protect-summary-desc' )
+                               ->params( $action, $restrictions, $expiryText )->escaped();
+               }
+
+               return $protectDescription;
+       }
+
+       private function formatExpiry( $expiry ) {
+               if ( wfIsInfinity( $expiry ) ) {
+                       return $this->context->msg( 'protect-expiry-indefinite' )->text();
+               }
+               $lang = $this->context->getLanguage();
+               $user = $this->context->getUser();
+               return $this->context->msg(
+                       'protect-expiring-local',
+                       $lang->userTimeAndDate( $expiry, $user ),
+                       $lang->userDate( $expiry, $user ),
+                       $lang->userTime( $expiry, $user )
+               )->text();
+       }
+
 }
index 1f00650..0b5b8b6 100644 (file)
@@ -339,7 +339,9 @@ class BitmapHandler extends TransformationalImageHandler {
 
                $src_image = call_user_func( $loader, $params['srcPath'] );
 
-               $rotation = function_exists( 'imagerotate' ) && !isset( $params['disableRotation'] ) ? $this->getRotation( $image ) : 0;
+               $rotation = function_exists( 'imagerotate' ) && !isset( $params['disableRotation'] ) ?
+                       $this->getRotation( $image ) :
+                       0;
                list( $width, $height ) = $this->extractPreRotationDimensions( $params, $rotation );
                $dst_image = imagecreatetruecolor( $width, $height );
 
index 5ba5c68..b2d2443 100644 (file)
@@ -269,9 +269,11 @@ class ExifBitmapHandler extends BitmapHandler {
        }
 
        /**
-        * Swaps an embedded ICC profile for another, if found. Depends on exiftool, no-op if not installed.
+        * Swaps an embedded ICC profile for another, if found.
+        * Depends on exiftool, no-op if not installed.
         * @param string $filepath File to be manipulated (will be overwritten)
-        * @param string $oldProfileString Exact name of color profile to look for (the one that will be replaced)
+        * @param string $oldProfileString Exact name of color profile to look for
+        *  (the one that will be replaced)
         * @param string $profileFilepath ICC profile file to apply to the file
         * @since 1.26
         * @return bool
index 0fee8cc..b4892c4 100644 (file)
@@ -1,4 +1,6 @@
 <?php
+// @codingStandardsIgnoreFile
+// PHPCS can't handle the level of nesting in this file
 /**
  * Formatting of image metadata values into human readable form.
  *
index 94aca61..b998d18 100644 (file)
@@ -187,4 +187,25 @@ class GIFHandler extends BitmapHandler {
 
                return $wgLang->commaList( $info );
        }
+
+       /**
+        * Return the duration of the GIF file.
+        *
+        * Shown in the &query=imageinfo&iiprop=size api query.
+        *
+        * @param $file File
+        * @return float The duration of the file.
+        */
+       public function getLength( $file ) {
+               $serMeta = $file->getMetadata();
+               MediaWiki\suppressWarnings();
+               $metadata = unserialize( $serMeta );
+               MediaWiki\restoreWarnings();
+
+               if ( !$metadata || !isset( $metadata['duration'] ) || !$metadata['duration'] ) {
+                       return 0.0;
+               } else {
+                       return (float)$metadata['duration'];
+               }
+       }
 }
index 3a59996..9b19921 100644 (file)
@@ -866,14 +866,21 @@ abstract class MediaHandler {
         * Gets configuration for the file warning message. Return value of
         * the following structure:
         *   array(
-        *     'module' => 'example.filewarning.messages', // Required, module with messages loaded for the client
-        *     'messages' => array( // Required, array of names of messages
-        *       'main' => 'example-filewarning-main', // Required, main warning message
-        *       'header' => 'example-filewarning-header', // Optional, header for warning dialog
-        *       'footer' => 'example-filewarning-footer', // Optional, footer for warning dialog
-        *       'info' => 'example-filewarning-info', // Optional, text for more-information link (see below)
+        *     // Required, module with messages loaded for the client
+        *     'module' => 'example.filewarning.messages',
+        *     // Required, array of names of messages
+        *     'messages' => array(
+        *       // Required, main warning message
+        *       'main' => 'example-filewarning-main',
+        *       // Optional, header for warning dialog
+        *       'header' => 'example-filewarning-header',
+        *       // Optional, footer for warning dialog
+        *       'footer' => 'example-filewarning-footer',
+        *       // Optional, text for more-information link (see below)
+        *       'info' => 'example-filewarning-info',
         *     ),
-        *     'link' => 'http://example.com', // Optional, link for more information
+        *     // Optional, link for more information
+        *     'link' => 'http://example.com',
         *   )
         *
         * Returns null if no warning is necessary.
index c3f0832..e297fe1 100644 (file)
@@ -175,9 +175,29 @@ class PNGHandler extends BitmapHandler {
                return $wgLang->commaList( $info );
        }
 
+       /**
+        * Return the duration of an APNG file.
+        *
+        * Shown in the &query=imageinfo&iiprop=size api query.
+        *
+        * @param $file File
+        * @return float The duration of the file.
+        */
+       public function getLength( $file ) {
+               $serMeta = $file->getMetadata();
+               MediaWiki\suppressWarnings();
+               $metadata = unserialize( $serMeta );
+               MediaWiki\restoreWarnings();
+
+               if ( !$metadata || !isset( $metadata['duration'] ) || !$metadata['duration'] ) {
+                       return 0.0;
+               } else {
+                       return (float)$metadata['duration'];
+               }
+       }
+
        // PNGs should be easy to support, but it will need some sharpening applied
        // and another user test to check if the perceived quality change is noticeable
-
        public function supportsBucketing() {
                return false;
        }
index 64a7e8a..e424b9b 100644 (file)
@@ -429,8 +429,14 @@ class XMPReader implements LoggerAwareInterface {
                }
                $len = unpack( 'Nlength/Noffset', substr( $content, 32, 8 ) );
 
-               if ( !$len || $len['length'] < 4 || $len['offset'] < 0 || $len['offset'] > $len['length'] ) {
-                       $this->logger->info( __METHOD__ . 'Error reading extended XMP block, invalid length or offset.' );
+               if ( !$len ||
+                       $len['length'] < 4 ||
+                       $len['offset'] < 0 ||
+                       $len['offset'] > $len['length']
+               ) {
+                       $this->logger->info(
+                               __METHOD__ . 'Error reading extended XMP block, invalid length or offset.'
+                       );
 
                        return false;
                }
@@ -710,7 +716,6 @@ class XMPReader implements LoggerAwareInterface {
         * @throws RuntimeException
         */
        private function endElementModeLi( $elm ) {
-
                list( $ns, $tag ) = explode( ' ', $this->curItem[0], 2 );
                $info = $this->items[$ns][$tag];
                $finalName = isset( $info['map_name'] )
@@ -734,7 +739,9 @@ class XMPReader implements LoggerAwareInterface {
                                $this->results['xmp-' . $info['map_group']][$finalName]['_type'] = 'lang';
                        }
                } else {
-                       throw new RuntimeException( __METHOD__ . " expected </rdf:seq> or </rdf:bag> but instead got $elm." );
+                       throw new RuntimeException(
+                               __METHOD__ . " expected </rdf:seq> or </rdf:bag> but instead got $elm."
+                       );
                }
        }
 
index 1a52930..cb3754a 100644 (file)
@@ -60,7 +60,9 @@ class MultiWriteBagOStuff extends BagOStuff {
                parent::__construct( $params );
 
                if ( empty( $params['caches'] ) || !is_array( $params['caches'] ) ) {
-                       throw new InvalidArgumentException( __METHOD__ . ': "caches" parameter must be an array of caches' );
+                       throw new InvalidArgumentException(
+                               __METHOD__ . ': "caches" parameter must be an array of caches'
+                       );
                }
 
                $this->caches = array();
index 9b9e3cb..e1cd76c 100644 (file)
@@ -248,7 +248,9 @@ class ImagePage extends Article {
                Hooks::run( 'ImagePageShowTOC', array( $this, &$r ) );
 
                if ( $metadata ) {
-                       $r[] = '<li><a href="#metadata">' . $this->getContext()->msg( 'metadata' )->escaped() . '</a></li>';
+                       $r[] = '<li><a href="#metadata">' .
+                               $this->getContext()->msg( 'metadata' )->escaped() .
+                               '</a></li>';
                }
 
                return '<ul id="filetoc">' . implode( "\n", $r ) . '</ul>';
@@ -389,9 +391,13 @@ class ImagePage extends Article {
                                        }
                                        if ( count( $otherSizes ) ) {
                                                $msgsmall .= ' ' .
-                                               Html::rawElement( 'span', array( 'class' => 'mw-filepage-other-resolutions' ),
-                                                       $this->getContext()->msg( 'show-big-image-other' )->rawParams( $lang->pipeList( $otherSizes ) )->
-                                                       params( count( $otherSizes ) )->parse()
+                                               Html::rawElement(
+                                                       'span',
+                                                       array( 'class' => 'mw-filepage-other-resolutions' ),
+                                                       $this->getContext()->msg( 'show-big-image-other' )
+                                                               ->rawParams( $lang->pipeList( $otherSizes ) )
+                                                               ->params( count( $otherSizes ) )
+                                                               ->parse()
                                                );
                                        }
                                } elseif ( $width == 0 && $height == 0 ) {
@@ -667,9 +673,14 @@ EOT
                $wrap = "<div class=\"sharedUploadNotice\">\n$1\n</div>\n";
                $repo = $this->mPage->getFile()->getRepo()->getDisplayName();
 
-               if ( $descUrl && $descText && $this->getContext()->msg( 'sharedupload-desc-here' )->plain() !== '-' ) {
+               if ( $descUrl &&
+                       $descText &&
+                       $this->getContext()->msg( 'sharedupload-desc-here' )->plain() !== '-'
+               ) {
                        $out->wrapWikiMsg( $wrap, array( 'sharedupload-desc-here', $repo, $descUrl ) );
-               } elseif ( $descUrl && $this->getContext()->msg( 'sharedupload-desc-there' )->plain() !== '-' ) {
+               } elseif ( $descUrl &&
+                       $this->getContext()->msg( 'sharedupload-desc-there' )->plain() !== '-'
+               ) {
                        $out->wrapWikiMsg( $wrap, array( 'sharedupload-desc-there', $repo, $descUrl ) );
                } else {
                        $out->wrapWikiMsg( $wrap, array( 'sharedupload', $repo ), ''/*BACKCOMPAT*/ );
@@ -854,7 +865,9 @@ EOT
                                $liContents = $link;
                        } elseif ( count( $redirects[$element->page_title] ) === 0 ) {
                                # Redirect without usages
-                               $liContents = $this->getContext()->msg( 'linkstoimage-redirect' )->rawParams( $link, '' )->parse();
+                               $liContents = $this->getContext()->msg( 'linkstoimage-redirect' )
+                                       ->rawParams( $link, '' )
+                                       ->parse();
                        } else {
                                # Redirect with usages
                                $li = '';
@@ -923,7 +936,10 @@ EOT
                        } else {
                                $link = Linker::makeExternalLink( $file->getDescriptionUrl(),
                                        $file->getTitle()->getPrefixedText() );
-                               $fromSrc = $this->getContext()->msg( 'shared-repo-from', $file->getRepo()->getDisplayName() )->text();
+                               $fromSrc = $this->getContext()->msg(
+                                       'shared-repo-from',
+                                       $file->getRepo()->getDisplayName()
+                               )->text();
                        }
                        $out->addHTML( "<li>{$link} {$fromSrc}</li>\n" );
                }
@@ -1068,8 +1084,10 @@ EOT
                );
                $submit = Xml::submitButton( $this->getContext()->msg( 'img-lang-go' )->text() );
 
-               $formContents = $this->getContext()->msg( 'img-lang-info' )->rawParams( $select, $submit )->parse()
-                       . Html::hidden( 'title', $this->getTitle()->getPrefixedDBkey() );
+               $formContents = $this->getContext()->msg( 'img-lang-info' )
+                       ->rawParams( $select, $submit )
+                       ->parse();
+               $formContents .= Html::hidden( 'title', $this->getTitle()->getPrefixedDBkey() );
 
                $langSelectLine = Html::rawElement( 'div', array( 'id' => 'mw-imglangselector-line' ),
                        Html::rawElement( 'form', array( 'action' => $wgScript ), $formContents )
@@ -1141,7 +1159,9 @@ EOT
                } else {
                        # Creating thumb links triggers thumbnail generation.
                        # Just generate the thumb for the current users prefs.
-                       $thumbSizes = array( $this->getImageLimitsFromOption( $this->getContext()->getUser(), 'thumbsize' ) );
+                       $thumbSizes = array(
+                               $this->getImageLimitsFromOption( $this->getContext()->getUser(), 'thumbsize' )
+                       );
                        if ( !$this->displayImg->mustRender() ) {
                                // We can safely include a link to the "full-size" preview,
                                // without actually rendering.
index d1cec60..8b88986 100644 (file)
@@ -2409,6 +2409,7 @@ class WikiPage implements Page, IDBAccessObject {
 
                $logRelationsValues = array();
                $logRelationsField = null;
+               $logParamsDetails = array();
 
                if ( $id ) { // Protection of existing page
                        if ( !Hooks::run( 'ArticleProtect', array( &$this, &$user, $limit, $reason ) ) ) {
@@ -2467,6 +2468,7 @@ class WikiPage implements Page, IDBAccessObject {
                                        __METHOD__
                                );
                                if ( $restrictions != '' ) {
+                                       $cascadeValue = ( $cascade && $action == 'edit' ) ? 1 : 0;
                                        $dbw->insert(
                                                'page_restrictions',
                                                array(
@@ -2474,12 +2476,18 @@ class WikiPage implements Page, IDBAccessObject {
                                                        'pr_page' => $id,
                                                        'pr_type' => $action,
                                                        'pr_level' => $restrictions,
-                                                       'pr_cascade' => ( $cascade && $action == 'edit' ) ? 1 : 0,
+                                                       'pr_cascade' => $cascadeValue,
                                                        'pr_expiry' => $dbw->encodeExpiry( $expiry[$action] )
                                                ),
                                                __METHOD__
                                        );
                                        $logRelationsValues[] = $dbw->insertId();
+                                       $logParamsDetails[] = array(
+                                               'type' => $action,
+                                               'level' => $restrictions,
+                                               'expiry' => $expiry[$action],
+                                               'cascade' => (bool)$cascadeValue,
+                                       );
                                }
                        }
 
@@ -2510,6 +2518,11 @@ class WikiPage implements Page, IDBAccessObject {
                                                'pt_reason' => $reason,
                                        ), __METHOD__
                                );
+                               $logParamsDetails[] = array(
+                                       'type' => 'create',
+                                       'level' => $limit['create'],
+                                       'expiry' => $expiry['create'],
+                               );
                        } else {
                                $dbw->delete( 'protected_titles',
                                        array(
@@ -2527,15 +2540,24 @@ class WikiPage implements Page, IDBAccessObject {
                        $params = array();
                } else {
                        $protectDescriptionLog = $this->protectDescriptionLog( $limit, $expiry );
-                       $params = array( $protectDescriptionLog, $cascade ? 'cascade' : '' );
+                       $params = array(
+                               '4::description' => $protectDescriptionLog, // parameter for IRC
+                               '5:bool:cascade' => $cascade,
+                               'details' => $logParamsDetails, // parameter for localize and api
+                       );
                }
 
                // Update the protection log
-               $log = new LogPage( 'protect' );
-               $logId = $log->addEntry( $logAction, $this->mTitle, $reason, $params, $user );
+               $logEntry = new ManualLogEntry( 'protect', $logAction );
+               $logEntry->setTarget( $this->mTitle );
+               $logEntry->setComment( $reason );
+               $logEntry->setPerformer( $user );
+               $logEntry->setParameters( $params );
                if ( $logRelationsField !== null && count( $logRelationsValues ) ) {
-                       $log->addRelations( $logRelationsField, $logRelationsValues, $logId );
+                       $logEntry->setRelations( array( $logRelationsField => $logRelationsValues ) );
                }
+               $logId = $logEntry->insert();
+               $logEntry->publish( $logId );
 
                return Status::newGood();
        }
@@ -2916,7 +2938,7 @@ class WikiPage implements Page, IDBAccessObject {
 
                // remove secondary indexes, etc
                $updates = $this->getDeletionUpdates( $content );
-               DataUpdate::runUpdates( $updates );
+               DataUpdate::runUpdates( $updates, 'enqueue' );
 
                // Reparse any pages transcluding this page
                LinksUpdate::queueRecursiveJobsForTable( $this->mTitle, 'templatelinks' );
index 677da63..3e5e8a1 100644 (file)
@@ -1474,8 +1474,9 @@ class Parser {
                # The characters '<' and '>' (which were escaped by
                # removeHTMLtags()) should not be included in
                # URLs, per RFC 2396.
+               # Make &nbsp; terminate a URL as well (bug T84937)
                $m2 = array();
-               if ( preg_match( '/&(lt|gt);/', $url, $m2, PREG_OFFSET_CAPTURE ) ) {
+               if ( preg_match( '/&(lt|gt|nbsp|#x0*(3[CcEe]|[Aa]0)|#0*(60|62|160));/', $url, $m2, PREG_OFFSET_CAPTURE ) ) {
                        $trail = substr( $url, $m2[0][1] ) . $trail;
                        $url = substr( $url, 0, $m2[0][1] );
                }
@@ -4842,16 +4843,7 @@ class Parser {
                $ts = $this->mOptions->getTimestamp();
                $timestamp = MWTimestamp::getLocalInstance( $ts );
                $ts = $timestamp->format( 'YmdHis' );
-               $tzMsg = $timestamp->format( 'T' );  # might vary on DST changeover!
-
-               # Allow translation of timezones through wiki. format() can return
-               # whatever crap the system uses, localised or not, so we cannot
-               # ship premade translations.
-               $key = 'timezone-' . strtolower( trim( $tzMsg ) );
-               $msg = wfMessage( $key )->inContentLanguage();
-               if ( $msg->exists() ) {
-                       $tzMsg = $msg->text();
-               }
+               $tzMsg = $timestamp->getTimezoneMessage()->inContentLanguage()->text();
 
                $d = $wgContLang->timeanddate( $ts, false, false ) . " ($tzMsg)";
 
@@ -5326,7 +5318,6 @@ class Parser {
                $ig->setParser( $this );
                $ig->setHideBadImages();
                $ig->setAttributes( Sanitizer::validateTagAttributes( $params, 'table' ) );
-               $this->getOutput()->addModuleStyles( 'mediawiki.page.gallery.styles' );
 
                if ( isset( $params['showfilename'] ) ) {
                        $ig->setShowFilename( true );
index 6c58453..50a77ec 100644 (file)
@@ -32,7 +32,7 @@
 function wfGetRusage() {
        if ( !function_exists( 'getrusage' ) ) {
                return false;
-       } elseif ( defined( 'HHVM_VERSION' ) ) {
+       } elseif ( defined( 'HHVM_VERSION' ) && PHP_OS === 'Linux' ) {
                return getrusage( 2 /* RUSAGE_THREAD */ );
        } else {
                return getrusage( 0 /* RUSAGE_SELF */ );
index 23e29d8..f91aeff 100644 (file)
@@ -114,12 +114,22 @@ class ExtensionRegistry {
        }
 
        public function loadFromQueue() {
+               global $wgVersion;
                if ( !$this->queued ) {
                        return;
                }
 
+               // A few more things to vary the cache on
+               $versions = array(
+                       'registration' => self::CACHE_VERSION,
+                       'mediawiki' => $wgVersion
+               );
+
                // See if this queue is in APC
-               $key = wfMemcKey( 'registration', md5( json_encode( $this->queued ) ), self::CACHE_VERSION );
+               $key = wfMemcKey(
+                       'registration',
+                       md5( json_encode( $this->queued + $versions ) )
+               );
                $data = $this->cache->get( $key );
                if ( $data ) {
                        $this->exportExtractedData( $data );
index 1f48514..ab36701 100644 (file)
@@ -941,10 +941,10 @@ class ResourceLoader implements LoggerAwareInterface {
                global $wgShowExceptionDetails;
 
                if ( !$wgShowExceptionDetails ) {
-                       return 'Internal error';
+                       return MWExceptionHandler::getPublicLogMessage( $e );
                }
 
-               return $e->__toString();
+               return MWExceptionHandler::getLogMessage( $e );
        }
 
        /**
index da729fd..ef51e0c 100644 (file)
  * @since 1.24
  */
 class ResourceLoaderEditToolbarModule extends ResourceLoaderFileModule {
-       /**
-        * Serialize a string (escape and quote) for use as a CSS string value.
-        * http://www.w3.org/TR/2013/WD-cssom-20131205/#serialize-a-string
-        *
-        * @param string $value
-        * @return string
-        * @throws Exception
-        */
-       private static function cssSerializeString( $value ) {
-               if ( strstr( $value, "\0" ) ) {
-                       throw new Exception( "Invalid character in CSS string" );
-               }
-               $value = strtr( $value, array( '\\' => '\\\\', '"' => '\\"' ) );
-               $value = preg_replace_callback( '/[\x01-\x1f\x7f-\x9f]/', function ( $match ) {
-                       return '\\' . base_convert( ord( $match[0] ), 10, 16 ) . ' ';
-               }, $value );
-               return '"' . $value . '"';
-       }
 
        /**
         * Get language-specific LESS variables for this module.
@@ -58,7 +40,7 @@ class ResourceLoaderEditToolbarModule extends ResourceLoaderFileModule {
 
                // less.php tries to be helpful and parse our variables as LESS source code
                foreach ( $vars as $key => &$value ) {
-                       $value = self::cssSerializeString( $value );
+                       $value = CSSMin::serializeStringValue( $value );
                }
 
                return $vars;
index 7fbc1cb..ca10ab7 100644 (file)
@@ -417,21 +417,8 @@ class ResourceLoaderFileModule extends ResourceLoaderModule {
                );
                // Collect referenced files
                $this->localFileRefs = array_unique( $this->localFileRefs );
-               // If the list has been modified since last time we cached it, update the cache
-               try {
-                       if ( $this->localFileRefs !== $this->getFileDependencies( $context->getSkin() ) ) {
-                               $dbw = wfGetDB( DB_MASTER );
-                               $dbw->replace( 'module_deps',
-                                       array( array( 'md_module', 'md_skin' ) ), array(
-                                               'md_module' => $this->getName(),
-                                               'md_skin' => $context->getSkin(),
-                                               'md_deps' => FormatJson::encode( $this->localFileRefs ),
-                                       )
-                               );
-                       }
-               } catch ( Exception $e ) {
-                       wfDebugLog( 'resourceloader', __METHOD__ . ": failed to update DB: $e" );
-               }
+               $this->saveFileDependencies( $context->getSkin(), $this->localFileRefs );
+
                return $styles;
        }
 
@@ -966,12 +953,44 @@ class ResourceLoaderFileModule extends ResourceLoaderModule {
         * @return string CSS source
         */
        protected function compileLessFile( $fileName, $compiler = null ) {
+               static $cache;
+
+               if ( !$cache ) {
+                       $cache = ObjectCache::newAccelerator( CACHE_ANYTHING );
+               }
+
+               // Construct a cache key from the LESS file name and a hash digest
+               // of the LESS variables used for compilation.
+               $varsHash = hash( 'md4', serialize( ResourceLoader::getLessVars( $this->getConfig() ) ) );
+               $cacheKey = wfGlobalCacheKey( 'LESS', $fileName, $varsHash );
+               $cachedCompile = $cache->get( $cacheKey );
+
+               // If we got a cached value, we have to validate it by getting a
+               // checksum of all the files that were loaded by the parser and
+               // ensuring it matches the cached entry's.
+               if ( isset( $cachedCompile['hash'] ) ) {
+                       $contentHash = FileContentsHasher::getFileContentsHash( $cachedCompile['files'] );
+                       if ( $contentHash === $cachedCompile['hash'] ) {
+                               $this->localFileRefs += $cachedCompile['files'];
+                               return $cachedCompile['css'];
+                       }
+               }
+
                if ( !$compiler ) {
                        $compiler = $this->getLessCompiler();
                }
-               $result = $compiler->parseFile( $fileName )->getCss();
-               $this->localFileRefs += array_keys( $compiler->AllParsedFiles() );
-               return $result;
+
+               $css = $compiler->parseFile( $fileName )->getCss();
+               $files = $compiler->AllParsedFiles();
+               $this->localFileRefs = array_merge( $this->localFileRefs, $files );
+
+               $cache->set( $cacheKey, array(
+                       'css'   => $css,
+                       'files' => $files,
+                       'hash'  => FileContentsHasher::getFileContentsHash( $files ),
+               ), 60 * 60 * 24 );  // 86400 seconds, or 24 hours.
+
+               return $css;
        }
 
        /**
index 1d3ffb5..80c8220 100644 (file)
@@ -374,12 +374,13 @@ abstract class ResourceLoaderModule {
 
        /**
         * Get the files this module depends on indirectly for a given skin.
-        * Currently these are only image files referenced by the module's CSS.
+        *
+        * These are only image files referenced by the module's stylesheet.
         *
         * @param string $skin Skin name
         * @return array List of files
         */
-       public function getFileDependencies( $skin ) {
+       protected function getFileDependencies( $skin ) {
                // Try in-object cache first
                if ( isset( $this->fileDeps[$skin] ) ) {
                        return $this->fileDeps[$skin];
@@ -405,8 +406,11 @@ abstract class ResourceLoaderModule {
        }
 
        /**
-        * Set preloaded file dependency information. Used so we can load this
-        * information for all modules at once.
+        * Set in-object cache for file dependencies.
+        *
+        * This is used to retrieve data in batches. See ResourceLoader::preloadModuleInfo().
+        * To save the data, use saveFileDependencies().
+        *
         * @param string $skin Skin name
         * @param array $deps Array of file names
         */
@@ -414,6 +418,31 @@ abstract class ResourceLoaderModule {
                $this->fileDeps[$skin] = $deps;
        }
 
+       /**
+        * Set the files this module depends on indirectly for a given skin.
+        *
+        * @since 1.26
+        * @param string $skin Skin name
+        * @param array $localFileRefs List of files
+        */
+       protected function saveFileDependencies( $skin, $localFileRefs ) {
+               try {
+                       // If the list has been modified since last time we cached it, update the cache
+                       if ( $localFileRefs !== $this->getFileDependencies( $skin ) ) {
+                               $dbw = wfGetDB( DB_MASTER );
+                               $dbw->replace( 'module_deps',
+                                       array( array( 'md_module', 'md_skin' ) ), array(
+                                               'md_module' => $this->getName(),
+                                               'md_skin' => $skin,
+                                               'md_deps' => FormatJson::encode( $localFileRefs ),
+                                       )
+                               );
+                       }
+               } catch ( Exception $e ) {
+                       wfDebugLog( 'resourceloader', __METHOD__ . ": failed to update DB: $e" );
+               }
+       }
+
        /**
         * Get the last modification timestamp of the messages in this module for a given language.
         * @param string $lang Language code
@@ -445,8 +474,10 @@ abstract class ResourceLoaderModule {
        }
 
        /**
-        * Set a preloaded message blob last modification timestamp. Used so we
-        * can load this information for all modules at once.
+        * Set in-object cache for message blob time.
+        *
+        * This is used to retrieve data in batches. See ResourceLoader::preloadModuleInfo().
+        *
         * @param string $lang Language code
         * @param int $mtime UNIX timestamp
         */
@@ -857,35 +888,6 @@ abstract class ResourceLoaderModule {
         * @return string Hash
         */
        protected static function safeFileHash( $filePath ) {
-               static $cache;
-
-               if ( !$cache ) {
-                       $cache = ObjectCache::newAccelerator( CACHE_NONE );
-               }
-
-               MediaWiki\suppressWarnings();
-               $mtime = filemtime( $filePath );
-               MediaWiki\restoreWarnings();
-               if ( !$mtime ) {
-                       return '';
-               }
-
-               $cacheKey = wfGlobalCacheKey( 'resourceloader', __METHOD__, $filePath );
-               $cachedHash = $cache->get( $cacheKey );
-               if ( isset( $cachedHash['mtime'] ) && $cachedHash['mtime'] === $mtime ) {
-                       return $cachedHash['hash'];
-               }
-
-               MediaWiki\suppressWarnings();
-               $contents = file_get_contents( $filePath );
-               MediaWiki\restoreWarnings();
-               if ( !$contents ) {
-                       return '';
-               }
-
-               $hash = hash( 'md4', $contents );
-               $cache->set( $cacheKey, array( 'mtime' => $mtime, 'hash' => $hash ), 60 * 60 * 24 );
-
-               return $hash;
+               return FileContentsHasher::getFileContentsHash( $filePath );
        }
 }
index 7d5d38f..98c05b5 100644 (file)
@@ -547,7 +547,9 @@ class SearchHighlighter {
                $text = ltrim( $text ) . "\n"; // make sure the preg_match may find the last line
                $text = str_replace( "\n\n", "\n", $text ); // remove empty lines
                preg_match( "/^(.*\n){0,$contextlines}/", $text, $match );
-               $text = htmlspecialchars( substr( trim( $match[0] ), 0, $contextlines * $contextchars ) ); // trim and limit to max number of chars
+
+               // Trim and limit to max number of chars
+               $text = htmlspecialchars( substr( trim( $match[0] ), 0, $contextlines * $contextchars ) );
                return str_replace( "\n", '<br>', $text );
        }
 }
index 95631f8..029919c 100644 (file)
@@ -113,9 +113,9 @@ class MediaWikiSite extends Site {
                        return $t->getPrefixedText();
                } else {
 
-                       // Make sure the string is normalized into NFC (due to the bug 40017)
+                       // Make sure the string is normalized into NFC (due to T42017)
                        // but do nothing to the whitespaces, that should work appropriately.
-                       // @see https://bugzilla.wikimedia.org/show_bug.cgi?id=40017
+                       // @see https://phabricator.wikimedia.org/T42017
                        $pageName = UtfNormal\Validator::cleanUp( $pageName );
 
                        // Build the args for the specific call
index 4cdf6dd..e2bc629 100644 (file)
@@ -42,6 +42,7 @@ class SpecialImport extends SpecialPage {
        private $history = true;
        private $includeTemplates = false;
        private $pageLinkDepth;
+       private $importSources;
 
        /**
         * Constructor
@@ -66,6 +67,9 @@ class SpecialImport extends SpecialPage {
 
                $this->getOutput()->addModules( 'mediawiki.special.import' );
 
+               $this->importSources = $this->getConfig()->get( 'ImportSources' );
+               Hooks::run( 'ImportSources', array( &$this->importSources ) );
+
                $user = $this->getUser();
                if ( !$user->isAllowedAny( 'import', 'importupload' ) ) {
                        throw new PermissionsError( 'import' );
@@ -136,16 +140,17 @@ class SpecialImport extends SpecialPage {
                        }
                        $this->interwiki = $this->fullInterwikiPrefix = $request->getVal( 'interwiki' );
                        // does this interwiki have subprojects?
-                       $importSources = $this->getConfig()->get( 'ImportSources' );
-                       $hasSubprojects = array_key_exists( $this->interwiki, $importSources );
-                       if ( !$hasSubprojects && !in_array( $this->interwiki, $importSources ) ) {
+                       $hasSubprojects = array_key_exists( $this->interwiki, $this->importSources );
+                       if ( !$hasSubprojects && !in_array( $this->interwiki, $this->importSources ) ) {
                                $source = Status::newFatal( "import-invalid-interwiki" );
                        } else {
                                if ( $hasSubprojects ) {
                                        $this->subproject = $request->getVal( 'subproject' );
                                        $this->fullInterwikiPrefix .= ':' . $request->getVal( 'subproject' );
                                }
-                               if ( $hasSubprojects && !in_array( $this->subproject, $importSources[$this->interwiki] ) ) {
+                               if ( $hasSubprojects &&
+                                       !in_array( $this->subproject, $this->importSources[$this->interwiki] )
+                               ) {
                                        $source = Status::newFatal( "import-invalid-interwiki" );
                                } else {
                                        $this->history = $request->getCheck( 'interwikiHistory' );
@@ -306,7 +311,6 @@ class SpecialImport extends SpecialPage {
                $user = $this->getUser();
                $out = $this->getOutput();
                $this->addHelpLink( '//meta.wikimedia.org/wiki/Special:MyLanguage/Help:Import', true );
-               $importSources = $this->getConfig()->get( 'ImportSources' );
 
                if ( $user->isAllowed( 'importupload' ) ) {
                        $mappingSelection = $this->getMappingFormPart( 'upload' );
@@ -356,12 +360,12 @@ class SpecialImport extends SpecialPage {
                                        Xml::closeElement( 'fieldset' )
                        );
                } else {
-                       if ( empty( $importSources ) ) {
+                       if ( empty( $this->importSources ) ) {
                                $out->addWikiMsg( 'importnosources' );
                        }
                }
 
-               if ( $user->isAllowed( 'import' ) && !empty( $importSources ) ) {
+               if ( $user->isAllowed( 'import' ) && !empty( $this->importSources ) ) {
                        # Show input field for import depth only if $wgExportMaxLinkDepth > 0
                        $importDepth = '';
                        if ( $this->getConfig()->get( 'ExportMaxLinkDepth' ) > 0 ) {
@@ -403,7 +407,7 @@ class SpecialImport extends SpecialPage {
                        );
 
                        $needSubprojectField = false;
-                       foreach ( $importSources as $key => $value ) {
+                       foreach ( $this->importSources as $key => $value ) {
                                if ( is_int( $key ) ) {
                                        $key = $value;
                                } elseif ( $value !== $key ) {
@@ -435,7 +439,7 @@ class SpecialImport extends SpecialPage {
                                );
 
                                $subprojectsToAdd = array();
-                               foreach ( $importSources as $key => $value ) {
+                               foreach ( $this->importSources as $key => $value ) {
                                        if ( is_array( $value ) ) {
                                                $subprojectsToAdd = array_merge( $subprojectsToAdd, $value );
                                        }
index 2d79aaf..3ea56c6 100644 (file)
@@ -52,7 +52,7 @@ class SpecialListFiles extends IncludableSpecialPage {
                if ( $this->including() ) {
                        $out->addParserOutputContent( $pager->getBodyOutput() );
                } else {
-                       $out->addHTML( $pager->getForm() );
+                       $pager->getForm();
                        $out->addParserOutputContent( $pager->getFullOutput() );
                }
        }
index 8091f1b..f7a0a20 100644 (file)
@@ -342,6 +342,7 @@ class MovePageForm extends UnlistedSpecialPage {
                                'id' => 'wpReason',
                                'maxLength' => 200,
                                'infusable' => true,
+                               'value' => $this->reason,
                        ) ),
                        array(
                                'label' => $this->msg( 'movereason' )->text(),
@@ -445,8 +446,6 @@ class MovePageForm extends UnlistedSpecialPage {
                }
 
                if ( $confirm ) {
-                       $watchChecked = $user->isLoggedIn() && ( $this->watch || $user->getBoolOption( 'watchmoves' )
-                               || $user->isWatched( $this->oldTitle ) );
                        $fields[] = new OOUI\FieldLayout(
                                new OOUI\CheckboxInputWidget( array(
                                        'name' => 'wpConfirm',
index 251a8e0..c8d4aa6 100644 (file)
@@ -315,7 +315,7 @@ class SpecialNewpages extends IncludableSpecialPage {
                        array()
                );
 
-               $query = array( 'redirect' => 'no' );
+               $query = $title->isRedirect() ? array( 'redirect' => 'no' ) : array();
 
                // Linker::linkKnown() uses 'known' and 'noclasses' options.
                // This breaks the colouration for stubs.
index fd0745f..3f67e93 100644 (file)
@@ -388,7 +388,7 @@ class SpecialSearch extends SpecialPage {
                        }
                }
 
-               $out->addHTML( '<div class="visualClear"></div>\n' );
+               $out->addHTML( '<div class="visualClear"></div>' );
                if ( $prevnext ) {
                        $out->addHTML( "<p class='mw-search-pager-bottom'>{$prevnext}</p>\n" );
                }
@@ -415,10 +415,7 @@ class SpecialSearch extends SpecialPage {
                        return false;
                }
 
-               // Generate a random number between 0 and 1. If the
-               // number is less than the desired percentages run it.
-               $rand = rand( 0, getrandmax() ) / getrandmax();
-               return $this->getConfig()->get( 'SearchRunSuggestedQueryPercent' ) > $rand;
+               return $this->getConfig()->get( 'SearchRunSuggestedQuery' );
        }
 
        /**
@@ -436,7 +433,7 @@ class SpecialSearch extends SpecialPage {
                $suggest = Linker::linkKnown(
                        $this->getPageTitle(),
                        $textMatches->getSuggestionSnippet() ?: null,
-                       array(),
+                       array( 'id' => 'mw-search-DYM-suggestion' ),
                        $stParams
                );
 
@@ -470,7 +467,7 @@ class SpecialSearch extends SpecialPage {
                $rewritten = Linker::linkKnown(
                        $this->getPageTitle(),
                        $textMatches->getQueryAfterRewriteSnippet() ?: null,
-                       array(),
+                       array( 'id' => 'mw-search-DYM-rewritten' ),
                        $stParams
                );
 
@@ -479,7 +476,7 @@ class SpecialSearch extends SpecialPage {
                $original = Linker::linkKnown(
                        $this->getPageTitle(),
                        htmlspecialchars( $term ),
-                       array(),
+                       array( 'id' => 'mw-search-DYM-original' ),
                        $stParams
                );
 
index 21f1194..ee78a61 100644 (file)
@@ -1380,7 +1380,7 @@ class LoginForm extends SpecialPage {
 
                if ( $this->mType == 'signup' ) {
                        // XXX hack pending RL or JS parse() support for complex content messages
-                       // https://bugzilla.wikimedia.org/show_bug.cgi?id=25349
+                       // https://phabricator.wikimedia.org/T27349
                        $out->addJsConfigVars( 'wgCreateacctImgcaptchaHelp',
                                $this->msg( 'createacct-imgcaptcha-help' )->parse() );
 
index 23e445f..e2187f0 100644 (file)
@@ -1,6 +1,7 @@
 <?php
 
 namespace MediaWiki\Tidy;
+
 use MWHttpRequest;
 use Exception;
 
index 083f402..3f549d0 100644 (file)
@@ -86,4 +86,3 @@ class RaggettWrapper {
        }
 
 }
-?>
index 1d994aa..61fd3e4 100644 (file)
@@ -36,5 +36,5 @@ abstract class TidyDriverBase {
         * @param string HTML document fragment to clean up
         * @param string The corrected HTML output
         */
-       public abstract function tidy( $text );
+       abstract public function tidy( $text );
 }
index f578745..f897a79 100644 (file)
@@ -175,7 +175,6 @@ class UploadFromUrl extends UploadBase {
                $url = $request->getVal( 'wpUploadFileURL' );
 
                return !empty( $url )
-                       && Http::isValidURI( $url )
                        && $wgUser->isAllowed( 'upload_by_url' );
        }
 
index 4f8e0b1..b9d30d7 100644 (file)
@@ -36,25 +36,25 @@ class AvroValidator {
         *  returned.
         */
        public static function getErrors( AvroSchema $schema, $datum ) {
-               switch ( $schema->type) {
+               switch ( $schema->type ) {
                case AvroSchema::NULL_TYPE:
-                       if ( !is_null($datum) ) {
+                       if ( !is_null( $datum ) ) {
                                return self::wrongType( 'null', $datum );
                        }
                        return array();
                case AvroSchema::BOOLEAN_TYPE:
-                       if ( !is_bool($datum) ) {
+                       if ( !is_bool( $datum ) ) {
                                return self::wrongType( 'boolean', $datum );
                        }
                        return array();
                case AvroSchema::STRING_TYPE:
                case AvroSchema::BYTES_TYPE:
-                       if ( !is_string($datum) ) {
+                       if ( !is_string( $datum ) ) {
                                return self::wrongType( 'string', $datum );
                        }
                        return array();
                case AvroSchema::INT_TYPE:
-                       if ( !is_int($datum) ) {
+                       if ( !is_int( $datum ) ) {
                                return self::wrongType( 'integer', $datum );
                        }
                        if ( AvroSchema::INT_MIN_VALUE > $datum
@@ -68,7 +68,7 @@ class AvroValidator {
                        }
                        return array();
                case AvroSchema::LONG_TYPE:
-                       if ( !is_int($datum) ) {
+                       if ( !is_int( $datum ) ) {
                                return self::wrongType( 'integer', $datum );
                        }
                        if ( AvroSchema::LONG_MIN_VALUE > $datum
@@ -83,32 +83,29 @@ class AvroValidator {
                        return array();
                case AvroSchema::FLOAT_TYPE:
                case AvroSchema::DOUBLE_TYPE:
-                       if ( !is_float($datum) && !is_int($datum) ) {
+                       if ( !is_float( $datum ) && !is_int( $datum ) ) {
                                return self::wrongType( 'float or integer', $datum );
                        }
                        return array();
                case AvroSchema::ARRAY_SCHEMA:
-                       if (!is_array($datum)) {
+                       if ( !is_array( $datum ) ) {
                                return self::wrongType( 'array', $datum );
                        }
                        $errors = array();
-                       foreach ($datum as $d) {
-                               $result = $this->validate( $schema->items(), $d );
+                       foreach ( $datum as $d ) {
+                               $result = self::getErrors( $schema->items(), $d );
                                if ( $result ) {
                                        $errors[] = $result;
                                }
                        }
-                       if ( $errors ) {
-                               return $errors;
-                       }
-                       return array();
+                       return $errors;
                case AvroSchema::MAP_SCHEMA:
-                       if (!is_array($datum)) {
+                       if ( !is_array( $datum ) ) {
                                return self::wrongType( 'array', $datum );
                        }
                        $errors = array();
-                       foreach ($datum as $k => $v) {
-                       if ( !is_string($k) ) {
+                       foreach ( $datum as $k => $v ) {
+                               if ( !is_string( $k ) ) {
                                        $errors[] = self::wrongType( 'string key', $k );
                                }
                                $result = self::getErrors( $schema->values(), $v );
@@ -119,7 +116,7 @@ class AvroValidator {
                        return $errors;
                case AvroSchema::UNION_SCHEMA:
                        $errors = array();
-                       foreach ($schema->schemas() as $schema) {
+                       foreach ( $schema->schemas() as $schema ) {
                                $result = self::getErrors( $schema, $datum );
                                if ( !$result ) {
                                        return array();
diff --git a/includes/utils/FileContentsHasher.php b/includes/utils/FileContentsHasher.php
new file mode 100644 (file)
index 0000000..67eb9d2
--- /dev/null
@@ -0,0 +1,111 @@
+<?php
+/**
+ * Generate hash digests of file contents to help with cache invalidation.
+ *
+ * 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
+ */
+class FileContentsHasher {
+
+       /** @var BagOStuff */
+       protected $cache;
+
+       /** @var FileContentsHasher */
+       private static $instance;
+
+       /**
+        * Constructor.
+        */
+       public function __construct() {
+               $this->cache = ObjectCache::newAccelerator( 'hash' );
+       }
+
+       /**
+        * Get the singleton instance of this class.
+        *
+        * @return FileContentsHasher
+        */
+       public static function singleton() {
+               if ( !self::$instance ) {
+                       self::$instance = new self;
+               }
+
+               return self::$instance;
+       }
+
+       /**
+        * Get a hash of a file's contents, either by retrieving a previously-
+        * computed hash from the cache, or by computing a hash from the file.
+        *
+        * @private
+        * @param string $filePath Full path to the file.
+        * @param string $algo Name of selected hashing algorithm.
+        * @return string|bool Hash of file contents, or false if the file could not be read.
+        */
+       public function getFileContentsHashInternal( $filePath, $algo = 'md4' ) {
+               $mtime = MediaWiki\quietCall( 'filemtime', $filePath );
+               if ( $mtime === false ) {
+                       return false;
+               }
+
+               $cacheKey = wfGlobalCacheKey( __CLASS__, $filePath, $mtime, $algo );
+               $hash = $this->cache->get( $cacheKey );
+
+               if ( $hash ) {
+                       return $hash;
+               }
+
+               $contents = MediaWiki\quietCall( 'file_get_contents', $filePath );
+               if ( $contents === false ) {
+                       return false;
+               }
+
+               $hash = hash( $algo, $contents );
+               $this->cache->set( $cacheKey, $hash, 60 * 60 * 24 );  // 24h
+
+               return $hash;
+       }
+
+       /**
+        * Get a hash of the combined contents of one or more files, either by
+        * retrieving a previously-computed hash from the cache, or by computing
+        * a hash from the files.
+        *
+        * @param string|string[] $filePaths One or more file paths.
+        * @param string $algo Name of selected hashing algorithm.
+        * @return string|bool Hash of files' contents, or false if no file could not be read.
+        */
+       public static function getFileContentsHash( $filePaths, $algo = 'md4' ) {
+               $instance = self::singleton();
+
+               if ( !is_array( $filePaths ) ) {
+                       $filePaths = (array) $filePaths;
+               }
+
+               if ( count( $filePaths ) === 1 ) {
+                       return $instance->getFileContentsHashInternal( $filePaths[0], $algo );
+               }
+
+               sort( $filePaths );
+               $hashes = array_map( function ( $filePath ) use ( $instance, $algo ) {
+                       return $instance->getFileContentsHashInternal( $filePath, $algo ) ?: '';
+               }, $filePaths );
+
+               $hashes = implode( '', $hashes );
+               return $hashes ? hash( $algo, $hashes ) : false;
+       }
+}
index 1613536..be97d83 100644 (file)
@@ -34,6 +34,8 @@ if ( function_exists( 'mb_strtoupper' ) ) {
        mb_internal_encoding( 'UTF-8' );
 }
 
+use CLDRPluralRuleParser\Evaluator;
+
 /**
  * Internationalisation code
  * @ingroup Language
@@ -2678,7 +2680,7 @@ class Language {
                # Even with //IGNORE iconv can whine about illegal characters in
                # *input* string. We just ignore those too.
                # REF: http://bugs.php.net/bug.php?id=37166
-               # REF: https://bugzilla.wikimedia.org/show_bug.cgi?id=16885
+               # REF: https://phabricator.wikimedia.org/T18885
                MediaWiki\suppressWarnings();
                $text = iconv( $in, $out . '//IGNORE', $string );
                MediaWiki\restoreWarnings();
@@ -4968,7 +4970,7 @@ class Language {
         */
        public function getPluralRuleIndexNumber( $number ) {
                $pluralRules = $this->getCompiledPluralRules();
-               $form = CLDRPluralRuleEvaluator::evaluateCompiled( $number, $pluralRules );
+               $form = Evaluator::evaluateCompiled( $number, $pluralRules );
                return $form;
        }
 
index a81f17f..5a14003 100644 (file)
        'nv' => 'Diné bizaad', # Navajo
        'ny' => 'Chi-Chewa',    # Chichewa
        'oc' => 'occitan',              # Occitan
+       'olo' => 'Livvinкarjala',              # Livvi-Karelian
        'om' => 'Oromoo',               # Oromo
        'or' => 'ଓଡ଼ିଆ',              # Oriya
        'os' => 'Ирон', # Ossetic, bug 29091
index 2b6bd58..a2c308d 100644 (file)
        "nstab-template": "Шапхъэ",
        "nstab-help": "IэпыIэгъу нэкIубгъу",
        "nstab-category": "Категорие",
+       "mainpage-nstab": "НэкӀубгъо шъхьаӀ",
        "nosuchaction": "Ащ фэдэ шIагъэ щыIэп",
        "nosuchspecialpage": "Афэдэ специал нэкIубгъо щыIэп",
        "error": "Къончагъэ",
        "cannotdelete-title": "НэкIубгъоу \"$1\" тегъэкIыгъэн лъэкIырэп",
        "badtitle": "ЦӀэ дэгъуэп",
        "badtitletext": "УзкIэупчIэрэ нэкIубгъом ыцIэр къуанчэ, е нэкIы, е бзэзэпыщэ е интервики гъэнэфагъэп.\nМыхъущт символ агъэфедагъэнкIи мэхъу.",
+       "title-invalid-characters": "УзыкIэупчIэрэ нэкIубгъуацIэм символ фыкъуагъэ хэт: \"$1\".",
        "viewsource": "Еплъ лъапсэм",
        "viewsource-title": "Еплъ лъапсэм $1 пае",
        "protectedpagetext": "ЕIэзэнхэм ыкIи нэмыкI шIэнмэ яягъэ къэмыкIынэу мы нэкIубгъор ухъумагъэу щыт.",
        "noname": "НэбгырацIэ тэрэз итхагъэп.",
        "loginsuccesstitle": "ШIоу ухэхьагъ",
        "nouserspecified": "НэбгырацIэр птхын фае.",
+       "login-userblocked": "Мы нэбгырэр блокыгъэ. Системэм хэхьашъущтэп.",
        "wrongpassword": "ШъэфгущыIэр тэрэзэп.\nДжыри зэ еплъ.",
        "wrongpasswordempty": "ШъэфгущыIэр нэкIы.\nДжыри зэ еплъ.",
        "passwordtooshort": "ШъэфгущыIэр мыхъуми {{PLURAL:$1|1 символ |символ $1}} хъун фае.",
        "passwordreset-emailsent": "ШъэфгущыIэм и зэтедзым пае емэйл агъэхьыгъ.",
        "passwordreset-emailsent-capture": "ШъэфгущыIэм изэтедз фэгъэхьыгъэ емэйлыр гъахьыгъэ, ычIэгъкIэ ар олъэгъу.",
        "changeemail": "Зэблэхъу емэйл адресыр",
+       "changeemail-no-info": "Мы нэкIубгъом занкIэу укIонэу уфаемэ, системэм ухэхьэгъэн фае.",
        "changeemail-oldemail": "Джырэ емэйл адрес:",
        "changeemail-newemail": "Емэйл адресыкIэр:",
        "changeemail-none": "(зи)",
        "upload-misc-error": "ЗэхэмышIыкIыгъэ илъхьан фыкъуагъэ",
        "upload-http-error": "HTTP фыкъуагъэ горэ хъугъэ: $1",
        "upload-dialog-title": "Файлыр илъхь",
-       "upload-dialog-error": "Фыкъуагъэ горэ хъугъэ",
        "upload-dialog-button-cancel": "Ыуж икӀ",
        "upload-dialog-button-done": "ЗэшIуэкIыгъэ",
        "upload-dialog-button-save": "Итх",
        "upload-dialog-button-upload": "Илъхь",
-       "upload-dialog-label-select-file": "Къыхэх файл",
-       "upload-dialog-label-infoform-title": "Къэбар",
-       "upload-dialog-label-infoform-name": "ЦIэ",
-       "upload-dialog-label-infoform-description": "АгурыбгъэIон",
-       "upload-dialog-label-usage-title": "Гъэфедэныгъэ",
-       "upload-dialog-label-usage-filename": "ФайлыцIэ",
+       "upload-process-error": "Фыкъуагъэ горэ хъугъэ",
+       "upload-form-label-select-file": "Къыхэх файл",
+       "upload-form-label-infoform-title": "Къэбар",
+       "upload-form-label-infoform-name": "ЦIэ",
+       "upload-form-label-infoform-description": "АгурыбгъэIон",
+       "upload-form-label-usage-title": "Гъэфедэныгъэ",
+       "upload-form-label-usage-filename": "ФайлыцIэ",
        "backend-fail-notexists": "Файлэу $1 щыIэп.",
        "backend-fail-delete": "Файлэу \"$1\" тегъэкIын лъэкIырэп.",
        "backend-fail-read": "Файлэу \"$1\" еплъын лъэкIырэп.",
index f2ee892..8835795 100644 (file)
        "upload-http-error": "'n HTTP-fout het voorgekom: $1",
        "upload-copy-upload-invalid-domain": "Gekopieerde oplaaie word nie vanuit die domein toegelaat nie.",
        "upload-dialog-title": "Laai lêer op",
-       "upload-dialog-error": "'n Fout het voorgekom",
-       "upload-dialog-warning": "'n Waarskuwing is uitgereik",
        "upload-dialog-button-cancel": "Kanselleer",
        "upload-dialog-button-done": "Gedoen",
        "upload-dialog-button-save": "Stoor",
        "upload-dialog-button-upload": "Oplaai",
-       "upload-dialog-label-select-file": "Kies lêer",
-       "upload-dialog-label-infoform-title": "Details",
-       "upload-dialog-label-infoform-name": "Naam",
-       "upload-dialog-label-infoform-description": "Beskrywing",
-       "upload-dialog-label-usage-title": "Gebruik",
-       "upload-dialog-label-usage-filename": "Lêernaam",
+       "upload-process-error": "'n Fout het voorgekom",
+       "upload-process-warning": "'n Waarskuwing is uitgereik",
+       "upload-form-label-select-file": "Kies lêer",
+       "upload-form-label-infoform-title": "Details",
+       "upload-form-label-infoform-name": "Naam",
+       "upload-form-label-infoform-description": "Beskrywing",
+       "upload-form-label-usage-title": "Gebruik",
+       "upload-form-label-usage-filename": "Lêernaam",
        "backend-fail-stream": "Kon nie die lêer $1 uitstroom nie.",
        "backend-fail-backup": "Kon nie 'n rugsteunkopie van die lêer $1 maak nie.",
        "backend-fail-notexists": "Die lêer $1 bestaan nie.",
index 1b89eec..9d4c88f 100644 (file)
        "api-error-badtoken": "Error interna: Simbolo incorrecto.",
        "api-error-copyuploaddisabled": "A puyada por URL ye desactivada en iste servidor.",
        "api-error-duplicate": "Ya existe{{PLURAL:$1| [$2 unatro fichero]|[$2 belatros fichers]}} en o puesto con o mesmo conteniu.",
-       "api-error-duplicate-archive": "Ya {{PLURAL:$1|existiba [$2 unatro fichero]|existiban [$2 atros fichers]}} en o puesto con o mesmo conteniu, pero {{PLURAL:$1|estió|estioron}} {{PLURAL:$1|eliminau|eliminaus}}.",
+       "api-error-duplicate-archive": "Ya {{PLURAL:$1|existiba unatro fichero|existiban atros fichers}} en o puesto con o mesmo conteniu, pero {{PLURAL:$1|estió eliminau|estioron eliminaus}}.",
        "api-error-empty-file": "O fichero que ninvió yera vuedo.",
        "api-error-emptypage": "No se permite a creyación de pachinas nuevas en blanco.",
        "api-error-fetchfileerror": "Error interna: Bella cosa salió malament mientres s'obteneba o fichero.",
index c651e0c..356eaf5 100644 (file)
        "nstab-template": "قالب",
        "nstab-help": "صفحة مساعدة",
        "nstab-category": "تصنيف",
+       "mainpage-nstab": "الصفحة الرئيسية",
        "nosuchaction": "لا يوجد فعل كالذي طلبت",
        "nosuchactiontext": "الفعل المحدد بواسطة المسار غير صحيح.\nربما تكون قد كتبت المسار بطريقة غير صحيحة، أو اتبعت رابطا غير صحيح.\nو قد يكون مرجع هذا علة في {{SITENAME}}.",
        "nosuchspecialpage": "لا توجد صفحة خاصة بهذا الاسم",
        "upload-http-error": "صودف خطأ HTTP: $1",
        "upload-copy-upload-invalid-domain": "رفع النسخ غير متاح من هذا الموقع",
        "upload-dialog-title": "رفع الملف",
-       "upload-dialog-error": "حدث خطأ",
-       "upload-dialog-warning": "حدث تنبيه",
        "upload-dialog-button-cancel": "إلغاء",
        "upload-dialog-button-done": "تم",
        "upload-dialog-button-save": "احفظ",
        "upload-dialog-button-upload": "رفع",
-       "upload-dialog-label-select-file": "اختر ملفا",
-       "upload-dialog-label-infoform-title": "التفاصيل",
-       "upload-dialog-label-infoform-name": "الاسم",
-       "upload-dialog-label-infoform-description": "الوصف",
-       "upload-dialog-label-usage-title": "الاستخدام",
-       "upload-dialog-label-usage-filename": "اسم الملف",
+       "upload-process-error": "حدث خطأ",
+       "upload-process-warning": "حدث تنبيه",
+       "upload-form-label-select-file": "اختر ملفا",
+       "upload-form-label-infoform-title": "التفاصيل",
+       "upload-form-label-infoform-name": "الاسم",
+       "upload-form-label-infoform-description": "الوصف",
+       "upload-form-label-usage-title": "الاستخدام",
+       "upload-form-label-usage-filename": "اسم الملف",
        "backend-fail-stream": "لا يمكن عرض الملف $1.",
        "backend-fail-backup": "لا يمكن صنع نسخة أحتياطية للملف $1.",
        "backend-fail-notexists": "الملف $1 غير موجود.",
        "logentry-newusers-create2": "أنشأ $1 حسابا {{GENDER:$2|للمستخدم|للمستخدمة}} $3",
        "logentry-newusers-byemail": "ُ{{GENDER:$2|أنشأ|أنشأت}} $1 حساب المستخدم $3 وأُرسلت كلمة السر بالبريد الإلكتروني",
        "logentry-newusers-autocreate": "أنشئ حساب {{GENDER:$2|المستخدم|المستخدمة}} $1 تلقائيًا",
+       "logentry-protect-move_prot": "$1 {{GENDER:$2|نقل}} إعدادات الحماية من $4 إلى $3",
        "logentry-rights-rights": "{{GENDER:$2|غيّر|غيّرت}} $1 عضوية $3 من $4 إلى $5",
        "logentry-rights-rights-legacy": "{{GENDER:$2|غيّر|غيّرت}} $1 عضوية $3",
        "logentry-rights-autopromote": "تمت تلقائيا ترقية {{GENDER:$2|المستخدم|المستخدمة}} $1 من  $4 إلى $5",
        "api-error-badtoken": "خطأ داخلي: رمز مميز غير صحيح.",
        "api-error-copyuploaddisabled": "تم تعطيل تحميل من رابط على هذا الخادم.",
        "api-error-duplicate": "هناك {{PLURAL:$1|هو [$2 ملف آخر [|كذلك]$2 بعض الملفات الأخرى]}} مسبقاً على الموقع بنفس المضمون.",
-       "api-error-duplicate-archive": "هناك {{PLURAL:$1|كان [$2 ملف آخر] |كذلك [$2 بعض الملفات الأخرى]}} مسبقاً على الموقع بنفس المضمون، ولكن {{PLURAL:$1|أنه تم | إجراء}} الحذف لها.",
+       "api-error-duplicate-archive": "هناك {{PLURAL:$1|كان ملف آخر |كذلك بعض الملفات الأخرى}} مسبقاً على الموقع بنفس المضمون، ولكن {{PLURAL:$1|أنه تم | إجراء}} الحذف لها.",
        "api-error-empty-file": "كان ملف الذي قمت بإرسال فارغة.",
        "api-error-emptypage": "إنشاء صفحات فارغة جديدة، غير مسموح به.",
        "api-error-fetchfileerror": "خطأ داخلي: قد حدث خطأ أثناء إحضار الملف.",
index 5ea9db5..d55e744 100644 (file)
        "upload-http-error": "এটা HTTP ত্ৰুটিয়ে দেখা দিছে: $1",
        "upload-copy-upload-invalid-domain": "এই ডমেইনত কপী আপল'ড নাপাব।",
        "upload-dialog-title": "ফাইল আপল’ড কৰক",
-       "upload-dialog-error": "এটা ত্ৰুটি পোৱা গৈছে",
-       "upload-dialog-warning": "এটা সতৰ্কবাণী পোৱা গৈছে",
        "upload-dialog-button-cancel": "বাতিল কৰক",
        "upload-dialog-button-done": "কৰা হ’ল",
        "upload-dialog-button-save": "সাঁচি থওক",
        "upload-dialog-button-upload": "আপল'ড",
-       "upload-dialog-label-select-file": "ফাইল নিৰ্বাচন কৰক",
-       "upload-dialog-label-infoform-title": "বিস্তাৰিত",
-       "upload-dialog-label-infoform-name": "নাম",
-       "upload-dialog-label-infoform-description": "বিৱৰণ",
-       "upload-dialog-label-usage-title": "ব্যৱহাৰ",
-       "upload-dialog-label-usage-filename": "ফাইলৰ নাম",
+       "upload-process-error": "এটা ত্ৰুটি পোৱা গৈছে",
+       "upload-process-warning": "এটা সতৰ্কবাণী পোৱা গৈছে",
+       "upload-form-label-select-file": "ফাইল নিৰ্বাচন কৰক",
+       "upload-form-label-infoform-title": "বিস্তাৰিত",
+       "upload-form-label-infoform-name": "নাম",
+       "upload-form-label-infoform-description": "বিৱৰণ",
+       "upload-form-label-usage-title": "ব্যৱহাৰ",
+       "upload-form-label-usage-filename": "ফাইলৰ নাম",
        "backend-fail-stream": "$1 ফাইলটো ষ্ট্ৰীম কৰিব পৰা নগ'ল।",
        "backend-fail-backup": "$1 ফাইলটো বেকআপ্‌ কৰিব পৰা নগ'ল।",
        "backend-fail-notexists": "$1 ফাইলটোৰ কোনো অস্তিত্ব নাই।",
        "api-error-badtoken": "আভ্যন্তৰীণ ত্ৰুটি: ভুল টোকেন।",
        "api-error-copyuploaddisabled": "ইউ আৰ এলৰ মাধ্যমেৰে আপল'ড কৰাটো এই চাৰ্ভাৰত নিষ্ক্ৰিয় কৰা হৈছে।",
        "api-error-duplicate": "এই চাইটত একে বিষয়বস্তুৰ {{PLURAL:$1|[$2 আন এটা ফাইল]|[$2 আন কিছুমান ফাইল]}} ইতিমধ্যেই আছে।",
-       "api-error-duplicate-archive": "এই চাইটত একে বিষয়বস্তুৰ {{PLURAL:$1|[$2 আন এটা ফাইল]|[$2 আন কিছুমান ফাইল]}} ইতিমধ্যেই আছিল, কিন্তু {{PLURAL:$1|সেইটো|সেইবোৰ}} বিলোপ কৰা হৈছে।",
+       "api-error-duplicate-archive": "এই চাইটত একে বিষয়বস্তুৰ {{PLURAL:$1|আন এটা ফাইল|আন কিছুমান ফাইল}} ইতিমধ্যেই আছিল, কিন্তু {{PLURAL:$1|সেইটো|সেইবোৰ}} বিলোপ কৰা হৈছে।",
        "api-error-empty-file": "আপুনি দাখিল কৰা ফাইলটো খালী ।",
        "api-error-emptypage": "নতুন, খালি পৃষ্ঠা সৃষ্টি কৰিবলৈ অনুমতি নাই।",
        "api-error-fetchfileerror": "আভ্যন্তৰীণ ত্ৰুটি: ফাইলটো অনাত কিবা সমস্যা হৈছে।",
index 476c3f6..4e6f970 100644 (file)
        "upload-http-error": "Hebo un error HTTP: $1",
        "upload-copy-upload-invalid-domain": "La xubida de copies nun ta disponible dende esti dominiu.",
        "upload-dialog-title": "Xubir ficheru",
-       "upload-dialog-error": "Hebo un error",
-       "upload-dialog-warning": "Hebo un avisu",
        "upload-dialog-button-cancel": "Encaboxar",
        "upload-dialog-button-done": "Fecho",
        "upload-dialog-button-save": "Guardar",
        "upload-dialog-button-upload": "Xubir",
-       "upload-dialog-label-select-file": "Seleiciona un ficheru",
-       "upload-dialog-label-infoform-title": "Detalles",
-       "upload-dialog-label-infoform-name": "Nome",
-       "upload-dialog-label-infoform-description": "Descripción",
-       "upload-dialog-label-usage-title": "Usu",
-       "upload-dialog-label-usage-filename": "Nome del ficheru",
+       "upload-process-error": "Hebo un error",
+       "upload-process-warning": "Hebo un avisu",
+       "upload-form-label-select-file": "Seleiciona un ficheru",
+       "upload-form-label-infoform-title": "Detalles",
+       "upload-form-label-infoform-name": "Nome",
+       "upload-form-label-infoform-description": "Descripción",
+       "upload-form-label-usage-title": "Usu",
+       "upload-form-label-usage-filename": "Nome del ficheru",
        "backend-fail-stream": "Nun se pudo tresmitir el ficheru $1.",
        "backend-fail-backup": "Nun se pudo facer copia de seguridá del ficheru $1.",
        "backend-fail-notexists": "El ficheru $1 nun esiste.",
        "api-error-badtoken": "Fallu internu: token incorreutu.",
        "api-error-copyuploaddisabled": "Xubir d'una URL ta desactivao nesti sirvidor.",
        "api-error-duplicate": "Yá hai {{PLURAL:$1|[$2 otru ficheru]|[$2 otros ficheros]}} nesti sitiu col mesmu conteníu.",
-       "api-error-duplicate-archive": "Había {{PLURAL:$1|[$2 otru ficheru]|[$2 otros ficheros]}} nesti sitiu col mesmu conteníu, pero se {{PLURAL:$1|desanició|desaniciaron}}.",
+       "api-error-duplicate-archive": "Había {{PLURAL:$1|otru ficheru|otros ficheros}} nesti sitiu col mesmu conteníu, pero se {{PLURAL:$1|desanició|desaniciaron}}.",
        "api-error-empty-file": "El ficheru qu'unviasti taba baleru.",
        "api-error-emptypage": "Nun se permite la creación de páxines nueves baleres.",
        "api-error-fetchfileerror": "Fallu internu: daqué nun funcionó al buscar el ficheru.",
index e513bcb..4e97b8c 100644 (file)
@@ -2,7 +2,8 @@
        "@metadata": {
                "authors": [
                        "1AnuraagPandey",
-                       "राम प्रसाद जोशी"
+                       "राम प्रसाद जोशी",
+                       "Macofe"
                ]
        },
        "tog-underline": "कड़ि अधोरेखन:",
        "api-error-badtoken": "आंतरिक त्रुटि: खराब टोकन।",
        "api-error-copyuploaddisabled": "URL द्वारा इस सर्वर पर अपलोड अक्षम है।",
        "api-error-duplicate": "वहाँ {{PLURAL:$1| [ $2 अन्य फ़ाइल] | रहे हैं [ $2 कुछ अन्य फ़ाइलों]}} एक ही सामग्री के साथ साइट पर पहले से ही है.",
-       "api-error-duplicate-archive": "वहाँ {{PLURAL:$1|था [$2 कुछ अन्य फ़ाइल] |were [$2 कुछ अन्य फ़ाइलें]}}, पहले से ही {{PLURAL:$1|यह was|they थे}} परन्तु  हटा दिये गये",
+       "api-error-duplicate-archive": "वहाँ {{PLURAL:$1|था कुछ अन्य फ़ाइल|कुछ अन्य फ़ाइलें}}, पहले से ही {{PLURAL:$1|यह was|they थे}} परन्तु  हटा दिये गये",
        "api-error-empty-file": "आप कय दीहल फ़ाइल खाली रहा।",
        "api-error-emptypage": "नँवा अव खाली पन्ना बनावै कय अनुमति नाइ है",
        "api-error-fetchfileerror": "आंतरिक त्रुटि: जब फ़ाइल लाया जा रहा तो कुछ गलत हो गया था।",
index ea4a144..4226290 100644 (file)
        "upload-too-many-redirects": "آدرس ده چوخ یؤنلندیرمه وار",
        "upload-http-error": "اچ تی تی پی ختاسی وار : $1",
        "upload-copy-upload-invalid-domain": "فایل یوکلنمه سی بو بازه ده امکانی یوخدور",
+       "upload-dialog-button-cancel": "وازگئچ",
+       "upload-dialog-button-done": "اولدو",
+       "upload-dialog-button-save": "ساخلا",
+       "upload-dialog-button-upload": "یوکله",
+       "upload-process-error": "بیر یالنیشلیق اولدو",
+       "upload-process-warning": "بیر اویاری اولدو",
+       "upload-form-label-select-file": "فایل سئچ",
+       "upload-form-label-infoform-title": "جوزئیات",
+       "upload-form-label-infoform-name": "آد",
+       "upload-form-label-infoform-description": "آچیقلاما",
+       "upload-form-label-usage-title": "ایشلتمه",
+       "upload-form-label-usage-filename": "فایل آدی",
        "backend-fail-stream": "$1 فای‌لی یولانمامادی.",
        "backend-fail-backup": "بو فایل $1 اوچون نوسخه پشتیان یارتماق اولماز",
        "backend-fail-notexists": "\"$1\" فای‌لی مؤوجود دئییل",
        "version-libraries": "نصب اولونموش کیتابخانا",
        "version-libraries-library": "کیتاب‌ائوی",
        "version-libraries-version": "نوسخه‌",
+       "version-libraries-description": "آچیقلاما",
        "version-libraries-authors": "یازارلار",
        "redirect": "فایل، ایستیفاده‌چی، صفحه یا نوسخه آی‌دی-سی ایله یول‌لاندیرما",
        "redirect-legend": "بیر فایل یا صحیفه‌یه یول‌لاندیرما",
        "tags-edit": "دَییشدیر",
        "tags-delete": "سیل",
        "tags-activate": "ائتکینلشدیر",
+       "tags-deactivate": "چالیشقان اولمایان",
        "tags-hitcount": "$1 {{PLURAL:$1|دییشیکلیک|دییشیک‌لیک}}",
+       "tags-create-reason": "ندن:",
        "tags-create-submit": "یارات",
+       "tags-delete-reason": "ندن:",
+       "tags-activate-reason": "ندن:",
+       "tags-deactivate-reason": "ندن:",
        "tags-edit-title": "اِتیکِتلری دَییشدیر",
        "tags-edit-manage-link": "اِتیکِتلری ایداره ائت",
        "tags-edit-revision-selected": "[[:$2]]-نین سئچیلمیش {{PLURAL:$1|نوسخه‌سی|نوسخه‌لری}}:",
        "api-error-badtoken": "ایچری خطاسی: پیس کود.",
        "api-error-copyuploaddisabled": "بو خیدمتچی‌ده، اینترنت آدرسی‌له فایل یوکله‌مک یاساقلانیب‌دیر.",
        "api-error-duplicate": "بو یاست‌دا، همن بیلگیلرله، باشقا {{PLURAL:$1|[$2 فایل]|[$2 فایل‌لار]}} واردیر.",
-       "api-error-duplicate-archive": "بو یاست‌دا، همن بیلگیلرله، باشقا {{PLURAL:$1|[$2 فایل]|[$2 فایل‌لار]}} وار ایدی، اما {{PLURAL:$1|سیلینیب‌دیر|سیلینیبلر}}.",
+       "api-error-duplicate-archive": "بو یاست‌دا، همن بیلگیلرله، باشقا {{PLURAL:$1|فایل|فایل‌لار}} وار ایدی، اما {{PLURAL:$1|سیلینیب‌دیر|سیلینیبلر}}.",
        "api-error-empty-file": "سیز یول‌لادیغینیز فایل، بوش ایدی.",
        "api-error-emptypage": "یئنی بوش صحیفه یارادماغا ایجازه یوخدور.",
        "api-error-fetchfileerror": "ایچری خطا: فایلی گتیرمک‌ده بیر ایشکال قاباغا گلدی.",
index 2ad427d..081b25b 100644 (file)
        "api-error-badtoken": "Panlaog na kasalaan: Raot na pangilip",
        "api-error-copyuploaddisabled": "An pagkakarga sa paagi kan URL pinag-untok sa serbidor na ini.",
        "api-error-duplicate": "Igwa {{PLURAL:$1|nin [$2 ibang sagunson]|mga [$2 iba pang mga sagunson]}} na yaon sa sityo na igwa nin kaparehong laog.",
-       "api-error-duplicate-archive": "Igwa {{PLURAL:$1|kaidto nin [$2 ibang sagunson]|kaidto nin [$2 ibang mga sagunson]}} na yaon sa sityo na igwa nin kaparehong laog, alagad {{PLURAL:$1|ini kaidto|sinda kaidto}} pinagpura na.",
+       "api-error-duplicate-archive": "Igwa {{PLURAL:$1|kaidto nin ibang sagunson|kaidto nin ibang mga sagunson}} na yaon sa sityo na igwa nin kaparehong laog, alagad {{PLURAL:$1|ini kaidto|sinda kaidto}} pinagpura na.",
        "api-error-empty-file": "An sagunson na saimong pinagsumite daeng laog.",
        "api-error-emptypage": "Nagmumukna nin bago, mayong laog na mga pahina dae pinagtutugutan.",
        "api-error-fetchfileerror": "Panlaog na kasalaan: May bagay na naging sala habang hinahakot an sagunson.",
index 714f929..ea1ae33 100644 (file)
@@ -14,7 +14,8 @@
                        "Zedlik",
                        "Тест",
                        "아라",
-                       "Liashko"
+                       "Liashko",
+                       "Macofe"
                ]
        },
        "tog-underline": "Падкрэсьліваць спасылкі:",
        "nstab-template": "Шаблён",
        "nstab-help": "Старонка дапамогі",
        "nstab-category": "Катэгорыя",
+       "mainpage-nstab": "Галоўная старонка",
        "nosuchaction": "Няма такога дзеяньня",
        "nosuchactiontext": "Дзеяньне, пазначанае ў URL, зьяўляецца няслушным.\nМагчыма, вы ўвялі няслушны URL або перайшлі па няслушнай спасылцы.\nГэта можа быць і памылкай у праграмным забесьпячэньні {{GRAMMAR:родны|{{SITENAME}}}}.",
        "nosuchspecialpage": "Такой спэцыяльнай старонкі не існуе",
        "createacct-captcha": "Праверка бясьпекі",
        "createacct-imgcaptcha-ph": "Увядзіце тэкст, што бачыце вышэй",
        "createacct-submit": "Стварыць рахунак",
-       "createacct-another-submit": "СÑ\82ваÑ\80Ñ\8bÑ\86Ñ\8c Ñ\96нÑ\88Ñ\8b Ñ\80аÑ\85Ñ\83нак",
+       "createacct-another-submit": "Стварыць рахунак",
        "createacct-benefit-heading": "{{SITENAME}} створаная людзьмі, такімі як вы.",
        "createacct-benefit-body1": "{{PLURAL:$1|праўка|праўкі|правак}}",
        "createacct-benefit-body2": "{{PLURAL:$1|старонка|старонкі|старонак}}",
        "upload-http-error": "Узьнікла памылка HTTP: $1",
        "upload-copy-upload-invalid-domain": "Капіяваньне загрузак не дазволенае ў гэтым дамэне.",
        "upload-dialog-title": "Загрузка файла",
-       "upload-dialog-error": "Адбылася памылка",
-       "upload-dialog-warning": "Зьявілася папярэджаньне",
        "upload-dialog-button-cancel": "Адмяніць",
        "upload-dialog-button-done": "Зроблена",
        "upload-dialog-button-save": "Захаваць",
        "upload-dialog-button-upload": "Загрузіць",
-       "upload-dialog-label-select-file": "Абраць файл",
-       "upload-dialog-label-infoform-title": "Падрабязнасьці",
-       "upload-dialog-label-infoform-name": "Назва",
-       "upload-dialog-label-infoform-description": "Апісаньне",
-       "upload-dialog-label-usage-title": "Выкарыстаньне",
-       "upload-dialog-label-usage-filename": "Назва файлу",
+       "upload-process-error": "Адбылася памылка",
+       "upload-process-warning": "Зьявілася папярэджаньне",
+       "upload-form-label-select-file": "Абраць файл",
+       "upload-form-label-infoform-title": "Падрабязнасьці",
+       "upload-form-label-infoform-name": "Назва",
+       "upload-form-label-infoform-description": "Апісаньне",
+       "upload-form-label-usage-title": "Выкарыстаньне",
+       "upload-form-label-usage-filename": "Назва файлу",
        "backend-fail-stream": "Немагчыма накіраваць файл $1.",
        "backend-fail-backup": "Немагчыма зрабіць рэзэрвовую копію файла $1.",
        "backend-fail-notexists": "Файл $1 не існуе.",
        "filerevert-legend": "Вярнуць папярэднюю вэрсію файла",
        "filerevert-intro": "Вы вяртаеце '''[[Media:$1|$1]]''' да [вэрсіі $4 ад $3, $2].",
        "filerevert-comment": "Прычына:",
-       "filerevert-defaultcomment": "Вернутая вэрсія ад $2, $1",
+       "filerevert-defaultcomment": "Вернутая вэрсія ад $2 $1 ($3)",
        "filerevert-submit": "Вярнуць",
        "filerevert-success": "'''[[Media:$1|$1]]''' быў вернуты да [вэрсіі $4 ад $3, $2].",
        "filerevert-badversion": "Не існуе папярэдняй лякальнай вэрсіі гэтага файла з пазначанай датай.",
        "emailccsubject": "Копія Вашага ліста да $1: $2",
        "emailsent": "Ліст адасланы",
        "emailsenttext": "Ваш ліст быў адасланы.",
-       "emailuserfooter": "Гэты ліст быў дасланы {{GENDER:$2|ўдзельнікам|ўдзельніцай}} $1 да {{GENDER:$2|ўдзельніка|ўдзельніцы}} $2 з дапамогай функцыі «{{int:emailuser}}» {{GRAMMAR:родны|{{SITENAME}}}}.",
+       "emailuserfooter": "Гэты ліст быў дасланы {{GENDER:$1|ўдзельнікам|ўдзельніцай}} $1 да {{GENDER:$2|ўдзельніка|ўдзельніцы}} $2 з дапамогай функцыі «{{int:emailuser}}» {{GRAMMAR:родны|{{SITENAME}}}}.",
        "usermessage-summary": "Паведамленьне пра выхад з сыстэмы.",
        "usermessage-editor": "Дастаўка сыстэмных паведамленьняў",
        "watchlist": "Сьпіс назіраньня",
        "api-error-badaccess-groups": "У Вас няма дазволу загружаць файлы ў гэтую вікі.",
        "api-error-badtoken": "Унутраная памылка: няслушны ключ.",
        "api-error-copyuploaddisabled": "Загрузка з URL-адрасу забароненая на гэтым сэрвэры.",
-       "api-error-duplicate": "Ужо {{PLURAL:$1|1=Ñ\96Ñ\81нÑ\83е [$2 Ñ\96нÑ\88Ñ\8b Ñ\84айл]|Ñ\96Ñ\81нÑ\83Ñ\8eÑ\86Ñ\8c [$2 Ñ\96нÑ\88Ñ\8bÑ\8f Ñ\84айлÑ\8b]}} з такім жа зьместам.",
-       "api-error-duplicate-archive": "Раней на сайце {{PLURAL:$1|1=быў [$2 файл]|былі [$2 файлы]}} з дакладна такім жа зьместам, але {{PLURAL:$1|1=ён быў выдалены|яны былі выдаленыя}}.",
+       "api-error-duplicate": "Ð\9dа Ñ\81айÑ\86е Ñ\9eжо {{PLURAL:$1|1=Ñ\96Ñ\81нÑ\83е Ñ\96нÑ\88Ñ\8b Ñ\84айл|Ñ\96Ñ\81нÑ\83Ñ\8eÑ\86Ñ\8c Ñ\96нÑ\88Ñ\8bÑ\8f Ñ\84айлÑ\8b}} з такім жа зьместам.",
+       "api-error-duplicate-archive": "Раней на сайце {{PLURAL:$1|1=быў файл|былі файлы}} з дакладна такім жа зьместам, але {{PLURAL:$1|1=ён быў выдалены|яны былі выдаленыя}}.",
        "api-error-empty-file": "Дасланы Вамі файл быў пусты.",
        "api-error-emptypage": "Стварэньне новых пустых старонак забаронена.",
        "api-error-fetchfileerror": "Унутраная памылка: падчас атрыманьня файла штосьці здарылася.",
index 7e62e7e..748b6f4 100644 (file)
@@ -22,7 +22,8 @@
                        "아라",
                        "Unomano",
                        "Mikalai Udodau",
-                       "Artificial123"
+                       "Artificial123",
+                       "Macofe"
                ]
        },
        "tog-underline": "Падкрэсліваць спасылкі:",
        "api-error-badtoken": "Унутраная памылка: няслушны ключ.",
        "api-error-copyuploaddisabled": "Загрузка з URL-адрасу забароненая на гэтым серверы.",
        "api-error-duplicate": "Ужо {{PLURAL:$1|існуе [$2 іншы файл]|існуюць [$2 іншыя файлы]}} з такім жа зместам.",
-       "api-error-duplicate-archive": "Раней на сайце {{PLURAL:$1|ўжо быў [$2 файл]|былі [$2 файлы]}} з дакладна такім жа зместам, але {{PLURAL:$1|ён быў выдалены|яны былі выдаленыя}}.",
+       "api-error-duplicate-archive": "Раней на сайце {{PLURAL:$1|ўжо быў файл|былі файлы}} з дакладна такім жа зместам, але {{PLURAL:$1|ён быў выдалены|яны былі выдаленыя}}.",
        "api-error-empty-file": "Дасланы Вамі файл быў пусты.",
        "api-error-emptypage": "Стварэнне новых пустых старонак забаронена.",
        "api-error-fetchfileerror": "Унутраная памылка: падчас атрымання файла штосьці здарылася.",
index 1ba7372..60d95bb 100644 (file)
@@ -28,7 +28,8 @@
                        "ShadeOfGrey",
                        "PetaRZ",
                        "Macofe",
-                       "V111P"
+                       "V111P",
+                       "Лорд Бъмбъри"
                ]
        },
        "tog-underline": "Подчертаване на препратките:",
        "pool-timeout": "Изтичане на времето за заключване на страницата",
        "pool-queuefull": "Опашката за заявки е пълна",
        "pool-errorunknown": "Непозната грешка",
+       "poolcounter-usage-error": "Грешка при ползване $1",
        "aboutsite": "За {{SITENAME}}",
        "aboutpage": "Project:За {{SITENAME}}",
        "copyright": "Освен ако не е посочено друго, съдържанието е достъпно при условията на $1.",
        "nstab-template": "Шаблон",
        "nstab-help": "Помощ",
        "nstab-category": "Категория",
+       "mainpage-nstab": "Главна страница",
        "nosuchaction": "Няма такова действие",
        "nosuchactiontext": "Действието, указано в интернет адреса, е невалидно.\nМоже би сте допуснали грешка в изписването на адреса или сте последвали некоректна хипервръзка.\nПроблемът може да се дължи и на грешка в софтуера на {{SITENAME}}.",
        "nosuchspecialpage": "Няма такава специална страница",
        "readonly_lag": "Базата от данни беше автоматично заключена, докато подчинените сървъри успеят да се съгласуват с основния сървър.",
        "internalerror": "Вътрешна грешка",
        "internalerror_info": "Вътрешна грешка: $1",
+       "internalerror-fatal-exception": "Фатална грешка от тип „$1“",
        "filecopyerror": "Файлът „$1“ не можа да бъде копиран като „$2“.",
        "filerenameerror": "Файлът „$1“ не можа да бъде преименуван на „$2“.",
        "filedeleteerror": "Файлът „$1“ не можа да бъде изтрит.",
        "no-null-revision": "Не може да бъде създадена празна версия на страницата „$1“",
        "badtitle": "Невалидно заглавие",
        "badtitletext": "Желаното заглавие на страница е невалидно, празно или неправилна препратка към друго уики. Възможно е да съдържа знаци, които не са позволени в заглавия.",
+       "title-invalid-utf8": "Желаната страница съдържа невалиден низ с кодиране UTF-8",
+       "title-invalid-interwiki": "Желаното заглавие на страница съдържа препратка към друго уики, което не може да бъде ползвано в заглавия.",
+       "title-invalid-talk-namespace": "Желаното заглавие на страница се отнася към беседа, която не съществува",
+       "title-invalid-characters": "Желаното заглавие на статия съдържа невалидни знаци: „$1“",
+       "title-invalid-relative": "Заглавието съдържа относителен път. Относителни заглавия на статии (./,../) са невалидни, защото често ще са недостижимо, когато биват извиквани от браузъра на потребителя.",
+       "title-invalid-magic-tilde": "Желаното заглавие на статия съдържа невалидна поредица от вълчнички (<nowiki>~~~</nowiki>).",
+       "title-invalid-too-long": "Желаното заглавие на статия е твърде дълго. Трябва да е не по-дълго от $1 {{PLURAL:$1|байт|байта}} в кодиране UTF-8.",
        "perfcached": "Следните данни са извлечени от склада и затова може да не отговарят на текущото състояние. В складираното копие {{PLURAL:$1|е допустим най-много един резултат|са допустими най-много $1 резултата}}.",
        "perfcachedts": "Данните са складирани и обновени за последно на $1. Най-много {{PLURAL:$4|един резултат е допустим и наличен|$4 резултата са допустими и налични}} в складираното копие.",
        "querypage-no-updates": "Обновяването на тази страница в момента е изключено. Засега данните тук няма да бъдат обновявани.",
        "api-error-badtoken": "Вътрешна грешка: неправилен маркер.",
        "api-error-copyuploaddisabled": "Качването през URL е забранено на този сървър.",
        "api-error-duplicate": "На сайта вече има качени {{PLURAL:$1|[$2 друг файл]|[$2 други файла]}} с идентично съдържание.",
-       "api-error-duplicate-archive": "На сайта вече е имало {{PLURAL:$1|качен [$2 друг файл]|качени [$2 други файла]}} с идентично съдържание, {{PLURAL:$1|който е бил изтрит|които са били изтрити}}.",
+       "api-error-duplicate-archive": "На сайта вече е имало {{PLURAL:$1|качен друг файл|качени други файла}} с идентично съдържание, {{PLURAL:$1|който е бил изтрит|които са били изтрити}}.",
        "api-error-empty-file": "Заявеният за качване файл беше празен.",
        "api-error-emptypage": "Създаването на нови, празени страници, не е разрешено.",
        "api-error-fetchfileerror": "Вътрешна грешка: Нещо се обърка при извличане на файла.",
index 9467810..92a1404 100644 (file)
        "upload-file-error": "इंटरनल खराबी",
        "upload-misc-error": "नामालूम अपलोड खराबी",
        "upload-dialog-title": "फाइल अपलोड",
-       "upload-dialog-error": "कौनो खराबी आ गइल",
-       "upload-dialog-warning": "कौनो चेतावनी बा",
        "upload-dialog-button-cancel": "कैंसिल",
        "upload-dialog-button-done": "पूरा भइल",
        "upload-dialog-button-save": "सहेजीं",
        "upload-dialog-button-upload": "अपलोड",
-       "upload-dialog-label-select-file": "फाइल चुनीं",
-       "upload-dialog-label-infoform-title": "डिटेल जानकारी",
-       "upload-dialog-label-infoform-name": "नाँव",
-       "upload-dialog-label-infoform-description": "विवरण",
-       "upload-dialog-label-usage-title": "इस्तेमाल",
-       "upload-dialog-label-usage-filename": "फाइल नाँव",
+       "upload-process-error": "कौनो खराबी आ गइल",
+       "upload-process-warning": "कौनो चेतावनी बा",
+       "upload-form-label-select-file": "फाइल चुनीं",
+       "upload-form-label-infoform-title": "डिटेल जानकारी",
+       "upload-form-label-infoform-name": "नाँव",
+       "upload-form-label-infoform-description": "विवरण",
+       "upload-form-label-usage-title": "इस्तेमाल",
+       "upload-form-label-usage-filename": "फाइल नाँव",
        "backend-fail-stream": "फाइल \"$1\" स्ट्रीम ना हो पावल।",
        "backend-fail-backup": "फाइल \"$1\" के बैकअप ना हो पावल।",
        "backend-fail-notexists": "फाइल $1 मौजूद नइखे।",
index 0c2377d..b35e3a2 100644 (file)
@@ -25,7 +25,8 @@
                        "Aftabuzzaman",
                        "Wikisagnik",
                        "Aashaa",
-                       "Sayma Jahan"
+                       "Sayma Jahan",
+                       "Macofe"
                ]
        },
        "tog-underline": "সংযোগগুলির নিচে দাগ দেখানো হোক:",
        "upload-http-error": "একটি এইচটিটিপি ত্রুটি দেখা দিয়েছে: $1",
        "upload-copy-upload-invalid-domain": "এই ডোমেইন থেকে আপলোড সম্ভব নয়।",
        "upload-dialog-title": "ফাইল আপলোড করুন",
-       "upload-dialog-error": "একটি ত্রুটি দেখা দিয়েছে",
-       "upload-dialog-warning": "একটি সতর্কবার্তা দেখা দিয়েছে",
        "upload-dialog-button-cancel": "বাতিল",
        "upload-dialog-button-done": "সম্পন্ন",
        "upload-dialog-button-save": "সংরক্ষণ",
        "upload-dialog-button-upload": "আপলোড",
-       "upload-dialog-label-select-file": "ফাইল নির্বাচন করুন",
-       "upload-dialog-label-infoform-title": "বিস্তারিত",
-       "upload-dialog-label-infoform-name": "নাম",
-       "upload-dialog-label-infoform-description": "বিবরণ",
-       "upload-dialog-label-usage-title": "ব্যবহার",
-       "upload-dialog-label-usage-filename": "ফাইলের নাম",
+       "upload-process-error": "একটি ত্রুটি দেখা দিয়েছে",
+       "upload-process-warning": "একটি সতর্কবার্তা দেখা দিয়েছে",
+       "upload-form-label-select-file": "ফাইল নির্বাচন করুন",
+       "upload-form-label-infoform-title": "বিস্তারিত",
+       "upload-form-label-infoform-name": "নাম",
+       "upload-form-label-infoform-description": "বিবরণ",
+       "upload-form-label-usage-title": "ব্যবহার",
+       "upload-form-label-usage-filename": "ফাইলের নাম",
        "backend-fail-stream": "\"$1\" ফাইলের স্ট্রিম দেখানো যাচ্ছে না।",
        "backend-fail-backup": "\"$1\" ফাইলের ব্যাকআপ তৈরী সম্ভব নয়।",
        "backend-fail-notexists": "\"$1\" নামের কোনো ফাইল নেই।",
        "databasenotlocked": "ডাটাবেজ বন্ধ নয়।",
        "lockedbyandtime": "({{GENDER:$1|$1}} $2 এর $3 সময়ে)",
        "move-page": "$1 স্থানান্তর",
-       "move-page-legend": "পাতাটি সরিয়ে ফেলুন",
+       "move-page-legend": "পাতা স্থানান্তর",
        "movepagetext": "নিচের ফর্মটি ব্যবহার করে একটি পাতার শিরোনাম পরিবর্তন করা যাবে, এবং সেই সাথে নতুন শিরোনামে এর সমগ্র ইতিহাস স্থানান্তর করা যাবে।\nপুরনো শিরোনামটি নতুন শিরোনামটির প্রতি একটি পুনর্নির্দেশনা ধারণ করবে।\nযেসমস্ত পুনর্নির্দেশনা পুরনো শিরোনামটির দিকে নির্দেশ করছিল, সেগুলি স্বয়ংক্রিয়ভাবে হালনাগাদ করতে পারবেন।\nযদি তা না চান, তবে [[Special:DoubleRedirects|দ্বি-পুনর্নির্দেশনা]] বা [[Special:BrokenRedirects|অচল পুনর্নির্দেশনাগুলি]] পরীক্ষা করে দেখতে ভুলবেন না।\nসংযোগগুলি যাতে তাদের লক্ষ্যে পৌঁছায়, তা নিশ্চিত করার দায়িত্ব আপনার।\n\nলক্ষ্য করুন যে যদি নতুন শিরোনামে ইতিমধ্যেই একটি পাতা থেকে থাকে, তবে উৎস পাতাটি সেই শিরোনামে স্থানান্তর করা হবে '''না''', যদি না নতুন শিরোনামের পাতাটি খালি থাকে বা একটি পুননির্দেশনা হয় এবং এর কোন অতীত সম্পাদনা ইতিহাস না থাকে।\nঅর্থাৎ আপনি ভুল করে নাম পরিবর্তন করলে সহজেই পুরনো নামে ফেরত যেতে পারবেন, কিন্তু ইতিমধ্যে বিদ্যমান কোন পাতার উপরে লিখতে পারবেন না।\n\n'''সতর্কীকরণ!'''\nকোন জনপ্রিয় পাতার ক্ষেত্রে এই পরিবর্তনটি খুবই আকস্মিক হতে পারে; অগ্রসর হবার আগে এই কাজটির ফলাফল কী হতে পারে, সে ব্যাপারে অনুগ্রহ করে নিশ্চিত হোন।",
        "movepagetext-noredirectfixer": "নিচের ফর্মটি ব্যবহার করে একটি পাতার শিরোনাম পরিবর্তন করা যাবে, এবং সেই সাথে নতুন শিরোনামে এর সমগ্র ইতিহাস স্থানান্তর করা যাবে।\nপুরনো শিরোনামটি নতুন শিরোনামটির প্রতি একটি পুনর্নির্দেশনা ধারণ করবে।\n[[Special:DoubleRedirects|দ্বি-পুনর্নির্দেশনা]] বা [[Special:BrokenRedirects|অচল পুনর্নির্দেশনাগুলি]] পরীক্ষা করে দেখতে ভুলবেন না।\nসংযোগগুলি যাতে তাদের লক্ষ্যে পৌঁছায়, তা নিশ্চিত করার দায়িত্ব আপনার।\n\nলক্ষ্য করুন যে যদি নতুন শিরোনামে ইতিমধ্যেই একটি পাতা থেকে থাকে, তবে উৎস পাতাটি সেই শিরোনামে স্থানান্তর করা হবে '''না''', যদি না নতুন শিরোনামের পাতাটি খালি থাকে বা একটি পুননির্দেশনা হয় এবং এর কোন অতীত সম্পাদনা ইতিহাস না থাকে। \nঅর্থাৎ আপনি ভুল করে নাম পরিবর্তন করলে সহজেই পুরনো নামে ফেরত যেতে পারবেন, কিন্তু ইতিমধ্যে বিদ্যমান কোন পাতার উপরে লিখতে পারবেন না।\n\n'''সতর্কীকরণ!'''\nকোন জনপ্রিয় পাতার ক্ষেত্রে এই পরিবর্তনটি খুবই আকস্মিক হতে পারে;\nঅগ্রসর হবার আগে এই কাজটির ফলাফল কী হতে পারে, সে ব্যাপারে অনুগ্রহ করে নিশ্চিত হোন।",
        "movepagetalktext": "পাতাটির সাথে সাথে সংশ্লিষ্ট আলোচনা পাতাটিও স্বয়ংক্রিয়ভাবে সরানো হবে '''যদি না:'''\n*খালি নয় এমন একটি আলাপ পাতা নতুন শিরোনামটির অধীনে ইতিমধ্যেই বিদ্যমান থাকে, অথবা\n*আপনি নিচের বাক্সটি থেকে টিক সরিয়ে নিতে পারেন।\n\nএসব ক্ষেত্রে আপনি চাইলে নিজের হাতে পাতাটিকে সরাতে বা একত্রীকরণ করতে পারেন।",
        "cant-move-to-category-page": "আপনার পাতাটিকে বিষয়শ্রেণী পাতায় স্থানান্তরের অনুমতি নেই।",
        "newtitle": "এই নতুন শিরোনামে",
        "move-watch": "এই পাতাটি নজরে রাখুন",
-       "movepagebtn": "পাতা à¦¸à¦°à¦¾ন",
+       "movepagebtn": "পাতা à¦¸à§\8dথানানà§\8dতর à¦\95রà§\81ন",
        "pagemovedsub": "সরিয়ে নেওয়া হয়েছে",
        "movepage-moved": "'''\"$1\"-কে \"$2\" শিরোনামে স্থানান্তর করা হয়েছে'''",
        "movepage-moved-redirect": "একটি পুনর্নির্দেশনা তৈরি হয়েছে।",
        "movepage-moved-noredirect": "পুনর্নির্দেশ তৈরীতে বাধা দেয়া হয়েছে।",
        "articleexists": "এই শিরোনামে একটি পাতা ইতোমধ্যে সৃষ্টি হয়ে গেছে, অথবা আপনি যে শিরোনামটি পছন্দ করেছেন তা গ্রহণযোগ্য নয়। দয়া করে অন্য একটি শিরোনাম দিয়ে চেষ্টা করুন।",
        "cantmove-titleprotected": "আপনি এই অবস্থানে পাতাটিকে স্থানান্তর করতে পারেন না, কারণ এই নতুন শিরোনামটি সৃষ্টি করা থেকে সুরক্ষিত।",
-       "movetalk": "সংশ্লিষ্ট আলাপের পাতা সরিয়ে নাও",
+       "movetalk": "সংশ্লিষ্ট আলাপ পাতা স্থানান্তর করো",
        "move-subpages": "উপপাতা স্থানান্তর ($1টি পর্যন্ত)",
        "move-talk-subpages": "উপপাতার আলাপ পাতা স্থানান্তর ($1টি পর্যন্ত)",
        "movepage-page-exists": "$1 পাতাটি ইতিমধ্যেই বিদ্যমান এবং স্বয়ংক্রিয়ভাবে পুনর্লিখন করা সম্ভব নয়।",
        "api-error-badtoken": "অভ্যন্তরীণ ত্রুটি: খারাপ টোকেন।",
        "api-error-copyuploaddisabled": "এই সার্ভারে ইউআরএল-এর মাধ্যমে আপলোড করার সুবিধা নিস্ক্রিয় রয়েছে।",
        "api-error-duplicate": "ইতোমধ্যে এই সাইটে একই রকমের কন্টেট সমৃদ্ধ {{PLURAL:$1|[$2 অন্য ফাইল]|[$2 কিছু অন্য ফাইল]}} রয়েছে।",
-       "api-error-duplicate-archive": "একই নাম ও বিষয়বস্তু বিশিষ্ট {{PLURAL:$1| [$2 অপর একটি ফাইল]| [$2 কয়েকটি ফাইল]}} পূর্বে এই উইকিতে ছিলো, এবং {{PLURAL:$1|সেটিকে|সেগুলোকে}} অপসারণ করা হয়েছে।",
+       "api-error-duplicate-archive": "একই নাম ও বিষয়বস্তু বিশিষ্ট {{PLURAL:$1|অপর একটি ফাইল|কয়েকটি ফাইল}} পূর্বে এই উইকিতে ছিলো, এবং {{PLURAL:$1|সেটিকে|সেগুলোকে}} অপসারণ করা হয়েছে।",
        "api-error-empty-file": "আপনার জমাকৃত ফাইলটি খালি।",
        "api-error-emptypage": "নতুন পাতা তৈরি হচ্ছে, খালি পাতা গ্রহণযোগ্য নয়।",
        "api-error-fetchfileerror": "অভ্যন্তরীণ ত্রুটি: ফাইল নিয়ে আসার সময় কোনো সমস্যা হয়েছিলো।",
index 4f97519..89277a2 100644 (file)
        "api-error-badtoken": "Fazi diabarzh : \"jedouer\" fall.",
        "api-error-copyuploaddisabled": "Diweredekaet eo an enporzhioù dre URL war ar servijer-mañ.",
        "api-error-duplicate": "Bez' {{PLURAL:$1|ez eus [$2 restr all]|[$2 restr all]}} gant an hevelep danvez war al lec'hienn-mañ c'hoazh",
-       "api-error-duplicate-archive": "Bez' e oa {{PLURAL:$1|[$2 ur restr all]|[$2 restroù all]}} c'hoazh enno an hevelep danvez, nemet {{PLURAL:$1|eo bet diverket|int bet diverket}}.",
+       "api-error-duplicate-archive": "Bez' e oa {{PLURAL:$1|ur restr all|restroù all}} c'hoazh enno an hevelep danvez, nemet {{PLURAL:$1|eo bet diverket|int bet diverket}}.",
        "api-error-empty-file": "Ar restr hoc'h eus roet a oa goullo.",
        "api-error-emptypage": "N'eo ket aotreet krouiñ pajennoù goullo.",
        "api-error-fetchfileerror": "Fazi diabarzh : aet ez eus un dra bennak a-dreuz en ur glask adtapout ar restr.",
index 7d77f31..b615a7d 100644 (file)
        "api-error-badtoken": "Unutrašnja greška: token nije ispravan.",
        "api-error-copyuploaddisabled": "Postavljanja putem URL-a su onemogućena na ovom serveru.",
        "api-error-duplicate": "Već postoji {{PLURAL:$1|[$2 druga datoteka]|[$2 druge datoteke]}} na ovoj stranici sa istim sadržajem",
-       "api-error-duplicate-archive": "{{PLURAL:$1|Postojala je [$2 druga datoteka]|Postojale su [$2 neke druge datoteke]}} na sajtu sa istim sadržajem, ali {{PLURAL:$1|je obrisana|su obrisane}}.",
+       "api-error-duplicate-archive": "{{PLURAL:$1|Postojala je druga datoteka|Postojale su neke druge datoteke}} na sajtu sa istim sadržajem, ali {{PLURAL:$1|je obrisana|su obrisane}}.",
        "api-error-empty-file": "Datoteka koju ste poslali je bila prazna.",
        "api-error-emptypage": "Stvaranje novih praznih stranica nije dozvoljeno.",
        "api-error-fetchfileerror": "Unutrašnja greška: pojavio se neki problem pri dobijanju podataka o datoteci.",
index 4cb9fa9..5007e73 100644 (file)
        "nstab-template": "Plantilla",
        "nstab-help": "Ajuda",
        "nstab-category": "Categoria",
+       "mainpage-nstab": "Pàgina principal",
        "nosuchaction": "No es reconeix aquesta operació",
        "nosuchactiontext": "L'acció especificada per la URL no és vàlida.\nPotser heu escrit malament la URL o heu seguit un enllaç incorrecte.\nAixò també pot ser causat per un error al programari utilitzat pel projecte {{SITENAME}}.",
        "nosuchspecialpage": "No es troba la pàgina especial que busqueu",
        "upload-http-error": "Ha ocorregut un error HTTP: $1",
        "upload-copy-upload-invalid-domain": "Les càrregues de còpia no són disponibles des d'aquest domini.",
        "upload-dialog-title": "Carrega un fitxer",
-       "upload-dialog-error": "S’ha produït un error",
-       "upload-dialog-warning": "S'ha produït un avís",
        "upload-dialog-button-cancel": "Cancel·la",
        "upload-dialog-button-done": "Fet",
        "upload-dialog-button-save": "Desa",
        "upload-dialog-button-upload": "Carrega",
-       "upload-dialog-label-select-file": "Seleccioneu fitxer",
-       "upload-dialog-label-infoform-title": "Detalls",
-       "upload-dialog-label-infoform-name": "Nom",
-       "upload-dialog-label-infoform-description": "Descripció",
-       "upload-dialog-label-usage-title": "Ús",
-       "upload-dialog-label-usage-filename": "Nom del fitxer",
+       "upload-process-error": "S’ha produït un error",
+       "upload-process-warning": "S'ha produït un avís",
+       "upload-form-label-select-file": "Seleccioneu fitxer",
+       "upload-form-label-infoform-title": "Detalls",
+       "upload-form-label-infoform-name": "Nom",
+       "upload-form-label-infoform-description": "Descripció",
+       "upload-form-label-usage-title": "Ús",
+       "upload-form-label-usage-filename": "Nom del fitxer",
        "backend-fail-stream": "No s'ha pogut transmetre el fitxer $1.",
        "backend-fail-backup": "No s'ha pogut fer una còpia de seguretat del fitxer $1.",
        "backend-fail-notexists": "El fitxer $1 no existeix.",
        "api-error-badtoken": "Error intern: argument incorrecte.",
        "api-error-copyuploaddisabled": "Les càrregues via URL estan desactivades en aquest servidor.",
        "api-error-duplicate": "Ja hi ha {{PLURAL:$1|[$2 un altre fitxer]|[$2 altres fitxers]}} en aquest lloc web amb el mateix contingut.",
-       "api-error-duplicate-archive": "Aquí ja hi ha hagut {{PLURAL:$1|[$2 un altre fitxer]|[$2 altres fitxers]}} amb el mateix contingut, i {{PLURAL:$1|va ser esborrat|varen ser esborrats}}.",
+       "api-error-duplicate-archive": "Aquí ja hi ha hagut {{PLURAL:$1|un altre fitxer|altres fitxers}} amb el mateix contingut, i {{PLURAL:$1|va ser esborrat|varen ser esborrats}}.",
        "api-error-empty-file": "El fitxer que heu tramès està buit.",
        "api-error-emptypage": "No es permet la creació de pàgines noves en blanc.",
        "api-error-fetchfileerror": "Error intern: quelcom no ha funcionat en accedir al fitxer.",
index be9eaff..795fd4a 100644 (file)
        "nstab-template": "Šablona",
        "nstab-help": "Nápověda",
        "nstab-category": "Kategorie",
+       "mainpage-nstab": "Hlavní strana",
        "nosuchaction": "Neznámý úkon",
        "nosuchactiontext": "Činnost („action“) uvedená v URL je neplatná.\nZřejmě jste se při zadávání adresy překlepli nebo jste přešli na chybný odkaz.\nMůže se ale jednat také o chybu v softwaru {{GRAMMAR:2sg|{{SITENAME}}}}.",
        "nosuchspecialpage": "Neexistující speciální stránka",
        "createacct-captcha": "Bezpečnostní kontrola",
        "createacct-imgcaptcha-ph": "Opište výše zobrazený text",
        "createacct-submit": "Vytvořit účet",
-       "createacct-another-submit": "Vytvořit jiný účet",
+       "createacct-another-submit": "Vytvořit účet",
        "createacct-benefit-heading": "{{grammar:4sg|{{SITENAME}}}} tvoří lidé jako vy.",
        "createacct-benefit-body1": "{{PLURAL:$1|editace|editace|editací}}",
        "createacct-benefit-body2": "{{PLURAL:$1|stránka|stránky|stránek}}",
        "changeemail-password": "Vaše heslo do {{gender:2sg|{{SITENAME}}}}:",
        "changeemail-submit": "Změnit e-mail",
        "changeemail-throttled": "Provedli jste příliš mnoho pokusů o přihlášení.\nČekejte prosím $1 a zkuste to znovu.",
+       "changeemail-nochange": "Zadejte prosím odlišnou e-mailovou adresu.",
        "resettokens": "Reinicializace klíčů",
        "resettokens-text": "Na této stránce můžete reinicializovat klíče, které umožňují přístup k jistým soukromým údajům spojeným s vaším účtem.\n\n{{GENDER:|Měl|Měla|Měli}} byste to provést v případě, že jste je omylem někomu {{GENDER:|prozradil|prozradila|prozradili}}, nebo byl váš účet narušen.",
        "resettokens-no-tokens": "Neexistují žádné klíče, které by bylo možno reinicializovat.",
        "permissionserrorstext-withaction": "Z {{PLURAL:$1|následujícího důvodu|následujících důvodů}} nemáte oprávnění $2:",
        "recreate-moveddeleted-warn": "'''Upozornění: Pokoušíte se znovuzaložit stránku, která byla v minulosti smazána.'''\n\nZvažte, zda je vhodné v editaci této stránky pokračovat.\nNíže vidíte soupis přesunů a smazání této stránky:",
        "moveddeleted-notice": "Tato stránka byla smazána.\nPodrobnosti si můžete prohlédnout v níže zobrazeném seznamu provedených přesunů a smazání této stránky.",
+       "moveddeleted-notice-recent": "Omlouváme se, ale tato stránka byla nedávno (v posledních 24 hodinách) smazána. Pro úplnost je níže zobrazen soupis přesunů a smazání této stránky.",
        "log-fulllog": "Zobrazit všechny záznamy",
        "edit-hook-aborted": "Editace byla bez bližšího vysvětlení zrušena přípojným bodem.",
        "edit-gone-missing": "Stránku se nepodařilo aktualizovat.\nZřejmě byla smazána.",
        "group-bot": "Boti",
        "group-sysop": "Správci",
        "group-bureaucrat": "Byrokraté",
-       "group-suppress": "Dohlížitelé",
+       "group-suppress": "Utajovatelé",
        "group-all": "(všichni)",
        "group-user-member": "{{GENDER:$1|uživatel|uživatelka|uživatel}}",
        "group-autoconfirmed-member": "automaticky {{GENDER:$1|schválený uživatel|schválená uživatelka|schválený uživatel}}",
        "group-bot-member": "{{GENDER:$1|bot|botka|bot}}",
        "group-sysop-member": "{{GENDER:$1|správce|správkyně|správce}}",
        "group-bureaucrat-member": "{{GENDER:$1|byrokrat|byrokratka|byrokrat}}",
-       "group-suppress-member": "{{GENDER:$1|dohlížitel|dohlížitelka|dohlížitel}}",
+       "group-suppress-member": "{{GENDER:$1|utajovatel|utajovatelka|utajovatel}}",
        "grouppage-user": "{{ns:project}}:Uživatelé",
        "grouppage-autoconfirmed": "{{ns:project}}:Automaticky schválení uživatelé",
        "grouppage-bot": "{{ns:project}}:Boti",
        "grouppage-sysop": "{{ns:project}}:Správci",
        "grouppage-bureaucrat": "{{ns:project}}:Byrokraté",
-       "grouppage-suppress": "{{ns:project}}:Dohlížitelé",
+       "grouppage-suppress": "{{ns:project}}:Utajovatelé",
        "right-read": "Čtení stránek",
        "right-edit": "Editace stránek",
        "right-createpage": "Zakládání stránek (které nejsou diskusní)",
        "upload-http-error": "Došlo k chybě HTTP: $1",
        "upload-copy-upload-invalid-domain": "Načítání kopírováním není dostupné z této domény.",
        "upload-dialog-title": "Načtení souboru",
-       "upload-dialog-error": "Došlo k chybě",
-       "upload-dialog-warning": "Objevilo se upozornění",
        "upload-dialog-button-cancel": "Storno",
        "upload-dialog-button-done": "Hotovo",
        "upload-dialog-button-save": "Uložit",
        "upload-dialog-button-upload": "Načíst",
-       "upload-dialog-label-select-file": "Výběr souboru",
-       "upload-dialog-label-infoform-title": "Podrobnosti",
-       "upload-dialog-label-infoform-name": "Název",
-       "upload-dialog-label-infoform-description": "Popis",
-       "upload-dialog-label-usage-title": "Použití",
-       "upload-dialog-label-usage-filename": "Jméno souboru",
+       "upload-process-error": "Došlo k chybě",
+       "upload-process-warning": "Objevilo se upozornění",
+       "upload-form-label-select-file": "Výběr souboru",
+       "upload-form-label-infoform-title": "Podrobnosti",
+       "upload-form-label-infoform-name": "Název",
+       "upload-form-label-infoform-description": "Popis",
+       "upload-form-label-usage-title": "Použití",
+       "upload-form-label-usage-filename": "Jméno souboru",
        "backend-fail-stream": "Soubor $1 nelze streamovat.",
        "backend-fail-backup": "Soubor $1 nelze zazálohovat.",
        "backend-fail-notexists": "Soubor $1 neexistuje.",
        "filerevert-legend": "Vrátit zpět soubor",
        "filerevert-intro": "Vracíte zpět '''[[Media:$1|$1]]''' na [$4 verzi z $3 $2].",
        "filerevert-comment": "Důvod:",
-       "filerevert-defaultcomment": "Navrácena verze nahraná v $2 dne $1.",
+       "filerevert-defaultcomment": "Návrat na verzi z $2, $1 ($3)",
        "filerevert-submit": "Vrátit zpět",
        "filerevert-success": "Soubor '''[[Media:$1|$1]]''' byl vrácen zpět na [$4 verzi z $3 $2].",
        "filerevert-badversion": "Není dostupná předchozí verze tohoto souboru s odpovídající časovou značkou.",
        "nopagetext": "Cílová stránka, kterou jste specifikovali, neexistuje.",
        "pager-newer-n": "{{PLURAL:$1|1 novější|$1 novější|$1 novějších}}",
        "pager-older-n": "{{PLURAL:$1|1 starší|$1 starší|$1 starších}}",
-       "suppress": "Dozor",
+       "suppress": "Utajit",
        "querypage-disabled": "Tato speciální stránka je z výkonnostních důvodů vypnuta.",
        "apihelp": "Nápověda k API",
        "apihelp-no-such-module": "Modul „$1“ nebyl nalezen.",
        "api-error-badaccess-groups": "Nemáte povoleno nahrávat soubory na tuto wiki.",
        "api-error-badtoken": "Vnitřní chyba: špatný token.",
        "api-error-copyuploaddisabled": "Načítání z URL je na tomto severu zakázáno.",
-       "api-error-duplicate": "Na této wiki již {{PLURAL:$1|existuje [$2 jiný soubor]|existují [$2 jiné soubory]}} se shodným obsahem.",
-       "api-error-duplicate-archive": "[$2 {{PLURAL:$1|Soubor|Soubory}}] se stejným obsahem již zde dříve {{PLURAL:$1|byl|byly}}, ale {{PLURAL:$1|byl smazán|byly smazány}}.",
+       "api-error-duplicate": "Na této wiki již {{PLURAL:$1|existuje jiný soubor|existují jiné soubory}} se shodným obsahem.",
+       "api-error-duplicate-archive": "{{PLURAL:$1|Soubor|Soubory}} se stejným obsahem již zde dříve {{PLURAL:$1|byl|byly}}, ale {{PLURAL:$1|byl smazán|byly smazány}}.",
        "api-error-empty-file": "Načtený soubor je prázdný.",
        "api-error-emptypage": "Zakládání prázdných stránek není dovoleno.",
        "api-error-fetchfileerror": "Vnitřní chyba: došlo k chybě při stahování souboru.",
index b8f4003..ec3a284 100644 (file)
        "api-error-badtoken": "Gwall mewnol: tocyn gwael.",
        "api-error-copyuploaddisabled": "Nid oes modd uwchlwytho drwy URL ar y gweinydd hwn",
        "api-error-duplicate": "Mae {{PLURAL:$1||[$2 ffeil arall]|[$2 ffeiliau eraill]|[$2 ffeiliau eraill]|[$2 ffeiliau eraill]|[$2 ffeiliau eraill]}} gyda'r un cynnwys {{PLURAL:$1||ynddi|ynddynt|ynddynt|ynddynt|ynddynt}} eisoes ar y wici hwn",
-       "api-error-duplicate-archive": "Fe fu {{PLURAL:$1|[$2 ffeil arall gyda'r un cynnwys ynddi]|[$2 ffeiliau eraill gyda'r un cynnwys ynddynt]}} ar y safle, ond fe'{{PLURAL:$1|i|u}} dilëwyd.",
+       "api-error-duplicate-archive": "Fe fu {{PLURAL:$1|ffeil arall gyda'r un cynnwys ynddi|ffeiliau eraill gyda'r un cynnwys ynddynt}} ar y safle, ond fe'{{PLURAL:$1|i|u}} dilëwyd.",
        "api-error-empty-file": "Mae'r ffeil a gyflwynwyd gennych yn wag.",
        "api-error-emptypage": "Ni chaniateir dechrau tudalen newydd, a honno'n wag.",
        "api-error-fetchfileerror": "Gwall mewnol: aeth rhywbeth o'i le tra'n cywain y ffeil.",
index f067a23..93b0f7e 100644 (file)
@@ -49,7 +49,8 @@
                        "아라",
                        "Thomsen",
                        "Knud Winckelmann",
-                       "Macofe"
+                       "Macofe",
+                       "Jyllanj"
                ]
        },
        "tog-underline": "Understreg henvisninger:",
@@ -84,7 +85,7 @@
        "tog-watchlisthideminor": "Skjul mindre ændringer i overvågningslisten",
        "tog-watchlisthideliu": "Skjul indloggede brugeres redigeringer i overvågningslisten",
        "tog-watchlisthideanons": "Skjul anonyme brugeres redigeringer i overvågningslisten",
-       "tog-watchlisthidepatrolled": "Skjul patrujerede ændringer fra overvågningslisten",
+       "tog-watchlisthidepatrolled": "Skjul patruljerede ændringer fra overvågningslisten",
        "tog-ccmeonemails": "Send mig kopier af e-mails som jeg sender til andre brugere",
        "tog-diffonly": "Vis ikke sideindhold neden under versionssammenligninger",
        "tog-showhiddencats": "Vis skjulte kategorier",
        "yourname": "Dit brugernavn:",
        "userlogin-yourname": "Brugernavn",
        "userlogin-yourname-ph": "Indtast dit brugernavn",
-       "createacct-another-username-ph": "Indtast Brugernavn",
+       "createacct-another-username-ph": "Indtast brugernavn",
        "yourpassword": "Din adgangskode:",
        "userlogin-yourpassword": "Adgangskode",
        "userlogin-yourpassword-ph": "Indtast din adgangskode",
        "api-error-badtoken": "Intern fejl: ugyldigt mærke.",
        "api-error-copyuploaddisabled": "At lægge filer op via hjemmesideadresser er slået fra på denne server.",
        "api-error-duplicate": "Der er allerede {{PLURAL:$1|[$2 en anden fil]|[$2 nogle andre filer]}} med samme indhold på webstedet.",
-       "api-error-duplicate-archive": "Der var allerede {{PLURAL:$1|en [$2 anden fil]|[$2 nogle andre filer]}} med samme indhold på webstedet, men {{PLURAL:$1|den|de}} blev slettet.",
+       "api-error-duplicate-archive": "Der var allerede {{PLURAL:$1|en anden fil|nogle andre filer}} med samme indhold på webstedet, men {{PLURAL:$1|den|de}} blev slettet.",
        "api-error-empty-file": "Den fil du indsendte var tom.",
        "api-error-emptypage": "Det er ikke tilladt at oprette nye, tomme sider.",
        "api-error-fetchfileerror": "Intern fejl: noget gik galt under hentningen af filen.",
index 89fe6cd..0e03756 100644 (file)
        "group-bot": "Bots",
        "group-sysop": "Administratoren",
        "group-bureaucrat": "Bürokraten",
-       "group-suppress": "Oversighter",
+       "group-suppress": "Unterdrücker",
        "group-all": "(alle)",
        "group-user-member": "{{GENDER:$1|Benutzer|Benutzerin}}",
        "group-autoconfirmed-member": "{{GENDER:$1|Automatisch bestätigter Benutzer|Automatisch bestätigte Benutzerin}}",
        "group-bot-member": "Bot",
        "group-sysop-member": "{{GENDER:$1|Administrator|Administratorin}}",
        "group-bureaucrat-member": "{{GENDER:$1|Bürokrat|Bürokratin}}",
-       "group-suppress-member": "{{GENDER:$1|Oversighter|Oversighterin}}",
+       "group-suppress-member": "{{GENDER:$1|Unterdrücker|Unterdrückerin}}",
        "grouppage-user": "{{ns:project}}:Benutzer",
        "grouppage-autoconfirmed": "{{ns:project}}:Automatisch bestätigte Benutzer",
        "grouppage-bot": "{{ns:project}}:Bots",
        "grouppage-sysop": "{{ns:project}}:Administratoren",
        "grouppage-bureaucrat": "{{ns:project}}:Bürokraten",
-       "grouppage-suppress": "{{ns:project}}:Oversighter",
+       "grouppage-suppress": "{{ns:project}}:Unterdrücker",
        "right-read": "Seiten lesen",
        "right-edit": "Seiten bearbeiten",
        "right-createpage": "Seiten erstellen (die keine Diskussionsseiten sind)",
        "upload-http-error": "Ein HTTP-Fehler ist aufgetreten: $1",
        "upload-copy-upload-invalid-domain": "Als Kopie hochladbare Dateien sind über diese Domain nicht verfügbar.",
        "upload-dialog-title": "Datei hochladen",
-       "upload-dialog-error": "Es ist ein Fehler aufgetreten",
-       "upload-dialog-warning": "Es ist eine Warnung aufgetreten",
        "upload-dialog-button-cancel": "Abbrechen",
        "upload-dialog-button-done": "Schließen",
        "upload-dialog-button-save": "Speichern",
        "upload-dialog-button-upload": "Hochladen",
-       "upload-dialog-label-select-file": "Datei auswählen",
-       "upload-dialog-label-infoform-title": "Einzelheiten",
-       "upload-dialog-label-infoform-name": "Name",
-       "upload-dialog-label-infoform-description": "Beschreibung",
-       "upload-dialog-label-usage-title": "Verwendung",
-       "upload-dialog-label-usage-filename": "Dateiname",
+       "upload-process-error": "Es ist ein Fehler aufgetreten",
+       "upload-process-warning": "Es ist eine Warnung aufgetreten",
+       "upload-form-label-select-file": "Datei auswählen",
+       "upload-form-label-infoform-title": "Einzelheiten",
+       "upload-form-label-infoform-name": "Name",
+       "upload-form-label-infoform-description": "Beschreibung",
+       "upload-form-label-usage-title": "Verwendung",
+       "upload-form-label-usage-filename": "Dateiname",
        "backend-fail-stream": "Die Datei $1 konnte nicht übertragen werden.",
        "backend-fail-backup": "Die Datei $1 konnte nicht gesichert werden.",
        "backend-fail-notexists": "Die Datei $1 ist nicht vorhanden.",
        "filerevert-legend": "Datei zurücksetzen",
        "filerevert-intro": "Du setzt die Datei '''[[Media:$1|$1]]''' auf die [$4 Version vom $2, $3 Uhr] zurück.",
        "filerevert-comment": "Grund:",
-       "filerevert-defaultcomment": "Zurückgesetzt auf die Version vom $1, $2 Uhr",
+       "filerevert-defaultcomment": "Zurückgesetzt auf die Version vom $1, $2 Uhr ($3)",
        "filerevert-submit": "Zurücksetzen",
        "filerevert-success": "'''[[Media:$1|$1]]''' wurde auf die [$4 Version vom $2, $3 Uhr] zurückgesetzt.",
        "filerevert-badversion": "Es gibt keine Version der Datei zu dem angegebenen Zeitpunkt.",
        "nopagetext": "Die angegebene Seite ist nicht vorhanden.",
        "pager-newer-n": "{{PLURAL:$1|nächster|nächste $1}}",
        "pager-older-n": "{{PLURAL:$1|vorheriger|vorherige $1}}",
-       "suppress": "Oversight",
+       "suppress": "Unterdrücken",
        "querypage-disabled": "Diese Spezialseite wurde aus Gründen der Leistungserhaltung deaktiviert.",
        "apihelp": "API-Hilfe",
        "apihelp-no-such-module": "Modul „$1“ nicht gefunden.",
        "emailccsubject": "Kopie deiner Nachricht an $1: $2",
        "emailsent": "E-Mail verschickt",
        "emailsenttext": "Deine E-Mail wurde verschickt.",
-       "emailuserfooter": "Diese E-Mail wurde von „$1“ an „$2“ durch die Funktion „{{int:emailuser}}“ bei {{SITENAME}} gesendet.",
+       "emailuserfooter": "Diese E-Mail wurde von „$1“ an „{{GENDER:$2|$2}}“ durch die Funktion „{{int:emailuser}}“ bei {{SITENAME}} {{GENDER:$1|gesendet}}.",
        "usermessage-summary": "Systemnachricht gespeichert.",
        "usermessage-editor": "System-Messenger",
        "usermessage-template": "MediaWiki:Benutzernachricht",
        "logentry-newusers-byemail": "Benutzerkonto $3 wurde von $1 {{GENDER:$2|erstellt}} und das Passwort wurde per E-Mail zugesandt",
        "logentry-newusers-autocreate": "Benutzerkonto $1 wurde automatisch {{GENDER:$2|erstellt}}",
        "logentry-protect-move_prot": "$1 {{GENDER:$2|verschob}} die Schutzeinstellungen von $4 nach $3",
+       "logentry-protect-unprotect": "$1 {{GENDER:$2|entfernte}} den Schutz der Seite $3",
+       "logentry-protect-protect": "$1 {{GENDER:$2|schützte}} die Seite $3 $4",
+       "logentry-protect-protect-cascade": "$1 {{GENDER:$2|schützte}} die Seite $3 $4 [kaskadierend]",
+       "logentry-protect-modify": "$1 {{GENDER:$2|änderte}} den Schutzstatus der Seite $3 $4",
+       "logentry-protect-modify-cascade": "$1 {{GENDER:$2|änderte}} den Schutzstatus der Seite $3 $4 [kaskadierend]",
        "logentry-rights-rights": "$1 {{GENDER:$2|änderte}} die Gruppenzugehörigkeit für $3 von $4 zu $5",
        "logentry-rights-rights-legacy": "$1 {{GENDER:$2|änderte}} die Gruppenzugehörigkeit für $3",
        "logentry-rights-autopromote": "$1 wurde automatisch von $4 zu $5 {{GENDER:$2|zugeordnet}}",
index b56d4dd..ab4c2ba 100644 (file)
        "api-error-badtoken": "Xetaya zerreki: Antışo xırabın.",
        "api-error-copyuploaddisabled": "URL barkerdış ena waster dı qefılyayo.",
        "api-error-duplicate": "Ena {{PLURAL:$1|ze ke [zey $2]|biya [zey dosya da $2]}} zeq wesiqa biya wendeyê.",
-       "api-error-duplicate-archive": "Ena {{PLURAL:$1|vurneyaya [$2 zey na dosya]| [zerrey cı zey $2 dosya]}} aseno,feqet {{PLURAL:$1|ena dosya|tewr veri}} besterneyaya.",
+       "api-error-duplicate-archive": "Ena {{PLURAL:$1|vurneyaya zey na dosya|zerrey cı zey dosya}} aseno,feqet {{PLURAL:$1|ena dosya|tewr veri}} besterneyaya.",
        "api-error-empty-file": "Dosyaya ke şıma rışta venga.",
        "api-error-emptypage": "Newi, pelaya veng vıraştışi rê mısade nêdeyêno.",
        "api-error-fetchfileerror": "Xırabiya zerrek:Dosya grotış dı tay çi raşt nêşı.",
index 99879da..88a6a34 100644 (file)
@@ -4,7 +4,8 @@
                        "जनक राज भट्ट",
                        "बिप्लब आनन्द",
                        "रमेश सिंह बोहरा",
-                       "राम प्रसाद जोशी"
+                       "राम प्रसाद जोशी",
+                       "Macofe"
                ]
        },
        "tog-underline": "सम्बन्ध निम्न रेखाङ्कन:",
        "fileexists": "यै नामको फाइल पैल्ली नैं छ, यदि तम परिवर्तन गद्या कुरडीमू सुनिश्चित छैनौ भण्या कृपया <strong>[[:$1]]</strong> जाँच गर।\n[[$1|thumb]]",
        "filewasdeleted": "यै नामको एक फाइल पहिली पनि अपलोड गरिबर पछि हटाई सकियाको छ।\nपुनः अपलोड गद्दु पूर्व तम $1 लाई निक्करी जाँच गर ।",
        "upload-dialog-title": "चित्र अपलोड गर",
-       "upload-dialog-error": "गल्ती भयो",
-       "upload-dialog-warning": "सूचना हुनु",
        "upload-dialog-button-cancel": "रद्द",
        "upload-dialog-button-done": "सकियो",
        "upload-dialog-button-save": "सङ्ग्रह गद्या",
        "upload-dialog-button-upload": "अपलोड",
-       "upload-dialog-label-select-file": "फाइल छान",
-       "upload-dialog-label-infoform-title": "विवरण",
-       "upload-dialog-label-infoform-name": "नाऊ",
-       "upload-dialog-label-infoform-description": "बेलिविस्तार",
-       "upload-dialog-label-usage-title": "रिती",
-       "upload-dialog-label-usage-filename": "फाइल नाउ",
+       "upload-process-error": "गल्ती भयो",
+       "upload-process-warning": "सूचना हुनु",
+       "upload-form-label-select-file": "फाइल छान",
+       "upload-form-label-infoform-title": "विवरण",
+       "upload-form-label-infoform-name": "नाऊ",
+       "upload-form-label-infoform-description": "बेलिविस्तार",
+       "upload-form-label-usage-title": "रिती",
+       "upload-form-label-usage-filename": "फाइल नाउ",
        "uploadstash-nofiles": "तमरा कोइ पनि स्टाश गर्याका फाइलहरू नाइथिन् ।",
        "uploadstash-badtoken": "त्यो कार्य असफलभयो , सायद तमरो सम्पादन अधिकार समाप्त भयो । पुन: प्रयास गर ।",
        "uploadstash-refresh": "फाइलहरूको सूची ताजा गर्न्या",
        "feedback-bugornote": "यदि तमी कुनै प्राविधिक समस्यालाई विस्तारले सम्झाउन तयार छौ भण्या कृपया [$1 बग राख]।\nयदि हैन, भण्या तमी तल दियाको सरल फारमको प्रयोग गद्दसक्द्याहौ । तमरो टिप्पणी, तमरो प्रयोगकर्ता नाम र तमरो ब्राउजरको नाम सहित \"[$3 $2]\" पानामी जोडिन्याछ ।",
        "searchsuggest-search": "खोज",
        "api-error-duplicate": "यै साइटमी पहिलीबठे यस्तै सामग्री {{PLURAL:$1|भयाको [$2 अर्को फाइल छ]|भयाका  [$2 केहि अरु फाइलहरू छन्]}} ।",
-       "api-error-duplicate-archive": "यै साइटमी पहिलेबाट यस्तै सामग्री {{PLURAL:$1|भयाको [$2 अर्को फाइल थियो]|भयाका  [$2 केहि अरु फाइलहरू थिए]}} ।\nतर {{PLURAL:$1|यो मेट्याको थियो|यी मेटायाका थिए}} ।",
+       "api-error-duplicate-archive": "यै साइटमी पहिलेबाट यस्तै सामग्री {{PLURAL:$1|भयाको अर्को फाइल थियो|भयाका केहि अरु फाइलहरू थिए}} ।\nतर {{PLURAL:$1|यो मेट्याको थियो|यी मेटायाका थिए}} ।",
        "expand_templates_preview_fail_html": "<em>किनकि {{SITENAME}} सिधै एचटिएमयल सक्षम छ र तमीले लग इन गर्या छैनौ, पूर्वावलोकन लुकाइयाको छ ताकि सम्भावित जाभास्क्रिप्ट आक्रमणलाई रोक्द सकियोस् ।</em>\n\n<strong>यदि यो मान्य पूर्ववावलोकन प्रयास हो भण्या पुन प्रयास गर ।</strong>\nयदि यसले कार्य पूर्ण भएन भण्या [[Special:UserLogout|लग आउट गरिबर]] फेरी लग इन गर्या ।",
        "expand_templates_preview_fail_html_anon": "<em>किनकि {{SITENAME}} सिधै एचटिएमयल सक्षम छ र तमीले लग इन गर्या छैनौ, पूर्वावलोकन लुकाइयाको छ ताकि सम्भावित जाभास्क्रिप्ट आक्रमणलाई रोक्द सकियोस् ।</em>\n\n<strong>यदि यो मान्य पूर्वावलोकन प्रयास हो भण्या कृपया [[Special:UserLogin|लग इन गरिबर]] पुनः प्रयास गर्या ।</strong>",
        "default-skin-not-found": "ओह! तमरो विकिको पूर्व निर्धारित खोल जस्तो कि <code dir=\"ltr\">$wgDefaultSkin</code> मी बताइयाको<code>$1</code>, उपलब्ध नाईथिन् ।\n\nतमरो इन्स्टलेसन यी खोलहरूलाई सम्मिलित गर्दछ {{PLURAL:$4|खोल|खोलहरू}}। हेर [https://www.mediawiki.org/wiki/Manual:Skin_configuration Manual: खोललाई सम्मिलित गर्नु] ताकि तमीलाई जानकारी होस् कि कसरि {{PLURAL:$4|उसलाई|उसलाई सम्मिलित गर्न सकियोस् र निर्धारितलाई तय गद्दे}}।\n\n$2\n\n; यदि तमीले अहिले मीडियाविकि इन्स्टाल गर्याका छौ:\n: तमीले सम्भवत गिटबठे इन्स्टाल गर्याका छौ, वा सिधै स्रोत कोडबठे गर्याका छौ जैको लागि कुनै अर्कै तारिका प्रयोग गरियाको छ । यो आशा अनुरूप छ । कोशिश गर केहि खोलहरू\n[https://www.mediawiki.org/wiki/Category:All_skins mediawiki.org's मीडियाविकिको खोल डाइरेक्ट्रीबाट डाउनलोड गद्या], जैको लागि तमी:\n:* डाउनलोड गर [https://www.mediawiki.org/wiki/Download टरबल इन्स्टालर], जुन कयौं खोलहरू र विस्तारमी उपलब्ध छन्। तमी खोलहरूको कोड <code>skins/</code> त्यसको डाइरेक्ट्रीबाट कपी-पेस्ट गद्द सक्द्या हौ। \n:* व्यक्तिगत खोलहरू टरबलबठे डाउनलोड गर\n[https://www.mediawiki.org/wiki/Special:SkinDistributor मीडिया विकि] बठे।\n:* [https://www.mediawiki.org/wiki/Download_from_Git#Using_Git_to_download_MediaWiki_skins गिटको प्रयोग गरेर डाउनलोड गद्द सकन्छौ]।\n: यदि तमी विकासकर्ता हौ भण्या यसो गद्दा तमरो गिट-रिपजिटरीमी केहि हुनुहुँदैन । \n; यदि तमीले अहिले मीडियाविकिलाई अपग्रेड गर्याका छौ:\n: मीडियाविकि १.२४ र यैको नवीन रूप स्वतः रूपले खोलहरूलाई सक्षम गद्दैनन् (हेर [https://www.mediawiki.org/wiki/Manual:Skin_autodiscovery Manual:खोलहरूको स्वतः खोज])। तमी निम्नलिखितलाई पेस्ट गद्द सकन्छौ: {{PLURAL:$5|लाइन|लाइनहरू}}  <code>LocalSettings.php</code> मी ताकि {{PLURAL:$5|उसले|सबै}} सक्षम होस् जस्तो कि तमीले इन्स्टाल गर्याको {{PLURAL:$5|खोल|खोलहरू}}को मामिलामी:\n\n<pre dir=\"ltr\">$3</pre>\n\n; यदि तमीले अहिले परिवर्तन गर्याका छौ<code>LocalSettings.php</code>:\n: खोल नामहरूको अगाडी डबल-क्लिक गर जसले तमलाई विभिन्न प्रकारहरूको विकल्प दिन्छ।"
index 5adfcef..829e363 100644 (file)
        "upload-http-error": "Εμφανίστηκε κάποιο σφάλμα HTTP: $1",
        "upload-copy-upload-invalid-domain": "Δεν υπάρχουν διαθέσιμα ανεβάσματα αντιγράφων από αυτό τον τομέα.",
        "upload-dialog-title": "Ανέβασμα αρχείου",
-       "upload-dialog-error": "Ένα σφάλμα συνέβη",
-       "upload-dialog-warning": "Προέκυψε μία προειδοποίηση",
        "upload-dialog-button-cancel": "Ακύρωση",
        "upload-dialog-button-done": "Ολοκληρώθηκε",
        "upload-dialog-button-save": "Αποθήκευση",
        "upload-dialog-button-upload": "Ανέβασμα",
-       "upload-dialog-label-select-file": "Επιλογή αρχείου",
-       "upload-dialog-label-infoform-title": "Λεπτομέρειες",
-       "upload-dialog-label-infoform-name": "Όνομα",
-       "upload-dialog-label-infoform-description": "Περιγραφή",
-       "upload-dialog-label-usage-title": "Χρήση",
-       "upload-dialog-label-usage-filename": "Όνομα αρχείου",
+       "upload-process-error": "Ένα σφάλμα συνέβη",
+       "upload-process-warning": "Προέκυψε μία προειδοποίηση",
+       "upload-form-label-select-file": "Επιλογή αρχείου",
+       "upload-form-label-infoform-title": "Λεπτομέρειες",
+       "upload-form-label-infoform-name": "Όνομα",
+       "upload-form-label-infoform-description": "Περιγραφή",
+       "upload-form-label-usage-title": "Χρήση",
+       "upload-form-label-usage-filename": "Όνομα αρχείου",
        "backend-fail-stream": "Αδύνατη η μετάδοση του αρχείου $1.",
        "backend-fail-backup": "Αδύνατη η δημιουργία αντίγραφου ασφαλείας του αρχείου $1.",
        "backend-fail-notexists": "Το αρχείο $1 δεν υπάρχει.",
        "api-error-badtoken": "Εσωτερικό σφάλμα: εσφαλμένο διακριτικό.",
        "api-error-copyuploaddisabled": "Η επιφόρτωση από URL είναι απενεργοποιημένη σε αυτόν το διακομιστή.",
        "api-error-duplicate": "{{PLURAL:$1|Υπάρχει  [$2 άλλο αρχείο]|Υπάρχουν [$2 άλλα αρχεία]}} ήδη στον ιστότοπο με το ίδιο περιεχόμενο.",
-       "api-error-duplicate-archive": "{{PLURAL:$1|Υπήρχε ήδη [$2 άλλο αρχείο] |Υπήρχαν ήδη [$2 άλλα αρχεία]}} στον ιστότοπο με το ίδιο περιεχόμενο, αλλά {{PLURAL:$1|διαγράφηκε|διαγράφηκαν}}.",
+       "api-error-duplicate-archive": "{{PLURAL:$1|Υπήρχε ήδη άλλο αρχείο|Υπήρχαν ήδη άλλα αρχεία}} στον ιστότοπο με το ίδιο περιεχόμενο, αλλά {{PLURAL:$1|διαγράφηκε|διαγράφηκαν}}.",
        "api-error-empty-file": "Το αρχείο που υποβάλλατε ήταν κενό.",
        "api-error-emptypage": "Η δημιουργία νέων, κενών σελιδών δεν επιτρέπετε.",
        "api-error-fetchfileerror": "Εσωτερικό σφάλμα: κάτι πήγε στραβά κατά την ανάκτηση του αρχείου.",
index 6218d70..611dbd6 100644 (file)
        "createacct-imgcaptcha-help": "",
        "createacct-imgcaptcha-ph": "Enter the text you see above",
        "createacct-submit": "Create your account",
-       "createacct-another-submit": "Create another account",
+       "createacct-another-submit": "Create account",
        "createacct-benefit-heading": "{{SITENAME}} is made by people like you.",
        "createacct-benefit-icon1": "icon-edits",
        "createacct-benefit-head1": "{{NUMBEROFEDITS}}",
        "group-bot": "Bots",
        "group-sysop": "Administrators",
        "group-bureaucrat": "Bureaucrats",
-       "group-suppress": "Oversights",
+       "group-suppress": "Suppressors",
        "group-all": "(all)",
        "group-user-member": "{{GENDER:$1|user}}",
        "group-autoconfirmed-member": "{{GENDER:$1|autoconfirmed user}}",
        "group-bot-member": "{{GENDER:$1|bot}}",
        "group-sysop-member": "{{GENDER:$1|administrator}}",
        "group-bureaucrat-member": "{{GENDER:$1|bureaucrat}}",
-       "group-suppress-member": "{{GENDER:$1|oversight}}",
+       "group-suppress-member": "{{GENDER:$1|suppressor}}",
        "grouppage-user": "{{ns:project}}:Users",
        "grouppage-autoconfirmed": "{{ns:project}}:Autoconfirmed users",
        "grouppage-bot": "{{ns:project}}:Bots",
        "grouppage-sysop": "{{ns:project}}:Administrators",
        "grouppage-bureaucrat": "{{ns:project}}:Bureaucrats",
-       "grouppage-suppress": "{{ns:project}}:Oversight",
+       "grouppage-suppress": "{{ns:project}}:Suppress",
        "right-read": "Read pages",
        "right-edit": "Edit pages",
        "right-createpage": "Create pages (which are not discussion pages)",
        "upload-http-error": "An HTTP error occurred: $1",
        "upload-copy-upload-invalid-domain": "Copy uploads are not available from this domain.",
        "upload-dialog-title": "Upload file",
-       "upload-dialog-error": "An error occurred",
-       "upload-dialog-warning": "A warning occurred",
        "upload-dialog-button-cancel": "Cancel",
        "upload-dialog-button-done": "Done",
        "upload-dialog-button-save": "Save",
        "upload-dialog-button-upload": "Upload",
-       "upload-dialog-label-select-file": "Select file",
-       "upload-dialog-label-infoform-title": "Details",
-       "upload-dialog-label-infoform-name": "Name",
-       "upload-dialog-label-infoform-description": "Description",
-       "upload-dialog-label-usage-title": "Usage",
-       "upload-dialog-label-usage-filename": "File name",
+       "upload-process-error": "An error occurred",
+       "upload-process-warning": "A warning occurred",
+       "upload-form-label-select-file": "Select file",
+       "upload-form-label-infoform-title": "Details",
+       "upload-form-label-infoform-name": "Name",
+       "upload-form-label-infoform-description": "Description",
+       "upload-form-label-usage-title": "Usage",
+       "upload-form-label-usage-filename": "File name",
        "backend-fail-stream": "Could not stream file \"$1\".",
        "backend-fail-backup": "Could not backup file \"$1\".",
        "backend-fail-notexists": "The file $1 does not exist.",
        "filerevert-legend": "Revert file",
        "filerevert-intro": "You are about to revert the file <strong>[[Media:$1|$1]]</strong> to the [$4 version as of $3, $2].",
        "filerevert-comment": "Reason:",
-       "filerevert-defaultcomment": "Reverted to version as of $2, $1",
+       "filerevert-defaultcomment": "Reverted to version as of $2, $1 ($3)",
        "filerevert-submit": "Revert",
        "filerevert-success": "<strong>[[Media:$1|$1]]</strong> has been reverted to the [$4 version as of $3, $2].",
        "filerevert-badversion": "There is no previous local version of this file with the provided timestamp.",
        "nopagetext": "The target page you have specified does not exist.",
        "pager-newer-n": "{{PLURAL:$1|newer 1|newer $1}}",
        "pager-older-n": "{{PLURAL:$1|older 1|older $1}}",
-       "suppress": "Oversight",
+       "suppress": "Suppress",
        "querypage-disabled": "This special page is disabled for performance reasons.",
        "apihelp": "API help",
        "apihelp-summary": "",
        "emailccsubject": "Copy of your message to $1: $2",
        "emailsent": "Email sent",
        "emailsenttext": "Your email message has been sent.",
-       "emailuserfooter": "This email was sent by $1 to $2 by the \"{{int:emailuser}}\" function at {{SITENAME}}.",
+       "emailuserfooter": "This email was {{GENDER:$1|sent}} by $1 to {{GENDER:$2|$2}} by the \"{{int:emailuser}}\" function at {{SITENAME}}.",
        "usermessage-summary": "Leaving system message.",
        "usermessage-editor": "System messenger",
        "usermessage-template": "MediaWiki:UserMessage",
        "htmlform-title-not-exists": "[[:$1]] does not exist.",
        "htmlform-user-not-exists": "<strong>$1</strong> does not exist.",
        "htmlform-user-not-valid": "<strong>$1</strong> isn't a valid username.",
+       "rawmessage": "$1",
        "sqlite-has-fts": "$1 with full-text search support",
        "sqlite-no-fts": "$1 without full-text search support",
        "logentry-delete-delete": "$1 {{GENDER:$2|deleted}} page $3",
        "logentry-newusers-byemail": "User account $3 was {{GENDER:$2|created}} by $1 and password was sent by email",
        "logentry-newusers-autocreate": "User account $1 was {{GENDER:$2|created}} automatically",
        "logentry-protect-move_prot": "$1 {{GENDER:$2|moved}} protection settings from $4 to $3",
+       "logentry-protect-unprotect": "$1 {{GENDER:$2|removed}} protection from $3",
+       "logentry-protect-protect": "$1 {{GENDER:$2|protected}} $3 $4",
+       "logentry-protect-protect-cascade": "$1 {{GENDER:$2|protected}} $3 $4 [cascading]",
+       "logentry-protect-modify": "$1 {{GENDER:$2|changed}} protection level for $3 $4",
+       "logentry-protect-modify-cascade": "$1 {{GENDER:$2|changed}} protection level for $3 $4 [cascading]",
+       "logentry-protect-unprotect": "$1 {{GENDER:$2|removed}} protection from $3",
        "logentry-rights-rights": "$1 {{GENDER:$2|changed}} group membership for $3 from $4 to $5",
        "logentry-rights-rights-legacy": "$1 {{GENDER:$2|changed}} group membership for $3",
        "logentry-rights-autopromote": "$1 was automatically {{GENDER:$2|promoted}} from $4 to $5",
index 2463205..37fcc46 100644 (file)
@@ -40,7 +40,8 @@
                        "아라",
                        "Fitoschido",
                        "Sudastelaro",
-                       "Ochilov"
+                       "Ochilov",
+                       "Macofe"
                ]
        },
        "tog-underline": "Substreki ligilojn",
        "api-error-badtoken": "Interna eraro: fuŝaĵo.",
        "api-error-copyuploaddisabled": "Alŝuto per URL-adreso estas malebligata en tiu ĉi servilo.",
        "api-error-duplicate": "Jam estas {{PLURAL:$1|[$2 alia dosiero]|[$2 pluraj aliaj dosieroj]}} de sama enhavo en la retejo.",
-       "api-error-duplicate-archive": "Jam estis {{PLURAL:$1|[$2 alia dosiero]|[$2 pluraj aliaj dosieroj]}} de sama enhavo en la retejo, sed {{PLURAL:$1|ĝi estis forigita|ili estis forigitaj}}.",
+       "api-error-duplicate-archive": "Jam estis {{PLURAL:$1|alia dosiero|pluraj aliaj dosieroj}} de sama enhavo en la retejo, sed {{PLURAL:$1|ĝi estis forigita|ili estis forigitaj}}.",
        "api-error-empty-file": "La dosiero kiun vi sendis estis malplena.",
        "api-error-emptypage": "Kreo de novaj malplenaj paĝoj ne estas permesita.",
        "api-error-fetchfileerror": "Interna eraro: io misfunkciis dum la dosiera prenado.",
index 3236d67..fa3c2ca 100644 (file)
        "nstab-template": "Plantilla",
        "nstab-help": "Ayuda",
        "nstab-category": "Categoría",
+       "mainpage-nstab": "Página principal",
        "nosuchaction": "No existe esa acción",
        "nosuchactiontext": "La acción especificada en la URL no es válida.\nEs posible que hayas escrito mal la URL o que hayas seguido un enlace incorrecto.\nEsto también podría indicar un error en el software utilizado por {{SITENAME}}.",
        "nosuchspecialpage": "No existe esa página especial",
        "createacct-captcha": "Comprobación de seguridad",
        "createacct-imgcaptcha-ph": "Escribe el texto de arriba",
        "createacct-submit": "Crea tu cuenta",
-       "createacct-another-submit": "Crear otra cuenta",
+       "createacct-another-submit": "Crear cuenta",
        "createacct-benefit-heading": "Personas como tú son las que construyen {{SITENAME}}.",
        "createacct-benefit-body1": "{{PLURAL:$1|edición|ediciones}}",
        "createacct-benefit-body2": "{{PLURAL:$1|página|páginas}}",
        "permissionserrorstext-withaction": "No tienes permiso para $2, por {{PLURAL:$1|el siguiente motivo|los siguientes motivos}}:",
        "recreate-moveddeleted-warn": "<strong>Atención: estás volviendo a crear una página que ha sido borrada anteriormente.</strong>\n\nPiensa si es adecuado continuar editando la página.\nA continuación, se proporciona el registro de borrado y traslados de esta página para más información:",
        "moveddeleted-notice": "Esta página ha sido borrada.\nA continuación, se proporciona el registro de borrados y traslados de la página para más información.",
+       "moveddeleted-notice-recent": "Esta página se ha eliminado recientemente (dentro de las últimas 24 horas).\nEl registro de eliminación y traslado de la página se muestran a continuación como referencia.",
        "log-fulllog": "Ver el registro completo",
        "edit-hook-aborted": "Una extensión ha evitado la edición.\nNo hay explicación disponible.",
        "edit-gone-missing": "No se ha podido actualizar la página.\nParece haber sido borrada.",
        "upload-http-error": "Ha ocurrido un error HTTP: $1",
        "upload-copy-upload-invalid-domain": "No se pueden realizar subidas remotas desde este dominio.",
        "upload-dialog-title": "Subir archivo",
-       "upload-dialog-error": "Ha ocurrido un error",
-       "upload-dialog-warning": "Ha ocurrido una advertencia",
        "upload-dialog-button-cancel": "Cancelar",
        "upload-dialog-button-done": "Hecho",
        "upload-dialog-button-save": "Guardar",
        "upload-dialog-button-upload": "Subir",
-       "upload-dialog-label-select-file": "Seleccionar archivo",
-       "upload-dialog-label-infoform-title": "Detalles",
-       "upload-dialog-label-infoform-name": "Nombre",
-       "upload-dialog-label-infoform-description": "Descripción",
-       "upload-dialog-label-usage-title": "Uso",
-       "upload-dialog-label-usage-filename": "Nombre del archivo",
+       "upload-process-error": "Ha ocurrido un error",
+       "upload-process-warning": "Ha ocurrido una advertencia",
+       "upload-form-label-select-file": "Seleccionar archivo",
+       "upload-form-label-infoform-title": "Detalles",
+       "upload-form-label-infoform-name": "Nombre",
+       "upload-form-label-infoform-description": "Descripción",
+       "upload-form-label-usage-title": "Uso",
+       "upload-form-label-usage-filename": "Nombre del archivo",
        "backend-fail-stream": "No se pudo transmitir el archivo «$1».",
        "backend-fail-backup": "No se pudo hacer copia de seguridad del archivo «$1».",
        "backend-fail-notexists": "El archivo  $1  no existe.",
        "nopagetext": "La página destino que has especificado no existe.",
        "pager-newer-n": "{{PLURAL:$1|1 siguiente|$1 siguientes}}",
        "pager-older-n": "{{PLURAL:$1|1 anterior|$1 anteriores}}",
-       "suppress": "Supresor de ediciones",
+       "suppress": "Supresor",
        "querypage-disabled": "Esta página especial está deshabilitada por motivos de rendimiento.",
        "apihelp": "Ayuda de la API",
        "apihelp-no-such-module": "No se encontró el módulo \"$1\".",
        "api-error-badaccess-groups": "No puedes cargar archivos en este wiki.",
        "api-error-badtoken": "Error interno: Símbolo incorrecto.",
        "api-error-copyuploaddisabled": "La subida por URL está desactivada en este servidor.",
-       "api-error-duplicate": "Ya existe{{PLURAL:$1| [$2 otro archivo]|[$2 n otros archivos]}} en el sitio con el mismo contenido.",
-       "api-error-duplicate-archive": "Ya {{PLURAL:$1|existía [$2 otro archivo]|existían [$2 otros archivos]}} en el sitio con el mismo contenido, pero {{PLURAL:$1|fue eliminado|fueron eliminados}}.",
+       "api-error-duplicate": "Ya {{PLURAL:$1|existe otro archivo|existen otros archivos}} en el sitio con el mismo contenido.",
+       "api-error-duplicate-archive": "Ya {{PLURAL:$1|existía otro archivo|existían otros archivos}} en el sitio con el mismo contenido, pero {{PLURAL:$1|fue eliminado|fueron eliminados}}.",
        "api-error-empty-file": "El archivo que enviaste estaba vacío.",
        "api-error-emptypage": "No se pueden crear páginas nuevas que estén vacías.",
        "api-error-fetchfileerror": "Error interno: Algo salió mal mientras se obtenía el archivo.",
index 5384ea9..6dcb457 100644 (file)
@@ -25,7 +25,8 @@
                        "Boxmein",
                        "Roland",
                        "Postituvi",
-                       "Purodha"
+                       "Purodha",
+                       "Macofe"
                ]
        },
        "tog-underline": "Linkide allakriipsutus:",
        "upload-http-error": "HTTP-viga: $1",
        "upload-copy-upload-invalid-domain": "Sellest domeenist pole kopeerimise teel üleslaadimine võimalik.",
        "upload-dialog-title": "Faili üleslaadimine",
-       "upload-dialog-error": "Esines tõrge",
-       "upload-dialog-warning": "Esines hoiatus",
        "upload-dialog-button-cancel": "Loobu",
        "upload-dialog-button-done": "Valmis",
        "upload-dialog-button-save": "Salvesta",
        "upload-dialog-button-upload": "Laadi üles",
-       "upload-dialog-label-select-file": "Vali fail",
-       "upload-dialog-label-infoform-title": "Üksikasjad",
-       "upload-dialog-label-infoform-name": "Pealkiri",
-       "upload-dialog-label-infoform-description": "Kirjeldus",
-       "upload-dialog-label-usage-title": "Kasutus",
-       "upload-dialog-label-usage-filename": "Failinimi",
+       "upload-process-error": "Esines tõrge",
+       "upload-process-warning": "Esines hoiatus",
+       "upload-form-label-select-file": "Vali fail",
+       "upload-form-label-infoform-title": "Üksikasjad",
+       "upload-form-label-infoform-name": "Pealkiri",
+       "upload-form-label-infoform-description": "Kirjeldus",
+       "upload-form-label-usage-title": "Kasutus",
+       "upload-form-label-usage-filename": "Failinimi",
        "backend-fail-stream": "Faili $1 ei saanud edastada.",
        "backend-fail-backup": "Faili $1 ei saanud varundada.",
        "backend-fail-notexists": "Faili $1 pole olemas.",
        "api-error-badtoken": "Sisemine tõrge: Sobimatu nimi.",
        "api-error-copyuploaddisabled": "URLi kaudu üleslaadimine on selles serveris keelatud.",
        "api-error-duplicate": "Siin on {{PLURAL:$1|[$2 teine samasisuline fail]|[$2 mõned teised samasisulised failid]}} juba olemas.",
-       "api-error-duplicate-archive": "Siin {{PLURAL:$1|on [$2 teine samasisuline fail]|olid [$2 mõned teised samasisulised failid]}} juba olemas, aga {{PLURAL:$1|see|need}} kustutati.",
+       "api-error-duplicate-archive": "Siin {{PLURAL:$1|on teine samasisuline fail|olid mõned teised samasisulised failid}} juba olemas, aga {{PLURAL:$1|see|need}} kustutati.",
        "api-error-empty-file": "Üleslaaditav fail on tühi.",
        "api-error-emptypage": "Uute tühjade lehekülgede loomine pole lubatud.",
        "api-error-fetchfileerror": "Sisemine tõrge: Midagi läks faili kättesaamisel valesti.",
index 1e46351..59ffe63 100644 (file)
        "nstab-template": "Txantiloi",
        "nstab-help": "Laguntza orrialdea",
        "nstab-category": "Kategoria",
-       "nosuchaction": "Ekintza hori ez da existitizen",
+       "mainpage-nstab": "Azala",
+       "nosuchaction": "Ekintza hori ez da existitzen",
        "nosuchactiontext": "URL bidez zehaztutako ekintza okerra da.\nURLa gaizki idatzi duzu, edo hautsitako lotura jarraitu duzu.\nHonek akatsa indikatzen du {{SITENAME}}-(e)n.",
-       "nosuchspecialpage": "Ez da aparteko orrialde hori existitzen",
+       "nosuchspecialpage": "Orri berezi hori ez existitzen",
        "nospecialpagetext": "<strong>Orri berezi baliogabe bat eskatu duzu.</strong>\n\nBada orri berezien zerrenda bat, [[Special:SpecialPages|{{int:specialpages}}]] orrian.",
        "error": "Errorea",
        "databaseerror": "Datu-base errorea",
        "createacct-captcha": "Segurtasun froga",
        "createacct-imgcaptcha-ph": "Sartu gainean ikusten duzun testua",
        "createacct-submit": "Kontua sortu",
-       "createacct-another-submit": "Beste kontu bat sortu",
+       "createacct-another-submit": "Kontu bat sortu",
        "createacct-benefit-heading": "{{SITENAME}} zu bezalako pertsonek egiten dute.",
        "createacct-benefit-body1": "{{PLURAL:$1|edizio bat|$1 edizio}}",
        "createacct-benefit-body2": "{{PLURAL:$1|Orrialde 1|$1 orrialde}}",
        "rev-deleted-text-permission": "Orrialdearen berrikuspen hau '''ezabatua''' izan da.\nXehetasunak [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} ezabaketa erregistroan] ikus daitezke.",
        "rev-deleted-text-unhide": "Orriaren bertsio hau '''ezabatu''' da.\nXehetasunak ikusgai daude [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} ezabatze erregistroan].\nAdministratzailea zarenez, oraindik [$1 bertsio hau ikus dezakezu], nahi izanez gero.",
        "rev-suppressed-text-unhide": "Orriaren bertsio hau '''ezeztatu''' da.\nXehetasunak ikusgai daude [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} ezeztatze erregistroan].\nAdministratzailea zarenez, oraindik [$1 bertsio hau ikus dezakezu], nahi izanez gero.",
-       "rev-deleted-text-view": "Orrialdearen berrikuspen hau '''ezabatua''' izan da.\nZuk ikusteko aukera daukazu; xehetasunak [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} ezabaketa erregistroan] ikus ditzakezu.",
+       "rev-deleted-text-view": "Orriaren berrikuspen hau '''ezabatua''' izan da.\nZuk ikusteko aukera daukazu; xehetasunak [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} ezabaketa erregistroan] ikus ditzakezu.",
        "rev-suppressed-text-view": "Berrikuspen hau '''ezabatua''' izan da.\nAdministratzaile bezala ikus dezakezu; xehetasun gehiagorako [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} ezabapen erregistrora joan].",
        "rev-deleted-no-diff": "Ezin duzu ezberdintasun hau ikusi, berrikuspenetako bat '''ezabatua''' izan delako.\nXehetasunak [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} ezabaketa erregistroan] aurki ditzakezu.",
        "rev-suppressed-no-diff": "Ezin duzu ezberdintasunik ikusi berrikuspenen bat '''ezabatua''' izan delako.",
        "revdelete-legend": "Berrikuspen mugapenak ezarri:",
        "revdelete-hide-text": "Berrikuspenaren testua ezkutatu",
        "revdelete-hide-image": "Fitxategiaren edukia ezkutatu",
-       "revdelete-hide-name": "Ezkutatu ekintza eta helburua",
+       "revdelete-hide-name": "Ezkutatu helburua eta parametroak",
        "revdelete-hide-comment": "Aldaketaren iruzkina ezkutatu",
        "revdelete-hide-user": "Egilearen erabiltzaile izena/IPa ezkutatu",
        "revdelete-hide-restricted": "Mugapen hauek administratzaileei zein besteei aplikatu",
        "upload-http-error": "HTTP errorea gertatu da: $1",
        "upload-copy-upload-invalid-domain": "Domeinu honetan ezin dira igoerak kopiatu.",
        "upload-dialog-title": "Igo fitxategia",
-       "upload-dialog-error": "Errore bat gertatu da",
        "upload-dialog-button-cancel": "Utzi",
        "upload-dialog-button-done": "Egina",
        "upload-dialog-button-save": "Gorde",
        "upload-dialog-button-upload": "Igo",
-       "upload-dialog-label-select-file": "Fitxategia Aukeratu",
-       "upload-dialog-label-infoform-title": "Xehetasunak",
-       "upload-dialog-label-infoform-name": "Izena",
-       "upload-dialog-label-infoform-description": "Deskribapena",
-       "upload-dialog-label-usage-title": "Erabilera",
-       "upload-dialog-label-usage-filename": "Fitxategiaren izena",
+       "upload-process-error": "Errore bat gertatu da",
+       "upload-form-label-select-file": "Fitxategia Aukeratu",
+       "upload-form-label-infoform-title": "Xehetasunak",
+       "upload-form-label-infoform-name": "Izena",
+       "upload-form-label-infoform-description": "Deskribapena",
+       "upload-form-label-usage-title": "Erabilera",
+       "upload-form-label-usage-filename": "Fitxategiaren izena",
        "backend-fail-stream": "Ezin izan da \"$1\" fitxategiaren stream egin.",
        "backend-fail-backup": "Ezin izan da \"$1\" fitxategiaren backup egin.",
        "backend-fail-notexists": "$1 fitxategia ez da existitzen.",
        "undeletedrevisions-files": "{{PLURAL:$1|berrikuspen|berrikuspen}} eta {{PLURAL:$2|fitxategi|fitxategi}} leheneratu dira",
        "undeletedfiles": "{{PLURAL:$1|fitxategi|fitxategi}} leheneratu dira",
        "cannotundelete": "Ezabatutako birgaitzean akatsa: $1",
-       "undeletedpage": "'''$1 leheneratu egin da'''\n\n[[Special:Log/delete|Ezabaketa erregistrora]] jo azken ezabaketa eta leheneraketak ikusteko.",
-       "undelete-header": "Berriki ezabatutako orrialdeak ikusteko [[Special:Log/delete|ezabaketa erregistrora]] jo.",
+       "undeletedpage": "'''«$1» leheneratu da'''\n\nAzken ezabatze eta leheneratzeak ikusteko, jo ezazu [[Special:Log/delete|ezabaketa erregistrora]].",
+       "undelete-header": "Berriki ezabatutako orriak ikusteko, jo ezazu [[Special:Log/delete|ezabaketa erregistrora]].",
        "undelete-search-title": "Ezabatutako orrialdeak bilatu",
        "undelete-search-box": "Ezabatutako orrialdeak bilatu",
        "undelete-search-prefix": "Honela hasten diren orrialdeak erakutsi:",
        "tooltip-ca-nstab-main": "Eduki orrialdea ikusi",
        "tooltip-ca-nstab-user": "Lankide orrialdea ikusi",
        "tooltip-ca-nstab-media": "Media orrialdea ikusi",
-       "tooltip-ca-nstab-special": "Hau aparteko orrialde bat da, ezin duzu orrialdea aldatu.",
+       "tooltip-ca-nstab-special": "Hau orri berezi bat da, ezin duzu orria aldatu.",
        "tooltip-ca-nstab-project": "Proiektuaren orrialdea ikusi",
        "tooltip-ca-nstab-image": "Irudiaren orrialdea ikusi",
        "tooltip-ca-nstab-mediawiki": "Sistemaren mezua ikusi",
        "compare-rev2": "2. berrikuspena",
        "compare-submit": "Alderatu",
        "compare-invalid-title": "Zehaztutako izenburua ez dago zuzen.",
-       "compare-title-not-exists": "Zehazturiko izenburua ez da existitzen.",
+       "compare-title-not-exists": "Adierazi duzun izenburua ez da existitzen.",
        "compare-revision-not-exists": "Zehazturiko berrikuspena ez da existitzen.",
        "dberr-problems": "Barkatu! Webgune honek zailtasun teknikoak jasaten ari da.",
        "dberr-again": "Saiatu pare bat minutu itxaroten edo kargatu ezazu orrialdea berriro.",
index 886a7e1..a163ddb 100644 (file)
        "nstab-template": "الگو",
        "nstab-help": "صفحهٔ راهنما",
        "nstab-category": "رده",
+       "mainpage-nstab": "صفحه اصلی",
        "nosuchaction": "چنین عملی وجود ندارد",
        "nosuchactiontext": "عمل مشخص‌شده در نشانی اینترنتی نامجاز است.\nممکن است نشانی اینترنتی را اشتباه وارد کرده باشید یا پیوند مشکل‌داری را دنبال کرده باشید.\nهمچنین ممکن است ایرادی در نرم‌افزار استفاده‌شده در {{SITENAME}} وجود داشته باشد.",
        "nosuchspecialpage": "چنین صفحهٔ ویژه‌ای وجود ندارد",
        "changeemail-password": "گذرواژهٔ {{SITENAME}} شما:",
        "changeemail-submit": "تغییر ایمیل",
        "changeemail-throttled": "شما به مراتب برای ورود تلاش کرده‌اید.\nلطفاً پیش از آنکه دوباره تلاش کنید $1 صبر کنید.",
+       "changeemail-nochange": "لطفاً رایانامهٔ جدید و متفاوتی وارد کنید.",
        "resettokens": "بازنشانی شناساننده‌ها",
        "resettokens-text": "شما می توانید شناساننده‌ها که اجازهٔ دسترسی به برخی داده‌های خصوصی مرتبط با حسابتان را می‌دهد بازنشانی کنید.\nدر صورتی باید این کار را انجام دهید که تصادقاً آن‌ها را با کسی در میان گذاشته‌اید یا به حسابتان نفوذ شده است.",
        "resettokens-no-tokens": "هیچ شناساننده‌ای برای بازنشانی وجود ندارد.",
        "permissionserrorstext-withaction": "شما اجازهٔ $2 را به این {{PLURAL:$1|دلیل|دلایل}} ندارید:",
        "recreate-moveddeleted-warn": "<strong>هشدار: شما در حال ایجاد صفحه‌ای هستید که قبلاً حذف شده‌است.</strong>\n\nدر نظر داشته باشید که آیا ادامهٔ ویرایش این صفحه کار درستی‌است یا نه.\nسیاههٔ حذف و انتقال این صفحه در زیر نشان داده شده‌است:",
        "moveddeleted-notice": "این صفحه حذف شده‌است.\nدر زیر سیاههٔ حذف و انتقال این صفحه نمایش داده شده‌است.",
+       "moveddeleted-notice-recent": "متاسفانه صفحه قبلا حذف شده‌است (در ۲۴ ساعت اخیر) \nدلیل حذف و سیاههٔ انتقال در پائین موجود است.",
        "log-fulllog": "مشاهدهٔ سیاههٔ کامل",
        "edit-hook-aborted": "ویرایش توسط قلاب لغو شد.\nتوضیحی در این مورد داده نشد.",
        "edit-gone-missing": "امکان به‌روز کردن صفحه وجود ندارد.\nبه نظرمی‌رسد که صفحه حذف شده باشد.",
        "upload-http-error": "یک خطای اچ‌تی‌تی‌پی رخ داد: $1",
        "upload-copy-upload-invalid-domain": "بارگذاری کپی پرونده‌ها از این دامنه امکان‌پذیر نیست.",
        "upload-dialog-title": "بارگذاری پرونده",
-       "upload-dialog-error": "يک خطا اتفاق افتاد",
-       "upload-dialog-warning": "یک هشدار رخ‌داد",
        "upload-dialog-button-cancel": "لغو",
        "upload-dialog-button-done": "انجام شد",
        "upload-dialog-button-save": "ذخیره",
        "upload-dialog-button-upload": "بارگذاری",
-       "upload-dialog-label-select-file": "یک فایل انتخاب کنید",
-       "upload-dialog-label-infoform-title": "جزئیات",
-       "upload-dialog-label-infoform-name": "نام",
-       "upload-dialog-label-infoform-description": "توضیحات",
-       "upload-dialog-label-usage-title": "کاربرد",
-       "upload-dialog-label-usage-filename": "نام پرونده",
+       "upload-process-error": "يک خطا اتفاق افتاد",
+       "upload-process-warning": "یک هشدار رخ‌داد",
+       "upload-form-label-select-file": "یک فایل انتخاب کنید",
+       "upload-form-label-infoform-title": "جزئیات",
+       "upload-form-label-infoform-name": "نام",
+       "upload-form-label-infoform-description": "توضیحات",
+       "upload-form-label-usage-title": "کاربرد",
+       "upload-form-label-usage-filename": "نام پرونده",
        "backend-fail-stream": "نمی‌توان پروندهٔ $1 را ارسال کرد.",
        "backend-fail-backup": "نمی‌توان نسخهٔ پشتیبان برای پروندهٔ $1 ایجاد کرد.",
        "backend-fail-notexists": "پروندهٔ $1 وجود ندارد.",
        "api-error-badaccess-groups": "شما اجازهٔ بارگذاری پرونده‌ها را در این ویکی ندارید.",
        "api-error-badtoken": "خطای داخلی: کد امنیتی اشتباه (Bad token).",
        "api-error-copyuploaddisabled": "بارگذاری با استفاده از نشانی اینترنتی در این کارساز غیرفعال است.",
-       "api-error-duplicate": "{{PLURAL:$1|[$2 پروندهٔ دیگری]|[$2 چند پروندهٔ دیگر]}} در تارنما با محتوای یکسان وجود داشت.",
-       "api-error-duplicate-archive": "{{PLURAL:$1|[$2 پروندهٔ دیگری]|[$2 چند پروندهٔ دیگر]}} در تارنما با محتوای یکسان وجود داشت، ولی حذف {{PLURAL:$1|شده است|شده‌اند}}.",
+       "api-error-duplicate": "{{PLURAL:$1|پروندهٔ دیگری|چند پروندهٔ دیگر}} در تارنما با محتوای یکسان وجود داشت.",
+       "api-error-duplicate-archive": "{{PLURAL:$1| پروندهٔ دیگری|چند پروندهٔ دیگر}} در تارنما با محتوای یکسان وجود داشت، ولی حذف {{PLURAL:$1|شده است|شده‌اند}}.",
        "api-error-empty-file": "پرونده‌ای که شما ارسال کردید خالی بود.",
        "api-error-emptypage": "ایجاد صفحه‌های خالی مجاز نیست.",
        "api-error-fetchfileerror": "خطای داخلی: در هنگام گرفتن پرونده، یک چیزی درست پیش نرفت.",
index 79be76a..f242172 100644 (file)
        "nstab-template": "Malline",
        "nstab-help": "Ohjesivu",
        "nstab-category": "Luokka",
+       "mainpage-nstab": "Etusivu",
        "nosuchaction": "Toimintoa ei ole olemassa",
        "nosuchactiontext": "URL:ssä määritelty toiminto ei ole kelvollinen.\nOlet saattanut kirjoittaa URL:in väärin tai olet seurannut virheellistä linkkiä.\nKyseessä voi myös mahdollisesti olla virhe sivuston {{SITENAME}} käyttämässä ohjelmistossa.",
        "nosuchspecialpage": "Kyseistä toimintosivua ei ole",
        "changeemail-password": "{{SITENAME}}-salasanasi:",
        "changeemail-submit": "Muuta sähköpostiosoite",
        "changeemail-throttled": "Olet tehnyt liian monta kirjautumisyritystä.\nOdota $1 ennen kuin yrität uudelleen.",
+       "changeemail-nochange": "Anna joku toinen sähköpostiosoite.",
        "resettokens": "Uudista avaimet",
        "resettokens-text": "Tällä sivulla voit uudistaa avaimesi (''eng.'' reset tokens), jotka mahdollistavat pääsyn käyttäjätunnukseesi liittyviin tiettyihin yksityisiin tietoihin.\n\nSinun pitäisi tehdä tämä, jos olet vahingossa jakanut avaimet jonkun kanssa tai jos käyttäjätunnuksesi on vaarannettu.",
        "resettokens-no-tokens": "Avaimia ei ole uudistettavaksi.",
        "permissionserrorstext-withaction": "Sinulla ei ole oikeutta {{lcfirst:$2}} {{PLURAL:$1|seuraavasta syystä|seuraavista syistä}}:",
        "recreate-moveddeleted-warn": "'''Varoitus: Olet luomassa sellaista sivua, joka on aikaisemmin poistettu.'''\n\nHarkitse, kannattaako tätä sivua luoda uudelleen. \nAlla on tämän sivun poisto- ja siirtohistoria:",
        "moveddeleted-notice": "Tämä sivu on poistettu. Alla on tämän sivun poisto- ja siirtohistoria.",
+       "moveddeleted-notice-recent": "Valitettavasti tämä sivu on poistettu aivan äskettäin (viimeisen 24 tunnin aikana).\nAlla näkyy sivun poisto- ja siirtolokin tietoja.",
        "log-fulllog": "Näytä loki kokonaan",
        "edit-hook-aborted": "Laajennuskoodi esti muokkauksen antamatta syytä.",
        "edit-gone-missing": "Sivun päivitys ei onnistunut.\nSe on ilmeisesti poistettu.",
        "upload-http-error": "HTTP-virhe: $1",
        "upload-copy-upload-invalid-domain": "Tiedostojen tallentamista tästä verkko-osoitteesta ei ole sallittu.",
        "upload-dialog-title": "Tiedoston tallennus",
-       "upload-dialog-error": "Tapahtui virhe",
-       "upload-dialog-warning": "Sisältää varoituksen",
        "upload-dialog-button-cancel": "Peru",
        "upload-dialog-button-done": "Valmis",
        "upload-dialog-button-save": "Tallenna",
        "upload-dialog-button-upload": "Tallenna",
-       "upload-dialog-label-select-file": "Valitse tiedosto",
-       "upload-dialog-label-infoform-title": "Yksityiskohdat",
-       "upload-dialog-label-infoform-name": "Nimi",
-       "upload-dialog-label-infoform-description": "Kuvaus",
-       "upload-dialog-label-usage-title": "Käyttö",
-       "upload-dialog-label-usage-filename": "Tiedostonimi",
+       "upload-process-error": "Tapahtui virhe",
+       "upload-process-warning": "Sisältää varoituksen",
+       "upload-form-label-select-file": "Valitse tiedosto",
+       "upload-form-label-infoform-title": "Yksityiskohdat",
+       "upload-form-label-infoform-name": "Nimi",
+       "upload-form-label-infoform-description": "Kuvaus",
+       "upload-form-label-usage-title": "Käyttö",
+       "upload-form-label-usage-filename": "Tiedostonimi",
        "backend-fail-stream": "Tiedoston $1 virtauttaminen epäonnistui.",
        "backend-fail-backup": "Tiedostoa $1 ei voitu varmuuskopioida.",
        "backend-fail-notexists": "Tiedostoa $1 ei ole olemassa.",
        "filerevert-legend": "Tiedoston palautus",
        "filerevert-intro": "Olet palauttamassa takaisin tiedostoa '''[[Media:$1|$1]]''' [$4 versioon, joka luotiin $2 kello $3].",
        "filerevert-comment": "Syy:",
-       "filerevert-defaultcomment": "Palautettiin takaisin versioon, joka luotiin $1 kello $2 (UTC)",
+       "filerevert-defaultcomment": "Palautettiin takaisin versioon, joka tehtiin $1 kello $2 ($3)",
        "filerevert-submit": "Suorita palauttaminen",
        "filerevert-success": "'''[[Media:$1|$1]]''' on palautettu takaisin [$4 versioon, joka luotiin $2 kello $3].",
        "filerevert-badversion": "Tiedostosta ei ole luotu versiota kyseisellä ajan hetkellä.",
        "api-error-badaccess-groups": "Sinulla ei ole oikeutta tallentaa tiedostoja tähän wikiin.",
        "api-error-badtoken": "Sisäinen virhe: virheellinen tarkistussumma.",
        "api-error-copyuploaddisabled": "Tallentaminen URL-osoitteesta ei ole käytössä.",
-       "api-error-duplicate": "Samansisältöisiä tiedostoja löytyi {{PLURAL:$1|[$2 yksi kappale]|[$2 useampia kappaleita]}}.",
-       "api-error-duplicate-archive": "Sivustolla oli aiemmin {{PLURAL:$1|[$2 toinen samansisältöinen tiedosto]|[$2 toisia samansisältöisiä tiedostoja]}}, mutta {{PLURAL:$1|se|ne}} poistettiin.",
+       "api-error-duplicate": "Samansisältöisiä tiedostoja löytyi {{PLURAL:$1|[yksi kappale]|[useampia kappaleita]}}.",
+       "api-error-duplicate-archive": "Sivustolla oli aiemmin {{PLURAL:$1|toinen samansisältöinen tiedosto|toisia samansisältöisiä tiedostoja}}, mutta {{PLURAL:$1|se|ne}} poistettiin.",
        "api-error-empty-file": "Määrittämäsi tiedosto on tyhjä.",
        "api-error-emptypage": "Ei ole sallittua luoda uutta, tyhjää sivua.",
        "api-error-fetchfileerror": "Sisäinen virhe: Jotakin meni pieleen kun tiedostoa haettiin.",
index 7eb3b97..3ad232c 100644 (file)
        "createacct-captcha": "Contrôle de sécurité",
        "createacct-imgcaptcha-ph": "Entrez le texte que vous voyez ci-dessus",
        "createacct-submit": "Créez votre compte",
-       "createacct-another-submit": "Créer un autre compte",
+       "createacct-another-submit": "Créer le compte",
        "createacct-benefit-heading": "{{SITENAME}} est écrit par des gens comme vous.",
        "createacct-benefit-body1": "{{PLURAL:$1|modification|modifications}}",
        "createacct-benefit-body2": "{{PLURAL:$1|article|articles}}",
        "group-bot": "Robots",
        "group-sysop": "Administrateurs",
        "group-bureaucrat": "Bureaucrates",
-       "group-suppress": "Superviseurs",
+       "group-suppress": "Limitateurs",
        "group-all": "(tous)",
        "group-user-member": "{{GENDER:$1|utilisateur|utilisatrice}}",
        "group-autoconfirmed-member": "{{GENDER:$1|utilisateur enregistré|utilisatrice enregistrée}}",
        "group-bot-member": "{{GENDER:$1|robot}}",
        "group-sysop-member": "{{GENDER:$1|administrateur|administratrice}}",
        "group-bureaucrat-member": "{{GENDER:$1|bureaucrate}}",
-       "group-suppress-member": "{{GENDER:$1|superviseur|superviseuse}}",
+       "group-suppress-member": "{{GENDER:$1|limitateur|limitatrice}}",
        "grouppage-user": "{{ns:project}}:Utilisateurs",
        "grouppage-autoconfirmed": "{{ns:project}}:Utilisateurs enregistrés",
        "grouppage-bot": "{{ns:project}}:Robots",
        "grouppage-sysop": "{{ns:project}}:Administrateurs",
        "grouppage-bureaucrat": "{{ns:project}}:Bureaucrates",
-       "grouppage-suppress": "{{ns:project}}:Superviseurs",
+       "grouppage-suppress": "{{ns:project}}:Suppress",
        "right-read": "Lire les pages",
        "right-edit": "Modifier les pages",
        "right-createpage": "Créer des pages (qui ne sont pas des pages de discussion)",
        "upload-http-error": "Une erreur HTTP est survenue : $1",
        "upload-copy-upload-invalid-domain": "La copie des téléversements n’est pas disponible depuis ce domaine.",
        "upload-dialog-title": "Téléverser un fichier",
-       "upload-dialog-error": "Une erreur est survenue",
-       "upload-dialog-warning": "Un avertissement s’est produit",
        "upload-dialog-button-cancel": "Annuler",
        "upload-dialog-button-done": "Fait",
        "upload-dialog-button-save": "Enregistrer",
        "upload-dialog-button-upload": "Téléverser",
-       "upload-dialog-label-select-file": "Sélectionner un fichier",
-       "upload-dialog-label-infoform-title": "Détails",
-       "upload-dialog-label-infoform-name": "Nom",
-       "upload-dialog-label-infoform-description": "Description",
-       "upload-dialog-label-usage-title": "Utilisation",
-       "upload-dialog-label-usage-filename": "Nom du fichier",
+       "upload-process-error": "Une erreur est survenue",
+       "upload-process-warning": "Un avertissement s’est produit",
+       "upload-form-label-select-file": "Sélectionner un fichier",
+       "upload-form-label-infoform-title": "Détails",
+       "upload-form-label-infoform-name": "Nom",
+       "upload-form-label-infoform-description": "Description",
+       "upload-form-label-usage-title": "Utilisation",
+       "upload-form-label-usage-filename": "Nom du fichier",
        "backend-fail-stream": "Impossible de lire le fichier $1.",
        "backend-fail-backup": "Impossible de sauvegarder le fichier $1.",
        "backend-fail-notexists": "Le fichier $1 n’existe pas.",
        "filerevert-legend": "Rétablir le fichier",
        "filerevert-intro": "Vous êtes sur le point de rétablir le fichier '''[[Media:$1|$1]]''' à la [$4 version du $2 à $3].",
        "filerevert-comment": "Motif :",
-       "filerevert-defaultcomment": "Version du $1 à $2 rétablie",
+       "filerevert-defaultcomment": "Retour sur la version du $2, $1 ($3)",
        "filerevert-submit": "Rétablir",
        "filerevert-success": "'''[[Media:$1|$1]]''' a été rétabli à [$4 la version du $2 à $3].",
        "filerevert-badversion": "Il n'y a pas localement de version antérieure du fichier qui porte la date indiquée.",
        "nopagetext": "La page cible que vous avez indiquée n'existe pas.",
        "pager-newer-n": "{{PLURAL:$1|plus récente|$1 plus récentes}}",
        "pager-older-n": "{{PLURAL:$1|plus ancienne|$1 plus anciennes}}",
-       "suppress": "Superviser",
+       "suppress": "Supprimer",
        "querypage-disabled": "Cette page spéciale est désactivée pour des raisons de performances.",
        "apihelp": "Aide de l’API",
        "apihelp-no-such-module": "Le module « $1 » est introuvable.",
        "logentry-newusers-byemail": "Le compte utilisateur $3 {{GENDER:$2|a été créé}} par $1 et le mot de passe a été envoyé par courriel",
        "logentry-newusers-autocreate": "Le compte $1 {{GENDER:$2|a été créé}} automatiquement",
        "logentry-protect-move_prot": "$1 {{GENDER:$2|a déplacé}} les paramètres de protection de $4 vers $3",
+       "logentry-protect-unprotect": "$1 {{GENDER:$2|a supprimé}} la protection de $3",
+       "logentry-protect-protect": "$1 {{GENDER:$2|a protégé}} $3 $4",
+       "logentry-protect-protect-cascade": "$1 {{GENDER:$2|a protégé}} $3 $4 [protection en cascade]",
+       "logentry-protect-modify": "$1 {{GENDER:2|a modifié}} le niveau de protection de $3 $4",
+       "logentry-protect-modify-cascade": "$1 {{GENDER:2|a modifié}} le niveau de protection de $3 $4 [protection en cascade]",
        "logentry-rights-rights": "$1 {{GENDER:$2|a modifié}} l'appartenance au groupe pour $3 de $4 à $5",
        "logentry-rights-rights-legacy": "$1 {{GENDER:$2|a modifié}} l'appartenance au groupe pour $3",
        "logentry-rights-autopromote": "$1 {{GENDER:$2|a été promu}} automatiquement de $4 à $5",
index 39c9410..96f3927 100644 (file)
        "api-error-badtoken": "Èrror de dedens : crouyo « jeton ».",
        "api-error-copyuploaddisabled": "Los tèlèchargements per URL sont dèsactivâs sur cél sèrvor.",
        "api-error-duplicate": "Y at {{PLURAL:$1|[$2 un ôtro fichiér]|[$2 d’ôtros fichiérs]}} ja sur lo seto avouéc lo mémo contegnu.",
-       "api-error-duplicate-archive": "Y avéve {{PLURAL:$1|[$2 un ôtro fichiér]|[$2 d’ôtros fichiérs]}} ja sur lo seto avouéc lo mémo contegnu, mas {{PLURAL:$1|il at étâ suprimâ|ils ont étâ suprimâs}}.",
+       "api-error-duplicate-archive": "Y avéve {{PLURAL:$1|un ôtro fichiér|d’ôtros fichiérs}} ja sur lo seto avouéc lo mémo contegnu, mas {{PLURAL:$1|il at étâ suprimâ|ils ont étâ suprimâs}}.",
        "api-error-empty-file": "Lo fichiér que vos éd somês ére vouedo.",
        "api-error-emptypage": "La crèacion de pâges novèles vouedes est pas ôtorisâ.",
        "api-error-fetchfileerror": "Èrror de dedens : quârque-ren s’est mâl passâ pendent la rècupèracion du fichiér.",
index 09d69d8..4d693bf 100644 (file)
@@ -8,7 +8,8 @@
                        "Murma174",
                        "Pyt",
                        "아라",
-                       "Purodha"
+                       "Purodha",
+                       "Macofe"
                ]
        },
        "tog-underline": "Ferwisangen onerstrik:",
        "upload-http-error": "Diar as en HTTP-feeler mä: $1",
        "upload-copy-upload-invalid-domain": "Kopiin faan datein kön faan detdiar domeen ei huuchschüürd wurd.",
        "upload-dialog-title": "Datei huuchschüür",
-       "upload-dialog-error": "Diar as wat skiaf gingen",
-       "upload-dialog-warning": "Diar as en wäärnang kimen",
        "upload-dialog-button-cancel": "Ufbreeg",
        "upload-dialog-button-done": "Klaar",
        "upload-dialog-button-save": "Seekre",
        "upload-dialog-button-upload": "Huuchschüür",
-       "upload-dialog-label-select-file": "Datei ütjschük",
-       "upload-dialog-label-infoform-title": "Enkelthaiden",
-       "upload-dialog-label-infoform-name": "Nööm",
-       "upload-dialog-label-infoform-description": "Beskriiwang",
-       "upload-dialog-label-usage-title": "Brük",
-       "upload-dialog-label-usage-filename": "Dateinööm",
+       "upload-process-error": "Diar as wat skiaf gingen",
+       "upload-process-warning": "Diar as en wäärnang kimen",
+       "upload-form-label-select-file": "Datei ütjschük",
+       "upload-form-label-infoform-title": "Enkelthaiden",
+       "upload-form-label-infoform-name": "Nööm",
+       "upload-form-label-infoform-description": "Beskriiwang",
+       "upload-form-label-usage-title": "Brük",
+       "upload-form-label-usage-filename": "Dateinööm",
        "backend-fail-stream": "Det datei $1 küd ei auerdraanj wurd.",
        "backend-fail-backup": "Det datei $1 küd ei seekert wurd.",
        "backend-fail-notexists": "Det datei $1 jaft at ei.",
        "api-error-badtoken": "Intern feeler: Token as ferkiard.",
        "api-error-copyuploaddisabled": "Det huuchschüüren auer URL as üüb didiar server ei aktiif.",
        "api-error-duplicate": "Uun det Wiki {{PLURAL:$1|as al [$2 en ööder datei]|san al [$2 muar datein]}} mä detsalew banen.",
-       "api-error-duplicate-archive": "Diar wiar al {{PLURAL:$1|[$2 ööder datei]|[$2 ööder datein]}} mä detsalew banen. {{PLURAL:$1|Hat as |Jo san}} oober stregen wurden.",
+       "api-error-duplicate-archive": "Diar wiar al {{PLURAL:$1|ööder datei|ööder datein}} mä detsalew banen. {{PLURAL:$1|Hat as |Jo san}} oober stregen wurden.",
        "api-error-empty-file": "Det datei, wat dü huuchschüürd heest, as leesag.",
        "api-error-emptypage": "Dü mutst nian leesag sidjen nei iinstel.",
        "api-error-fetchfileerror": "Intern feeler: Bi't ufrepen faan det datei as wat skiaf gingen.",
index f42dd11..7d652a2 100644 (file)
        "api-error-badtoken": "Mearachd taobh a-staigh: Droch thòcan.",
        "api-error-copyuploaddisabled": "Tha luchdadh suas le URL à comas air an fhrithealaiche seo.",
        "api-error-duplicate": "Tha {{PLURAL:$1|[$2 faidhle eile]|are [$2 faidhlichean eile]}} air an làrach seo mar-thà sa bheil an aon susbaint.",
-       "api-error-duplicate-archive": "Tha {{PLURAL:$1|[$2 faidhle eile]|are [$2 faidhlichean eile]}} air an làrach seo mar-thà sa bheil an aon susbaint ach chaidh {{PLURAL:$1|a sguabadh|an sguabadh}} às.",
+       "api-error-duplicate-archive": "Tha {{PLURAL:$1|faidhle eile|faidhlichean eile}} air an làrach seo mar-thà sa bheil an aon susbaint ach chaidh {{PLURAL:$1|a sguabadh|an sguabadh}} às.",
        "api-error-empty-file": "Tha am faidhle a chuir thu a-null falamh.",
        "api-error-emptypage": "Chan fhaodar duilleagan falamh ùra a chruthachadh.",
        "api-error-fetchfileerror": "Mearachd taobh a-staigh: Chaidh rudeigin cearr le faighinn an fhaidhle.",
index 6d153b1..18d854f 100644 (file)
        "nstab-template": "Modelo",
        "nstab-help": "Páxina de axuda",
        "nstab-category": "Categoría",
+       "mainpage-nstab": "Páxina principal",
        "nosuchaction": "Non existe esa acción",
        "nosuchactiontext": "A acción especificada polo enderezo URL é inválida.\nPode que non o escribise ben ou que seguise unha ligazón incorrecta.\nIsto tamén podería indicar un erro en {{SITENAME}}.",
        "nosuchspecialpage": "Non existe esa páxina especial",
        "changeemail-password": "O seu contrasinal en {{SITENAME}}:",
        "changeemail-submit": "Cambiar o correo electrónico",
        "changeemail-throttled": "Fixo demasiados intentos de acceder ao sistema.\nPor favor, agarde $1 antes de probar outra vez.",
+       "changeemail-nochange": "Por favor, indique unha nova dirección de correo diferente.",
        "resettokens": "Restablecer os pases",
        "resettokens-text": "Aquí pode restablecer os pases que permiten acceder a certos datos privados asociados á súa conta.\n\nDebería facelo se os compartiu accidentalmente con alguén ou se a súa conta foi comprometida.",
        "resettokens-no-tokens": "Non hai ningún pase que restablecer.",
        "upload-http-error": "Produciuse un erro HTTP: $1",
        "upload-copy-upload-invalid-domain": "A copia de cargas non está dispoñible neste dominio.",
        "upload-dialog-title": "Subir un ficheiro",
-       "upload-dialog-error": "Houbo un erro",
-       "upload-dialog-warning": "Produciuse unha advertencia",
        "upload-dialog-button-cancel": "Cancelar",
        "upload-dialog-button-done": "Feito",
        "upload-dialog-button-save": "Gardar",
        "upload-dialog-button-upload": "Subir",
-       "upload-dialog-label-select-file": "Seleccionar un ficheiro",
-       "upload-dialog-label-infoform-title": "Detalles",
-       "upload-dialog-label-infoform-name": "Nome",
-       "upload-dialog-label-infoform-description": "Descrición",
-       "upload-dialog-label-usage-title": "Uso",
-       "upload-dialog-label-usage-filename": "Nome do ficheiro",
+       "upload-process-error": "Houbo un erro",
+       "upload-process-warning": "Produciuse unha advertencia",
+       "upload-form-label-select-file": "Seleccionar un ficheiro",
+       "upload-form-label-infoform-title": "Detalles",
+       "upload-form-label-infoform-name": "Nome",
+       "upload-form-label-infoform-description": "Descrición",
+       "upload-form-label-usage-title": "Uso",
+       "upload-form-label-usage-filename": "Nome do ficheiro",
        "backend-fail-stream": "Non se puido transmitir o ficheiro \"$1\".",
        "backend-fail-backup": "Non se puido facer unha copia de seguridade do ficheiro \"$1\".",
        "backend-fail-notexists": "O ficheiro \"$1\" non existe.",
        "api-error-badaccess-groups": "Non ten os permisos necesarios para cargar ficheiros neste wiki.",
        "api-error-badtoken": "Erro interno: Pase incorrecto.",
        "api-error-copyuploaddisabled": "As cargas mediante URL están desactivadas neste servidor.",
-       "api-error-duplicate": "Xa hai {{PLURAL:$1|[$2 outro ficheiro]|[$2 outros ficheiros]}} no wiki co mesmo contido",
-       "api-error-duplicate-archive": "Había {{PLURAL:$1|[$2 outro ficheiro]|[$2 outros ficheiros]}} no sitio co mesmo contido, pero {{PLURAL:$1|foi borrado|foron borrados}}.",
+       "api-error-duplicate": "Xa hai {{PLURAL:$1|outro ficheiro| outros ficheiros}} no wiki co mesmo contido.",
+       "api-error-duplicate-archive": "Había {{PLURAL:$1|outro ficheiro|outros ficheiros}} no sitio co mesmo contido, pero {{PLURAL:$1|foi borrado|foron borrados}}.",
        "api-error-empty-file": "O ficheiro que enviou estaba baleiro.",
        "api-error-emptypage": "Non está permitida a creación de páxinas novas que estean baleiras.",
        "api-error-fetchfileerror": "Erro interno: Houbo un problema ao buscar o ficheiro.",
index 3ea206e..f1eea2e 100644 (file)
@@ -19,7 +19,8 @@
                        "Urhixidur",
                        "לערי ריינהארט",
                        "80686",
-                       "아라"
+                       "아라",
+                       "Macofe"
                ]
        },
        "tog-underline": "Links unterstryche:",
        "upload-http-error": "E HTTP-Fähler isch ufträtte: $1",
        "upload-copy-upload-invalid-domain": "As Kopi uffeladbari Dateie sin iber die Domain nit verfiegbar.",
        "upload-dialog-title": "Datei ufelade",
-       "upload-dialog-error": "Es het e Fähler ’gä",
-       "upload-dialog-warning": "Es het e Warnig ’gä",
        "upload-dialog-button-cancel": "Abbräche",
        "upload-dialog-button-done": "Fertig",
        "upload-dialog-button-save": "Spychere",
        "upload-dialog-button-upload": "Ufelade",
-       "upload-dialog-label-select-file": "Datei ussueche",
-       "upload-dialog-label-infoform-title": "Details",
-       "upload-dialog-label-infoform-name": "Name",
-       "upload-dialog-label-infoform-description": "Beschrybig",
-       "upload-dialog-label-usage-title": "Verwändig",
-       "upload-dialog-label-usage-filename": "Dateiname",
+       "upload-process-error": "Es het e Fähler ’gä",
+       "upload-process-warning": "Es het e Warnig ’gä",
+       "upload-form-label-select-file": "Datei ussueche",
+       "upload-form-label-infoform-title": "Details",
+       "upload-form-label-infoform-name": "Name",
+       "upload-form-label-infoform-description": "Beschrybig",
+       "upload-form-label-usage-title": "Verwändig",
+       "upload-form-label-usage-filename": "Dateiname",
        "backend-fail-stream": "D Datei $1 het nit chenne ibertrait wäre.",
        "backend-fail-backup": "D Datei $1 het nit chenne gsicheret wäre.",
        "backend-fail-notexists": "D Datei $1 git s nit.",
        "api-error-badtoken": "Intärne Fähler: Dr Token isch fählerhaft.",
        "api-error-copyuploaddisabled": "S Uffelade iber e URL isch uf däm Server deaktiviert.",
        "api-error-duplicate": "S git im Wiki scho {{PLURAL:$1|[$2 ei anderi Datei]|[$2 anderi Dateie]}} mit em glyche Inhalt.",
-       "api-error-duplicate-archive": "Es {{PLURAL:$1|isch scho [$2 e andri Datei]|sin scho [$2 anderi Dateie]}} mit em glyche Inhalt vorhande gsi. {{PLURAL:$1|Si isch|Si sin}} allerdings glöscht worde.",
+       "api-error-duplicate-archive": "Es {{PLURAL:$1|isch scho e andri Datei|sin scho anderi Dateie}} mit em glyche Inhalt vorhande gsi. {{PLURAL:$1|Si isch|Si sin}} allerdings glöscht worde.",
        "api-error-empty-file": "D Datei, wu Du uffeglade hesch, isch läär.",
        "api-error-emptypage": "S isch nit erlaubt, neji lääri Syte aazlege.",
        "api-error-fetchfileerror": "Intärne Fähler: Bim Abruefe vu dr Datei isch e Fähler ufträtte.",
index d476e5a..0bd1984 100644 (file)
        "upload-dialog-button-done": "સંપન્ન",
        "upload-dialog-button-save": "સાચવો",
        "upload-dialog-button-upload": "ચઢાવો",
-       "upload-dialog-label-select-file": "ફાઈલ પસંદ કરો",
-       "upload-dialog-label-infoform-title": "વિગતો",
-       "upload-dialog-label-infoform-name": "નામ",
-       "upload-dialog-label-infoform-description": "વર્ણન",
-       "upload-dialog-label-usage-title": "વપરાશ",
-       "upload-dialog-label-usage-filename": "ફાઈલનું નામ",
+       "upload-form-label-select-file": "ફાઈલ પસંદ કરો",
+       "upload-form-label-infoform-title": "વિગતો",
+       "upload-form-label-infoform-name": "નામ",
+       "upload-form-label-infoform-description": "વર્ણન",
+       "upload-form-label-usage-title": "વપરાશ",
+       "upload-form-label-usage-filename": "ફાઈલનું નામ",
        "backend-fail-stream": "ફાઈલ $1 ને લાવી ન શકાઈ.",
        "backend-fail-backup": "ફાઈલ $1 ની પ્રત ન સાચવી શકાઈ.",
        "backend-fail-notexists": "ફાઈલ $1 ઉપલબ્ધ નથી.",
index 3a38c7a..622996e 100644 (file)
        "createacct-captcha": "בדיקת אבטחה",
        "createacct-imgcaptcha-ph": "יש להקליד את הטקסט המופיע למעלה",
        "createacct-submit": "יצירת החשבון שלך",
-       "createacct-another-submit": "יצירת חשבון אחר",
+       "createacct-another-submit": "יצירת חשבון",
        "createacct-benefit-heading": "אנשים כמוך יוצרים את {{SITENAME}}.",
        "createacct-benefit-body1": "{{PLURAL:$1|עריכה|עריכות}}",
        "createacct-benefit-body2": "{{PLURAL:$1|דף|דפים}}",
        "upload-http-error": "התרחשה שגיאת HTTP‏: $1",
        "upload-copy-upload-invalid-domain": "העלאת קבצים משרת זה אינה אפשרית.",
        "upload-dialog-title": "העלאת קובץ",
-       "upload-dialog-error": "אירעה שגיאה",
-       "upload-dialog-warning": "אירעה אזהרה",
        "upload-dialog-button-cancel": "ביטול",
        "upload-dialog-button-done": "בוצע",
        "upload-dialog-button-save": "שמירה",
        "upload-dialog-button-upload": "העלאה",
-       "upload-dialog-label-select-file": "בחירת קובץ",
-       "upload-dialog-label-infoform-title": "פרטים",
-       "upload-dialog-label-infoform-name": "שם",
-       "upload-dialog-label-infoform-description": "תיאור",
-       "upload-dialog-label-usage-title": "שימושים",
-       "upload-dialog-label-usage-filename": "שם הקובץ",
+       "upload-process-error": "אירעה שגיאה",
+       "upload-process-warning": "אירעה אזהרה",
+       "upload-form-label-select-file": "בחירת קובץ",
+       "upload-form-label-infoform-title": "פרטים",
+       "upload-form-label-infoform-name": "שם",
+       "upload-form-label-infoform-description": "תיאור",
+       "upload-form-label-usage-title": "שימושים",
+       "upload-form-label-usage-filename": "שם הקובץ",
        "backend-fail-stream": "לא הייתה אפשרות להזרים את הקובץ \"$1\".",
        "backend-fail-backup": "לא הייתה אפשרות לגבות את הקובץ \"$1\".",
        "backend-fail-notexists": "הקובץ \"$1\" אינו קיים.",
        "filerevert-legend": "שחזור קובץ",
        "filerevert-intro": "אתם עומדים לשחזר את הקובץ '''[[Media:$1|$1]]''' ל[$4 גרסה מ־$3, $2].",
        "filerevert-comment": "סיבה:",
-       "filerevert-defaultcomment": "שוחזר לגרסה מ־$2, $1",
+       "filerevert-defaultcomment": "שוחזר לגרסה מ־$2, $1 ($3)",
        "filerevert-submit": "שחזור",
        "filerevert-success": "<strong>[[Media:$1|$1]]</strong> שוחזר ל[$4 גרסה מ־$3, $2].",
        "filerevert-badversion": "אין גרסה מקומית קודמת של הקובץ שהועלתה בתאריך המבוקש.",
        "emailccsubject": "העתק של הודעתך למשתמש $1: $2",
        "emailsent": "הדואר נשלח",
        "emailsenttext": "הודעת הדואר האלקטרוני שלך נשלחה.",
-       "emailuserfooter": "דואר זה נשלח על־ידי $1 ל{{GRAMMAR:תחילית|$2}} באמצעות פעולת \"{{int:emailuser}}\" ב{{GRAMMAR:תחילית|{{SITENAME}}}}.",
+       "emailuserfooter": "$1 {{GENDER:$1|שלח|שלחה}} את הדוא\"ל הזה ל{{GRAMMAR:תחילית|$2}} באמצעות פעולת \"{{int:emailuser}}\" ב{{GRAMMAR:תחילית|{{SITENAME}}}}.",
        "usermessage-summary": "השארת הודעת מערכת.",
        "usermessage-editor": "שולח הודעות המערכת",
        "watchlist": "רשימת המעקב",
        "logentry-newusers-byemail": "חשבון המשתמש $3 נוצר על־ידי $1 והסיסמה נשלחה בדוא\"ל",
        "logentry-newusers-autocreate": "חשבון המשתמש $1 {{GENDER:$2|נוצר}} אוטומטית",
        "logentry-protect-move_prot": "$1 {{GENDER:$2|העביר|העבירה}} את הגדרות ההגנה מהדף $4 אל הדף $3",
+       "logentry-protect-unprotect": "$1 {{GENDER:$2|הסיר|הסירה}} את ההגנה מהדף $3",
+       "logentry-protect-protect": "$1 {{GENDER:$2|הגן|הגנה}} על הדף $3 $4",
+       "logentry-protect-protect-cascade": "$1 {{GENDER:$2|הגן|הגנה}} על הדף $3 $4 [מדורג]",
+       "logentry-protect-modify": "$1 {{GENDER:$2|שינה|שינתה}} את רמת ההגנה של הדף $3 $4",
+       "logentry-protect-modify-cascade": "$1 {{GENDER:$2|שינה|שינתה}} את רמת ההגנה של הדף $3 $4 [מדורג]",
        "logentry-rights-rights": "$1 {{GENDER:$2|שינה|שינתה}} את ההרשאות של $3 מ{{GRAMMAR:תחילית|$4}} ל{{GRAMMAR:תחילית|$5}}&rlm;",
        "logentry-rights-rights-legacy": "$1 {{GENDER:$2|שינה|שינתה}} את ההרשאות של $3&rlm;",
        "logentry-rights-autopromote": "$1 קודם אוטומטית מ{{GRAMMAR:תחילית|$4}} ל{{GRAMMAR:תחילית|$5}}",
index 7230388..d447208 100644 (file)
@@ -2,7 +2,8 @@
        "@metadata": {
                "authors": [
                        "Paul Beppler",
-                       "Midnight Gambler"
+                       "Midnight Gambler",
+                       "Macofe"
                ]
        },
        "tog-underline": "Links (Verbinnunge) unnerstreiche:",
        "api-error-badtoken": "Interner Fehler: Der Token ist fehlerhaft.",
        "api-error-copyuploaddisabled": "Das Hochloode doorrich URL woard uff dem Server deaktiviert.",
        "api-error-duplicate": "Do gebts im Wiki schon {{PLURAL:$1|[$2 en anner Datei]|[$2 mehrre andere Dateie]}} mit dem gleiche Inhalt.",
-       "api-error-duplicate-archive": "Es {{PLURAL:$1|war schon [$2 annre Datei]|woore schon [$2 annre Dateie]}} mit dem gleiche Inhalt voarhand. {{PLURAL:$1|Sie woard|Sie woorre}} awer abgewischt.",
+       "api-error-duplicate-archive": "Es {{PLURAL:$1|war schon annre Datei|woore schon annre Dateie}} mit dem gleiche Inhalt voarhand. {{PLURAL:$1|Sie woard|Sie woorre}} awer abgewischt.",
        "api-error-empty-file": "Die hochgeloodne Datei woor leer.",
        "api-error-emptypage": "Es ist net erlaubt, neie leere Seite erstelle.",
        "api-error-fetchfileerror": "Interner Fehler: Bei dem Datei abrufe ist en Fehler uffgetret.",
index ac011ba..8779d9c 100644 (file)
        "api-error-badtoken": "Belső hiba: hibás token.",
        "api-error-copyuploaddisabled": "Az URL-címes feltöltés nem engedélyezett ezen a kiszolgálón.",
        "api-error-duplicate": "Már van {{PLURAL:$1|egy|néhány}} [$2 másik fájl] az oldalon ugyanilyen tartalommal",
-       "api-error-duplicate-archive": "Az oldalon {{PLURAL:$1|szerepelt|szerepeltek}} [$2 más {{PLURAL:$1|fájl|fájlok}}] is ugyanezzel a tartalommal, de törölve {{PLURAL:$1|lett|lettek}}.",
+       "api-error-duplicate-archive": "Az oldalon {{PLURAL:$1|szerepelt|szerepeltek}} más {{PLURAL:$1|fájl|fájlok}} is ugyanezzel a tartalommal, de törölve {{PLURAL:$1|lett|lettek}}.",
        "api-error-empty-file": "Az általad elküldött fájl üres volt.",
        "api-error-emptypage": "Új, üres lap létrehozása nem engedélyezett.",
        "api-error-fetchfileerror": "Belső hiba: valami baj történt a fájl beolvasása közben.",
index 89aa4b1..84040f4 100644 (file)
        "nstab-template": "Patrono",
        "nstab-help": "Pagina de adjuta",
        "nstab-category": "Categoria",
+       "mainpage-nstab": "Pagina principal",
        "nosuchaction": "Non existe tal action",
        "nosuchactiontext": "Le action specificate in le adresse URL non es valide.\nEs possibile que tu ha mal entrate le URL o sequite un ligamine incorrecte.\nIsto poterea equalmente indicar un defecto in le software usate per {{SITENAME}}.",
        "nosuchspecialpage": "Pagina special invalide",
        "createacct-captcha": "Controlo de securitate",
        "createacct-imgcaptcha-ph": "Scribe le texto que tu vide hic supra",
        "createacct-submit": "Crear tu conto",
-       "createacct-another-submit": "Crear un altere conto",
+       "createacct-another-submit": "Crear conto",
        "createacct-benefit-heading": "{{SITENAME}} es facite per gente como tu.",
        "createacct-benefit-body1": "{{PLURAL:$1|modification|modificationes}}",
        "createacct-benefit-body2": "{{PLURAL:$1|pagina|paginas}}",
        "permissionserrorstext-withaction": "Tu non ha le permission de $2, pro le sequente {{PLURAL:$1|motivo|motivos}}:",
        "recreate-moveddeleted-warn": "'''Attention: Tu es sur le puncto de recrear un pagina que ha essite delite anteriormente.'''\n\nTu deberea considerar si il es appropriate continuar a modificar iste pagina.\nEcce le registro de deletiones e de renominationes pro iste pagina:",
        "moveddeleted-notice": "Iste pagina ha essite delite.\nIn basso se revela le registro de deletiones e de modificationes del pagina pro ulterior informationes.",
+       "moveddeleted-notice-recent": "Regrettabilemente iste pagina ha essite delite (in le ultime 24 horas).\nLe registro de deletion e renomination pro le pagina es fornite hic infra pro vostre information.",
        "log-fulllog": "Vider le registro complete",
        "edit-hook-aborted": "Modification abortate per un extension.\nNulle explication disponibile.",
        "edit-gone-missing": "Impossibile actualisar le pagina.\nPare que illo ha essite delite.",
        "group-bot": "Bots",
        "group-sysop": "Administratores",
        "group-bureaucrat": "Bureaucrates",
-       "group-suppress": "Supervisores",
+       "group-suppress": "Suppressores",
        "group-all": "(totes)",
        "group-user-member": "{{GENDER:$1|usator|usatrice|usator}}",
        "group-autoconfirmed-member": "{{GENDER:$1|usator|usatrice|usator}} autoconfirmate",
        "group-bot-member": "{{GENDER:$1|robot}}",
        "group-sysop-member": "{{GENDER:$1|administrator|administratrice|administrator}}",
        "group-bureaucrat-member": "{{GENDER:$1|bureaucrate}}",
-       "group-suppress-member": "{{GENDER:$1|supervisor|supervisora}}",
+       "group-suppress-member": "{{GENDER:$1|suppressor}}",
        "grouppage-user": "{{ns:project}}:Usatores",
        "grouppage-autoconfirmed": "{{ns:project}}:Usatores autoconfirmate",
        "grouppage-bot": "{{ns:project}}:Bots",
        "grouppage-sysop": "{{ns:project}}:Administratores",
        "grouppage-bureaucrat": "{{ns:project}}:Bureaucrates",
-       "grouppage-suppress": "{{ns:project}}:Supervisores",
+       "grouppage-suppress": "{{ns:project}}:Suppressores",
        "right-read": "Leger paginas",
        "right-edit": "Modificar paginas",
        "right-createpage": "Crear paginas (non discussion)",
        "upload-http-error": "Un error HTTP occurreva: $1",
        "upload-copy-upload-invalid-domain": "Le incargamento de copias non es disponibile ab iste dominio.",
        "upload-dialog-title": "Incargar file",
-       "upload-dialog-error": "Un error ha occurrite",
-       "upload-dialog-warning": "Un advertimento se ha producite",
        "upload-dialog-button-cancel": "Cancellar",
        "upload-dialog-button-done": "Facite",
        "upload-dialog-button-save": "Salveguardar",
        "upload-dialog-button-upload": "Incargar",
-       "upload-dialog-label-select-file": "Seliger file",
-       "upload-dialog-label-infoform-title": "Detalios",
-       "upload-dialog-label-infoform-name": "Nomine",
-       "upload-dialog-label-infoform-description": "Description",
-       "upload-dialog-label-usage-title": "Uso",
-       "upload-dialog-label-usage-filename": "Nomine del file",
+       "upload-process-error": "Un error ha occurrite",
+       "upload-process-warning": "Un advertimento se ha producite",
+       "upload-form-label-select-file": "Seliger file",
+       "upload-form-label-infoform-title": "Detalios",
+       "upload-form-label-infoform-name": "Nomine",
+       "upload-form-label-infoform-description": "Description",
+       "upload-form-label-usage-title": "Uso",
+       "upload-form-label-usage-filename": "Nomine del file",
        "backend-fail-stream": "Non poteva transmitter le file $1.",
        "backend-fail-backup": "Non poteva facer un copia de reserva del file $1.",
        "backend-fail-notexists": "Le file $1 non existe.",
        "filerevert-legend": "Reverter file",
        "filerevert-intro": "Tu reverte '''[[Media:$1|$1]]''' al [$4 version del $3 a $2].",
        "filerevert-comment": "Motivo:",
-       "filerevert-defaultcomment": "Revertite al version del $2 a $1",
+       "filerevert-defaultcomment": "Revertite al version del $2, $1 ($3)",
        "filerevert-submit": "Reverter",
        "filerevert-success": "'''[[Media:$1|$1]]''' ha essite revertite al [$4 version del $3 a $2].",
        "filerevert-badversion": "Non existe un version local anterior de iste file con le data e hora providite.",
        "nopagetext": "Le pagina de destination que tu ha specificate non existe.",
        "pager-newer-n": "{{PLURAL:$1|1 plus recente|$1 plus recente}}",
        "pager-older-n": "{{PLURAL:$1|1 minus recente|$1 minus recente}}",
-       "suppress": "Supervisor",
+       "suppress": "Supprimer",
        "querypage-disabled": "Iste pagina special es disactivate pro evitar de supercargar le systema.",
        "apihelp": "Adjuta con le API",
        "apihelp-no-such-module": "Modulo \"$1\" non trovate.",
        "emailccsubject": "Copia de tu message a $1: $2",
        "emailsent": "E-mail inviate",
        "emailsenttext": "Tu message de e-mail ha essite inviate.",
-       "emailuserfooter": "Iste e-mail ha essite inviate per $1 a $2 con le function \"{{int:emailuser}}\" in {{SITENAME}}.",
+       "emailuserfooter": "Iste e-mail ha essite {{GENDER:$1|inviate}} per $1 a {{GENDER:$2|$2}} con le function \"{{int:emailuser}}\" in {{SITENAME}}.",
        "usermessage-summary": "Lassante un message de systema.",
        "usermessage-editor": "Messagero del systema",
        "watchlist": "Observatorio",
        "logentry-newusers-byemail": "Le conto de usator $3 ha essite {{GENDER:$2|create}} per $1 e le contrasigno ha essite inviate per e-mail",
        "logentry-newusers-autocreate": "Le conto $1 ha essite {{GENDER:$2|create}} automaticamente",
        "logentry-protect-move_prot": "$1 {{GENDER:$2|displaciava}} le parametros de protection de $4 a $3",
+       "logentry-protect-unprotect": "$1 {{GENDER:$2|removeva}} le protection de $3",
+       "logentry-protect-protect": "$1 {{GENDER:$2|protegeva}} $3 $4",
+       "logentry-protect-protect-cascade": "$1 {{GENDER:$2|protegeva}} $3 $4 [in cascada]",
+       "logentry-protect-modify": "$1 {{GENDER:$2|cambiava}} le nivello de protection de $3 $4",
+       "logentry-protect-modify-cascade": "$1 {{GENDER:$2|cambiava}} le nivello de protection de $3 $4 [in cascada]",
        "logentry-rights-rights": "$1 {{GENDER:$2|cambiava}} le appertinentia a gruppos pro $3 de $4 a $5",
        "logentry-rights-rights-legacy": "$1 {{GENDER:$2|cambiava}} le appertinentia a gruppos pro $3",
        "logentry-rights-autopromote": "$1 ha essite automaticamente {{GENDER:$2|promovite}} de $4 a $5",
        "api-error-badaccess-groups": "Tu non ha le permission de incargar files in iste wiki.",
        "api-error-badtoken": "Error interne: indicio invalide.",
        "api-error-copyuploaddisabled": "Le incargamentos per URL es disactivate in iste servitor.",
-       "api-error-duplicate": "Existe jam [$2 {{PLURAL:$1|un altere file|altere files}}] in le wiki con le mesme contento.",
-       "api-error-duplicate-archive": "Il habeva jam {{PLURAL:$1|[$2 un altere file]|[$2 altere files]}} in le sito con le mesme contento, ma {{PLURAL:$1|illo|illos}} ha essite delite.",
+       "api-error-duplicate": "Existe jam {{PLURAL:$1|un altere file|altere files}} in le wiki con le mesme contento.",
+       "api-error-duplicate-archive": "Il habeva jam {{PLURAL:$1|un altere file|altere files}} in le sito con le mesme contento, ma {{PLURAL:$1|illo|illos}} ha essite delite.",
        "api-error-empty-file": "Le file que tu submitteva es vacue.",
        "api-error-emptypage": "Le creation de nove paginas vacue non es permittite.",
        "api-error-fetchfileerror": "Error interne: qualcosa errava durante le obtention del file.",
index 6a0f328..49da7a9 100644 (file)
        "nstab-template": "Plantilia",
        "nstab-help": "Panid ti tulong",
        "nstab-category": "Kategoria",
+       "mainpage-nstab": "Umuna a Panid",
        "nosuchaction": "Awan ti kasta nga aramid",
        "nosuchactiontext": "Ti aramid a nainaganan babaen ti URL ket imbalido.\nMabalin a madi ti naimakiniliam nga URL, wenno sinurotmo ti saan a nasayaat a silpo.\nMabalinmo pay nga ibaga ti parikut ti sopwer nga us-usaren babaen ti {{SITENAME}}.",
        "nosuchspecialpage": "Awan ti kasta nga espesial a panid",
        "createacct-captcha": "Panagpatalged ti seguridad",
        "createacct-imgcaptcha-ph": "Ikabil ti teksto a makitam dita ngato",
        "createacct-submit": "Partuatem ti pakabilangam",
-       "createacct-another-submit": "Agpartuat iti sabali a pakabilangan",
+       "createacct-another-submit": "Agpartuat iti pakabilangan",
        "createacct-benefit-heading": "Ti {{SITENAME}} ket inar-aramid babaen ti tattao a kasla kenka.",
        "createacct-benefit-body1": "{{PLURAL:$1|nga inurnos|nga inur-urnos}}",
        "createacct-benefit-body2": "{{PLURAL:$1|a panid|a pampanid}}",
        "changeemail-password": "Ti bukodmo a kontrasenias ti {{SITENAME}}:",
        "changeemail-submit": "Sukatan ti esurat",
        "changeemail-throttled": "Adu unay ti panagpadasmo a sumrek.\nPangngaasi nga aguray ti $1 sakbay a padasen manen.",
+       "changeemail-nochange": "Pangngaasi nga agikabil iti sabali a baro nga adres ti esurat.",
        "resettokens": "Isaad manen dagiti tandaan",
        "resettokens-text": "Mabalinmo nga isaad manen dagiti tandaan a mangpalubos ti panagserrek ti naisangayan a pribado datos a mainaig ti pakabilangam ditoy.\n\nAramidem daytoy no aksidente nga inbingaymo dagitoy iti sabali wenno ti pakabilangam ket nakomprimiso.",
        "resettokens-no-tokens": "Awan dagiti maisaad manen a tandaan.",
        "group-bot": "Dagiti bot",
        "group-sysop": "Dagiti administrador",
        "group-bureaucrat": "Dagiti burokrata",
-       "group-suppress": "Pakapansin",
+       "group-suppress": "Dagiti agilaplapped",
        "group-all": "(amin)",
        "group-user-member": "{{GENDER:$1|agar-aramat}}",
        "group-autoconfirmed-member": "{{GENDER:$1|automatiko a napasingkedan nga agar-aramat}}",
        "group-bot-member": "{{GENDER:$1|bot}}",
        "group-sysop-member": "{{GENDER:$1|administrador}}",
        "group-bureaucrat-member": "{{GENDER:$1|burokrata}}",
-       "group-suppress-member": "{{GENDER:$1|pagpansin}}",
+       "group-suppress-member": "{{GENDER:$1|agilaplapped}}",
        "grouppage-user": "{{ns:project}}:Dagiti agar-aramat",
        "grouppage-autoconfirmed": "{{ns:project}}:Dagiti automatiko a napasingkedan nga agar-aramat",
        "grouppage-bot": "{{ns:project}}:Dagiti bot",
        "grouppage-sysop": "{{ns:project}}:Dagiti administrador",
        "grouppage-bureaucrat": "{{ns:project}}:Dagiti burokrata",
-       "grouppage-suppress": "{{ns:project}}:Pagpansin",
+       "grouppage-suppress": "{{ns:project}}:Ilapped",
        "right-read": "Basaen dagiti panid",
        "right-edit": "Agurnos kadagiti panid",
        "right-createpage": "Agpartuat kadagiti panid (saan a pagtutungtongan a pampanid)",
        "upload-http-error": "Adda napasamak a biddut ti HTTP: $1",
        "upload-copy-upload-invalid-domain": "Dagiti kopia a panagikarga ket saan a magun-od manipud iti daytoy a dominio.",
        "upload-dialog-title": "Agikarga iti papeles",
-       "upload-dialog-error": "Adda napasamak a biddut",
-       "upload-dialog-warning": "Adda napasamak a ballaag",
        "upload-dialog-button-cancel": "Ukasen",
        "upload-dialog-button-done": "Nalpasen",
        "upload-dialog-button-save": "Idulin",
        "upload-dialog-button-upload": "Agikarga",
-       "upload-dialog-label-select-file": "Pilien ti papeles",
-       "upload-dialog-label-infoform-title": "Dagiti salaysay",
-       "upload-dialog-label-infoform-name": "Nagan",
-       "upload-dialog-label-infoform-description": "Deskripsion",
-       "upload-dialog-label-usage-title": "Panagusar",
-       "upload-dialog-label-usage-filename": "Nagan ti papeles",
+       "upload-process-error": "Adda napasamak a biddut",
+       "upload-process-warning": "Adda napasamak a ballaag",
+       "upload-form-label-select-file": "Pilien ti papeles",
+       "upload-form-label-infoform-title": "Dagiti salaysay",
+       "upload-form-label-infoform-name": "Nagan",
+       "upload-form-label-infoform-description": "Deskripsion",
+       "upload-form-label-usage-title": "Panagusar",
+       "upload-form-label-usage-filename": "Nagan ti papeles",
        "backend-fail-stream": "Saan a maipan ti papeles $1.",
        "backend-fail-backup": "Saan a makaidulin ti kapada ti papeles ti $1.",
        "backend-fail-notexists": "Awan ti papeles ti $1.",
        "filerevert-legend": "Isubli ti papeles",
        "filerevert-intro": "Mangrugrugika nga agipasubli ti papeles ti <strong>[[Media:$1|$1]]</strong> iti [$4 bersion manipud idi $3, $2].",
        "filerevert-comment": "Rason:",
-       "filerevert-defaultcomment": "Naisubli iti bersion manipud idi $2, $1",
+       "filerevert-defaultcomment": "Naisubli iti bersion manipud idi $2, $1 ($3)",
        "filerevert-submit": "Isubli",
        "filerevert-success": "Ti <strong>[[Media:$1|$1]]</strong> ket naipasubli iti [$4 bersion manipud idi $3, $2].",
        "filerevert-badversion": "Awan ti dati a lokal a bersion iti daytoy a papeles a naited ti dayta nga oras ken petsa.",
        "nopagetext": "Awan ti puntaan a panid a nainaganam.",
        "pager-newer-n": "{{PLURAL:$1|nabarbaro a 1|nabarbaro a $1}}",
        "pager-older-n": "{{PLURAL:$1|nadadaan a 1|nadadaan a $1}}",
-       "suppress": "Pakapansin",
+       "suppress": "Ilapped",
        "querypage-disabled": "Daytoy nga espesial a panid ket nabaldado gapu kadagiti rason ti kasayaat ti panagpataray.",
        "apihelp": "Tulong ti API",
        "apihelp-no-such-module": "Saan a nabirukan ti modulo ti \"$1\".",
        "emailccsubject": "Kopia ti mensahem kenni $1: $2",
        "emailsent": "Naipatuloden ti esurat",
        "emailsenttext": "Naipatuloden ti esurat a mensahem.",
-       "emailuserfooter": "Daytoy nga esurat ket impatulod babaen ni $1 kenni $2 iti \"{{int:emailuser}}\" nga annong iti {{SITENAME}}",
+       "emailuserfooter": "Daytoy nga esurat ket {{GENDER:$1|impatulod}} babaen ni $1 kenni {{GENDER:$2|$2}} babaen ti \"{{int:emailuser}}\" nga annong iti {{SITENAME}}",
        "usermessage-summary": "Pumanpanaw iti mesahe ti sistema.",
        "usermessage-editor": "Mensahero ti sistema",
        "watchlist": "Bambantayan",
        "api-error-badtoken": "Akin-uneg a biddut: Dakes a tandaan.",
        "api-error-copyuploaddisabled": "Ti panagikarga babaen ti URL ket nabaldado iti daytoy server.",
        "api-error-duplicate": "Adda {{PLURAL:$1|ket [$2 a sabali a papeles] |dagiti [$2 sabsabali a papeles]}} nga addaan ditoy a sitio nga agpada ti linaon.",
-       "api-error-duplicate-archive": "Adda {{PLURAL:$1|idi [$2 sabali a papeles]|dagidi [$2 sabali a papeles]}} nga addaan ditoy a sitio nga agpada ti linaonda, ngem {{PLURAL:$1|daytoy|dagitoy}} ket naikkat.",
+       "api-error-duplicate-archive": "Adda {{PLURAL:$1|idi sabali a papeles|dagidi sabali a papeles}} nga addaan ditoy a sitio nga agpada ti linaonda, ngem {{PLURAL:$1|daytoy|dagitoy}} ket naikkat.",
        "api-error-empty-file": "Ti papeles nga intedmo ket awan linaon.",
        "api-error-emptypage": "Agparprtuat ti baro, dagiti awan ti linaon a panid ket saan a maipalubos.",
        "api-error-fetchfileerror": "Akin-uneg a biddut: Addaan ti dakes a napasamak bayat nga agal-ala ti papeles.",
index ef52420..d23e57a 100644 (file)
        "api-error-badtoken": "Innri villa: Skemmdur tóki.",
        "api-error-copyuploaddisabled": "Ekki er hægt að hlaða upp með vefslóð á þessum vefþjón.",
        "api-error-duplicate": "Það {{PLURAL:$1|er [$2 önnur skrá]|eru [$2 aðrar skrár]}} þegar til á vefsvæðinu sem hafa sama innihald.",
-       "api-error-duplicate-archive": "Það {{PLURAL:$1|var [$2 önnur skrá]|voru [$2 aðrar skrár]}} þegar á síðunni með sama innihald, en {{PLURAL:$1|henni|þeim}} var eytt.",
+       "api-error-duplicate-archive": "Það {{PLURAL:$1|var önnur skrá|voru aðrar skrár}} þegar á síðunni með sama innihald, en {{PLURAL:$1|henni|þeim}} var eytt.",
        "api-error-empty-file": "Skráin sem þú valdir er tóm.",
        "api-error-emptypage": "Stofnun nýrra, tómra síðna er óheimil.",
        "api-error-fetchfileerror": "Innri villa: Mistókst að sækja skránna.",
index 9f243f2..be41378 100644 (file)
        "createacct-captcha": "Controllo di sicurezza",
        "createacct-imgcaptcha-ph": "Inserisci il testo che vedi sopra",
        "createacct-submit": "Crea la tua utenza",
-       "createacct-another-submit": "Crea un'altra utenza",
+       "createacct-another-submit": "Crea utenza",
        "createacct-benefit-heading": "{{SITENAME}} cresce grazie a persone come te.",
        "createacct-benefit-body1": "{{PLURAL:$1|modifica|modifiche}}",
        "createacct-benefit-body2": "{{PLURAL:$1|pagina|pagine}}",
        "group-bot": "Bot",
        "group-sysop": "Amministratori",
        "group-bureaucrat": "Burocrati",
-       "group-suppress": "Oversight",
+       "group-suppress": "Soppressori",
        "group-all": "(tutti)",
        "group-user-member": "{{GENDER:$1|utente}}",
        "group-autoconfirmed-member": "{{GENDER:$1|utente autoconvalidato|utente autoconvalidata|utente autoconvalidato/a}}",
        "group-bot-member": "{{GENDER:$1|bot}}",
        "group-sysop-member": "{{GENDER:$1|amministratore|amministratrice|amministratore/trice}}",
        "group-bureaucrat-member": "{{GENDER:$1|burocrate}}",
-       "group-suppress-member": "{{GENDER:$1|oversight}}",
+       "group-suppress-member": "{{GENDER:$1|soppressore|sopprimitrice}}",
        "grouppage-user": "{{ns:project}}:Utenti",
        "grouppage-autoconfirmed": "{{ns:project}}:Utenti autoconvalidati",
        "grouppage-bot": "{{ns:project}}:Bot",
        "grouppage-sysop": "{{ns:project}}:Amministratori",
        "grouppage-bureaucrat": "{{ns:project}}:Burocrati",
-       "grouppage-suppress": "{{ns:project}}:Oversight",
+       "grouppage-suppress": "{{ns:project}}:Soppressori",
        "right-read": "Legge pagine",
        "right-edit": "Modifica pagine",
        "right-createpage": "Crea pagine (escluse le pagine di discussione)",
        "upload-http-error": "Si è verificato un errore HTTP: $1",
        "upload-copy-upload-invalid-domain": "Non è consentito il caricamento di copie da questo dominio.",
        "upload-dialog-title": "Carica file",
-       "upload-dialog-error": "Si è verificato un errore",
-       "upload-dialog-warning": "Si è verificato un avviso",
        "upload-dialog-button-cancel": "Annulla",
        "upload-dialog-button-done": "Fatto",
        "upload-dialog-button-save": "Salva",
        "upload-dialog-button-upload": "Carica",
-       "upload-dialog-label-select-file": "Seleziona file",
-       "upload-dialog-label-infoform-title": "Dettagli",
-       "upload-dialog-label-infoform-name": "Nome",
-       "upload-dialog-label-infoform-description": "Descrizione",
-       "upload-dialog-label-usage-title": "Utilizzo",
-       "upload-dialog-label-usage-filename": "Nome del file",
+       "upload-process-error": "Si è verificato un errore",
+       "upload-process-warning": "Si è verificato un avviso",
+       "upload-form-label-select-file": "Seleziona file",
+       "upload-form-label-infoform-title": "Dettagli",
+       "upload-form-label-infoform-name": "Nome",
+       "upload-form-label-infoform-description": "Descrizione",
+       "upload-form-label-usage-title": "Utilizzo",
+       "upload-form-label-usage-filename": "Nome del file",
        "backend-fail-stream": "Impossibile trasmettere il file $1.",
        "backend-fail-backup": "Impossibile eseguire il backup del file $1 .",
        "backend-fail-notexists": "Il file $1 non esiste.",
        "filerevert-legend": "Ripristina file",
        "filerevert-intro": "Si sta per ripristinare il file '''[[Media:$1|$1]]''' alla [$4 versione del $2, $3].",
        "filerevert-comment": "Motivo:",
-       "filerevert-defaultcomment": "Ripristinata la versione del $2, $1",
+       "filerevert-defaultcomment": "Ripristinata la versione del $2, $1 ($3)",
        "filerevert-submit": "Ripristina",
        "filerevert-success": "'''Il file [[Media:$1|$1]]''' è stato ripristinato alla [$4 versione del $2, $3].",
        "filerevert-badversion": "Non esistono versioni locali precedenti del file con il timestamp richiesto.",
        "nopagetext": "La pagina richiesta non esiste.",
        "pager-newer-n": "{{PLURAL:$1|1 più recente|$1 più recenti}}",
        "pager-older-n": "{{PLURAL:$1|1 meno recente|$1 meno recenti}}",
-       "suppress": "Oversight",
+       "suppress": "Sopprimi",
        "querypage-disabled": "Questa pagina speciale è disattivata per motivi di prestazioni.",
        "apihelp": "Aiuto API",
        "apihelp-no-such-module": "Modulo \"$1\" non trovato.",
        "emailccsubject": "Copia del messaggio inviato a $1: $2",
        "emailsent": "Messaggio inviato",
        "emailsenttext": "Il messaggio e-mail è stato inviato.",
-       "emailuserfooter": "Questa email è stata inviata da $1 a $2 attraverso la funzione \"{{int:emailuser}}\" su {{SITENAME}}.",
+       "emailuserfooter": "Questa email è stata {{GENDER:$1|inviata}} da $1 a {{GENDER:$2|$2}} attraverso la funzione \"{{int:emailuser}}\" su {{SITENAME}}.",
        "usermessage-summary": "Messaggio di sistema",
        "usermessage-editor": "Messaggero di sistema",
        "usermessage-template": "MediaWiki:MessaggioUtente",
        "api-error-badaccess-groups": "Non sei autorizzato a caricare documenti su questa wiki.",
        "api-error-badtoken": "Errore interno: token errato.",
        "api-error-copyuploaddisabled": "Il caricamento tramite URL è disabilitato su questo server.",
-       "api-error-duplicate": "Sul sito {{PLURAL:$1|c'è già [$2 un altro documento]|ci sono già [$2 altri documenti]}} con lo stesso contenuto.",
-       "api-error-duplicate-archive": "{{PLURAL:$1|C'era [$2 un altro file]|C'erano [$2 altri file]}} già nel sito con lo stesso contenuto, ma {{PLURAL:$1|è stato cancellato|sono stati cancellati}}.",
+       "api-error-duplicate": "Sul sito {{PLURAL:$1|c'è già un altro documento|ci sono già altri documenti}} con lo stesso contenuto.",
+       "api-error-duplicate-archive": "{{PLURAL:$1|C'era un altro file|C'erano altri file}} già nel sito con lo stesso contenuto, ma {{PLURAL:$1|è stato cancellato|sono stati cancellati}}.",
        "api-error-empty-file": "Il file selezionato era vuoto.",
        "api-error-emptypage": "La creazione di nuove pagine vuote non è consentita.",
        "api-error-fetchfileerror": "Errore interno: c'è stato un problema durante il recupero del documento.",
index 48ed171..841a37f 100644 (file)
        "createacct-captcha": "自動作成防止チェック",
        "createacct-imgcaptcha-ph": "上に表示されている文字列を入力",
        "createacct-submit": "アカウントを作成",
-       "createacct-another-submit": "アカウントを作成",
+       "createacct-another-submit": "アカウントを作成",
        "createacct-benefit-heading": "{{SITENAME}}は、あなたのような人々が創っています。",
        "createacct-benefit-body1": "{{PLURAL:$1|編集}}",
        "createacct-benefit-body2": "{{PLURAL:$1|ページ}}",
        "upload-http-error": "HTTP エラー発生: $1",
        "upload-copy-upload-invalid-domain": "このドメインからのアップロードは許可されていません。",
        "upload-dialog-title": "ファイルをアップロード",
-       "upload-dialog-error": "エラーが発生しました",
-       "upload-dialog-warning": "警告",
        "upload-dialog-button-cancel": "中止",
        "upload-dialog-button-done": "完了",
        "upload-dialog-button-save": "保存",
        "upload-dialog-button-upload": "アップロード",
-       "upload-dialog-label-select-file": "ファイル選択",
-       "upload-dialog-label-infoform-title": "詳細",
-       "upload-dialog-label-infoform-name": "名前",
-       "upload-dialog-label-infoform-description": "説明",
-       "upload-dialog-label-usage-title": "使用法",
-       "upload-dialog-label-usage-filename": "ファイル名",
+       "upload-process-error": "エラーが発生しました",
+       "upload-process-warning": "警告",
+       "upload-form-label-select-file": "ファイル選択",
+       "upload-form-label-infoform-title": "詳細",
+       "upload-form-label-infoform-name": "名前",
+       "upload-form-label-infoform-description": "説明",
+       "upload-form-label-usage-title": "使用法",
+       "upload-form-label-usage-filename": "ファイル名",
        "backend-fail-stream": "ファイル $1 をストリームできませんでした。",
        "backend-fail-backup": "ファイル $1 をバックアップできませんでした。",
        "backend-fail-notexists": "ファイル $1 は存在しません。",
        "filerevert-legend": "ファイルを差し戻す",
        "filerevert-intro": "ファイル<strong>[[Media:$1|$1]]</strong>を[$4 $2$3版]に差し戻そうとしています。",
        "filerevert-comment": "理由:",
-       "filerevert-defaultcomment": "$1$2の版へ差し戻し",
+       "filerevert-defaultcomment": "$1$2の版へ差し戻し ($3)",
        "filerevert-submit": "差し戻す",
        "filerevert-success": "<strong>[[Media:$1|$1]]</strong>は[$4 $2$3の版]に差し戻されました。",
        "filerevert-badversion": "このファイルに指定された時刻印を持つ過去の版はありません。",
        "emailccsubject": "$1 に送信したメールの控え: $2",
        "emailsent": "メールを送信しました",
        "emailsenttext": "メールを送信しました。",
-       "emailuserfooter": "このメールは$1から$2へ、{{SITENAME}}の「{{int:emailuser}}」機能で送信されました。",
+       "emailuserfooter": "このメールは$1から{{GENDER:$2|$2}}へ、{{SITENAME}}の「{{int:emailuser}}」機能で{{GENDER:$1|送信}}されました。",
        "usermessage-summary": "システムメッセージを残す。",
        "usermessage-editor": "システムメッセンジャー",
        "watchlist": "ウォッチリスト",
        "logentry-newusers-byemail": "利用者アカウント $3 が $1 によって{{GENDER:$2|作成され}}、そのパスワードがメールで送信されました",
        "logentry-newusers-autocreate": "利用者アカウント $1 が自動的に{{GENDER:$2|作成されました}}",
        "logentry-protect-move_prot": "$1 が保護設定を $4 から $3 に{{GENDER:$2|移動しました}}",
+       "logentry-protect-unprotect": "$1 が $3 からの保護を{{GENDER:$2|削除}}しました",
+       "logentry-protect-protect": "$1 が $3 の $4 を{{GENDER:$2|保護}}しました",
+       "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 が $3 の所属グループを $4 から $5 に{{GENDER:$2|変更しました}}",
        "logentry-rights-rights-legacy": "$1 が $3 の所属グループを{{GENDER:$2|変更しました}}",
        "logentry-rights-autopromote": "$1 が $4 から $5 に自動的に{{GENDER:$2|昇格しました}}",
        "api-error-badaccess-groups": "このウィキへのファイルのアップロードが許可されていません。",
        "api-error-badtoken": "内部エラー: トークンが正しくありません。",
        "api-error-copyuploaddisabled": "URLによるアップロードはこのサーバーでは無効になっています。",
-       "api-error-duplicate": "当ウェブサイト上には、既に同じ内容の{{PLURAL:$1|[$2 他のファイル]が|[$2 他のファイルがいくつか]}}存在しています。",
-       "api-error-duplicate-archive": "サイト上に同じ内容の{{PLURAL:$1|[$2 別のファイル]が|[$2 他のファイルがいくつか]}}既にありましたが、{{PLURAL:$1|それは|それらは}}削除されました。",
+       "api-error-duplicate": "当ウェブサイト上には、既に同じ内容の{{PLURAL:$1|他のファイルが|他のファイルがいくつか}}存在しています。",
+       "api-error-duplicate-archive": "サイト上に同じ内容の{{PLURAL:$1|別のファイルが|他のファイルがいくつか}}既にありましたが、{{PLURAL:$1|それは|それらは}}削除されました。",
        "api-error-empty-file": "送信されたファイルは空でした。",
        "api-error-emptypage": "内容がないページの新規作成は許可されていません。",
        "api-error-fetchfileerror": "内部エラー: ファイルを取得する際に問題が発生しました。",
index 46ca07c..2fe06b6 100644 (file)
        "tog-underline": "Unjestreg henwisnenge:",
        "tog-hideminor": "Sjul lie øndrenge i listen öwe siensti øndrenge",
        "tog-hidepatrolled": "Sjul patruljiirtje redigiirenge i siensti øndrenge",
-       "tog-extendwatchlist": "Udvedet liste ve seneste ændrenger",
-       "tog-usenewrc": "Førbedret liste åver seneste ændrenger (JavaScript)",
+       "tog-newpageshidepatrolled": "Sjul patruljiirtje side på listen öwe nyj side",
+       "tog-extendwatchlist": "Utwiid öwewågnengslisten te å wis åll øndrenge å ett kons di nysti",
+       "tog-usenewrc": "Gruppiir øndrenge pro siid i listen öwe siensti øndrenge å i öwewågnengslisten",
        "tog-numberheadings": "Automatisk nummeriireng å öweskrifte",
        "tog-showtoolbar": "Wis wærktyeslinje te redigiireng",
        "tog-editondblclick": "Redigiir side mä doppeltklikk",
        "tog-editsectiononrightclick": "Redigiir åsnit we å højaklikk på dæes title",
-       "tog-watchcreations": "Tilføj sider a åpretter til miin åvervågnengsliste",
-       "tog-watchdefault": "Tilføj sider a redigærer til miin åvervågnengsliste",
-       "tog-watchmoves": "Tilføj sider a flytter til miin åvervågnengsliste",
-       "tog-watchdeletion": "Tilføj sider a sletter til miin åvervågnengsliste",
+       "tog-watchcreations": "Tilfye side, æ oprette, å fili, æ lägge op, te min öwewågnengslist",
+       "tog-watchdefault": "Tilfye side å file, æ redigiire, te min öwewågnengslist",
+       "tog-watchmoves": "Tilfye side å file, æ flytte, te min öwewågnengslist",
+       "tog-watchdeletion": "Tilfye side å file, æ slette, te min öwewågnengslist",
+       "tog-watchrollback": "Tilfye side, hwor æ hår utförtj en tebagrullneng te min öwewågnengslist",
        "tog-minordefault": "Markiir som standard åll redigiirenge som lie",
        "tog-previewontop": "Wis forhånjswisneng öwe redigiirengsboksi",
        "tog-previewonfirst": "Wis forhånjswisneng we fösjti redigiireng",
-       "tog-enotifwatchlistpages": "Send mig en e-mail ve sideændrenger",
+       "tog-enotifwatchlistpages": "Senj mej i e-mail we øndrenge te en siid elle i fil på min öwewågnengslist",
        "tog-enotifusertalkpages": "Senj mej i e-mail nær min brugediskusjonssiid øndas",
-       "tog-enotifminoredits": "Send mig også en e-mail ve mendre ændrenger åf åvervågede sider",
+       "tog-enotifminoredits": "Senj mej åsså i e-mail we lie øndrenge å side å file på min öwewågnengslist",
        "tog-enotifrevealaddr": "Wis min e-mailadress i e-mails mä besked om øndrenge",
        "tog-shownumberswatching": "Wis åntal bruga som öwewåge",
-       "tog-fancysig": "Signaturer uden åtåmatisk henvesnenge",
-       "tog-uselivepreview": "Brug åtåmatisk førhåndsvesnenge (eksperimentel)",
+       "tog-oldsig": "Nuwærenje signatur:",
+       "tog-fancysig": "Behånjl signatur som wikitekst uen automatisk henwisneng",
+       "tog-uselivepreview": "Benøtt løbenje forhånjswisneng",
        "tog-forceeditsummary": "Adwar mej hwes æ ett utfylle beskriiwelsfeltje",
        "tog-watchlisthideown": "Sjul ejne øndrenge i öwewågnengslisten",
        "tog-watchlisthidebots": "Sjul øndrenge fra bots i öwewågnengslisten",
        "tog-watchlisthideminor": "Sjul lie øndrenge i öwewågningslisten",
+       "tog-watchlisthideliu": "Sjul inloggtje brugas redigiirenge i öwewågnengslist",
+       "tog-watchlisthideanons": "Sjul anonym brugas redigiirenge i öwewågnengslist",
+       "tog-watchlisthidepatrolled": "Sjul patruljiirtje øndrenge fra öwewågnengslist",
        "tog-ccmeonemails": "Senj mej kopie å e-mails som æ senje te anjer bruga",
        "tog-diffonly": "Wis ett sidinholj nier unje versjonssammelliiknenge",
        "tog-showhiddencats": "Wis sjulen kategorie",
+       "tog-norollbackdiff": "Wis ett forskell ette tebagrullneng",
+       "tog-useeditwarning": "Adwar mej, hwes æ forlade en redigiirengssiid mä øndrenge, som ett ä djiemen",
+       "tog-prefershttps": "Brug ålltins en siker forbinjels, nær du ä loggen in",
        "underline-always": "Åltid",
        "underline-never": "Åller",
-       "underline-default": "æfter brovserendstellenge",
+       "underline-default": "Brug dej browserens instelleng elle standarden for walgtje utsienje",
+       "editfont-style": "Skriftstil we redigiireng:",
+       "editfont-default": "Brug browseris instelleng",
+       "editfont-monospace": "Fästbrieddeskrift",
+       "editfont-sansserif": "Skrift uen fyed",
+       "editfont-serif": "Skrift mä fyed",
        "sunday": "søndaw",
        "monday": "måndaw",
        "tuesday": "tirsdaw",
        "oct": "okt",
        "nov": "nov",
        "dec": "dec",
+       "january-date": "$1. januar",
+       "february-date": "$1. februar",
+       "march-date": "$1. mårts",
+       "april-date": "$1. åpril",
+       "may-date": "$1. maj",
+       "june-date": "$1. juni",
+       "july-date": "$1. juli",
+       "august-date": "$1. ågust",
+       "september-date": "$1. september",
+       "october-date": "$1. oktober",
+       "november-date": "$1. november",
+       "december-date": "$1. december",
        "pagecategories": "{{PLURAL:$1|Kategori|Kategorie}}",
        "category_header": "Side i kategorien \"$1\"",
        "subcategories": "Unjekategorie",
        "hidden-categories": "{{PLURAL:$1|Sjultj kategori|Sjultj kategorie}}",
        "hidden-category-category": "Sjulen kategorie",
        "category-subcat-count": "{{PLURAL:$2|Kategorien hår en unjekategori.|Kategorien inholje nierstoenje {{PLURAL:$1|unjerkategori|$1 unjerkategorie}}, å i ållt $2.}}",
+       "category-subcat-count-limited": "Kategorien inholje {{PLURAL:$1|nästi unjekategorien|di nästi unjekategorie}}.",
        "category-article-count": "Kategorien inholje {{PLURAL:$2|kons den nierstoenje siid|{{PLURAL:$1|den nierstoenje siid|di nierstoenje $1 side}} å i ållt $2.}}",
+       "category-article-count-limited": "Kategorien inholje {{PLURAL:$1|nierstoenje siid|di nierstoenje $1 side}}.",
        "category-file-count": "Kategorien inholje {{PLURAL:$2|kons den nierstoenje siid|{{PLURAL:$1|i nierstoenje fil|di nierstoenje $1 file}} å i ållt $2.}}",
+       "category-file-count-limited": "Kategorien inholje {{PLURAL:$1|nierstoenje fili|di nierstoenje $1 file}}.",
        "listingcontinuesabbrev": "forts.",
+       "index-category": "Indeksiirtje side",
+       "noindex-category": "Intj-indeksiirtje side",
+       "broken-file-category": "Side mä bruden filhenwisnenge",
        "about": "Om",
        "article": "Artikel",
        "newwindow": "(åbne i e ny winjye)",
        "cancel": "Åbryd",
        "moredotdotdot": "Mier...",
+       "morenotlisted": "Listen ä ett komplett.",
        "mypage": "Siid",
        "mytalk": "Diskusjon",
        "anontalk": "Diskusjonssiid for denn IP-adress",
        "qbmyoptions": "Min instellenge",
        "faq": "OSS",
        "faqpage": "Project:OSS",
+       "actions": "Hånjlenge",
        "namespaces": "Naunrum",
        "variants": "Variantje",
        "navigation-heading": "Navigasjonsmenu",
        "view": "Wis",
        "view-foreign": "Wis på $1",
        "edit": "Redigiir",
+       "edit-local": "Redigiir lokal beskriiwels",
        "create": "Oprett",
        "create-local": "Tilfye lokal beskriiwels",
        "editthispage": "Redigiir siid",
        "create-this-page": "Oprett siden",
        "delete": "Slett",
        "deletethispage": "Slett siid",
+       "undeletethispage": "Djenta siden",
        "undelete_short": "Fortryd slettneng å {{PLURAL:$1|jen versjon|$1 versjone}}",
+       "viewdeleted_short": "Wis {{PLURAL:$1|en sletten redigiireng|$1 sletten redigiirenge}}",
        "protect": "Beskøtt",
        "protect_change": "ønda",
        "protectthispage": "Beskøtt siid",
        "jumpto": "Skift te:",
        "jumptonavigation": "navigasjon",
        "jumptosearch": "Syegneng",
+       "view-pool-error": "Beklawe, men serveren ä i yeblikk öwebelasten. \nFor mång bruga forsyege å sietj siden.\nWentj e yeblikk, för du forsyege å besyeg siden idjen.\n\n$1",
+       "generic-pool-error": "Beklawe, men serveren ä i yeblikk öwebelasten.\nFor mång bruga forsyeg å sietj siden.\nWentj e yeblikk för du forsyege å besyeg siden idjen.",
+       "pool-timeout": "Timeout mens man wentje på låsnengen",
+       "pool-queuefull": "Pool-køen ä full",
+       "pool-errorunknown": "Utjentj fejl",
+       "pool-servererror": "Pool-counterservicen ä ett te rådihed ($1).",
+       "poolcounter-usage-error": "Brugsfejl: $1",
        "aboutsite": "Om {{SITENAME}}",
        "aboutpage": "Project:Om",
        "copyright": "Inholje ä utdjøwen unje $1 mämenna ånj ä åndjøwen.",
        "ok": "OK",
        "retrievedfrom": "Hentjen fra \"$1\"",
        "youhavenewmessages": "{{PLURAL:$3|Du hår}} $1 ($2).",
+       "youhavenewmessagesfromusers": "{{PLURAL:$4|Du hår}} $1 fra {{PLURAL:$3|i ånj bruge| $3 bruga}} ($2).",
+       "youhavenewmessagesmanyusers": "Du hår $1 fra mång bruga ($2).",
+       "newmessageslinkplural": "{{PLURAL:$1|e ny besked|999=nyj beskede}}",
+       "newmessagesdifflinkplural": "siensti {{PLURAL:$1|øndreng|999=øndrenge}}",
        "youhavenewmessagesmulti": "Du hår nyj beskede på $1",
        "editsection": "redigiir",
        "editold": "redigiir",
        "toc": "Inholjsfortejnels",
        "showtoc": "wis",
        "hidetoc": "sjul",
+       "collapsible-collapse": "Folj sammel",
+       "collapsible-expand": "Folj ut",
+       "confirmable-confirm": "Ä {{GENDER:$1|du}} siker?",
+       "confirmable-yes": "Ja",
+       "confirmable-no": "Nej",
        "thisisdeleted": "Sie elle djensätt $1?",
        "viewdeleted": "Wis $1?",
        "restorelink": "{{PLURAL:$1|en sletten øndreng|$1 sletten øndrenge}}",
        "page-rss-feed": "\"$1\" RSS-feed",
        "page-atom-feed": "\"$1\" Atom-feed",
        "red-link-title": "$1 (siden ä ett skrøwen ennu)",
+       "sort-descending": "Sortiir fallenje",
+       "sort-ascending": "Sortiir stigenje",
        "nstab-main": "Siid",
        "nstab-user": "Brugesiid",
        "nstab-media": "Mediesiid",
        "nstab-template": "Skabelon",
        "nstab-help": "Hjælp",
        "nstab-category": "Kategori",
+       "mainpage-nstab": "Forsiid",
        "nosuchaction": "Funksjonen finjs ett",
-       "nosuchactiontext": "Funksje ångævet i'n URL ken ekke genkendes åf æ MediaWiki-softwær",
+       "nosuchactiontext": "Hånjlengen som ä åndjøwen i URL'i ä udjylji.\nDu kan ha skrøwen URL'i forkiertj, elle fuljtj en ukorrekt henwisneng.\nDä kan åsså skyljs en fejl i programmet som brugs å {{SITENAME}}.",
        "nosuchspecialpage": "En sån specialsiid finjs ett",
-       "nospecialpagetext": "Du harst bedt en sonstside'm, der ekke ken genkendes åf æ MediaWiki-softwær.",
+       "nospecialpagetext": "<strong>Du hår bien om en specialsiid, som ett kan djenkentjs å MediaWiki-softwari.</strong>\n\nEn list öwe djylji specialside finjs på [[Special:SpecialPages|{{int:specialpages}}]].",
        "error": "Fejl",
        "databaseerror": "Databasefejl",
+       "databaseerror-text": "Där opstue fejl i en forspöyrgsel te databasi.\nDetj kan indikiir en fejl i softwari.",
+       "databaseerror-textcl": "Där opstue fejl i en forspöyrgsel te databasi.",
+       "databaseerror-query": "Forspöyrgsel: $1",
+       "databaseerror-function": "Funksjon: $1",
+       "databaseerror-error": "Fejl: $1",
        "laggedslavemode": "'''Bemærk:''' Den wisen siid inholje muliwis ett di nysti øndrenge.",
        "readonly": "Databasi ä skriiwbeskøtten",
        "enterlockreason": "Skriiw en begrunjels for skriiwbeskøttelsen, mä samt en wurdiireng å, nær skriiwbeskøttelsen ophäws idjen",
        "badtitle": "Udjylji titel",
        "badtitletext": "Titelen å onske siden war ett tillæt, tomm elle siden ä fortjiertj henwistj fra {{SITENAME}} på e ånj spraw.\nDen kan inholj iet elle flier tejn, som ett ma ånwenjs i title.",
        "viewsource": "Wis tjeljtekst",
+       "viewsource-title": "Sie tjeljkoden te $1",
+       "actionthrottled": "Begrænsneng å hånjleng",
        "viewsourcetext": "Du ken dog se og åfskreve'n keldekode til æ side:",
+       "welcomeuser": "Wælkomen, $1!",
+       "welcomecreation-msg": "Det konto ä bløwen opretten.\nGlæmm ett å ønda din [[Special:Preferences|instellenge for {{SITENAME}}]].",
        "yourname": "Det brugenaun:",
        "userlogin-yourname": "Brugenaun",
        "userlogin-yourname-ph": "Intast det brugenaun",
+       "createacct-another-username-ph": "Intast brugenaun",
        "yourpassword": "Din adgångskode",
        "userlogin-yourpassword": "Adgångskode",
        "userlogin-yourpassword-ph": "Intast din adgångskode",
        "createacct-yourpassword-ph": "Intast en adgångskode",
+       "yourpasswordagain": "Djentast adgångskode:",
        "createacct-yourpasswordagain": "Bekräft adgångskode",
        "createacct-yourpasswordagain-ph": "Intast adgångskode idjen",
-       "remembermypassword": "Husk min adgangskode til næste gang (for a maximum of $1 {{PLURAL:$1|day|days}})",
+       "remembermypassword": "Husk min brugenaun i denn browser (hyest $1 {{PLURAL:$1|daw}})",
        "userlogin-remembermypassword": "Husk mej",
+       "userlogin-signwithsecure": "Brug siker forbinjels",
+       "yourdomainname": "Det domænnaun:",
+       "password-change-forbidden": "Du kan ett ønda adgångskoder på wikien.",
+       "externaldberror": "Där ä opstotj en fejl i i ekstern adgångsdatabase, elle du hår ett rettihede te å opdatiir denn.",
        "login": "Logg på",
        "nav-login-createaccount": "Oprett e konto ellerlogg på",
        "userlogin": "Oprett e konto elle logg på",
+       "userloginnocreate": "Logg på",
        "logout": "Logg å",
        "userlogout": "Logg å",
+       "notloggedin": "Ikke loggen på",
        "userlogin-noaccount": "Hår du ett e konto?",
        "userlogin-joinproject": "Slut dej te {{SITENAME}}",
        "nologin": "Du hår iet kronto? $1.",
        "createaccount": "Oprett e ny brugekonto",
        "gotaccount": "Hår du ållried e konto? '''$1'''.",
        "gotaccountlink": "Logg på",
+       "userlogin-resetlink": "Hår du glommen din login-oplysnenge?",
        "userlogin-resetpassword-link": "Glommen din adgångskode?",
        "userlogin-helplink2": "Hjälp mä å logg på",
+       "userlogin-loggedin": "Du ä ållried loggen på som {{GENDER:$1|$1}}. Brug formulari nierfor te å logg på som i ånj bruge.",
+       "userlogin-createanother": "Oprett e ånj konto",
+       "createacct-emailrequired": "E-mailadress",
        "createacct-emailoptional": "E-mailadress (walgfri)",
        "createacct-email-ph": "Intast dej e-mailadress",
+       "createacct-another-email-ph": "Intast e-mailadress",
        "createacct-captcha": "Sikerhedskontroll",
        "createacct-imgcaptcha-ph": "Intast wenlist öwestoenje tekst",
        "createacct-submit": "Oprett det konto",
        "pt-createaccount": "Oprett konto",
        "pt-userlogout": "Logg å",
        "retypenew": "Djentast ny adgångskode",
+       "resetpass-submit-cancel": "Åbryd",
        "passwordreset": "Nullstell adgångskode",
        "bold_sample": "Fied tekst",
        "bold_tip": "Fied tekst",
        "editing": "Redigiire $1",
        "creating": "Oprette $1",
        "editingsection": "Redigiire $1 (åsnit)",
+       "editingcomment": "Redigiire $1 (ny åsnit)",
+       "editconflict": "Redigiirengskonflikt: $1",
        "copyrightwarning": "Bemærk wenlist å åll bidraw te {{SITENAME}} ä å betrakt som utdjøwen unje $2 (se $1 for detalje).\nHwes du ett onske å din tekst skal utsätts for nådesløs redigiirenge å å den kan blyw kopiiirtj ette forgodtbefinjenje, så skal du ett placiir den her.<br />\nDu låwe os åsså, å du siel hår forfatten teksten elle hår kopiiirtj den fra en public domain-tjelj elle en tilswarenje fri tjelj.\n'''Lägg åller material her som ä beskøttetj å anjas ophawsrett uen dæes tillædels!'''",
        "templatesused": "{{PLURAL:$1|Skabelon|Skabelone}} som ä brugtj på siden:",
        "templatesusedpreview": "Følgende skablåner bruges åf denne ertikelførhåndsvesnenge:",
        "permissionserrorstext-withaction": "Du hår ett rettihede te å $2 å följenje {{PLURAL:$1|grunj}}:",
        "recreate-moveddeleted-warn": "'''Advarsel: Du ä we å djenskab en tidlia sletten siid.'''\n\nÖwewæj om dä ä passenje å djenoprett siden. Slettnengs- å flyttlogger for siden ä wisen nierfor.",
        "moveddeleted-notice": "Siden ä bløwen sletten.\nSlettnengs- å flyttlogger for siden ä wisen nierfor.",
+       "postedit-confirmation-saved": "Din redigiireng ä djiemen.",
+       "content-model-wikitext": "wikitekst",
+       "content-model-text": "ållminneli tekst",
+       "content-model-javascript": "JavaScript",
+       "content-json-empty-object": "Tomm objekt",
+       "content-json-empty-array": "Tomm array",
        "viewpagelogs": "Wis loggliste for denn siid",
        "currentrev": "Nuwærenje versjon",
        "currentrev-asof": "Nuwærenje versjon fra $1",
        "histlegend": "Selektiir forskell: Markiir radiobokser å versjone som du will jämnför å trykk på enter elle knappi på bueni.<br />\n\nForklareng: <strong>({{int:cur}})</strong> = forskell te den nuwærenje versjon, <strong>({{int:last}})</strong> = forskell te den förri versjon, <strong>{{int:minoreditletter}}</strong> = lie øndreng.",
        "histfirst": "älsti",
        "histlast": "nysti",
+       "historysize": "({{PLURAL:$1|1 byte|$1 bytes}})",
+       "history-feed-title": "Versjonshistori",
        "history-feed-item-nocomment": "$1 mä $2",
        "rev-delundel": "ønda sijtbarhed",
        "history-title": "$1: Versjonshistorik",
        "search-redirect": "(omdirigiireng $1)",
        "search-section": "(åsnit $1)",
        "search-suggest": "Mientje du: $1",
+       "search-interwiki-caption": "Systeprojekte",
+       "search-interwiki-default": "Resultate fra $1:",
+       "search-interwiki-more": "(mier)",
+       "search-relatedarticle": "Relatiirtj",
+       "searchrelated": "relatiirtj",
        "searchall": "åll",
        "search-showingresults": "{{PLURAL:$4|Resultat <strong>$1</strong> å <strong>$3</strong>|Resultat <strong>$1 - $2</strong> å <strong>$3</strong>}}",
        "search-nonefound": "Syegnengen djij ien resultate.",
        "powersearch-ns": "Syeg i naunrummen:",
        "preferences": "Instellenge",
        "mypreferences": "Instellenge",
+       "prefs-skin": "Utsienje",
        "skin-preview": "Forhånjswisneng",
+       "prefs-misc": "Forskelli",
+       "prefs-resetpass": "Skift adgångskode",
+       "prefs-changeemail": "Ønda e-mailadress",
+       "prefs-setemail": "Åndjie en e-mailadress",
+       "prefs-email": "Instellenge for e-mail",
+       "prefs-rendering": "Utsienje",
+       "saveprefs": "Djiem instellenge",
+       "restoreprefs": "Djensätt åll standardinstellenge (i åll seksjone)",
+       "prefs-editing": "Redigiireng",
+       "rows": "Räkke:",
+       "columns": "Kolonne:",
+       "searchresultshead": "Syegresultate",
+       "stub-threshold": "Græns for stumplinkformatiireng ($1):",
+       "stub-threshold-sample-link": "eksempel",
+       "stub-threshold-disabled": "Deaktiviirtj",
+       "recentchangesdays": "Åntal daw som skal wises i siensti øndrenge:",
+       "recentchangesdays-max": "Maksimal $1 {{PLURAL:$1|daw}}",
+       "recentchangescount": "Åntal redigiirenge som skal wises som standard:",
+       "prefs-help-recentchangescount": "Dä djälje for siensti øndrenge, historike å logge.",
+       "prefs-help-watchlist-token2": "Detj ä hemmeli lygli te webfeed å din öwewågnengslist.\nHwes anjer tjenne den, will man wær istånj te å läs din öwewågnengslist, så diel den ett.\n[[Special:ResetTokens|Klikk her hwes du hår brug å nullstell den]].",
+       "savedprefs": "Din instellenge ä bløwen djiemen.",
+       "timezonelegend": "Tidszone:",
+       "localtime": "Lokaltiid:",
+       "timezoneuseserverdefault": "Brug wikiis standardinstelleng ($1)",
+       "timezoneuseoffset": "Ånj (åndjie forskell)",
+       "servertime": "Serveris tiid:",
+       "guesstimezone": "Hentj tidszone fra browseri",
+       "timezoneregion-africa": "Afrika",
+       "timezoneregion-america": "Amerika",
+       "timezoneregion-antarctica": "Antarktis",
+       "timezoneregion-arctic": "Arktis",
+       "timezoneregion-asia": "Asien",
+       "timezoneregion-atlantic": "Atlantjerhawe",
+       "timezoneregion-australia": "Australien",
+       "timezoneregion-europe": "Europa",
+       "timezoneregion-indian": "Indisk Ocean",
+       "timezoneregion-pacific": "Stillhawe",
+       "allowemail": "Tillæd e-mail fra anjer bruga",
+       "prefs-searchoptions": "Syeg",
+       "prefs-namespaces": "Naunrum",
+       "default": "standard",
+       "prefs-files": "File",
+       "prefs-custom-css": "Personli CSS",
+       "prefs-custom-js": "Personli JavaScript",
+       "prefs-common-css-js": "Fælls CSS/JS for åll utsienje:",
+       "prefs-reset-intro": "Du kan brug siden te å tebagstell åll din instellenge te standardinstellenger.\nDä kan ett djendjörs.",
+       "prefs-emailconfirm-label": "Bekräftels å e-mail:",
        "youremail": "E-mail:",
+       "username": "{{GENDER:$1|Brugenaun}}:",
+       "prefs-memberingroups": "{{GENDER:$2|Mälemm}} å {{PLURAL:$1|gruppen|grupper}}:",
+       "prefs-registration": "Registriirengstidspuntj:",
        "yourrealname": "Det rijti naun:",
-       "prefs-help-realname": "* <strong>Dit rigtege navn</strong> (valgfrit): Hves du vælger at åplyse dit navn hvil dette bleve brugt til at tilskreve dig dit arbejde.",
+       "yourlanguage": "Spraw:",
+       "yourvariant": "Sprawvariantj for inholj:",
+       "prefs-help-variant": "Sprawvaruabtheb elle rettskriiwneng, som du forträkke, å denn wikis inholjsside wises i.",
+       "yournick": "Ny signatur:",
+       "prefs-help-signature": "Kommentare på diskusjonsside bör signiirs mä \"<nowiki>~~~~</nowiki>\" som will blyw konvertiirtj te din signatur å e tidsstempel.",
+       "badsig": "Syntaksi i signaturen ä udjylji; kontrolliir wenlist den brugtje HTML.",
+       "badsiglength": "Din signatur ä for lång. Den ma hyest inholj $1 {{PLURAL:$1|tejn}}.",
+       "yourgender": "Hwant forträkke du å blyw beskriiwen?",
+       "prefs-help-realname": "Åndjiels å rijti naun ä walgfritj.\nHwes du wælge å oplys det naun, wil dä blyw brugtj te å tilskriiw dej det arbejt.",
+       "prefs-editor": "Redigiirengsprogramme",
+       "prefs-preview": "Forhånjswisneng",
+       "prefs-advancedrc": "Avanciirtje instellinge",
+       "prefs-advancedrendering": "Avanciirtje instellinge",
+       "prefs-advancedsearchoptions": "Avanciirtje instellinge",
+       "prefs-advancedwatchlist": "Avanciirtje instellinge",
+       "prefs-displayrc": "Instellenge for wisneng",
+       "prefs-displaywatchlist": "Wisnengsmulihede",
+       "prefs-tokenwatchlist": "Märk",
+       "prefs-diffs": "Forskell",
        "grouppage-sysop": "{{ns:project}}:Administrore",
        "right-writeapi": "Brug redigiirengsdieli å API",
        "newuserlogpage": "Brugeoprettelslogg",
        "newuserlogpagetext": "Detj ä i logg öwe di siensti opretten bruga.",
        "rightslog": "Rettihedslogg",
+       "action-history": "sie historik for siden",
        "nchanges": "$1 {{PLURAL:$1|øndreng|øndrenge}}",
        "enhancedrc-history": "historik",
        "recentchanges": "Siensti øndrenge",
        "recentchanges-label-plusminus": "Störrelsen på siden bløw øndan mä detj åntal bytes",
        "recentchanges-legend-heading": "'''Forklareng:'''",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (sie åsså [[Special:NewPages|listen öwe nyj side]])",
-       "rcnotefrom": "Nedenfør ses ændrengerne frå '''$2''' til '''$1''' vest.",
+       "rcnotefrom": "Nierfor ä op te '''$1''' {{PLURAL:$5|øndreng|øndrenge}} sien '''$2''' wisen.",
        "rclistfrom": "Wis nyj øndrenge startenje fra $3 kl. $2",
        "rcshowhideminor": "$1 lie øndrenge",
        "rcshowhideminor-show": "Wis",
        "rcshowhidebots-show": "Sjul",
        "rcshowhidebots-hide": "Sjul",
        "rcshowhideliu": "$1 registriirtje bruga",
+       "rcshowhideliu-show": "Wis",
        "rcshowhideliu-hide": "Sjul",
        "rcshowhideanons": "$1 anonym bruga",
        "rcshowhideanons-show": "Wis",
        "rcshowhideanons-hide": "Sjul",
        "rcshowhidepatr": "$1 kontrolliirtje øndrenge",
+       "rcshowhidepatr-show": "Wis",
+       "rcshowhidepatr-hide": "Sjul",
        "rcshowhidemine": "$1 ejne bidraw",
        "rcshowhidemine-show": "Wis",
        "rcshowhidemine-hide": "Sjul",
        "randompage": "Tefælji siid",
        "randomredirect": "Tefælji henwisnenge",
        "statistics": "Statistik",
+       "pageswithprop": "Side mä e side-ejnskåp",
+       "pageswithprop-legend": "Side mä e side-ejnskåp",
        "doubleredirects": "Doppelt omdirigiirenge",
        "brokenredirects": "Defekt omdirigiirenge",
        "withoutinterwiki": "Side uen henwisnenge te anjer spraw",
        "unusedimages": "Ubrugtje file",
        "wantedcategories": "Onske kategorie",
        "wantedpages": "Onske side",
-       "mostlinked": "Side mä fliest henwisnenge",
+       "wantedfiles": "Onske file",
+       "wantedtemplates": "Onske skabelone",
+       "mostlinked": "Side mä di fliesti henwisnenge",
        "mostlinkedcategories": "Miest-brugtje kategorie",
        "mostlinkedtemplates": "Miest-brugtje side",
        "mostcategories": "Side mä di fliesti kategorie",
        "mostimages": "Miest-brugtje file",
+       "mostinterwikis": "Side mä di fliesti interwikilinks",
        "mostrevisions": "Side mä di fliesti versjone",
        "prefixindex": "Åll side som bedjynne mä",
        "shortpages": "Kort side",
        "longpages": "Lång side",
        "deadendpages": "Blinjenjside",
        "protectedpages": "Skriiwbeskøttetj side",
+       "protectedtitles": "Beskøttetj sidenaun",
        "listusers": "Brugelist",
        "newpages": "Nysti side",
        "ancientpages": "Älsti side",
        "linksearch-text": "Wildcards som \"*.wikipedia.org\" kan benøtts.\nDär skal som minimum åndjies e topnivå-domæn som f. eks. \"*.org\".<br />\n{{PLURAL:$2|Unjestötten protokol|Unjestötten protokolle}}: $1 (bruge automatisk http:// hwes där ett ä åndjøwen no protokol).",
        "linksearch-line": "$2 linke te $1",
        "linksearch-error": "Wildcards ma kons benøtts i starti å hostnaune.",
+       "trackingcategories": "Spørengskategorie",
        "emailuser": "E-mail te bruge",
        "watchlist": "Öwewågnengslist",
        "mywatchlist": "Öwewågnengslist",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|diskusjon]])",
        "version": "Informasjon om MediaWiki",
        "specialpages": "Specialside",
+       "specialpages-group-maintenance": "Weliholjelsside",
+       "specialpages-group-pages": "Sideliste",
        "tag-filter": "[[Special:Tags|Tag]]filtjer:",
        "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|Tag|Tags}}]]: $2)",
        "logentry-delete-delete": "$1 {{GENDER:$2|slettetj}} siden $3",
index 12d0e4c..527cf04 100644 (file)
        "api-error-badtoken": "Kasalahan njero: Token èlèk.",
        "api-error-copyuploaddisabled": "Ngunggah saka URL dipatèni nèng sasana iki.",
        "api-error-duplicate": "Ana {{PLURAL:$1|[$2 berkas liya]|[$2 pirang-pirang berkas liya]}} sing wis ana nèng situsé saha isiné padha.",
-       "api-error-duplicate-archive": "Ana {{PLURAL:$1|[$2 berkas liya]|[$2 pirang-pirang berkas liya]}} sing wis ana nèng situsé saha isiné padha, nanging {{PLURAL:$1|kuwi|kuwi kabèh}} wis dibusak.",
+       "api-error-duplicate-archive": "Ana {{PLURAL:$1|berkas liya|pirang-pirang berkas liya}} sing wis ana nèng situsé saha isiné padha, nanging {{PLURAL:$1|kuwi|kuwi kabèh}} wis dibusak.",
        "api-error-empty-file": "Berkas sing Sampéyan kirim kosong.",
        "api-error-emptypage": "Nggawé kaca kosong anyar ora dilikaké.",
        "api-error-fetchfileerror": "Kasalahan njero: Ana sing salah nalika ngètukaké berkas iki.",
index f3401a1..8e1f092 100644 (file)
@@ -23,7 +23,8 @@
                        "გიორგიმელა",
                        "아라",
                        "Macofe",
-                       "SHOTHA"
+                       "SHOTHA",
+                       "Gi777ga"
                ]
        },
        "tog-underline": "ბმულების ხაზგასმა:",
        "nstab-template": "თარგი",
        "nstab-help": "დახმარება",
        "nstab-category": "კატეგორია",
+       "mainpage-nstab": "მთავარი გვერდი",
        "nosuchaction": "მოქმედება არ არსებობს",
        "nosuchactiontext": "URL-ის მიერ მითითებული მოქმედება მცდარია.\nშესაძლოა შეცდომით აკრიფეთ URL, ან არასწორ ბმულზე გადახვედით.\nაგრეთვე შესაძლოა, {{SITENAME}}-ს მიერ გამოყენებულ პროგრამულ უზრუნველყოფაში იყოს შეცდომა.",
        "nosuchspecialpage": "სპეციალური გვერდი არ არსებობს",
        "actionthrottled": "სიჩქარის შეზღუდვა.",
        "actionthrottledtext": "სპამთან ბრძოლასთან დაკავშირებით აკრძალულია შემდეგი მონაცემების მრავალჯერ გამეორება. გთხოვთ გაიმეოროთ იგი მოგვიანებით.",
        "protectedpagetext": "ეს გვერდი დაბლოკილია რედაქტირებისათვის ან სხვა მოქმედებისათვის.",
-       "viewsourcetext": "თქვენ შეგიძლიათ ნახოთ ამ გვერდის საწყისი ფაილი და მისი ასლი შექმნათ:",
-       "viewyourtext": "თქვენ შეგიძლიათ იხილოთ და დააკოპიროთ  '''თქვენი რედაქტირებების''' საწყისი ტექსტი ამ გვერდზე:",
+       "viewsourcetext": "თქვენ შეგიძლიათ ნახოთ ამ გვერდის საწყისი ფაილი და მისი ასლი შექმნათ.",
+       "viewyourtext": "თქვენ შეგიძლიათ იხილოთ და დააკოპიროთ  <strong>თქვენი რედაქტირებების</strong> საწყისი ტექსტი ამ გვერდზე:",
        "protectedinterface": "ეს გვერდი წარმოადგენს ტექსტურ ინტერფეისს პროგრამული უზრუნველყოფისათვის და დაცულია ვანდალიზმის აღკვეთის მიზნით.",
        "editinginterface": "'''ყურადღება:''' თქვენ არედაქტირებთ გვერდს, რომელიც პროგრამის ინტერფეისის ტექსტს შეიცავს. \nამ გვერდზე განხორციელებული რედაქტირება გამოიწვევს ამ ვიკის სხვა მომხმარებელთა სამუშაო ინტერფეისის შეცვლასაც. \nიმისათვის, რომ დაამატოთ ან შეცვალოთ თარგმანები ყველა ვიკიში, გთხოვთ, გამოიყენოთ მედიავიკის ლოკალიზაციის პროექტი [//translatewiki.net/ translatewiki.net].",
        "translateinterface": "თარგმანების ყველა ვიკიში დასამატებლად ან შესაცვლელად, გთხოვთ გამოიყენოთ მედიავიკებისლოკალიზაციის პროექტი [//translatewiki.net/ translatewiki.net].",
        "createacct-captcha": "უსაფრთხოების შემოწმება",
        "createacct-imgcaptcha-ph": "შეიყვანეთ ზემოთ მოცემული ტექსტი",
        "createacct-submit": "შექმენით თქვენი ანგარიში",
-       "createacct-another-submit": "á\83¡á\83®á\83\95á\83\90 á\83\90á\83\9cá\83\92á\83\90á\83 á\83\98á\83¨á\83\98á\83¡ á\83¨á\83\94á\83¥á\83\9bá\83\9cá\83\90",
+       "createacct-another-submit": "ანგარიშის შექმნა",
        "createacct-benefit-heading": "{{SITENAME}} შექმნილია თქვენნაირი ადამიანების მიერ.",
        "createacct-benefit-body1": "{{PLURAL:$1|რედაქტირება|რედაქტირება}}",
        "createacct-benefit-body2": "{{PLURAL:$1|გვერდი|გვერდი}}",
        "changeemail-password": "თქვენი პაროლი პროექტში {{SITENAME}}:",
        "changeemail-submit": "ელ-ფოსტის შეცვლა",
        "changeemail-throttled": "თქვენ უკვე ძალიან ბევრჯერ სცადეთ შესვლა.\nგთხოვთ, მოიცადოთ $1, სანამ კიდევ სცდიდეთ.",
+       "changeemail-nochange": "გთხოვთ, შეიყვანოთ ახალი, განსხვავებული ელექტრონული მისამართი.",
        "resettokens": "ტოკენების ჩამოყრა",
        "resettokens-text": "თქვენ შეგიძლიათ ჩამოყაროთ ტოკენები, რომლებიც შესაძლებლობას იძლევიან შესვლას განსაზღვრულ პირად მონაცემებში, დაკავშირებულს თქვენ ანგარიშთან აქ. \n\nთქვენ ეს აუცილებლად უნდა გააკეთოთ, თუ თქვენ ის შემთხვევით გააცანით სხვას ან თუკი თქვენი ანგარიში იქნა გატეხილი.",
        "resettokens-no-tokens": "არ არის ტოკენები ჩამოსაყრელად",
        "yourdiff": "განსხვავებები",
        "copyrightwarning": "ყურადღება მიაქციეთ: ნებისმიერი წვლილი გვერდზე {{SITENAME}} $2 ლიცენზიას ექვემდებარება (იხ. $1 დეტალებისთვის). თუ არ გსურთ თქვენი ნამუშევარი თავისუფლად გავრცელდეს და მისი დაუნდობელი რედაქტირება მოხდეს, მაშინ ნუ შეიყვანთ მას აქ.<br />\nთქვენ ასევე პირობას დებთ, რომ ეს თქვენი დაწერილია, ან გადმოღებულია საზოგადოებრივი დომენიდან, ან მსგავსი თავისუფალი წყაროდან.\n'''ნუ შემოიტანთ საავტორო უფლებებით დაცულ ნაშრომს ავტორის თანხმობის გარეშე!'''",
        "copyrightwarning2": "*გაითვალისწინეთ, რომ ნებისმიერი წვლილი {{SITENAME}}-ში შეიძლება ჩასწორდეს, შეიცვალოს ან წაიშალოს სხვა რედაქტორების მიერ.\n*თუ არ გსურთ, რომ თქვენი ნამუშევარი შეუზღუდავად იქნეს რედაქტირებული, მას აქ ნუ განათავსებთ.<br />\n*თქვენ აგრეთვე პირობას დებთ, რომ თქვენს მიერ განთავსებული ტექსტი თქვენი დაწერილია, ან გადმოწერილია საზოგადოებრივი დომენიდან ან მსგავსი თავისუფალი წყაროდან. (იხ. $1 დეტალებისთვის).\n*'''ნუ შემოიტანთ საავტორო უფლებებით დაცულ ნაშრომს ავტორის ნებართვის გარეშე!'''",
+       "editpage-cannot-use-custom-model": "ამ გვერდის მასალის მოდელი არ შეიძლება, რომ შეიცვალოს.",
        "longpageerror": "'''შეცდომა: თქვენს მიერ აკრეფილი ტექსტის ზომა {{PLURAL:$1|$1 კილობაიტია}}, რაც აღემატება, დადგენილ {{PLURAL:$2|$2 კილობაიტიან}} ზღვარს. გვერდის შენახვა შეუძლებელია.'''",
        "readonlywarning": "'''გაფრთხილება: მონაცემთა ბაზა დახურულია პერიოდული შემოწმებისთვის, შესაბამისად თქვენ ვერ შეძლებთ რედაქტირებას ამ მომენტში.'''\nსასურველია ტექსტის ასლი შეინახოთ ტექსტურ რედაქტორში და მოგვიანებით შემოიტანოთ.\n\nმონაცემთა ბაზის დამბლოკველმა ადმინისტრატორმა შემდეგი კომენტარი დატოვა: $1",
        "protectedpagewarning": "'''ყურადღება:  ეს გვერდი დაბლოკილია და მისი რედაქტირება შეუძლიათ მხოლოდ მომხმარებლებს ადმინისტრატორის უფლებებით'''\nიხილეთ დაცვის ჟურნალის ჩანაწერი",
        "columns": "სვეტები",
        "searchresultshead": "ძიება",
        "stub-threshold": "გაფორმების გასაუმჯობესებლად <a href=\"#\" class=\"stub\"> მოცემულია ესკიზების ბმულები</a> (ბაიტებში):",
+       "stub-threshold-sample-link": "მაგალითი",
        "stub-threshold-disabled": "გათიშულია",
        "recentchangesdays": "ბოლო ცვლილებებში საჩვენებელი დღეები:",
        "recentchangesdays-max": "მაქსიმუმ $1 {{PLURAL:$1|დღე}}",
        "unpatrolledletter": "!",
        "number_of_watching_users_pageview": "[$1 მომხმარებლის/ები კონტროლი]",
        "rc_categories": "მხოლოდ კატეგორიებიდან (განაცალკევეთ \"|\"-ის მიხედვით)",
-       "rc_categories_any": "ნებისმიერი",
+       "rc_categories_any": "á\83\90á\83 á\83©á\83\94á\83£á\83\9aá\83\98á\83\93á\83\90á\83\9c á\83\9cá\83\94á\83\91á\83\98á\83¡á\83\9bá\83\98á\83\94á\83 á\83\98",
        "rc-change-size": "$1",
        "rc-change-size-new": "ზომა ცვლილების შემდეგ არის: {{PLURAL:$1|ბაიტი|ბაიტი}}",
        "newsectionsummary": "/* $1 */ ახალი სექცია",
        "upload-too-many-redirects": "URL შეიცავს ძალიან ბევრ გადამისამართებებს",
        "upload-http-error": "მოხდა HTTP შეცდომა: $1",
        "upload-copy-upload-invalid-domain": "ამ დომენში ატვირთვების კოპირება არ არის ხელმისაწვდომი.",
+       "upload-dialog-title": "ფაილის ატვირთვა",
+       "upload-dialog-button-cancel": "გაუქმება",
+       "upload-dialog-button-done": "შესრულდა",
+       "upload-dialog-button-save": "შენახვა",
+       "upload-dialog-button-upload": "ატვირთვა",
+       "upload-process-error": "მოხდა შეცდომა",
+       "upload-process-warning": "გაჩნდა გაფრთხილება",
+       "upload-form-label-select-file": "ფაილის არჩევა",
+       "upload-form-label-infoform-title": "დეტალები",
+       "upload-form-label-infoform-name": "სახელი",
+       "upload-form-label-infoform-description": "აღწერა",
+       "upload-form-label-usage-title": "გამოყენება",
+       "upload-form-label-usage-filename": "ფაილის სახელი",
        "backend-fail-stream": "ფაილი $1 ტრანსლირება ვერ მოხერხდა.",
        "backend-fail-backup": "ფაილი $1 სარეზერვო ასლის გაკეთება ვერ მოხერხდა.",
        "backend-fail-notexists": "ფაილი $1 არ არსებობს.",
        "filerevert-legend": "დააბრუნე ფაილი",
        "filerevert-intro": "<span class=\"plainlinks\">თქვენ აბრუნებთ '''[[Media:$1|$1]]'''  [$4 ვერსიას $3, $2]-თან.</span>",
        "filerevert-comment": "მიზეზი:",
-       "filerevert-defaultcomment": "დაბრუნება ვერსიასთან $2, $1-დან.",
+       "filerevert-defaultcomment": "დაბრუნება ვერსიასთან $2, $1-დან ($3).",
        "filerevert-submit": "გაუქმება",
        "filerevert-success": "'''[[Media:$1|$1]]''' დაუბრუნდა ვერსიას [$4  $3, $2]-დან.",
        "filerevert-badversion": "არ არსებობს ფაილის წინა ლოკალური ვერსია მოთხოვნილი  თარიღითა და დროით",
        "nopagetext": "მოცემული სამიზნო გვერდი არ არის მიითებული.",
        "pager-newer-n": "{{PLURAL:$1|უახლესი 1|უახლესი $1}}",
        "pager-older-n": "{{PLURAL:$1|უფრო ძველი 1|უფრო ძველი $1}}",
-       "suppress": "á\83\93á\83\90á\83\9bá\83\90á\83\9aá\83\95ა",
+       "suppress": "á\83©á\83\90á\83®á\83¨á\83\9dá\83\91ა",
        "querypage-disabled": "ეს სპეცგვერდი გამორთულია წარმადობის გასაზრდელად.",
        "apihelp": "API დახმარება",
        "apihelp-no-such-module": "მოდული „$1“ ვერ მოიძებნა.",
        "booksources-text": "ქვემოთ არის ვებ გვერდების ბმულების სია სადაც იყიდება ახალი და ნახმარი წიგნები, და შესაძლოა შეიცავდნენ დამატებით ინფორმაციას წიგნების შესახებ, რომლებსაც ეძებთ:",
        "booksources-invalid-isbn": "თქვენს მიერ მითითებული ISBN, შეცდომას შეიცავს.  შეამოწმეთ, თუ თავდაპირველი წყარო სწორადაა აკრეფილი.",
        "specialloguserlabel": "შემსრულებელი:",
-       "speciallogtitlelabel": "მიზანი (სათაური, ან მომხმარებელი):",
+       "speciallogtitlelabel": "მიზანი (სათაური, ან {{ns:user}}:მომხმარებლის სახელი):",
        "log": "ჟურნალები",
        "all-logs-page": "ყველა საზოგადო ჟურნალი",
        "alllogstext": "{{SITENAME}}-ის ყველა არსებული ჩანაწერის კომბინირებული ჩვენება.\nშეგიძლიათ გაცხრილოთ იგი ჩანაწერის ტიპის, მომხმარებლის სახელის, ან დაკავშირებული გვერდის შერჩევით.",
        "rollback-success": "გაუქმდა რედაქტირება $1; დაბრუნება ვერსიაზე $2.",
        "sessionfailure-title": "სეანსის შეცდომა",
        "sessionfailure": "ჩანს, რომ პრობლემაა თქვენი რეგისტრაციის სესიისათვის;\nეს მოქმედება შეჩერდა თქვენი სესიაში შემოჭრის თავიდან ასაცილებლად.\nგთხოვთ, დააწკაპუნოთ ღილაკს „უკან“ და თავიდან ჩართოთ გვერდი, რომლიდანაც შემოხვედით და სცადოთ განმეორებით.",
+       "changecontentmodel-legend": "შინაარსის მოდელის შეცვლა",
+       "changecontentmodel-title-label": "გვერდის სათაური",
+       "changecontentmodel-model-label": "შინაარსის ახალი მოდელი",
+       "changecontentmodel-reason-label": "მიზეზი:",
+       "changecontentmodel-success-title": "შინაარსის მოდელი შეიცვალა",
+       "logentry-contentmodel-change-revertlink": "დაბრუნება",
+       "logentry-contentmodel-change-revert": "დაბრუნება",
        "protectlogpage": "დაცვის ისტორია",
        "protectlogtext": "ქვემოთ წარმოდგენილია გვერდის დაცვის დონის ცვლილებების სია. \nიხილეთ ასევე [[Special:ProtectedPages|დაცული გვერდების სია]] ამ მომენტისთვის.",
        "protectedarticle": "დაცულია გვერდი: „[[$1]]“",
        "variantname-zh-my": "my",
        "variantname-zh": "zh",
        "variantname-gan-hans": "hans",
+       "variantname-kk-kz": "kk-kz",
+       "variantname-kk-tr": "kk-tr",
+       "variantname-kk-cn": "kk-cn",
+       "variantname-kk-cyrl": "kk-cyrl",
+       "variantname-kk-latn": "kk-latn",
+       "variantname-kk-arab": "kk-arab",
+       "variantname-kk": "kk",
        "metadata": "მეტამონაცემები",
        "metadata-help": "ეს ფაილი შეიცავს დამატებით ინფორმაციას, სავარაუდოდ ციფრული კამერიდან ან სკანერიდან, რომელიც მის შესაქმნელად გამოიყენეს. თუ ფაილის ორიგინალი შეცვლილია, ზოგიერთი დეტალი შესაძლოა სრულად არ ასახავდეს ფაილში შეტანილ ცვლილებებს.",
        "metadata-expand": "დამატებითი ინფორმაციის ჩვენება",
        "version-libraries": "დაინსტალირებული ბიბლიოთეკები",
        "version-libraries-library": "ბიბლიოთეკა",
        "version-libraries-version": "ვერსია",
+       "version-libraries-license": "ლიცენზია",
+       "version-libraries-description": "აღწერა",
+       "version-libraries-authors": "ავტორები",
        "redirect": "გადამისამართება ფაილიდან, მომხმარებლიდან, გვერდიდან ან ვერსიის იდენტიფიკატორიდან",
        "redirect-legend": "გადამისამართება ფაილზე ან გვერდზე",
        "redirect-summary": "ეს დამხმარე გვერდი ამისამართებს ფაილის (ფაილის სახელიდან) გვერდზე, (გვერდის ან ვერსიის იდენტიფიკატორიდან) ან მომხმარებლის გვერდზე (მომხმარებლის რაოდენობრივი იდენტიფიკატორიდან). გამოყენება: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/page/64308]], [[{{#Special:Redirect}}/revision/328429]] ან [[{{#Special:Redirect}}/user/101]].",
        "htmlform-cloner-create": "მეტის დამატება",
        "htmlform-cloner-delete": "წაშლა",
        "htmlform-cloner-required": "აუცილებელია სულ მცირე ერთი მნიშვნელობა.",
+       "htmlform-title-not-exists": "[[:$1]] არ არსებობს.",
+       "htmlform-user-not-exists": "<strong>$1</strong> არ არსებობს.",
+       "htmlform-user-not-valid": "<strong>$1</strong> არ არის სწორი მომხმარებლის სახელი.",
        "sqlite-has-fts": "$1 სრული ტექსტის ძიების მხარდაჭერით",
        "sqlite-no-fts": "$1 სრული ტექსტის ძიების მხარდაჭერის გარეშე",
        "logentry-delete-delete": "მომხმარებელმა $1 {{GENDER:$2|წაშალა}} გვერდი: „$3“",
        "api-error-badtoken": "შიდა შეცდომა: ცუდი ტოკენი.",
        "api-error-copyuploaddisabled": "ამ სერვერზე URL-მისამართის საშუალებით ატვირთვა გამორთულია.",
        "api-error-duplicate": "უკვე {{PLURAL:$1|არსებობს [$2 სხვა ფაილი]|არსებობს [$2 სხვა მსგავსი ფაილი]}} ანალოგიური შინაარსით.",
-       "api-error-duplicate-archive": "საიტზე ადრე {{PLURAL:$1|უკვე იყო [$2 ფაილი]}} ანალოგიური შინაარსით, მაგრამ {{PLURAL:$1|ის წაიშალა|ისინი წაიშალა}}.",
+       "api-error-duplicate-archive": "საიტზე ადრე {{PLURAL:$1|უკვე იყო ფაილი}} ანალოგიური შინაარსით, მაგრამ {{PLURAL:$1|ის წაიშალა|ისინი წაიშალა}}.",
        "api-error-empty-file": "არჩეული ფაილი ცარიელია.",
        "api-error-emptypage": "ახალი, ცარიელი გვერდების შექმნა აკრძალულია.",
        "api-error-fetchfileerror": "შიდა შეცდომა: ფაილის მიღებისას მოხდა რაღაც შეცდომა.",
        "special-characters-title-endash": "ტირე",
        "special-characters-title-emdash": "გრძელი ტირე",
        "special-characters-title-minus": "მინუსის ნიშანი",
+       "mw-widgets-dateinput-no-date": "თარიღი არ არის არჩეული",
        "mw-widgets-dateinput-placeholder-day": "წწწწ-თთ-დდ",
        "mw-widgets-dateinput-placeholder-month": "წწწწ-თთ",
        "mw-widgets-titleinput-description-new-page": "გვერდი ჯერ არ არსებობს",
index c753608..d2ef3e0 100644 (file)
@@ -12,7 +12,8 @@
                        "Urhixidur",
                        "아라",
                        "SalemB",
-                       "Mezgoug"
+                       "Mezgoug",
+                       "Macofe"
                ]
        },
        "tog-underline": "Aderrer n yezdayen:",
        "api-error-badtoken": "Tuccḍa tagensit : yir « tiddest ».",
        "api-error-copyuploaddisabled": "Issenɣal seg URL nsan ɣef aqeddac agi.",
        "api-error-duplicate": "{{PLURAL:$1|[Yella yakan $2 afaylu nniḍen]|[Llan yakan $2 ifuyla nniḍen]}} ɣef asmel agi s ugbur am winna.",
-       "api-error-duplicate-archive": "{{PLURAL:$1|[Yella yakan $2 afaylu nniḍen]|[Llan yakan $2 ifuyla nniḍen]}} ɣef asmel agi s ugbur am winna, maca {{PLURAL:$1|yetwekkes|tetwekksen}}.",
+       "api-error-duplicate-archive": "{{PLURAL:$1|Yella yakan afaylu nniḍen|Llan yakan ifuyla nniḍen}} ɣef asmel agi s ugbur am winna, maca {{PLURAL:$1|yetwekkes|tetwekksen}}.",
        "api-error-empty-file": "Afaylu id ceggɛeḍ d-ilem.",
        "api-error-emptypage": "Asnulfu n isebtar ilemawen ur yareg ara.",
        "api-error-fetchfileerror": "Tuccḍa tagensit : Yella kra ur yaɛeddan ara s luwqem deg tiririt n ufaylu.",
index 5d881be..a595f86 100644 (file)
        "upload-http-error": "HTTP қатесі кездесті: $1",
        "upload-copy-upload-invalid-domain": "Бұл домейннен еселеп жүктеу қолжетімді емес.",
        "upload-dialog-title": "Файл жүктеу",
-       "upload-dialog-error": "Белгісіз қате кездесті",
-       "upload-dialog-warning": "Ескерту кездесті",
        "upload-dialog-button-cancel": "Қажет емес",
        "upload-dialog-button-done": "Бітті",
        "upload-dialog-button-save": "Сақтау",
        "upload-dialog-button-upload": "Жүктеу",
-       "upload-dialog-label-select-file": "Файлды таңдау",
-       "upload-dialog-label-infoform-title": "Егжей-тегжейі",
-       "upload-dialog-label-infoform-name": "Атауы",
-       "upload-dialog-label-infoform-description": "Сипаттамасы",
-       "upload-dialog-label-usage-title": "Қолданылуы",
-       "upload-dialog-label-usage-filename": "Файл атауы",
+       "upload-process-error": "Белгісіз қате кездесті",
+       "upload-process-warning": "Ескерту кездесті",
+       "upload-form-label-select-file": "Файлды таңдау",
+       "upload-form-label-infoform-title": "Егжей-тегжейі",
+       "upload-form-label-infoform-name": "Атауы",
+       "upload-form-label-infoform-description": "Сипаттамасы",
+       "upload-form-label-usage-title": "Қолданылуы",
+       "upload-form-label-usage-filename": "Файл атауы",
        "backend-fail-stream": "«$1» файлы ақпады.",
        "backend-fail-backup": "«$1» файлының сақтық есесі жасалмады.",
        "backend-fail-notexists": "$1 файлы бар емес.",
index 0cc83c4..c703b25 100644 (file)
        "upload-too-many-redirects": "URLនេះមានតំណភ្ជាប់បញ្ជូនបន្តច្រើនពេកហើយ",
        "upload-http-error": "មានកំហុសHTTPមួយបានកើតឡើង៖ $1",
        "upload-dialog-title": "ផ្ទុកឯកសារឡើង",
-       "upload-dialog-error": "មានបញ្ហាកើតឡើង",
-       "upload-dialog-warning": "មានការព្រមាន",
        "upload-dialog-button-cancel": "បោះបង់",
        "upload-dialog-button-done": "រួចរាល់",
        "upload-dialog-button-save": "រក្សាទុក",
        "upload-dialog-button-upload": "ផ្ទុកឡើង",
-       "upload-dialog-label-select-file": "ជ្រើសរើសឯកសារ",
-       "upload-dialog-label-infoform-title": "ព័ត៌មាន​លំអិត",
-       "upload-dialog-label-infoform-name": "ឈ្មោះ​",
-       "upload-dialog-label-infoform-description": "ការ​ពិពណ៌នា",
-       "upload-dialog-label-usage-title": "បម្រើបម្រាស់",
-       "upload-dialog-label-usage-filename": "ឈ្មោះឯកសារ",
+       "upload-process-error": "មានបញ្ហាកើតឡើង",
+       "upload-process-warning": "មានការព្រមាន",
+       "upload-form-label-select-file": "ជ្រើសរើសឯកសារ",
+       "upload-form-label-infoform-title": "ព័ត៌មាន​លំអិត",
+       "upload-form-label-infoform-name": "ឈ្មោះ​",
+       "upload-form-label-infoform-description": "ការ​ពិពណ៌នា",
+       "upload-form-label-usage-title": "បម្រើបម្រាស់",
+       "upload-form-label-usage-filename": "ឈ្មោះឯកសារ",
        "backend-fail-notexists": "គ្មានឯកសារ \"$1\" ទេ។",
        "backend-fail-notsame": "ឯកសារដែលមិនដូចគ្នាបេះបិទមួយមានរួចហើយនៅ \"$1\"។",
        "backend-fail-delete": "មិនអាចលុបឯកសារ \"$1\" បានទេ។",
index b5986cf..da37a07 100644 (file)
@@ -52,7 +52,8 @@
                        "Lefion",
                        "Leeheonjin",
                        "Hwangjy9",
-                       "Kurousagi"
+                       "Kurousagi",
+                       "Macofe"
                ]
        },
        "tog-underline": "링크에 밑줄:",
        "nstab-template": "틀",
        "nstab-help": "도움말",
        "nstab-category": "분류",
+       "mainpage-nstab": "대문",
        "nosuchaction": "이러한 명령이 없습니다",
        "nosuchactiontext": "URL에 지정한 명령이 올바르지 않습니다.\nURL을 잘못 입력했거나, 올바르지 않은 링크를 따라갔을 수 있습니다.\n{{SITENAME}}에 사용하는 소프트웨어의 버그가 있을 수 있습니다.",
        "nosuchspecialpage": "해당하는 특수 문서가 없습니다.",
        "createacct-captcha": "보안 검사",
        "createacct-imgcaptcha-ph": "위에 보이는 텍스트를 입력하세요",
        "createacct-submit": "계정 만들기",
-       "createacct-another-submit": "다른 계정 만들기",
+       "createacct-another-submit": "계정 만들기",
        "createacct-benefit-heading": "{{SITENAME}} 프로젝트는 여러분과 같은 사람들이 만듭니다.",
        "createacct-benefit-body1": "{{PLURAL:$1|편집}}",
        "createacct-benefit-body2": "{{PLURAL:$1|문서}}",
        "changeemail-password": "{{SITENAME}} 비밀번호:",
        "changeemail-submit": "이메일 주소 바꾸기",
        "changeemail-throttled": "로그인에 연속으로 너무 많이 실패하였습니다.\n$1 기다렸다가 다시 시도하세요.",
+       "changeemail-nochange": "다른 이메일 주소를 입력해 주세요.",
        "resettokens": "토큰 재설정",
        "resettokens-text": "여기에 당신의 계정과 관련된 특정 개인 데이터에 접근을 허용하는 토큰을 재설정합니다.\n\n토큰이 다른 사람에게 알려졌거나 계정이 침해되었을 때는 재설정해야 합니다.",
        "resettokens-no-tokens": "재설정할 토큰이 없습니다.",
        "rows": "줄 수:",
        "columns": "열 수:",
        "searchresultshead": "검색",
-       "stub-threshold": "링크를 <a href=\"#\" class=\"stub\">토막글</a> 형식으로 보여줄 문서 크기 (바이트 수):",
+       "stub-threshold": "링크를 토막글 형식으로 보여줄 문서 크기 ($1):",
+       "stub-threshold-sample-link": "샘플",
        "stub-threshold-disabled": "비활성화됨",
        "recentchangesdays": "최근 바뀜에 보여줄 날짜 수:",
        "recentchangesdays-max": "최대 $1{{PLURAL:$1|일}}",
        "upload-http-error": "HTTP 오류 발생: $1",
        "upload-copy-upload-invalid-domain": "이 도메인에 속하지 않는 웹사이트의 파일을 올릴 수 없습니다.",
        "upload-dialog-title": "파일 올리기",
-       "upload-dialog-error": "오류가 발생했습니다",
-       "upload-dialog-warning": "경고가 일어났습니다",
        "upload-dialog-button-cancel": "취소",
        "upload-dialog-button-done": "완료",
        "upload-dialog-button-save": "저장",
        "upload-dialog-button-upload": "올리기",
-       "upload-dialog-label-select-file": "파일을 선택해주세요.",
-       "upload-dialog-label-infoform-title": "자세한 사항",
-       "upload-dialog-label-infoform-name": "이름",
-       "upload-dialog-label-infoform-description": "설명",
-       "upload-dialog-label-usage-title": "사용",
-       "upload-dialog-label-usage-filename": "파일 이름",
+       "upload-process-error": "오류가 발생했습니다",
+       "upload-process-warning": "경고가 일어났습니다",
+       "upload-form-label-select-file": "파일을 선택해주세요.",
+       "upload-form-label-infoform-title": "자세한 사항",
+       "upload-form-label-infoform-name": "이름",
+       "upload-form-label-infoform-description": "설명",
+       "upload-form-label-usage-title": "사용",
+       "upload-form-label-usage-filename": "파일 이름",
        "backend-fail-stream": "\"$1\" 파일을 스트림할 수 없습니다.",
        "backend-fail-backup": "\"$1\" 파일을 백업할 수 없습니다.",
        "backend-fail-notexists": "$1 파일이 존재하지 않습니다.",
        "filerevert-legend": "파일 되돌리기",
        "filerevert-intro": "'''[[Media:$1|$1]]''' 파일을 [$4 $2 $3 버전]으로 되돌립니다.",
        "filerevert-comment": "이유:",
-       "filerevert-defaultcomment": "$1 $2 판으로 되돌림",
+       "filerevert-defaultcomment": "$1 $2 ($3) 판으로 되돌림",
        "filerevert-submit": "되돌리기",
        "filerevert-success": "'''[[Media:$1|$1]]''' 파일을 [$4 $2 $3 버전]으로 되돌렸습니다.",
        "filerevert-badversion": "입력된 시간 기록을 가진 파일의 로컬 버전이 없습니다.",
        "booksources-text": "아래의 목록은 새 책이나 중고 책을 판매하는 바깥 사이트로, 원하는 책의 정보를 얻을 수 있습니다.",
        "booksources-invalid-isbn": "입력한 ISBN이 올바르지 않은 것으로 보입니다. 원본과 대조해 문제가 있는지 확인해보세요.",
        "specialloguserlabel": "작업 수행자:",
-       "speciallogtitlelabel": "대상 (제목 또는 사용자):",
+       "speciallogtitlelabel": "대상 (제목 또는 {{ns:user}}:사용자_이름 으로사용자):",
        "log": "기록 목록",
        "all-logs-page": "모든 공개 기록",
        "alllogstext": "{{SITENAME}}에서의 기록이 모두 나와 있습니다.\n기록 종류, 사용자 이름, 문서 이름을 선택해서 볼 수 있습니다. (대소문자를 구별합니다.)",
        "api-error-badaccess-groups": "이 위키에 파일을 올릴 권한이 없습니다.",
        "api-error-badtoken": "내부 오류: 토큰이 잘못되었습니다.",
        "api-error-copyuploaddisabled": "이 서버에서 URL을 통해 파일 올리기가 비활성화되어 있습니다.",
-       "api-error-duplicate": "이 위키에 내용이 똑같은 {{PLURAL:$1|[$2 다른 파일]}}이 있습니다.",
-       "api-error-duplicate-archive": "같은 내용을 담고 있던 {{PLURAL:$1|[$2 다른 파일]}}이 있었지만 이 {{PLURAL:$1|파일}}은 삭제되었습니다.",
+       "api-error-duplicate": "이 위키에 내용이 똑같은 {{PLURAL:$1|다른 파일}}이 있습니다.",
+       "api-error-duplicate-archive": "같은 내용을 담고 있던 {{PLURAL:$1|다른 파일}}이 있었지만 이 {{PLURAL:$1|파일}}은 삭제되었습니다.",
        "api-error-empty-file": "올리려는 파일이 비어 있습니다.",
        "api-error-emptypage": "새 문서로 빈 문서를 만들 수 없습니다.",
        "api-error-fetchfileerror": "내부 오류: 파일을 불러오는 중 문제가 발생했습니다.",
index 855f15c..140d80a 100644 (file)
        "upload-http-error": "Ene <i lang=\"en\">HTTP</i>-Fäähler es opjetrodde: $1",
        "upload-copy-upload-invalid-domain": "Fun dä Domain künne mer nix noh heh huh laade. Di es nit zohjelohße.",
        "upload-dialog-title": "Dateij huhlahde",
-       "upload-dialog-error": "Ene Fähler es opjetrodde",
-       "upload-dialog-warning": "En Warnong wood ußjejovve.",
        "upload-dialog-button-cancel": "Ophühre!",
        "upload-dialog-button-done": "Jedonn",
        "upload-dialog-button-save": "Faßhalde",
        "upload-dialog-button-upload": "Lohß Jonn!",
-       "upload-dialog-label-select-file": "De ußjesöhk Dattei",
-       "upload-dialog-label-infoform-title": "Eijnzelheijte",
-       "upload-dialog-label-infoform-name": "Nahme",
-       "upload-dialog-label-infoform-description": "Äkliehrong",
-       "upload-dialog-label-usage-title": "Der Jebruch",
-       "upload-dialog-label-usage-filename": "Dä Dattei iehre Nahme",
+       "upload-process-error": "Ene Fähler es opjetrodde",
+       "upload-process-warning": "En Warnong wood ußjejovve.",
+       "upload-form-label-select-file": "De ußjesöhk Dattei",
+       "upload-form-label-infoform-title": "Eijnzelheijte",
+       "upload-form-label-infoform-name": "Nahme",
+       "upload-form-label-infoform-description": "Äkliehrong",
+       "upload-form-label-usage-title": "Der Jebruch",
+       "upload-form-label-usage-filename": "Dä Dattei iehre Nahme",
        "backend-fail-stream": "Mer kunnte di Dattei $1 nit övverdraare.",
        "backend-fail-backup": "Mer kunnte kein Sescherongskopih vun dä Dattei $1 maache.",
        "backend-fail-notexists": "En Dattei $1 jidd et nit.",
        "version-entrypoints": "<i lang=\"en\" xml:lang=\"en\">URL</i>s för enzeschteije",
        "version-entrypoints-header-entrypoint": "Enschteesch",
        "version-entrypoints-header-url": "<i lang=\"en\">URL</i>",
-       "version-entrypoints-articlepath": "[https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:$wgArticlePath Der Pad noh de Atikele]",
-       "version-entrypoints-scriptpath": "[https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:$wgScriptPath Der Pad noh de Skrepte]",
+       "version-entrypoints-articlepath": "[https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:$wgArticlePath Der Pahd noh de Atikele]",
+       "version-entrypoints-scriptpath": "[https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:$wgScriptPath Der Pahd noh de Skrepte]",
        "version-libraries": "Enschtallehrte Projrammsammlonge",
        "version-libraries-library": "Projrammsammlong",
        "version-libraries-version": "Väsjohn",
index dc88ddf..4815ec7 100644 (file)
        "nstab-template": "Schabloun",
        "nstab-help": "Hëllef-Säit",
        "nstab-category": "Kategorie",
+       "mainpage-nstab": "Haaptsäit",
        "nosuchaction": "Dës Aktioun gëtt et net",
        "nosuchactiontext": "Déi Aktioun, déi an der URL ugi war, ass net valabel.\nMéiglecherweis hutt dir Iech bei der URL vertippt, oder Dir hutt op en net korrekte Link geklickt.\nEt kann awer och sinn datt et e Bug a(n) der Software op {{SITENAME}} gëtt.",
        "nosuchspecialpage": "Spezialsäit gëtt et net",
        "createacct-captcha": "Sécherheets-Check",
        "createacct-imgcaptcha-ph": "Gitt den Text an deen Dir hei driwwer gesitt",
        "createacct-submit": "Äre Benotzerkont uleeën",
-       "createacct-another-submit": "Maacht een anere Benotzerkonnt op",
+       "createacct-another-submit": "Benotzerkont uleeën",
        "createacct-benefit-heading": "{{SITENAME}} gëtt vu Leit wéi Iech gemaach.",
        "createacct-benefit-body1": "{{PLURAL:$1|Ännerung|Ännerungen}}",
        "createacct-benefit-body2": "{{PLURAL:$1|Säit|Säiten}}",
        "group-bot": "Botten",
        "group-sysop": "Administrateuren",
        "group-bureaucrat": "Bürokraten",
-       "group-suppress": "Iwwersiicht",
+       "group-suppress": "Ënnerdrécker",
        "group-all": "(all)",
        "group-user-member": "{{GENDER:$1|Benotzer}}",
        "group-autoconfirmed-member": "{{GENDER:$1|automatesch confirméiert Benotzer}}",
        "grouppage-bot": "{{ns:project}}:Botten",
        "grouppage-sysop": "{{ns:project}}:Administrateuren",
        "grouppage-bureaucrat": "{{ns:project}}:Bürokraten",
-       "grouppage-suppress": "{{ns:project}}:Iwwersiicht",
+       "grouppage-suppress": "{{ns:project}}:Ënnerdrécken",
        "right-read": "Säite liesen",
        "right-edit": "Säiten änneren",
        "right-createpage": "Säiten uleeën (déi keng Diskussiounssäite sinn)",
        "upload-http-error": "Et ass en HTTP-Feeler geschitt: $1",
        "upload-copy-upload-invalid-domain": "Vun dësem Domain ass d'Eropluede vu Kopien net méiglech.",
        "upload-dialog-title": "Fichier eroplueden",
-       "upload-dialog-error": "Et ass e Feeler geschitt",
-       "upload-dialog-warning": "Eng Warnung gouf ausgeschwat",
        "upload-dialog-button-cancel": "Ofbriechen",
        "upload-dialog-button-done": "Fäerdeg",
        "upload-dialog-button-save": "Späicheren",
        "upload-dialog-button-upload": "Eroplueden",
-       "upload-dialog-label-select-file": "Fichier eraussichen",
-       "upload-dialog-label-infoform-title": "Detailer",
-       "upload-dialog-label-infoform-name": "Numm",
-       "upload-dialog-label-infoform-description": "Beschreiwung",
-       "upload-dialog-label-usage-title": "Benotzung",
-       "upload-dialog-label-usage-filename": "Numm vum Fichier",
+       "upload-process-error": "Et ass e Feeler geschitt",
+       "upload-process-warning": "Eng Warnung gouf ausgeschwat",
+       "upload-form-label-select-file": "Fichier eraussichen",
+       "upload-form-label-infoform-title": "Detailer",
+       "upload-form-label-infoform-name": "Numm",
+       "upload-form-label-infoform-description": "Beschreiwung",
+       "upload-form-label-usage-title": "Benotzung",
+       "upload-form-label-usage-filename": "Numm vum Fichier",
        "backend-fail-stream": "De Fichier $1 konnt net iwwerdroe ginn.",
        "backend-fail-backup": "De Fichier $1 konnt net geséchert ginn.",
        "backend-fail-notexists": "De Fichier $1 gëtt et net.",
        "filerevert-legend": "De Fichier zrécksetzen.",
        "filerevert-intro": "Dir setzt de Fichier '''[[Media:$1|$1]]''' op d'[$4 Versioun vum $2, $3 Auer] zréck.",
        "filerevert-comment": "Bemierkung:",
-       "filerevert-defaultcomment": "zréckgesat op d'Versioun vum $1, $2 Auer",
+       "filerevert-defaultcomment": "Zréckgesat op d'Versioun vum $1, $2 Auer ($3)",
        "filerevert-submit": "Zrécksetzen",
        "filerevert-success": "<strong>[[Media:$1|$1]]</strong> gouf op d'[$4 Versioun vum $2, $3 Auer] zréckgesat.",
        "filerevert-badversion": "Et gëtt keng vireg lokal Versioun vun deem Fichier mat der Zäitinformatioun déi Dir uginn hutt.",
        "nopagetext": "Déi Zilsäit déi dir uginn hutt gëtt et net.",
        "pager-newer-n": "{{PLURAL:$1|nächsten|nächst $1}}",
        "pager-older-n": "{{PLURAL:$1|vireg|vireg $1}}",
-       "suppress": "Iwwersiicht",
+       "suppress": "Ënnerdrécken",
        "querypage-disabled": "Dës Spezialsäit ass aus Performance-Grënn ausgeschalt.",
        "apihelp": "API-Hëllef",
        "apihelp-no-such-module": "Modul \"$1\" net fonnt.",
        "api-error-badaccess-groups": "Et ass Iech net erlaabt fir Fichieren op dës Wiki eropzelueden.",
        "api-error-badtoken": "Interne Feeler: falschen Token.",
        "api-error-copyuploaddisabled": "D'Eroplueden iwwer eng URL ass op dësem Server desaktivéiert.",
-       "api-error-duplicate": "Et gëtt schonn {{PLURAL:$1|[$2 en anere Fichier]|[$2 aner Fichiere]}} mat dem selwechten Inhalt op dem Site",
-       "api-error-duplicate-archive": "Et gouf schonn {{PLURAL:$1| [een anere Fichier]|[$2 aner Fichieren]}} op dem Site mat deemselwechten Inhalt, {{PLURAL:$1|e gouf was|se goufen}} awer geläscht.",
+       "api-error-duplicate": "Et gëtt schonn {{PLURAL:$1|en anere Fichier|e puer aner Fichiere}} mat dem selwechten Inhalt op dem Site",
+       "api-error-duplicate-archive": "Et gouf schonn {{PLURAL:$1| een anere Fichier|e puer aner Fichieren}} op dem Site mat deemselwechten Inhalt, {{PLURAL:$1|e gouf|se goufen}} awer geläscht.",
        "api-error-empty-file": "De Fichier deen Dir geschéckt hutt war eidel.",
        "api-error-emptypage": "Et ass net erlaabt nei, eidel Säiten unzeleeën.",
        "api-error-fetchfileerror": "Interne Feeler: beim Opruffe vum Fichier huet eppes net funktionéiert.",
index 82f8593..c999710 100644 (file)
                        "아라",
                        "Aswanas",
                        "Pofka",
-                       "Albertas"
+                       "Albertas",
+                       "Macofe",
+                       "Zygimantus"
                ]
        },
-       "tog-underline": "Pabraukti nuorodas:",
+       "tog-underline": "Nuorodos pabraukimas:",
        "tog-hideminor": "Slėpti smulkius pakeitimus naujausių keitimų sąraše",
        "tog-hidepatrolled": "Slėpti patikrintus keitimus paskutinių keitimų sąraše",
        "tog-newpageshidepatrolled": "Slėpti patikrintus puslapius iš naujausių straipsnių sąrašo",
        "sat": "Šeš",
        "january": "sausio",
        "february": "vasario",
-       "march": "Kovo",
+       "march": "kovo",
        "april": "balandžio",
        "may_long": "gegužės",
        "june": "birželio",
        "july": "liepos",
        "august": "rugpjūčio",
-       "september": "Rugsėjo",
+       "september": "rugsėjo",
        "october": "spalio",
        "november": "lapkričio",
        "december": "gruodžio",
        "cancel": "Atšaukti",
        "moredotdotdot": "Daugiau...",
        "morenotlisted": "Šis sąrašas nėra išsamus.",
-       "mypage": "Naudotojo puslapis",
-       "mytalk": "Mano aptarimas",
+       "mypage": "Puslapis",
+       "mytalk": "Aptarimas",
        "anontalk": "Šio IP aptarimas",
        "navigation": "Naršymas",
        "and": "&#32;ir",
        "qbpageoptions": "Šis puslapis",
        "qbmyoptions": "Mano puslapiai",
        "faq": "DUK",
-       "faqpage": "Project:DUK",
+       "faqpage": "Projektas:DUK",
        "actions": "Veiksmai",
        "namespaces": "Vardų sritys",
        "variants": "Variantai",
        "create-this-page": "Sukurti šį puslapį",
        "delete": "Trinti",
        "deletethispage": "Ištrinti šį puslapį",
-       "undeletethispage": "Attrinti šį puslapį",
+       "undeletethispage": "Atkurti šį puslapį",
        "undelete_short": "Atkurti $1 {{PLURAL:$1:redagavimą|redagavimus|redagavimų}}",
        "viewdeleted_short": "Peržiūrėti $1 {{PLURAL:$1|ištrintą keitimą|ištrintus keitimus|ištrintų keitimų}}",
        "protect": "Užrakinti",
        "pool-servererror": "Saugyklos skaitiklio paslauga negalima ($1).",
        "poolcounter-usage-error": "Naudojimo klaida: $1",
        "aboutsite": "Apie {{SITENAME}}",
-       "aboutpage": "Project:About",
+       "aboutpage": "Projektas:Apie",
        "copyright": "Turinys pateikiamas pagal  $1  jei nenurodyta kitaip.",
        "copyrightpage": "{{ns:project}}:Autorinės teisės",
        "currentevents": "Naujienos",
        "nstab-template": "Šablonas",
        "nstab-help": "Pagalbos puslapis",
        "nstab-category": "Kategorija",
+       "mainpage-nstab": "Pagrindinis puslapis",
        "nosuchaction": "Nėra tokio veiksmo",
        "nosuchactiontext": "Veiksmas, nurodytas adrese, neatpažintas.\nGalbūt Jūs padarėte klaidą adrese ar paspaudėte ant neteisingos nuorodos.\nŠios problemos priežastis taip pat gali būti klaida programinėje įrangoje, kurią naudoja {{SITENAME}}.",
        "nosuchspecialpage": "Nėra tokio specialiojo puslapio",
        "databaseerror": "Duomenų bazės klaida",
        "databaseerror-text": "Įvyko duomenų bazės klaida.\nTai gali rodyti programinės įrangos sutrikimą.",
        "databaseerror-textcl": "Įvyko duomenų bazės klaida.",
-       "databaseerror-query": "Užklausa:$1",
+       "databaseerror-query": "Užklausa: $1",
        "databaseerror-function": "Paskirtis: $1",
        "databaseerror-error": "Klaida: $1",
        "laggedslavemode": "Dėmesio: Puslapyje gali nesimatyti naujausių pakeitimų.",
        "createacct-captcha": "Saugumo patikrinimas",
        "createacct-imgcaptcha-ph": "Įveskite tekstą, kurį matote aukščiau",
        "createacct-submit": "Sukurkite savo paskyrą",
-       "createacct-another-submit": "Sukurti kitą paskyrą",
+       "createacct-another-submit": "Sukurti paskyrą",
        "createacct-benefit-heading": "{{SITENAME}} sukurtas žmonių kaip jūs.",
        "createacct-benefit-body1": "{{PLURAL:$1|keitimas|keitimai|keitimų}}",
        "createacct-benefit-body2": "{{PLURAL:$1|puslapis|puslapiai}}",
        "permissionserrorstext-withaction": "Jūs neturite leidimo $2 dėl {{PLURAL:$1|šios priežasties|šių priežasčių}}:",
        "recreate-moveddeleted-warn": "'''Dėmesio: Jūs atkuriate puslapį, kuris anksčiau buvo ištrintas.'''\n\nTurėtumėte nuspręsti, ar reikėtų toliau redaguoti šį puslapį.\nJūsų patogumui čia pateikiamas šio puslapio šalinimų ir perkėlimų sąrašas:",
        "moveddeleted-notice": "Šis puslapis buvo ištrintas.\nŽemiau pateikiamas puslapio šalinimų ir pervadinimų sąrašas.",
+       "moveddeleted-notice-recent": "Atsiprašome, šis puslapis nesenai buvo ištrintas (per pastarąsias 24 valandas). Puslapio ištrynimo ir perkėlimo istorija yra pateikiama žemiau kaip nuoroda.",
        "log-fulllog": "Rodyti visą istoriją",
        "edit-hook-aborted": "Keitimas nutrauktas užlūžimo.\nTam nėra paaiškinimo.",
        "edit-gone-missing": "Negalima atnaujinti puslapio.\nGreičiausiai jis yra ištrintas.",
        "grouppage-bot": "{{ns:project}}:Robotai",
        "grouppage-sysop": "{{ns:project}}:Administratoriai",
        "grouppage-bureaucrat": "{{ns:project}}:Biurokratai",
-       "grouppage-suppress": "{{ns:project}}:Peržiūra",
+       "grouppage-suppress": "{{ns:project}}:Slopinti",
        "right-read": "Skaityti puslapius",
        "right-edit": "Redaguoti puslapius",
        "right-createpage": "Kurti puslapius (kurie nėra aptarimų puslapiai)",
        "upload-http-error": "Įvyko HTTP klaida: $1",
        "upload-copy-upload-invalid-domain": "Pakrovimų kopijos yra neleidžiamos iš šio domeno.",
        "upload-dialog-title": "Įkelti failą",
-       "upload-dialog-error": "Įvyko klaida",
-       "upload-dialog-warning": "Įvyko įspėjimas",
        "upload-dialog-button-cancel": "Atšaukti",
        "upload-dialog-button-done": "Atlikta",
        "upload-dialog-button-save": "Išsaugoti",
        "upload-dialog-button-upload": "Įkelti",
-       "upload-dialog-label-select-file": "Pasirinkti failą",
-       "upload-dialog-label-infoform-title": "Detalės",
-       "upload-dialog-label-infoform-name": "Pavadinimas",
-       "upload-dialog-label-infoform-description": "Aprašymas",
-       "upload-dialog-label-usage-title": "Naudojimas",
-       "upload-dialog-label-usage-filename": "Failo pavadinimas",
+       "upload-process-error": "Įvyko klaida",
+       "upload-process-warning": "Įvyko įspėjimas",
+       "upload-form-label-select-file": "Pasirinkti failą",
+       "upload-form-label-infoform-title": "Detalės",
+       "upload-form-label-infoform-name": "Pavadinimas",
+       "upload-form-label-infoform-description": "Aprašymas",
+       "upload-form-label-usage-title": "Naudojimas",
+       "upload-form-label-usage-filename": "Failo pavadinimas",
        "backend-fail-stream": "Negali būti apdorotas failas $1.",
        "backend-fail-backup": "Negali būti išsaugotas failas $1.",
        "backend-fail-notexists": "Failas $1 neegzistuoja.",
        "nopagetext": "Adresas, kurį nurodėte, neegzistuoja.",
        "pager-newer-n": "$1 {{PLURAL:$1|naujesnis|naujesni|naujesnių}}",
        "pager-older-n": "$1 {{PLURAL:$1|senesnis|senesni|senesnių}}",
-       "suppress": "Peržiūra",
+       "suppress": "Slopinti",
        "querypage-disabled": "Šiame specialiajame puslapyje yra išjungta dėl neefektyvumo.",
        "apihelp": "API pagalba",
        "apihelp-no-such-module": "Nerasta modulio $1.",
        "api-error-badtoken": "Vidinė klaida: blogai atpažinimo ženklas.",
        "api-error-copyuploaddisabled": "Siuntimas pagal URL yra išjungtas šiame serveryje.",
        "api-error-duplicate": "Jau {{PLURAL:$1|yra [$2 kitas failas]|yra [$2 kiti failai]}} puslapyje su tuo pačiu turiniu..",
-       "api-error-duplicate-archive": "Jau {{PLURAL:$1|buvo [$2 kitas failas]|buvo [$2 kitų failų]}} puslapyje su tuo pačiu turiniu, bet {{PLURAL:$1|buvo|buvo}} ištrinti.",
+       "api-error-duplicate-archive": "Jau {{PLURAL:$1|buvo kitas failas|buvo kitų failų}} puslapyje su tuo pačiu turiniu, bet {{PLURAL:$1|buvo|buvo}} ištrinti.",
        "api-error-empty-file": "Pateikta failas buvo tuščias.",
        "api-error-emptypage": "Kurti naujus, tuščius puslapius neleidžiama.",
        "api-error-fetchfileerror": "Vidinė klaida: Kažkas nutiko gaunant failą.",
index 1d8f185..fbe61bb 100644 (file)
        "upload-dialog-button-done": "Gatavs",
        "upload-dialog-button-save": "Saglabāt",
        "upload-dialog-button-upload": "Augšupielādēt",
-       "upload-dialog-label-select-file": "Izvēlieties file",
-       "upload-dialog-label-infoform-title": "Papildinformācija",
-       "upload-dialog-label-infoform-description": "Apraksts",
-       "upload-dialog-label-usage-title": "Pielietojums",
-       "upload-dialog-label-usage-filename": "Faila nosaukums",
+       "upload-form-label-select-file": "Izvēlieties file",
+       "upload-form-label-infoform-title": "Papildinformācija",
+       "upload-form-label-infoform-description": "Apraksts",
+       "upload-form-label-usage-title": "Pielietojums",
+       "upload-form-label-usage-filename": "Faila nosaukums",
        "backend-fail-stream": "Nevar straumēt failu $1.",
        "backend-fail-backup": "Nevar dublēt failu $1.",
        "backend-fail-notexists": "Fails $1 nepastāv.",
index f427852..6487b45 100644 (file)
        "upload-http-error": "Nisy tsy fetezana HTTP nitranga : $1",
        "upload-copy-upload-invalid-domain": "Tsy misy eto amin'ity dômenina ity ny tahaky ny upload.",
        "upload-dialog-title": "Hanafatra rakitra",
-       "upload-dialog-error": "Nisy hadisoana nitranga",
-       "upload-dialog-warning": "Nisy fampitandremana nitranga",
        "upload-dialog-button-cancel": "Aoka",
        "upload-dialog-button-done": "Vita",
        "upload-dialog-button-save": "Tehirizina",
        "upload-dialog-button-upload": "Mampiditra",
-       "upload-dialog-label-select-file": "Hifidy rakitra",
-       "upload-dialog-label-infoform-title": "Antsipirihany",
-       "upload-dialog-label-infoform-name": "Anarana",
-       "upload-dialog-label-infoform-description": "Famisavisana",
-       "upload-dialog-label-usage-title": "Fampiasana",
-       "upload-dialog-label-usage-filename": "Anaran-drakitra",
+       "upload-process-error": "Nisy hadisoana nitranga",
+       "upload-process-warning": "Nisy fampitandremana nitranga",
+       "upload-form-label-select-file": "Hifidy rakitra",
+       "upload-form-label-infoform-title": "Antsipirihany",
+       "upload-form-label-infoform-name": "Anarana",
+       "upload-form-label-infoform-description": "Famisavisana",
+       "upload-form-label-usage-title": "Fampiasana",
+       "upload-form-label-usage-filename": "Anaran-drakitra",
        "backend-fail-stream": "Tsy afaka mamaky ilay rakitra $1.",
        "backend-fail-backup": "Tsy afaka mitahiry ilay rakitra $1.",
        "backend-fail-notexists": "Tsy misy ilay rakitra $1.",
index 2f9e6f2..308fce4 100644 (file)
        "nstab-template": "Шаблон",
        "nstab-help": "Страница за помош",
        "nstab-category": "Категорија",
+       "mainpage-nstab": "Главна страница",
        "nosuchaction": "Нема такво дејство",
        "nosuchactiontext": "Дејството укажано во URL-адресата е погрешно.\nМожеби имате грешка во пишувањето на адресата, или пак имате проследено погрешна врска.\nОва може да се должи и на грешка во програмската опрема на {{SITENAME}}.",
        "nosuchspecialpage": "Не постои таква службена страница",
        "createacct-captcha": "Безбедносна проверка",
        "createacct-imgcaptcha-ph": "Внесете го гореприкажаниот текст",
        "createacct-submit": "Направи ја",
-       "createacct-another-submit": "Создајте друга сметка",
+       "createacct-another-submit": "Создај сметка",
        "createacct-benefit-heading": "{{SITENAME}} е дело на луѓе како вас.",
        "createacct-benefit-body1": "{{PLURAL:$1|уредување|уредувања}}",
        "createacct-benefit-body2": "{{PLURAL:$1|страница|страници}}",
        "group-bot": "Ботови",
        "group-sysop": "Администратори",
        "group-bureaucrat": "Бирократи",
-       "group-suppress": "СкÑ\80ивачи",
+       "group-suppress": "Ð\9fÑ\80иÑ\82аÑ\98Ñ\83вачи",
        "group-all": "(сите)",
        "group-user-member": "корисник",
        "group-autoconfirmed-member": "автопотврден корисник",
        "group-bot-member": "бот",
        "group-sysop-member": "администратор",
        "group-bureaucrat-member": "бирократ",
-       "group-suppress-member": "{{GENDER:$1|скривачи}}",
+       "group-suppress-member": "{{GENDER:$1|притајувач}}",
        "grouppage-user": "{{ns:project}}:Корисници",
        "grouppage-autoconfirmed": "{{ns:project}}:Автопотврдени корисници",
        "grouppage-bot": "{{ns:project}}:Ботови",
        "grouppage-sysop": "{{ns:project}}:Администратори",
        "grouppage-bureaucrat": "{{ns:project}}:Бирократи",
-       "grouppage-suppress": "{{ns:project}}:СкÑ\80ивање",
+       "grouppage-suppress": "{{ns:project}}:Ð\9fÑ\80иÑ\82аÑ\98Ñ\83вање",
        "right-read": "Читање страници",
        "right-edit": "Уредување страници",
        "right-createpage": "Создавање на страници (кои не се страници за разговор)",
        "upload-http-error": "Се појави грешка во HTTP: $1.",
        "upload-copy-upload-invalid-domain": "Примероци од подигањата не се достапни на овој домен.",
        "upload-dialog-title": "Подигни податотека",
-       "upload-dialog-error": "Се појави грешка",
-       "upload-dialog-warning": "Се јави предупредување",
        "upload-dialog-button-cancel": "Откажи",
        "upload-dialog-button-done": "Готово",
        "upload-dialog-button-save": "Зачувај",
        "upload-dialog-button-upload": "Подигни",
-       "upload-dialog-label-select-file": "Одберете податотека",
-       "upload-dialog-label-infoform-title": "Подробно",
-       "upload-dialog-label-infoform-name": "Назив",
-       "upload-dialog-label-infoform-description": "Опис",
-       "upload-dialog-label-usage-title": "Употреба",
-       "upload-dialog-label-usage-filename": "Назив на податотеката",
+       "upload-process-error": "Се појави грешка",
+       "upload-process-warning": "Се јави предупредување",
+       "upload-form-label-select-file": "Одберете податотека",
+       "upload-form-label-infoform-title": "Подробно",
+       "upload-form-label-infoform-name": "Назив",
+       "upload-form-label-infoform-description": "Опис",
+       "upload-form-label-usage-title": "Употреба",
+       "upload-form-label-usage-filename": "Назив на податотеката",
        "backend-fail-stream": "Не можев да ја емитувам податотеката $1.",
        "backend-fail-backup": "Не можев да направам резерва на податотеката $1.",
        "backend-fail-notexists": "Податотеката $1 не постои.",
        "filerevert-legend": "Врати податотека",
        "filerevert-intro": "Ја враќате '''[[Media:$1|$1]]''' на [$4 верзијата од $3, $2].",
        "filerevert-comment": "Причина:",
-       "filerevert-defaultcomment": "Вратена на верзија од $2, $1",
+       "filerevert-defaultcomment": "Вратена на верзија од $2, $1 ($3)",
        "filerevert-submit": "Врати",
        "filerevert-success": "'''[[Media:$1|$1]]''' е вратен на [$4 верзијата од $3, $2].",
        "filerevert-badversion": "Нема претходна месна верзија на оваа податотека со даденото време.",
        "nopagetext": "Целната страница која ја наведовте не постои.",
        "pager-newer-n": "{{PLURAL:$1|понова 1|понови $1}}",
        "pager-older-n": "{{PLURAL:$1|постара 1|постари $1}}",
-       "suppress": "СкÑ\80иваÑ\9aе",
+       "suppress": "Ð\9fÑ\80иÑ\82аи",
        "querypage-disabled": "Оваа службена страница е оневозможена за да не попречува на делотворноста.",
        "apihelp": "Помош со извршникот",
        "apihelp-no-such-module": "Модулот „$1“ не е пронајден.",
        "emailccsubject": "Копија од вашата порака до $1: $2",
        "emailsent": "Писмото е испратено",
        "emailsenttext": "Писмото е испратено.",
-       "emailuserfooter": "$1 го испрати писмово на $2 со помош на функцијата „{{int:emailuser}}“ на {{SITENAME}}.",
+       "emailuserfooter": "$1 го испрати писмово на {{GENDER:$2|$2}} со помош на функцијата „{{int:emailuser}}“ на {{SITENAME}}.",
        "usermessage-summary": "Оставете системска порака.",
        "usermessage-editor": "Системски гласник",
        "usermessage-template": "MediaWiki:КорисникПорака",
        "logentry-newusers-byemail": "$1 {{GENDER:$2|ја направи}} корисничката сметка $3. Лозинката ви ја испративме по е-пошта",
        "logentry-newusers-autocreate": "Автоматски {{GENDER:$2|создадена}} корисничката сметка $1",
        "logentry-protect-move_prot": "$1 ги {{GENDER:$2|премести}} заштитните поставки од $4 на $3",
+       "logentry-protect-unprotect": "$1 {{GENDER:$2|ја отстрани}} заштитата од $3",
+       "logentry-protect-protect": "$1 {{GENDER:$2|стави заштита}} на $3 $4",
+       "logentry-protect-protect-cascade": "$1 {{GENDER:$2|стави заштита}} на $3 $4 [каскадно]",
+       "logentry-protect-modify": "$1 {{GENDER:$2|го измени}} степенот на заштита на $3 $4",
+       "logentry-protect-modify-cascade": "$1 {{GENDER:$2|го измени}} степенот на заштита на $3 $4 [каскадно]",
        "logentry-rights-rights": "$1 {{GENDER:$2|го измени}} групното членство на $3 од $4 во $5",
        "logentry-rights-rights-legacy": "$1 {{GENDER:$2|го измени}} групното членство во $3",
        "logentry-rights-autopromote": "$1 автоматски {{GENDER:$2|унапреден|унапредена}} од $4 во $5",
        "api-error-badaccess-groups": "Не ви е дозволено да подигате податотеки на ова вики.",
        "api-error-badtoken": "Внатрешна грешка: неисправна шифра.",
        "api-error-copyuploaddisabled": "Подигањата со URL се оневозможени на овој опслужувач.",
-       "api-error-duplicate": "Веќе {{PLURAL:$1|постои [$2 друга податотека]|постојат [$2 други податотеки]}} со истата содржина",
-       "api-error-duplicate-archive": "На мрежното место веќе {{PLURAL:$1|постоела [$2 друга податотека]|постоеле [$2 други податотеки]}} со истата содржина, но во меѓувреме {{PLURAL:$1|е избришана|се избришани}}.",
+       "api-error-duplicate": "Веќе {{PLURAL:$1|постои друга податотека|постојат други податотеки}} со истата содржина.",
+       "api-error-duplicate-archive": "На мрежното место веќе {{PLURAL:$1|постоела друга податотека|постоеле други податотеки}} со истата содржина, но во меѓувреме {{PLURAL:$1|била избришана|биле избришани}}.",
        "api-error-empty-file": "Поднесената податотека е празна.",
        "api-error-emptypage": "Создавањето на нови празни страници не е дозволено.",
        "api-error-fetchfileerror": "Внатрешна грешка: нешто тргна наопаку при добивањето на податотеката.",
index e45be59..79f988e 100644 (file)
@@ -29,7 +29,8 @@
                        "לערי ריינהארט",
                        "아라",
                        "Viswaprabha",
-                       "Nesi"
+                       "Nesi",
+                       "Macofe"
                ]
        },
        "tog-underline": "കണ്ണികൾക്ക് അടിവരയിടുക:",
        "nstab-template": "ഫലകം",
        "nstab-help": "സഹായ താൾ",
        "nstab-category": "വർഗ്ഗം",
+       "mainpage-nstab": "പ്രധാന താൾ",
        "nosuchaction": "ഈ പ്രവൃത്തി അസാധുവാണ്‌",
        "nosuchactiontext": "യു.ആർ.എൽ. വഴി നിർവചിച്ച പ്രവർത്തനം വിക്കി തിരിച്ചറിഞ്ഞില്ല. താങ്കൾ യു.ആർ.എൽ. തെറ്റായി നൽകിയിരിക്കാം അല്ലെങ്കിൽ ഒരു തെറ്റായ ലിങ്കുവഴി വന്നിരിക്കാം.\nഒരുപക്ഷേ, ഇത് {{SITENAME}} ഉപയോഗിക്കുന്ന സോഫ്റ്റ്‌വെയറിലെ ബഗ്ഗും ആകാം.",
        "nosuchspecialpage": "അത്തരമൊരു പ്രത്യേകതാൾ നിലവിലില്ല",
        "createacct-captcha": "സുരക്ഷാ പരിശോധന",
        "createacct-imgcaptcha-ph": "മുകളിൽ കാണുന്ന എഴുത്ത് ഇവിടെ നൽകുക",
        "createacct-submit": "താങ്കളുടെ അംഗത്വം സൃഷ്ടിക്കുക",
-       "createacct-another-submit": "മറàµ\8dà´±àµ\8aà´°àµ\81 à´\85à´\82à´\97à´¤àµ\8dവമàµ\86à´\9fàµ\81à´\95àµ\8dà´\95àµ\81à´\95",
+       "createacct-another-submit": "അംഗത്വമെടുക്കുക",
        "createacct-benefit-heading": "താങ്കളെപ്പോലെയുള്ളവരാണ്  {{SITENAME}}  പടുത്തുയർത്തിയിരിക്കുന്നത്.",
        "createacct-benefit-body1": "{{PLURAL:$1|തിരുത്ത്|തിരുത്തുകൾ}}",
        "createacct-benefit-body2": "{{PLURAL:$1|താൾ|താളുകൾ}}",
        "changeemail-password": "താങ്കളുടെ {{SITENAME}} രഹസ്യവാക്ക്:",
        "changeemail-submit": "ഇമെയിലിൽ മാറ്റംവരുത്തുക",
        "changeemail-throttled": "താങ്കൾ നിരവധി തവണ പ്രവേശിക്കാൻ ശ്രമിച്ചിരിക്കുന്നു.\nവീണ്ടും ശ്രമിക്കുന്നതിനു മുമ്പ് ദയവായി $1 കാത്തിരിക്കുക.",
+       "changeemail-nochange": "ദയവായി വേറെ ഒരു പുതിയ ഇമെയിൽ വിലാസം നൽകുക.",
        "resettokens": "ചീട്ടുകൾ പുനഃസജ്ജീകരിക്കുക",
        "resettokens-text": "താങ്കളുടെ അംഗത്വവുമായി ബന്ധപ്പെട്ടുള്ള ചില സ്വകാര്യവിവരങ്ങളിലേയ്ക്ക് ഇവിടെ ലഭ്യത സാദ്ധ്യമാക്കുന്ന ചീട്ടുകൾ താങ്കൾക്ക് പുനഃസജ്ജീകരിക്കാവുന്നതാണ്.\n\nതാങ്കളുടെ അംഗത്വവിവരങ്ങൾ മറ്റാർക്കെങ്കിലും അറിയാതെ കൈമാറിയിട്ടുണ്ടെങ്കിലോ താങ്കളുടെ അംഗത്വം അപഹരിക്കപ്പെട്ടുവെങ്കിലോ താങ്കളിത്  ചെയ്യേണ്ടതാണ്.",
        "resettokens-no-tokens": "ചീട്ടുകളൊന്നും പുനഃസജ്ജീകരിക്കാനില്ല.",
        "permissionserrorstext-withaction": "താങ്കൾക്ക് $2 എന്ന പ്രവൃത്തി ചെയ്യാൻ അനുമതി ഇല്ല, {{PLURAL:$1|കാരണം|കാരണങ്ങൾ}} താഴെ കൊടുത്തിരിക്കുന്നു:",
        "recreate-moveddeleted-warn": "'''മുന്നറിയിപ്പ്: മുമ്പ് മായ്ച്ചുകളഞ്ഞ താളാണ്‌ താങ്കൾ വീണ്ടും ചേർക്കാൻ ശ്രമിക്കുന്നത്'''\n\nതാങ്കൾ ചെയ്യുന്നത് ശരിയായ നടപടിയാണോ എന്നു പരിശോധിക്കുക. ഉറപ്പിനായി ഈ താളിന്റെ മായ്ക്കൽ രേഖയും മാറ്റൽ രേഖയും കൂടെ ചേർത്തിരിക്കുന്നു.",
        "moveddeleted-notice": "ഈ താൾ മായ്ക്കപ്പെട്ടിരിക്കുന്നു.\nഈ താളിന്റെ മായ്ക്കൽ രേഖ പരിശോധനയ്ക്കായി താഴെ കൊടുത്തിരിക്കുന്നു",
+       "moveddeleted-notice-recent": "ക്ഷമിക്കുക, ഈ താൾ ഈയടുത്ത് (കഴിഞ്ഞ 24 മണിക്കൂറിനുള്ളിൽ) മായ്ക്കപ്പെട്ടു.\nഅവലംബമായി മായ്ക്കലിന്റെയും താൾ നീക്കിയതിന്റെയും രേഖ താഴെ കൊടുത്തിരിക്കുന്നു.",
        "log-fulllog": "എല്ലാ രേഖകളും കാണുക",
        "edit-hook-aborted": "തിരുത്തൽ കൊളുത്തിനാൽ റദ്ദാക്കിയിരിക്കുന്നു.\nവിശദീകരണമൊന്നും നൽകിയിട്ടില്ല.",
        "edit-gone-missing": "ഈ താൾ പുതുക്കുവാൻ സാധിക്കുകയില്ല.\nഇത് മായ്ക്കപ്പെട്ടതായി കാണുന്നു.",
        "group-bot": "യന്ത്രങ്ങൾ",
        "group-sysop": "കാര്യനിർവാഹകർ",
        "group-bureaucrat": "ബ്യൂറോക്രാറ്റുകൾ",
-       "group-suppress": "à´®àµ\87ൽനàµ\8bà´\9fàµ\8dà´\9fà´\99àµ\8dà´\99ൾ",
+       "group-suppress": "à´\85മർà´\9aàµ\8dà´\9aà´\95ർ",
        "group-all": "(എല്ലാം)",
        "group-user-member": "{{GENDER:$1|ഉപയോക്താവ്}}",
        "group-autoconfirmed-member": "{{GENDER:$1|യാന്ത്രികമായി സ്ഥിരീകരിക്കപ്പെട്ട ഉപയോക്താവ്}}",
        "group-bot-member": "{{GENDER:$1|യന്ത്രം}}",
        "group-sysop-member": "{{GENDER:$1|കാര്യനിർവാഹകൻ|കാര്യനിർവാഹക}}",
        "group-bureaucrat-member": "{{GENDER:$1|ബ്യൂറോക്രാറ്റ്}}",
-       "group-suppress-member": "{{GENDER:$1|à´®àµ\87ൽനàµ\8bà´\9fàµ\8dà´\9fà´\82}}",
+       "group-suppress-member": "{{GENDER:$1|à´\85മർà´\9aàµ\8dà´\9aà´\95|à´\85മർà´\9aàµ\8dà´\9aà´\95ൻ}}",
        "grouppage-user": "{{ns:project}}:ഉപയോക്താക്കൾ",
        "grouppage-autoconfirmed": "{{ns:project}}:യാന്ത്രികമായി സ്ഥിരീകരിക്കപ്പെട്ട ഉപയോക്താക്കൾ",
        "grouppage-bot": "{{ns:project}}:യന്ത്രങ്ങൾ",
        "grouppage-sysop": "{{ns:project}}:കാര്യനിർവാഹകർ",
        "grouppage-bureaucrat": "{{ns:project}}:ബ്യൂറോക്രാറ്റ്",
-       "grouppage-suppress": "{{ns:project}}:à´®àµ\87ൽനàµ\8bà´\9fàµ\8dà´\9fà´\82",
+       "grouppage-suppress": "{{ns:project}}:à´\92à´¤àµ\81à´\95àµ\8dà´\95ൽ",
        "right-read": "\nതാളുകൾ വായിക്കുക",
        "right-edit": "താളുകൾ തിരുത്തുക",
        "right-createpage": "താളുകൾ സൃഷ്ടിക്കുക (സംവാദം താളുകൾ അല്ലാത്തവ)",
        "upload-http-error": "ഒരു എച്ച്.റ്റി.റ്റി.പി. പിഴവു സംഭവിച്ചിരിക്കുന്നു: $1",
        "upload-copy-upload-invalid-domain": "ഈ ഡൊമൈനിൽ നിന്നും പകർത്തി അപ്‌ലോഡ് ചെയ്യൽ ലഭ്യമല്ല.",
        "upload-dialog-title": "പ്രമാണം അപ്‌ലോഡ് ചെയ്യുക",
-       "upload-dialog-error": "ഒരു പിഴവുണ്ടായി",
-       "upload-dialog-warning": "ഒരു മുന്നറിയിപ്പുണ്ടായി",
        "upload-dialog-button-cancel": "റദ്ദാക്കുക",
        "upload-dialog-button-done": "ചെയ്തു കഴിഞ്ഞു",
        "upload-dialog-button-save": "സേവ് ചെയ്യുക",
        "upload-dialog-button-upload": "അപ്‌‌ലോഡ്",
-       "upload-dialog-label-select-file": "പ്രമാണം തിരഞ്ഞെടുക്കുക",
-       "upload-dialog-label-infoform-title": "വിശദാംശങ്ങൾ",
-       "upload-dialog-label-infoform-name": "പേര്‌",
-       "upload-dialog-label-infoform-description": "വിവരണം",
-       "upload-dialog-label-usage-title": "ഉപയോഗം",
-       "upload-dialog-label-usage-filename": "പ്രമാണത്തിന്റെ പേര്",
+       "upload-process-error": "ഒരു പിഴവുണ്ടായി",
+       "upload-process-warning": "ഒരു മുന്നറിയിപ്പുണ്ടായി",
+       "upload-form-label-select-file": "പ്രമാണം തിരഞ്ഞെടുക്കുക",
+       "upload-form-label-infoform-title": "വിശദാംശങ്ങൾ",
+       "upload-form-label-infoform-name": "പേര്‌",
+       "upload-form-label-infoform-description": "വിവരണം",
+       "upload-form-label-usage-title": "ഉപയോഗം",
+       "upload-form-label-usage-filename": "പ്രമാണത്തിന്റെ പേര്",
        "backend-fail-stream": "$1 എന്ന പ്രമാണം സ്ട്രീം ചെയ്യാൻ കഴിഞ്ഞില്ല.",
        "backend-fail-backup": "$1 എന്ന പ്രമാണത്തിന്റെ ബാക്ക്അപ് എടുക്കാൻ കഴിഞ്ഞില്ല.",
        "backend-fail-notexists": "$1 എന്ന പ്രമാണം നിലവിലില്ല.",
        "filerevert-legend": "പ്രമാണം തിരസ്ക്കരിക്കുക",
        "filerevert-intro": "താങ്കൾ '''[[Media:$1|$1]]''' എന്ന പ്രമാണത്തെ, [$4 $2, $3-ന് ഉണ്ടായിരുന്ന പതിപ്പിലേക്ക്] സേവ് ചെയ്യുകയാണ്‌.",
        "filerevert-comment": "കാരണം:",
-       "filerevert-defaultcomment": "$2 ൽ ഉണ്ടായിരുന്ന $1 പതിപ്പിലേക്കു സേവ് ചെയ്തിരിക്കുന്നു",
+       "filerevert-defaultcomment": "$1, $2 ($3)-നു ഉണ്ടായിരുന്ന പതിപ്പിലേക്കു സേവ് ചെയ്തിരിക്കുന്നു",
        "filerevert-submit": "പുനഃസ്ഥാപിക്കുക",
        "filerevert-success": "'''[[Media:$1|$1]]''' യെ,  [$3, $2 ഉണ്ടായിരുന്ന $4] പതിപ്പിലേക്കു സേവ് ചെയ്തിരിക്കുന്നു.",
        "filerevert-badversion": "താങ്കൾ തന്ന സമയവുമായി യോജിക്കുന്ന മുൻ പതിപ്പുകൾ ഒന്നും തന്നെ ഈ പ്രമാണത്തിനില്ല.",
        "nopagetext": "താങ്കൾ വ്യക്തമാക്കിയ ലക്ഷ്യതാൾ നിലവിലില്ല.",
        "pager-newer-n": "{{PLURAL:$1|പുതിയ 1|പുതിയ $1}}",
        "pager-older-n": "{{PLURAL:$1|പഴയ 1|പഴയ $1}}",
-       "suppress": "à´®àµ\87ൽനàµ\8bà´\9fàµ\8dà´\9fà´\82",
+       "suppress": "à´\92à´¤àµ\81à´\95àµ\8dà´\95àµ\81à´\95",
        "querypage-disabled": "പ്രവർത്തനമികവിനെ ബാധിക്കുന്ന കാരണങ്ങളാൽ ഈ പ്രത്യേക താൾ പ്രവർത്തന രഹിതമാക്കിയിരിക്കുന്നു.",
        "apihelp": "എ.പി.ഐ. സഹായം",
        "apihelp-no-such-module": "ഘടകം \"$1\" കണ്ടെത്താനായില്ല.",
        "emailccsubject": "$1 എന്ന ഉപയോക്താവിനയച്ച സന്ദേശത്തിന്റെ പകർപ്പ്: $2",
        "emailsent": "ഇമെയിൽ അയച്ചിരിക്കുന്നു",
        "emailsenttext": "താങ്കളുടെ ഇമെയിൽ അയച്ചു കഴിഞ്ഞിരിക്കുന്നു.",
-       "emailuserfooter": "ഈ ഇമെയിൽ, {{SITENAME}} സംരംഭത്തിലെ \"{{int:emailuser}}\" എന്ന സൗകര്യം ഉപയോഗിച്ച്, $1 എന്ന ഉപയോക്താവ് $2 എന്ന ഉപയോക്താവിന് അയച്ചതാണ്.",
+       "emailuserfooter": "ഈ ഇമെയിൽ, {{SITENAME}} സംരംഭത്തിലെ \"{{int:emailuser}}\" എന്ന സൗകര്യം ഉപയോഗിച്ച്, $1 എന്ന ഉപയോക്താവ് {{GENDER:$2|$2}} എന്ന ഉപയോക്താവിന് {{GENDER:$1|അയച്ചതാണ്}}.",
        "usermessage-summary": "വ്യവസ്ഥാസന്ദേശം ഉപേക്ഷിക്കുക.",
        "usermessage-editor": "വ്യവസ്ഥാസന്ദേശകൻ",
        "watchlist": "ശ്രദ്ധിക്കുന്നവ",
        "api-error-badaccess-groups": "ഈ വിക്കിയിൽ പ്രമാണങ്ങൾ അപ്‌ലോഡ് ചെയ്യാൻ താങ്കൾക്കനുവാദമില്ല.",
        "api-error-badtoken": "ആന്തരിക പിഴവ്: ഗുണകരമല്ലാത്ത ചീട്ട്.",
        "api-error-copyuploaddisabled": "യൂ.ആർ.എൽ. ഉപയോഗിച്ചുള്ള അപ്‌ലോഡ് ഈ സെർവറിൽ പ്രവർത്തനസജ്ജമാക്കിയിട്ടില്ല.",
-       "api-error-duplicate": "വിക്കിയിൽ ഇതേ ഉള്ളടക്കമുള്ള {{PLURAL:$1|[$2 മറ്റൊരു പ്രമാണം]|[$2 മറ്റ് പ്രമാണങ്ങൾ]}} മുമ്പേയുണ്ട്.",
-       "api-error-duplicate-archive": "സൈറ്റിൽ ഇതേ ഉള്ളടക്കമുള്ള {{PLURAL:$1|[$2 മറ്റൊരു പ്രമാണം]|[$2 മറ്റ് പ്രമാണങ്ങൾ]}} ഉണ്ടായിരുന്നു, പക്ഷേ {{PLURAL:$1|അത്|അവ}} മായ്ക്കപ്പെട്ടിട്ടുണ്ട്.",
+       "api-error-duplicate": "വിക്കിയിൽ ഇതേ ഉള്ളടക്കമുള്ള {{PLURAL:$1|മറ്റൊരു പ്രമാണം|മറ്റ് പ്രമാണങ്ങൾ}} മുമ്പേയുണ്ട്.",
+       "api-error-duplicate-archive": "സൈറ്റിൽ ഇതേ ഉള്ളടക്കമുള്ള {{PLURAL:$1|മറ്റൊരു പ്രമാണം|മറ്റ് പ്രമാണങ്ങൾ}} ഉണ്ടായിരുന്നു, പക്ഷേ {{PLURAL:$1|അത്|അവ}} മായ്ക്കപ്പെട്ടിട്ടുണ്ട്.",
        "api-error-empty-file": "താങ്കൾ സമർപ്പിച്ച പ്രമാണം ശൂന്യമാണ്.",
        "api-error-emptypage": "ശൂന്യമായ പുതിയ താളുകൾ സൃഷ്ടിക്കുന്നത് അനുവദിക്കുന്നില്ല.",
        "api-error-fetchfileerror": "ആന്തരിക പിഴവ്: പ്രമാണം ശേഖരിച്ചുകൊണ്ടിരുന്നപ്പോൾ എന്തോ പിഴവുണ്ടായി.",
index c61c74f..4ee0e12 100644 (file)
        "api-error-badtoken": "Ralat dalaman: token tak elok.",
        "api-error-copyuploaddisabled": "Ciri memuat naik melalui URL dimatikan di pelayan ini.",
        "api-error-duplicate": "Di tapak ini sudah ada {{PLURAL:$1|[$2 satu fail lain]|[$2 fail-fail lain]}} yang sama kandungannya.",
-       "api-error-duplicate-archive": "Di tapak ini pernah ada {{PLURAL:$1|[$2 satu fail lain]|[$2 fail-fail lain]}} yang sama kandungannya, tetapi telah dihapuskan.",
+       "api-error-duplicate-archive": "Di tapak ini pernah ada {{PLURAL:$1|satu fail lain|fail-fail lain}} yang sama kandungannya, tetapi telah dihapuskan.",
        "api-error-empty-file": "Fail yang anda serahkan adalah kosong.",
        "api-error-emptypage": "Anda tidak dibenarkan membuat laman baru yang kosong.",
        "api-error-fetchfileerror": "Ralat dalaman: ada malasah ketika mengambil fail itu.",
index 432b416..8ecf7ba 100644 (file)
@@ -10,7 +10,8 @@
                        "Spacebirdy",
                        "محک",
                        "아라",
-                       "Alirezaaa"
+                       "Alirezaaa",
+                       "Macofe"
                ]
        },
        "tog-underline": "پیوندون زیر خط دکشی بواشه",
        "api-error-badtoken": "خطای داخلی: کد امنیتی اشتبائه (Bad token).",
        "api-error-copyuploaddisabled": "باربی‌یشتن با استفاده از نشونی اینترنتی این کارساز دله غیرفعاله.",
        "api-error-duplicate": "{{PLURAL:$1|[$2 پروندهٔ دیگه‌یی]|[$2 چن پروندهٔ دیگه]}} وب‌گاه دله با محتوای ات‌تی دیی‌یه.",
-       "api-error-duplicate-archive": "{{PLURAL:$1|[$2 پروندهٔ دیگه‌یی]|[$2 چن پروندهٔ دیگه]}} وب‌گاه دله با محتوای اتجور وجود داشته، ولی حذف {{PLURAL:$1|بیی‌یه|بیی‌نه}}.",
-       "api-error-duplicate-archive-popup-title": "{{PLURAL:$1|پروندهٔ|پرونده‌ئون}} تکراری که اسا حذف بیی‌نه",
-       "api-error-duplicate-popup-title": "{{PLURAL:$1|پرونده|پرونده‌ئون}} تکراری",
+       "api-error-duplicate-archive": "{{PLURAL:$1|پروندهٔ دیگه‌یی|چن پروندهٔ دیگه}} وب‌گاه دله با محتوای اتجور وجود داشته، ولی حذف {{PLURAL:$1|بیی‌یه|بیی‌نه}}.",
        "api-error-empty-file": "پرونده‌ای که شما برسنینی خالی بی‌یه.",
        "api-error-fetchfileerror": "خطای داخلی: زمون بییتن پرونده، اتا چی درست پیش نشی‌یه.",
        "api-error-file-too-large": "پرونده‌ای که شما برسنینی خله خله گت بی‌یه.",
index 7b1173a..0047452 100644 (file)
        "nstab-template": "Nemachiòtl",
        "nstab-help": "Tèpalèwilistli",
        "nstab-category": "Tlaìxmatkàtlàlilòtl",
+       "mainpage-nstab": "Huēyitlaīxtli",
        "nosuchaction": "Ahmo ia tlachīhualiztli",
        "nosuchspecialpage": "Âmò ka inòn nònkuâkìskàtlaìxtlapalli",
        "nospecialpagetext": "<strong>Tiknẻki sè nònkuâkìskàtlaìxtlapalli tlèn âmò kä.</strong>\n\nKualli tikỉtas sè ìntlapòpòwaltekpànal in nònkuâkìskàtlaìxtlapaltìn ìpan [[Special:SpecialPages|{{int:specialpages}}]].",
        "error": "Ahcuallōtl",
        "databaseerror": "Tlahcuilōltzintlān īahcuallo",
+       "databaseerror-query": "Tlahtlanilli: $1",
        "laggedslavemode": "Xiquitta: huel ahmo ia achi yancuīc in tlapatlaliztli inīn zāzanilco.",
        "readonly": "Mactzīntlantli tzahtzacticah",
        "missingarticle-rev": "(tlachiyaliztli ītlapōhual: $1)",
        "edit-gone-missing": "Ahmo huelīti yancuīya zāzanilli.\nHueliz ōmopolo.",
        "edit-conflict": "Tlapatlaliztli yāōyōtōn",
        "edit-already-exists": "Ahmo mohuelīti mochīhua yancuīc zāzanilli.\nYe ia.",
+       "content-model-javascript": "JavaScript",
        "cantcreateaccounttitle": "Ahmo huelītih mochīhua cuentah",
        "cantcreateaccount-text": "[[User:$3|$3]] ōcquīxti cuentah tlachīhualiztli īpal inīn IP ('''$1''').\n\nĪxtlamatiliztli īpal $3 cah ''$2''",
        "viewpagelogs": "Tiquinttāz tlahcuilōlloh inīn zāzaniltechcopa",
        "email": "E-mail",
        "prefs-help-realname": "Melāhuac motōca.\nIntlā ticnequi, tlācah quimatīzqueh motequi.",
        "prefs-help-email-required": "Tihuīquilia quihcuiloa mo e-mailcān.",
+       "prefs-signature": "Motōcā",
        "userrights-user-editname": "Xihcuiloa cē tlatequitiltilīltōcāitl:",
        "editusergroup": "Tiquimpatlāz tlatequitiltilīlli olōlli",
        "userrights-editusergroup": "Tiquimpatlāz tlatequitiltilīlli olōlli",
        "savefile": "Quipiyāz tlahcuilōlli",
        "uploaddisabled": "Ahmo mohuelīti tlahcuilōlquetzā",
        "uploaddisabledtext": "Ahmo huelīti moquetzazqueh tlahcuilōlli.",
+       "upload-source": "Mēyalihcuilōlli",
        "sourcefilename": "Tōcāhuīcpa:",
+       "sourceurl": "Mēyal-URL:",
        "destfilename": "Tōcāhuīc:",
        "watchthisupload": "Tictlachiyāz inīn zāzanilli",
        "upload-success-subj": "Cualli quetzaliztli",
+       "upload-form-label-infoform-name": "Tōcāitl",
+       "upload-form-label-usage-filename": "Ihcuilōlli ītōcā",
        "upload_source_file": " (cē tlahcuilōlli mochīuhpōhualhuazco)",
        "listfiles_search_for": "Tlatēmōz mēdiatl tōcācopa:",
        "imgfile": "īxiptli",
        "listfiles_name": "Tōcāitl",
        "listfiles_user": "Tlatequitiltilīlli",
        "listfiles_size": "Octacayōtl (bytes)",
+       "listfiles_count": "Cuepaliztli",
+       "listfiles-latestversion-yes": "Quēmah",
+       "listfiles-latestversion-no": "Ahmō",
        "file-anchor-link": "Īxiptli",
        "filehist": "Tlahcuilōlli tlahcuilōlloh",
        "filehist-deleteall": "tiquimpolōz mochīntīn",
        "deadendpages": "Ahtlaquīzaliztli zāzaniltin",
        "protectedpages": "Zāzaniltin ōmoquīxti",
        "protectedpages-indef": "Zan ahcāhuitl tlaquīxtiliztli",
+       "protectedpages-page": "Tlaīxtli",
+       "protectedpages-reason": "Tleīpampa",
        "protectedtitles": "Tōcāitl ōmoquīxtih",
        "listusers": "Tlatequitiltilīlli",
        "newpages": "Yancuīc zāzaniltin",
        "delete-edit-reasonlist": "Tiquimpatlāz īxtlamatiliztli tlapoloaliztechcopa",
        "rollbacklink": "tlacuepāz",
        "rollback-success": "Ōmotlacuep $1 ītlahcuilōl; āxcān achto $2 ītlahcuilōl.",
+       "changecontentmodel-title-label": "Tlaīxtōcāitl",
+       "changecontentmodel-reason-label": "Tleīpampa:",
        "protectedarticle": "ōmoquīxti \"[[$1]]\"",
        "unprotectedarticle": "ōahmoquīxtih «[[$1]]»",
        "prot_1movedto2": "[[$1]] ōmozacac īhuīc [[$2]]",
+       "protectcomment": "Tleīpampa:",
        "protectexpiry": "Tlamiliztli:",
        "protect_expiry_invalid": "Ahcualli tlamiliztli cāhuitl.",
        "protect-default": "Ticmācāhuaz mochintin in tlatequitiltilīltin",
        "undeletebtn": "Ahticpolōz",
        "undeletelink": "tiquittaz/ticpahtīz",
        "undeleteviewlink": "tiquittāz",
+       "undeletecomment": "Tleīpampa:",
        "undelete-search-box": "Tiquintlatēmōz zāzaniltin ōmopolōz",
        "undelete-search-prefix": "Tiquittāz zāzaniltin mopēhua īca:",
        "undelete-search-submit": "Tlatēmōz",
        "ipb-unblock": "Ahtiquitzacuilīz IP nozo tlatequitiltilīlli",
        "unblockip": "Ahtiquitzacuilīz tlatequitiltilīlli",
        "ipblocklist": "Tlatequitiltilīltzacualli",
+       "blocklist-reason": "Tleīpampa",
        "ipblocklist-submit": "Tlatēmōz",
        "infiniteblock": "ahtlamic",
        "expiringblock": "tlami īpan $1 īpan $2",
        "thumbnail-more": "Tiquihuēyiyāz",
        "thumbnail_error": "Aiuhcāyōtl ihcuāc mochīhuaya tepitōntli: $1",
        "import": "Tiquincōhuāz zāzaniltin",
+       "import-interwiki-sourcewiki": "Mēyalhuiqui:",
+       "import-interwiki-sourcepage": "Mēyallaīxtli:",
        "import-interwiki-submit": "Tiquicōhuāz",
        "import-upload-filename": "Tlahcuilōltōcāitl:",
        "importstart": "Motlacōhua zāzaniltin...",
        "siteusers": "$1 {{PLURAL:$2|tlatequitiltilīlli}} īpan {{SITENAME}}",
        "spam_reverting": "Mocuepacah īhuīc xōcoyōc tlapatlaliztli ahmo tzonhuilizca īhuīc $1",
        "spam_blanking": "Mochi tlapatlaliztli quimpiyah tzonhuiliztli īhuīc $1, iztāctiliacah",
+       "pageinfo-firstuser": "Tlaīxchīuhqui",
+       "pageinfo-contentpage-yes": "Quēmah",
+       "pageinfo-protect-cascading-yes": "Quēmah",
        "previousdiff": "← Achtopa",
        "nextdiff": "Oc ye cencah yancuīc tlapatlaliztli →",
        "widthheightpage": "$1 × $2, $3 {{PLURAL:|zāzanilli|zāzanilli}}",
        "file-info-size": "$1 × $2 pixel; zāzanilli octacayōtl: $3; machiyōtl MIME: $4",
        "file-nohires": "Ahmo ia achi cualli ahmo occē īxiptli.",
-       "show-big-image": "Mochi cuallōtl",
+       "show-big-image": "Tzīntilicihcuilōlli",
        "newimages": "Yancuīc īxipcān",
        "imagelisttext": "Nicān {{PLURAL:$1|mopiya|mopiyah}} '''$1''' īxiptli $2 iuhcopa.",
        "noimages": "Ahtlein ic tlatta.",
index b885177..de16baa 100644 (file)
        "nstab-template": "Modello",
        "nstab-help": "Ajùto",
        "nstab-category": "Categurìa",
+       "mainpage-nstab": "Paggena prencepale",
        "nosuchaction": "Operazione nun ricanusciuta",
        "nosuchactiontext": "L'azione specificata dint'a l'URL nun è bbona.\nPuò darse ca l'URL fosse stata digitata 'n modo sbagliàto o che fosse stato seguito nu link sbagliàto.\nChesto putesse innecà pùre nu bug dint'a {{SITENAME}}.",
        "nosuchspecialpage": "Chista paggena speciale nun ce sta",
        "createacct-captcha": "Cuntrollo 'e sicurezza",
        "createacct-imgcaptcha-ph": "Scrivite 'o testo ca vedite ncoppa",
        "createacct-submit": "Cria 'a toja utenza",
-       "createacct-another-submit": "Cria n'atu cunto",
+       "createacct-another-submit": "Cria nu cunto",
        "createacct-benefit-heading": "{{SITENAME}} è fatta 'e perzone comme te.",
        "createacct-benefit-body1": "{{PLURAL:$1|càgnamiento|càgnamiente}}",
        "createacct-benefit-body2": "{{PLURAL:$1|paggena|paggene}}",
        "permissionserrorstext-withaction": "Nun haje premmesse abbastante pe' $2, {{PLURAL:$1|'o mutivo è chesto|'e mutive so' chiste}}:",
        "recreate-moveddeleted-warn": "'''Attenziò: staje a crià na paggena scancellata già.'''\n\nVire si è bbuono 'e cuntinuà a cagnà sta paggena. L'elenco ch' 'e relative scancellamiente e spustamente s'è scritto ccà abbascio pe' ffà comodo:",
        "moveddeleted-notice": "Sta paggena è stata scancellata.\nL'elenco d' 'e relative scancellamiente e spustamente s'è scritto ccà abbascio pe' n'avé nfurmazione.",
+       "moveddeleted-notice-recent": "Scusate, sta mmasciata è stata scancellata mo mo (dint'a sti 24 ore).\n\nL'aziune 'e scancellazione e spustamento pe' sta paggena so dispunibbele ccà p' 'a cumpretezza.",
        "log-fulllog": "Vide log sano",
        "edit-hook-aborted": "'O cagnamiento è stato annullato 'a 'o «hook».\nNun dette spiegazione nisciuna.",
        "edit-gone-missing": "Nun se può agghiurnà 'a paggena.\nPare ch' 'è stata scancellata.",
        "group-bot": "Bot",
        "group-sysop": "Ammenistrature",
        "group-bureaucrat": "Burocrate",
-       "group-suppress": "Oversight",
+       "group-suppress": "Soppressure",
        "group-all": "(tutte)",
        "group-user-member": "{{GENDER:$1|utente}}",
        "group-autoconfirmed-member": "{{GENDER:$1|utente autocunfermato|utente autocunfermata|utente autocunfermato/a}}",
        "group-bot-member": "{{GENDER:$1|bot}}",
        "group-sysop-member": "{{GENDER:$1|ammenistratore|ammenistratrice|ammenistratore/trice}}",
        "group-bureaucrat-member": "{{GENDER:$1|burocrate}}",
-       "group-suppress-member": "{{GENDER:$1|oversight}}",
+       "group-suppress-member": "{{GENDER:$1|suppressure|supprimitrice}}",
        "grouppage-user": "{{ns:project}}:Utente",
        "grouppage-autoconfirmed": "{{ns:project}}:Utente autocunfermate",
        "grouppage-bot": "{{ns:project}}:Bot",
        "grouppage-sysop": "{{ns:project}}:Ammenistrature",
        "grouppage-bureaucrat": "{{ns:project}}:Burocrate",
-       "grouppage-suppress": "{{ns:project}}:Oversight",
+       "grouppage-suppress": "{{ns:project}}:Suppressure",
        "right-read": "Liegge paggene",
        "right-edit": "Cagna paggene",
        "right-createpage": "Crìa paggene (ca nun songo paggene 'e chiacchiera)",
        "upload-http-error": "N'errore HTTP è succiesso: $1",
        "upload-copy-upload-invalid-domain": "Nun è permessa 'a carreca 'e copie 'a chistu dumminio.",
        "upload-dialog-title": "Carreca file",
-       "upload-dialog-error": "N'errore cumparette",
-       "upload-dialog-warning": "N'avviso cumparette",
        "upload-dialog-button-cancel": "Canciella",
        "upload-dialog-button-done": "Fatto",
        "upload-dialog-button-save": "Sarva",
        "upload-dialog-button-upload": "Carreca",
-       "upload-dialog-label-select-file": "Sceglie file",
-       "upload-dialog-label-infoform-title": "Dettaglie",
-       "upload-dialog-label-infoform-name": "Nomme",
-       "upload-dialog-label-infoform-description": "Descrizzione",
-       "upload-dialog-label-usage-title": "Aúso",
-       "upload-dialog-label-usage-filename": "Nomme d' 'o file",
+       "upload-process-error": "N'errore cumparette",
+       "upload-process-warning": "N'avviso cumparette",
+       "upload-form-label-select-file": "Sceglie file",
+       "upload-form-label-infoform-title": "Dettaglie",
+       "upload-form-label-infoform-name": "Nomme",
+       "upload-form-label-infoform-description": "Descrizzione",
+       "upload-form-label-usage-title": "Aúso",
+       "upload-form-label-usage-filename": "Nomme d' 'o file",
        "backend-fail-stream": "Nun se può mannà 'o file \"$1\".",
        "backend-fail-backup": "Nun se può ffà 'o backup d' 'o file \"$1\".",
        "backend-fail-notexists": "'O file $1 nun esiste.",
        "filerevert-legend": "Arrepiglia 'o file",
        "filerevert-intro": "State arrepiglianno 'o file '''[[Media:$1|$1]]''' int' 'a [$4 verzione d' 'o $3, $2].",
        "filerevert-comment": "Mutive:",
-       "filerevert-defaultcomment": "Arripigliata 'a verzione d' 'o $2, $1",
+       "filerevert-defaultcomment": "Turnata 'a verzione comm' 'o $2, $1 ($3)",
        "filerevert-submit": "Arrepiglia",
        "filerevert-success": "'''[[Media:$1|$1]]''' è stat'arripigliato â verziona [$4 d' 'e $3 d' 'o $2].",
        "filerevert-badversion": "Nun ce sta na virziona lucale 'e stu file cu l'orario addimannato.",
        "nopagetext": "'A paggena 'e destinazione c'avite specificato nun esiste.",
        "pager-newer-n": "{{PLURAL:$1|1 cchiù nova|$1 cchiù nnove}}",
        "pager-older-n": "{{PLURAL:$1|1 cchiù viecchio|$1 cchiù viecchie}}",
-       "suppress": "Supervisione",
+       "suppress": "Supprime",
        "querypage-disabled": "Sta paggena speciale è stutata pe' mutive 'e prestaziune.",
        "apihelp": "Ajuto cu l'API",
        "apihelp-no-such-module": "'O modulo \"$1\" nun se trova.",
        "emailccsubject": "Copia d' 'a mmasciata tua 'a $1: $2",
        "emailsent": "Mmasciata e-mail mannata",
        "emailsenttext": "'A mmasciata d' 'a toja s'è mannata.",
-       "emailuserfooter": "Chista mmasciata e-mail è stata mannata 'a $1 a $2 p' 'a funziona \"{{int:emailuser}}\" 'e {{SITENAME}}.",
+       "emailuserfooter": "Chista mmasciata e-mail è stata {{GENDER:$1|mannata}} 'a $1 a {{GENDER:$2|$2}} p' 'a funziona \"{{int:emailuser}}\" 'e {{SITENAME}}.",
        "usermessage-summary": "Lassanno na mmasciata 'e sistema.",
        "usermessage-editor": "Mmasciatore d' 'o sistema",
        "watchlist": "Paggene cuntrullate",
        "api-error-badaccess-groups": "Tun putite carrecà file ncopp' 'a sta wiki.",
        "api-error-badtoken": "Errore interno: 'O token nun è buono.",
        "api-error-copyuploaddisabled": "'A funzione carrcà 'e n'URL nun è appicciata dint'a stu server.",
-       "api-error-duplicate": "Nce {{PLURAL:$1|stà [$2 n'atu file]|stanno [$2 ati file]}} ncopp' 'o sito ch' 'e stisse cuntenute.",
-       "api-error-duplicate-archive": "Nce {{PLURAL:$1|steva [$2 n'atu file]|stevano [$2 ati file]}} già ncopp' 'o sito ch' 'e stisse cuntenute, però {{PLURAL:$1|è stato|so' state}} scancellate.",
+       "api-error-duplicate": "Nce {{PLURAL:$1|sta è n'atu file|stanno ati file}} ncopp' 'o sito ch' 'e cuntenute eguale eguale.",
+       "api-error-duplicate-archive": "Nce {{PLURAL:$1|steva n'atu file|stevano ati file}} già ncopp' 'o sito ch' 'e stisse cuntenute, però {{PLURAL:$1|è stato|so' state}} scancellate.",
        "api-error-empty-file": "'O file ch'avite mannato è abbacante.",
        "api-error-emptypage": "'A criazione 'e paggene nuove abbacante nun è permessa.",
        "api-error-fetchfileerror": "Errore interno: Coccosa ascette stuorta quanno se steva 'analizzà stu file.",
index b2169c5..3dad7af 100644 (file)
        "api-error-badtoken": "Intern feil: Ugyldig nøkkel.",
        "api-error-copyuploaddisabled": "Opplasting ved URL er deaktivert på denne tjeneren.",
        "api-error-duplicate": "Det er allerede {{PLURAL:$1|en [$2 annen fil]|flere [$2 andre filer]}} på denne siden med samme innhold.",
-       "api-error-duplicate-archive": "Det fantes {{PLURAL:$1|[$2 en annen fil]|[$2 noen andre filer]}} på siden som hadde samme innhold, men {{PLURAL:$1|den|de}} ble slettet.",
+       "api-error-duplicate-archive": "Det fantes {{PLURAL:$1|en annen fil|noen andre filer}} på siden som hadde samme innhold, men {{PLURAL:$1|den|de}} ble slettet.",
        "api-error-empty-file": "Filen du sendte inn var tom.",
        "api-error-emptypage": "Det er ikke tillatt å opprette nye, tomme sider.",
        "api-error-fetchfileerror": "Intern feil: Noe gikk galt ved henting av denne filen.",
index 33cd815..08b8e2a 100644 (file)
        "api-error-badtoken": "Interne fout: t token klopt niet.",
        "api-error-copyuploaddisabled": "Bestaanden opsturen via n webadres is uutezet op disse server.",
        "api-error-duplicate": "Der {{PLURAL:$1|steet al [$2 n bestaand]|staon al [$2 bestaanden]}} mit de zelfde inhoud in de wiki.",
-       "api-error-duplicate-archive": "Der {{PLURAL:$1|was al [$2 n aander bestaand]|waren al [$2 $1 aandere bestaanden]}}  op de webstee mit de zelfde inhoud, mer {{PLURAL:$1|dat is|die bin}} vortedaon.",
+       "api-error-duplicate-archive": "Der {{PLURAL:$1|was al n aander bestaand|waren al $1 aandere bestaanden}}  op de webstee mit de zelfde inhoud, mer {{PLURAL:$1|dat is|die bin}} vortedaon.",
        "api-error-empty-file": "t Bestaand da'j op-estuurd hebben is leeg.",
        "api-error-emptypage": "Je maggen gien lege nieje ziejen anmaken.",
        "api-error-fetchfileerror": "Interne fout: der is iets verkeerd egaon mit t ophaolen van t bestaand.",
index eeb8b92..f2a09bb 100644 (file)
        "api-error-badtoken": "आन्तरिक समस्याः खराब टोकन ।",
        "api-error-copyuploaddisabled": "यस सर्वरमा URL द्वारा अपलोड गर्ने व्यवस्था निस्क्रिय गरिएकोछ।",
        "api-error-duplicate": "यस साइटमा पहिलेबाट यस्तै सामग्री {{PLURAL:$1|भएको [$2 अर्को फाइल छ]|भएका  [$2 केहि अरु फाइलहरू छन्]}} ।",
-       "api-error-duplicate-archive": "यस साइटमा पहिलेबाट यस्तै सामग्री {{PLURAL:$1|भएको [$2 अर्को फाइल थियो]|भएका  [$2 केहि अरु फाइलहरू थिए]}} ।\nतर {{PLURAL:$1|यो मेटाइएको थियो|यी मेटाइएका थिए}} ।",
+       "api-error-duplicate-archive": "यस साइटमा पहिलेबाट यस्तै सामग्री {{PLURAL:$1|भएको अर्को फाइल थियो|भएका  केहि अरु फाइलहरू थिए}} ।\nतर {{PLURAL:$1|यो मेटाइएको थियो|यी मेटाइएका थिए}} ।",
        "api-error-empty-file": "तपाईंले बुझाएको फाइल खालि छ।",
        "api-error-emptypage": "नयाँ तयार गर्दै, खाली पृष्ठ तयार गर्न अनुमति छैन ।",
        "api-error-fetchfileerror": "आन्तरिक समस्याः फाइल तान्दा केही कुरा गलत भएछ ।",
index 8fdd116..77f6b64 100644 (file)
        "nstab-template": "Sjabloon",
        "nstab-help": "Hulppagina",
        "nstab-category": "Categorie",
+       "mainpage-nstab": "Hoofdpagina",
        "nosuchaction": "Opgegeven handeling bestaat niet",
        "nosuchactiontext": "De opdracht in de URL is ongeldig.\nMogelijk heeft u een typefout gemaakt in de URL of een onjuiste koppeling gevolgd.\nHet kan ook wijzen op een fout in de software van {{SITENAME}}.",
        "nosuchspecialpage": "Deze speciale pagina bestaat niet",
        "upload-http-error": "Er is een HTTP-fout opgetreden: $1",
        "upload-copy-upload-invalid-domain": "Uploaden per kopie is niet beschikbaar vanuit dit domein.",
        "upload-dialog-title": "Bestand uploaden",
-       "upload-dialog-error": "Er is een fout opgetreden",
-       "upload-dialog-warning": "Een waarschuwing is opgetreden",
        "upload-dialog-button-cancel": "Annuleren",
        "upload-dialog-button-done": "Afgerond",
        "upload-dialog-button-save": "Opslaan",
        "upload-dialog-button-upload": "Upload",
-       "upload-dialog-label-select-file": "Selecteer bestand",
-       "upload-dialog-label-infoform-title": "Details",
-       "upload-dialog-label-infoform-name": "Naam",
-       "upload-dialog-label-infoform-description": "Beschrijving",
-       "upload-dialog-label-usage-title": "Gebruik",
-       "upload-dialog-label-usage-filename": "Bestandsnaam",
+       "upload-process-error": "Er is een fout opgetreden",
+       "upload-process-warning": "Een waarschuwing is opgetreden",
+       "upload-form-label-select-file": "Selecteer bestand",
+       "upload-form-label-infoform-title": "Details",
+       "upload-form-label-infoform-name": "Naam",
+       "upload-form-label-infoform-description": "Beschrijving",
+       "upload-form-label-usage-title": "Gebruik",
+       "upload-form-label-usage-filename": "Bestandsnaam",
        "backend-fail-stream": "Het was niet mogelijk het bestand \"$1\" te streamen.",
        "backend-fail-backup": "Het was niet mogelijk een reservekopie van het bestand $1 te maken.",
        "backend-fail-notexists": "Het bestand $1 bestaat niet.",
        "api-error-badtoken": "Interne fout: het token klopt niet.",
        "api-error-copyuploaddisabled": "Uploaden via URL is uitgeschakeld op deze server.",
        "api-error-duplicate": "Er {{PLURAL:$1|staat al [$2 een bestand]|staan al [$2 bestanden]}} met dezelfde inhoud in de wiki.",
-       "api-error-duplicate-archive": "Er {{PLURAL:$1|was al [$2 een ander bestand]|waren al [$2 $1 andere bestanden]}}  op de site met dezelfde inhoud, maar {{PLURAL:$1|dat is|die zijn}} verwijderd.",
+       "api-error-duplicate-archive": "Er {{PLURAL:$1|was al een ander bestand|waren al $1 andere bestanden}}  op de site met dezelfde inhoud, maar {{PLURAL:$1|dat is|die zijn}} verwijderd.",
        "api-error-empty-file": "Het bestand dat u hebt geüpload is leeg.",
        "api-error-emptypage": "Het aanmaken van nieuwe, lege pagina's is niet toegestaan.",
        "api-error-fetchfileerror": "Interne fout: er is iets misgegaan bij het ophalen van het bestand.",
index 0729e7a..6a7ea26 100644 (file)
        "api-error-badtoken": "Intern feil: ugild token.",
        "api-error-copyuploaddisabled": "Opplasting etter URL er avslege på tenaren.",
        "api-error-duplicate": "Det finst {{PLURAL:$1|[$2 ei anna fil]|[$2 andre filer]}} på nettstaden med same innhaldet.",
-       "api-error-duplicate-archive": "Det fanst {{PLURAL:$1|[$2 ei anna fil]|[$2 andre filer]}} på nettstaden med det same innhaldet, men {{PLURAL:$1|ho|dei}} vart sletta.",
+       "api-error-duplicate-archive": "Det fanst {{PLURAL:$1|ei anna fi]|andre file]}} på nettstaden med det same innhaldet, men {{PLURAL:$1|ho|dei}} vart sletta.",
        "api-error-empty-file": "Fila du sende var tom.",
        "api-error-emptypage": "Det er ikkje tillate å oppretta nye tomme sider.",
        "api-error-fetchfileerror": "Intern feil: Noko gjekk gale då fila vart henta.",
index c8ca3aa..d94e169 100644 (file)
@@ -12,7 +12,8 @@
                        "Spacebirdy",
                        "Горан Анђелковић",
                        "לערי ריינהארט",
-                       "아라"
+                       "아라",
+                       "Macofe"
                ]
        },
        "tog-underline": "Soslinhar los ligams :",
        "api-error-badtoken": "Error intèrna : marrit « geton ».",
        "api-error-copyuploaddisabled": "Los cargaments via URL son desactivats sus aqueste servidor.",
        "api-error-duplicate": "I a ja {{PLURAL:$1|[$2 un autre fichièr present]|[$2 d'autres fichièrs presents]}} sul site amb lo meteis contengut.",
-       "api-error-duplicate-archive": "I aviá ja {{PLURAL:$1|[$2 un autre fichièr present]|[$2 d'autres fichièrs presents]}} sul site amb lo meteis contengut, mas {{PLURAL:$1|es estat suprimit|son estats suprimits}}.",
+       "api-error-duplicate-archive": "I aviá ja {{PLURAL:$1|un autre fichièr present|d'autres fichièrs presents}} sul site amb lo meteis contengut, mas {{PLURAL:$1|es estat suprimit|son estats suprimits}}.",
        "api-error-empty-file": "Lo fichièr qu'avètz somés èra void.",
        "api-error-emptypage": "Creacion de paginas voidas pas autorizada.",
        "api-error-fetchfileerror": "Error intèrna : Quicòm s'es mal passat al moment de la recuperacion del fichièr.",
index f593f3c..e411a97 100644 (file)
@@ -27,7 +27,7 @@
        "tog-norollbackdiff": "Älä ozuta eroloi, konzu olet ottanuh järilleh aijemban versien järilleh tuondu -toimindol",
        "underline-always": "Ainos",
        "underline-never": "Nikonzu",
-       "editfont-style": "Edituičendualovehen kirjainstiilu:",
+       "editfont-style": "Edituičendualovehen kirjainstiil'u:",
        "editfont-sansserif": "Sans-serif -fontu",
        "editfont-serif": "Serif-fontu",
        "sunday": "Pyhäpäivy",
        "listingcontinuesabbrev": "(jatko)",
        "index-category": "Indeksiruitut sivut",
        "noindex-category": "Indeksiruičemattomat sivut",
-       "broken-file-category": "Sivut, kudamil on ruadamattomii failulinkilöi",
+       "broken-file-category": "Sivut, kudamil on avuamattomii failulinkilöi",
        "about": "Tieduo sovellukses",
        "article": "Yhtevyssivu",
        "newwindow": "(avata uvves ikkunas)",
        "morenotlisted": "Tämä listu ei ole valmis.",
        "mypage": "Sivu",
        "mytalk": "Pagin",
-       "anontalk": "Paginsivutälle IP-adressile",
+       "anontalk": "Paginsivu tälle IP-adressile",
        "navigation": "Navigatsii",
        "and": "&#32;da",
        "qbfind": "Eči",
        "redirectedfrom": "(siirretty $1:späi)",
        "redirectpagesub": "uvvellehohjavussivu",
        "redirectto": "Uvvellehohjuau sivuh:",
-       "lastmodifiedat": "Tädä sivuu on muutettu jälgimäizen kerran $1, $2 aigah.",
+       "lastmodifiedat": "Tädä sivuu on muutettu jälgimäzen kerran $1, $2 aigah.",
        "protectedpage": "Suojattu sivu",
        "jumpto": "Siirry",
        "jumptonavigation": "navigatsii",
        "internalerror": "Syväindölline haireh",
        "internalerror_info": "Syväindölline haireh: $1",
        "filecopyerror": "Failua \"$1\" ei voitu kopiruija failakse \"$2\".",
-       "filerenameerror": "Ei voi uvvellehnimittoä \"$1\"-failua nimele \"$2\".",
+       "filerenameerror": "Ei voi uvvellehnimittiä \"$1\"-failua nimele \"$2\".",
        "filedeleteerror": "Failua \"$1\" ei voitu ottua iäre.",
        "directorycreateerror": "Ei voi luadie al'bomua \"$1\".",
        "directoryreadonlyerror": "Al'bom $1 on kirjutussuojattu.",
        "password-name-match": "Peittosana pidäy olla eri migu käyttäinimi.",
        "password-login-forbidden": "Tämän käyttäinimen da peittosanan käyttö on estetty.",
        "mailmypassword": "Azeta peittosana uvvelleh",
-       "passwordremindertitle": "Uusi väliaigaine peittosana {{SITENAME}}-sivuh niškoi",
-       "passwordremindertext": "Kentah IP-adressispäi $1 kyzyi työndämäh uuttu peittosanua saitale {{SITENAME}} ($4). Väliaigaine peittosana käyttäjäle $2 on nygöi $3. Kirjuttai da vaihta peittosana. Väliaigaine peittosana vahnenou {{PLURAL:$5|yhten päivän|$5 päivän}} jälles.\n\nOllou kentah toine työndänyh tämän pakičuksen, libo ku ollet mustanuh sinun peittosanan da et tahto vaihtua sidä, voit jättiä tämän viestin huomivottah dajatkua vahnan peittosanan käyttyö.",
+       "passwordremindertitle": "Uuzi väliaigaine peittosana {{SITENAME}}-sivuh niškoi",
+       "passwordremindertext": "Kentah IP-adressispäi $1 kyzyi työndämäh uuttu peittosanua saitale {{SITENAME}} ($4). Väliaigaine peittosana käyttäjäle $2 on nygöi $3. Kirjuttai da vaihta peittosana. Väliaigaine peittosana vahnenou {{PLURAL:$5|yhten päivän|$5 päivän}} jälles.\n\nOllou kentah toine työndänyh tämän pakičuksen, libo ku ollet mustanuh sinun peittosanan da et tahto vaihtua sidä, voit jättiä tämän viestin huomivottah da jatkua vahnan peittosanan käyttyö.",
        "mailerror": "Haireh työndäjes sähköpoštua: $1",
        "accountcreated": "Tili luajittu",
        "loginlanguagelabel": "Kieli: $1",
        "changeemail-none": "(niyhty)",
        "changeemail-password": "Sinun {{SITENAME}}-peittosana:",
        "changeemail-submit": "Vaihta sähköpoštu",
-       "changeemail-throttled": "Olet oppinuh kirjuttuakseh liijan moni kerdua. Ole hyvä, vuota $1 enne ku opit uvvelleh.",
+       "changeemail-throttled": "Olet oppinuh kirjuttuakseh liijan moni kerdua. Ole hyvä, vuota $1 enne ku opit uvvessah.",
        "resettokens-tokens": "Avaimet:",
        "bold_sample": "Lihavoitu tekstu",
        "bold_tip": "Lihavoitu tekstu",
        "watchthis": "Tarkaile tädä sivuu",
        "savearticle": "Tallenda sivu",
        "preview": "Ezikačo",
-       "showpreview": "Ezikaččo",
+       "showpreview": "Ezikačo",
        "showdiff": "Luajitut korjavukset",
        "anoneditwarning": "<strong>Varaitus:</strong> Et ole kirjutannuhes. Luadinet muutoksii syväindölöih, sinun Ip-adressu tulou nägövih kaikile. Ku <strong>[$1 kirjutannuttos]</strong> libo <strong>[$2 registriiruičettos]</strong>, sinun syväindömuutokset nävytäh sinun käyttäinimel, toizien eduloin ližäkse.",
        "blockedtitle": "Käyttäi on estetty",
        "filename-tooshort": "Failunimi on liijan lyhyt.",
        "watchthisupload": "Valvo tädä failua",
        "upload-dialog-button-save": "Tallenda",
-       "upload-dialog-label-infoform-title": "Tiijot",
-       "upload-dialog-label-infoform-name": "Nimi",
-       "upload-dialog-label-infoform-description": "Kuvavus",
-       "upload-dialog-label-usage-title": "Käyttö",
-       "upload-dialog-label-usage-filename": "Failunimi",
+       "upload-form-label-infoform-title": "Tiijot",
+       "upload-form-label-infoform-name": "Nimi",
+       "upload-form-label-infoform-description": "Kuvavus",
+       "upload-form-label-usage-title": "Käyttö",
+       "upload-form-label-usage-filename": "Failunimi",
        "license-header": "Licenzii",
        "imgfile": "tiijosto",
        "listfiles_name": "Nimi",
index 78fac1c..75388b1 100644 (file)
        "api-error-badtoken": "ଭିତର ଅସୁବିଧା: ଖରାପ ଟୋକନ ।",
        "api-error-copyuploaddisabled": "URL ଦେଇ ଅପଲୋଡ଼ କରିବା ଏହି ସର୍ଭରରେ ଅଚଳ କରାଯାଇଅଛି ।",
        "api-error-duplicate": "ଏହି ସାଇଟରେ ସେହି ଏକା ତଥ୍ୟ ଥିବା {{PLURAL:$1| [$2 ଆଉ ଏକ ଫାଇଲ] ରହିଅଛି|[$2 ଆଉ କିଛି ଫାଇଲ] ରହି ଅଛନ୍ତି}} ।",
-       "api-error-duplicate-archive": "ସେହି ସାଇଟରେ ସେହି ଏକା ଭିତର ଭାଗ ସହିତ ଆଗରୁ {{PLURAL:$1|[$2 ଆଉ ଫାଇଲଟିଏ] ଥିଲା|[$2 ଆଉ କେତେକ ଫାଇଲ] ଥିଲା}}, କିନ୍ତୁ {{PLURAL:$1|ତାହାକୁ|ସେସବୁକୁ}} ଲିଭାଇ ଦିଆଯାଇଅଛି ।",
+       "api-error-duplicate-archive": "ସେହି ସାଇଟରେ ସେହି ଏକା ଭିତର ଭାଗ ସହିତ ଆଗରୁ {{PLURAL:$1|ଆଉ ଫାଇଲଟିଏ ଥିଲା|ଆଉ କେତେକ ଫାଇଲ ଥିଲା}}, କିନ୍ତୁ {{PLURAL:$1|ତାହାକୁ|ସେସବୁକୁ}} ଲିଭାଇ ଦିଆଯାଇଅଛି ।",
        "api-error-empty-file": "ଆପଣ ପଠାଇଥିବା ଫାଇଲଟି ଖାଲି ଅଟେ ।",
        "api-error-emptypage": "ନୂଆ, ଖାଲି ପୃଷ୍ଠ ତିଆରି କରିବାର ଅନୁମତି ନାହି ।",
        "api-error-fetchfileerror": "ଭିତର ଅସୁବିଧା: ଏହି ଫାଇଲଟି ପାଖରେ ପହଞ୍ଚିବା ବେଳେ କିଛି ଅସୁବିଧା ହେଲା ।",
index 9a2588d..788451c 100644 (file)
@@ -38,6 +38,7 @@
        "tog-watchdefault": "ਮੇਰੇ ਵੱਲੋਂ ਸੋਧੇ ਗਏ ਸਫ਼ੇ ਅਤੇ ਫ਼ਾਈਲਾਂ ਮੇਰੀ ਨਿਗਰਾਨੀ-ਲਿਸਟ ਵਿੱਚ ਪਾਓ",
        "tog-watchmoves": "ਮੇਰੇ ਵੱਲੋਂ ਬਦਲੇ ਸਿਰਲੇਖਾਂ ਵਾਲ਼ੇ ਸਫ਼ੇ ਅਤੇ ਫ਼ਾਈਲਾਂ ਮੇਰੀ ਨਿਗਰਾਨੀ-ਲਿਸਟ ਵਿੱਚ ਪਾਓ",
        "tog-watchdeletion": "ਮੇਰੇ ਵਲੋਂ ਮਿਟਾਏ ਗਏ ਸਫ਼ੇ ਅਤੇ ਫ਼ਾਈਲਾਂ ਮੇਰੀ ਨਿਗਰਾਨੀ-ਲਿਸਟ ਵਿੱਚ ਪਾਓ",
+       "tog-watchrollback": "ਮੇਰੇ ਦੁਆਰਾ ਮੋੜੇ ਗਏ ਸਫ਼ਿਅਾਂ ਨੂੰ ਮੇਰੀ ਧਿਆਨਸੂਚੀ ਵਿੱਚ ਸ਼ਾਮਿਲ ਕਰੋ",
        "tog-minordefault": "ਸਾਰੀਆਂ ਸੋਧਾਂ ’ਤੇ ਮੂਲ ਰੂਪ ਵਿਚ ਛੋਟੇ ਹੋਣ ਦਾ ਨਿਸ਼ਾਨ ਲਾਓ",
        "tog-previewontop": "ਸੋਧ ਬਕਸੇ ਤੋਂ ਪਹਿਲਾਂ ਝਲਕ ਵਖਾਓ",
        "tog-previewonfirst": "ਪਹਿਲੀ ਸੋਧ ਉੱਤੇ ਝਲਕ ਵਖਾਓ",
@@ -48,7 +49,7 @@
        "tog-shownumberswatching": "ਨਜ਼ਰ ਰੱਖ ਰਹੇ ਵਰਤੋਂਕਾਰਾਂ ਦੀ ਗਿਣਤੀ ਵਖਾਓ",
        "tog-oldsig": "ਮੌਜੂਦਾ ਦਸਤਖ਼ਤ:",
        "tog-fancysig": "ਦਸਤਖ਼ਤ ਨੂੰ ਬਤੌਰ ਵਿਕੀਲਿਖਤ ਮੰਨੋ (ਬਿਨਾਂ ਆਟੋਮੈਟਿਕ ਲਿੰਕ)",
-       "tog-uselivepreview": "ਸਿੱਧà©\80 à¨\9dਲà¨\95 à¨µà¨°à¨¤à©\8b (ਤà¨\9cਰਬà©\87-à¨\85ਧà©\80ਨ)",
+       "tog-uselivepreview": "ਮà©\8cà¨\9cà©\82ਦਾ à¨\9dਲà¨\95 à¨µà¨°à¨¤à©\8b",
        "tog-forceeditsummary": "ਜਦੋਂ ਮੈਂ ਖ਼ਾਲੀ ਸੋਧ ਸਾਰ ਦੇਵਾਂ ਤਾਂ ਮੈਨੂੰ ਆਗਾਹ ਕਰੋ",
        "tog-watchlisthideown": "ਨਿਗਰਾਨੀ-ਲਿਸਟ ਵਿੱਚੋਂ ਮੇਰੀਆਂ ਸੋਧਾਂ ਲੁਕਾਓ",
        "tog-watchlisthidebots": "ਨਿਗਰਾਨੀ-ਲਿਸਟ ਵਿੱਚੋਂ ਬੋਟਾਂ ਦੀਆਂ ਸੋਧਾਂ ਲੁਕਾਓ",
        "jumptonavigation": "ਨੇਵੀਗੇਸ਼ਨ",
        "jumptosearch": "ਖੋਜ",
        "view-pool-error": "ਅਫ਼ਸੋਸ, ਸਰਵਰ ਇਸ ਵੇਲੇ ਓਵਰਲੋਡ ਹੈ।\nਬਹੁਤ ਸਾਰੇ ਮੈਂਬਰ ਇਸ ਸਫ਼ੇ ਨੂੰ ਵੇਖਣ ਦੀ ਕੋਸ਼ਿਸ਼ ਕਰ ਰਹੇ ਹਨ।\nਫੇਰ ਕੋਸ਼ਿਸ਼ ਕਰਨ ਤੋਂ ਪਹਿਲਾਂ ਥੋੜੀ ਉਡੀਕ ਕਰੋ ਜੀ।\n$1",
+       "generic-pool-error": "ਮੁਆਫ਼ ਕਰੋ, ਇਸ ਵੇਲੇ ਸਰਵਰ ਕੰਮ ਨਹੀਂ ਕਰ ਰਹੇ।\nਇਸ ਚੀਜ਼ ਨੂੰ ਇਸ ਵੇਲੇ ਬਹੁਤ ਜ਼ਿਆਦਾ ਵਰਤੋਂਕਾਰ ਵੇਖਣ ਦੀ ਕੋਸ਼ਿਸ਼ ਕਰ ਰਹੇ ਹਨ।\nਇਸ ਚੀਜ਼ ਨੂੰ ਦੁਆਰਾ ਦੇਖਣ ਦੀ ਕੋਸ਼ਿਸ਼ ਕਰਨ ਤੋਂ ਪਹਿਲਾਂ ਥੋੜ੍ਹੀ ਦੇਰ ਇੰਤਜ਼ਾਰ ਕਰੋ।",
        "pool-timeout": "ਲਾਕ ਲਈ ਉਡੀਕ ਦਾ ਵਕਤ ਖ਼ਤਮ ਹੋ ਗਿਆ ਹੈ",
        "pool-queuefull": "ਪੂਲ ਕਤਾਰ ਭਰੀ ਹੋਈ ਹੈ",
        "pool-errorunknown": "ਅਣਜਾਣ ਗਲਤੀ",
        "nstab-template": "ਫਰਮਾ",
        "nstab-help": "ਮਦਦ ਸਫ਼ਾ",
        "nstab-category": "ਸ਼੍ਰੇਣੀ",
+       "mainpage-nstab": "ਮੁੱਖ ਸਫ਼ਾ",
        "nosuchaction": "ਅਜਿਹੀ ਕੋਈ ਕਾਰਵਾਈ ਨਹੀਂ ਹੈ",
        "nosuchactiontext": "URL ਦੁਆਰਾ ਦੱਸਿਆ ਕੰਮ ਗ਼ਲਤ ਹੈ।\nਸ਼ਾਇਦ ਤੁਸੀਂ URL ਸਹੀ ਨਹੀਂ ਲਿਖਿਆ ਜਾਂ ਕਿਸੇ ਗ਼ਲਤ ਲਿੰਕ ਤੇ ਆਏ ਹੋ।\nਇਹ ਵੀ ਹੋ ਸਕਦਾ ਹੈ ਕਿ ਇਹ {{SITENAME}} ਦੁਆਰੇ ਵਰਤੇ ਜਾਂਦੇ ਸਾਫ਼ਟਵੇਅਰ ਵਿਚਲੀ ਗ਼ਲਤੀ ਵੱਲ ਇਸ਼ਾਰਾ ਹੋਵੇ।",
        "nosuchspecialpage": "ਅਜਿਹਾ ਕੋਈ ਖ਼ਾਸ ਸਫ਼ਾ ਨਹੀਂ ਹੈ",
        "filerenameerror": "ਫ਼ਾਈਲ ''$1'' ਦਾ ਨਾਂ ''$2'' ਨਹੀਂ ਸਾ ਸਕਿਆ।",
        "filedeleteerror": "''$1'' ਫ਼ਾਈਲ ਹਟਾਈ ਨਹੀਂ ਜਾ ਸਕੀ।",
        "directorycreateerror": "ਡਾਇਰੈਕਟਰੀ ''$1'' ਬਣਾਈ ਨਹੀਂ ਜਾ ਸਕੀ।",
+       "directoryreadonlyerror": "\"$1\" ਨਾਮਾਵਲੀ ਸਿਰਫ਼ ਪੜ੍ਹਣਯੋਗ ਹੈ।",
+       "directorynotreadableerror": "\"$1\" ਨਾਮਾਵਲੀ ਪੜ੍ਹਣਯੋਗ ਨਹੀਂ ਹੈ।",
        "filenotfound": "ਫ਼ਾਈਲ ''$1'' ਲੱਭੀ ਨਹੀਂ ਜਾ ਸਕੀ।",
        "unexpected": "ਅਣਉਮੀਦਿਆ ਮੁੱਲ: \"$1\"=\"$2\"।",
        "formerror": "ਗ਼ਲਤੀ: ਫ਼ਾਰਮ ਪੇਸ਼ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ",
        "changeemail-password": "ਤੁਹਾਡਾ {{SITENAME}} ਪਾਸਵਰਡ:",
        "changeemail-submit": "ਈ-ਮੇਲ ਬਦਲੋ",
        "changeemail-throttled": "ਤੁਸੀਂ ਦਾਖ਼ਲ ਹੋਣ ਦੀਆਂ ਬਹੁਤ ਸਾਰੀਆਂ ਕੋਸ਼ਿਸ਼ਾਂ ਕੀਤੀਆਂ ਹਨ।\nਮੁੜ ਕੋਸ਼ਿਸ਼ ਕਰਨ ਤੋਂ ਪਹਿਲਾਂ $1 ਉਡੀਕ ਕਰੋ ਜੀ।",
+       "changeemail-nochange": "ਕਿਰਪਾ ਕਰਕੇ ਕੋਈ ਵੱਖਰਾ ਈਮੇਲ ਪਤਾ ਭਰੋ।",
        "resettokens": "ਟੋਕਨ ਮੁੜ-ਸੈੱਟ ਕਰੋ",
        "resettokens-text": "ਤੁਸੀਂ ਆਪਣੀਆਂ ਨਿਸ਼ਾਨੀਆਂ, ਜੋ ਤੁਹਾਡੇ ਖਾਤੇ ਨਾਲ਼ ਜੁੜੇ ਖ਼ਾਸ ਨਿੱਜੀ ਅੰਕੜਿਆਂ ਤੱਕ ਪੁੱਜਣ ਵਾਸਤੇ ਇਜਾਜ਼ਤ ਦਿੰਦੀਆਂ ਹਨ, ਨੂੰ ਇੱਥੇ ਮੁੜ-ਬਣਾ ਸਕਦੇ ਹੋ।\n\nਤੁਹਾਨੂੰ ਇਹ ਤਾਂ ਕਰਨਾ ਚਾਹੀਦਾ ਹੈ ਜੇਕਰ ਤੁਸੀਂ ਇਹਨਾਂ ਨੂੰ ਰੱਬ-ਸਬੱਬੀ ਕਿਸੇ ਨਾਲ਼ ਸਾਂਝਾ ਕਰ ਦਿੱਤਾ ਜਾਂ ਤੁਹਾਡਾ ਖਾਤਾ ਖ਼ਤਰੇ ਵਿੱਚ ਆ ਗਿਆ ਹੈ।",
        "resettokens-no-tokens": "ਨਵੀਆਂ ਬਣਾਉਣ ਵਾਸਤੇ ਕੋਈ ਨਿਸ਼ਾਨੀਆਂ ਨਹੀਂ ਹਨ।",
        "content-model-text": "ਆਮ ਲਿਖਤ",
        "content-model-javascript": "ਜਾਵਾਸਕਰਿਪਟ",
        "content-model-css": "ਸੀਐਸਐਸ",
+       "content-json-empty-object": "ਖਾਲੀ ਚੀਜ਼",
+       "content-json-empty-array": "ਖਾਲੀ ਤਰਤੀਬ",
        "post-expand-template-inclusion-warning": "'''ਖ਼ਬਰਦਾਰ:''' ਫਰਮੇ ਦਾ ਅਕਾਰ ਬਹੁਤ ਵੱਡਾ ਹੈ। ਕੁਝ ਫਰਮੇ ਸ਼ਾਮਲ ਨਹੀਂ ਹੋਣਗੇ।",
        "post-expand-template-inclusion-category": "ਉਹ ਸਫ਼ੇ ਜਿੱਥੇ ਫਰਮੇ ਸ਼ਾਮਲ ਕਰਨ ਦਾ ਅਕਾਰ ਹੱਦੋਂ ਵੱਧ ਗਿਆ ਹੈ",
        "post-expand-template-argument-warning": "'''ਚੇਤਾਵਨੀ:'''\nਇਸ ਪੰਨੇ ਤੇ ਘੱਟੋ ਘੱਟ ਇੱਕ ਐਸੀ ਸਾਂਚਾ ਬਹਿਸ ਹੈ ਜਿਸ ਦਾ ਅਕਾਰ ਬਹੁਤ ਵੱਡਾ ਹੈ। ਅਜਿਹੀਆਂ ਬਹਿਸਾਂ ਨੂੰ ਛੱਡ ਦਿੱਤਾ ਗਿਆ ਹੈ।",
        "parser-template-loop-warning": "ਫਰਮੇ ਦਾ ਲੂਪ ਲੱਭਿਆ: [[$1]]",
        "undo-success": "ਇਹ ਸੋਧ ਨਕਾਰੀ ਜਾ ਸਕਦੀ ਹੈ।\nਮਿਹਰਬਾਨੀ ਕਰਕੇ ਇਹ ਤਸਦੀਕ ਕਰਨ ਲਈ ਹੇਠਲੀ ਤੁਲਨਾ ਜਾਂਚੋ ਕਿ ਇਹ ਓਹੀ ਹੈ ਜੋ ਤੁਸੀਂ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ ਅਤੇ ਫਿਰ ਸੋਧ ਨਕਾਰਨ ਲਈ ਤਬਦੀਲੀਆਂ ਸਾਂਭ ਦਿਓ।",
        "undo-norev": "ਸੋਧ ਨਕਾਰੀ ਨਹੀਂ ਜਾ ਸਕਦੀ ਕਿਉਂਕਿ ਇਹ ਮੌਜੂਦ ਨਹੀਂ ਜਾਂ ਮਿਟਾ ਦਿੱਤੀ ਗਈ ਹੈ।",
+       "undo-nochange": "ਲਗਦਾ ਹੈ ਕਿ ਿਹ ਸੋਧ ਪਹਿਲਾਂ ਹੀ ਮੋੜ ਦਿੱਤੀ ਗਈ ਹੈ।",
        "undo-summary": "[[Special:Contributions/$2|$2]] ([[User talk:$2|ਗੱਲ-ਬਾਤ]]) ਦੀ ਸੋਧ $1 ਨਕਾਰੀ",
        "undo-summary-username-hidden": "ਗੁਪਤ ਵਰਤੋਂਕਾਰ ਵੱਲੋਂ ਕੀਤੀ $1 ਸੋਧ ਰੱਦ ਕਰੋ",
        "cantcreateaccounttitle": "ਖਾਤਾ ਬਣਾਇਆ ਨਹੀਂ ਜਾ ਸਕਦਾ",
        "rows": "ਕਤਾਰਾਂ:",
        "columns": "ਕਾਲਮ:",
        "searchresultshead": "ਖੋਜ",
+       "stub-threshold-sample-link": "ਨਮੂਨਾ",
        "stub-threshold-disabled": "ਬੰਦ ਹੈ",
        "recentchangesdays": "ਤਾਜ਼ਾ ਤਬਦੀਲੀਆਂ ਵਿਚ ਵਿਖਾਉਣ ਲਈ ਦਿਨ:",
        "recentchangesdays-max": "ਵੱਧ ਤੋਂ ਵੱਧ $1 {{PLURAL:$1|ਦਿਨ|ਦਿਨ}}",
        "right-editmyuserjs": "ਆਪਣੀਆਂ ਵਰਤੋਂਕਾਰ ਜਾਵਾਸਕਰਿਪਟ ਫ਼ਾਈਲਾਂ ਸੋਧੋ",
        "right-viewmywatchlist": "ਆਪਣੀ ਨਿਗਰਾਨੀ-ਲਿਸਟ ਵੇਖੋ",
        "right-editmywatchlist": "ਆਪਣੀ ਨਿਗਰਾਨੀ-ਲਿਸਟ ਸੋਧੋ। ਧਿਆਨ ਦਿਓ ਕਿ ਕੁਝ ਸਫ਼ੇ ਇਸ ਹੱਕ ਤੋਂ ਬਿਨਾਂ ਵੀ ਜੁੜਨਗੇ।",
+       "right-viewmyprivateinfo": "ਆਪਣਾ ਨਿੱਜੀ ਡਾਟਾ ਵੇਖੋ (ਉਦਾਹਰਨ ਵਜੋਂ ਈਮੇਲ ਪਤਾ, ਅਸਲੀ ਨਾਂ)",
+       "right-editmyprivateinfo": "ਆਪਣਾ ਨਿੱਜੀ ਡਾਟਾ ਸੋਧੋ (ਉਦਾਹਰਨ ਵਜੋਂ ਈਮੇਲ ਪਤਾ, ਅਸਲੀ ਨਾਂ)",
        "right-editmyoptions": "ਆਪਣੀਆਂ ਤਰਜੀਹਾਂ ਸੋਧੋ",
+       "right-import": "ਹੋਰ ਵਿਕੀਅਾਂ ਤੋਂ ਸਫ਼ੇ ਦਰਾਮਦ ਕਰੋ",
+       "right-importupload": "ਕਿਸੇ ਫ਼ਾਈਲ ਅਪਲੋਡ ਤੋਂ ਸਫ਼ੇ ਦਰਾਮਦ ਕਰੋ",
        "right-unwatchedpages": "ਨਜ਼ਰ ਨਾ ਰੱਖੇ ਜਾ ਰਹੇ ਸਫ਼ਿਆਂ ਦੀ ਲਿਸਟ ਵੇਖਣੀ",
        "right-mergehistory": "ਸਫ਼ਿਆਂ ਦੇ ਅਤੀਤਾਂ ਨੂੰ ਰਲ਼ਾਉਣਾ",
        "right-userrights": "ਸਾਰੇ ਵਰਤੋਂਕਾਰਾਂ ਦੇ ਹੱਕ ਬਦਲਣੇ",
        "action-viewmywatchlist": "ਆਪਣੀ ਨਿਗਰਾਨੀ-ਲਿਸਟ ਵੇਖੋ",
        "action-viewmyprivateinfo": "ਆਪਣੀ ਨਿੱਜੀ ਜਾਣਕਾਰੀ ਵੇਖੋ",
        "action-editmyprivateinfo": "ਆਪਣੀ ਨਿੱਜੀ ਜਾਣਕਾਰੀ ਸੋਧੋ",
+       "action-editcontentmodel": "ਕਿਸੇ ਸਫ਼ੇ ਦਾ ਸਮੱਗਰੀ ਮਾਡਲ ਸੋਧੋ",
        "nchanges": "$1 {{PLURAL:$1|ਤਬਦੀਲੀ|ਤਬਦੀਲੀਆਂ}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|ਆਖ਼ਰੀ ਫੇਰੀ ਤੋਂ ਲੈ ਕੇ}}",
        "enhancedrc-history": "ਅਤੀਤ",
        "newpageletter": "ਨ",
        "boteditletter": "ਬੋਟ",
        "number_of_watching_users_pageview": "[$1 ਵੇਖ ਰਹੇ ਹਨ {{PLURAL:$1|ਯੂਜ਼ਰ}}]",
-       "rc_categories_any": "ਕੋਈ ਵੀ",
+       "rc_categories_any": "à¨\9aà©\81ਣà©\87 à¨¹à©\8bà¨\87à¨\85ਾà¨\82 à¨µà¨¿à©±à¨\9aà©\8bà¨\82 à¨\95à©\8bà¨\88 à¨µà©\80",
        "rc-change-size-new": "$1 {{PLURAL:$|ਬਾਈਟ|ਬਾਈਟਾਂ}} ਤਬਦੀਲੀ ਤੋਂ ਬਾਅਦ",
        "newsectionsummary": "/* $1 */ ਨਵਾਂ ਭਾਗ",
        "rc-enhanced-expand": "ਵੇਰਵੇ ਵੇਖਾਓ",
        "tmp-create-error": "ਆਰਜ਼ੀ ਫ਼ਾਈਲ ਬਣਾਈ ਨਾ ਜਾ ਸਕੀ।",
        "tmp-write-error": "ਆਰਜ਼ੀ ਫ਼ਾਈਲ ਲਿਖਣ ਲਈ ਗ਼ਲਤੀ ਆਈ।",
        "large-file": "ਫ਼ਾਈਲਾਂ $1 ਤੋਂ ਵੱਡੀਆਂ ਨਾ ਹੋਣ ਦੀ ਸਲਾਹ ਦਿੱਤੀ ਜਾਂਦੀ ਹੈ;\nਇਹ ਫ਼ਾਈਲ $2 ਦੀ ਹੈ।",
+       "largefileserver": "ਇਹ ਫ਼ਾਈਲ ਸਰਵਰ ਦੇ ਮੁਤਾਬਕ ਵੱਡੀ ਹੈ।",
        "windows-nonascii-filename": "ਵਿਕੀ ਖ਼ਾਸ ਚਿੰਨ੍ਹਾਂ ਵਾਲੇ ਫ਼ਾਈਲ ਨਾਮਾਂ ਨੂੰ ਇਜਾਜ਼ਤ ਨਹੀਂ ਦਿੰਦਾ।",
-       "fileexists": "à¨\87ਹ à¨«à¨¼à¨¾à¨\88ਲ à¨¨à¨¾à¨\82 à¨ªà¨¹à¨¿à¨²à¨¾à¨\82 à¨¹à©\80 à¨®à©\8cà¨\9cà©\82ਦ à¨¹à©\88। à¨\9cà©\87 à¨¤à©\81ਸà©\80à¨\82 à¨\87ਹਨà©\82à©° à¨¬à¨¦à¨²à¨£ à¨¬à¨¾à¨°à©\87 à¨¦à©\8dਰਿà©\9c à¨¨à¨¹à©\80à¨\82 à¨¹à©\8b à¨¤à¨¾à¨\82  <strong>[[:$1]]</strong> à¨µà©\87à¨\96à©\8b à¨\9cà©\80। [[$1|thumb]]",
+       "fileexists": "à¨\87ਸ à¨¨à¨¾à¨\82 à¨¦à©\80 à¨«à¨¼à¨¾à¨\88ਲ à¨ªà¨¹à¨¿à¨²à¨¾à¨\82 à¨¹à©\80 à¨®à©\8cà¨\9cà©\82ਦ à¨¹à©\88। à¨\9cà©\87 {{GENDER:|ਤà©\81ਸà©\80à¨\82}} à¨\87ਸ à¨¨à©\82à©° à¨¬à¨¦à¨²à¨£ à¨¬à¨¾à¨°à©\87 à¨¦à©\8dਰਿà©\9c à¨¨à¨¹à©\80à¨\82 à¨¹à©\8b à¨¤à¨¾à¨\82  <strong>[[:$1]]</strong> à¨µà©\87à¨\96à©\8b à¨\9cà©\80।\n [[$1|thumb]]",
        "fileexists-extension": "ਇਸ ਨਾਂ ਨਾਲ਼ ਰਲਦੀ ਫ਼ਾਈਲ ਮੌਜੂਦ ਹੈ: [[$2|thumb]]\n* ਅੱਪਲੋਡ ਕੀਤੀ ਜਾਂਦੀ ਫ਼ਾਈਲ ਦਾ ਨਾਂ: <strong>[[:$1]]</strong>\n* ਮੌਜੂਦ ਫ਼ਾਈਲ ਦਾ ਨਾਂ: <strong>[[:$2]]</strong>\nਕੋਈ ਵੱਖਰਾ ਨਾਂ ਚੁਣੋ ਜੀ।",
        "file-exists-duplicate": "ਇਹ ਫ਼ਾਈਲ {{PLURAL:$1|ਇਸ ਫ਼ਾਈਲ|ਇਹਨਾਂ ਫ਼ਾਈਲਾਂ}} ਦੀ ਨਕਲ ਹੈ:",
        "uploadwarning": "ਅੱਪਲੋਡ ਚਿਤਾਵਨੀ",
index 82221b9..9977080 100644 (file)
        "createacct-captcha": "Kontrola bezpieczeństwa",
        "createacct-imgcaptcha-ph": "Wpisz tekst widoczny powyżej",
        "createacct-submit": "Utwórz konto",
-       "createacct-another-submit": "Utwórz kolejne konto",
+       "createacct-another-submit": "Utwórz konto",
        "createacct-benefit-heading": "{{grammar:B.lp|{{SITENAME}}}} tworzą ludzie tacy jak Ty.",
        "createacct-benefit-body1": "{{PLURAL:$1|edycja|edycje|edycji}}",
        "createacct-benefit-body2": "{{PLURAL:$1|strona|strony|stron}}",
        "upload-http-error": "Wystąpił błąd protokołu HTTP – $1",
        "upload-copy-upload-invalid-domain": "Przesyłanie kopii z tej domeny nie jest dostępne.",
        "upload-dialog-title": "Prześlij plik",
-       "upload-dialog-error": "Wystąpił błąd",
-       "upload-dialog-warning": "Pojawiło się ostrzeżenie",
        "upload-dialog-button-cancel": "Anuluj",
        "upload-dialog-button-done": "Gotowe",
        "upload-dialog-button-save": "Zapisz",
        "upload-dialog-button-upload": "Prześlij",
-       "upload-dialog-label-select-file": "Wybierz plik",
-       "upload-dialog-label-infoform-title": "Szczegóły",
-       "upload-dialog-label-infoform-name": "Nazwa",
-       "upload-dialog-label-infoform-description": "Opis",
-       "upload-dialog-label-usage-title": "Korzystanie",
-       "upload-dialog-label-usage-filename": "Nazwa pliku",
+       "upload-process-error": "Wystąpił błąd",
+       "upload-process-warning": "Pojawiło się ostrzeżenie",
+       "upload-form-label-select-file": "Wybierz plik",
+       "upload-form-label-infoform-title": "Szczegóły",
+       "upload-form-label-infoform-name": "Nazwa",
+       "upload-form-label-infoform-description": "Opis",
+       "upload-form-label-usage-title": "Korzystanie",
+       "upload-form-label-usage-filename": "Nazwa pliku",
        "backend-fail-stream": "Nie można odczytać pliku $1.",
        "backend-fail-backup": "Nie można utworzyć kopii zapasowej pliku  $1 .",
        "backend-fail-notexists": "Plik  $1  nie istnieje.",
        "filerevert-legend": "Przywracanie poprzedniej wersji pliku",
        "filerevert-intro": "Zamierzasz przywrócić '''[[Media:$1|$1]]''' do [$4 wersji z $3, $2].",
        "filerevert-comment": "Powód:",
-       "filerevert-defaultcomment": "Przywrócono wersję z $2, $1",
+       "filerevert-defaultcomment": "Przywrócono wersję z $1, $2 ($3)",
        "filerevert-submit": "Przywróć",
        "filerevert-success": "Plik '''[[Media:$1|$1]]''' został cofnięty do [$4 wersji z $3, $2].",
        "filerevert-badversion": "Brak poprzedniej lokalnej wersji tego pliku z podaną datą.",
        "api-error-badtoken": "Błąd wewnętrzny – nieprawidłowy kod weryfikacyjny (token).",
        "api-error-copyuploaddisabled": "Przesyłanie poprzez podanie adresu URL zostało na tym serwerze wyłączone.",
        "api-error-duplicate": "{{PLURAL:$1|Jest już [$2 inny plik]|Są już [$2 inne pliki]}} o tej samej zawartości",
-       "api-error-duplicate-archive": "{{PLURAL:$1|Był już [$2 inny plik]|Były już [$2 inne pliki]}} o takiej samej zawartości, ale {{PLURAL:$1|został usunięty|zostały usunięte}}.",
+       "api-error-duplicate-archive": "{{PLURAL:$1|Był już inny plik|Były już inne pliki}} o takiej samej zawartości, ale {{PLURAL:$1|został usunięty|zostały usunięte}}.",
        "api-error-empty-file": "Przesłany przez Ciebie plik jest pusty.",
        "api-error-emptypage": "Tworzenie nowych, pustych stron jest niedozwolone.",
        "api-error-fetchfileerror": "Błąd wewnętrzny – wystąpił błąd w trakcie pobierania pliku.",
index f868ab3..edea055 100644 (file)
@@ -15,7 +15,8 @@
                        "Shirayuki",
                        "아라",
                        "Kolega2357",
-                       "Purodha"
+                       "Purodha",
+                       "Macofe"
                ]
        },
        "tog-underline": "Anliure con la sotliniadura",
        "api-error-badtoken": "Eror antern: sìmbol pa bon.",
        "api-error-copyuploaddisabled": "Le carie a travers ëd liure a son disabilità ansima a cost servent.",
        "api-error-duplicate": "A-i {{PLURAL:$1|é [$2 n'àutr archivi]|son [$2 àutri archivi]}} già an sël sit col ël midem contnù.",
-       "api-error-duplicate-archive": "A-i {{PLURAL:$1|era [$2 n'àutr archivi]|ero [$2 àutri archivi]}} già an sël sit con ël midem contnù, ma {{PLURAL:$1|a l'é stàit|a son ëstàit}} ëscancelà.",
+       "api-error-duplicate-archive": "A-i {{PLURAL:$1|era n'àutr archivi|ero àutri archivi}} già an sël sit con ël midem contnù, ma {{PLURAL:$1|a l'é stàit|a son ëstàit}} ëscancelà.",
        "api-error-empty-file": "L'archivi ch'a l'ha mandà a l'era veuid.",
        "api-error-emptypage": "La creassion ëd pàgine neuve veujde a l'é nen përmëttùa.",
        "api-error-fetchfileerror": "Eror antern: quaicòs a l'é andàit mal antramentre ch'as arcuperava l'archivi.",
index b5c45e4..125eddf 100644 (file)
@@ -9,7 +9,8 @@
                        "ZaDiak",
                        "아라",
                        "Amire80",
-                       "Obaid Raza"
+                       "Obaid Raza",
+                       "Macofe"
                ]
        },
        "tog-underline": "حوڑ تھلے لین:",
        "api-error-badtoken": "اندر دی غلطی: برا ٹوکن",
        "api-error-copyuploaddisabled": "یو آر ایل نال فائل چڑھانا ایس سرور تے نکام",
        "api-error-duplicate": "ایتھے {{PLURAL:$1|ہے [$2 اک ہور فائل]|ہین [$2 کچ ہور فائلاں]}} ایسے مواد نال ایس تھاں تے پہلے ای ہے۔",
-       "api-error-duplicate-archive": "ایتھے  {{PLURAL:$1|سی [$2 اک ہور فائل]|سن [$2 کج ہور فائلاں]}} پہلے ای ایس تھاں تے اے اکو جے مواد نال پر {{PLURAL:$1|اے سی|اوہ سن}} مٹایا گیا۔",
+       "api-error-duplicate-archive": "ایتھے  {{PLURAL:$1|سی اک ہور فائل|سن کج ہور فائلاں}} پہلے ای ایس تھاں تے اے اکو جے مواد نال پر {{PLURAL:$1|اے سی|اوہ سن}} مٹایا گیا۔",
        "api-error-empty-file": "جیڑی فائل تسی دسی اے اوہ حالی اے۔",
        "api-error-emptypage": "نواں بناریا آن، خالی صفیاں دی اجازت نئیں۔",
        "api-error-fetchfileerror": "اندر دی غلطی: فائل لیندیاں کوئی غلطی ہوئی۔",
index 6383cfd..c6dced9 100644 (file)
        "nstab-template": "کينډۍ",
        "nstab-help": "لارښود مخ",
        "nstab-category": "وېشنيزه",
+       "mainpage-nstab": "لومړی مخ",
        "nosuchaction": "هېڅ داسې کومه کړنه نشته",
        "nosuchactiontext": "کومه کړنه چې د URL لخوا ځانگړې شوې سمه نه ده.\nکېدای شي چې URL مو سم نه وي ټايپ کړی، او يا مو يوه ناسمه تړنه څارلې وي.\nدا د دې هم ښکارندويي کوي چې کېدای شي چې د {{SITENAME}} لخوا کارېدونکې ساوترې کې يوه تېروتنه وي.",
        "nosuchspecialpage": "داسې هېڅ کوم ځانگړی مخ نشته",
        "badtitletext": "ستاسې د غوښتل شوي مخ سرليک سم نه وو، يا مو د سرليک ځای تش وو او يا هم د ژبو خپلمنځي تړنې څخه يا د ويکي گانو خپلمنځي سرليکونو څخه يو ناسم توری مو پکې کارولی وي.\nکېدای شي چې ستاسې په ورکړ شوي سرليک کې يو يا څو داسې توري وي چې د سرليک په توگه بايد و نه کارېږي.",
        "title-invalid-interwiki": "په سرليک کې يوه ويکي خپلمنځي تړنه ده",
        "perfcached": "لاندينی اومتوک په حافظه کې ساتل شوی او کېدای شي اوسمهاله شوی نه وي.  اکثر بريد له مخې {{PLURAL:$1|يوه پايله|$1 پايلې}} په حافظه کې شته.",
+       "perfcachedts": "لاندې راغلی اومتوک په حافظه کې ساتل شوی او وروستی ځل په $1 هم مهاله شوی. په ساتلې حافظې کې ډېر تر ډېره {{PLURAL:$4|يوه پايله ده|$4 پايلې دي}}.",
        "querypage-no-updates": "د دې مخ اوسمهالېدنې ناچارن شوي.\nپه ښکاره توگه د دې ځای اومتوک به نه وي تازه شوي.",
        "viewsource": "سرچينه کتل",
        "viewsource-title": "د $1 سرچينه کتل",
        "yourpassword": "پټنوم:",
        "userlogin-yourpassword": "پټنوم",
        "userlogin-yourpassword-ph": "پټنوم مو وليکۍ",
-       "createacct-yourpassword-ph": "پټنوم مو وټاپۍ",
+       "createacct-yourpassword-ph": "پټنوم مو وټاپئ",
        "yourpasswordagain": "پټنوم بيا وليکه",
        "createacct-yourpasswordagain": "پټنوم مو تاييد کړۍ",
-       "createacct-yourpasswordagain-ph": "پټنوم مو بيا وټاپۍ",
+       "createacct-yourpasswordagain-ph": "پټنوم مو بيا وټاپئ",
        "remembermypassword": "زما پټنوم په دې کمپيوټر (تر $1 {{PLURAL:$1|ورځې|ورځو}}) په ياد وساته!",
        "userlogin-remembermypassword": "غونډال کې مې ننوتلی وساته",
        "userlogin-signwithsecure": "خوندي اړيکتيا کارول",
        "userlogin-createanother": "بل گڼون جوړول",
        "createacct-emailrequired": "برېښليک پته",
        "createacct-emailoptional": "برېښليک پته (اختياري)",
-       "createacct-email-ph": "برېښليک پته مو وټاپۍ",
+       "createacct-email-ph": "برېښليک پته مو وټاپئ",
        "createacct-another-email-ph": "برېښليک پته مو ورکړۍ",
        "createaccountmail": "يو لنډمهاله ناټاکلی پټنوم کارول او ځانگړې شوې برېښليک پتې ته ورلېږل",
        "createacct-realname": "آر نوم (اختياري)",
        "createacct-reason": "سبب",
        "createacct-reason-ph": "ولې تاسې بل گڼون جوړول غوااړۍ",
        "createacct-captcha": "امنيتي تدبير",
-       "createacct-imgcaptcha-ph": "پورته تاسې ته ښکاره شوی متن وټاپۍ",
+       "createacct-imgcaptcha-ph": "پورته ښکاره شوی متن دلته وټاپئ",
        "createacct-submit": "گڼون مو جوړ کړئ",
        "createacct-another-submit": "بل گڼون جوړول",
        "createacct-benefit-heading": "{{SITENAME}} ستاسې په شان خلکو لخوا جوړ شوی.",
        "prefs-namespaces": "نوم-تشيالونه",
        "default": "تلواليز",
        "prefs-files": "دوتنې",
-       "prefs-custom-css": "ځاني CSS",
+       "prefs-custom-css": "دوديزه سي اس اس",
        "prefs-custom-js": "ځاني جاواسکرېپټ",
        "prefs-common-css-js": "د ټولو پوښونو لپاره د CSS/جاواسکرېپټ دوتنه:",
        "prefs-emailconfirm-label": "د برېښليک باورتيا:",
        "prefs-registration": "د نومليکنې وخت:",
        "yourrealname": "اصلي نوم:",
        "yourlanguage": "ژبه:",
+       "yourvariant": "د ژبگړدود مېنځپانگه:",
        "yournick": "نوی لاسليک:",
        "badsiglength": "ستاسو لاسليک ډېر اوږد دی.\nبايد چې لاسليک مو له $1 {{PLURAL:$1|توري|تورو}} نه لږ وي.",
        "yourgender": "څنگه غواړۍ ځان څرگند کړۍ؟",
        "gender-unknown": "ناڅرگنده",
        "gender-male": "نارينه",
        "gender-female": "ښځينه",
+       "prefs-help-gender": "دا غوره توب اختياري دی.\nساوتری د خپلو ارزښتونو په کارولو سره تاسې ته مخاطب کېږي او د کره جنسيت او کره گرامري صيغې له مخې ستاسې يادونه کوي.\nدا مالومات به په عامه توگه ښکاري.",
        "email": "برېښليک",
        "prefs-help-realname": "آر نوم ورکول ستاسې د خوښې کار دی.\nکه تاسې خپل آر نوم ورکړۍ، نو ستاسې ټولې کړنې به ستاسې په نوم اړوندې شي.",
        "prefs-help-email": "د برېښليک ورکړه ستاسې په خوښه ده، خو په ورکړې سره به يې د يوه نوي پټنوم د لېږلو چار آسانه کړي هغه هم کله چې تاسې نه خپل پټنوم هېر شوی وي.",
        "boteditletter": "ر",
        "number_of_watching_users_pageview": "[$1  {{PLURAL:$1|کتونکی کارن|کتونکي کارنان}}]",
        "rc_categories": "د وېشنيزو بريدونه (په \"|\" بېلول)",
-       "rc_categories_any": "هر يو",
+       "rc_categories_any": "Ù\84Ù\87 Ù¼Ø§Ú©Ù\84 Ø´Ù\88Ù\8aÙ\88 Ù\87ر Ù\8aÙ\88",
        "rc-change-size-new": "$1 {{PLURAL:$1|بايټ|بايټونه}} د بدلون وروسته",
        "newsectionsummary": "/* $1 */ نوې برخه",
        "rc-enhanced-expand": "تفصيل ښکاره کول",
        "upload-misc-error": "د پورته کېدنې نامالومه تېروتنه",
        "upload-http-error": "د HTTP يوه ستونزه رامېنځ ته شوې: $1",
        "upload-dialog-title": "دوتنه پورته کول",
-       "upload-dialog-error": "يوه ستونزه پېښه شوې",
-       "upload-dialog-warning": "يوه گواښنه رامېنځ ته شوه",
        "upload-dialog-button-cancel": "ناگارل",
        "upload-dialog-button-done": "ترسره شو",
        "upload-dialog-button-save": "خوندي کول",
        "upload-dialog-button-upload": "پورته کول",
-       "upload-dialog-label-select-file": "دوتنه ټاکل",
-       "upload-dialog-label-infoform-title": "ځانگړنې",
-       "upload-dialog-label-infoform-name": "نوم",
-       "upload-dialog-label-infoform-description": "څرگندونه",
-       "upload-dialog-label-usage-title": "کارېدنې",
-       "upload-dialog-label-usage-filename": "د دوتنې نوم",
+       "upload-process-error": "يوه ستونزه پېښه شوې",
+       "upload-process-warning": "يوه گواښنه رامېنځ ته شوه",
+       "upload-form-label-select-file": "دوتنه ټاکل",
+       "upload-form-label-infoform-title": "ځانگړنې",
+       "upload-form-label-infoform-name": "نوم",
+       "upload-form-label-infoform-description": "څرگندونه",
+       "upload-form-label-usage-title": "کارېدنې",
+       "upload-form-label-usage-filename": "د دوتنې نوم",
        "backend-fail-notexists": "د $1 په نوم دوتنه نشته.",
        "backend-fail-delete": "د \"$1\" دوتنه ړنګه نه شوه.",
        "backend-fail-alreadyexists": "د $1 دوتنه له پخوا نه شته.",
        "pageswithprop-submit": "ورځه",
        "doubleredirects": "دوه ځلي ورگرځېدنې",
        "brokenredirects": "ماتې ورگرځېدنې",
+       "brokenredirectstext": "لاندينۍ مخ گرځونې ناموجوده مخونو سره تړنې لري:",
        "brokenredirects-edit": "سمول",
        "brokenredirects-delete": "ړنگول",
        "withoutinterwiki": "د ژبې د تړنو بې برخې مخونه",
        "nmembers": "$1 {{PLURAL:$1|غړی|غړي}}",
        "nmemberschanged": "$1 → $2 {{PLURAL:$2|غړی|غړي}}",
        "nrevisions": "$1 {{PLURAL:$1|بڼه|بڼې}}",
-       "nimagelinks": "په $1 {{PLURAL:$1|کارېدلی مخ|کارېدلي مخونه}}",
+       "nimagelinks": "په $1 {{PLURAL:$1|مخ|مخونو}} کارېدلی",
        "ntransclusions": "په $1 {{PLURAL:$1|مخ|مخونو}} کارېدلی",
        "specialpage-empty": "د دې راپور لپاره کومې پايلې نشته.",
        "lonelypages": "يتيم مخونه",
        "editcomment": "د سمون لنډيز دا و: \"''$1''\".",
        "changecontentmodel-title-label": "مخ سرليک",
        "changecontentmodel-reason-label": "سبب:",
+       "logentry-contentmodel-change-revertlink": "په څټ گرځول",
+       "logentry-contentmodel-change-revert": "په څټ گرځول",
        "protectlogpage": "د ژغورنې يادښت",
        "protectlogtext": "دلته لاندې د ژغورل شويو مخونو د بدلونونو لړليک راغلی.\nد دم گړۍ فعالو مخ ژغورنو لړليک لپاره د [[Special:ProtectedPages|ژغورل شويو مخونو لړليک]] وگورئ.",
        "protectedarticle": "\"[[$1]]\" وژغورل شو",
        "sp-contributions-search": "د ونډو پلټنه",
        "sp-contributions-username": "IP پته يا کارن-نوم:",
        "sp-contributions-toponly": "يوازې هغه سمونونه چې تر ټولو تازه بڼې لري ښکاره کول",
+       "sp-contributions-newonly": "يوازې د مخ جوړېدنې سمونونه ښکاره کول",
        "sp-contributions-submit": "پلټل",
        "whatlinkshere": "د دې مخ تړنې",
        "whatlinkshere-title": "هغه مخونه چې د \"$1\" سره تړنې لري",
        "ipb_already_blocked": "پر \"$1\" د پخوا نه بنديز دی",
        "ipb-needreblock": "پر $1 د پخوا نه بنديز لگېدلی.\nآيا تاسې د امستنو بدلول غواړۍ؟",
        "ipb-otherblocks-header": "{{PLURAL:$1|بل بنديز|نور بنديزونه}}",
+       "ip_range_invalid": "ناسم آی پي بريد.",
        "lockdb": "توکبنسټ تړل",
        "unlockdb": "توکبنسټ پرانيستل",
        "lockconfirm": "هو، زه د توکبنسټ تړل غواړم.",
        "exif-unknowndate": "ناڅرگنده نېټه",
        "exif-orientation-1": "نورمال",
        "exif-componentsconfiguration-0": "نشته دی",
+       "exif-exposureprogram-1": "لاسي",
        "exif-exposureprogram-2": "نورماله پروګرام",
        "exif-subjectdistance-value": "$1 متره",
        "exif-meteringmode-0": "ناجوت",
index 4c41935..0de51d7 100644 (file)
        "upload-dialog-button-done": "Feito",
        "upload-dialog-button-save": "Salvar",
        "upload-dialog-button-upload": "Enviar",
-       "upload-dialog-label-select-file": "Selecionar arquivo",
-       "upload-dialog-label-infoform-title": "Detalhes",
-       "upload-dialog-label-infoform-name": "Nome",
-       "upload-dialog-label-infoform-description": "Descrição",
-       "upload-dialog-label-usage-title": "Uso",
-       "upload-dialog-label-usage-filename": "Nome do arquivo",
+       "upload-form-label-select-file": "Selecionar arquivo",
+       "upload-form-label-infoform-title": "Detalhes",
+       "upload-form-label-infoform-name": "Nome",
+       "upload-form-label-infoform-description": "Descrição",
+       "upload-form-label-usage-title": "Uso",
+       "upload-form-label-usage-filename": "Nome do arquivo",
        "backend-fail-stream": "Não foi possível transmitir o arquivo  $1.",
        "backend-fail-backup": "Não foi possível fazer backup do arquivo  $1 .",
        "backend-fail-notexists": "O arquivo $1 não existe.",
        "api-error-badtoken": "Erro interno: token inválido.",
        "api-error-copyuploaddisabled": "O upload por URL está desativado neste servidor.",
        "api-error-duplicate": "Já {{PLURAL:$1|há [$2 outro arquivo]|existem [$2 outros arquivos]}} com o mesmo conteúdo",
-       "api-error-duplicate-archive": "Já {{PLURAL:$1|existiu [$2 outro arquivo]|existiram [$2 outros arquivos]}} neste site com o mesmo conteúdo que, no entanto, {{PLURAL:$1|foi removido|foram removidos}}.",
+       "api-error-duplicate-archive": "Já {{PLURAL:$1|existiu outro arquivo|existiram outros arquivos}} neste site com o mesmo conteúdo que, no entanto, {{PLURAL:$1|foi removido|foram removidos}}.",
        "api-error-empty-file": "O arquivo que você enviou está vazio.",
        "api-error-emptypage": "Não é permitido criar páginas novas vazias.",
        "api-error-fetchfileerror": "Erro interno: ocorreu um problema indeterminado ao acessar o arquivo.",
index 5c340e0..b431452 100644 (file)
        "upload-http-error": "Ocorreu um erro HTTP: $1",
        "upload-copy-upload-invalid-domain": "Não é possível realizar carregamentos remotos neste domínio.",
        "upload-dialog-title": "Carregar ficheiro",
-       "upload-dialog-error": "Ocorreu um erro",
-       "upload-dialog-warning": "Ocorreu um aviso",
        "upload-dialog-button-cancel": "Cancelar",
        "upload-dialog-button-done": "Feito",
        "upload-dialog-button-save": "Gravar",
        "upload-dialog-button-upload": "Carregar",
-       "upload-dialog-label-select-file": "Selecionar ficheiro",
-       "upload-dialog-label-infoform-title": "Detalhes",
-       "upload-dialog-label-infoform-name": "Nome",
-       "upload-dialog-label-infoform-description": "Descrição",
-       "upload-dialog-label-usage-title": "Uso",
-       "upload-dialog-label-usage-filename": "Nome do ficheiro",
+       "upload-process-error": "Ocorreu um erro",
+       "upload-process-warning": "Ocorreu um aviso",
+       "upload-form-label-select-file": "Selecionar ficheiro",
+       "upload-form-label-infoform-title": "Detalhes",
+       "upload-form-label-infoform-name": "Nome",
+       "upload-form-label-infoform-description": "Descrição",
+       "upload-form-label-usage-title": "Uso",
+       "upload-form-label-usage-filename": "Nome do ficheiro",
        "backend-fail-stream": "Não foi possível transmitir o ficheiro \"$1\".",
        "backend-fail-backup": "Não foi possível fazer cópia de segurança do ficheiro \"$1\".",
        "backend-fail-notexists": "O ficheiro $1 não existe.",
index 39ba6db..848e709 100644 (file)
        "rev-suppressed-unhide-diff": "Parameters:\n* $1 - a HTML link to the diff\n{{Related|Rev-deleted-diff}}",
        "rev-deleted-diff-view": "{{Related|Rev-deleted-diff}}",
        "rev-suppressed-diff-view": "{{Related|Rev-deleted-diff}}",
-       "rev-delundel": "Link in page history for oversight (see also {{msg-mw|rev-showdeleted}})",
-       "rev-showdeleted": "Link in page history for oversight (see also {{msg-mw|rev-delundel}})\n{{Identical|Show}}",
+       "rev-delundel": "Link in page history for revdel (see also {{msg-mw|rev-showdeleted}})",
+       "rev-showdeleted": "Link in page history for revdel (see also {{msg-mw|rev-delundel}})\n{{Identical|Show}}",
        "revisiondelete": "{{RevisionDelete}}\n\n{{doc-special|RevisionDelete|unlisted=1}}",
        "revdelete-nooldid-title": "{{RevisionDelete}}",
        "revdelete-nooldid-text": "{{RevisionDelete}}",
        "revdelete-confirm": "This message is a part of the [[mw:RevisionDelete|RevisionDelete]] feature.\n\nRefers to {{msg-mw|Policy-url}}.\n\nSee also:\n* {{msg-mw|Revdelete-suppress}}\n* {{msg-mw|Revdelete-suppress-text}}\n[[File:RevDelete Special-RevisionDelete (r60428).png|frame|center|Screenshot of the interface]]",
        "revdelete-suppress-text": "Used as usage text in [[Special:RevisionDelete]].\n\nSee also:\n* {{msg-mw|Revdelete-suppress}}\n* {{msg-mw|Revdelete-confirm}}",
        "revdelete-legend": "{{RevisionDelete}}\nUsed as legend for the form.\n[[File:RevDelete Special-RevisionDelete (r60428).png|frame|center|Screenshot of the interface]]\n\nSee also:\n* {{msg-mw|Revdelete-log|label for dropdown}}\n* {{msg-mw|Revdelete-reason-dropdown|item list for dropdown|notext=1}}\n* {{msg-mw|Revdelete-reasonotherlist|item in dropdown}}\n* {{msg-mw|Revdelete-otherreason|label for input box}}\n* {{msg-mw|Revdelete-submit|submit button}}",
-       "revdelete-hide-text": "Option for oversight. This message is a part of the [[mw:RevisionDelete|RevisionDelete]] feature.\n[[File:RevDelete Special-RevisionDelete (r60428).png|frame|center|Screenshot of the interface]]\n{{Related|Revdelete-hide}}",
-       "revdelete-hide-image": "Option for <del>oversight</del> [[:mw:RevisionDelete|RevisionDelete]] feature.\n{{Related|Revdelete-hide}}",
-       "revdelete-hide-name": "Option for oversight.\n{{Related|Revdelete-hide}}",
-       "revdelete-hide-comment": "Option for oversight. {{RevisionDelete}}\n[[File:RevDelete Special-RevisionDelete (r60428).png|frame|center|Screenshot of the interface]]\n{{Related|Revdelete-hide}}\n{{Identical|Edit summary}}",
-       "revdelete-hide-user": "Option for oversight. {{RevisionDelete}}\n[[File:RevDelete Special-RevisionDelete (r60428).png|frame|center|Screenshot of the interface]]\n{{Related|Revdelete-hide}}",
-       "revdelete-hide-restricted": "Option for oversight.\n{{Related|Revdelete-hide}}",
+       "revdelete-hide-text": "Option for revdel. This message is a part of the [[mw:RevisionDelete|RevisionDelete]] feature.\n[[File:RevDelete Special-RevisionDelete (r60428).png|frame|center|Screenshot of the interface]]\n{{Related|Revdelete-hide}}",
+       "revdelete-hide-image": "Option for revdel.\n{{Related|Revdelete-hide}}",
+       "revdelete-hide-name": "Option for revdel.\n{{Related|Revdelete-hide}}",
+       "revdelete-hide-comment": "Option for revdel. {{RevisionDelete}}\n[[File:RevDelete Special-RevisionDelete (r60428).png|frame|center|Screenshot of the interface]]\n{{Related|Revdelete-hide}}\n{{Identical|Edit summary}}",
+       "revdelete-hide-user": "Option for revdel. {{RevisionDelete}}\n[[File:RevDelete Special-RevisionDelete (r60428).png|frame|center|Screenshot of the interface]]\n{{Related|Revdelete-hide}}",
+       "revdelete-hide-restricted": "Option for suppression.\n{{Related|Revdelete-hide}}",
        "revdelete-radio-same": "This message is a part of the [[mw:RevisionDelete|RevisionDelete]] feature. The message is a caption for a column of radioboxes inside a box with {{msg-mw|Revdelete-legend}} as a title.\n[[File:RevDelete Special-RevisionDelete (r60428).png|frame|center|Screenshot of the interface]]\nThere are three radio buttons in each row, and the captions above each column read:\n* {{msg-mw|Revdelete-radio-same}}\n* {{msg-mw|Revdelete-radio-set}}\n* {{msg-mw|Revdelete-radio-unset}}",
        "revdelete-radio-set": "This message is a part of the [[mw:RevisionDelete|RevisionDelete]] feature. The message is a caption for a column of radioboxes inside a box with {{msg-mw|Revdelete-legend}} as a title.\n[[File:RevDelete Special-RevisionDelete (r60428).png|frame|center|Screenshot of the interface]]\nThere are three radio buttons in each row, and the captions above each column read:\n* {{msg-mw|Revdelete-radio-same}}\n* {{msg-mw|Revdelete-radio-set}}\n* {{msg-mw|Revdelete-radio-unset}}\n{{Identical|Hidden}}",
        "revdelete-radio-unset": "This message is a part of the [[mw:RevisionDelete|RevisionDelete]] feature. The message is a caption for a column of radioboxes inside a box with {{msg-mw|Revdelete-legend}} as a title.\n[[File:RevDelete Special-RevisionDelete (r60428).png|frame|center|Screenshot of the interface]]\nThere are three radio buttons in each row, and the captions above each column read:\n* {{msg-mw|Revdelete-radio-same}}\n* {{msg-mw|Revdelete-radio-set}}\n* {{msg-mw|Revdelete-radio-unset}}\n{{Identical|Visible}}",
-       "revdelete-suppress": "Option for oversight; used in [[Special:RevisionDelete]].\n\nSee also:\n* {{msg-mw|Revdelete-suppress-text}}\n* {{msg-mw|Revdelete-confirm}}",
+       "revdelete-suppress": "Option for suppression; used in [[Special:RevisionDelete]].\n\nSee also:\n* {{msg-mw|Revdelete-suppress-text}}\n* {{msg-mw|Revdelete-confirm}}",
        "revdelete-unsuppress": "{{RevisionDelete}}",
-       "revdelete-log": "{{Identical|Reason}}\n{{RevisionDelete}}\nUsed as log comment text for oversight.\n[[File:RevDelete Special-RevisionDelete (r60428).png|frame|center|Screenshot of the interface]]\n\nSee also:\n* {{msg-mw|Revdelete-legend|legend for the form}}\n* {{msg-mw|Revdelete-reason-dropdown|item list for dropdown|notext=1}}\n* {{msg-mw|Revdelete-reasonotherlist|item in dropdown}}\n* {{msg-mw|Revdelete-otherreason|label for input box}}\n* {{msg-mw|Revdelete-submit|submit button}}",
+       "revdelete-log": "{{Identical|Reason}}\n{{RevisionDelete}}\nUsed as log comment text for revdel.\n[[File:RevDelete Special-RevisionDelete (r60428).png|frame|center|Screenshot of the interface]]\n\nSee also:\n* {{msg-mw|Revdelete-legend|legend for the form}}\n* {{msg-mw|Revdelete-reason-dropdown|item list for dropdown|notext=1}}\n* {{msg-mw|Revdelete-reasonotherlist|item in dropdown}}\n* {{msg-mw|Revdelete-otherreason|label for input box}}\n* {{msg-mw|Revdelete-submit|submit button}}",
        "revdelete-submit": "{{RevisionDelete}}\nThis is the submit button on [[Special:RevisionDelete]]. Parameters:\n* $1 - number of revisions\nSee also:\n* {{msg-mw|Revdelete-legend|legend for the form}}\n* {{msg-mw|Revdelete-log|label for dropdown}}\n* {{msg-mw|Revdelete-reason-dropdown|item list for dropdown|notext=1}}\n* {{msg-mw|Revdelete-reasonotherlist|item in dropdown}}\n* {{msg-mw|Revdelete-otherreason|label for input box}}\n[[File:RevDelete Special-RevisionDelete (r60428).png|frame|center|Screenshot of the interface]]",
        "revdelete-success": "{{RevisionDelete}}\n\nPossible alternative text - 'Restrictions on the revision visibility were successfully changed.'",
        "revdelete-failure": "{{RevisionDelete}}\nPossible alternative text - \"Restrictions on the revision visibility could not be changed\"\n\nParameters:\n* $1 - ...",
        "group-bot": "{{doc-group|bot}}\n{{Identical|Bot}}",
        "group-sysop": "{{doc-group|sysop}}\n{{Identical|Administrator}}",
        "group-bureaucrat": "{{doc-group|bureaucrat}}",
-       "group-suppress": "{{doc-group|suppress}}\nThis is an 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|Oversight}}",
+       "group-suppress": "{{doc-group|suppress}}\nThis is an 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}}",
        "group-all": "The name of the user group that contains all users, including anonymous users\n\n{{Identical|All}}",
        "group-user-member": "{{doc-group|user|member}}\n{{Identical|User}}",
        "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-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|Oversight}}",
+       "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-bureaucrat": "{{doc-group|bureaucrat|page}}",
-       "grouppage-suppress": "{{doc-group|suppress|page}}\n{{Identical|Oversight}}",
+       "grouppage-suppress": "{{doc-group|suppress|page}}\n{{Identical|Suppress}}",
        "right-read": "{{doc-right|read}}\nBasic right to read any page.",
        "right-edit": "{{doc-right|edit}}\nBasic right to edit pages that are not protected.\n{{Identical|Edit page}}",
        "right-createpage": "{{doc-right|createpage}}\nBasic right to create pages. The right to edit discussion/talk pages is {{msg-mw|right-createtalk}}.",
        "upload-http-error": "Parameters:\n* $1 - error message",
        "upload-copy-upload-invalid-domain": "Error message shown if a user is trying to upload (i.e. copy) a file from a website that is not in $wgCopyUploadsDomains (if set).\n\nSee also:\n* {{msg-mw|http-invalid-url}}\n* {{msg-mw|tmp-create-error}}\n* {{msg-mw|tmp-write-error}}",
        "upload-dialog-title": "Title of the upload dialog box\n{{Identical|Upload file}}",
-       "upload-dialog-error": "Error message from upload",
-       "upload-dialog-warning": "Warning message from upload",
        "upload-dialog-button-cancel": "Button to cancel the dialog\n{{Identical|Cancel}}",
        "upload-dialog-button-done": "Button to close the dialog once upload is complete\n{{Identical|Done}}",
        "upload-dialog-button-save": "Button to save the file\n{{Identical|Save}}",
        "upload-dialog-button-upload": "Button to initiate upload\n{{Identical|Upload}}",
-       "upload-dialog-label-select-file": "Label for the select file widget\n{{Identical|Select file}}",
-       "upload-dialog-label-infoform-title": "Title for the information form\n{{Identical|Detail}}",
-       "upload-dialog-label-infoform-name": "Label for the file name input\n{{Identical|Name}}",
-       "upload-dialog-label-infoform-description": "Label for the file description input\n{{Identical|Description}}",
-       "upload-dialog-label-usage-title": "Title for the insert form showing how to use the uploaded item.\n{{Identical|Usage}}",
-       "upload-dialog-label-usage-filename": "Label for the file name input\n{{Identical|Filename}}",
+       "upload-process-error": "Error message from upload",
+       "upload-process-warning": "Warning message from upload",
+       "upload-form-label-select-file": "Label for the select file widget\n{{Identical|Select file}}",
+       "upload-form-label-infoform-title": "Title for the information form\n{{Identical|Detail}}",
+       "upload-form-label-infoform-name": "Label for the file name input\n{{Identical|Name}}",
+       "upload-form-label-infoform-description": "Label for the file description input\n{{Identical|Description}}",
+       "upload-form-label-usage-title": "Title for the insert form showing how to use the uploaded item.\n{{Identical|Usage}}",
+       "upload-form-label-usage-filename": "Label for the file name input\n{{Identical|Filename}}",
        "backend-fail-stream": "Parameters:\n* $1 - a filename",
        "backend-fail-backup": "Parameters:\n* $1 - a filename",
        "backend-fail-notexists": "Parameters:\n* $1 - a filename",
        "filerevert-legend": "{{Identical|Revert}}",
        "filerevert-intro": "Message displayed when you try to revert a version of a file.\n* $1 is the name of the media\n* $2 is a date\n* $3 is a time\n* $4 is a URL and must follow square bracket: [$4\n{{Identical|Revert}}",
        "filerevert-comment": "{{Identical|Reason}}",
-       "filerevert-defaultcomment": "Parameters:\n* $1 - a date\n* $2 - a time\n{{Identical|Revert}}",
+       "filerevert-defaultcomment": "Parameters:\n* $1 - a date\n* $2 - a time\n* $3 - a timezone\n{{Identical|Revert}}",
        "filerevert-submit": "{{Identical|Revert}}",
        "filerevert-success": "Message displayed when you succeed in reverting a version of a file.\n* $1 is the name of the media\n* $2 is a date\n* $3 is a time\n* $4 is an URL and must follow square bracket: [$4\n{{Identical|Revert}}",
        "filerevert-badversion": "Used as error message.",
        "nopagetext": "Used as text on special pages like [[Special:MovePage]] (when the oldtitle does not exist) or [[Special:PermaLink]].\n\nThe title is {{msg-mw|nopagetitle}}.\n\nSee also:\n* {{msg-mw|Nopagetitle|title}}\n* {{msg-mw|Nopagetext|text}}",
        "pager-newer-n": "This is part of the navigation message on the top and bottom of Special pages which are lists of things in date order, e.g. the User's contributions page. It is passed as the second argument of {{msg-mw|Viewprevnext}}. $1 is the number of items shown per page.\n{{Identical|Newer}}",
        "pager-older-n": "This is part of the navigation message on the top and bottom of Special pages which are lists of things in date order, e.g. the User's contributions page. It is passed as the first argument of {{msg-mw|Viewprevnext}}. $1 is the number of items shown per page.",
-       "suppress": "{{Identical|Oversight}}",
+       "suppress": "{{Identical|Suppress}}",
        "querypage-disabled": "On special pages that use expensive database queries but are not cacheable, this message is displayed when 'miser mode' is on (i.e. no expensive queries allowed).",
        "apihelp": "{{doc-special|ApiHelp}}",
        "apihelp-summary": "{{doc-specialpagesummary|ApiHelp}}",
        "logentry-contentmodel-change-revert": "Prefilled edit summary when reverting a content model change. {{identical|revertmove}}",
        "protectlogpage": "{{doc-logpage}}\n\nTitle of [[Special:Log/protect]].",
        "protectlogtext": "Text in [[Special:Log/protect]].",
-       "protectedarticle": "Text describing an action on [[Special:Log]]. $1 is a page title.",
-       "modifiedarticleprotection": "Text describing an action on [[Special:Log]]. $1 is a page title.",
-       "unprotectedarticle": "Used as action in the log. Parameters:\n* $1 - target page title",
+       "protectedarticle": "This is a ''logentry'' message only used on IRC.\nText describing an action. $1 is a page title.",
+       "modifiedarticleprotection": "This is a ''logentry'' message only used on IRC.\nText describing an action. $1 is a page title.",
+       "unprotectedarticle": "This is a ''logentry'' message only used on IRC.\nUsed as action. Parameters:\n* $1 - target page title",
        "movedarticleprotection": "This is a ''logentry'' message only used on IRC. It appears in the log if a protected page is renamed.\n\nExample:\n<code>00:51, 16 September 2010 Siebrand +(Talk • contribs • block) moved protection settings from \"User:Siebrand/prot-move\" to \"User:Siebrand/prot-moved\" ‎ (User:Siebrand/prot-move moved to User:Siebrand/prot-moved: prot_move test.)</code>\n\nParameters:\n* $1 - target page title\n* $2 - source page title",
        "protect-title": "Title for the protection form. $1 is the title of the page to be (un)protected.",
        "protect-title-notallowed": "Same as {{msg-mw|Protect-title}}, but when the user does not have the right to change protection levels.\n\nParameters:\n* $1 - page title",
        "protect-fallback": "This message is used as an option in the protection form on wikis were extra protection levels have been configured.\n\nParameters:\n* $1 - undefined protection level (not localized). Defined protection levels are: \"sysop\" and \"autoconfirmed\"\n\nSee also:\n* {{msg-mw|Protect-level-sysop}}\n* {{msg-mw|Protect-level-autoconfirmed}}",
        "protect-level-autoconfirmed": "Used as protect level.\n\nSee example: {{canonicalurl:Main_Page|action=info}}",
        "protect-level-sysop": "Used as protect level.\n\nSee example: {{canonicalurl:Main_Page|action=info}}",
-       "protect-summary-desc": "{{Optional}}\nUsed in edit summary for description of a protecting restriction.\n* $1 is action, taken from restriction-*\n* $2 is restriction, taken from protect-level-*\n* $3 is {{msg-mw|protect-expiring}} or {{msg-mw|protect-expiry-indefinite}}",
+       "protect-summary-desc": "{{Optional}}\nUsed in edit summary for description of a protecting restriction.\n* $1 is action, taken from restriction-*\n* $2 is restriction, taken from protect-level-*\n* $3 is {{msg-mw|protect-expiring}}, {{msg-mw|protect-expiring-local}} or {{msg-mw|protect-expiry-indefinite}}",
        "protect-summary-cascade": "Used in edit summary when cascade protecting a page. Appears in protection log. See [[Special:Log]] and [[m:Special:Log]].\n\nAlso used in [[Special:ProtectedPages]] when a page is cascade protected. See example: [[m:Special:ProtectedPages]].<br />\nSee also:\n*{{msg-mw|Restriction-level-sysop}}\n*{{msg-mw|Restriction-level-autoconfirmed}}",
-       "protect-expiring": "Used as expiry text in page history, and in [[Special:Protectedtitles]], [[Special:Protectedpages]], and extension FlaggedRevs.\n* $1 - a date and time\n* $2 - a date (optional)\n* $3 - a time (optional)\nIf the expiry is indefinite, {{msg-mw|protect-expiry-indefinite}} is used.\n{{Identical|Expires $1 (UTC)}}",
-       "protect-expiring-local": "Parameter:\n* $1 - a timestamp like \"22:51, 23 July 2011 (UTC)\" depending on the wiki content language.\n{{Identical|Expire}}",
+       "protect-expiring": "Used as expiry text in page history, and in [[Special:Protectedtitles]], [[Special:Protectedpages]], and extension FlaggedRevs.\n* $1 - a date and time\n* $2 - a date (optional)\n* $3 - a time (optional)\nIf the expiry is indefinite, {{msg-mw|protect-expiry-indefinite}} is used.\n{{Identical|Expires $1 (UTC)}}\n\n\nSimilar to {{msg-mw|protect-expiring-local}}",
+       "protect-expiring-local": "Parameter:\n* $1 - a timestamp like \"22:51, 23 July 2011 (UTC)\" depending on the wiki content language.\n* $2 - a date (optional)\n* $3 - a time (optional)\n{{Identical|Expire}}\n\nSimilar to {{msg-mw|protect-expiring}}",
        "protect-expiry-indefinite": "Used as expiry text in page history, and in [[Special:Protectedtitles]], [[Special:Protectedpages]], and extension FlaggedRevs.\n\nIf the expiry is definite, {{msg-mw|protect-expiring}} is used.\n{{Identical|Indefinite}}",
        "protect-cascade": "See [[meta:Protect]] for more information.",
        "protect-cantedit": "Used as error message when changing the protection levels of the page.",
        "htmlform-title-not-exists": "Error message shown if the page title provided by the user does not exist. $1 is the page title.",
        "htmlform-user-not-exists": "Error message shown if a user with the name provided by the user does not exist. $1 is the username.",
        "htmlform-user-not-valid": "Error message shown if the name provided by the user isn't a valid username. $1 is the username.",
+       "rawmessage": "{{notranslate}} Used to pass arbitrary text as a message specifier array",
        "sqlite-has-fts": "Shown on [[Special:Version]].\nParameters:\n* $1 - version",
        "sqlite-no-fts": "Shown on [[Special:Version]].\nParameters:\n* $1 - version",
        "logentry-delete-delete": "{{Logentry|[[Special:Log/delete]]}}",
        "logentry-newusers-byemail": "{{Logentry|[[Special:Log/newusers]]}}\n\n$4 is the name of the user that was created.",
        "logentry-newusers-autocreate": "{{Logentry|[[Special:Log/newusers]]}}\n\n$4 is the gender of the target user.",
        "logentry-protect-move_prot": "{{Logentry|[[Special:Log/protect]]}}\n* $4 - the old title",
+       "logentry-protect-unprotect": "{{Logentry|[[Special:Log/protect]]}}",
+       "logentry-protect-protect": "{{Logentry|[[Special:Log/protect]]}}\n\n* $4 - protect expiry (formatted with {{msg-mw|protect-summary-desc}}, multiple possible)",
+       "logentry-protect-protect-cascade": "{{Logentry|[[Special:Log/protect]]}}\n\n* $4 - protect expiry (formatted with {{msg-mw|protect-summary-desc}}, multiple possible)\nFor word \"cascading\" see {{msg-mw|protect-summary-cascade}}",
+       "logentry-protect-modify": "{{Logentry|[[Special:Log/protect]]}}\n\n* $4 - protect expiry (formatted with {{msg-mw|protect-summary-desc}}, multiple possible)",
+       "logentry-protect-modify-cascade": "{{Logentry|[[Special:Log/protect]]}}\n\n* $4 - protect expiry (formatted with {{msg-mw|protect-summary-desc}}, multiple possible)\nFor word \"cascading\" see {{msg-mw|protect-summary-cascade}}",
        "logentry-rights-rights": "* $1 - username\n* $2 - (see below)\n* $3 - username\n* $4 - list of user groups or {{msg-mw|Rightsnone}}\n* $5 - list of user groups or {{msg-mw|Rightsnone}}\n----\n{{Logentry|[[Special:Log/rights]]}}",
        "logentry-rights-rights-legacy": "* $1 - username\n* $2 - (see below)\n* $3 - username\n----\n{{Logentry|[[Special:Log/rights]]}}",
        "logentry-rights-autopromote": "* $1 - username\n* $2 - (see below)\n* $3 - (see below)\n* $4 - comma separated list of old user groups or {{msg-mw|Rightsnone}}\n* $5 - comma separated list of new user groups\n----\n{{Logentry|[[Special:Log/rights]]}}",
index e7d89be..800bd77 100644 (file)
        "api-error-badtoken": "Ukhupi pantasqa: Mana allinta sananchasqa.",
        "api-error-copyuploaddisabled": "URL nisqawanqa kay sirwiqpi manam churkuyta atinki.",
        "api-error-duplicate": "Kay tiyaypiqa huk {{PLURAL:$1|[$2 willañiqim]|[$2 willañiqikunam]}} kachkanñam kaqlla samiqniyuq kaq.",
-       "api-error-duplicate-archive": "Kay tiyaypiqa huk {{PLURAL:$1|[$2 willañiqim]|[$2 willañiqikunam]}} karqanñam kaqlla samiqniyuq kaq, kunantaq qullusqañam.",
+       "api-error-duplicate-archive": "Kay tiyaypiqa huk {{PLURAL:$1|willañiqim|willañiqikunam}} karqanñam kaqlla samiqniyuq kaq, kunantaq qullusqañam.",
        "api-error-empty-file": "Kachasqayki willañiqiqa ch'usaqmi.",
        "api-error-emptypage": "Musuq ch'usaq p'anqakunata kamariyqa manam saqillasqachu.",
        "api-error-fetchfileerror": "Ukhupi pantasqa: Willañiqita chaskiykachachkaptiyki ima mana allin kaqpas tukurqan.",
index d92ea42..3ff8eb3 100644 (file)
        "api-error-badtoken": "Errur interna: Token fauss.",
        "api-error-copyuploaddisabled": "La funcziun da transferir dad ina URL è deactivada sin quest server.",
        "api-error-duplicate": "I dat gia {{PLURAL:$1|ina [$2 autra datoteca]|[$2 autras datotecas]}} cun il medem cuntegn.",
-       "api-error-duplicate-archive": "I deva gia {{PLURAL:$1|ina [$2 autra datoteca]|[$2 autras datotecas]}} cun il medem cuntegn, {{PLURAL:$1|quella è dentant vegnida stizzada|quellas èn dentant vegnidas stizzadas}}.",
+       "api-error-duplicate-archive": "I deva gia {{PLURAL:$1|ina autra datoteca|autras datotecas}} cun il medem cuntegn, {{PLURAL:$1|quella è dentant vegnida stizzada|quellas èn dentant vegnidas stizzadas}}.",
        "api-error-empty-file": "La datoteca tramessa è vida.",
        "api-error-emptypage": "Crear paginas novas e vidas n'è betg lubì.",
        "api-error-fetchfileerror": "Errur interna: Insatge n'ha betg funcziunà durant retschaiver la datoteca.",
index bf7a1a6..49f7b55 100644 (file)
@@ -26,7 +26,8 @@
                        "XXN",
                        "Fitoschido",
                        "Macofe",
-                       "ImGelu"
+                       "ImGelu",
+                       "Wintereu"
                ]
        },
        "tog-underline": "Sublinierea legăturilor:",
        "nstab-template": "Format",
        "nstab-help": "Ajutor",
        "nstab-category": "Categorie",
+       "mainpage-nstab": "Pagina principală",
        "nosuchaction": "Această acțiune nu există",
        "nosuchactiontext": "Acțiunea specificată în URL este invalidă.\nS-ar putea să fi introdus greșit URL-ul, sau să fi urmat o legătură incorectă.\nAceasta s-ar putea să indice și un bug în programul folosit de {{SITENAME}}.",
        "nosuchspecialpage": "Această pagină specială nu există",
        "changeemail-password": "Parola dumneavoastră la {{SITENAME}}:",
        "changeemail-submit": "Modifică adresa de e-mail",
        "changeemail-throttled": "Ați avut prea multe încercări de a vă autentifica.\nVă rugăm să așteptați $1 până să reîncercați.",
+       "changeemail-nochange": "Vă rugăm să introduceți o nouă adresă de e-mail diferită.",
        "resettokens": "Resetare jetoane",
        "resettokens-text": "Puteți reseta, aici, jetoanele care permit accesul la anumite date asociate contului dumneavoastră.\n\nAr trebui să faceți acest lucru numai dacă le-ați partajat accidental cu altcineva ori contul dumneavoastră a fost compromis.",
        "resettokens-no-tokens": "Nu există jetoane de resetat.",
        "permissionserrorstext-withaction": "Nu aveți permisiunea să $2, din {{PLURAL:$1|următorul motivul|următoarele motive}}:",
        "recreate-moveddeleted-warn": "'''Atenție: Recreați o pagină care a fost ștearsă anterior.'''\n\nAsigurați-vă că este oportună recrearea acestei pagini.\nJurnalul ștergerilor și al mutărilor pentru această pagină este disponibil:",
        "moveddeleted-notice": "Această pagină a fost ștearsă.\nJurnalul ștergerilor și al redenumirilor este disponibil mai jos.",
+       "moveddeleted-notice-recent": "Ne cerem scuze, dar această pagină a fost ștearsă recent (în ultimele 24 de ore).\nJurnalele de ștergere și redenumire ale paginii sunt disponibile mai jos cu scop informativ.",
        "log-fulllog": "Vezi tot jurnalul",
        "edit-hook-aborted": "Modificarea a fost abandonată din cauza unui hook.\nNicio explicație furnizată.",
        "edit-gone-missing": "Pagina nu s-a putut actualiza.\nSe pare că a fost ștearsă.",
        "upload-http-error": "A avut loc o eroare HTTP: $1",
        "upload-copy-upload-invalid-domain": "Încărcarea copiilor nu este disponibilă pentru acest domeniu.",
        "upload-dialog-title": "Încărcare fișier",
-       "upload-dialog-error": "A apărut o eroare",
-       "upload-dialog-warning": "A apărut o atenționare",
        "upload-dialog-button-cancel": "Revocare",
        "upload-dialog-button-done": "Realizat",
        "upload-dialog-button-save": "Salvare",
        "upload-dialog-button-upload": "Încarcă",
-       "upload-dialog-label-select-file": "Selectează fișier",
-       "upload-dialog-label-infoform-title": "Detalii",
-       "upload-dialog-label-infoform-name": "Nume",
-       "upload-dialog-label-infoform-description": "Descriere",
-       "upload-dialog-label-usage-title": "Utilizare",
-       "upload-dialog-label-usage-filename": "Numele fișierului",
+       "upload-process-error": "A apărut o eroare",
+       "upload-process-warning": "A apărut o atenționare",
+       "upload-form-label-select-file": "Selectează fișier",
+       "upload-form-label-infoform-title": "Detalii",
+       "upload-form-label-infoform-name": "Nume",
+       "upload-form-label-infoform-description": "Descriere",
+       "upload-form-label-usage-title": "Utilizare",
+       "upload-form-label-usage-filename": "Numele fișierului",
        "backend-fail-stream": "Imposibil de citit fișierul $1.",
        "backend-fail-backup": "Imposibil de efectuat o copie de rezervă a fișierului $1.",
        "backend-fail-notexists": "Fișierul $1 nu există.",
        "tooltip-t-contributions": "Vezi lista de contribuții ale acestui utilizator",
        "tooltip-t-emailuser": "Trimite un e-mail acestui utilizator",
        "tooltip-t-info": "Mai multe informații despre această pagină",
-       "tooltip-t-upload": "Încărcare de fișiere",
+       "tooltip-t-upload": "Încărcare fișiere",
        "tooltip-t-specialpages": "Lista tuturor paginilor speciale",
        "tooltip-t-print": "Versiunea de tipărit a acestei pagini",
        "tooltip-t-permalink": "Legătura permanentă către această versiune a paginii",
        "api-error-badaccess-groups": "Nu aveți dreptul să încărcați fișiere pe acest wiki.",
        "api-error-badtoken": "Eroare internă: jeton greșit.",
        "api-error-copyuploaddisabled": "Încărcarea prin URL este dezactivată pe acest server.",
-       "api-error-duplicate": "Există {{PLURAL:$1|un [$2 alt fișier]|[$2 alte fișiere]}} deja încărcate cu același conținut.",
-       "api-error-duplicate-archive": "{{PLURAL:$1|A existat [$2 un alt fișier]|Au existat [$2 alte fișiere]}} cu același conținut pe site, dar {{PLURAL:$1|a fost|au fost}} șterse.",
+       "api-error-duplicate": "Există {{PLURAL:$1|un alt fișier|alte fișiere}} deja încărcate cu același conținut.",
+       "api-error-duplicate-archive": "{{PLURAL:$1|A existat un alt fișier|Au existat alte fișiere}} cu același conținut pe site, dar {{PLURAL:$1|a fost|au fost}} șterse.",
        "api-error-empty-file": "Fișierul încărcat de dumneavoastră este gol.",
        "api-error-emptypage": "Crearea paginilor noi, goale nu este permisă.",
        "api-error-fetchfileerror": "Eroare internă: ceva nu a funcționat corect la prelucrarea fișierului.",
index 9c0026c..21e37ba 100644 (file)
        "upload-http-error": "S'a verificate 'n'errore HTTP: $1",
        "upload-copy-upload-invalid-domain": "'A copie de le carecaminde non g'è disponibbile da stu dominie.",
        "upload-dialog-title": "Careche 'u file",
-       "upload-dialog-error": "Ave assute 'n'errore",
-       "upload-dialog-warning": "Ha assute 'n'avvise",
        "upload-dialog-button-cancel": "Annulle",
        "upload-dialog-button-done": "Fatte",
        "upload-dialog-button-save": "Reggìstre",
        "upload-dialog-button-upload": "Careche",
-       "upload-dialog-label-select-file": "Scacchie 'u file",
-       "upload-dialog-label-infoform-title": "Dettaglie",
-       "upload-dialog-label-infoform-name": "Nome",
-       "upload-dialog-label-infoform-description": "Descrizione",
-       "upload-dialog-label-usage-title": "Ause",
-       "upload-dialog-label-usage-filename": "Nome d'u file",
+       "upload-process-error": "Ave assute 'n'errore",
+       "upload-process-warning": "Ha assute 'n'avvise",
+       "upload-form-label-select-file": "Scacchie 'u file",
+       "upload-form-label-infoform-title": "Dettaglie",
+       "upload-form-label-infoform-name": "Nome",
+       "upload-form-label-infoform-description": "Descrizione",
+       "upload-form-label-usage-title": "Ause",
+       "upload-form-label-usage-filename": "Nome d'u file",
        "backend-fail-stream": "Non ge pozze trasmettere 'u file $1.",
        "backend-fail-backup": "Non ge pozze cupià 'u file $1.",
        "backend-fail-notexists": "'U file $1 non g'esiste.",
        "api-error-badtoken": "Errore inderne: Gettone errate.",
        "api-error-copyuploaddisabled": "'U carecamende da URL jè disabbilitate sus a stu server.",
        "api-error-duplicate": "{{PLURAL:$1|Stè [$2 'n'otre file]|Stonne [$2 otre file]}} sus a 'u site cu 'u stesse condenute.",
-       "api-error-duplicate-archive": "{{PLURAL:$1|Stave [$2 'n'otre file]|Stavane [$2 otre file]}} già sus a 'u site cu 'u stesse condenute, ma {{PLURAL:$1|ha state|onne state}} scangellate.",
+       "api-error-duplicate-archive": "{{PLURAL:$1|Stave 'n'otre file|Stavane otre file}} già sus a 'u site cu 'u stesse condenute, ma {{PLURAL:$1|ha state|onne state}} scangellate.",
        "api-error-empty-file": "'U file ca tu è confermate ere vacande.",
        "api-error-emptypage": "Quanne se ne ccreje une, le pàggene vacande non ge sò permesse.",
        "api-error-fetchfileerror": "Errore inderne: Quacchecose ha sciute stuèrte quanne ste analizzave 'u file.",
index 25a198e..bd49b6f 100644 (file)
@@ -83,7 +83,8 @@
                        "Marina Melik-Adamyan",
                        "Normalex",
                        "WindEwriX",
-                       "Nzeemin"
+                       "Nzeemin",
+                       "INS Pirat"
                ]
        },
        "tog-underline": "Подчёркивание ссылок:",
        "newwindow": "&nbsp;(в новом окне)",
        "cancel": "Отменить",
        "moredotdotdot": "Далее…",
-       "morenotlisted": "Этот список не является полным.",
+       "morenotlisted": "Этот список неполон.",
        "mypage": "Страница",
        "mytalk": "Обсуждение",
        "anontalk": "Обсуждение для этого IP-адреса",
        "no-null-revision": "Не удалось создать новую нулевую правку для страницы «$1»",
        "badtitle": "Недопустимое название",
        "badtitletext": "Запрашиваемое название страницы неправильно, пусто, либо неверно указано межъязыковое или интервики название. Возможно, в названии используются недопустимые символы.",
-       "title-invalid-empty": " Ð\97аголовок Ð·Ð°Ð¿Ñ\80оÑ\88енной Ñ\81Ñ\82Ñ\80аниÑ\86Ñ\8b Ð¿Ñ\83Ñ\81Ñ\82 Ð¸Ð»Ð¸ Ñ\8fвлÑ\8fеÑ\82Ñ\81Ñ\8f Ð½Ð°Ð·Ð²Ð°Ð½Ð¸ÐµÐ¼ Ð¿Ñ\80оÑ\81Ñ\82Ñ\80анÑ\81Ñ\82ва Ð¸Ð¼Ðµн.",
+       "title-invalid-empty": " Ð\97аголовок Ð·Ð°Ð¿Ñ\80оÑ\88енной Ñ\81Ñ\82Ñ\80аниÑ\86Ñ\8b Ð¿Ñ\83Ñ\81Ñ\82 Ð¸Ð»Ð¸ Ñ\81одеÑ\80жиÑ\82 Ñ\82олÑ\8cко Ð½Ð°Ð·Ð²Ð°Ð½Ð¸Ðµ Ð¿Ñ\80оÑ\81Ñ\82Ñ\80анÑ\81Ñ\82ва Ð¸Ð¼Ñ\91н.",
        "title-invalid-utf8": "Запрашиваемое название страницы содержит некорректную последовательность символов UTF-8.",
        "title-invalid-interwiki": "Запрашиваемое название страницы содержит интервики-ссылку, которая не может быть использована в названиях.",
        "title-invalid-talk-namespace": "Запрашиваемое название страницы ссылается на страницу обсуждения, которая не может существовать.",
        "userrights-groups-help": "Вы можете изменить группы, в которые входит этот участник.\n* Если около названия группы стоит отметка, значит участник входит в эту группу.\n* Если отметка не стоит — участник не относится к соответствующей группе.\n* Знак * отмечает, что вы не сможете удалить участника из группы, если добавите его в неё, или наоборот.",
        "userrights-reason": "Причина:",
        "userrights-no-interwiki": "У вас нет разрешения изменять права участников на других вики.",
-       "userrights-nodatabase": "База данных $1 не существует или не является локальной.",
+       "userrights-nodatabase": "База данных $1 не существует или расположена не локально.",
        "userrights-nologin": "Вы должны [[Special:UserLogin|представиться системе]] с учётной записи администратора, чтобы присваивать права участникам.",
        "userrights-notallowed": "У вас нет разрешения добавлять и удалять права участников.",
        "userrights-changeable-col": "Группы, которые вы можете изменять",
        "fileexists": "Файл с этим именем уже существует, пожалуйста, проверьте <strong>[[:$1]]</strong>, если {{GENDER:|вы}} не уверены, что хотите заменить его.\n[[$1|thumb]]",
        "filepageexists": "Страница описания для этого файла уже создана как <strong>[[:$1]]</strong>, но файла с таким именем сейчас нет.\nВведённое описание не появится на странице описания файла.\nЧтобы добавить новое описание, вам придётся изменить его вручную.\n[[$1|thumb]]",
        "fileexists-extension": "Существует файл с похожим именем: [[$2|thumb]]\n* Имя загруженного файла: <strong>[[:$1]]</strong>\n* Имя существующего файла: <strong>[[:$2]]</strong>\nМожет быть, вы хотите использовать более отличающееся имя?",
-       "fileexists-thumbnail-yes": "Файл, Ð²ÐµÑ\80оÑ\8fÑ\82но, Ñ\8fвлÑ\8fеÑ\82Ñ\81Ñ\8f Ñ\83менÑ\8cÑ\88енной ÐºÐ¾Ð¿Ð¸ÐµÐ¹ (миниаÑ\82Ñ\8eÑ\80ой). [[$1|thumb]]\nÐ\9fожалÑ\83йÑ\81Ñ\82а, Ð¿Ñ\80овеÑ\80Ñ\8cÑ\82е Ñ\84айл <strong>[[:$1]]</strong>.\nÐ\95Ñ\81ли Ñ\83казаннÑ\8bй Ñ\84айл Ñ\8fвлÑ\8fеÑ\82Ñ\81Ñ\8f Ñ\82ем Ð¶Ðµ Ð¸Ð·Ð¾Ð±Ñ\80ажением, не стоит загружать отдельно его уменьшенную копию.",
+       "fileexists-thumbnail-yes": "Ð\92еÑ\80оÑ\8fÑ\82но, Ñ\8dÑ\82оÑ\82 Ñ\84айл â\80\94 Ñ\83менÑ\8cÑ\88еннаÑ\8f ÐºÐ¾Ð¿Ð¸Ñ\8f <em>(миниаÑ\82Ñ\8eÑ\80а)</em> Ñ\83же Ð¸Ð¼ÐµÑ\8eÑ\89егоÑ\81Ñ\8f Ñ\84айла. \n[[$1|thumb]]\nÐ\9fожалÑ\83йÑ\81Ñ\82а, Ð¿Ñ\80овеÑ\80Ñ\8cÑ\82е Ñ\84айл <strong>[[:$1]]</strong>.\nÐ\95Ñ\81ли Ñ\8dÑ\82о Ñ\82о Ð¶Ðµ Ñ\81амое Ð¸Ð·Ð¾Ð±Ñ\80ажение, не стоит загружать отдельно его уменьшенную копию.",
        "file-thumbnail-no": "Название файла начинается с <strong>$1</strong>.\nВероятно, это уменьшенная копия изображения ''(миниатюра)''.\nЕсли у вас есть данное изображение в полном размере, пожалуйста, загрузите его или измените имя файла.",
        "fileexists-forbidden": "Файл с этим именем уже существует и не может быть перезаписан.\nЕсли всё равно хотите загрузить данный файл, пожалуйста, вернитесь назад и загрузите его под другим именем. [[File:$1|thumb|center|$1]]",
        "fileexists-shared-forbidden": "Файл с этим именем уже существует в общем хранилище файлов.\nЕсли вы всё-таки хотите загрузить этот файл, пожалуйста, вернитесь назад и измените имя файла. [[File:$1|thumb|center|$1]]",
-       "file-exists-duplicate": "Этот файл является дубликатом {{PLURAL:$1|1=следующего файла|следующих файлов}}:",
+       "file-exists-duplicate": "Этот файл — дубликат {{PLURAL:$1|1=следующего файла|следующих файлов}}:",
        "file-deleted-duplicate": "Подобный файл ([[:$1]]) уже удалялся. Пожалуйста, ознакомьтесь с историей удаления файла, прежде чем загружать его снова.",
        "file-deleted-duplicate-notitle": "Файл, идентичный этому файлу, был ранее удалён, а имя файла было запрещено.\nВам следует попросить кого-нибудь с правами просмотра данных по запрещённым файлам, чтобы он проанализировал ситуацию перед тем, как загружать файл снова.",
        "uploadwarning": "Предупреждение",
        "upload-options": "Параметры загрузки",
        "watchthisupload": "Следить за этим файлом",
        "filewasdeleted": "Файл с таким именем уже существовал ранее, но был удалён. Пожалуйста, проверьте $1 перед повторной загрузкой.",
-       "filename-bad-prefix": "Имя загружаемого файла начинается с «'''$1'''» и, вероятно, является одним из шаблонных имён, которые цифровые фотокамеры дают снимкам. Пожалуйста, выберите имя, лучше описывающее содержание файла.",
+       "filename-bad-prefix": "Имя загружаемого файла начинается с «<strong>$1</strong>» и, вероятно, представляет собой одно из шаблонных имён, генерируемых цифровыми фотокамерами. Пожалуйста, выберите имя, лучше описывающее содержание файла.",
        "filename-prefix-blacklist": " #<!-- оставьте эту строчку как есть --> <pre>\n# Синтаксис следующий:\n#   * Всё, что начинается с символа «#», считается комментарием (до конца строки)\n#   * Каждая непустая строка — префикс стандартного названия файла, которое обычно даёт цифровая камера\nCIMG # Casio\nDSC_ # Nikon\nDSCF # Fuji\nDSCN # Nikon\nDUW # некоторые мобильные телефоны\nIMG # общее\nJD # Jenoptik\nMGP # Pentax\nPICT # различные\n #</pre> <!-- оставьте эту строчку как есть -->",
        "upload-success-subj": "Загрузка успешно завершена",
        "upload-success-msg": "Ваша загрузка [$2] прошла успешно. Вы можете посмотреть результат здесь: [[:{{ns:file}}:$1]]",
        "upload-http-error": "Произошла ошибка HTTP: $1",
        "upload-copy-upload-invalid-domain": "Копирование загрузок не доступно в этом домене.",
        "upload-dialog-title": "Загрузить файл",
-       "upload-dialog-error": "Произошла ошибка",
-       "upload-dialog-warning": "Появилось предупреждение",
        "upload-dialog-button-cancel": "Отменить",
        "upload-dialog-button-done": "Готово",
        "upload-dialog-button-save": "Сохранить",
        "upload-dialog-button-upload": "Загрузить",
-       "upload-dialog-label-select-file": "Выбрать файл",
-       "upload-dialog-label-infoform-title": "Подробности",
-       "upload-dialog-label-infoform-name": "Имя",
-       "upload-dialog-label-infoform-description": "Описание",
-       "upload-dialog-label-usage-title": "Использование",
-       "upload-dialog-label-usage-filename": "Имя файла",
+       "upload-process-error": "Произошла ошибка",
+       "upload-process-warning": "Появилось предупреждение",
+       "upload-form-label-select-file": "Выбрать файл",
+       "upload-form-label-infoform-title": "Подробности",
+       "upload-form-label-infoform-name": "Имя",
+       "upload-form-label-infoform-description": "Описание",
+       "upload-form-label-usage-title": "Использование",
+       "upload-form-label-usage-filename": "Имя файла",
        "backend-fail-stream": "Не удалось транслировать файл $1.",
        "backend-fail-backup": "Невозможно сделать резервную копию файла $1.",
        "backend-fail-notexists": "Файл $1 не существует.",
        "backend-fail-hashes": "Не удалось получить хэши файлов для сравнения.",
        "backend-fail-notsame": "Уже есть неидентичный файл $1.",
-       "backend-fail-invalidpath": "$1 не является допустимым путём хранения.",
+       "backend-fail-invalidpath": "$1 — некорректный путь к хранилищу.",
        "backend-fail-delete": "Не удалось удалить файл  $1.",
        "backend-fail-describe": "Не удалось изменить метаданные файла «$1».",
        "backend-fail-alreadyexists": "Файл $1 уже существует.",
        "lockmanager-fail-svr-acquire": "Не удалось получить блокировку на сервере  $1.",
        "lockmanager-fail-svr-release": "Не удалось снять блокировки на сервере $1 .",
        "zip-file-open-error": "Произошла ошибка при открытии файла для проверки архива.",
-       "zip-wrong-format": "Указанный файл не является файлом ZIP.",
+       "zip-wrong-format": "Указанный файл — не ZIP-архив.",
        "zip-bad": "ZIP-файл повреждён или не может быть прочитан.\nОн не может быть должным образом проверен.",
        "zip-unsupported": "Этот ZIP-файл использует возможности, не поддерживаемые MediaWiki.\nОн не может быть должным образом проверен.",
        "uploadstash": "Скрытная загрузка",
        "img-auth-nofile": "Файл «$1» не существует.",
        "img-auth-isdir": "Вы пытаетесь получить доступ к каталогу «$1».\nРазрешён только доступ к файлам.",
        "img-auth-streaming": "Потоковая передача «$1».",
-       "img-auth-public": "Назначением img_auth.php является вывод файлов из закрытой вики.\nЭта вики настроена как общедоступная.\nДля оптимизации безопасности img_auth.php отключена.",
+       "img-auth-public": "Назначение img_auth.php — вывод файлов из закрытой вики.\nЭта вики настроена как общедоступная.\nДля оптимизации безопасности img_auth.php отключена.",
        "img-auth-noread": "Участник не имеет доступа на чтение «$1».",
        "http-invalid-url": "Ошибочный URL: $1",
        "http-invalid-scheme": "Не поддерживаются адреса со схемой «$1»",
        "rollbacklinkcount": "откатить $1 {{PLURAL:$1|правку|правки|правок}}",
        "rollbacklinkcount-morethan": "откатить больше, чем $1 {{PLURAL:$1|правку|правки|правок}}",
        "rollbackfailed": "Ошибка при совершении отката",
-       "cantrollback": "Ð\9dевозможно Ð¾Ñ\82каÑ\82иÑ\82Ñ\8c Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ\8f. Ð\9fоÑ\81ледний, ÐºÑ\82о Ð²Ð½Ð¾Ñ\81ил Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ\8f, Ñ\8fвлÑ\8fеÑ\82Ñ\81Ñ\8f ÐµÐ´Ð¸Ð½Ñ\81Ñ\82веннÑ\8bм Ð°Ð²Ñ\82оÑ\80ом этой страницы.",
+       "cantrollback": "Ð\9dевозможно Ð¾Ñ\82каÑ\82иÑ\82Ñ\8c Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ\8f. Ð\9fоÑ\81ледним, ÐºÑ\82о Ð²Ð½Ð¾Ñ\81ил Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ\8f, Ð±Ñ\8bл ÐµÐ´Ð¸Ð½Ñ\81Ñ\82веннÑ\8bй Ð°Ð²Ñ\82оÑ\80 этой страницы.",
        "alreadyrolled": "Невозможно откатить последние изменения страницы «[[:$1]]», совершённые [[User:$2|$2]] ([[User talk:$2|обсуждение]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]),\nпоскольку кто-то другой уже успел откатить эти правки или отредактировать страницу.\n\nПоследние изменения {{GENDER:$3|внёс|внесла}} [[User:$3|$3]] ([[User talk:$3|обсуждение]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
        "editcomment": "Было дано описание изменения: ''$1''.",
        "revertpage": "Откат правок [[Special:Contributions/$2|$2]] ([[User talk:$2|обсуждение]]) к версии [[User:$1|$1]]",
        "lockedbyandtime": "($1 $2 $3)",
        "move-page": "$1 — переименование",
        "move-page-legend": "Переименование страницы",
-       "movepagetext": "Воспользовавшись нижеприведённой формой, вы переименуете страницу, одновременно переместив на новое место её журнал изменений.\nСтарое название станет перенаправлением на новое.\nВы можете автоматически обновить перенаправления, которые вели на старое название.\nЕсли вы этого не сделаете, пожалуйста, проверьте наличие [[Special:DoubleRedirects|двойных]] и [[Special:BrokenRedirects|разорванных перенаправлений]].\nВы отвечаете за то, чтобы ссылки продолжали и далее указывать туда, куда предполагалось.\n\nОбратите внимание, что страница '''не будет''' переименована, если уже существует страница с названием, идентичным выбранному, кроме случаев, когда такая страница является перенаправлением или пуста, и при этом не имеет истории правок.\nЭто означает, что если вы сделали преименование ошибочно, вы можете переименовать страницу обратно в то название, которое у неё только что было, но вы не можете случайно затереть существующую страницу.\n\n'''Предупреждение!'''\nПереименование ''популярных'' страниц может привести к масштабным и неожиданным изменениям.\nПожалуйста, прежде чем продолжать, убедитесь, что понимаете все возможные последствия.",
-       "movepagetext-noredirectfixer": "Воспользовавшись формой ниже, вы переименуете страницу, одновременно переместив на новое место её журнал изменений.\nСтарое название станет перенаправлением на новое название.\nПожалуйста, проверьте наличие [[Special:DoubleRedirects|двойных]] и [[Special:BrokenRedirects|разорванных перенаправлений]].\nВы отвечаете за то, чтобы ссылки продолжали и далее указывать туда, куда предполагалось.\n\nОбратите внимание, что страница <strong>не будет</strong> переименована, если страница с новым названием уже существует, кроме случаев, если она является перенаправлением или пуста и не имеет истории правок.\nЭто означает, что вы можете переименовать страницу обратно в то название, которое у него только что было, если вы переименовали по ошибке, но вы не можете случайно затереть существующую страницу.\n\n<strong>ПРЕДУПРЕЖДЕНИЕ!</strong>\nПереименование может привести к масштабным и неожиданным изменениям для популярных страниц.\nПожалуйста, прежде чем вы продолжите, убедитесь, что вы понимаете все возможные последствия.",
+       "movepagetext": "Воспользовавшись нижеприведённой формой, вы переименуете страницу, одновременно переместив на новое место её журнал изменений.\nСтарое название станет перенаправлением на новое.\nВы можете автоматически обновить перенаправления, которые вели на старое название.\nЕсли вы этого не сделаете, пожалуйста, проверьте наличие [[Special:DoubleRedirects|двойных]] и [[Special:BrokenRedirects|разорванных перенаправлений]].\nВы отвечаете за то, чтобы ссылки продолжали и далее указывать туда, куда предполагалось.\n\nОбратите внимание, что страница <strong>не будет</strong> переименована, если уже существует страница с названием, идентичным выбранному, кроме случаев, когда такая страница пуста или представляет собой перенаправление, и при этом не имеет истории правок.\nЭто означает, что сделав ошибочное переименование, вы можете переименовать страницу обратно в то название, которое у неё только что было, но не можете случайно затереть существующую страницу.\n\n<strong>Предупреждение!</strong>\nПереименование популярных страниц может привести к масштабным и неожиданным изменениям.\nПожалуйста, прежде чем продолжать, убедитесь, что понимаете все возможные последствия.",
+       "movepagetext-noredirectfixer": "Воспользовавшись формой ниже, вы переименуете страницу, одновременно переместив на новое место её журнал изменений.\nСтарое название станет перенаправлением на новое название.\nПожалуйста, проверьте наличие [[Special:DoubleRedirects|двойных]] и [[Special:BrokenRedirects|разорванных перенаправлений]].\nВы отвечаете за то, чтобы ссылки продолжали и далее указывать туда, куда предполагалось.\n\nОбратите внимание, что страница <strong>не будет</strong> переименована, если страница с новым названием уже существует, кроме случаев, если она пуста или представляет собой перенаправление, и при этом не имеет истории правок.\nЭто означает, что сделав ошибочное переименование, вы сможете переименовать страницу обратно в то название, которое у неё только что было, но не сможете случайно затереть существующую страницу.\n\n<strong>Предупреждение!</strong>\nПереименование может привести к масштабным и неожиданным изменениям для популярных страниц.\nПожалуйста, прежде чем продолжить, убедитесь, что понимаете все возможные последствия.",
        "movepagetalktext": "Присоединённая страница обсуждения будет также автоматически переименована, '''кроме случаев, когда:'''\n\n*Не пустая страница обсуждения уже существует под таким же именем или\n*Вы не поставили галочку в поле ниже.\n\nВ этих случаях, вы будете вынуждены переместить или объединить страницы вручную, если это нужно.",
        "movearticle": "Переименовать страницу",
        "moveuserpage-warning": "'''Внимание.''' Вы собираетесь переименовать страницу участника. Пожалуйста, обратите внимание, что переименована будет только страница, участник '''не''' будет переименован.",
        "version-poweredby-others": "другие",
        "version-poweredby-translators": "переводчики translatewiki.net",
        "version-credits-summary": "Хотим поблагодарить следующих участников за их вклад в развитие [[Special:Version|MediaWiki]].",
-       "version-license-info": "MediaWiki является свободным программным обеспечением, которое вы можете распространять и/или изменять в соответствии с условиями лицензии GNU General Public License, опубликованной фондом свободного программного обеспечения; второй версии, либо любой более поздней версии.\n\nMediaWiki распространяется в надежде, что она будет полезной, но БЕЗ КАКИХ-ЛИБО ГАРАНТИЙ, даже без подразумеваемых гарантий КОММЕРЧЕСКОЙ ЦЕННОСТИ или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЕННОЙ ЦЕЛИ. См. лицензию GNU General Public License для более подробной информации.\n\nВы должны были получить [{{SERVER}}{{SCRIPTPATH}}/COPYING копию GNU General Public License] вместе с этой программой, если нет, то напишите Free Software Foundation, Inc., по адресу: 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA или [//www.gnu.org/licenses/old-licenses/gpl-2.0.html прочтите её онлайн].",
+       "version-license-info": "MediaWiki — свободное программное обеспечение, которое вы можете распространять и/или изменять в соответствии с условиями лицензии GNU General Public License, опубликованной фондом свободного программного обеспечения; второй версии, либо любой более поздней версии.\n\nMediaWiki распространяется в надежде, что она будет полезной, но БЕЗ КАКИХ-ЛИБО ГАРАНТИЙ, даже без подразумеваемых гарантий КОММЕРЧЕСКОЙ ЦЕННОСТИ или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЕННОЙ ЦЕЛИ. См. лицензию GNU General Public License для более подробной информации.\n\nВы должны были получить [{{SERVER}}{{SCRIPTPATH}}/COPYING копию GNU General Public License] вместе с этой программой, если нет, то напишите Free Software Foundation, Inc., по адресу: 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA или [//www.gnu.org/licenses/old-licenses/gpl-2.0.html прочтите её онлайн].",
        "version-software": "Установленное программное обеспечение",
        "version-software-product": "Продукт",
        "version-software-version": "Версия",
        "tags-delete-title": "Удалить метку",
        "tags-delete-explanation-initial": "Вы собираетесь удалить метку «$1» из базы данных.",
        "tags-delete-explanation-in-use": "Она будет удалена из {{PLURAL:$2|$2 версии или записи в журнале, к которой|всех $2 версиях и/или записях в журнале, к которым}} она применяется в настоящее время.",
-       "tags-delete-explanation-warning": "Это действие является <strong>необратимым</strong> и <strong>не может быть отменено</strong> даже администраторами базы данных. Вы должны быть уверены, что это действительно метка, которую вы хотите удалить.",
+       "tags-delete-explanation-warning": "Это действие <strong>необратимо</strong> и <strong>не может быть отменено</strong> даже администраторами базы данных. Вы должны быть уверены, что это действительно метка, которую вы хотите удалить.",
        "tags-delete-explanation-active": "<strong>Метка «$1» по-прежнему активна и будет по-прежнему применяться в будущем.</strong> Чтобы этого не происходило, перейдите туда, где установлено использование метки, и отключить её там.",
        "tags-delete-reason": "Причина:",
        "tags-delete-submit": "Безвозвратно удалить эту метку",
        "dberr-outofdate": "Но имейте в виду, что его индекс может оказаться устаревшим.",
        "dberr-cachederror": "Ниже представлена закэшированная версия запрашиваемой страницы, возможно, она не отражает последних изменений.",
        "htmlform-invalid-input": "Часть введённых вами данных вызвала проблемы",
-       "htmlform-select-badoption": "Указанное Ð²Ð°Ð¼Ð¸ Ð·Ð½Ð°Ñ\87ение Ð½Ðµ Ñ\8fвлÑ\8fеÑ\82Ñ\81Ñ\8f Ð´Ð¾Ð¿Ñ\83Ñ\81Ñ\82имÑ\8bм.",
-       "htmlform-int-invalid": "Указанное вами значение не является целым числом.",
-       "htmlform-float-invalid": "Указанное Ð²Ð°Ð¼Ð¸ Ð·Ð½Ð°Ñ\87ение Ð½Ðµ Ñ\8fвлÑ\8fеÑ\82Ñ\81Ñ\8f Ñ\87иÑ\81лом.",
+       "htmlform-select-badoption": "Указано Ð½ÐµÐ´Ð¾Ð¿Ñ\83Ñ\81Ñ\82имое Ð·Ð½Ð°Ñ\87ение.",
+       "htmlform-int-invalid": "Указанное вами значение — не целое число.",
+       "htmlform-float-invalid": "Указано Ð½ÐµÑ\87иÑ\81ловое Ð·Ð½Ð°Ñ\87ение.",
        "htmlform-int-toolow": "Указанное вами значение ниже минимального — $1",
        "htmlform-int-toohigh": "указанное вами значение выше максимального — $1",
        "htmlform-required": "это значение необходимо",
        "htmlform-cloner-delete": "Удалить",
        "htmlform-cloner-required": "Требуется по крайней мере одно значение.",
        "htmlform-title-badnamespace": "[[:$1]] находится не в пространстве имён «{{ns:$2}}».",
-       "htmlform-title-not-creatable": "«$1» не является создаваемым заголовком страницы",
+       "htmlform-title-not-creatable": "«$1» — заголовок страницы, которая не может быть создана",
        "htmlform-title-not-exists": "[[:$1]] не существует.",
        "htmlform-user-not-exists": "<strong>$1</strong> не существует.",
-       "htmlform-user-not-valid": "<strong>$1</strong> не является допустимым именем пользователя.",
+       "htmlform-user-not-valid": "<strong>$1</strong> — недопустимое имя учётной записи.",
        "sqlite-has-fts": "$1 с поддержкой полнотекстового поиска",
        "sqlite-no-fts": "$1 без поддержки полнотекстового поиска",
        "logentry-delete-delete": "$1 {{GENDER:$2|удалил|удалила}} страницу $3",
        "api-error-badaccess-groups": "Вам не разрешено загружать файлы в эту вики.",
        "api-error-badtoken": "Внутренняя ошибка:  некорректный токен.",
        "api-error-copyuploaddisabled": "Загрузка по URL-адресу отключена на этом сервере.",
-       "api-error-duplicate": "Уже {{PLURAL:$1|1=существует [$2 другой файл]|существуют [$2 другие файлы]}} с таким же содержимым",
-       "api-error-duplicate-archive": "Раньше на сайте {{PLURAL:$1|1=уже был [$2 файл]|были [$2 файлы]}} с точно таким же содержанием, но {{PLURAL:$1|1=он был удалён|они были удалены}}.",
+       "api-error-duplicate": "Уже {{PLURAL:$1|существует другой файл|существуют другие файлы}} с таким же содержимым.",
+       "api-error-duplicate-archive": "Раньше на сайте {{PLURAL:$1|1=уже был файл|были файлы}} с точно таким же содержанием, но {{PLURAL:$1|1=он был удалён|они были удалены}}.",
        "api-error-empty-file": "Отправленный вами файл пуст.",
        "api-error-emptypage": "Не допускается создание новых пустых страниц.",
        "api-error-fetchfileerror": "Внутренняя ошибка: что-то пошло не так при получении файла.",
index 980206f..192e974 100644 (file)
        "api-error-badtoken": "Внутрїшня хыба: планый знак.",
        "api-error-copyuploaddisabled": "Наладовованя з URL є на тім сервері заказане.",
        "api-error-duplicate": "На тій вікі уж {{PLURAL:$1|екзістує [$2 другый файл]|екзістують [$2 іншы файлы]}} з такым самым обсягом.",
-       "api-error-duplicate-archive": "{{PLURAL:$1|быв [$2 другый файл]|были [$2 даякы другы файлы]}} з такым самым обсягом уж гев оперед {{PLURAL:$1|быв|были}}, але {{PLURAL:$1|быв змазаный|были змазаны}}.",
+       "api-error-duplicate-archive": "{{PLURAL:$1|быв другый файл|были даякы другы файлы}} з такым самым обсягом уж гев оперед {{PLURAL:$1|быв|были}}, але {{PLURAL:$1|быв змазаный|были змазаны}}.",
        "api-error-empty-file": "Наладованый файл є порожнїй.",
        "api-error-emptypage": "Створїня новых, порожнїх сторінк неслободно.",
        "api-error-fetchfileerror": "Внутрїшня хыба: трафила ся хыба під час обтриманя файлу.",
index d2916a8..20830d9 100644 (file)
        "api-error-badtoken": "आन्तरिकदोषः : दुष्टप्रतीकः ।",
        "api-error-copyuploaddisabled": "अस्मिन् वितारके युआर् एल् द्वारा उत्तारणं निष्क्रियम् ।",
        "api-error-duplicate": "{{PLURAL:$1| [ $2 अन्यसञ्चिकाः] | सन्ति [ $2 काश्चन अन्यसञ्चिकाः]}} एकस्मिन् एव ।",
-       "api-error-duplicate-archive": "तत्र {{PLURAL:$1|आसीत् [$2 काश्चन अन्यसञ्चिकाः] |  [$2काचन अन्यसञ्चिकाः]}}, पूर्वमेव {{PLURAL:$1|यह was|they आसन्}} किन्तु अपनीताः ।",
+       "api-error-duplicate-archive": "तत्र {{PLURAL:$1|आसीत् काश्चन अन्यसञ्चिकाः|काचन अन्यसञ्चिकाः}}, पूर्वमेव {{PLURAL:$1|यह was|they आसन्}} किन्तु अपनीताः ।",
        "api-error-empty-file": "समर्पिता सञ्चिका रिक्ता आसीत् ।",
        "api-error-emptypage": "नूतनस्य रिक्तस्य पृष्ठस्य सर्जनं निषिद्धम् ।",
        "api-error-fetchfileerror": "आन्तरिकदोषः : सञ्चिकायाः प्राप्त्यवसरे कश्चन दोषः जातः ।",
index 657e867..3e0c9f0 100644 (file)
        "nstab-template": "Халыып",
        "nstab-help": "Көмө",
        "nstab-category": "Категория",
+       "mainpage-nstab": "Сүрүн сирэй",
        "nosuchaction": "Маннык дьайыы суох",
        "nosuchactiontext": "Бу URL-га баар дьайыы сыыһалаах.\nБаҕар URL суруйарга алҕаһаабытыҥ буолуо эбэтэр сыыһалаах сигэннэн тахсыбытыҥ буолуо.\n{{SITENAME}} бэйэтин сыыһата эмиэ буолуон сөп.",
        "nosuchspecialpage": "Маннык анал сирэй суох",
        "changeemail-password": "{{SITENAME}} ситим-сиргэ киирэр тылыҥ:",
        "changeemail-submit": "Аадырыһы уларыт",
        "changeemail-throttled": "Ааккын аһара элбэхтик билиһиннэрэ сатаатыҥ.\nБука диэн $1 буолан баран өссө киирэн көрөөр.",
+       "changeemail-nochange": "Бука диэн, атын аадырыста суруй.",
        "resettokens": "Токеннары бырах",
        "resettokens-text": "Бу ситим-сиргэ бэлиэтэммит ааккын кытта ситимнээх токеннары сотуоххун сөп.\n\nАлҕас кимиэхэ эрэ биэрэн кэбиспит буоллаххына эбэтэр ким эрэ эн ааккынан алдьатан киирбит буоллаҕына маны туһаныахха сөп.",
        "resettokens-no-tokens": "Сотуллар токен суох эбит.",
        "upload-dialog-button-done": "Оҥоһулунна",
        "upload-dialog-button-save": "Бигэргэт",
        "upload-dialog-button-upload": "Киллэрии",
-       "upload-dialog-label-select-file": "Билэни тал",
-       "upload-dialog-label-infoform-title": "Сиһилии",
-       "upload-dialog-label-infoform-name": "Аата",
-       "upload-dialog-label-infoform-description": "Быһаарыыта",
-       "upload-dialog-label-usage-title": "Туһаныы",
-       "upload-dialog-label-usage-filename": "Билэ аата",
+       "upload-process-error": "Алҕас таҕыста",
+       "upload-process-warning": "Сэрэтии үөскээтэ",
+       "upload-form-label-select-file": "Билэни тал",
+       "upload-form-label-infoform-title": "Сиһилии",
+       "upload-form-label-infoform-name": "Аата",
+       "upload-form-label-infoform-description": "Быһаарыыта",
+       "upload-form-label-usage-title": "Туһаныы",
+       "upload-form-label-usage-filename": "Билэ аата",
        "backend-fail-stream": "$1 билэни ыытар табыллыбата.",
        "backend-fail-backup": "Бу билэ $1 резервнэй куопуйатын оҥорор табыллыбата.",
        "backend-fail-notexists": "Маннык $1 билэ суох эбит.",
        "booksources-text": "Манна кинигэ туһунан атын саайтарга ыйынньыктар хомулуннулар, онно баҕар эбии информация көстүөҕэ.",
        "booksources-invalid-isbn": "ISBN, арааһа, сыыһалаах. Нүөмэр көһөрөргө алҕас тахсыбатаҕын хат көр эрэ.",
        "specialloguserlabel": "Толорооччу:",
-       "speciallogtitlelabel": "Сорук (тиэкис эбэтэр киһи аата)",
+       "speciallogtitlelabel": "Сыал (тиэкис эбэтэр {{ns:user}}:кыттааччы аата):",
        "log": "Сурунааллар",
        "all-logs-page": "Көстөр сурунааллар барыта",
        "alllogstext": "{{SITENAME}} сурунаалларын уопсай испииһэгэ.\nСурунаал көрүҥүнэн, кыттааччы аатынан (улахан-кыра буукубата учуоттанар) эбэтэр сирэй аатынан (эмиэ улахана-кырата учуоттанар) наардыаххытын сөп.",
        "api-error-badtoken": "Ис алҕас: Омсолоох токен.",
        "api-error-copyuploaddisabled": "URL көмөтүнэн киллэрии бу сиэрбэргэ араарыллыбыт.",
        "api-error-duplicate": "Маннык иһинээҕилээх {{PLURAL:$1|[$2 атын билэ] баар эбит}}",
-       "api-error-duplicate-archive": "Урут ситим-сиргэ маннык иһинээҕилээх {{PLURAL:$1|[$2 билэ] баар |[$2 билэлэр] бааллар}} этэ, ол гынан баран {{PLURAL:$1|сотуллубута|сотуллубуттара}}.",
+       "api-error-duplicate-archive": "Урут ситим-сиргэ маннык иһинээҕилээх {{PLURAL:$1|билэ баар|билэлэр бааллар}} этэ, ол гынан баран {{PLURAL:$1|сотуллубута|сотуллубуттара}}.",
        "api-error-empty-file": "Ыыппыт билэҥ кураанах.",
        "api-error-emptypage": "Саҥа кураанах сирэйи оҥорор табыллыбат.",
        "api-error-fetchfileerror": "Ис алҕас: билэни ыларга туох эрэ сатаммата.",
index 9f84a26..5013665 100644 (file)
        "help": "Guida",
        "search": "Arricerca",
        "searchbutton": "Va arricerca",
-       "go": "Trova",
+       "go": "Attrova",
        "searcharticle": "Vai",
        "history": "Crunuluggìa dâ pàggina",
        "history_short": "Crunuluggìa",
        "pool-servererror": "Lu sirvizziu di cuntaturi dî pool nun è dispunìbbili ($1)",
        "poolcounter-usage-error": "Erruri d'utilizzu: $1",
        "aboutsite": "Nfurmazzioni supra a {{SITENAME}}",
-       "aboutpage": "Project:Àutri nformazzioni",
+       "aboutpage": "Project:Àutri nfurmazzioni",
        "copyright": "Lu cuntinutu è utilizzàbbili secunnu la $1, sarvu minzioni cuntraria.",
        "copyrightpage": "{{ns:project}}:Copyright",
        "currentevents": "Nutizzî",
        "versionrequiredtext": "P'usari sta pàggina ci voli la virsioni $1 dû software MediaWiki. Talìa [[Special:Version|sta pàggina]]",
        "ok": "Va bonu",
        "retrievedfrom": "Estrattu di \"$1\"",
-       "youhavenewmessages": "Arricivisti $1 ($2).",
-       "youhavenewmessagesfromusers": "Arricivisti $1 di {{PLURAL:$3|n'àutru utenti|$3 utenti}} ($2).",
+       "youhavenewmessages": "{{PLURAL:$3|Arricivisti}} $1 ($2).",
+       "youhavenewmessagesfromusers": "{{PLURAL:$4|Arricivisti}} $1 di {{PLURAL:$3|n'àutru utenti|$3 utenti}} ($2).",
        "youhavenewmessagesmanyusers": "Arricivisti $1 di tanti utenti ($2).",
        "newmessageslinkplural": "{{PLURAL:$1|nu missaggiu novu|999=missaggi novi}}",
        "newmessagesdifflinkplural": "{{PLURAL:$1|ùrtimu canciamentu|999=ùrtimi canciamenti}}",
        "nstab-template": "Template",
        "nstab-help": "Pàggina di guida",
        "nstab-category": "Catigurìa",
+       "mainpage-nstab": "Pàggina principali",
        "nosuchaction": "Opirazzioni nun ricanusciuta",
        "nosuchactiontext": "L'azzioni spicificata nâ URL nun è vailida.\nPoi aviri sbagghiatu a digitari â URL, o cliccatu supra nu link sbagghiatu.\nChistu putissi ndicari nu bug nô software usatu da {{SITENAME}}.",
        "nosuchspecialpage": "Sta pàggina spiciali nun è dispunìbbili",
-       "nospecialpagetext": "<strong>Hai cercatu na pàggina spiciali nun vàlida.</strong>\n\nL'alencu dî pàggini spiciali vàlidi s'attrova 'n [[Special:SpecialPages|Alencu dî pàggini spiciali]].",
+       "nospecialpagetext": "<strong>Arricircasti na pàggina spiciali nun vàlida.</strong>\n\nL'elencu dî pàggini spiciali vàlidi s'attrova n [[Special:SpecialPages|Elencu dî pàggini spiciali]].",
        "error": "Erruri",
        "databaseerror": "Erruri dû database",
        "databaseerror-text": "Si virificau n'erruri nti na dimanna dû databbasi.\nPutissi siri ca c'è nu bacu ntô prugramma.",
        "createacct-captcha": "Cuntrollu di sicurizza",
        "createacct-imgcaptcha-ph": "Nzirìsci lu testu ca vidi ccassupra",
        "createacct-submit": "Crea lu tò cuntu",
-       "createacct-another-submit": "Crea n'àutru cuntu",
+       "createacct-another-submit": "Crìa un cuntu",
        "createacct-benefit-heading": "{{SITENAME}} è fatta di pirsuni comu a tìa.",
        "createacct-benefit-body1": "{{PLURAL:$1|cuntribbutu|cuntribbuti}}",
        "createacct-benefit-body2": "{{PLURAL:$1|pàggina|pàggini}}",
        "changeemail-password": "La tò password di {{SITENAME}}:",
        "changeemail-submit": "Cancia nnirizzu",
        "changeemail-throttled": "Facisti troppi tintativi di trasuta.\nPi favuri aspetta $1 prima di pruvari n'àutra vota.",
+       "changeemail-nochange": "Pi’ favuri metti nu nnirizzu di posta elittrònica diffirenti di chiddu ca già cc’è.",
        "resettokens": "Azziramentu dî token",
        "resettokens-text": "Ccà poi azzirari li ''token'' chi dùnanu accessu a certi dati risirvati assuciati ô tò cuntu.\n\nSta cosa s'avissi a fari si pi sbagghiu li facisti sapiri a quarchidunu o si lu tò cuntu fu cumprumisu.",
        "resettokens-no-tokens": "Nun ci sunnu token d'azzirari.",
        "permissionserrorstext-withaction": "Nun hai lu pirmissu di $2, pi {{PLURAL:$1|stu mutivu|sti mutivi}}:",
        "recreate-moveddeleted-warn": "'''Accura: stai pi criari na pàggina chi fu cancillata 'n passatu.'''\n\nAccuràtivi ch'è uppurtunu cuntinuari a canciari sta pàggina.\nL'alencu dî cancillazzioni e spustamenti rilativi veni ripurtatu ccà pi cummudità:",
        "moveddeleted-notice": "Sta pàggina fu scancillata. La lista di li scancillazzioni e spustamenti veni ammustrata di sècutu pi nfurmazzioni.",
+       "moveddeleted-notice-recent": "Nni dispiaci, ma sta pàggina ricintimenti fu scancillata (nta l’ùrtimi 24 uri).\nComu rifirimentu, ccassutta cci sunnu li riggistra dî scancillazzioni e dî spustamenti rilativi a’ sta pàggina.",
        "log-fulllog": "Talìa lu riggistru cumpletu",
        "edit-hook-aborted": "Canciamentu annullatu di n'hook.\nNun desi nudda spigazzioni.",
        "edit-gone-missing": "Nun si pò aggiurnari la pàggina.\nPari ca fu cancillata.",
        "revdelete-modify-no-access": "Impussibili canciari l'oggettu cu data $1 $2 in quantu fu identificatu comu \"riservatu\" e nun si disponi di lu rilativu accessu.",
        "revdelete-modify-missing": "Impossibili canciari l'oggettu cu ID $1 in quantu nun è presenti nô database.",
        "revdelete-no-change": "'''Attenzioni:''' l'oggettu cu data $1 $2 aveva già l'impostazioni di visibilità richiesti.",
-       "revdelete-concurrent-change": "Impussibili canciari l'oggettu cu data $1 $2 in quantu lu sò statu fu canciatu da n'autru utenti mentri si tintava lu canciamentu.\nCuntrolla lu log.",
+       "revdelete-concurrent-change": "Mpussìbbili canciari l'oggettu cu data $1 $2 n quantu lu sò statu fu canciatu di n'àutru utenti mentri si tintava lu canciamentu.\nCuntrolla lu log.",
        "revdelete-only-restricted": "Erruri nta l'ammucciamentu di l'elimentu datatu $2,$1: Nun si ponnu livari elimenti dâ vista di l'amministratura senza puru scègghiri una di l'àutri opzioni di visibbilità.",
        "revdelete-reason-dropdown": "* Mutivi cchiù cumuni pâ cancillazzioni\n** Viulazzioni dû drittu d'auturi\n** Cummenti o nfurmazzioni pirsunali nun oppurtuni\n** Nomu utenti nun oppurtunu\n** Nfurmazzioni putinzialmenti diffamatorî",
        "revdelete-otherreason": "Àutru/ultiriuri mutivu:",
        "mergehistory-empty": "Nudda virsioni di jùnciri.",
        "mergehistory-success": "$3 {{PLURAL:$3|virsioni di [[:$1]] fu junciuta|$3 virsioni di [[:$1]] foru junciuti}} â crunuluggìa di [[:$2]].",
        "mergehistory-fail": "Nun fu pussìbbili jùnciri li crunuluggìi, pi favuri cuntrolla n'àutra vota li paràmitri chi spicìficanu li pàggini e li dati.",
-       "mergehistory-fail-toobig": "Nun fu pussìbbili jùnciri li crunuluggìi pirchì s'avìssiru a spustari cchiossai virsioni dû lìmiti chi è $1.",
+       "mergehistory-fail-toobig": "Nun fu pussìbbili jùnciri li crunuluggìi pirchì s'avìssiru a spustari cchiossai virsioni dû lìmiti chi è {{PLURAL:$1|$1}}.",
        "mergehistory-no-source": "La pàggina d'orìggini $1 nun esisti.",
        "mergehistory-no-destination": "La pàggina di distinazzioni $1 nun esisti.",
        "mergehistory-invalid-source": "La pàggina d'orìggini havi a aviri nu tìtulu vàlidu.",
        "rows": "Righi:",
        "columns": "Culonni:",
        "searchresultshead": "Arricerca",
-       "stub-threshold": "Sogghia pi furmattari na liami comu bozza ($1):",
+       "stub-threshold": "Sogghia pi furmattari nu liami comu bozza ($1):",
        "stub-threshold-sample-link": "esempiu",
        "stub-threshold-disabled": "Disattivatu",
        "recentchangesdays": "Nùmmiru di jorna a ammustrari nta l'ùrtimi canciamenti:",
        "yournick": "Firma nova:",
        "prefs-help-signature": "Li cummenti ntê pàggini di discussioni s'avìssiru a firmari cu \"<nowiki>~~~~</nowiki>\", ca veni cunvirtutu ntâ tò firma cu appressu la data.",
        "badsig": "Firma grezza nun vàlida.\nCuntrolla l'etichetti HTML.",
-       "badsiglength": "La tò firma è troppu longa.\nNun havi a èssiri cchiù longa di $1 {{PLURAL:$1|caràttiri|caràttiri}}.",
+       "badsiglength": "La tò firma è troppu longa.\nNun havi a èssiri cchiù longa di $1 {{PLURAL:$1|caràttiri}}.",
        "yourgender": "Comu prifirisci èssiri discrivutu?",
        "gender-unknown": "Quannu t'ammintua, si pò, lu prugramma adòpira lu gèniri grammaticali nèutru",
        "gender-male": "È riggistratu supra {{SITENAME}}",
        "fileexists-forbidden": "Nu file cu stu nomu asisti già e nun pò essiri sovrascrittu. Turnari n'arreri e canciari lu nomu cu lu quali carricari lu file. [[File:$1|thumb|center|$1]]",
        "fileexists-shared-forbidden": "Nu file cu stu nomu asisti già nta l'archiviu dî risursi multimidiali cundivisi. Siddu voi ancora carricari lu file, pi favuri torna n'arreri e cancia lu nomu ca voi dari a lu file. [[File:$1|thumb|center|$1]]",
        "file-exists-duplicate": "Stu file è na copia duppiuni {{PLURAL:$1|dû|dî}} file ccà di sècutu:",
-       "file-deleted-duplicate": "Nu file lu stissu comu a chistu file ([[:$1]]) vinni scanciallatu prima di ora. S'aviss'a cuntrullari la stòria e lu picchì dâ scancillazzioni dû file prima di ri-caricàrilu.",
+       "file-deleted-duplicate": "Nu file lu stissu comu a stu file ([[:$1]]) vinni scancillatu prima di ora. S'avissi a cuntrullari la storia e lu pirchì dâ scancillazzioni dû file prima di carricàrilu arrè.",
        "file-deleted-duplicate-notitle": "Nu file idènticu a chistu fu cancillatu, e lu tìtulu fu supprimutu.\nTu avissi a addumannari a quarchidunu chi havi la pussibbilità di vìdiri lu cuntinutu dû file suppressu di valutari la situazzioni, prima di prucèdiri a carricàrilu n'àutra vota.",
        "uploadwarning": "Avvisu pû carricamentu",
        "uploadwarning-text": "Cancia ccassutta la discrizzioni dû file e prova n'àutra vota.",
        "upload-http-error": "Ammattìu n'erruri HTTP: $1",
        "upload-copy-upload-invalid-domain": "Lu carricamentu di copî nun è cunzintutu di stu duminiu.",
        "upload-dialog-title": "Carricamentu dûn file",
-       "upload-dialog-error": "Ammattìu n'erruri",
-       "upload-dialog-warning": "Ammattìu n'avvisu",
        "upload-dialog-button-cancel": "Annulla",
        "upload-dialog-button-done": "Finutu",
        "upload-dialog-button-save": "Sarva",
        "upload-dialog-button-upload": "Càrrica",
-       "upload-dialog-label-select-file": "Scegghi lu file",
-       "upload-dialog-label-infoform-title": "Dittagghî",
-       "upload-dialog-label-infoform-name": "Nomu",
-       "upload-dialog-label-infoform-description": "Discrizzioni",
-       "upload-dialog-label-usage-title": "Usu",
-       "upload-dialog-label-usage-filename": "Nomu dû file",
+       "upload-process-error": "Ammattìu n'erruri",
+       "upload-process-warning": "Ammattìu n'avvisu",
+       "upload-form-label-select-file": "Scegghi lu file",
+       "upload-form-label-infoform-title": "Dittagghî",
+       "upload-form-label-infoform-name": "Nomu",
+       "upload-form-label-infoform-description": "Discrizzioni",
+       "upload-form-label-usage-title": "Usu",
+       "upload-form-label-usage-filename": "Nomu dû file",
        "backend-fail-stream": "Nun fu pussìbbili trasmèttiri lu file \"$1\".",
        "backend-fail-backup": "Nun fu pussìbbili fari na copia di riserva dû file \"$1\".",
        "backend-fail-notexists": "Lu file $1 nun esisti.",
        "filehist-nothumb": "Nudda miniatura",
        "filehist-user": "Utenti",
        "filehist-dimensions": "Diminsioni",
-       "filehist-filesize": "Dimensioni dû file",
+       "filehist-filesize": "Diminzioni dû file",
        "filehist-comment": "Cummentu",
        "imagelinks": "Usu dû file",
        "linkstoimage": "{{PLURAL:$1|La pàggina siquenti richiàma|Li $1 pàggini siquenti richiàmanu}} stu file:",
        "filerevert-legend": "Riprìstina file",
        "filerevert-intro": "Stai pi ripristinari lu file '''[[Media:$1|$1]]''' â [virsioni $4 dô $2, $3].",
        "filerevert-comment": "Mutivu:",
-       "filerevert-defaultcomment": "Ripristinata la virsioni dô $1, $2",
+       "filerevert-defaultcomment": "Ripristinata la virsioni dô $1, $2 ($3)",
        "filerevert-submit": "Riprìstina",
        "filerevert-success": "'''Lu file [[Media:$1|$1]]''' hà statu ripristinatu â [$4 virsioni dô $2, $3].",
        "filerevert-badversion": "Nun esistanu virsiona locali pricidenti dô file cû timestamp richiestu.",
        "suppress": "Supravisuri",
        "querypage-disabled": "Sta pàggina spiciali fu disattivata pi mutivi di pristazzioni.",
        "apihelp": "Guida a l'API",
-       "apihelp-no-such-module": "Mòdulu \"$\" nun attruvatu.",
+       "apihelp-no-such-module": "Mòdulu «$1» nun attruvatu.",
        "booksources": "Fonti libbrarî",
        "booksources-search-legend": "Arricerca di fonti libbrarî",
        "booksources-isbn": "Còdici ISBN:",
        "listusers-blocked": "(bluccatu)",
        "activeusers": "Lista di l'utenti attivi",
        "activeusers-intro": "Chista è na lista di l'utenti chi fìciru na quarchi attività {{PLURAL:$1|nta l'ùrtimu jornu|nta l'ùrtimi $1 jorna}}.",
-       "activeusers-count": "$1 {{PLURAL:$1|azzioni|azzioni}} nta {{PLURAL:$3|l'ùrtimu jornu|l'ùrtimi $3 jorna}}",
+       "activeusers-count": "$1 {{PLURAL:$1|azzioni}} nta {{PLURAL:$3|l'ùrtimu jornu|l'ùrtimi $3 jorna}}",
        "activeusers-from": "Ammustra l'utenti a pàrtiri di:",
        "activeusers-hidebots": "Ammuccia li bot",
        "activeusers-hidesysops": "Ammuccia l'amministratura",
        "emailccsubject": "Copia dû missaggiu ca mannasti a $1: $2",
        "emailsent": "Missaggiu di posta elittrònica mannatu",
        "emailsenttext": "Lu tò missaggiu di posta elittrònica fu mannatu.",
-       "emailuserfooter": "Stu missaggiu fu mannatu di $1 a $2 pi menzu dâ funzioni \"{{int:emailuser}}\" supra a {{SITENAME}}.",
+       "emailuserfooter": "Stu missaggiu fu mannatu di {{GENDER:$1|$1}} a {{GENDER:$2|$2}} pi menzu dâ funzioni «{{int:emailuser}}» supra a {{SITENAME}}.",
        "usermessage-summary": "Lassatu nu missaggiu di sistema.",
        "usermessage-editor": "Missaggeri di sistema",
        "watchlist": "Lista taliata",
        "exbeforeblank": "lu cuntinutu prima dû svacantamentu era: \"$1\"",
        "delete-confirm": "Cancella \"$1\"",
        "delete-legend": "Cancella",
-       "historywarning": "<strong>Accura:</strong> La pàggina ca stai pi cancillari havi na crunuluggìa cu $1 virsioni:",
+       "historywarning": "<strong>Accura:</strong> La pàggina ca stai pi cancillari havi na crunuluggìa cu $1 {{PLURAL:$1|virsioni}}:",
        "confirmdeletetext": "Stai cancillannu na pàggina cu tutta la sò crunuluggìa.\nPi favuri, cunferma ca ntenni fari sta cosa, ca capisti li cunziguenzi, e chi lu fai secunnu [[{{MediaWiki:Policy-url}}|li lìnii guida]].",
        "actioncomplete": "Azzioni cumpritata",
        "actionfailed": "Azioni fallita",
        "deletereasonotherlist": "Àutru mutivu",
        "deletereason-dropdown": "* Mutivi cchiù cumuni pâ cancillazzioni\n** Spam\n** Vannalismu\n** Viulazzioni di lu drittu d'auturi\n** Dumanna di l'auturi\n** Rimannu scassatu",
        "delete-edit-reasonlist": "Cancia li mutivi dâ cancillazzioni",
-       "delete-toobig": "Sta pàggina havi na crunuluggìa dî canciamenti assai longa, cchiossai di $1 {{PLURAL:$1|virsioni|virsioni}}).\nLa cancillazzioni dî pàggini comu a chista è risirvata, pi scanzari la pussibbilitati di pruvucari senza vulìrilu prubblemi a {{SITENAME}}.",
-       "delete-warning-toobig": "Sta pàggina havi na crunuluggìa dî canciamenti assai longa, cchiossai di $1 {{PLURAL:$1|virsioni|virsioni}}).\nLa sò cancillazzioni pò disturbari lu funziunamentu di  {{SITENAME}}; prucedi cu cautela.",
+       "delete-toobig": "Sta pàggina havi na crunuluggìa dî canciamenti assai longa, cchiossai di $1 {{PLURAL:$1|virsioni}}.\nLa cancillazzioni dî pàggini comu a chista è risirvata, pi scanzari la pussibbilitati di pruvucari senza vulìrilu prubblemi a {{SITENAME}}.",
+       "delete-warning-toobig": "Sta pàggina havi na crunuluggìa dî canciamenti assai longa, cchiossai di $1 {{PLURAL:$1|virsioni}}.\nLa sò cancillazzioni pò disturbari lu funziunamentu di  {{SITENAME}}; prucedi cu cautela.",
        "deleteprotected": "Nun poi cancillari sta pàggina pirchì fu prutiggiuta.",
        "deleting-backlinks-warning": "'''Accura:''' [[Special:WhatLinksHere/{{FULLPAGENAME}}|Àutri pàggini]] sunnu culligati o nclùdinu la pàggina chi stai cancillannu.",
        "rollback": "Annullamentu di canciamenti",
        "ipaddressorusername": "Nnirizzu IP o nomu utenti:",
        "ipbexpiry": "Durata dû bloccu:",
        "ipbreason": "Mutivu:",
-       "ipbreason-dropdown": "*Mutivi cchiù cumuni pî blocchi\n** Nzerimentu di nformazziuni falsi\n** Cancillazzioni di cuntinuti dê pàggini\n** Liami prumozziunalu a siti sterni\n** Nzserimentu di cuntinuti privi di sensu\n** Cumportamenti ntimidatori o molestie\n** Usu ndebitu di cchiù cunti\n** Nomu utenti nun accittabbili",
+       "ipbreason-dropdown": "*Mutivi cchiù cumuni pî blocchi\n** Nzirimentu di nfurmazzioni fàusi\n** Cancillazzioni di cuntinuti dê pàggini\n** Liami prumuzziunali a siti esterni\n** Nzirimentu di cuntinuti privi di senzu\n** Cumpurtamenti ntimidatori o mulesti\n** Usu nun duvutu di cchiù cunti\n** Nomu utenti nun accittàbbili",
        "ipb-hardblock": "Mpidisci a l'utenti trasuti di fari canciamenti di stu nnirizzu IP",
        "ipbcreateaccount": "Mpidisci la criazzioni di cunti",
        "ipbemailban": "Mpidisci a l'utenti di mannari posta elittrònica",
        "move-over-sharedrepo": "== Lu file già esisti ==\n[[:$1]] già esisti ntôn dipòsitu cunnivisu. Spustari nu file nta stu titulu suprascrivi lu file cunnivisu.",
        "file-exists-sharedrepo": "Lu nomu di file scigghiutu già è adupiratu ntôn dipòsitu cunnivisu.\nPi favuri scegghi n'àutru nomu.",
        "export": "Esporta pàggini",
-       "exporttext": "È pussìbbili spurtari lu testu e la cronoluggìa dî canciamenti di na pàggina o d'un gruppu di pàggini n furmatu XML pi mpurtàrili n àutri siti ca utilìzzanu lu software MediaWiki, attraversu la pàggina [[Special:Import|di mportu]].\n\nPi spurtari li pàggini innicari li tìtuli ntâ casella di testu suttastanti, unu pi riga, e spicificari siddu s'addisìa ottèniri la virsioni currenti e tutti li virsioni pricidenti, cu li dati dâ cronuluggìa dâ pàggina, oppuru surtantu l'ùrtima virsioni e li dati currispunnenti a l'ùrtimu canciamentu.\n\nNta st'ùrtimu casu si pò macari utilizzari na lijami, p'asempiu [[{{#Special:Export}}/{{MediaWiki:Mainpage}}]] pi spurtari \"[[{{MediaWiki:Mainpage}}]]\".",
+       "exporttext": "È pussìbbili spurtari lu testu e la crunuluggìa dî canciamenti di na pàggina o d'un gruppu di pàggini n furmatu XML pi mpurtàrili n àutri siti ca utilìzzanu lu software MediaWiki, attraversu la pàggina [[Special:Import|di mportu]].\n\nPi spurtari li pàggini innicari li tìtuli ntâ casella di testu suttastanti, unu pi riga, e spicificari siddu s'addisìa ottèniri la virsioni currenti e tutti li virsioni pricidenti, cu li dati dâ crunuluggìa dâ pàggina, oppuru surtantu l'ùrtima virsioni e li dati currispunnenti a l'ùrtimu canciamentu.\n\nNta st'ùrtimu casu si pò macari utilizzari nu lijami, p'asempiu [[{{#Special:Export}}/{{MediaWiki:Mainpage}}]] pi spurtari \"[[{{MediaWiki:Mainpage}}]]\".",
        "exportall": "Esporta tutti li pàggini",
-       "exportcuronly": "Ncludi sulu la rivisioni attuali, nun la ntera cronoluggìa",
-       "exportnohistory": "---- '''Nota:''' l'espurtazzioni dâ ntera cronoluggìa dî pàggini attraversu sta nterfaccia hà stata disattivata pi mutivi ligati a li pristazzioni dû sistema.",
+       "exportcuronly": "Ncludi sulu la rivisioni attuali, nun la crunuluggìa ntera",
+       "exportnohistory": "---- '''Nota:''' l'espurtazzioni dâ ntera crunuluggìa dî pàggini attraversu sta nterfaccia fu disattivata pi mutivi ligati a li pristazzioni dû sistema.",
        "exportlistauthors": "Ncludi n'elencu cumpletu dî cuntribbuturi p'ogni pàggina",
        "export-submit": "Espurtazzioni",
        "export-addcattext": "Agghiunci pàggini dâ catigurìa:",
        "import-interwiki-text": "Silizziunari na wiki e lu tìtulu di na pàggina a mpurtari.\nLi dati dî virsioni e li noma di l'autura sunnu mantinuti.\nTutti li mpurtazzioni d'àutri wiki vèninu riggistrati ntô [[Special:Log/import|riggistru dî mpurtazzioni]].",
        "import-interwiki-sourcewiki": "Wiki surgenti:",
        "import-interwiki-sourcepage": "Pàggina surgenti:",
-       "import-interwiki-history": "Copia la ntera cronoluggìa di sta pàggina",
+       "import-interwiki-history": "Copia la crunuluggìa ntera di sta pàggina",
        "import-interwiki-templates": "Includi tutti li template",
        "import-interwiki-submit": "Mporta",
        "import-mapping-default": "Mporta ntê distinazzioni pridifinuti",
        "importcantopen": "Mpussìbbili grapiri lu file di mpurtazzioni",
        "importbadinterwiki": "Lijami inter-wiki erratu",
        "importsuccess": "Mpurtazzioni arrinisciuta.",
-       "importnosources": "Nun fu difinuta nudda wiki d'unni mpurtari e li carricamenti diretti dâ cronuluggìa sunnu disattivati.",
+       "importnosources": "Nun fu difinuta nudda wiki d'unni mpurtari e li carricamenti diretti dâ crunuluggìa sunnu disattivati.",
        "importnofile": "Nun hà statu carrcatu nuddu file pi la mpurtazzioni.",
        "importuploaderrorsize": "Caricamentu dû file pi la mpurtazzioni non arrinisciutu. Lu file è cchiù granni di li diminzioni màssimi cunzentiti pi l'upload.",
        "importuploaderrorpartial": "Caricamentu dû file pi la mpurtazzioni non arrinisciutu. Sulamenti na parti dû file vinni caricatu.",
-       "importuploaderrortemp": "Caricamentu dû file pi la mpurtazzioni non arrinisciutu. Manca na cartedda timpurània.",
+       "importuploaderrortemp": "Carricamentu dû file pi la mpurtazzioni nun arrinisciutu. Ammanca na cartedda timpurània.",
        "import-parse-failure": "Sbagghiu d'anàlisi ntâ mpurtazzioni XML",
        "import-noarticle": "Nudda pàggina di mpurtari.",
        "import-nonewrevisions": "Nudda virsioni fu mpurtata (o già c'èranu tutti, o foru sautati tutti pirchì ammatteru erruri).",
        "import-rootpage-invalid": "La pàggina ràdica spicificata nun è vàlida comu tìtulu.",
        "import-rootpage-nosubpage": "Lu namespace \"$1\" dâ pàggina ràdica nun cunzenti suttapàggini.",
        "importlogpage": "Riggistru dî mpurtazzioni",
-       "importlogpagetext": "Riggistru dî mpurtazzioni d'ufficiu di pàggini pruvinenti d'àutri wiki, cumpleti di cronoluggìa.",
+       "importlogpagetext": "Riggistru dî mpurtazzioni d'ufficiu di pàggini pruvinenti d'àutri wiki, cumpleti di crunuluggìa.",
        "import-logentry-upload-detail": "{{PLURAL:$1|na virsioni mpurtata|$1 virsioni mpurtati}}",
        "import-logentry-interwiki-detail": "{{PLURAL:$1|na virsioni mpurtata|$1 virsioni mpurtati}} di $2",
        "javascripttest": "Virìfichi JavaScript",
        "redirect-revision": "ID di virsioni di pàggina",
        "redirect-file": "Nomu di file",
        "redirect-not-exists": "Valuri nun attruvatu",
-       "fileduplicatesearch": "Circata dê file duppiuni",
+       "fileduplicatesearch": "Arricerca dê file duppiuni",
        "fileduplicatesearch-summary": "Circata di pussìbbili dupppiuni dû file 'n basi ô valuri di ''hash''.",
        "fileduplicatesearch-legend": "Circata di nu duppiuni",
        "fileduplicatesearch-filename": "Nomu dû file:",
        "specialpages-note-top": "Liggenna",
        "specialpages-note": "* Pàggini spiciali nurmali.\n* <span class=\"mw-specialpagerestricted\">Pàggini spiciali risirvati.</strong>",
        "specialpages-group-maintenance": "Resocunti di manutinzioni",
-       "specialpages-group-other": "Autri pàggini spiciali",
+       "specialpages-group-other": "Àutri pàggini spiciali",
        "specialpages-group-login": "Trasuta / criazzioni di cunti",
        "specialpages-group-changes": "Ùrtimi canciamenti e riggistri",
        "specialpages-group-media": "File multimidiali - caricamentu e rennicunti",
        "api-error-badaccess-groups": "Nun hai lu pirmissu di carricari file nta sta wiki.",
        "api-error-badtoken": "Erruri nternu: Token sbagghiatu",
        "api-error-copyuploaddisabled": "Lu carricamentu a pàrtiri d'URL è disattivatu nta stu server.",
-       "api-error-duplicate": "Già {{PLURAL:$1|c'è [$2 n'àutru file]|ci sunnu [$2 àutri file]}} supra ô situ chi {{PLURAL:$1|havi|hannu}} lu stissu cuntinutu.",
-       "api-error-duplicate-archive": "{{PLURAL:$1|C'era [$2 n'àutru file]|C'èranu [$2 àutri file]}} supra ô situ c'{{PLURAL:$1|avìa|avìanu}} lu stissu cuntinutu, ma {{PLURAL:$1|fu cancillatu|foru cancillati}}.",
+       "api-error-duplicate": "Già {{PLURAL:$1|c'è n'àutru file]|ci sunnu àutri file]}} supra ô situ chi {{PLURAL:$1|havi|hannu}} lu stissu cuntinutu.",
+       "api-error-duplicate-archive": "{{PLURAL:$1|C'era n'àutru file|C'èranu àutri file}} supra ô situ c'{{PLURAL:$1|avìa|avìanu}} lu stissu cuntinutu, ma {{PLURAL:$1|fu cancillatu|foru cancillati}}.",
        "api-error-empty-file": "Lu file chi mannasti era vacanti.",
        "api-error-emptypage": "Criari pàggini novi e vacanti nun è cunzintutu.",
        "api-error-fetchfileerror": "Erruri nternu: Quarchi cosa nun funziunau mentri chi si carricava lu file.",
index 450af83..7140361 100644 (file)
        "api-error-badtoken": "Inby mistak: Bad token.",
        "api-error-copyuploaddisabled": "Uplaidin bi URL is disabled oan this server.",
        "api-error-duplicate": "Thaur {{PLURAL:$1|is [$2 anither file]|ar [$2 some ither files]}} awreadie oan the site wi the same content.",
-       "api-error-duplicate-archive": "Thaur {{PLURAL:$1|wis [$2 anither file]|were [$2 some ither files]}} awreadie oan the site wi the same content, but {{PLURAL:$1|it wis|thay were}} delytit.",
+       "api-error-duplicate-archive": "Thaur {{PLURAL:$1|wis anither file|were some ither files}} awreadie oan the site wi the same content, but {{PLURAL:$1|it wis|thay were}} delytit.",
        "api-error-empty-file": "The file that ye haunnit in wis tuim.",
        "api-error-emptypage": "Cræftin new, tuim pages isna permittit.",
        "api-error-fetchfileerror": "Internal mistak: Sommit went wrang while fetchin the file.",
index f4f5a2f..4ffc8e5 100644 (file)
        "api-error-badtoken": "Kunahere firka: Tokore laala.",
        "api-error-copyuploaddisabled": "URL zijandiyan n' ka kay feršikaa woo ga.",
        "api-error-duplicate": "{{PLURAL:$1|ti [ti tuku tana $2]}} kaŋ ga bara interneti nungoo ga ka ben nda gundakuna follokaa da.",
-       "api-error-duplicate-archive": "{{PLURAL:$1|ti [ti tuku tana $2]}} kaŋ ga bara interneti nungoo ga ka ben nda gundakuna follokaa da, amma {{PLURAL:$1|an' ka|in' ka}} tuusandi.",
+       "api-error-duplicate-archive": "{{PLURAL:$1|ti ti tuku tana}} kaŋ ga bara interneti nungoo ga ka ben nda gundakuna follokaa da, amma {{PLURAL:$1|an' ka|in' ka}} tuusandi.",
        "api-error-empty-file": "Tukoo kaŋ war n'a sanba mma koonu.",
        "api-error-emptypage": "Boro ši hin ka moo taaga, koonuyaŋ tee.",
        "api-error-fetchfileerror": "Kunahere firka: Haya foo mana boori tuku zaayan waate.",
index 47b4076..2a6eea4 100644 (file)
        "api-error-badtoken": "Unutrašnja greška: token nije ispravan.",
        "api-error-copyuploaddisabled": "Postavljanja putem URL-a su onemogućena na ovom serveru.",
        "api-error-duplicate": "Već postoji {{PLURAL:$1|[$2 druga datoteka]|[$2 druge datoteke]}} na ovoj stranici sa istim sadržajem",
-       "api-error-duplicate-archive": "{{PLURAL:$1|Postojala je [$2 druga datoteka]|Postojale su [$2 neke druge datoteke]}} na sajtu sa istim sadržajem, ali {{PLURAL:$1|je obrisana|su obrisane}}.",
+       "api-error-duplicate-archive": "{{PLURAL:$1|Postojala je druga datoteka|Postojale su neke druge datoteke}} na sajtu sa istim sadržajem, ali {{PLURAL:$1|je obrisana|su obrisane}}.",
        "api-error-empty-file": "Datoteka koju ste poslali je bila prazna.",
        "api-error-emptypage": "Stvaranje novih praznih stranica nije dozvoljeno.",
        "api-error-fetchfileerror": "Unutrašnja greška: pojavio se neki problem pri dobijanju podataka o datoteci.",
index c263031..851be56 100644 (file)
        "api-error-badtoken": "අභ්‍යන්තර දෝෂය: නොසුදුසු ටෝකනය.",
        "api-error-copyuploaddisabled": "URL මඟින් උඩුගතකිරීම මෙම සර්වරයේදී අක්‍රීය කොට තිබේ.",
        "api-error-duplicate": "එකම අන්තර්ගතය සමඟ මෙවැනිම {{PLURAL:$1|[වෙනත් ගොනු $2 ක්]|[වෙනත් ගොනු $2 ක්]}} අඩවිය තුල දැනටමත් පවතියි.",
-       "api-error-duplicate-archive": "එකම අන්තර්ගතය සමඟ මෙවැනිම {{PLURAL:$1|[වෙනත් ගොනු $2 ක්]|[වෙනත් ගොනු $2 ක්]}} අඩවිය තුල දැනටමත් තිබුනා, නමුත්  {{PLURAL:$1|ඒක|ඒවා}} මකා දමා ඇත.",
+       "api-error-duplicate-archive": "එකම අන්තර්ගතය සමඟ මෙවැනිම {{PLURAL:$1|වෙනත් ගොනු ක්|වෙනත් ගොනු ක්}} අඩවිය තුල දැනටමත් තිබුනා, නමුත්  {{PLURAL:$1|ඒක|ඒවා}} මකා දමා ඇත.",
        "api-error-empty-file": "ඔබ ඉදිරිපත්කල ගොනුව හිස් එකකි.",
        "api-error-emptypage": "නවතම එකක් තනමින්, හිස් පිටුවලට ඉඩ නොදේ.",
        "api-error-fetchfileerror": "අභ්‍යන්තර දෝෂය: ගොනුව පැමිණවීම අතරතුරදී කුමක්දෝ වැරදුණා.",
index 15b17d7..ef8c1e6 100644 (file)
        "api-error-badtoken": "Vnútorná chyba: Zlý token.",
        "api-error-copyuploaddisabled": "Nahrávanie z URL je na tomto serveri zakázané.",
        "api-error-duplicate": "{{PLURAL:$1|[$2 ďalší súbor]|[$2 ďalšie súbory]}} s rovnakým obsahom už na tejto wiki existujú",
-       "api-error-duplicate-archive": "{{PLURAL:$1|[$2 ďalší súbor]|[$2 ďalšie súbory]}} s rovnakým obsahom už na tejto wiki existoval, ale {{PLURAL:$1|bol zmazaný|boli zmazané}}.",
+       "api-error-duplicate-archive": "{{PLURAL:$1|ďalší súbor|ďalšie súbory}} s rovnakým obsahom už na tejto wiki existoval, ale {{PLURAL:$1|bol zmazaný|boli zmazané}}.",
        "api-error-empty-file": "Súbor, ktorý ste poslali bol prázdny.",
        "api-error-emptypage": "Vytváranie nových, prázdnych stránok nie je dovolené.",
        "api-error-fetchfileerror": "Vnútorná chyba: Niečo pokazilo počas sťahovania súboru.",
index be35f72..54c9ef8 100644 (file)
        "createacct-captcha": "Varnostno preverjanje",
        "createacct-imgcaptcha-ph": "Vnesite zgornje besedilo",
        "createacct-submit": "Ustvarite svoj račun",
-       "createacct-another-submit": "Ustvarite še en račun",
+       "createacct-another-submit": "Ustvarite račun",
        "createacct-benefit-heading": "{{GRAMMAR:tožilnik|{{SITENAME}}}} ustvarjajo ljudje, kot ste vi.",
        "createacct-benefit-body1": "{{PLURAL:$1|urejanje|urejanji|urejanja|urejanj}}",
        "createacct-benefit-body2": "{{PLURAL:$1|stran|strani}}",
        "upload-http-error": "Prišlo je do napake HTTP: $1",
        "upload-copy-upload-invalid-domain": "Nalaganje kopij s te domene ni možno.",
        "upload-dialog-title": "Naloži datoteko",
-       "upload-dialog-error": "Prišlo je do napake",
-       "upload-dialog-warning": "Pojavilo se je opozorilo",
        "upload-dialog-button-cancel": "Prekliči",
        "upload-dialog-button-done": "Končano",
        "upload-dialog-button-save": "Shrani",
        "upload-dialog-button-upload": "Naloži",
-       "upload-dialog-label-select-file": "Izberi datoteko",
-       "upload-dialog-label-infoform-title": "Podrobnosti",
-       "upload-dialog-label-infoform-name": "Ime",
-       "upload-dialog-label-infoform-description": "Opis",
-       "upload-dialog-label-usage-title": "Uporaba",
-       "upload-dialog-label-usage-filename": "Ime datoteke",
+       "upload-process-error": "Prišlo je do napake",
+       "upload-process-warning": "Pojavilo se je opozorilo",
+       "upload-form-label-select-file": "Izberi datoteko",
+       "upload-form-label-infoform-title": "Podrobnosti",
+       "upload-form-label-infoform-name": "Ime",
+       "upload-form-label-infoform-description": "Opis",
+       "upload-form-label-usage-title": "Uporaba",
+       "upload-form-label-usage-filename": "Ime datoteke",
        "backend-fail-stream": "Ne morem pretakati datoteke $1.",
        "backend-fail-backup": "Ne morem varnostno kopirati datoteke $1.",
        "backend-fail-notexists": "Datoteka $1 ne obstaja.",
        "filerevert-legend": "Vrni datoteko",
        "filerevert-intro": "Vračate datoteko '''[[Media:$1|$1]]''' na [$4 različico $3, $2].",
        "filerevert-comment": "Razlog:",
-       "filerevert-defaultcomment": "Vrnjeno na različico $2, $1.",
+       "filerevert-defaultcomment": "Vrnjeno na različico $2, $1 ($3)",
        "filerevert-submit": "Vrni",
        "filerevert-success": "Datoteka '''[[Media:$1|$1]]''' je bila vrnjena na [$4 različico $3, $2].",
        "filerevert-badversion": "Ne najdem preteklih lokalnih verzij datoteke s podanim časovnim žigom.",
        "emailccsubject": "Kopija tvojega sporočila iz $1: $2",
        "emailsent": "E-pismo je poslano!",
        "emailsenttext": "E-pismo je poslano.",
-       "emailuserfooter": "To e-poštno sporočilo je bilo poslal(-a) $1 uporabniku $2 s funkcijo »{{int:emailuser}}« na {{GRAMMAR:dative|{{SITENAME}}}}.",
+       "emailuserfooter": "To e-poštno sporočilo je {{GENDER:$1|poslal|poslala|poslal(-a)}} $1 uporabniku {{GENDER:$2|$2}} s funkcijo »{{int:emailuser}}« na {{GRAMMAR:dative|{{SITENAME}}}}.",
        "usermessage-summary": "Pusti sistemsko sporočilo.",
        "usermessage-editor": "Sistemski sporočevalec",
        "watchlist": "Spisek nadzorov",
index 7655509..26659a6 100644 (file)
                        "Kosovastar"
                ]
        },
-       "tog-underline": "Nënvizizimi i lidhjes:",
-       "tog-hideminor": "Fsheh redaktimet e vogla nga ndryshimet e fundit",
-       "tog-hidepatrolled": "Fshih redaktimet e vrojtuara në ndryshimet e fundit",
-       "tog-newpageshidepatrolled": "Fshih faqet e vrojtuara nga lista e faqeve të reja",
+       "tog-underline": "Nënvizimi i lidhjes:",
+       "tog-hideminor": "Fshih redaktimet e vogla nga ndryshimet e fundit",
+       "tog-hidepatrolled": "Fshih redaktimet e paturlluara nga ndryshimet e fundit",
+       "tog-newpageshidepatrolled": "Fshih faqet e patrulluara nga lista e faqeve të reja",
        "tog-extendwatchlist": "Zgjero listën e faqeve të vëzhguara që t'i tregojë të gjitha ndryshimet, jo vetëm më të fundit.",
        "tog-usenewrc": "Grupo ndryshimet sipas faqeve në ndryshime së fundmi dhe listën mbikqyrëse (kërkon JavaScript)",
        "tog-numberheadings": "Numëro automatikish titujt",
-       "tog-showtoolbar": "Trego mjetet e redaktimit (kërkon JavaScript)",
-       "tog-editondblclick": "Redakto faqet me dopio-klik (kërkon JavaScript)",
-       "tog-editsectiononrightclick": "Lejo redaktimin e seksioneve duke klikuar me të djathtën mbi titullin e seksionit (kërkon JavaScript)",
+       "tog-showtoolbar": "Shfaq shiritin e veglave të redaktorit",
+       "tog-editondblclick": "Redakto faqe në klikim të dyfishtë",
+       "tog-editsectiononrightclick": "Aktivizo redaktimin e seksioneve duke klikuar me të djathtën mbi titullin e seksionit",
        "tog-watchcreations": "Shtoi faqet e krijuara dhe skedat e ngarkuara prej meje tek lista e faqeve nën mbikqyrje",
        "tog-watchdefault": "Shto faqet dhe skedat e redaktuara prej meje tek lista e faqeve nën mbikqyrje",
        "tog-watchmoves": "Shto faqet dhe skedat e zhvendosura prej meje tek lista e faqeve nën mbikqyrje",
        "november-gen": "nëntor",
        "december-gen": "dhjetor",
        "jan": "Jan",
-       "feb": "Shku",
+       "feb": "Shk",
        "mar": "Mar",
        "apr": "Pri",
        "may": "Maj",
        "jun": "Qer",
        "jul": "Korr",
        "aug": "Gush",
-       "sep": "Shta",
+       "sep": "Sht",
        "oct": "Tet",
        "nov": "Nën",
        "dec": "Dhje",
        "november-date": "$1 nëntor",
        "december-date": "$1 dhjetor",
        "pagecategories": "{{PLURAL:$1|Kategoria|Kategoritë}}",
-       "category_header": "Artikuj në kategorinë \"$1\"",
-       "subcategories": "Nën-kategori",
-       "category-media-header": "Skeda në kategorinë \"$1\"",
-       "category-empty": "''Kjo kategori aktualisht nuk përmban asnjë faqe apo media.''",
+       "category_header": "Faqet në kategorinë \"$1\"",
+       "subcategories": "Nënkategoritë",
+       "category-media-header": "Media në kategorinë \"$1\"",
+       "category-empty": "<em>Kjo kategori aktualisht nuk përmban asnjë faqe apo media.</em>",
        "hidden-categories": "{{PLURAL:$1|Kategori e fshehur|Kategori të fshehura}}",
        "hidden-category-category": "Kategori të fshehura",
        "category-subcat-count": "{{PLURAL:$2|Kjo kategori ka vetëm këtë nën-kategori.|Kjo kategori ka {{PLURAL:$1|këtë nën-kategori|$1 këto nën-kategori}}, nga $2 gjithësej.}}",
        "noindex-category": "Faqe jo të indeksuara",
        "broken-file-category": "Faqet me lidhjet file thyer",
        "about": "Rreth",
-       "article": "Artikulli",
+       "article": "Faqja e përmbajtjes",
        "newwindow": "(hapet në një dritare të re)",
        "cancel": "Anulo",
        "moredotdotdot": "Më shumë...",
        "morenotlisted": "Kjo listë nuk është e plotë.",
        "mypage": "Faqja",
-       "mytalk": "Diskutimet",
+       "mytalk": "Diskuto",
        "anontalk": "Diskutimet për këtë IP",
-       "navigation": "Shfleto",
+       "navigation": "Navigimi",
        "and": "&#32;dhe",
        "qbfind": "Gjeni",
        "qbbrowse": "Shfletoni",
-       "qbedit": "Redaktoni",
+       "qbedit": "Redakto",
        "qbpageoptions": "Kjo faqe",
        "qbmyoptions": "Faqet e mia",
-       "faq": "Pyetje që bëhen shpesh",
-       "faqpage": "Projekt: Pyetje që bëhen shpesh",
+       "faq": "Pyetje të shpeshta",
+       "faqpage": "Project: Pyetje të shpeshta",
        "actions": "Veprimet",
        "namespaces": "Hapsirat e emrit",
-       "variants": "Variante",
-       "navigation-heading": "Menuja e navigimit",
+       "variants": "Variantat",
+       "navigation-heading": "Menyja e navigimit",
        "errorpagetitle": "Gabim",
        "returnto": "Kthehuni tek $1",
        "tagline": "Nga {{SITENAME}}",
        "history": "Historiku i faqes",
        "history_short": "Historiku",
        "updatedmarker": "përditësuar që nga vizita ime e fundit",
-       "printableversion": "Version për printer",
+       "printableversion": "Versioni i printueshëm",
        "permalink": "Lidhje e përhershme",
        "print": "Printo",
        "view": "Shiko",
        "view-foreign": "Pamja <span class=\"notranslate\" translate=\"asnjë\">$1</span>",
        "edit": "Redakto",
-       "edit-local": "Modifiko burimin lokalt të përshkrimit",
+       "edit-local": "Redakto përshkrimin lokal",
        "create": "Krijo",
        "create-local": "Shtonipërshkrimin lokal",
        "editthispage": "Redakto këtë faqe",
-       "create-this-page": "Krijoje këtë faqe",
-       "delete": "Grise",
+       "create-this-page": "Krijo këtë faqe",
+       "delete": "Fshi",
        "deletethispage": "Grise këtë faqe",
        "undeletethispage": "Rikthe faqen",
        "undelete_short": "Anullo fshirjen {{PLURAL:$1|një redaktim|$1 redaktime}}",
        "viewtalkpage": "Shiko diskutimet",
        "otherlanguages": "Në gjuhë të tjera",
        "redirectedfrom": "(Përcjellë nga $1)",
-       "redirectpagesub": "Faqe përcjellëse",
-       "redirectto": "Përcjellim për tek:",
+       "redirectpagesub": "Ridrejto faqen",
+       "redirectto": "Ridrejto tek:",
        "lastmodifiedat": "Kjo faqe është ndryshuar për herë te fundit më $1, në orën $2.",
        "viewcount": "Kjo faqe është shikuar {{PLURAL:$1|një|$1 herë}} .",
        "protectedpage": "Faqe e mbrojtur",
        "nstab-template": "Stampa",
        "nstab-help": "Ndihmë",
        "nstab-category": "Kategoria",
+       "mainpage-nstab": "Faqja kryesore",
        "nosuchaction": "Nuk ekziston ky veprim",
        "nosuchactiontext": "Veprimi i specifikuar nga URL është i pavlefshëm.\nJu mund të keni bërë një gabim në shkrimin e URL-së, ose keni ndjekur një lidhje të pasaktë.\nKjo mund të vijë edhe si rezultat i një gabimi në programin e përdorur nga {{SITENAME}}.",
        "nosuchspecialpage": "Nuk ekziston kjo faqe speciale",
        "badtitle": "Titull i pasaktë",
        "badtitletext": "Titulli i faqes që kërkuat nuk ishte i saktë, ishte bosh, ose ishte një titull ndër-gjuhësor/inter-wiki me lidhje të pasaktë.\nMund të përmbajë një ose më shumë germa, të cilat nuk mund të përdoren në tituj.",
        "title-invalid-empty": "Titulli i faqes së kërkuar është bosh ose përmban vetëm emrin e një hapësire.",
-       "title-invalid-interwiki": "Titulli përmban një lidhje ndërwiki",
+       "title-invalid-utf8": "Titulli i faqes së kërkuar përmban një varg të pavlefshëm UTF-8.",
+       "title-invalid-interwiki": "Titulli i faqes së kërkuar përmban një lidhje interwiki që nuk mund të përdoret në tituj.",
        "title-invalid-talk-namespace": "Titulli i faqes së kërkuar i referohet një faqeje diskutimi që nuk mund të ekzistojë.",
        "title-invalid-characters": "Titulli i faqes së kërkuar përmban karaktere të pavlefshme: \"$1\".",
+       "title-invalid-relative": "Titulli ka shteg relativ. Titujt relativ të faqes (./, ../) janë të pavlefshëm, për shkak se ata shpesh do të jenë të pakapshëm kur trajtohen nga shfletuesi i përdoruesit.",
+       "title-invalid-magic-tilde": "Titulli i faqes përmban tildë të pavlefshme magjike (<nowiki>~~~</nowiki>).",
+       "title-invalid-too-long": "Titulli i faqes së kërkuar është shumë i gjatë. Duhet të jetë jo më shumë se $1 {{PLURAL:$1|byte|byte}} në kodimin UTF-8.",
+       "title-invalid-leading-colon": "Titulli i faqes së kërkuar përmban dy pika (:) të pavlefshme në fillim.",
        "perfcached": "nformacioni i mëposhtëm është kopje e ruajtur dhe mund të mos jetë i përditësuar. E shumta  {{PLURAL:$1|një rezultat është|$1 rezultate janë}} ruajtur në kopje.",
        "perfcachedts": "Informacioni i mëposhtëm është një kopje e rifreskuar më $1. E shumta  {{PLURAL:$4|një rezultat është|$4 rezultate janë}} ruajtur në kopje.",
        "querypage-no-updates": "Përditësimet për këtë faqe për momentin janë të ç'aktivizuara.\nKëtu informacioni nuk do të jetë i përditësuar.",
        "actionthrottled": "Veprim u ndalua",
        "actionthrottledtext": "Si masë sigurie anti-spam, është e ndaluar kryerja e shpeshtë e një veprimi brenda një hapësire kohore shumë të shkurtër. Ju kryet shumë herë të njëjtin veprim brenda një kohe shumë të shkurtër.\nJu lutemi, provojeni përsëri pas disa minutash.",
        "protectedpagetext": "Kjo faqe është e mbrojtur për të parandaluar redaktimi apo veprime të tjera.",
-       "viewsourcetext": "Ju mund të shikoni dhe kopjoni tekstin e kësaj faqeje:",
-       "viewyourtext": "Ju mund të shikoni dhe të kopjoni tekstin e '''ndryshimeve tuaja''' tek kjo faqe:",
+       "viewsourcetext": "Ju mund të shikoni dhe kopjoni tekstin burimor të kësaj faqe.",
+       "viewyourtext": "Ju mund të shikoni dhe kopjoni tekstin burimor të <strong>redaktimeve tuaja</strong> në këtë faqe.",
        "protectedinterface": "Kjo faqe përmban tekstin e dritares së programit, për këtë arsye mbrohet për të shmangur abuzimet.",
        "editinginterface": "'''Kujdes:''' Po redaktoni një faqe që përdoret për tekstin dritares së programit. \nNdryshimet në këtë faqe do të ndikojnë pamjen e dritares për përdoruesit e tjerë.\nPër përkthime, ju lutem konsideroni përdorimin e [//translatewiki.net/wiki/Main_Page?setlang=en translatewiki.net], projektin e lokalizimit MediaWiki.",
        "translateinterface": "Të shtoni ose të ndryshojë përkthime për të gjitha wikis, ju lutem përdorimin e [//translatewiki.net/ translatewiki.net], MediaWiki lokalizimin e projektit.",
        "exception-nologin-text": "Ju lutem [[Special:Userlogin|hyni brënda]] për të qenë  në gjendje të hyni në këtë faqe ose veprim.",
        "exception-nologin-text-manual": "Ju lutem <span class=\"notranslate\" translate=\"asnjë\">$1</span> që të jeni në gjendje për të hyrë në këtë faqe ose të veproni.",
        "virus-badscanner": "Konfiguracion i parregullt: Skaner i panjohur virusesh: ''$1''",
-       "virus-scanfailed": "skani dështoi (code $1)",
+       "virus-scanfailed": "skanimi dështoi (code $1)",
        "virus-unknownscanner": "antivirus i pa njohur:",
        "logouttext": "'''Ju keni dalë jashtë.''' \n \n Kini parasysh që disa faqe mund të shfaqen sikur të ishit i identifikuar derisa të fshini ''cache''-in e shfletuesit tuaj.",
        "welcomeuser": "Mirë se vini, $1!",
        "nologinlink": "Hapeni",
        "createaccount": "Hap një llogari",
        "gotaccount": "Keni një llogari? '''$1'''.",
-       "gotaccountlink": "Hyni",
+       "gotaccountlink": "Identifikohuni",
        "userlogin-resetlink": "Keni harruar të dhënat tuaja të identifikimit?",
-       "userlogin-resetpassword-link": "Harruat fjalëkalimin",
-       "userlogin-helplink2": "Ndihmoni me hyrjen në",
+       "userlogin-resetpassword-link": "Keni harruar fjalëkalimin?",
+       "userlogin-helplink2": "Ndihmë rreth identifikimit",
        "userlogin-loggedin": "Ju tashmë janë të regjistruar si <span class=\"notranslate\" translate=\"asnjë\">{{GJINIA:$1|</span><span class=\"notranslate\" translate=\"asnjë\">$1</span>}}.\nPërdorim formularin më poshtë që të hyni në si një përdorues tjetër.",
        "userlogin-createanother": "Krijo një llogari tjeter",
        "createacct-emailrequired": "Posta elektronike",
        "createacct-captcha": "kontroll sigurie",
        "createacct-imgcaptcha-ph": "Shkruaj tekstin që ju shihni më lartë",
        "createacct-submit": "Krijoni llogarinë tuaj",
-       "createacct-another-submit": "Krijo një llogari tjeter",
+       "createacct-another-submit": "Krijo një llogari",
        "createacct-benefit-heading": "{{SITENAME}} është bërë nga njerëz si ju.",
        "createacct-benefit-body1": "{{PLURAL:$1|redaktim|redaktime}}",
        "createacct-benefit-body2": "{{PLURAL:$1|faqe|faqe}}",
-       "createacct-benefit-body3": "{{PLURAL:$1|kontribues}} kohët e fundit",
+       "createacct-benefit-body3": "{{PLURAL:$1|kontribuesi i|kontribuesit e}} fundit",
        "badretype": "Fjalëkalimet nuk janë njësoj.",
        "userexists": "Emri i përdoruesit që kërkuat është në përdorim. \nZgjidhni një emër tjetër.",
        "loginerror": "Gabim gjatë identifikimit",
        "passwordreset-emailtitle": "Detajet e llogarisë në {{SITENAME}}",
        "passwordreset-emailtext-ip": "Dikush (ndoshta ju, nga IP adresa $1) kërkoi një kujtesë për \ndetajet e llogarisë suaj {{SITENAME}} ($4).Përdoruesi në vijim {{PLURAL:$3|llogari është|llogaritë janë}} të lidhur me këtë postë elektronike:\n\n$2\n\n{{PLURAL:$3|Ky fjalëkalim i përkohshëm|Këto fjalëkalime të përkohshme}} do të përfundojë për {{PLURAL:$5|një ditë|$5 ditë}}.\n\nJu duhet të kyçeni dhe të zgjidhni një fjalëkalim të ri tani. Nëse dikush tjetër e ka bërë këtë kërkesës, ose në qoftë se ju mbani mend fjalëkalimin tuaj origjinal, dhe ju nuk dëshirojni të ndryshoni atë, ju mund të injoroni këtë mesazh dhe do të vazhdoni përdorimin e fjalëkalimit tuaj të vjetër.",
        "passwordreset-emailtext-user": "Përdoruesi  $1 në {{SITENAME }} ka kërkuar një kujtesë për të dhënat e llogarisë suaj për {{SITENAME }} ($4). Përdoruesi në vijim {{PLURAL:$3 | llogaria është | llogaritë janë}} të lidhur me këtë postë elektronike: \n\n$2\n\n{{PLURAL:$3 | Ky fjalëkalim i përkohshëm | Këto fjalëkalime të përkohshme}} do të përfundojë në {{PLURAL:$5 | një ditë | $5 ditë}}.\nJu duhet të kyçeni dhe të zgjidhni një fjalëkalim të ri tani. Nëse dikush tjetër e ka bërë këtë kërkesës, ose në qoftë se ju mbani mend fjalëkalimin tuaj origjinal, dhe ju nuk dëshirojni të ndryshoni atë, ju mund të injoroni këtë mesazh dhe do të vazhdoni përdorimin e fjalëkalimit tuaj të vjetër.",
-       "passwordreset-emailelement": "Nofka: \n$1\n\nFjalëkalimi i përkohshëm: \n$2",
+       "passwordreset-emailelement": "Emri i përdoruesit: \n$1\n\nFjalëkalimi i përkohshëm: \n$2",
        "passwordreset-emailsent": "Një email për rivendosjen e fjalëkalimit është dërguar.",
        "passwordreset-emailsent-capture": "Një email për rivendosjen e fjalëkalimit është dërguar, i cili tregohet më poshtë.",
        "passwordreset-emailerror-capture": "U dërgua një e-mail kujtesë, i cili tregohet më poshtë, por dërgesa për tek përdoruesi qe e pamundur: $1",
        "prefs-edits": "Numri i redaktimeve:",
        "prefs-skin": "Pamja",
        "skin-preview": "Parapamje",
-       "datedefault": "Parazgjedhje",
+       "datedefault": "E parazgjedhur",
        "prefs-labs": "Karakteristikat laboratorik",
        "prefs-user-pages": "Faqet e përdoruesit",
        "prefs-personal": "Përdoruesi",
-       "prefs-rc": "Ndryshime së fundmi",
+       "prefs-rc": "Ndryshimet e fundit",
        "prefs-watchlist": "Lista mbikqyrëse",
        "prefs-editwatchlist": "Redakto listën mbikqyrëse",
        "prefs-watchlist-days": "Numri i ditëve të treguara tek lista mbikqyrëse:",
        "prefs-resetpass": "Ndrysho fjalëkalimin",
        "prefs-changeemail": "Ndrysho postën elektronike",
        "prefs-setemail": "Vendos adresën e postës elektronike",
-       "prefs-email": "Opsionet E-mail",
+       "prefs-email": "Opsionet e emailit",
        "prefs-rendering": "Dukja",
        "saveprefs": "Ruaj parapëlqimet",
        "restoreprefs": "Rikthe të gjitha të dhënat e mëparshme",
        "allowemail": "Lejo përdoruesit të më dërgojnë email",
        "prefs-searchoptions": "Kërko",
        "prefs-namespaces": "Hapësirat",
-       "default": "parazgjedhje",
+       "default": "e parazgjedhur",
        "prefs-files": "Figura",
        "prefs-custom-css": "CSS i përpunuem",
        "prefs-custom-js": "JavaScripti i përpunuar",
        "prefs-info": "Informatat bazike",
        "prefs-i18n": "Internacionalizimi",
        "prefs-signature": "Firma",
-       "prefs-dateformat": "Data i formatit",
-       "prefs-timeoffset": "Kohë të kompensuar",
+       "prefs-dateformat": "Formati i datës",
+       "prefs-timeoffset": "Diferenca kohore",
        "prefs-advancedediting": "Opsionet e avancuar",
        "prefs-editor": "redaktor",
        "prefs-preview": "Parapamje",
-       "prefs-advancedrc": "Opsionet e avancuar",
-       "prefs-advancedrendering": "Opsionet e avancuar",
-       "prefs-advancedsearchoptions": "Opsionet e avancuar",
-       "prefs-advancedwatchlist": "Opsionet e avancuar",
+       "prefs-advancedrc": "Opsione të avancuara",
+       "prefs-advancedrendering": "Opsione të avancuara",
+       "prefs-advancedsearchoptions": "Opsione të avancuara",
+       "prefs-advancedwatchlist": "Opsione të avancuara",
        "prefs-displayrc": "Shfaq opsionet",
        "prefs-displaywatchlist": "Shfaq opsionet",
        "prefs-diffs": "Ndryshimet",
        "group-bot": "Robot",
        "group-sysop": "Administrues",
        "group-bureaucrat": "Burokrat",
-       "group-suppress": "Kujdestari",
+       "group-suppress": "Shtypësit",
        "group-all": "(të gjitha)",
        "group-user-member": "{{GENDER:$1|përdorues|përdoruese}}",
        "group-autoconfirmed-member": "{{GENDER:$1|përdorues i vërtetuar automatikisht|përdoruese e vërtetuar automatikisht}}",
        "grouppage-bot": "{{ns:project}}:Robotë",
        "grouppage-sysop": "{{ns:project}}:Administruesit",
        "grouppage-bureaucrat": "{{ns:project}}:Burokratë",
-       "grouppage-suppress": "{{ns:project}}:Kujdestari",
+       "grouppage-suppress": "{{ns:project}}:Shtypur",
        "right-read": "Lexo faqe",
        "right-edit": "Redakto faqet",
        "right-createpage": "Hap faqe (që nuk janë faqe diskutimi)",
        "right-override-export-depth": "Eksoprto faqet duke përfshirë e lidhura deri në një thellësi prej 5",
        "right-sendemail": "Dërgo e-mail tek përdoruesit e tjerë",
        "right-passwordreset": "Shiko e-mail-et e rivendosjes së fjalëkalimit",
+       "right-managechangetags": "Krijoni dhe fshini [[Special:Tags|tags]] nga baza e të dhënave",
+       "right-applychangetags": "Aplikoni [[Special:Tags|tags]] së bashku me ndryshimet",
+       "right-changetags": "Shtoni dhe të largoni në mënyrë arbitrare [[Special:Tags|tags]] në rishikimet individuale dhe regjistrimet e historikut",
        "newuserlogpage": "Regjistri i llogarive",
        "newuserlogpagetext": "Ky është një regjistër i llogarive të fundit që janë hapur",
        "rightslog": "Regjistri i privilegjeve të përdoruesit",
        "action-createpage": "krijo faqe",
        "action-createtalk": "krijo faqe diskutimi",
        "action-createaccount": "krijo këtë llogari përdoruesi",
+       "action-history": "shiko historinë e kësaj faqeje",
        "action-minoredit": "shëno këtë redaktim si të vogël",
        "action-move": "zhvendos këtë faqe",
        "action-move-subpages": "zhvendos këtë faqe dhe nënfaqet e saj",
        "action-move-rootuserpages": "lëviz rrënjët e faqeve të përdoruesve",
+       "action-move-categorypages": "lëviz faqet kategori",
        "action-movefile": "lëviz këtë skedë",
        "action-upload": "ngarko këtë skedë",
        "action-reupload": "rishkruaj këtë skedë ekzistuese",
        "action-block": "blloko përdoruesin",
        "action-protect": "ndrysho nivelin e mbrojtjes për këtë faqe",
        "action-rollback": "ritkthen shpejt redaktimet e përdoruesit të fundit që redaktoi një faqe të veçantë",
-       "action-import": "importo këtë faqe nga një wiki tjetër",
-       "action-importupload": "importo këtë faqe nga një ngarkim i një skedari",
+       "action-import": "importo faqe nga një wiki tjetër",
+       "action-importupload": "Importo faqe nga një ngarkim skede",
        "action-patrol": "shëno redaktimin e tjerëve si të patrulluar",
        "action-autopatrol": "shëno redaktimet tua si të patrulluara",
        "action-unwatchedpages": "shiko listën e faqeve të pa vrojtuara",
        "action-userrights-interwiki": "ndrysho të drejtat e përdoruesve në wiki-t tjera",
        "action-siteadmin": "mbyll ose hap bazën e të dhënave",
        "action-sendemail": "dërgo e-maile",
+       "action-editmywatchlist": "redaktoni listën tuaj mbikqyrëse",
+       "action-viewmywatchlist": "shikoni listën tuaj mbikqyrëse",
        "nchanges": "$1 {{PLURAL:$1|ndryshim|ndryshime}}",
+       "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|që nga vizita e fundit}}",
        "enhancedrc-history": "historia",
        "recentchanges": "Ndryshimet e fundit",
        "recentchanges-legend": "Zgjedhjet e ndryshimeve momentale",
-       "recentchanges-summary": "Ndiqni ndryshime së fundmi tek kjo faqe.",
+       "recentchanges-summary": "Ndiq ndryshimet më të fundit të Wiktionary në këtë faqe.",
        "recentchanges-noresult": "Nuk ka ndryshime gjatë periudhës së dhënë që plotësojnë këto kritere.",
        "recentchanges-feed-description": "Ndjek ndryshimet më të fundit në wiki tek kjo fushë.",
        "recentchanges-label-newpage": "Ky redaktim krijoi një faqe të re",
        "recentchanges-label-plusminus": "Madhësia e faqes ndryshoi me këtë numër bajtësh",
        "recentchanges-legend-heading": "'''Legjenda:'''",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (shiko gjithashtu [[Special:NewPages|listën e faqeve të reja]])",
-       "rcnotefrom": "Më poshtë janë ndryshime së fundmi nga <b>$2</b> (treguar deri në <b>$1</b>).",
+       "rcnotefrom": "Më poshtë {{PLURAL:$5|është shfaqur ndryshimi|janë shfaqur ndryshimet}} që nga <strong>$3, $4</strong> (deri në <strong>$1</strong>).",
        "rclistfrom": "Tregon ndryshime së fundmi duke filluar nga $3 $2",
        "rcshowhideminor": "$1 redaktimet e vogla",
        "rcshowhideminor-show": "Shfaq",
        "rcshowhidebots-show": "Shfaq",
        "rcshowhidebots-hide": "Fshih",
        "rcshowhideliu": "$1 përdorues të regjistruar",
+       "rcshowhideliu-show": "Shfaq",
        "rcshowhideliu-hide": "Fshih",
        "rcshowhideanons": "$1 përdoruesit anonim",
        "rcshowhideanons-show": "Shfaq",
        "rcshowhideanons-hide": "Fshih",
        "rcshowhidepatr": "$1 redaktime të patrulluara",
+       "rcshowhidepatr-show": "Shfaq",
+       "rcshowhidepatr-hide": "Fshih",
        "rcshowhidemine": "$1 redaktimet e mia",
        "rcshowhidemine-show": "Shfaq",
        "rcshowhidemine-hide": "Fshih",
        "boteditletter": "b",
        "number_of_watching_users_pageview": "[$1 duke u mbikqyrur nga {{PLURAL:$1|përdorues|përdorues}}]",
        "rc_categories": "Kufizimi i kategorive (të ndara me \"|\")",
-       "rc_categories_any": "Të gjitha",
+       "rc_categories_any": "Ndonjë nga të zgjedhurat",
        "rc-change-size-new": "$1 {{PLURAL:$1|bajt|bajtë}} pas ndryshimit",
        "newsectionsummary": "/* $1 */ seksion i ri",
        "rc-enhanced-expand": "Trego detajet",
        "reuploaddesc": "Kthehu tek formulari i dhënies.",
        "upload-tryagain": "Dërgo përshkrimin e modifikuar të skedarit",
        "uploadnologin": "Nuk keni hyrë brënda",
-       "uploadnologintext": "Duhet të keni [[Special:UserLogin|hyrë brenda]] për të dhënë skeda.",
+       "uploadnologintext": "Ju lutem $1 ngarkoni skedat.",
        "upload_directory_missing": "Direktoriumi ($1) i ngarkimit po mungon dhe nuk është arritur që të krijohet nga webserveri.",
        "upload_directory_read_only": "Skedari i ngarkimit ($1) nuk mund të shkruhet nga shërbyesi.",
        "uploaderror": "Gabim dhënie",
        "nopagetext": "Faqja e kërkuar nuk ekziston.",
        "pager-newer-n": "{{PLURAL:$1|1 më i reja|$1 më të reja}}",
        "pager-older-n": "{{PLURAL:$1|1 më i vjetër|$1 më të vjetra}}",
-       "suppress": "Kujdestari",
+       "suppress": "Shtypur",
        "querypage-disabled": "Kjo faqe speciale është çaktivizuar për arsye të performancës.",
        "apihelp-no-such-module": "Moduli \"$1\" nuk u gjet.",
        "booksources": "Burime librash",
        "delete-warning-toobig": "Kjo faqe ka një historik të madh redaktimesh, më shumë se $1 {{PLURAL:$1|version|versione}}.\nGrisja e saj mund të ndërpresë operacionet e bazës së të dhënave të {{SITENAME}};\nvazhdoni me kujdes.",
        "rollback": "Riktheji mbrapsh redaktimet",
        "rollbacklink": "riktheje",
-       "rollbacklinkcount": "riktheni $1 {{PLURAL:$1|ndryshim|ndryshime}}",
+       "rollbacklinkcount": "rikthe $1 {{PLURAL:$1|ndryshim|ndryshime}}",
        "rollbacklinkcount-morethan": "riktheni më tepër $1 {{PLURAL:$1|ndryshim|ndryshime}}",
        "rollbackfailed": "Rikthimi dështoi",
        "cantrollback": "Redaktimi nuk mund të kthehej;\nredaktori i fundit është i vetmi autor i këtij artikulli.",
        "protect-existing-expiry": "Koha ekzistuese e skadimit: $3, $2",
        "protect-otherreason": "Arsye tjera/shtesë:",
        "protect-otherreason-op": "Arsyeja tjetër",
-       "protect-dropdown": "*Arsye të zakonshme të mbrojtjes\n**Vandalizëm i tepërt\n**Spam i tepërt\n**Paralajmërim i redaktimeve joprodukive\n**Faqe e trafikut të lartë",
+       "protect-dropdown": "*Arsye të përgjithshme të mbrojtjes\n**Vandalizëm i tepërt\n**Spam i tepërt\n**Paralajmërim i redaktimeve joprodukive\n**Faqe me trafik të lartë",
        "protect-edit-reasonlist": "Redakto arsyet e mbrojtjes",
        "protect-expiry-options": "1 Orë:1 hour,1 Ditë:1 day,1 Javë:1 week,2 Javë:2 weeks,1 Muaj:1 month,3 Muaj:3 months,6 Muaj:6 months,1 Vjet:1 year,Pa kufi:infinite",
        "restriction-type": "Lejet:",
        "blocklog-showsuppresslog": "Ky përdorues ka qenë i bllokuar dhe i fshehur më parë.\nRegjistri i bllokimeve është poshtë për referncë:",
        "blocklogentry": "bllokoi [[$1]] për një kohë prej: $2 $3",
        "reblock-logentry": "ndryshoi parametrat e bllokimit për [[$1]] me një kohë prej $2 $3",
-       "blocklogtext": "Ky është një regjistër i veprimeve bllokuese dhe zhbllokuese të përdoruesit.\n\nAdresat I të bllokuara automatikisht nuk janë të paraqitura. \n\nShikoni dhe [[Special:BlockList|listën e të bllokuarve]] për një listë të bllokimeve dhe ndalimeve vepruese të tanishme.",
+       "blocklogtext": "Ky është një regjistër i veprimeve bllokuese dhe zhbllokuese të përdoruesit.\n\nAdresat e IP-ve të bllokuara automatikisht nuk janë të paraqitura. \n\nShih [[Special:BlockList|listën e IP-ve të bllokuara]] të bllokimeve të tanishme.",
        "unblocklogentry": "çbllokoi \"$1\"",
        "block-log-flags-anononly": "vetëm anonimët",
        "block-log-flags-nocreate": "krijimi i llogarive është pamundësuar",
        "ipb_already_blocked": "\"$1\" është i bllokuar",
        "ipb-needreblock": "$1 është i bllokuar.\nDëshironi t'i ndryshoni parametrat?",
        "ipb-otherblocks-header": "{{PLURAL:$1|Bllokim tjetër|Bllokime të tjera}}",
-       "unblock-hideuser": "Ju nuk mund të zhbllokoni këtë përdorues, përderisa nofka e tij është e fshehur.",
-       "ipb_cant_unblock": "Gabim: Bllokimi ID $1 nuk u gjet.\n\nMund të jetë zhbllokuar deri tani.",
+       "unblock-hideuser": "Nuk mund ta zhbllokosh këtë përdorues, përderisa nofka e tij është e fshehur.",
+       "ipb_cant_unblock": "Gabim: ID $1 e bllokuar nuk u gjet. Mund të jetë zhbllokuar deri tani.",
        "ipb_blocked_as_range": "Gabim: Adresa IP $1 nuk është bllokuar direkt dhe nuk mund të zhbllokohet.\nAjo është, megjithatë, e bllokuar si pjesë e rangut $2, që nuk mund të zhbllokohet.",
        "ip_range_invalid": "Shtrirje IP gabim.",
        "ip_range_toolarge": "Radhitja e bllokimeve më të mëdha se /$1 nuk lejohet.",
        "lockdbsuccesssub": "Regjistri u bllokua me sukses",
        "unlockdbsuccesssub": "Regjistri u zhbllokua me sukses",
        "lockdbsuccesstext": "Regjistri është bllokuar.<br />\nKujtohuni ta [[Special:UnlockDB|çbllokoni]] pasi të keni mbaruar mirëmbajtjen.",
-       "unlockdbsuccesstext": "Regjistri i {{SITENAME}} është zhbllokuar.",
+       "unlockdbsuccesstext": "Baza e të dhënave {{SITENAME}} është zhbllokuar.",
        "lockfilenotwritable": "Skeda për bllokimin e regjistrit s'mund të shkruhet.\nShërbyesi i rrjetit duhet të jetë në gjendje të shkruaj këtë skedë për të bllokuar ose çbllokuar regjistrin.",
        "databasenotlocked": "Regjistri nuk është bllokuar.",
        "lockedbyandtime": "(nga {{GENDER:$1|$1}} më $2 në $3)",
        "move-leave-redirect": "Lini një përcjellim prapa",
        "protectedpagemovewarning": "'''Kujdes''': Kjo faqe është mbrojtur, kështu që vetëm përdoruesit me privilegje administratorësh mund ta zhvendosin atë.\nVeprimi i fundit mbi këtë faqe është poshtë për referncë:",
        "semiprotectedpagemovewarning": "'''Kujdes''': Kjo faqe është mbrojtur, kështu që vetëm përdoruesit e regjistruar mund ta zhvendosin atë.\nVeprimi i fundit mbi këtë faqe është poshtë për referncë:",
-       "move-over-sharedrepo": "== Skeda ekziston ==\n[[:$1]] ekziston në një magazinë të përbashkët. Zhvendosja e një skede tek ky titull do të prishë skedën e përbashkët.",
+       "move-over-sharedrepo": "== Skeda ekziston ==\n[[:$1]] ekziston në një magazinë të përbashkët. Zhvendosja e një skede tek ky titull do të mbishkruajë skedën e përbashkët.",
        "file-exists-sharedrepo": "Emri i zgjedhur i skedës është në përdorim në një magazinë të përbashkët.\nJu lutemi zgjidhni në emët tjetër.",
        "export": "Eksportoni faqe",
        "exporttext": "Mund të eksportoni tekstin dhe historinë e redaktimit e një faqeje ose disa faqesh të mbështjesha në XML; kjo mund të importohet në një wiki tjetër që përdor softuerin MediaWiki (tani për tani, ky opsion nuk është përfshirë tek {{SITENAME}}).\n\nPër të eksportuar faqe, thjesht shtypni një emër për çdo rresht, ose krijoni lidhje të tipit [[{{#Special:Export}}/{{MediaWiki:Mainpage}}]] si [[{{MediaWiki:Mainpage}}]].",
        "specialpages-group-maintenance": "Përmbledhje mirëmbajtjeje",
        "specialpages-group-other": "Faqe speciale të tjera",
        "specialpages-group-login": "Hyrje dhe hapje llogarie",
-       "specialpages-group-changes": "Ndryshime së fundmi dhe regjistra",
+       "specialpages-group-changes": "Ndryshimet më të fundit dhe regjistrat",
        "specialpages-group-media": "Përmbledhje media dhe ngarkime",
        "specialpages-group-users": "Përdoruesit dhe privilegjet",
        "specialpages-group-highuse": "Faqe të shumëpërdorura",
        "sqlite-no-fts": "$1 pa mbështetje të kërkimit me teskt të plotë",
        "logentry-delete-delete": "$1 {{GENDER:$2|grisi}} faqen $3",
        "logentry-delete-restore": "$1 {{GENDER:$2|riktheu}} faqen $3",
-       "logentry-delete-event": "$1 {{GENDER:$2|ndryshoi}} dukshmërinë e {{PLURAL:$5|e një ngjarjeje regjistri|$5 ngjarjeve regjitri}} në $3: $4",
-       "logentry-delete-revision": "$1 {{GENDER:$2|ndryshoi}} dukshmërinë e {{PLURAL:$5|një rishikimi|$5 rishikimeve}} në faqen $3: $4",
+       "logentry-delete-event": "$1 {{GENDER:$2|ndryshoi}} dukshmërinë e {{PLURAL:$5|e një ngjarjeje regjistri|$5 ngjarjeve regjistri}} në $3: $4",
+       "logentry-delete-revision": "$1 {{GENDER:$2|ka dryshuar}} dukshmërinë e {{PLURAL:$5|një rishikimi|$5 rishikimeve}} në faqen $3: $4",
        "logentry-delete-event-legacy": "$1 {{GENDER:$2|ndryshoi}} dukshmërinë e ngjarjeve të regjistit në $3",
-       "logentry-delete-revision-legacy": "$1 {{GENDER:$2|ndryshpi}} dukshmërinë e rishikimeve në faqen $3",
+       "logentry-delete-revision-legacy": "$1 {{GENDER:$2|ndryshoi}} dukshmërinë e rishikimeve në faqen $3",
        "logentry-suppress-delete": "$1 {{GENDER:$2|shtypi}} faqen $3",
        "logentry-suppress-event": "$1 në mënyrë sekrete ndryshoi dukshmërinë e {{PLURAL:$5|një ngjarje regjistri|$5 ngjarjeve regjistri}} në $3: $4",
        "logentry-suppress-revision": "$1 në mënyrë sekrete ndryshoi dukshmërinë e {{PLURAL:$5|një versioni|$5 versioneve}} në $3: $4",
        "logentry-newusers-create": "Llogaria e {{GENDER:$2|përdoruesit|përdorueses}} $1 u krijua.",
        "logentry-newusers-create2": "Llogaria e përdoruesit $3 është {{GENDER:$2|krijuar}} nga $1",
        "logentry-newusers-autocreate": "Llogaria e {{GENDER:$2|përdoruesit|përdorueses}} $1 u {{GENDER:$2|krijua}} automatikisht",
+       "logentry-protect-unprotect": "$1 {{GENDER:$2|ka hequr}} mbrojtjen nga faqja $3",
+       "logentry-protect-protect": "$1 {{GENDER:$2|ka mbrojtur}} faqen $3 $4",
+       "logentry-protect-protect-cascade": "$1 {{GENDER:$2|ka mbrojtur}} faqen $3 $4 [cascading]",
+       "logentry-protect-modify": "$1 {{GENDER:$2|ka ndryshuar}} nivelin e mbrojtjes për $3 $4",
+       "logentry-protect-modify-cascade": "$1 {{GENDER:$2|ka ndryshuar}} nivelin e mbrojtjes për $3 $4 [cascading]",
        "logentry-rights-rights": "$1 {{GENDER:$2|ndërroi}} anëtarësinë e grupit për $3 nga $4 në $5",
        "logentry-rights-autopromote": "$1 është {{GENDER:$2|promovuar}} automatikisht nga $4 në $5",
        "logentry-upload-upload": "$1 {{GENDER:$2|ngarkoi}} $3",
        "api-error-badtoken": "Gabim i brendshëm: Shenjë e keqe.",
        "api-error-copyuploaddisabled": "Ngarkimi nga URL-ja është çaktivizuar në këtë server.",
        "api-error-duplicate": "{{PLURAL:$1|Ekziston [$2 një skedë tjetër]|Ekzistojnë [$2 disa skeda të tjera]}} me të njëjtën përmbajtje.",
-       "api-error-duplicate-archive": "{{Ekzistonte [$2 një skedë tjetër]|Ekzistonin [$2 disa skeda të tjera]}} me të njëjtën përmbajtje, por {{PLURAL:$1|u gris|u grisën}}.",
+       "api-error-duplicate-archive": "{{Ekzistonte një skedë tjetër|Ekzistonin disa skeda të tjera}} me të njëjtën përmbajtje, por {{PLURAL:$1|ajo është|ato janë}} fshirë.",
        "api-error-empty-file": "Skeda që paraqitët ishte bosh.",
        "api-error-emptypage": "Nuk lejohet krijimi i faqeve të reja bosh.",
        "api-error-fetchfileerror": "Gabim i brendshëm: Diçka shkoi keq gjatë marrjes së skedës.",
index d5162a7..f814d2a 100644 (file)
@@ -27,7 +27,8 @@
                        "아라",
                        "Nemo bis",
                        "Aktron",
-                       "Srdjan m"
+                       "Srdjan m",
+                       "Macofe"
                ]
        },
        "tog-underline": "Подвлачење веза:",
        "upload-dialog-button-done": "Готово",
        "upload-dialog-button-save": "Сачувај",
        "upload-dialog-button-upload": "Пошаљи",
-       "upload-dialog-label-select-file": "Изабери датотеку",
-       "upload-dialog-label-infoform-title": "Детаљи",
-       "upload-dialog-label-infoform-name": "Назив",
-       "upload-dialog-label-infoform-description": "Опис",
-       "upload-dialog-label-usage-filename": "Назив датотеке",
+       "upload-form-label-select-file": "Изабери датотеку",
+       "upload-form-label-infoform-title": "Детаљи",
+       "upload-form-label-infoform-name": "Назив",
+       "upload-form-label-infoform-description": "Опис",
+       "upload-form-label-usage-filename": "Назив датотеке",
        "backend-fail-stream": "Не могу да емитујем датотеку $1.",
        "backend-fail-backup": "Не могу да направим резерву датотеке $1.",
        "backend-fail-notexists": "Датотека $1 не постоји.",
        "api-error-badtoken": "Унутрашња грешка: неисправан жетон.",
        "api-error-copyuploaddisabled": "Отпремање путем адресе је онемогућено на овом серверу.",
        "api-error-duplicate": "Већ {{PLURAL:$1|постоји [$2 друга датотека]|постоје [$2 друге датотеке]}} с истим садржајем.",
-       "api-error-duplicate-archive": "{{PLURAL:$1|Постојала је [$2 друга датотека]|Постојале су [$2 друге датотеке]}} с истим садржајем, али {{PLURAL:$1|је обрисана|су обрисане}}.",
+       "api-error-duplicate-archive": "{{PLURAL:$1|Постојала је друга датотека|Постојале су друге датотеке}} с истим садржајем, али {{PLURAL:$1|је обрисана|су обрисане}}.",
        "api-error-empty-file": "Послата датотека је празна.",
        "api-error-emptypage": "Стварање нових празних страница није дозвољено.",
        "api-error-fetchfileerror": "Унутрашња грешка: дошло је до грешке при добављању датотеке.",
index 14c8669..4d58a29 100644 (file)
@@ -18,7 +18,8 @@
                        "לערי ריינהארט",
                        "아라",
                        "Nemo bis",
-                       "Srdjan m"
+                       "Srdjan m",
+                       "Macofe"
                ]
        },
        "tog-underline": "Podvlačenje veza:",
        "api-error-badtoken": "Unutrašnja greška: neispravan žeton.",
        "api-error-copyuploaddisabled": "Otpremanje putem adrese je onemogućeno na ovom serveru.",
        "api-error-duplicate": "Već {{PLURAL:$1|postoji [$2 druga datoteka]|postoje [$2 druge datoteke]}} s istim sadržajem.",
-       "api-error-duplicate-archive": "{{PLURAL:$1|Postojala je [$2 druga datoteka]|Postojale su [$2 druge datoteke]}} s istim sadržajem, ali {{PLURAL:$1|je obrisana|su obrisane}}.",
+       "api-error-duplicate-archive": "{{PLURAL:$1|Postojala je druga datoteka|Postojale su druge datoteke}} s istim sadržajem, ali {{PLURAL:$1|je obrisana|su obrisane}}.",
        "api-error-empty-file": "Poslata datoteka je prazna.",
        "api-error-emptypage": "Stvaranje novih praznih stranica nije dozvoljeno.",
        "api-error-fetchfileerror": "Unutrašnja greška: došlo je do greške pri dobavljanju datoteke.",
index a8e5900..8ae161a 100644 (file)
        "nstab-template": "Mall",
        "nstab-help": "Hjälpsida",
        "nstab-category": "Kategori",
+       "mainpage-nstab": "Huvudsida",
        "nosuchaction": "Funktionen finns inte",
        "nosuchactiontext": "Den handling som specificerats av webbadressen är ogiltig.\nDu kan ha stavat webbadressen fel, eller följt en felaktig länk.\nDet kan även bero på en bugg i mjukvaran som används på {{SITENAME}}.",
        "nosuchspecialpage": "Någon sådan specialsida finns inte",
        "createacct-captcha": "Säkerhetskontroll",
        "createacct-imgcaptcha-ph": "Fyll i texten du ser ovan",
        "createacct-submit": "Skapa ditt konto",
-       "createacct-another-submit": "Skapa ett till konto",
+       "createacct-another-submit": "Skapa konto",
        "createacct-benefit-heading": "{{SITENAME}} är skapad av människor som dig.",
        "createacct-benefit-body1": "{{PLURAL:$1|redigering|redigeringar}}",
        "createacct-benefit-body2": "{{PLURAL:$1|sida|sidor}}",
        "permissionserrorstext-withaction": "Du har inte behörighet att $2, av följande {{PLURAL:$1|anledning|anledningar}}:",
        "recreate-moveddeleted-warn": "'''Varning: Du återskapar en sida som tidigare raderats.'''\n\nDu bör överväga om det är lämpligt att fortsätta redigera den här sidan.\nRaderings- och sidflyttningsloggen för den här sidan visas här som hjälp:",
        "moveddeleted-notice": "Den här sidan har raderats.\nRaderings- och sidflyttningsloggen för sidan visas nedan som referens.",
+       "moveddeleted-notice-recent": "Tyvärr, denna sida raderades nyligen (inom de senaste 24 timmarna).\nLoggen för radering och flyttning av sidan visas nedan som referens.",
        "log-fulllog": "Visa fullständig logg",
        "edit-hook-aborted": "Redigering avbruten av hook.\nDen gav ingen förklaring.",
        "edit-gone-missing": "Kunde inte uppdatera sidan.\nDet verkar som att den har raderats.",
        "upload-http-error": "Ett HTTP-fel uppstod: $1",
        "upload-copy-upload-invalid-domain": "Uppladdning av kopior är inte tillgängligt från denna domän.",
        "upload-dialog-title": "Ladda upp fil",
-       "upload-dialog-error": "Ett fel uppstod",
-       "upload-dialog-warning": "En varning uppstod",
        "upload-dialog-button-cancel": "Avbryt",
        "upload-dialog-button-done": "Klar",
        "upload-dialog-button-save": "Spara",
        "upload-dialog-button-upload": "Ladda upp",
-       "upload-dialog-label-select-file": "Välj fil",
-       "upload-dialog-label-infoform-title": "Detaljer",
-       "upload-dialog-label-infoform-name": "Namn",
-       "upload-dialog-label-infoform-description": "Beskrivning",
-       "upload-dialog-label-usage-title": "Användning",
-       "upload-dialog-label-usage-filename": "Filnamn",
+       "upload-process-error": "Ett fel uppstod",
+       "upload-process-warning": "En varning uppstod",
+       "upload-form-label-select-file": "Välj fil",
+       "upload-form-label-infoform-title": "Detaljer",
+       "upload-form-label-infoform-name": "Namn",
+       "upload-form-label-infoform-description": "Beskrivning",
+       "upload-form-label-usage-title": "Användning",
+       "upload-form-label-usage-filename": "Filnamn",
        "backend-fail-stream": "Kunde inte strömma filen $1.",
        "backend-fail-backup": "Kunde inte säkerhetskopiera filen ''$1''.",
        "backend-fail-notexists": "Filen $1 finns inte.",
        "filerevert-legend": "Återställ fil",
        "filerevert-intro": "Du återställer '''[[Media:$1|$1]]''' till [$4 versionen från $2 kl. $3].",
        "filerevert-comment": "Anledning:",
-       "filerevert-defaultcomment": "Återställer till versionen från $1 kl. $2.",
+       "filerevert-defaultcomment": "Återställer till versionen från $1 kl. $2 ($3)",
        "filerevert-submit": "Återställ",
        "filerevert-success": "'''[[Media:$1|$1]]''' har återställts till [$4 versionen från $2 kl. $3].",
        "filerevert-badversion": "Det finns ingen tidigare version av filen från den angivna tidpunkten.",
        "emailccsubject": "Kopia av ditt meddelande till $1: $2",
        "emailsent": "E-post har nu skickats",
        "emailsenttext": "Ditt e-postmeddelande har skickats",
-       "emailuserfooter": "Detta e-postmeddelande skickades av $1 till $2 med funktionen \"{{int:emailuser}}\" på {{SITENAME}}.",
+       "emailuserfooter": "Detta e-postmeddelande {{GENDER:$1|skickades}} av $1 till {{GENDER:$2|$2}} med funktionen \"{{int:emailuser}}\" på {{SITENAME}}.",
        "usermessage-summary": "Lämnar systemmeddelande.",
        "usermessage-editor": "Systemmeddelare",
        "watchlist": "Bevakningslista",
        "api-error-badaccess-groups": "Du får inte ladda upp filer till denna wiki.",
        "api-error-badtoken": "Internt fel: felaktig nyckel.",
        "api-error-copyuploaddisabled": "Uppladdning via URL är inaktiverad på den här servern.",
-       "api-error-duplicate": "Det finns redan {{PLURAL:$1|[$2 en annan fil]|[$2 andra filer]}} på webbplatsen med samma innehåll.",
-       "api-error-duplicate-archive": "Det fanns redan {{PLURAL:$1|[$2 en annan fil]|[$2 några andra filer]}} på webbplatsen med samma innehåll, men {{PLURAL:$1|den har|de har}} raderats.",
+       "api-error-duplicate": "Det finns redan {{PLURAL:$1|en annan fil|andra filer}} på webbplatsen med samma innehåll.",
+       "api-error-duplicate-archive": "Det fanns redan {{PLURAL:$1|en annan fil|några andra filer}} på webbplatsen med samma innehåll, men {{PLURAL:$1|den har|de har}} raderats.",
        "api-error-empty-file": "Filen du skickade var tom.",
        "api-error-emptypage": "Det är inte tillåtet att skapa nya, tomma sidor.",
        "api-error-fetchfileerror": "Internt fel: något gick fel vid hämtningen av filen.",
index e14d59d..ffba35c 100644 (file)
        "api-error-badtoken": "உள்ளகப் பிழை: தவறான அடையாளம்.",
        "api-error-copyuploaddisabled": "உரலி மூலம் பதிவேற்றுவது இந்த வழங்கியில் செயலிழக்கச் செய்யப்பட்டுள்ளது.",
        "api-error-duplicate": "There {{PLURAL:$1|is [$2 another file]|are [$2 some other files]}} already on the site with the same content.",
-       "api-error-duplicate-archive": "ஏற்கனவே இத்தளத்தில் இதே உள்ளடக்கத்தைக் கொண்ட {{PLURAL:$1|கோப்பு [$2 இருந்தது]|[$2 கோப்புகள் இருந்தன]}}, ஆனால் {{PLURAL:$1|அது நீக்கப்பட்டுவிட்டது|அவை நீக்கப்பட்டுவிட்டன.}}",
+       "api-error-duplicate-archive": "ஏற்கனவே இத்தளத்தில் இதே உள்ளடக்கத்தைக் கொண்ட {{PLURAL:$1|கோப்பு இருந்தது|கோப்புகள் இருந்தன}}, ஆனால் {{PLURAL:$1|அது நீக்கப்பட்டுவிட்டது|அவை நீக்கப்பட்டுவிட்டன.}}",
        "api-error-empty-file": "நீங்கள் அளித்த கோப்பு காலியாக உள்ளது.",
        "api-error-emptypage": "புதிய, காலி பக்கங்கள் உருவாக்கல் அனுமதிக்கப்படவில்லை.",
        "api-error-fetchfileerror": "உள்ளகப் பிழை: கோப்பைப் பெறுகையில் ஏதோ தவறு நேர்ந்துவிட்டது.",
index ee53ac7..560978b 100644 (file)
        "api-error-badtoken": "అంతర్గత లోపం: చెడు టోకెన్.",
        "api-error-copyuploaddisabled": "URL ద్వారా ఎక్కించడం ఈ సర్వరులో అశక్తం చెయ్యబడింది.",
        "api-error-duplicate": "ఇదే విషయ పాఠ్యంతో ఈ సైటులో ఈసరికే {{PLURAL:$1|[$2 మరో ఫైలు] ఉంది|[$2 ఇతర ఫైళ్ళు] ఉన్నాయి}}.",
-       "api-error-duplicate-archive": "ఇదే విషయ పాఠ్యంతో ఈ సైటులో ఈసరికే {{PLURAL:$1|[$2 మరో ఫైలు] ఉండేది|[$2 ఇతర ఫైళ్ళు] ఉండేవి}}. అయితే {{PLURAL:$1|అది తొలగించబడింది|అవి తొలగించబడ్డాయి}}.",
+       "api-error-duplicate-archive": "ఇదే విషయ పాఠ్యంతో ఈ సైటులో ఈసరికే {{PLURAL:$1|మరో ఫైలు ఉండేది|ఇతర ఫైళ్ళు ఉండేవి}}. అయితే {{PLURAL:$1|అది తొలగించబడింది|అవి తొలగించబడ్డాయి}}.",
        "api-error-empty-file": "మీరు దాఖలుచేసిన ఫైల్ ఖాళీది.",
        "api-error-emptypage": "కొత్త మరియు ఖాళీ పేజీలను సృష్టించడానికి అనుమతి లేదు.",
        "api-error-fetchfileerror": "అంతర్గత లోపం: ఈ ఫైలును తేవడంలో ఏదో తప్పు జరిగింది.",
index b0ce154..e29f413 100644 (file)
        "upload-http-error": "เกิดข้อผิดพลาดเอชทีทีพี: $1",
        "upload-copy-upload-invalid-domain": "ไม่สามารถคัดลอกการอัปโหลดจากโดเมนนี้",
        "upload-dialog-title": "อัปโหลดไฟล์",
-       "upload-dialog-error": "เกิดข้อผิดพลาด",
-       "upload-dialog-warning": "เกิดคำเตือน",
        "upload-dialog-button-cancel": "ยกเลิก",
        "upload-dialog-button-done": "เสร็จสิ้น",
        "upload-dialog-button-save": "บันทึก",
        "upload-dialog-button-upload": "อัปโหลด",
-       "upload-dialog-label-select-file": "เลือกไฟล์",
-       "upload-dialog-label-infoform-title": "รายละเอียด",
-       "upload-dialog-label-infoform-name": "ชื่อ",
-       "upload-dialog-label-infoform-description": "คำอธิบาย",
-       "upload-dialog-label-usage-title": "การใช้",
-       "upload-dialog-label-usage-filename": "ชื่อไฟล์",
+       "upload-process-error": "เกิดข้อผิดพลาด",
+       "upload-process-warning": "เกิดคำเตือน",
+       "upload-form-label-select-file": "เลือกไฟล์",
+       "upload-form-label-infoform-title": "รายละเอียด",
+       "upload-form-label-infoform-name": "ชื่อ",
+       "upload-form-label-infoform-description": "คำอธิบาย",
+       "upload-form-label-usage-title": "การใช้",
+       "upload-form-label-usage-filename": "ชื่อไฟล์",
        "backend-fail-backup": "ไม่สามารถสำรองไฟล์ \"$1\"",
        "backend-fail-notexists": "ไม่มีไฟล์ $1",
        "backend-fail-delete": "ไม่สามารถลบไฟล์ \"$1\"",
index 4245da2..857fa04 100644 (file)
@@ -14,7 +14,8 @@
                        "לערי ריינהארט",
                        "아라",
                        "Ianlopez1115",
-                       "Leeheonjin"
+                       "Leeheonjin",
+                       "Macofe"
                ]
        },
        "tog-underline": "Pagsasalungguhit ng link:",
        "api-error-badtoken": "Panloob na kamalian: masamang kahalip.",
        "api-error-copyuploaddisabled": "Ang pagkakarga ng URL ay hindi pinagagana sa tagapaghaing ito.",
        "api-error-duplicate": "May {{PLURAL:$1|[$2 ibang talaksan]g|[$2 ibang ilang mga talaksan]g}} nasa wiki na na may katulad na nilalaman",
-       "api-error-duplicate-archive": "Nagkaroon {{PLURAL:$1|dati ng [$2 iba pang talaksan]|dati ng mga [$2 ilang iba pang mga talaksan]}} na umiiral na sa sityo na may katulad na nilalaman, ngunit {{PLURAL:$1|ito ay|ito ay mga}} nabura na.",
+       "api-error-duplicate-archive": "Nagkaroon {{PLURAL:$1|dati ng iba pang talaksan|dati ng mga ilang iba pang mga talaksan}} na umiiral na sa sityo na may katulad na nilalaman, ngunit {{PLURAL:$1|ito ay|ito ay mga}} nabura na.",
        "api-error-empty-file": "Walang laman ang ipinasa mong talaksan.",
        "api-error-emptypage": "Lumilikha ng bago, hindi pinapayagan ang mga pahinang walang laman.",
        "api-error-fetchfileerror": "Panloob na kamalian: may naganap na pagkakamali habang kinukuha ang talaksan.",
index f768935..24b7adc 100644 (file)
        "upload-dialog-button-cancel": "İptal",
        "upload-dialog-button-save": "Kaydet",
        "upload-dialog-button-upload": "Yükle",
-       "upload-dialog-label-select-file": "Dosya seç",
-       "upload-dialog-label-infoform-title": "Ayrıntılar",
-       "upload-dialog-label-infoform-name": "Ad",
-       "upload-dialog-label-infoform-description": "Açıklama",
-       "upload-dialog-label-usage-title": "Kullanımı",
-       "upload-dialog-label-usage-filename": "Dosya adı",
+       "upload-form-label-select-file": "Dosya seç",
+       "upload-form-label-infoform-title": "Ayrıntılar",
+       "upload-form-label-infoform-name": "Ad",
+       "upload-form-label-infoform-description": "Açıklama",
+       "upload-form-label-usage-title": "Kullanımı",
+       "upload-form-label-usage-filename": "Dosya adı",
        "backend-fail-stream": "$1 dosyası okunamadı.",
        "backend-fail-backup": "\"$1\" dosyası yedeklenemedi.",
        "backend-fail-notexists": "$1 dosyası mevcut değil.",
        "api-error-badtoken": "İç hata: Bozuk anahtar.",
        "api-error-copyuploaddisabled": "URL ile yükleme bu sunucuda devre dışı bırakılmıştır.",
        "api-error-duplicate": "Sitede zaten aynı içerikte başka {{PLURAL:$1|bir [$2 dosya]|[$2 dosyalar]}} var.",
-       "api-error-duplicate-archive": "Sitede zaten aynı içerikte başka {{PLURAL:$1|bir [$2 dosya]|[$2 dosyalar]}} vardı, ama {{PLURAL:$1|silindi|silindiler}}.",
+       "api-error-duplicate-archive": "Sitede zaten aynı içerikte başka {{PLURAL:$1|bir dosya|dosyalar}} vardı, ama {{PLURAL:$1|silindi|silindiler}}.",
        "api-error-empty-file": "Gönderdiğiniz dosya boş.",
        "api-error-emptypage": "Yeni, boş bir sayfa oluşturmaya izin verilmez.",
        "api-error-fetchfileerror": "İç hata: Dosya alınırken bir hata oluştu.",
index ff33f5b..4b04f77 100644 (file)
        "api-error-badtoken": "Эчке хата: дөрес булмаган токен.",
        "api-error-copyuploaddisabled": "URL-адрес буенча йөкләү бу серверда сүндерелгән.",
        "api-error-duplicate": "Мондый эчтәлекле {{PLURAL:$1|[$2 башка файл]}} да бар.",
-       "api-error-duplicate-archive": "Элек сайтта мондый эчтәлекле {{PLURAL:$1|[$2 башка файл]}} бар иде инде, ләкин {{PLURAL:$1|1=аны бетерделәр|аларны бетерделәр}}.",
+       "api-error-duplicate-archive": "Элек сайтта мондый эчтәлекле {{PLURAL:$1|башка файл}} бар иде инде, ләкин {{PLURAL:$1|1=аны бетерделәр|аларны бетерделәр}}.",
        "api-error-empty-file": "Сезнең тарафтан җибәрелгән файл буш.",
        "api-error-emptypage": "Яңа буш сәхифәләр төзү рөхсәт ителми",
        "api-error-unknown-code": "Билгесез хата: \"$1\"",
index 82a8497..d0985a5 100644 (file)
        "upload-http-error": "Відбулася помилка HTTP: $1",
        "upload-copy-upload-invalid-domain": "З цього домену завантаження неможливе.",
        "upload-dialog-title": "Завантажити файл",
-       "upload-dialog-error": "Сталася помилка",
-       "upload-dialog-warning": "З'явилось попередження",
        "upload-dialog-button-cancel": "Скасувати",
        "upload-dialog-button-done": "Готово",
        "upload-dialog-button-save": "Зберегти",
        "upload-dialog-button-upload": "Завантажити",
-       "upload-dialog-label-select-file": "Обрати файл",
-       "upload-dialog-label-infoform-title": "Деталі",
-       "upload-dialog-label-infoform-name": "Назва",
-       "upload-dialog-label-infoform-description": "Опис",
-       "upload-dialog-label-usage-title": "Використання",
-       "upload-dialog-label-usage-filename": "Назва файлу",
+       "upload-process-error": "Сталася помилка",
+       "upload-process-warning": "З'явилось попередження",
+       "upload-form-label-select-file": "Обрати файл",
+       "upload-form-label-infoform-title": "Деталі",
+       "upload-form-label-infoform-name": "Назва",
+       "upload-form-label-infoform-description": "Опис",
+       "upload-form-label-usage-title": "Використання",
+       "upload-form-label-usage-filename": "Назва файлу",
        "backend-fail-stream": "Не вдалося транслювати файл $1.",
        "backend-fail-backup": "Не вдалося створити резервну копію файлу $1.",
        "backend-fail-notexists": "Файл $1 не існує.",
        "api-error-badtoken": "Внутрішня помилка: некоректний токен.",
        "api-error-copyuploaddisabled": "На цьому сервері вимкнене завантаження за URL-адресою.",
        "api-error-duplicate": "Вже {{PLURAL:$1|1=існує  [$2 інший файл]|існують [$2 інші файли]}} з таким самим вмістом.",
-       "api-error-duplicate-archive": "Раніше на сайті вже {{PLURAL:$1|1=був [$2 файл]|були [$2 файли]}} з ідентичним вмістом, але {{PLURAL:$1|1=його|їх}} вилучили.",
+       "api-error-duplicate-archive": "Раніше на сайті вже {{PLURAL:$1|1=був файл|були файли}} з ідентичним вмістом, але {{PLURAL:$1|1=його|їх}} вилучили.",
        "api-error-empty-file": "Файл, який ви надіслали, порожній.",
        "api-error-emptypage": "Створення нової порожньої сторінки неприпустиме.",
        "api-error-fetchfileerror": "Внутрішня помилка: щось пішло не так під час отримання файлу.",
index dd9ed60..07e4bb4 100644 (file)
        "nstab-template": "Modèl",
        "nstab-help": "Ajuto",
        "nstab-category": "Categoria",
+       "mainpage-nstab": "Pagina prinsipale",
        "nosuchaction": "Operasion no riconossua",
        "nosuchactiontext": "L'asion spesifegà ne l'URL no a xè vałida.\nXè posibiłe che l'URL sia sta dizità en modo erato o che sia sta seguio on cołegamento no vałido.\nCiò podaria anca indicare on bug en {{SITENAME}}.",
        "nosuchspecialpage": "Pajina prinsipałe no disponibiłe",
        "upload-dialog-button-done": "Fato",
        "upload-dialog-button-save": "Salva",
        "upload-dialog-button-upload": "Carga",
-       "upload-dialog-label-select-file": "Siegli el file",
-       "upload-dialog-label-infoform-name": "Nome",
-       "upload-dialog-label-infoform-description": "Descrision",
-       "upload-dialog-label-usage-title": "Uso",
-       "upload-dialog-label-usage-filename": "Nome del file",
+       "upload-form-label-select-file": "Siegli el file",
+       "upload-form-label-infoform-name": "Nome",
+       "upload-form-label-infoform-description": "Descrision",
+       "upload-form-label-usage-title": "Uso",
+       "upload-form-label-usage-filename": "Nome del file",
        "backend-fail-stream": "Inposibiłe traxmetare el file $1.",
        "backend-fail-backup": "Inposibiłe fare el backup del file $1.",
        "backend-fail-notexists": "El file $1 no existe.",
        "api-error-badtoken": "Eror interno: token fałà.",
        "api-error-copyuploaddisabled": "El cargamento tramite URL el xe dixabiłità so sto server.",
        "api-error-duplicate": "So'l sito {{PLURAL:$1|ghe xe xà [$2 'n altro documento]|ghe xe xà [$2 altri documenti]}} có 'l steso contegnuo.",
-       "api-error-duplicate-archive": "{{PLURAL:$1|Ghe xera [$2 'n altro file]|Ghe xera [$2 altri file]}} xà inte'l sito có 'l steso contegnuo, ma {{PLURAL:$1|el xe sta scansełà|i xe stai scansełai}}.",
+       "api-error-duplicate-archive": "{{PLURAL:$1|Ghe xera 'n altro file|Ghe xera altri file}} xà inte'l sito có 'l steso contegnuo, ma {{PLURAL:$1|el xe sta scansełà|i xe stai scansełai}}.",
        "api-error-empty-file": "El file sełesionà el gera vodo.",
        "api-error-emptypage": "Ła creasion de nove pàjine vode nó ła xe consentia.",
        "api-error-fetchfileerror": "Eror interno: ghe xe sta un problema durante el recupero del documento.",
index 1d1c08c..9dc8fbc 100644 (file)
        "upload-http-error": "Xảy ra lỗi HTTP: $1",
        "upload-copy-upload-invalid-domain": "Không có sẵn các bản sao tải lên tại tên miền này.",
        "upload-dialog-title": "Tải tập tin lên",
-       "upload-dialog-error": "Đã xuất hiện lỗi",
-       "upload-dialog-warning": "Đã xuất hiện cảnh báo",
        "upload-dialog-button-cancel": "Hủy bỏ",
        "upload-dialog-button-done": "Xong",
        "upload-dialog-button-save": "Lưu",
        "upload-dialog-button-upload": "Tải lên",
-       "upload-dialog-label-select-file": "Chọn tập tin",
-       "upload-dialog-label-infoform-title": "Chi tiết",
-       "upload-dialog-label-infoform-name": "Tên",
-       "upload-dialog-label-infoform-description": "Miêu tả",
-       "upload-dialog-label-usage-title": "Sử dụng",
-       "upload-dialog-label-usage-filename": "Tên tập tin",
+       "upload-process-error": "Đã xuất hiện lỗi",
+       "upload-process-warning": "Đã xuất hiện cảnh báo",
+       "upload-form-label-select-file": "Chọn tập tin",
+       "upload-form-label-infoform-title": "Chi tiết",
+       "upload-form-label-infoform-name": "Tên",
+       "upload-form-label-infoform-description": "Miêu tả",
+       "upload-form-label-usage-title": "Sử dụng",
+       "upload-form-label-usage-filename": "Tên tập tin",
        "backend-fail-stream": "Không thể gửi luồng tập tin $1.",
        "backend-fail-backup": "Không thể sao lưu tập tin $1.",
        "backend-fail-notexists": "Tập tin $1 không tồn tại.",
        "api-error-badtoken": "Lỗi nội bộ: Dấu hiệu bị hỏng.",
        "api-error-copyuploaddisabled": "Chức năng tải lên từ URL đã bị tắt trên máy chủ này.",
        "api-error-duplicate": "Wiki này đã có [$2 {{PLURAL:$1|tập tin|$1 tập tin}} cùng nội dung] có tên khác",
-       "api-error-duplicate-archive": "{{PLURAL:$1|Một|Các}} [$2 tập tin khác] cùng nội dung đã tồn tại trên website, nhưng {{PLURAL:$1|nó|chúng}} đã bị xóa.",
+       "api-error-duplicate-archive": "{{PLURAL:$1|Một|Các}} tập tin khác cùng nội dung đã tồn tại trên website, nhưng {{PLURAL:$1|nó|chúng}} đã bị xóa.",
        "api-error-empty-file": "Bạn đã gửi tập tin rỗng.",
        "api-error-emptypage": "Không cho phép tạo mới các trang rỗng.",
        "api-error-fetchfileerror": "Lỗi nội bộ: Việc tải tập tin bị thất bại.",
index 5acfa3e..414db38 100644 (file)
        "mailnologin": "Nole adresse d' evoyeu",
        "mailnologintext": "Po-z evoyî èn emile a èn ôte uzeu i vs fåt esse [[Special:UserLogin|elodjî]] eyet aveur ene adresse emile d' evoyeu ki soeye valide dins vos [[Special:Preferences|preferinces]].",
        "emailuser": "Emiler a l' uzeu",
-       "emailpage": "Emilaedje a èn uzeu",
        "emailpagetext": "Vos ploz eployî l' formulrece chal pa dzo po-z evoyî èn emile a l' uzeu.\nL' adresse emile k' i gn a dins [[Special:Preferences|vos preferinces]] serè-st eployeye\ncome adresse di l' evoyeu (adresse «From:» di l' emile),\npo ki l' riçuveu poye risponde.",
        "defemailsubject": "Emile da l' uzeu «$1» so {{SITENAME}}",
        "noemailtitle": "Pont d' adresse emile",
        "api-error-badaccess-groups": "Vos n' avoz nén l' droet d' eberweter des fitchîs so ç' wiki cial",
        "api-error-copyuploaddisabled": "Les eberwetaedjes pa URL ni vont nén so ç' sierveu cial.",
        "api-error-duplicate": "N a ddja {{PLURAL:$1|[$2 èn ôte fitchî]|[$2 des ôtes fitchîs]}} avou l' minme contnou so nosse waibe",
-       "api-error-duplicate-archive": "N aveut ddja {{PLURAL:$1|[$2 èn ôte fitchî]|[$2 des ôtes fitchîs]}} avou l' minme contnou so nosse waibe, mins {{PLURAL:$1|il a stî rsaetchî|il ont stî rsaetchîs}}.",
-       "api-error-duplicate-archive-popup-title": "Ricopyî {{PLURAL:$1|l' fitchî|les fitchîs}} k' {{PLURAL:$1|a stî rsaetchî|k' ont stî rsaetchîs}}",
-       "api-error-duplicate-popup-title": "{{PLURAL:$1|fitchî|fitchîs}} a dobe",
+       "api-error-duplicate-archive": "N aveut ddja {{PLURAL:$1|èn ôte fitchî|des ôtes fitchîs}} avou l' minme contnou so nosse waibe, mins {{PLURAL:$1|il a stî rsaetchî|il ont stî rsaetchîs}}.",
        "api-error-empty-file": "Vosse fitchî est vude.",
        "api-error-emptypage": "C' est nén permetou d' ahiver ene novele pådje et l' leyî vude.",
        "api-error-fetchfileerror": "Aroke divintrinne : ene sacwè a fwait berwete cwand on-z a volou rawè vosse fitchî",
index 0a79897..af06d31 100644 (file)
        "upload-http-error": "მოხდა HTTP შეცდომა: $1",
        "upload-copy-upload-invalid-domain": "ამ დომენში ატვირთვების კოპირება არ არის ხელმისაწვდომი.",
        "upload-dialog-title": "გეხარგე ფაილეფი",
-       "upload-dialog-error": "ჩილათაქ მოხვადჷ",
        "upload-dialog-button-cancel": "გოუქვაფა",
        "upload-dialog-button-done": "ღოლამირჷ რე",
        "upload-dialog-button-save": "ჩუალა",
        "upload-dialog-button-upload": "ეხარგუა",
-       "upload-dialog-label-select-file": "გეგშაგორით ფაილი",
-       "upload-dialog-label-infoform-title": "დეტალეფი",
-       "upload-dialog-label-infoform-name": "ჯოხო",
-       "upload-dialog-label-infoform-description": "ეჭარუა",
-       "upload-dialog-label-usage-title": "გიმორინაფა",
-       "upload-dialog-label-usage-filename": "ფაილიშ ჯოხო",
+       "upload-process-error": "ჩილათაქ მოხვადჷ",
+       "upload-form-label-select-file": "გეგშაგორით ფაილი",
+       "upload-form-label-infoform-title": "დეტალეფი",
+       "upload-form-label-infoform-name": "ჯოხო",
+       "upload-form-label-infoform-description": "ეჭარუა",
+       "upload-form-label-usage-title": "გიმორინაფა",
+       "upload-form-label-usage-filename": "ფაილიშ ჯოხო",
        "backend-fail-stream": "ფაილი $1 ტრანსლირება ვერ მოხერხდა.",
        "backend-fail-backup": "ფაილი $1 სარეზერვო ასლის გაკეთება ვერ მოხერხდა.",
        "backend-fail-notexists": "ფაილი $1 არ არსებობს.",
index 064e619..dafae55 100644 (file)
        "upload-http-error": "א HTTP גרײַז האט פאַסירט: $1",
        "upload-copy-upload-invalid-domain": "ארויפלאדן טעקעס פון דעם דאמיין נישט מעגלעך.",
        "upload-dialog-title": "אַרױפֿלאָדן טעקע",
-       "upload-dialog-error": "א גרײַז האט פאסירט",
-       "upload-dialog-warning": "א ווארענונג האט פאסירט",
        "upload-dialog-button-cancel": "אַנולירן",
        "upload-dialog-button-done": "ערליידיקט",
        "upload-dialog-button-save": "אויפֿהיטן",
        "upload-dialog-button-upload": "אַרויפֿלאָדן",
-       "upload-dialog-label-select-file": "קלויבן טעקע",
-       "upload-dialog-label-infoform-title": "פרטים",
-       "upload-dialog-label-infoform-name": "נאָמען",
-       "upload-dialog-label-infoform-description": "באַשרײבונג",
-       "upload-dialog-label-usage-title": "באניץ",
-       "upload-dialog-label-usage-filename": "טעקע נאמען",
+       "upload-process-error": "א גרײַז האט פאסירט",
+       "upload-process-warning": "א ווארענונג האט פאסירט",
+       "upload-form-label-select-file": "קלויבן טעקע",
+       "upload-form-label-infoform-title": "פרטים",
+       "upload-form-label-infoform-name": "נאָמען",
+       "upload-form-label-infoform-description": "באַשרײבונג",
+       "upload-form-label-usage-title": "באניץ",
+       "upload-form-label-usage-filename": "טעקע נאמען",
        "backend-fail-stream": "קען נישט מאכן שטראמען טעקע $1.",
        "backend-fail-notexists": "נישט פֿאראן די טעקע $1.",
        "backend-fail-notsame": "א נישט־אידענטישע טעקע עקזיסטירט שוין ביי \"$1\".",
index 4e59491..dd1bb51 100644 (file)
        "api-error-badtoken": "Àsìṣe inú: Ìdáramọ̀ búburú.",
        "api-error-copyuploaddisabled": "Ìrùsókè pẹ̀lú URL jẹ́ dídálẹ́kun lórí ẹ̀rọ-ìpèsè yìí.",
        "api-error-duplicate": "{{PLURAL:$1|[$2 Fáìlì míràn]|[$2 Àwọn fáìlì míràn]}} kan tilẹ̀ wà lórí ibiìtàkùn pẹ̀lú àkóónú kannáà.",
-       "api-error-duplicate-archive": "{{PLURAL:$1|[$2 Fáìlì míràn]|[$2 Àwọn fáìlì míràn]}} kan tilẹ̀ wà lórí ibiìtàkùn pẹ̀lú àkóónú kannáà, sùgbọ́n {{PLURAL:$1|ó|wọ́n}} ti jẹ́ píparẹ́.",
+       "api-error-duplicate-archive": "{{PLURAL:$1|Fáìlì míràn|Àwọn fáìlì míràn}} kan tilẹ̀ wà lórí ibiìtàkùn pẹ̀lú àkóónú kannáà, sùgbọ́n {{PLURAL:$1|ó|wọ́n}} ti jẹ́ píparẹ́.",
        "api-error-empty-file": "Fáílì tí ẹ fisílẹ̀ jẹ́ òfo.",
        "api-error-emptypage": "Ẹ kò ní àyè láti dá ojúewé tuntun tó jẹ́ òfo.",
        "api-error-fetchfileerror": "Àsìṣe inú: Kò le mú fáìlì ná jáde nítorí àsìṣe.",
index d5dc7ad..c87d14c 100644 (file)
        "upload-http-error": "一個HTTP錯誤發生咗: $1",
        "upload-copy-upload-invalid-domain": "從嗰個域名度冇複製上傳功能",
        "upload-dialog-title": "上載檔案",
-       "upload-dialog-error": "出錯",
-       "upload-dialog-warning": "警告",
        "upload-dialog-button-cancel": "取消",
        "upload-dialog-button-done": "搞掂",
        "upload-dialog-button-save": "儲存",
        "upload-dialog-button-upload": "上載",
-       "upload-dialog-label-select-file": "揀檔案",
-       "upload-dialog-label-infoform-title": "細節",
-       "upload-dialog-label-infoform-name": "名",
-       "upload-dialog-label-infoform-description": "描述",
-       "upload-dialog-label-usage-title": "用法",
-       "upload-dialog-label-usage-filename": "文件名",
+       "upload-process-error": "出錯",
+       "upload-process-warning": "警告",
+       "upload-form-label-select-file": "揀檔案",
+       "upload-form-label-infoform-title": "細節",
+       "upload-form-label-infoform-name": "名",
+       "upload-form-label-infoform-description": "描述",
+       "upload-form-label-usage-title": "用法",
+       "upload-form-label-usage-filename": "文件名",
        "backend-fail-stream": "傳送唔到檔案「$1」。",
        "backend-fail-backup": "檔案 \"$1\" 唔備份得。",
        "backend-fail-notexists": "檔案$1唔存在。",
index 547d498..1f3b48f 100644 (file)
        "nstab-template": "模板",
        "nstab-help": "帮助页面",
        "nstab-category": "分类",
+       "mainpage-nstab": "首页",
        "nosuchaction": "无此操作",
        "nosuchactiontext": "URL指定的操作无效。您可能输入了错误的URL地址,或是点击了错误的链接。这也可能表明{{SITENAME}}使用的软件存在漏洞。",
        "nosuchspecialpage": "此特殊页面不存在",
        "createacct-captcha": "安全检查",
        "createacct-imgcaptcha-ph": "请输入上图中的文字",
        "createacct-submit": "创建您的账户",
-       "createacct-another-submit": "创建另一个账户",
+       "createacct-another-submit": "创建账户",
        "createacct-benefit-heading": "{{SITENAME}}是由同你一样的人们构筑的。",
        "createacct-benefit-body1": "{{PLURAL:$1|编辑}}",
        "createacct-benefit-body2": "{{PLURAL:$1|页面}}",
        "permissionserrorstext-withaction": "因为以下{{PLURAL:$1|原因}},你没有权限$2:",
        "recreate-moveddeleted-warn": "'''警告:你正在重新创建曾经被删除的页面。'''\n\n你应该考虑继续编辑本页是否合适。这里提供本页的删除和移动日志以供参考:",
        "moveddeleted-notice": "本页面已被删除。下面提供本页的删除和移动日志以供参考。",
+       "moveddeleted-notice-recent": "抱歉,此页面刚刚被删除(在最近24小时内)。\n页面的删除和移动日志在下方提供以供参考。",
        "log-fulllog": "查看完整日志",
        "edit-hook-aborted": "编辑被hook指令取消。\n无解释。",
        "edit-gone-missing": "不能更新页面。\n它可能刚刚被删除。",
        "group-bot": "机器人",
        "group-sysop": "管理员",
        "group-bureaucrat": "行政员",
-       "group-suppress": "ç\9b\91ç\9d£å\91\98",
+       "group-suppress": "ç¦\81æ­¢æ\89§è¡\8cè\80\85",
        "group-all": "(所有)",
        "group-user-member": "{{GENDER:$1|用户}}",
        "group-autoconfirmed-member": "自动确认用户",
        "group-bot-member": "机器人",
        "group-sysop-member": "{{GENDER:$1|管理员}}",
        "group-bureaucrat-member": "行政员",
-       "group-suppress-member": "{{GENDER:$1|ç\9b\91ç\9d£å\91\98}}",
+       "group-suppress-member": "{{GENDER:$1|ç¦\81æ­¢æ\89§è¡\8cè\80\85}}",
        "grouppage-user": "{{ns:project}}:用户",
        "grouppage-autoconfirmed": "{{ns:project}}:自动确认用户",
        "grouppage-bot": "{{ns:project}}:机器人",
        "grouppage-sysop": "{{ns:project}}:管理员",
        "grouppage-bureaucrat": "{{ns:project}}:行政员",
-       "grouppage-suppress": "{{ns:project}}:ç\9b\91ç\9d£",
+       "grouppage-suppress": "{{ns:project}}:ç¦\81æ­¢",
        "right-read": "阅读页面",
        "right-edit": "编辑页面",
        "right-createpage": "创建非讨论页面",
        "upload-http-error": "发生HTTP错误:$1",
        "upload-copy-upload-invalid-domain": "不能从该域名上载文件副本。",
        "upload-dialog-title": "上传文件",
-       "upload-dialog-error": "发生错误",
-       "upload-dialog-warning": "发生一条警告",
        "upload-dialog-button-cancel": "取消",
        "upload-dialog-button-done": "完成",
        "upload-dialog-button-save": "保存",
        "upload-dialog-button-upload": "上传",
-       "upload-dialog-label-select-file": "选择文件",
-       "upload-dialog-label-infoform-title": "详细信息",
-       "upload-dialog-label-infoform-name": "名称",
-       "upload-dialog-label-infoform-description": "说明",
-       "upload-dialog-label-usage-title": "用法",
-       "upload-dialog-label-usage-filename": "文件名",
+       "upload-process-error": "发生错误",
+       "upload-process-warning": "发生一条警告",
+       "upload-form-label-select-file": "选择文件",
+       "upload-form-label-infoform-title": "详细信息",
+       "upload-form-label-infoform-name": "名称",
+       "upload-form-label-infoform-description": "说明",
+       "upload-form-label-usage-title": "用法",
+       "upload-form-label-usage-filename": "文件名",
        "backend-fail-stream": "无法流传送文件$1。",
        "backend-fail-backup": "无法备份文件$1。",
        "backend-fail-notexists": "条目$1不存在。",
        "filerevert-legend": "恢复文件",
        "filerevert-intro": "你将要恢复文件'''[[Media:$1|$1]]'''至[$4 $2 $3的版本]。",
        "filerevert-comment": "原因:",
-       "filerevert-defaultcomment": "恢复至$1 $2的版本",
+       "filerevert-defaultcomment": "回退至$1 $2($3)的版本",
        "filerevert-submit": "恢复",
        "filerevert-success": "<strong>[[Media:$1|$1]]</strong>已经恢复至[$4 $2 $3的版本]。",
        "filerevert-badversion": "文件并无所请求时间戳下的早期本地版本。",
        "nopagetext": "您所指定的目标页面并不存在。",
        "pager-newer-n": "前$1个",
        "pager-older-n": "后$1个",
-       "suppress": "ç\9b\91ç\9d£",
+       "suppress": "ç¦\81æ­¢",
        "querypage-disabled": "本特殊页面因性能问题而停用。",
        "apihelp": "API 帮助",
        "apihelp-no-such-module": "找不到模块“$1”。",
        "emailccsubject": "您发送给$1的消息的副本:$2",
        "emailsent": "电子邮件已发送",
        "emailsenttext": "您的电子邮件已经发出。",
-       "emailuserfooter": "本电子邮件是通过{{SITENAME}}的“{{int:emailuser}}”功能被$1发送至$2的。",
+       "emailuserfooter": "本电子邮件是通过{{SITENAME}}的“{{int:emailuser}}”功能被$1{{GENDER:$1|发送}}至{{GENDER:$2|$2}}的。",
        "usermessage-summary": "留下系统消息。",
        "usermessage-editor": "系统信息编辑器",
        "watchlist": "监视列表",
        "api-error-badaccess-groups": "您没有将文件上传到此 wiki 的权限。",
        "api-error-badtoken": "内部错误:会话无效。",
        "api-error-copyuploaddisabled": "通过URL上传的功能已被此服务器禁用。",
-       "api-error-duplicate": "在网站上已经具有相同内容的{{PLURAL:$1|[$2 另一个文件]|[$2 另一些文件]}}。",
-       "api-error-duplicate-archive": "在网站上曾经具有相同内容的{{PLURAL:$1|[$2 另一个文件]|[$2 另一些文件]}},但已被删除。",
+       "api-error-duplicate": "在网站上已经具有相同内容的{{PLURAL:$1|另一个文件|另一些文件}}。",
+       "api-error-duplicate-archive": "在网站上曾经具有相同内容的{{PLURAL:$1|另一个文件|另一些文件}},但已被删除。",
        "api-error-empty-file": "您提交的文件是空的。",
        "api-error-emptypage": "不能创建没有内容的新页面。",
        "api-error-fetchfileerror": "内部错误:获取文件时发生错误。",
index d267a69..d09f04b 100644 (file)
@@ -61,7 +61,8 @@
                        "EagerLin",
                        "Cbliu",
                        "Citizen01",
-                       "Zhxy 519"
+                       "Zhxy 519",
+                       "Macofe"
                ]
        },
        "tog-underline": "底線標示連結:",
        "upload-http-error": "發生 HTTP 錯誤:$1",
        "upload-copy-upload-invalid-domain": "此網域不允許複製上傳的檔案。",
        "upload-dialog-title": "上傳檔案",
-       "upload-dialog-error": "發生錯誤",
-       "upload-dialog-warning": "發生警告",
        "upload-dialog-button-cancel": "取消",
        "upload-dialog-button-done": "完成",
        "upload-dialog-button-save": "儲存",
        "upload-dialog-button-upload": "上傳",
-       "upload-dialog-label-select-file": "選擇檔案",
-       "upload-dialog-label-infoform-title": "詳細資訊",
-       "upload-dialog-label-infoform-name": "名稱",
-       "upload-dialog-label-infoform-description": "描述",
-       "upload-dialog-label-usage-title": "用法",
-       "upload-dialog-label-usage-filename": "檔案名稱",
+       "upload-process-error": "發生錯誤",
+       "upload-process-warning": "發生警告",
+       "upload-form-label-select-file": "選擇檔案",
+       "upload-form-label-infoform-title": "詳細資訊",
+       "upload-form-label-infoform-name": "名稱",
+       "upload-form-label-infoform-description": "描述",
+       "upload-form-label-usage-title": "用法",
+       "upload-form-label-usage-filename": "檔案名稱",
        "backend-fail-stream": "無法傳輸檔案 \"$1\"。",
        "backend-fail-backup": "無法備份檔案 \"$1\"。",
        "backend-fail-notexists": "檔案 $1 不存在。",
        "api-error-badtoken": "內部錯誤:密鑰錯誤。",
        "api-error-copyuploaddisabled": "此伺服器已停用使用 URL 上傳檔案的功能。",
        "api-error-duplicate": "在網站上已有相同內容的{{PLURAL:$1|[$2 其他檔案]|[$2 其他檔案]}}。",
-       "api-error-duplicate-archive": "在網站上曾有相同內容的{{PLURAL:$1|[$2 其他檔案]|[$2 其他檔案]}},但已被刪除。",
+       "api-error-duplicate-archive": "在網站上曾有相同內容的{{PLURAL:$1|其他檔案|其他檔案}},但已被刪除。",
        "api-error-empty-file": "您送出的檔案是空的。",
        "api-error-emptypage": "不允許建立空白的頁面。",
        "api-error-fetchfileerror": "內部錯誤:取得檔案時發生錯誤。",
index 4f84bfb..def341b 100644 (file)
@@ -33,7 +33,7 @@ $namespaceAliases = array(
        'Espezial' => NS_SPECIAL,
 );
 
-// Remove Spanish gender aliases (bug 37090)
+// Remove Spanish gender aliases (bug T39090)
 $namespaceGenderAliases = array();
 
 $magicWords = array(
index f90746c..937b1bc 100644 (file)
@@ -8,4 +8,5 @@
  *
  */
 
-$fallback = 'ne';
\ No newline at end of file
+$fallback = 'ne';
+
diff --git a/languages/messages/MessagesOlo.php b/languages/messages/MessagesOlo.php
new file mode 100644 (file)
index 0000000..ba3d364
--- /dev/null
@@ -0,0 +1,14 @@
+<?php
+/** Livvi-Karelian (Livvinкarjala)
+ *
+ * To improve a translation please visit https://translatewiki.net
+ *
+ * @ingroup Language
+ * @file
+ *
+ */
+
+$fallback = 'ru';
+
+$linkTrail = '/^([a-zčČšŠžŽäÄöÖ]+)(.*)$/sDu';
+
diff --git a/languages/utils/CLDRPluralRuleConverter.php b/languages/utils/CLDRPluralRuleConverter.php
deleted file mode 100644 (file)
index 2eabcab..0000000
+++ /dev/null
@@ -1,322 +0,0 @@
-<?php
-/**
- * @author Niklas Laxström, Tim Starling
- *
- * @copyright Copyright © 2010-2012, Niklas Laxström
- * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
- *
- * @file
- * @since 1.20
- */
-
-/**
- * Helper class for converting rules to reverse polish notation (RPN).
- */
-class CLDRPluralRuleConverter {
-       /**
-        * The input string
-        *
-        * @var string
-        */
-       public $rule;
-
-       /**
-        * The current position
-        *
-        * @var int
-        */
-       public $pos;
-
-       /**
-        * The past-the-end position
-        *
-        * @var int
-        */
-       public $end;
-
-       /**
-        * The operator stack
-        *
-        * @var array
-        */
-       public $operators = array();
-
-       /**
-        * The operand stack
-        *
-        * @var array
-        */
-       public $operands = array();
-
-       /**
-        * Precedence levels. Note that there's no need to worry about associativity
-        * for the level 4 operators, since they return boolean and don't accept
-        * boolean inputs.
-        */
-       private static $precedence = array(
-               'or' => 2,
-               'and' => 3,
-               'is' => 4,
-               'is-not' => 4,
-               'in' => 4,
-               'not-in' => 4,
-               'within' => 4,
-               'not-within' => 4,
-               'mod' => 5,
-               ',' => 6,
-               '..' => 7,
-       );
-
-       /**
-        * A character list defining whitespace, for use in strspn() etc.
-        */
-       const WHITESPACE_CLASS = " \t\r\n";
-
-       /**
-        * Same for digits. Note that the grammar given in UTS #35 doesn't allow
-        * negative numbers or decimal separators.
-        */
-       const NUMBER_CLASS = '0123456789';
-
-       /**
-        * A character list of symbolic operands.
-        */
-       const OPERAND_SYMBOLS = 'nivwft';
-
-       /**
-        * An anchored regular expression which matches a word at the current offset.
-        */
-       const WORD_REGEX = '/[a-zA-Z@]+/A';
-
-       /**
-        * Convert a rule to RPN. This is the only public entry point.
-        *
-        * @param string $rule The rule to convert
-        * @return string The RPN representation of the rule
-        */
-       public static function convert( $rule ) {
-               $parser = new self( $rule );
-
-               return $parser->doConvert();
-       }
-
-       /**
-        * Private constructor.
-        * @param string $rule
-        */
-       protected function __construct( $rule ) {
-               $this->rule = $rule;
-               $this->pos = 0;
-               $this->end = strlen( $rule );
-       }
-
-       /**
-        * Do the operation.
-        *
-        * @return string The RPN representation of the rule (e.g. "5 3 mod n is")
-        */
-       protected function doConvert() {
-               $expectOperator = true;
-
-               // Iterate through all tokens, saving the operators and operands to a
-               // stack per Dijkstra's shunting yard algorithm.
-               /** @var CLDRPluralRuleConverterOperator $token */
-               while ( false !== ( $token = $this->nextToken() ) ) {
-                       // In this grammar, there are only binary operators, so every valid
-                       // rule string will alternate between operator and operand tokens.
-                       $expectOperator = !$expectOperator;
-
-                       if ( $token instanceof CLDRPluralRuleConverterExpression ) {
-                               // Operand
-                               if ( $expectOperator ) {
-                                       $token->error( 'unexpected operand' );
-                               }
-                               $this->operands[] = $token;
-                               continue;
-                       } else {
-                               // Operator
-                               if ( !$expectOperator ) {
-                                       $token->error( 'unexpected operator' );
-                               }
-                               // Resolve higher precedence levels
-                               $lastOp = end( $this->operators );
-                               while ( $lastOp && self::$precedence[$token->name] <= self::$precedence[$lastOp->name] ) {
-                                       $this->doOperation( $lastOp, $this->operands );
-                                       array_pop( $this->operators );
-                                       $lastOp = end( $this->operators );
-                               }
-                               $this->operators[] = $token;
-                       }
-               }
-
-               // Finish off the stack
-               while ( $op = array_pop( $this->operators ) ) {
-                       $this->doOperation( $op, $this->operands );
-               }
-
-               // Make sure the result is sane. The first case is possible for an empty
-               // string input, the second should be unreachable.
-               if ( !count( $this->operands ) ) {
-                       $this->error( 'condition expected' );
-               } elseif ( count( $this->operands ) > 1 ) {
-                       $this->error( 'missing operator or too many operands' );
-               }
-
-               $value = $this->operands[0];
-               if ( $value->type !== 'boolean' ) {
-                       $this->error( 'the result must have a boolean type' );
-               }
-
-               return $this->operands[0]->rpn;
-       }
-
-       /**
-        * Fetch the next token from the input string.
-        *
-        * @return CLDRPluralRuleConverterFragment The next token
-        */
-       protected function nextToken() {
-               if ( $this->pos >= $this->end ) {
-                       return false;
-               }
-
-               // Whitespace
-               $length = strspn( $this->rule, self::WHITESPACE_CLASS, $this->pos );
-               $this->pos += $length;
-
-               if ( $this->pos >= $this->end ) {
-                       return false;
-               }
-
-               // Number
-               $length = strspn( $this->rule, self::NUMBER_CLASS, $this->pos );
-               if ( $length !== 0 ) {
-                       $token = $this->newNumber( substr( $this->rule, $this->pos, $length ), $this->pos );
-                       $this->pos += $length;
-
-                       return $token;
-               }
-
-               // Two-character operators
-               $op2 = substr( $this->rule, $this->pos, 2 );
-               if ( $op2 === '..' || $op2 === '!=' ) {
-                       $token = $this->newOperator( $op2, $this->pos, 2 );
-                       $this->pos += 2;
-
-                       return $token;
-               }
-
-               // Single-character operators
-               $op1 = $this->rule[$this->pos];
-               if ( $op1 === ',' || $op1 === '=' || $op1 === '%' ) {
-                       $token = $this->newOperator( $op1, $this->pos, 1 );
-                       $this->pos++;
-
-                       return $token;
-               }
-
-               // Word
-               if ( !preg_match( self::WORD_REGEX, $this->rule, $m, 0, $this->pos ) ) {
-                       $this->error( 'unexpected character "' . $this->rule[$this->pos] . '"' );
-               }
-               $word1 = strtolower( $m[0] );
-               $word2 = '';
-               $nextTokenPos = $this->pos + strlen( $word1 );
-               if ( $word1 === 'not' || $word1 === 'is' ) {
-                       // Look ahead one word
-                       $nextTokenPos += strspn( $this->rule, self::WHITESPACE_CLASS, $nextTokenPos );
-                       if ( $nextTokenPos < $this->end
-                               && preg_match( self::WORD_REGEX, $this->rule, $m, 0, $nextTokenPos )
-                       ) {
-                               $word2 = strtolower( $m[0] );
-                               $nextTokenPos += strlen( $word2 );
-                       }
-               }
-
-               // Two-word operators like "is not" take precedence over single-word operators like "is"
-               if ( $word2 !== '' ) {
-                       $bothWords = "{$word1}-{$word2}";
-                       if ( isset( self::$precedence[$bothWords] ) ) {
-                               $token = $this->newOperator( $bothWords, $this->pos, $nextTokenPos - $this->pos );
-                               $this->pos = $nextTokenPos;
-
-                               return $token;
-                       }
-               }
-
-               // Single-word operators
-               if ( isset( self::$precedence[$word1] ) ) {
-                       $token = $this->newOperator( $word1, $this->pos, strlen( $word1 ) );
-                       $this->pos += strlen( $word1 );
-
-                       return $token;
-               }
-
-               // The single-character operand symbols
-               if ( strpos( self::OPERAND_SYMBOLS, $word1 ) !== false ) {
-                       $token = $this->newNumber( $word1, $this->pos );
-                       $this->pos++;
-
-                       return $token;
-               }
-
-               // Samples
-               if ( $word1 === '@integer' || $word1 === '@decimal' ) {
-                       // Samples are like comments, they have no effect on rule evaluation.
-                       // They run from the first sample indicator to the end of the string.
-                       $this->pos = $this->end;
-
-                       return false;
-               }
-
-               $this->error( 'unrecognised word' );
-       }
-
-       /**
-        * For the binary operator $op, pop its operands off the stack and push
-        * a fragment with rpn and type members describing the result of that
-        * operation.
-        *
-        * @param CLDRPluralRuleConverterOperator $op
-        */
-       protected function doOperation( $op ) {
-               if ( count( $this->operands ) < 2 ) {
-                       $op->error( 'missing operand' );
-               }
-               $right = array_pop( $this->operands );
-               $left = array_pop( $this->operands );
-               $result = $op->operate( $left, $right );
-               $this->operands[] = $result;
-       }
-
-       /**
-        * Create a numerical expression object
-        *
-        * @param string $text
-        * @param int $pos
-        * @return CLDRPluralRuleConverterExpression The numerical expression
-        */
-       protected function newNumber( $text, $pos ) {
-               return new CLDRPluralRuleConverterExpression( $this, 'number', $text, $pos, strlen( $text ) );
-       }
-
-       /**
-        * Create a binary operator
-        *
-        * @param string $type
-        * @param int $pos
-        * @param int $length
-        * @return CLDRPluralRuleConverterOperator The operator
-        */
-       protected function newOperator( $type, $pos, $length ) {
-               return new CLDRPluralRuleConverterOperator( $this, $type, $pos, $length );
-       }
-
-       /**
-        * Throw an error
-        * @param string $message
-        */
-       protected function error( $message ) {
-               throw new CLDRPluralRuleError( $message );
-       }
-}
diff --git a/languages/utils/CLDRPluralRuleConverterExpression.php b/languages/utils/CLDRPluralRuleConverterExpression.php
deleted file mode 100644 (file)
index 1ee6b4c..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-<?php
-/**
- * @author Niklas Laxström, Tim Starling
- *
- * @copyright Copyright © 2010-2012, Niklas Laxström
- * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
- *
- * @file
- * @since 1.20
- */
-
-/**
- * Helper for CLDRPluralRuleConverter.
- * An expression object, representing a region of the input string (for error
- * messages), the RPN notation used to evaluate it, and the result type for
- * validation.
- */
-class CLDRPluralRuleConverterExpression extends CLDRPluralRuleConverterFragment {
-       /** @var string */
-       public $type;
-
-       /** @var string */
-       public $rpn;
-
-       function __construct( $parser, $type, $rpn, $pos, $length ) {
-               parent::__construct( $parser, $pos, $length );
-               $this->type = $type;
-               $this->rpn = $rpn;
-       }
-
-       public function isType( $type ) {
-               if ( $type === 'range' && ( $this->type === 'range' || $this->type === 'number' ) ) {
-                       return true;
-               }
-               if ( $type === $this->type ) {
-                       return true;
-               }
-
-               return false;
-       }
-}
diff --git a/languages/utils/CLDRPluralRuleConverterFragment.php b/languages/utils/CLDRPluralRuleConverterFragment.php
deleted file mode 100644 (file)
index df299cb..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-<?php
-/**
- * @author Niklas Laxström, Tim Starling
- *
- * @copyright Copyright © 2010-2012, Niklas Laxström
- * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
- *
- * @file
- * @since 1.20
- */
-
-/**
- * Helper for CLDRPluralRuleConverter.
- * The base class for operators and expressions, describing a region of the input string.
- */
-class CLDRPluralRuleConverterFragment {
-       public $parser, $pos, $length, $end;
-
-       function __construct( $parser, $pos, $length ) {
-               $this->parser = $parser;
-               $this->pos = $pos;
-               $this->length = $length;
-               $this->end = $pos + $length;
-       }
-
-       public function error( $message ) {
-               $text = $this->getText();
-               throw new CLDRPluralRuleError( "$message at position " . ( $this->pos + 1 ) . ": \"$text\"" );
-       }
-
-       public function getText() {
-               return substr( $this->parser->rule, $this->pos, $this->length );
-       }
-}
diff --git a/languages/utils/CLDRPluralRuleConverterOperator.php b/languages/utils/CLDRPluralRuleConverterOperator.php
deleted file mode 100644 (file)
index de17f29..0000000
+++ /dev/null
@@ -1,114 +0,0 @@
-<?php
-/**
- * @author Niklas Laxström, Tim Starling
- *
- * @copyright Copyright © 2010-2012, Niklas Laxström
- * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
- *
- * @file
- * @since 1.20
- */
-
-/**
- * Helper for CLDRPluralRuleConverter.
- * An operator object, representing a region of the input string (for error
- * messages), and the binary operator at that location.
- */
-class CLDRPluralRuleConverterOperator extends CLDRPluralRuleConverterFragment {
-       /** @var string The name */
-       public $name;
-
-       /**
-        * Each op type has three characters: left operand type, right operand type and result type
-        *
-        *   b = boolean
-        *   n = number
-        *   r = range
-        *
-        * A number is a kind of range.
-        *
-        * @var array
-        */
-       private static $opTypes = array(
-               'or' => 'bbb',
-               'and' => 'bbb',
-               'is' => 'nnb',
-               'is-not' => 'nnb',
-               'in' => 'nrb',
-               'not-in' => 'nrb',
-               'within' => 'nrb',
-               'not-within' => 'nrb',
-               'mod' => 'nnn',
-               ',' => 'rrr',
-               '..' => 'nnr',
-       );
-
-       /**
-        * Map converting from the abbrevation to the full form.
-        *
-        * @var array
-        */
-       private static $typeSpecMap = array(
-               'b' => 'boolean',
-               'n' => 'number',
-               'r' => 'range',
-       );
-
-       /**
-        * Map for converting the new operators introduced in Rev 33 to the old forms
-        */
-       private static $aliasMap = array(
-               '%' => 'mod',
-               '!=' => 'not-in',
-               '=' => 'in'
-       );
-
-       /**
-        * Initialize a new instance of a CLDRPluralRuleConverterOperator object
-        *
-        * @param CLDRPluralRuleConverter $parser The parser
-        * @param string $name The operator name
-        * @param int $pos The length
-        * @param int $length
-        */
-       function __construct( $parser, $name, $pos, $length ) {
-               parent::__construct( $parser, $pos, $length );
-               if ( isset( self::$aliasMap[$name] ) ) {
-                       $name = self::$aliasMap[$name];
-               }
-               $this->name = $name;
-       }
-
-       /**
-        * Compute the operation
-        *
-        * @param CLDRPluralRuleConverterExpression $left The left part of the expression
-        * @param CLDRPluralRuleConverterExpression $right The right part of the expression
-        * @return CLDRPluralRuleConverterExpression The result of the operation
-        */
-       public function operate( $left, $right ) {
-               $typeSpec = self::$opTypes[$this->name];
-
-               $leftType = self::$typeSpecMap[$typeSpec[0]];
-               $rightType = self::$typeSpecMap[$typeSpec[1]];
-               $resultType = self::$typeSpecMap[$typeSpec[2]];
-
-               $start = min( $this->pos, $left->pos, $right->pos );
-               $end = max( $this->end, $left->end, $right->end );
-               $length = $end - $start;
-
-               $newExpr = new CLDRPluralRuleConverterExpression( $this->parser, $resultType,
-                       "{$left->rpn} {$right->rpn} {$this->name}",
-                       $start, $length );
-
-               if ( !$left->isType( $leftType ) ) {
-                       $newExpr->error( "invalid type for left operand: expected $leftType, got {$left->type}" );
-               }
-
-               if ( !$right->isType( $rightType ) ) {
-                       $newExpr->error( "invalid type for right operand: expected $rightType, got {$right->type}" );
-               }
-
-               return $newExpr;
-       }
-}
diff --git a/languages/utils/CLDRPluralRuleError.php b/languages/utils/CLDRPluralRuleError.php
deleted file mode 100644 (file)
index cc0b5d2..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-<?php
-/**
- * @author Niklas Laxström, Tim Starling
- *
- * @copyright Copyright © 2010-2012, Niklas Laxström
- * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
- *
- * @file
- * @since 1.20
- */
-
-/**
- * The exception class for all the classes in this file. This will be thrown
- * back to the caller if there is any validation error.
- */
-class CLDRPluralRuleError extends MWException {
-       function __construct( $message ) {
-               parent::__construct( 'CLDR plural rule error: ' . $message );
-       }
-}
diff --git a/languages/utils/CLDRPluralRuleEvaluator.php b/languages/utils/CLDRPluralRuleEvaluator.php
deleted file mode 100644 (file)
index 7e7208a..0000000
+++ /dev/null
@@ -1,187 +0,0 @@
-<?php
-
-/**
- * Parse and evaluate a plural rule.
- *
- * UTS #35 Revision 33
- * http://www.unicode.org/reports/tr35/tr35-33/tr35-numbers.html#Language_Plural_Rules
- *
- * @author Niklas Laxström, Tim Starling
- *
- * @copyright Copyright © 2010-2012, Niklas Laxström
- * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0
- * or later
- *
- * 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
- * @since 1.20
- */
-class CLDRPluralRuleEvaluator {
-       /**
-        * Evaluate a number against a set of plural rules. If a rule passes,
-        * return the index of plural rule.
-        *
-        * @param int $number The number to be evaluated against the rules
-        * @param array $rules The associative array of plural rules in pluralform => rule format.
-        * @return int The index of the plural form which passed the evaluation
-        */
-       public static function evaluate( $number, array $rules ) {
-               $rules = self::compile( $rules );
-
-               return self::evaluateCompiled( $number, $rules );
-       }
-
-       /**
-        * Convert a set of rules to a compiled form which is optimised for
-        * fast evaluation. The result will be an array of strings, and may be cached.
-        *
-        * @param array $rules The rules to compile
-        * @return array An array of compile rules.
-        */
-       public static function compile( array $rules ) {
-               // We can't use array_map() for this because it generates a warning if
-               // there is an exception.
-               foreach ( $rules as &$rule ) {
-                       $rule = CLDRPluralRuleConverter::convert( $rule );
-               }
-
-               return $rules;
-       }
-
-       /**
-        * Evaluate a compiled set of rules returned by compile(). Do not allow
-        * the user to edit the compiled form, or else PHP errors may result.
-        *
-        * @param string $number The number to be evaluated against the rules, in English, or it
-        *   may be a type convertible to string.
-        * @param array $rules The associative array of plural rules in pluralform => rule format.
-        * @return int The index of the plural form which passed the evaluation
-        */
-       public static function evaluateCompiled( $number, array $rules ) {
-               // Calculate the values of the operand symbols
-               $number = strval( $number );
-               if ( !preg_match( '/^ -? ( ([0-9]+) (?: \. ([0-9]+) )? )$/x', $number, $m ) ) {
-                       wfDebug( __METHOD__ . ": invalid number input, returning 'other'\n" );
-
-                       return count( $rules );
-               }
-               if ( !isset( $m[3] ) ) {
-                       $operandSymbols = array(
-                               'n' => intval( $m[1] ),
-                               'i' => intval( $m[1] ),
-                               'v' => 0,
-                               'w' => 0,
-                               'f' => 0,
-                               't' => 0
-                       );
-               } else {
-                       $absValStr = $m[1];
-                       $intStr = $m[2];
-                       $fracStr = $m[3];
-                       $operandSymbols = array(
-                               'n' => floatval( $absValStr ),
-                               'i' => intval( $intStr ),
-                               'v' => strlen( $fracStr ),
-                               'w' => strlen( rtrim( $fracStr, '0' ) ),
-                               'f' => intval( $fracStr ),
-                               't' => intval( rtrim( $fracStr, '0' ) ),
-                       );
-               }
-
-               // The compiled form is RPN, with tokens strictly delimited by
-               // spaces, so this is a simple RPN evaluator.
-               foreach ( $rules as $i => $rule ) {
-                       $stack = array();
-                       $zero = ord( '0' );
-                       $nine = ord( '9' );
-                       foreach ( StringUtils::explode( ' ', $rule ) as $token ) {
-                               $ord = ord( $token );
-                               if ( isset( $operandSymbols[$token] ) ) {
-                                       $stack[] = $operandSymbols[$token];
-                               } elseif ( $ord >= $zero && $ord <= $nine ) {
-                                       $stack[] = intval( $token );
-                               } else {
-                                       $right = array_pop( $stack );
-                                       $left = array_pop( $stack );
-                                       $result = self::doOperation( $token, $left, $right );
-                                       $stack[] = $result;
-                               }
-                       }
-                       if ( $stack[0] ) {
-                               return $i;
-                       }
-               }
-               // None of the provided rules match. The number belongs to category
-               // 'other', which comes last.
-               return count( $rules );
-       }
-
-       /**
-        * Do a single operation
-        *
-        * @param string $token The token string
-        * @param mixed $left The left operand. If it is an object, its state may be destroyed.
-        * @param mixed $right The right operand
-        * @throws CLDRPluralRuleError
-        * @return mixed The operation result
-        */
-       private static function doOperation( $token, $left, $right ) {
-               if ( in_array( $token, array( 'in', 'not-in', 'within', 'not-within' ) ) ) {
-                       if ( !( $right instanceof CLDRPluralRuleEvaluatorRange ) ) {
-                               $right = new CLDRPluralRuleEvaluatorRange( $right );
-                       }
-               }
-               switch ( $token ) {
-                       case 'or':
-                               return $left || $right;
-                       case 'and':
-                               return $left && $right;
-                       case 'is':
-                               return $left == $right;
-                       case 'is-not':
-                               return $left != $right;
-                       case 'in':
-                               return $right->isNumberIn( $left );
-                       case 'not-in':
-                               return !$right->isNumberIn( $left );
-                       case 'within':
-                               return $right->isNumberWithin( $left );
-                       case 'not-within':
-                               return !$right->isNumberWithin( $left );
-                       case 'mod':
-                               if ( is_int( $left ) ) {
-                                       return (int)fmod( $left, $right );
-                               }
-
-                               return fmod( $left, $right );
-                       case ',':
-                               if ( $left instanceof CLDRPluralRuleEvaluatorRange ) {
-                                       $range = $left;
-                               } else {
-                                       $range = new CLDRPluralRuleEvaluatorRange( $left );
-                               }
-                               $range->add( $right );
-
-                               return $range;
-                       case '..':
-                               return new CLDRPluralRuleEvaluatorRange( $left, $right );
-                       default:
-                               throw new CLDRPluralRuleError( "Invalid RPN token" );
-               }
-       }
-}
diff --git a/languages/utils/CLDRPluralRuleEvaluatorRange.php b/languages/utils/CLDRPluralRuleEvaluatorRange.php
deleted file mode 100644 (file)
index 996c22e..0000000
+++ /dev/null
@@ -1,110 +0,0 @@
-<?php
-/**
- * @author Niklas Laxström, Tim Starling
- *
- * @copyright Copyright © 2010-2012, Niklas Laxström
- * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
- *
- * @file
- * @since 1.20
- */
-
-/**
- * Evaluator helper class representing a range list.
- */
-class CLDRPluralRuleEvaluatorRange {
-       /**
-        * The parts
-        *
-        * @var array
-        */
-       public $parts = array();
-
-       /**
-        * Initialize a new instance of CLDRPluralRuleEvaluatorRange
-        *
-        * @param int $start The start of the range
-        * @param int|bool $end The end of the range, or false if the range is not bounded.
-        */
-       function __construct( $start, $end = false ) {
-               if ( $end === false ) {
-                       $this->parts[] = $start;
-               } else {
-                       $this->parts[] = array( $start, $end );
-               }
-       }
-
-       /**
-        * Determine if the given number is inside the range.
-        *
-        * @param int $number The number to check
-        * @param bool $integerConstraint If true, also asserts the number is an integer;
-        *   otherwise, number simply has to be inside the range.
-        * @return bool True if the number is inside the range; otherwise, false.
-        */
-       function isNumberIn( $number, $integerConstraint = true ) {
-               foreach ( $this->parts as $part ) {
-                       if ( is_array( $part ) ) {
-                               if ( ( !$integerConstraint || floor( $number ) === (float)$number )
-                                       && $number >= $part[0] && $number <= $part[1]
-                               ) {
-                                       return true;
-                               }
-                       } else {
-                               if ( $number == $part ) {
-                                       return true;
-                               }
-                       }
-               }
-
-               return false;
-       }
-
-       /**
-        * Readable alias for isNumberIn( $number, false ), and the implementation
-        * of the "within" operator.
-        *
-        * @param int $number The number to check
-        * @return bool True if the number is inside the range; otherwise, false.
-        */
-       function isNumberWithin( $number ) {
-               return $this->isNumberIn( $number, false );
-       }
-
-       /**
-        * Add another part to this range.
-        *
-        * @param CLDRPluralRuleEvaluatorRange|int $other The part to add, either
-        *   a range object itself or a single number.
-        */
-       function add( $other ) {
-               if ( $other instanceof self ) {
-                       $this->parts = array_merge( $this->parts, $other->parts );
-               } else {
-                       $this->parts[] = $other;
-               }
-       }
-
-       /**
-        * Returns the string representation of the rule evaluator range.
-        * The purpose of this method is to help debugging.
-        *
-        * @return string The string representation of the rule evaluator range
-        */
-       function __toString() {
-               $s = 'Range(';
-               foreach ( $this->parts as $i => $part ) {
-                       if ( $i ) {
-                               $s .= ', ';
-                       }
-                       if ( is_array( $part ) ) {
-                               $s .= $part[0] . '..' . $part[1];
-                       } else {
-                               $s .= $part;
-                       }
-               }
-               $s .= ')';
-
-               return $s;
-       }
-}
index 3b3fdee..f3b9c3c 100644 (file)
@@ -1,3 +1,3 @@
 -- Hopefully temporary index.
--- For https://bugzilla.wikimedia.org/show_bug.cgi?id=21279
+-- For https://phabricator.wikimedia.org/T23279
 CREATE INDEX /*i*/ar_revid ON /*$wgDBprefix*/archive ( ar_rev_id );
\ No newline at end of file
index 22cc587..9a991b8 100644 (file)
@@ -1,7 +1,7 @@
 --
 -- patch-backlinkindexes.sql
 --
--- Per bug 6440 / http://bugzilla.wikimedia.org/show_bug.cgi?id=6440
+-- Per task T8440 / https://phabricator.wikimedia.org/T8440
 --
 -- Improve performance of the "what links here"-type queries
 --
index 24ad84f..e2b2c3a 100644 (file)
@@ -1,7 +1,7 @@
 --
 -- patch-categorylinksindex.sql
 --
--- Per bug 10280 / http://bugzilla.wikimedia.org/show_bug.cgi?id=10280
+-- Per task T12280 / https://phabricator.wikimedia.org/T12280
 --
 -- Improve enum continuation performance of the what pages belong to a category query
 --
index 06ae17f..4e19b79 100644 (file)
@@ -19,7 +19,7 @@
  *
  * @file
  * @author TyA <tya.wiki@gmail.com>
- * @see [[bugzilla:30976]]
+ * @see https://phabricator.wikimedia.org/T32976
  * @ingroup Maintenance
  */
 
index 3f5d6b6..7bca0ec 100644 (file)
@@ -178,7 +178,7 @@ class PopulateContentModel extends Maintenance {
                                        if ( $dbFormat === $defaultFormat ) {
                                                $toSave[$defaultModel][] = $row->{$key};
                                        } else { // non-default format, just update now
-                                               $this->output( "Updating model to match format for $table $id of $title... ");
+                                               $this->output( "Updating model to match format for $table $id of $title... " );
                                                $dbw->update(
                                                        $table,
                                                        array( $model_column => $defaultModel ),
index 36ad394..8a28177 100644 (file)
--- a/phpcs.xml
+++ b/phpcs.xml
@@ -1,6 +1,14 @@
 <?xml version="1.0"?>
 <ruleset name="MediaWiki">
-       <rule ref="vendor/mediawiki/mediawiki-codesniffer/MediaWiki"/>
+       <rule ref="vendor/mediawiki/mediawiki-codesniffer/MediaWiki">
+               <!-- Disable failing rules -->
+               <exclude name="Generic.Files.LineLength"/>
+               <exclude name="PSR2.Methods.MethodDeclaration.Underscore"/>
+               <exclude name="MediaWiki.NamingConventions.PrefixedGlobalFunctions.wfPrefix"/>
+               <exclude name="Squiz.Classes.ValidClassName.NotCamelCaps"/>
+               <exclude name="MediaWiki.WhiteSpace.SpaceAfterControlStructure.Incorrect"/>
+               <exclude name="Generic.WhiteSpace.DisallowSpaceIndent.SpacesUsed"/>
+       </rule>
        <file>.</file>
        <arg name="encoding" value="utf8"/>
        <arg name="extensions" value="php,php5,inc,sample"/>
index 4e59312..6aeb206 100644 (file)
@@ -1151,25 +1151,37 @@ return array(
                ),
        ),
        'mediawiki.Upload.Dialog' => array(
-               'scripts' => 'resources/src/mediawiki/mediawiki.Upload.Dialog.js',
+               'scripts' => array(
+                       'resources/src/mediawiki/mediawiki.Upload.Dialog.js',
+               ),
                'dependencies' => array(
-                       'oojs-ui',
-                       'mediawiki.Upload',
+                       'mediawiki.Upload.BookletLayout',
                ),
                'messages' => array(
                        'upload-dialog-title',
-                       'upload-dialog-error',
-                       'upload-dialog-warning',
                        'upload-dialog-button-cancel',
                        'upload-dialog-button-done',
                        'upload-dialog-button-save',
                        'upload-dialog-button-upload',
-                       'upload-dialog-label-select-file',
-                       'upload-dialog-label-infoform-title',
-                       'upload-dialog-label-infoform-name',
-                       'upload-dialog-label-infoform-description',
-                       'upload-dialog-label-usage-title',
-                       'upload-dialog-label-usage-filename',
+               ),
+       ),
+       'mediawiki.Upload.BookletLayout' => array(
+               'scripts' => array(
+                       'resources/src/mediawiki/mediawiki.Upload.BookletLayout.js',
+               ),
+               'dependencies' => array(
+                       'oojs-ui',
+                       'mediawiki.Upload',
+               ),
+               'messages' => array(
+                       'upload-process-error',
+                       'upload-process-warning',
+                       'upload-form-label-select-file',
+                       'upload-form-label-infoform-title',
+                       'upload-form-label-infoform-name',
+                       'upload-form-label-infoform-description',
+                       'upload-form-label-usage-title',
+                       'upload-form-label-usage-filename',
                ),
        ),
        'mediawiki.toc' => array(
@@ -1759,6 +1771,8 @@ return array(
        'mediawiki.legacy.commonPrint' => array(
                'position' => 'top',
                'styles' => array(
+                       // @todo: Remove mediawiki.page.gallery when cache has cleared
+                       'resources/src/mediawiki.page/mediawiki.page.gallery.print.css' => array( 'media' => 'print' ),
                        // @todo: Remove mediawiki.action.view.filepage.print.css when cache has cleared
                        'resources/src/mediawiki.action/mediawiki.action.view.filepage.print.css' => array( 'media' => 'print' ),
                        'resources/src/mediawiki.legacy/commonPrint.css' => array( 'media' => 'print' )
@@ -1773,6 +1787,8 @@ return array(
        'mediawiki.legacy.shared' => array(
                'position' => 'top',
                'styles' => array(
+                       // @todo: Remove when mediawiki.page.gallery in cached html.
+                       'resources/src/mediawiki.page/mediawiki.page.gallery.css',
                        // @todo: Remove mediawiki.action.view.filepage.css
                        // and mediawiki.legacy/images/checker.png when cache has cleared
                        'resources/src/mediawiki.action/mediawiki.action.view.filepage.css',
@@ -1877,7 +1893,9 @@ return array(
                        'resources/src/mediawiki.widgets/mw.widgets.DateInputWidget.js',
                        'resources/src/mediawiki.widgets/mw.widgets.NamespaceInputWidget.js',
                        'resources/src/mediawiki.widgets/mw.widgets.ComplexNamespaceInputWidget.js',
+                       'resources/src/mediawiki.widgets/mw.widgets.TitleWidget.js',
                        'resources/src/mediawiki.widgets/mw.widgets.TitleInputWidget.js',
+                       'resources/src/mediawiki.widgets/mw.widgets.TitleSearchWidget.js',
                        'resources/src/mediawiki.widgets/mw.widgets.ComplexTitleInputWidget.js',
                        'resources/src/mediawiki.widgets/mw.widgets.TitleOptionWidget.js',
                        'resources/src/mediawiki.widgets/mw.widgets.UserInputWidget.js',
@@ -1887,7 +1905,7 @@ return array(
                        'default' => array(
                                'resources/src/mediawiki.widgets/mw.widgets.CalendarWidget.less',
                                'resources/src/mediawiki.widgets/mw.widgets.DateInputWidget.less',
-                               'resources/src/mediawiki.widgets/mw.widgets.TitleInputWidget.css',
+                               'resources/src/mediawiki.widgets/mw.widgets.TitleWidget.less',
                        ),
                ),
                'dependencies' => array(
index eaa138b..f6857e8 100644 (file)
                                new RegExp( /(https?|ftp|file):\/\// )
                        ],
                        isoDate: [
-                               new RegExp( /^\d{4}[\/\-]\d{1,2}[\/\-]\d{1,2}$/ )
+                               new RegExp( /^([-+]?\d{1,4})-([01]\d)-([0-3]\d)([T\s]((([01]\d|2[0-3])(:?[0-5]\d)?|24:?00)?(:?([0-5]\d))?([.,]\d+)?)([zZ]|([\+-])([01]\d|2[0-3]):?([0-5]\d)?)?)?/ ),
+                               new RegExp( /^([-+]?\d{1,4})-([01]\d)-([0-3]\d)/ )
                        ],
                        usLongDate: [
                                new RegExp( /^[A-Za-z]{3,10}\.? [0-9]{1,2}, ([0-9]{4}|'?[0-9]{2}) (([0-2]?[0-9]:[0-5][0-9])|([0-1]?[0-9]:[0-5][0-9]\s(AM|PM)))$/ )
                        return ts.rgx.isoDate[ 0 ].test( s );
                },
                format: function ( s ) {
-                       return $.tablesorter.formatFloat( ( s !== '' ) ? new Date( s.replace(
-                       new RegExp( /-/g ), '/' ) ).getTime() : '0' );
+                       var isodate,
+                               matches;
+                       if ( !Date.prototype.toISOString ) {
+                               // Old browsers don't understand iso, Fallback to US date parsing and ignore the time part.
+                               matches = $.trim( s ).match( ts.rgx.isoDate[ 1 ] );
+                               if ( matches ) {
+                                       isodate = new Date( matches[ 2 ]  + '/' + matches[ 3 ] + '/' + matches[ 1 ] );
+                               } else {
+                                       return $.tablesorter.formatFloat( 0 );
+                               }
+                       } else {
+                               isodate = new Date( $.trim( s ) );
+                       }
+                       return $.tablesorter.formatFloat( ( isodate !== undefined ) ? isodate.getTime() : 0 );
                },
                type: 'numeric'
        } );
index ab4535b..dd23e87 100644 (file)
                                if ( response.parse.modules ) {
                                        mw.loader.load( response.parse.modules.concat(
                                                response.parse.modulescripts,
-                                               response.parse.modulestyles,
-                                               response.parse.modulemessages ) );
+                                               response.parse.modulestyles
+                                       ) );
                                }
                                if ( response.parse.displaytitle ) {
                                        $displaytitle = $( $.parseHTML( response.parse.displaytitle ) );
index 677d26d..c6ae5ac 100644 (file)
                 *
                 * TODO: Is there a way we can ask the browser what's supported in `<img>`s?
                 *
-                * TODO: Put SVG back after working around Firefox 7 bug <https://bugzilla.wikimedia.org/show_bug.cgi?id=31643>
+                * TODO: Put SVG back after working around Firefox 7 bug <https://phabricator.wikimedia.org/T33643>
                 *
                 * @param {File} file
                 * @return {boolean}
index 8d6452c..995561e 100644 (file)
@@ -5,34 +5,67 @@
  * @license The MIT License (MIT); see LICENSE.txt
  */
 ( function ( $, mw ) {
+       var CSP;
 
        /**
         * Category selector widget. Displays an OO.ui.CapsuleMultiSelectWidget
         * and autocompletes with available categories.
         *
-        * @class
+        *     var selector = new mw.widgets.CategorySelector( {
+        *       searchTypes: [
+        *         mw.widgets.CategorySelector.SearchType.OpenSearch,
+        *         mw.widgets.CategorySelector.SearchType.InternalSearch
+        *       ]
+        *     } );
+        *
+        *     $( '#content' ).append( selector.$element );
+        *
+        *     selector.setSearchType( [ mw.widgets.CategorySelector.SearchType.SubCategories ] );
+        *
+        *
+        * @class mw.widgets.CategorySelector
         * @uses mw.Api
         * @extends OO.ui.CapsuleMultiSelectWidget
         *
         * @constructor
         * @param {Object} [config] Configuration options
         * @cfg {number} [limit=10] Maximum number of results to load
+        * @cfg {mw.widgets.CategorySelector.SearchType[]} [searchTypes=[mw.widgets.CategorySelector.SearchType.OpenSearch]]
+        *   Default search API to use when searching.
         */
-       mw.widgets.CategorySelector = function ( config ) {
+       function CategorySelector( config ) {
                // Config initialization
-               config = $.extend( { limit: 10 }, config );
+               config = $.extend( {
+                       limit: 10,
+                       searchTypes: [ CategorySelector.SearchType.OpenSearch ]
+               }, config );
                this.limit = config.limit;
+               this.searchTypes = config.searchTypes;
+               this.validateSearchTypes();
 
                // Parent constructor
-               mw.widgets.CategorySelector.parent.call( this, config );
+               mw.widgets.CategorySelector.parent.call( this, $.extend( true, {}, config, {
+                       menu: {
+                               filterFromInput: false
+                       },
+                       // This allows the user to both select non-existent categories, and prevents the selector from
+                       // being wiped from #onMenuItemsChange when we change the available options in the dropdown
+                       allowArbitrary: true
+               } ) );
 
                // Event handler to call the autocomplete methods
                this.$input.on( 'change input cut paste', OO.ui.debounce( this.updateMenuItems.bind( this ), 100 ) );
-       };
+
+               // Initialize
+               this.catNsId = mw.config.get( 'wgNamespaceIds' ).category;
+               this.api = new mw.Api();
+
+       }
 
        /* Setup */
 
-       OO.inheritClass( mw.widgets.CategorySelector, OO.ui.CapsuleMultiSelectWidget );
+       OO.inheritClass( CategorySelector, OO.ui.CapsuleMultiSelectWidget );
+       CSP = CategorySelector.prototype;
 
        /* Methods */
 
@@ -44,7 +77,8 @@
         * @private
         * @method
         */
-       mw.widgets.CategorySelector.prototype.updateMenuItems = function () {
+       CSP.updateMenuItems = function () {
+               this.getMenu().clearItems();
                this.getNewMenuItems( this.$input.val() ).then( function ( items ) {
                        var existingItems, filteredItems,
                                menu = this.getMenu();
                                } );
                        } );
 
-                       menu.addItems( filteredItems ).updateItemVisibility();
+                       menu.addItems( filteredItems ).toggle( true );
                }.bind( this ) );
        };
 
         * @param {string} input The input used to prefix search categories
         * @return {jQuery.Promise} Resolves with an array of categories
         */
-       mw.widgets.CategorySelector.prototype.getNewMenuItems = function ( input ) {
-               var deferred = new $.Deferred(),
-                       catNsId = mw.config.get( 'wgNamespaceIds' ).category,
-                       api = new mw.Api();
-
-               api.get( {
-                       action: 'opensearch',
-                       namespace: catNsId,
-                       limit: this.limit,
-                       search: input
-               } ).done( function ( res ) {
-                       var categoryNames = res[ 1 ].map( function ( name ) {
-                               return mw.Title.newFromText( name, catNsId ).getMainText();
+       CSP.getNewMenuItems = function ( input ) {
+               var i,
+                       promises = [],
+                       deferred = new $.Deferred();
+
+               for ( i = 0; i < this.searchTypes.length; i++ ) {
+                       promises.push( this.searchCategories( input, this.searchTypes[ i ] ) );
+               }
+
+               $.when.apply( $, promises ).done( function () {
+                       var categories, categoryNames,
+                               allData = [],
+                               dataSets = Array.prototype.slice.apply( arguments );
+
+                       // Collect values from all results
+                       allData = allData.concat.apply( allData, dataSets );
+
+                       // Remove duplicates
+                       categories = allData.filter( function ( value, index, self ) {
+                               return self.indexOf( value ) === index;
+                       } );
+
+                       // Get titles
+                       categoryNames = categories.map( function ( name ) {
+                               return mw.Title.newFromText( name, this.catNsId ).getMainText();
                        } );
 
                        deferred.resolve( categoryNames );
+
                } );
 
                return deferred.promise();
        };
+
+       /**
+        * Validates the values in `this.searchType`.
+        *
+        * @private
+        * @return {boolean}
+        */
+       CSP.validateSearchTypes = function () {
+               var validSearchTypes = false,
+                       searchTypeEnumCount = Object.keys( CategorySelector.SearchType ).length;
+
+               // Check if all values are in the SearchType enum
+               validSearchTypes = this.searchTypes.every( function ( searchType ) {
+                       return searchType > -1 && searchType < searchTypeEnumCount;
+               } );
+
+               if ( validSearchTypes === false ) {
+                       throw new Error( 'Unknown searchType in searchTypes' );
+               }
+
+               // If the searchTypes has CategorySelector.SearchType.SubCategories
+               // it can be the only search type.
+               if ( this.searchTypes.indexOf( CategorySelector.SearchType.SubCategories ) > -1 &&
+                       this.searchTypes.length > 1
+               ) {
+                       throw new Error( 'Can\'t have additional search types with CategorySelector.SearchType.SubCategories' );
+               }
+
+               // If the searchTypes has CategorySelector.SearchType.ParentCategories
+               // it can be the only search type.
+               if ( this.searchTypes.indexOf( CategorySelector.SearchType.ParentCategories ) > -1 &&
+                       this.searchTypes.length > 1
+               ) {
+                       throw new Error( 'Can\'t have additional search types with CategorySelector.SearchType.ParentCategories' );
+               }
+
+               return true;
+       };
+
+       /**
+        * Sets and validates the value of `this.searchType`.
+        *
+        * @param {mw.widgets.CategorySelector.SearchType[]} searchTypes
+        */
+       CSP.setSearchTypes = function ( searchTypes ) {
+               this.searchTypes = searchTypes;
+               this.validateSearchTypes();
+       };
+
+       /**
+        * Searches categories based on input and searchType.
+        *
+        * @private
+        * @method
+        * @param {string} input The input used to prefix search categories
+        * @param {mw.widgets.CategorySelector.SearchType} searchType
+        * @return {jQuery.Promise} Resolves with an array of categories
+        */
+       CSP.searchCategories = function ( input, searchType ) {
+               var deferred = new $.Deferred();
+
+               switch ( searchType ) {
+                       case CategorySelector.SearchType.OpenSearch:
+                               this.api.get( {
+                                       action: 'opensearch',
+                                       namespace: this.catNsId,
+                                       limit: this.limit,
+                                       search: input
+                               } ).done( function ( res ) {
+                                       var categories = res[ 1 ];
+                                       deferred.resolve( categories );
+                               } );
+                               break;
+
+                       case CategorySelector.SearchType.InternalSearch:
+                               this.api.get( {
+                                       action: 'query',
+                                       list: 'allpages',
+                                       apnamespace: this.catNsId,
+                                       aplimit: this.limit,
+                                       apfrom: input,
+                                       apprefix: input
+                               } ).done( function ( res ) {
+                                       var categories = res.query.allpages.map( function ( page ) {
+                                               return page.title;
+                                       } );
+                                       deferred.resolve( categories );
+                               } );
+                               break;
+
+                       case CategorySelector.SearchType.Exists:
+                               if ( input.indexOf( '|' ) > -1 ) {
+                                       deferred.resolve( [] );
+                                       break;
+                               }
+
+                               this.api.get( {
+                                       action: 'query',
+                                       prop: 'info',
+                                       titles: 'Category:' + input
+                               } ).done( function ( res ) {
+                                       var page,
+                                               categories = [];
+
+                                       for ( page in res.query.pages ) {
+                                               if ( parseInt( page, 10 ) > -1 ) {
+                                                       categories.push( res.query.pages[ page ].title );
+                                               }
+                                       }
+
+                                       deferred.resolve( categories );
+                               } );
+                               break;
+
+                       case CategorySelector.SearchType.SubCategories:
+                               if ( input.indexOf( '|' ) > -1 ) {
+                                       deferred.resolve( [] );
+                                       break;
+                               }
+
+                               this.api.get( {
+                                       action: 'query',
+                                       list: 'categorymembers',
+                                       cmtype: 'subcat',
+                                       cmlimit: this.limit,
+                                       cmtitle: 'Category:' + input
+                               } ).done( function ( res ) {
+                                       var categories = res.query.categorymembers.map( function ( category ) {
+                                               return category.title;
+                                       } );
+                                       deferred.resolve( categories );
+                               } );
+                               break;
+
+                       case CategorySelector.SearchType.ParentCategories:
+                               if ( input.indexOf( '|' ) > -1 ) {
+                                       deferred.resolve( [] );
+                                       break;
+                               }
+
+                               this.api.get( {
+                                       action: 'query',
+                                       prop: 'categories',
+                                       cllimit: this.limit,
+                                       titles: 'Category:' + input
+                               } ).done( function ( res )  {
+                                       var page,
+                                               categories = [];
+
+                                       for ( page in res.query.pages ) {
+                                               if ( parseInt( page, 10 ) > -1 ) {
+                                                       if ( $.isArray( res.query.pages[ page ].categories ) ) {
+                                                               categories.push.apply( categories, res.query.pages[ page ].categories.map( function ( category ) {
+                                                                       return category.title;
+                                                               } ) );
+                                                       }
+                                               }
+                                       }
+
+                                       deferred.resolve( categories );
+                               } );
+                               break;
+
+                       default:
+                               throw new Error( 'Unknown searchType' );
+               }
+
+               return deferred.promise();
+       };
+
+       /**
+        * @enum mw.widgets.CategorySelector.SearchType
+        * Types of search available.
+        */
+       CategorySelector.SearchType = {
+               /** Search using action=opensearch */
+               OpenSearch: 0,
+
+               /** Search using action=query */
+               InternalSearch: 1,
+
+               /** Search for existing categories with the exact title */
+               Exists: 2,
+
+               /** Search only subcategories  */
+               SubCategories: 3,
+
+               /** Search only parent categories */
+               ParentCategories: 4
+       };
+
+       mw.widgets.CategorySelector = CategorySelector;
 }( jQuery, mediaWiki ) );
diff --git a/resources/src/mediawiki.widgets/mw.widgets.TitleInputWidget.css b/resources/src/mediawiki.widgets/mw.widgets.TitleInputWidget.css
deleted file mode 100644 (file)
index 2c24b2b..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-/*!
- * MediaWiki Widgets - TitleInputWidget styles.
- *
- * @copyright 2011-2015 MediaWiki Widgets Team and others; see AUTHORS.txt
- * @license The MIT License (MIT); see LICENSE.txt
- */
-
-.mw-widget-titleInputWidget-menu-withImages .mw-widget-titleOptionWidget {
-       -webkit-box-sizing: border-box;
-       -moz-box-sizing: border-box;
-       box-sizing: border-box;
-       min-height: 3.75em;
-       margin-left: 3.75em;
-}
-
-.mw-widget-titleInputWidget-menu-withImages .mw-widget-titleOptionWidget:not(:last-child) {
-       margin-bottom: 1px;
-}
-
-.mw-widget-titleInputWidget-menu-withImages .oo-ui-iconElement .oo-ui-iconElement-icon {
-       display: block;
-       width: 3.75em;
-       height: 3.75em;
-       left: -3.75em;
-       background-color: #ccc;
-       opacity: 0.4;
-}
-
-.mw-widget-titleInputWidget-menu-withImages .oo-ui-iconElement .mw-widget-titleOptionWidget-hasImage {
-       border: 0;
-       background-size: cover;
-       opacity: 1;
-}
-
-.mw-widget-titleInputWidget-menu-withImages .mw-widget-titleOptionWidget .oo-ui-labelElement-label {
-       line-height: 2.8em;
-}
-
-.mw-widget-titleOptionWidget-description {
-       display: none;
-}
-
-.mw-widget-titleInputWidget-menu-withDescriptions .mw-widget-titleOptionWidget .oo-ui-labelElement-label {
-       line-height: 1.5em;
-}
-
-.mw-widget-titleInputWidget-menu-withDescriptions .mw-widget-titleOptionWidget-description {
-       display: block;
-       white-space: nowrap;
-       text-overflow: ellipsis;
-       overflow: hidden;
-}
-
-.oo-ui-menuOptionWidget:not(.oo-ui-optionWidget-selected) .mw-widget-titleOptionWidget-description,
-.oo-ui-menuOptionWidget.oo-ui-optionWidget-highlighted .mw-widget-titleOptionWidget-description {
-       color: #888;
-}
index d5a7abc..fc1007e 100644 (file)
         *
         * @class
         * @extends OO.ui.TextInputWidget
+        * @mixins mw.widgets.TitleWidget
         * @mixins OO.ui.mixin.LookupElement
         *
         * @constructor
-        * @param {Object} [config] Configuration options
-        * @cfg {number} [limit=10] Number of results to show
-        * @cfg {number} [namespace] Namespace to prepend to queries
-        * @cfg {boolean} [relative=true] If a namespace is set, return a title relative to it
         * @cfg {boolean} [suggestions=true] Display search suggestions
-        * @cfg {boolean} [showRedirectTargets=true] Show the targets of redirects
-        * @cfg {boolean} [showRedlink] Show red link to exact match if it doesn't exist
-        * @cfg {boolean} [showImages] Show page images
-        * @cfg {boolean} [showDescriptions] Show page descriptions
-        * @cfg {Object} [cache] Result cache which implements a 'set' method, taking keyed values as an argument
         */
        mw.widgets.TitleInputWidget = function MwWidgetsTitleInputWidget( config ) {
-               var widget = this;
-
-               // Config initialization
-               config = $.extend( {
-                       maxLength: 255,
-                       limit: 10
-               }, config );
+               config = config || {};
 
                // Parent constructor
-               mw.widgets.TitleInputWidget.parent.call( this, $.extend( {}, config, { autocomplete: false } ) );
+               mw.widgets.TitleInputWidget.parent.call( this, $.extend( {}, config, {
+                       validate: this.isQueryValid.bind( this ),
+                       autocomplete: false
+               } ) );
 
                // Mixin constructors
+               mw.widgets.TitleWidget.call( this, config );
                OO.ui.mixin.LookupElement.call( this, config );
 
                // Properties
-               this.limit = config.limit;
-               this.maxLength = config.maxLength;
-               this.namespace = config.namespace !== undefined ? config.namespace : null;
-               this.relative = config.relative !== undefined ? config.relative : true;
                this.suggestions = config.suggestions !== undefined ? config.suggestions : true;
-               this.showRedirectTargets = config.showRedirectTargets !== false;
-               this.showRedlink = !!config.showRedlink;
-               this.showImages = !!config.showImages;
-               this.showDescriptions = !!config.showDescriptions;
-               this.cache = config.cache;
 
                // Initialization
                this.$element.addClass( 'mw-widget-titleInputWidget' );
-               this.lookupMenu.$element.addClass( 'mw-widget-titleInputWidget-menu' );
+               this.lookupMenu.$element.addClass( 'mw-widget-titleWidget-menu' );
                if ( this.showImages ) {
-                       this.lookupMenu.$element.addClass( 'mw-widget-titleInputWidget-menu-withImages' );
+                       this.lookupMenu.$element.addClass( 'mw-widget-titleWidget-menu-withImages' );
                }
                if ( this.showDescriptions ) {
-                       this.lookupMenu.$element.addClass( 'mw-widget-titleInputWidget-menu-withDescriptions' );
+                       this.lookupMenu.$element.addClass( 'mw-widget-titleWidget-menu-withDescriptions' );
                }
                this.setLookupsDisabled( !this.suggestions );
-
-               this.interwikiPrefixes = [];
-               this.interwikiPrefixesPromise = new mw.Api().get( {
-                       action: 'query',
-                       meta: 'siteinfo',
-                       siprop: 'interwikimap'
-               } ).done( function ( data ) {
-                       $.each( data.query.interwikimap, function ( index, interwiki ) {
-                               widget.interwikiPrefixes.push( interwiki.prefix );
-                       } );
-               } );
        };
 
        /* Setup */
 
        OO.inheritClass( mw.widgets.TitleInputWidget, OO.ui.TextInputWidget );
+       OO.mixinClass( mw.widgets.TitleInputWidget, mw.widgets.TitleWidget );
        OO.mixinClass( mw.widgets.TitleInputWidget, OO.ui.mixin.LookupElement );
 
        /* Methods */
 
        /**
-        * Get the namespace to prepend to titles in suggestions, if any.
-        *
-        * @return {number|null} Namespace number
+        * @inheritdoc mw.widgets.TitleWidget
         */
-       mw.widgets.TitleInputWidget.prototype.getNamespace = function () {
-               return this.namespace;
+       mw.widgets.TitleInputWidget.prototype.getQueryValue = function () {
+               return this.getValue();
        };
 
        /**
-        * Set the namespace to prepend to titles in suggestions, if any.
-        *
-        * @param {number|null} namespace Namespace number
+        * @inheritdoc mw.widgets.TitleWidget
         */
        mw.widgets.TitleInputWidget.prototype.setNamespace = function ( namespace ) {
-               this.namespace = namespace;
+               // Mixin method
+               mw.widgets.TitleWidget.prototype.setNamespace.call( this, namespace );
+
                this.lookupCache = {};
                this.closeLookupMenu();
        };
 
+       /**
+        * @inheritdoc
+        */
+       mw.widgets.TitleInputWidget.prototype.getLookupRequest = function () {
+               return this.getSuggestionsPromise();
+       };
+
+       /**
+        * @inheritdoc OO.ui.mixin.LookupElement
+        */
+       mw.widgets.TitleInputWidget.prototype.getLookupCacheDataFromResponse = function ( response ) {
+               return response.query || {};
+       };
+
+       /**
+        * @inheritdoc OO.ui.mixin.LookupElement
+        */
+       mw.widgets.TitleInputWidget.prototype.getLookupMenuOptionsFromData = function ( response ) {
+               return this.getOptionsFromData( response );
+       };
+
        /**
         * @inheritdoc
         */
                return retval;
        };
 
-       /**
-        * @inheritdoc
-        */
-       mw.widgets.TitleInputWidget.prototype.getLookupRequest = function () {
-               var req,
-                       widget = this,
-                       promiseAbortObject = { abort: function () {
-                               // Do nothing. This is just so OOUI doesn't break due to abort being undefined.
-                       } };
-
-               if ( mw.Title.newFromText( this.value ) ) {
-                       return this.interwikiPrefixesPromise.then( function () {
-                               var params, props,
-                                       interwiki = widget.value.substring( 0, widget.value.indexOf( ':' ) );
-                               if (
-                                       interwiki && interwiki !== '' &&
-                                       widget.interwikiPrefixes.indexOf( interwiki ) !== -1
-                               ) {
-                                       return $.Deferred().resolve( { query: {
-                                               pages: [ {
-                                                       title: widget.value
-                                               } ]
-                                       } } ).promise( promiseAbortObject );
-                               } else {
-                                       params = {
-                                               action: 'query',
-                                               generator: 'prefixsearch',
-                                               gpssearch: widget.value,
-                                               gpsnamespace: widget.namespace !== null ? widget.namespace : undefined,
-                                               gpslimit: widget.limit,
-                                               ppprop: 'disambiguation'
-                                       };
-                                       props = [ 'info', 'pageprops' ];
-                                       if ( widget.showRedirectTargets ) {
-                                               params.redirects = '1';
-                                       }
-                                       if ( widget.showImages ) {
-                                               props.push( 'pageimages' );
-                                               params.pithumbsize = 80;
-                                               params.pilimit = widget.limit;
-                                       }
-                                       if ( widget.showDescriptions ) {
-                                               props.push( 'pageterms' );
-                                               params.wbptterms = 'description';
-                                       }
-                                       params.prop = props.join( '|' );
-                                       req = new mw.Api().get( params );
-                                       promiseAbortObject.abort = req.abort.bind( req ); // todo: ew
-                                       return req;
-                               }
-                       } ).promise( promiseAbortObject );
-               } else {
-                       // Don't send invalid titles to the API.
-                       // Just pretend it returned nothing so we can show the 'invalid title' section
-                       return $.Deferred().resolve( {} ).promise( promiseAbortObject );
-               }
-       };
-
-       /**
-        * Get lookup cache item from server response data.
-        *
-        * @method
-        * @param {Mixed} response Response from server
-        */
-       mw.widgets.TitleInputWidget.prototype.getLookupCacheDataFromResponse = function ( response ) {
-               return response.query || {};
-       };
-
-       /**
-        * Get list of menu items from a server response.
-        *
-        * @param {Object} data Query result
-        * @returns {OO.ui.MenuOptionWidget[]} Menu items
-        */
-       mw.widgets.TitleInputWidget.prototype.getLookupMenuOptionsFromData = function ( data ) {
-               var i, len, index, pageExists, pageExistsExact, suggestionPage, page, redirect, redirects,
-                       items = [],
-                       titles = [],
-                       titleObj = mw.Title.newFromText( this.value ),
-                       redirectsTo = {},
-                       pageData = {};
-
-               if ( data.redirects ) {
-                       for ( i = 0, len = data.redirects.length; i < len; i++ ) {
-                               redirect = data.redirects[ i ];
-                               redirectsTo[ redirect.to ] = redirectsTo[ redirect.to ] || [];
-                               redirectsTo[ redirect.to ].push( redirect.from );
-                       }
-               }
-
-               for ( index in data.pages ) {
-                       suggestionPage = data.pages[ index ];
-                       pageData[ suggestionPage.title ] = {
-                               missing: suggestionPage.missing !== undefined,
-                               redirect: suggestionPage.redirect !== undefined,
-                               disambiguation: OO.getProp( suggestionPage, 'pageprops', 'disambiguation' ) !== undefined,
-                               imageUrl: OO.getProp( suggestionPage, 'thumbnail', 'source' ),
-                               description: OO.getProp( suggestionPage, 'terms', 'description' )
-                       };
-
-                       // Throw away pages from wrong namespaces. This can happen when 'showRedirectTargets' is true
-                       // and we encounter a cross-namespace redirect.
-                       if ( this.namespace === null || this.namespace === suggestionPage.ns ) {
-                               titles.push( suggestionPage.title );
-                       }
-
-                       redirects = redirectsTo[ suggestionPage.title ] || [];
-                       for ( i = 0, len = redirects.length; i < len; i++ ) {
-                               pageData[ redirects[ i ] ] = {
-                                       missing: false,
-                                       redirect: true,
-                                       disambiguation: false,
-                                       description: mw.msg( 'mw-widgets-titleinput-description-redirect', suggestionPage.title )
-                               };
-                               titles.push( redirects[ i ] );
-                       }
-               }
-
-               // If not found, run value through mw.Title to avoid treating a match as a
-               // mismatch where normalisation would make them matching (bug 48476)
-
-               pageExistsExact = titles.indexOf( this.value ) !== -1;
-               pageExists = pageExistsExact || (
-                       titleObj && titles.indexOf( titleObj.getPrefixedText() ) !== -1
-               );
-
-               if ( !pageExists ) {
-                       pageData[ this.value ] = {
-                               missing: true, redirect: false, disambiguation: false,
-                               description: mw.msg( 'mw-widgets-titleinput-description-new-page' )
-                       };
-               }
-
-               if ( this.cache ) {
-                       this.cache.set( pageData );
-               }
-
-               // Offer the exact text as a suggestion if the page exists
-               if ( pageExists && !pageExistsExact ) {
-                       titles.unshift( this.value );
-               }
-               // Offer the exact text as a new page if the title is valid
-               if ( this.showRedlink && !pageExists && titleObj ) {
-                       titles.push( this.value );
-               }
-               for ( i = 0, len = titles.length; i < len; i++ ) {
-                       page = pageData[ titles[ i ] ] || {};
-                       items.push( new mw.widgets.TitleOptionWidget( this.getOptionWidgetData( titles[ i ], page ) ) );
-               }
-
-               return items;
-       };
-
-       /**
-        * Get menu option widget data from the title and page data
-        *
-        * @param {mw.Title} title Title object
-        * @param {Object} data Page data
-        * @return {Object} Data for option widget
-        */
-       mw.widgets.TitleInputWidget.prototype.getOptionWidgetData = function ( title, data ) {
-               var mwTitle = new mw.Title( title );
-               return {
-                       data: this.namespace !== null && this.relative
-                               ? mwTitle.getRelativeText( this.namespace )
-                               : title,
-                       title: mwTitle,
-                       imageUrl: this.showImages ? data.imageUrl : null,
-                       description: this.showDescriptions ? data.description : null,
-                       missing: data.missing,
-                       redirect: data.redirect,
-                       disambiguation: data.disambiguation,
-                       query: this.value
-               };
-       };
-
-       /**
-        * Get title object corresponding to given value, or #getValue if not given.
-        *
-        * @param {string} [value] Value to get a title for
-        * @returns {mw.Title|null} Title object, or null if value is invalid
-        */
-       mw.widgets.TitleInputWidget.prototype.getTitle = function ( value ) {
-               var title = value !== undefined ? value : this.getValue(),
-                       // mw.Title doesn't handle null well
-                       titleObj = mw.Title.newFromText( title, this.namespace !== null ? this.namespace : undefined );
-
-               return titleObj;
-       };
-
        /**
         * @inheritdoc
         */
        mw.widgets.TitleInputWidget.prototype.cleanUpValue = function ( value ) {
                var widget = this;
+
+               // Parent method
                value = mw.widgets.TitleInputWidget.parent.prototype.cleanUpValue.call( this, value );
+
                return $.trimByteLength( this.value, value, this.maxLength, function ( value ) {
                        var title = widget.getTitle( value );
                        return title ? title.getMain() : value;
                } ).newVal;
        };
 
-       /**
-        * @inheritdoc
-        */
-       mw.widgets.TitleInputWidget.prototype.isValid = function () {
-               return $.Deferred().resolve( !!this.getTitle() ).promise();
-       };
-
 }( jQuery, mediaWiki ) );
index ec0c935..1154c9f 100644 (file)
                                        .text( config.description )
                        );
                }
+
+               // Events
+               this.$link.on( 'click', function () {
+                       return false;
+               } );
        };
 
        /* Setup */
diff --git a/resources/src/mediawiki.widgets/mw.widgets.TitleSearchWidget.js b/resources/src/mediawiki.widgets/mw.widgets.TitleSearchWidget.js
new file mode 100644 (file)
index 0000000..0e2546f
--- /dev/null
@@ -0,0 +1,83 @@
+/*!
+ * MediaWiki Widgets - TitleSearchWidget class.
+ *
+ * @copyright 2011-2015 MediaWiki Widgets Team and others; see AUTHORS.txt
+ * @license The MIT License (MIT); see LICENSE.txt
+ */
+( function ( $, mw ) {
+
+       /**
+        * Creates an mw.widgets.TitleSearchWidget object.
+        *
+        * @class
+        * @extends OO.ui.SearchWidget
+        * @mixins mw.widgets.TitleWidget
+        *
+        * @constructor
+        */
+       mw.widgets.TitleSearchWidget = function MwWidgetsTitleSearchWidget( config ) {
+               config = config || {};
+
+               // Parent constructor
+               mw.widgets.TitleSearchWidget.parent.call( this, config );
+
+               // Mixin constructors
+               mw.widgets.TitleWidget.call( this, config );
+
+               this.query.setValidation( this.isQueryValid.bind( this ) );
+
+               // Events
+               this.results.connect( this, { choose: 'onTitleSearchResultsChoose' } );
+
+               // Initialization
+               this.$element.addClass( 'mw-widget-titleSearchWidget' );
+               this.results.$element.addClass( 'mw-widget-titleWidget-menu' );
+               if ( this.showImages ) {
+                       this.results.$element.addClass( 'mw-widget-titleWidget-menu-withImages' );
+               }
+               if ( this.showDescriptions ) {
+                       this.results.$element.addClass( 'mw-widget-titleWidget-menu-withDescriptions' );
+               }
+               if ( this.maxLength !== undefined ) {
+                       this.getQuery().$input.attr( 'maxlength', this.maxLength );
+               }
+       };
+
+       /* Setup */
+
+       OO.inheritClass( mw.widgets.TitleSearchWidget, OO.ui.SearchWidget );
+       OO.mixinClass( mw.widgets.TitleSearchWidget, mw.widgets.TitleWidget );
+
+       /* Methods */
+
+       /**
+        * @inheritdoc mw.widgets.TitleWidget
+        */
+       mw.widgets.TitleSearchWidget.prototype.getQueryValue = function () {
+               return this.getQuery().getValue();
+       };
+
+       /**
+        * Handle choose events from the result widget
+        *
+        * @param {OO.ui.OptionWidget} item Chosen item
+        */
+       mw.widgets.TitleSearchWidget.prototype.onTitleSearchResultsChoose = function ( item ) {
+               this.getQuery().setValue( item.getData() );
+       };
+
+       /**
+        * @inheritdoc
+        */
+       mw.widgets.TitleSearchWidget.prototype.onQueryChange = function () {
+               var widget = this;
+
+               this.getSuggestionsPromise().done( function ( response ) {
+                       // Parent method
+                       mw.widgets.TitleSearchWidget.parent.prototype.onQueryChange.call( widget );
+
+                       widget.results.addItems( widget.getOptionsFromData( response.query || {} ) );
+               } );
+       };
+
+}( jQuery, mediaWiki ) );
diff --git a/resources/src/mediawiki.widgets/mw.widgets.TitleWidget.js b/resources/src/mediawiki.widgets/mw.widgets.TitleWidget.js
new file mode 100644 (file)
index 0000000..f51c559
--- /dev/null
@@ -0,0 +1,285 @@
+/*!
+ * MediaWiki Widgets - TitleWidget class.
+ *
+ * @copyright 2011-2015 MediaWiki Widgets Team and others; see AUTHORS.txt
+ * @license The MIT License (MIT); see LICENSE.txt
+ */
+( function ( $, mw ) {
+
+       /**
+        * Mixin for title widgets
+        *
+        * @class
+        * @abstract
+        *
+        * @constructor
+        * @param {Object} [config] Configuration options
+        * @cfg {number} [limit=10] Number of results to show
+        * @cfg {number} [namespace] Namespace to prepend to queries
+        * @cfg {number} [maxLength=255] Maximum query length
+        * @cfg {boolean} [relative=true] If a namespace is set, return a title relative to it
+        * @cfg {boolean} [suggestions=true] Display search suggestions
+        * @cfg {boolean} [showRedirectTargets=true] Show the targets of redirects
+        * @cfg {boolean} [showRedlink] Show red link to exact match if it doesn't exist
+        * @cfg {boolean} [showImages] Show page images
+        * @cfg {boolean} [showDescriptions] Show page descriptions
+        * @cfg {Object} [cache] Result cache which implements a 'set' method, taking keyed values as an argument
+        */
+       mw.widgets.TitleWidget = function MwWidgetsTitleWidget( config ) {
+               var widget = this;
+
+               // Config initialization
+               config = $.extend( {
+                       maxLength: 255,
+                       limit: 10
+               }, config );
+
+               // Properties
+               this.limit = config.limit;
+               this.maxLength = config.maxLength;
+               this.namespace = config.namespace !== undefined ? config.namespace : null;
+               this.relative = config.relative !== undefined ? config.relative : true;
+               this.suggestions = config.suggestions !== undefined ? config.suggestions : true;
+               this.showRedirectTargets = config.showRedirectTargets !== false;
+               this.showRedlink = !!config.showRedlink;
+               this.showImages = !!config.showImages;
+               this.showDescriptions = !!config.showDescriptions;
+               this.cache = config.cache;
+
+               // Initialization
+               this.interwikiPrefixes = [];
+               this.interwikiPrefixesPromise = new mw.Api().get( {
+                       action: 'query',
+                       meta: 'siteinfo',
+                       siprop: 'interwikimap'
+               } ).done( function ( data ) {
+                       $.each( data.query.interwikimap, function ( index, interwiki ) {
+                               widget.interwikiPrefixes.push( interwiki.prefix );
+                       } );
+               } );
+       };
+
+       /* Setup */
+
+       OO.initClass( mw.widgets.TitleWidget );
+
+       /* Methods */
+
+       /**
+        * Get the current value of the search query
+        *
+        * @abstract
+        * @return {string} Search query
+        */
+       mw.widgets.TitleWidget.prototype.getQueryValue = null;
+
+       /**
+        * Get the namespace to prepend to titles in suggestions, if any.
+        *
+        * @return {number|null} Namespace number
+        */
+       mw.widgets.TitleWidget.prototype.getNamespace = function () {
+               return this.namespace;
+       };
+
+       /**
+        * Set the namespace to prepend to titles in suggestions, if any.
+        *
+        * @param {number|null} namespace Namespace number
+        */
+       mw.widgets.TitleWidget.prototype.setNamespace = function ( namespace ) {
+               this.namespace = namespace;
+       };
+
+       /**
+        * Get a promise which resolves with an API repsonse for suggested
+        * links for the current query.
+        */
+       mw.widgets.TitleWidget.prototype.getSuggestionsPromise = function () {
+               var req,
+                       query = this.getQueryValue(),
+                       widget = this,
+                       promiseAbortObject = { abort: function () {
+                               // Do nothing. This is just so OOUI doesn't break due to abort being undefined.
+                       } };
+
+               if ( mw.Title.newFromText( query ) ) {
+                       return this.interwikiPrefixesPromise.then( function () {
+                               var params, props,
+                                       interwiki = query.substring( 0, query.indexOf( ':' ) );
+                               if (
+                                       interwiki && interwiki !== '' &&
+                                       widget.interwikiPrefixes.indexOf( interwiki ) !== -1
+                               ) {
+                                       return $.Deferred().resolve( { query: {
+                                               pages: [ {
+                                                       title: query
+                                               } ]
+                                       } } ).promise( promiseAbortObject );
+                               } else {
+                                       params = {
+                                               action: 'query',
+                                               generator: 'prefixsearch',
+                                               gpssearch: query,
+                                               gpsnamespace: widget.namespace !== null ? widget.namespace : undefined,
+                                               gpslimit: widget.limit,
+                                               ppprop: 'disambiguation'
+                                       };
+                                       props = [ 'info', 'pageprops' ];
+                                       if ( widget.showRedirectTargets ) {
+                                               params.redirects = '1';
+                                       }
+                                       if ( widget.showImages ) {
+                                               props.push( 'pageimages' );
+                                               params.pithumbsize = 80;
+                                               params.pilimit = widget.limit;
+                                       }
+                                       if ( widget.showDescriptions ) {
+                                               props.push( 'pageterms' );
+                                               params.wbptterms = 'description';
+                                       }
+                                       params.prop = props.join( '|' );
+                                       req = new mw.Api().get( params );
+                                       promiseAbortObject.abort = req.abort.bind( req ); // todo: ew
+                                       return req;
+                               }
+                       } ).promise( promiseAbortObject );
+               } else {
+                       // Don't send invalid titles to the API.
+                       // Just pretend it returned nothing so we can show the 'invalid title' section
+                       return $.Deferred().resolve( {} ).promise( promiseAbortObject );
+               }
+       };
+
+       /**
+        * Get option widgets from the server response
+        *
+        * @param {Object} data Query result
+        * @returns {OO.ui.OptionWidget[]} Menu items
+        */
+       mw.widgets.TitleWidget.prototype.getOptionsFromData = function ( data ) {
+               var i, len, index, pageExists, pageExistsExact, suggestionPage, page, redirect, redirects,
+                       items = [],
+                       titles = [],
+                       titleObj = mw.Title.newFromText( this.getQueryValue() ),
+                       redirectsTo = {},
+                       pageData = {};
+
+               if ( data.redirects ) {
+                       for ( i = 0, len = data.redirects.length; i < len; i++ ) {
+                               redirect = data.redirects[ i ];
+                               redirectsTo[ redirect.to ] = redirectsTo[ redirect.to ] || [];
+                               redirectsTo[ redirect.to ].push( redirect.from );
+                       }
+               }
+
+               for ( index in data.pages ) {
+                       suggestionPage = data.pages[ index ];
+                       pageData[ suggestionPage.title ] = {
+                               missing: suggestionPage.missing !== undefined,
+                               redirect: suggestionPage.redirect !== undefined,
+                               disambiguation: OO.getProp( suggestionPage, 'pageprops', 'disambiguation' ) !== undefined,
+                               imageUrl: OO.getProp( suggestionPage, 'thumbnail', 'source' ),
+                               description: OO.getProp( suggestionPage, 'terms', 'description' )
+                       };
+
+                       // Throw away pages from wrong namespaces. This can happen when 'showRedirectTargets' is true
+                       // and we encounter a cross-namespace redirect.
+                       if ( this.namespace === null || this.namespace === suggestionPage.ns ) {
+                               titles.push( suggestionPage.title );
+                       }
+
+                       redirects = redirectsTo[ suggestionPage.title ] || [];
+                       for ( i = 0, len = redirects.length; i < len; i++ ) {
+                               pageData[ redirects[ i ] ] = {
+                                       missing: false,
+                                       redirect: true,
+                                       disambiguation: false,
+                                       description: mw.msg( 'mw-widgets-titleinput-description-redirect', suggestionPage.title )
+                               };
+                               titles.push( redirects[ i ] );
+                       }
+               }
+
+               // If not found, run value through mw.Title to avoid treating a match as a
+               // mismatch where normalisation would make them matching (bug 48476)
+
+               pageExistsExact = titles.indexOf( this.getQueryValue() ) !== -1;
+               pageExists = pageExistsExact || (
+                       titleObj && titles.indexOf( titleObj.getPrefixedText() ) !== -1
+               );
+
+               if ( !pageExists ) {
+                       pageData[ this.getQueryValue() ] = {
+                               missing: true, redirect: false, disambiguation: false,
+                               description: mw.msg( 'mw-widgets-titleinput-description-new-page' )
+                       };
+               }
+
+               if ( this.cache ) {
+                       this.cache.set( pageData );
+               }
+
+               // Offer the exact text as a suggestion if the page exists
+               if ( pageExists && !pageExistsExact ) {
+                       titles.unshift( this.getQueryValue() );
+               }
+               // Offer the exact text as a new page if the title is valid
+               if ( this.showRedlink && !pageExists && titleObj ) {
+                       titles.push( this.getQueryValue() );
+               }
+               for ( i = 0, len = titles.length; i < len; i++ ) {
+                       page = pageData[ titles[ i ] ] || {};
+                       items.push( new mw.widgets.TitleOptionWidget( this.getOptionWidgetData( titles[ i ], page ) ) );
+               }
+
+               return items;
+       };
+
+       /**
+        * Get menu option widget data from the title and page data
+        *
+        * @param {mw.Title} title Title object
+        * @param {Object} data Page data
+        * @return {Object} Data for option widget
+        */
+       mw.widgets.TitleWidget.prototype.getOptionWidgetData = function ( title, data ) {
+               var mwTitle = new mw.Title( title );
+               return {
+                       data: this.namespace !== null && this.relative
+                               ? mwTitle.getRelativeText( this.namespace )
+                               : title,
+                       title: mwTitle,
+                       imageUrl: this.showImages ? data.imageUrl : null,
+                       description: this.showDescriptions ? data.description : null,
+                       missing: data.missing,
+                       redirect: data.redirect,
+                       disambiguation: data.disambiguation,
+                       query: this.getQueryValue()
+               };
+       };
+
+       /**
+        * Get title object corresponding to given value, or #getQueryValue if not given.
+        *
+        * @param {string} [value] Value to get a title for
+        * @returns {mw.Title|null} Title object, or null if value is invalid
+        */
+       mw.widgets.TitleWidget.prototype.getTitle = function ( value ) {
+               var title = value !== undefined ? value : this.getQueryValue(),
+                       // mw.Title doesn't handle null well
+                       titleObj = mw.Title.newFromText( title, this.namespace !== null ? this.namespace : undefined );
+
+               return titleObj;
+       };
+
+       /**
+        * Check if the query is valid
+        *
+        * @return {boolean} The query is valid
+        */
+       mw.widgets.TitleWidget.prototype.isQueryValid = function () {
+               return !!this.getTitle();
+       };
+
+}( jQuery, mediaWiki ) );
diff --git a/resources/src/mediawiki.widgets/mw.widgets.TitleWidget.less b/resources/src/mediawiki.widgets/mw.widgets.TitleWidget.less
new file mode 100644 (file)
index 0000000..93c4b20
--- /dev/null
@@ -0,0 +1,63 @@
+/*!
+ * MediaWiki Widgets - TitleWidget styles.
+ *
+ * @copyright 2011-2015 MediaWiki Widgets Team and others; see AUTHORS.txt
+ * @license The MIT License (MIT); see LICENSE.txt
+ */
+
+.mw-widget-titleOptionWidget-description {
+       display: none;
+}
+
+.mw-widget-titleWidget {
+       &-menu-withImages {
+               .mw-widget-titleOptionWidget {
+                       -webkit-box-sizing: border-box;
+                       -moz-box-sizing: border-box;
+                       box-sizing: border-box;
+                       min-height: 3.75em;
+                       margin-left: 3.75em;
+               }
+
+               .mw-widget-titleOptionWidget:not(:last-child) {
+                       margin-bottom: 1px;
+               }
+
+               .oo-ui-iconElement .oo-ui-iconElement-icon {
+                       display: block;
+                       width: 3.75em;
+                       height: 3.75em;
+                       left: -3.75em;
+                       background-color: #ccc;
+                       opacity: 0.4;
+               }
+
+               .oo-ui-iconElement .mw-widget-titleOptionWidget-hasImage {
+                       border: 0;
+                       background-size: cover;
+                       opacity: 1;
+               }
+
+               .mw-widget-titleOptionWidget .oo-ui-labelElement-label {
+                       line-height: 2.8em;
+               }
+       }
+
+       &-menu-withDescriptions {
+               .mw-widget-titleOptionWidget .oo-ui-labelElement-label {
+                       line-height: 1.5em;
+               }
+
+               .mw-widget-titleOptionWidget-description {
+                       display: block;
+                       white-space: nowrap;
+                       text-overflow: ellipsis;
+                       overflow: hidden;
+               }
+       }
+}
+
+.oo-ui-menuOptionWidget:not(.oo-ui-optionWidget-selected) .mw-widget-titleOptionWidget-description,
+.oo-ui-menuOptionWidget.oo-ui-optionWidget-highlighted .mw-widget-titleOptionWidget-description {
+       color: #888;
+}
index fe8520b..3a70a66 100644 (file)
@@ -11,8 +11,8 @@
         * See <https://commons.wikimedia.org/wiki/Commons:Structured_data> for
         * a more detailed description of how that system works.
         *
-        * TODO this currently only supports uploads under CC-BY-SA 4.0,
-        *     and should really have support for more licenses.
+        * **TODO: This currently only supports uploads under CC-BY-SA 4.0,
+        * and should really have support for more licenses.**
         *
         * @inheritdoc
         */
diff --git a/resources/src/mediawiki/mediawiki.Upload.BookletLayout.js b/resources/src/mediawiki/mediawiki.Upload.BookletLayout.js
new file mode 100644 (file)
index 0000000..cb532c3
--- /dev/null
@@ -0,0 +1,414 @@
+( function ( $, mw ) {
+
+       /**
+        * mw.Upload.BookletLayout encapsulates the process of uploading a file
+        * to MediaWiki using the {@link mw.Upload upload model}.
+        * The booklet emits events that can be used to get the stashed
+        * upload and the final file. It can be extended to accept
+        * additional fields from the user for specific scenarios like
+        * for Commons, or campaigns.
+        *
+        * ## Structure
+        *
+        * The {@link OO.ui.BookletLayout booklet layout} has three steps:
+        *
+        *  - **Upload**: Has a {@link OO.ui.SelectFileWidget field} to get the file object.
+        *
+        * - **Information**: Has a {@link OO.ui.FormLayout form} to collect metadata. This can be
+        *   extended.
+        *
+        * - **Insert**: Has details on how to use the file that was uploaded.
+        *
+        * Each step has a form associated with it defined in
+        * {@link #renderUploadForm renderUploadForm},
+        * {@link #renderInfoForm renderInfoForm}, and
+        * {@link #renderInsertForm renderInfoForm}. The
+        * {@link #getFile getFile},
+        * {@link #getFilename getFilename}, and
+        * {@link #getText getText} methods are used to get
+        * the information filled in these forms, required to call
+        * {@link mw.Upload mw.Upload}.
+        *
+        * ## Usage
+        *
+        * See the {@link mw.Upload.Dialog upload dialog}.
+        *
+        * The {@link #event-fileUploaded fileUploaded},
+        * and {@link #event-fileSaved fileSaved} events can
+        * be used to get details of the upload.
+        *
+        * ## Extending
+        *
+        * To extend using {@link mw.Upload mw.Upload}, override
+        * {@link #renderInfoForm renderInfoForm} to render
+        * the form required for the specific use-case. Update the
+        * {@link #getFilename getFilename}, and
+        * {@link #getText getText} methods to return data
+        * from your newly created form. If you added new fields you'll also have
+        * to update the {@link #clear} method.
+        *
+        * If you plan to use a different upload model, apart from what is mentioned
+        * above, you'll also have to override the
+        * {@link #createUpload createUpload} method to
+        * return the new model. The {@link #saveFile saveFile}, and
+        * the {@link #uploadFile uploadFile} methods need to be
+        * overriden to use the new model and data returned from the forms.
+        *
+        * @class
+        * @extends OO.ui.BookletLayout
+        *
+        * @constructor
+        * @param {Object} config Configuration options
+        */
+       mw.Upload.BookletLayout = function ( config ) {
+               // Parent constructor
+               mw.Upload.BookletLayout.parent.call( this, config );
+
+               this.renderUploadForm();
+               this.renderInfoForm();
+               this.renderInsertForm();
+
+               this.addPages( [
+                       new OO.ui.PageLayout( 'upload', {
+                               scrollable: true,
+                               padded: true,
+                               content: [ this.uploadForm ]
+                       } ),
+                       new OO.ui.PageLayout( 'info', {
+                               scrollable: true,
+                               padded: true,
+                               content: [ this.infoForm ]
+                       } ),
+                       new OO.ui.PageLayout( 'insert', {
+                               scrollable: true,
+                               padded: true,
+                               content: [ this.insertForm ]
+                       } )
+               ] );
+       };
+
+       /* Setup */
+
+       OO.inheritClass( mw.Upload.BookletLayout, OO.ui.BookletLayout );
+
+       /* Events */
+
+       /**
+        * The file has finished uploading
+        *
+        * @event fileUploaded
+        */
+
+       /**
+        * The file has been saved to the database
+        *
+        * @event fileSaved
+        */
+
+       /**
+        * The upload form has changed
+        *
+        * @event uploadValid
+        * @param {boolean} isValid The form is valid
+        */
+
+       /**
+        * The info form has changed
+        *
+        * @event infoValid
+        * @param {boolean} isValid The form is valid
+        */
+
+       /* Properties */
+
+       /**
+        * @property {OO.ui.FormLayout} uploadForm
+        * The form rendered in the first step to get the file object.
+        * Rendered in {@link #renderUploadForm renderUploadForm}.
+        */
+
+       /**
+        * @property {OO.ui.FormLayout} infoForm
+        * The form rendered in the second step to get metadata.
+        * Rendered in {@link #renderInfoForm renderInfoForm}
+        */
+
+       /**
+        * @property {OO.ui.FormLayout} insertForm
+        * The form rendered in the third step to show usage
+        * Rendered in {@link #renderInsertForm renderInsertForm}
+        */
+
+       /* Methods */
+
+       /**
+        * Initialize for a new upload
+        */
+       mw.Upload.BookletLayout.prototype.initialize = function () {
+               this.clear();
+               this.upload = this.createUpload();
+               this.setPage( 'upload' );
+       };
+
+       /**
+        * Create a new upload model
+        *
+        * @protected
+        * @return {mw.Upload} Upload model
+        */
+       mw.Upload.BookletLayout.prototype.createUpload = function () {
+               return new mw.Upload();
+       };
+
+       /* Uploading */
+
+       /**
+        * Uploads the file that was added in the upload form. Uses
+        * {@link #getFile getFile} to get the HTML5
+        * file object.
+        *
+        * @protected
+        * @fires fileUploaded
+        * @return {jQuery.Promise}
+        */
+       mw.Upload.BookletLayout.prototype.uploadFile = function () {
+               var file = this.getFile();
+
+               this.filenameWidget.setValue( file.name );
+               this.setPage( 'info' );
+
+               this.upload.setFile( file );
+               this.uploadPromise = this.upload.uploadToStash();
+               this.uploadPromise.then( this.emit.bind( this, 'fileUploaded' ) );
+
+               return this.uploadPromise;
+       };
+
+       /**
+        * Saves the stash finalizes upload. Uses
+        * {@link #getFilename getFilename}, and
+        * {@link #getText getText} to get details from
+        * the form.
+        *
+        * @protected
+        * @fires fileSaved
+        * @returns {jQuery.Promise} Rejects the promise with an
+        * {@link OO.ui.Error error}, or resolves if the upload was successful.
+        */
+       mw.Upload.BookletLayout.prototype.saveFile = function () {
+               var layout = this,
+                       deferred = $.Deferred();
+
+               this.upload.setFilename( this.getFilename() );
+               this.upload.setText( this.getText() );
+
+               this.uploadPromise.always( function () {
+
+                       if ( layout.upload.getState() === mw.Upload.State.ERROR ) {
+                               deferred.reject( new OO.ui.Error( mw.msg( 'upload-process-error' )  ) );
+                               return false;
+                       }
+
+                       if ( layout.upload.getState() === mw.Upload.State.WARNING ) {
+                               deferred.reject( new OO.ui.Error( mw.msg( 'upload-process-error' )  ) );
+                               return false;
+                       }
+
+                       layout.upload.finishStashUpload().always( function () {
+                               var name;
+
+                               if ( layout.upload.getState() === mw.Upload.State.ERROR ) {
+                                       deferred.reject( new OO.ui.Error( mw.msg( 'upload-process-error' ) ) );
+                                       return false;
+                               }
+
+                               if ( layout.upload.getState() === mw.Upload.State.WARNING ) {
+                                       deferred.reject( new OO.ui.Error( mw.msg( 'upload-process-warning' ) ) );
+                                       return false;
+                               }
+
+                               // Normalize page name and localise the 'File:' prefix
+                               name = new mw.Title( 'File:' + layout.upload.getFilename() ).toString();
+                               layout.filenameUsageWidget.setValue( '[[' + name + ']]' );
+                               layout.setPage( 'insert' );
+
+                               deferred.resolve();
+                               layout.emit( 'fileSaved' );
+                       } );
+               } );
+
+               return deferred.promise();
+       };
+
+       /* Form renderers */
+
+       /**
+        * Renders and returns the upload form and sets the
+        * {@link #uploadForm uploadForm} property.
+        *
+        * @protected
+        * @fires selectFile
+        * @returns {OO.ui.FormLayout}
+        */
+       mw.Upload.BookletLayout.prototype.renderUploadForm = function () {
+               var fieldset;
+
+               this.selectFileWidget = new OO.ui.SelectFileWidget();
+               fieldset = new OO.ui.FieldsetLayout( { label: mw.msg( 'upload-form-label-select-file' ) } );
+               fieldset.addItems( [ this.selectFileWidget ] );
+               this.uploadForm = new OO.ui.FormLayout( { items: [ fieldset ] } );
+
+               // Validation
+               this.selectFileWidget.on( 'change', this.onUploadFormChange.bind( this ) );
+
+               return this.uploadForm;
+       };
+
+       /**
+        * Handle change events to the upload form
+        *
+        * @protected
+        * @fires uploadValid
+        */
+       mw.Upload.BookletLayout.prototype.onUploadFormChange = function () {
+               this.emit( 'uploadValid', !!this.selectFileWidget.getValue() );
+       };
+
+       /**
+        * Renders and returns the information form for collecting
+        * metadata and sets the {@link #infoForm infoForm}
+        * property.
+        *
+        * @protected
+        * @returns {OO.ui.FormLayout}
+        */
+       mw.Upload.BookletLayout.prototype.renderInfoForm = function () {
+               var fieldset;
+
+               this.filenameWidget = new OO.ui.TextInputWidget( {
+                       indicator: 'required',
+                       required: true,
+                       validate: /.+/
+               } );
+               this.descriptionWidget = new OO.ui.TextInputWidget( {
+                       indicator: 'required',
+                       required: true,
+                       validate: /.+/,
+                       multiline: true,
+                       autosize: true
+               } );
+
+               fieldset = new OO.ui.FieldsetLayout( {
+                       label: mw.msg( 'upload-form-label-infoform-title' )
+               } );
+               fieldset.addItems( [
+                       new OO.ui.FieldLayout( this.filenameWidget, {
+                               label: mw.msg( 'upload-form-label-infoform-name' ),
+                               align: 'top'
+                       } ),
+                       new OO.ui.FieldLayout( this.descriptionWidget, {
+                               label: mw.msg( 'upload-form-label-infoform-description' ),
+                               align: 'top'
+                       } )
+               ] );
+               this.infoForm = new OO.ui.FormLayout( { items: [ fieldset ] } );
+
+               this.filenameWidget.on( 'change', this.onInfoFormChange.bind( this ) );
+               this.descriptionWidget.on( 'change', this.onInfoFormChange.bind( this ) );
+
+               return this.infoForm;
+       };
+
+       /**
+        * Handle change events to the info form
+        *
+        * @protected
+        * @fires infoValid
+        */
+       mw.Upload.BookletLayout.prototype.onInfoFormChange = function () {
+               var layout = this;
+               $.when(
+                       this.filenameWidget.getValidity(),
+                       this.descriptionWidget.getValidity()
+               ).done( function () {
+                       layout.emit( 'infoValid', true );
+               } ).fail( function () {
+                       layout.emit( 'infoValid', false );
+               } );
+       };
+
+       /**
+        * Renders and returns the insert form to show file usage and
+        * sets the {@link #insertForm insertForm} property.
+        *
+        * @protected
+        * @returns {OO.ui.FormLayout}
+        */
+       mw.Upload.BookletLayout.prototype.renderInsertForm = function () {
+               var fieldset;
+
+               this.filenameUsageWidget = new OO.ui.TextInputWidget();
+               fieldset = new OO.ui.FieldsetLayout( {
+                       label: mw.msg( 'upload-form-label-usage-title' )
+               } );
+               fieldset.addItems( [
+                       new OO.ui.FieldLayout( this.filenameUsageWidget, {
+                               label: mw.msg( 'upload-form-label-usage-filename' ),
+                               align: 'top'
+                       } )
+               ] );
+               this.insertForm = new OO.ui.FormLayout( { items: [ fieldset ] } );
+
+               return this.insertForm;
+       };
+
+       /* Getters */
+
+       /**
+        * Gets the file object from the
+        * {@link #uploadForm upload form}.
+        *
+        * @protected
+        * @returns {File|null}
+        */
+       mw.Upload.BookletLayout.prototype.getFile = function () {
+               return this.selectFileWidget.getValue();
+       };
+
+       /**
+        * Gets the file name from the
+        * {@link #infoForm information form}.
+        *
+        * @protected
+        * @returns {string}
+        */
+       mw.Upload.BookletLayout.prototype.getFilename = function () {
+               return this.filenameWidget.getValue();
+       };
+
+       /**
+        * Gets the page text from the
+        * {@link #infoForm information form}.
+        *
+        * @protected
+        * @returns {string}
+        */
+       mw.Upload.BookletLayout.prototype.getText = function () {
+               return this.descriptionWidget.getValue();
+       };
+
+       /* Setters */
+
+       /**
+        * Clear the values of all fields
+        *
+        * @protected
+        */
+       mw.Upload.BookletLayout.prototype.clear = function () {
+               this.selectFileWidget.setValue( null );
+               this.filenameWidget.setValue( null ).setValidityFlag( true );
+               this.descriptionWidget.setValue( null ).setValidityFlag( true );
+               this.filenameUsageWidget.setValue( null );
+       };
+
+}( jQuery, mediaWiki ) );
index 36fbd46..de005f8 100644 (file)
@@ -1,33 +1,7 @@
 ( function ( $, mw ) {
 
        /**
-        * mw.Upload.Dialog encapsulates the process of uploading a file
-        * to MediaWiki using the {@link mw.Upload mw.Upload} model.
-        * The dialog emits events that can be used to get the stashed
-        * upload and the final file. It can be extended to accept
-        * additional fields from the user for specific scenarios like
-        * for Commons, or campaigns.
-        *
-        * ## Structure
-        *
-        * The {@link OO.ui.ProcessDialog dialog} has three steps:
-        *
-        *  - **Upload**: Has a {@link OO.ui.SelectFileWidget field} to get the file object.
-        *
-        * - **Information**: Has a {@link OO.ui.FormLayout form} to collect metadata. This can be
-        *   extended.
-        *
-        * - **Insert**: Has details on how to use the file that was uploaded.
-        *
-        * Each step has a form associated with it defined in
-        * {@link mw.Upload.Dialog#renderUploadForm renderUploadForm},
-        * {@link mw.Upload.Dialog#renderInfoForm renderInfoForm}, and
-        * {@link mw.Upload.Dialog#renderInsertForm renderInfoForm}. The
-        * {@link mw.Upload.Dialog#getFile getFile},
-        * {@link mw.Upload.Dialog#getFilename getFilename}, and
-        * {@link mw.Upload.Dialog#getText getText} methods are used to get
-        * the information filled in these forms, required to call
-        * {@link mw.Upload mw.Upload}.
+        * mw.Upload.Dialog controls a {@link mw.Upload.BookletLayout BookletLayout}.
         *
         * ## Usage
         *
         *     windowManager.addWindows( [ uploadDialog ] );
         *     windowManager.openWindow( uploadDialog );
         *
-        * The dialog's closing promise,
-        * {@link mw.Upload.Dialog#event-fileUploaded fileUploaded},
-        * and {@link mw.Upload.Dialog#event-fileSaved fileSaved} events can
-        * be used to get details of the upload.
-        *
-        * ## Extending
-        *
-        * To extend using {@link mw.Upload mw.Upload}, override
-        * {@link mw.Upload.Dialog#renderInfoForm renderInfoForm} to render
-        * the form required for the specific use-case. Update the
-        * {@link mw.Upload.Dialog#getFilename getFilename}, and
-        * {@link mw.Upload.Dialog#getText getText} methods to return data
-        * from your newly created form. If you added new fields you'll also have
-        * to update the {@link #getTeardownProcess} method.
-        *
-        * If you plan to use a different upload model, apart from what is mentioned
-        * above, you'll also have to override the
-        * {@link mw.Upload.Dialog#getUploadObject getUploadObject} method to
-        * return the new model. The {@link mw.Upload.Dialog#saveFile saveFile}, and
-        * the {@link mw.Upload.Dialog#uploadFile uploadFile} methods need to be
-        * overriden to use the new model and data returned from the forms.
+        * The dialog's closing promise can be used to get details of the upload.
         *
         * @class mw.Upload.Dialog
         * @uses mw.Upload
@@ -93,7 +47,7 @@
                        flags: 'safe',
                        action: 'cancel',
                        label: mw.msg( 'upload-dialog-button-cancel' ),
-                       modes: [ 'upload', 'insert', 'save' ]
+                       modes: [ 'upload', 'insert', 'info' ]
                },
                {
                        flags: [ 'primary', 'progressive' ],
                        flags: [ 'primary', 'constructive' ],
                        label: mw.msg( 'upload-dialog-button-save' ),
                        action: 'save',
-                       modes: 'save'
+                       modes: 'info'
                },
                {
                        flags: [ 'primary', 'progressive' ],
                        modes: 'upload'
                }
        ];
-       /*jshint +W024*/
 
-       /* Properties */
+       /*jshint +W024*/
 
-       /**
-        * @property {OO.ui.FormLayout} uploadForm
-        * The form rendered in the first step to get the file object.
-        * Rendered in {@link mw.Upload.Dialog#renderUploadForm renderUploadForm}.
-        */
+       /* Methods */
 
        /**
-        * @property {OO.ui.FormLayout} infoForm
-        * The form rendered in the second step to get metadata.
-        * Rendered in {@link mw.Upload.Dialog#renderInfoForm renderInfoForm}
+        * @inheritdoc
         */
+       mw.Upload.Dialog.prototype.initialize = function () {
+               // Parent method
+               mw.Upload.Dialog.parent.prototype.initialize.call( this );
 
-       /**
-        * @property {OO.ui.FormLayout} insertForm
-        * The form rendered in the third step to show usage
-        * Rendered in {@link mw.Upload.Dialog#renderInsertForm renderInsertForm}
-        */
+               this.uploadBooklet = this.createUploadBooklet();
+               this.uploadBooklet.connect( this, {
+                       set: 'onUploadBookletSet',
+                       uploadValid: 'onUploadValid',
+                       infoValid: 'onInfoValid'
+               } );
 
-       /* Events */
+               this.$body.append( this.uploadBooklet.$element );
+       };
 
        /**
-        * A `fileUploaded` event is emitted from the
-        * {@link mw.Upload.Dialog#uploadFile uploadFile} method.
+        * Create an upload booklet
         *
-        * @event fileUploaded
+        * @protected
+        * @return {mw.Upload.BookletLayout} An upload booklet
         */
+       mw.Upload.Dialog.prototype.createUploadBooklet = function () {
+               return new mw.Upload.BookletLayout();
+       };
 
        /**
-        * A `fileSaved` event is emitted from the
-        * {@link mw.Upload.Dialog#saveFile saveFile} method.
-        *
-        * @event fileSaved
+        * @inheritdoc
         */
-
-       /* Methods */
+       mw.Upload.Dialog.prototype.getBodyHeight = function () {
+               return 300;
+       };
 
        /**
-        * @inheritdoc
+        * Handle panelNameSet events from the upload booklet
+        *
+        * @protected
+        * @param {OO.ui.PageLayout} page Current page
         */
-       mw.Upload.Dialog.prototype.initialize = function () {
-               mw.Upload.Dialog.parent.prototype.initialize.call( this );
-
-               this.renderUploadForm();
-               this.renderInfoForm();
-               this.renderInsertForm();
-
-               this.uploadFormPanel = new OO.ui.PanelLayout( {
-                       scrollable: true,
-                       padded: true,
-                       content: [ this.uploadForm ]
-               } );
-               this.infoFormPanel = new OO.ui.PanelLayout( {
-                       scrollable: true,
-                       padded: true,
-                       content: [ this.infoForm ]
-               } );
-               this.insertFormPanel = new OO.ui.PanelLayout( {
-                       scrollable: true,
-                       padded: true,
-                       content: [ this.insertForm ]
-               } );
-
-               this.panels = new OO.ui.StackLayout();
-               this.panels.addItems( [
-                       this.uploadFormPanel,
-                       this.infoFormPanel,
-                       this.insertFormPanel
-               ] );
-
-               this.$body.append( this.panels.$element );
+       mw.Upload.Dialog.prototype.onUploadBookletSet = function ( page ) {
+               this.actions.setMode( page.getName() );
+               this.actions.setAbilities( { upload: false, save: false } );
        };
 
        /**
-        * @inheritdoc
+        * Handle uploadValid events
+        *
+        * {@link OO.ui.ActionSet#setAbilities Sets abilities}
+        * for the dialog accordingly.
+        *
+        * @protected
+        * @param {boolean} isValid The panel is complete and valid
         */
-       mw.Upload.Dialog.prototype.getBodyHeight = function () {
-               return 300;
+       mw.Upload.Dialog.prototype.onUploadValid = function ( isValid ) {
+               this.actions.setAbilities( { upload: isValid } );
        };
 
        /**
-        * Switch between the panels.
+        * Handle infoValid events
         *
-        * @param {string} panel Panel name: 'upload', 'info', 'insert'
+        * {@link OO.ui.ActionSet#setAbilities Sets abilities}
+        * for the dialog accordingly.
+        *
+        * @protected
+        * @param {boolean} isValid The panel is complete and valid
         */
-       mw.Upload.Dialog.prototype.switchPanels = function ( panel ) {
-               switch ( panel ) {
-               case 'upload':
-                       this.panels.setItem( this.uploadFormPanel );
-                       this.actions.setMode( 'upload' );
-                       break;
-               case 'info':
-                       this.panels.setItem( this.infoFormPanel );
-                       this.actions.setMode( 'save' );
-                       break;
-               case 'insert':
-                       this.panels.setItem( this.insertFormPanel );
-                       this.actions.setMode( 'insert' );
-                       break;
-               }
+       mw.Upload.Dialog.prototype.onInfoValid = function ( isValid ) {
+               this.actions.setAbilities( { save: isValid } );
        };
 
        /**
        mw.Upload.Dialog.prototype.getSetupProcess = function ( data ) {
                return mw.Upload.Dialog.parent.prototype.getSetupProcess.call( this, data )
                        .next( function () {
-                               this.upload = this.getUploadObject();
-                               this.switchPanels( 'upload' );
-                               this.actions.setAbilities( { upload: false } );
+                               this.uploadBooklet.initialize();
                        }, this );
        };
 
                var dialog = this;
 
                if ( action === 'upload' ) {
-                       return new OO.ui.Process( function () {
-                               dialog.filenameWidget.setValue( dialog.getFile().name );
-                               dialog.switchPanels( 'info' );
-                               dialog.actions.setAbilities( { save: false } );
-                               return dialog.uploadFile();
-                       } );
+                       return new OO.ui.Process( this.uploadBooklet.uploadFile() );
                }
                if ( action === 'save' ) {
-                       return new OO.ui.Process( dialog.saveFile() );
+                       return new OO.ui.Process( this.uploadBooklet.saveFile() );
                }
                if ( action === 'insert' ) {
                        return new OO.ui.Process( function () {
                        } );
                }
                if ( action === 'cancel' ) {
-                       return new OO.ui.Process( dialog.close() );
+                       return new OO.ui.Process( this.close() );
                }
 
                return mw.Upload.Dialog.parent.prototype.getActionProcess.call( this, action );
        mw.Upload.Dialog.prototype.getTeardownProcess = function ( data ) {
                return mw.Upload.Dialog.parent.prototype.getTeardownProcess.call( this, data )
                        .next( function () {
-                               // Clear the values of all fields
-                               this.selectFileWidget.setValue( null );
-                               this.filenameWidget.setValue( null ).setValidityFlag( true );
-                               this.descriptionWidget.setValue( null ).setValidityFlag( true );
-                               this.filenameUsageWidget.setValue( null );
+                               this.uploadBooklet.clear();
                        }, this );
        };
 
-       /* Uploading */
-
-       /**
-        * Get the upload model object required for this dialog. Can be
-        * extended to different models.
-        *
-        * @return {mw.Upload}
-        */
-       mw.Upload.Dialog.prototype.getUploadObject = function () {
-               return new mw.Upload();
-       };
-
-       /**
-        * Uploads the file that was added in the upload form. Uses
-        * {@link mw.Upload.Dialog#getFile getFile} to get the HTML5
-        * file object.
-        *
-        * @protected
-        * @fires fileUploaded
-        * @return {jQuery.Promise}
-        */
-       mw.Upload.Dialog.prototype.uploadFile = function () {
-               var dialog = this,
-                       file = this.getFile();
-               this.upload.setFile( file );
-               this.uploadPromise = this.upload.uploadToStash();
-               this.uploadPromise.then( function () {
-                       dialog.emit( 'fileUploaded' );
-               } );
-
-               return this.uploadPromise;
-       };
-
-       /**
-        * Saves the stash finalizes upload. Uses
-        * {@link mw.Upload.Dialog#getFilename getFilename}, and
-        * {@link mw.Upload.Dialog#getText getText} to get details from
-        * the form.
-        *
-        * @protected
-        * @fires fileSaved
-        * @returns {jQuery.Promise} Rejects the promise with an
-        * {@link OO.ui.Error error}, or resolves if the upload was successful.
-        */
-       mw.Upload.Dialog.prototype.saveFile = function () {
-               var dialog = this,
-                       promise = $.Deferred();
-
-               this.upload.setFilename( this.getFilename() );
-               this.upload.setText( this.getText() );
-
-               this.uploadPromise.always( function () {
-
-                       if ( dialog.upload.getState() === mw.Upload.State.ERROR ) {
-                               promise.reject( new OO.ui.Error( mw.msg( 'upload-dialog-error' )  ) );
-                               return false;
-                       }
-
-                       if ( dialog.upload.getState() === mw.Upload.State.WARNING ) {
-                               promise.reject( new OO.ui.Error( mw.msg( 'upload-dialog-error' )  ) );
-                               return false;
-                       }
-
-                       dialog.upload.finishStashUpload().always( function () {
-                               var name;
-
-                               if ( dialog.upload.getState() === mw.Upload.State.ERROR ) {
-                                       promise.reject( new OO.ui.Error( mw.msg( 'upload-dialog-error' ) ) );
-                                       return false;
-                               }
-
-                               if ( dialog.upload.getState() === mw.Upload.State.WARNING ) {
-                                       promise.reject( new OO.ui.Error( mw.msg( 'upload-dialog-warning' ) ) );
-                                       return false;
-                               }
-
-                               // Normalize page name and localise the 'File:' prefix
-                               name = new mw.Title( 'File:' + dialog.upload.getFilename() ).toString();
-                               dialog.filenameUsageWidget.setValue( '[[' + name + ']]' );
-                               dialog.switchPanels( 'insert' );
-
-                               promise.resolve();
-                               dialog.emit( 'fileSaved' );
-                       } );
-               } );
-
-               return promise.promise();
-       };
-
-       /* Form renderers */
-
-       /**
-        * Renders and returns the upload form and sets the
-        * {@link mw.Upload.Dialog#uploadForm uploadForm} property.
-        * Validates the form and
-        * {@link OO.ui.ActionSet#setAbilities sets abilities}
-        * for the dialog accordingly.
-        *
-        * @protected
-        * @returns {OO.ui.FormLayout}
-        */
-       mw.Upload.Dialog.prototype.renderUploadForm = function () {
-               var fieldset,
-                       dialog = this;
-
-               this.selectFileWidget = new OO.ui.SelectFileWidget();
-               fieldset = new OO.ui.FieldsetLayout( { label: mw.msg( 'upload-dialog-label-select-file' ) } );
-               fieldset.addItems( [ this.selectFileWidget ] );
-               this.uploadForm = new OO.ui.FormLayout( { items: [ fieldset ] } );
-
-               // Validation
-               this.selectFileWidget.on( 'change', function ( value ) {
-                       dialog.actions.setAbilities( { upload: !!value } );
-               } );
-
-               return this.uploadForm;
-       };
-
-       /**
-        * Renders and returns the information form for collecting
-        * metadata and sets the {@link mw.Upload.Dialog#infoForm infoForm}
-        * property.
-        * Validates the form and
-        * {@link OO.ui.ActionSet#setAbilities sets abilities}
-        * for the dialog accordingly.
-        *
-        * @protected
-        * @returns {OO.ui.FormLayout}
-        */
-       mw.Upload.Dialog.prototype.renderInfoForm = function () {
-               var fieldset,
-                       dialog = this;
-
-               this.filenameWidget = new OO.ui.TextInputWidget( {
-                       indicator: 'required',
-                       required: true,
-                       validate: /.+/
-               } );
-               this.descriptionWidget = new OO.ui.TextInputWidget( {
-                       indicator: 'required',
-                       required: true,
-                       validate: /.+/,
-                       multiline: true,
-                       autosize: true
-               } );
-
-               fieldset = new OO.ui.FieldsetLayout( {
-                       label: mw.msg( 'upload-dialog-label-infoform-title' )
-               } );
-               fieldset.addItems( [
-                       new OO.ui.FieldLayout( this.filenameWidget, {
-                               label: mw.msg( 'upload-dialog-label-infoform-name' ),
-                               align: 'top'
-                       } ),
-                       new OO.ui.FieldLayout( this.descriptionWidget, {
-                               label: mw.msg( 'upload-dialog-label-infoform-description' ),
-                               align: 'top'
-                       } )
-               ] );
-               this.infoForm = new OO.ui.FormLayout( { items: [ fieldset ] } );
-
-               // Validation
-               function checkValidity() {
-                       $.when(
-                               dialog.filenameWidget.getValidity(),
-                               dialog.descriptionWidget.getValidity()
-                       ).done( function () {
-                               dialog.actions.setAbilities( { save: true } );
-                       } ).fail( function () {
-                               dialog.actions.setAbilities( { save: false } );
-                       } );
-               }
-               this.filenameWidget.on( 'change', checkValidity );
-               this.descriptionWidget.on( 'change', checkValidity );
-
-               return this.infoForm;
-       };
-
-       /**
-        * Renders and returns the insert form to show file usage and
-        * sets the {@link mw.Upload.Dialog#insertForm insertForm} property.
-        *
-        * @protected
-        * @returns {OO.ui.FormLayout}
-        */
-       mw.Upload.Dialog.prototype.renderInsertForm = function () {
-               var fieldset;
-
-               this.filenameUsageWidget = new OO.ui.TextInputWidget();
-               fieldset = new OO.ui.FieldsetLayout( {
-                       label: mw.msg( 'upload-dialog-label-usage-title' )
-               } );
-               fieldset.addItems( [
-                       new OO.ui.FieldLayout( this.filenameUsageWidget, {
-                               label: mw.msg( 'upload-dialog-label-usage-filename' ),
-                               align: 'top'
-                       } )
-               ] );
-               this.insertForm = new OO.ui.FormLayout( { items: [ fieldset ] } );
-
-               return this.insertForm;
-       };
-
-       /* Getters */
-
-       /**
-        * Gets the file object from the
-        * {@link mw.Upload.Dialog#uploadForm upload form}.
-        *
-        * @protected
-        * @returns {File|null}
-        */
-       mw.Upload.Dialog.prototype.getFile = function () {
-               return this.selectFileWidget.getValue();
-       };
-
-       /**
-        * Gets the file name from the
-        * {@link mw.Upload.Dialog#infoForm information form}.
-        *
-        * @protected
-        * @returns {string}
-        */
-       mw.Upload.Dialog.prototype.getFilename = function () {
-               return this.filenameWidget.getValue();
-       };
-
-       /**
-        * Gets the page text from the
-        * {@link mw.Upload.Dialog#infoForm information form}.
-        *
-        * @protected
-        * @returns {string}
-        */
-       mw.Upload.Dialog.prototype.getText = function () {
-               return this.descriptionWidget.getValue();
-       };
 }( jQuery, mediaWiki ) );
index c8c63f3..7719a28 100644 (file)
@@ -4646,6 +4646,9 @@ http://example.com?
 http://example.com)
 http://example.com/url_with_(brackets)
 (http://example.com/url_without_brackets)
+http://example.com/url_with_entity&amp;
+http://example.com/url_with_entity&#x26;
+http://example.com/url_with_entity&#038;
 http://example.com/url_with_entity&nbsp;
 http://example.com/url_with_entity&#xA0;
 http://example.com/url_with_entity&#160;
@@ -4663,12 +4666,15 @@ http://example.com/url_with_entity&#60;
 <a rel="nofollow" class="external free" href="http://example.com">http://example.com</a>)
 <a rel="nofollow" class="external free" href="http://example.com/url_with_(brackets)">http://example.com/url_with_(brackets)</a>
 (<a rel="nofollow" class="external free" href="http://example.com/url_without_brackets">http://example.com/url_without_brackets</a>)
-<a rel="nofollow" class="external free" href="http://example.com/url_with_entity ">http://example.com/url_with_entity </a>
-<a rel="nofollow" class="external free" href="http://example.com/url_with_entity ">http://example.com/url_with_entity </a>
-<a rel="nofollow" class="external free" href="http://example.com/url_with_entity ">http://example.com/url_with_entity </a>
+<a rel="nofollow" class="external free" href="http://example.com/url_with_entity&amp;">http://example.com/url_with_entity&amp;</a>
+<a rel="nofollow" class="external free" href="http://example.com/url_with_entity&amp;">http://example.com/url_with_entity&amp;</a>
+<a rel="nofollow" class="external free" href="http://example.com/url_with_entity&amp;">http://example.com/url_with_entity&amp;</a>
+<a rel="nofollow" class="external free" href="http://example.com/url_with_entity">http://example.com/url_with_entity</a>&#160;
+<a rel="nofollow" class="external free" href="http://example.com/url_with_entity">http://example.com/url_with_entity</a>&#xa0;
+<a rel="nofollow" class="external free" href="http://example.com/url_with_entity">http://example.com/url_with_entity</a>&#160;
 <a rel="nofollow" class="external free" href="http://example.com/url_with_entity">http://example.com/url_with_entity</a>&lt;
-<a rel="nofollow" class="external free" href="http://example.com/url_with_entity%3C">http://example.com/url_with_entity%3C</a>
-<a rel="nofollow" class="external free" href="http://example.com/url_with_entity%3C">http://example.com/url_with_entity%3C</a>
+<a rel="nofollow" class="external free" href="http://example.com/url_with_entity">http://example.com/url_with_entity</a>&#x3c;
+<a rel="nofollow" class="external free" href="http://example.com/url_with_entity">http://example.com/url_with_entity</a>&#60;
 </p>
 !! html/parsoid
 <p><a rel="mw:ExtLink" href="http://example.com">http://example.com</a>,
@@ -10348,12 +10354,52 @@ BUG 553: link with two variables in a piped link
 {|
 |[[{{{1}}}|{{{2}}}]]
 |}
-!! html
+!! html/php
 <table>
 <tr>
 <td>[[{{{1}}}|{{{2}}}]]
 </td></tr></table>
 
+!! html/parsoid
+<table>
+<tbody><tr><td>[[<span about="#mwt5" typeof="mw:Param" data-parsoid='{"src":"{{{1}}}"}'>{{{1}}}</span>|<span about="#mwt2" typeof="mw:Param" data-parsoid='{"src":"{{{2}}}"}'>{{{2}}}</span>]]</td></tr>
+!! end
+
+# See: T2553
+!! test
+Abort table cell attribute parsing on wikilink
+!! wikitext
+{|
+| testing [[one|two]] | three || four
+| testing one two | three || four
+|}
+!! html/php
+<table>
+<tr>
+<td> testing <a href="/index.php?title=One&amp;action=edit&amp;redlink=1" class="new" title="One (page does not exist)">two</a> | three </td>
+<td> four
+</td>
+<td> three </td>
+<td> four
+</td></tr></table>
+
+!! html/parsoid
+<table>
+<tbody><tr data-parsoid='{"autoInsertedEnd":true,"autoInsertedStart":true}'><td data-parsoid='{"autoInsertedEnd":true}'> testing <a rel="mw:WikiLink" href="./One" title="One" data-parsoid='{"stx":"piped","a":{"href":"./One"},"sa":{"href":"one"}}'>two</a> | three </td><td data-parsoid='{"stx_v":"row","autoInsertedEnd":true}'> four</td>
+<td data-parsoid='{"a":{"testing":null,"one":null,"two":null},"sa":{"testing":"","one":"","two":""},"autoInsertedEnd":true}'> three </td><td data-parsoid='{"stx_v":"row","autoInsertedEnd":true}'> four</td></tr>
+</tbody></table>
+!! end
+
+!! test
+Don't abort table cell attribute parsing if wikilink is found in template arg
+!! wikitext
+{|
+| Test {{#tag:ref|One two "[[three]]" four}}
+|}
+!! html/parsoid
+<table>
+<tbody><tr><td> Test <ref about="#mwt1" typeof="mw:Transclusion" data-parsoid='{"pi":[[{"k":"1","spc":["","","",""]}]]}' data-mw='{"parts":[{"template":{"target":{"wt":"#tag:ref","function":"#tag"},"params":{"1":{"wt":"One two \"[[three]]\" four"}},"i":0}}]}'>One two "<a rel="mw:WikiLink" href="./Three" title="Three">three</a>" four</ref></td></tr>
+</tbody></table>
 !! end
 
 !! test
@@ -11813,6 +11859,17 @@ Templates: Ugly templates: 6. Template encapsulation test: Cyclical nesting of t
 </table></td></tr></tbody></table>
 !! end
 
+!! test
+Templates: Parameters substituted at the top-level
+!! wikitext
+{{{foo|''who'' {{echo|me}}? '''never!'''}}}
+!! html/php
+<p><i>who</i> me? <b>never!</b>
+</p>
+!! html/parsoid
+<p about="#mwt2" typeof="mw:Param" data-parsoid="{&quot;src&quot;:&quot;{{{foo|''who'' {{echo|me}}? '''never!'''}}}&quot;}"><i>who</i> me? <b>never!</b></p>
+!! end
+
 !!test
 Parser Functions: 1. Simple example
 !! wikitext
@@ -17968,11 +18025,15 @@ Special:Search page linking.
 {{!}} is a magic word
 !! wikitext
 {{!}} is a magic word there and {{!}} is still a magic word here
+| is not a magic word here but {{!}} is still a magic word here
 !! html/php
 <p>| is a magic word there and | is still a magic word here
+| is not a magic word here but | is still a magic word here
 </p>
 !! html/parsoid
-<p><span about="#mwt1" typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"!","href":"./Template:!"},"params":{},"i":0}}]}' data-parsoid='{"pi":[[]]}'>|</span> is a magic word there and <span about="#mwt2" typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"!","href":"./Template:!"},"params":{},"i":0}}]}' data-parsoid='{"pi":[[]]}'>|</span> is still a magic word here</p>
+<p><span about="#mwt1" typeof="mw:Transclusion" data-parsoid='{"pi":[[]]}' data-mw='{"parts":[{"template":{"target":{"wt":"!","href":"./Template:!"},"params":{},"i":0}}]}'>|</span> is a magic word there and <span about="#mwt2" typeof="mw:Transclusion" data-parsoid='{"pi":[[]]}' data-mw='{"parts":[{"template":{"target":{"wt":"!","href":"./Template:!"},"params":{},"i":0}}]}'>|</span> is still a magic word here
+| is not a magic word here but <span about="#mwt3" typeof="mw:Transclusion" data-parsoid='{"pi":[[]]}' data-mw='{"parts":[{"template":{"target":{"wt":"!","href":"./Template:!"},"params":{},"i":0}}]}'>|</span> is still a magic word here</p>
+
 !! end
 
 !! test
@@ -23739,11 +23800,15 @@ RT-ed inter-element separators should be valid separators
 {|
 |- [[foo]]
 |}
-!! html
+!! html/php
 <table>
 
 </table>
 
+!! html/parsoid
+<table>
+<tbody><tr data-parsoid='{"startTagSrc":"|-","a":{"[[foo]]":null},"sa":{"[[foo]]":""},"autoInsertedEnd":true}'></tr>
+</tbody></table>
 !!end
 
 # Parsoid-only since PHP parser relies on Tidy for correct output
@@ -26054,6 +26119,37 @@ parsoid={
 </table>
 !! end
 
+## T111151 Remove font elements without attributes
+!! test
+5a. font tags without attributes should be dropped in scrubWikitext mode
+!! options
+parsoid={
+  "modes": ["html2wt"],
+  "scrubWikitext": true
+}
+!! html
+<font>foo</font>
+<font><font>bar</font></font>
+<font class="x">boo</font>
+!! wikitext
+foo
+bar
+<font class="x">boo</font>
+!! end
+
+!! test
+5b. font tags should not be dropped without scrubWikitext being enabled
+!! options
+parsoid={
+  "modes": ["html2wt"],
+  "scrubWikitext": false
+}
+!! html
+<font>foo</font>
+!! wikitext
+<font>foo</font>
+!! end
+
 !! test
 Escape nowiki DOM elements
 !! options
index 7dc7027..0d71922 100644 (file)
@@ -103,6 +103,12 @@ abstract class MediaWikiTestCase extends PHPUnit_Framework_TestCase {
                 */
                ObjectCache::$instances[CACHE_DB] = new HashBagOStuff;
 
+               // Sandbox APC by replacing with in-process hash instead.
+               // Ensures values are removed between tests.
+               ObjectCache::$instances['apc'] =
+               ObjectCache::$instances['xcache'] =
+               ObjectCache::$instances['wincache'] = new HashBagOStuff;
+
                $needsResetDB = false;
 
                if ( $this->needsDB() ) {
@@ -217,7 +223,9 @@ abstract class MediaWikiTestCase extends PHPUnit_Framework_TestCase {
 
        protected function tearDown() {
                $status = ob_get_status();
-               if ( isset( $status['name'] ) && $status['name'] === 'MediaWikiTestCase::wfResetOutputBuffersBarrier' ) {
+               if ( isset( $status['name'] ) &&
+                       $status['name'] === 'MediaWikiTestCase::wfResetOutputBuffersBarrier'
+               ) {
                        ob_end_flush();
                }
 
@@ -1151,7 +1159,7 @@ abstract class MediaWikiTestCase extends PHPUnit_Framework_TestCase {
 
        /**
         * Note: we are overriding this method to remove the deprecated error
-        * @see https://bugzilla.wikimedia.org/show_bug.cgi?id=69505
+        * @see https://phabricator.wikimedia.org/T71505
         * @see https://github.com/sebastianbergmann/phpunit/issues/1292
         * @deprecated
         *
diff --git a/tests/phpunit/data/filecontentshasher/hash.svg b/tests/phpunit/data/filecontentshasher/hash.svg
new file mode 100644 (file)
index 0000000..44068ba
--- /dev/null
@@ -0,0 +1,110 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="200"
+   height="200"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.48.2 r9819"
+   sodipodi:docname="New document 1">
+  <defs
+     id="defs4" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="1.765"
+     inkscape:cx="101.66909"
+     inkscape:cy="64.929256"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     fit-margin-top="0"
+     fit-margin-left="0"
+     fit-margin-right="0"
+     fit-margin-bottom="0"
+     showborder="false"
+     inkscape:showpageshadow="false"
+     inkscape:window-width="1132"
+     inkscape:window-height="961"
+     inkscape:window-x="0"
+     inkscape:window-y="0"
+     inkscape:window-maximized="0" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(-193.20113,-609.30267)">
+    <g
+       id="g3829">
+      <g
+         transform="translate(-61.473095,237.81998)"
+         id="g3821">
+        <rect
+           style="fill:#000000;fill-opacity:1;stroke:none"
+           id="rect2998"
+           width="120.67989"
+           height="19.546741"
+           x="298.017"
+           y="434.23184"
+           ry="0" />
+        <rect
+           style="fill:#000000;fill-opacity:1;stroke:none"
+           id="rect2998-1"
+           width="120.67989"
+           height="19.546741"
+           x="290.65155"
+           y="488.76447"
+           ry="0" />
+        <rect
+           style="fill:#000000;fill-opacity:1;stroke:none"
+           id="rect2998-7"
+           width="183.60896"
+           height="19.546741"
+           x="384.33142"
+           y="-455.46609"
+           ry="0"
+           transform="matrix(-0.13731609,0.99052728,-1,0,0,0)" />
+        <rect
+           style="fill:#000000;fill-opacity:1;stroke:none"
+           id="rect2998-7-4"
+           width="183.60896"
+           height="19.546741"
+           x="384.04288"
+           y="-406.21848"
+           ry="0"
+           transform="matrix(-0.13731609,0.99052728,-1,0,0,0)" />
+      </g>
+      <rect
+         y="609.30267"
+         x="193.20113"
+         height="200"
+         width="200"
+         id="rect3827"
+         style="fill:none;stroke:none" />
+    </g>
+  </g>
+</svg>
diff --git a/tests/phpunit/data/filecontentshasher/primes.txt b/tests/phpunit/data/filecontentshasher/primes.txt
new file mode 100644 (file)
index 0000000..a2fe1fb
--- /dev/null
@@ -0,0 +1,105 @@
+                         The First 1,000 Primes
+                          (the 1,000th is 7919)
+         For more information on primes see http://primes.utm.edu/
+
+      2      3      5      7     11     13     17     19     23     29
+     31     37     41     43     47     53     59     61     67     71
+     73     79     83     89     97    101    103    107    109    113
+    127    131    137    139    149    151    157    163    167    173
+    179    181    191    193    197    199    211    223    227    229
+    233    239    241    251    257    263    269    271    277    281
+    283    293    307    311    313    317    331    337    347    349
+    353    359    367    373    379    383    389    397    401    409
+    419    421    431    433    439    443    449    457    461    463
+    467    479    487    491    499    503    509    521    523    541
+    547    557    563    569    571    577    587    593    599    601
+    607    613    617    619    631    641    643    647    653    659
+    661    673    677    683    691    701    709    719    727    733
+    739    743    751    757    761    769    773    787    797    809
+    811    821    823    827    829    839    853    857    859    863
+    877    881    883    887    907    911    919    929    937    941
+    947    953    967    971    977    983    991    997   1009   1013
+   1019   1021   1031   1033   1039   1049   1051   1061   1063   1069
+   1087   1091   1093   1097   1103   1109   1117   1123   1129   1151
+   1153   1163   1171   1181   1187   1193   1201   1213   1217   1223
+   1229   1231   1237   1249   1259   1277   1279   1283   1289   1291
+   1297   1301   1303   1307   1319   1321   1327   1361   1367   1373
+   1381   1399   1409   1423   1427   1429   1433   1439   1447   1451
+   1453   1459   1471   1481   1483   1487   1489   1493   1499   1511
+   1523   1531   1543   1549   1553   1559   1567   1571   1579   1583
+   1597   1601   1607   1609   1613   1619   1621   1627   1637   1657
+   1663   1667   1669   1693   1697   1699   1709   1721   1723   1733
+   1741   1747   1753   1759   1777   1783   1787   1789   1801   1811
+   1823   1831   1847   1861   1867   1871   1873   1877   1879   1889
+   1901   1907   1913   1931   1933   1949   1951   1973   1979   1987
+   1993   1997   1999   2003   2011   2017   2027   2029   2039   2053
+   2063   2069   2081   2083   2087   2089   2099   2111   2113   2129
+   2131   2137   2141   2143   2153   2161   2179   2203   2207   2213
+   2221   2237   2239   2243   2251   2267   2269   2273   2281   2287
+   2293   2297   2309   2311   2333   2339   2341   2347   2351   2357
+   2371   2377   2381   2383   2389   2393   2399   2411   2417   2423
+   2437   2441   2447   2459   2467   2473   2477   2503   2521   2531
+   2539   2543   2549   2551   2557   2579   2591   2593   2609   2617
+   2621   2633   2647   2657   2659   2663   2671   2677   2683   2687
+   2689   2693   2699   2707   2711   2713   2719   2729   2731   2741
+   2749   2753   2767   2777   2789   2791   2797   2801   2803   2819
+   2833   2837   2843   2851   2857   2861   2879   2887   2897   2903
+   2909   2917   2927   2939   2953   2957   2963   2969   2971   2999
+   3001   3011   3019   3023   3037   3041   3049   3061   3067   3079
+   3083   3089   3109   3119   3121   3137   3163   3167   3169   3181
+   3187   3191   3203   3209   3217   3221   3229   3251   3253   3257
+   3259   3271   3299   3301   3307   3313   3319   3323   3329   3331
+   3343   3347   3359   3361   3371   3373   3389   3391   3407   3413
+   3433   3449   3457   3461   3463   3467   3469   3491   3499   3511
+   3517   3527   3529   3533   3539   3541   3547   3557   3559   3571
+   3581   3583   3593   3607   3613   3617   3623   3631   3637   3643
+   3659   3671   3673   3677   3691   3697   3701   3709   3719   3727
+   3733   3739   3761   3767   3769   3779   3793   3797   3803   3821
+   3823   3833   3847   3851   3853   3863   3877   3881   3889   3907
+   3911   3917   3919   3923   3929   3931   3943   3947   3967   3989
+   4001   4003   4007   4013   4019   4021   4027   4049   4051   4057
+   4073   4079   4091   4093   4099   4111   4127   4129   4133   4139
+   4153   4157   4159   4177   4201   4211   4217   4219   4229   4231
+   4241   4243   4253   4259   4261   4271   4273   4283   4289   4297
+   4327   4337   4339   4349   4357   4363   4373   4391   4397   4409
+   4421   4423   4441   4447   4451   4457   4463   4481   4483   4493
+   4507   4513   4517   4519   4523   4547   4549   4561   4567   4583
+   4591   4597   4603   4621   4637   4639   4643   4649   4651   4657
+   4663   4673   4679   4691   4703   4721   4723   4729   4733   4751
+   4759   4783   4787   4789   4793   4799   4801   4813   4817   4831
+   4861   4871   4877   4889   4903   4909   4919   4931   4933   4937
+   4943   4951   4957   4967   4969   4973   4987   4993   4999   5003
+   5009   5011   5021   5023   5039   5051   5059   5077   5081   5087
+   5099   5101   5107   5113   5119   5147   5153   5167   5171   5179
+   5189   5197   5209   5227   5231   5233   5237   5261   5273   5279
+   5281   5297   5303   5309   5323   5333   5347   5351   5381   5387
+   5393   5399   5407   5413   5417   5419   5431   5437   5441   5443
+   5449   5471   5477   5479   5483   5501   5503   5507   5519   5521
+   5527   5531   5557   5563   5569   5573   5581   5591   5623   5639
+   5641   5647   5651   5653   5657   5659   5669   5683   5689   5693
+   5701   5711   5717   5737   5741   5743   5749   5779   5783   5791
+   5801   5807   5813   5821   5827   5839   5843   5849   5851   5857
+   5861   5867   5869   5879   5881   5897   5903   5923   5927   5939
+   5953   5981   5987   6007   6011   6029   6037   6043   6047   6053
+   6067   6073   6079   6089   6091   6101   6113   6121   6131   6133
+   6143   6151   6163   6173   6197   6199   6203   6211   6217   6221
+   6229   6247   6257   6263   6269   6271   6277   6287   6299   6301
+   6311   6317   6323   6329   6337   6343   6353   6359   6361   6367
+   6373   6379   6389   6397   6421   6427   6449   6451   6469   6473
+   6481   6491   6521   6529   6547   6551   6553   6563   6569   6571
+   6577   6581   6599   6607   6619   6637   6653   6659   6661   6673
+   6679   6689   6691   6701   6703   6709   6719   6733   6737   6761
+   6763   6779   6781   6791   6793   6803   6823   6827   6829   6833
+   6841   6857   6863   6869   6871   6883   6899   6907   6911   6917
+   6947   6949   6959   6961   6967   6971   6977   6983   6991   6997
+   7001   7013   7019   7027   7039   7043   7057   7069   7079   7103
+   7109   7121   7127   7129   7151   7159   7177   7187   7193   7207
+   7211   7213   7219   7229   7237   7243   7247   7253   7283   7297
+   7307   7309   7321   7331   7333   7349   7351   7369   7393   7411
+   7417   7433   7451   7457   7459   7477   7481   7487   7489   7499
+   7507   7517   7523   7529   7537   7541   7547   7549   7559   7561
+   7573   7577   7583   7589   7591   7603   7607   7621   7639   7643
+   7649   7669   7673   7681   7687   7691   7699   7703   7717   7723
+   7727   7741   7753   7757   7759   7789   7793   7817   7823   7829
+   7841   7853   7867   7873   7877   7879   7883   7901   7907   7919
+end.
diff --git a/tests/phpunit/includes/ConsecutiveParametersMatcher.php b/tests/phpunit/includes/ConsecutiveParametersMatcher.php
deleted file mode 100644 (file)
index adf74bb..0000000
+++ /dev/null
@@ -1,123 +0,0 @@
-<?php
-/*
- * This file is part of the PHPUnit_MockObject package.
- *
- * (c) Sebastian Bergmann <sebastian@phpunit.de>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-/**
- * Invocation matcher which looks for sets of specific parameters in the invocations.
- *
- * Checks the parameters of the incoming invocations, the parameter list is
- * checked against the defined constraints in $parameters. If the constraint
- * is met it will return true in matches().
- *
- * It takes a list of match groups and and increases a call index after each invocation.
- * So the first invocation uses the first group of constraints, the second the next and so on.
- */
-class PHPUnit_Framework_MockObject_Matcher_ConsecutiveParameters extends PHPUnit_Framework_MockObject_Matcher_StatelessInvocation
-{
-    /**
-     * @var array
-     */
-    private $_parameterGroups = array();
-
-    /**
-     * @var array
-     */
-    private $_invocations = array();
-
-    /**
-     * @param array $parameterGroups
-     */
-    public function __construct(array $parameterGroups)
-    {
-        foreach ($parameterGroups as $index => $parameters) {
-            foreach ($parameters as $parameter) {
-                if (!($parameter instanceof \PHPUnit_Framework_Constraint)) {
-                    $parameter = new \PHPUnit_Framework_Constraint_IsEqual($parameter);
-                }
-                $this->_parameterGroups[$index][] = $parameter;
-            }
-        }
-    }
-
-    /**
-     * @return string
-     */
-    public function toString()
-    {
-        $text = 'with consecutive parameters';
-
-        return $text;
-    }
-
-    /**
-     * @param  PHPUnit_Framework_MockObject_Invocation $invocation
-     * @return bool
-     */
-    public function matches(PHPUnit_Framework_MockObject_Invocation $invocation)
-    {
-        $this->_invocations[] = $invocation;
-        $callIndex            = count($this->_invocations) - 1;
-        $this->verifyInvocation($invocation, $callIndex);
-
-        return false;
-    }
-
-    public function verify()
-    {
-        foreach ($this->_invocations as $callIndex => $invocation) {
-            $this->verifyInvocation($invocation, $callIndex);
-        }
-    }
-
-    /**
-     * Verify a single invocation
-     *
-     * @param  PHPUnit_Framework_MockObject_Invocation      $invocation
-     * @param  int                                          $callIndex
-     * @throws PHPUnit_Framework_ExpectationFailedException
-     */
-    private function verifyInvocation(PHPUnit_Framework_MockObject_Invocation $invocation, $callIndex)
-    {
-
-        if (isset($this->_parameterGroups[$callIndex])) {
-            $parameters = $this->_parameterGroups[$callIndex];
-        } else {
-          // no parameter assertion for this call index
-            return;
-        }
-
-        if ($invocation === null) {
-            throw new PHPUnit_Framework_ExpectationFailedException(
-                'Mocked method does not exist.'
-            );
-        }
-
-        if (count($invocation->parameters) < count($parameters)) {
-            throw new PHPUnit_Framework_ExpectationFailedException(
-                sprintf(
-                    'Parameter count for invocation %s is too low.',
-                    $invocation->toString()
-                )
-            );
-        }
-
-        foreach ($parameters as $i => $parameter) {
-            $parameter->evaluate(
-                $invocation->parameters[$i],
-                sprintf(
-                    'Parameter %s for invocation #%d %s does not match expected ' .
-                    'value.',
-                    $i,
-                    $callIndex,
-                    $invocation->toString()
-                )
-            );
-        }
-    }
-}
index e89e36f..69dcd4c 100644 (file)
@@ -551,10 +551,10 @@ class GlobalTest extends MediaWikiTestCase {
 
        public static function provideMakeUrlIndexes() {
                return array(
+                       // Testcase for T30627
                        array(
-                               // just a regular :)
-                               'https://bugzilla.wikimedia.org/show_bug.cgi?id=28627',
-                               array( 'https://org.wikimedia.bugzilla./show_bug.cgi?id=28627' )
+                               'https://example.org/test.cgi?id=12345',
+                               array( 'https://org.example./test.cgi?id=12345' )
                        ),
                        array(
                                // mailtos are handled special
@@ -563,7 +563,7 @@ class GlobalTest extends MediaWikiTestCase {
                                array( 'mailto:org.wikimedia@wiki.' )
                        ),
 
-                       // file URL cases per bug 28627...
+                       // file URL cases per T30627...
                        array(
                                // three slashes: local filesystem path Unix-style
                                'file:///whatever/you/like.txt',
@@ -587,12 +587,12 @@ class GlobalTest extends MediaWikiTestCase {
                        // Those will survive the algorithm but with results that
                        // are less consistent.
 
-                       // protocol-relative URL cases per bug 29854...
+                       // protocol-relative URL cases per T31854...
                        array(
-                               '//bugzilla.wikimedia.org/show_bug.cgi?id=28627',
+                               '//example.org/test.cgi?id=12345',
                                array(
-                                       'http://org.wikimedia.bugzilla./show_bug.cgi?id=28627',
-                                       'https://org.wikimedia.bugzilla./show_bug.cgi?id=28627'
+                                       'http://org.example./test.cgi?id=12345',
+                                       'https://org.example./test.cgi?id=12345'
                                )
                        ),
                );
index 1c3e853..df54914 100644 (file)
@@ -114,7 +114,7 @@ class HtmlFormatterTest extends MediaWikiTestCase {
                                array(),
                                $removeTags, // Have some rules to trigger a DOM parse
                        ),
-                       // https://bugzilla.wikimedia.org/show_bug.cgi?id=53086
+                       // https://phabricator.wikimedia.org/T55086
                        array(
                                'Foo<sup id="cite_ref-1" class="reference"><a href="#cite_note-1">[1]</a></sup>'
                                        . ' <a href="/wiki/Bar" title="Bar" class="mw-redirect">Bar</a>',
index ea753e8..7ef44e7 100644 (file)
@@ -10,6 +10,9 @@
 class ImportTest extends MediaWikiLangTestCase {
 
        private function getInputStreamSource( $xml ) {
+               if ( ini_get( 'allow_url_fopen' ) != 1 ) {
+                       $this->markTestSkipped( 'bug 73283: this test needs allow_url_fopen to be enabled' );
+               }
                $file = 'data:application/xml,' . $xml;
                $status = ImportStreamSource::newFromFile( $file );
                if ( !$status->isGood() ) {
index f0d905e..fbb3982 100644 (file)
@@ -323,8 +323,6 @@ class OutputPageTest extends MediaWikiTestCase {
                                        array( 'Cookie', array( 'string-contains=phpsessid' ) ),
                                        array( 'Cookie', array( 'string-contains=phpsessid' ) ),
                                        array( 'Accept-Language', array( 'string-contains=en', 'string-contains=en' ) ),
-
-
                                ),
                                'Vary: Cookie, Accept-Language',
                                'X-Vary-Options: Cookie;string-contains=phpsessid,Accept-Language;string-contains=en',
@@ -359,6 +357,7 @@ class NullMessageBlobStore extends MessageBlobStore {
 
        public function updateMessage( $key ) {
        }
+
        public function clear() {
        }
 }
index 291ed31..7ec898f 100644 (file)
@@ -64,7 +64,16 @@ class StatusTest extends MediaWikiLangTestCase {
                $this->assertTrue( $status->ok );
                $status = Status::newFatal( 'foo', 1, 2 );
                $this->assertFalse( $status->ok );
-               $this->assertArrayEquals( array( array( 'type' => 'error', 'message' => 'foo', 'params' => array( 1, 2 ) ) ), $status->errors );
+               $this->assertArrayEquals(
+                       array(
+                               array(
+                                       'type' => 'error',
+                                       'message' => 'foo',
+                                       'params' => array( 1, 2 )
+                               )
+                       ),
+                       $status->errors
+               );
        }
 
        /**
index 9233416..f7ae08e 100644 (file)
@@ -62,7 +62,7 @@ class WikiMapTest extends MediaWikiLangTestCase {
        public function provideMakeForeignLink() {
                return array(
                        'unknown' => array( false, 'xyzzy', 'Foo' ),
-                       'enwiki' => array( '<a class="external" rel="nofollow" href="http://en.example.org/w/Foo">Foo</a>', 'enwiki', 'Foo' ),
+                       'enwiki' => array( '<a class="external" rel="nofollow" href="http://en.example.org/w/Foo">Foo</a>', 'enwiki', 'Foo' ),
                        'ruwiki' => array( '<a class="external" rel="nofollow" href="//ru.example.org/wiki/%D0%A4%D1%83">вар</a>', 'ruwiki', 'Фу', 'вар' ),
                );
        }
@@ -77,7 +77,7 @@ class WikiMapTest extends MediaWikiLangTestCase {
        public function provideForeignUserLink() {
                return array(
                        'unknown' => array( false, 'xyzzy', 'Foo' ),
-                       'enwiki' => array( '<a class="external" rel="nofollow" href="http://en.example.org/w/User:Foo">User:Foo</a>', 'enwiki', 'Foo' ),
+                       'enwiki' => array( '<a class="external" rel="nofollow" href="http://en.example.org/w/User:Foo">User:Foo</a>', 'enwiki', 'Foo' ),
                        'ruwiki' => array( '<a class="external" rel="nofollow" href="//ru.example.org/wiki/User:%D0%A4%D1%83">вар</a>', 'ruwiki', 'Фу', 'вар' ),
                );
        }
@@ -92,7 +92,7 @@ class WikiMapTest extends MediaWikiLangTestCase {
        public function provideGetForeignURL() {
                return array(
                        'unknown' => array( false, 'xyzzy', 'Foo' ),
-                       'enwiki' => array( 'http://en.example.org/w/Foo', 'enwiki', 'Foo' ),
+                       'enwiki' => array( 'http://en.example.org/w/Foo', 'enwiki', 'Foo' ),
                        'ruwiki with fragement' => array( '//ru.example.org/wiki/%D0%A4%D1%83#%D0%B2%D0%B0%D1%80', 'ruwiki', 'Фу', 'вар' ),
                );
        }
index 3babb97..63e0c8e 100644 (file)
@@ -92,7 +92,7 @@ class ActionTest extends MediaWikiTestCase {
        }
 
        public function testGetActionName_editredlinkWorkaround() {
-               // See https://bugzilla.wikimedia.org/show_bug.cgi?id=20966
+               // See https://phabricator.wikimedia.org/T22966
                $context = $this->getContext( 'editredlink' );
                $actionName = Action::getActionName( $context );
 
@@ -100,7 +100,7 @@ class ActionTest extends MediaWikiTestCase {
        }
 
        public function testGetActionName_historysubmitWorkaround() {
-               // See https://bugzilla.wikimedia.org/show_bug.cgi?id=20966
+               // See https://phabricator.wikimedia.org/T22966
                $context = $this->getContext( 'historysubmit' );
                $actionName = Action::getActionName( $context );
 
@@ -108,7 +108,7 @@ class ActionTest extends MediaWikiTestCase {
        }
 
        public function testGetActionName_revisiondeleteWorkaround() {
-               // See https://bugzilla.wikimedia.org/show_bug.cgi?id=20966
+               // See https://phabricator.wikimedia.org/T22966
                $context = $this->getContext( 'historysubmit' );
                $context->getRequest()->setVal( 'revisiondelete', true );
                $actionName = Action::getActionName( $context );
index 575efd6..e0488b7 100644 (file)
@@ -30,11 +30,11 @@ class ApiBlockTest extends ApiTestCase {
 
        /**
         * This test has probably always been broken and use an invalid token
-        * Bug tracking brokenness is https://bugzilla.wikimedia.org/35646
+        * Bug tracking brokenness is https://phabricator.wikimedia.org/T37646
         *
         * Root cause is https://gerrit.wikimedia.org/r/3434
         * Which made the Block/Unblock API to actually verify the token
-        * previously always considered valid (bug 34212).
+        * previously always considered valid (T36212).
         */
        public function testMakeNormalBlock() {
                $tokens = $this->getTokens();
index c852d72..40de254 100644 (file)
@@ -11,7 +11,7 @@
  * @todo Port the other Upload tests, and other API tests to this framework
  *
  * @todo Broken test, reports false errors from time to time.
- * See https://bugzilla.wikimedia.org/26169
+ * See https://phabricator.wikimedia.org/T28169
  *
  * @todo This is pretty sucky... needs to be prettified.
  *
index 0cb44e9..09c7c0c 100644 (file)
@@ -134,7 +134,7 @@ class ApiFormatPhpTest extends ApiFormatTestBase {
                } catch ( UsageException $ex ) {
                        ob_end_clean();
                        $this->assertSame(
-                               'This response cannot be represented using format=php. See https://bugzilla.wikimedia.org/show_bug.cgi?id=66776',
+                               'This response cannot be represented using format=php. See https://phabricator.wikimedia.org/T68776',
                                $ex->getMessage(),
                                'Expected exception'
                        );
index 4d1a936..fd287b5 100644 (file)
@@ -41,44 +41,6 @@ class RecentChangeTest extends MediaWikiTestCase {
                $this->assertEquals( $expected, $rc->getAttributes() );
        }
 
-       /**
-        * The testIrcMsgForAction* tests are supposed to cover the hacky
-        * LogFormatter::getIRCActionText / bug 34508
-        *
-        * Third parties bots listen to those messages. They are clever enough
-        * to fetch the i18n messages from the wiki and then analyze the IRC feed
-        * to reverse engineer the $1, $2 messages.
-        * One thing bots can not detect is when MediaWiki change the meaning of
-        * a message like what happened when we deployed 1.19. $1 became the user
-        * performing the action which broke basically all bots around.
-        *
-        * Should cover the following log actions (which are most commonly used by bots):
-        * - block/block
-        * - block/unblock
-        * - block/reblock
-        * - delete/delete
-        * - delete/restore
-        * - newusers/create
-        * - newusers/create2
-        * - newusers/autocreate
-        * - move/move
-        * - move/move_redir
-        * - protect/protect
-        * - protect/modifyprotect
-        * - protect/unprotect
-        * - protect/move_prot
-        * - upload/upload
-        * - merge/merge
-        * - import/upload
-        * - import/interwiki
-        *
-        * As well as the following Auto Edit Summaries:
-        * - blank
-        * - replace
-        * - rollback
-        * - undo
-        */
-
        /**
         * @covers RecentChange::parseParams
         */
index c4d87c2..59bfb03 100644 (file)
@@ -111,7 +111,7 @@ class CssContentTest extends JavaScriptContentTest {
                );
        }
 
-               public static function dataEquals() {
+       public static function dataEquals() {
                return array(
                        array( new CssContent( 'hallo' ), null, false ),
                        array( new CssContent( 'hallo' ), new CssContent( 'hallo' ), true ),
index 0ee2712..898fa53 100644 (file)
@@ -253,7 +253,7 @@ class JavaScriptContentTest extends TextContentTest {
         * @covers JavaScriptContent::updateRedirect
         * @dataProvider provideUpdateRedirect
         */
-       public function testUpdateRedirect( $oldText, $expectedText) {
+       public function testUpdateRedirect( $oldText, $expectedText ) {
                $this->setMwGlobals( array(
                        'wgServer' => '//example.org',
                        'wgScriptPath' => '/w/index.php',
index 090f439..9866ce1 100644 (file)
@@ -24,7 +24,7 @@ use MediaWikiTestCase;
 use Monolog\Logger;
 
 // not available in the version of phpunit mw uses, so copied into repo
-require_once __DIR__ . '/../../../ConsecutiveParametersMatcher.php';
+require_once __DIR__ . '/../../../phpunit/ConsecutiveParametersMatcher.php';
 
 class KafkaHandlerTest extends MediaWikiTestCase {
 
@@ -52,10 +52,10 @@ class KafkaHandlerTest extends MediaWikiTestCase {
                $produce = $this->getMockBuilder( 'Kafka\Produce' )
                        ->disableOriginalConstructor()
                        ->getMock();
-               $produce->expects($this->any())
-                       ->method('getAvailablePartitions')
-                       ->will($this->returnValue( array( 'A' ) ) );
-               $produce->expects($this->once())
+               $produce->expects( $this->any() )
+                       ->method( 'getAvailablePartitions' )
+                       ->will( $this->returnValue( array( 'A' ) ) );
+               $produce->expects( $this->once() )
                        ->method( 'setMessages' )
                        ->with( $expect, $this->anything(), $this->anything() );
 
index a618889..adcbe74 100644 (file)
@@ -145,4 +145,5 @@ class SwiftFileBackendTest extends MediaWikiTestCase {
                        )
                );
        }
-}
\ No newline at end of file
+}
+
index 681e368..ea3f862 100644 (file)
@@ -99,7 +99,7 @@ class FileBackendDBRepoWrapperTest extends MediaWikiTestCase {
                        ->will( $this->returnValue( '96246614d75ba1703bdfd5d7660bb57407aaf5d9' ) );
 
                $backendMock->expects( $this->once() )
-                       ->method( 'getFileContentsMulti')
+                       ->method( 'getFileContentsMulti' )
                        ->will( $this->returnValue( array( $sha1Path => 'foo' ) ) );
 
                $result = $wrapperMock->getFileContentsMulti( array( 'srcs' => array( $filenamePath ) ) );
index 551d3a7..679382b 100644 (file)
@@ -64,12 +64,12 @@ class MigrateFileRepoLayoutTest extends MediaWikiTestCase {
 
        protected function deleteFilesRecursively( $directory ) {
                foreach ( glob( $directory . '/*' ) as $file ) {
-               if ( is_dir( $file ) ) {
-                       $this->deleteFilesRecursively( $file );
-               } else {
-                       unlink( $file );
-               }
-               }
+                       if ( is_dir( $file ) ) {
+                               $this->deleteFilesRecursively( $file );
+                       } else {
+                               unlink( $file );
+                       }
+               }
 
                rmdir( $directory );
        }
@@ -103,7 +103,7 @@ class MigrateFileRepoLayoutTest extends MediaWikiTestCase {
                        . '/'
                        . substr( $sha1, 2, 1 )
                        . '/'
-                       . $sha1 ;
+                       . $sha1;
 
                $this->assertEquals( file_get_contents( $expectedOriginalFilepath ), $this->text, 'New sha1 file should be exist and have the right contents' );
 
index 844c9af..1816708 100644 (file)
@@ -307,6 +307,44 @@ class LogFormatterTest extends MediaWikiLangTestCase {
                );
        }
 
+       /**
+        * The testIrcMsgForAction* tests are supposed to cover the hacky
+        * LogFormatter::getIRCActionText / bug 34508
+        *
+        * Third parties bots listen to those messages. They are clever enough
+        * to fetch the i18n messages from the wiki and then analyze the IRC feed
+        * to reverse engineer the $1, $2 messages.
+        * One thing bots can not detect is when MediaWiki change the meaning of
+        * a message like what happened when we deployed 1.19. $1 became the user
+        * performing the action which broke basically all bots around.
+        *
+        * Should cover the following log actions (which are most commonly used by bots):
+        * - block/block
+        * - block/unblock
+        * - block/reblock
+        * - delete/delete
+        * - delete/restore
+        * - newusers/create
+        * - newusers/create2
+        * - newusers/autocreate
+        * - move/move
+        * - move/move_redir
+        * - protect/protect
+        * - protect/modifyprotect
+        * - protect/unprotect
+        * - protect/move_prot
+        * - upload/upload
+        * - merge/merge
+        * - import/upload
+        * - import/interwiki
+        *
+        * As well as the following Auto Edit Summaries:
+        * - blank
+        * - replace
+        * - rollback
+        * - undo
+        */
+
        /**
         * @covers LogFormatter::getIRCActionComment
         * @covers LogFormatter::getIRCActionText
@@ -462,13 +500,13 @@ class LogFormatterTest extends MediaWikiLangTestCase {
         */
        public function testIrcMsgForLogTypeProtect() {
                $protectParams = array(
-                       '[edit=sysop] (indefinite) ‎[move=sysop] (indefinite)'
+                       '4::description' => '[edit=sysop] (indefinite) ‎[move=sysop] (indefinite)'
                );
                $sep = $this->context->msg( 'colon-separator' )->text();
 
                # protect/protect
                $this->assertIRCComment(
-                       $this->context->msg( 'protectedarticle', 'SomeTitle ' . $protectParams[0] )
+                       $this->context->msg( 'protectedarticle', 'SomeTitle ' . $protectParams['4::description'] )
                                ->plain() . $sep . $this->user_comment,
                        'protect', 'protect',
                        $protectParams,
@@ -485,7 +523,7 @@ class LogFormatterTest extends MediaWikiLangTestCase {
 
                # protect/modify
                $this->assertIRCComment(
-                       $this->context->msg( 'modifiedarticleprotection', 'SomeTitle ' . $protectParams[0] )
+                       $this->context->msg( 'modifiedarticleprotection', 'SomeTitle ' . $protectParams['4::description'] )
                                ->plain() . $sep . $this->user_comment,
                        'protect', 'modify',
                        $protectParams,
index e88452b..f175482 100644 (file)
@@ -49,6 +49,7 @@ abstract class LogFormatterTestCase extends MediaWikiLangTestCase {
 
        private static function removeSomeHtml( $html ) {
                $html = str_replace( '&quot;', '"', $html );
+               $html = preg_replace( '/\xE2\x80[\x8E\x8F]/', '', $html ); // Strip lrm/rlm
                return trim( preg_replace( '/<(a|span)[^>]*>([^<]*)<\/\1>/', '$2', $html ) );
        }
 
index 611b2df..718c08c 100644 (file)
@@ -2,6 +2,365 @@
 
 class ProtectLogFormatterTest extends LogFormatterTestCase {
 
+       /**
+        * Provide different rows from the logging table to test
+        * for backward compatibility.
+        * Do not change the existing data, just add a new database row
+        */
+       public static function provideProtectLogDatabaseRows() {
+               return array(
+                       // Current format
+                       array(
+                               array(
+                                       'type' => 'protect',
+                                       'action' => 'protect',
+                                       'comment' => 'protect comment',
+                                       'namespace' => NS_MAIN,
+                                       'title' => 'ProtectPage',
+                                       'params' => array(
+                                               '4::description' => '[edit=sysop] (indefinite)[move=sysop] (indefinite)',
+                                               '5:bool:cascade' => false,
+                                               'details' => array(
+                                                       array(
+                                                               'type' => 'edit',
+                                                               'level' => 'sysop',
+                                                               'expiry' => 'infinity',
+                                                               'cascade' => false,
+                                                       ),
+                                                       array(
+                                                               'type' => 'move',
+                                                               'level' => 'sysop',
+                                                               'expiry' => 'infinity',
+                                                               'cascade' => false,
+                                                       ),
+                                               ),
+                                       ),
+                               ),
+                               array(
+                                       'text' => 'User protected ProtectPage [Edit=Allow only administrators] (indefinite) [Move=Allow only administrators] (indefinite)',
+                                       'api' => array(
+                                               'description' => '[edit=sysop] (indefinite)[move=sysop] (indefinite)',
+                                               'cascade' => false,
+                                               'details' => array(
+                                                       array(
+                                                               'type' => 'edit',
+                                                               'level' => 'sysop',
+                                                               'expiry' => 'infinite',
+                                                               'cascade' => false,
+                                                       ),
+                                                       array(
+                                                               'type' => 'move',
+                                                               'level' => 'sysop',
+                                                               'expiry' => 'infinite',
+                                                               'cascade' => false,
+                                                       ),
+                                               ),
+                                       ),
+                               ),
+                       ),
+
+                       // Current format with cascade
+                       array(
+                               array(
+                                       'type' => 'protect',
+                                       'action' => 'protect',
+                                       'comment' => 'protect comment',
+                                       'namespace' => NS_MAIN,
+                                       'title' => 'ProtectPage',
+                                       'params' => array(
+                                               '4::description' => '[edit=sysop] (indefinite)[move=sysop] (indefinite)',
+                                               '5:bool:cascade' => true,
+                                               'details' => array(
+                                                       array(
+                                                               'type' => 'edit',
+                                                               'level' => 'sysop',
+                                                               'expiry' => 'infinity',
+                                                               'cascade' => true,
+                                                       ),
+                                                       array(
+                                                               'type' => 'move',
+                                                               'level' => 'sysop',
+                                                               'expiry' => 'infinity',
+                                                               'cascade' => false,
+                                                       ),
+                                               ),
+                                       ),
+                               ),
+                               array(
+                                       'text' => 'User protected ProtectPage [Edit=Allow only administrators] (indefinite) [Move=Allow only administrators] (indefinite) [cascading]',
+                                       'api' => array(
+                                               'description' => '[edit=sysop] (indefinite)[move=sysop] (indefinite)',
+                                               'cascade' => true,
+                                               'details' => array(
+                                                       array(
+                                                               'type' => 'edit',
+                                                               'level' => 'sysop',
+                                                               'expiry' => 'infinite',
+                                                               'cascade' => true,
+                                                       ),
+                                                       array(
+                                                               'type' => 'move',
+                                                               'level' => 'sysop',
+                                                               'expiry' => 'infinite',
+                                                               'cascade' => false,
+                                                       ),
+                                               ),
+                                       ),
+                               ),
+                       ),
+
+                       // Legacy format
+                       array(
+                               array(
+                                       'type' => 'protect',
+                                       'action' => 'protect',
+                                       'comment' => 'protect comment',
+                                       'namespace' => NS_MAIN,
+                                       'title' => 'ProtectPage',
+                                       'params' => array(
+                                               '[edit=sysop] (indefinite)[move=sysop] (indefinite)',
+                                               '',
+                                       ),
+                               ),
+                               array(
+                                       'legacy' => true,
+                                       'text' => 'User protected ProtectPage [edit=sysop] (indefinite)[move=sysop] (indefinite)',
+                                       'api' => array(
+                                               'description' => '[edit=sysop] (indefinite)[move=sysop] (indefinite)',
+                                               'cascade' => false,
+                                       ),
+                               ),
+                       ),
+
+                       // Legacy format with cascade
+                       array(
+                               array(
+                                       'type' => 'protect',
+                                       'action' => 'protect',
+                                       'comment' => 'protect comment',
+                                       'namespace' => NS_MAIN,
+                                       'title' => 'ProtectPage',
+                                       'params' => array(
+                                               '[edit=sysop] (indefinite)[move=sysop] (indefinite)',
+                                               'cascade',
+                                       ),
+                               ),
+                               array(
+                                       'legacy' => true,
+                                       'text' => 'User protected ProtectPage [edit=sysop] (indefinite)[move=sysop] (indefinite) [cascading]',
+                                       'api' => array(
+                                               'description' => '[edit=sysop] (indefinite)[move=sysop] (indefinite)',
+                                               'cascade' => true,
+                                       ),
+                               ),
+                       ),
+               );
+       }
+
+
+       /**
+        * @dataProvider provideProtectLogDatabaseRows
+        */
+       public function testProtectLogDatabaseRows( $row, $extra ) {
+               $this->doTestLogFormatter( $row, $extra );
+       }
+
+       /**
+        * Provide different rows from the logging table to test
+        * for backward compatibility.
+        * Do not change the existing data, just add a new database row
+        */
+       public static function provideModifyLogDatabaseRows() {
+               return array(
+                       // Current format
+                       array(
+                               array(
+                                       'type' => 'protect',
+                                       'action' => 'modify',
+                                       'comment' => 'protect comment',
+                                       'namespace' => NS_MAIN,
+                                       'title' => 'ProtectPage',
+                                       'params' => array(
+                                               '4::description' => '[edit=sysop] (indefinite)[move=sysop] (indefinite)',
+                                               '5:bool:cascade' => false,
+                                               'details' => array(
+                                                       array(
+                                                               'type' => 'edit',
+                                                               'level' => 'sysop',
+                                                               'expiry' => 'infinity',
+                                                               'cascade' => false,
+                                                       ),
+                                                       array(
+                                                               'type' => 'move',
+                                                               'level' => 'sysop',
+                                                               'expiry' => 'infinity',
+                                                               'cascade' => false,
+                                                       ),
+                                               ),
+                                       ),
+                               ),
+                               array(
+                                       'text' => 'User changed protection level for ProtectPage [Edit=Allow only administrators] (indefinite) [Move=Allow only administrators] (indefinite)',
+                                       'api' => array(
+                                               'description' => '[edit=sysop] (indefinite)[move=sysop] (indefinite)',
+                                               'cascade' => false,
+                                               'details' => array(
+                                                       array(
+                                                               'type' => 'edit',
+                                                               'level' => 'sysop',
+                                                               'expiry' => 'infinite',
+                                                               'cascade' => false,
+                                                       ),
+                                                       array(
+                                                               'type' => 'move',
+                                                               'level' => 'sysop',
+                                                               'expiry' => 'infinite',
+                                                               'cascade' => false,
+                                                       ),
+                                               ),
+                                       ),
+                               ),
+                       ),
+
+                       // Current format with cascade
+                       array(
+                               array(
+                                       'type' => 'protect',
+                                       'action' => 'modify',
+                                       'comment' => 'protect comment',
+                                       'namespace' => NS_MAIN,
+                                       'title' => 'ProtectPage',
+                                       'params' => array(
+                                               '4::description' => '[edit=sysop] (indefinite)[move=sysop] (indefinite)',
+                                               '5:bool:cascade' => true,
+                                               'details' => array(
+                                                       array(
+                                                               'type' => 'edit',
+                                                               'level' => 'sysop',
+                                                               'expiry' => 'infinity',
+                                                               'cascade' => true,
+                                                       ),
+                                                       array(
+                                                               'type' => 'move',
+                                                               'level' => 'sysop',
+                                                               'expiry' => 'infinity',
+                                                               'cascade' => false,
+                                                       ),
+                                               ),
+                                       ),
+                               ),
+                               array(
+                                       'text' => 'User changed protection level for ProtectPage [Edit=Allow only administrators] (indefinite) [Move=Allow only administrators] (indefinite) [cascading]',
+                                       'api' => array(
+                                               'description' => '[edit=sysop] (indefinite)[move=sysop] (indefinite)',
+                                               'cascade' => true,
+                                               'details' => array(
+                                                       array(
+                                                               'type' => 'edit',
+                                                               'level' => 'sysop',
+                                                               'expiry' => 'infinite',
+                                                               'cascade' => true,
+                                                       ),
+                                                       array(
+                                                               'type' => 'move',
+                                                               'level' => 'sysop',
+                                                               'expiry' => 'infinite',
+                                                               'cascade' => false,
+                                                       ),
+                                               ),
+                                       ),
+                               ),
+                       ),
+
+                       // Legacy format
+                       array(
+                               array(
+                                       'type' => 'protect',
+                                       'action' => 'modify',
+                                       'comment' => 'protect comment',
+                                       'namespace' => NS_MAIN,
+                                       'title' => 'ProtectPage',
+                                       'params' => array(
+                                               '[edit=sysop] (indefinite)[move=sysop] (indefinite)',
+                                               '',
+                                       ),
+                               ),
+                               array(
+                                       'legacy' => true,
+                                       'text' => 'User changed protection level for ProtectPage [edit=sysop] (indefinite)[move=sysop] (indefinite)',
+                                       'api' => array(
+                                               'description' => '[edit=sysop] (indefinite)[move=sysop] (indefinite)',
+                                               'cascade' => false,
+                                       ),
+                               ),
+                       ),
+
+                       // Legacy format with cascade
+                       array(
+                               array(
+                                       'type' => 'protect',
+                                       'action' => 'modify',
+                                       'comment' => 'protect comment',
+                                       'namespace' => NS_MAIN,
+                                       'title' => 'ProtectPage',
+                                       'params' => array(
+                                               '[edit=sysop] (indefinite)[move=sysop] (indefinite)',
+                                               'cascade',
+                                       ),
+                               ),
+                               array(
+                                       'legacy' => true,
+                                       'text' => 'User changed protection level for ProtectPage [edit=sysop] (indefinite)[move=sysop] (indefinite) [cascading]',
+                                       'api' => array(
+                                               'description' => '[edit=sysop] (indefinite)[move=sysop] (indefinite)',
+                                               'cascade' => true,
+                                       ),
+                               ),
+                       ),
+               );
+       }
+
+
+       /**
+        * @dataProvider provideModifyLogDatabaseRows
+        */
+       public function testModifyLogDatabaseRows( $row, $extra ) {
+               $this->doTestLogFormatter( $row, $extra );
+       }
+
+       /**
+        * Provide different rows from the logging table to test
+        * for backward compatibility.
+        * Do not change the existing data, just add a new database row
+        */
+       public static function provideUnprotectLogDatabaseRows() {
+               return array(
+                       // Current format
+                       array(
+                               array(
+                                       'type' => 'protect',
+                                       'action' => 'unprotect',
+                                       'comment' => 'unprotect comment',
+                                       'namespace' => NS_MAIN,
+                                       'title' => 'ProtectPage',
+                                       'params' => array(),
+                               ),
+                               array(
+                                       'text' => 'User removed protection from ProtectPage',
+                                       'api' => array(),
+                               ),
+                       ),
+               );
+       }
+
+
+       /**
+        * @dataProvider provideUnprotectLogDatabaseRows
+        */
+       public function testUnprotectLogDatabaseRows( $row, $extra ) {
+               $this->doTestLogFormatter( $row, $extra );
+       }
+
        /**
         * Provide different rows from the logging table to test
         * for backward compatibility.
index 87ffd99..de1e153 100644 (file)
@@ -139,4 +139,24 @@ class GIFHandlerTest extends MediaWikiMediaTestCase {
                        ),
                );
        }
+
+       /**
+        * @param $filename string
+        * @param $expectedLength float
+        * @dataProvider provideGetLength
+        */
+       public function testGetLength( $filename, $expectedLength ) {
+               $file = $this->dataFile( $filename, 'image/gif' );
+               $actualLength = $file->getLength();
+               $this->assertEquals( $expectedLength, $actualLength, '', 0.00001 );
+       }
+
+       public function provideGetLength() {
+               return array(
+                       array( 'animated.gif', 2.4 ),
+                       array( 'animated-xmp.gif', 2.4 ),
+                       array( 'nonanimated', 0.0 ),
+                       array( 'Bishzilla_blink.gif', 1.4 ),
+               );
+       }
 }
index 36872a7..96ede90 100644 (file)
@@ -128,4 +128,24 @@ class PNGHandlerTest extends MediaWikiMediaTestCase {
                        ),
                );
        }
+
+       /**
+        * @param $filename string
+        * @param $expectedLength float
+        * @dataProvider provideGetLength
+        */
+       public function testGetLength( $filename, $expectedLength ) {
+               $file = $this->dataFile( $filename, 'image/png' );
+               $actualLength = $file->getLength();
+               $this->assertEquals( $expectedLength, $actualLength, '', 0.00001 );
+       }
+
+       public function provideGetLength() {
+               return array(
+                       array( 'Animated_PNG_example_bouncing_beach_ball.png', 1.5 ),
+                       array( 'Png-native-test.png', 0.0 ),
+                       array( 'greyscale-png.png', 0.0 ),
+                       array( '1bit-png.png', 0.0 ),
+               );
+       }
 }
index d36710a..285f280 100644 (file)
@@ -24,7 +24,7 @@ class WebPHandlerTest extends MediaWikiTestCase {
                        array( "\x52\x49\x46\x46\x90\x68\x01\x00\x57\x45\x42\x50\x56\x50\x38\x4C\x83\x68\x01\x00\x2F\x8F\x01\x4B\x10\x8D\x38\x6C\xDB\x46\x92\xE0\xE0\x82\x7B\x6C",
                                array( 'compression' => 'lossless', 'width' => 400, 'height' => 301 ) ),
                        array( "\x52\x49\x46\x46\x64\x5B\x00\x00\x57\x45\x42\x50\x56\x50\x38\x58\x0A\x00\x00\x00\x10\x00\x00\x00\x8F\x01\x00\x2C\x01\x00\x41\x4C\x50\x48\xE5\x0E",
-                               array( 'compression' => 'unknown', 'animated' => false, 'transparency' => true, 'width' => 400, 'height' => 301) ),
+                               array( 'compression' => 'unknown', 'animated' => false, 'transparency' => true, 'width' => 400, 'height' => 301 ) ),
                        array( "\x52\x49\x46\x46\xA8\x72\x00\x00\x57\x45\x42\x50\x56\x50\x38\x4C\x9B\x72\x00\x00\x2F\x81\x81\x62\x10\x8D\x40\x8C\x24\x39\x6E\x73\x73\x38\x01\x96",
                                array( 'compression' => 'lossless', 'width' => 386, 'height' => 395 ) ),
                        array( "\x52\x49\x46\x46\xE0\x42\x00\x00\x57\x45\x42\x50\x56\x50\x38\x58\x0A\x00\x00\x00\x10\x00\x00\x00\x81\x01\x00\x8A\x01\x00\x41\x4C\x50\x48\x56\x10",
@@ -50,9 +50,9 @@ class WebPHandlerTest extends MediaWikiTestCase {
                        array( "\x52\x49\x46\x46\x7A\x19\x03\x00\x57\x45\x42\x50\x56\x50\x38\x20\x6E\x19\x03\x00\xB2\xF8\x09\x9D\x01\x2A\x00\x05\xD0\x02\x3E\xAD\x46\x99\x4A\xA5",
                                array( 'compression' => 'lossy', 'width' => 1280, 'height' => 720 ) ),
                        array( "\x52\x49\x46\x46\x44\xB3\x02\x00\x57\x45\x42\x50\x56\x50\x38\x20\x38\xB3\x02\x00\x52\x57\x06\x9D\x01\x2A\x00\x04\x04\x03\x3E\xA5\x44\x96\x49\x26",
-                               array( 'compression' => 'lossy', 'width' => 1024, 'height' => 772) ),
+                               array( 'compression' => 'lossy', 'width' => 1024, 'height' => 772 ) ),
                        array( "\x52\x49\x46\x46\x02\x43\x01\x00\x57\x45\x42\x50\x56\x50\x38\x20\xF6\x42\x01\x00\x12\xC0\x05\x9D\x01\x2A\x00\x04\xF0\x02\x3E\x79\x34\x93\x47\xA4",
-                               array( 'compression' => 'lossy', 'width' => 1024, 'height' => 752) ),
+                               array( 'compression' => 'lossy', 'width' => 1024, 'height' => 752 ) ),
 
                        // Animated file from https://groups.google.com/a/chromium.org/d/topic/blink-dev/Y8tRC4mdQz8/discussion
                        array( "\x52\x49\x46\x46\xD0\x0B\x02\x00\x57\x45\x42\x50\x56\x50\x38\x58\x0A\x00\x00\x00\x12\x00\x00\x00\x3F\x01\x00\x3F\x01\x00\x41\x4E",
index 345fd0a..1ebba1a 100644 (file)
@@ -193,7 +193,7 @@ class PreprocessorTest extends MediaWikiTestCase {
        }
 
        /**
-        * Tests from Bug 28642 · https://bugzilla.wikimedia.org/28642
+        * Tests from T30642 · https://phabricator.wikimedia.org/T30642
         */
        public static function provideHeadings() {
                // @codingStandardsIgnoreStart Ignore Generic.Files.LineLength.TooLong
diff --git a/tests/phpunit/includes/phpunit/ConsecutiveParametersMatcher.php b/tests/phpunit/includes/phpunit/ConsecutiveParametersMatcher.php
new file mode 100644 (file)
index 0000000..8de467f
--- /dev/null
@@ -0,0 +1,124 @@
+<?php
+// @codingStandardsIgnoreFile
+/*
+ * This file is part of the PHPUnit_MockObject package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Invocation matcher which looks for sets of specific parameters in the invocations.
+ *
+ * Checks the parameters of the incoming invocations, the parameter list is
+ * checked against the defined constraints in $parameters. If the constraint
+ * is met it will return true in matches().
+ *
+ * It takes a list of match groups and and increases a call index after each invocation.
+ * So the first invocation uses the first group of constraints, the second the next and so on.
+ */
+class PHPUnit_Framework_MockObject_Matcher_ConsecutiveParameters extends PHPUnit_Framework_MockObject_Matcher_StatelessInvocation
+{
+    /**
+     * @var array
+     */
+    private $_parameterGroups = array();
+
+    /**
+     * @var array
+     */
+    private $_invocations = array();
+
+    /**
+     * @param array $parameterGroups
+     */
+    public function __construct(array $parameterGroups)
+    {
+        foreach ($parameterGroups as $index => $parameters) {
+            foreach ($parameters as $parameter) {
+                if (!($parameter instanceof \PHPUnit_Framework_Constraint)) {
+                    $parameter = new \PHPUnit_Framework_Constraint_IsEqual($parameter);
+                }
+                $this->_parameterGroups[$index][] = $parameter;
+            }
+        }
+    }
+
+    /**
+     * @return string
+     */
+    public function toString()
+    {
+        $text = 'with consecutive parameters';
+
+        return $text;
+    }
+
+    /**
+     * @param  PHPUnit_Framework_MockObject_Invocation $invocation
+     * @return bool
+     */
+    public function matches(PHPUnit_Framework_MockObject_Invocation $invocation)
+    {
+        $this->_invocations[] = $invocation;
+        $callIndex            = count($this->_invocations) - 1;
+        $this->verifyInvocation($invocation, $callIndex);
+
+        return false;
+    }
+
+    public function verify()
+    {
+        foreach ($this->_invocations as $callIndex => $invocation) {
+            $this->verifyInvocation($invocation, $callIndex);
+        }
+    }
+
+    /**
+     * Verify a single invocation
+     *
+     * @param  PHPUnit_Framework_MockObject_Invocation      $invocation
+     * @param  int                                          $callIndex
+     * @throws PHPUnit_Framework_ExpectationFailedException
+     */
+    private function verifyInvocation(PHPUnit_Framework_MockObject_Invocation $invocation, $callIndex)
+    {
+
+        if (isset($this->_parameterGroups[$callIndex])) {
+            $parameters = $this->_parameterGroups[$callIndex];
+        } else {
+          // no parameter assertion for this call index
+            return;
+        }
+
+        if ($invocation === null) {
+            throw new PHPUnit_Framework_ExpectationFailedException(
+                'Mocked method does not exist.'
+            );
+        }
+
+        if (count($invocation->parameters) < count($parameters)) {
+            throw new PHPUnit_Framework_ExpectationFailedException(
+                sprintf(
+                    'Parameter count for invocation %s is too low.',
+                    $invocation->toString()
+                )
+            );
+        }
+
+        foreach ($parameters as $i => $parameter) {
+            $parameter->evaluate(
+                $invocation->parameters[$i],
+                sprintf(
+                    'Parameter %s for invocation #%d %s does not match expected ' .
+                    'value.',
+                    $i,
+                    $callIndex,
+                    $invocation->toString()
+                )
+            );
+        }
+    }
+}
diff --git a/tests/phpunit/includes/phpunit/LICENSE b/tests/phpunit/includes/phpunit/LICENSE
new file mode 100644 (file)
index 0000000..fe178b0
--- /dev/null
@@ -0,0 +1,33 @@
+PHPUnit
+
+Copyright (c) 2001-2014, Sebastian Bergmann <sebastian@phpunit.de>.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+ * Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+
+ * 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.
+
+ * Neither the name of Sebastian Bergmann nor the names of his
+   contributors may be used to endorse or promote products derived
+   from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS 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 THE
+COPYRIGHT OWNER OR CONTRIBUTORS 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.
diff --git a/tests/phpunit/includes/phpunit/README b/tests/phpunit/includes/phpunit/README
new file mode 100644 (file)
index 0000000..3ec3fd9
--- /dev/null
@@ -0,0 +1,2 @@
+This directory contains classes duplicated from new versions of phpunit
+that also work in the older php 3.7.37 used by wmf CI servers.
index 13c2838..df9b552 100644 (file)
@@ -207,7 +207,12 @@ class SpecialSearchTestMockResultSet extends SearchResultSet {
        protected $results;
        protected $suggestion;
 
-       public function __construct( $suggestion = null, $rewrittenQuery = null, array $results = array(), $containedSyntax = false) {
+       public function __construct(
+               $suggestion = null,
+               $rewrittenQuery = null,
+               array $results = array(),
+               $containedSyntax = false
+       ) {
                $this->suggestion = $suggestion;
                $this->rewrittenQuery = $rewrittenQuery;
                $this->results = $results;
index 52c242c..d63af9a 100644 (file)
@@ -19,6 +19,10 @@ class AvroValidatorTest extends PHPUnit_Framework_TestCase {
 
        public function getErrorsProvider() {
                $stringSchema = AvroSchema::parse( json_encode( array( 'type' => 'string' ) ) );
+               $stringArraySchema = AvroSchema::parse( json_encode( array(
+                       'type' => 'array',
+                       'items' => 'string',
+               ) ) );
                $recordSchema = AvroSchema::parse( json_encode( array(
                        'type' => 'record',
                        'name' => 'ut',
@@ -80,6 +84,18 @@ class AvroValidatorTest extends PHPUnit_Framework_TestCase {
                                        )
                                ) )
                        ),
+                       array(
+                               'Empty array is accepted',
+                               $stringArraySchema, array(), array()
+                       ),
+                       array(
+                               'correct array element accepted',
+                               $stringArraySchema, array( 'fizzbuzz' ), array()
+                       ),
+                       array(
+                               'incorrect array element rejected',
+                               $stringArraySchema, array( '12', 34 ), array( 'Expected string, but recieved integer' )
+                       ),
                );
        }
 
index a2b35f3..4684658 100644 (file)
@@ -23,7 +23,7 @@ class BatchRowUpdateTest extends MediaWikiTestCase {
                $writer->write( $updates );
        }
 
-       static protected function mockUpdate( array $changes ) {
+       protected static function mockUpdate( array $changes ) {
                static $i = 0;
                return array(
                        'primaryKey' => array( 'event_id' => $i++ ),
@@ -53,7 +53,7 @@ class BatchRowUpdateTest extends MediaWikiTestCase {
                $this->assertEquals( count( $response ) - 1, $pos );
        }
 
-       static public function provider_readerGetPrimaryKey() {
+       public static function provider_readerGetPrimaryKey() {
                $row = array(
                        'id_field' => 42,
                        'some_col' => 'dvorak',
@@ -84,7 +84,7 @@ class BatchRowUpdateTest extends MediaWikiTestCase {
                $this->assertEquals( $expected, $reader->extractPrimaryKeys( (object) $row ), $message );
        }
 
-       static public function provider_readerSetFetchColumns() {
+       public static function provider_readerSetFetchColumns() {
                return array(
 
                        array(
@@ -136,7 +136,7 @@ class BatchRowUpdateTest extends MediaWikiTestCase {
                $reader->rewind();
        }
 
-       static public function provider_readerSelectConditions() {
+       public static function provider_readerSelectConditions() {
                return array(
 
                        array(
diff --git a/tests/phpunit/includes/utils/FileContentsHasherTest.php b/tests/phpunit/includes/utils/FileContentsHasherTest.php
new file mode 100644 (file)
index 0000000..a03e1fc
--- /dev/null
@@ -0,0 +1,55 @@
+<?php
+
+/**
+ * @covers FileContentsHasherTest
+ */
+class FileContentsHasherTest extends MediaWikiTestCase {
+
+       public function provideSingleFile() {
+               return array_map( function ( $file ) {
+                       return array( $file, file_get_contents( $file ) );
+               }, glob( __DIR__ . '/../../data/filecontentshasher/*.*' ) );
+       }
+
+       public function provideMultipleFiles() {
+               return array(
+                       array( $this->provideSingleFile() )
+               );
+       }
+
+       /**
+        * @covers FileContentsHasher::getFileContentHash
+        * @covers FileContentsHasher::getFileContentsHashInternal
+        * @dataProvider provideSingleFile
+        */
+       public function testSingleFileHash( $fileName, $contents ) {
+               foreach ( array( 'md4', 'md5' ) as $algo ) {
+                       $expectedHash = hash( $algo, $contents );
+                       $actualHash = FileContentsHasher::getFileContentsHash( $fileName, $algo );
+                       $this->assertEquals( $expectedHash, $actualHash );
+                       $actualHashRepeat = FileContentsHasher::getFileContentsHash( $fileName, $algo );
+                       $this->assertEquals( $expectedHash, $actualHashRepeat );
+               }
+       }
+
+       /**
+        * @covers FileContentsHasher::getFileContentHash
+        * @covers FileContentsHasher::getFileContentsHashInternal
+        * @dataProvider provideMultipleFiles
+        */
+       public function testMultipleFileHash( $files ) {
+               $fileNames = array();
+               $hashes = array();
+               foreach ( $files as $fileInfo ) {
+                       list( $fileName, $contents ) = $fileInfo;
+                       $fileNames[] = $fileName;
+                       $hashes[] = md5( $contents );
+               }
+
+               $expectedHash = md5( implode( '', $hashes ) );
+               $actualHash = FileContentsHasher::getFileContentsHash( $fileNames, 'md5' );
+               $this->assertEquals( $expectedHash, $actualHash );
+               $actualHashRepeat = FileContentsHasher::getFileContentsHash( $fileNames, 'md5' );
+               $this->assertEquals( $expectedHash, $actualHashRepeat );
+       }
+}
diff --git a/tests/phpunit/languages/utils/CLDRPluralRuleEvaluatorTest.php b/tests/phpunit/languages/utils/CLDRPluralRuleEvaluatorTest.php
deleted file mode 100644 (file)
index 8e3b114..0000000
+++ /dev/null
@@ -1,151 +0,0 @@
-<?php
-/**
- * @author Niklas Laxström
- * @file
- */
-
-/**
- * @covers CLDRPluralRuleEvaluator
- */
-class CLDRPluralRuleEvaluatorTest extends MediaWikiTestCase {
-       /**
-        * @dataProvider validTestCases
-        */
-       function testValidRules( $expected, $rules, $number, $comment ) {
-               $result = CLDRPluralRuleEvaluator::evaluate( $number, (array)$rules );
-               $this->assertEquals( $expected, $result, $comment );
-       }
-
-       /**
-        * @dataProvider invalidTestCases
-        * @expectedException CLDRPluralRuleError
-        */
-       function testInvalidRules( $rules, $comment ) {
-               CLDRPluralRuleEvaluator::evaluate( 1, (array)$rules );
-       }
-
-       function validTestCases() {
-               $tests = array(
-                       # expected, rule, number, comment
-                       array( 0, 'n is 1', 1, 'integer number and is' ),
-                       array( 0, 'n is 1', "1", 'string integer number and is' ),
-                       array( 0, 'n is 1', 1.0, 'float number and is' ),
-                       array( 0, 'n is 1', "1.0", 'string float number and is' ),
-                       array( 1, 'n is 1', 1.1, 'float number and is' ),
-                       array( 1, 'n is 1', 2, 'float number and is' ),
-
-                       array( 0, 'n in 1,3,5', 3, '' ),
-                       array( 1, 'n not in 1,3,5', 5, '' ),
-
-                       array( 1, 'n in 1,3,5', 2, '' ),
-                       array( 0, 'n not in 1,3,5', 4, '' ),
-
-                       array( 0, 'n in 1..3', 2, '' ),
-                       array( 0, 'n in 1..3', 3, 'in is inclusive' ),
-                       array( 1, 'n in 1..3', 0, '' ),
-
-                       array( 1, 'n not in 1..3', 2, '' ),
-                       array( 1, 'n not in 1..3', 3, 'in is inclusive' ),
-                       array( 0, 'n not in 1..3', 0, '' ),
-
-                       array( 1, 'n is not 1 and n is not 2 and n is not 3', 1, 'and relation' ),
-                       array( 0, 'n is not 1 and n is not 2 and n is not 4', 3, 'and relation' ),
-
-                       array( 0, 'n is not 1 or n is 1', 1, 'or relation' ),
-                       array( 1, 'n is 1 or n is 2', 3, 'or relation' ),
-
-                       array( 0, 'n              is      1', 1, 'extra whitespace' ),
-
-                       array( 0, 'n mod 3 is 1', 7, 'mod' ),
-                       array( 0, 'n mod 3 is not 1', 4.3, 'mod with floats' ),
-
-                       array( 0, 'n within 1..3', 2, 'within with integer' ),
-                       array( 0, 'n within 1..3', 2.5, 'within with float' ),
-                       array( 0, 'n in 1..3', 2, 'in with integer' ),
-                       array( 1, 'n in 1..3', 2.5, 'in with float' ),
-
-                       array( 0, 'n in 3 or n is 4 and n is 5', 3, 'and binds more tightly than or' ),
-                       array( 1, 'n is 3 or n is 4 and n is 5', 4, 'and binds more tightly than or' ),
-
-                       array( 0, 'n mod 10 in 3..4,9 and n mod 100 not in 10..19,70..79,90..99', 24, 'breton rule' ),
-                       array( 1, 'n mod 10 in 3..4,9 and n mod 100 not in 10..19,70..79,90..99', 25, 'breton rule' ),
-
-                       array( 0, 'n within 0..2 and n is not 2', 0, 'french rule' ),
-                       array( 0, 'n within 0..2 and n is not 2', 1, 'french rule' ),
-                       array( 0, 'n within 0..2 and n is not 2', 1.2, 'french rule' ),
-                       array( 1, 'n within 0..2 and n is not 2', 2, 'french rule' ),
-
-                       array( 1, 'n in 3..10,13..19', 2, 'scottish rule - ranges with comma' ),
-                       array( 0, 'n in 3..10,13..19', 4, 'scottish rule - ranges with comma' ),
-                       array( 1, 'n in 3..10,13..19', 12.999, 'scottish rule - ranges with comma' ),
-                       array( 0, 'n in 3..10,13..19', 13, 'scottish rule - ranges with comma' ),
-
-                       array( 0, '5 mod 3 is n', 2, 'n as result of mod - no need to pass' ),
-
-                       # Revision 33 new operand examples
-                       # expected, rule, number, comment
-                       array( 0, 'i is 1', '1.00', 'new operand i' ),
-                       array( 0, 'v is 2', '1.00', 'new operand v' ),
-                       array( 0, 'w is 0', '1.00', 'new operand w' ),
-                       array( 0, 'f is 0', '1.00', 'new operand f' ),
-                       array( 0, 't is 0', '1.00', 'new operand t' ),
-
-                       array( 0, 'i is 1', '1.30', 'new operand i' ),
-                       array( 0, 'v is 2', '1.30', 'new operand v' ),
-                       array( 0, 'w is 1', '1.30', 'new operand w' ),
-                       array( 0, 'f is 30', '1.30', 'new operand f' ),
-                       array( 0, 't is 3', '1.30', 'new operand t' ),
-
-                       array( 0, 'i is 1', '1.03', 'new operand i' ),
-                       array( 0, 'v is 2', '1.03', 'new operand v' ),
-                       array( 0, 'w is 2', '1.03', 'new operand w' ),
-                       array( 0, 'f is 3', '1.03', 'new operand f' ),
-                       array( 0, 't is 3', '1.03', 'new operand t' ),
-
-                       # Revision 33 new operator aliases
-                       # expected, rule, number, comment
-                       array( 0, 'n % 3 is 1', 7, 'new % operator' ),
-                       array( 0, 'n = 1,3,5', 3, 'new = operator' ),
-                       array( 1, 'n != 1,3,5', 5, 'new != operator' ),
-
-                       # Revision 33 samples
-                       # expected, rule, number, comment
-                       // @codingStandardsIgnoreStart Ignore Generic.Files.LineLength.TooLong
-                       array( 0, 'n in 1,3,5@integer 3~10, 103~110, 1003, … @decimal 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 103.0, 1003.0, …', 3, 'samples' ),
-                       // @codingStandardsIgnoreEnd
-
-                       # Revision 33 some test cases from CLDR
-                       array( 0, 'i = 1 and v = 0 or i = 0 and t = 1', '0.1', 'pt one' ),
-                       array( 0, 'i = 1 and v = 0 or i = 0 and t = 1', '0.01', 'pt one' ),
-                       array( 0, 'i = 1 and v = 0 or i = 0 and t = 1', '0.10', 'pt one' ),
-                       array( 0, 'i = 1 and v = 0 or i = 0 and t = 1', '0.010', 'pt one' ),
-                       array( 0, 'i = 1 and v = 0 or i = 0 and t = 1', '0.100', 'pt one' ),
-                       array( 1, 'i = 1 and v = 0 or i = 0 and t = 1', '0.0', 'pt other' ),
-                       array( 1, 'i = 1 and v = 0 or i = 0 and t = 1', '0.2', 'pt other' ),
-                       array( 1, 'i = 1 and v = 0 or i = 0 and t = 1', '10.0', 'pt other' ),
-                       array( 1, 'i = 1 and v = 0 or i = 0 and t = 1', '100.0', 'pt other' ),
-                       // @codingStandardsIgnoreStart Ignore Generic.Files.LineLength.TooLong
-                       array( 0, 'v = 0 and i % 10 = 2..4 and i % 100 != 12..14 or f % 10 = 2..4 and f % 100 != 12..14', '2', 'bs few' ),
-                       array( 0, 'v = 0 and i % 10 = 2..4 and i % 100 != 12..14 or f % 10 = 2..4 and f % 100 != 12..14', '4', 'bs few' ),
-                       array( 0, 'v = 0 and i % 10 = 2..4 and i % 100 != 12..14 or f % 10 = 2..4 and f % 100 != 12..14', '22', 'bs few' ),
-                       array( 0, 'v = 0 and i % 10 = 2..4 and i % 100 != 12..14 or f % 10 = 2..4 and f % 100 != 12..14', '102', 'bs few' ),
-                       array( 0, 'v = 0 and i % 10 = 2..4 and i % 100 != 12..14 or f % 10 = 2..4 and f % 100 != 12..14', '0.2', 'bs few' ),
-                       array( 0, 'v = 0 and i % 10 = 2..4 and i % 100 != 12..14 or f % 10 = 2..4 and f % 100 != 12..14', '0.4', 'bs few' ),
-                       array( 0, 'v = 0 and i % 10 = 2..4 and i % 100 != 12..14 or f % 10 = 2..4 and f % 100 != 12..14', '10.2', 'bs few' ),
-                       array( 1, 'v = 0 and i % 10 = 2..4 and i % 100 != 12..14 or f % 10 = 2..4 and f % 100 != 12..14', '10.0', 'bs other' ),
-                       // @codingStandardsIgnoreEnd
-               );
-
-               return $tests;
-       }
-
-       function invalidTestCases() {
-               $tests = array(
-                       array( 'n mod mod 5 is 1', 'mod mod' ),
-                       array( 'n', 'just n' ),
-                       array( 'n is in 5', 'is in' ),
-               );
-
-               return $tests;
-       }
-}
index e2fc824..454e9c1 100644 (file)
@@ -816,7 +816,10 @@ class MaintenanceTest extends MediaWikiTestCase {
         */
        public function testGetConfig() {
                $this->assertInstanceOf( 'Config', $this->m->getConfig() );
-               $this->assertSame( ConfigFactory::getDefaultInstance()->makeConfig( 'main' ), $this->m->getConfig() );
+               $this->assertSame(
+                       ConfigFactory::getDefaultInstance()->makeConfig( 'main' ),
+                       $this->m->getConfig()
+               );
        }
 
        /**
index 587d6d0..5591aa1 100755 (executable)
@@ -42,14 +42,28 @@ class PHPUnitMaintClass extends Maintenance {
                        false, # not required
                        false # no arg needed
                );
-               $this->addOption( 'regex', 'Only run parser tests that match the given regex.', false, true );
+               $this->addOption(
+                       'regex',
+                       'Only run parser tests that match the given regex.',
+                       false,
+                       true
+               );
                $this->addOption( 'file', 'File describing parser tests.', false, true );
                $this->addOption( 'use-filebackend', 'Use filebackend', false, true );
                $this->addOption( 'use-bagostuff', 'Use bagostuff', false, true );
                $this->addOption( 'use-jobqueue', 'Use jobqueue', false, true );
-               $this->addOption( 'keep-uploads', 'Re-use the same upload directory for each test, don\'t delete it.', false, false );
+               $this->addOption(
+                       'keep-uploads',
+                       'Re-use the same upload directory for each test, don\'t delete it.',
+                       false,
+                       false
+               );
                $this->addOption( 'use-normal-tables', 'Use normal DB tables.', false, false );
-               $this->addOption( 'reuse-db', 'Init DB only if tables are missing and keep after finish.', false, false );
+               $this->addOption(
+                       'reuse-db', 'Init DB only if tables are missing and keep after finish.',
+                       false,
+                       false
+               );
        }
 
        public function finalSetup() {
@@ -66,11 +80,21 @@ class PHPUnitMaintClass extends Maintenance {
                // wfWarn should cause tests to fail
                $wgDevelopmentWarnings = true;
 
+               // Make sure all caches and stashes are either disabled or use
+               // in-process cache only to prevent tests from using any preconfigured
+               // cache meant for the local wiki from outside the test run.
+               // See also MediaWikiTestCase::run() which mocks CACHE_DB and APC.
+
+               // Disabled in DefaultSettings, override local settings
+               $wgMainWANCache =
                $wgMainCacheType = CACHE_NONE;
-               $wgMainWANCache = CACHE_NONE;
-               $wgMessageCacheType = CACHE_NONE;
-               $wgParserCacheType = CACHE_NONE;
-               $wgLanguageConverterCacheType = CACHE_NONE;
+               // Uses CACHE_ANYTHING in DefaultSettings, use hash instead of db
+               $wgMessageCacheType =
+               $wgParserCacheType =
+               $wgSessionCacheType =
+               $wgLanguageConverterCacheType = 'hash';
+               // Uses db-replicated in DefaultSettings
+               $wgMainStash = 'hash';
 
                $wgUseDatabaseMessages = false; # Set for future resets
 
index 1acbc24..d93dafb 100644 (file)
@@ -26,6 +26,8 @@ phpunit.php enables colors for other OSs at runtime
                </testsuite>
                <testsuite name="skins">
                        <directory>skins</directory>
+                       <directory>structure</directory>
+                       <file>suites/LessTestSuite.php</file>
                </testsuite>
                <!-- As there is a class Maintenance, we cannot use the
                     name "maintenance" directly -->
index 032551d..1726a48 100644 (file)
@@ -5,7 +5,7 @@
         */
 
        var text, ipv4,
-               simpleMDYDatesInMDY, simpleMDYDatesInDMY, oldMDYDates, complexMDYDates, clobberedDates, MYDates, YDates,
+               simpleMDYDatesInMDY, simpleMDYDatesInDMY, oldMDYDates, complexMDYDates, clobberedDates, MYDates, YDates, ISODates,
                currencyData, transformedCurrencyData;
 
        QUnit.module( 'jquery.tablesorter.parsers', QUnit.newMwEnvironment( {
        ];
        parserTest( 'Y Dates', 'date', YDates );
 
+       ISODates = [
+               [ '2000',               false, 946684800000, 'Plain 4-digit year' ],
+               [ '2000-01',            false, 946684800000, 'Year with month' ],
+               [ '2000-01-01', true, 946684800000, 'Year with month and day' ],
+               [ '2000-13-01', true, 0, 'Non existant month' ],
+               [ '2000-01-32', true, 0, 'Non existant day' ],
+               [ '2000-01-01T12:30:30',                true, 946729830000, 'Date with a time' ],
+               [ '2000-01-01T12:30:30Z',       true, 946729830000, 'Date with a UTC+0 time' ],
+               [ '2000-01-01T24:30:30Z',       true, 0, 'Date with invalid hours' ],
+               [ '2000-01-01T12:60:30Z',       true, 0, 'Date with invalid minutes' ],
+               [ '2000-01-01T12:30:61Z',       true, 0, 'Date with invalid amount of seconds' ],
+               [ '2000-01-01T23:59:59Z',       true, 946771199000, 'Edges of time' ],
+               [ '2000-01-01T12:30:30.111Z',   true, 946729830111, 'Date with milliseconds' ],
+               [ '2000-01-01T12:30:30.11111Z', true, 946729830111, 'Date with too high precision' ],
+               [ '2000-01-01T12:30:30,111Z',   true, 0, 'Date with milliseconds and , separator' ],
+               [ '2000-01-01T12:30:30+01:00',  true, 946726230000, 'Date time in UTC+1' ],
+               [ '2000-01-01T12:30:30+01:30',  true, 946724430000, 'Date time in UTC+1:30' ],
+               [ '2000-01-01T12:30:30-01:00',  true, 946733430000, 'Date time in UTC-1' ],
+               [ '2000-01-01T12:30:30-01:30',  true, 946735230000, 'Date time in UTC-1:30' ],
+               [ '2000-01-01T12:30:30.111+01:00', true, 946726230111, 'Date time and milliseconds in UTC+1 ' ]
+               /* Disable testcases, because behavior is browser dependant */
+               /*
+               [ '2000-11-31', true, 0, '31 days in 30 day month' ],
+               [ '50-01-01',   false, -60589296000000, 'Year with just two digits' ],
+               [ '-1000-01-01',        true, -93724128000000, 'Year BC' ],
+               [ '+1000-01-01',        true, -30610224000000, 'Date with +sign' ],
+               [ '2000-01-01 12:30:30Z',       true, 0, 'Date and time with no T marker' ],
+               [ '2000-01-01T12:30:60Z',       true, 946729860000, 'Date with leap second' ],
+               [ '2000-01-01T12:30:30-24:00',  true, 946816230000, 'Date time in UTC-24' ],
+               [ '2000-01-01T12:30:30+24:00',  true, 946643430000, 'Date time in UTC+24' ],
+               [ '2000-01-01T12:30:30+0100',   true, 946726230000, 'Time without separator in timezone offset' ]
+               */
+       ];
+       parserTest( 'ISO Dates', 'isoDate', ISODates );
+
        currencyData = [
                [ '1.02 $',     true, 1.02, '' ],
                [ '$ 3.00',     true, 3, '' ],
index d4d0ce1..8026cc3 100644 (file)
                        [ 'January 01 2010' ],
                        [ 'January 16 2010' ],
                        [ 'February 05 2010' ]
+               ],
+               isoDateSorting = [
+                       [ '2010-02-01' ],
+                       [ '2009-12-25T12:30:45.001Z' ],
+                       [ '2010-01-31' ],
+                       [ '2009' ],
+                       [ '2009-12-25T12:30:45' ],
+                       [ '2009-12-25T12:30:45.111' ],
+                       [ '2009-12-25T12:30:45+01:00' ]
+               ],
+               isoDateSortingSorted = [
+                       [ '2009' ],
+                       [ '2009-12-25T12:30:45' ],
+                       [ '2009-12-25T12:30:45+01:00' ],
+                       [ '2009-12-25T12:30:45.001Z' ],
+                       [ '2009-12-25T12:30:45.111' ],
+                       [ '2010-01-31' ],
+                       [ '2010-02-01' ]
                ];
 
        QUnit.module( 'jquery.tablesorter', QUnit.newMwEnvironment( {
                }
        );
 
+       tableTest(
+               'ISO date sorting',
+               [ 'isoDate' ],
+               isoDateSorting,
+               isoDateSortingSorted,
+               function ( $table ) {
+                       mw.config.set( 'wgDefaultDateFormat', 'dmy' );
+
+                       $table.tablesorter();
+                       $table.find( '.headerSort:eq(0)' ).click();
+               }
+       );
+
        QUnit.test( 'Sorting images using alt text', 1, function ( assert ) {
                var $table = $(
                        '<table class="sortable">' +
index e7f4517..161b8e1 100644 (file)
@@ -41,7 +41,7 @@
                        'gender-msg-currentuser': '{{GENDER:|blue|pink|green}}',
 
                        'plural-msg': 'Found $1 {{PLURAL:$1|item|items}}',
-                       // See https://bugzilla.wikimedia.org/69993
+                       // See https://phabricator.wikimedia.org/T71993
                        'plural-msg-explicit-forms-nested': 'Found {{PLURAL:$1|$1 results|0=no results in {{SITENAME}}|1=$1 result}}',
                        // Assume the grammar form grammar_case_foo is not valid in any language
                        'grammar-msg': 'Przeszukaj {{GRAMMAR:grammar_case_foo|{{SITENAME}}}}',
index 111d85b..cec0552 100644 (file)
 
                hello = mw.message( 'hello' );
 
-               // https://bugzilla.wikimedia.org/show_bug.cgi?id=44459
+               // https://phabricator.wikimedia.org/T46459
                assert.equal( hello.format, 'text', 'Message property "format" defaults to "text"' );
 
                assert.strictEqual( hello.map, mw.messages, 'Message property "map" defaults to the global instance in mw.messages' );