Merge "Added "maxPartitionsTry" option to JobQueueFederated"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Tue, 5 Nov 2013 22:27:35 +0000 (22:27 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Tue, 5 Nov 2013 22:27:35 +0000 (22:27 +0000)
672 files changed:
.gitignore
CREDITS
FAQ
HISTORY
RELEASE-NOTES-1.22
RELEASE-NOTES-1.23 [new file with mode: 0644]
composer-example.json [new file with mode: 0644]
composer.json [deleted file]
docs/hooks.txt
docs/memcached.txt
includes/ArrayUtils.php [deleted file]
includes/Article.php
includes/AuthPlugin.php
includes/AutoLoader.php
includes/CallableUpdate.php [deleted file]
includes/Cdb.php [deleted file]
includes/Cdb_PHP.php [deleted file]
includes/ChangeTags.php
includes/ConfEditor.php [deleted file]
includes/DataUpdate.php [deleted file]
includes/DefaultSettings.php
includes/DeferredUpdates.php [deleted file]
includes/DeprecatedGlobal.php
includes/EditPage.php
includes/Exception.php
includes/GlobalFunctions.php
includes/HashRing.php [deleted file]
includes/HistoryBlob.php
includes/IP.php [deleted file]
includes/ImagePage.php
includes/Init.php
includes/Linker.php
includes/LinksUpdate.php [deleted file]
includes/MWCryptRand.php [deleted file]
includes/MWFunction.php [deleted file]
includes/MappedIterator.php [deleted file]
includes/Message.php
includes/Metadata.php
includes/OutputPage.php
includes/PHPVersionError.php
includes/ProxyTools.php
includes/Revision.php
includes/Sanitizer.php
includes/ScopedCallback.php [deleted file]
includes/ScopedPHPTimeout.php [deleted file]
includes/SiteStats.php
includes/Skin.php
includes/SkinTemplate.php
includes/SpecialPageFactory.php
includes/SqlDataUpdate.php [deleted file]
includes/Status.php
includes/StringUtils.php [deleted file]
includes/Title.php
includes/UIDGenerator.php [deleted file]
includes/User.php
includes/ViewCountUpdate.php [deleted file]
includes/WatchedItem.php
includes/Wiki.php
includes/WikiPage.php
includes/XmlTypeCheck.php [deleted file]
includes/ZipDirectoryReader.php [deleted file]
includes/api/ApiLogin.php
includes/api/ApiMain.php
includes/api/ApiParse.php
includes/api/ApiQueryImageInfo.php [changed mode: 0644->0755]
includes/api/ApiQueryRandom.php
includes/api/ApiQuerySiteinfo.php
includes/cache/HTMLCacheUpdate.php [deleted file]
includes/cache/LocalisationCache.php
includes/cache/MessageCache.php
includes/cache/SquidUpdate.php [deleted file]
includes/changes/RecentChange.php
includes/clientpool/RedisConnectionPool.php
includes/db/Database.php
includes/db/DatabaseError.php
includes/db/DatabaseSqlite.php
includes/db/LoadMonitor.php
includes/deferred/CallableUpdate.php [new file with mode: 0644]
includes/deferred/DataUpdate.php [new file with mode: 0644]
includes/deferred/DeferredUpdates.php [new file with mode: 0644]
includes/deferred/HTMLCacheUpdate.php [new file with mode: 0644]
includes/deferred/LinksUpdate.php [new file with mode: 0644]
includes/deferred/SearchUpdate.php [new file with mode: 0644]
includes/deferred/SiteStatsUpdate.php [new file with mode: 0644]
includes/deferred/SqlDataUpdate.php [new file with mode: 0644]
includes/deferred/SquidUpdate.php [new file with mode: 0644]
includes/deferred/ViewCountUpdate.php [new file with mode: 0644]
includes/diff/DairikiDiff.php
includes/diff/DifferenceEngine.php
includes/filebackend/FileBackend.php
includes/filebackend/FileBackendStore.php
includes/filebackend/FileOp.php
includes/filebackend/SwiftFileBackend.php
includes/filebackend/lockmanager/DBLockManager.php
includes/filebackend/lockmanager/LockManager.php
includes/filebackend/lockmanager/MemcLockManager.php
includes/filebackend/lockmanager/QuorumLockManager.php
includes/filebackend/lockmanager/RedisLockManager.php
includes/filerepo/FileRepoStatus.php
includes/filerepo/ForeignAPIRepo.php [changed mode: 0644->0755]
includes/filerepo/file/File.php
includes/filerepo/file/ForeignAPIFile.php [changed mode: 0644->0755]
includes/installer/DatabaseInstaller.php
includes/installer/DatabaseUpdater.php
includes/installer/InstallDocFormatter.php
includes/installer/Installer.i18n.php
includes/installer/Installer.php
includes/installer/LocalSettingsGenerator.php
includes/installer/MysqlInstaller.php
includes/installer/MysqlUpdater.php
includes/installer/OracleInstaller.php
includes/installer/OracleUpdater.php
includes/installer/PostgresInstaller.php
includes/installer/PostgresUpdater.php
includes/installer/SqliteInstaller.php
includes/installer/SqliteUpdater.php
includes/installer/WebInstaller.php
includes/installer/WebInstallerOutput.php
includes/installer/WebInstallerPage.php
includes/job/JobQueueGroup.php
includes/json/FormatJson.php
includes/libs/ScopedPHPTimeout.php [new file with mode: 0644]
includes/libs/XmlTypeCheck.php [new file with mode: 0644]
includes/limit.sh
includes/logging/LogPager.php
includes/media/ExifBitmap.php
includes/media/FormatMetadata.php [changed mode: 0644->0755]
includes/media/GIF.php
includes/media/MediaHandler.php [changed mode: 0644->0755]
includes/media/PNG.php
includes/media/SVG.php
includes/normal/UtfNormalTest.php
includes/objectcache/RedisBagOStuff.php
includes/parser/CoreParserFunctions.php
includes/parser/LinkHolderArray.php
includes/parser/Parser.php
includes/parser/ParserOutput.php
includes/parser/Tidy.php
includes/rcfeed/RedisPubSubFeedEngine.php [new file with mode: 0644]
includes/resourceloader/ResourceLoader.php
includes/resourceloader/ResourceLoaderStartUpModule.php
includes/search/SearchUpdate.php [deleted file]
includes/specials/SpecialBlockme.php [deleted file]
includes/specials/SpecialChangePassword.php
includes/specials/SpecialContributions.php
includes/specials/SpecialDeletedContributions.php
includes/specials/SpecialPreferences.php
includes/specials/SpecialRecentchanges.php
includes/specials/SpecialRecentchangeslinked.php
includes/specials/SpecialSearch.php
includes/specials/SpecialUnblock.php
includes/specials/SpecialUpload.php
includes/specials/SpecialUserlogin.php
includes/specials/SpecialWatchlist.php
includes/templates/Usercreate.php
includes/templates/Userlogin.php
includes/upload/UploadBase.php
includes/upload/UploadFromChunks.php
includes/upload/UploadFromStash.php
includes/upload/UploadStash.php
includes/utils/ArrayUtils.php [new file with mode: 0644]
includes/utils/Cdb.php [new file with mode: 0644]
includes/utils/Cdb_PHP.php [new file with mode: 0644]
includes/utils/ConfEditor.php [new file with mode: 0644]
includes/utils/HashRing.php [new file with mode: 0644]
includes/utils/IP.php [new file with mode: 0644]
includes/utils/MWCryptRand.php [new file with mode: 0644]
includes/utils/MWFunction.php [new file with mode: 0644]
includes/utils/MappedIterator.php [new file with mode: 0644]
includes/utils/README [new file with mode: 0644]
includes/utils/ScopedCallback.php [new file with mode: 0644]
includes/utils/StringUtils.php [new file with mode: 0644]
includes/utils/UIDGenerator.php [new file with mode: 0644]
includes/utils/ZipDirectoryReader.php [new file with mode: 0644]
languages/Language.php
languages/LanguageConverter.php
languages/Names.php
languages/messages/MessagesAce.php
languages/messages/MessagesAf.php
languages/messages/MessagesAln.php
languages/messages/MessagesAm.php
languages/messages/MessagesAn.php
languages/messages/MessagesAng.php
languages/messages/MessagesAr.php
languages/messages/MessagesArc.php
languages/messages/MessagesArn.php
languages/messages/MessagesAry.php
languages/messages/MessagesArz.php
languages/messages/MessagesAs.php
languages/messages/MessagesAst.php
languages/messages/MessagesAvk.php
languages/messages/MessagesAz.php
languages/messages/MessagesAzb.php
languages/messages/MessagesBa.php
languages/messages/MessagesBcc.php
languages/messages/MessagesBcl.php
languages/messages/MessagesBe.php
languages/messages/MessagesBe_tarask.php
languages/messages/MessagesBg.php
languages/messages/MessagesBjn.php
languages/messages/MessagesBn.php
languages/messages/MessagesBr.php
languages/messages/MessagesBs.php
languages/messages/MessagesCa.php
languages/messages/MessagesCe.php
languages/messages/MessagesCkb.php
languages/messages/MessagesCrh_cyrl.php
languages/messages/MessagesCrh_latn.php
languages/messages/MessagesCs.php
languages/messages/MessagesCsb.php
languages/messages/MessagesCu.php
languages/messages/MessagesCv.php
languages/messages/MessagesCy.php
languages/messages/MessagesDa.php
languages/messages/MessagesDe.php
languages/messages/MessagesDiq.php
languages/messages/MessagesDsb.php
languages/messages/MessagesDv.php
languages/messages/MessagesEl.php
languages/messages/MessagesEn.php
languages/messages/MessagesEo.php
languages/messages/MessagesEs.php
languages/messages/MessagesEt.php
languages/messages/MessagesEu.php
languages/messages/MessagesExt.php
languages/messages/MessagesFa.php
languages/messages/MessagesFi.php
languages/messages/MessagesFo.php
languages/messages/MessagesFr.php
languages/messages/MessagesFrp.php
languages/messages/MessagesFrr.php
languages/messages/MessagesFy.php
languages/messages/MessagesGa.php
languages/messages/MessagesGan_hans.php
languages/messages/MessagesGan_hant.php
languages/messages/MessagesGd.php
languages/messages/MessagesGl.php
languages/messages/MessagesGrc.php
languages/messages/MessagesGsw.php
languages/messages/MessagesGu.php
languages/messages/MessagesGv.php
languages/messages/MessagesHak.php
languages/messages/MessagesHaw.php
languages/messages/MessagesHe.php
languages/messages/MessagesHi.php
languages/messages/MessagesHif_latn.php
languages/messages/MessagesHr.php
languages/messages/MessagesHsb.php
languages/messages/MessagesHu.php
languages/messages/MessagesHy.php
languages/messages/MessagesIa.php
languages/messages/MessagesId.php
languages/messages/MessagesIg.php
languages/messages/MessagesIlo.php
languages/messages/MessagesInh.php
languages/messages/MessagesIo.php
languages/messages/MessagesIs.php
languages/messages/MessagesIt.php
languages/messages/MessagesJa.php
languages/messages/MessagesJv.php
languages/messages/MessagesKa.php
languages/messages/MessagesKaa.php
languages/messages/MessagesKab.php
languages/messages/MessagesKiu.php
languages/messages/MessagesKk_arab.php
languages/messages/MessagesKk_cyrl.php
languages/messages/MessagesKk_latn.php
languages/messages/MessagesKm.php
languages/messages/MessagesKn.php
languages/messages/MessagesKo.php
languages/messages/MessagesKrc.php
languages/messages/MessagesKsh.php
languages/messages/MessagesKu_latn.php
languages/messages/MessagesKy.php
languages/messages/MessagesLa.php
languages/messages/MessagesLb.php
languages/messages/MessagesLfn.php
languages/messages/MessagesLi.php
languages/messages/MessagesLoz.php
languages/messages/MessagesLt.php
languages/messages/MessagesLtg.php
languages/messages/MessagesLv.php
languages/messages/MessagesLzh.php
languages/messages/MessagesMai.php
languages/messages/MessagesMdf.php
languages/messages/MessagesMg.php
languages/messages/MessagesMin.php
languages/messages/MessagesMk.php
languages/messages/MessagesMl.php
languages/messages/MessagesMn.php
languages/messages/MessagesMr.php
languages/messages/MessagesMs.php
languages/messages/MessagesMt.php
languages/messages/MessagesMwl.php
languages/messages/MessagesMy.php
languages/messages/MessagesMyv.php
languages/messages/MessagesNah.php
languages/messages/MessagesNb.php
languages/messages/MessagesNds.php
languages/messages/MessagesNds_nl.php
languages/messages/MessagesNe.php
languages/messages/MessagesNl.php
languages/messages/MessagesNn.php
languages/messages/MessagesNso.php
languages/messages/MessagesOc.php
languages/messages/MessagesOr.php
languages/messages/MessagesPa.php
languages/messages/MessagesPam.php
languages/messages/MessagesPdc.php
languages/messages/MessagesPl.php
languages/messages/MessagesPms.php
languages/messages/MessagesPnb.php
languages/messages/MessagesPnt.php
languages/messages/MessagesPrg.php
languages/messages/MessagesPs.php
languages/messages/MessagesPt.php
languages/messages/MessagesPt_br.php
languages/messages/MessagesQqq.php
languages/messages/MessagesQu.php
languages/messages/MessagesRm.php
languages/messages/MessagesRo.php
languages/messages/MessagesRoa_tara.php
languages/messages/MessagesRu.php
languages/messages/MessagesRue.php
languages/messages/MessagesSa.php
languages/messages/MessagesSah.php
languages/messages/MessagesSat.php
languages/messages/MessagesSc.php
languages/messages/MessagesScn.php
languages/messages/MessagesSco.php
languages/messages/MessagesSdc.php
languages/messages/MessagesSe.php
languages/messages/MessagesSgs.php
languages/messages/MessagesSh.php
languages/messages/MessagesSi.php
languages/messages/MessagesSk.php
languages/messages/MessagesSl.php
languages/messages/MessagesSli.php
languages/messages/MessagesSo.php
languages/messages/MessagesSq.php
languages/messages/MessagesSr_ec.php
languages/messages/MessagesSr_el.php
languages/messages/MessagesStq.php
languages/messages/MessagesSu.php
languages/messages/MessagesSv.php
languages/messages/MessagesSw.php
languages/messages/MessagesSzl.php
languages/messages/MessagesTa.php
languages/messages/MessagesTe.php
languages/messages/MessagesTg_cyrl.php
languages/messages/MessagesTg_latn.php
languages/messages/MessagesTh.php
languages/messages/MessagesTk.php
languages/messages/MessagesTl.php
languages/messages/MessagesTo.php
languages/messages/MessagesTr.php
languages/messages/MessagesTt_cyrl.php
languages/messages/MessagesTt_latn.php
languages/messages/MessagesUdm.php
languages/messages/MessagesUg_arab.php
languages/messages/MessagesUk.php
languages/messages/MessagesUr.php
languages/messages/MessagesUz.php
languages/messages/MessagesVec.php
languages/messages/MessagesVep.php
languages/messages/MessagesVi.php
languages/messages/MessagesVmf.php
languages/messages/MessagesVo.php
languages/messages/MessagesVot.php
languages/messages/MessagesVro.php
languages/messages/MessagesWa.php
languages/messages/MessagesWar.php
languages/messages/MessagesWo.php
languages/messages/MessagesWuu.php
languages/messages/MessagesXal.php
languages/messages/MessagesYi.php
languages/messages/MessagesYo.php
languages/messages/MessagesYue.php
languages/messages/MessagesZh_hans.php
languages/messages/MessagesZh_hant.php
maintenance/archives/patch-rc_source.sql [new file with mode: 0644]
maintenance/cleanupUploadStash.php
maintenance/eval.php
maintenance/fuzz-tester.php
maintenance/jsduck/categories.json
maintenance/language/messageTypes.inc
maintenance/language/messages.inc
maintenance/mwjsduck-gen
maintenance/oracle/tables.sql
maintenance/populateRecentChangesSource.php [new file with mode: 0644]
maintenance/populateRevisionLength.php
maintenance/postgres/tables.sql
maintenance/proxyCheck.php [deleted file]
maintenance/rebuildrecentchanges.php
maintenance/tables.sql
maintenance/update.php
resources/Resources.php
resources/jquery/jquery.tablesorter.js
resources/mediawiki.api/mediawiki.api.js
resources/mediawiki.special/mediawiki.special.preferences.css
resources/mediawiki.special/mediawiki.special.vforms.css
resources/mediawiki/mediawiki.Title.js
resources/mediawiki/mediawiki.inspect.js
resources/mediawiki/mediawiki.js
resources/mediawiki/mediawiki.user.js
resources/mediawiki/mediawiki.util.js
skins/common/IEFixes.js
skins/common/commonElements.css
skins/common/shared.css
skins/common/wikibits.js
skins/vector/beta/variables.less
skins/vector/collapsibleNav.js
skins/vector/images/preferences-break.png [deleted file]
skins/vector/images/preferences-fade.png [deleted file]
skins/vector/images/preferences/break.png [new file with mode: 0644]
skins/vector/images/preferences/fade.png [new file with mode: 0644]
skins/vector/screen.less
skins/vector/special.preferences.less [new file with mode: 0644]
skins/vector/styles-beta.less
skins/vector/styles.less
skins/vector/variables.less
tests/TestsAutoLoader.php
tests/parser/parserTest.inc
tests/parser/parserTests.txt
tests/phpunit/MediaWikiTestCase.php
tests/phpunit/data/autoloader/TestAutoloadedCamlClass.php [new file with mode: 0644]
tests/phpunit/data/autoloader/TestAutoloadedClass.php [new file with mode: 0644]
tests/phpunit/data/autoloader/TestAutoloadedLocalClass.php [new file with mode: 0644]
tests/phpunit/data/autoloader/TestAutoloadedSerializedClass.php [new file with mode: 0644]
tests/phpunit/data/media/README
tests/phpunit/data/media/Tux.svg [new file with mode: 0644]
tests/phpunit/includes/ArticleTablesTest.php
tests/phpunit/includes/ArticleTest.php
tests/phpunit/includes/BlockTest.php
tests/phpunit/includes/CdbTest.php
tests/phpunit/includes/CollationTest.php
tests/phpunit/includes/DiffHistoryBlobTest.php
tests/phpunit/includes/EditPageTest.php
tests/phpunit/includes/ExternalStoreTest.php
tests/phpunit/includes/ExtraParserTest.php
tests/phpunit/includes/FallbackTest.php [new file with mode: 0644]
tests/phpunit/includes/FauxRequestTest.php
tests/phpunit/includes/FauxResponseTest.php
tests/phpunit/includes/FormOptionsInitializationTest.php
tests/phpunit/includes/FormOptionsTest.php
tests/phpunit/includes/GlobalFunctions/GlobalTest.php
tests/phpunit/includes/GlobalFunctions/GlobalWithDBTest.php
tests/phpunit/includes/GlobalFunctions/wfAssembleUrlTest.php
tests/phpunit/includes/GlobalFunctions/wfBCP47Test.php
tests/phpunit/includes/GlobalFunctions/wfBaseConvertTest.php
tests/phpunit/includes/GlobalFunctions/wfBaseNameTest.php
tests/phpunit/includes/GlobalFunctions/wfExpandUrlTest.php
tests/phpunit/includes/GlobalFunctions/wfGetCallerTest.php
tests/phpunit/includes/GlobalFunctions/wfParseUrlTest.php
tests/phpunit/includes/GlobalFunctions/wfRemoveDotSegmentsTest.php
tests/phpunit/includes/GlobalFunctions/wfShorthandToIntegerTest.php
tests/phpunit/includes/GlobalFunctions/wfTimestampTest.php
tests/phpunit/includes/GlobalFunctions/wfUrlencodeTest.php
tests/phpunit/includes/HTMLCheckMatrixTest.php
tests/phpunit/includes/HashRingTest.php
tests/phpunit/includes/HooksTest.php
tests/phpunit/includes/HtmlFormatterTest.php
tests/phpunit/includes/HtmlTest.php
tests/phpunit/includes/HttpTest.php
tests/phpunit/includes/IPTest.php
tests/phpunit/includes/LanguageConverterTest.php
tests/phpunit/includes/LicensesTest.php
tests/phpunit/includes/LinkerTest.php
tests/phpunit/includes/LinksUpdateTest.php
tests/phpunit/includes/LocalFileTest.php
tests/phpunit/includes/LocalisationCacheTest.php
tests/phpunit/includes/MWExceptionHandlerTest.php [new file with mode: 0644]
tests/phpunit/includes/MWFunctionTest.php
tests/phpunit/includes/MWNamespaceTest.php
tests/phpunit/includes/MessageTest.php
tests/phpunit/includes/OutputPageTest.php
tests/phpunit/includes/PathRouterTest.php
tests/phpunit/includes/PreferencesTest.php
tests/phpunit/includes/Providers.php [deleted file]
tests/phpunit/includes/RecentChangeTest.php
tests/phpunit/includes/RequestContextTest.php
tests/phpunit/includes/ResourceLoaderTest.php
tests/phpunit/includes/RevisionStorageTest.php
tests/phpunit/includes/RevisionStorageTest_ContentHandlerUseDB.php
tests/phpunit/includes/RevisionTest.php
tests/phpunit/includes/SampleTest.php
tests/phpunit/includes/SanitizerTest.php
tests/phpunit/includes/SanitizerValidateEmailTest.php
tests/phpunit/includes/SiteConfigurationTest.php
tests/phpunit/includes/StatusTest.php [new file with mode: 0644]
tests/phpunit/includes/StringUtilsTest.php
tests/phpunit/includes/TemplateCategoriesTest.php
tests/phpunit/includes/TestUser.php
tests/phpunit/includes/TimeAdjustTest.php
tests/phpunit/includes/TimestampTest.php
tests/phpunit/includes/TitleMethodsTest.php
tests/phpunit/includes/TitlePermissionTest.php
tests/phpunit/includes/TitleTest.php
tests/phpunit/includes/UIDGeneratorTest.php
tests/phpunit/includes/UserMailerTest.php [new file with mode: 0644]
tests/phpunit/includes/UserTest.php
tests/phpunit/includes/WebRequestTest.php
tests/phpunit/includes/WikiPageTest.php
tests/phpunit/includes/WikiPageTest_ContentHandlerUseDB.php
tests/phpunit/includes/XmlJsTest.php
tests/phpunit/includes/XmlSelectTest.php
tests/phpunit/includes/XmlTest.php
tests/phpunit/includes/XmlTypeCheckTest.php
tests/phpunit/includes/ZipDirectoryReaderTest.php
tests/phpunit/includes/api/ApiAccountCreationTest.php [deleted file]
tests/phpunit/includes/api/ApiBaseTest.php [new file with mode: 0644]
tests/phpunit/includes/api/ApiBlockTest.php
tests/phpunit/includes/api/ApiCreateAccountTest.php [new file with mode: 0644]
tests/phpunit/includes/api/ApiEditPageTest.php
tests/phpunit/includes/api/ApiLoginTest.php [new file with mode: 0644]
tests/phpunit/includes/api/ApiMainTest.php [new file with mode: 0644]
tests/phpunit/includes/api/ApiOptionsTest.php
tests/phpunit/includes/api/ApiParseTest.php
tests/phpunit/includes/api/ApiPurgeTest.php
tests/phpunit/includes/api/ApiTest.php [deleted file]
tests/phpunit/includes/api/ApiTestCase.php
tests/phpunit/includes/api/ApiTestContext.php [new file with mode: 0644]
tests/phpunit/includes/api/ApiTokensTest.php [new file with mode: 0644]
tests/phpunit/includes/api/ApiUnblockTest.php [new file with mode: 0644]
tests/phpunit/includes/api/ApiUploadTest.php
tests/phpunit/includes/api/ApiWatchTest.php
tests/phpunit/includes/api/MockApi.php [new file with mode: 0644]
tests/phpunit/includes/api/PrefixUniquenessTest.php
tests/phpunit/includes/api/UserWrapper.php [new file with mode: 0644]
tests/phpunit/includes/api/format/ApiFormatJsonTest.php [new file with mode: 0644]
tests/phpunit/includes/api/format/ApiFormatPhpTest.php
tests/phpunit/includes/api/format/ApiFormatTestBase.php
tests/phpunit/includes/api/format/ApiFormatWddxTest.php [new file with mode: 0644]
tests/phpunit/includes/api/query/ApiQueryBasicTest.php
tests/phpunit/includes/api/query/ApiQueryContinue2Test.php
tests/phpunit/includes/api/query/ApiQueryContinueTest.php
tests/phpunit/includes/api/query/ApiQueryContinueTestBase.php
tests/phpunit/includes/api/query/ApiQueryRevisionsTest.php
tests/phpunit/includes/api/query/ApiQueryTest.php
tests/phpunit/includes/api/query/ApiQueryTestBase.php
tests/phpunit/includes/cache/GenderCacheTest.php
tests/phpunit/includes/cache/MessageCacheTest.php
tests/phpunit/includes/cache/ProcessCacheLRUTest.php
tests/phpunit/includes/content/WikitextContentTest.php
tests/phpunit/includes/db/DatabaseMysqlBaseTest.php
tests/phpunit/includes/db/DatabaseSQLTest.php
tests/phpunit/includes/db/DatabaseSqliteTest.php
tests/phpunit/includes/db/DatabaseTest.php
tests/phpunit/includes/db/ORMTableTest.php
tests/phpunit/includes/db/TestORMRowTest.php
tests/phpunit/includes/debug/MWDebugTest.php
tests/phpunit/includes/diff/DifferenceEngineTest.php [new file with mode: 0644]
tests/phpunit/includes/filebackend/FileBackendTest.php
tests/phpunit/includes/filerepo/FileRepoTest.php
tests/phpunit/includes/filerepo/StoreBatchTest.php
tests/phpunit/includes/installer/InstallDocFormatterTest.php
tests/phpunit/includes/installer/OracleInstallerTest.php
tests/phpunit/includes/jobqueue/JobQueueTest.php
tests/phpunit/includes/json/FormatJsonTest.php
tests/phpunit/includes/libs/CSSJanusTest.php
tests/phpunit/includes/libs/CSSMinTest.php
tests/phpunit/includes/libs/GenericArrayObjectTest.php
tests/phpunit/includes/libs/IEUrlExtensionTest.php
tests/phpunit/includes/libs/JavaScriptMinifierTest.php
tests/phpunit/includes/logging/LogFormatterTest.php
tests/phpunit/includes/media/BitmapMetadataHandlerTest.php
tests/phpunit/includes/media/BitmapScalingTest.php
tests/phpunit/includes/media/ExifBitmapTest.php
tests/phpunit/includes/media/ExifRotationTest.php
tests/phpunit/includes/media/ExifTest.php
tests/phpunit/includes/media/FakeDimensionFile.php [new file with mode: 0644]
tests/phpunit/includes/media/FormatMetadataTest.php
tests/phpunit/includes/media/GIFMetadataExtractorTest.php
tests/phpunit/includes/media/GIFTest.php
tests/phpunit/includes/media/IPTCTest.php
tests/phpunit/includes/media/JpegMetadataExtractorTest.php
tests/phpunit/includes/media/JpegTest.php
tests/phpunit/includes/media/MediaHandlerTest.php
tests/phpunit/includes/media/PNGMetadataExtractorTest.php
tests/phpunit/includes/media/PNGTest.php
tests/phpunit/includes/media/SVGMetadataExtractorTest.php
tests/phpunit/includes/media/SVGTest.php [new file with mode: 0644]
tests/phpunit/includes/media/TiffTest.php
tests/phpunit/includes/media/XMPTest.php
tests/phpunit/includes/media/XMPValidateTest.php
tests/phpunit/includes/normal/CleanUpTest.php
tests/phpunit/includes/objectcache/BagOStuffTest.php
tests/phpunit/includes/parser/MagicVariableTest.php
tests/phpunit/includes/parser/NewParserTest.php
tests/phpunit/includes/parser/ParserMethodsTest.php
tests/phpunit/includes/parser/ParserOutputTest.php
tests/phpunit/includes/parser/ParserPreloadTest.php
tests/phpunit/includes/parser/PreprocessorTest.php
tests/phpunit/includes/parser/TagHooksTest.php
tests/phpunit/includes/parser/TidyTest.php [new file with mode: 0644]
tests/phpunit/includes/search/SearchEngineTest.php
tests/phpunit/includes/search/SearchUpdateTest.php
tests/phpunit/includes/site/MediaWikiSiteTest.php
tests/phpunit/includes/site/SiteListTest.php
tests/phpunit/includes/site/SiteSQLStoreTest.php
tests/phpunit/includes/site/SiteTest.php
tests/phpunit/includes/specials/QueryAllSpecialPagesTest.php
tests/phpunit/includes/specials/SpecialPreferencesTest.php
tests/phpunit/includes/specials/SpecialRecentchangesTest.php
tests/phpunit/includes/specials/SpecialSearchTest.php
tests/phpunit/includes/upload/UploadBaseTest.php
tests/phpunit/includes/upload/UploadFromUrlTest.php
tests/phpunit/includes/upload/UploadStashTest.php
tests/phpunit/languages/LanguageAmTest.php
tests/phpunit/languages/LanguageArTest.php
tests/phpunit/languages/LanguageBeTest.php
tests/phpunit/languages/LanguageBe_taraskTest.php
tests/phpunit/languages/LanguageBhoTest.php
tests/phpunit/languages/LanguageBsTest.php
tests/phpunit/languages/LanguageClassesTestCase.php
tests/phpunit/languages/LanguageCsTest.php
tests/phpunit/languages/LanguageCuTest.php
tests/phpunit/languages/LanguageCyTest.php
tests/phpunit/languages/LanguageDsbTest.php
tests/phpunit/languages/LanguageFrTest.php
tests/phpunit/languages/LanguageGaTest.php
tests/phpunit/languages/LanguageGdTest.php
tests/phpunit/languages/LanguageGvTest.php
tests/phpunit/languages/LanguageHeTest.php
tests/phpunit/languages/LanguageHiTest.php
tests/phpunit/languages/LanguageHrTest.php
tests/phpunit/languages/LanguageHsbTest.php
tests/phpunit/languages/LanguageHuTest.php
tests/phpunit/languages/LanguageHyTest.php
tests/phpunit/languages/LanguageKshTest.php
tests/phpunit/languages/LanguageLnTest.php
tests/phpunit/languages/LanguageLtTest.php
tests/phpunit/languages/LanguageLvTest.php
tests/phpunit/languages/LanguageMgTest.php
tests/phpunit/languages/LanguageMkTest.php
tests/phpunit/languages/LanguageMlTest.php
tests/phpunit/languages/LanguageMoTest.php
tests/phpunit/languages/LanguageMtTest.php
tests/phpunit/languages/LanguageNlTest.php
tests/phpunit/languages/LanguageNsoTest.php
tests/phpunit/languages/LanguagePlTest.php
tests/phpunit/languages/LanguageRoTest.php
tests/phpunit/languages/LanguageRuTest.php
tests/phpunit/languages/LanguageSeTest.php
tests/phpunit/languages/LanguageSgsTest.php
tests/phpunit/languages/LanguageShTest.php
tests/phpunit/languages/LanguageSkTest.php
tests/phpunit/languages/LanguageSlTest.php
tests/phpunit/languages/LanguageSmaTest.php
tests/phpunit/languages/LanguageSrTest.php
tests/phpunit/languages/LanguageTest.php
tests/phpunit/languages/LanguageTiTest.php
tests/phpunit/languages/LanguageTlTest.php
tests/phpunit/languages/LanguageTrTest.php
tests/phpunit/languages/LanguageUkTest.php
tests/phpunit/languages/LanguageUzTest.php
tests/phpunit/languages/LanguageWaTest.php
tests/phpunit/languages/utils/CLDRPluralRuleEvaluatorTest.php
tests/phpunit/maintenance/DumpTestCase.php
tests/phpunit/maintenance/MaintenanceTest.php
tests/phpunit/maintenance/backupPrefetchTest.php
tests/phpunit/maintenance/backupTextPassTest.php
tests/phpunit/maintenance/backup_LogTest.php
tests/phpunit/maintenance/backup_PageTest.php
tests/phpunit/maintenance/fetchTextTest.php
tests/phpunit/maintenance/getSlaveServerTest.php
tests/phpunit/skins/SideBarTest.php
tests/phpunit/structure/AutoLoaderTest.php
tests/phpunit/structure/ResourcesTest.php
tests/qunit/suites/resources/jquery/jquery.localize.test.js
tests/qunit/suites/resources/mediawiki/mediawiki.Title.test.js
tests/qunit/suites/resources/mediawiki/mediawiki.user.test.js

index 9c0c3b6..7d1a309 100644 (file)
@@ -41,6 +41,7 @@ sftp-config.json
 
 # Building & testing
 node_modules/
+.sass-cache/
 
 # Composer
 /vendor
diff --git a/CREDITS b/CREDITS
index 7927c3d..23636ae 100644 (file)
--- a/CREDITS
+++ b/CREDITS
@@ -1,4 +1,4 @@
-MediaWiki 1.22 is a collaborative project released under the
+MediaWiki 1.23 is a collaborative project released under the
 GNU General Public License v2. We would like to recognize the
 following names for their contribution to the product.
 
diff --git a/FAQ b/FAQ
index 0aedb7e..cfacf14 100644 (file)
--- a/FAQ
+++ b/FAQ
@@ -1,2 +1,2 @@
 The MediaWiki FAQ can be found at:
-http://www.mediawiki.org/wiki/Manual:FAQ
+https://www.mediawiki.org/wiki/Manual:FAQ
diff --git a/HISTORY b/HISTORY
index 45eab2e..88cc906 100644 (file)
--- a/HISTORY
+++ b/HISTORY
@@ -1,4 +1,4 @@
-Change notes from older releases. For current info see RELEASE-NOTES-1.22.
+Change notes from older releases. For current info see RELEASE-NOTES-1.23.
 
 == MediaWiki 1.21 ==
 
index b80d04a..25d5c42 100644 (file)
@@ -61,6 +61,10 @@ production.
 * The precise format of metric datagrams produced by the UDP profiler and stats counter
   may now be specified as $wgUDPProfilerFormatString and $wgStatsFormatString,
   respectively.
+* (bug 54597) $wgBlockOpenProxies, $wgProxyPorts, $wgProxyScriptPath, and
+  $wgProxyMemcExpiry have been removed, along with the open proxy scanner
+  script they were added for.
+* Default value of $wgMaxShellMemory has been tripled (it's now 300 MB).
 
 === New features in 1.22 ===
 * (bug 44525) mediawiki.jqueryMsg can now parse (whitelisted) HTML elements and attributes.
@@ -73,6 +77,8 @@ production.
   version of the Vector extension as this feature may conflict.
 * New 'mediawiki.ui' CSS module providing mw-ui-* styles for buttons and a
   compact vertical form layout.
+* HTMLForm supports a new display format 'vform' which applies this compact vertical
+  layout and button styling. Special:PasswordReset uses this format.
 * New versions of login (Special:UserLogin) and create account
   (Special:UserLogin/signup) forms using the "vform" compact vertical form layout.
   These forms use new messages that assume a "Help logging in" link, see
@@ -221,8 +227,6 @@ production.
 * Add deferrable update support for callback/closure.
 * Add TitleMove hook before page renames.
 * Revision deletion backend code is moved out of SpecialRevisiondelete
-* Add a variable (wgRedactedFunctionArguments) to redact the values sent as certain function
-  parameters from exception stack traces.
 * Added {{REVISIONSIZE}} variable to get the current size of a revision.
 * Add support for the LESS stylesheet language to ResourceLoader. LESS is a
   stylesheet language that compiles into CSS. ResourceLoader file modules may
@@ -248,8 +252,17 @@ production.
   output in a HTML comment.
 * The 'UnwatchArticle' and 'WatchArticle' hooks now support a Status object
   instead of just a boolean return value to abort the hook.
+* Added a hook, SpecialWatchlistGetNonRevisionTypes, to allow extensions
+  with custom recentchanges entries to hook into the Watchlist without 
+  clobbering each other.
+* A hidden, empty input field was added to the edit form, and any edit that fills
+  it in will be rejected. This prevents against the simplest form of spambots.
+  Previously in the "SimpleAntiSpam" extension by Ryan Schmidt.
+* populateRevisionLength.php maintenance script updated to also populate
+  archive.ar_len field.
 
 === Bug fixes in 1.22 ===
+* (bug 47271) $wgContentHandlerUseDB should be set to false during the upgrade
 * Disable Special:PasswordReset when $wgEnableEmail is false. Previously one
   could still navigate to the page by entering the URL directly.
 * (bug 47138) Fixed a fatal error when a blocked user tries to automatically
@@ -333,6 +346,8 @@ production.
 * (bug 47191) Fixed "Column 'si_title' cannot be part of FULLTEXT index"
   MySQL error when installing using the binary character set option.
 * (bug 45288) Support mysqli PHP extension
+* (bug 55818) BREAKING CHANGE: Removed undocumented 'Debug' hook in wfDebug.
+  This resolves an infinite loop when using $wgDebugFunctionEntry = true.
 
 === API changes in 1.22 ===
 * (bug 25553) The JSON output formatter now leaves forward slashes unescaped
@@ -405,6 +420,15 @@ changes to languages because of Bugzilla reports.
 * (bug 46751) Made Buryat (Russia) (буряад) (bxr) fallback to Russian.
 
 === Other changes in 1.22 ===
+* The rc_type field in the recentchanges table has been superseded by a new
+  rc_source field.  The rc_source field is a string representation of the
+  change type where rc_type was a numeric constant.  This field is not yet
+  queried but will be in a future point release of 1.22.  
+** Utilize update.php to create and populate this new field.  On larger wiki's
+   which do not wish to update recentchanges table in one large update please
+   review the sql and comments in maintenance/archives/patch-rc_source.sql.
+** The rc_type field of recentchanges will be deprecated in a future point
+   release.
 * BREAKING CHANGE: Implementation of MediaWiki's JS and JSON value encoding
   has changed:
 ** MediaWiki no longer supports PHP installations in which the native JSON
@@ -502,6 +526,13 @@ changes to languages because of Bugzilla reports.
   to addModules() and made protected.
 * Methods WatchAction::doWatch and WatchAction::doUnwatch now return a Status
   object instead of a boolean.
+* Information boxes (CSS classes errorbox, warningbox, successbox) have been
+  made more subtle.
+* BREAKING CHANGE: The module 'mediawiki.legacy.IEFixes' has been removed as it was
+  unused. The file skins/common/IEFixes.js remains but is only used by wikibits.
+  The file never contained any re-usable components. To use it in a skin, load
+  'mediawiki.legacy.wikibits' (which IEFixes depends on) and that will import
+  IEFixes automatically if user agent conditions are met.
 
 == Compatibility ==
 
diff --git a/RELEASE-NOTES-1.23 b/RELEASE-NOTES-1.23
new file mode 100644 (file)
index 0000000..efb0805
--- /dev/null
@@ -0,0 +1,114 @@
+Security reminder: MediaWiki does not require PHP's register_globals. If you
+have it on, turn it '''off''' if you can.
+
+== MediaWiki 1.23 ==
+
+THIS IS NOT A RELEASE YET
+
+MediaWiki 1.23 is an alpha-quality branch and is not recommended for use in
+production.
+
+=== Configuration changes in 1.23 ===
+* $wgDebugLogGroups values may be set to an associative array with a
+  'destination' key specifying the log destination. The array may also contain
+  a 'sample' key with a positive integer value N indicating that the log group
+  should be sampled by dispatching one in every N messages on average. The
+  sampling is random.
+* In addition to the current exception log format, MediaWiki now serializes
+  exception metadata to JSON and logs it to the 'exception-json' log group.
+  This makes MediaWiki easier to integrate with log aggregation and analysis
+  tools.
+
+=== New features in 1.23 ===
+* ResourceLoader can utilize the Web Storage API to cache modules client-side.
+  Compared to the browser cache, caching in Web Storage allows ResourceLoader
+  to be more granular about evicting stale modules from the cache while
+  retaining the ability to retrieve multiple modules in a single HTTP request.
+  This capability can be enabled by setting $wgResourceLoaderStorageEnabled to
+  true. This feature is currently considered experimental and should only be
+  enabled with care.
+* (bug 6092) Add expensive parser functions {{REVISIONID:}}, {{REVISIONUSER:}}
+  and {{REVISIONTIMESTAMP:}} (with friends).
+
+=== Bug fixes in 1.23 ===
+* (bug 41759) The "updated since last visit" markers (on history pages, recent
+  changes and watchlist) and the talk page message indicator are now correctly
+  updated when the user is viewing old revisions of pages, instead of always
+  acting as if the latest revision was being viewed.
+
+=== API changes in 1.23 ===
+* (bug 54884) action=parse&prop=categories now indicates hidden and missing
+  categories.
+
+=== Languages updated in 1.23===
+
+MediaWiki supports over 350 languages. Many localisations are updated
+regularly. Below only new and removed languages are listed, as well as
+changes to languages because of Bugzilla reports.
+
+=== Other changes in 1.23 ===
+* The global variable $wgArticle has been removed after a lengthy deprecation
+
+== Compatibility ==
+
+MediaWiki 1.23 requires PHP 5.3.2 or later.
+
+MySQL is the recommended DBMS. PostgreSQL or SQLite can also be used, but
+support for them is somewhat less mature. There is experimental support for
+Oracle.
+
+The supported versions are:
+
+* MySQL 5.0.2 or later
+* PostgreSQL 8.3 or later
+* SQLite 3.3.7 or later
+* Oracle 9.0.1 or later
+
+== Upgrading ==
+
+1.23 has several database changes since 1.22, and will not work without schema
+updates. Note that due to changes to some very large tables like the revision
+table, the schema update may take quite long (minutes on a medium sized site,
+many hours on a large site).
+
+If upgrading from before 1.11, and you are using a wiki as a commons
+repository, make sure that it is updated as well. Otherwise, errors may arise
+due to database schema changes.
+
+If upgrading from before 1.7, you may want to run refreshLinks.php to ensure
+new database fields are filled with data.
+
+If you are upgrading from MediaWiki 1.4.x or earlier, you should upgrade to
+1.5 first. The upgrade script maintenance/upgrade1_5.php has been removed
+with MediaWiki 1.21.
+
+Don't forget to always back up your database before upgrading!
+
+See the file UPGRADE for more detailed upgrade instructions.
+
+For notes on 1.21.x and older releases, see HISTORY.
+
+== Online documentation ==
+
+Documentation for both end-users and site administrators is available on
+MediaWiki.org, and is covered under the GNU Free Documentation License (except
+for pages that explicitly state that their contents are in the public domain):
+
+       https://www.mediawiki.org/wiki/Documentation
+
+== Mailing list ==
+
+A mailing list is available for MediaWiki user support and discussion:
+
+       https://lists.wikimedia.org/mailman/listinfo/mediawiki-l
+
+A low-traffic announcements-only list is also available:
+
+       https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce
+
+It's highly recommended that you sign up for one of these lists if you're
+going to run a public MediaWiki, so you can be notified of security fixes.
+
+== IRC help ==
+
+There's usually someone online in #mediawiki on irc.freenode.net.
diff --git a/composer-example.json b/composer-example.json
new file mode 100644 (file)
index 0000000..6c4d37f
--- /dev/null
@@ -0,0 +1,11 @@
+{
+       "require": {
+               "php": ">=5.3.2"
+       },
+       "suggest": {
+               "ext-fileinfo": "*",
+               "ext-mbstring": "*",
+               "ext-wikidiff2": "*",
+               "ext-apc": "*"
+       }
+}
diff --git a/composer.json b/composer.json
deleted file mode 100644 (file)
index ded3365..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-{
-       "name": "mediawiki/core",
-       "description": "Free software wiki application developed by the Wikimedia Foundation and others",
-       "keywords": ["mediawiki", "wiki"],
-       "homepage": "https://www.mediawiki.org/",
-       "authors": [
-               {
-                       "name": "MediaWiki Community",
-                       "homepage": "https://www.mediawiki.org/wiki/Special:Version/Credits"
-               }
-       ],
-       "license": "GPL-2.0",
-       "support": {
-               "issues": "https://bugzilla.wikimedia.org/",
-               "irc": "irc://irc.freenode.net/mediawiki",
-               "wiki": "https://www.mediawiki.org/"
-       },
-       "require": {
-               "php": ">=5.3.2"
-       },
-       "require-dev": {
-               "phpunit/phpunit": "*"
-       },
-       "suggest": {
-               "ext-fileinfo": "*",
-               "ext-mbstring": "*",
-               "ext-wikidiff2": "*",
-               "ext-apc": "*"
-       }
-}
index 53993de..96a72df 100644 (file)
@@ -866,10 +866,6 @@ etc.
 'DatabaseOraclePostInit': Called after initialising an Oracle database
 &$db: the DatabaseOracle object
 
-'Debug': Called when outputting a debug log line via wfDebug() or wfDebugLog()
-$text: plaintext string to be output
-$group: null or a string naming a logging group (as defined in $wgDebugLogGroups)
-
 'NewDifferenceEngine': Called when a new DifferenceEngine object is made
 $title: the diff page title (nullable)
 &$oldId: the actual old Id to use in the diff
@@ -1161,6 +1157,16 @@ $title: Title object that we need to get a sortkey for
 underscore) magic words. Called by MagicWord.
 &$doubleUnderscoreIDs: array of strings
 
+'GetExtendedMetadata': Get extended file metadata for the API
+&$combinedMeta: Array of the form: 'MetadataPropName' => array(
+'value' => prop value, 'source' => 'name of hook' ).
+$file: File object of file in question
+$context: RequestContext (including language to use)
+$single: Only extract the current language; if false, the prop value should
+be in the metadata multi-language array format:
+mediawiki.org/wiki/Manual:File_metadata_handling#Multi-language_array_format
+&$maxCacheTime: how long the results can be cached
+
 'GetFullURL': Modify fully-qualified URLs used in redirects/export/offsite data.
 $title: Title object of page
 $url: string value as output (out parameter, can modify)
@@ -1602,7 +1608,7 @@ $wcOnlySysopsCanPatrol: config setting indicating whether the user must be a
 something completely different, after the basic globals have been set up, but
 before ordinary actions take place.
 $output: $wgOut
-$article: $wgArticle
+$article: Article on which the action will be performed
 $title: $wgTitle
 $user: $wgUser
 $request: $wgRequest
@@ -2369,6 +2375,11 @@ $special: the special page object
 &$fields: array of query fields
 $values: array of variables with watchlist options
 
+'SpecialWatchlistGetNonRevisionTypes': Called when building sql query for
+SpecialWatchlist. Allows extensions to register custom values they have 
+inserted to rc_type so they can be returned as part of the watchlist.
+&$nonRevisionTypes: array of values in the rc_type field of recentchanges table
+
 'TestCanonicalRedirect': Called when about to force a redirect to a canonical
 URL for a title when we have no other parameters on the URL. Gives a chance for
 extensions that alter page view behavior radically to abort that redirect or
@@ -2578,6 +2589,7 @@ $user: User (object) whose permission is being checked
 'UserClearNewTalkNotification': Called when clearing the "You have new
 messages!" message, return false to not delete it.
 $user: User (object) that will clear the message
+$oldid: ID of the talk page revision being viewed (0 means the most recent one)
 
 'UserComparePasswords': Called when checking passwords, return false to
 override the default password checks.
@@ -2742,6 +2754,12 @@ $userId: User id of the current user
 $userText: User name of the current user
 &$items: Array of user tool links as HTML fragments
 
+'ValidateExtendedMetadataCache': Called to validate the cached metadata in
+FormatMetadata::getExtendedMeta (return false means cache will be
+invalidated and GetExtendedMetadata hook called again).
+$timestamp: The timestamp metadata was generated
+$file: The file the metadata is for
+
 'WantedPages::getQueryInfo': Called in WantedPagesPage::getQueryInfo(), can be
 used to alter the SQL query which gets the list of wanted pages.
 &$wantedPages: WantedPagesPage object
index f54a4e7..16c5760 100644 (file)
@@ -78,7 +78,7 @@ usage evenly), make its entry a subarray:
 == PHP client for memcached ==
 
 MediaWiki uses a fork of Ryan T. Dean's pure-PHP memcached client.
-The newer PECL module is not yet supported.
+It also supports the PECL PHP extension for memcached.
 
 MediaWiki uses three object for object caching:
 * $wgMemc, controlled by $wgMainCacheType
@@ -91,7 +91,7 @@ database. If the cache daemon can't be contacted, it should also
 disable itself fairly smoothly.
 
 By default, $wgMemc is used but when it is $parserMemc or $messageMemc
-this is mentionned below.
+this is mentioned below.
 
 == Keys used ==
 
diff --git a/includes/ArrayUtils.php b/includes/ArrayUtils.php
deleted file mode 100644 (file)
index 985271f..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-<?php
-
-class ArrayUtils {
-       /**
-        * Sort the given array in a pseudo-random order which depends only on the
-        * given key and each element value. This is typically used for load
-        * balancing between servers each with a local cache.
-        *
-        * Keys are preserved. The input array is modified in place.
-        *
-        * Note: Benchmarking on PHP 5.3 and 5.4 indicates that for small
-        * strings, md5() is only 10% slower than hash('joaat',...) etc.,
-        * since the function call overhead dominates. So there's not much
-        * justification for breaking compatibility with installations
-        * compiled with ./configure --disable-hash.
-        *
-        * @param $array The array to sort
-        * @param $key The string key
-        * @param $separator A separator used to delimit the array elements and the
-        *     key. This can be chosen to provide backwards compatibility with
-        *     various consistent hash implementations that existed before this
-        *     function was introduced.
-        */
-       public static function consistentHashSort( &$array, $key, $separator = "\000" ) {
-               $hashes = array();
-               foreach ( $array as $elt ) {
-                       $hashes[$elt] = md5( $elt . $separator . $key );
-               }
-               uasort( $array, function ( $a, $b ) use ( $hashes ) {
-                       return strcmp( $hashes[$a], $hashes[$b] );
-               } );
-       }
-
-       /**
-        * Given an array of non-normalised probabilities, this function will select
-        * an element and return the appropriate key
-        *
-        * @param $weights array
-        *
-        * @return bool|int|string
-        */
-       public static function pickRandom( $weights ) {
-               if ( !is_array( $weights ) || count( $weights ) == 0 ) {
-                       return false;
-               }
-
-               $sum = array_sum( $weights );
-               if ( $sum == 0 ) {
-                       # No loads on any of them
-                       # In previous versions, this triggered an unweighted random selection,
-                       # but this feature has been removed as of April 2006 to allow for strict
-                       # separation of query groups.
-                       return false;
-               }
-               $max = mt_getrandmax();
-               $rand = mt_rand( 0, $max ) / $max * $sum;
-
-               $sum = 0;
-               foreach ( $weights as $i => $w ) {
-                       $sum += $w;
-                       # Do not return keys if they have 0 weight.
-                       # Note that the "all 0 weight" case is handed above
-                       if ( $w > 0 && $sum >= $rand ) {
-                               break;
-                       }
-               }
-               return $i;
-       }
-}
index 0b18221..928fda0 100644 (file)
@@ -586,7 +586,7 @@ class Article implements Page {
                                wfDebug( __METHOD__ . ": done file cache\n" );
                                # tell wgOut that output is taken care of
                                $outputPage->disable();
-                               $this->mPage->doViewUpdates( $user );
+                               $this->mPage->doViewUpdates( $user, $oldid );
                                wfProfileOut( __METHOD__ );
 
                                return;
@@ -765,7 +765,7 @@ class Article implements Page {
                $outputPage->setFollowPolicy( $policy['follow'] );
 
                $this->showViewFooter();
-               $this->mPage->doViewUpdates( $user );
+               $this->mPage->doViewUpdates( $user, $oldid );
 
                $outputPage->addModules( 'mediawiki.action.view.postEdit' );
 
@@ -815,10 +815,10 @@ class Article implements Page {
                $this->mRevIdFetched = $de->mNewid;
                $de->showDiffPage( $diffOnly );
 
-               if ( $diff == 0 || $diff == $this->mPage->getLatest() ) {
-                       # Run view updates for current revision only
-                       $this->mPage->doViewUpdates( $user );
-               }
+               // Run view updates for the newer revision being diffed (and shown below the diff if not $diffOnly)
+               list( $old, $new ) = $de->mapDiffPrevNext( $oldid, $diff );
+               // New can be false, convert it to 0 - this conveniently means the latest revision
+               $this->mPage->doViewUpdates( $user, (int)$new );
        }
 
        /**
index 84cf3d5..c7b0c97 100644 (file)
@@ -34,7 +34,6 @@
  * someone logs in who can be authenticated externally.
  */
 class AuthPlugin {
-
        /**
         * @var string
         */
@@ -46,7 +45,7 @@ class AuthPlugin {
         * you might need to munge it (for instance, for lowercase initial
         * letters).
         *
-        * @param string $username username.
+        * @param string $username Username.
         * @return bool
         */
        public function userExists( $username ) {
@@ -60,8 +59,8 @@ class AuthPlugin {
         * you might need to munge it (for instance, for lowercase initial
         * letters).
         *
-        * @param string $username username.
-        * @param string $password user password.
+        * @param string $username Username.
+        * @param string $password User password.
         * @return bool
         */
        public function authenticate( $username, $password ) {
@@ -72,7 +71,7 @@ class AuthPlugin {
        /**
         * Modify options in the login template.
         *
-        * @param $template UserLoginTemplate object.
+        * @param UserLoginTemplate $template
         * @param string $type 'signup' or 'login'. Added in 1.16.
         */
        public function modifyUITemplate( &$template, &$type ) {
@@ -83,7 +82,7 @@ class AuthPlugin {
        /**
         * Set the domain this plugin is supposed to use when authenticating.
         *
-        * @param string $domain authentication domain.
+        * @param string $domain Authentication domain.
         */
        public function setDomain( $domain ) {
                $this->domain = $domain;
@@ -105,7 +104,7 @@ class AuthPlugin {
        /**
         * Check to see if the specific domain is a valid domain.
         *
-        * @param string $domain authentication domain.
+        * @param string $domain Authentication domain.
         * @return bool
         */
        public function validDomain( $domain ) {
@@ -121,7 +120,7 @@ class AuthPlugin {
         * The User object is passed by reference so it can be modified; don't
         * forget the & on your function declaration.
         *
-        * @param $user User object
+        * @param User $user
         * @return bool
         */
        public function updateUser( &$user ) {
@@ -140,7 +139,7 @@ class AuthPlugin {
         *
         * This is just a question, and shouldn't perform any actions.
         *
-        * @return Boolean
+        * @return bool
         */
        public function autoCreate() {
                return false;
@@ -151,9 +150,9 @@ class AuthPlugin {
         * and use the same keys. 'Realname' 'Emailaddress' and 'Nickname'
         * all reference this.
         *
-        * @param $prop string
+        * @param string $prop
         *
-        * @return Boolean
+        * @return bool
         */
        public function allowPropChange( $prop = '' ) {
                if ( $prop == 'realname' && is_callable( array( $this, 'allowRealNameChange' ) ) ) {
@@ -193,8 +192,8 @@ class AuthPlugin {
         *
         * Return true if successful.
         *
-        * @param $user User object.
-        * @param string $password password.
+        * @param User $user
+        * @param string $password Password.
         * @return bool
         */
        public function setPassword( $user, $password ) {
@@ -216,10 +215,10 @@ class AuthPlugin {
         * Update user groups in the external authentication database.
         * Return true if successful.
         *
-        * @param $user User object.
-        * @param $addgroups Groups to add.
-        * @param $delgroups Groups to remove.
-        * @return Boolean
+        * @param User $user
+        * @param array $addgroups Groups to add.
+        * @param array $delgroups Groups to remove.
+        * @return bool
         */
        public function updateExternalDBGroups( $user, $addgroups, $delgroups = array() ) {
                return true;
@@ -228,7 +227,7 @@ class AuthPlugin {
        /**
         * Check to see if external accounts can be created.
         * Return true if external accounts can be created.
-        * @return Boolean
+        * @return bool
         */
        public function canCreateAccounts() {
                return false;
@@ -238,11 +237,11 @@ class AuthPlugin {
         * Add a user to the external authentication database.
         * Return true if successful.
         *
-        * @param $user User: only the name should be assumed valid at this point
-        * @param $password String
-        * @param $email String
-        * @param $realname String
-        * @return Boolean
+        * @param User $user Only the name should be assumed valid at this point
+        * @param string $password
+        * @param string $email
+        * @param string $realname
+        * @return bool
         */
        public function addUser( $user, $password, $email = '', $realname = '' ) {
                return true;
@@ -254,7 +253,7 @@ class AuthPlugin {
         *
         * This is just a question, and shouldn't perform any actions.
         *
-        * @return Boolean
+        * @return bool
         */
        public function strict() {
                return false;
@@ -264,8 +263,8 @@ class AuthPlugin {
         * Check if a user should authenticate locally if the global authentication fails.
         * If either this or strict() returns true, local authentication is not used.
         *
-        * @param string $username username.
-        * @return Boolean
+        * @param string $username Username.
+        * @return bool
         */
        public function strictUserAuth( $username ) {
                return false;
@@ -279,8 +278,8 @@ class AuthPlugin {
         * The User object is passed by reference so it can be modified; don't
         * forget the & on your function declaration.
         *
-        * @param $user User object.
-        * @param $autocreate Boolean: True if user is being autocreated on login
+        * @param User $user
+        * @param bool $autocreate True if user is being autocreated on login
         */
        public function initUser( &$user, $autocreate = false ) {
                # Override this to do something.
@@ -289,7 +288,7 @@ class AuthPlugin {
        /**
         * If you want to munge the case of an account name before the final
         * check, now is your chance.
-        * @param $username string
+        * @param string $username
         * @return string
         */
        public function getCanonicalName( $username ) {
@@ -299,7 +298,7 @@ class AuthPlugin {
        /**
         * Get an instance of a User object
         *
-        * @param $user User
+        * @param User $user
         *
         * @return AuthPluginUser
         */
index 7fa9096..1417c77 100644 (file)
@@ -33,7 +33,6 @@ $wgAutoloadLocalClasses = array(
        'AjaxDispatcher' => 'includes/AjaxDispatcher.php',
        'AjaxResponse' => 'includes/AjaxResponse.php',
        'AlphabeticPager' => 'includes/Pager.php',
-       'ArrayUtils' => 'includes/ArrayUtils.php',
        'Article' => 'includes/Article.php',
        'AtomFeed' => 'includes/Feed.php',
        'AuthPlugin' => 'includes/AuthPlugin.php',
@@ -47,31 +46,17 @@ $wgAutoloadLocalClasses = array(
        'Categoryfinder' => 'includes/Categoryfinder.php',
        'CategoryPage' => 'includes/CategoryPage.php',
        'CategoryViewer' => 'includes/CategoryViewer.php',
-       'CdbFunctions' => 'includes/Cdb_PHP.php',
-       'CdbReader' => 'includes/Cdb.php',
-       'CdbReader_DBA' => 'includes/Cdb.php',
-       'CdbReader_PHP' => 'includes/Cdb_PHP.php',
-       'CdbWriter' => 'includes/Cdb.php',
-       'CdbWriter_DBA' => 'includes/Cdb.php',
-       'CdbWriter_PHP' => 'includes/Cdb_PHP.php',
        'ChangesFeed' => 'includes/ChangesFeed.php',
        'ChangeTags' => 'includes/ChangeTags.php',
        'ChannelFeed' => 'includes/Feed.php',
        'Collation' => 'includes/Collation.php',
        'ConcatenatedGzipHistoryBlob' => 'includes/HistoryBlob.php',
-       'ConfEditor' => 'includes/ConfEditor.php',
-       'ConfEditorParseError' => 'includes/ConfEditor.php',
-       'ConfEditorToken' => 'includes/ConfEditor.php',
        'Cookie' => 'includes/Cookie.php',
        'CookieJar' => 'includes/Cookie.php',
        'CurlHttpRequest' => 'includes/HttpFunctions.php',
-       'DeferrableUpdate' => 'includes/DeferredUpdates.php',
-       'DeferredUpdates' => 'includes/DeferredUpdates.php',
-       'MWCallableUpdate' => 'includes/CallableUpdate.php',
        'DeprecatedGlobal' => 'includes/DeprecatedGlobal.php',
        'DerivativeRequest' => 'includes/WebRequest.php',
        'DiffHistoryBlob' => 'includes/HistoryBlob.php',
-       'DoubleReplacer' => 'includes/StringUtils.php',
        'DummyLinker' => 'includes/Linker.php',
        'Dump7ZipOutput' => 'includes/Export.php',
        'DumpBZip2Output' => 'includes/Export.php',
@@ -87,7 +72,6 @@ $wgAutoloadLocalClasses = array(
        'EditPage' => 'includes/EditPage.php',
        'EmailNotification' => 'includes/UserMailer.php',
        'ErrorPageError' => 'includes/Exception.php',
-       'ExplodeIterator' => 'includes/StringUtils.php',
        'FakeTitle' => 'includes/FakeTitle.php',
        'Fallback' => 'includes/Fallback.php',
        'FatalError' => 'includes/Exception.php',
@@ -102,8 +86,6 @@ $wgAutoloadLocalClasses = array(
        'FormOptions' => 'includes/FormOptions.php',
        'FormSpecialPage' => 'includes/SpecialPage.php',
        'GitInfo' => 'includes/GitInfo.php',
-       'HashRing' => 'includes/HashRing.php',
-       'HashtableReplacer' => 'includes/StringUtils.php',
        'HistoryBlob' => 'includes/HistoryBlob.php',
        'HistoryBlobCurStub' => 'includes/HistoryBlob.php',
        'HistoryBlobStub' => 'includes/HistoryBlob.php',
@@ -145,7 +127,6 @@ $wgAutoloadLocalClasses = array(
        'IncludableSpecialPage' => 'includes/SpecialPage.php',
        'IndexPager' => 'includes/Pager.php',
        'Interwiki' => 'includes/interwiki/Interwiki.php',
-       'IP' => 'includes/IP.php',
        'LCStore' => 'includes/cache/LocalisationCache.php',
        'LCStore_Accel' => 'includes/cache/LocalisationCache.php',
        'LCStore_CDB' => 'includes/cache/LocalisationCache.php',
@@ -155,23 +136,18 @@ $wgAutoloadLocalClasses = array(
        'Licenses' => 'includes/Licenses.php',
        'Linker' => 'includes/Linker.php',
        'LinkFilter' => 'includes/LinkFilter.php',
-       'LinksUpdate' => 'includes/LinksUpdate.php',
-       'LinksDeletionUpdate' => 'includes/LinksUpdate.php',
        'LocalisationCache' => 'includes/cache/LocalisationCache.php',
        'LocalisationCache_BulkLoad' => 'includes/cache/LocalisationCache.php',
        'MagicWord' => 'includes/MagicWord.php',
        'MagicWordArray' => 'includes/MagicWord.php',
        'MailAddress' => 'includes/UserMailer.php',
-       'MappedIterator' => 'includes/MappedIterator.php',
        'MediaWiki' => 'includes/Wiki.php',
        'MediaWiki_I18N' => 'includes/SkinTemplate.php',
        'Message' => 'includes/Message.php',
        'MessageBlobStore' => 'includes/MessageBlobStore.php',
        'MimeMagic' => 'includes/MimeMagic.php',
-       'MWCryptRand' => 'includes/MWCryptRand.php',
        'MWException' => 'includes/Exception.php',
        'MWExceptionHandler' => 'includes/Exception.php',
-       'MWFunction' => 'includes/MWFunction.php',
        'MWHookException' => 'includes/Hooks.php',
        'MWHttpRequest' => 'includes/HttpFunctions.php',
        'MWInit' => 'includes/Init.php',
@@ -201,9 +177,6 @@ $wgAutoloadLocalClasses = array(
        'ReadOnlyError' => 'includes/Exception.php',
        'RedirectSpecialArticle' => 'includes/SpecialPage.php',
        'RedirectSpecialPage' => 'includes/SpecialPage.php',
-       'RegexlikeReplacer' => 'includes/StringUtils.php',
-       'ReplacementArray' => 'includes/StringUtils.php',
-       'Replacer' => 'includes/StringUtils.php',
        'ReverseChronologicalPager' => 'includes/Pager.php',
        'RevisionItem' => 'includes/RevisionList.php',
        'RevisionItemBase' => 'includes/RevisionList.php',
@@ -212,14 +185,9 @@ $wgAutoloadLocalClasses = array(
        'RevisionList' => 'includes/RevisionList.php',
        'RSSFeed' => 'includes/Feed.php',
        'Sanitizer' => 'includes/Sanitizer.php',
-       'DataUpdate' => 'includes/DataUpdate.php',
-       'SqlDataUpdate' => 'includes/SqlDataUpdate.php',
-       'ScopedCallback' => 'includes/ScopedCallback.php',
-       'ScopedPHPTimeout' => 'includes/ScopedPHPTimeout.php',
        'SiteConfiguration' => 'includes/SiteConfiguration.php',
        'SiteStats' => 'includes/SiteStats.php',
        'SiteStatsInit' => 'includes/SiteStats.php',
-       'SiteStatsUpdate' => 'includes/SiteStats.php',
        'Skin' => 'includes/Skin.php',
        'SkinTemplate' => 'includes/SkinTemplate.php',
        'SpecialCreateAccount' => 'includes/SpecialPage.php',
@@ -238,7 +206,6 @@ $wgAutoloadLocalClasses = array(
        'StatCounter' => 'includes/StatCounter.php',
        'Status' => 'includes/Status.php',
        'StreamFile' => 'includes/StreamFile.php',
-       'StringUtils' => 'includes/StringUtils.php',
        'StubContLang' => 'includes/StubObject.php',
        'StubObject' => 'includes/StubObject.php',
        'StubUserLang' => 'includes/StubObject.php',
@@ -249,7 +216,6 @@ $wgAutoloadLocalClasses = array(
        'TitleArray' => 'includes/TitleArray.php',
        'TitleArrayFromResult' => 'includes/TitleArray.php',
        'ThrottledError' => 'includes/Exception.php',
-       'UIDGenerator' => 'includes/UIDGenerator.php',
        'UnlistedSpecialPage' => 'includes/SpecialPage.php',
        'UploadSourceAdapter' => 'includes/Import.php',
        'UppercaseCollation' => 'includes/Collation.php',
@@ -261,7 +227,6 @@ $wgAutoloadLocalClasses = array(
        'UserCache' => 'includes/cache/UserCache.php',
        'UserMailer' => 'includes/UserMailer.php',
        'UserRightsProxy' => 'includes/UserRightsProxy.php',
-       'ViewCountUpdate' => 'includes/ViewCountUpdate.php',
        'WantedQueryPage' => 'includes/QueryPage.php',
        'WatchedItem' => 'includes/WatchedItem.php',
        'WebRequest' => 'includes/WebRequest.php',
@@ -283,25 +248,7 @@ $wgAutoloadLocalClasses = array(
        'XmlJsCode' => 'includes/Xml.php',
        'XMLReader2' => 'includes/Import.php',
        'XmlSelect' => 'includes/Xml.php',
-       'XmlTypeCheck' => 'includes/XmlTypeCheck.php',
        'ZhClient' => 'includes/ZhClient.php',
-       'ZipDirectoryReader' => 'includes/ZipDirectoryReader.php',
-       'ZipDirectoryReaderError' => 'includes/ZipDirectoryReader.php',
-
-       # content handler
-       'AbstractContent' => 'includes/content/AbstractContent.php',
-       'ContentHandler' => 'includes/content/ContentHandler.php',
-       'Content' => 'includes/content/Content.php',
-       'CssContentHandler' => 'includes/content/CssContentHandler.php',
-       'CssContent' => 'includes/content/CssContent.php',
-       'JavaScriptContentHandler' => 'includes/content/JavaScriptContentHandler.php',
-       'JavaScriptContent' => 'includes/content/JavaScriptContent.php',
-       'MessageContent' => 'includes/content/MessageContent.php',
-       'MWContentSerializationException' => 'includes/content/ContentHandler.php',
-       'TextContentHandler' => 'includes/content/TextContentHandler.php',
-       'TextContent' => 'includes/content/TextContent.php',
-       'WikitextContentHandler' => 'includes/content/WikitextContentHandler.php',
-       'WikitextContent' => 'includes/content/WikitextContent.php',
 
        # includes/actions
        'CachedAction' => 'includes/actions/CachedAction.php',
@@ -440,7 +387,6 @@ $wgAutoloadLocalClasses = array(
        'FileDependency' => 'includes/cache/CacheDependency.php',
        'GenderCache' => 'includes/cache/GenderCache.php',
        'GlobalDependency' => 'includes/cache/CacheDependency.php',
-       'HTMLCacheUpdate' => 'includes/cache/HTMLCacheUpdate.php',
        'HTMLFileCache' => 'includes/cache/HTMLFileCache.php',
        'LinkBatch' => 'includes/cache/LinkBatch.php',
        'LinkCache' => 'includes/cache/LinkCache.php',
@@ -448,7 +394,6 @@ $wgAutoloadLocalClasses = array(
        'ObjectFileCache' => 'includes/cache/ObjectFileCache.php',
        'ProcessCacheLRU' => 'includes/cache/ProcessCacheLRU.php',
        'ResourceFileCache' => 'includes/cache/ResourceFileCache.php',
-       'SquidUpdate' => 'includes/cache/SquidUpdate.php',
        'TitleDependency' => 'includes/cache/CacheDependency.php',
        'TitleListDependency' => 'includes/cache/CacheDependency.php',
 
@@ -463,6 +408,21 @@ $wgAutoloadLocalClasses = array(
        'RedisConnectionPool' => 'includes/clientpool/RedisConnectionPool.php',
        'RedisConnRef' => 'includes/clientpool/RedisConnectionPool.php',
 
+       # includes/content
+       'AbstractContent' => 'includes/content/AbstractContent.php',
+       'ContentHandler' => 'includes/content/ContentHandler.php',
+       'Content' => 'includes/content/Content.php',
+       'CssContentHandler' => 'includes/content/CssContentHandler.php',
+       'CssContent' => 'includes/content/CssContent.php',
+       'JavaScriptContentHandler' => 'includes/content/JavaScriptContentHandler.php',
+       'JavaScriptContent' => 'includes/content/JavaScriptContent.php',
+       'MessageContent' => 'includes/content/MessageContent.php',
+       'MWContentSerializationException' => 'includes/content/ContentHandler.php',
+       'TextContentHandler' => 'includes/content/TextContentHandler.php',
+       'TextContent' => 'includes/content/TextContent.php',
+       'WikitextContentHandler' => 'includes/content/WikitextContentHandler.php',
+       'WikitextContent' => 'includes/content/WikitextContent.php',
+
        # includes/context
        'ContextSource' => 'includes/context/ContextSource.php',
        'DerivativeContext' => 'includes/context/DerivativeContext.php',
@@ -530,6 +490,20 @@ $wgAutoloadLocalClasses = array(
        # includes/debug
        'MWDebug' => 'includes/debug/Debug.php',
 
+       # includes/deferred
+       'DataUpdate' => 'includes/deferred/DataUpdate.php',
+       'DeferrableUpdate' => 'includes/deferred/DeferredUpdates.php',
+       'DeferredUpdates' => 'includes/deferred/DeferredUpdates.php',
+       'HTMLCacheUpdate' => 'includes/deferred/HTMLCacheUpdate.php',
+       'LinksDeletionUpdate' => 'includes/deferred/LinksUpdate.php',
+       'LinksUpdate' => 'includes/deferred/LinksUpdate.php',
+       'MWCallableUpdate' => 'includes/deferred/CallableUpdate.php',
+       'SearchUpdate' => 'includes/deferred/SearchUpdate.php',
+       'SiteStatsUpdate' => 'includes/deferred/SiteStatsUpdate.php',
+       'SqlDataUpdate' => 'includes/deferred/SqlDataUpdate.php',
+       'SquidUpdate' => 'includes/deferred/SquidUpdate.php',
+       'ViewCountUpdate' => 'includes/deferred/ViewCountUpdate.php',
+
        # includes/diff
        '_DiffEngine' => 'includes/diff/DairikiDiff.php',
        '_DiffOp' => 'includes/diff/DairikiDiff.php',
@@ -708,6 +682,8 @@ $wgAutoloadLocalClasses = array(
        'JSParser' => 'includes/libs/jsminplus.php',
        'JSToken' => 'includes/libs/jsminplus.php',
        'JSTokenizer' => 'includes/libs/jsminplus.php',
+       'ScopedPHPTimeout' => 'includes/libs/ScopedPHPTimeout.php',
+       'XmlTypeCheck' => 'includes/libs/XmlTypeCheck.php',
 
        # includes/libs/lessphp
        'lessc' => 'includes/libs/lessc.inc.php',
@@ -851,6 +827,7 @@ $wgAutoloadLocalClasses = array(
 
        # includes/rcfeed
        'RCFeedEngine' => 'includes/rcfeed/RCFeedEngine.php',
+       'RedisPubSubFeedEngine' => 'includes/rcfeed/RedisPubSubFeedEngine.php',
        'UDPRCFeedEngine' => 'includes/rcfeed/UDPRCFeedEngine.php',
        'RCFeedFormatter' => 'includes/rcfeed/RCFeedFormatter.php',
        'IRCColourfulRCFeedFormatter' => 'includes/rcfeed/IRCColourfulRCFeedFormatter.php',
@@ -908,7 +885,6 @@ $wgAutoloadLocalClasses = array(
        'SearchResultSet' => 'includes/search/SearchEngine.php',
        'SearchResultTooMany' => 'includes/search/SearchEngine.php',
        'SearchSqlite' => 'includes/search/SearchSqlite.php',
-       'SearchUpdate' => 'includes/search/SearchUpdate.php',
        'SqliteSearchResultSet' => 'includes/search/SearchSqlite.php',
        'SqlSearchResultSet' => 'includes/search/SearchEngine.php',
 
@@ -973,7 +949,6 @@ $wgAutoloadLocalClasses = array(
        'SpecialBlankpage' => 'includes/specials/SpecialBlankpage.php',
        'SpecialBlock' => 'includes/specials/SpecialBlock.php',
        'SpecialBlockList' => 'includes/specials/SpecialBlockList.php',
-       'SpecialBlockme' => 'includes/specials/SpecialBlockme.php',
        'SpecialBookSources' => 'includes/specials/SpecialBooksources.php',
        'SpecialCachedPage' => 'includes/specials/SpecialCachedPage.php',
        'SpecialCategories' => 'includes/specials/SpecialCategories.php',
@@ -1067,6 +1042,35 @@ $wgAutoloadLocalClasses = array(
        'UploadStashWrongOwnerException' => 'includes/upload/UploadStash.php',
        'UploadStashNoSuchKeyException' => 'includes/upload/UploadStash.php',
 
+       # includes/utils
+       'ArrayUtils' => 'includes/utils/ArrayUtils.php',
+       'CdbFunctions' => 'includes/utils/Cdb_PHP.php',
+       'CdbReader' => 'includes/utils/Cdb.php',
+       'CdbReader_DBA' => 'includes/utils/Cdb.php',
+       'CdbReader_PHP' => 'includes/utils/Cdb_PHP.php',
+       'CdbWriter' => 'includes/utils/Cdb.php',
+       'CdbWriter_DBA' => 'includes/utils/Cdb.php',
+       'CdbWriter_PHP' => 'includes/utils/Cdb_PHP.php',
+       'ConfEditor' => 'includes/utils/ConfEditor.php',
+       'ConfEditorParseError' => 'includes/utils/ConfEditor.php',
+       'ConfEditorToken' => 'includes/utils/ConfEditor.php',
+       'DoubleReplacer' => 'includes/utils/StringUtils.php',
+       'ExplodeIterator' => 'includes/utils/StringUtils.php',
+       'HashRing' => 'includes/utils/HashRing.php',
+       'HashtableReplacer' => 'includes/utils/StringUtils.php',
+       'IP' => 'includes/utils/IP.php',
+       'MWCryptRand' => 'includes/utils/MWCryptRand.php',
+       'MWFunction' => 'includes/utils/MWFunction.php',
+       'MappedIterator' => 'includes/utils/MappedIterator.php',
+       'RegexlikeReplacer' => 'includes/utils/StringUtils.php',
+       'ReplacementArray' => 'includes/utils/StringUtils.php',
+       'Replacer' => 'includes/utils/StringUtils.php',
+       'ScopedCallback' => 'includes/utils/ScopedCallback.php',
+       'StringUtils' => 'includes/utils/StringUtils.php',
+       'UIDGenerator' => 'includes/utils/UIDGenerator.php',
+       'ZipDirectoryReader' => 'includes/utils/ZipDirectoryReader.php',
+       'ZipDirectoryReaderError' => 'includes/utils/ZipDirectoryReader.php',
+
        # languages
        'ConverterRule' => 'languages/LanguageConverter.php',
        'FakeConverter' => 'languages/Language.php',
@@ -1136,6 +1140,8 @@ $wgAutoloadLocalClasses = array(
 );
 
 class AutoLoader {
+       static $autoloadLocalClassesLower = null;
+
        /**
         * autoload - take a class name and attempt to load it
         *
@@ -1145,7 +1151,8 @@ class AutoLoader {
         * as well.
         */
        static function autoload( $className ) {
-               global $wgAutoloadClasses, $wgAutoloadLocalClasses;
+               global $wgAutoloadClasses, $wgAutoloadLocalClasses,
+                       $wgAutoloadAttemptLowercase;
 
                // Workaround for PHP bug <https://bugs.php.net/bug.php?id=49143> (5.3.2. is broken, it's
                // fixed in 5.3.6). Strip leading backslashes from class names. When namespaces are used,
@@ -1160,26 +1167,37 @@ class AutoLoader {
                        $filename = $wgAutoloadLocalClasses[$className];
                } elseif ( isset( $wgAutoloadClasses[$className] ) ) {
                        $filename = $wgAutoloadClasses[$className];
-               } else {
-                       # Try a different capitalisation
-                       # The case can sometimes be wrong when unserializing PHP 4 objects
+               } elseif ( $wgAutoloadAttemptLowercase ) {
+                       /*
+                        * Try a different capitalisation.
+                        *
+                        * PHP 4 objects are always serialized with the classname coerced to lowercase,
+                        * and we are plagued with several legacy uses created by MediaWiki < 1.5, see
+                        * https://wikitech.wikimedia.org/wiki/Text_storage_data
+                        */
                        $filename = false;
                        $lowerClass = strtolower( $className );
 
-                       foreach ( $wgAutoloadLocalClasses as $class2 => $file2 ) {
-                               if ( strtolower( $class2 ) == $lowerClass ) {
-                                       $filename = $file2;
-                               }
+                       if ( self::$autoloadLocalClassesLower === null ) {
+                               self::$autoloadLocalClassesLower = array_change_key_case( $wgAutoloadLocalClasses, CASE_LOWER );
                        }
 
-                       if ( !$filename ) {
+                       if ( isset( self::$autoloadLocalClassesLower[$lowerClass] ) ) {
                                if ( function_exists( 'wfDebug' ) ) {
-                                       wfDebug( "Class {$className} not found; skipped loading\n" );
+                                       wfDebug( "Class {$className} was loaded using incorrect case.\n" );
                                }
+                               $filename = self::$autoloadLocalClassesLower[$lowerClass];
+                       }
+               }
 
-                               # Give up
-                               return false;
+               if ( !$filename ) {
+                       if ( function_exists( 'wfDebug' ) ) {
+                               # FIXME: This is not very polite.  Assume we do not manage the class.
+                               wfDebug( "Class {$className} not found; skipped loading\n" );
                        }
+
+                       # Give up
+                       return false;
                }
 
                # Make an absolute path, this improves performance by avoiding some stat calls
diff --git a/includes/CallableUpdate.php b/includes/CallableUpdate.php
deleted file mode 100644 (file)
index 6eb5541..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-<?php
-
-/**
- * Deferrable Update for closure/callback
- */
-class MWCallableUpdate implements DeferrableUpdate {
-
-       /**
-        * @var closure/callabck
-        */
-       private $callback;
-
-       /**
-        * @param callable $callback
-        */
-       public function __construct( $callback ) {
-               if ( !is_callable( $callback ) ) {
-                       throw new MWException( 'Not a valid callback/closure!' );
-               }
-               $this->callback = $callback;
-       }
-
-       /**
-        * Run the update
-        */
-       public function doUpdate() {
-               call_user_func( $this->callback );
-       }
-
-}
diff --git a/includes/Cdb.php b/includes/Cdb.php
deleted file mode 100644 (file)
index 81c0afe..0000000
+++ /dev/null
@@ -1,184 +0,0 @@
-<?php
-/**
- * Native CDB file reader and writer.
- *
- * 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
- */
-
-/**
- * Read from a CDB file.
- * Native and pure PHP implementations are provided.
- * http://cr.yp.to/cdb.html
- */
-abstract class CdbReader {
-       /**
-        * Open a file and return a subclass instance
-        *
-        * @param $fileName string
-        *
-        * @return CdbReader
-        */
-       public static function open( $fileName ) {
-               if ( self::haveExtension() ) {
-                       return new CdbReader_DBA( $fileName );
-               } else {
-                       wfDebug( "Warning: no dba extension found, using emulation.\n" );
-                       return new CdbReader_PHP( $fileName );
-               }
-       }
-
-       /**
-        * Returns true if the native extension is available
-        *
-        * @return bool
-        */
-       public static function haveExtension() {
-               if ( !function_exists( 'dba_handlers' ) ) {
-                       return false;
-               }
-               $handlers = dba_handlers();
-               if ( !in_array( 'cdb', $handlers ) || !in_array( 'cdb_make', $handlers ) ) {
-                       return false;
-               }
-               return true;
-       }
-
-       /**
-        * Construct the object and open the file
-        */
-       abstract function __construct( $fileName );
-
-       /**
-        * Close the file. Optional, you can just let the variable go out of scope.
-        */
-       abstract function close();
-
-       /**
-        * Get a value with a given key. Only string values are supported.
-        *
-        * @param $key string
-        */
-       abstract public function get( $key );
-}
-
-/**
- * Write to a CDB file.
- * Native and pure PHP implementations are provided.
- */
-abstract class CdbWriter {
-       /**
-        * Open a writer and return a subclass instance.
-        * The user must have write access to the directory, for temporary file creation.
-        *
-        * @param $fileName string
-        *
-        * @return CdbWriter_DBA|CdbWriter_PHP
-        */
-       public static function open( $fileName ) {
-               if ( CdbReader::haveExtension() ) {
-                       return new CdbWriter_DBA( $fileName );
-               } else {
-                       wfDebug( "Warning: no dba extension found, using emulation.\n" );
-                       return new CdbWriter_PHP( $fileName );
-               }
-       }
-
-       /**
-        * Create the object and open the file
-        *
-        * @param $fileName string
-        */
-       abstract function __construct( $fileName );
-
-       /**
-        * Set a key to a given value. The value will be converted to string.
-        * @param $key string
-        * @param $value string
-        */
-       abstract public function set( $key, $value );
-
-       /**
-        * Close the writer object. You should call this function before the object
-        * goes out of scope, to write out the final hashtables.
-        */
-       abstract public function close();
-}
-
-/**
- * Reader class which uses the DBA extension
- */
-class CdbReader_DBA {
-       var $handle;
-
-       function __construct( $fileName ) {
-               $this->handle = dba_open( $fileName, 'r-', 'cdb' );
-               if ( !$this->handle ) {
-                       throw new MWException( 'Unable to open CDB file "' . $fileName . '"' );
-               }
-       }
-
-       function close() {
-               if ( isset( $this->handle ) ) {
-                       dba_close( $this->handle );
-               }
-               unset( $this->handle );
-       }
-
-       function get( $key ) {
-               return dba_fetch( $key, $this->handle );
-       }
-}
-
-/**
- * Writer class which uses the DBA extension
- */
-class CdbWriter_DBA {
-       var $handle, $realFileName, $tmpFileName;
-
-       function __construct( $fileName ) {
-               $this->realFileName = $fileName;
-               $this->tmpFileName = $fileName . '.tmp.' . mt_rand( 0, 0x7fffffff );
-               $this->handle = dba_open( $this->tmpFileName, 'n', 'cdb_make' );
-               if ( !$this->handle ) {
-                       throw new MWException( 'Unable to open CDB file for write "' . $fileName . '"' );
-               }
-       }
-
-       function set( $key, $value ) {
-               return dba_insert( $key, $value, $this->handle );
-       }
-
-       function close() {
-               if ( isset( $this->handle ) ) {
-                       dba_close( $this->handle );
-               }
-               if ( wfIsWindows() ) {
-                       unlink( $this->realFileName );
-               }
-               if ( !rename( $this->tmpFileName, $this->realFileName ) ) {
-                       throw new MWException( 'Unable to move the new CDB file into place.' );
-               }
-               unset( $this->handle );
-       }
-
-       function __destruct() {
-               if ( isset( $this->handle ) ) {
-                       $this->close();
-               }
-       }
-}
diff --git a/includes/Cdb_PHP.php b/includes/Cdb_PHP.php
deleted file mode 100644 (file)
index a38b9a8..0000000
+++ /dev/null
@@ -1,493 +0,0 @@
-<?php
-/**
- * This is a port of D.J. Bernstein's CDB to PHP. It's based on the copy that
- * appears in PHP 5.3. Changes are:
- *    * Error returns replaced with exceptions
- *    * Exception thrown if sizes or offsets are between 2GB and 4GB
- *    * Some variables renamed
- *
- * 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
- */
-
-/**
- * Common functions for readers and writers
- */
-class CdbFunctions {
-       /**
-        * Take a modulo of a signed integer as if it were an unsigned integer.
-        * $b must be less than 0x40000000 and greater than 0
-        *
-        * @param $a
-        * @param $b
-        *
-        * @return int
-        */
-       public static function unsignedMod( $a, $b ) {
-               if ( $a & 0x80000000 ) {
-                       $m = ( $a & 0x7fffffff ) % $b + 2 * ( 0x40000000 % $b );
-                       return $m % $b;
-               } else {
-                       return $a % $b;
-               }
-       }
-
-       /**
-        * Shift a signed integer right as if it were unsigned
-        * @param $a
-        * @param $b
-        * @return int
-        */
-       public static function unsignedShiftRight( $a, $b ) {
-               if ( $b == 0 ) {
-                       return $a;
-               }
-               if ( $a & 0x80000000 ) {
-                       return ( ( $a & 0x7fffffff ) >> $b ) | ( 0x40000000 >> ( $b - 1 ) );
-               } else {
-                       return $a >> $b;
-               }
-       }
-
-       /**
-        * The CDB hash function.
-        *
-        * @param $s string
-        *
-        * @return
-        */
-       public static function hash( $s ) {
-               $h = 5381;
-               for ( $i = 0; $i < strlen( $s ); $i++ ) {
-                       $h5 = ( $h << 5 ) & 0xffffffff;
-                       // Do a 32-bit sum
-                       // Inlined here for speed
-                       $sum = ( $h & 0x3fffffff ) + ( $h5 & 0x3fffffff );
-                       $h =
-                               (
-                                       ( $sum & 0x40000000 ? 1 : 0 )
-                                       + ( $h & 0x80000000 ? 2 : 0 )
-                                       + ( $h & 0x40000000 ? 1 : 0 )
-                                       + ( $h5 & 0x80000000 ? 2 : 0 )
-                                       + ( $h5 & 0x40000000 ? 1 : 0 )
-                               ) << 30
-                               | ( $sum & 0x3fffffff );
-                       $h ^= ord( $s[$i] );
-                       $h &= 0xffffffff;
-               }
-               return $h;
-       }
-}
-
-/**
- * CDB reader class
- */
-class CdbReader_PHP extends CdbReader {
-       /** The filename */
-       var $fileName;
-
-       /** The file handle */
-       var $handle;
-
-       /* number of hash slots searched under this key */
-       var $loop;
-
-       /* initialized if loop is nonzero */
-       var $khash;
-
-       /* initialized if loop is nonzero */
-       var $kpos;
-
-       /* initialized if loop is nonzero */
-       var $hpos;
-
-       /* initialized if loop is nonzero */
-       var $hslots;
-
-       /* initialized if findNext() returns true */
-       var $dpos;
-
-       /* initialized if cdb_findnext() returns 1 */
-       var $dlen;
-
-       /**
-        * @param $fileName string
-        * @throws MWException
-        */
-       function __construct( $fileName ) {
-               $this->fileName = $fileName;
-               $this->handle = fopen( $fileName, 'rb' );
-               if ( !$this->handle ) {
-                       throw new MWException( 'Unable to open CDB file "' . $this->fileName . '".' );
-               }
-               $this->findStart();
-       }
-
-       function close() {
-               if ( isset( $this->handle ) ) {
-                       fclose( $this->handle );
-               }
-               unset( $this->handle );
-       }
-
-       /**
-        * @param $key
-        * @return bool|string
-        */
-       public function get( $key ) {
-               // strval is required
-               if ( $this->find( strval( $key ) ) ) {
-                       return $this->read( $this->dlen, $this->dpos );
-               } else {
-                       return false;
-               }
-       }
-
-       /**
-        * @param $key
-        * @param $pos
-        * @return bool
-        */
-       protected function match( $key, $pos ) {
-               $buf = $this->read( strlen( $key ), $pos );
-               return $buf === $key;
-       }
-
-       protected function findStart() {
-               $this->loop = 0;
-       }
-
-       /**
-        * @throws MWException
-        * @param $length
-        * @param $pos
-        * @return string
-        */
-       protected function read( $length, $pos ) {
-               if ( fseek( $this->handle, $pos ) == -1 ) {
-                       // This can easily happen if the internal pointers are incorrect
-                       throw new MWException(
-                               'Seek failed, file "' . $this->fileName . '" may be corrupted.' );
-               }
-
-               if ( $length == 0 ) {
-                       return '';
-               }
-
-               $buf = fread( $this->handle, $length );
-               if ( $buf === false || strlen( $buf ) !== $length ) {
-                       throw new MWException(
-                               'Read from CDB file failed, file "' . $this->fileName . '" may be corrupted.' );
-               }
-               return $buf;
-       }
-
-       /**
-        * Unpack an unsigned integer and throw an exception if it needs more than 31 bits
-        * @param $s
-        * @throws MWException
-        * @return mixed
-        */
-       protected function unpack31( $s ) {
-               $data = unpack( 'V', $s );
-               if ( $data[1] > 0x7fffffff ) {
-                       throw new MWException(
-                               'Error in CDB file "' . $this->fileName . '", integer too big.' );
-               }
-               return $data[1];
-       }
-
-       /**
-        * Unpack a 32-bit signed integer
-        * @param $s
-        * @return int
-        */
-       protected function unpackSigned( $s ) {
-               $data = unpack( 'va/vb', $s );
-               return $data['a'] | ( $data['b'] << 16 );
-       }
-
-       /**
-        * @param $key
-        * @return bool
-        */
-       protected function findNext( $key ) {
-               if ( !$this->loop ) {
-                       $u = CdbFunctions::hash( $key );
-                       $buf = $this->read( 8, ( $u << 3 ) & 2047 );
-                       $this->hslots = $this->unpack31( substr( $buf, 4 ) );
-                       if ( !$this->hslots ) {
-                               return false;
-                       }
-                       $this->hpos = $this->unpack31( substr( $buf, 0, 4 ) );
-                       $this->khash = $u;
-                       $u = CdbFunctions::unsignedShiftRight( $u, 8 );
-                       $u = CdbFunctions::unsignedMod( $u, $this->hslots );
-                       $u <<= 3;
-                       $this->kpos = $this->hpos + $u;
-               }
-
-               while ( $this->loop < $this->hslots ) {
-                       $buf = $this->read( 8, $this->kpos );
-                       $pos = $this->unpack31( substr( $buf, 4 ) );
-                       if ( !$pos ) {
-                               return false;
-                       }
-                       $this->loop += 1;
-                       $this->kpos += 8;
-                       if ( $this->kpos == $this->hpos + ( $this->hslots << 3 ) ) {
-                               $this->kpos = $this->hpos;
-                       }
-                       $u = $this->unpackSigned( substr( $buf, 0, 4 ) );
-                       if ( $u === $this->khash ) {
-                               $buf = $this->read( 8, $pos );
-                               $keyLen = $this->unpack31( substr( $buf, 0, 4 ) );
-                               if ( $keyLen == strlen( $key ) && $this->match( $key, $pos + 8 ) ) {
-                                       // Found
-                                       $this->dlen = $this->unpack31( substr( $buf, 4 ) );
-                                       $this->dpos = $pos + 8 + $keyLen;
-                                       return true;
-                               }
-                       }
-               }
-               return false;
-       }
-
-       /**
-        * @param $key
-        * @return bool
-        */
-       protected function find( $key ) {
-               $this->findStart();
-               return $this->findNext( $key );
-       }
-}
-
-/**
- * CDB writer class
- */
-class CdbWriter_PHP extends CdbWriter {
-       var $handle, $realFileName, $tmpFileName;
-
-       var $hplist;
-       var $numentries, $pos;
-
-       /**
-        * @param $fileName string
-        */
-       function __construct( $fileName ) {
-               $this->realFileName = $fileName;
-               $this->tmpFileName = $fileName . '.tmp.' . mt_rand( 0, 0x7fffffff );
-               $this->handle = fopen( $this->tmpFileName, 'wb' );
-               if ( !$this->handle ) {
-                       $this->throwException(
-                               'Unable to open CDB file "' . $this->tmpFileName . '" for write.' );
-               }
-               $this->hplist = array();
-               $this->numentries = 0;
-               $this->pos = 2048; // leaving space for the pointer array, 256 * 8
-               if ( fseek( $this->handle, $this->pos ) == -1 ) {
-                       $this->throwException( 'fseek failed in file "' . $this->tmpFileName . '".' );
-               }
-       }
-
-       function __destruct() {
-               if ( isset( $this->handle ) ) {
-                       $this->close();
-               }
-       }
-
-       /**
-        * @param $key
-        * @param $value
-        * @return
-        */
-       public function set( $key, $value ) {
-               if ( strval( $key ) === '' ) {
-                       // DBA cross-check hack
-                       return;
-               }
-               $this->addbegin( strlen( $key ), strlen( $value ) );
-               $this->write( $key );
-               $this->write( $value );
-               $this->addend( strlen( $key ), strlen( $value ), CdbFunctions::hash( $key ) );
-       }
-
-       /**
-        * @throws MWException
-        */
-       public function close() {
-               $this->finish();
-               if ( isset( $this->handle ) ) {
-                       fclose( $this->handle );
-               }
-               if ( wfIsWindows() && file_exists( $this->realFileName ) ) {
-                       unlink( $this->realFileName );
-               }
-               if ( !rename( $this->tmpFileName, $this->realFileName ) ) {
-                       $this->throwException( 'Unable to move the new CDB file into place.' );
-               }
-               unset( $this->handle );
-       }
-
-       /**
-        * @throws MWException
-        * @param $buf
-        */
-       protected function write( $buf ) {
-               $len = fwrite( $this->handle, $buf );
-               if ( $len !== strlen( $buf ) ) {
-                       $this->throwException( 'Error writing to CDB file "' . $this->tmpFileName . '".' );
-               }
-       }
-
-       /**
-        * @throws MWException
-        * @param $len
-        */
-       protected function posplus( $len ) {
-               $newpos = $this->pos + $len;
-               if ( $newpos > 0x7fffffff ) {
-                       $this->throwException(
-                               'A value in the CDB file "' . $this->tmpFileName . '" is too large.' );
-               }
-               $this->pos = $newpos;
-       }
-
-       /**
-        * @param $keylen
-        * @param $datalen
-        * @param $h
-        */
-       protected function addend( $keylen, $datalen, $h ) {
-               $this->hplist[] = array(
-                       'h' => $h,
-                       'p' => $this->pos
-               );
-
-               $this->numentries++;
-               $this->posplus( 8 );
-               $this->posplus( $keylen );
-               $this->posplus( $datalen );
-       }
-
-       /**
-        * @throws MWException
-        * @param $keylen
-        * @param $datalen
-        */
-       protected function addbegin( $keylen, $datalen ) {
-               if ( $keylen > 0x7fffffff ) {
-                       $this->throwException( 'Key length too long in file "' . $this->tmpFileName . '".' );
-               }
-               if ( $datalen > 0x7fffffff ) {
-                       $this->throwException( 'Data length too long in file "' . $this->tmpFileName . '".' );
-               }
-               $buf = pack( 'VV', $keylen, $datalen );
-               $this->write( $buf );
-       }
-
-       /**
-        * @throws MWException
-        */
-       protected function finish() {
-               // Hack for DBA cross-check
-               $this->hplist = array_reverse( $this->hplist );
-
-               // Calculate the number of items that will be in each hashtable
-               $counts = array_fill( 0, 256, 0 );
-               foreach ( $this->hplist as $item ) {
-                       ++ $counts[255 & $item['h']];
-               }
-
-               // Fill in $starts with the *end* indexes
-               $starts = array();
-               $pos = 0;
-               for ( $i = 0; $i < 256; ++$i ) {
-                       $pos += $counts[$i];
-                       $starts[$i] = $pos;
-               }
-
-               // Excessively clever and indulgent code to simultaneously fill $packedTables
-               // with the packed hashtables, and adjust the elements of $starts
-               // to actually point to the starts instead of the ends.
-               $packedTables = array_fill( 0, $this->numentries, false );
-               foreach ( $this->hplist as $item ) {
-                       $packedTables[--$starts[255 & $item['h']]] = $item;
-               }
-
-               $final = '';
-               for ( $i = 0; $i < 256; ++$i ) {
-                       $count = $counts[$i];
-
-                       // The size of the hashtable will be double the item count.
-                       // The rest of the slots will be empty.
-                       $len = $count + $count;
-                       $final .= pack( 'VV', $this->pos, $len );
-
-                       $hashtable = array();
-                       for ( $u = 0; $u < $len; ++$u ) {
-                               $hashtable[$u] = array( 'h' => 0, 'p' => 0 );
-                       }
-
-                       // Fill the hashtable, using the next empty slot if the hashed slot
-                       // is taken.
-                       for ( $u = 0; $u < $count; ++$u ) {
-                               $hp = $packedTables[$starts[$i] + $u];
-                               $where = CdbFunctions::unsignedMod(
-                                       CdbFunctions::unsignedShiftRight( $hp['h'], 8 ), $len );
-                               while ( $hashtable[$where]['p'] ) {
-                                       if ( ++$where == $len ) {
-                                               $where = 0;
-                                       }
-                               }
-                               $hashtable[$where] = $hp;
-                       }
-
-                       // Write the hashtable
-                       for ( $u = 0; $u < $len; ++$u ) {
-                               $buf = pack( 'vvV',
-                                       $hashtable[$u]['h'] & 0xffff,
-                                       CdbFunctions::unsignedShiftRight( $hashtable[$u]['h'], 16 ),
-                                       $hashtable[$u]['p'] );
-                               $this->write( $buf );
-                               $this->posplus( 8 );
-                       }
-               }
-
-               // Write the pointer array at the start of the file
-               rewind( $this->handle );
-               if ( ftell( $this->handle ) != 0 ) {
-                       $this->throwException( 'Error rewinding to start of file "' . $this->tmpFileName . '".' );
-               }
-               $this->write( $final );
-       }
-
-       /**
-        * Clean up the temp file and throw an exception
-        *
-        * @param $msg string
-        * @throws MWException
-        */
-       protected function throwException( $msg ) {
-               if ( $this->handle ) {
-                       fclose( $this->handle );
-                       unlink( $this->tmpFileName );
-               }
-               throw new MWException( $msg );
-       }
-}
index 3fc27f9..7ec641d 100644 (file)
@@ -232,7 +232,7 @@ class ChangeTags {
                }
 
                $data = array( Html::rawElement( 'label', array( 'for' => 'tagfilter' ), wfMessage( 'tag-filter' )->parse() ),
-                       Xml::input( 'tagfilter', 20, $selected, array( 'class' => 'mw-tagfilter-input' ) ) );
+                       Xml::input( 'tagfilter', 20, $selected, array( 'class' => 'mw-tagfilter-input', 'id' => 'tagfilter' ) ) );
 
                if ( !$fullForm ) {
                        return $data;
diff --git a/includes/ConfEditor.php b/includes/ConfEditor.php
deleted file mode 100644 (file)
index 67cb87d..0000000
+++ /dev/null
@@ -1,1109 +0,0 @@
-<?php
-/**
- * Configuration file editor.
- *
- * 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
- */
-
-/**
- * This is a state machine style parser with two internal stacks:
- *   * A next state stack, which determines the state the machine will progress to next
- *   * A path stack, which keeps track of the logical location in the file.
- *
- * Reference grammar:
- *
- * file = T_OPEN_TAG *statement
- * statement = T_VARIABLE "=" expression ";"
- * expression = array / scalar / T_VARIABLE
- * array = T_ARRAY "(" [ element *( "," element ) [ "," ] ] ")"
- * element = assoc-element / expression
- * assoc-element = scalar T_DOUBLE_ARROW expression
- * scalar = T_LNUMBER / T_DNUMBER / T_STRING / T_CONSTANT_ENCAPSED_STRING
- */
-class ConfEditor {
-       /** The text to parse */
-       var $text;
-
-       /** The token array from token_get_all() */
-       var $tokens;
-
-       /** The current position in the token array */
-       var $pos;
-
-       /** The current 1-based line number */
-       var $lineNum;
-
-       /** The current 1-based column number */
-       var $colNum;
-
-       /** The current 0-based byte number */
-       var $byteNum;
-
-       /** The current ConfEditorToken object */
-       var $currentToken;
-
-       /** The previous ConfEditorToken object */
-       var $prevToken;
-
-       /**
-        * The state machine stack. This is an array of strings where the topmost
-        * element will be popped off and become the next parser state.
-        */
-       var $stateStack;
-
-       /**
-        * The path stack is a stack of associative arrays with the following elements:
-        *    name              The name of top level of the path
-        *    level             The level (number of elements) of the path
-        *    startByte         The byte offset of the start of the path
-        *    startToken        The token offset of the start
-        *    endByte           The byte offset of thee
-        *    endToken          The token offset of the end, plus one
-        *    valueStartToken   The start token offset of the value part
-        *    valueStartByte    The start byte offset of the value part
-        *    valueEndToken     The end token offset of the value part, plus one
-        *    valueEndByte      The end byte offset of the value part, plus one
-        *    nextArrayIndex    The next numeric array index at this level
-        *    hasComma          True if the array element ends with a comma
-        *    arrowByte         The byte offset of the "=>", or false if there isn't one
-        */
-       var $pathStack;
-
-       /**
-        * The elements of the top of the pathStack for every path encountered, indexed
-        * by slash-separated path.
-        */
-       var $pathInfo;
-
-       /**
-        * Next serial number for whitespace placeholder paths (\@extra-N)
-        */
-       var $serial;
-
-       /**
-        * Editor state. This consists of the internal copy/insert operations which
-        * are applied to the source string to obtain the destination string.
-        */
-       var $edits;
-
-       /**
-        * Simple entry point for command-line testing
-        *
-        * @param $text string
-        *
-        * @return string
-        */
-       static function test( $text ) {
-               try {
-                       $ce = new self( $text );
-                       $ce->parse();
-               } catch ( ConfEditorParseError $e ) {
-                       return $e->getMessage() . "\n" . $e->highlight( $text );
-               }
-               return "OK";
-       }
-
-       /**
-        * Construct a new parser
-        */
-       public function __construct( $text ) {
-               $this->text = $text;
-       }
-
-       /**
-        * Edit the text. Returns the edited text.
-        * @param array $ops of operations.
-        *
-        * Operations are given as an associative array, with members:
-        *    type:     One of delete, set, append or insert (required)
-        *    path:     The path to operate on (required)
-        *    key:      The array key to insert/append, with PHP quotes
-        *    value:    The value, with PHP quotes
-        *
-        * delete
-        *    Deletes an array element or statement with the specified path.
-        *    e.g.
-        *        array('type' => 'delete', 'path' => '$foo/bar/baz' )
-        *    is equivalent to the runtime PHP code:
-        *        unset( $foo['bar']['baz'] );
-        *
-        * set
-        *    Sets the value of an array element. If the element doesn't exist, it
-        *    is appended to the array. If it does exist, the value is set, with
-        *    comments and indenting preserved.
-        *
-        * append
-        *    Appends a new element to the end of the array. Adds a trailing comma.
-        *    e.g.
-        *        array( 'type' => 'append', 'path', '$foo/bar',
-        *            'key' => 'baz', 'value' => "'x'" )
-        *    is like the PHP code:
-        *        $foo['bar']['baz'] = 'x';
-        *
-        * insert
-        *    Insert a new element at the start of the array.
-        *
-        * @throws MWException
-        * @return string
-        */
-       public function edit( $ops ) {
-               $this->parse();
-
-               $this->edits = array(
-                       array( 'copy', 0, strlen( $this->text ) )
-               );
-               foreach ( $ops as $op ) {
-                       $type = $op['type'];
-                       $path = $op['path'];
-                       $value = isset( $op['value'] ) ? $op['value'] : null;
-                       $key = isset( $op['key'] ) ? $op['key'] : null;
-
-                       switch ( $type ) {
-                       case 'delete':
-                               list( $start, $end ) = $this->findDeletionRegion( $path );
-                               $this->replaceSourceRegion( $start, $end, false );
-                               break;
-                       case 'set':
-                               if ( isset( $this->pathInfo[$path] ) ) {
-                                       list( $start, $end ) = $this->findValueRegion( $path );
-                                       $encValue = $value; // var_export( $value, true );
-                                       $this->replaceSourceRegion( $start, $end, $encValue );
-                                       break;
-                               }
-                               // No existing path, fall through to append
-                               $slashPos = strrpos( $path, '/' );
-                               $key = var_export( substr( $path, $slashPos + 1 ), true );
-                               $path = substr( $path, 0, $slashPos );
-                               // Fall through
-                       case 'append':
-                               // Find the last array element
-                               $lastEltPath = $this->findLastArrayElement( $path );
-                               if ( $lastEltPath === false ) {
-                                       throw new MWException( "Can't find any element of array \"$path\"" );
-                               }
-                               $lastEltInfo = $this->pathInfo[$lastEltPath];
-
-                               // Has it got a comma already?
-                               if ( strpos( $lastEltPath, '@extra' ) === false && !$lastEltInfo['hasComma'] ) {
-                                       // No comma, insert one after the value region
-                                       list( , $end ) = $this->findValueRegion( $lastEltPath );
-                                       $this->replaceSourceRegion( $end - 1, $end - 1, ',' );
-                               }
-
-                               // Make the text to insert
-                               list( $start, $end ) = $this->findDeletionRegion( $lastEltPath );
-
-                               if ( $key === null ) {
-                                       list( $indent, ) = $this->getIndent( $start );
-                                       $textToInsert = "$indent$value,";
-                               } else {
-                                       list( $indent, $arrowIndent ) =
-                                               $this->getIndent( $start, $key, $lastEltInfo['arrowByte'] );
-                                       $textToInsert = "$indent$key$arrowIndent=> $value,";
-                               }
-                               $textToInsert .= ( $indent === false ? ' ' : "\n" );
-
-                               // Insert the item
-                               $this->replaceSourceRegion( $end, $end, $textToInsert );
-                               break;
-                       case 'insert':
-                               // Find first array element
-                               $firstEltPath = $this->findFirstArrayElement( $path );
-                               if ( $firstEltPath === false ) {
-                                       throw new MWException( "Can't find array element of \"$path\"" );
-                               }
-                               list( $start, ) = $this->findDeletionRegion( $firstEltPath );
-                               $info = $this->pathInfo[$firstEltPath];
-
-                               // Make the text to insert
-                               if ( $key === null ) {
-                                       list( $indent, ) = $this->getIndent( $start );
-                                       $textToInsert = "$indent$value,";
-                               } else {
-                                       list( $indent, $arrowIndent ) =
-                                               $this->getIndent( $start, $key, $info['arrowByte'] );
-                                       $textToInsert = "$indent$key$arrowIndent=> $value,";
-                               }
-                               $textToInsert .= ( $indent === false ? ' ' : "\n" );
-
-                               // Insert the item
-                               $this->replaceSourceRegion( $start, $start, $textToInsert );
-                               break;
-                       default:
-                               throw new MWException( "Unrecognised operation: \"$type\"" );
-                       }
-               }
-
-               // Do the edits
-               $out = '';
-               foreach ( $this->edits as $edit ) {
-                       if ( $edit[0] == 'copy' ) {
-                               $out .= substr( $this->text, $edit[1], $edit[2] - $edit[1] );
-                       } else { // if ( $edit[0] == 'insert' )
-                               $out .= $edit[1];
-                       }
-               }
-
-               // Do a second parse as a sanity check
-               $this->text = $out;
-               try {
-                       $this->parse();
-               } catch ( ConfEditorParseError $e ) {
-                       throw new MWException(
-                               "Sorry, ConfEditor broke the file during editing and it won't parse anymore: " .
-                               $e->getMessage() );
-               }
-               return $out;
-       }
-
-       /**
-        * Get the variables defined in the text
-        * @return array( varname => value )
-        */
-       function getVars() {
-               $vars = array();
-               $this->parse();
-               foreach ( $this->pathInfo as $path => $data ) {
-                       if ( $path[0] != '$' ) {
-                               continue;
-                       }
-                       $trimmedPath = substr( $path, 1 );
-                       $name = $data['name'];
-                       if ( $name[0] == '@' ) {
-                               continue;
-                       }
-                       if ( $name[0] == '$' ) {
-                               $name = substr( $name, 1 );
-                       }
-                       $parentPath = substr( $trimmedPath, 0,
-                               strlen( $trimmedPath ) - strlen( $name ) );
-                       if ( substr( $parentPath, -1 ) == '/' ) {
-                               $parentPath = substr( $parentPath, 0, -1 );
-                       }
-
-                       $value = substr( $this->text, $data['valueStartByte'],
-                               $data['valueEndByte'] - $data['valueStartByte']
-                       );
-                       $this->setVar( $vars, $parentPath, $name,
-                               $this->parseScalar( $value ) );
-               }
-               return $vars;
-       }
-
-       /**
-        * Set a value in an array, unless it's set already. For instance,
-        * setVar( $arr, 'foo/bar', 'baz', 3 ); will set
-        * $arr['foo']['bar']['baz'] = 3;
-        * @param $array array
-        * @param string $path slash-delimited path
-        * @param $key mixed Key
-        * @param $value mixed Value
-        */
-       function setVar( &$array, $path, $key, $value ) {
-               $pathArr = explode( '/', $path );
-               $target =& $array;
-               if ( $path !== '' ) {
-                       foreach ( $pathArr as $p ) {
-                               if ( !isset( $target[$p] ) ) {
-                                       $target[$p] = array();
-                               }
-                               $target =& $target[$p];
-                       }
-               }
-               if ( !isset( $target[$key] ) ) {
-                       $target[$key] = $value;
-               }
-       }
-
-       /**
-        * Parse a scalar value in PHP
-        * @return mixed Parsed value
-        */
-       function parseScalar( $str ) {
-               if ( $str !== '' && $str[0] == '\'' ) {
-                       // Single-quoted string
-                       // @todo FIXME: trim() call is due to mystery bug where whitespace gets
-                       // appended to the token; without it we ended up reading in the
-                       // extra quote on the end!
-                       return strtr( substr( trim( $str ), 1, -1 ),
-                               array( '\\\'' => '\'', '\\\\' => '\\' ) );
-               }
-               if ( $str !== '' && $str[0] == '"' ) {
-                       // Double-quoted string
-                       // @todo FIXME: trim() call is due to mystery bug where whitespace gets
-                       // appended to the token; without it we ended up reading in the
-                       // extra quote on the end!
-                       return stripcslashes( substr( trim( $str ), 1, -1 ) );
-               }
-               if ( substr( $str, 0, 4 ) == 'true' ) {
-                       return true;
-               }
-               if ( substr( $str, 0, 5 ) == 'false' ) {
-                       return false;
-               }
-               if ( substr( $str, 0, 4 ) == 'null' ) {
-                       return null;
-               }
-               // Must be some kind of numeric value, so let PHP's weak typing
-               // be useful for a change
-               return $str;
-       }
-
-       /**
-        * Replace the byte offset region of the source with $newText.
-        * Works by adding elements to the $this->edits array.
-        */
-       function replaceSourceRegion( $start, $end, $newText = false ) {
-               // Split all copy operations with a source corresponding to the region
-               // in question.
-               $newEdits = array();
-               foreach ( $this->edits as $edit ) {
-                       if ( $edit[0] !== 'copy' ) {
-                               $newEdits[] = $edit;
-                               continue;
-                       }
-                       $copyStart = $edit[1];
-                       $copyEnd = $edit[2];
-                       if ( $start >= $copyEnd || $end <= $copyStart ) {
-                               // Outside this region
-                               $newEdits[] = $edit;
-                               continue;
-                       }
-                       if ( ( $start < $copyStart && $end > $copyStart )
-                               || ( $start < $copyEnd && $end > $copyEnd )
-                       ) {
-                               throw new MWException( "Overlapping regions found, can't do the edit" );
-                       }
-                       // Split the copy
-                       $newEdits[] = array( 'copy', $copyStart, $start );
-                       if ( $newText !== false ) {
-                               $newEdits[] = array( 'insert', $newText );
-                       }
-                       $newEdits[] = array( 'copy', $end, $copyEnd );
-               }
-               $this->edits = $newEdits;
-       }
-
-       /**
-        * Finds the source byte region which you would want to delete, if $pathName
-        * was to be deleted. Includes the leading spaces and tabs, the trailing line
-        * break, and any comments in between.
-        * @param $pathName
-        * @throws MWException
-        * @return array
-        */
-       function findDeletionRegion( $pathName ) {
-               if ( !isset( $this->pathInfo[$pathName] ) ) {
-                       throw new MWException( "Can't find path \"$pathName\"" );
-               }
-               $path = $this->pathInfo[$pathName];
-               // Find the start
-               $this->firstToken();
-               while ( $this->pos != $path['startToken'] ) {
-                       $this->nextToken();
-               }
-               $regionStart = $path['startByte'];
-               for ( $offset = -1; $offset >= -$this->pos; $offset-- ) {
-                       $token = $this->getTokenAhead( $offset );
-                       if ( !$token->isSkip() ) {
-                               // If there is other content on the same line, don't move the start point
-                               // back, because that will cause the regions to overlap.
-                               $regionStart = $path['startByte'];
-                               break;
-                       }
-                       $lfPos = strrpos( $token->text, "\n" );
-                       if ( $lfPos === false ) {
-                               $regionStart -= strlen( $token->text );
-                       } else {
-                               // The line start does not include the LF
-                               $regionStart -= strlen( $token->text ) - $lfPos - 1;
-                               break;
-                       }
-               }
-               // Find the end
-               while ( $this->pos != $path['endToken'] ) {
-                       $this->nextToken();
-               }
-               $regionEnd = $path['endByte']; // past the end
-               for ( $offset = 0; $offset < count( $this->tokens ) - $this->pos; $offset++ ) {
-                       $token = $this->getTokenAhead( $offset );
-                       if ( !$token->isSkip() ) {
-                               break;
-                       }
-                       $lfPos = strpos( $token->text, "\n" );
-                       if ( $lfPos === false ) {
-                               $regionEnd += strlen( $token->text );
-                       } else {
-                               // This should point past the LF
-                               $regionEnd += $lfPos + 1;
-                               break;
-                       }
-               }
-               return array( $regionStart, $regionEnd );
-       }
-
-       /**
-        * Find the byte region in the source corresponding to the value part.
-        * This includes the quotes, but does not include the trailing comma
-        * or semicolon.
-        *
-        * The end position is the past-the-end (end + 1) value as per convention.
-        * @param $pathName
-        * @throws MWException
-        * @return array
-        */
-       function findValueRegion( $pathName ) {
-               if ( !isset( $this->pathInfo[$pathName] ) ) {
-                       throw new MWException( "Can't find path \"$pathName\"" );
-               }
-               $path = $this->pathInfo[$pathName];
-               if ( $path['valueStartByte'] === false || $path['valueEndByte'] === false ) {
-                       throw new MWException( "Can't find value region for path \"$pathName\"" );
-               }
-               return array( $path['valueStartByte'], $path['valueEndByte'] );
-       }
-
-       /**
-        * Find the path name of the last element in the array.
-        * If the array is empty, this will return the \@extra interstitial element.
-        * If the specified path is not found or is not an array, it will return false.
-        * @return bool|int|string
-        */
-       function findLastArrayElement( $path ) {
-               // Try for a real element
-               $lastEltPath = false;
-               foreach ( $this->pathInfo as $candidatePath => $info ) {
-                       $part1 = substr( $candidatePath, 0, strlen( $path ) + 1 );
-                       $part2 = substr( $candidatePath, strlen( $path ) + 1, 1 );
-                       if ( $part2 == '@' ) {
-                               // Do nothing
-                       } elseif ( $part1 == "$path/" ) {
-                               $lastEltPath = $candidatePath;
-                       } elseif ( $lastEltPath !== false ) {
-                               break;
-                       }
-               }
-               if ( $lastEltPath !== false ) {
-                       return $lastEltPath;
-               }
-
-               // Try for an interstitial element
-               $extraPath = false;
-               foreach ( $this->pathInfo as $candidatePath => $info ) {
-                       $part1 = substr( $candidatePath, 0, strlen( $path ) + 1 );
-                       if ( $part1 == "$path/" ) {
-                               $extraPath = $candidatePath;
-                       } elseif ( $extraPath !== false ) {
-                               break;
-                       }
-               }
-               return $extraPath;
-       }
-
-       /**
-        * Find the path name of first element in the array.
-        * If the array is empty, this will return the \@extra interstitial element.
-        * If the specified path is not found or is not an array, it will return false.
-        * @return bool|int|string
-        */
-       function findFirstArrayElement( $path ) {
-               // Try for an ordinary element
-               foreach ( $this->pathInfo as $candidatePath => $info ) {
-                       $part1 = substr( $candidatePath, 0, strlen( $path ) + 1 );
-                       $part2 = substr( $candidatePath, strlen( $path ) + 1, 1 );
-                       if ( $part1 == "$path/" && $part2 != '@' ) {
-                               return $candidatePath;
-                       }
-               }
-
-               // Try for an interstitial element
-               foreach ( $this->pathInfo as $candidatePath => $info ) {
-                       $part1 = substr( $candidatePath, 0, strlen( $path ) + 1 );
-                       if ( $part1 == "$path/" ) {
-                               return $candidatePath;
-                       }
-               }
-               return false;
-       }
-
-       /**
-        * Get the indent string which sits after a given start position.
-        * Returns false if the position is not at the start of the line.
-        * @return array
-        */
-       function getIndent( $pos, $key = false, $arrowPos = false ) {
-               $arrowIndent = ' ';
-               if ( $pos == 0 || $this->text[$pos - 1] == "\n" ) {
-                       $indentLength = strspn( $this->text, " \t", $pos );
-                       $indent = substr( $this->text, $pos, $indentLength );
-               } else {
-                       $indent = false;
-               }
-               if ( $indent !== false && $arrowPos !== false ) {
-                       $arrowIndentLength = $arrowPos - $pos - $indentLength - strlen( $key );
-                       if ( $arrowIndentLength > 0 ) {
-                               $arrowIndent = str_repeat( ' ', $arrowIndentLength );
-                       }
-               }
-               return array( $indent, $arrowIndent );
-       }
-
-       /**
-        * Run the parser on the text. Throws an exception if the string does not
-        * match our defined subset of PHP syntax.
-        */
-       public function parse() {
-               $this->initParse();
-               $this->pushState( 'file' );
-               $this->pushPath( '@extra-' . ( $this->serial++ ) );
-               $token = $this->firstToken();
-
-               while ( !$token->isEnd() ) {
-                       $state = $this->popState();
-                       if ( !$state ) {
-                               $this->error( 'internal error: empty state stack' );
-                       }
-
-                       switch ( $state ) {
-                       case 'file':
-                               $this->expect( T_OPEN_TAG );
-                               $token = $this->skipSpace();
-                               if ( $token->isEnd() ) {
-                                       break 2;
-                               }
-                               $this->pushState( 'statement', 'file 2' );
-                               break;
-                       case 'file 2':
-                               $token = $this->skipSpace();
-                               if ( $token->isEnd() ) {
-                                       break 2;
-                               }
-                               $this->pushState( 'statement', 'file 2' );
-                               break;
-                       case 'statement':
-                               $token = $this->skipSpace();
-                               if ( !$this->validatePath( $token->text ) ) {
-                                       $this->error( "Invalid variable name \"{$token->text}\"" );
-                               }
-                               $this->nextPath( $token->text );
-                               $this->expect( T_VARIABLE );
-                               $this->skipSpace();
-                               $arrayAssign = false;
-                               if ( $this->currentToken()->type == '[' ) {
-                                       $this->nextToken();
-                                       $token = $this->skipSpace();
-                                       if ( !$token->isScalar() ) {
-                                               $this->error( "expected a string or number for the array key" );
-                                       }
-                                       if ( $token->type == T_CONSTANT_ENCAPSED_STRING ) {
-                                               $text = $this->parseScalar( $token->text );
-                                       } else {
-                                               $text = $token->text;
-                                       }
-                                       if ( !$this->validatePath( $text ) ) {
-                                               $this->error( "Invalid associative array name \"$text\"" );
-                                       }
-                                       $this->pushPath( $text );
-                                       $this->nextToken();
-                                       $this->skipSpace();
-                                       $this->expect( ']' );
-                                       $this->skipSpace();
-                                       $arrayAssign = true;
-                               }
-                               $this->expect( '=' );
-                               $this->skipSpace();
-                               $this->startPathValue();
-                               if ( $arrayAssign ) {
-                                       $this->pushState( 'expression', 'array assign end' );
-                               } else {
-                                       $this->pushState( 'expression', 'statement end' );
-                               }
-                               break;
-                       case 'array assign end':
-                       case 'statement end':
-                               $this->endPathValue();
-                               if ( $state == 'array assign end' ) {
-                                       $this->popPath();
-                               }
-                               $this->skipSpace();
-                               $this->expect( ';' );
-                               $this->nextPath( '@extra-' . ( $this->serial++ ) );
-                               break;
-                       case 'expression':
-                               $token = $this->skipSpace();
-                               if ( $token->type == T_ARRAY ) {
-                                       $this->pushState( 'array' );
-                               } elseif ( $token->isScalar() ) {
-                                       $this->nextToken();
-                               } elseif ( $token->type == T_VARIABLE ) {
-                                       $this->nextToken();
-                               } else {
-                                       $this->error( "expected simple expression" );
-                               }
-                               break;
-                       case 'array':
-                               $this->skipSpace();
-                               $this->expect( T_ARRAY );
-                               $this->skipSpace();
-                               $this->expect( '(' );
-                               $this->skipSpace();
-                               $this->pushPath( '@extra-' . ( $this->serial++ ) );
-                               if ( $this->isAhead( ')' ) ) {
-                                       // Empty array
-                                       $this->pushState( 'array end' );
-                               } else {
-                                       $this->pushState( 'element', 'array end' );
-                               }
-                               break;
-                       case 'array end':
-                               $this->skipSpace();
-                               $this->popPath();
-                               $this->expect( ')' );
-                               break;
-                       case 'element':
-                               $token = $this->skipSpace();
-                               // Look ahead to find the double arrow
-                               if ( $token->isScalar() && $this->isAhead( T_DOUBLE_ARROW, 1 ) ) {
-                                       // Found associative element
-                                       $this->pushState( 'assoc-element', 'element end' );
-                               } else {
-                                       // Not associative
-                                       $this->nextPath( '@next' );
-                                       $this->startPathValue();
-                                       $this->pushState( 'expression', 'element end' );
-                               }
-                               break;
-                       case 'element end':
-                               $token = $this->skipSpace();
-                               if ( $token->type == ',' ) {
-                                       $this->endPathValue();
-                                       $this->markComma();
-                                       $this->nextToken();
-                                       $this->nextPath( '@extra-' . ( $this->serial++ ) );
-                                       // Look ahead to find ending bracket
-                                       if ( $this->isAhead( ")" ) ) {
-                                               // Found ending bracket, no continuation
-                                               $this->skipSpace();
-                                       } else {
-                                               // No ending bracket, continue to next element
-                                               $this->pushState( 'element' );
-                                       }
-                               } elseif ( $token->type == ')' ) {
-                                       // End array
-                                       $this->endPathValue();
-                               } else {
-                                       $this->error( "expected the next array element or the end of the array" );
-                               }
-                               break;
-                       case 'assoc-element':
-                               $token = $this->skipSpace();
-                               if ( !$token->isScalar() ) {
-                                       $this->error( "expected a string or number for the array key" );
-                               }
-                               if ( $token->type == T_CONSTANT_ENCAPSED_STRING ) {
-                                       $text = $this->parseScalar( $token->text );
-                               } else {
-                                       $text = $token->text;
-                               }
-                               if ( !$this->validatePath( $text ) ) {
-                                       $this->error( "Invalid associative array name \"$text\"" );
-                               }
-                               $this->nextPath( $text );
-                               $this->nextToken();
-                               $this->skipSpace();
-                               $this->markArrow();
-                               $this->expect( T_DOUBLE_ARROW );
-                               $this->skipSpace();
-                               $this->startPathValue();
-                               $this->pushState( 'expression' );
-                               break;
-                       }
-               }
-               if ( count( $this->stateStack ) ) {
-                       $this->error( 'unexpected end of file' );
-               }
-               $this->popPath();
-       }
-
-       /**
-        * Initialise a parse.
-        */
-       protected function initParse() {
-               $this->tokens = token_get_all( $this->text );
-               $this->stateStack = array();
-               $this->pathStack = array();
-               $this->firstToken();
-               $this->pathInfo = array();
-               $this->serial = 1;
-       }
-
-       /**
-        * Set the parse position. Do not call this except from firstToken() and
-        * nextToken(), there is more to update than just the position.
-        */
-       protected function setPos( $pos ) {
-               $this->pos = $pos;
-               if ( $this->pos >= count( $this->tokens ) ) {
-                       $this->currentToken = ConfEditorToken::newEnd();
-               } else {
-                       $this->currentToken = $this->newTokenObj( $this->tokens[$this->pos] );
-               }
-               return $this->currentToken;
-       }
-
-       /**
-        * Create a ConfEditorToken from an element of token_get_all()
-        * @return ConfEditorToken
-        */
-       function newTokenObj( $internalToken ) {
-               if ( is_array( $internalToken ) ) {
-                       return new ConfEditorToken( $internalToken[0], $internalToken[1] );
-               } else {
-                       return new ConfEditorToken( $internalToken, $internalToken );
-               }
-       }
-
-       /**
-        * Reset the parse position
-        */
-       function firstToken() {
-               $this->setPos( 0 );
-               $this->prevToken = ConfEditorToken::newEnd();
-               $this->lineNum = 1;
-               $this->colNum = 1;
-               $this->byteNum = 0;
-               return $this->currentToken;
-       }
-
-       /**
-        * Get the current token
-        */
-       function currentToken() {
-               return $this->currentToken;
-       }
-
-       /**
-        * Advance the current position and return the resulting next token
-        */
-       function nextToken() {
-               if ( $this->currentToken ) {
-                       $text = $this->currentToken->text;
-                       $lfCount = substr_count( $text, "\n" );
-                       if ( $lfCount ) {
-                               $this->lineNum += $lfCount;
-                               $this->colNum = strlen( $text ) - strrpos( $text, "\n" );
-                       } else {
-                               $this->colNum += strlen( $text );
-                       }
-                       $this->byteNum += strlen( $text );
-               }
-               $this->prevToken = $this->currentToken;
-               $this->setPos( $this->pos + 1 );
-               return $this->currentToken;
-       }
-
-       /**
-        * Get the token $offset steps ahead of the current position.
-        * $offset may be negative, to get tokens behind the current position.
-        * @return ConfEditorToken
-        */
-       function getTokenAhead( $offset ) {
-               $pos = $this->pos + $offset;
-               if ( $pos >= count( $this->tokens ) || $pos < 0 ) {
-                       return ConfEditorToken::newEnd();
-               } else {
-                       return $this->newTokenObj( $this->tokens[$pos] );
-               }
-       }
-
-       /**
-        * Advances the current position past any whitespace or comments
-        */
-       function skipSpace() {
-               while ( $this->currentToken && $this->currentToken->isSkip() ) {
-                       $this->nextToken();
-               }
-               return $this->currentToken;
-       }
-
-       /**
-        * Throws an error if the current token is not of the given type, and
-        * then advances to the next position.
-        */
-       function expect( $type ) {
-               if ( $this->currentToken && $this->currentToken->type == $type ) {
-                       return $this->nextToken();
-               } else {
-                       $this->error( "expected " . $this->getTypeName( $type ) .
-                               ", got " . $this->getTypeName( $this->currentToken->type ) );
-               }
-       }
-
-       /**
-        * Push a state or two on to the state stack.
-        */
-       function pushState( $nextState, $stateAfterThat = null ) {
-               if ( $stateAfterThat !== null ) {
-                       $this->stateStack[] = $stateAfterThat;
-               }
-               $this->stateStack[] = $nextState;
-       }
-
-       /**
-        * Pop a state from the state stack.
-        * @return mixed
-        */
-       function popState() {
-               return array_pop( $this->stateStack );
-       }
-
-       /**
-        * Returns true if the user input path is valid.
-        * This exists to allow "/" and "@" to be reserved for string path keys
-        * @return bool
-        */
-       function validatePath( $path ) {
-               return strpos( $path, '/' ) === false && substr( $path, 0, 1 ) != '@';
-       }
-
-       /**
-        * Internal function to update some things at the end of a path region. Do
-        * not call except from popPath() or nextPath().
-        */
-       function endPath() {
-               $key = '';
-               foreach ( $this->pathStack as $pathInfo ) {
-                       if ( $key !== '' ) {
-                               $key .= '/';
-                       }
-                       $key .= $pathInfo['name'];
-               }
-               $pathInfo['endByte'] = $this->byteNum;
-               $pathInfo['endToken'] = $this->pos;
-               $this->pathInfo[$key] = $pathInfo;
-       }
-
-       /**
-        * Go up to a new path level, for example at the start of an array.
-        */
-       function pushPath( $path ) {
-               $this->pathStack[] = array(
-                       'name' => $path,
-                       'level' => count( $this->pathStack ) + 1,
-                       'startByte' => $this->byteNum,
-                       'startToken' => $this->pos,
-                       'valueStartToken' => false,
-                       'valueStartByte' => false,
-                       'valueEndToken' => false,
-                       'valueEndByte' => false,
-                       'nextArrayIndex' => 0,
-                       'hasComma' => false,
-                       'arrowByte' => false
-               );
-       }
-
-       /**
-        * Go down a path level, for example at the end of an array.
-        */
-       function popPath() {
-               $this->endPath();
-               array_pop( $this->pathStack );
-       }
-
-       /**
-        * Go to the next path on the same level. This ends the current path and
-        * starts a new one. If $path is \@next, the new path is set to the next
-        * numeric array element.
-        */
-       function nextPath( $path ) {
-               $this->endPath();
-               $i = count( $this->pathStack ) - 1;
-               if ( $path == '@next' ) {
-                       $nextArrayIndex =& $this->pathStack[$i]['nextArrayIndex'];
-                       $this->pathStack[$i]['name'] = $nextArrayIndex;
-                       $nextArrayIndex++;
-               } else {
-                       $this->pathStack[$i]['name'] = $path;
-               }
-               $this->pathStack[$i] =
-                       array(
-                               'startByte' => $this->byteNum,
-                               'startToken' => $this->pos,
-                               'valueStartToken' => false,
-                               'valueStartByte' => false,
-                               'valueEndToken' => false,
-                               'valueEndByte' => false,
-                               'hasComma' => false,
-                               'arrowByte' => false,
-                       ) + $this->pathStack[$i];
-       }
-
-       /**
-        * Mark the start of the value part of a path.
-        */
-       function startPathValue() {
-               $path =& $this->pathStack[count( $this->pathStack ) - 1];
-               $path['valueStartToken'] = $this->pos;
-               $path['valueStartByte'] = $this->byteNum;
-       }
-
-       /**
-        * Mark the end of the value part of a path.
-        */
-       function endPathValue() {
-               $path =& $this->pathStack[count( $this->pathStack ) - 1];
-               $path['valueEndToken'] = $this->pos;
-               $path['valueEndByte'] = $this->byteNum;
-       }
-
-       /**
-        * Mark the comma separator in an array element
-        */
-       function markComma() {
-               $path =& $this->pathStack[count( $this->pathStack ) - 1];
-               $path['hasComma'] = true;
-       }
-
-       /**
-        * Mark the arrow separator in an associative array element
-        */
-       function markArrow() {
-               $path =& $this->pathStack[count( $this->pathStack ) - 1];
-               $path['arrowByte'] = $this->byteNum;
-       }
-
-       /**
-        * Generate a parse error
-        */
-       function error( $msg ) {
-               throw new ConfEditorParseError( $this, $msg );
-       }
-
-       /**
-        * Get a readable name for the given token type.
-        * @return string
-        */
-       function getTypeName( $type ) {
-               if ( is_int( $type ) ) {
-                       return token_name( $type );
-               } else {
-                       return "\"$type\"";
-               }
-       }
-
-       /**
-        * Looks ahead to see if the given type is the next token type, starting
-        * from the current position plus the given offset. Skips any intervening
-        * whitespace.
-        * @return bool
-        */
-       function isAhead( $type, $offset = 0 ) {
-               $ahead = $offset;
-               $token = $this->getTokenAhead( $offset );
-               while ( !$token->isEnd() ) {
-                       if ( $token->isSkip() ) {
-                               $ahead++;
-                               $token = $this->getTokenAhead( $ahead );
-                               continue;
-                       } elseif ( $token->type == $type ) {
-                               // Found the type
-                               return true;
-                       } else {
-                               // Not found
-                               return false;
-                       }
-               }
-               return false;
-       }
-
-       /**
-        * Get the previous token object
-        */
-       function prevToken() {
-               return $this->prevToken;
-       }
-
-       /**
-        * Echo a reasonably readable representation of the tokenizer array.
-        */
-       function dumpTokens() {
-               $out = '';
-               foreach ( $this->tokens as $token ) {
-                       $obj = $this->newTokenObj( $token );
-                       $out .= sprintf( "%-28s %s\n",
-                               $this->getTypeName( $obj->type ),
-                               addcslashes( $obj->text, "\0..\37" ) );
-               }
-               echo "<pre>" . htmlspecialchars( $out ) . "</pre>";
-       }
-}
-
-/**
- * Exception class for parse errors
- */
-class ConfEditorParseError extends MWException {
-       var $lineNum, $colNum;
-       function __construct( $editor, $msg ) {
-               $this->lineNum = $editor->lineNum;
-               $this->colNum = $editor->colNum;
-               parent::__construct( "Parse error on line {$editor->lineNum} " .
-                       "col {$editor->colNum}: $msg" );
-       }
-
-       function highlight( $text ) {
-               $lines = StringUtils::explode( "\n", $text );
-               foreach ( $lines as $lineNum => $line ) {
-                       if ( $lineNum == $this->lineNum - 1 ) {
-                               return "$line\n" . str_repeat( ' ', $this->colNum - 1 ) . "^\n";
-                       }
-               }
-               return '';
-       }
-
-}
-
-/**
- * Class to wrap a token from the tokenizer.
- */
-class ConfEditorToken {
-       var $type, $text;
-
-       static $scalarTypes = array( T_LNUMBER, T_DNUMBER, T_STRING, T_CONSTANT_ENCAPSED_STRING );
-       static $skipTypes = array( T_WHITESPACE, T_COMMENT, T_DOC_COMMENT );
-
-       static function newEnd() {
-               return new self( 'END', '' );
-       }
-
-       function __construct( $type, $text ) {
-               $this->type = $type;
-               $this->text = $text;
-       }
-
-       function isSkip() {
-               return in_array( $this->type, self::$skipTypes );
-       }
-
-       function isScalar() {
-               return in_array( $this->type, self::$scalarTypes );
-       }
-
-       function isEnd() {
-               return $this->type == 'END';
-       }
-}
diff --git a/includes/DataUpdate.php b/includes/DataUpdate.php
deleted file mode 100644 (file)
index 7b9ac28..0000000
+++ /dev/null
@@ -1,126 +0,0 @@
-<?php
-/**
- * Base code for update jobs that do something with some secondary
- * data extracted from article.
- *
- * 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
- */
-
-/**
- * Abstract base class for update jobs that do something with some secondary
- * data extracted from article.
- *
- * @note: subclasses should NOT start or commit transactions in their doUpdate() method,
- *        a transaction will automatically be wrapped around the update. If need be,
- *        subclasses can override the beginTransaction() and commitTransaction() methods.
- */
-abstract class DataUpdate implements DeferrableUpdate {
-
-       /**
-        * Constructor
-        */
-       public function __construct() {
-               # noop
-       }
-
-       /**
-        * Begin an appropriate transaction, if any.
-        * This default implementation does nothing.
-        */
-       public function beginTransaction() {
-               //noop
-       }
-
-       /**
-        * Commit the transaction started via beginTransaction, if any.
-        * This default implementation does nothing.
-        */
-       public function commitTransaction() {
-               //noop
-       }
-
-       /**
-        * Abort / roll back the transaction started via beginTransaction, if any.
-        * This default implementation does nothing.
-        */
-       public function rollbackTransaction() {
-               //noop
-       }
-
-       /**
-        * Convenience method, calls doUpdate() on every DataUpdate in the array.
-        *
-        * This methods supports transactions logic by first calling beginTransaction()
-        * on all updates in the array, then calling doUpdate() on each, and, if all goes well,
-        * then calling commitTransaction() on each update. If an error occurs,
-        * rollbackTransaction() will be called on any update object that had beginTransaction()
-        * called but not yet commitTransaction().
-        *
-        * This allows for limited transactional logic across multiple backends for storing
-        * secondary data.
-        *
-        * @param array $updates a list of DataUpdate instances
-        * @throws Exception|null
-        */
-       public static function runUpdates( $updates ) {
-               if ( empty( $updates ) ) {
-                       return; # nothing to do
-               }
-
-               $open_transactions = array();
-               $exception = null;
-
-               /**
-                * @var $update DataUpdate
-                * @var $trans DataUpdate
-                */
-
-               try {
-                       // begin transactions
-                       foreach ( $updates as $update ) {
-                               $update->beginTransaction();
-                               $open_transactions[] = $update;
-                       }
-
-                       // do work
-                       foreach ( $updates as $update ) {
-                               $update->doUpdate();
-                       }
-
-                       // commit transactions
-                       while ( count( $open_transactions ) > 0 ) {
-                               $trans = array_pop( $open_transactions );
-                               $trans->commitTransaction();
-                       }
-               } catch ( Exception $ex ) {
-                       $exception = $ex;
-                       wfDebug( "Caught exception, will rethrow after rollback: " . $ex->getMessage() );
-               }
-
-               // rollback remaining transactions
-               while ( count( $open_transactions ) > 0 ) {
-                       $trans = array_pop( $open_transactions );
-                       $trans->rollbackTransaction();
-               }
-
-               if ( $exception ) {
-                       throw $exception; // rethrow after cleanup
-               }
-       }
-
-}
index a0a1b3e..92bb05e 100644 (file)
@@ -63,7 +63,7 @@ $wgConf = new SiteConfiguration;
  * MediaWiki version number
  * @since 1.2
  */
-$wgVersion = '1.22alpha';
+$wgVersion = '1.23alpha';
 
 /**
  * Name of the site. It must be changed in LocalSettings.php
@@ -3322,6 +3322,22 @@ $wgResourceLoaderLESSImportPaths = array(
        "$IP/resources/mediawiki.less/",
 );
 
+/**
+ * Whether ResourceLoader should attempt to persist modules in localStorage on
+ * browsers that support the Web Storage API.
+ *
+ * @since 1.23 - Client-side module persistence is experimental. Exercise care.
+ */
+$wgResourceLoaderStorageEnabled = false;
+
+/**
+ * Cache version for client-side ResourceLoader module storage. You can trigger
+ * invalidation of the contents of the module store by incrementing this value.
+ *
+ * @since 1.23
+ */
+$wgResourceLoaderStorageVersion = 1;
+
 /** @} */ # End of resource loader settings }
 
 /*************************************************************************//**
@@ -3936,7 +3952,7 @@ $wgReservedUsernames = array(
        'ScriptImporter', // Default user name used by maintenance/importSiteScripts.php
        'msg:double-redirect-fixer', // Automatic double redirect fix
        'msg:usermessage-editor', // Default user for leaving user messages
-       'msg:proxyblocker', // For Special:Blockme
+       'msg:proxyblocker', // For $wgProxyList and Special:Blockme (removed in 1.22)
 );
 
 /**
@@ -4365,6 +4381,20 @@ $wgRestrictionLevels = array( '', 'autoconfirmed', 'sysop' );
  */
 $wgCascadingRestrictionLevels = array( 'sysop' );
 
+/**
+ * Restriction levels that should be considered "semiprotected"
+ *
+ * Certain places in the interface recognize a dichotomy between "protected"
+ * and "semiprotected", without further distinguishing the specific levels. In
+ * general, if anyone can be eligible to edit a protection level merely by
+ * reaching some condition in $wgAutopromote, it should probably be considered
+ * "semiprotected".
+ *
+ * 'autoconfirmed' is quietly rewritten to 'editsemiprotected' for backwards compatibility.
+ * 'sysop' is not changed, since it really shouldn't be here.
+ */
+$wgSemiprotectedRestrictionLevels = array( 'autoconfirmed' );
+
 /**
  * Set the minimum permissions required to edit pages in each
  * namespace.  If you list more than one permission, a user must
@@ -4695,31 +4725,6 @@ $wgPasswordAttemptThrottle = array( 'count' => 5, 'seconds' => 300 );
  * @{
  */
 
-/**
- * If you enable this, every editor's IP address will be scanned for open HTTP
- * proxies.
- *
- * @warning Don't enable this. Many sysops will report "hostile TCP port scans"
- * to your ISP and ask for your server to be shut down.
- * You have been warned.
- */
-$wgBlockOpenProxies = false;
-
-/**
- * Port we want to scan for a proxy
- */
-$wgProxyPorts = array( 80, 81, 1080, 3128, 6588, 8000, 8080, 8888, 65506 );
-
-/**
- * Script used to scan
- */
-$wgProxyScriptPath = "$IP/maintenance/proxyCheck.php";
-
-/**
- * Expiration time for cached proxy IPs
- */
-$wgProxyMemcExpiry = 86400;
-
 /**
  * This should always be customised in LocalSettings.php
  */
@@ -4890,10 +4895,29 @@ $wgDebugDBTransactions = false;
 $wgDebugDumpSql = false;
 
 /**
- * Set to an array of log group keys to filenames.
+ * Map of string log group names to log destinations.
+ *
  * If set, wfDebugLog() output for that group will go to that file instead
  * of the regular $wgDebugLogFile. Useful for enabling selective logging
  * in production.
+ *
+ * Log destinations may be string values specifying a filename or URI, or they
+ * may be filename or an associative array mapping 'destination' to the desired
+ * filename. The associative array may also contain a 'sample' key with an
+ * integer value, specifying a sampling factor.
+ *
+ * @par Example:
+ * @code
+ * $wgDebugLogGroups['redis'] = '/var/log/mediawiki/redis.log';
+ * @endcode
+ *
+ * @par Advanced example:
+ * @code
+ * $wgDebugLogGroups['memcached'] = (
+ *     'destination' => '/var/log/mediawiki/memcached.log',
+ *     'sample' => 1000,  // log 1 message out of every 1,000.
+ * );
+ * @endcode
  */
 $wgDebugLogGroups = array();
 
@@ -4935,37 +4959,6 @@ $wgShowSQLErrors = false;
  */
 $wgShowExceptionDetails = false;
 
-/**
- * Array of functions which need parameters redacted from stack traces shown to
- * clients and logged. Keys are in the format '[class::]function', and the
- * values should be either an integer or an array of integers. These are the
- * indexes of the parameters which need to be kept secret.
- * @since 1.22
- */
-$wgRedactedFunctionArguments = array(
-       'AuthPlugin::setPassword' => 1,
-       'AuthPlugin::authenticate' => 1,
-       'AuthPlugin::addUser' => 1,
-
-       'DatabaseBase::__construct' => 2,
-       'DatabaseBase::open' => 2,
-
-       'SpecialChangeEmail::attemptChange' => 1,
-       'SpecialChangePassword::attemptReset' => 0,
-
-       'User::setPassword' => 0,
-       'User::setInternalPassword' => 0,
-       'User::checkPassword' => 0,
-       'User::setNewpassword' => 0,
-       'User::comparePasswords' => array( 0, 1 ),
-       'User::checkTemporaryPassword' => 0,
-       'User::setToken' => 0,
-       'User::crypt' => 0,
-       'User::oldCrypt' => 0,
-       'User::getPasswordValidity' => 0,
-       'User::isValidPassword' => 0,
-);
-
 /**
  * If true, show a backtrace for database errors
  */
@@ -5584,6 +5577,7 @@ $wgRCFeeds = array();
  * Keys are scheme names, values are names of engine classes.
  */
 $wgRCEngines = array(
+       'redis' => 'RedisPubSubFeedEngine',
        'udp' => 'UDPRCFeedEngine',
 );
 
@@ -5963,6 +5957,13 @@ $wgSpecialPages = array();
  */
 $wgAutoloadClasses = array();
 
+/**
+ * Switch controlling legacy case-insensitive classloading.
+ * Do not disable if your wiki must support data created by PHP4, or by
+ * MediaWiki 1.4 or earlier.
+ */
+$wgAutoloadAttemptLowercase = true;
+
 /**
  * An array of extension types and inside that their names, versions, authors,
  * urls, descriptions and pointers to localized description msgs. Note that
@@ -6639,7 +6640,7 @@ $wgCrossSiteAJAXdomainExceptions = array();
 /**
  * Maximum amount of virtual memory available to shell processes under linux, in KB.
  */
-$wgMaxShellMemory = 102400;
+$wgMaxShellMemory = 307200;
 
 /**
  * Maximum file size created by shell processes under linux, in KB
diff --git a/includes/DeferredUpdates.php b/includes/DeferredUpdates.php
deleted file mode 100644 (file)
index a321414..0000000
+++ /dev/null
@@ -1,129 +0,0 @@
-<?php
-/**
- * Interface and manager for deferred updates.
- *
- * 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
- */
-
-/**
- * Interface that deferrable updates should implement. Basically required so we
- * can validate input on DeferredUpdates::addUpdate()
- *
- * @since 1.19
- */
-interface DeferrableUpdate {
-       /**
-        * Perform the actual work
-        */
-       function doUpdate();
-}
-
-/**
- * Class for managing the deferred updates.
- *
- * @since 1.19
- */
-class DeferredUpdates {
-       /**
-        * Store of updates to be deferred until the end of the request.
-        */
-       private static $updates = array();
-
-       /**
-        * Add an update to the deferred list
-        * @param $update DeferrableUpdate Some object that implements doUpdate()
-        */
-       public static function addUpdate( DeferrableUpdate $update ) {
-               array_push( self::$updates, $update );
-       }
-
-       /**
-        * HTMLCacheUpdates are the most common deferred update people use. This
-        * is a shortcut method for that.
-        * @see HTMLCacheUpdate::__construct()
-        * @param $title
-        * @param $table
-        */
-       public static function addHTMLCacheUpdate( $title, $table ) {
-               self::addUpdate( new HTMLCacheUpdate( $title, $table ) );
-       }
-
-       /**
-        * Add a callable update.  In a lot of cases, we just need a callback/closure,
-        * defining a new DeferrableUpdate object is not necessary
-        * @see MWCallableUpdate::__construct()
-        * @param callable $callable
-        */
-       public static function addCallableUpdate( $callable ) {
-               self::addUpdate( new MWCallableUpdate( $callable ) );
-       }
-
-       /**
-        * Do any deferred updates and clear the list
-        *
-        * @param string $commit set to 'commit' to commit after every update to
-        *                prevent lock contention
-        */
-       public static function doUpdates( $commit = '' ) {
-               global $wgDeferredUpdateList;
-
-               wfProfileIn( __METHOD__ );
-
-               $updates = array_merge( $wgDeferredUpdateList, self::$updates );
-
-               // No need to get master connections in case of empty updates array
-               if ( !count( $updates ) ) {
-                       wfProfileOut( __METHOD__ );
-                       return;
-               }
-
-               $doCommit = $commit == 'commit';
-               if ( $doCommit ) {
-                       $dbw = wfGetDB( DB_MASTER );
-               }
-
-               foreach ( $updates as $update ) {
-                       try {
-                               $update->doUpdate();
-
-                               if ( $doCommit && $dbw->trxLevel() ) {
-                                       $dbw->commit( __METHOD__, 'flush' );
-                               }
-                       } catch ( MWException $e ) {
-                               // We don't want exceptions thrown during deferred updates to
-                               // be reported to the user since the output is already sent.
-                               // Instead we just log them.
-                               if ( !$e instanceof ErrorPageError ) {
-                                       wfDebugLog( 'exception', $e->getLogMessage() );
-                               }
-                       }
-               }
-
-               self::clearPendingUpdates();
-               wfProfileOut( __METHOD__ );
-       }
-
-       /**
-        * Clear all pending updates without performing them. Generally, you don't
-        * want or need to call this. Unit tests need it though.
-        */
-       public static function clearPendingUpdates() {
-               global $wgDeferredUpdateList;
-               $wgDeferredUpdateList = self::$updates = array();
-       }
-}
index d48bd0b..8a63786 100644 (file)
@@ -23,7 +23,6 @@
 /**
  * Class to allow throwing wfDeprecated warnings
  * when people use globals that we do not want them to.
- * (For example like $wgArticle)
  */
 
 class DeprecatedGlobal extends StubObject {
index 12cd4b3..530e267 100644 (file)
@@ -840,7 +840,6 @@ class EditPage {
                if ( $this->textbox1 === false ) {
                        return false;
                }
-               wfProxyCheck();
                return true;
        }
 
@@ -1382,6 +1381,24 @@ class EditPage {
                        return $status;
                }
 
+               $spam = $wgRequest->getText( 'wpAntispam' );
+               if ( $spam !== '' ) {
+                       wfDebugLog(
+                               'SimpleAntiSpam',
+                               $wgUser->getName() .
+                               ' editing "' .
+                               $this->mTitle->getPrefixedText() .
+                               '" submitted bogus field "' .
+                               $spam .
+                               '"'
+                       );
+                       $status->fatal( 'spamprotectionmatch', false );
+                       $status->value = self::AS_SPAM_ERROR;
+                       wfProfileOut( __METHOD__ . '-checks' );
+                       wfProfileOut( __METHOD__ );
+                       return $status;
+               }
+
                try {
                        # Construct Content object
                        $textbox_content = $this->toEditContent( $this->textbox1 );
@@ -2191,6 +2208,14 @@ class EditPage {
                        call_user_func_array( $formCallback, array( &$wgOut ) );
                }
 
+               // Add an empty field to trip up spambots
+               $wgOut->addHTML(
+                       Xml::openElement( 'div', array( 'id' => 'antispam-container', 'style' => 'display: none;' ) )
+                       . Html::rawElement( 'label', array( 'for' => 'wpAntiSpam' ), wfMessage( 'simpleantispam-label' )->parse() )
+                       . Xml::element( 'input', array( 'type' => 'text', 'name' => 'wpAntispam', 'id' => 'wpAntispam', 'value' => '' ) )
+                       . Xml::closeElement( 'div' )
+               );
+
                wfRunHooks( 'EditPage::showEditForm:fields', array( &$this, &$wgOut ) );
 
                // Put these up at the top to ensure they aren't lost on early form submission
@@ -2859,7 +2884,15 @@ HTML
                return self::getCopyrightWarning( $this->mTitle );
        }
 
-       public static function getCopyrightWarning( $title ) {
+       /**
+        * Get the copyright warning, by default returns wikitext
+        *
+        * @param Title $title
+        * @param string $format output format, valid values are any function of
+        *                       a Message object
+        * @return string
+        */
+       public static function getCopyrightWarning( $title, $format = 'plain' ) {
                global $wgRightsText;
                if ( $wgRightsText ) {
                        $copywarnMsg = array( 'copyrightwarning',
@@ -2873,7 +2906,7 @@ HTML
                wfRunHooks( 'EditPageCopyrightWarning', array( $title, &$copywarnMsg ) );
 
                return "<div id=\"editpage-copywarn\">\n" .
-                       call_user_func_array( 'wfMessage', $copywarnMsg )->plain() . "\n</div>";
+                       call_user_func_array( 'wfMessage', $copywarnMsg )->$format() . "\n</div>";
        }
 
        /**
index fba857f..008be15 100644 (file)
@@ -30,7 +30,6 @@
  * @ingroup Exception
  */
 class MWException extends Exception {
-
        /**
         * Should the exception use $wgOut to output the error?
         *
@@ -43,6 +42,16 @@ class MWException extends Exception {
                        !empty( $GLOBALS['wgTitle'] );
        }
 
+       /**
+        * Whether to log this exception in the exception debug log.
+        *
+        * @since 1.23
+        * @return boolean
+        */
+       function isLoggable() {
+               return true;
+       }
+
        /**
         * Can the extension use the Message class/wfMessage to get i18n-ed messages?
         *
@@ -74,7 +83,9 @@ class MWException extends Exception {
                        return null; // Just silently ignore
                }
 
-               if ( !array_key_exists( $name, $wgExceptionHooks ) || !is_array( $wgExceptionHooks[$name] ) ) {
+               if ( !array_key_exists( $name, $wgExceptionHooks ) ||
+                       !is_array( $wgExceptionHooks[$name] )
+               ) {
                        return null;
                }
 
@@ -82,7 +93,11 @@ class MWException extends Exception {
                $callargs = array_merge( array( $this ), $args );
 
                foreach ( $hooks as $hook ) {
-                       if ( is_string( $hook ) || ( is_array( $hook ) && count( $hook ) >= 2 && is_string( $hook[0] ) ) ) { // 'function' or array( 'class', hook' )
+                       if (
+                               is_string( $hook ) ||
+                               ( is_array( $hook ) && count( $hook ) >= 2 && is_string( $hook[0] ) )
+                       ) {
+                               // 'function' or array( 'class', hook' )
                                $result = call_user_func_array( $hook, $callargs );
                        } else {
                                $result = null;
@@ -125,8 +140,8 @@ class MWException extends Exception {
                global $wgShowExceptionDetails;
 
                if ( $wgShowExceptionDetails ) {
-                       return '<p>' . nl2br( htmlspecialchars( $this->getMessage() ) ) .
-                               '</p><p>Backtrace:</p><p>' . nl2br( htmlspecialchars( MWExceptionHandler::formatRedactedTrace( $this ) ) ) .
+                       return '<p>' . nl2br( htmlspecialchars( MWExceptionHandler::getLogMessage( $this ) ) ) .
+                               '</p><p>Backtrace:</p><p>' . nl2br( htmlspecialchars( MWExceptionHandler::getRedactedTraceAsString( $this ) ) ) .
                                "</p>\n";
                } else {
                        return "<div class=\"errorbox\">" .
@@ -150,8 +165,8 @@ class MWException extends Exception {
                global $wgShowExceptionDetails;
 
                if ( $wgShowExceptionDetails ) {
-                       return $this->getMessage() .
-                               "\nBacktrace:\n" . MWExceptionHandler::formatRedactedTrace( $this ) . "\n";
+                       return MWExceptionHandler::getLogMessage( $this ) .
+                               "\nBacktrace:\n" . MWExceptionHandler::getRedactedTraceAsString( $this ) . "\n";
                } else {
                        return "Set \$wgShowExceptionDetails = true; " .
                                "in LocalSettings.php to show detailed debugging information.\n";
@@ -164,7 +179,8 @@ class MWException extends Exception {
         * @return string
         */
        function getPageTitle() {
-               return $this->msg( 'internalerror', "Internal error" );
+               global $wgSitename;
+               return $this->msg( 'pagetitle', "$1 - $wgSitename", $this->msg( 'internalerror', 'Internal error' ) );
        }
 
        /**
@@ -209,13 +225,14 @@ class MWException extends Exception {
 
                        $wgOut->output();
                } else {
-                       header( "Content-Type: text/html; charset=utf-8" );
-                       echo "<!doctype html>\n" .
+                       header( 'Content-Type: text/html; charset=utf-8' );
+                       echo "<!DOCTYPE html>\n" .
                                '<html><head>' .
                                '<title>' . htmlspecialchars( $this->getPageTitle() ) . '</title>' .
+                               '<style>body { font-family: sans-serif; margin: 0; padding: 0.5em 2em; }</style>' .
                                "</head><body>\n";
 
-                       $hookResult = $this->runHooks( get_class( $this ) . "Raw" );
+                       $hookResult = $this->runHooks( get_class( $this ) . 'Raw' );
                        if ( $hookResult ) {
                                echo $hookResult;
                        } else {
@@ -242,8 +259,8 @@ class MWException extends Exception {
                } elseif ( self::isCommandLine() ) {
                        MWExceptionHandler::printError( $this->getText() );
                } else {
-                       header( "HTTP/1.1 500 MediaWiki exception" );
-                       header( "Status: 500 MediaWiki exception", true );
+                       header( 'HTTP/1.1 500 MediaWiki exception' );
+                       header( 'Status: 500 MediaWiki exception', true );
                        header( "Content-Type: $wgMimeType; charset=utf-8", true );
 
                        $this->reportHTML();
@@ -480,11 +497,11 @@ class UserBlockedError extends ErrorPageError {
 class UserNotLoggedIn extends ErrorPageError {
 
        /**
-        * @param $reasonMsg A message key containing the reason for the error.
+        * @param string $reasonMsg A message key containing the reason for the error.
         *        Optional, default: 'exception-nologin-text'
-        * @param $titleMsg A message key to set the page title.
+        * @param string $titleMsg A message key to set the page title.
         *        Optional, default: 'exception-nologin'
-        * @param $params Parameters to wfMessage().
+        * @param array $params Parameters to wfMessage().
         *        Optional, default: null
         */
        public function __construct(
@@ -601,8 +618,10 @@ class MWExceptionHandler {
                                $message = "MediaWiki internal error.\n\n";
 
                                if ( $wgShowExceptionDetails ) {
-                                       $message .= 'Original exception: ' . self::formatRedactedTrace( $e ) . "\n\n" .
-                                               'Exception caught inside exception handler: ' . $e2->__toString();
+                                       $message .= 'Original exception: ' . self::getLogMessage( $e ) .
+                                                "\nBacktrace:\n" . self::getRedactedTraceAsString( $e ) .
+                                                "\n\nException caught inside exception handler: " . self::getLogMessage( $e2 ) .
+                                                "\nBacktrace:\n" . self::getRedactedTraceAsString( $e2 );
                                } else {
                                        $message .= "Exception caught inside exception handler.\n\n" .
                                                "Set \$wgShowExceptionDetails = true; at the bottom of LocalSettings.php " .
@@ -618,10 +637,12 @@ class MWExceptionHandler {
                                }
                        }
                } else {
-                       $message = "Unexpected non-MediaWiki exception encountered, of type \"" . get_class( $e ) . "\"";
+                       $message = "Unexpected non-MediaWiki exception encountered, of type \"" .
+                               get_class( $e ) . "\"";
 
                        if ( $wgShowExceptionDetails ) {
-                               $message .= "\nexception '" . get_class( $e ) . "' in " . $e->getFile() . ":" . $e->getLine() . "\nStack trace:\n" . self::formatRedactedTrace( $e ) . "\n";
+                               $message .= "\n" . MWExceptionHandler::getLogMessage( $e ) . "\nBacktrace:\n" .
+                                       self::getRedactedTraceAsString( $e ) . "\n";
                        }
 
                        if ( $cmdLine ) {
@@ -639,8 +660,9 @@ class MWExceptionHandler {
         * @param string $message Failure text
         */
        public static function printError( $message ) {
-               # NOTE: STDERR may not be available, especially if php-cgi is used from the command line (bug #15602).
-               #      Try to produce meaningful output anyway. Using echo may corrupt output to STDOUT though.
+               # NOTE: STDERR may not be available, especially if php-cgi is used from the
+               # command line (bug #15602). Try to produce meaningful output anyway. Using
+               # echo may corrupt output to STDOUT though.
                if ( defined( 'STDERR' ) ) {
                        fwrite( STDERR, $message );
                } else {
@@ -678,64 +700,67 @@ class MWExceptionHandler {
        }
 
        /**
-        * Get the stack trace from the exception as a string, redacting certain function arguments in the process
-        * @param Exception $e The exception
-        * @return string The stack trace as a string
+        * Generate a string representation of an exception's stack trace
+        *
+        * Like Exception::getTraceAsString, but replaces argument values with
+        * argument type or class name.
+        *
+        * @param Exception $e
+        * @return string
         */
-       public static function formatRedactedTrace( Exception $e ) {
-               global $wgRedactedFunctionArguments;
-               $finalExceptionText = '';
+       public static function getRedactedTraceAsString( Exception $e ) {
+               $text = '';
 
-               foreach ( $e->getTrace() as $i => $call ) {
-                       $checkFor = array();
-                       if ( isset( $call['class'] ) ) {
-                               $checkFor[] = $call['class'] . '::' . $call['function'];
-                               foreach ( class_parents( $call['class'] ) as $parent ) {
-                                       $checkFor[] = $parent . '::' . $call['function'];
-                               }
-                       } else {
-                               $checkFor[] = $call['function'];
-                       }
-
-                       foreach ( $checkFor as $check ) {
-                               if ( isset( $wgRedactedFunctionArguments[$check] ) ) {
-                                       foreach ( (array)$wgRedactedFunctionArguments[$check] as $argNo ) {
-                                               $call['args'][$argNo] = 'REDACTED';
-                                       }
-                               }
-                       }
-
-                       if ( isset( $call['file'] ) && isset( $call['line'] ) ) {
-                               $finalExceptionText .= "#{$i} {$call['file']}({$call['line']}): ";
+               foreach ( self::getRedactedTrace( $e ) as $level => $frame ) {
+                       if ( isset( $frame['file'] ) && isset( $frame['line'] ) ) {
+                               $text .= "#{$level} {$frame['file']}({$frame['line']}): ";
                        } else {
                                // 'file' and 'line' are unset for calls via call_user_func (bug 55634)
                                // This matches behaviour of Exception::getTraceAsString to instead
                                // display "[internal function]".
-                               $finalExceptionText .= "#{$i} [internal function]: ";
+                               $text .= "#{$level} [internal function]: ";
                        }
 
-                       if ( isset( $call['class'] ) ) {
-                               $finalExceptionText .= $call['class'] . $call['type'] . $call['function'];
+                       if ( isset( $frame['class'] ) ) {
+                               $text .= $frame['class'] . $frame['type'] . $frame['function'];
                        } else {
-                               $finalExceptionText .= $call['function'];
+                               $text .= $frame['function'];
                        }
-                       $args = array();
-                       if ( isset( $call['args'] ) ) {
-                               foreach ( $call['args'] as $arg ) {
-                                       if ( is_object( $arg ) ) {
-                                               $args[] = 'Object(' . get_class( $arg ) . ')';
-                                       } elseif( is_array( $arg ) ) {
-                                               $args[] = 'Array';
-                                       } else {
-                                               $args[] = var_export( $arg, true );
-                                       }
-                               }
+
+                       if ( isset( $frame['args'] ) ) {
+                               $text .= '(' . implode( ', ', $frame['args'] ) . ")\n";
+                       } else {
+                               $text .= "()\n";
                        }
-                       $finalExceptionText .=  '(' . implode( ', ', $args ) . ")\n";
                }
-               return $finalExceptionText . '#' . ( $i + 1 ) . ' {main}';
+
+               $level = $level + 1;
+               $text .= "#{$level} {main}";
+
+               return $text;
        }
 
+       /**
+        * Return a copy of an exception's backtrace as an array.
+        *
+        * Like Exception::getTrace, but replaces each element in each frame's
+        * argument array with the name of its class (if the element is an object)
+        * or its type (if the element is a PHP primitive).
+        *
+        * @since 1.22
+        * @param Exception $e
+        * @return array
+        */
+       public static function getRedactedTrace( Exception $e ) {
+               return array_map( function ( $frame ) {
+                       if ( isset( $frame['args'] ) ) {
+                               $frame['args'] = array_map( function ( $arg ) {
+                                       return is_object( $arg ) ? get_class( $arg ) : gettype( $arg );
+                               }, $frame['args'] );
+                       }
+                       return $frame;
+               }, $e->getTrace() );
+       }
 
        /**
         * Get the ID for this error.
@@ -754,6 +779,21 @@ class MWExceptionHandler {
                return $e->_mwLogId;
        }
 
+       /**
+        * If the exception occurred in the course of responding to a request,
+        * returns the requested URL. Otherwise, returns false.
+        *
+        * @since 1.23
+        * @return string|bool
+        */
+       public static function getURL() {
+               global $wgRequest;
+               if ( !isset( $wgRequest ) || $wgRequest instanceof FauxRequest ) {
+                       return false;
+               }
+               return $wgRequest->getRequestURL();
+       }
+
        /**
         * Return the requested URL and point to file and line number from which the
         * exception occurred.
@@ -763,23 +803,88 @@ class MWExceptionHandler {
         * @return string
         */
        public static function getLogMessage( Exception $e ) {
-               global $wgRequest;
-
                $id = self::getLogId( $e );
                $file = $e->getFile();
                $line = $e->getLine();
                $message = $e->getMessage();
+               $url = self::getURL() ?: '[no req]';
 
-               if ( isset( $wgRequest ) && !$wgRequest instanceof FauxRequest ) {
-                       $url = $wgRequest->getRequestURL();
-                       if ( !$url ) {
-                               $url = '[no URL]';
-                       }
-               } else {
-                       $url = '[no req]';
+               return "[$id] $url   Exception from line $line of $file: $message";
+       }
+
+       /**
+        * Serialize an Exception object to JSON.
+        *
+        * The JSON object will have keys 'id', 'file', 'line', 'message', and
+        * 'url'. These keys map to string values, with the exception of 'line',
+        * which is a number, and 'url', which may be either a string URL or or
+        * null if the exception did not occur in the context of serving a web
+        * request.
+        *
+        * If $wgLogExceptionBacktrace is true, it will also have a 'backtrace'
+        * key, mapped to the array return value of Exception::getTrace, but with
+        * each element in each frame's "args" array (if set) replaced with the
+        * argument's class name (if the argument is an object) or type name (if
+        * the argument is a PHP primitive).
+        *
+        * @par Sample JSON record ($wgLogExceptionBacktrace = false):
+        * @code
+        *  {
+        *    "id": "c41fb419",
+        *    "file": "/var/www/mediawiki/includes/cache/MessageCache.php",
+        *    "line": 704,
+        *    "message": "Non-string key given",
+        *    "url": "/wiki/Main_Page"
+        *  }
+        * @endcode
+        *
+        * @par Sample JSON record ($wgLogExceptionBacktrace = true):
+        * @code
+        *  {
+        *    "id": "dc457938",
+        *    "file": "/vagrant/mediawiki/includes/cache/MessageCache.php",
+        *    "line": 704,
+        *    "message": "Non-string key given",
+        *    "url": "/wiki/Main_Page",
+        *    "backtrace": [{
+        *      "file": "/vagrant/mediawiki/extensions/VisualEditor/VisualEditor.hooks.php",
+        *      "line": 80,
+        *      "function": "get",
+        *      "class": "MessageCache",
+        *      "type": "->",
+        *      "args": ["array"]
+        *    }]
+        *  }
+        * @endcode
+        *
+        * @since 1.23
+        * @param Exception $e
+        * @param bool $pretty Add non-significant whitespace to improve readability (default: false).
+        * @param int $escaping Bitfield consisting of FormatJson::.*_OK class constants.
+        * @return string|bool: JSON string if successful; false upon failure
+        */
+       public static function jsonSerializeException( Exception $e, $pretty = false, $escaping = 0 ) {
+               global $wgLogExceptionBacktrace;
+
+               $exceptionData = array(
+                       'id' => self::getLogId( $e ),
+                       'file' => $e->getFile(),
+                       'line' => $e->getLine(),
+                       'message' => $e->getMessage(),
+               );
+
+               // Because MediaWiki is first and foremost a web application, we set a
+               // 'url' key unconditionally, but set it to null if the exception does
+               // not occur in the context of a web request, as a way of making that
+               // fact visible and explicit.
+               $exceptionData['url'] = self::getURL() ?: null;
+
+               if ( $wgLogExceptionBacktrace ) {
+                       // Argument values may not be serializable, so redact them.
+                       $exceptionData['backtrace'] = self::getRedactedTrace( $e );
                }
 
-               return "[$id] $url   Exception from line $line of $file: $message";
+               return FormatJson::encode( $exceptionData, $pretty, $escaping );
        }
 
        /**
@@ -794,14 +899,20 @@ class MWExceptionHandler {
        public static function logException( Exception $e ) {
                global $wgLogExceptionBacktrace;
 
-               $log = self::getLogMessage( $e );
-               if ( $log ) {
+               if ( !( $e instanceof MWException ) || $e->isLoggable() ) {
+                       $log = self::getLogMessage( $e );
                        if ( $wgLogExceptionBacktrace ) {
-                               wfDebugLog( 'exception', $log . "\n" . self::formatRedactedTrace( $e ) . "\n" );
+                               wfDebugLog( 'exception', $log . "\n" . $e->getTraceAsString() . "\n" );
                        } else {
                                wfDebugLog( 'exception', $log );
                        }
+
+                       $json = self::jsonSerializeException( $e, false, FormatJson::ALL_OK );
+                       if ( $json !== false ) {
+                               wfDebugLog( 'exception-json', $json, false );
+                       }
                }
+
        }
 
 }
index 8241d81..2216ccc 100644 (file)
@@ -81,7 +81,6 @@ if ( !function_exists( 'mb_strpos' ) ) {
        function mb_strpos( $haystack, $needle, $offset = 0, $encoding = '' ) {
                return Fallback::mb_strpos( $haystack, $needle, $offset, $encoding );
        }
-
 }
 
 if ( !function_exists( 'mb_strrpos' ) ) {
@@ -283,8 +282,8 @@ function wfRandom() {
        # The maximum random value is "only" 2^31-1, so get two random
        # values to reduce the chance of dupes
        $max = mt_getrandmax() + 1;
-       $rand = number_format( ( mt_rand() * $max + mt_rand() )
-               / $max / $max, 12, '.', '' );
+       $rand = number_format( ( mt_rand() * $max + mt_rand() ) / $max / $max, 12, '.', '' );
+
        return $rand;
 }
 
@@ -330,6 +329,7 @@ function wfRandomString( $length = 32 ) {
  */
 function wfUrlencode( $s ) {
        static $needle;
+
        if ( is_null( $s ) ) {
                $needle = null;
                return '';
@@ -337,7 +337,9 @@ function wfUrlencode( $s ) {
 
        if ( is_null( $needle ) ) {
                $needle = array( '%3B', '%40', '%24', '%21', '%2A', '%28', '%29', '%2C', '%2F' );
-               if ( !isset( $_SERVER['SERVER_SOFTWARE'] ) || ( strpos( $_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS/7' ) === false ) ) {
+               if ( !isset( $_SERVER['SERVER_SOFTWARE'] ) ||
+                       ( strpos( $_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS/7' ) === false )
+               ) {
                        $needle[] = '%3A';
                }
        }
@@ -470,15 +472,17 @@ function wfAppendQuery( $url, $query ) {
 }
 
 /**
- * Expand a potentially local URL to a fully-qualified URL.  Assumes $wgServer
+ * Expand a potentially local URL to a fully-qualified URL. Assumes $wgServer
  * is correct.
  *
  * The meaning of the PROTO_* constants is as follows:
  * PROTO_HTTP: Output a URL starting with http://
  * PROTO_HTTPS: Output a URL starting with https://
  * PROTO_RELATIVE: Output a URL starting with // (protocol-relative URL)
- * PROTO_CURRENT: Output a URL starting with either http:// or https:// , depending on which protocol was used for the current incoming request
- * PROTO_CANONICAL: For URLs without a domain, like /w/index.php , use $wgCanonicalServer. For protocol-relative URLs, use the protocol of $wgCanonicalServer
+ * PROTO_CURRENT: Output a URL starting with either http:// or https:// , depending
+ *    on which protocol was used for the current incoming request
+ * PROTO_CANONICAL: For URLs without a domain, like /w/index.php , use $wgCanonicalServer.
+ *    For protocol-relative URLs, use the protocol of $wgCanonicalServer
  * PROTO_INTERNAL: Like PROTO_CANONICAL, but uses $wgInternalServer instead of $wgCanonicalServer
  *
  * @todo this won't work with current-path-relative URLs
@@ -486,10 +490,9 @@ function wfAppendQuery( $url, $query ) {
  *
  * @param string $url either fully-qualified or a local path + query
  * @param $defaultProto Mixed: one of the PROTO_* constants. Determines the
- *                             protocol to use if $url or $wgServer is
- *                             protocol-relative
+ *    protocol to use if $url or $wgServer is protocol-relative
  * @return string Fully-qualified URL, current-path-relative URL or false if
- *                no valid URL can be constructed
+ *    no valid URL can be constructed
  */
 function wfExpandUrl( $url, $defaultProto = PROTO_CURRENT ) {
        global $wgServer, $wgCanonicalServer, $wgInternalServer;
@@ -513,8 +516,9 @@ function wfExpandUrl( $url, $defaultProto = PROTO_CURRENT ) {
                if ( $serverHasProto ) {
                        $defaultProto = $bits['scheme'] . '://';
                } else {
-                       // $wgCanonicalServer or $wgInternalServer doesn't have a protocol. This really isn't supposed to happen
-                       // Fall back to HTTP in this ridiculous case
+                       // $wgCanonicalServer or $wgInternalServer doesn't have a protocol.
+                       // This really isn't supposed to happen. Fall back to HTTP in this
+                       // ridiculous case.
                        $defaultProto = PROTO_HTTP;
                }
        }
@@ -524,7 +528,8 @@ function wfExpandUrl( $url, $defaultProto = PROTO_CURRENT ) {
        if ( substr( $url, 0, 2 ) == '//' ) {
                $url = $defaultProtoWithoutSlashes . $url;
        } elseif ( substr( $url, 0, 1 ) == '/' ) {
-               // If $serverUrl is protocol-relative, prepend $defaultProtoWithoutSlashes, otherwise leave it alone
+               // If $serverUrl is protocol-relative, prepend $defaultProtoWithoutSlashes,
+               // otherwise leave it alone.
                $url = ( $serverHasProto ? '' : $defaultProtoWithoutSlashes ) . $serverUrl . $url;
        }
 
@@ -739,9 +744,10 @@ function wfUrlProtocolsWithoutProtRel() {
 /**
  * parse_url() work-alike, but non-broken.  Differences:
  *
- * 1) Does not raise warnings on bad URLs (just returns false)
- * 2) Handles protocols that don't use :// (e.g., mailto: and news: , as well as protocol-relative URLs) correctly
- * 3) Adds a "delimiter" element to the array, either '://', ':' or '//' (see (2))
+ * 1) Does not raise warnings on bad URLs (just returns false).
+ * 2) Handles protocols that don't use :// (e.g., mailto: and news:, as well as
+ *    protocol-relative URLs) correctly.
+ * 3) Adds a "delimiter" element to the array, either '://', ':' or '//' (see (2)).
  *
  * @param string $url a URL to parse
  * @return Array: bits of the URL in an associative array, per PHP docs
@@ -749,8 +755,9 @@ function wfUrlProtocolsWithoutProtRel() {
 function wfParseUrl( $url ) {
        global $wgUrlProtocols; // Allow all protocols defined in DefaultSettings/LocalSettings.php
 
-       // Protocol-relative URLs are handled really badly by parse_url(). It's so bad that the easiest
-       // way to handle them is to just prepend 'http:' and strip the protocol out later
+       // Protocol-relative URLs are handled really badly by parse_url(). It's so
+       // bad that the easiest way to handle them is to just prepend 'http:' and
+       // strip the protocol out later.
        $wasRelative = substr( $url, 0, 2 ) == '//';
        if ( $wasRelative ) {
                $url = "http:$url";
@@ -816,7 +823,11 @@ function wfParseUrl( $url ) {
  * @return string
  */
 function wfExpandIRI( $url ) {
-       return preg_replace_callback( '/((?:%[89A-F][0-9A-F])+)/i', 'wfExpandIRI_callback', wfExpandUrl( $url ) );
+       return preg_replace_callback(
+               '/((?:%[89A-F][0-9A-F])+)/i',
+               'wfExpandIRI_callback',
+               wfExpandUrl( $url )
+       );
 }
 
 /**
@@ -931,14 +942,12 @@ function wfDebug( $text, $logonly = false ) {
                MWDebug::debugMsg( $text );
        }
 
-       if ( wfRunHooks( 'Debug', array( $text, null /* no log group */ ) ) ) {
-               if ( $wgDebugLogFile != '' && !$wgProfileOnly ) {
-                       # Strip unprintables; they can switch terminal modes when binary data
-                       # gets dumped, which is pretty annoying.
-                       $text = preg_replace( '![\x00-\x08\x0b\x0c\x0e-\x1f]!', ' ', $text );
-                       $text = $wgDebugLogPrefix . $text;
-                       wfErrorLog( $text, $wgDebugLogFile );
-               }
+       if ( $wgDebugLogFile != '' && !$wgProfileOnly ) {
+               # Strip unprintables; they can switch terminal modes when binary data
+               # gets dumped, which is pretty annoying.
+               $text = preg_replace( '![\x00-\x08\x0b\x0c\x0e-\x1f]!', ' ', $text );
+               $text = $wgDebugLogPrefix . $text;
+               wfErrorLog( $text, $wgDebugLogFile );
        }
 }
 
@@ -999,7 +1008,12 @@ function wfDebugMem( $exact = false ) {
 
 /**
  * Send a line to a supplementary debug log file, if configured, or main debug log if not.
- * $wgDebugLogGroups[$logGroup] should be set to a filename to send to a separate log.
+ * To configure a supplementary log file, set $wgDebugLogGroups[$logGroup] to a string
+ * filename or an associative array mapping 'destination' to the desired filename. The
+ * associative array may also contain a 'sample' key with an integer value, specifying
+ * a sampling factor.
+ *
+ * @since 1.23 support for sampling log messages via $wgDebugLogGroups.
  *
  * @param $logGroup String
  * @param $text String
@@ -1009,16 +1023,28 @@ function wfDebugMem( $exact = false ) {
 function wfDebugLog( $logGroup, $text, $public = true ) {
        global $wgDebugLogGroups;
        $text = trim( $text ) . "\n";
-       if ( isset( $wgDebugLogGroups[$logGroup] ) ) {
-               $time = wfTimestamp( TS_DB );
-               $wiki = wfWikiID();
-               $host = wfHostname();
-               if ( wfRunHooks( 'Debug', array( $text, $logGroup ) ) ) {
-                       wfErrorLog( "$time $host $wiki: $text", $wgDebugLogGroups[$logGroup] );
+
+       if ( !isset( $wgDebugLogGroups[$logGroup] ) ) {
+               if ( $public === true ) {
+                       wfDebug( "[$logGroup] $text", false );
+               }
+               return;
+       }
+
+       $logConfig = $wgDebugLogGroups[$logGroup];
+       if ( is_array( $logConfig ) ) {
+               if ( isset( $logConfig['sample'] ) && mt_rand( 1, $logConfig['sample'] ) !== 1 ) {
+                       return;
                }
-       } elseif ( $public === true ) {
-               wfDebug( "[$logGroup] $text", false );
+               $destination = $logConfig['destination'];
+       } else {
+               $destination = strval( $logConfig );
        }
+
+       $time = wfTimestamp( TS_DB );
+       $wiki = wfWikiID();
+       $host = wfHostname();
+       wfErrorLog( "$time $host $wiki: $text", $destination );
 }
 
 /**
@@ -1057,7 +1083,8 @@ function wfLogDBError( $text ) {
  * Throws a warning that $function is deprecated
  *
  * @param $function String
- * @param string|bool $version Version of MediaWiki that the function was deprecated in (Added in 1.19).
+ * @param string|bool $version Version of MediaWiki that the function
+ *    was deprecated in (Added in 1.19).
  * @param string|bool $component Added in 1.19.
  * @param $callerOffset integer: How far up the call stack is the original
  *    caller. 2 = function that called the function that called
@@ -2329,7 +2356,15 @@ function wfSuppressWarnings( $end = false ) {
                }
        } else {
                if ( !$suppressCount ) {
-                       $originalLevel = error_reporting( E_ALL & ~( E_WARNING | E_NOTICE | E_USER_WARNING | E_USER_NOTICE | E_DEPRECATED | E_USER_DEPRECATED | E_STRICT ) );
+                       $originalLevel = error_reporting( E_ALL & ~(
+                               E_WARNING |
+                               E_NOTICE |
+                               E_USER_WARNING |
+                               E_USER_NOTICE |
+                               E_DEPRECATED |
+                               E_USER_DEPRECATED |
+                               E_STRICT
+                       ) );
                }
                ++$suppressCount;
        }
@@ -2454,12 +2489,12 @@ function wfIsWindows() {
 }
 
 /**
- * Check if we are running under HipHop
+ * Check if we are running under HHVM
  *
  * @return Bool
  */
-function wfIsHipHop() {
-       return defined( 'HPHP_VERSION' );
+function wfIsHHVM() {
+       return defined( 'HHVM_VERSION' );
 }
 
 /**
@@ -2660,12 +2695,14 @@ function wfEscapeShellArg() {
 
                if ( wfIsWindows() ) {
                        // Escaping for an MSVC-style command line parser and CMD.EXE
+                       // @codingStandardsIgnoreStart For long URLs
                        // Refs:
                        //  * http://web.archive.org/web/20020708081031/http://mailman.lyra.org/pipermail/scite-interest/2002-March/000436.html
                        //  * http://technet.microsoft.com/en-us/library/cc723564.aspx
                        //  * Bug #13518
                        //  * CR r63214
                        // Double the backslashes before any double quotes. Escape the double quotes.
+                       // @codingStandardsIgnoreEnd
                        $tokens = preg_split( '/(\\\\*")/', $arg, -1, PREG_SPLIT_DELIM_CAPTURE );
                        $arg = '';
                        $iteration = 0;
@@ -2714,9 +2751,9 @@ function wfShellExecDisabled() {
                        $functions = explode( ',', ini_get( 'disable_functions' ) );
                        $functions = array_map( 'trim', $functions );
                        $functions = array_map( 'strtolower', $functions );
-                       if ( in_array( 'passthru', $functions ) ) {
-                               wfDebug( "passthru is in disabled_functions\n" );
-                               $disabled = 'passthru';
+                       if ( in_array( 'proc_open', $functions ) ) {
+                               wfDebug( "proc_open is in disabled_functions\n" );
+                               $disabled = 'disabled';
                        }
                }
        }
@@ -2728,16 +2765,21 @@ function wfShellExecDisabled() {
  * configuration if supported.
  * @param string $cmd Command line, properly escaped for shell.
  * @param &$retval null|Mixed optional, will receive the program's exit code.
- *                 (non-zero is usually failure)
+ *                 (non-zero is usually failure). If there is an error from
+ *                 read, select, or proc_open(), this will be set to -1.
  * @param array $environ optional environment variables which should be
  *                 added to the executed command environment.
  * @param array $limits optional array with limits(filesize, memory, time, walltime)
  *                 this overwrites the global wgShellMax* limits.
- * @param array $options Array of options. Only one is "duplicateStderr" => true, which
- *                 Which duplicates stderr to stdout, including errors from limit.sh
+ * @param array $options Array of options:
+ *    - duplicateStderr: Set this to true to duplicate stderr to stdout, 
+ *      including errors from limit.sh
+ *      
  * @return string collected stdout as a string
  */
-function wfShellExec( $cmd, &$retval = null, $environ = array(), $limits = array(), $options = array() ) {
+function wfShellExec( $cmd, &$retval = null, $environ = array(),
+       $limits = array(), $options = array()
+) {
        global $IP, $wgMaxShellMemory, $wgMaxShellFileSize, $wgMaxShellTime,
                $wgMaxShellWallClockTime, $wgShellCgroup;
 
@@ -2746,7 +2788,7 @@ function wfShellExec( $cmd, &$retval = null, $environ = array(), $limits = array
                $retval = 1;
                return $disabled == 'safemode' ?
                        'Unable to run external programs in safe mode.' :
-                       'Unable to run external programs, passthru() is disabled.';
+                       'Unable to run external programs, proc_open() is disabled.';
        }
 
        $includeStderr = isset( $options['duplicateStderr'] ) && $options['duplicateStderr'];
@@ -2772,11 +2814,8 @@ function wfShellExec( $cmd, &$retval = null, $environ = array(), $limits = array
        }
        $cmd = $envcmd . $cmd;
 
+       $useLogPipe = false;
        if ( php_uname( 's' ) == 'Linux' ) {
-               $stderrDuplication = '';
-               if ( $includeStderr ) {
-                       $stderrDuplication = 'exec 2>&1; ';
-               }
                $time = intval ( isset( $limits['time'] ) ? $limits['time'] : $wgMaxShellTime );
                if ( isset( $limits['walltime'] ) ) {
                        $wallTime = intval( $limits['walltime'] );
@@ -2792,14 +2831,16 @@ function wfShellExec( $cmd, &$retval = null, $environ = array(), $limits = array
                        $cmd = '/bin/bash ' . escapeshellarg( "$IP/includes/limit.sh" ) . ' ' .
                                escapeshellarg( $cmd ) . ' ' .
                                escapeshellarg(
-                                       $stderrDuplication .
+                                       "MW_INCLUDE_STDERR=" . ( $includeStderr ? '1' : '' ) . ';' .
                                        "MW_CPU_LIMIT=$time; " .
                                        'MW_CGROUP=' . escapeshellarg( $wgShellCgroup ) . '; ' .
                                        "MW_MEM_LIMIT=$mem; " .
                                        "MW_FILE_SIZE_LIMIT=$filesize; " .
-                                       "MW_WALL_CLOCK_LIMIT=$wallTime"
+                                       "MW_WALL_CLOCK_LIMIT=$wallTime; " .
+                                       "MW_USE_LOG_PIPE=yes"
                                );
-               } else {
+                       $useLogPipe = true;
+               } elseif ( $includeStderr ) {
                        $cmd .= ' 2>&1';
                }
        } elseif ( $includeStderr ) {
@@ -2807,19 +2848,135 @@ function wfShellExec( $cmd, &$retval = null, $environ = array(), $limits = array
        }
        wfDebug( "wfShellExec: $cmd\n" );
 
-       // Default to an unusual value that shouldn't happen naturally,
-       // so in the unlikely event of a weird php bug, it would be
-       // more obvious what happened.
-       $retval = 200;
-       ob_start();
-       passthru( $cmd, $retval );
-       $output = ob_get_contents();
-       ob_end_clean();
+       $desc = array(
+               0 => array( 'file', 'php://stdin', 'r' ),
+               1 => array( 'pipe', 'w' ),
+               2 => array( 'file', 'php://stderr', 'w' ) );
+       if ( $useLogPipe ) {
+               $desc[3] = array( 'pipe', 'w' );
+       }
+       $pipes = null;
+       $proc = proc_open( $cmd, $desc, $pipes );
+       if ( !$proc ) {
+               wfDebugLog( 'exec', "proc_open() failed: $cmd\n" );
+               $retval = -1;
+               return '';
+       }
+       $outBuffer = $logBuffer = '';
+       $emptyArray = array();
+       $status = false;
+       $logMsg = false;
+
+       // According to the documentation, it is possible for stream_select()
+       // to fail due to EINTR. I haven't managed to induce this in testing 
+       // despite sending various signals. If it did happen, the error 
+       // message would take the form: 
+       //
+       // stream_select(): unable to select [4]: Interrupted system call (max_fd=5)
+       //
+       // where [4] is the value of the macro EINTR and "Interrupted system
+       // call" is string which according to the Linux manual is "possibly"
+       // localised according to LC_MESSAGES.
+       $eintr = defined( 'SOCKET_EINTR' ) ? SOCKET_EINTR : 4;
+       $eintrMessage = "stream_select(): unable to select [$eintr]";
+
+       // Build a table mapping resource IDs to pipe FDs to work around a
+       // PHP 5.3 issue in which stream_select() does not preserve array keys
+       // <https://bugs.php.net/bug.php?id=53427>.
+       $fds = array();
+       foreach ( $pipes as $fd => $pipe ) {
+               $fds[(int)$pipe] = $fd;
+       }
+
+       while ( true ) {
+               $status = proc_get_status( $proc );
+               if ( !$status['running'] ) {
+                       break;
+               }
+               $status = false;
+
+               $readyPipes = $pipes;
+
+               // Clear last error
+               @trigger_error( '' );
+               if ( @stream_select( $readyPipes, $emptyArray, $emptyArray, null ) === false ) {
+                       $error = error_get_last();
+                       if ( strncmp( $error['message'], $eintrMessage, strlen( $eintrMessage ) ) == 0 ) {
+                               continue;
+                       } else {
+                               trigger_error( $error['message'], E_USER_WARNING );
+                               $logMsg = $error['message'];
+                               break;
+                       }
+               }
+               foreach ( $readyPipes as $pipe ) {
+                       $block = fread( $pipe, 65536 );
+                       $fd = $fds[(int)$pipe];
+                       if ( $block === '' ) {
+                               // End of file
+                               fclose( $pipes[$fd] );
+                               unset( $pipes[$fd] );
+                               if ( !$pipes ) {
+                                       break 2;
+                               }
+                       } elseif ( $block === false ) {
+                               // Read error
+                               $logMsg = "Error reading from pipe";
+                               break 2;
+                       } elseif ( $fd == 1 ) {
+                               // From stdout
+                               $outBuffer .= $block;
+                       } elseif ( $fd == 3 ) {
+                               // From log FD
+                               $logBuffer .= $block;
+                               if ( strpos( $block, "\n" ) !== false ) {
+                                       $lines = explode( "\n", $logBuffer );
+                                       $logBuffer = array_pop( $lines );
+                                       foreach ( $lines as $line ) {
+                                               wfDebugLog( 'exec', $line );
+                                       }
+                               }
+                       }
+               }
+       }
+
+       foreach ( $pipes as $pipe ) {
+               fclose( $pipe );
+       }
 
-       if ( $retval == 127 ) {
-               wfDebugLog( 'exec', "Possibly missing executable file: $cmd\n" );
+       // Use the status previously collected if possible, since proc_get_status()
+       // just calls waitpid() which will not return anything useful the second time.
+       if ( $status === false ) {
+               $status = proc_get_status( $proc );
        }
-       return $output;
+
+       if ( $logMsg !== false ) {
+               // Read/select error
+               $retval = -1;
+               proc_close( $proc );
+       } elseif ( $status['signaled'] ) {
+               $logMsg = "Exited with signal {$status['termsig']}";
+               $retval = 128 + $status['termsig'];
+               proc_close( $proc );
+       } else {
+               if ( $status['running'] ) {
+                       $retval = proc_close( $proc );
+               } else {
+                       $retval = $status['exitcode'];
+                       proc_close( $proc );
+               }
+               if ( $retval == 127 ) {
+                       $logMsg = "Possibly missing executable file";
+               } elseif ( $retval >= 129 && $retval <= 192 ) {
+                       $logMsg = "Probably exited with signal " . ( $retval - 128 );
+               }
+       }
+
+       if ( $logMsg !== false ) {
+               wfDebugLog( 'exec', "$logMsg: $cmd\n" );
+       }
+
+       return $outBuffer;
 }
 
 /**
@@ -3073,6 +3230,14 @@ function wfUsePHP( $req_ver ) {
  * This is useful for extensions which due to their nature are not kept in sync
  * with releases
  *
+ * Note: Due to the behavior of PHP's version_compare() which is used in this
+ * fuction, if you want to allow the 'wmf' development versions add a 'c' (or
+ * any single letter other than 'a', 'b' or 'p') as a post-fix to your
+ * targeted version number. For example if you wanted to allow any variation
+ * of 1.22 use `wfUseMW( '1.22c' )`. Using an 'a' or 'b' instead of 'c' will
+ * not result in the same comparison due to the internal logic of
+ * version_compare().
+ *
  * @see perldoc -f use
  *
  * @param $req_ver Mixed: the version to check, can be a string, an integer, or
@@ -3100,9 +3265,12 @@ function wfUseMW( $req_ver ) {
  * @return String
  */
 function wfBaseName( $path, $suffix = '' ) {
-       $encSuffix = ( $suffix == '' )
-               ? ''
-               : ( '(?:' . preg_quote( $suffix, '#' ) . ')?' );
+       if ( $suffix == '' ) {
+               $encSuffix = '';
+       } else {
+               $encSuffix = '(?:' . preg_quote( $suffix, '#' ) . ')?';
+       }
+
        $matches = array();
        if ( preg_match( "#([^/\\\\]*?){$encSuffix}[/\\\\]*$#", $path, $matches ) ) {
                return $matches[1];
@@ -3183,7 +3351,9 @@ function wfDoUpdates( $commit = '' ) {
  * @param string $engine Either "gmp", "bcmath", or "php"
  * @return string|bool The output number as a string, or false on error
  */
-function wfBaseConvert( $input, $sourceBase, $destBase, $pad = 1, $lowercase = true, $engine = 'auto' ) {
+function wfBaseConvert( $input, $sourceBase, $destBase, $pad = 1,
+       $lowercase = true, $engine = 'auto'
+) {
        $input = (string)$input;
        if (
                $sourceBase < 2 ||
@@ -3193,7 +3363,10 @@ function wfBaseConvert( $input, $sourceBase, $destBase, $pad = 1, $lowercase = t
                $sourceBase != (int)$sourceBase ||
                $destBase != (int)$destBase ||
                $pad != (int)$pad ||
-               !preg_match( "/^[" . substr( '0123456789abcdefghijklmnopqrstuvwxyz', 0, $sourceBase ) . "]+$/i", $input )
+               !preg_match(
+                       "/^[" . substr( '0123456789abcdefghijklmnopqrstuvwxyz', 0, $sourceBase ) . "]+$/i",
+                       $input
+               )
        ) {
                return false;
        }
@@ -3327,9 +3500,11 @@ function wfFixSessionID() {
        // We treat it as disabled if it doesn't have an entropy length of at least 32
        $entropyEnabled = wfCheckEntropy();
 
-       // If built-in entropy is not enabled or not sufficient override php's built in session id generation code
+       // If built-in entropy is not enabled or not sufficient override PHP's
+       // built in session id generation code
        if ( !$entropyEnabled ) {
-               wfDebug( __METHOD__ . ": PHP's built in entropy is disabled or not sufficient, overriding session id generation using our cryptrand source.\n" );
+               wfDebug( __METHOD__ . ": PHP's built in entropy is disabled or not sufficient, " .
+                       "overriding session id generation using our cryptrand source.\n" );
                session_id( MWCryptRand::generateHex( 32 ) );
        }
 }
@@ -3635,9 +3810,7 @@ function wfBoolToStr( $value ) {
  * @return string
  */
 function wfGetNull() {
-       return wfIsWindows()
-               ? 'NUL'
-               : '/dev/null';
+       return wfIsWindows() ? 'NUL' : '/dev/null';
 }
 
 /**
@@ -3647,14 +3820,17 @@ function wfGetNull() {
  * in maintenance scripts, to avoid causing too much lag.  Of course, this is
  * a no-op if there are no slaves.
  *
- * @param $maxLag Integer (deprecated)
- * @param $wiki mixed Wiki identifier accepted by wfGetLB
- * @param $cluster string cluster name accepted by LBFactory
+ * @param int|bool $maxLag (deprecated)
+ * @param mixed $wiki Wiki identifier accepted by wfGetLB
+ * @param string|bool $cluster Cluster name accepted by LBFactory. Default: false.
  */
 function wfWaitForSlaves( $maxLag = false, $wiki = false, $cluster = false ) {
-       $lb = ( $cluster !== false )
-               ? wfGetLBFactory()->getExternalLB( $cluster )
-               : wfGetLB( $wiki );
+       if( $cluster !== false ) {
+               $lb = wfGetLBFactory()->getExternalLB( $cluster );
+       } else {
+               $lb = wfGetLB( $wiki );
+       }
+
        // bug 27975 - Don't try to wait for slaves if there are none
        // Prevents permission error when getting master position
        if ( $lb->getServerCount() > 1 ) {
@@ -3709,8 +3885,10 @@ function wfCountDown( $n ) {
  *              characters before hashing.
  * @return string
  * @codeCoverageIgnore
- * @deprecated since 1.20; Please use MWCryptRand for security purposes and wfRandomString for pseudo-random strings
- * @warning This method is NOT secure. Additionally it has many callers that use it for pseudo-random purposes.
+ * @deprecated since 1.20; Please use MWCryptRand for security purposes and
+ * wfRandomString for pseudo-random strings
+ * @warning This method is NOT secure. Additionally it has many callers that
+ * use it for pseudo-random purposes.
  */
 function wfGenerateToken( $salt = '' ) {
        wfDeprecated( __METHOD__, '1.20' );
diff --git a/includes/HashRing.php b/includes/HashRing.php
deleted file mode 100644 (file)
index 930f8c0..0000000
+++ /dev/null
@@ -1,142 +0,0 @@
-<?php
-/**
- * Convenience class for weighted consistent hash rings.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- * @author Aaron Schulz
- */
-
-/**
- * Convenience class for weighted consistent hash rings
- *
- * @since 1.22
- */
-class HashRing {
-       /** @var Array (location => weight) */
-       protected $sourceMap = array();
-       /** @var Array (location => (start, end)) */
-       protected $ring = array();
-
-       const RING_SIZE = 268435456; // 2^28
-
-       /**
-        * @param array $map (location => weight)
-        */
-       public function __construct( array $map ) {
-               $map = array_filter( $map, function( $w ) { return $w > 0; } );
-               if ( !count( $map ) ) {
-                       throw new MWException( "Ring is empty or all weights are zero." );
-               }
-               $this->sourceMap = $map;
-               // Sort the locations based on the hash of their names
-               $hashes = array();
-               foreach ( $map as $location => $weight ) {
-                       $hashes[$location] = sha1( $location );
-               }
-               uksort( $map, function ( $a, $b ) use ( $hashes ) {
-                       return strcmp( $hashes[$a], $hashes[$b] );
-               } );
-               // Fit the map to weight-proportionate one with a space of size RING_SIZE
-               $sum = array_sum( $map );
-               $standardMap = array();
-               foreach ( $map as $location => $weight ) {
-                       $standardMap[$location] = (int)floor( $weight / $sum * self::RING_SIZE );
-               }
-               // Build a ring of RING_SIZE spots, with each location at a spot in location hash order
-               $index = 0;
-               foreach ( $standardMap as $location => $weight ) {
-                       // Location covers half-closed interval [$index,$index + $weight)
-                       $this->ring[$location] = array( $index, $index + $weight );
-                       $index += $weight;
-               }
-               // Make sure the last location covers what is left
-               end( $this->ring );
-               $this->ring[key( $this->ring )][1] = self::RING_SIZE;
-       }
-
-       /**
-        * Get the location of an item on the ring
-        *
-        * @param string $item
-        * @return string Location
-        */
-       public function getLocation( $item ) {
-               $locations = $this->getLocations( $item, 1 );
-               return $locations[0];
-       }
-
-       /**
-        * Get the location of an item on the ring, as well as the next clockwise locations
-        *
-        * @param string $item
-        * @param integer $limit Maximum number of locations to return
-        * @return array List of locations
-        */
-       public function getLocations( $item, $limit ) {
-               $locations = array();
-               $primaryLocation = null;
-               $spot = hexdec( substr( sha1( $item ), 0, 7 ) ); // first 28 bits
-               foreach ( $this->ring as $location => $range ) {
-                       if ( count( $locations ) >= $limit ) {
-                               break;
-                       }
-                       // The $primaryLocation is the location the item spot is in.
-                       // After that is reached, keep appending the next locations.
-                       if ( ( $range[0] <= $spot && $spot < $range[1] ) || $primaryLocation !== null ) {
-                               if ( $primaryLocation === null ) {
-                                       $primaryLocation = $location;
-                               }
-                               $locations[] = $location;
-                       }
-               }
-               // If more locations are requested, wrap-around and keep adding them
-               reset( $this->ring );
-               while ( count( $locations ) < $limit ) {
-                       list( $location, ) = each( $this->ring );
-                       if ( $location === $primaryLocation ) {
-                               break; // don't go in circles
-                       }
-                       $locations[] = $location;
-               }
-               return $locations;
-       }
-
-       /**
-        * Get the map of locations to weight (ignores 0-weight items)
-        *
-        * @return array
-        */
-       public function getLocationWeights() {
-               return $this->sourceMap;
-       }
-
-       /**
-        * Get a new hash ring with a location removed from the ring
-        *
-        * @param string $location
-        * @return HashRing|bool Returns false if no non-zero weighted spots are left
-        */
-       public function newWithoutLocation( $location ) {
-               $map = $this->sourceMap;
-               unset( $map[$location] );
-               if ( count( $map ) ) {
-                       return new self( $map );
-               }
-               return false;
-       }
-}
index 31aa0f8..9c50895 100644 (file)
@@ -290,7 +290,7 @@ class HistoryBlobStub {
  * of megabytes of data during the conversion downtime.
  *
  * Serialized HistoryBlobCurStub objects will be inserted into the text table
- * on conversion if $wgFastSchemaUpgrades is set to true.
+ * on conversion if $wgLegacySchemaConversion is set to true.
  */
 class HistoryBlobCurStub {
        var $mCurId;
diff --git a/includes/IP.php b/includes/IP.php
deleted file mode 100644 (file)
index 73834a5..0000000
+++ /dev/null
@@ -1,761 +0,0 @@
-<?php
-/**
- * Functions and constants to play with IP addresses and ranges
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- * @author Antoine Musso "<hashar at free dot fr>", Aaron Schulz
- */
-
-// Some regex definition to "play" with IP address and IP address blocks
-
-// An IPv4 address is made of 4 bytes from x00 to xFF which is d0 to d255
-define( 'RE_IP_BYTE', '(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|0?[0-9]?[0-9])' );
-define( 'RE_IP_ADD', RE_IP_BYTE . '\.' . RE_IP_BYTE . '\.' . RE_IP_BYTE . '\.' . RE_IP_BYTE );
-// An IPv4 block is an IP address and a prefix (d1 to d32)
-define( 'RE_IP_PREFIX', '(3[0-2]|[12]?\d)' );
-define( 'RE_IP_BLOCK', RE_IP_ADD . '\/' . RE_IP_PREFIX );
-
-// An IPv6 address is made up of 8 words (each x0000 to xFFFF).
-// However, the "::" abbreviation can be used on consecutive x0000 words.
-define( 'RE_IPV6_WORD', '([0-9A-Fa-f]{1,4})' );
-define( 'RE_IPV6_PREFIX', '(12[0-8]|1[01][0-9]|[1-9]?\d)' );
-define( 'RE_IPV6_ADD',
-       '(?:' . // starts with "::" (including "::")
-               ':(?::|(?::' . RE_IPV6_WORD . '){1,7})' .
-       '|' . // ends with "::" (except "::")
-               RE_IPV6_WORD . '(?::' . RE_IPV6_WORD . '){0,6}::' .
-       '|' . // contains one "::" in the middle (the ^ makes the test fail if none found)
-               RE_IPV6_WORD . '(?::((?(-1)|:))?' . RE_IPV6_WORD . '){1,6}(?(-2)|^)' .
-       '|' . // contains no "::"
-               RE_IPV6_WORD . '(?::' . RE_IPV6_WORD . '){7}' .
-       ')'
-);
-// An IPv6 block is an IP address and a prefix (d1 to d128)
-define( 'RE_IPV6_BLOCK', RE_IPV6_ADD . '\/' . RE_IPV6_PREFIX );
-// For IPv6 canonicalization (NOT for strict validation; these are quite lax!)
-define( 'RE_IPV6_GAP', ':(?:0+:)*(?::(?:0+:)*)?' );
-define( 'RE_IPV6_V4_PREFIX', '0*' . RE_IPV6_GAP . '(?:ffff:)?' );
-
-// This might be useful for regexps used elsewhere, matches any IPv6 or IPv6 address or network
-define( 'IP_ADDRESS_STRING',
-       '(?:' .
-               RE_IP_ADD . '(?:\/' . RE_IP_PREFIX . ')?' . // IPv4
-       '|' .
-               RE_IPV6_ADD . '(?:\/' . RE_IPV6_PREFIX . ')?' . // IPv6
-       ')'
-);
-
-/**
- * A collection of public static functions to play with IP address
- * and IP blocks.
- */
-class IP {
-       /**
-        * Determine if a string is as valid IP address or network (CIDR prefix).
-        * SIIT IPv4-translated addresses are rejected.
-        * Note: canonicalize() tries to convert translated addresses to IPv4.
-        *
-        * @param string $ip possible IP address
-        * @return Boolean
-        */
-       public static function isIPAddress( $ip ) {
-               return (bool)preg_match( '/^' . IP_ADDRESS_STRING . '$/', $ip );
-       }
-
-       /**
-        * Given a string, determine if it as valid IP in IPv6 only.
-        * Note: Unlike isValid(), this looks for networks too.
-        *
-        * @param string $ip possible IP address
-        * @return Boolean
-        */
-       public static function isIPv6( $ip ) {
-               return (bool)preg_match( '/^' . RE_IPV6_ADD . '(?:\/' . RE_IPV6_PREFIX . ')?$/', $ip );
-       }
-
-       /**
-        * Given a string, determine if it as valid IP in IPv4 only.
-        * Note: Unlike isValid(), this looks for networks too.
-        *
-        * @param string $ip possible IP address
-        * @return Boolean
-        */
-       public static function isIPv4( $ip ) {
-               return (bool)preg_match( '/^' . RE_IP_ADD . '(?:\/' . RE_IP_PREFIX . ')?$/', $ip );
-       }
-
-       /**
-        * Validate an IP address. Ranges are NOT considered valid.
-        * SIIT IPv4-translated addresses are rejected.
-        * Note: canonicalize() tries to convert translated addresses to IPv4.
-        *
-        * @param $ip String
-        * @return Boolean: True if it is valid.
-        */
-       public static function isValid( $ip ) {
-               return ( preg_match( '/^' . RE_IP_ADD . '$/', $ip )
-                       || preg_match( '/^' . RE_IPV6_ADD . '$/', $ip ) );
-       }
-
-       /**
-        * Validate an IP Block (valid address WITH a valid prefix).
-        * SIIT IPv4-translated addresses are rejected.
-        * Note: canonicalize() tries to convert translated addresses to IPv4.
-        *
-        * @param $ipblock String
-        * @return Boolean: True if it is valid.
-        */
-       public static function isValidBlock( $ipblock ) {
-               return ( preg_match( '/^' . RE_IPV6_BLOCK . '$/', $ipblock )
-                       || preg_match( '/^' . RE_IP_BLOCK . '$/', $ipblock ) );
-       }
-
-       /**
-        * Convert an IP into a verbose, uppercase, normalized form.
-        * IPv6 addresses in octet notation are expanded to 8 words.
-        * IPv4 addresses are just trimmed.
-        *
-        * @param string $ip IP address in quad or octet form (CIDR or not).
-        * @return String
-        */
-       public static function sanitizeIP( $ip ) {
-               $ip = trim( $ip );
-               if ( $ip === '' ) {
-                       return null;
-               }
-               if ( self::isIPv4( $ip ) || !self::isIPv6( $ip ) ) {
-                       return $ip; // nothing else to do for IPv4 addresses or invalid ones
-               }
-               // Remove any whitespaces, convert to upper case
-               $ip = strtoupper( $ip );
-               // Expand zero abbreviations
-               $abbrevPos = strpos( $ip, '::' );
-               if ( $abbrevPos !== false ) {
-                       // We know this is valid IPv6. Find the last index of the
-                       // address before any CIDR number (e.g. "a:b:c::/24").
-                       $CIDRStart = strpos( $ip, "/" );
-                       $addressEnd = ( $CIDRStart !== false )
-                               ? $CIDRStart - 1
-                               : strlen( $ip ) - 1;
-                       // If the '::' is at the beginning...
-                       if ( $abbrevPos == 0 ) {
-                               $repeat = '0:';
-                               $extra = ( $ip == '::' ) ? '0' : ''; // for the address '::'
-                               $pad = 9; // 7+2 (due to '::')
-                       // If the '::' is at the end...
-                       } elseif ( $abbrevPos == ( $addressEnd - 1 ) ) {
-                               $repeat = ':0';
-                               $extra = '';
-                               $pad = 9; // 7+2 (due to '::')
-                       // If the '::' is in the middle...
-                       } else {
-                               $repeat = ':0';
-                               $extra = ':';
-                               $pad = 8; // 6+2 (due to '::')
-                       }
-                       $ip = str_replace( '::',
-                               str_repeat( $repeat, $pad - substr_count( $ip, ':' ) ) . $extra,
-                               $ip
-                       );
-               }
-               // Remove leading zeros from each bloc as needed
-               $ip = preg_replace( '/(^|:)0+(' . RE_IPV6_WORD . ')/', '$1$2', $ip );
-               return $ip;
-       }
-
-       /**
-        * Prettify an IP for display to end users.
-        * This will make it more compact and lower-case.
-        *
-        * @param $ip string
-        * @return string
-        */
-       public static function prettifyIP( $ip ) {
-               $ip = self::sanitizeIP( $ip ); // normalize (removes '::')
-               if ( self::isIPv6( $ip ) ) {
-                       // Split IP into an address and a CIDR
-                       if ( strpos( $ip, '/' ) !== false ) {
-                               list( $ip, $cidr ) = explode( '/', $ip, 2 );
-                       } else {
-                               list( $ip, $cidr ) = array( $ip, '' );
-                       }
-                       // Get the largest slice of words with multiple zeros
-                       $offset = 0;
-                       $longest = $longestPos = false;
-                       while ( preg_match(
-                               '!(?:^|:)0(?::0)+(?:$|:)!', $ip, $m, PREG_OFFSET_CAPTURE, $offset
-                       ) ) {
-                               list( $match, $pos ) = $m[0]; // full match
-                               if ( strlen( $match ) > strlen( $longest ) ) {
-                                       $longest = $match;
-                                       $longestPos = $pos;
-                               }
-                               $offset = ( $pos + strlen( $match ) ); // advance
-                       }
-                       if ( $longest !== false ) {
-                               // Replace this portion of the string with the '::' abbreviation
-                               $ip = substr_replace( $ip, '::', $longestPos, strlen( $longest ) );
-                       }
-                       // Add any CIDR back on
-                       if ( $cidr !== '' ) {
-                               $ip = "{$ip}/{$cidr}";
-                       }
-                       // Convert to lower case to make it more readable
-                       $ip = strtolower( $ip );
-               }
-               return $ip;
-       }
-
-       /**
-        * Given a host/port string, like one might find in the host part of a URL
-        * per RFC 2732, split the hostname part and the port part and return an
-        * array with an element for each. If there is no port part, the array will
-        * have false in place of the port. If the string was invalid in some way,
-        * false is returned.
-        *
-        * This was easy with IPv4 and was generally done in an ad-hoc way, but
-        * with IPv6 it's somewhat more complicated due to the need to parse the
-        * square brackets and colons.
-        *
-        * A bare IPv6 address is accepted despite the lack of square brackets.
-        *
-        * @param string $both The string with the host and port
-        * @return array
-        */
-       public static function splitHostAndPort( $both ) {
-               if ( substr( $both, 0, 1 ) === '[' ) {
-                       if ( preg_match( '/^\[(' . RE_IPV6_ADD . ')\](?::(?P<port>\d+))?$/', $both, $m ) ) {
-                               if ( isset( $m['port'] ) ) {
-                                       return array( $m[1], intval( $m['port'] ) );
-                               } else {
-                                       return array( $m[1], false );
-                               }
-                       } else {
-                               // Square bracket found but no IPv6
-                               return false;
-                       }
-               }
-               $numColons = substr_count( $both, ':' );
-               if ( $numColons >= 2 ) {
-                       // Is it a bare IPv6 address?
-                       if ( preg_match( '/^' . RE_IPV6_ADD . '$/', $both ) ) {
-                               return array( $both, false );
-                       } else {
-                               // Not valid IPv6, but too many colons for anything else
-                               return false;
-                       }
-               }
-               if ( $numColons >= 1 ) {
-                       // Host:port?
-                       $bits = explode( ':', $both );
-                       if ( preg_match( '/^\d+/', $bits[1] ) ) {
-                               return array( $bits[0], intval( $bits[1] ) );
-                       } else {
-                               // Not a valid port
-                               return false;
-                       }
-               }
-               // Plain hostname
-               return array( $both, false );
-       }
-
-       /**
-        * Given a host name and a port, combine them into host/port string like
-        * you might find in a URL. If the host contains a colon, wrap it in square
-        * brackets like in RFC 2732. If the port matches the default port, omit
-        * the port specification
-        *
-        * @param $host string
-        * @param $port int
-        * @param $defaultPort bool|int
-        * @return string
-        */
-       public static function combineHostAndPort( $host, $port, $defaultPort = false ) {
-               if ( strpos( $host, ':' ) !== false ) {
-                       $host = "[$host]";
-               }
-               if ( $defaultPort !== false && $port == $defaultPort ) {
-                       return $host;
-               } else {
-                       return "$host:$port";
-               }
-       }
-
-       /**
-        * Given an unsigned integer, returns an IPv6 address in octet notation
-        *
-        * @param $ip_int String: IP address.
-        * @return String
-        */
-       public static function toOctet( $ip_int ) {
-               return self::hexToOctet( wfBaseConvert( $ip_int, 10, 16, 32, false ) );
-       }
-
-       /**
-        * Convert an IPv4 or IPv6 hexadecimal representation back to readable format
-        *
-        * @param string $hex number, with "v6-" prefix if it is IPv6
-        * @return String: quad-dotted (IPv4) or octet notation (IPv6)
-        */
-       public static function formatHex( $hex ) {
-               if ( substr( $hex, 0, 3 ) == 'v6-' ) { // IPv6
-                       return self::hexToOctet( substr( $hex, 3 ) );
-               } else { // IPv4
-                       return self::hexToQuad( $hex );
-               }
-       }
-
-       /**
-        * Converts a hexadecimal number to an IPv6 address in octet notation
-        *
-        * @param $ip_hex String: pure hex (no v6- prefix)
-        * @return String (of format a:b:c:d:e:f:g:h)
-        */
-       public static function hexToOctet( $ip_hex ) {
-               // Pad hex to 32 chars (128 bits)
-               $ip_hex = str_pad( strtoupper( $ip_hex ), 32, '0', STR_PAD_LEFT );
-               // Separate into 8 words
-               $ip_oct = substr( $ip_hex, 0, 4 );
-               for ( $n = 1; $n < 8; $n++ ) {
-                       $ip_oct .= ':' . substr( $ip_hex, 4 * $n, 4 );
-               }
-               // NO leading zeroes
-               $ip_oct = preg_replace( '/(^|:)0+(' . RE_IPV6_WORD . ')/', '$1$2', $ip_oct );
-               return $ip_oct;
-       }
-
-       /**
-        * Converts a hexadecimal number to an IPv4 address in quad-dotted notation
-        *
-        * @param $ip_hex String: pure hex
-        * @return String (of format a.b.c.d)
-        */
-       public static function hexToQuad( $ip_hex ) {
-               // Pad hex to 8 chars (32 bits)
-               $ip_hex = str_pad( strtoupper( $ip_hex ), 8, '0', STR_PAD_LEFT );
-               // Separate into four quads
-               $s = '';
-               for ( $i = 0; $i < 4; $i++ ) {
-                       if ( $s !== '' ) {
-                               $s .= '.';
-                       }
-                       $s .= base_convert( substr( $ip_hex, $i * 2, 2 ), 16, 10 );
-               }
-               return $s;
-       }
-
-       /**
-        * Determine if an IP address really is an IP address, and if it is public,
-        * i.e. not RFC 1918 or similar
-        * Comes from ProxyTools.php
-        *
-        * @param $ip String
-        * @return Boolean
-        */
-       public static function isPublic( $ip ) {
-               if ( self::isIPv6( $ip ) ) {
-                       return self::isPublic6( $ip );
-               }
-               $n = self::toUnsigned( $ip );
-               if ( !$n ) {
-                       return false;
-               }
-
-               // ip2long accepts incomplete addresses, as well as some addresses
-               // followed by garbage characters. Check that it's really valid.
-               if ( $ip != long2ip( $n ) ) {
-                       return false;
-               }
-
-               static $privateRanges = false;
-               if ( !$privateRanges ) {
-                       $privateRanges = array(
-                               array( '10.0.0.0', '10.255.255.255' ), # RFC 1918 (private)
-                               array( '172.16.0.0', '172.31.255.255' ), # RFC 1918 (private)
-                               array( '192.168.0.0', '192.168.255.255' ), # RFC 1918 (private)
-                               array( '0.0.0.0', '0.255.255.255' ), # this network
-                               array( '127.0.0.0', '127.255.255.255' ), # loopback
-                       );
-               }
-
-               foreach ( $privateRanges as $r ) {
-                       $start = self::toUnsigned( $r[0] );
-                       $end = self::toUnsigned( $r[1] );
-                       if ( $n >= $start && $n <= $end ) {
-                               return false;
-                       }
-               }
-               return true;
-       }
-
-       /**
-        * Determine if an IPv6 address really is an IP address, and if it is public,
-        * i.e. not RFC 4193 or similar
-        *
-        * @param $ip String
-        * @return Boolean
-        */
-       private static function isPublic6( $ip ) {
-               static $privateRanges = false;
-               if ( !$privateRanges ) {
-                       $privateRanges = array(
-                               array( 'fc00::', 'fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff' ), # RFC 4193 (local)
-                               array( '0:0:0:0:0:0:0:1', '0:0:0:0:0:0:0:1' ), # loopback
-                       );
-               }
-               $n = self::toHex( $ip );
-               foreach ( $privateRanges as $r ) {
-                       $start = self::toHex( $r[0] );
-                       $end = self::toHex( $r[1] );
-                       if ( $n >= $start && $n <= $end ) {
-                               return false;
-                       }
-               }
-               return true;
-       }
-
-       /**
-        * Return a zero-padded upper case hexadecimal representation of an IP address.
-        *
-        * Hexadecimal addresses are used because they can easily be extended to
-        * IPv6 support. To separate the ranges, the return value from this
-        * function for an IPv6 address will be prefixed with "v6-", a non-
-        * hexadecimal string which sorts after the IPv4 addresses.
-        *
-        * @param string $ip quad dotted/octet IP address.
-        * @return String
-        */
-       public static function toHex( $ip ) {
-               if ( self::isIPv6( $ip ) ) {
-                       $n = 'v6-' . self::IPv6ToRawHex( $ip );
-               } else {
-                       $n = self::toUnsigned( $ip );
-                       if ( $n !== false ) {
-                               $n = wfBaseConvert( $n, 10, 16, 8, false );
-                       }
-               }
-               return $n;
-       }
-
-       /**
-        * Given an IPv6 address in octet notation, returns a pure hex string.
-        *
-        * @param string $ip octet ipv6 IP address.
-        * @return String: pure hex (uppercase)
-        */
-       private static function IPv6ToRawHex( $ip ) {
-               $ip = self::sanitizeIP( $ip );
-               if ( !$ip ) {
-                       return null;
-               }
-               $r_ip = '';
-               foreach ( explode( ':', $ip ) as $v ) {
-                       $r_ip .= str_pad( $v, 4, 0, STR_PAD_LEFT );
-               }
-               return $r_ip;
-       }
-
-       /**
-        * Given an IP address in dotted-quad/octet notation, returns an unsigned integer.
-        * Like ip2long() except that it actually works and has a consistent error return value.
-        * Comes from ProxyTools.php
-        *
-        * @param string $ip quad dotted IP address.
-        * @return Mixed: string/int/false
-        */
-       public static function toUnsigned( $ip ) {
-               if ( self::isIPv6( $ip ) ) {
-                       $n = self::toUnsigned6( $ip );
-               } else {
-                       $n = ip2long( $ip );
-                       if ( $n < 0 ) {
-                               $n += pow( 2, 32 );
-                               # On 32-bit platforms (and on Windows), 2^32 does not fit into an int,
-                               # so $n becomes a float. We convert it to string instead.
-                               if ( is_float( $n ) ) {
-                                       $n = (string)$n;
-                               }
-                       }
-               }
-               return $n;
-       }
-
-       /**
-        * @param $ip
-        * @return String
-        */
-       private static function toUnsigned6( $ip ) {
-               return wfBaseConvert( self::IPv6ToRawHex( $ip ), 16, 10 );
-       }
-
-       /**
-        * Convert a network specification in CIDR notation
-        * to an integer network and a number of bits
-        *
-        * @param string $range IP with CIDR prefix
-        * @return array(int or string, int)
-        */
-       public static function parseCIDR( $range ) {
-               if ( self::isIPv6( $range ) ) {
-                       return self::parseCIDR6( $range );
-               }
-               $parts = explode( '/', $range, 2 );
-               if ( count( $parts ) != 2 ) {
-                       return array( false, false );
-               }
-               list( $network, $bits ) = $parts;
-               $network = ip2long( $network );
-               if ( $network !== false && is_numeric( $bits ) && $bits >= 0 && $bits <= 32 ) {
-                       if ( $bits == 0 ) {
-                               $network = 0;
-                       } else {
-                               $network &= ~( ( 1 << ( 32 - $bits ) ) - 1 );
-                       }
-                       # Convert to unsigned
-                       if ( $network < 0 ) {
-                               $network += pow( 2, 32 );
-                       }
-               } else {
-                       $network = false;
-                       $bits = false;
-               }
-               return array( $network, $bits );
-       }
-
-       /**
-        * Given a string range in a number of formats,
-        * return the start and end of the range in hexadecimal.
-        *
-        * Formats are:
-        *     1.2.3.4/24          CIDR
-        *     1.2.3.4 - 1.2.3.5   Explicit range
-        *     1.2.3.4             Single IP
-        *
-        *     2001:0db8:85a3::7344/96                                   CIDR
-        *     2001:0db8:85a3::7344 - 2001:0db8:85a3::7344   Explicit range
-        *     2001:0db8:85a3::7344                                      Single IP
-        * @param string $range IP range
-        * @return array(string, string)
-        */
-       public static function parseRange( $range ) {
-               // CIDR notation
-               if ( strpos( $range, '/' ) !== false ) {
-                       if ( self::isIPv6( $range ) ) {
-                               return self::parseRange6( $range );
-                       }
-                       list( $network, $bits ) = self::parseCIDR( $range );
-                       if ( $network === false ) {
-                               $start = $end = false;
-                       } else {
-                               $start = sprintf( '%08X', $network );
-                               $end = sprintf( '%08X', $network + pow( 2, ( 32 - $bits ) ) - 1 );
-                       }
-               // Explicit range
-               } elseif ( strpos( $range, '-' ) !== false ) {
-                       list( $start, $end ) = array_map( 'trim', explode( '-', $range, 2 ) );
-                       if ( self::isIPv6( $start ) && self::isIPv6( $end ) ) {
-                               return self::parseRange6( $range );
-                       }
-                       if ( self::isIPv4( $start ) && self::isIPv4( $end ) ) {
-                               $start = self::toUnsigned( $start );
-                               $end = self::toUnsigned( $end );
-                               if ( $start > $end ) {
-                                       $start = $end = false;
-                               } else {
-                                       $start = sprintf( '%08X', $start );
-                                       $end = sprintf( '%08X', $end );
-                               }
-                       } else {
-                               $start = $end = false;
-                       }
-               } else {
-                       # Single IP
-                       $start = $end = self::toHex( $range );
-               }
-               if ( $start === false || $end === false ) {
-                       return array( false, false );
-               } else {
-                       return array( $start, $end );
-               }
-       }
-
-       /**
-        * Convert a network specification in IPv6 CIDR notation to an
-        * integer network and a number of bits
-        *
-        * @param $range
-        *
-        * @return array(string, int)
-        */
-       private static function parseCIDR6( $range ) {
-               # Explode into <expanded IP,range>
-               $parts = explode( '/', IP::sanitizeIP( $range ), 2 );
-               if ( count( $parts ) != 2 ) {
-                       return array( false, false );
-               }
-               list( $network, $bits ) = $parts;
-               $network = self::IPv6ToRawHex( $network );
-               if ( $network !== false && is_numeric( $bits ) && $bits >= 0 && $bits <= 128 ) {
-                       if ( $bits == 0 ) {
-                               $network = "0";
-                       } else {
-                               # Native 32 bit functions WONT work here!!!
-                               # Convert to a padded binary number
-                               $network = wfBaseConvert( $network, 16, 2, 128 );
-                               # Truncate the last (128-$bits) bits and replace them with zeros
-                               $network = str_pad( substr( $network, 0, $bits ), 128, 0, STR_PAD_RIGHT );
-                               # Convert back to an integer
-                               $network = wfBaseConvert( $network, 2, 10 );
-                       }
-               } else {
-                       $network = false;
-                       $bits = false;
-               }
-               return array( $network, (int)$bits );
-       }
-
-       /**
-        * Given a string range in a number of formats, return the
-        * start and end of the range in hexadecimal. For IPv6.
-        *
-        * Formats are:
-        *     2001:0db8:85a3::7344/96                                   CIDR
-        *     2001:0db8:85a3::7344 - 2001:0db8:85a3::7344   Explicit range
-        *     2001:0db8:85a3::7344/96                                   Single IP
-        *
-        * @param $range
-        *
-        * @return array(string, string)
-        */
-       private static function parseRange6( $range ) {
-               # Expand any IPv6 IP
-               $range = IP::sanitizeIP( $range );
-               // CIDR notation...
-               if ( strpos( $range, '/' ) !== false ) {
-                       list( $network, $bits ) = self::parseCIDR6( $range );
-                       if ( $network === false ) {
-                               $start = $end = false;
-                       } else {
-                               $start = wfBaseConvert( $network, 10, 16, 32, false );
-                               # Turn network to binary (again)
-                               $end = wfBaseConvert( $network, 10, 2, 128 );
-                               # Truncate the last (128-$bits) bits and replace them with ones
-                               $end = str_pad( substr( $end, 0, $bits ), 128, 1, STR_PAD_RIGHT );
-                               # Convert to hex
-                               $end = wfBaseConvert( $end, 2, 16, 32, false );
-                               # see toHex() comment
-                               $start = "v6-$start";
-                               $end = "v6-$end";
-                       }
-               // Explicit range notation...
-               } elseif ( strpos( $range, '-' ) !== false ) {
-                       list( $start, $end ) = array_map( 'trim', explode( '-', $range, 2 ) );
-                       $start = self::toUnsigned6( $start );
-                       $end = self::toUnsigned6( $end );
-                       if ( $start > $end ) {
-                               $start = $end = false;
-                       } else {
-                               $start = wfBaseConvert( $start, 10, 16, 32, false );
-                               $end = wfBaseConvert( $end, 10, 16, 32, false );
-                       }
-                       # see toHex() comment
-                       $start = "v6-$start";
-                       $end = "v6-$end";
-               } else {
-                       # Single IP
-                       $start = $end = self::toHex( $range );
-               }
-               if ( $start === false || $end === false ) {
-                       return array( false, false );
-               } else {
-                       return array( $start, $end );
-               }
-       }
-
-       /**
-        * Determine if a given IPv4/IPv6 address is in a given CIDR network
-        *
-        * @param string $addr the address to check against the given range.
-        * @param string $range the range to check the given address against.
-        * @return Boolean: whether or not the given address is in the given range.
-        */
-       public static function isInRange( $addr, $range ) {
-               $hexIP = self::toHex( $addr );
-               list( $start, $end ) = self::parseRange( $range );
-               return ( strcmp( $hexIP, $start ) >= 0 &&
-                       strcmp( $hexIP, $end ) <= 0 );
-       }
-
-       /**
-        * Convert some unusual representations of IPv4 addresses to their
-        * canonical dotted quad representation.
-        *
-        * This currently only checks a few IPV4-to-IPv6 related cases.  More
-        * unusual representations may be added later.
-        *
-        * @param string $addr something that might be an IP address
-        * @return String: valid dotted quad IPv4 address or null
-        */
-       public static function canonicalize( $addr ) {
-               // remove zone info (bug 35738)
-               $addr = preg_replace( '/\%.*/', '', $addr );
-
-               if ( self::isValid( $addr ) ) {
-                       return $addr;
-               }
-               // Turn mapped addresses from ::ce:ffff:1.2.3.4 to 1.2.3.4
-               if ( strpos( $addr, ':' ) !== false && strpos( $addr, '.' ) !== false ) {
-                       $addr = substr( $addr, strrpos( $addr, ':' ) + 1 );
-                       if ( self::isIPv4( $addr ) ) {
-                               return $addr;
-                       }
-               }
-               // IPv6 loopback address
-               $m = array();
-               if ( preg_match( '/^0*' . RE_IPV6_GAP . '1$/', $addr, $m ) ) {
-                       return '127.0.0.1';
-               }
-               // IPv4-mapped and IPv4-compatible IPv6 addresses
-               if ( preg_match( '/^' . RE_IPV6_V4_PREFIX . '(' . RE_IP_ADD . ')$/i', $addr, $m ) ) {
-                       return $m[1];
-               }
-               if ( preg_match( '/^' . RE_IPV6_V4_PREFIX . RE_IPV6_WORD .
-                       ':' . RE_IPV6_WORD . '$/i', $addr, $m ) )
-               {
-                       return long2ip( ( hexdec( $m[1] ) << 16 ) + hexdec( $m[2] ) );
-               }
-
-               return null; // give up
-       }
-
-       /**
-        * Gets rid of unneeded numbers in quad-dotted/octet IP strings
-        * For example, 127.111.113.151/24 -> 127.111.113.0/24
-        * @param string $range IP address to normalize
-        * @return string
-        */
-       public static function sanitizeRange( $range ) {
-               list( /*...*/, $bits ) = self::parseCIDR( $range );
-               list( $start, /*...*/ ) = self::parseRange( $range );
-               $start = self::formatHex( $start );
-               if ( $bits === false ) {
-                       return $start; // wasn't actually a range
-               }
-               return "$start/$bits";
-       }
-}
index 7ea06b0..cf05ee2 100644 (file)
@@ -128,7 +128,7 @@ class ImagePage extends Article {
                                $out->setPageTitle( $this->getTitle()->getPrefixedText() );
                                $out->addHTML( $this->viewRedirect( Title::makeTitle( NS_FILE, $this->mPage->getFile()->getName() ),
                                        /* $appendSubtitle */ true, /* $forceKnown */ true ) );
-                               $this->mPage->doViewUpdates( $this->getContext()->getUser() );
+                               $this->mPage->doViewUpdates( $this->getContext()->getUser(), $this->getOldID() );
                                return;
                        }
                }
@@ -165,7 +165,7 @@ class ImagePage extends Article {
                        # Just need to set the right headers
                        $out->setArticleFlag( true );
                        $out->setPageTitle( $this->getTitle()->getPrefixedText() );
-                       $this->mPage->doViewUpdates( $this->getContext()->getUser() );
+                       $this->mPage->doViewUpdates( $this->getContext()->getUser(), $this->getOldID() );
                }
 
                # Show shared description, if needed
@@ -600,7 +600,7 @@ EOT
                $this->loadFile();
 
                $descUrl = $this->mPage->getFile()->getDescriptionUrl();
-               $descText = $this->mPage->getFile()->getDescriptionText();
+               $descText = $this->mPage->getFile()->getDescriptionText( $this->getContext()->getLanguage() );
 
                /* Add canonical to head if there is no local page for this shared file */
                if ( $descUrl && $this->mPage->getID() == 0 ) {
index 64431f0..dd5e2d7 100644 (file)
@@ -48,7 +48,7 @@ class MWInit {
         * @return bool
         */
        static function isHipHop() {
-               return defined( 'HPHP_VERSION' );
+               return wfIsHHVM();
        }
 
        /**
index 5bb9230..4dcdfd5 100644 (file)
@@ -1960,6 +1960,7 @@ class Linker {
         * @return String: HTML output
         */
        public static function formatTemplates( $templates, $preview = false, $section = false, $more = null ) {
+               global $wgLang;
                wfProfileIn( __METHOD__ );
 
                $outText = '';
@@ -1987,13 +1988,28 @@ class Linker {
 
                        usort( $templates, 'Title::compare' );
                        foreach ( $templates as $titleObj ) {
-                               $r = $titleObj->getRestrictions( 'edit' );
-                               if ( in_array( 'sysop', $r ) ) {
-                                       $protected = wfMessage( 'template-protected' )->parse();
-                               } elseif ( in_array( 'autoconfirmed', $r ) ) {
-                                       $protected = wfMessage( 'template-semiprotected' )->parse();
-                               } else {
-                                       $protected = '';
+                               $protected = '';
+                               $restrictions = $titleObj->getRestrictions( 'edit' );
+                               if ( $restrictions ) {
+                                       // Check backwards-compatible messages
+                                       $msg = null;
+                                       if ( $restrictions === array( 'sysop' ) ) {
+                                               $msg = wfMessage( 'template-protected' );
+                                       } elseif ( $restrictions === array( 'autoconfirmed' ) ) {
+                                               $msg = wfMessage( 'template-semiprotected' );
+                                       }
+                                       if ( $msg && !$msg->isDisabled() ) {
+                                               $protected = $msg->parse();
+                                       } else {
+                                               // Construct the message from restriction-level-*
+                                               // e.g. restriction-level-sysop, restriction-level-autoconfirmed
+                                               $msgs = array();
+                                               foreach ( $restrictions as $r ) {
+                                                       $msgs[] = wfMessage( "restriction-level-$r" )->parse();
+                                               }
+                                               $protected = wfMessage( 'parentheses' )
+                                                       ->rawParams( $wgLang->commaList( $msgs ) )->escaped();
+                                       }
                                }
                                if ( $titleObj->quickUserCan( 'edit' ) ) {
                                        $editLink = self::link(
@@ -2100,9 +2116,10 @@ class Linker {
                        $accesskey = self::accesskey( $name );
                        if ( $accesskey !== false ) {
                                if ( $tooltip === false || $tooltip === '' ) {
-                                       $tooltip = "[$accesskey]";
+                                       $tooltip = wfMessage( 'brackets', $accesskey )->escaped();
                                } else {
-                                       $tooltip .= " [$accesskey]";
+                                       $tooltip .= wfMessage( 'word-separator' )->escaped();
+                                       $tooltip .= wfMessage( 'brackets', $accesskey )->escaped();
                                }
                        }
                }
diff --git a/includes/LinksUpdate.php b/includes/LinksUpdate.php
deleted file mode 100644 (file)
index fdd0e3c..0000000
+++ /dev/null
@@ -1,892 +0,0 @@
-<?php
-/**
- * Updater for link tracking tables after a page edit.
- *
- * 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
- */
-
-/**
- * See docs/deferred.txt
- *
- * @todo document (e.g. one-sentence top-level class description).
- */
-class LinksUpdate extends SqlDataUpdate {
-
-       // @todo make members protected, but make sure extensions don't break
-
-       public $mId,         //!< Page ID of the article linked from
-               $mTitle,         //!< Title object of the article linked from
-               $mParserOutput,  //!< Parser output
-               $mLinks,         //!< Map of title strings to IDs for the links in the document
-               $mImages,        //!< DB keys of the images used, in the array key only
-               $mTemplates,     //!< Map of title strings to IDs for the template references, including broken ones
-               $mExternals,     //!< URLs of external links, array key only
-               $mCategories,    //!< Map of category names to sort keys
-               $mInterlangs,    //!< Map of language codes to titles
-               $mProperties,    //!< Map of arbitrary name to value
-               $mDb,            //!< Database connection reference
-               $mOptions,       //!< SELECT options to be used (array)
-               $mRecursive;     //!< Whether to queue jobs for recursive updates
-
-       /**
-        * @var null|array Added links if calculated.
-        */
-       private $linkInsertions = null;
-
-       /**
-        * @var null|array Deleted links if calculated.
-        */
-       private $linkDeletions = null;
-
-       /**
-        * Constructor
-        *
-        * @param $title Title of the page we're updating
-        * @param $parserOutput ParserOutput: output from a full parse of this page
-        * @param $recursive Boolean: queue jobs for recursive updates?
-        * @throws MWException
-        */
-       function __construct( $title, $parserOutput, $recursive = true ) {
-               parent::__construct( false ); // no implicit transaction
-
-               if ( !( $title instanceof Title ) ) {
-                       throw new MWException( "The calling convention to LinksUpdate::LinksUpdate() has changed. " .
-                               "Please see Article::editUpdates() for an invocation example.\n" );
-               }
-
-               if ( !( $parserOutput instanceof ParserOutput ) ) {
-                       throw new MWException( "The calling convention to LinksUpdate::__construct() has changed. " .
-                               "Please see WikiPage::doEditUpdates() for an invocation example.\n" );
-               }
-
-               $this->mTitle = $title;
-               $this->mId = $title->getArticleID();
-
-               if ( !$this->mId ) {
-                       throw new MWException( "The Title object did not provide an article ID. Perhaps the page doesn't exist?" );
-               }
-
-               $this->mParserOutput = $parserOutput;
-
-               $this->mLinks = $parserOutput->getLinks();
-               $this->mImages = $parserOutput->getImages();
-               $this->mTemplates = $parserOutput->getTemplates();
-               $this->mExternals = $parserOutput->getExternalLinks();
-               $this->mCategories = $parserOutput->getCategories();
-               $this->mProperties = $parserOutput->getProperties();
-               $this->mInterwikis = $parserOutput->getInterwikiLinks();
-
-               # Convert the format of the interlanguage links
-               # I didn't want to change it in the ParserOutput, because that array is passed all
-               # the way back to the skin, so either a skin API break would be required, or an
-               # inefficient back-conversion.
-               $ill = $parserOutput->getLanguageLinks();
-               $this->mInterlangs = array();
-               foreach ( $ill as $link ) {
-                       list( $key, $title ) = explode( ':', $link, 2 );
-                       $this->mInterlangs[$key] = $title;
-               }
-
-               foreach ( $this->mCategories as &$sortkey ) {
-                       # If the sortkey is longer then 255 bytes,
-                       # it truncated by DB, and then doesn't get
-                       # matched when comparing existing vs current
-                       # categories, causing bug 25254.
-                       # Also. substr behaves weird when given "".
-                       if ( $sortkey !== '' ) {
-                               $sortkey = substr( $sortkey, 0, 255 );
-                       }
-               }
-
-               $this->mRecursive = $recursive;
-
-               wfRunHooks( 'LinksUpdateConstructed', array( &$this ) );
-       }
-
-       /**
-        * Update link tables with outgoing links from an updated article
-        */
-       public function doUpdate() {
-               wfRunHooks( 'LinksUpdate', array( &$this ) );
-               $this->doIncrementalUpdate();
-               wfRunHooks( 'LinksUpdateComplete', array( &$this ) );
-       }
-
-       protected function doIncrementalUpdate() {
-               wfProfileIn( __METHOD__ );
-
-               # Page links
-               $existing = $this->getExistingLinks();
-               $this->linkDeletions = $this->getLinkDeletions( $existing );
-               $this->linkInsertions = $this->getLinkInsertions( $existing );
-               $this->incrTableUpdate( 'pagelinks', 'pl', $this->linkDeletions, $this->linkInsertions );
-
-               # Image links
-               $existing = $this->getExistingImages();
-
-               $imageDeletes = $this->getImageDeletions( $existing );
-               $this->incrTableUpdate( 'imagelinks', 'il', $imageDeletes,
-                       $this->getImageInsertions( $existing ) );
-
-               # Invalidate all image description pages which had links added or removed
-               $imageUpdates = $imageDeletes + array_diff_key( $this->mImages, $existing );
-               $this->invalidateImageDescriptions( $imageUpdates );
-
-               # External links
-               $existing = $this->getExistingExternals();
-               $this->incrTableUpdate( 'externallinks', 'el', $this->getExternalDeletions( $existing ),
-                       $this->getExternalInsertions( $existing ) );
-
-               # Language links
-               $existing = $this->getExistingInterlangs();
-               $this->incrTableUpdate( 'langlinks', 'll', $this->getInterlangDeletions( $existing ),
-                       $this->getInterlangInsertions( $existing ) );
-
-               # Inline interwiki links
-               $existing = $this->getExistingInterwikis();
-               $this->incrTableUpdate( 'iwlinks', 'iwl', $this->getInterwikiDeletions( $existing ),
-                       $this->getInterwikiInsertions( $existing ) );
-
-               # Template links
-               $existing = $this->getExistingTemplates();
-               $this->incrTableUpdate( 'templatelinks', 'tl', $this->getTemplateDeletions( $existing ),
-                       $this->getTemplateInsertions( $existing ) );
-
-               # Category links
-               $existing = $this->getExistingCategories();
-
-               $categoryDeletes = $this->getCategoryDeletions( $existing );
-
-               $this->incrTableUpdate( 'categorylinks', 'cl', $categoryDeletes,
-                       $this->getCategoryInsertions( $existing ) );
-
-               # Invalidate all categories which were added, deleted or changed (set symmetric difference)
-               $categoryInserts = array_diff_assoc( $this->mCategories, $existing );
-               $categoryUpdates = $categoryInserts + $categoryDeletes;
-               $this->invalidateCategories( $categoryUpdates );
-               $this->updateCategoryCounts( $categoryInserts, $categoryDeletes );
-
-               # Page properties
-               $existing = $this->getExistingProperties();
-
-               $propertiesDeletes = $this->getPropertyDeletions( $existing );
-
-               $this->incrTableUpdate( 'page_props', 'pp', $propertiesDeletes,
-                       $this->getPropertyInsertions( $existing ) );
-
-               # Invalidate the necessary pages
-               $changed = $propertiesDeletes + array_diff_assoc( $this->mProperties, $existing );
-               $this->invalidateProperties( $changed );
-
-               # Refresh links of all pages including this page
-               # This will be in a separate transaction
-               if ( $this->mRecursive ) {
-                       $this->queueRecursiveJobs();
-               }
-
-               wfProfileOut( __METHOD__ );
-       }
-
-       /**
-        * Queue recursive jobs for this page
-        *
-        * Which means do LinksUpdate on all templates
-        * that include the current page, using the job queue.
-        */
-       function queueRecursiveJobs() {
-               self::queueRecursiveJobsForTable( $this->mTitle, 'templatelinks' );
-       }
-
-       /**
-        * Queue a RefreshLinks job for any table.
-        *
-        * @param Title $title Title to do job for
-        * @param String $table Table to use (e.g. 'templatelinks')
-        */
-       public static function queueRecursiveJobsForTable( Title $title, $table ) {
-               wfProfileIn( __METHOD__ );
-               if ( $title->getBacklinkCache()->hasLinks( $table ) ) {
-                       $job = new RefreshLinksJob2(
-                               $title,
-                               array(
-                                       'table' => $table,
-                               ) + Job::newRootJobParams( // "overall" refresh links job info
-                                       "refreshlinks:{$table}:{$title->getPrefixedText()}"
-                               )
-                       );
-                       JobQueueGroup::singleton()->push( $job );
-                       JobQueueGroup::singleton()->deduplicateRootJob( $job );
-               }
-               wfProfileOut( __METHOD__ );
-       }
-
-       /**
-        * @param $cats
-        */
-       function invalidateCategories( $cats ) {
-               $this->invalidatePages( NS_CATEGORY, array_keys( $cats ) );
-       }
-
-       /**
-        * Update all the appropriate counts in the category table.
-        * @param array $added associative array of category name => sort key
-        * @param array $deleted associative array of category name => sort key
-        */
-       function updateCategoryCounts( $added, $deleted ) {
-               $a = WikiPage::factory( $this->mTitle );
-               $a->updateCategoryCounts(
-                       array_keys( $added ), array_keys( $deleted )
-               );
-       }
-
-       /**
-        * @param $images
-        */
-       function invalidateImageDescriptions( $images ) {
-               $this->invalidatePages( NS_FILE, array_keys( $images ) );
-       }
-
-       /**
-        * Update a table by doing a delete query then an insert query
-        * @param $table
-        * @param $prefix
-        * @param $deletions
-        * @param $insertions
-        */
-       function incrTableUpdate( $table, $prefix, $deletions, $insertions ) {
-               if ( $table == 'page_props' ) {
-                       $fromField = 'pp_page';
-               } else {
-                       $fromField = "{$prefix}_from";
-               }
-               $where = array( $fromField => $this->mId );
-               if ( $table == 'pagelinks' || $table == 'templatelinks' || $table == 'iwlinks' ) {
-                       if ( $table == 'iwlinks' ) {
-                               $baseKey = 'iwl_prefix';
-                       } else {
-                               $baseKey = "{$prefix}_namespace";
-                       }
-                       $clause = $this->mDb->makeWhereFrom2d( $deletions, $baseKey, "{$prefix}_title" );
-                       if ( $clause ) {
-                               $where[] = $clause;
-                       } else {
-                               $where = false;
-                       }
-               } else {
-                       if ( $table == 'langlinks' ) {
-                               $toField = 'll_lang';
-                       } elseif ( $table == 'page_props' ) {
-                               $toField = 'pp_propname';
-                       } else {
-                               $toField = $prefix . '_to';
-                       }
-                       if ( count( $deletions ) ) {
-                               $where[] = "$toField IN (" . $this->mDb->makeList( array_keys( $deletions ) ) . ')';
-                       } else {
-                               $where = false;
-                       }
-               }
-               if ( $where ) {
-                       $this->mDb->delete( $table, $where, __METHOD__ );
-               }
-               if ( count( $insertions ) ) {
-                       $this->mDb->insert( $table, $insertions, __METHOD__, 'IGNORE' );
-                       wfRunHooks( 'LinksUpdateAfterInsert', array( $this, $table, $insertions ) );
-               }
-       }
-
-       /**
-        * Get an array of pagelinks insertions for passing to the DB
-        * Skips the titles specified by the 2-D array $existing
-        * @param $existing array
-        * @return array
-        */
-       private function getLinkInsertions( $existing = array() ) {
-               $arr = array();
-               foreach ( $this->mLinks as $ns => $dbkeys ) {
-                       $diffs = isset( $existing[$ns] )
-                               ? array_diff_key( $dbkeys, $existing[$ns] )
-                               : $dbkeys;
-                       foreach ( $diffs as $dbk => $id ) {
-                               $arr[] = array(
-                                       'pl_from' => $this->mId,
-                                       'pl_namespace' => $ns,
-                                       'pl_title' => $dbk
-                               );
-                       }
-               }
-               return $arr;
-       }
-
-       /**
-        * Get an array of template insertions. Like getLinkInsertions()
-        * @param $existing array
-        * @return array
-        */
-       private function getTemplateInsertions( $existing = array() ) {
-               $arr = array();
-               foreach ( $this->mTemplates as $ns => $dbkeys ) {
-                       $diffs = isset( $existing[$ns] ) ? array_diff_key( $dbkeys, $existing[$ns] ) : $dbkeys;
-                       foreach ( $diffs as $dbk => $id ) {
-                               $arr[] = array(
-                                       'tl_from' => $this->mId,
-                                       'tl_namespace' => $ns,
-                                       'tl_title' => $dbk
-                               );
-                       }
-               }
-               return $arr;
-       }
-
-       /**
-        * Get an array of image insertions
-        * Skips the names specified in $existing
-        * @param $existing array
-        * @return array
-        */
-       private function getImageInsertions( $existing = array() ) {
-               $arr = array();
-               $diffs = array_diff_key( $this->mImages, $existing );
-               foreach ( $diffs as $iname => $dummy ) {
-                       $arr[] = array(
-                               'il_from' => $this->mId,
-                               'il_to' => $iname
-                       );
-               }
-               return $arr;
-       }
-
-       /**
-        * Get an array of externallinks insertions. Skips the names specified in $existing
-        * @param $existing array
-        * @return array
-        */
-       private function getExternalInsertions( $existing = array() ) {
-               $arr = array();
-               $diffs = array_diff_key( $this->mExternals, $existing );
-               foreach ( $diffs as $url => $dummy ) {
-                       foreach ( wfMakeUrlIndexes( $url ) as $index ) {
-                               $arr[] = array(
-                                       'el_from' => $this->mId,
-                                       'el_to' => $url,
-                                       'el_index' => $index,
-                               );
-                       }
-               }
-               return $arr;
-       }
-
-       /**
-        * Get an array of category insertions
-        *
-        * @param array $existing mapping existing category names to sort keys. If both
-        * match a link in $this, the link will be omitted from the output
-        *
-        * @return array
-        */
-       private function getCategoryInsertions( $existing = array() ) {
-               global $wgContLang, $wgCategoryCollation;
-               $diffs = array_diff_assoc( $this->mCategories, $existing );
-               $arr = array();
-               foreach ( $diffs as $name => $prefix ) {
-                       $nt = Title::makeTitleSafe( NS_CATEGORY, $name );
-                       $wgContLang->findVariantLink( $name, $nt, true );
-
-                       if ( $this->mTitle->getNamespace() == NS_CATEGORY ) {
-                               $type = 'subcat';
-                       } elseif ( $this->mTitle->getNamespace() == NS_FILE ) {
-                               $type = 'file';
-                       } else {
-                               $type = 'page';
-                       }
-
-                       # Treat custom sortkeys as a prefix, so that if multiple
-                       # things are forced to sort as '*' or something, they'll
-                       # sort properly in the category rather than in page_id
-                       # order or such.
-                       $sortkey = Collation::singleton()->getSortKey(
-                               $this->mTitle->getCategorySortkey( $prefix ) );
-
-                       $arr[] = array(
-                               'cl_from' => $this->mId,
-                               'cl_to' => $name,
-                               'cl_sortkey' => $sortkey,
-                               'cl_timestamp' => $this->mDb->timestamp(),
-                               'cl_sortkey_prefix' => $prefix,
-                               'cl_collation' => $wgCategoryCollation,
-                               'cl_type' => $type,
-                       );
-               }
-               return $arr;
-       }
-
-       /**
-        * Get an array of interlanguage link insertions
-        *
-        * @param array $existing mapping existing language codes to titles
-        *
-        * @return array
-        */
-       private function getInterlangInsertions( $existing = array() ) {
-               $diffs = array_diff_assoc( $this->mInterlangs, $existing );
-               $arr = array();
-               foreach ( $diffs as $lang => $title ) {
-                       $arr[] = array(
-                               'll_from' => $this->mId,
-                               'll_lang' => $lang,
-                               'll_title' => $title
-                       );
-               }
-               return $arr;
-       }
-
-       /**
-        * Get an array of page property insertions
-        * @param $existing array
-        * @return array
-        */
-       function getPropertyInsertions( $existing = array() ) {
-               $diffs = array_diff_assoc( $this->mProperties, $existing );
-               $arr = array();
-               foreach ( $diffs as $name => $value ) {
-                       $arr[] = array(
-                               'pp_page' => $this->mId,
-                               'pp_propname' => $name,
-                               'pp_value' => $value,
-                       );
-               }
-               return $arr;
-       }
-
-       /**
-        * Get an array of interwiki insertions for passing to the DB
-        * Skips the titles specified by the 2-D array $existing
-        * @param $existing array
-        * @return array
-        */
-       private function getInterwikiInsertions( $existing = array() ) {
-               $arr = array();
-               foreach ( $this->mInterwikis as $prefix => $dbkeys ) {
-                       $diffs = isset( $existing[$prefix] ) ? array_diff_key( $dbkeys, $existing[$prefix] ) : $dbkeys;
-                       foreach ( $diffs as $dbk => $id ) {
-                               $arr[] = array(
-                                       'iwl_from' => $this->mId,
-                                       'iwl_prefix' => $prefix,
-                                       'iwl_title' => $dbk
-                               );
-                       }
-               }
-               return $arr;
-       }
-
-       /**
-        * Given an array of existing links, returns those links which are not in $this
-        * and thus should be deleted.
-        * @param $existing array
-        * @return array
-        */
-       private function getLinkDeletions( $existing ) {
-               $del = array();
-               foreach ( $existing as $ns => $dbkeys ) {
-                       if ( isset( $this->mLinks[$ns] ) ) {
-                               $del[$ns] = array_diff_key( $existing[$ns], $this->mLinks[$ns] );
-                       } else {
-                               $del[$ns] = $existing[$ns];
-                       }
-               }
-               return $del;
-       }
-
-       /**
-        * Given an array of existing templates, returns those templates which are not in $this
-        * and thus should be deleted.
-        * @param $existing array
-        * @return array
-        */
-       private function getTemplateDeletions( $existing ) {
-               $del = array();
-               foreach ( $existing as $ns => $dbkeys ) {
-                       if ( isset( $this->mTemplates[$ns] ) ) {
-                               $del[$ns] = array_diff_key( $existing[$ns], $this->mTemplates[$ns] );
-                       } else {
-                               $del[$ns] = $existing[$ns];
-                       }
-               }
-               return $del;
-       }
-
-       /**
-        * Given an array of existing images, returns those images which are not in $this
-        * and thus should be deleted.
-        * @param $existing array
-        * @return array
-        */
-       private function getImageDeletions( $existing ) {
-               return array_diff_key( $existing, $this->mImages );
-       }
-
-       /**
-        * Given an array of existing external links, returns those links which are not
-        * in $this and thus should be deleted.
-        * @param $existing array
-        * @return array
-        */
-       private function getExternalDeletions( $existing ) {
-               return array_diff_key( $existing, $this->mExternals );
-       }
-
-       /**
-        * Given an array of existing categories, returns those categories which are not in $this
-        * and thus should be deleted.
-        * @param $existing array
-        * @return array
-        */
-       private function getCategoryDeletions( $existing ) {
-               return array_diff_assoc( $existing, $this->mCategories );
-       }
-
-       /**
-        * Given an array of existing interlanguage links, returns those links which are not
-        * in $this and thus should be deleted.
-        * @param $existing array
-        * @return array
-        */
-       private function getInterlangDeletions( $existing ) {
-               return array_diff_assoc( $existing, $this->mInterlangs );
-       }
-
-       /**
-        * Get array of properties which should be deleted.
-        * @param $existing array
-        * @return array
-        */
-       function getPropertyDeletions( $existing ) {
-               return array_diff_assoc( $existing, $this->mProperties );
-       }
-
-       /**
-        * Given an array of existing interwiki links, returns those links which are not in $this
-        * and thus should be deleted.
-        * @param $existing array
-        * @return array
-        */
-       private function getInterwikiDeletions( $existing ) {
-               $del = array();
-               foreach ( $existing as $prefix => $dbkeys ) {
-                       if ( isset( $this->mInterwikis[$prefix] ) ) {
-                               $del[$prefix] = array_diff_key( $existing[$prefix], $this->mInterwikis[$prefix] );
-                       } else {
-                               $del[$prefix] = $existing[$prefix];
-                       }
-               }
-               return $del;
-       }
-
-       /**
-        * Get an array of existing links, as a 2-D array
-        *
-        * @return array
-        */
-       private function getExistingLinks() {
-               $res = $this->mDb->select( 'pagelinks', array( 'pl_namespace', 'pl_title' ),
-                       array( 'pl_from' => $this->mId ), __METHOD__, $this->mOptions );
-               $arr = array();
-               foreach ( $res as $row ) {
-                       if ( !isset( $arr[$row->pl_namespace] ) ) {
-                               $arr[$row->pl_namespace] = array();
-                       }
-                       $arr[$row->pl_namespace][$row->pl_title] = 1;
-               }
-               return $arr;
-       }
-
-       /**
-        * Get an array of existing templates, as a 2-D array
-        *
-        * @return array
-        */
-       private function getExistingTemplates() {
-               $res = $this->mDb->select( 'templatelinks', array( 'tl_namespace', 'tl_title' ),
-                       array( 'tl_from' => $this->mId ), __METHOD__, $this->mOptions );
-               $arr = array();
-               foreach ( $res as $row ) {
-                       if ( !isset( $arr[$row->tl_namespace] ) ) {
-                               $arr[$row->tl_namespace] = array();
-                       }
-                       $arr[$row->tl_namespace][$row->tl_title] = 1;
-               }
-               return $arr;
-       }
-
-       /**
-        * Get an array of existing images, image names in the keys
-        *
-        * @return array
-        */
-       private function getExistingImages() {
-               $res = $this->mDb->select( 'imagelinks', array( 'il_to' ),
-                       array( 'il_from' => $this->mId ), __METHOD__, $this->mOptions );
-               $arr = array();
-               foreach ( $res as $row ) {
-                       $arr[$row->il_to] = 1;
-               }
-               return $arr;
-       }
-
-       /**
-        * Get an array of existing external links, URLs in the keys
-        *
-        * @return array
-        */
-       private function getExistingExternals() {
-               $res = $this->mDb->select( 'externallinks', array( 'el_to' ),
-                       array( 'el_from' => $this->mId ), __METHOD__, $this->mOptions );
-               $arr = array();
-               foreach ( $res as $row ) {
-                       $arr[$row->el_to] = 1;
-               }
-               return $arr;
-       }
-
-       /**
-        * Get an array of existing categories, with the name in the key and sort key in the value.
-        *
-        * @return array
-        */
-       private function getExistingCategories() {
-               $res = $this->mDb->select( 'categorylinks', array( 'cl_to', 'cl_sortkey_prefix' ),
-                       array( 'cl_from' => $this->mId ), __METHOD__, $this->mOptions );
-               $arr = array();
-               foreach ( $res as $row ) {
-                       $arr[$row->cl_to] = $row->cl_sortkey_prefix;
-               }
-               return $arr;
-       }
-
-       /**
-        * Get an array of existing interlanguage links, with the language code in the key and the
-        * title in the value.
-        *
-        * @return array
-        */
-       private function getExistingInterlangs() {
-               $res = $this->mDb->select( 'langlinks', array( 'll_lang', 'll_title' ),
-                       array( 'll_from' => $this->mId ), __METHOD__, $this->mOptions );
-               $arr = array();
-               foreach ( $res as $row ) {
-                       $arr[$row->ll_lang] = $row->ll_title;
-               }
-               return $arr;
-       }
-
-       /**
-        * Get an array of existing inline interwiki links, as a 2-D array
-        * @return array (prefix => array(dbkey => 1))
-        */
-       protected function getExistingInterwikis() {
-               $res = $this->mDb->select( 'iwlinks', array( 'iwl_prefix', 'iwl_title' ),
-                       array( 'iwl_from' => $this->mId ), __METHOD__, $this->mOptions );
-               $arr = array();
-               foreach ( $res as $row ) {
-                       if ( !isset( $arr[$row->iwl_prefix] ) ) {
-                               $arr[$row->iwl_prefix] = array();
-                       }
-                       $arr[$row->iwl_prefix][$row->iwl_title] = 1;
-               }
-               return $arr;
-       }
-
-       /**
-        * Get an array of existing categories, with the name in the key and sort key in the value.
-        *
-        * @return array
-        */
-       private function getExistingProperties() {
-               $res = $this->mDb->select( 'page_props', array( 'pp_propname', 'pp_value' ),
-                       array( 'pp_page' => $this->mId ), __METHOD__, $this->mOptions );
-               $arr = array();
-               foreach ( $res as $row ) {
-                       $arr[$row->pp_propname] = $row->pp_value;
-               }
-               return $arr;
-       }
-
-       /**
-        * Return the title object of the page being updated
-        * @return Title
-        */
-       public function getTitle() {
-               return $this->mTitle;
-       }
-
-       /**
-        * Returns parser output
-        * @since 1.19
-        * @return ParserOutput
-        */
-       public function getParserOutput() {
-               return $this->mParserOutput;
-       }
-
-       /**
-        * Return the list of images used as generated by the parser
-        * @return array
-        */
-       public function getImages() {
-               return $this->mImages;
-       }
-
-       /**
-        * Invalidate any necessary link lists related to page property changes
-        * @param $changed
-        */
-       private function invalidateProperties( $changed ) {
-               global $wgPagePropLinkInvalidations;
-
-               foreach ( $changed as $name => $value ) {
-                       if ( isset( $wgPagePropLinkInvalidations[$name] ) ) {
-                               $inv = $wgPagePropLinkInvalidations[$name];
-                               if ( !is_array( $inv ) ) {
-                                       $inv = array( $inv );
-                               }
-                               foreach ( $inv as $table ) {
-                                       $update = new HTMLCacheUpdate( $this->mTitle, $table );
-                                       $update->doUpdate();
-                               }
-                       }
-               }
-       }
-
-       /**
-        * Fetch page links added by this LinksUpdate.  Only available after the update is complete.
-        * @since 1.22
-        * @return null|array of Titles
-        */
-       public function getAddedLinks() {
-               if ( $this->linkInsertions === null ) {
-                       return null;
-               }
-               $result = array();
-               foreach ( $this->linkInsertions as $insertion ) {
-                       $result[] = Title::makeTitle( $insertion[ 'pl_namespace' ], $insertion[ 'pl_title' ] );
-               }
-               return $result;
-       }
-
-       /**
-        * Fetch page links removed by this LinksUpdate.  Only available after the update is complete.
-        * @since 1.22
-        * @return null|array of Titles
-        */
-       public function getRemovedLinks() {
-               if ( $this->linkDeletions === null ) {
-                       return null;
-               }
-               $result = array();
-               foreach ( $this->linkDeletions as $ns => $titles ) {
-                       foreach ( $titles as $title => $unused ) {
-                               $result[] = Title::makeTitle( $ns, $title );
-                       }
-               }
-               return $result;
-       }
-}
-
-/**
- * Update object handling the cleanup of links tables after a page was deleted.
- **/
-class LinksDeletionUpdate extends SqlDataUpdate {
-
-       protected $mPage;     //!< WikiPage the wikipage that was deleted
-
-       /**
-        * Constructor
-        *
-        * @param $page WikiPage Page we are updating
-        * @throws MWException
-        */
-       function __construct( WikiPage $page ) {
-               parent::__construct( false ); // no implicit transaction
-
-               $this->mPage = $page;
-
-               if ( !$page->exists() ) {
-                       throw new MWException( "Page ID not known, perhaps the page doesn't exist?" );
-               }
-       }
-
-       /**
-        * Do some database updates after deletion
-        */
-       public function doUpdate() {
-               $title = $this->mPage->getTitle();
-               $id = $this->mPage->getId();
-
-               # Delete restrictions for it
-               $this->mDb->delete( 'page_restrictions', array( 'pr_page' => $id ), __METHOD__ );
-
-               # Fix category table counts
-               $cats = array();
-               $res = $this->mDb->select( 'categorylinks', 'cl_to', array( 'cl_from' => $id ), __METHOD__ );
-
-               foreach ( $res as $row ) {
-                       $cats[] = $row->cl_to;
-               }
-
-               $this->mPage->updateCategoryCounts( array(), $cats );
-
-               # If using cascading deletes, we can skip some explicit deletes
-               if ( !$this->mDb->cascadingDeletes() ) {
-                       # Delete outgoing links
-                       $this->mDb->delete( 'pagelinks', array( 'pl_from' => $id ), __METHOD__ );
-                       $this->mDb->delete( 'imagelinks', array( 'il_from' => $id ), __METHOD__ );
-                       $this->mDb->delete( 'categorylinks', array( 'cl_from' => $id ), __METHOD__ );
-                       $this->mDb->delete( 'templatelinks', array( 'tl_from' => $id ), __METHOD__ );
-                       $this->mDb->delete( 'externallinks', array( 'el_from' => $id ), __METHOD__ );
-                       $this->mDb->delete( 'langlinks', array( 'll_from' => $id ), __METHOD__ );
-                       $this->mDb->delete( 'iwlinks', array( 'iwl_from' => $id ), __METHOD__ );
-                       $this->mDb->delete( 'redirect', array( 'rd_from' => $id ), __METHOD__ );
-                       $this->mDb->delete( 'page_props', array( 'pp_page' => $id ), __METHOD__ );
-               }
-
-               # If using cleanup triggers, we can skip some manual deletes
-               if ( !$this->mDb->cleanupTriggers() ) {
-                       # Clean up recentchanges entries...
-                       $this->mDb->delete( 'recentchanges',
-                               array( 'rc_type != ' . RC_LOG,
-                                       'rc_namespace' => $title->getNamespace(),
-                                       'rc_title' => $title->getDBkey() ),
-                               __METHOD__ );
-                       $this->mDb->delete( 'recentchanges',
-                               array( 'rc_type != ' . RC_LOG, 'rc_cur_id' => $id ),
-                               __METHOD__ );
-               }
-       }
-
-       /**
-        * Update all the appropriate counts in the category table.
-        * @param array $added associative array of category name => sort key
-        * @param array $deleted associative array of category name => sort key
-        */
-       function updateCategoryCounts( $added, $deleted ) {
-               $a = WikiPage::factory( $this->mTitle );
-               $a->updateCategoryCounts(
-                       array_keys( $added ), array_keys( $deleted )
-               );
-       }
-}
diff --git a/includes/MWCryptRand.php b/includes/MWCryptRand.php
deleted file mode 100644 (file)
index bac018e..0000000
+++ /dev/null
@@ -1,497 +0,0 @@
-<?php
-/**
- * A cryptographic random generator class used for generating secret keys
- *
- * This is based in part on Drupal code as well as what we used in our own code
- * prior to introduction of this class.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @author Daniel Friesen
- * @file
- */
-
-class MWCryptRand {
-
-       /**
-        * Minimum number of iterations we want to make in our drift calculations.
-        */
-       const MIN_ITERATIONS = 1000;
-
-       /**
-        * Number of milliseconds we want to spend generating each separate byte
-        * of the final generated bytes.
-        * This is used in combination with the hash length to determine the duration
-        * we should spend doing drift calculations.
-        */
-       const MSEC_PER_BYTE = 0.5;
-
-       /**
-        * Singleton instance for public use
-        */
-       protected static $singleton = null;
-
-       /**
-        * The hash algorithm being used
-        */
-       protected $algo = null;
-
-       /**
-        * The number of bytes outputted by the hash algorithm
-        */
-       protected $hashLength = null;
-
-       /**
-        * A boolean indicating whether the previous random generation was done using
-        * cryptographically strong random number generator or not.
-        */
-       protected $strong = null;
-
-       /**
-        * Initialize an initial random state based off of whatever we can find
-        */
-       protected function initialRandomState() {
-               // $_SERVER contains a variety of unstable user and system specific information
-               // It'll vary a little with each page, and vary even more with separate users
-               // It'll also vary slightly across different machines
-               $state = serialize( $_SERVER );
-
-               // To try vary the system information of the state a bit more
-               // by including the system's hostname into the state
-               $state .= wfHostname();
-
-               // Try to gather a little entropy from the different php rand sources
-               $state .= rand() . uniqid( mt_rand(), true );
-
-               // Include some information about the filesystem's current state in the random state
-               $files = array();
-
-               // We know this file is here so grab some info about ourselves
-               $files[] = __FILE__;
-
-               // We must also have a parent folder, and with the usual file structure, a grandparent
-               $files[] = __DIR__;
-               $files[] = dirname( __DIR__ );
-
-               // The config file is likely the most often edited file we know should be around
-               // so include its stat info into the state.
-               // The constant with its location will almost always be defined, as WebStart.php defines
-               // MW_CONFIG_FILE to $IP/LocalSettings.php unless being configured with MW_CONFIG_CALLBACK (eg. the installer)
-               if ( defined( 'MW_CONFIG_FILE' ) ) {
-                       $files[] = MW_CONFIG_FILE;
-               }
-
-               foreach ( $files as $file ) {
-                       wfSuppressWarnings();
-                       $stat = stat( $file );
-                       wfRestoreWarnings();
-                       if ( $stat ) {
-                               // stat() duplicates data into numeric and string keys so kill off all the numeric ones
-                               foreach ( $stat as $k => $v ) {
-                                       if ( is_numeric( $k ) ) {
-                                               unset( $k );
-                                       }
-                               }
-                               // The absolute filename itself will differ from install to install so don't leave it out
-                               if ( ( $path = realpath( $file ) ) !== false ) {
-                                       $state .= $path;
-                               } else {
-                                       $state .= $file;
-                               }
-                               $state .= implode( '', $stat );
-                       } else {
-                               // The fact that the file isn't there is worth at least a
-                               // minuscule amount of entropy.
-                               $state .= '0';
-                       }
-               }
-
-               // Try and make this a little more unstable by including the varying process
-               // id of the php process we are running inside of if we are able to access it
-               if ( function_exists( 'getmypid' ) ) {
-                       $state .= getmypid();
-               }
-
-               // If available try to increase the instability of the data by throwing in
-               // the precise amount of memory that we happen to be using at the moment.
-               if ( function_exists( 'memory_get_usage' ) ) {
-                       $state .= memory_get_usage( true );
-               }
-
-               // It's mostly worthless but throw the wiki's id into the data for a little more variance
-               $state .= wfWikiID();
-
-               // If we have a secret key or proxy key set then throw it into the state as well
-               global $wgSecretKey, $wgProxyKey;
-               if ( $wgSecretKey ) {
-                       $state .= $wgSecretKey;
-               } elseif ( $wgProxyKey ) {
-                       $state .= $wgProxyKey;
-               }
-
-               return $state;
-       }
-
-       /**
-        * Randomly hash data while mixing in clock drift data for randomness
-        *
-        * @param string $data The data to randomly hash.
-        * @return String The hashed bytes
-        * @author Tim Starling
-        */
-       protected function driftHash( $data ) {
-               // Minimum number of iterations (to avoid slow operations causing the loop to gather little entropy)
-               $minIterations = self::MIN_ITERATIONS;
-               // Duration of time to spend doing calculations (in seconds)
-               $duration = ( self::MSEC_PER_BYTE / 1000 ) * $this->hashLength();
-               // Create a buffer to use to trigger memory operations
-               $bufLength = 10000000;
-               $buffer = str_repeat( ' ', $bufLength );
-               $bufPos = 0;
-
-               // Iterate for $duration seconds or at least $minIterations number of iterations
-               $iterations = 0;
-               $startTime = microtime( true );
-               $currentTime = $startTime;
-               while ( $iterations < $minIterations || $currentTime - $startTime < $duration ) {
-                       // Trigger some memory writing to trigger some bus activity
-                       // This may create variance in the time between iterations
-                       $bufPos = ( $bufPos + 13 ) % $bufLength;
-                       $buffer[$bufPos] = ' ';
-                       // Add the drift between this iteration and the last in as entropy
-                       $nextTime = microtime( true );
-                       $delta = (int)( ( $nextTime - $currentTime ) * 1000000 );
-                       $data .= $delta;
-                       // Every 100 iterations hash the data and entropy
-                       if ( $iterations % 100 === 0 ) {
-                               $data = sha1( $data );
-                       }
-                       $currentTime = $nextTime;
-                       $iterations++;
-               }
-               $timeTaken = $currentTime - $startTime;
-               $data = $this->hash( $data );
-
-               wfDebug( __METHOD__ . ": Clock drift calculation " .
-                       "(time-taken=" . ( $timeTaken * 1000 ) . "ms, " .
-                       "iterations=$iterations, " .
-                       "time-per-iteration=" . ( $timeTaken / $iterations * 1e6 ) . "us)\n" );
-               return $data;
-       }
-
-       /**
-        * Return a rolling random state initially build using data from unstable sources
-        * @return string A new weak random state
-        */
-       protected function randomState() {
-               static $state = null;
-               if ( is_null( $state ) ) {
-                       // Initialize the state with whatever unstable data we can find
-                       // It's important that this data is hashed right afterwards to prevent
-                       // it from being leaked into the output stream
-                       $state = $this->hash( $this->initialRandomState() );
-               }
-               // Generate a new random state based on the initial random state or previous
-               // random state by combining it with clock drift
-               $state = $this->driftHash( $state );
-               return $state;
-       }
-
-       /**
-        * Decide on the best acceptable hash algorithm we have available for hash()
-        * @throws MWException
-        * @return String A hash algorithm
-        */
-       protected function hashAlgo() {
-               if ( !is_null( $this->algo ) ) {
-                       return $this->algo;
-               }
-
-               $algos = hash_algos();
-               $preference = array( 'whirlpool', 'sha256', 'sha1', 'md5' );
-
-               foreach ( $preference as $algorithm ) {
-                       if ( in_array( $algorithm, $algos ) ) {
-                               $this->algo = $algorithm;
-                               wfDebug( __METHOD__ . ": Using the {$this->algo} hash algorithm.\n" );
-                               return $this->algo;
-                       }
-               }
-
-               // We only reach here if no acceptable hash is found in the list, this should
-               // be a technical impossibility since most of php's hash list is fixed and
-               // some of the ones we list are available as their own native functions
-               // But since we already require at least 5.2 and hash() was default in
-               // 5.1.2 we don't bother falling back to methods like sha1 and md5.
-               throw new MWException( "Could not find an acceptable hashing function in hash_algos()" );
-       }
-
-       /**
-        * Return the byte-length output of the hash algorithm we are
-        * using in self::hash and self::hmac.
-        *
-        * @return int Number of bytes the hash outputs
-        */
-       protected function hashLength() {
-               if ( is_null( $this->hashLength ) ) {
-                       $this->hashLength = strlen( $this->hash( '' ) );
-               }
-               return $this->hashLength;
-       }
-
-       /**
-        * Generate an acceptably unstable one-way-hash of some text
-        * making use of the best hash algorithm that we have available.
-        *
-        * @param $data string
-        * @return String A raw hash of the data
-        */
-       protected function hash( $data ) {
-               return hash( $this->hashAlgo(), $data, true );
-       }
-
-       /**
-        * Generate an acceptably unstable one-way-hmac of some text
-        * making use of the best hash algorithm that we have available.
-        *
-        * @param $data string
-        * @param $key string
-        * @return String A raw hash of the data
-        */
-       protected function hmac( $data, $key ) {
-               return hash_hmac( $this->hashAlgo(), $data, $key, true );
-       }
-
-       /**
-        * @see self::wasStrong()
-        */
-       public function realWasStrong() {
-               if ( is_null( $this->strong ) ) {
-                       throw new MWException( __METHOD__ . ' called before generation of random data' );
-               }
-               return $this->strong;
-       }
-
-       /**
-        * @see self::generate()
-        */
-       public function realGenerate( $bytes, $forceStrong = false ) {
-               wfProfileIn( __METHOD__ );
-
-               wfDebug( __METHOD__ . ": Generating cryptographic random bytes for " . wfGetAllCallers( 5 ) . "\n" );
-
-               $bytes = floor( $bytes );
-               static $buffer = '';
-               if ( is_null( $this->strong ) ) {
-                       // Set strength to false initially until we know what source data is coming from
-                       $this->strong = true;
-               }
-
-               if ( strlen( $buffer ) < $bytes ) {
-                       // If available make use of mcrypt_create_iv URANDOM source to generate randomness
-                       // On unix-like systems this reads from /dev/urandom but does it without any buffering
-                       // and bypasses openbasedir restrictions, so it's preferable to reading directly
-                       // On Windows starting in PHP 5.3.0 Windows' native CryptGenRandom is used to generate
-                       // entropy so this is also preferable to just trying to read urandom because it may work
-                       // on Windows systems as well.
-                       if ( function_exists( 'mcrypt_create_iv' ) ) {
-                               wfProfileIn( __METHOD__ . '-mcrypt' );
-                               $rem = $bytes - strlen( $buffer );
-                               $iv = mcrypt_create_iv( $rem, MCRYPT_DEV_URANDOM );
-                               if ( $iv === false ) {
-                                       wfDebug( __METHOD__ . ": mcrypt_create_iv returned false.\n" );
-                               } else {
-                                       $buffer .= $iv;
-                                       wfDebug( __METHOD__ . ": mcrypt_create_iv generated " . strlen( $iv ) . " bytes of randomness.\n" );
-                               }
-                               wfProfileOut( __METHOD__ . '-mcrypt' );
-                       }
-               }
-
-               if ( strlen( $buffer ) < $bytes ) {
-                       // If available make use of openssl's random_pseudo_bytes method to attempt to generate randomness.
-                       // However don't do this on Windows with PHP < 5.3.4 due to a bug:
-                       // http://stackoverflow.com/questions/1940168/openssl-random-pseudo-bytes-is-slow-php
-                       // http://git.php.net/?p=php-src.git;a=commitdiff;h=cd62a70863c261b07f6dadedad9464f7e213cad5
-                       if ( function_exists( 'openssl_random_pseudo_bytes' )
-                               && ( !wfIsWindows() || version_compare( PHP_VERSION, '5.3.4', '>=' ) )
-                       ) {
-                               wfProfileIn( __METHOD__ . '-openssl' );
-                               $rem = $bytes - strlen( $buffer );
-                               $openssl_bytes = openssl_random_pseudo_bytes( $rem, $openssl_strong );
-                               if ( $openssl_bytes === false ) {
-                                       wfDebug( __METHOD__ . ": openssl_random_pseudo_bytes returned false.\n" );
-                               } else {
-                                       $buffer .= $openssl_bytes;
-                                       wfDebug( __METHOD__ . ": openssl_random_pseudo_bytes generated " . strlen( $openssl_bytes ) . " bytes of " . ( $openssl_strong ? "strong" : "weak" ) . " randomness.\n" );
-                               }
-                               if ( strlen( $buffer ) >= $bytes ) {
-                                       // openssl tells us if the random source was strong, if some of our data was generated
-                                       // using it use it's say on whether the randomness is strong
-                                       $this->strong = !!$openssl_strong;
-                               }
-                               wfProfileOut( __METHOD__ . '-openssl' );
-                       }
-               }
-
-               // Only read from urandom if we can control the buffer size or were passed forceStrong
-               if ( strlen( $buffer ) < $bytes && ( function_exists( 'stream_set_read_buffer' ) || $forceStrong ) ) {
-                       wfProfileIn( __METHOD__ . '-fopen-urandom' );
-                       $rem = $bytes - strlen( $buffer );
-                       if ( !function_exists( 'stream_set_read_buffer' ) && $forceStrong ) {
-                               wfDebug( __METHOD__ . ": Was forced to read from /dev/urandom without control over the buffer size.\n" );
-                       }
-                       // /dev/urandom is generally considered the best possible commonly
-                       // available random source, and is available on most *nix systems.
-                       wfSuppressWarnings();
-                       $urandom = fopen( "/dev/urandom", "rb" );
-                       wfRestoreWarnings();
-
-                       // Attempt to read all our random data from urandom
-                       // php's fread always does buffered reads based on the stream's chunk_size
-                       // so in reality it will usually read more than the amount of data we're
-                       // asked for and not storing that risks depleting the system's random pool.
-                       // If stream_set_read_buffer is available set the chunk_size to the amount
-                       // of data we need. Otherwise read 8k, php's default chunk_size.
-                       if ( $urandom ) {
-                               // php's default chunk_size is 8k
-                               $chunk_size = 1024 * 8;
-                               if ( function_exists( 'stream_set_read_buffer' ) ) {
-                                       // If possible set the chunk_size to the amount of data we need
-                                       stream_set_read_buffer( $urandom, $rem );
-                                       $chunk_size = $rem;
-                               }
-                               $random_bytes = fread( $urandom, max( $chunk_size, $rem ) );
-                               $buffer .= $random_bytes;
-                               fclose( $urandom );
-                               wfDebug( __METHOD__ . ": /dev/urandom generated " . strlen( $random_bytes ) . " bytes of randomness.\n" );
-                               if ( strlen( $buffer ) >= $bytes ) {
-                                       // urandom is always strong, set to true if all our data was generated using it
-                                       $this->strong = true;
-                               }
-                       } else {
-                               wfDebug( __METHOD__ . ": /dev/urandom could not be opened.\n" );
-                       }
-                       wfProfileOut( __METHOD__ . '-fopen-urandom' );
-               }
-
-               // If we cannot use or generate enough data from a secure source
-               // use this loop to generate a good set of pseudo random data.
-               // This works by initializing a random state using a pile of unstable data
-               // and continually shoving it through a hash along with a variable salt.
-               // We hash the random state with more salt to avoid the state from leaking
-               // out and being used to predict the /randomness/ that follows.
-               if ( strlen( $buffer ) < $bytes ) {
-                       wfDebug( __METHOD__ . ": Falling back to using a pseudo random state to generate randomness.\n" );
-               }
-               while ( strlen( $buffer ) < $bytes ) {
-                       wfProfileIn( __METHOD__ . '-fallback' );
-                       $buffer .= $this->hmac( $this->randomState(), mt_rand() );
-                       // This code is never really cryptographically strong, if we use it
-                       // at all, then set strong to false.
-                       $this->strong = false;
-                       wfProfileOut( __METHOD__ . '-fallback' );
-               }
-
-               // Once the buffer has been filled up with enough random data to fulfill
-               // the request shift off enough data to handle the request and leave the
-               // unused portion left inside the buffer for the next request for random data
-               $generated = substr( $buffer, 0, $bytes );
-               $buffer = substr( $buffer, $bytes );
-
-               wfDebug( __METHOD__ . ": " . strlen( $buffer ) . " bytes of randomness leftover in the buffer.\n" );
-
-               wfProfileOut( __METHOD__ );
-               return $generated;
-       }
-
-       /**
-        * @see self::generateHex()
-        */
-       public function realGenerateHex( $chars, $forceStrong = false ) {
-               // hex strings are 2x the length of raw binary so we divide the length in half
-               // odd numbers will result in a .5 that leads the generate() being 1 character
-               // short, so we use ceil() to ensure that we always have enough bytes
-               $bytes = ceil( $chars / 2 );
-               // Generate the data and then convert it to a hex string
-               $hex = bin2hex( $this->generate( $bytes, $forceStrong ) );
-               // A bit of paranoia here, the caller asked for a specific length of string
-               // here, and it's possible (eg when given an odd number) that we may actually
-               // have at least 1 char more than they asked for. Just in case they made this
-               // call intending to insert it into a database that does truncation we don't
-               // want to give them too much and end up with their database and their live
-               // code having two different values because part of what we gave them is truncated
-               // hence, we strip out any run of characters longer than what we were asked for.
-               return substr( $hex, 0, $chars );
-       }
-
-       /** Publicly exposed static methods **/
-
-       /**
-        * Return a singleton instance of MWCryptRand
-        * @return MWCryptRand
-        */
-       protected static function singleton() {
-               if ( is_null( self::$singleton ) ) {
-                       self::$singleton = new self;
-               }
-               return self::$singleton;
-       }
-
-       /**
-        * Return a boolean indicating whether or not the source used for cryptographic
-        * random bytes generation in the previously run generate* call
-        * was cryptographically strong.
-        *
-        * @return bool Returns true if the source was strong, false if not.
-        */
-       public static function wasStrong() {
-               return self::singleton()->realWasStrong();
-       }
-
-       /**
-        * Generate a run of (ideally) cryptographically random data and return
-        * it in raw binary form.
-        * You can use MWCryptRand::wasStrong() if you wish to know if the source used
-        * was cryptographically strong.
-        *
-        * @param int $bytes the number of bytes of random data to generate
-        * @param bool $forceStrong Pass true if you want generate to prefer cryptographically
-        *                          strong sources of entropy even if reading from them may steal
-        *                          more entropy from the system than optimal.
-        * @return String Raw binary random data
-        */
-       public static function generate( $bytes, $forceStrong = false ) {
-               return self::singleton()->realGenerate( $bytes, $forceStrong );
-       }
-
-       /**
-        * Generate a run of (ideally) cryptographically random data and return
-        * it in hexadecimal string format.
-        * You can use MWCryptRand::wasStrong() if you wish to know if the source used
-        * was cryptographically strong.
-        *
-        * @param int $chars the number of hex chars of random data to generate
-        * @param bool $forceStrong Pass true if you want generate to prefer cryptographically
-        *                          strong sources of entropy even if reading from them may steal
-        *                          more entropy from the system than optimal.
-        * @return String Hexadecimal random data
-        */
-       public static function generateHex( $chars, $forceStrong = false ) {
-               return self::singleton()->realGenerateHex( $chars, $forceStrong );
-       }
-
-}
diff --git a/includes/MWFunction.php b/includes/MWFunction.php
deleted file mode 100644 (file)
index 6d11d17..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-<?php
-/**
- * Helper methods to call functions and instance 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
- */
-
-class MWFunction {
-
-       /**
-        * @deprecated since 1.22; use call_user_func()
-        * @param $callback
-        * @return mixed
-        */
-       public static function call( $callback ) {
-               wfDeprecated( __METHOD__, '1.22' );
-               $args = func_get_args();
-               return call_user_func_array( 'call_user_func', $args );
-       }
-
-       /**
-        * @deprecated since 1.22; use call_user_func_array()
-        * @param $callback
-        * @param $argsarams
-        * @return mixed
-        */
-       public static function callArray( $callback, $argsarams ) {
-               wfDeprecated( __METHOD__, '1.22' );
-               return call_user_func_array( $callback, $argsarams );
-       }
-
-       /**
-        * @param $class
-        * @param $args array
-        * @return object
-        */
-       public static function newObj( $class, $args = array() ) {
-               if ( !count( $args ) ) {
-                       return new $class;
-               }
-
-               $ref = new ReflectionClass( $class );
-               return $ref->newInstanceArgs( $args );
-       }
-
-}
diff --git a/includes/MappedIterator.php b/includes/MappedIterator.php
deleted file mode 100644 (file)
index 70d2032..0000000
+++ /dev/null
@@ -1,114 +0,0 @@
-<?php
-/**
- * Convenience class for generating iterators from iterators.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- * @author Aaron Schulz
- */
-
-/**
- * Convenience class for generating iterators from iterators.
- *
- * @since 1.21
- */
-class MappedIterator extends FilterIterator {
-       /** @var callable */
-       protected $vCallback;
-       /** @var callable */
-       protected $aCallback;
-       /** @var array */
-       protected $cache = array();
-
-       protected $rewound = false; // boolean; whether rewind() has been called
-
-       /**
-        * Build an new iterator from a base iterator by having the former wrap the
-        * later, returning the result of "value" callback for each current() invocation.
-        * The callback takes the result of current() on the base iterator as an argument.
-        * The keys of the base iterator are reused verbatim.
-        *
-        * An "accept" callback can also be provided which will be called for each value in
-        * the base iterator (post-callback) and will return true if that value should be
-        * included in iteration of the MappedIterator (otherwise it will be filtered out).
-        *
-        * @param Iterator|Array $iter
-        * @param callable $vCallback Value transformation callback
-        * @param array $options Options map (includes "accept") (since 1.22)
-        * @throws MWException
-        */
-       public function __construct( $iter, $vCallback, array $options = array() ) {
-               if ( is_array( $iter ) ) {
-                       $baseIterator = new ArrayIterator( $iter );
-               } elseif ( $iter instanceof Iterator ) {
-                       $baseIterator = $iter;
-               } else {
-                       throw new MWException( "Invalid base iterator provided." );
-               }
-               parent::__construct( $baseIterator );
-               $this->vCallback = $vCallback;
-               $this->aCallback = isset( $options['accept'] ) ? $options['accept'] : null;
-       }
-
-       public function next() {
-               $this->cache = array();
-               parent::next();
-       }
-
-       public function rewind() {
-               $this->rewound = true;
-               $this->cache = array();
-               parent::rewind();
-       }
-
-       public function accept() {
-               $value = call_user_func( $this->vCallback, $this->getInnerIterator()->current() );
-               $ok = ( $this->aCallback ) ? call_user_func( $this->aCallback, $value ) : true;
-               if ( $ok ) {
-                       $this->cache['current'] = $value;
-               }
-               return $ok;
-       }
-
-       public function key() {
-               $this->init();
-               return parent::key();
-       }
-
-       public function valid() {
-               $this->init();
-               return parent::valid();
-       }
-
-       public function current() {
-               $this->init();
-               if ( parent::valid() ) {
-                       return $this->cache['current'];
-               } else {
-                       return null; // out of range
-               }
-       }
-
-       /**
-        * Obviate the usual need for rewind() before using a FilterIterator in a manual loop
-        */
-       protected function init() {
-               if ( !$this->rewound ) {
-                       $this->rewind();
-               }
-       }
-}
index 208f96e..57c6264 100644 (file)
@@ -799,7 +799,7 @@ class Message {
         * @return Tuple(type, value)
         */
        protected function extractParam( $param ) {
-               if ( is_array( $param ) ){
+               if ( is_array( $param ) ) {
                        if ( isset( $param['raw'] ) ) {
                                return array( 'after', $param['raw'] );
                        } elseif ( isset( $param['num'] ) ) {
@@ -823,6 +823,11 @@ class Message {
                                );
                                return array( 'before', '[INVALID]' );
                        }
+               } elseif ( $param instanceof Message ) {
+                       // Message objects should not be before parameters because
+                       // then they'll get double escaped. If the message needs to be
+                       // escaped, it'll happen right here when we call toString().
+                       return array( 'after', $param->toString() );
                } else {
                        return array( 'before', $param );
                }
index 37df489..ea89f52 100644 (file)
@@ -28,10 +28,10 @@ abstract class RdfMetaData {
 
        /**
         * Constructor
-        * @param $article Article object
+        * @param Page $page
         */
-       public function __construct( Page $article ) {
-               $this->mArticle = $article;
+       public function __construct( Page $page ) {
+               $this->mArticle = $page;
        }
 
        abstract public function show();
@@ -40,7 +40,10 @@ abstract class RdfMetaData {
                global $wgOut, $wgRequest;
 
                $httpaccept = isset( $_SERVER['HTTP_ACCEPT'] ) ? $_SERVER['HTTP_ACCEPT'] : null;
-               $rdftype = wfNegotiateType( wfAcceptToPrefs( $httpaccept ), wfAcceptToPrefs( self::RDF_TYPE_PREFS ) );
+               $rdftype = wfNegotiateType(
+                       wfAcceptToPrefs( $httpaccept ),
+                       wfAcceptToPrefs( self::RDF_TYPE_PREFS )
+               );
 
                if ( !$rdftype ) {
                        throw new HttpError( 406, wfMessage( 'notacceptable' ) );
@@ -103,8 +106,8 @@ abstract class RdfMetaData {
        }
 
        /**
-        * @param $name string
-        * @param $title Title
+        * @param string $name
+        * @param Title $title
         */
        protected function page( $name, $title ) {
                $this->url( $name, $title->getFullURL() );
index cc3f9b3..7f0454f 100644 (file)
@@ -255,6 +255,11 @@ class OutputPage extends ContextSource {
         */
        private $mTarget = null;
 
+       /**
+        * @var bool: Whether output should contain table of contents
+        */
+       private $mEnableTOC = true;
+
        /**
         * Constructor for OutputPage. This should not be called directly.
         * Instead a new RequestContext should be created and it will implicitly create
@@ -669,7 +674,7 @@ class OutputPage extends ContextSource {
         *
         * @param $timestamp string
         *
-        * @return Boolean: true iff cache-ok headers was sent.
+        * @return Boolean: true if cache-ok headers was sent.
         */
        public function checkLastModified( $timestamp ) {
                global $wgCachePages, $wgCacheEpoch, $wgUseSquid, $wgSquidMaxage;
@@ -1606,6 +1611,7 @@ class OutputPage extends ContextSource {
         */
        function addParserOutput( &$parserOutput ) {
                $this->addParserOutputNoText( $parserOutput );
+               $parserOutput->setTOCEnabled( $this->mEnableTOC );
                $text = $parserOutput->getText();
                wfRunHooks( 'OutputPageBeforeHTML', array( &$this, &$text ) );
                $this->addHTML( $text );
@@ -3648,4 +3654,20 @@ $templates
                return array();
        }
 
+       /**
+        * Enables/disables TOC, doesn't override __NOTOC__
+        * @param bool $flag
+        * @since 1.22
+        */
+       public function enableTOC( $flag = true ) {
+               $this->mEnableTOC = $flag;
+       }
+
+       /**
+        * @return bool
+        * @since 1.22
+        */
+       public function isTOCEnabled() {
+               return $this->mEnableTOC;
+       }
 }
index 02d3546..e6d6ebf 100644 (file)
@@ -38,7 +38,7 @@
  * version are hardcoded here
  */
 function wfPHPVersionError( $type ) {
-       $mwVersion = '1.22';
+       $mwVersion = '1.23';
        $minimumVersionPHP = '5.3.2';
 
        $phpVersion = phpversion();
index b54a9a3..bf1c405 100644 (file)
@@ -84,43 +84,3 @@ function wfIsConfiguredProxy( $ip ) {
                in_array( $ip, $wgSquidServersNoPurge );
        return $trusted;
 }
-
-/**
- * Forks processes to scan the originating IP for an open proxy server
- * MemCached can be used to skip IPs that have already been scanned
- */
-function wfProxyCheck() {
-       global $wgBlockOpenProxies, $wgProxyPorts, $wgProxyScriptPath;
-       global $wgMemc, $wgProxyMemcExpiry, $wgRequest;
-       global $wgProxyKey;
-
-       if ( !$wgBlockOpenProxies ) {
-               return;
-       }
-
-       $ip = $wgRequest->getIP();
-
-       # Get MemCached key
-       $mcKey = wfMemcKey( 'proxy', 'ip', $ip );
-       $mcValue = $wgMemc->get( $mcKey );
-       $skip = (bool)$mcValue;
-
-       # Fork the processes
-       if ( !$skip ) {
-               $title = SpecialPage::getTitleFor( 'Blockme' );
-               $iphash = md5( $ip . $wgProxyKey );
-               $url = wfExpandUrl( $title->getFullURL( 'ip=' . $iphash ), PROTO_HTTP );
-
-               foreach ( $wgProxyPorts as $port ) {
-                       $params = implode( ' ', array(
-                                               escapeshellarg( $wgProxyScriptPath ),
-                                               escapeshellarg( $ip ),
-                                               escapeshellarg( $port ),
-                                               escapeshellarg( $url )
-                                               ));
-                       exec( "php $params >" . wfGetNull() . " 2>&1 &" );
-               }
-               # Set MemCached key
-               $wgMemc->set( $mcKey, 1, $wgProxyMemcExpiry );
-       }
-}
index 305c8ff..4446173 100644 (file)
@@ -116,11 +116,13 @@ class Revision implements IDBAccessObject {
                if ( $id ) {
                        // Use the specified ID
                        $conds['rev_id'] = $id;
+                       return self::newFromConds( $conds, (int)$flags );
                } else {
                        // Use a join to get the latest revision
                        $conds[] = 'rev_id=page_latest';
+                       $db = wfGetDB( ( $flags & self::READ_LATEST ) ? DB_MASTER : DB_SLAVE );
+                       return self::loadFromConds( $db, $conds, $flags );
                }
-               return self::newFromConds( $conds, (int)$flags );
        }
 
        /**
@@ -431,6 +433,36 @@ class Revision implements IDBAccessObject {
                return $fields;
        }
 
+       /**
+        * Return the list of revision fields that should be selected to create
+        * a new revision from an archive row.
+        * @return array
+        */
+       public static function selectArchiveFields() {
+               global $wgContentHandlerUseDB;
+               $fields = array(
+                       'ar_id',
+                       'ar_page_id',
+                       'ar_rev_id',
+                       'ar_text_id',
+                       'ar_timestamp',
+                       'ar_comment',
+                       'ar_user_text',
+                       'ar_user',
+                       'ar_minor_edit',
+                       'ar_deleted',
+                       'ar_len',
+                       'ar_parent_id',
+                       'ar_sha1',
+               );
+
+               if ( $wgContentHandlerUseDB ) {
+                       $fields[] = 'ar_content_format';
+                       $fields[] = 'ar_content_model';
+               }
+               return $fields;
+       }
+
        /**
         * Return the list of text fields that should be selected to read the
         * revision text
index 499d821..4dbc9dd 100644 (file)
@@ -1484,7 +1484,7 @@ class Sanitizer {
                }
 
                $block = array_merge( $common, array( 'align' ) );
-               $tablealign = array( 'align', 'char', 'charoff', 'valign' );
+               $tablealign = array( 'align', 'valign' );
                $tablecell = array(
                        'abbr',
                        'axis',
@@ -1504,7 +1504,7 @@ class Sanitizer {
                        # 7.5.4
                        'div'        => $block,
                        'center'     => $common, # deprecated
-                       'span'       => $block, # ??
+                       'span'       => $common,
 
                        # 7.5.5
                        'h1'         => $block,
@@ -1518,7 +1518,7 @@ class Sanitizer {
                        # address
 
                        # 8.2.4
-                       # bdo
+                       'bdo'        => $common,
 
                        # 9.2.1
                        'em'         => $common,
@@ -1534,7 +1534,7 @@ class Sanitizer {
 
                        # 9.2.2
                        'blockquote' => array_merge( $common, array( 'cite' ) ),
-                       # q
+                       'q'          => array_merge( $common, array( 'cite' ) ),
 
                        # 9.2.3
                        'sub'        => $common,
@@ -1544,10 +1544,10 @@ class Sanitizer {
                        'p'          => $block,
 
                        # 9.3.2
-                       'br'         => array( 'id', 'class', 'title', 'style', 'clear' ),
+                       'br'         => array_merge( $common, array( 'clear' ) ),
 
                        # http://www.whatwg.org/html/text-level-semantics.html#the-wbr-element
-                       'wbr'        => array( 'id', 'class', 'title', 'style' ),
+                       'wbr'        => $common,
 
                        # 9.3.4
                        'pre'        => array_merge( $common, array( 'width' ) ),
@@ -1574,16 +1574,16 @@ class Sanitizer {
                                                                ) ),
 
                        # 11.2.2
-                       'caption'    => array_merge( $common, array( 'align' ) ),
+                       'caption'    => $block,
 
                        # 11.2.3
-                       'thead'      => array_merge( $common, $tablealign ),
-                       'tfoot'      => array_merge( $common, $tablealign ),
-                       'tbody'      => array_merge( $common, $tablealign ),
+                       'thead'      => $common,
+                       'tfoot'      => $common,
+                       'tbody'      => $common,
 
                        # 11.2.4
-                       'colgroup'   => array_merge( $common, array( 'span', 'width' ), $tablealign ),
-                       'col'        => array_merge( $common, array( 'span', 'width' ), $tablealign ),
+                       'colgroup'   => array_merge( $common, array( 'span' ) ),
+                       'col'        => array_merge( $common, array( 'span' ) ),
 
                        # 11.2.5
                        'tr'         => array_merge( $common, array( 'bgcolor' ), $tablealign ),
@@ -1618,7 +1618,7 @@ class Sanitizer {
                        # basefont
 
                        # 15.3
-                       'hr'         => array_merge( $common, array( 'noshade', 'size', 'width' ) ),
+                       'hr'         => array_merge( $common, array( 'width' ) ),
 
                        # HTML Ruby annotation text module, simple ruby only.
                        # http://www.whatwg.org/html/text-level-semantics.html#the-ruby-element
diff --git a/includes/ScopedCallback.php b/includes/ScopedCallback.php
deleted file mode 100644 (file)
index ef22e0a..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
-<?php
-/**
- * This file deals with RAII style scoped callbacks.
- *
- * 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 for asserting that a callback happens when an dummy object leaves scope
- *
- * @since 1.21
- */
-class ScopedCallback {
-       /** @var callable */
-       protected $callback;
-
-       /**
-        * @param callable $callback
-        * @throws MWException
-        */
-       public function __construct( $callback ) {
-               if ( !is_callable( $callback ) ) {
-                       throw new MWException( "Provided callback is not valid." );
-               }
-               $this->callback = $callback;
-       }
-
-       /**
-        * Trigger a scoped callback and destroy it.
-        * This is the same is just setting it to null.
-        *
-        * @param ScopedCallback $sc
-        */
-       public static function consume( ScopedCallback &$sc = null ) {
-               $sc = null;
-       }
-
-       /**
-        * Destroy a scoped callback without triggering it
-        *
-        * @param ScopedCallback $sc
-        */
-       public static function cancel( ScopedCallback &$sc = null ) {
-               if ( $sc ) {
-                       $sc->callback = null;
-               }
-               $sc = null;
-       }
-
-       /**
-        * Trigger the callback when this leaves scope
-        */
-       function __destruct() {
-               if ( $this->callback !== null ) {
-                       call_user_func( $this->callback );
-               }
-       }
-}
diff --git a/includes/ScopedPHPTimeout.php b/includes/ScopedPHPTimeout.php
deleted file mode 100644 (file)
index d1493c3..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-<?php
-/**
- * Expansion of the PHP execution time limit feature for a function call.
- *
- * 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 to expand PHP execution time for a function call.
- * Use this when performing changes that should not be interrupted.
- *
- * On construction, set_time_limit() is called and set to $seconds.
- * If the client aborts the connection, PHP will continue to run.
- * When the object goes out of scope, the timer is restarted, with
- * the original time limit minus the time the object existed.
- */
-class ScopedPHPTimeout {
-       protected $startTime; // float; seconds
-       protected $oldTimeout; // integer; seconds
-       protected $oldIgnoreAbort; // boolean
-
-       protected static $stackDepth = 0; // integer
-       protected static $totalCalls = 0; // integer
-       protected static $totalElapsed = 0; // float; seconds
-
-       /* Prevent callers in infinite loops from running forever */
-       const MAX_TOTAL_CALLS = 1000000;
-       const MAX_TOTAL_TIME = 300; // seconds
-
-       /**
-        * @param $seconds integer
-        */
-       public function __construct( $seconds ) {
-               if ( ini_get( 'max_execution_time' ) > 0 ) { // CLI uses 0
-                       if ( self::$totalCalls >= self::MAX_TOTAL_CALLS ) {
-                               trigger_error( "Maximum invocations of " . __CLASS__ . " exceeded." );
-                       } elseif ( self::$totalElapsed >= self::MAX_TOTAL_TIME ) {
-                               trigger_error( "Time limit within invocations of " . __CLASS__ . " exceeded." );
-                       } elseif ( self::$stackDepth > 0 ) { // recursion guard
-                               trigger_error( "Resursive invocation of " . __CLASS__ . " attempted." );
-                       } else {
-                               $this->oldIgnoreAbort = ignore_user_abort( true );
-                               $this->oldTimeout = ini_set( 'max_execution_time', $seconds );
-                               $this->startTime = microtime( true );
-                               ++self::$stackDepth;
-                               ++self::$totalCalls; // proof against < 1us scopes
-                       }
-               }
-       }
-
-       /**
-        * Restore the original timeout.
-        * This does not account for the timer value on __construct().
-        */
-       public function __destruct() {
-               if ( $this->oldTimeout ) {
-                       $elapsed = microtime( true ) - $this->startTime;
-                       // Note: a limit of 0 is treated as "forever"
-                       set_time_limit( max( 1, $this->oldTimeout - (int)$elapsed ) );
-                       // If each scoped timeout is for less than one second, we end up
-                       // restoring the original timeout without any decrease in value.
-                       // Thus web scripts in an infinite loop can run forever unless we
-                       // take some measures to prevent this. Track total time and calls.
-                       self::$totalElapsed += $elapsed;
-                       --self::$stackDepth;
-                       ignore_user_abort( $this->oldIgnoreAbort );
-               }
-       }
-}
index 355993c..0df6d90 100644 (file)
@@ -251,231 +251,6 @@ class SiteStats {
        }
 }
 
-/**
- * Class for handling updates to the site_stats table
- */
-class SiteStatsUpdate implements DeferrableUpdate {
-       protected $views = 0;
-       protected $edits = 0;
-       protected $pages = 0;
-       protected $articles = 0;
-       protected $users = 0;
-       protected $images = 0;
-
-       // @todo deprecate this constructor
-       function __construct( $views, $edits, $good, $pages = 0, $users = 0 ) {
-               $this->views = $views;
-               $this->edits = $edits;
-               $this->articles = $good;
-               $this->pages = $pages;
-               $this->users = $users;
-       }
-
-       /**
-        * @param $deltas Array
-        * @return SiteStatsUpdate
-        */
-       public static function factory( array $deltas ) {
-               $update = new self( 0, 0, 0 );
-
-               $fields = array( 'views', 'edits', 'pages', 'articles', 'users', 'images' );
-               foreach ( $fields as $field ) {
-                       if ( isset( $deltas[$field] ) && $deltas[$field] ) {
-                               $update->$field = $deltas[$field];
-                       }
-               }
-
-               return $update;
-       }
-
-       public function doUpdate() {
-               global $wgSiteStatsAsyncFactor;
-
-               $rate = $wgSiteStatsAsyncFactor; // convenience
-               // If set to do so, only do actual DB updates 1 every $rate times.
-               // The other times, just update "pending delta" values in memcached.
-               if ( $rate && ( $rate < 0 || mt_rand( 0, $rate - 1 ) != 0 ) ) {
-                       $this->doUpdatePendingDeltas();
-               } else {
-                       // Need a separate transaction because this a global lock
-                       wfGetDB( DB_MASTER )->onTransactionIdle( array( $this, 'tryDBUpdateInternal' ) );
-               }
-       }
-
-       /**
-        * Do not call this outside of SiteStatsUpdate
-        *
-        * @return void
-        */
-       public function tryDBUpdateInternal() {
-               global $wgSiteStatsAsyncFactor;
-
-               $dbw = wfGetDB( DB_MASTER );
-               $lockKey = wfMemcKey( 'site_stats' ); // prepend wiki ID
-               if ( $wgSiteStatsAsyncFactor ) {
-                       // Lock the table so we don't have double DB/memcached updates
-                       if ( !$dbw->lockIsFree( $lockKey, __METHOD__ )
-                               || !$dbw->lock( $lockKey, __METHOD__, 1 ) // 1 sec timeout
-                       ) {
-                               $this->doUpdatePendingDeltas();
-                               return;
-                       }
-                       $pd = $this->getPendingDeltas();
-                       // Piggy-back the async deltas onto those of this stats update....
-                       $this->views += ( $pd['ss_total_views']['+'] - $pd['ss_total_views']['-'] );
-                       $this->edits += ( $pd['ss_total_edits']['+'] - $pd['ss_total_edits']['-'] );
-                       $this->articles += ( $pd['ss_good_articles']['+'] - $pd['ss_good_articles']['-'] );
-                       $this->pages += ( $pd['ss_total_pages']['+'] - $pd['ss_total_pages']['-'] );
-                       $this->users += ( $pd['ss_users']['+'] - $pd['ss_users']['-'] );
-                       $this->images += ( $pd['ss_images']['+'] - $pd['ss_images']['-'] );
-               }
-
-               // Build up an SQL query of deltas and apply them...
-               $updates = '';
-               $this->appendUpdate( $updates, 'ss_total_views', $this->views );
-               $this->appendUpdate( $updates, 'ss_total_edits', $this->edits );
-               $this->appendUpdate( $updates, 'ss_good_articles', $this->articles );
-               $this->appendUpdate( $updates, 'ss_total_pages', $this->pages );
-               $this->appendUpdate( $updates, 'ss_users', $this->users );
-               $this->appendUpdate( $updates, 'ss_images', $this->images );
-               if ( $updates != '' ) {
-                       $dbw->update( 'site_stats', array( $updates ), array(), __METHOD__ );
-               }
-
-               if ( $wgSiteStatsAsyncFactor ) {
-                       // Decrement the async deltas now that we applied them
-                       $this->removePendingDeltas( $pd );
-                       // Commit the updates and unlock the table
-                       $dbw->unlock( $lockKey, __METHOD__ );
-               }
-       }
-
-       /**
-        * @param $dbw DatabaseBase
-        * @return bool|mixed
-        */
-       public static function cacheUpdate( $dbw ) {
-               global $wgActiveUserDays;
-               $dbr = wfGetDB( DB_SLAVE, array( 'SpecialStatistics', 'vslow' ) );
-               # Get non-bot users than did some recent action other than making accounts.
-               # If account creation is included, the number gets inflated ~20+ fold on enwiki.
-               $activeUsers = $dbr->selectField(
-                       'recentchanges',
-                       'COUNT( DISTINCT rc_user_text )',
-                       array(
-                               'rc_user != 0',
-                               'rc_bot' => 0,
-                               'rc_log_type != ' . $dbr->addQuotes( 'newusers' ) . ' OR rc_log_type IS NULL',
-                               'rc_timestamp >= ' . $dbr->addQuotes( $dbr->timestamp( wfTimestamp( TS_UNIX ) - $wgActiveUserDays * 24 * 3600 ) ),
-                       ),
-                       __METHOD__
-               );
-               $dbw->update(
-                       'site_stats',
-                       array( 'ss_active_users' => intval( $activeUsers ) ),
-                       array( 'ss_row_id' => 1 ),
-                       __METHOD__
-               );
-               return $activeUsers;
-       }
-
-       protected function doUpdatePendingDeltas() {
-               $this->adjustPending( 'ss_total_views', $this->views );
-               $this->adjustPending( 'ss_total_edits', $this->edits );
-               $this->adjustPending( 'ss_good_articles', $this->articles );
-               $this->adjustPending( 'ss_total_pages', $this->pages );
-               $this->adjustPending( 'ss_users', $this->users );
-               $this->adjustPending( 'ss_images', $this->images );
-       }
-
-       /**
-        * @param $sql string
-        * @param $field string
-        * @param $delta integer
-        */
-       protected function appendUpdate( &$sql, $field, $delta ) {
-               if ( $delta ) {
-                       if ( $sql ) {
-                               $sql .= ',';
-                       }
-                       if ( $delta < 0 ) {
-                               $sql .= "$field=$field-" . abs( $delta );
-                       } else {
-                               $sql .= "$field=$field+" . abs( $delta );
-                       }
-               }
-       }
-
-       /**
-        * @param $type string
-        * @param string $sign ('+' or '-')
-        * @return string
-        */
-       private function getTypeCacheKey( $type, $sign ) {
-               return wfMemcKey( 'sitestatsupdate', 'pendingdelta', $type, $sign );
-       }
-
-       /**
-        * Adjust the pending deltas for a stat type.
-        * Each stat type has two pending counters, one for increments and decrements
-        * @param $type string
-        * @param $delta integer Delta (positive or negative)
-        * @return void
-        */
-       protected function adjustPending( $type, $delta ) {
-               global $wgMemc;
-
-               if ( $delta < 0 ) { // decrement
-                       $key = $this->getTypeCacheKey( $type, '-' );
-               } else { // increment
-                       $key = $this->getTypeCacheKey( $type, '+' );
-               }
-
-               $magnitude = abs( $delta );
-               if ( !$wgMemc->incr( $key, $magnitude ) ) { // not there?
-                       if ( !$wgMemc->add( $key, $magnitude ) ) { // race?
-                               $wgMemc->incr( $key, $magnitude );
-                       }
-               }
-       }
-
-       /**
-        * Get pending delta counters for each stat type
-        * @return Array Positive and negative deltas for each type
-        * @return void
-        */
-       protected function getPendingDeltas() {
-               global $wgMemc;
-
-               $pending = array();
-               foreach ( array( 'ss_total_views', 'ss_total_edits',
-                       'ss_good_articles', 'ss_total_pages', 'ss_users', 'ss_images' ) as $type )
-               {
-                       // Get pending increments and pending decrements
-                       $pending[$type]['+'] = (int)$wgMemc->get( $this->getTypeCacheKey( $type, '+' ) );
-                       $pending[$type]['-'] = (int)$wgMemc->get( $this->getTypeCacheKey( $type, '-' ) );
-               }
-
-               return $pending;
-       }
-
-       /**
-        * Reduce pending delta counters after updates have been applied
-        * @param array $pd Result of getPendingDeltas(), used for DB update
-        * @return void
-        */
-       protected function removePendingDeltas( array $pd ) {
-               global $wgMemc;
-
-               foreach ( $pd as $type => $deltas ) {
-                       foreach ( $deltas as $sign => $magnitude ) {
-                               // Lower the pending counter now that we applied these changes
-                               $wgMemc->decr( $this->getTypeCacheKey( $type, $sign ), $magnitude );
-                       }
-               }
-       }
-}
-
 /**
  * Class designed for counting of stats.
  */
index 5801806..170e96f 100644 (file)
@@ -1379,61 +1379,58 @@ abstract class Skin extends ContextSource {
 
                if ( count( $newtalks ) == 1 && $newtalks[0]['wiki'] === wfWikiID() ) {
                        $uTalkTitle = $user->getTalkPage();
-
-                       if ( !$uTalkTitle->equals( $out->getTitle() ) ) {
-                               $lastSeenRev = isset( $newtalks[0]['rev'] ) ? $newtalks[0]['rev'] : null;
-                               $nofAuthors = 0;
-                               if ( $lastSeenRev !== null ) {
-                                       $plural = true; // Default if we have a last seen revision: if unknown, use plural
-                                       $latestRev = Revision::newFromTitle( $uTalkTitle, false, Revision::READ_NORMAL );
-                                       if ( $latestRev !== null ) {
-                                               // Singular if only 1 unseen revision, plural if several unseen revisions.
-                                               $plural = $latestRev->getParentId() !== $lastSeenRev->getId();
-                                               $nofAuthors = $uTalkTitle->countAuthorsBetween(
-                                                       $lastSeenRev, $latestRev, 10, 'include_new' );
-                                       }
-                               } else {
-                                       // Singular if no revision -> diff link will show latest change only in any case
-                                       $plural = false;
+                       $lastSeenRev = isset( $newtalks[0]['rev'] ) ? $newtalks[0]['rev'] : null;
+                       $nofAuthors = 0;
+                       if ( $lastSeenRev !== null ) {
+                               $plural = true; // Default if we have a last seen revision: if unknown, use plural
+                               $latestRev = Revision::newFromTitle( $uTalkTitle, false, Revision::READ_NORMAL );
+                               if ( $latestRev !== null ) {
+                                       // Singular if only 1 unseen revision, plural if several unseen revisions.
+                                       $plural = $latestRev->getParentId() !== $lastSeenRev->getId();
+                                       $nofAuthors = $uTalkTitle->countAuthorsBetween(
+                                               $lastSeenRev, $latestRev, 10, 'include_new' );
                                }
-                               $plural = $plural ? 2 : 1;
-                               // 2 signifies "more than one revision". We don't know how many, and even if we did,
-                               // the number of revisions or authors is not necessarily the same as the number of
-                               // "messages".
-                               $newMessagesLink = Linker::linkKnown(
-                                       $uTalkTitle,
-                                       $this->msg( 'newmessageslinkplural' )->params( $plural )->escaped(),
-                                       array(),
-                                       array( 'redirect' => 'no' )
-                               );
+                       } else {
+                               // Singular if no revision -> diff link will show latest change only in any case
+                               $plural = false;
+                       }
+                       $plural = $plural ? 2 : 1;
+                       // 2 signifies "more than one revision". We don't know how many, and even if we did,
+                       // the number of revisions or authors is not necessarily the same as the number of
+                       // "messages".
+                       $newMessagesLink = Linker::linkKnown(
+                               $uTalkTitle,
+                               $this->msg( 'newmessageslinkplural' )->params( $plural )->escaped(),
+                               array(),
+                               array( 'redirect' => 'no' )
+                       );
 
-                               $newMessagesDiffLink = Linker::linkKnown(
-                                       $uTalkTitle,
-                                       $this->msg( 'newmessagesdifflinkplural' )->params( $plural )->escaped(),
-                                       array(),
-                                       $lastSeenRev !== null
-                                               ? array( 'oldid' => $lastSeenRev->getId(), 'diff' => 'cur' )
-                                               : array( 'diff' => 'cur' )
-                               );
+                       $newMessagesDiffLink = Linker::linkKnown(
+                               $uTalkTitle,
+                               $this->msg( 'newmessagesdifflinkplural' )->params( $plural )->escaped(),
+                               array(),
+                               $lastSeenRev !== null
+                                       ? array( 'oldid' => $lastSeenRev->getId(), 'diff' => 'cur' )
+                                       : array( 'diff' => 'cur' )
+                       );
 
-                               if ( $nofAuthors >= 1 && $nofAuthors <= 10 ) {
-                                       $newMessagesAlert = $this->msg(
-                                               'youhavenewmessagesfromusers',
-                                               $newMessagesLink,
-                                               $newMessagesDiffLink
-                                       )->numParams( $nofAuthors );
-                               } else {
-                                       // $nofAuthors === 11 signifies "11 or more" ("more than 10")
-                                       $newMessagesAlert = $this->msg(
-                                               $nofAuthors > 10 ? 'youhavenewmessagesmanyusers' : 'youhavenewmessages',
-                                               $newMessagesLink,
-                                               $newMessagesDiffLink
-                                       );
-                               }
-                               $newMessagesAlert = $newMessagesAlert->text();
-                               # Disable Squid cache
-                               $out->setSquidMaxage( 0 );
+                       if ( $nofAuthors >= 1 && $nofAuthors <= 10 ) {
+                               $newMessagesAlert = $this->msg(
+                                       'youhavenewmessagesfromusers',
+                                       $newMessagesLink,
+                                       $newMessagesDiffLink
+                               )->numParams( $nofAuthors );
+                       } else {
+                               // $nofAuthors === 11 signifies "11 or more" ("more than 10")
+                               $newMessagesAlert = $this->msg(
+                                       $nofAuthors > 10 ? 'youhavenewmessagesmanyusers' : 'youhavenewmessages',
+                                       $newMessagesLink,
+                                       $newMessagesDiffLink
+                               );
                        }
+                       $newMessagesAlert = $newMessagesAlert->text();
+                       # Disable Squid cache
+                       $out->setSquidMaxage( 0 );
                } elseif ( count( $newtalks ) ) {
                        $sep = $this->msg( 'newtalkseparator' )->escaped();
                        $msgs = array();
index 44cafe9..1e7ce13 100644 (file)
@@ -1717,6 +1717,10 @@ abstract class BaseTemplate extends QuickTemplate {
         * on the link) is present it will be used to generate a tooltip and
         * accesskey for the link.
         *
+        * The keys "context" and "primary" are ignored; these keys are used
+        * internally by skins and are not supposed to be included in the HTML
+        * output.
+        *
         * If you don't want an accesskey, set $item['tooltiponly'] = true;
         *
         * @param array $options can be used to affect the output of a link.
@@ -1757,7 +1761,7 @@ abstract class BaseTemplate extends QuickTemplate {
 
                if ( isset( $item['href'] ) || isset( $options['link-fallback'] ) ) {
                        $attrs = $item;
-                       foreach ( array( 'single-id', 'text', 'msg', 'tooltiponly' ) as $k ) {
+                       foreach ( array( 'single-id', 'text', 'msg', 'tooltiponly', 'context', 'primary' ) as $k ) {
                                unset( $attrs[$k] );
                        }
 
@@ -1994,9 +1998,10 @@ abstract class BaseTemplate extends QuickTemplate {
         * body and html tags.
         */
        function printTrail() { ?>
+<?php echo MWDebug::getDebugHTML( $this->getSkin()->getContext() ); ?>
 <?php $this->html( 'bottomscripts' ); /* JS call to runBodyOnloadHook */ ?>
 <?php $this->html( 'reporttime' ) ?>
-<?php echo MWDebug::getDebugHTML( $this->getSkin()->getContext() );
+<?php
        }
 
 }
index c03f1ba..11edc8a 100644 (file)
@@ -155,7 +155,6 @@ class SpecialPageFactory {
 
                // Unlisted / redirects
                'Blankpage'                 => 'SpecialBlankpage',
-               'Blockme'                   => 'SpecialBlockme',
                'Emailuser'                 => 'SpecialEmailUser',
                'Movepage'                  => 'MovePageForm',
                'Mycontributions'           => 'SpecialMycontributions',
diff --git a/includes/SqlDataUpdate.php b/includes/SqlDataUpdate.php
deleted file mode 100644 (file)
index 51188d8..0000000
+++ /dev/null
@@ -1,152 +0,0 @@
-<?php
-/**
- * Base code for update jobs that put some secondary data extracted
- * from article content into the database.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * 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
- */
-
-/**
- * Abstract base class for update jobs that put some secondary data extracted
- * from article content into the database.
- *
- * @note: subclasses should NOT start or commit transactions in their doUpdate() method,
- *        a transaction will automatically be wrapped around the update. Starting another
- *        one would break the outer transaction bracket. If need be, subclasses can override
- *        the beginTransaction() and commitTransaction() methods.
- */
-abstract class SqlDataUpdate extends DataUpdate {
-
-       protected $mDb;            //!< Database connection reference
-       protected $mOptions;       //!< SELECT options to be used (array)
-
-       private   $mHasTransaction; //!< bool whether a transaction is open on this object (internal use only!)
-       protected $mUseTransaction; //!< bool whether this update should be wrapped in a transaction
-
-       /**
-        * Constructor
-        *
-        * @param bool $withTransaction whether this update should be wrapped in a transaction (default: true).
-        *             A transaction is only started if no transaction is already in progress,
-        *             see beginTransaction() for details.
-        **/
-       public function __construct( $withTransaction = true ) {
-               global $wgAntiLockFlags;
-
-               parent::__construct();
-
-               if ( $wgAntiLockFlags & ALF_NO_LINK_LOCK ) {
-                       $this->mOptions = array();
-               } else {
-                       $this->mOptions = array( 'FOR UPDATE' );
-               }
-
-               // @todo get connection only when it's needed? make sure that doesn't break anything, especially transactions!
-               $this->mDb = wfGetDB( DB_MASTER );
-
-               $this->mWithTransaction = $withTransaction;
-               $this->mHasTransaction = false;
-       }
-
-       /**
-        * Begin a database transaction, if $withTransaction was given as true in the constructor for this SqlDataUpdate.
-        *
-        * Because nested transactions are not supported by the Database class, this implementation
-        * checks Database::trxLevel() and only opens a transaction if none is already active.
-        */
-       public function beginTransaction() {
-               if ( !$this->mWithTransaction ) {
-                       return;
-               }
-
-               // NOTE: nested transactions are not supported, only start a transaction if none is open
-               if ( $this->mDb->trxLevel() === 0 ) {
-                       $this->mDb->begin( get_class( $this ) . '::beginTransaction' );
-                       $this->mHasTransaction = true;
-               }
-       }
-
-       /**
-        * Commit the database transaction started via beginTransaction (if any).
-        */
-       public function commitTransaction() {
-               if ( $this->mHasTransaction ) {
-                       $this->mDb->commit( get_class( $this ) . '::commitTransaction' );
-                       $this->mHasTransaction = false;
-               }
-       }
-
-       /**
-        * Abort the database transaction started via beginTransaction (if any).
-        */
-       public function abortTransaction() {
-               if ( $this->mHasTransaction ) { //XXX: actually... maybe always?
-                       $this->mDb->rollback( get_class( $this ) . '::abortTransaction' );
-                       $this->mHasTransaction = false;
-               }
-       }
-
-       /**
-        * Invalidate the cache of a list of pages from a single namespace.
-        * This is intended for use by subclasses.
-        *
-        * @param $namespace Integer
-        * @param $dbkeys Array
-        */
-       protected function invalidatePages( $namespace, array $dbkeys ) {
-               if ( $dbkeys === array() ) {
-                       return;
-               }
-
-               /**
-                * Determine which pages need to be updated
-                * This is necessary to prevent the job queue from smashing the DB with
-                * large numbers of concurrent invalidations of the same page
-                */
-               $now = $this->mDb->timestamp();
-               $ids = array();
-               $res = $this->mDb->select( 'page', array( 'page_id' ),
-                       array(
-                               'page_namespace' => $namespace,
-                               'page_title' => $dbkeys,
-                               'page_touched < ' . $this->mDb->addQuotes( $now )
-                       ), __METHOD__
-               );
-
-               foreach ( $res as $row ) {
-                       $ids[] = $row->page_id;
-               }
-
-               if ( $ids === array() ) {
-                       return;
-               }
-
-               /**
-                * Do the update
-                * We still need the page_touched condition, in case the row has changed since
-                * the non-locking select above.
-                */
-               $this->mDb->update( 'page', array( 'page_touched' => $now ),
-                       array(
-                               'page_id' => $ids,
-                               'page_touched < ' . $this->mDb->addQuotes( $now )
-                       ), __METHOD__
-               );
-       }
-
-}
index 7a84fed..836c24a 100644 (file)
  * so that a lack of error-handling will be explicit.
  */
 class Status {
-       var $ok = true;
-       var $value;
+       public $ok = true;
+       public $value;
 
        /** Counters for batch operations */
-       public $successCount = 0, $failCount = 0;
+       public $successCount = 0;
+       public $failCount = 0;
+
        /** Array to indicate which items of the batch operations were successful */
        public $success = array();
 
-       /*semi-private*/ var $errors = array();
-       /*semi-private*/ var $cleanCallback = false;
+       public $errors = array();
+       public $cleanCallback = false;
 
        /**
         * Factory function for fatal errors
@@ -188,15 +190,18 @@ class Status {
                        }
                }
                if ( count( $this->errors ) == 1 ) {
-                       $s = $this->getErrorMessage( $this->errors[0] );
+                       $s = $this->getErrorMessage( $this->errors[0] )->plain();
                        if ( $shortContext ) {
                                $s = wfMessage( $shortContext, $s )->plain();
                        } elseif ( $longContext ) {
                                $s = wfMessage( $longContext, "* $s\n" )->plain();
                        }
                } else {
-                       $s = '* ' . implode( "\n* ",
-                               $this->getErrorMessageArray( $this->errors ) ) . "\n";
+                       $errors = $this->getErrorMessageArray( $this->errors );
+                       foreach ( $errors as &$error ) {
+                               $error = $error->plain();
+                       }
+                       $s = '* ' . implode( "\n* ", $errors ) . "\n";
                        if ( $longContext ) {
                                $s = wfMessage( $longContext, $s )->plain();
                        } elseif ( $shortContext ) {
@@ -206,6 +211,56 @@ class Status {
                return $s;
        }
 
+       /**
+        * Get the error list as a Message object
+        *
+        * @param string $shortContext a short enclosing context message name, to
+        *        be used when there is a single error
+        * @param string $longContext a long enclosing context message name, for a list
+        * @return Message
+        */
+       function getMessage( $shortContext = false, $longContext = false ) {
+               if ( count( $this->errors ) == 0 ) {
+                       if ( $this->ok ) {
+                               $this->fatal( 'internalerror_info',
+                                       __METHOD__ . " called for a good result, this is incorrect\n" );
+                       } else {
+                               $this->fatal( 'internalerror_info',
+                                       __METHOD__ . ": Invalid result object: no error text but not OK\n" );
+                       }
+               }
+               if ( count( $this->errors ) == 1 ) {
+                       $s = $this->getErrorMessage( $this->errors[0] );
+                       if ( $shortContext ) {
+                               $s = wfMessage( $shortContext, $s );
+                       } elseif ( $longContext ) {
+                               $wrapper = new RawMessage( "* \$1\n" );
+                               $wrapper->params( $s )->parse();
+                               $s = wfMessage( $longContext, $wrapper );
+                       }
+               } else {
+                       $msgs =  $this->getErrorMessageArray( $this->errors );
+                       $msgCount = count( $msgs );
+
+                       if ( $shortContext ) {
+                               $msgCount++;
+                       }
+
+                       $wrapper = new RawMessage( '* $' . implode( "\n* \$", range( 1, $msgCount ) ) );
+                       $s = $wrapper->params( $msgs )->parse();
+
+                       if ( $longContext ) {
+                               $s = wfMessage( $longContext, $wrapper );
+                       } elseif ( $shortContext ) {
+                               $wrapper = new RawMessage( "\n\$1\n", $wrapper );
+                               $wrapper->parse();
+                               $s = wfMessage( $shortContext, $wrapper );
+                       }
+               }
+
+               return $s;
+       }
+
        /**
         * Return the message for a single error.
         * @param $error Mixed With an array & two values keyed by
@@ -230,7 +285,7 @@ class Status {
                } else {
                        $msg = wfMessage( $error );
                }
-               return $msg->plain();
+               return $msg;
        }
 
        /**
@@ -294,7 +349,6 @@ class Status {
        /**
         * Returns a list of status messages of the given type
         * @param $type String
-        *
         * @return Array
         */
        protected function getStatusArray( $type ) {
@@ -302,7 +356,10 @@ class Status {
                foreach ( $this->errors as $error ) {
                        if ( $error['type'] === $type ) {
                                if ( $error['message'] instanceof Message ) {
-                                       $result[] = array_merge( array( $error['message']->getKey() ), $error['message']->getParams() );
+                                       $result[] = array_merge(
+                                               array( $error['message']->getKey() ),
+                                               $error['message']->getParams()
+                                       );
                                } elseif ( $error['params'] ) {
                                        $result[] = array_merge( array( $error['message'] ), $error['params'] );
                                } else {
@@ -310,6 +367,7 @@ class Status {
                                }
                        }
                }
+
                return $result;
        }
 
@@ -371,15 +429,6 @@ class Status {
                return $replaced;
        }
 
-       /**
-        * Backward compatibility function for WikiError -> Status migration
-        *
-        * @return String
-        */
-       public function getMessage() {
-               return $this->getWikiText();
-       }
-
        /**
         * @return mixed
         */
diff --git a/includes/StringUtils.php b/includes/StringUtils.php
deleted file mode 100644 (file)
index c1545e6..0000000
+++ /dev/null
@@ -1,606 +0,0 @@
-<?php
-/**
- * Methods to play with strings.
- *
- * 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
- */
-
-/**
- * A collection of static methods to play with strings.
- */
-class StringUtils {
-
-       /**
-        * Test whether a string is valid UTF-8.
-        *
-        * The function check for invalid byte sequences, overlong encoding but
-        * not for different normalisations.
-        *
-        * This relies internally on the mbstring function mb_check_encoding()
-        * hardcoded to check against UTF-8. Whenever the function is not available
-        * we fallback to a pure PHP implementation. Setting $disableMbstring to
-        * true will skip the use of mb_check_encoding, this is mostly intended for
-        * unit testing our internal implementation.
-        *
-        * @since 1.21
-        * @note In MediaWiki 1.21, this function did not provide proper UTF-8 validation.
-        * In particular, the pure PHP code path did not in fact check for overlong forms.
-        * Beware of this when backporting code to that version of MediaWiki.
-        *
-        * @param string $value String to check
-        * @param boolean $disableMbstring Whether to use the pure PHP
-        * implementation instead of trying mb_check_encoding. Intended for unit
-        * testing. Default: false
-        *
-        * @return boolean Whether the given $value is a valid UTF-8 encoded string
-        */
-       static function isUtf8( $value, $disableMbstring = false ) {
-               $value = (string)$value;
-
-               // If the mbstring extension is loaded, use it. However, before PHP 5.4, values above
-               // U+10FFFF are incorrectly allowed, so we have to check for them separately.
-               if ( !$disableMbstring && function_exists( 'mb_check_encoding' ) ) {
-                       static $newPHP;
-                       if ( $newPHP === null ) {
-                               $newPHP = !mb_check_encoding( "\xf4\x90\x80\x80", 'UTF-8' );
-                       }
-
-                       return mb_check_encoding( $value, 'UTF-8' ) &&
-                               ( $newPHP || preg_match( "/\xf4[\x90-\xbf]|[\xf5-\xff]/S", $value ) === 0 );
-               }
-
-               if ( preg_match( "/[\x80-\xff]/S", $value ) === 0 ) {
-                       // String contains only ASCII characters, has to be valid
-                       return true;
-               }
-
-               // PCRE implements repetition using recursion; to avoid a stack overflow (and segfault)
-               // for large input, we check for invalid sequences (<= 5 bytes) rather than valid
-               // sequences, which can be as long as the input string is. Multiple short regexes are
-               // used rather than a single long regex for performance.
-               static $regexes;
-               if ( $regexes === null ) {
-                       $cont = "[\x80-\xbf]";
-                       $after = "(?!$cont)"; // "(?:[^\x80-\xbf]|$)" would work here
-                       $regexes = array(
-                               // Continuation byte at the start
-                               "/^$cont/",
-
-                               // ASCII byte followed by a continuation byte
-                               "/[\\x00-\x7f]$cont/S",
-
-                               // Illegal byte
-                               "/[\xc0\xc1\xf5-\xff]/S",
-
-                               // Invalid 2-byte sequence, or valid one then an extra continuation byte
-                               "/[\xc2-\xdf](?!$cont$after)/S",
-
-                               // Invalid 3-byte sequence, or valid one then an extra continuation byte
-                               "/\xe0(?![\xa0-\xbf]$cont$after)/",
-                               "/[\xe1-\xec\xee\xef](?!$cont{2}$after)/S",
-                               "/\xed(?![\x80-\x9f]$cont$after)/",
-
-                               // Invalid 4-byte sequence, or valid one then an extra continuation byte
-                               "/\xf0(?![\x90-\xbf]$cont{2}$after)/",
-                               "/[\xf1-\xf3](?!$cont{3}$after)/S",
-                               "/\xf4(?![\x80-\x8f]$cont{2}$after)/",
-                       );
-               }
-
-               foreach ( $regexes as $regex ) {
-                       if ( preg_match( $regex, $value ) !== 0 ) {
-                               return false;
-                       }
-               }
-               return true;
-       }
-
-       /**
-        * Perform an operation equivalent to
-        *
-        *     preg_replace( "!$startDelim(.*?)$endDelim!", $replace, $subject );
-        *
-        * except that it's worst-case O(N) instead of O(N^2)
-        *
-        * Compared to delimiterReplace(), this implementation is fast but memory-
-        * hungry and inflexible. The memory requirements are such that I don't
-        * recommend using it on anything but guaranteed small chunks of text.
-        *
-        * @param $startDelim
-        * @param $endDelim
-        * @param $replace
-        * @param $subject
-        *
-        * @return string
-        */
-       static function hungryDelimiterReplace( $startDelim, $endDelim, $replace, $subject ) {
-               $segments = explode( $startDelim, $subject );
-               $output = array_shift( $segments );
-               foreach ( $segments as $s ) {
-                       $endDelimPos = strpos( $s, $endDelim );
-                       if ( $endDelimPos === false ) {
-                               $output .= $startDelim . $s;
-                       } else {
-                               $output .= $replace . substr( $s, $endDelimPos + strlen( $endDelim ) );
-                       }
-               }
-               return $output;
-       }
-
-       /**
-        * Perform an operation equivalent to
-        *
-        *   preg_replace_callback( "!$startDelim(.*)$endDelim!s$flags", $callback, $subject )
-        *
-        * This implementation is slower than hungryDelimiterReplace but uses far less
-        * memory. The delimiters are literal strings, not regular expressions.
-        *
-        * If the start delimiter ends with an initial substring of the end delimiter,
-        * e.g. in the case of C-style comments, the behavior differs from the model
-        * regex. In this implementation, the end must share no characters with the
-        * start, so e.g. /*\/ is not considered to be both the start and end of a
-        * comment. /*\/xy/*\/ is considered to be a single comment with contents /xy/.
-        *
-        * @param string $startDelim start delimiter
-        * @param string $endDelim end delimiter
-        * @param $callback Callback: function to call on each match
-        * @param $subject String
-        * @param string $flags regular expression flags
-        * @throws MWException
-        * @return string
-        */
-       static function delimiterReplaceCallback( $startDelim, $endDelim, $callback, $subject, $flags = '' ) {
-               $inputPos = 0;
-               $outputPos = 0;
-               $output = '';
-               $foundStart = false;
-               $encStart = preg_quote( $startDelim, '!' );
-               $encEnd = preg_quote( $endDelim, '!' );
-               $strcmp = strpos( $flags, 'i' ) === false ? 'strcmp' : 'strcasecmp';
-               $endLength = strlen( $endDelim );
-               $m = array();
-
-               while ( $inputPos < strlen( $subject ) &&
-                       preg_match( "!($encStart)|($encEnd)!S$flags", $subject, $m, PREG_OFFSET_CAPTURE, $inputPos ) )
-               {
-                       $tokenOffset = $m[0][1];
-                       if ( $m[1][0] != '' ) {
-                               if ( $foundStart &&
-                                       $strcmp( $endDelim, substr( $subject, $tokenOffset, $endLength ) ) == 0 )
-                               {
-                                       # An end match is present at the same location
-                                       $tokenType = 'end';
-                                       $tokenLength = $endLength;
-                               } else {
-                                       $tokenType = 'start';
-                                       $tokenLength = strlen( $m[0][0] );
-                               }
-                       } elseif ( $m[2][0] != '' ) {
-                               $tokenType = 'end';
-                               $tokenLength = strlen( $m[0][0] );
-                       } else {
-                               throw new MWException( 'Invalid delimiter given to ' . __METHOD__ );
-                       }
-
-                       if ( $tokenType == 'start' ) {
-                               # Only move the start position if we haven't already found a start
-                               # This means that START START END matches outer pair
-                               if ( !$foundStart ) {
-                                       # Found start
-                                       $inputPos = $tokenOffset + $tokenLength;
-                                       # Write out the non-matching section
-                                       $output .= substr( $subject, $outputPos, $tokenOffset - $outputPos );
-                                       $outputPos = $tokenOffset;
-                                       $contentPos = $inputPos;
-                                       $foundStart = true;
-                               } else {
-                                       # Move the input position past the *first character* of START,
-                                       # to protect against missing END when it overlaps with START
-                                       $inputPos = $tokenOffset + 1;
-                               }
-                       } elseif ( $tokenType == 'end' ) {
-                               if ( $foundStart ) {
-                                       # Found match
-                                       $output .= call_user_func( $callback, array(
-                                               substr( $subject, $outputPos, $tokenOffset + $tokenLength - $outputPos ),
-                                               substr( $subject, $contentPos, $tokenOffset - $contentPos )
-                                       ));
-                                       $foundStart = false;
-                               } else {
-                                       # Non-matching end, write it out
-                                       $output .= substr( $subject, $inputPos, $tokenOffset + $tokenLength - $outputPos );
-                               }
-                               $inputPos = $outputPos = $tokenOffset + $tokenLength;
-                       } else {
-                               throw new MWException( 'Invalid delimiter given to ' . __METHOD__ );
-                       }
-               }
-               if ( $outputPos < strlen( $subject ) ) {
-                       $output .= substr( $subject, $outputPos );
-               }
-               return $output;
-       }
-
-       /**
-        * Perform an operation equivalent to
-        *
-        *   preg_replace( "!$startDelim(.*)$endDelim!$flags", $replace, $subject )
-        *
-        * @param string $startDelim start delimiter regular expression
-        * @param string $endDelim end delimiter regular expression
-        * @param string $replace replacement string. May contain $1, which will be
-        *                 replaced by the text between the delimiters
-        * @param string $subject to search
-        * @param string $flags regular expression flags
-        * @return String: The string with the matches replaced
-        */
-       static function delimiterReplace( $startDelim, $endDelim, $replace, $subject, $flags = '' ) {
-               $replacer = new RegexlikeReplacer( $replace );
-               return self::delimiterReplaceCallback( $startDelim, $endDelim,
-                       $replacer->cb(), $subject, $flags );
-       }
-
-       /**
-        * More or less "markup-safe" explode()
-        * Ignores any instances of the separator inside <...>
-        * @param string $separator
-        * @param string $text
-        * @return array
-        */
-       static function explodeMarkup( $separator, $text ) {
-               $placeholder = "\x00";
-
-               // Remove placeholder instances
-               $text = str_replace( $placeholder, '', $text );
-
-               // Replace instances of the separator inside HTML-like tags with the placeholder
-               $replacer = new DoubleReplacer( $separator, $placeholder );
-               $cleaned = StringUtils::delimiterReplaceCallback( '<', '>', $replacer->cb(), $text );
-
-               // Explode, then put the replaced separators back in
-               $items = explode( $separator, $cleaned );
-               foreach ( $items as $i => $str ) {
-                       $items[$i] = str_replace( $placeholder, $separator, $str );
-               }
-
-               return $items;
-       }
-
-       /**
-        * Escape a string to make it suitable for inclusion in a preg_replace()
-        * replacement parameter.
-        *
-        * @param string $string
-        * @return string
-        */
-       static function escapeRegexReplacement( $string ) {
-               $string = str_replace( '\\', '\\\\', $string );
-               $string = str_replace( '$', '\\$', $string );
-               return $string;
-       }
-
-       /**
-        * Workalike for explode() with limited memory usage.
-        * Returns an Iterator
-        * @param string $separator
-        * @param string $subject
-        * @return ArrayIterator|ExplodeIterator
-        */
-       static function explode( $separator, $subject ) {
-               if ( substr_count( $subject, $separator ) > 1000 ) {
-                       return new ExplodeIterator( $separator, $subject );
-               } else {
-                       return new ArrayIterator( explode( $separator, $subject ) );
-               }
-       }
-}
-
-/**
- * Base class for "replacers", objects used in preg_replace_callback() and
- * StringUtils::delimiterReplaceCallback()
- */
-class Replacer {
-
-       /**
-        * @return array
-        */
-       function cb() {
-               return array( &$this, 'replace' );
-       }
-}
-
-/**
- * Class to replace regex matches with a string similar to that used in preg_replace()
- */
-class RegexlikeReplacer extends Replacer {
-       var $r;
-
-       /**
-        * @param string $r
-        */
-       function __construct( $r ) {
-               $this->r = $r;
-       }
-
-       /**
-        * @param array $matches
-        * @return string
-        */
-       function replace( $matches ) {
-               $pairs = array();
-               foreach ( $matches as $i => $match ) {
-                       $pairs["\$$i"] = $match;
-               }
-               return strtr( $this->r, $pairs );
-       }
-
-}
-
-/**
- * Class to perform secondary replacement within each replacement string
- */
-class DoubleReplacer extends Replacer {
-
-       /**
-        * @param $from
-        * @param $to
-        * @param int $index
-        */
-       function __construct( $from, $to, $index = 0 ) {
-               $this->from = $from;
-               $this->to = $to;
-               $this->index = $index;
-       }
-
-       /**
-        * @param array $matches
-        * @return mixed
-        */
-       function replace( $matches ) {
-               return str_replace( $this->from, $this->to, $matches[$this->index] );
-       }
-}
-
-/**
- * Class to perform replacement based on a simple hashtable lookup
- */
-class HashtableReplacer extends Replacer {
-       var $table, $index;
-
-       /**
-        * @param $table
-        * @param int $index
-        */
-       function __construct( $table, $index = 0 ) {
-               $this->table = $table;
-               $this->index = $index;
-       }
-
-       /**
-        * @param array $matches
-        * @return mixed
-        */
-       function replace( $matches ) {
-               return $this->table[$matches[$this->index]];
-       }
-}
-
-/**
- * Replacement array for FSS with fallback to strtr()
- * Supports lazy initialisation of FSS resource
- */
-class ReplacementArray {
-       /*mostly private*/ var $data = false;
-       /*mostly private*/ var $fss = false;
-
-       /**
-        * Create an object with the specified replacement array
-        * The array should have the same form as the replacement array for strtr()
-        * @param array $data
-        */
-       function __construct( $data = array() ) {
-               $this->data = $data;
-       }
-
-       /**
-        * @return array
-        */
-       function __sleep() {
-               return array( 'data' );
-       }
-
-       function __wakeup() {
-               $this->fss = false;
-       }
-
-       /**
-        * Set the whole replacement array at once
-        * @param array $data
-        */
-       function setArray( $data ) {
-               $this->data = $data;
-               $this->fss = false;
-       }
-
-       /**
-        * @return array|bool
-        */
-       function getArray() {
-               return $this->data;
-       }
-
-       /**
-        * Set an element of the replacement array
-        * @param string $from
-        * @param string $to
-        */
-       function setPair( $from, $to ) {
-               $this->data[$from] = $to;
-               $this->fss = false;
-       }
-
-       /**
-        * @param array $data
-        */
-       function mergeArray( $data ) {
-               $this->data = array_merge( $this->data, $data );
-               $this->fss = false;
-       }
-
-       /**
-        * @param ReplacementArray $other
-        */
-       function merge( $other ) {
-               $this->data = array_merge( $this->data, $other->data );
-               $this->fss = false;
-       }
-
-       /**
-        * @param string $from
-        */
-       function removePair( $from ) {
-               unset( $this->data[$from] );
-               $this->fss = false;
-       }
-
-       /**
-        * @param array $data
-        */
-       function removeArray( $data ) {
-               foreach ( $data as $from => $to ) {
-                       $this->removePair( $from );
-               }
-               $this->fss = false;
-       }
-
-       /**
-        * @param string $subject
-        * @return string
-        */
-       function replace( $subject ) {
-               if ( function_exists( 'fss_prep_replace' ) ) {
-                       wfProfileIn( __METHOD__ . '-fss' );
-                       if ( $this->fss === false ) {
-                               $this->fss = fss_prep_replace( $this->data );
-                       }
-                       $result = fss_exec_replace( $this->fss, $subject );
-                       wfProfileOut( __METHOD__ . '-fss' );
-               } else {
-                       wfProfileIn( __METHOD__ . '-strtr' );
-                       $result = strtr( $subject, $this->data );
-                       wfProfileOut( __METHOD__ . '-strtr' );
-               }
-               return $result;
-       }
-}
-
-/**
- * An iterator which works exactly like:
- *
- * foreach ( explode( $delim, $s ) as $element ) {
- *    ...
- * }
- *
- * Except it doesn't use 193 byte per element
- */
-class ExplodeIterator implements Iterator {
-       // The subject string
-       var $subject, $subjectLength;
-
-       // The delimiter
-       var $delim, $delimLength;
-
-       // The position of the start of the line
-       var $curPos;
-
-       // The position after the end of the next delimiter
-       var $endPos;
-
-       // The current token
-       var $current;
-
-       /**
-        * Construct a DelimIterator
-        * @param string $delim
-        * @param string $subject
-        */
-       function __construct( $delim, $subject ) {
-               $this->subject = $subject;
-               $this->delim = $delim;
-
-               // Micro-optimisation (theoretical)
-               $this->subjectLength = strlen( $subject );
-               $this->delimLength = strlen( $delim );
-
-               $this->rewind();
-       }
-
-       function rewind() {
-               $this->curPos = 0;
-               $this->endPos = strpos( $this->subject, $this->delim );
-               $this->refreshCurrent();
-       }
-
-       function refreshCurrent() {
-               if ( $this->curPos === false ) {
-                       $this->current = false;
-               } elseif ( $this->curPos >= $this->subjectLength ) {
-                       $this->current = '';
-               } elseif ( $this->endPos === false ) {
-                       $this->current = substr( $this->subject, $this->curPos );
-               } else {
-                       $this->current = substr( $this->subject, $this->curPos, $this->endPos - $this->curPos );
-               }
-       }
-
-       function current() {
-               return $this->current;
-       }
-
-       /**
-        * @return int|bool Current position or boolean false if invalid
-        */
-       function key() {
-               return $this->curPos;
-       }
-
-       /**
-        * @return string
-        */
-       function next() {
-               if ( $this->endPos === false ) {
-                       $this->curPos = false;
-               } else {
-                       $this->curPos = $this->endPos + $this->delimLength;
-                       if ( $this->curPos >= $this->subjectLength ) {
-                               $this->endPos = false;
-                       } else {
-                               $this->endPos = strpos( $this->subject, $this->delim, $this->curPos );
-                       }
-               }
-               $this->refreshCurrent();
-               return $this->current;
-       }
-
-       /**
-        * @return bool
-        */
-       function valid() {
-               return $this->curPos !== false;
-       }
-}
index 56e9b44..21872e4 100644 (file)
@@ -2458,31 +2458,31 @@ class Title {
        }
 
        /**
-        * Is this page "semi-protected" - the *only* protection is autoconfirm?
+        * Is this page "semi-protected" - the *only* protection levels are listed
+        * in $wgSemiprotectedRestrictionLevels?
         *
         * @param string $action Action to check (default: edit)
         * @return Bool
         */
        public function isSemiProtected( $action = 'edit' ) {
-               if ( $this->exists() ) {
-                       $restrictions = $this->getRestrictions( $action );
-                       if ( count( $restrictions ) > 0 ) {
-                               foreach ( $restrictions as $restriction ) {
-                                       if ( strtolower( $restriction ) != 'editsemiprotected' &&
-                                               strtolower( $restriction ) != 'autoconfirmed' // BC
-                                       ) {
-                                               return false;
-                                       }
-                               }
-                       } else {
-                               # Not protected
-                               return false;
-                       }
-                       return true;
-               } else {
-                       # If it doesn't exist, it can't be protected
+               global $wgSemiprotectedRestrictionLevels;
+
+               $restrictions = $this->getRestrictions( $action );
+               $semi = $wgSemiprotectedRestrictionLevels;
+               if ( !$restrictions || !$semi ) {
+                       // Not protected, or all protection is full protection
                        return false;
                }
+
+               // Remap autoconfirmed to editsemiprotected for BC
+               foreach ( array_keys( $semi, 'autoconfirmed' ) as $key ) {
+                       $semi[$key] = 'editsemiprotected';
+               }
+               foreach ( array_keys( $restrictions, 'autoconfirmed' ) as $key ) {
+                       $restrictions[$key] = 'editsemiprotected';
+               }
+
+               return !array_diff( $restrictions, $semi );
        }
 
        /**
@@ -3903,7 +3903,12 @@ class Title {
                        __METHOD__
                );
 
-               $this->resetArticleID( 0 );
+               // clean up the old title before reset article id - bug 45348
+               if ( !$redirectContent ) {
+                       WikiPage::onArticleDelete( $this );
+               }
+
+               $this->resetArticleID( 0 ); // 0 == non existing
                $nt->resetArticleID( $oldid );
                $newpage->loadPageData( WikiPage::READ_LOCKING ); // bug 46397
 
@@ -3919,13 +3924,12 @@ class Title {
                }
 
                # Recreate the redirect, this time in the other direction.
-               if ( !$redirectContent ) {
-                       WikiPage::onArticleDelete( $this );
-               } else {
+               if ( $redirectContent ) {
                        $redirectArticle = WikiPage::factory( $this );
                        $redirectArticle->loadFromRow( false, WikiPage::READ_LOCKING ); // bug 46397
                        $newid = $redirectArticle->insertOn( $dbw );
                        if ( $newid ) { // sanity
+                               $this->resetArticleID( $newid );
                                $redirectRevision = new Revision( array(
                                        'title' => $this, // for determining the default content model
                                        'page' => $newid,
diff --git a/includes/UIDGenerator.php b/includes/UIDGenerator.php
deleted file mode 100644 (file)
index 963e51a..0000000
+++ /dev/null
@@ -1,337 +0,0 @@
-<?php
-/**
- * This file deals with UID generation.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- * @author Aaron Schulz
- */
-
-/**
- * Class for getting statistically unique IDs
- *
- * @since 1.21
- */
-class UIDGenerator {
-       /** @var UIDGenerator */
-       protected static $instance = null;
-
-       protected $nodeId32; // string; node ID in binary (32 bits)
-       protected $nodeId48; // string; node ID in binary (48 bits)
-
-       protected $lockFile88; // string; local file path
-       protected $lockFile128; // string; local file path
-
-       /** @var Array */
-       protected $fileHandles = array(); // cache file handles
-
-       const QUICK_RAND = 1; // get randomness from fast and insecure sources
-
-       protected function __construct() {
-               $idFile = wfTempDir() . '/mw-' . __CLASS__ . '-UID-nodeid';
-               $nodeId = is_file( $idFile ) ? file_get_contents( $idFile ) : '';
-               // Try to get some ID that uniquely identifies this machine (RFC 4122)...
-               if ( !preg_match( '/^[0-9a-f]{12}$/i', $nodeId ) ) {
-                       wfSuppressWarnings();
-                       if ( wfIsWindows() ) {
-                               // http://technet.microsoft.com/en-us/library/bb490913.aspx
-                               $csv = trim( wfShellExec( 'getmac /NH /FO CSV' ) );
-                               $line = substr( $csv, 0, strcspn( $csv, "\n" ) );
-                               $info = str_getcsv( $line );
-                               $nodeId = isset( $info[0] ) ? str_replace( '-', '', $info[0] ) : '';
-                       } elseif ( is_executable( '/sbin/ifconfig' ) ) { // Linux/BSD/Solaris/OS X
-                               // See http://linux.die.net/man/8/ifconfig
-                               $m = array();
-                               preg_match( '/\s([0-9a-f]{2}(:[0-9a-f]{2}){5})\s/',
-                                       wfShellExec( '/sbin/ifconfig -a' ), $m );
-                               $nodeId = isset( $m[1] ) ? str_replace( ':', '', $m[1] ) : '';
-                       }
-                       wfRestoreWarnings();
-                       if ( !preg_match( '/^[0-9a-f]{12}$/i', $nodeId ) ) {
-                               $nodeId = MWCryptRand::generateHex( 12, true );
-                               $nodeId[1] = dechex( hexdec( $nodeId[1] ) | 0x1 ); // set multicast bit
-                       }
-                       file_put_contents( $idFile, $nodeId ); // cache
-               }
-               $this->nodeId32 = wfBaseConvert( substr( sha1( $nodeId ), 0, 8 ), 16, 2, 32 );
-               $this->nodeId48 = wfBaseConvert( $nodeId, 16, 2, 48 );
-               // If different processes run as different users, they may have different temp dirs.
-               // This is dealt with by initializing the clock sequence number and counters randomly.
-               $this->lockFile88 = wfTempDir() . '/mw-' . __CLASS__ . '-UID-88';
-               $this->lockFile128 = wfTempDir() . '/mw-' . __CLASS__ . '-UID-128';
-       }
-
-       /**
-        * @return UIDGenerator
-        */
-       protected static function singleton() {
-               if ( self::$instance === null ) {
-                       self::$instance = new self();
-               }
-               return self::$instance;
-       }
-
-       /**
-        * Get a statistically unique 88-bit unsigned integer ID string.
-        * The bits of the UID are prefixed with the time (down to the millisecond).
-        *
-        * These IDs are suitable as values for the shard key of distributed data.
-        * If a column uses these as values, it should be declared UNIQUE to handle collisions.
-        * New rows almost always have higher UIDs, which makes B-TREE updates on INSERT fast.
-        * They can also be stored "DECIMAL(27) UNSIGNED" or BINARY(11) in MySQL.
-        *
-        * UID generation is serialized on each server (as the node ID is for the whole machine).
-        *
-        * @param $base integer Specifies a base other than 10
-        * @return string Number
-        * @throws MWException
-        */
-       public static function newTimestampedUID88( $base = 10 ) {
-               if ( !is_integer( $base ) || $base > 36 || $base < 2 ) {
-                       throw new MWException( "Base must an integer be between 2 and 36" );
-               }
-               $gen = self::singleton();
-               $time = $gen->getTimestampAndDelay( 'lockFile88', 1, 1024 );
-               return wfBaseConvert( $gen->getTimestampedID88( $time ), 2, $base );
-       }
-
-       /**
-        * @param array $time (UIDGenerator::millitime(), clock sequence)
-        * @return string 88 bits
-        */
-       protected function getTimestampedID88( array $info ) {
-               list( $time, $counter ) = $info;
-               // Take the 46 MSBs of "milliseconds since epoch"
-               $id_bin = $this->millisecondsSinceEpochBinary( $time );
-               // Add a 10 bit counter resulting in 56 bits total
-               $id_bin .= str_pad( decbin( $counter ), 10, '0', STR_PAD_LEFT );
-               // Add the 32 bit node ID resulting in 88 bits total
-               $id_bin .= $this->nodeId32;
-               // Convert to a 1-27 digit integer string
-               if ( strlen( $id_bin ) !== 88 ) {
-                       throw new MWException( "Detected overflow for millisecond timestamp." );
-               }
-               return $id_bin;
-       }
-
-       /**
-        * Get a statistically unique 128-bit unsigned integer ID string.
-        * The bits of the UID are prefixed with the time (down to the millisecond).
-        *
-        * These IDs are suitable as globally unique IDs, without any enforced uniqueness.
-        * New rows almost always have higher UIDs, which makes B-TREE updates on INSERT fast.
-        * They can also be stored as "DECIMAL(39) UNSIGNED" or BINARY(16) in MySQL.
-        *
-        * UID generation is serialized on each server (as the node ID is for the whole machine).
-        *
-        * @param $base integer Specifies a base other than 10
-        * @return string Number
-        * @throws MWException
-        */
-       public static function newTimestampedUID128( $base = 10 ) {
-               if ( !is_integer( $base ) || $base > 36 || $base < 2 ) {
-                       throw new MWException( "Base must be an integer between 2 and 36" );
-               }
-               $gen = self::singleton();
-               $time = $gen->getTimestampAndDelay( 'lockFile128', 16384, 1048576 );
-               return wfBaseConvert( $gen->getTimestampedID128( $time ), 2, $base );
-       }
-
-       /**
-        * @param array $info (UIDGenerator::millitime(), counter, clock sequence)
-        * @return string 128 bits
-        */
-       protected function getTimestampedID128( array $info ) {
-               list( $time, $counter, $clkSeq ) = $info;
-               // Take the 46 MSBs of "milliseconds since epoch"
-               $id_bin = $this->millisecondsSinceEpochBinary( $time );
-               // Add a 20 bit counter resulting in 66 bits total
-               $id_bin .= str_pad( decbin( $counter ), 20, '0', STR_PAD_LEFT );
-               // Add a 14 bit clock sequence number resulting in 80 bits total
-               $id_bin .= str_pad( decbin( $clkSeq ), 14, '0', STR_PAD_LEFT );
-               // Add the 48 bit node ID resulting in 128 bits total
-               $id_bin .= $this->nodeId48;
-               // Convert to a 1-39 digit integer string
-               if ( strlen( $id_bin ) !== 128 ) {
-                       throw new MWException( "Detected overflow for millisecond timestamp." );
-               }
-               return $id_bin;
-       }
-
-       /**
-        * Return an RFC4122 compliant v4 UUID
-        *
-        * @param $flags integer Bitfield (supports UIDGenerator::QUICK_RAND)
-        * @return string
-        * @throws MWException
-        */
-       public static function newUUIDv4( $flags = 0 ) {
-               $hex = ( $flags & self::QUICK_RAND )
-                       ? wfRandomString( 31 )
-                       : MWCryptRand::generateHex( 31 );
-
-               return sprintf( '%s-%s-%s-%s-%s',
-                       // "time_low" (32 bits)
-                       substr( $hex, 0, 8 ),
-                       // "time_mid" (16 bits)
-                       substr( $hex, 8, 4 ),
-                       // "time_hi_and_version" (16 bits)
-                       '4' . substr( $hex, 12, 3 ),
-                       // "clk_seq_hi_res (8 bits, variant is binary 10x) and "clk_seq_low" (8 bits)
-                       dechex( 0x8 | ( hexdec( $hex[15] ) & 0x3 ) ) . $hex[16] . substr( $hex, 17, 2 ),
-                       // "node" (48 bits)
-                       substr( $hex, 19, 12 )
-               );
-       }
-
-       /**
-        * Return an RFC4122 compliant v4 UUID
-        *
-        * @param $flags integer Bitfield (supports UIDGenerator::QUICK_RAND)
-        * @return string 32 hex characters with no hyphens
-        * @throws MWException
-        */
-       public static function newRawUUIDv4( $flags = 0 ) {
-               return str_replace( '-', '', self::newUUIDv4( $flags ) );
-       }
-
-       /**
-        * Get a (time,counter,clock sequence) where (time,counter) is higher
-        * than any previous (time,counter) value for the given clock sequence.
-        * This is useful for making UIDs sequential on a per-node bases.
-        *
-        * @param string $lockFile Name of a local lock file
-        * @param $clockSeqSize integer The number of possible clock sequence values
-        * @param $counterSize integer The number of possible counter values
-        * @return Array (result of UIDGenerator::millitime(), counter, clock sequence)
-        * @throws MWException
-        */
-       protected function getTimestampAndDelay( $lockFile, $clockSeqSize, $counterSize ) {
-               // Get the UID lock file handle
-               if ( isset( $this->fileHandles[$lockFile] ) ) {
-                       $handle = $this->fileHandles[$lockFile];
-               } else {
-                       $handle = fopen( $this->$lockFile, 'cb+' );
-                       $this->fileHandles[$lockFile] = $handle ?: null; // cache
-               }
-               // Acquire the UID lock file
-               if ( $handle === false ) {
-                       throw new MWException( "Could not open '{$this->$lockFile}'." );
-               } elseif ( !flock( $handle, LOCK_EX ) ) {
-                       throw new MWException( "Could not acquire '{$this->$lockFile}'." );
-               }
-               // Get the current timestamp, clock sequence number, last time, and counter
-               rewind( $handle );
-               $data = explode( ' ', fgets( $handle ) ); // "<clk seq> <sec> <msec> <counter> <offset>"
-               $clockChanged = false; // clock set back significantly?
-               if ( count( $data ) == 5 ) { // last UID info already initialized
-                       $clkSeq = (int)$data[0] % $clockSeqSize;
-                       $prevTime = array( (int)$data[1], (int)$data[2] );
-                       $offset = (int)$data[4] % $counterSize; // random counter offset
-                       $counter = 0; // counter for UIDs with the same timestamp
-                       // Delay until the clock reaches the time of the last ID.
-                       // This detects any microtime() drift among processes.
-                       $time = $this->timeWaitUntil( $prevTime );
-                       if ( !$time ) { // too long to delay?
-                               $clockChanged = true; // bump clock sequence number
-                               $time = self::millitime();
-                       } elseif ( $time == $prevTime ) {
-                               // Bump the counter if there are timestamp collisions
-                               $counter = (int)$data[3] % $counterSize;
-                               if ( ++$counter >= $counterSize ) { // sanity (starts at 0)
-                                       flock( $handle, LOCK_UN ); // abort
-                                       throw new MWException( "Counter overflow for timestamp value." );
-                               }
-                       }
-               } else { // last UID info not initialized
-                       $clkSeq = mt_rand( 0, $clockSeqSize - 1 );
-                       $counter = 0;
-                       $offset = mt_rand( 0, $counterSize - 1 );
-                       $time = self::millitime();
-               }
-               // microtime() and gettimeofday() can drift from time() at least on Windows.
-               // The drift is immediate for processes running while the system clock changes.
-               // time() does not have this problem. See https://bugs.php.net/bug.php?id=42659.
-               if ( abs( time() - $time[0] ) >= 2 ) {
-                       // We don't want processes using too high or low timestamps to avoid duplicate
-                       // UIDs and clock sequence number churn. This process should just be restarted.
-                       flock( $handle, LOCK_UN ); // abort
-                       throw new MWException( "Process clock is outdated or drifted." );
-               }
-               // If microtime() is synced and a clock change was detected, then the clock went back
-               if ( $clockChanged ) {
-                       // Bump the clock sequence number and also randomize the counter offset,
-                       // which is useful for UIDs that do not include the clock sequence number.
-                       $clkSeq = ( $clkSeq + 1 ) % $clockSeqSize;
-                       $offset = mt_rand( 0, $counterSize - 1 );
-                       trigger_error( "Clock was set back; sequence number incremented." );
-               }
-               // Update the (clock sequence number, timestamp, counter)
-               ftruncate( $handle, 0 );
-               rewind( $handle );
-               fwrite( $handle, "{$clkSeq} {$time[0]} {$time[1]} {$counter} {$offset}" );
-               fflush( $handle );
-               // Release the UID lock file
-               flock( $handle, LOCK_UN );
-
-               return array( $time, ( $counter + $offset ) % $counterSize, $clkSeq );
-       }
-
-       /**
-        * Wait till the current timestamp reaches $time and return the current
-        * timestamp. This returns false if it would have to wait more than 10ms.
-        *
-        * @param array $time Result of UIDGenerator::millitime()
-        * @return Array|bool UIDGenerator::millitime() result or false
-        */
-       protected function timeWaitUntil( array $time ) {
-               do {
-                       $ct = self::millitime();
-                       if ( $ct >= $time ) { // http://php.net/manual/en/language.operators.comparison.php
-                               return $ct; // current timestamp is higher than $time
-                       }
-               } while ( ( ( $time[0] - $ct[0] ) * 1000 + ( $time[1] - $ct[1] ) ) <= 10 );
-
-               return false;
-       }
-
-       /**
-        * @param array $time Result of UIDGenerator::millitime()
-        * @return string 46 MSBs of "milliseconds since epoch" in binary (rolls over in 4201)
-        */
-       protected function millisecondsSinceEpochBinary( array $time ) {
-               list( $sec, $msec ) = $time;
-               $ts = 1000 * $sec + $msec;
-               if ( $ts > pow( 2, 52 ) ) {
-                       throw new MWException( __METHOD__ .
-                               ': sorry, this function doesn\'t work after the year 144680' );
-               }
-               return substr( wfBaseConvert( $ts, 10, 2, 46 ), -46 );
-       }
-
-       /**
-        * @return Array (current time in seconds, milliseconds since then)
-        */
-       protected static function millitime() {
-               list( $msec, $sec ) = explode( ' ', microtime() );
-               return array( (int)$sec, (int)( $msec * 1000 ) );
-       }
-
-       function __destruct() {
-               array_map( 'fclose', $this->fileHandles );
-       }
-}
index 12912e1..c86b966 100644 (file)
@@ -3021,8 +3021,9 @@ class User {
         * the next change of the page if it's watched etc.
         * @note If the user doesn't have 'editmywatchlist', this will do nothing.
         * @param $title Title of the article to look at
+        * @param int $oldid The revision id being viewed. If not given or 0, latest revision is assumed.
         */
-       public function clearNotification( &$title ) {
+       public function clearNotification( &$title, $oldid = 0 ) {
                global $wgUseEnotif, $wgShowUpdatedMarker;
 
                // Do nothing if the database is locked to writes
@@ -3035,12 +3036,25 @@ class User {
                        return;
                }
 
-               if ( $title->getNamespace() == NS_USER_TALK &&
-                       $title->getText() == $this->getName() ) {
-                       if ( !wfRunHooks( 'UserClearNewTalkNotification', array( &$this ) ) ) {
+               // If we're working on user's talk page, we should update the talk page message indicator
+               if ( $title->getNamespace() == NS_USER_TALK && $title->getText() == $this->getName() ) {
+                       if ( !wfRunHooks( 'UserClearNewTalkNotification', array( &$this, $oldid ) ) ) {
                                return;
                        }
-                       $this->setNewtalk( false );
+
+                       $nextid = $oldid ? $title->getNextRevisionID( $oldid ) : null;
+
+                       if ( !$oldid || !$nextid ) {
+                               // If we're looking at the latest revision, we should definitely clear it
+                               $this->setNewtalk( false );
+                       } else {
+                               // Otherwise we should update its revision, if it's present
+                               if ( $this->getNewtalk() ) {
+                                       // Naturally the other one won't clear by itself
+                                       $this->setNewtalk( false );
+                                       $this->setNewtalk( true, Revision::newFromId( $nextid ) );
+                               }
+                       }
                }
 
                if ( !$wgUseEnotif && !$wgShowUpdatedMarker ) {
@@ -3063,7 +3077,7 @@ class User {
                        $force = 'force';
                }
 
-               $this->getWatchedItem( $title )->resetNotificationTimestamp( $force );
+               $this->getWatchedItem( $title )->resetNotificationTimestamp( $force, $oldid );
        }
 
        /**
@@ -3091,14 +3105,12 @@ class User {
                if ( $id != 0 ) {
                        $dbw = wfGetDB( DB_MASTER );
                        $dbw->update( 'watchlist',
-                               array( /* SET */
-                                       'wl_notificationtimestamp' => null
-                               ), array( /* WHERE */
-                                       'wl_user' => $id
-                               ), __METHOD__
+                               array( /* SET */ 'wl_notificationtimestamp' => null ),
+                               array( /* WHERE */ 'wl_user' => $id ),
+                               __METHOD__
                        );
-               #       We also need to clear here the "you have new message" notification for the own user_talk page
-               #       This is cleared one page view later in Article::viewUpdates();
+                       // We also need to clear here the "you have new message" notification for the own user_talk page;
+                       // it's cleared one page view later in WikiPage::doViewUpdates().
                }
        }
 
diff --git a/includes/ViewCountUpdate.php b/includes/ViewCountUpdate.php
deleted file mode 100644 (file)
index 22a4649..0000000
+++ /dev/null
@@ -1,105 +0,0 @@
-<?php
-/**
- * Update for the 'page_counter' field
- *
- * 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
- */
-
-/**
- * Update for the 'page_counter' field, when $wgDisableCounters is false.
- *
- * Depending on $wgHitcounterUpdateFreq, this will directly increment the
- * 'page_counter' field or use the 'hitcounter' table and then collect the data
- * from that table to update the 'page_counter' field in a batch operation.
- */
-class ViewCountUpdate implements DeferrableUpdate {
-       protected $id;
-
-       /**
-        * Constructor
-        *
-        * @param $id Integer: page ID to increment the view count
-        */
-       public function __construct( $id ) {
-               $this->id = intval( $id );
-       }
-
-       /**
-        * Run the update
-        */
-       public function doUpdate() {
-               global $wgHitcounterUpdateFreq;
-
-               $dbw = wfGetDB( DB_MASTER );
-
-               if ( $wgHitcounterUpdateFreq <= 1 || $dbw->getType() == 'sqlite' ) {
-                       $dbw->update( 'page', array( 'page_counter = page_counter + 1' ), array( 'page_id' => $this->id ), __METHOD__ );
-                       return;
-               }
-
-               # Not important enough to warrant an error page in case of failure
-               try {
-                       $dbw->insert( 'hitcounter', array( 'hc_id' => $this->id ), __METHOD__ );
-                       $checkfreq = intval( $wgHitcounterUpdateFreq / 25 + 1 );
-                       if ( rand() % $checkfreq == 0 && $dbw->lastErrno() == 0 ) {
-                               $this->collect();
-                       }
-               } catch ( DBError $e ) {}
-       }
-
-       protected function collect() {
-               global $wgHitcounterUpdateFreq;
-
-               $dbw = wfGetDB( DB_MASTER );
-
-               $rown = $dbw->selectField( 'hitcounter', 'COUNT(*)', array(), __METHOD__ );
-
-               if ( $rown < $wgHitcounterUpdateFreq ) {
-                       return;
-               }
-
-               wfProfileIn( __METHOD__ . '-collect' );
-               $old_user_abort = ignore_user_abort( true );
-
-               $dbw->lockTables( array(), array( 'hitcounter' ), __METHOD__, false );
-
-               $dbType = $dbw->getType();
-               $tabletype = $dbType == 'mysql' ? "ENGINE=HEAP " : '';
-               $hitcounterTable = $dbw->tableName( 'hitcounter' );
-               $acchitsTable = $dbw->tableName( 'acchits' );
-               $pageTable = $dbw->tableName( 'page' );
-
-               $dbw->query( "CREATE TEMPORARY TABLE $acchitsTable $tabletype AS " .
-                       "SELECT hc_id,COUNT(*) AS hc_n FROM $hitcounterTable " .
-                       'GROUP BY hc_id', __METHOD__ );
-               $dbw->delete( 'hitcounter', '*', __METHOD__ );
-               $dbw->unlockTables( __METHOD__ );
-
-               if ( $dbType == 'mysql' ) {
-                       $dbw->query( "UPDATE $pageTable,$acchitsTable SET page_counter=page_counter + hc_n " .
-                               'WHERE page_id = hc_id', __METHOD__ );
-               } else {
-                       $dbw->query( "UPDATE $pageTable SET page_counter=page_counter + hc_n " .
-                               "FROM $acchitsTable WHERE page_id = hc_id", __METHOD__ );
-               }
-               $dbw->query( "DROP TABLE $acchitsTable", __METHOD__ );
-
-               ignore_user_abort( $old_user_abort );
-               wfProfileOut( __METHOD__ . '-collect' );
-       }
-}
index 1e07e7c..d2fb468 100644 (file)
@@ -173,8 +173,9 @@ class WatchedItem {
         *
         * @param $force Whether to force the write query to be executed even if the
         *        page is not watched or the notification timestamp is already NULL.
+        * @param int $oldid The revision id being viewed. If not given or 0, latest revision is assumed.
         */
-       public function resetNotificationTimestamp( $force = '' ) {
+       public function resetNotificationTimestamp( $force = '', $oldid = 0 ) {
                // Only loggedin user can have a watchlist
                if ( wfReadOnly() || $this->mUser->isAnon() || !$this->isAllowed( 'editmywatchlist' ) ) {
                        return;
@@ -187,10 +188,50 @@ class WatchedItem {
                        }
                }
 
+               $title = $this->getTitle();
+               if ( !$oldid ) {
+                       // No oldid given, assuming latest revision; clear the timestamp.
+                       $notificationTimestamp = null;
+               } elseif ( !$title->getNextRevisionID( $oldid ) ) {
+                       // Oldid given and is the latest revision for this title; clear the timestamp.
+                       $notificationTimestamp = null;
+               } else {
+                       // See if the version marked as read is more recent than the one we're viewing.
+                       // Call load() if it wasn't called before due to $force.
+                       $this->load();
+
+                       if ( $this->timestamp === null ) {
+                               // This can only happen if $force is enabled.
+                               $notificationTimestamp = null;
+                       } else {
+                               // Oldid given and isn't the latest; update the timestamp.
+                               // This will result in no further notification emails being sent!
+                               $dbr = wfGetDB( DB_SLAVE );
+                               $notificationTimestamp = $dbr->selectField(
+                                       'revision', 'rev_timestamp',
+                                       array( 'rev_page' => $title->getArticleID(), 'rev_id' => $oldid )
+                               );
+                               // We need to go one second to the future because of various strict comparisons
+                               // throughout the codebase
+                               $ts = new MWTimestamp( $notificationTimestamp );
+                               $ts->timestamp->add( new DateInterval( 'PT1S' ) );
+                               $notificationTimestamp = $ts->getTimestamp( TS_MW );
+
+                               if ( $notificationTimestamp < $this->timestamp ) {
+                                       if ( $force != 'force' ) {
+                                               return;
+                                       } else {
+                                               // This is a little silly…
+                                               $notificationTimestamp = $this->timestamp;
+                                       }
+                               }
+                       }
+               }
+
                // If the page is watched by the user (or may be watched), update the timestamp on any
                // any matching rows
                $dbw = wfGetDB( DB_MASTER );
-               $dbw->update( 'watchlist', array( 'wl_notificationtimestamp' => null ),
+               $dbw->update( 'watchlist', array( 'wl_notificationtimestamp' => $notificationTimestamp ),
                        $this->dbCond(), __METHOD__ );
                $this->timestamp = null;
        }
index edfbba2..a690176 100644 (file)
@@ -302,13 +302,6 @@ class MediaWiki {
                        $article = $this->initializeArticle();
                        if ( is_object( $article ) ) {
                                $pageView = true;
-                               /**
-                                * $wgArticle is deprecated, do not use it.
-                                * @deprecated since 1.18
-                                */
-                               global $wgArticle;
-                               $wgArticle = new DeprecatedGlobal( 'wgArticle', $article, '1.18' );
-
                                $this->performAction( $article, $requestTitle );
                        } elseif ( is_string( $article ) ) {
                                $output->redirect( $article );
@@ -588,6 +581,7 @@ class MediaWiki {
                                                $cache->loadFromFileCache( $this->context );
                                        }
                                        // Do any stats increment/watchlist stuff
+                                       // Assume we're viewing the latest revision (this should always be the case with file cache)
                                        $this->context->getWikiPage()->doViewUpdates( $this->context->getUser() );
                                        // Tell OutputPage that output is taken care of
                                        $this->context->getOutput()->disable();
@@ -687,7 +681,7 @@ class MediaWiki {
                                // We don't want exceptions thrown during job execution to
                                // be reported to the user since the output is already sent.
                                // Instead we just log them.
-                               wfDebugLog( 'exception', $e->getLogMessage() );
+                               MWExceptionHandler::logException( $e );
                        }
                }
        }
index 1965982..6d2d80c 100644 (file)
@@ -1134,9 +1134,10 @@ class WikiPage implements Page, IDBAccessObject {
 
        /**
         * Do standard deferred updates after page view
-        * @param $user User The relevant user
+        * @param User $user The relevant user
+        * @param int $oldid The revision id being viewed. If not given or 0, latest revision is assumed.
         */
-       public function doViewUpdates( User $user ) {
+       public function doViewUpdates( User $user, $oldid = 0 ) {
                global $wgDisableCounters;
                if ( wfReadOnly() ) {
                        return;
@@ -1149,7 +1150,7 @@ class WikiPage implements Page, IDBAccessObject {
                }
 
                // Update newtalk / watchlist notification status
-               $user->clearNotification( $this->mTitle );
+               $user->clearNotification( $this->mTitle, $oldid );
        }
 
        /**
@@ -1457,7 +1458,7 @@ class WikiPage implements Page, IDBAccessObject {
        }
 
        /**
-        * Returns true iff this page's content model supports sections.
+        * Returns true if this page's content model supports sections.
         *
         * @return boolean whether sections are supported.
         *
diff --git a/includes/XmlTypeCheck.php b/includes/XmlTypeCheck.php
deleted file mode 100644 (file)
index 92ca7d8..0000000
+++ /dev/null
@@ -1,184 +0,0 @@
-<?php
-/**
- * XML syntax and type checker.
- *
- * 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 XmlTypeCheck {
-       /**
-        * Will be set to true or false to indicate whether the file is
-        * well-formed XML. Note that this doesn't check schema validity.
-        */
-       public $wellFormed = false;
-
-       /**
-        * Will be set to true if the optional element filter returned
-        * a match at some point.
-        */
-       public $filterMatch = false;
-
-       /**
-        * Name of the document's root element, including any namespace
-        * as an expanded URL.
-        */
-       public $rootElement = '';
-
-       /**
-        * @param string $input a filename or string containing the XML element
-        * @param callable $filterCallback (optional)
-        *        Function to call to do additional custom validity checks from the
-        *        SAX element handler event. This gives you access to the element
-        *        namespace, name, and attributes, but not to text contents.
-        *        Filter should return 'true' to toggle on $this->filterMatch
-        * @param boolean $isFile (optional) indicates if the first parameter is a
-        *        filename (default, true) or if it is a string (false)
-        */
-       function __construct( $input, $filterCallback = null, $isFile = true ) {
-               $this->filterCallback = $filterCallback;
-               if ( $isFile ) {
-                       $this->validateFromFile( $input );
-               } else {
-                       $this->validateFromString( $input );
-               }
-       }
-
-       /**
-        * Alternative constructor: from filename
-        *
-        * @param string $fname the filename of an XML document
-        * @param callable $filterCallback (optional)
-        *        Function to call to do additional custom validity checks from the
-        *        SAX element handler event. This gives you access to the element
-        *        namespace, name, and attributes, but not to text contents.
-        *        Filter should return 'true' to toggle on $this->filterMatch
-        * @return XmlTypeCheck
-        */
-       public static function newFromFilename( $fname, $filterCallback = null ) {
-               return new self( $fname, $filterCallback, true );
-       }
-
-       /**
-        * Alternative constructor: from string
-        *
-        * @param string $string a string containing an XML element
-        * @param callable $filterCallback (optional)
-        *        Function to call to do additional custom validity checks from the
-        *        SAX element handler event. This gives you access to the element
-        *        namespace, name, and attributes, but not to text contents.
-        *        Filter should return 'true' to toggle on $this->filterMatch
-        * @return XmlTypeCheck
-        */
-       public static function newFromString( $string, $filterCallback = null ) {
-               return new self( $string, $filterCallback, false );
-       }
-
-       /**
-        * Get the root element. Simple accessor to $rootElement
-        *
-        * @return string
-        */
-       public function getRootElement() {
-               return $this->rootElement;
-       }
-
-       /**
-        * Get an XML parser with the root element handler.
-        * @see XmlTypeCheck::rootElementOpen()
-        * @return resource a resource handle for the XML parser
-        */
-       private function getParser() {
-               $parser = xml_parser_create_ns( 'UTF-8' );
-               // case folding violates XML standard, turn it off
-               xml_parser_set_option( $parser, XML_OPTION_CASE_FOLDING, false );
-               xml_set_element_handler( $parser, array( $this, 'rootElementOpen' ), false );
-               return $parser;
-       }
-
-       /**
-        * @param string $fname the filename
-        */
-       private function validateFromFile( $fname ) {
-               $parser = $this->getParser();
-
-               if ( file_exists( $fname ) ) {
-                       $file = fopen( $fname, "rb" );
-                       if ( $file ) {
-                               do {
-                                       $chunk = fread( $file, 32768 );
-                                       $ret = xml_parse( $parser, $chunk, feof( $file ) );
-                                       if ( $ret == 0 ) {
-                                               $this->wellFormed = false;
-                                               fclose( $file );
-                                               xml_parser_free( $parser );
-                                               return;
-                                       }
-                               } while ( !feof( $file ) );
-
-                               fclose( $file );
-                       }
-               }
-               $this->wellFormed = true;
-
-               xml_parser_free( $parser );
-       }
-
-       /**
-        *
-        * @param string $string the XML-input-string to be checked.
-        */
-       private function validateFromString( $string ) {
-               $parser = $this->getParser();
-               $ret = xml_parse( $parser, $string, true );
-               xml_parser_free( $parser );
-               if ( $ret == 0 ) {
-                       $this->wellFormed = false;
-                       return;
-               }
-               $this->wellFormed = true;
-       }
-
-       /**
-        * @param $parser
-        * @param $name
-        * @param $attribs
-        */
-       private function rootElementOpen( $parser, $name, $attribs ) {
-               $this->rootElement = $name;
-
-               if ( is_callable( $this->filterCallback ) ) {
-                       xml_set_element_handler( $parser, array( $this, 'elementOpen' ), false );
-                       $this->elementOpen( $parser, $name, $attribs );
-               } else {
-                       // We only need the first open element
-                       xml_set_element_handler( $parser, false, false );
-               }
-       }
-
-       /**
-        * @param $parser
-        * @param $name
-        * @param $attribs
-        */
-       private function elementOpen( $parser, $name, $attribs ) {
-               if ( call_user_func( $this->filterCallback, $name, $attribs ) ) {
-                       // Filter hit!
-                       $this->filterMatch = true;
-               }
-       }
-}
diff --git a/includes/ZipDirectoryReader.php b/includes/ZipDirectoryReader.php
deleted file mode 100644 (file)
index 307efce..0000000
+++ /dev/null
@@ -1,712 +0,0 @@
-<?php
-/**
- * ZIP file directories reader, for the purposes of upload verification.
- *
- * 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
- */
-
-/**
- * A class for reading ZIP file directories, for the purposes of upload
- * verification.
- *
- * Only a functional interface is provided: ZipFileReader::read(). No access is
- * given to object instances.
- *
- */
-class ZipDirectoryReader {
-       /**
-        * Read a ZIP file and call a function for each file discovered in it.
-        *
-        * Because this class is aimed at verification, an error is raised on
-        * suspicious or ambiguous input, instead of emulating some standard
-        * behavior.
-        *
-        * @param string $fileName The archive file name
-        * @param array $callback The callback function. It will be called for each file
-        *   with a single associative array each time, with members:
-        *
-        *      - name: The file name. Directories conventionally have a trailing
-        *        slash.
-        *
-        *      - mtime: The file modification time, in MediaWiki 14-char format
-        *
-        *      - size: The uncompressed file size
-        *
-        * @param array $options An associative array of read options, with the option
-        *    name in the key. This may currently contain:
-        *
-        *      - zip64: If this is set to true, then we will emulate a
-        *        library with ZIP64 support, like OpenJDK 7. If it is set to
-        *        false, then we will emulate a library with no knowledge of
-        *        ZIP64.
-        *
-        *        NOTE: The ZIP64 code is untested and probably doesn't work. It
-        *        turned out to be easier to just reject ZIP64 archive uploads,
-        *        since they are likely to be very rare. Confirming safety of a
-        *        ZIP64 file is fairly complex. What do you do with a file that is
-        *        ambiguous and broken when read with a non-ZIP64 reader, but valid
-        *        when read with a ZIP64 reader? This situation is normal for a
-        *        valid ZIP64 file, and working out what non-ZIP64 readers will make
-        *        of such a file is not trivial.
-        *
-        * @return Status object. The following fatal errors are defined:
-        *
-        *      - zip-file-open-error: The file could not be opened.
-        *
-        *      - zip-wrong-format: The file does not appear to be a ZIP file.
-        *
-        *      - zip-bad: There was something wrong or ambiguous about the file
-        *        data.
-        *
-        *      - zip-unsupported: The ZIP file uses features which
-        *        ZipDirectoryReader does not support.
-        *
-        * The default messages for those fatal errors are written in a way that
-        * makes sense for upload verification.
-        *
-        * If a fatal error is returned, more information about the error will be
-        * available in the debug log.
-        *
-        * Note that the callback function may be called any number of times before
-        * a fatal error is returned. If this occurs, the data sent to the callback
-        * function should be discarded.
-        */
-       public static function read( $fileName, $callback, $options = array() ) {
-               $zdr = new self( $fileName, $callback, $options );
-               return $zdr->execute();
-       }
-
-       /** The file name */
-       var $fileName;
-
-       /** The opened file resource */
-       var $file;
-
-       /** The cached length of the file, or null if it has not been loaded yet. */
-       var $fileLength;
-
-       /** A segmented cache of the file contents */
-       var $buffer;
-
-       /** The file data callback */
-       var $callback;
-
-       /** The ZIP64 mode */
-       var $zip64 = false;
-
-       /** Stored headers */
-       var $eocdr, $eocdr64, $eocdr64Locator;
-
-       var $data;
-
-       /** The "extra field" ID for ZIP64 central directory entries */
-       const ZIP64_EXTRA_HEADER = 0x0001;
-
-       /** The segment size for the file contents cache */
-       const SEGSIZE = 16384;
-
-       /** The index of the "general field" bit for UTF-8 file names */
-       const GENERAL_UTF8 = 11;
-
-       /** The index of the "general field" bit for central directory encryption */
-       const GENERAL_CD_ENCRYPTED = 13;
-
-       /**
-        * Private constructor
-        */
-       protected function __construct( $fileName, $callback, $options ) {
-               $this->fileName = $fileName;
-               $this->callback = $callback;
-
-               if ( isset( $options['zip64'] ) ) {
-                       $this->zip64 = $options['zip64'];
-               }
-       }
-
-       /**
-        * Read the directory according to settings in $this.
-        *
-        * @return Status
-        */
-       function execute() {
-               $this->file = fopen( $this->fileName, 'r' );
-               $this->data = array();
-               if ( !$this->file ) {
-                       return Status::newFatal( 'zip-file-open-error' );
-               }
-
-               $status = Status::newGood();
-               try {
-                       $this->readEndOfCentralDirectoryRecord();
-                       if ( $this->zip64 ) {
-                               list( $offset, $size ) = $this->findZip64CentralDirectory();
-                               $this->readCentralDirectory( $offset, $size );
-                       } else {
-                               if ( $this->eocdr['CD size'] == 0xffffffff
-                                       || $this->eocdr['CD offset'] == 0xffffffff
-                                       || $this->eocdr['CD entries total'] == 0xffff )
-                               {
-                                       $this->error( 'zip-unsupported', 'Central directory header indicates ZIP64, ' .
-                                               'but we are in legacy mode. Rejecting this upload is necessary to avoid ' .
-                                               'opening vulnerabilities on clients using OpenJDK 7 or later.' );
-                               }
-
-                               list( $offset, $size ) = $this->findOldCentralDirectory();
-                               $this->readCentralDirectory( $offset, $size );
-                       }
-               } catch ( ZipDirectoryReaderError $e ) {
-                       $status->fatal( $e->getErrorCode() );
-               }
-
-               fclose( $this->file );
-               return $status;
-       }
-
-       /**
-        * Throw an error, and log a debug message
-        */
-       function error( $code, $debugMessage ) {
-               wfDebug( __CLASS__ . ": Fatal error: $debugMessage\n" );
-               throw new ZipDirectoryReaderError( $code );
-       }
-
-       /**
-        * Read the header which is at the end of the central directory,
-        * unimaginatively called the "end of central directory record" by the ZIP
-        * spec.
-        */
-       function readEndOfCentralDirectoryRecord() {
-               $info = array(
-                       'signature' => 4,
-                       'disk' => 2,
-                       'CD start disk' => 2,
-                       'CD entries this disk' => 2,
-                       'CD entries total' => 2,
-                       'CD size' => 4,
-                       'CD offset' => 4,
-                       'file comment length' => 2,
-               );
-               $structSize = $this->getStructSize( $info );
-               $startPos = $this->getFileLength() - 65536 - $structSize;
-               if ( $startPos < 0 ) {
-                       $startPos = 0;
-               }
-
-               $block = $this->getBlock( $startPos );
-               $sigPos = strrpos( $block, "PK\x05\x06" );
-               if ( $sigPos === false ) {
-                       $this->error( 'zip-wrong-format',
-                               "zip file lacks EOCDR signature. It probably isn't a zip file." );
-               }
-
-               $this->eocdr = $this->unpack( substr( $block, $sigPos ), $info );
-               $this->eocdr['EOCDR size'] = $structSize + $this->eocdr['file comment length'];
-
-               if ( $structSize + $this->eocdr['file comment length'] != strlen( $block ) - $sigPos ) {
-                       $this->error( 'zip-bad', 'trailing bytes after the end of the file comment' );
-               }
-               if ( $this->eocdr['disk'] !== 0
-                       || $this->eocdr['CD start disk'] !== 0 )
-               {
-                       $this->error( 'zip-unsupported', 'more than one disk (in EOCDR)' );
-               }
-               $this->eocdr += $this->unpack(
-                       $block,
-                       array( 'file comment' => array( 'string', $this->eocdr['file comment length'] ) ),
-                       $sigPos + $structSize );
-               $this->eocdr['position'] = $startPos + $sigPos;
-       }
-
-       /**
-        * Read the header called the "ZIP64 end of central directory locator". An
-        * error will be raised if it does not exist.
-        */
-       function readZip64EndOfCentralDirectoryLocator() {
-               $info = array(
-                       'signature' => array( 'string', 4 ),
-                       'eocdr64 start disk' => 4,
-                       'eocdr64 offset' => 8,
-                       'number of disks' => 4,
-               );
-               $structSize = $this->getStructSize( $info );
-
-               $block = $this->getBlock( $this->getFileLength() - $this->eocdr['EOCDR size']
-                       - $structSize, $structSize );
-               $this->eocdr64Locator = $data = $this->unpack( $block, $info );
-
-               if ( $data['signature'] !== "PK\x06\x07" ) {
-                       // Note: Java will allow this and continue to read the
-                       // EOCDR64, so we have to reject the upload, we can't
-                       // just use the EOCDR header instead.
-                       $this->error( 'zip-bad', 'wrong signature on Zip64 end of central directory locator' );
-               }
-       }
-
-       /**
-        * Read the header called the "ZIP64 end of central directory record". It
-        * may replace the regular "end of central directory record" in ZIP64 files.
-        */
-       function readZip64EndOfCentralDirectoryRecord() {
-               if ( $this->eocdr64Locator['eocdr64 start disk'] != 0
-                       || $this->eocdr64Locator['number of disks'] != 0 )
-               {
-                       $this->error( 'zip-unsupported', 'more than one disk (in EOCDR64 locator)' );
-               }
-
-               $info = array(
-                       'signature' => array( 'string', 4 ),
-                       'EOCDR64 size' => 8,
-                       'version made by' => 2,
-                       'version needed' => 2,
-                       'disk' => 4,
-                       'CD start disk' => 4,
-                       'CD entries this disk' => 8,
-                       'CD entries total' => 8,
-                       'CD size' => 8,
-                       'CD offset' => 8
-               );
-               $structSize = $this->getStructSize( $info );
-               $block = $this->getBlock( $this->eocdr64Locator['eocdr64 offset'], $structSize );
-               $this->eocdr64 = $data = $this->unpack( $block, $info );
-               if ( $data['signature'] !== "PK\x06\x06" ) {
-                       $this->error( 'zip-bad', 'wrong signature on Zip64 end of central directory record' );
-               }
-               if ( $data['disk'] !== 0
-                       || $data['CD start disk'] !== 0 )
-               {
-                       $this->error( 'zip-unsupported', 'more than one disk (in EOCDR64)' );
-               }
-       }
-
-       /**
-        * Find the location of the central directory, as would be seen by a
-        * non-ZIP64 reader.
-        *
-        * @return List containing offset, size and end position.
-        */
-       function findOldCentralDirectory() {
-               $size = $this->eocdr['CD size'];
-               $offset = $this->eocdr['CD offset'];
-               $endPos = $this->eocdr['position'];
-
-               // Some readers use the EOCDR position instead of the offset field
-               // to find the directory, so to be safe, we check if they both agree.
-               if ( $offset + $size != $endPos ) {
-                       $this->error( 'zip-bad', 'the central directory does not immediately precede the end ' .
-                               'of central directory record' );
-               }
-               return array( $offset, $size );
-       }
-
-       /**
-        * Find the location of the central directory, as would be seen by a
-        * ZIP64-compliant reader.
-        *
-        * @return array List containing offset, size and end position.
-        */
-       function findZip64CentralDirectory() {
-               // The spec is ambiguous about the exact rules of precedence between the
-               // ZIP64 headers and the original headers. Here we follow zip_util.c
-               // from OpenJDK 7.
-               $size = $this->eocdr['CD size'];
-               $offset = $this->eocdr['CD offset'];
-               $numEntries = $this->eocdr['CD entries total'];
-               $endPos = $this->eocdr['position'];
-               if ( $size == 0xffffffff
-                       || $offset == 0xffffffff
-                       || $numEntries == 0xffff )
-               {
-                       $this->readZip64EndOfCentralDirectoryLocator();
-
-                       if ( isset( $this->eocdr64Locator['eocdr64 offset'] ) ) {
-                               $this->readZip64EndOfCentralDirectoryRecord();
-                               if ( isset( $this->eocdr64['CD offset'] ) ) {
-                                       $size = $this->eocdr64['CD size'];
-                                       $offset = $this->eocdr64['CD offset'];
-                                       $endPos = $this->eocdr64Locator['eocdr64 offset'];
-                               }
-                       }
-               }
-               // Some readers use the EOCDR position instead of the offset field
-               // to find the directory, so to be safe, we check if they both agree.
-               if ( $offset + $size != $endPos ) {
-                       $this->error( 'zip-bad', 'the central directory does not immediately precede the end ' .
-                               'of central directory record' );
-               }
-               return array( $offset, $size );
-       }
-
-       /**
-        * Read the central directory at the given location
-        */
-       function readCentralDirectory( $offset, $size ) {
-               $block = $this->getBlock( $offset, $size );
-
-               $fixedInfo = array(
-                       'signature' => array( 'string', 4 ),
-                       'version made by' => 2,
-                       'version needed' => 2,
-                       'general bits' => 2,
-                       'compression method' => 2,
-                       'mod time' => 2,
-                       'mod date' => 2,
-                       'crc-32' => 4,
-                       'compressed size' => 4,
-                       'uncompressed size' => 4,
-                       'name length' => 2,
-                       'extra field length' => 2,
-                       'comment length' => 2,
-                       'disk number start' => 2,
-                       'internal attrs' => 2,
-                       'external attrs' => 4,
-                       'local header offset' => 4,
-               );
-               $fixedSize = $this->getStructSize( $fixedInfo );
-
-               $pos = 0;
-               while ( $pos < $size ) {
-                       $data = $this->unpack( $block, $fixedInfo, $pos );
-                       $pos += $fixedSize;
-
-                       if ( $data['signature'] !== "PK\x01\x02" ) {
-                               $this->error( 'zip-bad', 'Invalid signature found in directory entry' );
-                       }
-
-                       $variableInfo = array(
-                               'name' => array( 'string', $data['name length'] ),
-                               'extra field' => array( 'string', $data['extra field length'] ),
-                               'comment' => array( 'string', $data['comment length'] ),
-                       );
-                       $data += $this->unpack( $block, $variableInfo, $pos );
-                       $pos += $this->getStructSize( $variableInfo );
-
-                       if ( $this->zip64 && (
-                                  $data['compressed size'] == 0xffffffff
-                               || $data['uncompressed size'] == 0xffffffff
-                               || $data['local header offset'] == 0xffffffff ) )
-                       {
-                               $zip64Data = $this->unpackZip64Extra( $data['extra field'] );
-                               if ( $zip64Data ) {
-                                       $data = $zip64Data + $data;
-                               }
-                       }
-
-                       if ( $this->testBit( $data['general bits'], self::GENERAL_CD_ENCRYPTED ) ) {
-                               $this->error( 'zip-unsupported', 'central directory encryption is not supported' );
-                       }
-
-                       // Convert the timestamp into MediaWiki format
-                       // For the format, please see the MS-DOS 2.0 Programmer's Reference,
-                       // pages 3-5 and 3-6.
-                       $time = $data['mod time'];
-                       $date = $data['mod date'];
-
-                       $year = 1980 + ( $date >> 9 );
-                       $month = ( $date >> 5 ) & 15;
-                       $day = $date & 31;
-                       $hour = ( $time >> 11 ) & 31;
-                       $minute = ( $time >> 5 ) & 63;
-                       $second = ( $time & 31 ) * 2;
-                       $timestamp = sprintf( "%04d%02d%02d%02d%02d%02d",
-                               $year, $month, $day, $hour, $minute, $second );
-
-                       // Convert the character set in the file name
-                       if ( !function_exists( 'iconv' )
-                               || $this->testBit( $data['general bits'], self::GENERAL_UTF8 ) )
-                       {
-                               $name = $data['name'];
-                       } else {
-                               $name = iconv( 'CP437', 'UTF-8', $data['name'] );
-                       }
-
-                       // Compile a data array for the user, with a sensible format
-                       $userData = array(
-                               'name' => $name,
-                               'mtime' => $timestamp,
-                               'size' => $data['uncompressed size'],
-                       );
-                       call_user_func( $this->callback, $userData );
-               }
-       }
-
-       /**
-        * Interpret ZIP64 "extra field" data and return an associative array.
-        * @return array|bool
-        */
-       function unpackZip64Extra( $extraField ) {
-               $extraHeaderInfo = array(
-                       'id' => 2,
-                       'size' => 2,
-               );
-               $extraHeaderSize = $this->getStructSize( $extraHeaderInfo );
-
-               $zip64ExtraInfo = array(
-                       'uncompressed size' => 8,
-                       'compressed size' => 8,
-                       'local header offset' => 8,
-                       'disk number start' => 4,
-               );
-
-               $extraPos = 0;
-               while ( $extraPos < strlen( $extraField ) ) {
-                       $extra = $this->unpack( $extraField, $extraHeaderInfo, $extraPos );
-                       $extraPos += $extraHeaderSize;
-                       $extra += $this->unpack( $extraField,
-                               array( 'data' => array( 'string', $extra['size'] ) ),
-                               $extraPos );
-                       $extraPos += $extra['size'];
-
-                       if ( $extra['id'] == self::ZIP64_EXTRA_HEADER ) {
-                               return $this->unpack( $extra['data'], $zip64ExtraInfo );
-                       }
-               }
-
-               return false;
-       }
-
-       /**
-        * Get the length of the file.
-        */
-       function getFileLength() {
-               if ( $this->fileLength === null ) {
-                       $stat = fstat( $this->file );
-                       $this->fileLength = $stat['size'];
-               }
-               return $this->fileLength;
-       }
-
-       /**
-        * Get the file contents from a given offset. If there are not enough bytes
-        * in the file to satisfy the request, an exception will be thrown.
-        *
-        * @param int $start The byte offset of the start of the block.
-        * @param int $length The number of bytes to return. If omitted, the remainder
-        *    of the file will be returned.
-        *
-        * @return string
-        */
-       function getBlock( $start, $length = null ) {
-               $fileLength = $this->getFileLength();
-               if ( $start >= $fileLength ) {
-                       $this->error( 'zip-bad', "getBlock() requested position $start, " .
-                               "file length is $fileLength" );
-               }
-               if ( $length === null ) {
-                       $length = $fileLength - $start;
-               }
-               $end = $start + $length;
-               if ( $end > $fileLength ) {
-                       $this->error( 'zip-bad', "getBlock() requested end position $end, " .
-                               "file length is $fileLength" );
-               }
-               $startSeg = floor( $start / self::SEGSIZE );
-               $endSeg = ceil( $end / self::SEGSIZE );
-
-               $block = '';
-               for ( $segIndex = $startSeg; $segIndex <= $endSeg; $segIndex++ ) {
-                       $block .= $this->getSegment( $segIndex );
-               }
-
-               $block = substr( $block,
-                       $start - $startSeg * self::SEGSIZE,
-                       $length );
-
-               if ( strlen( $block ) < $length ) {
-                       $this->error( 'zip-bad', 'getBlock() returned an unexpectedly small amount of data' );
-               }
-
-               return $block;
-       }
-
-       /**
-        * Get a section of the file starting at position $segIndex * self::SEGSIZE,
-        * of length self::SEGSIZE. The result is cached. This is a helper function
-        * for getBlock().
-        *
-        * If there are not enough bytes in the file to satisfy the request, the
-        * return value will be truncated. If a request is made for a segment beyond
-        * the end of the file, an empty string will be returned.
-        * @return string
-        */
-       function getSegment( $segIndex ) {
-               if ( !isset( $this->buffer[$segIndex] ) ) {
-                       $bytePos = $segIndex * self::SEGSIZE;
-                       if ( $bytePos >= $this->getFileLength() ) {
-                               $this->buffer[$segIndex] = '';
-                               return '';
-                       }
-                       if ( fseek( $this->file, $bytePos ) ) {
-                               $this->error( 'zip-bad', "seek to $bytePos failed" );
-                       }
-                       $seg = fread( $this->file, self::SEGSIZE );
-                       if ( $seg === false ) {
-                               $this->error( 'zip-bad', "read from $bytePos failed" );
-                       }
-                       $this->buffer[$segIndex] = $seg;
-               }
-               return $this->buffer[$segIndex];
-       }
-
-       /**
-        * Get the size of a structure in bytes. See unpack() for the format of $struct.
-        * @return int
-        */
-       function getStructSize( $struct ) {
-               $size = 0;
-               foreach ( $struct as $type ) {
-                       if ( is_array( $type ) ) {
-                               list( , $fieldSize ) = $type;
-                               $size += $fieldSize;
-                       } else {
-                               $size += $type;
-                       }
-               }
-               return $size;
-       }
-
-       /**
-        * Unpack a binary structure. This is like the built-in unpack() function
-        * except nicer.
-        *
-        * @param string $string The binary data input
-        *
-        * @param array $struct An associative array giving structure members and their
-        *    types. In the key is the field name. The value may be either an
-        *    integer, in which case the field is a little-endian unsigned integer
-        *    encoded in the given number of bytes, or an array, in which case the
-        *    first element of the array is the type name, and the subsequent
-        *    elements are type-dependent parameters. Only one such type is defined:
-        *       - "string": The second array element gives the length of string.
-        *          Not null terminated.
-        *
-        * @param int $offset The offset into the string at which to start unpacking.
-        *
-        * @throws MWException
-        * @return array Unpacked associative array. Note that large integers in the input
-        *    may be represented as floating point numbers in the return value, so
-        *    the use of weak comparison is advised.
-        */
-       function unpack( $string, $struct, $offset = 0 ) {
-               $size = $this->getStructSize( $struct );
-               if ( $offset + $size > strlen( $string ) ) {
-                       $this->error( 'zip-bad', 'unpack() would run past the end of the supplied string' );
-               }
-
-               $data = array();
-               $pos = $offset;
-               foreach ( $struct as $key => $type ) {
-                       if ( is_array( $type ) ) {
-                               list( $typeName, $fieldSize ) = $type;
-                               switch ( $typeName ) {
-                               case 'string':
-                                       $data[$key] = substr( $string, $pos, $fieldSize );
-                                       $pos += $fieldSize;
-                                       break;
-                               default:
-                                       throw new MWException( __METHOD__ . ": invalid type \"$typeName\"" );
-                               }
-                       } else {
-                               // Unsigned little-endian integer
-                               $length = intval( $type );
-
-                               // Calculate the value. Use an algorithm which automatically
-                               // upgrades the value to floating point if necessary.
-                               $value = 0;
-                               for ( $i = $length - 1; $i >= 0; $i-- ) {
-                                       $value *= 256;
-                                       $value += ord( $string[$pos + $i] );
-                               }
-
-                               // Throw an exception if there was loss of precision
-                               if ( $value > pow( 2, 52 ) ) {
-                                       $this->error( 'zip-unsupported', 'number too large to be stored in a double. ' .
-                                               'This could happen if we tried to unpack a 64-bit structure ' .
-                                               'at an invalid location.' );
-                               }
-                               $data[$key] = $value;
-                               $pos += $length;
-                       }
-               }
-
-               return $data;
-       }
-
-       /**
-        * Returns a bit from a given position in an integer value, converted to
-        * boolean.
-        *
-        * @param $value integer
-        * @param int $bitIndex The index of the bit, where 0 is the LSB.
-        * @return bool
-        */
-       function testBit( $value, $bitIndex ) {
-               return (bool)( ( $value >> $bitIndex ) & 1 );
-       }
-
-       /**
-        * Debugging helper function which dumps a string in hexdump -C format.
-        */
-       function hexDump( $s ) {
-               $n = strlen( $s );
-               for ( $i = 0; $i < $n; $i += 16 ) {
-                       printf( "%08X ", $i );
-                       for ( $j = 0; $j < 16; $j++ ) {
-                               print " ";
-                               if ( $j == 8 ) {
-                                       print " ";
-                               }
-                               if ( $i + $j >= $n ) {
-                                       print "  ";
-                               } else {
-                                       printf( "%02X", ord( $s[$i + $j] ) );
-                               }
-                       }
-
-                       print "  |";
-                       for ( $j = 0; $j < 16; $j++ ) {
-                               if ( $i + $j >= $n ) {
-                                       print " ";
-                               } elseif ( ctype_print( $s[$i + $j] ) ) {
-                                       print $s[$i + $j];
-                               } else {
-                                       print '.';
-                               }
-                       }
-                       print "|\n";
-               }
-       }
-}
-
-/**
- * Internal exception class. Will be caught by private code.
- */
-class ZipDirectoryReaderError extends Exception {
-       var $errorCode;
-
-       function __construct( $code ) {
-               $this->errorCode = $code;
-               parent::__construct( "ZipDirectoryReader error: $code" );
-       }
-
-       /**
-        * @return mixed
-        */
-       function getErrorCode() {
-               return $this->errorCode;
-       }
-}
index b51d441..13e58b8 100644 (file)
@@ -254,9 +254,9 @@ class ApiLogin extends ApiBase {
 
        public function getDescription() {
                return array(
-                       'Log in and get the authentication tokens. ',
+                       'Log in and get the authentication tokens.',
                        'In the event of a successful log-in, a cookie will be attached',
-                       'to your session. In the event of a failed log-in, you will not ',
+                       'to your session. In the event of a failed log-in, you will not',
                        'be able to attempt another log-in through this method for 5 seconds.',
                        'This is to prevent password guessing by automated password crackers'
                );
@@ -267,10 +267,10 @@ class ApiLogin extends ApiBase {
                        array( 'code' => 'NeedToken', 'info' => 'You need to resubmit your login with the specified token. See https://bugzilla.wikimedia.org/show_bug.cgi?id=23076' ),
                        array( 'code' => 'WrongToken', 'info' => 'You specified an invalid token' ),
                        array( 'code' => 'NoName', 'info' => 'You didn\'t set the lgname parameter' ),
-                       array( 'code' => 'Illegal', 'info' => ' You provided an illegal username' ),
-                       array( 'code' => 'NotExists', 'info' => ' The username you provided doesn\'t exist' ),
-                       array( 'code' => 'EmptyPass', 'info' => ' You didn\'t set the lgpassword parameter or you left it empty' ),
-                       array( 'code' => 'WrongPass', 'info' => ' The password you provided is incorrect' ),
+                       array( 'code' => 'Illegal', 'info' => 'You provided an illegal username' ),
+                       array( 'code' => 'NotExists', 'info' => 'The username you provided doesn\'t exist' ),
+                       array( 'code' => 'EmptyPass', 'info' => 'You didn\'t set the lgpassword parameter or you left it empty' ),
+                       array( 'code' => 'WrongPass', 'info' => 'The password you provided is incorrect' ),
                        array( 'code' => 'WrongPluginPass', 'info' => 'Same as "WrongPass", returned when an authentication plugin rather than MediaWiki itself rejected the password' ),
                        array( 'code' => 'CreateBlocked', 'info' => 'The wiki tried to automatically create a new account for you, but your IP address has been blocked from account creation' ),
                        array( 'code' => 'Throttled', 'info' => 'You\'ve logged in too many times in a short time' ),
index c10d85c..c11f16c 100644 (file)
@@ -383,13 +383,8 @@ class ApiMain extends ApiBase {
                        wfRunHooks( 'ApiMain::onException', array( $this, $e ) );
 
                        // Log it
-                       if ( $e instanceof MWException && !( $e instanceof UsageException ) ) {
-                               global $wgLogExceptionBacktrace;
-                               if ( $wgLogExceptionBacktrace ) {
-                                       wfDebugLog( 'exception', $e->getLogMessage() . "\n" . $e->getTraceAsString() . "\n" );
-                               } else {
-                                       wfDebugLog( 'exception', $e->getLogMessage() );
-                               }
+                       if ( !( $e instanceof UsageException ) ) {
+                               MWExceptionHandler::logException( $e );
                        }
 
                        // Handle any kind of exception by outputting properly formatted error message.
index a369994..301affb 100644 (file)
@@ -461,12 +461,41 @@ class ApiParse extends ApiBase {
 
        private function formatCategoryLinks( $links ) {
                $result = array();
+
+               if ( !$links ) {
+                       return $result;
+               }
+
+               // Fetch hiddencat property
+               $lb = new LinkBatch;
+               $lb->setArray( array( NS_CATEGORY => $links ) );
+               $db = $this->getDB();
+               $res = $db->select( array( 'page', 'page_props' ),
+                       array( 'page_title', 'pp_propname' ),
+                       $lb->constructSet( 'page', $db ),
+                       __METHOD__,
+                       array(),
+                       array( 'page_props' => array(
+                               'LEFT JOIN', array( 'pp_propname' => 'hiddencat', 'pp_page = page_id' )
+                       ) )
+               );
+               $hiddencats = array();
+               foreach ( $res as $row ) {
+                       $hiddencats[$row->page_title] = isset( $row->pp_propname );
+               }
+
                foreach ( $links as $link => $sortkey ) {
                        $entry = array();
                        $entry['sortkey'] = $sortkey;
                        ApiResult::setContent( $entry, $link );
+                       if ( !isset( $hiddencats[$link] ) ) {
+                               $entry['missing'] = '';
+                       } elseif ( $hiddencats[$link] ) {
+                               $entry['hidden'] = '';
+                       }
                        $result[] = $entry;
                }
+
                return $result;
        }
 
old mode 100644 (file)
new mode 100755 (executable)
index 0ea2868..81c9faf
@@ -49,6 +49,12 @@ class ApiQueryImageInfo extends ApiQueryBase {
 
                $scale = $this->getScale( $params );
 
+               $metadataOpts = array(
+                       'version' => $params['metadataversion'],
+                       'language' => $params['extmetadatalanguage'],
+                       'multilang' => $params['extmetadatamultilang'],
+               );
+
                $pageIds = $this->getPageSet()->getAllTitlesByNamespace();
                if ( !empty( $pageIds[NS_FILE] ) ) {
                        $titles = array_keys( $pageIds[NS_FILE] );
@@ -146,7 +152,9 @@ class ApiQueryImageInfo extends ApiQueryBase {
 
                                        $fit = $this->addPageSubItem( $pageId,
                                                self::getInfo( $img, $prop, $result,
-                                                       $finalThumbParams, $params['metadataversion'] ) );
+                                                       $finalThumbParams, $metadataOpts
+                                               )
+                                       );
                                        if ( !$fit ) {
                                                if ( count( $pageIds[NS_FILE] ) == 1 ) {
                                                        // See the 'the user is screwed' comment above
@@ -178,7 +186,7 @@ class ApiQueryImageInfo extends ApiQueryBase {
                                        $fit = self::getTransformCount() < self::TRANSFORM_LIMIT &&
                                                $this->addPageSubItem( $pageId,
                                                        self::getInfo( $oldie, $prop, $result,
-                                                               $finalThumbParams, $params['metadataversion']
+                                                               $finalThumbParams, $metadataOpts
                                                        )
                                                );
                                        if ( !$fit ) {
@@ -296,10 +304,24 @@ class ApiQueryImageInfo extends ApiQueryBase {
         * @param array $prop of properties to get (in the keys)
         * @param $result ApiResult object
         * @param array $thumbParams containing 'width' and 'height' items, or null
-        * @param string $version Version of image metadata (for things like jpeg which have different versions).
+        * @param string|array $metadataOpts Options for metadata fetching.
+        *   This is an array consisting of the keys:
+        *    'version': The metadata version for the metadata option
+        *    'language': The language for extmetadata property
+        *    'multilang': Return all translations in extmetadata property
         * @return Array: result array
         */
-       static function getInfo( $file, $prop, $result, $thumbParams = null, $version = 'latest' ) {
+       static function getInfo( $file, $prop, $result, $thumbParams = null, $metadataOpts = false ) {
+               global $wgContLang;
+
+               if ( !$metadataOpts || is_string( $metadataOpts ) ) {
+                       $metadataOpts = array(
+                               'version' => $metadataOpts ?: 'latest',
+                               'language' => $wgContLang,
+                               'multilang' => false,
+                       );
+               }
+               $version = $metadataOpts['version'];
                $vals = array();
                // Timestamp is shown even if the file is revdelete'd in interface
                // so do same here.
@@ -359,6 +381,7 @@ class ApiQueryImageInfo extends ApiQueryBase {
                $url = isset( $prop['url'] );
                $sha1 = isset( $prop['sha1'] );
                $meta = isset( $prop['metadata'] );
+               $extmetadata = isset( $prop['extmetadata'] );
                $mime = isset( $prop['mime'] );
                $mediatype = isset( $prop['mediatype'] );
                $archive = isset( $prop['archivename'] );
@@ -417,6 +440,17 @@ class ApiQueryImageInfo extends ApiQueryBase {
                        $vals['metadata'] = $metadata ? self::processMetaData( $metadata, $result ) : null;
                }
 
+               if ( $extmetadata ) {
+                       // Note, this should return an array where all the keys
+                       // start with a letter, and all the values are strings.
+                       // Thus there should be no issue with format=xml.
+                       $format = new FormatMetadata;
+                       $format->setSingleLanguage( !$metadataOpts['multilang'] );
+                       $format->getContext()->setLanguage( $metadataOpts['language'] );
+                       $extmetaArray = $format->fetchExtendedMetadata( $file );
+                       $vals['extmetadata'] = $extmetaArray;
+               }
+
                if ( $mime ) {
                        $vals['mime'] = $file->getMimeType();
                }
@@ -491,6 +525,7 @@ class ApiQueryImageInfo extends ApiQueryBase {
        }
 
        public function getAllowedParams() {
+               global $wgContLang;
                return array(
                        'prop' => array(
                                ApiBase::PARAM_ISMULTI => true,
@@ -522,6 +557,14 @@ class ApiQueryImageInfo extends ApiQueryBase {
                                ApiBase::PARAM_TYPE => 'string',
                                ApiBase::PARAM_DFLT => '1',
                        ),
+                       'extmetadatalanguage' => array(
+                               ApiBase::PARAM_TYPE => 'string',
+                               ApiBase::PARAM_DFLT => $wgContLang->getCode(),
+                       ),
+                       'extmetadatamultilang' => array(
+                               ApiBase::PARAM_TYPE => 'boolean',
+                               ApiBase::PARAM_DFLT => false,
+                       ),
                        'urlparam' => array(
                                ApiBase::PARAM_DFLT => '',
                                ApiBase::PARAM_TYPE => 'string',
@@ -564,6 +607,7 @@ class ApiQueryImageInfo extends ApiQueryBase {
                                ' (requires url and param ' . $modulePrefix . 'urlwidth)',
                        'mediatype' =>      ' mediatype     - Adds the media type of the image',
                        'metadata' =>       ' metadata      - Lists Exif metadata for the version of the image',
+                       'extmetadata' =>    ' extmetadata   - Lists formatted metadata combined from multiple sources. Results are HTML formatted.',
                        'archivename' =>    ' archivename   - Adds the file name of the archive version for non-latest versions',
                        'bitdepth' =>       ' bitdepth      - Adds the bit depth of the version',
                        'uploadwarning' =>  ' uploadwarning - Used by the Special:Upload page to get information about an existing file. Not intended for use outside MediaWiki core',
@@ -603,6 +647,10 @@ class ApiQueryImageInfo extends ApiQueryBase {
                        'end' => 'Timestamp to stop listing at',
                        'metadataversion' => array( "Version of metadata to use. if 'latest' is specified, use latest version.",
                                                "Defaults to '1' for backwards compatibility" ),
+                       'extmetadatalanguage' => array( 'What language to fetch extmetadata in. This affects both which',
+                                               'translation to fetch, if multiple are available, as well as how things',
+                                               'like numbers and various values are formatted.' ),
+                       'extmetadatamultilang' => 'If translations for extmetadata property are available, fetch all of them.',
                        'continue' => 'If the query response includes a continue value, use it here to get another page of results',
                        'localonly' => 'Look only for files in the local repository',
                );
index 2754bda..fae3377 100644 (file)
@@ -176,7 +176,7 @@ class ApiQueryRandom extends ApiQueryGeneratorBase {
        public function getDescription() {
                return array(
                        'Get a set of random pages',
-                       'NOTE: Pages are listed in a fixed sequence, only the starting point is random. This means that if, for example, "Main Page" is the first ',
+                       'NOTE: Pages are listed in a fixed sequence, only the starting point is random. This means that if, for example, "Main Page" is the first',
                        '      random page on your list, "List of fictional monkeys" will *always* be second, "List of people on stamps of Vanuatu" third, etc',
                        'NOTE: If the number of pages in the namespace is lower than rnlimit, you will get fewer pages. You will not get the same page twice'
                );
index a776706..ac9e85a 100644 (file)
@@ -153,8 +153,12 @@ class ApiQuerySiteinfo extends ApiQueryBase {
                }
 
                if ( $wgContLang->linkPrefixExtension() ) {
-                       $data['linkprefix'] = wfMessage( 'linkprefix' )->inContentLanguage()->text();
+                       $linkPrefixCharset = $wgContLang->linkPrefixCharset();
+                       $data['linkprefixcharset'] = $linkPrefixCharset;
+                       // For backwards compatability
+                       $data['linkprefix'] = "/^((?>.*[^$linkPrefixCharset]|))(.+)$/sDu";
                } else {
+                       $data['linkprefixcharset'] = '';
                        $data['linkprefix'] = '';
                }
 
diff --git a/includes/cache/HTMLCacheUpdate.php b/includes/cache/HTMLCacheUpdate.php
deleted file mode 100644 (file)
index 992809e..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
-<?php
-/**
- * HTML cache invalidation of all pages linking to a given title.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- * @ingroup Cache
- */
-
-/**
- * Class to invalidate the HTML cache of all the pages linking to a given title.
- *
- * @ingroup Cache
- */
-class HTMLCacheUpdate implements DeferrableUpdate {
-       /**
-        * @var Title
-        */
-       public $mTitle;
-
-       public $mTable;
-
-       /**
-        * @param $titleTo
-        * @param $table
-        * @param $start bool
-        * @param $end bool
-        */
-       function __construct( Title $titleTo, $table ) {
-               $this->mTitle = $titleTo;
-               $this->mTable = $table;
-       }
-
-       public function doUpdate() {
-               wfProfileIn( __METHOD__ );
-
-               $job = new HTMLCacheUpdateJob(
-                       $this->mTitle,
-                       array(
-                               'table' => $this->mTable,
-                       ) + Job::newRootJobParams( // "overall" refresh links job info
-                               "htmlCacheUpdate:{$this->mTable}:{$this->mTitle->getPrefixedText()}"
-                       )
-               );
-
-               $count = $this->mTitle->getBacklinkCache()->getNumLinks( $this->mTable, 200 );
-               if ( $count >= 200 ) { // many backlinks
-                       JobQueueGroup::singleton()->push( $job );
-                       JobQueueGroup::singleton()->deduplicateRootJob( $job );
-               } else { // few backlinks ($count might be off even if 0)
-                       $dbw = wfGetDB( DB_MASTER );
-                       $dbw->onTransactionIdle( function() use ( $job ) {
-                               $job->run(); // just do the purge query now
-                       } );
-               }
-
-               wfProfileOut( __METHOD__ );
-       }
-}
index 1bfd17b..c5a153a 100644 (file)
@@ -106,7 +106,7 @@ class LocalisationCache {
                'fallback', 'namespaceNames', 'bookstoreList',
                'magicWords', 'messages', 'rtl', 'capitalizeAllNouns', 'digitTransformTable',
                'separatorTransformTable', 'fallback8bitEncoding', 'linkPrefixExtension',
-               'linkTrail', 'namespaceAliases',
+               'linkTrail', 'linkPrefixCharset', 'namespaceAliases',
                'dateFormats', 'datePreferences', 'datePreferenceMigrationMap',
                'defaultDateFormat', 'extraUserToggles', 'specialPageAliases',
                'imageFiles', 'preloadedMessages', 'namespaceGenderAliases',
index 6ab1012..a92c87f 100644 (file)
@@ -865,7 +865,7 @@ class MessageCache {
         *
         * @param string $title Message cache key with initial uppercase letter.
         * @param string $code Code denoting the language to try.
-        * @return string|bool The message, or false iff it does not exist or on error
+        * @return string|bool The message, or false if it does not exist or on error
         */
        function getMsgFromNamespace( $title, $code ) {
                $this->load( $code );
diff --git a/includes/cache/SquidUpdate.php b/includes/cache/SquidUpdate.php
deleted file mode 100644 (file)
index 71afeba..0000000
+++ /dev/null
@@ -1,300 +0,0 @@
-<?php
-/**
- * Squid cache purging.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- * @ingroup Cache
- */
-
-/**
- * Handles purging appropriate Squid URLs given a title (or titles)
- * @ingroup Cache
- */
-class SquidUpdate {
-
-       /**
-        * Collection of URLs to purge.
-        * @var array
-        */
-       protected $urlArr;
-
-       /**
-        * @param array $urlArr Collection of URLs to purge
-        * @param bool|int $maxTitles Maximum number of unique URLs to purge
-        */
-       public function __construct( $urlArr = array(), $maxTitles = false ) {
-               global $wgMaxSquidPurgeTitles;
-               if ( $maxTitles === false ) {
-                       $maxTitles = $wgMaxSquidPurgeTitles;
-               }
-
-               // Remove duplicate URLs from list
-               $urlArr = array_unique( $urlArr );
-               if ( count( $urlArr ) > $maxTitles ) {
-                       // Truncate to desired maximum URL count
-                       $urlArr = array_slice( $urlArr, 0, $maxTitles );
-               }
-               $this->urlArr = $urlArr;
-       }
-
-       /**
-        * Create a SquidUpdate from the given Title object.
-        *
-        * The resulting SquidUpdate will purge the given Title's URLs as well as
-        * the pages that link to it. Capped at $wgMaxSquidPurgeTitles total URLs.
-        *
-        * @param Title $title
-        * @return SquidUpdate
-        */
-       public static function newFromLinksTo( Title $title ) {
-               global $wgMaxSquidPurgeTitles;
-               wfProfileIn( __METHOD__ );
-
-               # Get a list of URLs linking to this page
-               $dbr = wfGetDB( DB_SLAVE );
-               $res = $dbr->select( array( 'links', 'page' ),
-                       array( 'page_namespace', 'page_title' ),
-                       array(
-                               'pl_namespace' => $title->getNamespace(),
-                               'pl_title' => $title->getDBkey(),
-                               'pl_from=page_id' ),
-                       __METHOD__ );
-               $blurlArr = $title->getSquidURLs();
-               if ( $res->numRows() <= $wgMaxSquidPurgeTitles ) {
-                       foreach ( $res as $BL ) {
-                               $tobj = Title::makeTitle( $BL->page_namespace, $BL->page_title );
-                               $blurlArr[] = $tobj->getInternalURL();
-                       }
-               }
-
-               wfProfileOut( __METHOD__ );
-               return new SquidUpdate( $blurlArr );
-       }
-
-       /**
-        * Create a SquidUpdate from an array of Title objects, or a TitleArray object
-        *
-        * @param array $titles
-        * @param array $urlArr
-        * @return SquidUpdate
-        */
-       public static function newFromTitles( $titles, $urlArr = array() ) {
-               global $wgMaxSquidPurgeTitles;
-               $i = 0;
-               foreach ( $titles as $title ) {
-                       $urlArr[] = $title->getInternalURL();
-                       if ( $i++ > $wgMaxSquidPurgeTitles ) {
-                               break;
-                       }
-               }
-               return new SquidUpdate( $urlArr );
-       }
-
-       /**
-        * @param Title $title
-        * @return SquidUpdate
-        */
-       public static function newSimplePurge( Title $title ) {
-               $urlArr = $title->getSquidURLs();
-               return new SquidUpdate( $urlArr );
-       }
-
-       /**
-        * Purges the list of URLs passed to the constructor.
-        */
-       public function doUpdate() {
-               self::purge( $this->urlArr );
-       }
-
-       /**
-        * Purges a list of Squids defined in $wgSquidServers.
-        * $urlArr should contain the full URLs to purge as values
-        * (example: $urlArr[] = 'http://my.host/something')
-        * XXX report broken Squids per mail or log
-        *
-        * @param array $urlArr List of full URLs to purge
-        */
-       public static function purge( $urlArr ) {
-               global $wgSquidServers, $wgHTCPRouting;
-
-               if ( !$urlArr ) {
-                       return;
-               }
-
-               wfDebugLog( 'squid', __METHOD__ . ': ' . implode( ' ', $urlArr ) . "\n" );
-
-               if ( $wgHTCPRouting ) {
-                       self::HTCPPurge( $urlArr );
-               }
-
-               wfProfileIn( __METHOD__ );
-
-               // Remove duplicate URLs
-               $urlArr = array_unique( $urlArr );
-               // Maximum number of parallel connections per squid
-               $maxSocketsPerSquid = 8;
-               // Number of requests to send per socket
-               // 400 seems to be a good tradeoff, opening a socket takes a while
-               $urlsPerSocket = 400;
-               $socketsPerSquid = ceil( count( $urlArr ) / $urlsPerSocket );
-               if ( $socketsPerSquid > $maxSocketsPerSquid ) {
-                       $socketsPerSquid = $maxSocketsPerSquid;
-               }
-
-               $pool = new SquidPurgeClientPool;
-               $chunks = array_chunk( $urlArr, ceil( count( $urlArr ) / $socketsPerSquid ) );
-               foreach ( $wgSquidServers as $server ) {
-                       foreach ( $chunks as $chunk ) {
-                               $client = new SquidPurgeClient( $server );
-                               foreach ( $chunk as $url ) {
-                                       $client->queuePurge( $url );
-                               }
-                               $pool->addClient( $client );
-                       }
-               }
-               $pool->run();
-
-               wfProfileOut( __METHOD__ );
-       }
-
-       /**
-        * Send Hyper Text Caching Protocol (HTCP) CLR requests.
-        *
-        * @throws MWException
-        * @param array $urlArr Collection of URLs to purge
-        */
-       public static function HTCPPurge( $urlArr ) {
-               global $wgHTCPRouting, $wgHTCPMulticastTTL;
-               wfProfileIn( __METHOD__ );
-
-               // HTCP CLR operation
-               $htcpOpCLR = 4;
-
-               // @todo FIXME: PHP doesn't support these socket constants (include/linux/in.h)
-               if ( !defined( "IPPROTO_IP" ) ) {
-                       define( "IPPROTO_IP", 0 );
-                       define( "IP_MULTICAST_LOOP", 34 );
-                       define( "IP_MULTICAST_TTL", 33 );
-               }
-
-               // pfsockopen doesn't work because we need set_sock_opt
-               $conn = socket_create( AF_INET, SOCK_DGRAM, SOL_UDP );
-               if ( ! $conn ) {
-                       $errstr = socket_strerror( socket_last_error() );
-                       wfDebugLog( 'squid', __METHOD__ .
-                               ": Error opening UDP socket: $errstr\n" );
-                       wfProfileOut( __METHOD__ );
-                       return;
-               }
-
-               // Set socket options
-               socket_set_option( $conn, IPPROTO_IP, IP_MULTICAST_LOOP, 0 );
-               if ( $wgHTCPMulticastTTL != 1 ) {
-                       // Set multicast time to live (hop count) option on socket
-                       socket_set_option( $conn, IPPROTO_IP, IP_MULTICAST_TTL,
-                               $wgHTCPMulticastTTL );
-               }
-
-               // Remove duplicate URLs from collection
-               $urlArr = array_unique( $urlArr );
-               foreach ( $urlArr as $url ) {
-                       if ( !is_string( $url ) ) {
-                               wfProfileOut( __METHOD__ );
-                               throw new MWException( 'Bad purge URL' );
-                       }
-                       $url = self::expand( $url );
-                       $conf = self::getRuleForURL( $url, $wgHTCPRouting );
-                       if ( !$conf ) {
-                               wfDebugLog( 'squid', __METHOD__ .
-                                       "No HTCP rule configured for URL {$url} , skipping\n" );
-                               continue;
-                       }
-
-                       if ( isset( $conf['host'] ) && isset( $conf['port'] ) ) {
-                               // Normalize single entries
-                               $conf = array( $conf );
-                       }
-                       foreach ( $conf as $subconf ) {
-                               if ( !isset( $subconf['host'] ) || !isset( $subconf['port'] ) ) {
-                                       wfProfileOut( __METHOD__ );
-                                       throw new MWException( "Invalid HTCP rule for URL $url\n" );
-                               }
-                       }
-
-                       // Construct a minimal HTCP request diagram
-                       // as per RFC 2756
-                       // Opcode 'CLR', no response desired, no auth
-                       $htcpTransID = rand();
-
-                       $htcpSpecifier = pack( 'na4na*na8n',
-                               4, 'HEAD', strlen( $url ), $url,
-                               8, 'HTTP/1.0', 0 );
-
-                       $htcpDataLen = 8 + 2 + strlen( $htcpSpecifier );
-                       $htcpLen = 4 + $htcpDataLen + 2;
-
-                       // Note! Squid gets the bit order of the first
-                       // word wrong, wrt the RFC. Apparently no other
-                       // implementation exists, so adapt to Squid
-                       $htcpPacket = pack( 'nxxnCxNxxa*n',
-                               $htcpLen, $htcpDataLen, $htcpOpCLR,
-                               $htcpTransID, $htcpSpecifier, 2 );
-
-                       wfDebugLog( 'squid', __METHOD__ .
-                               "Purging URL $url via HTCP\n" );
-                       foreach ( $conf as $subconf ) {
-                               socket_sendto( $conn, $htcpPacket, $htcpLen, 0,
-                                       $subconf['host'], $subconf['port'] );
-                       }
-               }
-               wfProfileOut( __METHOD__ );
-       }
-
-       /**
-        * Expand local URLs to fully-qualified URLs using the internal protocol
-        * and host defined in $wgInternalServer. Input that's already fully-
-        * qualified will be passed through unchanged.
-        *
-        * This is used to generate purge URLs that may be either local to the
-        * main wiki or include a non-native host, such as images hosted on a
-        * second internal server.
-        *
-        * Client functions should not need to call this.
-        *
-        * @param string $url
-        * @return string
-        */
-       public static function expand( $url ) {
-               return wfExpandUrl( $url, PROTO_INTERNAL );
-       }
-
-       /**
-        * Find the HTCP routing rule to use for a given URL.
-        * @param string $url URL to match
-        * @param array $rules Array of rules, see $wgHTCPRouting for format and behavior
-        * @return mixed Element of $rules that matched, or false if nothing matched
-        */
-       private static function getRuleForURL( $url, $rules ) {
-               foreach ( $rules as $regex => $routing ) {
-                       if ( $regex === '' || preg_match( $regex, $url ) ) {
-                               return $routing;
-                       }
-               }
-               return false;
-       }
-}
index 980bd0a..8d4c9c1 100644 (file)
@@ -30,6 +30,7 @@
  *  rc_namespace    namespace #
  *  rc_title        non-prefixed db key
  *  rc_type         is new entry, used to determine whether updating is necessary
+ *  rc_source       string representation of change source
  *  rc_minor        is minor
  *  rc_cur_id       page_id of associated page entry
  *  rc_user         user id who made the entry
  * @todo document functions and variables
  */
 class RecentChange {
+
+       // Constants for the rc_source field.  Extensions may also have
+       // their own source constants.
+       const SRC_EDIT = 'mw.edit';
+       const SRC_NEW = 'mw.new';
+       const SRC_LOG = 'mw.log';
+       const SRC_EXTERNAL = 'mw.external'; // obsolete
+
        var $mAttribs = array(), $mExtra = array();
 
        /**
@@ -159,6 +168,7 @@ class RecentChange {
                        'rc_this_oldid',
                        'rc_last_oldid',
                        'rc_type',
+                       'rc_source',
                        'rc_patrolled',
                        'rc_ip',
                        'rc_old_len',
@@ -489,6 +499,7 @@ class RecentChange {
                        'rc_namespace'  => $title->getNamespace(),
                        'rc_title'      => $title->getDBkey(),
                        'rc_type'       => RC_EDIT,
+                       'rc_source'     => self::SRC_EDIT,
                        'rc_minor'      => $minor ? 1 : 0,
                        'rc_cur_id'     => $title->getArticleID(),
                        'rc_user'       => $user->getId(),
@@ -548,6 +559,7 @@ class RecentChange {
                        'rc_namespace'      => $title->getNamespace(),
                        'rc_title'          => $title->getDBkey(),
                        'rc_type'           => RC_NEW,
+                       'rc_source'         => self::SRC_NEW,
                        'rc_minor'          => $minor ? 1 : 0,
                        'rc_cur_id'         => $title->getArticleID(),
                        'rc_user'           => $user->getId(),
@@ -657,6 +669,7 @@ class RecentChange {
                        'rc_namespace'  => $target->getNamespace(),
                        'rc_title'      => $target->getDBkey(),
                        'rc_type'       => RC_LOG,
+                       'rc_source'     => self::SRC_LOG,
                        'rc_minor'      => 0,
                        'rc_cur_id'     => $target->getArticleID(),
                        'rc_user'       => $user->getId(),
@@ -701,7 +714,7 @@ class RecentChange {
        /**
         * Makes a pseudo-RC entry from a cur row
         *
-        * @deprected in 1.22
+        * @deprecated in 1.22
         * @param $row
         */
        public function loadFromCurRow( $row ) {
@@ -716,6 +729,7 @@ class RecentChange {
                        'rc_comment' => $row->rev_comment,
                        'rc_minor' => $row->rev_minor_edit ? 1 : 0,
                        'rc_type' => $row->page_is_new ? RC_NEW : RC_EDIT,
+                       'rc_source' => $row->page_is_new ? self::SRC_NEW : self::SRC_EDIT,
                        'rc_cur_id' => $row->page_id,
                        'rc_this_oldid' => $row->rev_id,
                        'rc_last_oldid' => isset( $row->rc_last_oldid ) ? $row->rc_last_oldid : 0,
index ef71b18..f7653eb 100644 (file)
@@ -336,7 +336,8 @@ class RedisConnRef {
                }
 
                if ( $conn->getLastError() ) { // script bug?
-                       wfDebugLog( 'redis', "Lua script error: " . $conn->getLastError() );
+                       $server = $this->server;
+                       wfDebugLog( 'redis', "Lua script error on server $server: " . $conn->getLastError() );
                }
 
                return $res;
index c3850b9..cd907e9 100644 (file)
@@ -281,6 +281,20 @@ abstract class DatabaseBase implements IDatabase, DatabaseType {
         */
        private $mTrxAutomatic = false;
 
+       /**
+        * Array of levels of atomicity within transactions
+        *
+        * @var SplStack
+        */
+       private $mTrxAtomicLevels;
+
+       /**
+        * Record if the current transaction was started implicitly by DatabaseBase::startAtomic
+        *
+        * @var Bool
+        */
+       private $mTrxAutomaticAtomic = false;
+
        /**
         * @since 1.21
         * @var file handle for upgrade
@@ -687,6 +701,7 @@ abstract class DatabaseBase implements IDatabase, DatabaseType {
        ) {
                global $wgDBprefix, $wgCommandLineMode, $wgDebugDBTransactions;
 
+               $this->mTrxAtomicLevels = new SplStack;
                $this->mFlags = $flags;
 
                if ( $this->mFlags & DBO_DEFAULT ) {
@@ -3111,7 +3126,7 @@ abstract class DatabaseBase implements IDatabase, DatabaseType {
         * @since 1.20
         */
        final public function onTransactionIdle( $callback ) {
-               $this->mTrxIdleCallbacks[] = $callback;
+               $this->mTrxIdleCallbacks[] = array( $callback, wfGetCaller() );
                if ( !$this->mTrxLevel ) {
                        $this->runOnTransactionIdleCallbacks();
                }
@@ -3130,7 +3145,7 @@ abstract class DatabaseBase implements IDatabase, DatabaseType {
         */
        final public function onTransactionPreCommitOrIdle( $callback ) {
                if ( $this->mTrxLevel ) {
-                       $this->mTrxPreCommitCallbacks[] = $callback;
+                       $this->mTrxPreCommitCallbacks[] = array( $callback, wfGetCaller() );
                } else {
                        $this->onTransactionIdle( $callback ); // this will trigger immediately
                }
@@ -3150,8 +3165,9 @@ abstract class DatabaseBase implements IDatabase, DatabaseType {
                        $this->mTrxIdleCallbacks = array(); // recursion guard
                        foreach ( $callbacks as $callback ) {
                                try {
+                                       list( $phpCallback ) = $callback;
                                        $this->clearFlag( DBO_TRX ); // make each query its own transaction
-                                       call_user_func( $callback );
+                                       call_user_func( $phpCallback );
                                        $this->setFlag( $autoTrx ? DBO_TRX : 0 ); // restore automatic begin()
                                } catch ( Exception $e ) {}
                        }
@@ -3174,7 +3190,8 @@ abstract class DatabaseBase implements IDatabase, DatabaseType {
                        $this->mTrxPreCommitCallbacks = array(); // recursion guard
                        foreach ( $callbacks as $callback ) {
                                try {
-                                       call_user_func( $callback );
+                                       list( $phpCallback ) = $callback;
+                                       call_user_func( $phpCallback );
                                } catch ( Exception $e ) {}
                        }
                } while ( count( $this->mTrxPreCommitCallbacks ) );
@@ -3184,6 +3201,39 @@ abstract class DatabaseBase implements IDatabase, DatabaseType {
                }
        }
 
+       /**
+        * Begin an atomic section of statements
+        *
+        * If a transaction has been started already, just keep track of the given
+        * section name to make sure the transaction is not committed pre-maturely.
+        * This function can be used in layers (with sub-sections), so use a stack
+        * to keep track of the different atomic sections. If there is no transaction,
+        * start one implicitly.
+        *
+        * The goal of this function is to create an atomic section of SQL queries
+        * without having to start a new transaction if it already exists.
+        *
+        * Atomic sections are more strict than transactions. With transactions,
+        * attempting to begin a new transaction when one is already running results
+        * in MediaWiki issuing a brief warning and doing an implicit commit. All
+        * atomic levels *must* be explicitly closed using DatabaseBase::endAtomic(),
+        * and any database transactions cannot be began or committed until all atomic
+        * levels are closed. There is no such thing as implicitly opening or closing
+        * an atomic section.
+        *
+        * @since 1.23
+        * @param string $fname
+        */
+       final public function startAtomic( $fname = __METHOD__ ) {
+               if ( !$this->mTrxLevel ) {
+                       $this->begin( $fname );
+                       $this->mTrxAutomatic = true;
+                       $this->mTrxAutomaticAtomic = true;
+               }
+
+               $this->mTrxAtomicLevels->push( $fname );
+       }
+
        /**
         * Begin a transaction. If a transaction is already in progress, that transaction will be committed before the
         * new transaction is started.
@@ -3200,7 +3250,13 @@ abstract class DatabaseBase implements IDatabase, DatabaseType {
                global $wgDebugDBTransactions;
 
                if ( $this->mTrxLevel ) { // implicit commit
-                       if ( !$this->mTrxAutomatic ) {
+                       if ( !$this->mTrxAtomicLevels->isEmpty() ) {
+                               // If the current transaction was an automatic atomic one, then we definitely have
+                               // a problem. Same if there is any unclosed atomic level.
+                               throw new DBUnexpectedError( $this,
+                                       "Attempted to start explicit transaction when atomic levels are still open."
+                               );
+                       } elseif ( !$this->mTrxAutomatic ) {
                                // We want to warn about inadvertently nested begin/commit pairs, but not about
                                // auto-committing implicit transactions that were started by query() via DBO_TRX
                                $msg = "$fname: Transaction already in progress (from {$this->mTrxFname}), " .
@@ -3229,6 +3285,8 @@ abstract class DatabaseBase implements IDatabase, DatabaseType {
                $this->mTrxFname = $fname;
                $this->mTrxDoneWrites = false;
                $this->mTrxAutomatic = false;
+               $this->mTrxAutomaticAtomic = false;
+               $this->mTrxAtomicLevels = new SplStack;
        }
 
        /**
@@ -3242,6 +3300,28 @@ abstract class DatabaseBase implements IDatabase, DatabaseType {
                $this->mTrxLevel = 1;
        }
 
+       /**
+        * Ends an atomic section of SQL statements
+        *
+        * Ends the next section of atomic SQL statements and commits the transaction
+        * if necessary.
+        *
+        * @since 1.23
+        * @see DatabaseBase::startAtomic
+        * @param string $fname
+        */
+       final public function endAtomic( $fname = __METHOD__ ) {
+               if ( $this->mTrxAtomicLevels->isEmpty() ||
+                       $this->mTrxAtomicLevels->pop() !== $fname
+               ) {
+                       throw new DBUnexpectedError( $this, 'Invalid atomic section ended.' );
+               }
+
+               if ( $this->mTrxAtomicLevels->isEmpty() && $this->mTrxAutomaticAtomic ) {
+                       $this->commit( $fname, 'flush' );
+               }
+       }
+
        /**
         * Commits a transaction previously started using begin().
         * If no transaction is in progress, a warning is issued.
@@ -3255,6 +3335,11 @@ abstract class DatabaseBase implements IDatabase, DatabaseType {
         *        that it is safe to ignore these warnings in your context.
         */
        final public function commit( $fname = __METHOD__, $flush = '' ) {
+               if ( !$this->mTrxAtomicLevels->isEmpty() ) {
+                       // There are still atomic sections open. This cannot be ignored
+                       throw new DBUnexpectedError( $this, "Attempted to commit transaction while atomic sections are still open" );
+               }
+
                if ( $flush != 'flush' ) {
                        if ( !$this->mTrxLevel ) {
                                wfWarn( "$fname: No transaction to commit, something got out of sync!" );
@@ -3274,6 +3359,7 @@ abstract class DatabaseBase implements IDatabase, DatabaseType {
                if ( $this->mTrxDoneWrites ) {
                        Profiler::instance()->transactionWritingOut( $this->mServer, $this->mDBname );
                }
+               $this->mTrxDoneWrites = false;
                $this->runOnTransactionIdleCallbacks();
        }
 
@@ -3305,9 +3391,11 @@ abstract class DatabaseBase implements IDatabase, DatabaseType {
                $this->doRollback( $fname );
                $this->mTrxIdleCallbacks = array(); // cancel
                $this->mTrxPreCommitCallbacks = array(); // cancel
+               $this->mTrxAtomicLevels = new SplStack;
                if ( $this->mTrxDoneWrites ) {
                        Profiler::instance()->transactionWritingOut( $this->mServer, $this->mDBname );
                }
+               $this->mTrxDoneWrites = false;
        }
 
        /**
@@ -3892,9 +3980,21 @@ abstract class DatabaseBase implements IDatabase, DatabaseType {
                return (string)$this->mConn;
        }
 
+       /**
+        * Run a few simple sanity checks
+        */
        public function __destruct() {
+               if ( $this->mTrxLevel && $this->mTrxDoneWrites ) {
+                       trigger_error( "Uncommitted DB writes (transaction from {$this->mTrxFname})." );
+               }
                if ( count( $this->mTrxIdleCallbacks ) || count( $this->mTrxPreCommitCallbacks ) ) {
-                       trigger_error( "Transaction idle or pre-commit callbacks still pending." ); // sanity
+                       $callers = array();
+                       foreach ( $this->mTrxIdleCallbacks as $callbackInfo ) {
+                               $callers[] = $callbackInfo[1];
+
+                       }
+                       $callers = implode( ', ', $callers );
+                       trigger_error( "DB transaction callbacks still pending (from $callers)." );
                }
        }
 }
index da391d7..f14a502 100644 (file)
@@ -66,8 +66,7 @@ class DBError extends MWException {
                $s = $this->getHTMLContent();
 
                if ( $wgShowDBErrorBacktrace ) {
-                       $s .= '<p>Backtrace:</p><p>' .
-                               nl2br( htmlspecialchars( $this->getTraceAsString() ) ) . '</p>';
+                       $s .= '<p>Backtrace:</p><pre>' . htmlspecialchars( $this->getTraceAsString() ) . '</pre>';
                }
 
                return $s;
@@ -134,27 +133,20 @@ class DBConnectionError extends DBError {
        }
 
        /**
-        * @return bool
+        * @return boolean
         */
-       function getLogMessage() {
-               # Don't send to the exception log
+       function isLoggable() {
+               // Don't send to the exception log, already in dberror log
                return false;
        }
 
-       /**
-        * @return string
-        */
-       function getPageTitle() {
-               return $this->msg( 'dberr-header', 'This wiki has a problem' );
-       }
-
        /**
         * @return string
         */
        function getHTML() {
                global $wgShowDBErrorBacktrace, $wgShowHostnames, $wgShowSQLErrors;
 
-               $sorry = htmlspecialchars( $this->msg( 'dberr-problems', "Sorry!\nThis site is experiencing technical difficulties." ) );
+               $sorry = htmlspecialchars( $this->msg( 'dberr-problems', 'Sorry! This site is experiencing technical difficulties.' ) );
                $again = htmlspecialchars( $this->msg( 'dberr-again', 'Try waiting a few minutes and reloading.' ) );
 
                if ( $wgShowHostnames || $wgShowSQLErrors ) {
@@ -169,17 +161,16 @@ class DBConnectionError extends DBError {
                # No database access
                MessageCache::singleton()->disable();
 
-               $text = "<h1>$sorry</h1><p>$again</p><p><small>$info</small></p>";
+               $html = "<h1>$sorry</h1><p>$again</p><p><small>$info</small></p>";
 
                if ( $wgShowDBErrorBacktrace ) {
-                       $text .= '<p>Backtrace:</p><p>' .
-                               nl2br( htmlspecialchars( $this->getTraceAsString() ) ) . '</p>';
+                       $html .= '<p>Backtrace:</p><pre>' . htmlspecialchars( $this->getTraceAsString() ) . '</pre>';
                }
 
-               $text .= '<hr />';
-               $text .= $this->searchForm();
+               $html .= '<hr />';
+               $html .= $this->searchForm();
 
-               return $text;
+               return $html;
        }
 
        protected function getTextContent() {
@@ -195,21 +186,21 @@ class DBConnectionError extends DBError {
        public function reportHTML() {
                global $wgUseFileCache;
 
-               # Check whether we can serve a file-cached copy of the page with the error underneath
+               // Check whether we can serve a file-cached copy of the page with the error underneath
                if ( $wgUseFileCache ) {
                        try {
                                $cache = $this->fileCachedPage();
-                               # Cached version on file system?
+                               // Cached version on file system?
                                if ( $cache !== null ) {
-                                       # Hack: extend the body for error messages
+                                       // Hack: extend the body for error messages
                                        $cache = str_replace( array( '</html>', '</body>' ), '', $cache );
-                                       # Add cache notice...
-                                       $cache .= '<div style="color:red;font-size:150%;font-weight:bold;">' .
+                                       // Add cache notice...
+                                       $cache .= '<div style="border:1px solid #ffd0d0;padding:1em;">' .
                                                htmlspecialchars( $this->msg( 'dberr-cachederror',
-                                                       'This is a cached copy of the requested page, and may not be up to date. ' ) ) .
+                                                       'This is a cached copy of the requested page, and may not be up to date.' ) ) .
                                                '</div>';
 
-                                       # Output cached page with notices on bottom and re-close body
+                                       // Output cached page with notices on bottom and re-close body
                                        echo "{$cache}<hr />{$this->getHTML()}</body></html>";
                                        return;
                                }
@@ -218,7 +209,7 @@ class DBConnectionError extends DBError {
                        }
                }
 
-               # We can't, cough and die in the usual fashion
+               // We can't, cough and die in the usual fashion
                parent::reportHTML();
        }
 
@@ -239,8 +230,8 @@ class DBConnectionError extends DBError {
 
                $trygoogle = <<<EOT
 <div style="margin: 1.5em">$usegoogle<br />
-<small>$outofdate</small></div>
-<!-- SiteSearch Google -->
+<small>$outofdate</small>
+</div>
 <form method="get" action="//www.google.com/search" id="googlesearch">
        <input type="hidden" name="domains" value="$server" />
        <input type="hidden" name="num" value="50" />
@@ -249,12 +240,11 @@ class DBConnectionError extends DBError {
 
        <input type="text" name="q" size="31" maxlength="255" value="$search" />
        <input type="submit" name="btnG" value="$googlesearch" />
-  <div>
-       <input type="radio" name="sitesearch" id="gwiki" value="$server" checked="checked" /><label for="gwiki">$sitename</label>
-       <input type="radio" name="sitesearch" id="gWWW" value="" /><label for="gWWW">WWW</label>
-  </div>
+       <p>
+               <label><input type="radio" name="sitesearch" value="$server" checked="checked" />$sitename</label>
+               <label><input type="radio" name="sitesearch" value="" />WWW</label>
+       </p>
 </form>
-<!-- SiteSearch Google -->
 EOT;
                return $trygoogle;
        }
@@ -266,15 +256,17 @@ EOT;
                global $wgTitle, $wgOut, $wgRequest;
 
                if ( $wgOut->isDisabled() ) {
-                       return ''; // Done already?
+                       // Done already?
+                       return '';
                }
 
-               if ( $wgTitle ) { // use $wgTitle if we managed to set it
+               if ( $wgTitle ) {
+                       // use $wgTitle if we managed to set it
                        $t = $wgTitle->getPrefixedDBkey();
                } else {
-                       # Fallback to the raw title URL param. We can't use the Title
-                       # class is it may hit the interwiki table and give a DB error.
-                       # We may get a cache miss due to not sanitizing the title though.
+                       // Fallback to the raw title URL param. We can't use the Title
+                       // class is it may hit the interwiki table and give a DB error.
+                       // We may get a cache miss due to not sanitizing the title though.
                        $t = str_replace( ' ', '_', $wgRequest->getVal( 'title' ) );
                        if ( $t == '' ) { // fallback to main page
                                $t = Title::newFromText(
@@ -318,15 +310,15 @@ class DBQueryError extends DBError {
        }
 
        /**
-        * @return bool
+        * @return boolean
         */
-       function getLogMessage() {
-               # Don't send to the exception log
+       function isLoggable() {
+               // Don't send to the exception log, already in dberror log
                return false;
        }
 
        /**
-        * @return String
+        * @return string
         */
        function getPageTitle() {
                return $this->msg( 'databaseerror', 'Database error' );
index 4a51226..06dfd84 100644 (file)
@@ -803,6 +803,9 @@ class DatabaseSqlite extends DatabaseBase {
                        $s = preg_replace( '/\(\d+\)/', '', $s );
                        // No FULLTEXT
                        $s = preg_replace( '/\bfulltext\b/i', '', $s );
+               } elseif ( preg_match( '/^\s*DROP INDEX/i', $s ) ) {
+                       // DROP INDEX is database-wide, not table-specific, so no ON <table> clause.
+                       $s = preg_replace( '/\sON\s+[^\s]*/i', '', $s );
                }
                return $s;
        }
index 519e2df..f10d07f 100644 (file)
@@ -36,9 +36,9 @@ interface LoadMonitor {
 
        /**
         * Perform pre-connection load ratio adjustment.
-        * @param $loads array
-        * @param string $group the selected query group
-        * @param $wiki String
+        * @param array $loads
+        * @param string|bool $group the selected query group. Default: false
+        * @param string|bool $wiki Default: false
         */
        function scaleLoads( &$loads, $group = false, $wiki = false );
 
@@ -71,6 +71,10 @@ interface LoadMonitor {
        function getLagTimes( $serverIndexes, $wiki );
 }
 
+/**
+ * @todo FIXME: Should be  LoadMonitorNull per naming conventions.
+ * PHP CodeSniffer Squiz.Classes.ValidClassName.NotCamelCaps
+ */
 class LoadMonitor_Null implements LoadMonitor {
        function __construct( $parent ) {
        }
@@ -96,13 +100,14 @@ class LoadMonitor_Null implements LoadMonitor {
  * Uses memcached to cache the replication lag for a short time
  *
  * @ingroup Database
+ * @todo FIXME: Should be  LoadMonitorMySQL per naming conventions.
+ * PHP CodeSniffer Squiz.Classes.ValidClassName.NotCamelCaps
  */
 class LoadMonitor_MySQL implements LoadMonitor {
-
        /**
         * @var LoadBalancer
         */
-       var $parent;
+       public $parent;
 
        /**
         * @param LoadBalancer $parent
diff --git a/includes/deferred/CallableUpdate.php b/includes/deferred/CallableUpdate.php
new file mode 100644 (file)
index 0000000..6eb5541
--- /dev/null
@@ -0,0 +1,30 @@
+<?php
+
+/**
+ * Deferrable Update for closure/callback
+ */
+class MWCallableUpdate implements DeferrableUpdate {
+
+       /**
+        * @var closure/callabck
+        */
+       private $callback;
+
+       /**
+        * @param callable $callback
+        */
+       public function __construct( $callback ) {
+               if ( !is_callable( $callback ) ) {
+                       throw new MWException( 'Not a valid callback/closure!' );
+               }
+               $this->callback = $callback;
+       }
+
+       /**
+        * Run the update
+        */
+       public function doUpdate() {
+               call_user_func( $this->callback );
+       }
+
+}
diff --git a/includes/deferred/DataUpdate.php b/includes/deferred/DataUpdate.php
new file mode 100644 (file)
index 0000000..7b9ac28
--- /dev/null
@@ -0,0 +1,126 @@
+<?php
+/**
+ * Base code for update jobs that do something with some secondary
+ * data extracted from article.
+ *
+ * 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
+ */
+
+/**
+ * Abstract base class for update jobs that do something with some secondary
+ * data extracted from article.
+ *
+ * @note: subclasses should NOT start or commit transactions in their doUpdate() method,
+ *        a transaction will automatically be wrapped around the update. If need be,
+ *        subclasses can override the beginTransaction() and commitTransaction() methods.
+ */
+abstract class DataUpdate implements DeferrableUpdate {
+
+       /**
+        * Constructor
+        */
+       public function __construct() {
+               # noop
+       }
+
+       /**
+        * Begin an appropriate transaction, if any.
+        * This default implementation does nothing.
+        */
+       public function beginTransaction() {
+               //noop
+       }
+
+       /**
+        * Commit the transaction started via beginTransaction, if any.
+        * This default implementation does nothing.
+        */
+       public function commitTransaction() {
+               //noop
+       }
+
+       /**
+        * Abort / roll back the transaction started via beginTransaction, if any.
+        * This default implementation does nothing.
+        */
+       public function rollbackTransaction() {
+               //noop
+       }
+
+       /**
+        * Convenience method, calls doUpdate() on every DataUpdate in the array.
+        *
+        * This methods supports transactions logic by first calling beginTransaction()
+        * on all updates in the array, then calling doUpdate() on each, and, if all goes well,
+        * then calling commitTransaction() on each update. If an error occurs,
+        * rollbackTransaction() will be called on any update object that had beginTransaction()
+        * called but not yet commitTransaction().
+        *
+        * This allows for limited transactional logic across multiple backends for storing
+        * secondary data.
+        *
+        * @param array $updates a list of DataUpdate instances
+        * @throws Exception|null
+        */
+       public static function runUpdates( $updates ) {
+               if ( empty( $updates ) ) {
+                       return; # nothing to do
+               }
+
+               $open_transactions = array();
+               $exception = null;
+
+               /**
+                * @var $update DataUpdate
+                * @var $trans DataUpdate
+                */
+
+               try {
+                       // begin transactions
+                       foreach ( $updates as $update ) {
+                               $update->beginTransaction();
+                               $open_transactions[] = $update;
+                       }
+
+                       // do work
+                       foreach ( $updates as $update ) {
+                               $update->doUpdate();
+                       }
+
+                       // commit transactions
+                       while ( count( $open_transactions ) > 0 ) {
+                               $trans = array_pop( $open_transactions );
+                               $trans->commitTransaction();
+                       }
+               } catch ( Exception $ex ) {
+                       $exception = $ex;
+                       wfDebug( "Caught exception, will rethrow after rollback: " . $ex->getMessage() );
+               }
+
+               // rollback remaining transactions
+               while ( count( $open_transactions ) > 0 ) {
+                       $trans = array_pop( $open_transactions );
+                       $trans->rollbackTransaction();
+               }
+
+               if ( $exception ) {
+                       throw $exception; // rethrow after cleanup
+               }
+       }
+
+}
diff --git a/includes/deferred/DeferredUpdates.php b/includes/deferred/DeferredUpdates.php
new file mode 100644 (file)
index 0000000..c385f13
--- /dev/null
@@ -0,0 +1,129 @@
+<?php
+/**
+ * Interface and manager for deferred updates.
+ *
+ * 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
+ */
+
+/**
+ * Interface that deferrable updates should implement. Basically required so we
+ * can validate input on DeferredUpdates::addUpdate()
+ *
+ * @since 1.19
+ */
+interface DeferrableUpdate {
+       /**
+        * Perform the actual work
+        */
+       function doUpdate();
+}
+
+/**
+ * Class for managing the deferred updates.
+ *
+ * @since 1.19
+ */
+class DeferredUpdates {
+       /**
+        * Store of updates to be deferred until the end of the request.
+        */
+       private static $updates = array();
+
+       /**
+        * Add an update to the deferred list
+        * @param $update DeferrableUpdate Some object that implements doUpdate()
+        */
+       public static function addUpdate( DeferrableUpdate $update ) {
+               array_push( self::$updates, $update );
+       }
+
+       /**
+        * HTMLCacheUpdates are the most common deferred update people use. This
+        * is a shortcut method for that.
+        * @see HTMLCacheUpdate::__construct()
+        * @param $title
+        * @param $table
+        */
+       public static function addHTMLCacheUpdate( $title, $table ) {
+               self::addUpdate( new HTMLCacheUpdate( $title, $table ) );
+       }
+
+       /**
+        * Add a callable update.  In a lot of cases, we just need a callback/closure,
+        * defining a new DeferrableUpdate object is not necessary
+        * @see MWCallableUpdate::__construct()
+        * @param callable $callable
+        */
+       public static function addCallableUpdate( $callable ) {
+               self::addUpdate( new MWCallableUpdate( $callable ) );
+       }
+
+       /**
+        * Do any deferred updates and clear the list
+        *
+        * @param string $commit set to 'commit' to commit after every update to
+        *                prevent lock contention
+        */
+       public static function doUpdates( $commit = '' ) {
+               global $wgDeferredUpdateList;
+
+               wfProfileIn( __METHOD__ );
+
+               $updates = array_merge( $wgDeferredUpdateList, self::$updates );
+
+               // No need to get master connections in case of empty updates array
+               if ( !count( $updates ) ) {
+                       wfProfileOut( __METHOD__ );
+                       return;
+               }
+
+               $doCommit = $commit == 'commit';
+               if ( $doCommit ) {
+                       $dbw = wfGetDB( DB_MASTER );
+               }
+
+               foreach ( $updates as $update ) {
+                       try {
+                               $update->doUpdate();
+
+                               if ( $doCommit && $dbw->trxLevel() ) {
+                                       $dbw->commit( __METHOD__, 'flush' );
+                               }
+                       } catch ( MWException $e ) {
+                               // We don't want exceptions thrown during deferred updates to
+                               // be reported to the user since the output is already sent.
+                               // Instead we just log them.
+                               if ( !$e instanceof ErrorPageError ) {
+                                       MWExceptionHandler::logException( $e );
+                               }
+                       }
+               }
+
+               self::clearPendingUpdates();
+               wfProfileOut( __METHOD__ );
+       }
+
+       /**
+        * Clear all pending updates without performing them. Generally, you don't
+        * want or need to call this. Unit tests need it though.
+        */
+       public static function clearPendingUpdates() {
+               global $wgDeferredUpdateList;
+               $wgDeferredUpdateList = self::$updates = array();
+       }
+}
diff --git a/includes/deferred/HTMLCacheUpdate.php b/includes/deferred/HTMLCacheUpdate.php
new file mode 100644 (file)
index 0000000..4147424
--- /dev/null
@@ -0,0 +1,71 @@
+<?php
+/**
+ * HTML cache invalidation of all pages linking to a given title.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup Cache
+ */
+
+/**
+ * Class to invalidate the HTML cache of all the pages linking to a given title.
+ *
+ * @ingroup Cache
+ */
+class HTMLCacheUpdate implements DeferrableUpdate {
+       /**
+        * @var Title
+        */
+       public $mTitle;
+
+       public $mTable;
+
+       /**
+        * @param $titleTo
+        * @param $table
+        */
+       function __construct( Title $titleTo, $table ) {
+               $this->mTitle = $titleTo;
+               $this->mTable = $table;
+       }
+
+       public function doUpdate() {
+               wfProfileIn( __METHOD__ );
+
+               $job = new HTMLCacheUpdateJob(
+                       $this->mTitle,
+                       array(
+                               'table' => $this->mTable,
+                       ) + Job::newRootJobParams( // "overall" refresh links job info
+                               "htmlCacheUpdate:{$this->mTable}:{$this->mTitle->getPrefixedText()}"
+                       )
+               );
+
+               $count = $this->mTitle->getBacklinkCache()->getNumLinks( $this->mTable, 200 );
+               if ( $count >= 200 ) { // many backlinks
+                       JobQueueGroup::singleton()->push( $job );
+                       JobQueueGroup::singleton()->deduplicateRootJob( $job );
+               } else { // few backlinks ($count might be off even if 0)
+                       $dbw = wfGetDB( DB_MASTER );
+                       $dbw->onTransactionIdle( function() use ( $job ) {
+                               $job->run(); // just do the purge query now
+                       } );
+               }
+
+               wfProfileOut( __METHOD__ );
+       }
+}
diff --git a/includes/deferred/LinksUpdate.php b/includes/deferred/LinksUpdate.php
new file mode 100644 (file)
index 0000000..fdd0e3c
--- /dev/null
@@ -0,0 +1,892 @@
+<?php
+/**
+ * Updater for link tracking tables after a page edit.
+ *
+ * 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
+ */
+
+/**
+ * See docs/deferred.txt
+ *
+ * @todo document (e.g. one-sentence top-level class description).
+ */
+class LinksUpdate extends SqlDataUpdate {
+
+       // @todo make members protected, but make sure extensions don't break
+
+       public $mId,         //!< Page ID of the article linked from
+               $mTitle,         //!< Title object of the article linked from
+               $mParserOutput,  //!< Parser output
+               $mLinks,         //!< Map of title strings to IDs for the links in the document
+               $mImages,        //!< DB keys of the images used, in the array key only
+               $mTemplates,     //!< Map of title strings to IDs for the template references, including broken ones
+               $mExternals,     //!< URLs of external links, array key only
+               $mCategories,    //!< Map of category names to sort keys
+               $mInterlangs,    //!< Map of language codes to titles
+               $mProperties,    //!< Map of arbitrary name to value
+               $mDb,            //!< Database connection reference
+               $mOptions,       //!< SELECT options to be used (array)
+               $mRecursive;     //!< Whether to queue jobs for recursive updates
+
+       /**
+        * @var null|array Added links if calculated.
+        */
+       private $linkInsertions = null;
+
+       /**
+        * @var null|array Deleted links if calculated.
+        */
+       private $linkDeletions = null;
+
+       /**
+        * Constructor
+        *
+        * @param $title Title of the page we're updating
+        * @param $parserOutput ParserOutput: output from a full parse of this page
+        * @param $recursive Boolean: queue jobs for recursive updates?
+        * @throws MWException
+        */
+       function __construct( $title, $parserOutput, $recursive = true ) {
+               parent::__construct( false ); // no implicit transaction
+
+               if ( !( $title instanceof Title ) ) {
+                       throw new MWException( "The calling convention to LinksUpdate::LinksUpdate() has changed. " .
+                               "Please see Article::editUpdates() for an invocation example.\n" );
+               }
+
+               if ( !( $parserOutput instanceof ParserOutput ) ) {
+                       throw new MWException( "The calling convention to LinksUpdate::__construct() has changed. " .
+                               "Please see WikiPage::doEditUpdates() for an invocation example.\n" );
+               }
+
+               $this->mTitle = $title;
+               $this->mId = $title->getArticleID();
+
+               if ( !$this->mId ) {
+                       throw new MWException( "The Title object did not provide an article ID. Perhaps the page doesn't exist?" );
+               }
+
+               $this->mParserOutput = $parserOutput;
+
+               $this->mLinks = $parserOutput->getLinks();
+               $this->mImages = $parserOutput->getImages();
+               $this->mTemplates = $parserOutput->getTemplates();
+               $this->mExternals = $parserOutput->getExternalLinks();
+               $this->mCategories = $parserOutput->getCategories();
+               $this->mProperties = $parserOutput->getProperties();
+               $this->mInterwikis = $parserOutput->getInterwikiLinks();
+
+               # Convert the format of the interlanguage links
+               # I didn't want to change it in the ParserOutput, because that array is passed all
+               # the way back to the skin, so either a skin API break would be required, or an
+               # inefficient back-conversion.
+               $ill = $parserOutput->getLanguageLinks();
+               $this->mInterlangs = array();
+               foreach ( $ill as $link ) {
+                       list( $key, $title ) = explode( ':', $link, 2 );
+                       $this->mInterlangs[$key] = $title;
+               }
+
+               foreach ( $this->mCategories as &$sortkey ) {
+                       # If the sortkey is longer then 255 bytes,
+                       # it truncated by DB, and then doesn't get
+                       # matched when comparing existing vs current
+                       # categories, causing bug 25254.
+                       # Also. substr behaves weird when given "".
+                       if ( $sortkey !== '' ) {
+                               $sortkey = substr( $sortkey, 0, 255 );
+                       }
+               }
+
+               $this->mRecursive = $recursive;
+
+               wfRunHooks( 'LinksUpdateConstructed', array( &$this ) );
+       }
+
+       /**
+        * Update link tables with outgoing links from an updated article
+        */
+       public function doUpdate() {
+               wfRunHooks( 'LinksUpdate', array( &$this ) );
+               $this->doIncrementalUpdate();
+               wfRunHooks( 'LinksUpdateComplete', array( &$this ) );
+       }
+
+       protected function doIncrementalUpdate() {
+               wfProfileIn( __METHOD__ );
+
+               # Page links
+               $existing = $this->getExistingLinks();
+               $this->linkDeletions = $this->getLinkDeletions( $existing );
+               $this->linkInsertions = $this->getLinkInsertions( $existing );
+               $this->incrTableUpdate( 'pagelinks', 'pl', $this->linkDeletions, $this->linkInsertions );
+
+               # Image links
+               $existing = $this->getExistingImages();
+
+               $imageDeletes = $this->getImageDeletions( $existing );
+               $this->incrTableUpdate( 'imagelinks', 'il', $imageDeletes,
+                       $this->getImageInsertions( $existing ) );
+
+               # Invalidate all image description pages which had links added or removed
+               $imageUpdates = $imageDeletes + array_diff_key( $this->mImages, $existing );
+               $this->invalidateImageDescriptions( $imageUpdates );
+
+               # External links
+               $existing = $this->getExistingExternals();
+               $this->incrTableUpdate( 'externallinks', 'el', $this->getExternalDeletions( $existing ),
+                       $this->getExternalInsertions( $existing ) );
+
+               # Language links
+               $existing = $this->getExistingInterlangs();
+               $this->incrTableUpdate( 'langlinks', 'll', $this->getInterlangDeletions( $existing ),
+                       $this->getInterlangInsertions( $existing ) );
+
+               # Inline interwiki links
+               $existing = $this->getExistingInterwikis();
+               $this->incrTableUpdate( 'iwlinks', 'iwl', $this->getInterwikiDeletions( $existing ),
+                       $this->getInterwikiInsertions( $existing ) );
+
+               # Template links
+               $existing = $this->getExistingTemplates();
+               $this->incrTableUpdate( 'templatelinks', 'tl', $this->getTemplateDeletions( $existing ),
+                       $this->getTemplateInsertions( $existing ) );
+
+               # Category links
+               $existing = $this->getExistingCategories();
+
+               $categoryDeletes = $this->getCategoryDeletions( $existing );
+
+               $this->incrTableUpdate( 'categorylinks', 'cl', $categoryDeletes,
+                       $this->getCategoryInsertions( $existing ) );
+
+               # Invalidate all categories which were added, deleted or changed (set symmetric difference)
+               $categoryInserts = array_diff_assoc( $this->mCategories, $existing );
+               $categoryUpdates = $categoryInserts + $categoryDeletes;
+               $this->invalidateCategories( $categoryUpdates );
+               $this->updateCategoryCounts( $categoryInserts, $categoryDeletes );
+
+               # Page properties
+               $existing = $this->getExistingProperties();
+
+               $propertiesDeletes = $this->getPropertyDeletions( $existing );
+
+               $this->incrTableUpdate( 'page_props', 'pp', $propertiesDeletes,
+                       $this->getPropertyInsertions( $existing ) );
+
+               # Invalidate the necessary pages
+               $changed = $propertiesDeletes + array_diff_assoc( $this->mProperties, $existing );
+               $this->invalidateProperties( $changed );
+
+               # Refresh links of all pages including this page
+               # This will be in a separate transaction
+               if ( $this->mRecursive ) {
+                       $this->queueRecursiveJobs();
+               }
+
+               wfProfileOut( __METHOD__ );
+       }
+
+       /**
+        * Queue recursive jobs for this page
+        *
+        * Which means do LinksUpdate on all templates
+        * that include the current page, using the job queue.
+        */
+       function queueRecursiveJobs() {
+               self::queueRecursiveJobsForTable( $this->mTitle, 'templatelinks' );
+       }
+
+       /**
+        * Queue a RefreshLinks job for any table.
+        *
+        * @param Title $title Title to do job for
+        * @param String $table Table to use (e.g. 'templatelinks')
+        */
+       public static function queueRecursiveJobsForTable( Title $title, $table ) {
+               wfProfileIn( __METHOD__ );
+               if ( $title->getBacklinkCache()->hasLinks( $table ) ) {
+                       $job = new RefreshLinksJob2(
+                               $title,
+                               array(
+                                       'table' => $table,
+                               ) + Job::newRootJobParams( // "overall" refresh links job info
+                                       "refreshlinks:{$table}:{$title->getPrefixedText()}"
+                               )
+                       );
+                       JobQueueGroup::singleton()->push( $job );
+                       JobQueueGroup::singleton()->deduplicateRootJob( $job );
+               }
+               wfProfileOut( __METHOD__ );
+       }
+
+       /**
+        * @param $cats
+        */
+       function invalidateCategories( $cats ) {
+               $this->invalidatePages( NS_CATEGORY, array_keys( $cats ) );
+       }
+
+       /**
+        * Update all the appropriate counts in the category table.
+        * @param array $added associative array of category name => sort key
+        * @param array $deleted associative array of category name => sort key
+        */
+       function updateCategoryCounts( $added, $deleted ) {
+               $a = WikiPage::factory( $this->mTitle );
+               $a->updateCategoryCounts(
+                       array_keys( $added ), array_keys( $deleted )
+               );
+       }
+
+       /**
+        * @param $images
+        */
+       function invalidateImageDescriptions( $images ) {
+               $this->invalidatePages( NS_FILE, array_keys( $images ) );
+       }
+
+       /**
+        * Update a table by doing a delete query then an insert query
+        * @param $table
+        * @param $prefix
+        * @param $deletions
+        * @param $insertions
+        */
+       function incrTableUpdate( $table, $prefix, $deletions, $insertions ) {
+               if ( $table == 'page_props' ) {
+                       $fromField = 'pp_page';
+               } else {
+                       $fromField = "{$prefix}_from";
+               }
+               $where = array( $fromField => $this->mId );
+               if ( $table == 'pagelinks' || $table == 'templatelinks' || $table == 'iwlinks' ) {
+                       if ( $table == 'iwlinks' ) {
+                               $baseKey = 'iwl_prefix';
+                       } else {
+                               $baseKey = "{$prefix}_namespace";
+                       }
+                       $clause = $this->mDb->makeWhereFrom2d( $deletions, $baseKey, "{$prefix}_title" );
+                       if ( $clause ) {
+                               $where[] = $clause;
+                       } else {
+                               $where = false;
+                       }
+               } else {
+                       if ( $table == 'langlinks' ) {
+                               $toField = 'll_lang';
+                       } elseif ( $table == 'page_props' ) {
+                               $toField = 'pp_propname';
+                       } else {
+                               $toField = $prefix . '_to';
+                       }
+                       if ( count( $deletions ) ) {
+                               $where[] = "$toField IN (" . $this->mDb->makeList( array_keys( $deletions ) ) . ')';
+                       } else {
+                               $where = false;
+                       }
+               }
+               if ( $where ) {
+                       $this->mDb->delete( $table, $where, __METHOD__ );
+               }
+               if ( count( $insertions ) ) {
+                       $this->mDb->insert( $table, $insertions, __METHOD__, 'IGNORE' );
+                       wfRunHooks( 'LinksUpdateAfterInsert', array( $this, $table, $insertions ) );
+               }
+       }
+
+       /**
+        * Get an array of pagelinks insertions for passing to the DB
+        * Skips the titles specified by the 2-D array $existing
+        * @param $existing array
+        * @return array
+        */
+       private function getLinkInsertions( $existing = array() ) {
+               $arr = array();
+               foreach ( $this->mLinks as $ns => $dbkeys ) {
+                       $diffs = isset( $existing[$ns] )
+                               ? array_diff_key( $dbkeys, $existing[$ns] )
+                               : $dbkeys;
+                       foreach ( $diffs as $dbk => $id ) {
+                               $arr[] = array(
+                                       'pl_from' => $this->mId,
+                                       'pl_namespace' => $ns,
+                                       'pl_title' => $dbk
+                               );
+                       }
+               }
+               return $arr;
+       }
+
+       /**
+        * Get an array of template insertions. Like getLinkInsertions()
+        * @param $existing array
+        * @return array
+        */
+       private function getTemplateInsertions( $existing = array() ) {
+               $arr = array();
+               foreach ( $this->mTemplates as $ns => $dbkeys ) {
+                       $diffs = isset( $existing[$ns] ) ? array_diff_key( $dbkeys, $existing[$ns] ) : $dbkeys;
+                       foreach ( $diffs as $dbk => $id ) {
+                               $arr[] = array(
+                                       'tl_from' => $this->mId,
+                                       'tl_namespace' => $ns,
+                                       'tl_title' => $dbk
+                               );
+                       }
+               }
+               return $arr;
+       }
+
+       /**
+        * Get an array of image insertions
+        * Skips the names specified in $existing
+        * @param $existing array
+        * @return array
+        */
+       private function getImageInsertions( $existing = array() ) {
+               $arr = array();
+               $diffs = array_diff_key( $this->mImages, $existing );
+               foreach ( $diffs as $iname => $dummy ) {
+                       $arr[] = array(
+                               'il_from' => $this->mId,
+                               'il_to' => $iname
+                       );
+               }
+               return $arr;
+       }
+
+       /**
+        * Get an array of externallinks insertions. Skips the names specified in $existing
+        * @param $existing array
+        * @return array
+        */
+       private function getExternalInsertions( $existing = array() ) {
+               $arr = array();
+               $diffs = array_diff_key( $this->mExternals, $existing );
+               foreach ( $diffs as $url => $dummy ) {
+                       foreach ( wfMakeUrlIndexes( $url ) as $index ) {
+                               $arr[] = array(
+                                       'el_from' => $this->mId,
+                                       'el_to' => $url,
+                                       'el_index' => $index,
+                               );
+                       }
+               }
+               return $arr;
+       }
+
+       /**
+        * Get an array of category insertions
+        *
+        * @param array $existing mapping existing category names to sort keys. If both
+        * match a link in $this, the link will be omitted from the output
+        *
+        * @return array
+        */
+       private function getCategoryInsertions( $existing = array() ) {
+               global $wgContLang, $wgCategoryCollation;
+               $diffs = array_diff_assoc( $this->mCategories, $existing );
+               $arr = array();
+               foreach ( $diffs as $name => $prefix ) {
+                       $nt = Title::makeTitleSafe( NS_CATEGORY, $name );
+                       $wgContLang->findVariantLink( $name, $nt, true );
+
+                       if ( $this->mTitle->getNamespace() == NS_CATEGORY ) {
+                               $type = 'subcat';
+                       } elseif ( $this->mTitle->getNamespace() == NS_FILE ) {
+                               $type = 'file';
+                       } else {
+                               $type = 'page';
+                       }
+
+                       # Treat custom sortkeys as a prefix, so that if multiple
+                       # things are forced to sort as '*' or something, they'll
+                       # sort properly in the category rather than in page_id
+                       # order or such.
+                       $sortkey = Collation::singleton()->getSortKey(
+                               $this->mTitle->getCategorySortkey( $prefix ) );
+
+                       $arr[] = array(
+                               'cl_from' => $this->mId,
+                               'cl_to' => $name,
+                               'cl_sortkey' => $sortkey,
+                               'cl_timestamp' => $this->mDb->timestamp(),
+                               'cl_sortkey_prefix' => $prefix,
+                               'cl_collation' => $wgCategoryCollation,
+                               'cl_type' => $type,
+                       );
+               }
+               return $arr;
+       }
+
+       /**
+        * Get an array of interlanguage link insertions
+        *
+        * @param array $existing mapping existing language codes to titles
+        *
+        * @return array
+        */
+       private function getInterlangInsertions( $existing = array() ) {
+               $diffs = array_diff_assoc( $this->mInterlangs, $existing );
+               $arr = array();
+               foreach ( $diffs as $lang => $title ) {
+                       $arr[] = array(
+                               'll_from' => $this->mId,
+                               'll_lang' => $lang,
+                               'll_title' => $title
+                       );
+               }
+               return $arr;
+       }
+
+       /**
+        * Get an array of page property insertions
+        * @param $existing array
+        * @return array
+        */
+       function getPropertyInsertions( $existing = array() ) {
+               $diffs = array_diff_assoc( $this->mProperties, $existing );
+               $arr = array();
+               foreach ( $diffs as $name => $value ) {
+                       $arr[] = array(
+                               'pp_page' => $this->mId,
+                               'pp_propname' => $name,
+                               'pp_value' => $value,
+                       );
+               }
+               return $arr;
+       }
+
+       /**
+        * Get an array of interwiki insertions for passing to the DB
+        * Skips the titles specified by the 2-D array $existing
+        * @param $existing array
+        * @return array
+        */
+       private function getInterwikiInsertions( $existing = array() ) {
+               $arr = array();
+               foreach ( $this->mInterwikis as $prefix => $dbkeys ) {
+                       $diffs = isset( $existing[$prefix] ) ? array_diff_key( $dbkeys, $existing[$prefix] ) : $dbkeys;
+                       foreach ( $diffs as $dbk => $id ) {
+                               $arr[] = array(
+                                       'iwl_from' => $this->mId,
+                                       'iwl_prefix' => $prefix,
+                                       'iwl_title' => $dbk
+                               );
+                       }
+               }
+               return $arr;
+       }
+
+       /**
+        * Given an array of existing links, returns those links which are not in $this
+        * and thus should be deleted.
+        * @param $existing array
+        * @return array
+        */
+       private function getLinkDeletions( $existing ) {
+               $del = array();
+               foreach ( $existing as $ns => $dbkeys ) {
+                       if ( isset( $this->mLinks[$ns] ) ) {
+                               $del[$ns] = array_diff_key( $existing[$ns], $this->mLinks[$ns] );
+                       } else {
+                               $del[$ns] = $existing[$ns];
+                       }
+               }
+               return $del;
+       }
+
+       /**
+        * Given an array of existing templates, returns those templates which are not in $this
+        * and thus should be deleted.
+        * @param $existing array
+        * @return array
+        */
+       private function getTemplateDeletions( $existing ) {
+               $del = array();
+               foreach ( $existing as $ns => $dbkeys ) {
+                       if ( isset( $this->mTemplates[$ns] ) ) {
+                               $del[$ns] = array_diff_key( $existing[$ns], $this->mTemplates[$ns] );
+                       } else {
+                               $del[$ns] = $existing[$ns];
+                       }
+               }
+               return $del;
+       }
+
+       /**
+        * Given an array of existing images, returns those images which are not in $this
+        * and thus should be deleted.
+        * @param $existing array
+        * @return array
+        */
+       private function getImageDeletions( $existing ) {
+               return array_diff_key( $existing, $this->mImages );
+       }
+
+       /**
+        * Given an array of existing external links, returns those links which are not
+        * in $this and thus should be deleted.
+        * @param $existing array
+        * @return array
+        */
+       private function getExternalDeletions( $existing ) {
+               return array_diff_key( $existing, $this->mExternals );
+       }
+
+       /**
+        * Given an array of existing categories, returns those categories which are not in $this
+        * and thus should be deleted.
+        * @param $existing array
+        * @return array
+        */
+       private function getCategoryDeletions( $existing ) {
+               return array_diff_assoc( $existing, $this->mCategories );
+       }
+
+       /**
+        * Given an array of existing interlanguage links, returns those links which are not
+        * in $this and thus should be deleted.
+        * @param $existing array
+        * @return array
+        */
+       private function getInterlangDeletions( $existing ) {
+               return array_diff_assoc( $existing, $this->mInterlangs );
+       }
+
+       /**
+        * Get array of properties which should be deleted.
+        * @param $existing array
+        * @return array
+        */
+       function getPropertyDeletions( $existing ) {
+               return array_diff_assoc( $existing, $this->mProperties );
+       }
+
+       /**
+        * Given an array of existing interwiki links, returns those links which are not in $this
+        * and thus should be deleted.
+        * @param $existing array
+        * @return array
+        */
+       private function getInterwikiDeletions( $existing ) {
+               $del = array();
+               foreach ( $existing as $prefix => $dbkeys ) {
+                       if ( isset( $this->mInterwikis[$prefix] ) ) {
+                               $del[$prefix] = array_diff_key( $existing[$prefix], $this->mInterwikis[$prefix] );
+                       } else {
+                               $del[$prefix] = $existing[$prefix];
+                       }
+               }
+               return $del;
+       }
+
+       /**
+        * Get an array of existing links, as a 2-D array
+        *
+        * @return array
+        */
+       private function getExistingLinks() {
+               $res = $this->mDb->select( 'pagelinks', array( 'pl_namespace', 'pl_title' ),
+                       array( 'pl_from' => $this->mId ), __METHOD__, $this->mOptions );
+               $arr = array();
+               foreach ( $res as $row ) {
+                       if ( !isset( $arr[$row->pl_namespace] ) ) {
+                               $arr[$row->pl_namespace] = array();
+                       }
+                       $arr[$row->pl_namespace][$row->pl_title] = 1;
+               }
+               return $arr;
+       }
+
+       /**
+        * Get an array of existing templates, as a 2-D array
+        *
+        * @return array
+        */
+       private function getExistingTemplates() {
+               $res = $this->mDb->select( 'templatelinks', array( 'tl_namespace', 'tl_title' ),
+                       array( 'tl_from' => $this->mId ), __METHOD__, $this->mOptions );
+               $arr = array();
+               foreach ( $res as $row ) {
+                       if ( !isset( $arr[$row->tl_namespace] ) ) {
+                               $arr[$row->tl_namespace] = array();
+                       }
+                       $arr[$row->tl_namespace][$row->tl_title] = 1;
+               }
+               return $arr;
+       }
+
+       /**
+        * Get an array of existing images, image names in the keys
+        *
+        * @return array
+        */
+       private function getExistingImages() {
+               $res = $this->mDb->select( 'imagelinks', array( 'il_to' ),
+                       array( 'il_from' => $this->mId ), __METHOD__, $this->mOptions );
+               $arr = array();
+               foreach ( $res as $row ) {
+                       $arr[$row->il_to] = 1;
+               }
+               return $arr;
+       }
+
+       /**
+        * Get an array of existing external links, URLs in the keys
+        *
+        * @return array
+        */
+       private function getExistingExternals() {
+               $res = $this->mDb->select( 'externallinks', array( 'el_to' ),
+                       array( 'el_from' => $this->mId ), __METHOD__, $this->mOptions );
+               $arr = array();
+               foreach ( $res as $row ) {
+                       $arr[$row->el_to] = 1;
+               }
+               return $arr;
+       }
+
+       /**
+        * Get an array of existing categories, with the name in the key and sort key in the value.
+        *
+        * @return array
+        */
+       private function getExistingCategories() {
+               $res = $this->mDb->select( 'categorylinks', array( 'cl_to', 'cl_sortkey_prefix' ),
+                       array( 'cl_from' => $this->mId ), __METHOD__, $this->mOptions );
+               $arr = array();
+               foreach ( $res as $row ) {
+                       $arr[$row->cl_to] = $row->cl_sortkey_prefix;
+               }
+               return $arr;
+       }
+
+       /**
+        * Get an array of existing interlanguage links, with the language code in the key and the
+        * title in the value.
+        *
+        * @return array
+        */
+       private function getExistingInterlangs() {
+               $res = $this->mDb->select( 'langlinks', array( 'll_lang', 'll_title' ),
+                       array( 'll_from' => $this->mId ), __METHOD__, $this->mOptions );
+               $arr = array();
+               foreach ( $res as $row ) {
+                       $arr[$row->ll_lang] = $row->ll_title;
+               }
+               return $arr;
+       }
+
+       /**
+        * Get an array of existing inline interwiki links, as a 2-D array
+        * @return array (prefix => array(dbkey => 1))
+        */
+       protected function getExistingInterwikis() {
+               $res = $this->mDb->select( 'iwlinks', array( 'iwl_prefix', 'iwl_title' ),
+                       array( 'iwl_from' => $this->mId ), __METHOD__, $this->mOptions );
+               $arr = array();
+               foreach ( $res as $row ) {
+                       if ( !isset( $arr[$row->iwl_prefix] ) ) {
+                               $arr[$row->iwl_prefix] = array();
+                       }
+                       $arr[$row->iwl_prefix][$row->iwl_title] = 1;
+               }
+               return $arr;
+       }
+
+       /**
+        * Get an array of existing categories, with the name in the key and sort key in the value.
+        *
+        * @return array
+        */
+       private function getExistingProperties() {
+               $res = $this->mDb->select( 'page_props', array( 'pp_propname', 'pp_value' ),
+                       array( 'pp_page' => $this->mId ), __METHOD__, $this->mOptions );
+               $arr = array();
+               foreach ( $res as $row ) {
+                       $arr[$row->pp_propname] = $row->pp_value;
+               }
+               return $arr;
+       }
+
+       /**
+        * Return the title object of the page being updated
+        * @return Title
+        */
+       public function getTitle() {
+               return $this->mTitle;
+       }
+
+       /**
+        * Returns parser output
+        * @since 1.19
+        * @return ParserOutput
+        */
+       public function getParserOutput() {
+               return $this->mParserOutput;
+       }
+
+       /**
+        * Return the list of images used as generated by the parser
+        * @return array
+        */
+       public function getImages() {
+               return $this->mImages;
+       }
+
+       /**
+        * Invalidate any necessary link lists related to page property changes
+        * @param $changed
+        */
+       private function invalidateProperties( $changed ) {
+               global $wgPagePropLinkInvalidations;
+
+               foreach ( $changed as $name => $value ) {
+                       if ( isset( $wgPagePropLinkInvalidations[$name] ) ) {
+                               $inv = $wgPagePropLinkInvalidations[$name];
+                               if ( !is_array( $inv ) ) {
+                                       $inv = array( $inv );
+                               }
+                               foreach ( $inv as $table ) {
+                                       $update = new HTMLCacheUpdate( $this->mTitle, $table );
+                                       $update->doUpdate();
+                               }
+                       }
+               }
+       }
+
+       /**
+        * Fetch page links added by this LinksUpdate.  Only available after the update is complete.
+        * @since 1.22
+        * @return null|array of Titles
+        */
+       public function getAddedLinks() {
+               if ( $this->linkInsertions === null ) {
+                       return null;
+               }
+               $result = array();
+               foreach ( $this->linkInsertions as $insertion ) {
+                       $result[] = Title::makeTitle( $insertion[ 'pl_namespace' ], $insertion[ 'pl_title' ] );
+               }
+               return $result;
+       }
+
+       /**
+        * Fetch page links removed by this LinksUpdate.  Only available after the update is complete.
+        * @since 1.22
+        * @return null|array of Titles
+        */
+       public function getRemovedLinks() {
+               if ( $this->linkDeletions === null ) {
+                       return null;
+               }
+               $result = array();
+               foreach ( $this->linkDeletions as $ns => $titles ) {
+                       foreach ( $titles as $title => $unused ) {
+                               $result[] = Title::makeTitle( $ns, $title );
+                       }
+               }
+               return $result;
+       }
+}
+
+/**
+ * Update object handling the cleanup of links tables after a page was deleted.
+ **/
+class LinksDeletionUpdate extends SqlDataUpdate {
+
+       protected $mPage;     //!< WikiPage the wikipage that was deleted
+
+       /**
+        * Constructor
+        *
+        * @param $page WikiPage Page we are updating
+        * @throws MWException
+        */
+       function __construct( WikiPage $page ) {
+               parent::__construct( false ); // no implicit transaction
+
+               $this->mPage = $page;
+
+               if ( !$page->exists() ) {
+                       throw new MWException( "Page ID not known, perhaps the page doesn't exist?" );
+               }
+       }
+
+       /**
+        * Do some database updates after deletion
+        */
+       public function doUpdate() {
+               $title = $this->mPage->getTitle();
+               $id = $this->mPage->getId();
+
+               # Delete restrictions for it
+               $this->mDb->delete( 'page_restrictions', array( 'pr_page' => $id ), __METHOD__ );
+
+               # Fix category table counts
+               $cats = array();
+               $res = $this->mDb->select( 'categorylinks', 'cl_to', array( 'cl_from' => $id ), __METHOD__ );
+
+               foreach ( $res as $row ) {
+                       $cats[] = $row->cl_to;
+               }
+
+               $this->mPage->updateCategoryCounts( array(), $cats );
+
+               # If using cascading deletes, we can skip some explicit deletes
+               if ( !$this->mDb->cascadingDeletes() ) {
+                       # Delete outgoing links
+                       $this->mDb->delete( 'pagelinks', array( 'pl_from' => $id ), __METHOD__ );
+                       $this->mDb->delete( 'imagelinks', array( 'il_from' => $id ), __METHOD__ );
+                       $this->mDb->delete( 'categorylinks', array( 'cl_from' => $id ), __METHOD__ );
+                       $this->mDb->delete( 'templatelinks', array( 'tl_from' => $id ), __METHOD__ );
+                       $this->mDb->delete( 'externallinks', array( 'el_from' => $id ), __METHOD__ );
+                       $this->mDb->delete( 'langlinks', array( 'll_from' => $id ), __METHOD__ );
+                       $this->mDb->delete( 'iwlinks', array( 'iwl_from' => $id ), __METHOD__ );
+                       $this->mDb->delete( 'redirect', array( 'rd_from' => $id ), __METHOD__ );
+                       $this->mDb->delete( 'page_props', array( 'pp_page' => $id ), __METHOD__ );
+               }
+
+               # If using cleanup triggers, we can skip some manual deletes
+               if ( !$this->mDb->cleanupTriggers() ) {
+                       # Clean up recentchanges entries...
+                       $this->mDb->delete( 'recentchanges',
+                               array( 'rc_type != ' . RC_LOG,
+                                       'rc_namespace' => $title->getNamespace(),
+                                       'rc_title' => $title->getDBkey() ),
+                               __METHOD__ );
+                       $this->mDb->delete( 'recentchanges',
+                               array( 'rc_type != ' . RC_LOG, 'rc_cur_id' => $id ),
+                               __METHOD__ );
+               }
+       }
+
+       /**
+        * Update all the appropriate counts in the category table.
+        * @param array $added associative array of category name => sort key
+        * @param array $deleted associative array of category name => sort key
+        */
+       function updateCategoryCounts( $added, $deleted ) {
+               $a = WikiPage::factory( $this->mTitle );
+               $a->updateCategoryCounts(
+                       array_keys( $added ), array_keys( $deleted )
+               );
+       }
+}
diff --git a/includes/deferred/SearchUpdate.php b/includes/deferred/SearchUpdate.php
new file mode 100644 (file)
index 0000000..82a413e
--- /dev/null
@@ -0,0 +1,185 @@
+<?php
+/**
+ * Search index updater
+ *
+ * See deferred.txt
+ *
+ * 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 Search
+ */
+
+/**
+ * Database independant search index updater
+ *
+ * @ingroup Search
+ */
+class SearchUpdate implements DeferrableUpdate {
+       /**
+        * Page id being updated
+        * @var int
+        */
+       private $id = 0;
+
+       /**
+        * Title we're updating
+        * @var Title
+        */
+       private $title;
+
+       /**
+        * Content of the page (not text)
+        * @var Content|false
+        */
+       private $content;
+
+       /**
+        * Constructor
+        *
+        * @param int $id Page id to update
+        * @param Title|string $title Title of page to update
+        * @param Content|string|false $c Content of the page to update.
+        *  If a Content object, text will be gotten from it. String is for back-compat.
+        *  Passing false tells the backend to just update the title, not the content
+        */
+       public function __construct( $id, $title, $c = false ) {
+               if ( is_string( $title ) ) {
+                       $nt = Title::newFromText( $title );
+               } else {
+                       $nt = $title;
+               }
+
+               if ( $nt ) {
+                       $this->id = $id;
+                       // is_string() check is back-compat for ApprovedRevs
+                       if ( is_string( $c ) ) {
+                               $this->content = new TextContent( $c );
+                       } else {
+                               $this->content = $c ?: false;
+                       }
+                       $this->title = $nt;
+               } else {
+                       wfDebug( "SearchUpdate object created with invalid title '$title'\n" );
+               }
+       }
+
+       /**
+        * Perform actual update for the entry
+        */
+       public function doUpdate() {
+               global $wgDisableSearchUpdate;
+
+               if ( $wgDisableSearchUpdate || !$this->id ) {
+                       return;
+               }
+
+               wfProfileIn( __METHOD__ );
+
+               $page = WikiPage::newFromId( $this->id, WikiPage::READ_LATEST );
+               $indexTitle = Title::indexTitle( $this->title->getNamespace(), $this->title->getText() );
+
+               foreach ( SearchEngine::getSearchTypes() as $type ) {
+                       $search = SearchEngine::create( $type );
+                       if ( !$search->supports( 'search-update' ) ) {
+                               continue;
+                       }
+
+                       $normalTitle = $search->normalizeText( $indexTitle );
+
+                       if ( $page === null ) {
+                               $search->delete( $this->id, $normalTitle );
+                               continue;
+                       } elseif ( $this->content === false ) {
+                               $search->updateTitle( $this->id, $normalTitle );
+                               continue;
+                       }
+
+                       $text = $search->getTextFromContent( $this->title, $this->content );
+                       if ( !$search->textAlreadyUpdatedForIndex() ) {
+                               $text = self::updateText( $text );
+                       }
+
+                       # Perform the actual update
+                       $search->update( $this->id, $normalTitle, $search->normalizeText( $text ) );
+               }
+
+               wfProfileOut( __METHOD__ );
+       }
+
+       /**
+        * Clean text for indexing. Only really suitable for indexing in databases.
+        * If you're using a real search engine, you'll probably want to override
+        * this behavior and do something nicer with the original wikitext.
+        */
+       public static function updateText( $text ) {
+               global $wgContLang;
+
+               # Language-specific strip/conversion
+               $text = $wgContLang->normalizeForSearch( $text );
+               $lc = SearchEngine::legalSearchChars() . '&#;';
+
+               wfProfileIn( __METHOD__ . '-regexps' );
+               $text = preg_replace( "/<\\/?\\s*[A-Za-z][^>]*?>/",
+                       ' ', $wgContLang->lc( " " . $text . " " ) ); # Strip HTML markup
+               $text = preg_replace( "/(^|\\n)==\\s*([^\\n]+)\\s*==(\\s)/sD",
+                       "\\1\\2 \\2 \\2\\3", $text ); # Emphasize headings
+
+               # Strip external URLs
+               $uc = "A-Za-z0-9_\\/:.,~%\\-+&;#?!=()@\\x80-\\xFF";
+               $protos = "http|https|ftp|mailto|news|gopher";
+               $pat = "/(^|[^\\[])({$protos}):[{$uc}]+([^{$uc}]|$)/";
+               $text = preg_replace( $pat, "\\1 \\3", $text );
+
+               $p1 = "/([^\\[])\\[({$protos}):[{$uc}]+]/";
+               $p2 = "/([^\\[])\\[({$protos}):[{$uc}]+\\s+([^\\]]+)]/";
+               $text = preg_replace( $p1, "\\1 ", $text );
+               $text = preg_replace( $p2, "\\1 \\3 ", $text );
+
+               # Internal image links
+               $pat2 = "/\\[\\[image:([{$uc}]+)\\.(gif|png|jpg|jpeg)([^{$uc}])/i";
+               $text = preg_replace( $pat2, " \\1 \\3", $text );
+
+               $text = preg_replace( "/([^{$lc}])([{$lc}]+)]]([a-z]+)/",
+                       "\\1\\2 \\2\\3", $text ); # Handle [[game]]s
+
+               # Strip all remaining non-search characters
+               $text = preg_replace( "/[^{$lc}]+/", " ", $text );
+
+               # Handle 's, s'
+               #
+               #   $text = preg_replace( "/([{$lc}]+)'s /", "\\1 \\1's ", $text );
+               #   $text = preg_replace( "/([{$lc}]+)s' /", "\\1s ", $text );
+               #
+               # These tail-anchored regexps are insanely slow. The worst case comes
+               # when Japanese or Chinese text (ie, no word spacing) is written on
+               # a wiki configured for Western UTF-8 mode. The Unicode characters are
+               # expanded to hex codes and the "words" are very long paragraph-length
+               # monstrosities. On a large page the above regexps may take over 20
+               # seconds *each* on a 1GHz-level processor.
+               #
+               # Following are reversed versions which are consistently fast
+               # (about 3 milliseconds on 1GHz-level processor).
+               #
+               $text = strrev( preg_replace( "/ s'([{$lc}]+)/", " s'\\1 \\1", strrev( $text ) ) );
+               $text = strrev( preg_replace( "/ 's([{$lc}]+)/", " s\\1", strrev( $text ) ) );
+
+               # Strip wiki '' and '''
+               $text = preg_replace( "/''[']*/", " ", $text );
+               wfProfileOut( __METHOD__ . '-regexps' );
+               return $text;
+       }
+}
diff --git a/includes/deferred/SiteStatsUpdate.php b/includes/deferred/SiteStatsUpdate.php
new file mode 100644 (file)
index 0000000..09ff87d
--- /dev/null
@@ -0,0 +1,245 @@
+<?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
+ */
+
+/**
+ * Class for handling updates to the site_stats table
+ */
+class SiteStatsUpdate implements DeferrableUpdate {
+       protected $views = 0;
+       protected $edits = 0;
+       protected $pages = 0;
+       protected $articles = 0;
+       protected $users = 0;
+       protected $images = 0;
+
+       // @todo deprecate this constructor
+       function __construct( $views, $edits, $good, $pages = 0, $users = 0 ) {
+               $this->views = $views;
+               $this->edits = $edits;
+               $this->articles = $good;
+               $this->pages = $pages;
+               $this->users = $users;
+       }
+
+       /**
+        * @param $deltas Array
+        * @return SiteStatsUpdate
+        */
+       public static function factory( array $deltas ) {
+               $update = new self( 0, 0, 0 );
+
+               $fields = array( 'views', 'edits', 'pages', 'articles', 'users', 'images' );
+               foreach ( $fields as $field ) {
+                       if ( isset( $deltas[$field] ) && $deltas[$field] ) {
+                               $update->$field = $deltas[$field];
+                       }
+               }
+
+               return $update;
+       }
+
+       public function doUpdate() {
+               global $wgSiteStatsAsyncFactor;
+
+               $rate = $wgSiteStatsAsyncFactor; // convenience
+               // If set to do so, only do actual DB updates 1 every $rate times.
+               // The other times, just update "pending delta" values in memcached.
+               if ( $rate && ( $rate < 0 || mt_rand( 0, $rate - 1 ) != 0 ) ) {
+                       $this->doUpdatePendingDeltas();
+               } else {
+                       // Need a separate transaction because this a global lock
+                       wfGetDB( DB_MASTER )->onTransactionIdle( array( $this, 'tryDBUpdateInternal' ) );
+               }
+       }
+
+       /**
+        * Do not call this outside of SiteStatsUpdate
+        *
+        * @return void
+        */
+       public function tryDBUpdateInternal() {
+               global $wgSiteStatsAsyncFactor;
+
+               $dbw = wfGetDB( DB_MASTER );
+               $lockKey = wfMemcKey( 'site_stats' ); // prepend wiki ID
+               if ( $wgSiteStatsAsyncFactor ) {
+                       // Lock the table so we don't have double DB/memcached updates
+                       if ( !$dbw->lockIsFree( $lockKey, __METHOD__ )
+                               || !$dbw->lock( $lockKey, __METHOD__, 1 ) // 1 sec timeout
+                       ) {
+                               $this->doUpdatePendingDeltas();
+                               return;
+                       }
+                       $pd = $this->getPendingDeltas();
+                       // Piggy-back the async deltas onto those of this stats update....
+                       $this->views += ( $pd['ss_total_views']['+'] - $pd['ss_total_views']['-'] );
+                       $this->edits += ( $pd['ss_total_edits']['+'] - $pd['ss_total_edits']['-'] );
+                       $this->articles += ( $pd['ss_good_articles']['+'] - $pd['ss_good_articles']['-'] );
+                       $this->pages += ( $pd['ss_total_pages']['+'] - $pd['ss_total_pages']['-'] );
+                       $this->users += ( $pd['ss_users']['+'] - $pd['ss_users']['-'] );
+                       $this->images += ( $pd['ss_images']['+'] - $pd['ss_images']['-'] );
+               }
+
+               // Build up an SQL query of deltas and apply them...
+               $updates = '';
+               $this->appendUpdate( $updates, 'ss_total_views', $this->views );
+               $this->appendUpdate( $updates, 'ss_total_edits', $this->edits );
+               $this->appendUpdate( $updates, 'ss_good_articles', $this->articles );
+               $this->appendUpdate( $updates, 'ss_total_pages', $this->pages );
+               $this->appendUpdate( $updates, 'ss_users', $this->users );
+               $this->appendUpdate( $updates, 'ss_images', $this->images );
+               if ( $updates != '' ) {
+                       $dbw->update( 'site_stats', array( $updates ), array(), __METHOD__ );
+               }
+
+               if ( $wgSiteStatsAsyncFactor ) {
+                       // Decrement the async deltas now that we applied them
+                       $this->removePendingDeltas( $pd );
+                       // Commit the updates and unlock the table
+                       $dbw->unlock( $lockKey, __METHOD__ );
+               }
+       }
+
+       /**
+        * @param $dbw DatabaseBase
+        * @return bool|mixed
+        */
+       public static function cacheUpdate( $dbw ) {
+               global $wgActiveUserDays;
+               $dbr = wfGetDB( DB_SLAVE, array( 'SpecialStatistics', 'vslow' ) );
+               # Get non-bot users than did some recent action other than making accounts.
+               # If account creation is included, the number gets inflated ~20+ fold on enwiki.
+               $activeUsers = $dbr->selectField(
+                       'recentchanges',
+                       'COUNT( DISTINCT rc_user_text )',
+                       array(
+                               'rc_user != 0',
+                               'rc_bot' => 0,
+                               'rc_log_type != ' . $dbr->addQuotes( 'newusers' ) . ' OR rc_log_type IS NULL',
+                               'rc_timestamp >= ' . $dbr->addQuotes( $dbr->timestamp( wfTimestamp( TS_UNIX ) - $wgActiveUserDays * 24 * 3600 ) ),
+                       ),
+                       __METHOD__
+               );
+               $dbw->update(
+                       'site_stats',
+                       array( 'ss_active_users' => intval( $activeUsers ) ),
+                       array( 'ss_row_id' => 1 ),
+                       __METHOD__
+               );
+               return $activeUsers;
+       }
+
+       protected function doUpdatePendingDeltas() {
+               $this->adjustPending( 'ss_total_views', $this->views );
+               $this->adjustPending( 'ss_total_edits', $this->edits );
+               $this->adjustPending( 'ss_good_articles', $this->articles );
+               $this->adjustPending( 'ss_total_pages', $this->pages );
+               $this->adjustPending( 'ss_users', $this->users );
+               $this->adjustPending( 'ss_images', $this->images );
+       }
+
+       /**
+        * @param $sql string
+        * @param $field string
+        * @param $delta integer
+        */
+       protected function appendUpdate( &$sql, $field, $delta ) {
+               if ( $delta ) {
+                       if ( $sql ) {
+                               $sql .= ',';
+                       }
+                       if ( $delta < 0 ) {
+                               $sql .= "$field=$field-" . abs( $delta );
+                       } else {
+                               $sql .= "$field=$field+" . abs( $delta );
+                       }
+               }
+       }
+
+       /**
+        * @param $type string
+        * @param string $sign ('+' or '-')
+        * @return string
+        */
+       private function getTypeCacheKey( $type, $sign ) {
+               return wfMemcKey( 'sitestatsupdate', 'pendingdelta', $type, $sign );
+       }
+
+       /**
+        * Adjust the pending deltas for a stat type.
+        * Each stat type has two pending counters, one for increments and decrements
+        * @param $type string
+        * @param $delta integer Delta (positive or negative)
+        * @return void
+        */
+       protected function adjustPending( $type, $delta ) {
+               global $wgMemc;
+
+               if ( $delta < 0 ) { // decrement
+                       $key = $this->getTypeCacheKey( $type, '-' );
+               } else { // increment
+                       $key = $this->getTypeCacheKey( $type, '+' );
+               }
+
+               $magnitude = abs( $delta );
+               if ( !$wgMemc->incr( $key, $magnitude ) ) { // not there?
+                       if ( !$wgMemc->add( $key, $magnitude ) ) { // race?
+                               $wgMemc->incr( $key, $magnitude );
+                       }
+               }
+       }
+
+       /**
+        * Get pending delta counters for each stat type
+        * @return Array Positive and negative deltas for each type
+        * @return void
+        */
+       protected function getPendingDeltas() {
+               global $wgMemc;
+
+               $pending = array();
+               foreach ( array( 'ss_total_views', 'ss_total_edits',
+                       'ss_good_articles', 'ss_total_pages', 'ss_users', 'ss_images' ) as $type )
+               {
+                       // Get pending increments and pending decrements
+                       $pending[$type]['+'] = (int)$wgMemc->get( $this->getTypeCacheKey( $type, '+' ) );
+                       $pending[$type]['-'] = (int)$wgMemc->get( $this->getTypeCacheKey( $type, '-' ) );
+               }
+
+               return $pending;
+       }
+
+       /**
+        * Reduce pending delta counters after updates have been applied
+        * @param array $pd Result of getPendingDeltas(), used for DB update
+        * @return void
+        */
+       protected function removePendingDeltas( array $pd ) {
+               global $wgMemc;
+
+               foreach ( $pd as $type => $deltas ) {
+                       foreach ( $deltas as $sign => $magnitude ) {
+                               // Lower the pending counter now that we applied these changes
+                               $wgMemc->decr( $this->getTypeCacheKey( $type, $sign ), $magnitude );
+                       }
+               }
+       }
+}
+
diff --git a/includes/deferred/SqlDataUpdate.php b/includes/deferred/SqlDataUpdate.php
new file mode 100644 (file)
index 0000000..51188d8
--- /dev/null
@@ -0,0 +1,152 @@
+<?php
+/**
+ * Base code for update jobs that put some secondary data extracted
+ * from article content into the database.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * 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
+ */
+
+/**
+ * Abstract base class for update jobs that put some secondary data extracted
+ * from article content into the database.
+ *
+ * @note: subclasses should NOT start or commit transactions in their doUpdate() method,
+ *        a transaction will automatically be wrapped around the update. Starting another
+ *        one would break the outer transaction bracket. If need be, subclasses can override
+ *        the beginTransaction() and commitTransaction() methods.
+ */
+abstract class SqlDataUpdate extends DataUpdate {
+
+       protected $mDb;            //!< Database connection reference
+       protected $mOptions;       //!< SELECT options to be used (array)
+
+       private   $mHasTransaction; //!< bool whether a transaction is open on this object (internal use only!)
+       protected $mUseTransaction; //!< bool whether this update should be wrapped in a transaction
+
+       /**
+        * Constructor
+        *
+        * @param bool $withTransaction whether this update should be wrapped in a transaction (default: true).
+        *             A transaction is only started if no transaction is already in progress,
+        *             see beginTransaction() for details.
+        **/
+       public function __construct( $withTransaction = true ) {
+               global $wgAntiLockFlags;
+
+               parent::__construct();
+
+               if ( $wgAntiLockFlags & ALF_NO_LINK_LOCK ) {
+                       $this->mOptions = array();
+               } else {
+                       $this->mOptions = array( 'FOR UPDATE' );
+               }
+
+               // @todo get connection only when it's needed? make sure that doesn't break anything, especially transactions!
+               $this->mDb = wfGetDB( DB_MASTER );
+
+               $this->mWithTransaction = $withTransaction;
+               $this->mHasTransaction = false;
+       }
+
+       /**
+        * Begin a database transaction, if $withTransaction was given as true in the constructor for this SqlDataUpdate.
+        *
+        * Because nested transactions are not supported by the Database class, this implementation
+        * checks Database::trxLevel() and only opens a transaction if none is already active.
+        */
+       public function beginTransaction() {
+               if ( !$this->mWithTransaction ) {
+                       return;
+               }
+
+               // NOTE: nested transactions are not supported, only start a transaction if none is open
+               if ( $this->mDb->trxLevel() === 0 ) {
+                       $this->mDb->begin( get_class( $this ) . '::beginTransaction' );
+                       $this->mHasTransaction = true;
+               }
+       }
+
+       /**
+        * Commit the database transaction started via beginTransaction (if any).
+        */
+       public function commitTransaction() {
+               if ( $this->mHasTransaction ) {
+                       $this->mDb->commit( get_class( $this ) . '::commitTransaction' );
+                       $this->mHasTransaction = false;
+               }
+       }
+
+       /**
+        * Abort the database transaction started via beginTransaction (if any).
+        */
+       public function abortTransaction() {
+               if ( $this->mHasTransaction ) { //XXX: actually... maybe always?
+                       $this->mDb->rollback( get_class( $this ) . '::abortTransaction' );
+                       $this->mHasTransaction = false;
+               }
+       }
+
+       /**
+        * Invalidate the cache of a list of pages from a single namespace.
+        * This is intended for use by subclasses.
+        *
+        * @param $namespace Integer
+        * @param $dbkeys Array
+        */
+       protected function invalidatePages( $namespace, array $dbkeys ) {
+               if ( $dbkeys === array() ) {
+                       return;
+               }
+
+               /**
+                * Determine which pages need to be updated
+                * This is necessary to prevent the job queue from smashing the DB with
+                * large numbers of concurrent invalidations of the same page
+                */
+               $now = $this->mDb->timestamp();
+               $ids = array();
+               $res = $this->mDb->select( 'page', array( 'page_id' ),
+                       array(
+                               'page_namespace' => $namespace,
+                               'page_title' => $dbkeys,
+                               'page_touched < ' . $this->mDb->addQuotes( $now )
+                       ), __METHOD__
+               );
+
+               foreach ( $res as $row ) {
+                       $ids[] = $row->page_id;
+               }
+
+               if ( $ids === array() ) {
+                       return;
+               }
+
+               /**
+                * Do the update
+                * We still need the page_touched condition, in case the row has changed since
+                * the non-locking select above.
+                */
+               $this->mDb->update( 'page', array( 'page_touched' => $now ),
+                       array(
+                               'page_id' => $ids,
+                               'page_touched < ' . $this->mDb->addQuotes( $now )
+                       ), __METHOD__
+               );
+       }
+
+}
diff --git a/includes/deferred/SquidUpdate.php b/includes/deferred/SquidUpdate.php
new file mode 100644 (file)
index 0000000..71afeba
--- /dev/null
@@ -0,0 +1,300 @@
+<?php
+/**
+ * Squid cache purging.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup Cache
+ */
+
+/**
+ * Handles purging appropriate Squid URLs given a title (or titles)
+ * @ingroup Cache
+ */
+class SquidUpdate {
+
+       /**
+        * Collection of URLs to purge.
+        * @var array
+        */
+       protected $urlArr;
+
+       /**
+        * @param array $urlArr Collection of URLs to purge
+        * @param bool|int $maxTitles Maximum number of unique URLs to purge
+        */
+       public function __construct( $urlArr = array(), $maxTitles = false ) {
+               global $wgMaxSquidPurgeTitles;
+               if ( $maxTitles === false ) {
+                       $maxTitles = $wgMaxSquidPurgeTitles;
+               }
+
+               // Remove duplicate URLs from list
+               $urlArr = array_unique( $urlArr );
+               if ( count( $urlArr ) > $maxTitles ) {
+                       // Truncate to desired maximum URL count
+                       $urlArr = array_slice( $urlArr, 0, $maxTitles );
+               }
+               $this->urlArr = $urlArr;
+       }
+
+       /**
+        * Create a SquidUpdate from the given Title object.
+        *
+        * The resulting SquidUpdate will purge the given Title's URLs as well as
+        * the pages that link to it. Capped at $wgMaxSquidPurgeTitles total URLs.
+        *
+        * @param Title $title
+        * @return SquidUpdate
+        */
+       public static function newFromLinksTo( Title $title ) {
+               global $wgMaxSquidPurgeTitles;
+               wfProfileIn( __METHOD__ );
+
+               # Get a list of URLs linking to this page
+               $dbr = wfGetDB( DB_SLAVE );
+               $res = $dbr->select( array( 'links', 'page' ),
+                       array( 'page_namespace', 'page_title' ),
+                       array(
+                               'pl_namespace' => $title->getNamespace(),
+                               'pl_title' => $title->getDBkey(),
+                               'pl_from=page_id' ),
+                       __METHOD__ );
+               $blurlArr = $title->getSquidURLs();
+               if ( $res->numRows() <= $wgMaxSquidPurgeTitles ) {
+                       foreach ( $res as $BL ) {
+                               $tobj = Title::makeTitle( $BL->page_namespace, $BL->page_title );
+                               $blurlArr[] = $tobj->getInternalURL();
+                       }
+               }
+
+               wfProfileOut( __METHOD__ );
+               return new SquidUpdate( $blurlArr );
+       }
+
+       /**
+        * Create a SquidUpdate from an array of Title objects, or a TitleArray object
+        *
+        * @param array $titles
+        * @param array $urlArr
+        * @return SquidUpdate
+        */
+       public static function newFromTitles( $titles, $urlArr = array() ) {
+               global $wgMaxSquidPurgeTitles;
+               $i = 0;
+               foreach ( $titles as $title ) {
+                       $urlArr[] = $title->getInternalURL();
+                       if ( $i++ > $wgMaxSquidPurgeTitles ) {
+                               break;
+                       }
+               }
+               return new SquidUpdate( $urlArr );
+       }
+
+       /**
+        * @param Title $title
+        * @return SquidUpdate
+        */
+       public static function newSimplePurge( Title $title ) {
+               $urlArr = $title->getSquidURLs();
+               return new SquidUpdate( $urlArr );
+       }
+
+       /**
+        * Purges the list of URLs passed to the constructor.
+        */
+       public function doUpdate() {
+               self::purge( $this->urlArr );
+       }
+
+       /**
+        * Purges a list of Squids defined in $wgSquidServers.
+        * $urlArr should contain the full URLs to purge as values
+        * (example: $urlArr[] = 'http://my.host/something')
+        * XXX report broken Squids per mail or log
+        *
+        * @param array $urlArr List of full URLs to purge
+        */
+       public static function purge( $urlArr ) {
+               global $wgSquidServers, $wgHTCPRouting;
+
+               if ( !$urlArr ) {
+                       return;
+               }
+
+               wfDebugLog( 'squid', __METHOD__ . ': ' . implode( ' ', $urlArr ) . "\n" );
+
+               if ( $wgHTCPRouting ) {
+                       self::HTCPPurge( $urlArr );
+               }
+
+               wfProfileIn( __METHOD__ );
+
+               // Remove duplicate URLs
+               $urlArr = array_unique( $urlArr );
+               // Maximum number of parallel connections per squid
+               $maxSocketsPerSquid = 8;
+               // Number of requests to send per socket
+               // 400 seems to be a good tradeoff, opening a socket takes a while
+               $urlsPerSocket = 400;
+               $socketsPerSquid = ceil( count( $urlArr ) / $urlsPerSocket );
+               if ( $socketsPerSquid > $maxSocketsPerSquid ) {
+                       $socketsPerSquid = $maxSocketsPerSquid;
+               }
+
+               $pool = new SquidPurgeClientPool;
+               $chunks = array_chunk( $urlArr, ceil( count( $urlArr ) / $socketsPerSquid ) );
+               foreach ( $wgSquidServers as $server ) {
+                       foreach ( $chunks as $chunk ) {
+                               $client = new SquidPurgeClient( $server );
+                               foreach ( $chunk as $url ) {
+                                       $client->queuePurge( $url );
+                               }
+                               $pool->addClient( $client );
+                       }
+               }
+               $pool->run();
+
+               wfProfileOut( __METHOD__ );
+       }
+
+       /**
+        * Send Hyper Text Caching Protocol (HTCP) CLR requests.
+        *
+        * @throws MWException
+        * @param array $urlArr Collection of URLs to purge
+        */
+       public static function HTCPPurge( $urlArr ) {
+               global $wgHTCPRouting, $wgHTCPMulticastTTL;
+               wfProfileIn( __METHOD__ );
+
+               // HTCP CLR operation
+               $htcpOpCLR = 4;
+
+               // @todo FIXME: PHP doesn't support these socket constants (include/linux/in.h)
+               if ( !defined( "IPPROTO_IP" ) ) {
+                       define( "IPPROTO_IP", 0 );
+                       define( "IP_MULTICAST_LOOP", 34 );
+                       define( "IP_MULTICAST_TTL", 33 );
+               }
+
+               // pfsockopen doesn't work because we need set_sock_opt
+               $conn = socket_create( AF_INET, SOCK_DGRAM, SOL_UDP );
+               if ( ! $conn ) {
+                       $errstr = socket_strerror( socket_last_error() );
+                       wfDebugLog( 'squid', __METHOD__ .
+                               ": Error opening UDP socket: $errstr\n" );
+                       wfProfileOut( __METHOD__ );
+                       return;
+               }
+
+               // Set socket options
+               socket_set_option( $conn, IPPROTO_IP, IP_MULTICAST_LOOP, 0 );
+               if ( $wgHTCPMulticastTTL != 1 ) {
+                       // Set multicast time to live (hop count) option on socket
+                       socket_set_option( $conn, IPPROTO_IP, IP_MULTICAST_TTL,
+                               $wgHTCPMulticastTTL );
+               }
+
+               // Remove duplicate URLs from collection
+               $urlArr = array_unique( $urlArr );
+               foreach ( $urlArr as $url ) {
+                       if ( !is_string( $url ) ) {
+                               wfProfileOut( __METHOD__ );
+                               throw new MWException( 'Bad purge URL' );
+                       }
+                       $url = self::expand( $url );
+                       $conf = self::getRuleForURL( $url, $wgHTCPRouting );
+                       if ( !$conf ) {
+                               wfDebugLog( 'squid', __METHOD__ .
+                                       "No HTCP rule configured for URL {$url} , skipping\n" );
+                               continue;
+                       }
+
+                       if ( isset( $conf['host'] ) && isset( $conf['port'] ) ) {
+                               // Normalize single entries
+                               $conf = array( $conf );
+                       }
+                       foreach ( $conf as $subconf ) {
+                               if ( !isset( $subconf['host'] ) || !isset( $subconf['port'] ) ) {
+                                       wfProfileOut( __METHOD__ );
+                                       throw new MWException( "Invalid HTCP rule for URL $url\n" );
+                               }
+                       }
+
+                       // Construct a minimal HTCP request diagram
+                       // as per RFC 2756
+                       // Opcode 'CLR', no response desired, no auth
+                       $htcpTransID = rand();
+
+                       $htcpSpecifier = pack( 'na4na*na8n',
+                               4, 'HEAD', strlen( $url ), $url,
+                               8, 'HTTP/1.0', 0 );
+
+                       $htcpDataLen = 8 + 2 + strlen( $htcpSpecifier );
+                       $htcpLen = 4 + $htcpDataLen + 2;
+
+                       // Note! Squid gets the bit order of the first
+                       // word wrong, wrt the RFC. Apparently no other
+                       // implementation exists, so adapt to Squid
+                       $htcpPacket = pack( 'nxxnCxNxxa*n',
+                               $htcpLen, $htcpDataLen, $htcpOpCLR,
+                               $htcpTransID, $htcpSpecifier, 2 );
+
+                       wfDebugLog( 'squid', __METHOD__ .
+                               "Purging URL $url via HTCP\n" );
+                       foreach ( $conf as $subconf ) {
+                               socket_sendto( $conn, $htcpPacket, $htcpLen, 0,
+                                       $subconf['host'], $subconf['port'] );
+                       }
+               }
+               wfProfileOut( __METHOD__ );
+       }
+
+       /**
+        * Expand local URLs to fully-qualified URLs using the internal protocol
+        * and host defined in $wgInternalServer. Input that's already fully-
+        * qualified will be passed through unchanged.
+        *
+        * This is used to generate purge URLs that may be either local to the
+        * main wiki or include a non-native host, such as images hosted on a
+        * second internal server.
+        *
+        * Client functions should not need to call this.
+        *
+        * @param string $url
+        * @return string
+        */
+       public static function expand( $url ) {
+               return wfExpandUrl( $url, PROTO_INTERNAL );
+       }
+
+       /**
+        * Find the HTCP routing rule to use for a given URL.
+        * @param string $url URL to match
+        * @param array $rules Array of rules, see $wgHTCPRouting for format and behavior
+        * @return mixed Element of $rules that matched, or false if nothing matched
+        */
+       private static function getRuleForURL( $url, $rules ) {
+               foreach ( $rules as $regex => $routing ) {
+                       if ( $regex === '' || preg_match( $regex, $url ) ) {
+                               return $routing;
+                       }
+               }
+               return false;
+       }
+}
diff --git a/includes/deferred/ViewCountUpdate.php b/includes/deferred/ViewCountUpdate.php
new file mode 100644 (file)
index 0000000..22a4649
--- /dev/null
@@ -0,0 +1,105 @@
+<?php
+/**
+ * Update for the 'page_counter' field
+ *
+ * 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
+ */
+
+/**
+ * Update for the 'page_counter' field, when $wgDisableCounters is false.
+ *
+ * Depending on $wgHitcounterUpdateFreq, this will directly increment the
+ * 'page_counter' field or use the 'hitcounter' table and then collect the data
+ * from that table to update the 'page_counter' field in a batch operation.
+ */
+class ViewCountUpdate implements DeferrableUpdate {
+       protected $id;
+
+       /**
+        * Constructor
+        *
+        * @param $id Integer: page ID to increment the view count
+        */
+       public function __construct( $id ) {
+               $this->id = intval( $id );
+       }
+
+       /**
+        * Run the update
+        */
+       public function doUpdate() {
+               global $wgHitcounterUpdateFreq;
+
+               $dbw = wfGetDB( DB_MASTER );
+
+               if ( $wgHitcounterUpdateFreq <= 1 || $dbw->getType() == 'sqlite' ) {
+                       $dbw->update( 'page', array( 'page_counter = page_counter + 1' ), array( 'page_id' => $this->id ), __METHOD__ );
+                       return;
+               }
+
+               # Not important enough to warrant an error page in case of failure
+               try {
+                       $dbw->insert( 'hitcounter', array( 'hc_id' => $this->id ), __METHOD__ );
+                       $checkfreq = intval( $wgHitcounterUpdateFreq / 25 + 1 );
+                       if ( rand() % $checkfreq == 0 && $dbw->lastErrno() == 0 ) {
+                               $this->collect();
+                       }
+               } catch ( DBError $e ) {}
+       }
+
+       protected function collect() {
+               global $wgHitcounterUpdateFreq;
+
+               $dbw = wfGetDB( DB_MASTER );
+
+               $rown = $dbw->selectField( 'hitcounter', 'COUNT(*)', array(), __METHOD__ );
+
+               if ( $rown < $wgHitcounterUpdateFreq ) {
+                       return;
+               }
+
+               wfProfileIn( __METHOD__ . '-collect' );
+               $old_user_abort = ignore_user_abort( true );
+
+               $dbw->lockTables( array(), array( 'hitcounter' ), __METHOD__, false );
+
+               $dbType = $dbw->getType();
+               $tabletype = $dbType == 'mysql' ? "ENGINE=HEAP " : '';
+               $hitcounterTable = $dbw->tableName( 'hitcounter' );
+               $acchitsTable = $dbw->tableName( 'acchits' );
+               $pageTable = $dbw->tableName( 'page' );
+
+               $dbw->query( "CREATE TEMPORARY TABLE $acchitsTable $tabletype AS " .
+                       "SELECT hc_id,COUNT(*) AS hc_n FROM $hitcounterTable " .
+                       'GROUP BY hc_id', __METHOD__ );
+               $dbw->delete( 'hitcounter', '*', __METHOD__ );
+               $dbw->unlockTables( __METHOD__ );
+
+               if ( $dbType == 'mysql' ) {
+                       $dbw->query( "UPDATE $pageTable,$acchitsTable SET page_counter=page_counter + hc_n " .
+                               'WHERE page_id = hc_id', __METHOD__ );
+               } else {
+                       $dbw->query( "UPDATE $pageTable SET page_counter=page_counter + hc_n " .
+                               "FROM $acchitsTable WHERE page_id = hc_id", __METHOD__ );
+               }
+               $dbw->query( "DROP TABLE $acchitsTable", __METHOD__ );
+
+               ignore_user_abort( $old_user_abort );
+               wfProfileOut( __METHOD__ . '-collect' );
+       }
+}
index 174c1d6..298d724 100644 (file)
@@ -692,7 +692,7 @@ class Diff {
        /**
         * Check for empty diff.
         *
-        * @return bool True iff two sequences were identical.
+        * @return bool True if two sequences were identical.
         */
        function isEmpty() {
                foreach ( $this->edits as $edit ) {
index e436f58..39f8a47 100644 (file)
@@ -34,37 +34,40 @@ define( 'MW_DIFF_VERSION', '1.11a' );
  * @ingroup DifferenceEngine
  */
 class DifferenceEngine extends ContextSource {
-       /**#@+
-        * @private
-        */
-       var $mOldid, $mNewid;
-       var $mOldTags, $mNewTags;
+       public $mOldid;
+       public $mNewid;
+       private $mOldTags;
+       private $mNewTags;
+
        /**
         * @var Content
         */
-       var $mOldContent, $mNewContent;
+       public $mOldContent;
+       public $mNewContent;
        protected $mDiffLang;
 
        /**
         * @var Title
         */
-       var $mOldPage, $mNewPage;
+       public $mOldPage;
+       public $mNewPage;
 
        /**
         * @var Revision
         */
-       var $mOldRev, $mNewRev;
+       public $mOldRev;
+       public $mNewRev;
        private $mRevisionsIdsLoaded = false; // Have the revisions IDs been loaded
-       var $mRevisionsLoaded = false; // Have the revisions been loaded
-       var $mTextLoaded = 0; // How many text blobs have been loaded, 0, 1 or 2?
-       var $mCacheHit = false; // Was the diff fetched from cache?
+       public $mRevisionsLoaded = false; // Have the revisions been loaded
+       public $mTextLoaded = 0; // How many text blobs have been loaded, 0, 1 or 2?
+       public $mCacheHit = false; // Was the diff fetched from cache?
 
        /**
         * Set this to true to add debug info to the HTML output.
         * Warning: this may cause RSS readers to spuriously mark articles as "new"
         * (bug 20601)
         */
-       var $enableDebugComment = false;
+       public $enableDebugComment = false;
 
        // If true, line X is not displayed when X is 1, for example to increase
        // readability and conserve space with many small diffs.
@@ -80,14 +83,14 @@ class DifferenceEngine extends ContextSource {
         * Constructor
         * @param $context IContextSource context to use, anything else will be ignored
         * @param $old Integer old ID we want to show and diff with.
-        * @param $new String either 'prev' or 'next'.
+        * @param $new String|int either 'prev' or 'next'. Default: 0.
         * @param $rcid Integer Deprecated, no longer used!
         * @param $refreshCache boolean If set, refreshes the diff cache
         * @param $unhide boolean If set, allow viewing deleted revs
         */
        function __construct( $context = null, $old = 0, $new = 0, $rcid = 0,
-               $refreshCache = false, $unhide = false )
-       {
+               $refreshCache = false, $unhide = false
+       {
                if ( $context instanceof IContextSource ) {
                        $this->setContext( $context );
                }
@@ -259,8 +262,8 @@ class DifferenceEngine extends ContextSource {
                                $out->setPageTitle( $this->msg( 'difference-title', $this->mNewPage->getPrefixedText() ) );
                                $samePage = true;
                        } else {
-                               $out->setPageTitle( $this->msg( 'difference-title-multipage', $this->mOldPage->getPrefixedText(),
-                                       $this->mNewPage->getPrefixedText() ) );
+                               $out->setPageTitle( $this->msg( 'difference-title-multipage',
+                                       $this->mOldPage->getPrefixedText(), $this->mNewPage->getPrefixedText() ) );
                                $out->addSubtitle( $this->msg( 'difference-multipage' ) );
                                $samePage = false;
                        }
@@ -273,7 +276,10 @@ class DifferenceEngine extends ContextSource {
                                                $rollback = '&#160;&#160;&#160;' . $rollbackLink;
                                        }
                                }
-                               if ( !$this->mOldRev->isDeleted( Revision::DELETED_TEXT ) && !$this->mNewRev->isDeleted( Revision::DELETED_TEXT ) ) {
+
+                               if ( !$this->mOldRev->isDeleted( Revision::DELETED_TEXT ) &&
+                                       !$this->mNewRev->isDeleted( Revision::DELETED_TEXT )
+                               ) {
                                        $undoLink = Html::element( 'a', array(
                                                        'href' => $this->mNewPage->getLocalURL( array(
                                                                'action' => 'edit',
@@ -359,7 +365,8 @@ class DifferenceEngine extends ContextSource {
                foreach ( $revisionTools as $tool ) {
                        $formattedRevisionTools[] = $this->msg( 'parentheses' )->rawParams( $tool )->escaped();
                }
-               $newRevisionHeader = $this->getRevisionHeader( $this->mNewRev, 'complete' ) . ' ' . implode( ' ', $formattedRevisionTools );
+               $newRevisionHeader = $this->getRevisionHeader( $this->mNewRev, 'complete' ) .
+                       ' ' . implode( ' ', $formattedRevisionTools );
                $newChangeTags = ChangeTags::formatSummaryRow( $this->mNewTags, 'diff' );
 
                $newHeader = '<div id="mw-diff-ntitle1"><strong>' . $newRevisionHeader . '</strong></div>' .
@@ -390,9 +397,13 @@ class DifferenceEngine extends ContextSource {
                                        array( $msg ) );
                        } else {
                                # Give explanation and add a link to view the diff...
-                               $link = $this->getTitle()->getFullURL( $this->getRequest()->appendQueryValue( 'unhide', '1', true ) );
+                               $query = $this->getRequest()->appendQueryValue( 'unhide', '1', true );
+                               $link = $this->getTitle()->getFullURL( $query );
                                $msg = $suppressed ? 'rev-suppressed-unhide-diff' : 'rev-deleted-unhide-diff';
-                               $out->wrapWikiMsg( "<div id='mw-$msg' class='mw-warning plainlinks'>\n$1\n</div>\n", array( $msg, $link ) );
+                               $out->wrapWikiMsg(
+                                       "<div id='mw-$msg' class='mw-warning plainlinks'>\n$1\n</div>\n",
+                                       array( $msg, $link )
+                               );
                        }
                # Otherwise, output a regular diff...
                } else {
@@ -400,7 +411,9 @@ class DifferenceEngine extends ContextSource {
                        $notice = '';
                        if ( $deleted ) {
                                $msg = $suppressed ? 'rev-suppressed-diff-view' : 'rev-deleted-diff-view';
-                               $notice = "<div id='mw-$msg' class='mw-warning plainlinks'>\n" . $this->msg( $msg )->parse() . "</div>\n";
+                               $notice = "<div id='mw-$msg' class='mw-warning plainlinks'>\n" .
+                                       $this->msg( $msg )->parse() .
+                                       "</div>\n";
                        }
                        $this->showDiff( $oldHeader, $newHeader, $notice );
                        if ( !$diffOnly ) {
@@ -505,6 +518,7 @@ class DifferenceEngine extends ContextSource {
                $out->addHTML( "<hr class='diff-hr' />
                <h2 class='diff-currentversion-title'>{$revHeader}</h2>\n" );
                # Page content may be handled by a hooked call instead...
+               # @codingStandardsIgnoreStart Ignoring long lines.
                if ( wfRunHooks( 'ArticleContentOnDiff', array( $this, $out ) ) ) {
                        $this->loadNewText();
                        $out->setRevisionId( $this->mNewid );
@@ -560,6 +574,8 @@ class DifferenceEngine extends ContextSource {
                                }
                        }
                }
+               # @codingStandardsIgnoreEnd
+
                # Add redundant patrol link on bottom...
                $out->addHTML( $this->markPatrolledLink() );
 
@@ -581,6 +597,9 @@ class DifferenceEngine extends ContextSource {
         * Get the diff text, send it to the OutputPage object
         * Returns false if the diff could not be generated, otherwise returns true
         *
+        * @param string|bool $otitle Header for old text or false
+        * @param string|bool $ntitle Header for new text or false
+        * @param string $notice
         * @return bool
         */
        function showDiff( $otitle, $ntitle, $notice = '' ) {
@@ -614,14 +633,17 @@ class DifferenceEngine extends ContextSource {
                $body = $this->getDiffBody();
                if ( $body === false ) {
                        return false;
-               } else {
-                       $multi = $this->getMultiNotice();
-                       // Display a message when the diff is empty
-                       if ( $body === '' ) {
-                               $notice .= '<div class="mw-diff-empty">' . $this->msg( 'diff-empty' )->parse() . "</div>\n";
-                       }
-                       return $this->addHeader( $body, $otitle, $ntitle, $multi, $notice );
                }
+
+               $multi = $this->getMultiNotice();
+               // Display a message when the diff is empty
+               if ( $body === '' ) {
+                       $notice .= '<div class="mw-diff-empty">' .
+                               $this->msg( 'diff-empty' )->parse() .
+                               "</div>\n";
+               }
+
+               return $this->addHeader( $body, $otitle, $ntitle, $multi, $notice );
        }
 
        /**
@@ -637,10 +659,14 @@ class DifferenceEngine extends ContextSource {
                if ( !$this->loadRevisionData() ) {
                        wfProfileOut( __METHOD__ );
                        return false;
-               } elseif ( $this->mOldRev && !$this->mOldRev->userCan( Revision::DELETED_TEXT, $this->getUser() ) ) {
+               } elseif ( $this->mOldRev &&
+                       !$this->mOldRev->userCan( Revision::DELETED_TEXT, $this->getUser() )
+               ) {
                        wfProfileOut( __METHOD__ );
                        return false;
-               } elseif ( $this->mNewRev && !$this->mNewRev->userCan( Revision::DELETED_TEXT, $this->getUser() ) ) {
+               } elseif ( $this->mNewRev &&
+                       !$this->mNewRev->userCan( Revision::DELETED_TEXT, $this->getUser() )
+               ) {
                        wfProfileOut( __METHOD__ );
                        return false;
                }
@@ -654,8 +680,8 @@ class DifferenceEngine extends ContextSource {
                // Cacheable?
                $key = false;
                if ( $this->mOldid && $this->mNewid ) {
-                       $key = wfMemcKey( 'diff', 'version', MW_DIFF_VERSION,
-                               'oldid', $this->mOldid, 'newid', $this->mNewid );
+                       $key = $this->getDiffBodyCacheKey();
+
                        // Try cache
                        if ( !$this->mRefreshCache ) {
                                $difftext = $wgMemc->get( $key );
@@ -695,6 +721,22 @@ class DifferenceEngine extends ContextSource {
                return $difftext;
        }
 
+       /**
+        * Returns the cache key for diff body text or content.
+        *
+        * @return string
+        * @since 1.23
+        * @throws MWException
+        */
+       protected function getDiffBodyCacheKey() {
+               if ( !$this->mOldid || !$this->mNewid ) {
+                       throw new MWException( 'mOldid and mNewid must be set to get diff cache key.' );
+               }
+
+               return wfMemcKey( 'diff', 'version', MW_DIFF_VERSION,
+                       'oldid', $this->mOldid, 'newid', $this->mNewid );
+       }
+
        /**
         * Generate a diff, no caching.
         *
@@ -945,11 +987,16 @@ class DifferenceEngine extends ContextSource {
                                $editQuery['oldid'] = $rev->getID();
                        }
 
-                       $msg = $this->msg( $title->quickUserCan( 'edit', $user ) ? 'editold' : 'viewsourceold' )->escaped();
+                       $key = $title->quickUserCan( 'edit', $user ) ? 'editold' : 'viewsourceold';
+                       $msg = $this->msg( $key )->escaped();
                        $header .= ' ' . $this->msg( 'parentheses' )->rawParams(
                                Linker::linkKnown( $title, $msg, array(), $editQuery ) )->plain();
                        if ( $rev->isDeleted( Revision::DELETED_TEXT ) ) {
-                               $header = Html::rawElement( 'span', array( 'class' => 'history-deleted' ), $header );
+                               $header = Html::rawElement(
+                                       'span',
+                                       array( 'class' => 'history-deleted' ),
+                                       $header
+                               );
                        }
                } else {
                        $header = Html::rawElement( 'span', array( 'class' => 'history-deleted' ), $header );
@@ -998,7 +1045,8 @@ class DifferenceEngine extends ContextSource {
                }
 
                if ( $multi != '' ) {
-                       $header .= "<tr><td colspan='{$multiColspan}' style='text-align: center;' class='diff-multi'>{$multi}</td></tr>";
+                       $header .= "<tr><td colspan='{$multiColspan}' style='text-align: center;' " .
+                               "class='diff-multi'>{$multi}</td></tr>";
                }
                if ( $notice != '' ) {
                        $header .= "<tr><td colspan='{$multiColspan}' style='text-align: center;'>{$notice}</td></tr>";
@@ -1041,6 +1089,33 @@ class DifferenceEngine extends ContextSource {
                $this->mDiffLang = wfGetLangObj( $lang );
        }
 
+       /**
+        * Maps a revision pair definition as accepted by DifferenceEngine constructor
+        * to a pair of actual integers representing revision ids.
+        *
+        * @param int $old Revision id, e.g. from URL parameter 'oldid'
+        * @param int|string $new Revision id or strings 'next' or 'prev', e.g. from URL parameter 'diff'
+        * @return array Array of two revision ids, older first, later second.
+        *     Zero signifies invalid argument passed.
+        *     false signifies that there is no previous/next revision ($old is the oldest/newest one).
+        */
+       public function mapDiffPrevNext( $old, $new ) {
+               if ( $new === 'prev' ) {
+                       // Show diff between revision $old and the previous one. Get previous one from DB.
+                       $newid = intval( $old );
+                       $oldid = $this->getTitle()->getPreviousRevisionID( $newid );
+               } elseif ( $new === 'next' ) {
+                       // Show diff between revision $old and the next one. Get next one from DB.
+                       $oldid = intval( $old );
+                       $newid = $this->getTitle()->getNextRevisionID( $oldid );
+               } else {
+                       $oldid = intval( $old );
+                       $newid = intval( $new );
+               }
+
+               return array( $oldid, $newid );
+       }
+
        /**
         * Load revision IDs
         */
@@ -1054,26 +1129,14 @@ class DifferenceEngine extends ContextSource {
                $old = $this->mOldid;
                $new = $this->mNewid;
 
-               if ( $new === 'prev' ) {
-                       # Show diff between revision $old and the previous one.
-                       # Get previous one from DB.
-                       $this->mNewid = intval( $old );
-                       $this->mOldid = $this->getTitle()->getPreviousRevisionID( $this->mNewid );
-               } elseif ( $new === 'next' ) {
-                       # Show diff between revision $old and the next one.
-                       # Get next one from DB.
-                       $this->mOldid = intval( $old );
-                       $this->mNewid = $this->getTitle()->getNextRevisionID( $this->mOldid );
-                       if ( $this->mNewid === false ) {
-                               # if no result, NewId points to the newest old revision. The only newer
-                               # revision is cur, which is "0".
-                               $this->mNewid = 0;
-                       }
-               } else {
-                       $this->mOldid = intval( $old );
-                       $this->mNewid = intval( $new );
-                       wfRunHooks( 'NewDifferenceEngine', array( $this->getTitle(), &$this->mOldid, &$this->mNewid, $old, $new ) );
+               list( $this->mOldid, $this->mNewid ) = self::mapDiffPrevNext( $old, $new );
+               if ( $new === 'next' && $this->mNewid === false ) {
+                       # if no result, NewId points to the newest old revision. The only newer
+                       # revision is cur, which is "0".
+                       $this->mNewid = 0;
                }
+
+               wfRunHooks( 'NewDifferenceEngine', array( $this->getTitle(), &$this->mOldid, &$this->mNewid, $old, $new ) );
        }
 
        /**
@@ -1099,9 +1162,15 @@ class DifferenceEngine extends ContextSource {
                $this->loadRevisionIds();
 
                // Load the new revision object
-               $this->mNewRev = $this->mNewid
-                       ? Revision::newFromId( $this->mNewid )
-                       : Revision::newFromTitle( $this->getTitle(), false, Revision::READ_NORMAL );
+               if ( $this->mNewid ) {
+                       $this->mNewRev = Revision::newFromId( $this->mNewid );
+               } else {
+                       $this->mNewRev = Revision::newFromTitle(
+                               $this->getTitle(),
+                               false,
+                               Revision::READ_NORMAL
+                       );
+               }
 
                if ( !$this->mNewRev instanceof Revision ) {
                        return false;
index f586578..4bcaa7f 100644 (file)
@@ -807,7 +807,7 @@ abstract class FileBackend {
         * @return ScopedCallback|null
         */
        final protected function getScopedPHPBehaviorForOps() {
-               if ( php_sapi_name() != 'cli' ) { // http://bugs.php.net/bug.php?id=47540
+               if ( PHP_SAPI != 'cli' ) { // http://bugs.php.net/bug.php?id=47540
                        $old = ignore_user_abort( true ); // avoid half-finished operations
                        return new ScopedCallback( function() use ( $old ) {
                                ignore_user_abort( $old );
index 0921e99..29089c9 100644 (file)
@@ -1035,7 +1035,7 @@ abstract class FileBackendStore extends FileBackend {
                // Clear any file cache entries
                $this->clearCache();
 
-               $supportedOps = array( 'create', 'store', 'copy', 'move', 'delete', 'null' );
+               $supportedOps = array( 'create', 'store', 'copy', 'move', 'delete', 'describe', 'null' );
                $async = ( $this->parallelize === 'implicit' && count( $ops ) > 1 );
                $maxConcurrency = $this->concurrency; // throttle
 
index 33a5c9e..fe83308 100644 (file)
@@ -384,7 +384,7 @@ abstract class FileOp {
 
        /**
         * precheckDestExistence() helper function to get the source file SHA-1.
-        * Subclasses should overwride this iff the source is not in storage.
+        * Subclasses should overwride this if the source is not in storage.
         *
         * @return string|bool Returns false on failure
         */
index a620f88..db090a9 100644 (file)
@@ -24,7 +24,7 @@
  */
 
 /**
- * @brief Class for an OpenStack Swift based file backend.
+ * @brief Class for an OpenStack Swift (or Ceph RGW) based file backend.
  *
  * This requires the SwiftCloudFiles MediaWiki extension, which includes
  * the php-cloudfiles library (https://github.com/rackspace/php-cloudfiles).
index e081987..3e934ba 100644 (file)
@@ -110,6 +110,19 @@ abstract class DBLockManager extends QuorumLockManager {
                $this->session = wfRandomString( 31 );
        }
 
+       // @TODO: change this code to work in one batch
+       protected function getLocksOnServer( $lockSrv, array $pathsByType ) {
+               $status = Status::newGood();
+               foreach ( $pathsByType as $type => $paths ) {
+                       $status->merge( $this->doGetLocksOnServer( $lockSrv, $paths, $type ) );
+               }
+               return $status;
+       }
+
+       protected function freeLocksOnServer( $lockSrv, array $pathsByType ) {
+               return Status::newGood();
+       }
+
        /**
         * @see QuorumLockManager::isServerUp()
         * @return bool
@@ -252,7 +265,7 @@ class MySqlLockManager extends DBLockManager {
         * @see DBLockManager::getLocksOnServer()
         * @return Status
         */
-       protected function getLocksOnServer( $lockSrv, array $paths, $type ) {
+       protected function doGetLocksOnServer( $lockSrv, array $paths, $type ) {
                $status = Status::newGood();
 
                $db = $this->getConnection( $lockSrv ); // checked in isServerUp()
@@ -318,14 +331,6 @@ class MySqlLockManager extends DBLockManager {
                return $status;
        }
 
-       /**
-        * @see QuorumLockManager::freeLocksOnServer()
-        * @return Status
-        */
-       protected function freeLocksOnServer( $lockSrv, array $paths, $type ) {
-               return Status::newGood(); // not supported
-       }
-
        /**
         * @see QuorumLockManager::releaseAllLocks()
         * @return Status
@@ -361,7 +366,7 @@ class PostgreSqlLockManager extends DBLockManager {
                self::LOCK_EX => self::LOCK_EX
        );
 
-       protected function getLocksOnServer( $lockSrv, array $paths, $type ) {
+       protected function doGetLocksOnServer( $lockSrv, array $paths, $type ) {
                $status = Status::newGood();
                if ( !count( $paths ) ) {
                        return $status; // nothing to lock
@@ -407,14 +412,6 @@ class PostgreSqlLockManager extends DBLockManager {
                return $status;
        }
 
-       /**
-        * @see QuorumLockManager::freeLocksOnServer()
-        * @return Status
-        */
-       protected function freeLocksOnServer( $lockSrv, array $paths, $type ) {
-               return Status::newGood(); // not supported
-       }
-
        /**
         * @see QuorumLockManager::releaseAllLocks()
         * @return Status
index f0e54ec..dad8a62 100644 (file)
@@ -98,7 +98,7 @@ abstract class LockManager {
        /**
         * Lock the resources at the given abstract paths
         *
-        * @param array $paths Map of LockManager::LOCK_* constants to lists of storage paths
+        * @param array $pathsByType Map of LockManager::LOCK_* constants to lists of paths
         * @param integer $timeout Timeout in seconds (0 means non-blocking) (since 1.21)
         * @return Status
         * @since 1.22
@@ -125,7 +125,7 @@ abstract class LockManager {
        /**
         * Unlock the resources at the given abstract paths
         *
-        * @param array $paths List of storage paths
+        * @param array $paths List of paths
         * @param $type integer LockManager::LOCK_* constant
         * @return Status
         */
@@ -136,7 +136,7 @@ abstract class LockManager {
        /**
         * Unlock the resources at the given abstract paths
         *
-        * @param array $paths Map of LockManager::LOCK_* constants to lists of storage paths
+        * @param array $pathsByType Map of LockManager::LOCK_* constants to lists of paths
         * @return Status
         * @since 1.22
         */
@@ -176,7 +176,7 @@ abstract class LockManager {
         * Normalize the $paths array by converting LOCK_UW locks into the
         * appropriate type and removing any duplicated paths for each lock type.
         *
-        * @param array $paths Map of LockManager::LOCK_* constants to lists of storage paths
+        * @param array $paths Map of LockManager::LOCK_* constants to lists of paths
         * @return Array
         * @since 1.22
         */
@@ -190,7 +190,7 @@ abstract class LockManager {
 
        /**
         * @see LockManager::lockByType()
-        * @param array $paths Map of LockManager::LOCK_* constants to lists of storage paths
+        * @param array $paths Map of LockManager::LOCK_* constants to lists of paths
         * @return Status
         * @since 1.22
         */
@@ -215,7 +215,7 @@ abstract class LockManager {
        /**
         * Lock resources with the given keys and lock type
         *
-        * @param array $paths List of storage paths
+        * @param array $paths List of paths
         * @param $type integer LockManager::LOCK_* constant
         * @return Status
         */
@@ -223,7 +223,7 @@ abstract class LockManager {
 
        /**
         * @see LockManager::unlockByType()
-        * @param array $paths Map of LockManager::LOCK_* constants to lists of storage paths
+        * @param array $paths Map of LockManager::LOCK_* constants to lists of paths
         * @return Status
         * @since 1.22
         */
@@ -238,7 +238,7 @@ abstract class LockManager {
        /**
         * Unlock resources with the given keys and lock type
         *
-        * @param array $paths List of storage paths
+        * @param array $paths List of paths
         * @param $type integer LockManager::LOCK_* constant
         * @return Status
         */
@@ -250,22 +250,10 @@ abstract class LockManager {
  * @since 1.19
  */
 class NullLockManager extends LockManager {
-       /**
-        * @see LockManager::doLock()
-        * @param $paths array
-        * @param $type int
-        * @return Status
-        */
        protected function doLock( array $paths, $type ) {
                return Status::newGood();
        }
 
-       /**
-        * @see LockManager::doUnlock()
-        * @param $paths array
-        * @param $type int
-        * @return Status
-        */
        protected function doUnlock( array $paths, $type ) {
                return Status::newGood();
        }
index 757aeee..5eab03e 100644 (file)
@@ -88,11 +88,44 @@ class MemcLockManager extends QuorumLockManager {
                $this->session = wfRandomString( 32 );
        }
 
+       // @TODO: change this code to work in one batch
+       protected function getLocksOnServer( $lockSrv, array $pathsByType ) {
+               $status = Status::newGood();
+
+               $lockedPaths = array();
+               foreach ( $pathsByType as $type => $paths ) {
+                       $status->merge( $this->doGetLocksOnServer( $lockSrv, $paths, $type ) );
+                       if ( $status->isOK() ) {
+                               $lockedPaths[$type] = isset( $lockedPaths[$type] )
+                                       ? array_merge( $lockedPaths[$type], $paths )
+                                       : $paths;
+                       } else {
+                               foreach ( $lockedPaths as $type => $paths ) {
+                                       $status->merge( $this->doFreeLocksOnServer( $lockSrv, $paths, $type ) );
+                               }
+                               break;
+                       }
+               }
+
+               return $status;
+       }
+
+       // @TODO: change this code to work in one batch
+       protected function freeLocksOnServer( $lockSrv, array $pathsByType ) {
+               $status = Status::newGood();
+
+               foreach ( $pathsByType as $type => $paths ) {
+                       $status->merge( $this->doFreeLocksOnServer( $lockSrv, $paths, $type ) );
+               }
+
+               return $status;
+       }
+
        /**
         * @see QuorumLockManager::getLocksOnServer()
         * @return Status
         */
-       protected function getLocksOnServer( $lockSrv, array $paths, $type ) {
+       protected function doGetLocksOnServer( $lockSrv, array $paths, $type ) {
                $status = Status::newGood();
 
                $memc = $this->getCache( $lockSrv );
@@ -164,7 +197,7 @@ class MemcLockManager extends QuorumLockManager {
         * @see QuorumLockManager::freeLocksOnServer()
         * @return Status
         */
-       protected function freeLocksOnServer( $lockSrv, array $paths, $type ) {
+       protected function doFreeLocksOnServer( $lockSrv, array $paths, $type ) {
                $status = Status::newGood();
 
                $memc = $this->getCache( $lockSrv );
index 3b96ad6..8356d32 100644 (file)
 abstract class QuorumLockManager extends LockManager {
        /** @var Array Map of bucket indexes to peer server lists */
        protected $srvsByBucket = array(); // (bucket index => (lsrv1, lsrv2, ...))
+       /** @var Array Map of degraded buckets */
+       protected $degradedBuckets = array(); // (buckey index => UNIX timestamp)
 
-       /**
-        * @see LockManager::doLock()
-        * @param $paths array
-        * @param $type int
-        * @return Status
-        */
        final protected function doLock( array $paths, $type ) {
+               return $this->doLockByType( array( $type => $paths ) );
+       }
+
+       final protected function doUnlock( array $paths, $type ) {
+               return $this->doUnlockByType( array( $type => $paths ) );
+       }
+
+       protected function doLockByType( array $pathsByType ) {
                $status = Status::newGood();
 
-               $pathsToLock = array(); // (bucket => paths)
+               $pathsToLock = array(); // (bucket => type => paths)
                // Get locks that need to be acquired (buckets => locks)...
-               foreach ( $paths as $path ) {
-                       if ( isset( $this->locksHeld[$path][$type] ) ) {
-                               ++$this->locksHeld[$path][$type];
-                       } else {
-                               $bucket = $this->getBucketFromPath( $path );
-                               $pathsToLock[$bucket][] = $path;
+               foreach ( $pathsByType as $type => $paths ) {
+                       foreach ( $paths as $path ) {
+                               if ( isset( $this->locksHeld[$path][$type] ) ) {
+                                       ++$this->locksHeld[$path][$type];
+                               } else {
+                                       $bucket = $this->getBucketFromPath( $path );
+                                       $pathsToLock[$bucket][$type][] = $path;
+                               }
                        }
                }
 
-               $lockedPaths = array(); // files locked in this attempt
+               $lockedPaths = array(); // files locked in this attempt (type => paths)
                // Attempt to acquire these locks...
-               foreach ( $pathsToLock as $bucket => $paths ) {
+               foreach ( $pathsToLock as $bucket => $pathsToLockByType ) {
                        // Try to acquire the locks for this bucket
-                       $status->merge( $this->doLockingRequestBucket( $bucket, $paths, $type ) );
+                       $status->merge( $this->doLockingRequestBucket( $bucket, $pathsToLockByType ) );
                        if ( !$status->isOK() ) {
-                               $status->merge( $this->doUnlock( $lockedPaths, $type ) );
+                               $status->merge( $this->doUnlockByType( $lockedPaths ) );
                                return $status;
                        }
                        // Record these locks as active
-                       foreach ( $paths as $path ) {
-                               $this->locksHeld[$path][$type] = 1; // locked
+                       foreach ( $pathsToLockByType as $type => $paths ) {
+                               foreach ( $paths as $path ) {
+                                       $this->locksHeld[$path][$type] = 1; // locked
+                                       // Keep track of what locks were made in this attempt
+                                       $lockedPaths[$type][] = $path;
+                               }
                        }
-                       // Keep track of what locks were made in this attempt
-                       $lockedPaths = array_merge( $lockedPaths, $paths );
                }
 
                return $status;
        }
 
-       /**
-        * @see LockManager::doUnlock()
-        * @param $paths array
-        * @param $type int
-        * @return Status
-        */
-       final protected function doUnlock( array $paths, $type ) {
+       protected function doUnlockByType( array $pathsByType ) {
                $status = Status::newGood();
 
-               $pathsToUnlock = array();
-               foreach ( $paths as $path ) {
-                       if ( !isset( $this->locksHeld[$path][$type] ) ) {
-                               $status->warning( 'lockmanager-notlocked', $path );
-                       } else {
-                               --$this->locksHeld[$path][$type];
-                               // Reference count the locks held and release locks when zero
-                               if ( $this->locksHeld[$path][$type] <= 0 ) {
-                                       unset( $this->locksHeld[$path][$type] );
-                                       $bucket = $this->getBucketFromPath( $path );
-                                       $pathsToUnlock[$bucket][] = $path;
-                               }
-                               if ( !count( $this->locksHeld[$path] ) ) {
-                                       unset( $this->locksHeld[$path] ); // no SH or EX locks left for key
+               $pathsToUnlock = array(); // (bucket => type => paths)
+               foreach ( $pathsByType as $type => $paths ) {
+                       foreach ( $paths as $path ) {
+                               if ( !isset( $this->locksHeld[$path][$type] ) ) {
+                                       $status->warning( 'lockmanager-notlocked', $path );
+                               } else {
+                                       --$this->locksHeld[$path][$type];
+                                       // Reference count the locks held and release locks when zero
+                                       if ( $this->locksHeld[$path][$type] <= 0 ) {
+                                               unset( $this->locksHeld[$path][$type] );
+                                               $bucket = $this->getBucketFromPath( $path );
+                                               $pathsToUnlock[$bucket][$type][] = $path;
+                                       }
+                                       if ( !count( $this->locksHeld[$path] ) ) {
+                                               unset( $this->locksHeld[$path] ); // no SH or EX locks left for key
+                                       }
                                }
                        }
                }
 
                // Remove these specific locks if possible, or at least release
                // all locks once this process is currently not holding any locks.
-               foreach ( $pathsToUnlock as $bucket => $paths ) {
-                       $status->merge( $this->doUnlockingRequestBucket( $bucket, $paths, $type ) );
+               foreach ( $pathsToUnlock as $bucket => $pathsToUnlockByType ) {
+                       $status->merge( $this->doUnlockingRequestBucket( $bucket, $pathsToUnlockByType ) );
                }
                if ( !count( $this->locksHeld ) ) {
                        $status->merge( $this->releaseAllLocks() );
+                       $this->degradedBuckets = array(); // safe to retry the normal quorum
                }
 
                return $status;
@@ -116,11 +121,10 @@ abstract class QuorumLockManager extends LockManager {
         * This is all or nothing; if any key is locked then this totally fails.
         *
         * @param $bucket integer
-        * @param array $paths List of resource keys to lock
-        * @param $type integer LockManager::LOCK_EX or LockManager::LOCK_SH
+        * @param array $pathsByType Map of LockManager::LOCK_* constants to lists of paths
         * @return Status
         */
-       final protected function doLockingRequestBucket( $bucket, array $paths, $type ) {
+       final protected function doLockingRequestBucket( $bucket, array $pathsByType ) {
                $status = Status::newGood();
 
                $yesVotes = 0; // locks made on trustable servers
@@ -131,10 +135,11 @@ abstract class QuorumLockManager extends LockManager {
                        if ( !$this->isServerUp( $lockSrv ) ) {
                                --$votesLeft;
                                $status->warning( 'lockmanager-fail-svr-acquire', $lockSrv );
+                               $this->degradedBuckets[$bucket] = time();
                                continue; // server down?
                        }
                        // Attempt to acquire the lock on this peer
-                       $status->merge( $this->getLocksOnServer( $lockSrv, $paths, $type ) );
+                       $status->merge( $this->getLocksOnServer( $lockSrv, $pathsByType ) );
                        if ( !$status->isOK() ) {
                                return $status; // vetoed; resource locked
                        }
@@ -158,21 +163,33 @@ abstract class QuorumLockManager extends LockManager {
         * Attempt to release locks with the peers for a bucket
         *
         * @param $bucket integer
-        * @param array $paths List of resource keys to lock
-        * @param $type integer LockManager::LOCK_EX or LockManager::LOCK_SH
+        * @param array $pathsByType Map of LockManager::LOCK_* constants to lists of paths
         * @return Status
         */
-       final protected function doUnlockingRequestBucket( $bucket, array $paths, $type ) {
+       final protected function doUnlockingRequestBucket( $bucket, array $pathsByType ) {
                $status = Status::newGood();
 
+               $yesVotes = 0; // locks freed on trustable servers
+               $votesLeft = count( $this->srvsByBucket[$bucket] ); // remaining peers
+               $quorum = floor( $votesLeft / 2 + 1 ); // simple majority
+               $isDegraded = isset( $this->degradedBuckets[$bucket] ); // not the normal quorum?
                foreach ( $this->srvsByBucket[$bucket] as $lockSrv ) {
                        if ( !$this->isServerUp( $lockSrv ) ) {
-                               $status->fatal( 'lockmanager-fail-svr-release', $lockSrv );
+                               $status->warning( 'lockmanager-fail-svr-release', $lockSrv );
                        // Attempt to release the lock on this peer
                        } else {
-                               $status->merge( $this->freeLocksOnServer( $lockSrv, $paths, $type ) );
+                               $status->merge( $this->freeLocksOnServer( $lockSrv, $pathsByType ) );
+                               ++$yesVotes; // success for this peer
+                               // Normally the first peers form the quorum, and the others are ignored.
+                               // Ignore them in this case, but not when an alternative quorum was used.
+                               if ( $yesVotes >= $quorum && !$isDegraded ) {
+                                       break; // lock released
+                               }
                        }
                }
+               // Set a bad status if the quorum was not met.
+               // Assumes the same "up" servers as during the acquire step.
+               $status->setResult( $yesVotes >= $quorum );
 
                return $status;
        }
@@ -190,7 +207,8 @@ abstract class QuorumLockManager extends LockManager {
        }
 
        /**
-        * Check if a lock server is up
+        * Check if a lock server is up.
+        * This should process cache results to reduce RTT.
         *
         * @param $lockSrv string
         * @return bool
@@ -198,14 +216,13 @@ abstract class QuorumLockManager extends LockManager {
        abstract protected function isServerUp( $lockSrv );
 
        /**
-        * Get a connection to a lock server and acquire locks on $paths
+        * Get a connection to a lock server and acquire locks
         *
         * @param $lockSrv string
-        * @param $paths array
-        * @param $type integer
+        * @param array $pathsByType Map of LockManager::LOCK_* constants to lists of paths
         * @return Status
         */
-       abstract protected function getLocksOnServer( $lockSrv, array $paths, $type );
+       abstract protected function getLocksOnServer( $lockSrv, array $pathsByType );
 
        /**
         * Get a connection to a lock server and release locks on $paths.
@@ -213,11 +230,10 @@ abstract class QuorumLockManager extends LockManager {
         * Subclasses must effectively implement this or releaseAllLocks().
         *
         * @param $lockSrv string
-        * @param $paths array
-        * @param $type integer
+        * @param array $pathsByType Map of LockManager::LOCK_* constants to lists of paths
         * @return Status
         */
-       abstract protected function freeLocksOnServer( $lockSrv, array $paths, $type );
+       abstract protected function freeLocksOnServer( $lockSrv, array $pathsByType );
 
        /**
         * Release all locks that this session is holding.
index e8e5996..43b0198 100644 (file)
@@ -78,7 +78,40 @@ class RedisLockManager extends QuorumLockManager {
                $this->session = wfRandomString( 32 );
        }
 
-       protected function getLocksOnServer( $lockSrv, array $paths, $type ) {
+       // @TODO: change this code to work in one batch
+       protected function getLocksOnServer( $lockSrv, array $pathsByType ) {
+               $status = Status::newGood();
+
+               $lockedPaths = array();
+               foreach ( $pathsByType as $type => $paths ) {
+                       $status->merge( $this->doGetLocksOnServer( $lockSrv, $paths, $type ) );
+                       if ( $status->isOK() ) {
+                               $lockedPaths[$type] = isset( $lockedPaths[$type] )
+                                       ? array_merge( $lockedPaths[$type], $paths )
+                                       : $paths;
+                       } else {
+                               foreach ( $lockedPaths as $type => $paths ) {
+                                       $status->merge( $this->doFreeLocksOnServer( $lockSrv, $paths, $type ) );
+                               }
+                               break;
+                       }
+               }
+
+               return $status;
+       }
+
+       // @TODO: change this code to work in one batch
+       protected function freeLocksOnServer( $lockSrv, array $pathsByType ) {
+               $status = Status::newGood();
+
+               foreach ( $pathsByType as $type => $paths ) {
+                       $status->merge( $this->doFreeLocksOnServer( $lockSrv, $paths, $type ) );
+               }
+
+               return $status;
+       }
+
+       protected function doGetLocksOnServer( $lockSrv, array $paths, $type ) {
                $status = Status::newGood();
 
                $server = $this->lockServers[$lockSrv];
@@ -162,7 +195,7 @@ LUA;
                return $status;
        }
 
-       protected function freeLocksOnServer( $lockSrv, array $paths, $type ) {
+       protected function doFreeLocksOnServer( $lockSrv, array $paths, $type ) {
                $status = Status::newGood();
 
                $server = $this->lockServers[$lockSrv];
index 6f28b10..5300e5e 100644 (file)
@@ -29,8 +29,7 @@ class FileRepoStatus extends Status {
        /**
         * Factory function for fatal errors
         *
-        * @param $repo FileRepo
-        *
+        * @param FileRepo $repo
         * @return FileRepoStatus
         */
        static function newFatal( $repo /*, parameters...*/ ) {
@@ -42,7 +41,7 @@ class FileRepoStatus extends Status {
        }
 
        /**
-        * @param $repo FileRepo
+        * @param FileRepo|bool $repo Default: false
         * @param $value
         * @return FileRepoStatus
         */
old mode 100644 (file)
new mode 100755 (executable)
index 5eec9a5..07cc03b
@@ -42,6 +42,16 @@ class ForeignAPIRepo extends FileRepo {
         * Update the version every time you make breaking or significant changes. */
        const VERSION = "2.1";
 
+       /**
+        * List of iiprop values for the thumbnail fetch queries.
+        * @since 1.23
+        */
+       protected static $imageInfoProps = array(
+               'url',
+               'thumbnail',
+               'timestamp',
+       );
+
        var $fileFactory = array( 'ForeignAPIFile', 'newFromTitle' );
        /* Check back with Commons after a day */
        var $apiThumbCacheExpiry = 86400; /* 24*60*60 */
@@ -238,7 +248,7 @@ class ForeignAPIRepo extends FileRepo {
        function getThumbUrl( $name, $width = -1, $height = -1, &$result = null, $otherParams = '' ) {
                $data = $this->fetchImageQuery( array(
                        'titles' => 'File:' . $name,
-                       'iiprop' => 'url|timestamp',
+                       'iiprop' => self::getIIProps(),
                        'iiurlwidth' => $width,
                        'iiurlheight' => $height,
                        'iiurlparam' => $otherParams,
@@ -265,7 +275,7 @@ class ForeignAPIRepo extends FileRepo {
        function getThumbError( $name, $width = -1, $height = -1, $otherParams = '', $lang = null ) {
                $data = $this->fetchImageQuery( array(
                        'titles' => 'File:' . $name,
-                       'iiprop' => 'url|timestamp',
+                       'iiprop' => self::getIIProps(),
                        'iiurlwidth' => $width,
                        'iiurlheight' => $height,
                        'iiurlparam' => $otherParams,
@@ -485,6 +495,14 @@ class ForeignAPIRepo extends FileRepo {
                }
        }
 
+       /**
+        * @return string
+        * @since 1.23
+        */
+       protected static function getIIProps() {
+               return join( '|', self::$imageInfoProps );
+       }
+
        /**
         * HTTP GET request to a mediawiki API (with caching)
         * @param $target string Used in cache key creation, mostly
index ec5f927..5a5221f 100644 (file)
@@ -512,6 +512,17 @@ abstract class File {
                return false;
        }
 
+       /**
+        * Like getMetadata but returns a handler independent array of common values.
+        * @see MediaHandler::getCommonMetaArray()
+        * @return Array or false if not supported
+        * @since 1.23
+        */
+       public function getCommonMetaArray() {
+               $handler = $this->getHandler();
+               return $handler->getCommonMetaArray( $this );
+       }
+
        /**
         * get versioned metadata
         *
old mode 100644 (file)
new mode 100755 (executable)
index ed96d44..0b3d5df
@@ -57,7 +57,10 @@ class ForeignAPIFile extends File {
                        'titles' => 'File:' . $title->getDBkey(),
                        'iiprop' => self::getProps(),
                        'prop' => 'imageinfo',
-                       'iimetadataversion' => MediaHandler::getMetadataVersion()
+                       'iimetadataversion' => MediaHandler::getMetadataVersion(),
+                       // extmetadata is language-dependant, accessing the current language here
+                       // would be problematic, so we just get them all
+                       'iiextmetadatamultilang' => 1,
                ) );
 
                $info = $repo->getImageInfo( $data );
@@ -86,7 +89,7 @@ class ForeignAPIFile extends File {
         * @return string
         */
        static function getProps() {
-               return 'timestamp|user|comment|url|size|sha1|metadata|mime|mediatype';
+               return 'timestamp|user|comment|url|size|sha1|metadata|mime|mediatype|extmetadata';
        }
 
        // Dummy functions...
@@ -169,6 +172,16 @@ class ForeignAPIFile extends File {
                return null;
        }
 
+       /**
+        * @return array|null extended metadata (see imageinfo API for format) or null on error
+        */
+       public function getExtendedMetadata() {
+               if ( isset( $this->mInfo['extmetadata'] ) ) {
+                       return $this->mInfo['extmetadata'];
+               }
+               return null;
+       }
+
        /**
         * @param $metadata array
         * @return array
index 8bb7826..0110ac5 100644 (file)
@@ -32,7 +32,7 @@ abstract class DatabaseInstaller {
        /**
         * The Installer object.
         *
-        * TODO: naming this parent is confusing, 'installer' would be clearer.
+        * @todo Naming this parent is confusing, 'installer' would be clearer.
         *
         * @var WebInstaller
         */
@@ -505,7 +505,8 @@ abstract class DatabaseInstaller {
                        return false;
                }
 
-               return $this->db->tableExists( 'cur', __METHOD__ ) || $this->db->tableExists( 'revision', __METHOD__ );
+               return $this->db->tableExists( 'cur', __METHOD__ ) ||
+                       $this->db->tableExists( 'revision', __METHOD__ );
        }
 
        /**
@@ -516,8 +517,18 @@ abstract class DatabaseInstaller {
        public function getInstallUserBox() {
                return Html::openElement( 'fieldset' ) .
                        Html::element( 'legend', array(), wfMessage( 'config-db-install-account' )->text() ) .
-                       $this->getTextBox( '_InstallUser', 'config-db-username', array( 'dir' => 'ltr' ), $this->parent->getHelpBox( 'config-db-install-username' ) ) .
-                       $this->getPasswordBox( '_InstallPassword', 'config-db-password', array( 'dir' => 'ltr' ), $this->parent->getHelpBox( 'config-db-install-password' ) ) .
+                       $this->getTextBox(
+                               '_InstallUser',
+                               'config-db-username',
+                               array( 'dir' => 'ltr' ),
+                               $this->parent->getHelpBox( 'config-db-install-username' )
+                       ) .
+                       $this->getPasswordBox(
+                               '_InstallPassword',
+                               'config-db-password',
+                               array( 'dir' => 'ltr' ),
+                               $this->parent->getHelpBox( 'config-db-install-password' )
+                       ) .
                        Html::closeElement( 'fieldset' );
        }
 
index fdd34d4..e56c7a9 100644 (file)
@@ -88,6 +88,11 @@ abstract class DatabaseUpdater {
         */
        protected $skipSchema = false;
 
+       /**
+        * Hold the value of $wgContentHandlerUseDB during the upgrade.
+        */
+       protected $holdContentHandlerUseDB = true;
+
        /**
         * Constructor
         *
@@ -130,7 +135,8 @@ abstract class DatabaseUpdater {
        }
 
        /**
-        * Loads LocalSettings.php, if needed, and initialises everything needed for LoadExtensionSchemaUpdates hook
+        * Loads LocalSettings.php, if needed, and initialises everything needed for
+        * LoadExtensionSchemaUpdates hook.
         */
        private function loadExtensions() {
                if ( !defined( 'MEDIAWIKI_INSTALL' ) ) {
@@ -289,11 +295,22 @@ abstract class DatabaseUpdater {
         * @param string $tableName The table name
         * @param string $oldIndexName The old index name
         * @param string $newIndexName The new index name
-        * @param $skipBothIndexExistWarning Boolean: Whether to warn if both the old and the new indexes exist. [facultative; by default, false]
+        * @param $skipBothIndexExistWarning Boolean: Whether to warn if both the old
+        * and the new indexes exist. [facultative; by default, false]
         * @param string $sqlPath The path to the SQL change path
         */
-       public function renameExtensionIndex( $tableName, $oldIndexName, $newIndexName, $sqlPath, $skipBothIndexExistWarning = false ) {
-               $this->extensionUpdates[] = array( 'renameIndex', $tableName, $oldIndexName, $newIndexName, $skipBothIndexExistWarning, $sqlPath, true );
+       public function renameExtensionIndex( $tableName, $oldIndexName, $newIndexName,
+               $sqlPath, $skipBothIndexExistWarning = false
+       ) {
+               $this->extensionUpdates[] = array(
+                       'renameIndex',
+                       $tableName,
+                       $oldIndexName,
+                       $newIndexName,
+                       $skipBothIndexExistWarning,
+                       $sqlPath,
+                       true
+               );
        }
 
        /**
@@ -758,12 +775,15 @@ abstract class DatabaseUpdater {
         * @param string $table Name of the table to modify
         * @param string $oldIndex Old name of the index
         * @param string $newIndex New name of the index
-        * @param $skipBothIndexExistWarning Boolean: Whether to warn if both the old and the new indexes exist.
+        * @param $skipBothIndexExistWarning Boolean: Whether to warn if both the
+        * old and the new indexes exist.
         * @param string $patch Path to the patch file
         * @param $fullpath Boolean: Whether to treat $patch path as a relative or not
         * @return Boolean false if this was skipped because schema changes are skipped
         */
-       protected function renameIndex( $table, $oldIndex, $newIndex, $skipBothIndexExistWarning, $patch, $fullpath = false ) {
+       protected function renameIndex( $table, $oldIndex, $newIndex,
+               $skipBothIndexExistWarning, $patch, $fullpath = false
+       ) {
                if ( !$this->doTable( $table ) ) {
                        return true;
                }
@@ -778,8 +798,11 @@ abstract class DatabaseUpdater {
                // Second requirement: the new index must be missing
                if ( $this->db->indexExists( $table, $newIndex, __METHOD__ ) ) {
                        $this->output( "...index $newIndex already set on $table table.\n" );
-                       if ( !$skipBothIndexExistWarning && $this->db->indexExists( $table, $oldIndex, __METHOD__ ) ) {
-                               $this->output( "...WARNING: $oldIndex still exists, despite it has been renamed into $newIndex (which also exists).\n" .
+                       if ( !$skipBothIndexExistWarning &&
+                               $this->db->indexExists( $table, $oldIndex, __METHOD__ )
+                       ) {
+                               $this->output( "...WARNING: $oldIndex still exists, despite it has " .
+                                       "been renamed into $newIndex (which also exists).\n" .
                                        "            $oldIndex should be manually removed if not needed anymore.\n" );
                        }
 
@@ -794,7 +817,11 @@ abstract class DatabaseUpdater {
                }
 
                // Requirements have been satisfied, patch can be applied
-               return $this->applyPatch( $patch, $fullpath, "Renaming index $oldIndex into $newIndex to table $table" );
+               return $this->applyPatch(
+                       $patch,
+                       $fullpath,
+                       "Renaming index $oldIndex into $newIndex to table $table"
+               );
        }
 
        /**
@@ -848,7 +875,8 @@ abstract class DatabaseUpdater {
                if ( !$this->db->tableExists( $table, __METHOD__ ) ) {
                        $this->output( "...$table table does not exist, skipping modify field patch.\n" );
                } elseif ( !$this->db->fieldExists( $table, $field, __METHOD__ ) ) {
-                       $this->output( "...$field field does not exist in $table table, skipping modify field patch.\n" );
+                       $this->output( "...$field field does not exist in $table table, " .
+                               "skipping modify field patch.\n" );
                } elseif ( $this->updateRowExists( $updateKey ) ) {
                        $this->output( "...$field in table $table already modified by patch $patch.\n" );
                } else {
@@ -1010,4 +1038,30 @@ abstract class DatabaseUpdater {
                $cl->execute();
                $this->output( "done.\n" );
        }
+
+       /**
+        * Turns off content handler fields during parts of the upgrade
+        * where they aren't available.
+        */
+       protected function disableContentHandlerUseDB() {
+               global $wgContentHandlerUseDB;
+
+               if( $wgContentHandlerUseDB ) {
+                       $this->output( "Turning off Content Handler DB fields for this part of upgrade.\n" );
+                       $this->holdContentHandlerUseDB = $wgContentHandlerUseDB;
+                       $wgContentHandlerUseDB = false;
+               }
+       }
+
+       /**
+        * Turns content handler fields back on.
+        */
+       protected function enableContentHandlerUseDB() {
+               global $wgContentHandlerUseDB;
+
+               if( $this->holdContentHandlerUseDB ) {
+                       $this->output( "Content Handler DB fields should be usable now.\n" );
+                       $wgContentHandlerUseDB = $this->holdContentHandlerUseDB;
+               }
+       }
 }
index 0042089..6d3819c 100644 (file)
@@ -47,7 +47,11 @@ class InstallDocFormatter {
                // turn (bug nnnn) into links
                $text = preg_replace_callback( '/bug (\d+)/', array( $this, 'replaceBugLinks' ), $text );
                // add links to manual to every global variable mentioned
-               $text = preg_replace_callback( '/(\$wg[a-z0-9_]+)/i', array( $this, 'replaceConfigLinks' ), $text );
+               $text = preg_replace_callback(
+                       '/(\$wg[a-z0-9_]+)/i',
+                       array( $this, 'replaceConfigLinks' ),
+                       $text
+               );
 
                return $text;
        }
index edf5ff2..ac768a0 100644 (file)
@@ -374,7 +374,7 @@ Specify a different project namespace.',
        'config-ns-conflict'               => 'The specified namespace "<nowiki>$1</nowiki>" conflicts with a default MediaWiki namespace.
 Specify a different project namespace.',
        'config-admin-box'                => 'Administrator account',
-       'config-admin-name'               => 'Your name:',
+       'config-admin-name'               => 'Your username:',
        'config-admin-password'           => 'Password:',
        'config-admin-password-confirm'   => 'Password again:',
        'config-admin-help'               => 'Enter your preferred username here, for example "Joe Bloggs".
index 5eaacf8..2f45005 100644 (file)
@@ -302,7 +302,8 @@ abstract class Installer {
        /**
         * URL to mediawiki-announce subscription
         */
-       protected $mediaWikiAnnounceUrl = 'https://lists.wikimedia.org/mailman/subscribe/mediawiki-announce';
+       protected $mediaWikiAnnounceUrl =
+               'https://lists.wikimedia.org/mailman/subscribe/mediawiki-announce';
 
        /**
         * Supported language codes for Mailman
@@ -983,6 +984,7 @@ abstract class Installer {
                        $this->showMessage( 'config-using-server', $server );
                        $this->setVar( 'wgServer', $server );
                }
+
                return true;
        }
 
@@ -1001,7 +1003,11 @@ abstract class Installer {
                $IP = dirname( dirname( __DIR__ ) );
                $this->setVar( 'IP', $IP );
 
-               $this->showMessage( 'config-using-uri', $this->getVar( 'wgServer' ), $this->getVar( 'wgScriptPath' ) );
+               $this->showMessage(
+                       'config-using-uri',
+                       $this->getVar( 'wgServer' ),
+                       $this->getVar( 'wgScriptPath' )
+               );
 
                return true;
        }
@@ -1195,7 +1201,8 @@ abstract class Installer {
                        }
                }
 
-               // Uses messages 'config-unicode-using-php', 'config-unicode-using-utf8', 'config-unicode-using-intl'
+               // Uses messages 'config-unicode-using-php', 'config-unicode-using-utf8',
+               // 'config-unicode-using-intl'
                if ( $useNormalizer === 'php' ) {
                        $this->showMessage( 'config-unicode-pure-php-warning' );
                } else {
@@ -1730,6 +1737,11 @@ abstract class Installer {
 
                // Some of the environment checks make shell requests, remove limits
                $GLOBALS['wgMaxShellMemory'] = 0;
+
+               // Don't bother embedding images into generated CSS, which is not cached
+               $GLOBALS['wgResourceLoaderLESSFunctions']['embeddable'] = function( $frame, $less ) {
+                       return $less->toBool( false );
+               };
        }
 
        /**
index 858fbee..56d8353 100644 (file)
@@ -202,7 +202,7 @@ class LocalSettingsGenerator {
                        $locale = '';
                }
 
-               //$rightsUrl = $this->values['wgRightsUrl'] ? '' : '#'; // TODO: Fixme, I'm unused!
+               //$rightsUrl = $this->values['wgRightsUrl'] ? '' : '#'; // @todo FIXME: I'm unused!
                $hashedUploads = $this->safeMode ? '' : '#';
                $metaNamespace = '';
                if ( $this->values['wgMetaNamespace'] !== $this->values['wgSitename'] ) {
index 5e420b6..5f76972 100644 (file)
@@ -64,10 +64,6 @@ class MysqlInstaller extends DatabaseInstaller {
                return 'mysql';
        }
 
-       public function __construct( $parent ) {
-               parent::__construct( $parent );
-       }
-
        /**
         * @return Bool
         */
@@ -86,7 +82,12 @@ class MysqlInstaller extends DatabaseInstaller {
         * @return string
         */
        public function getConnectForm() {
-               return $this->getTextBox( 'wgDBserver', 'config-db-host', array(), $this->parent->getHelpBox( 'config-db-host-help' ) ) .
+               return $this->getTextBox(
+                       'wgDBserver',
+                       'config-db-host',
+                       array(),
+                       $this->parent->getHelpBox( 'config-db-host-help' )
+               ) .
                        Html::openElement( 'fieldset' ) .
                        Html::element( 'legend', array(), wfMessage( 'config-db-wiki-settings' )->text() ) .
                        $this->getTextBox( 'wgDBname', 'config-db-name', array( 'dir' => 'ltr' ),
@@ -481,7 +482,10 @@ class MysqlInstaller extends DatabaseInstaller {
                $conn = $status->value;
                $dbName = $this->getVar( 'wgDBname' );
                if ( !$conn->selectDB( $dbName ) ) {
-                       $conn->query( "CREATE DATABASE " . $conn->addIdentifierQuotes( $dbName ) . "CHARACTER SET utf8", __METHOD__ );
+                       $conn->query(
+                               "CREATE DATABASE " . $conn->addIdentifierQuotes( $dbName ) . "CHARACTER SET utf8",
+                               __METHOD__
+                       );
                        $conn->selectDB( $dbName );
                }
                $this->setupSchemaVars();
index 93ea773..fb675d7 100644 (file)
@@ -31,208 +31,218 @@ class MysqlUpdater extends DatabaseUpdater {
 
        protected function getCoreUpdateList() {
                return array(
+                       array( 'disableContentHandlerUseDB' ),
+
                        // 1.2
                        array( 'addField', 'ipblocks',      'ipb_id',           'patch-ipblocks.sql' ),
                        array( 'addField', 'ipblocks',      'ipb_expiry',       'patch-ipb_expiry.sql' ),
                        array( 'doInterwikiUpdate' ),
                        array( 'doIndexUpdate' ),
-                       array( 'addTable', 'hitcounter',                        'patch-hitcounter.sql' ),
-                       array( 'addField', 'recentchanges', 'rc_type',          'patch-rc_type.sql' ),
+                       array( 'addTable', 'hitcounter', 'patch-hitcounter.sql' ),
+                       array( 'addField', 'recentchanges', 'rc_type', 'patch-rc_type.sql' ),
 
                        // 1.3
-                       array( 'addField', 'user',          'user_real_name',   'patch-user-realname.sql' ),
-                       array( 'addTable', 'querycache',                        'patch-querycache.sql' ),
-                       array( 'addTable', 'objectcache',                       'patch-objectcache.sql' ),
-                       array( 'addTable', 'categorylinks',                     'patch-categorylinks.sql' ),
+                       array( 'addField', 'user', 'user_real_name', 'patch-user-realname.sql' ),
+                       array( 'addTable', 'querycache', 'patch-querycache.sql' ),
+                       array( 'addTable', 'objectcache', 'patch-objectcache.sql' ),
+                       array( 'addTable', 'categorylinks', 'patch-categorylinks.sql' ),
                        array( 'doOldLinksUpdate' ),
                        array( 'doFixAncientImagelinks' ),
-                       array( 'addField', 'recentchanges', 'rc_ip',            'patch-rc_ip.sql' ),
+                       array( 'addField', 'recentchanges', 'rc_ip', 'patch-rc_ip.sql' ),
 
                        // 1.4
-                       array( 'addIndex', 'image',         'PRIMARY',          'patch-image_name_primary.sql' ),
-                       array( 'addField', 'recentchanges', 'rc_id',            'patch-rc_id.sql' ),
-                       array( 'addField', 'recentchanges', 'rc_patrolled',     'patch-rc-patrol.sql' ),
-                       array( 'addTable', 'logging',                           'patch-logging.sql' ),
-                       array( 'addField', 'user',          'user_token',       'patch-user_token.sql' ),
-                       array( 'addField', 'watchlist',     'wl_notificationtimestamp', 'patch-email-notification.sql' ),
+                       array( 'addIndex', 'image', 'PRIMARY', 'patch-image_name_primary.sql' ),
+                       array( 'addField', 'recentchanges', 'rc_id', 'patch-rc_id.sql' ),
+                       array( 'addField', 'recentchanges', 'rc_patrolled', 'patch-rc-patrol.sql' ),
+                       array( 'addTable', 'logging', 'patch-logging.sql' ),
+                       array( 'addField', 'user', 'user_token', 'patch-user_token.sql' ),
+                       array( 'addField', 'watchlist', 'wl_notificationtimestamp', 'patch-email-notification.sql' ),
                        array( 'doWatchlistUpdate' ),
-                       array( 'dropField', 'user',         'user_emailauthenticationtimestamp', 'patch-email-authentication.sql' ),
+                       array( 'dropField', 'user', 'user_emailauthenticationtimestamp',
+                               'patch-email-authentication.sql' ),
 
                        // 1.5
                        array( 'doSchemaRestructuring' ),
-                       array( 'addField', 'logging',       'log_params',       'patch-log_params.sql' ),
-                       array( 'checkBin', 'logging',       'log_title',        'patch-logging-title.sql', ),
-                       array( 'addField', 'archive',       'ar_rev_id',        'patch-archive-rev_id.sql' ),
-                       array( 'addField', 'page',          'page_len',         'patch-page_len.sql' ),
-                       array( 'dropField', 'revision',     'inverse_timestamp', 'patch-inverse_timestamp.sql' ),
-                       array( 'addField', 'revision',      'rev_text_id',      'patch-rev_text_id.sql' ),
-                       array( 'addField', 'revision',      'rev_deleted',      'patch-rev_deleted.sql' ),
-                       array( 'addField', 'image',         'img_width',        'patch-img_width.sql' ),
-                       array( 'addField', 'image',         'img_metadata',     'patch-img_metadata.sql' ),
-                       array( 'addField', 'user',          'user_email_token', 'patch-user_email_token.sql' ),
-                       array( 'addField', 'archive',       'ar_text_id',       'patch-archive-text_id.sql' ),
+                       array( 'addField', 'logging', 'log_params', 'patch-log_params.sql' ),
+                       array( 'checkBin', 'logging', 'log_title', 'patch-logging-title.sql', ),
+                       array( 'addField', 'archive', 'ar_rev_id', 'patch-archive-rev_id.sql' ),
+                       array( 'addField', 'page', 'page_len', 'patch-page_len.sql' ),
+                       array( 'dropField', 'revision', 'inverse_timestamp', 'patch-inverse_timestamp.sql' ),
+                       array( 'addField', 'revision', 'rev_text_id', 'patch-rev_text_id.sql' ),
+                       array( 'addField', 'revision', 'rev_deleted', 'patch-rev_deleted.sql' ),
+                       array( 'addField', 'image', 'img_width', 'patch-img_width.sql' ),
+                       array( 'addField', 'image', 'img_metadata', 'patch-img_metadata.sql' ),
+                       array( 'addField', 'user', 'user_email_token', 'patch-user_email_token.sql' ),
+                       array( 'addField', 'archive', 'ar_text_id', 'patch-archive-text_id.sql' ),
                        array( 'doNamespaceSize' ),
-                       array( 'addField', 'image',         'img_media_type',   'patch-img_media_type.sql' ),
+                       array( 'addField', 'image', 'img_media_type', 'patch-img_media_type.sql' ),
                        array( 'doPagelinksUpdate' ),
-                       array( 'dropField', 'image',        'img_type',         'patch-drop_img_type.sql' ),
+                       array( 'dropField', 'image', 'img_type', 'patch-drop_img_type.sql' ),
                        array( 'doUserUniqueUpdate' ),
                        array( 'doUserGroupsUpdate' ),
-                       array( 'addField', 'site_stats',    'ss_total_pages',   'patch-ss_total_articles.sql' ),
-                       array( 'addTable', 'user_newtalk',                      'patch-usernewtalk2.sql' ),
-                       array( 'addTable', 'transcache',                        'patch-transcache.sql' ),
-                       array( 'addField', 'interwiki',     'iw_trans',         'patch-interwiki-trans.sql' ),
+                       array( 'addField', 'site_stats', 'ss_total_pages', 'patch-ss_total_articles.sql' ),
+                       array( 'addTable', 'user_newtalk', 'patch-usernewtalk2.sql' ),
+                       array( 'addTable', 'transcache', 'patch-transcache.sql' ),
+                       array( 'addField', 'interwiki', 'iw_trans', 'patch-interwiki-trans.sql' ),
 
                        // 1.6
                        array( 'doWatchlistNull' ),
-                       array( 'addIndex', 'logging',         'times',            'patch-logging-times-index.sql' ),
-                       array( 'addField', 'ipblocks',        'ipb_range_start',  'patch-ipb_range_start.sql' ),
+                       array( 'addIndex', 'logging', 'times', 'patch-logging-times-index.sql' ),
+                       array( 'addField', 'ipblocks', 'ipb_range_start', 'patch-ipb_range_start.sql' ),
                        array( 'doPageRandomUpdate' ),
-                       array( 'addField', 'user',            'user_registration', 'patch-user_registration.sql' ),
+                       array( 'addField', 'user', 'user_registration', 'patch-user_registration.sql' ),
                        array( 'doTemplatelinksUpdate' ),
-                       array( 'addTable', 'externallinks',                       'patch-externallinks.sql' ),
-                       array( 'addTable', 'job',                                 'patch-job.sql' ),
-                       array( 'addField', 'site_stats',      'ss_images',        'patch-ss_images.sql' ),
-                       array( 'addTable', 'langlinks',                           'patch-langlinks.sql' ),
-                       array( 'addTable', 'querycache_info',                     'patch-querycacheinfo.sql' ),
-                       array( 'addTable', 'filearchive',                         'patch-filearchive.sql' ),
-                       array( 'addField', 'ipblocks',        'ipb_anon_only',    'patch-ipb_anon_only.sql' ),
-                       array( 'addIndex', 'recentchanges',   'rc_ns_usertext',   'patch-recentchanges-utindex.sql' ),
-                       array( 'addIndex', 'recentchanges',   'rc_user_text',     'patch-rc_user_text-index.sql' ),
+                       array( 'addTable', 'externallinks', 'patch-externallinks.sql' ),
+                       array( 'addTable', 'job', 'patch-job.sql' ),
+                       array( 'addField', 'site_stats', 'ss_images', 'patch-ss_images.sql' ),
+                       array( 'addTable', 'langlinks', 'patch-langlinks.sql' ),
+                       array( 'addTable', 'querycache_info', 'patch-querycacheinfo.sql' ),
+                       array( 'addTable', 'filearchive', 'patch-filearchive.sql' ),
+                       array( 'addField', 'ipblocks', 'ipb_anon_only', 'patch-ipb_anon_only.sql' ),
+                       array( 'addIndex', 'recentchanges', 'rc_ns_usertext', 'patch-recentchanges-utindex.sql' ),
+                       array( 'addIndex', 'recentchanges', 'rc_user_text', 'patch-rc_user_text-index.sql' ),
 
                        // 1.9
-                       array( 'addField', 'user',          'user_newpass_time', 'patch-user_newpass_time.sql' ),
-                       array( 'addTable', 'redirect',                           'patch-redirect.sql' ),
-                       array( 'addTable', 'querycachetwo',                      'patch-querycachetwo.sql' ),
-                       array( 'addField', 'ipblocks',      'ipb_enable_autoblock', 'patch-ipb_optional_autoblock.sql' ),
+                       array( 'addField', 'user', 'user_newpass_time', 'patch-user_newpass_time.sql' ),
+                       array( 'addTable', 'redirect', 'patch-redirect.sql' ),
+                       array( 'addTable', 'querycachetwo', 'patch-querycachetwo.sql' ),
+                       array( 'addField', 'ipblocks', 'ipb_enable_autoblock', 'patch-ipb_optional_autoblock.sql' ),
                        array( 'doBacklinkingIndicesUpdate' ),
-                       array( 'addField', 'recentchanges', 'rc_old_len',        'patch-rc_len.sql' ),
-                       array( 'addField', 'user',          'user_editcount',    'patch-user_editcount.sql' ),
+                       array( 'addField', 'recentchanges', 'rc_old_len', 'patch-rc_len.sql' ),
+                       array( 'addField', 'user', 'user_editcount', 'patch-user_editcount.sql' ),
 
                        // 1.10
                        array( 'doRestrictionsUpdate' ),
-                       array( 'addField', 'logging',       'log_id',           'patch-log_id.sql' ),
-                       array( 'addField', 'revision',      'rev_parent_id',    'patch-rev_parent_id.sql' ),
-                       array( 'addField', 'page_restrictions', 'pr_id',        'patch-page_restrictions_sortkey.sql' ),
-                       array( 'addField', 'revision',      'rev_len',          'patch-rev_len.sql' ),
-                       array( 'addField', 'recentchanges', 'rc_deleted',       'patch-rc_deleted.sql' ),
-                       array( 'addField', 'logging',       'log_deleted',      'patch-log_deleted.sql' ),
-                       array( 'addField', 'archive',       'ar_deleted',       'patch-ar_deleted.sql' ),
-                       array( 'addField', 'ipblocks',      'ipb_deleted',      'patch-ipb_deleted.sql' ),
-                       array( 'addField', 'filearchive',   'fa_deleted',       'patch-fa_deleted.sql' ),
-                       array( 'addField', 'archive',       'ar_len',           'patch-ar_len.sql' ),
+                       array( 'addField', 'logging', 'log_id', 'patch-log_id.sql' ),
+                       array( 'addField', 'revision', 'rev_parent_id', 'patch-rev_parent_id.sql' ),
+                       array( 'addField', 'page_restrictions', 'pr_id', 'patch-page_restrictions_sortkey.sql' ),
+                       array( 'addField', 'revision', 'rev_len', 'patch-rev_len.sql' ),
+                       array( 'addField', 'recentchanges', 'rc_deleted', 'patch-rc_deleted.sql' ),
+                       array( 'addField', 'logging', 'log_deleted', 'patch-log_deleted.sql' ),
+                       array( 'addField', 'archive', 'ar_deleted', 'patch-ar_deleted.sql' ),
+                       array( 'addField', 'ipblocks', 'ipb_deleted', 'patch-ipb_deleted.sql' ),
+                       array( 'addField', 'filearchive', 'fa_deleted', 'patch-fa_deleted.sql' ),
+                       array( 'addField', 'archive', 'ar_len', 'patch-ar_len.sql' ),
 
                        // 1.11
-                       array( 'addField', 'ipblocks',      'ipb_block_email',  'patch-ipb_emailban.sql' ),
+                       array( 'addField', 'ipblocks', 'ipb_block_email', 'patch-ipb_emailban.sql' ),
                        array( 'doCategorylinksIndicesUpdate' ),
-                       array( 'addField', 'oldimage',      'oi_metadata',      'patch-oi_metadata.sql' ),
-                       array( 'addIndex', 'archive',       'usertext_timestamp', 'patch-archive-user-index.sql' ),
-                       array( 'addIndex', 'image',         'img_usertext_timestamp', 'patch-image-user-index.sql' ),
-                       array( 'addIndex', 'oldimage',      'oi_usertext_timestamp', 'patch-oldimage-user-index.sql' ),
-                       array( 'addField', 'archive',       'ar_page_id',       'patch-archive-page_id.sql' ),
-                       array( 'addField', 'image',         'img_sha1',         'patch-img_sha1.sql' ),
+                       array( 'addField', 'oldimage', 'oi_metadata', 'patch-oi_metadata.sql' ),
+                       array( 'addIndex', 'archive', 'usertext_timestamp', 'patch-archive-user-index.sql' ),
+                       array( 'addIndex', 'image', 'img_usertext_timestamp', 'patch-image-user-index.sql' ),
+                       array( 'addIndex', 'oldimage', 'oi_usertext_timestamp', 'patch-oldimage-user-index.sql' ),
+                       array( 'addField', 'archive', 'ar_page_id', 'patch-archive-page_id.sql' ),
+                       array( 'addField', 'image', 'img_sha1', 'patch-img_sha1.sql' ),
 
                        // 1.12
-                       array( 'addTable', 'protected_titles',                  'patch-protected_titles.sql' ),
+                       array( 'addTable', 'protected_titles', 'patch-protected_titles.sql' ),
 
                        // 1.13
-                       array( 'addField', 'ipblocks',      'ipb_by_text',      'patch-ipb_by_text.sql' ),
-                       array( 'addTable', 'page_props',                        'patch-page_props.sql' ),
-                       array( 'addTable', 'updatelog',                         'patch-updatelog.sql' ),
-                       array( 'addTable', 'category',                          'patch-category.sql' ),
+                       array( 'addField', 'ipblocks', 'ipb_by_text', 'patch-ipb_by_text.sql' ),
+                       array( 'addTable', 'page_props', 'patch-page_props.sql' ),
+                       array( 'addTable', 'updatelog', 'patch-updatelog.sql' ),
+                       array( 'addTable', 'category', 'patch-category.sql' ),
                        array( 'doCategoryPopulation' ),
-                       array( 'addField', 'archive',       'ar_parent_id',     'patch-ar_parent_id.sql' ),
-                       array( 'addField', 'user_newtalk',  'user_last_timestamp', 'patch-user_last_timestamp.sql' ),
+                       array( 'addField', 'archive', 'ar_parent_id', 'patch-ar_parent_id.sql' ),
+                       array( 'addField', 'user_newtalk', 'user_last_timestamp', 'patch-user_last_timestamp.sql' ),
                        array( 'doPopulateParentId' ),
-                       array( 'checkBin', 'protected_titles', 'pt_title',      'patch-pt_title-encoding.sql', ),
+                       array( 'checkBin', 'protected_titles', 'pt_title', 'patch-pt_title-encoding.sql', ),
                        array( 'doMaybeProfilingMemoryUpdate' ),
                        array( 'doFilearchiveIndicesUpdate' ),
 
                        // 1.14
-                       array( 'addField', 'site_stats',    'ss_active_users',  'patch-ss_active_users.sql' ),
+                       array( 'addField', 'site_stats', 'ss_active_users', 'patch-ss_active_users.sql' ),
                        array( 'doActiveUsersInit' ),
-                       array( 'addField', 'ipblocks',      'ipb_allow_usertalk', 'patch-ipb_allow_usertalk.sql' ),
+                       array( 'addField', 'ipblocks', 'ipb_allow_usertalk', 'patch-ipb_allow_usertalk.sql' ),
 
                        // 1.15
                        array( 'doUniquePlTlIl' ),
-                       array( 'addTable', 'change_tag',                        'patch-change_tag.sql' ),
-                       array( 'addTable', 'tag_summary',                       'patch-tag_summary.sql' ),
-                       array( 'addTable', 'valid_tag',                         'patch-valid_tag.sql' ),
+                       array( 'addTable', 'change_tag', 'patch-change_tag.sql' ),
+                       array( 'addTable', 'tag_summary', 'patch-tag_summary.sql' ),
+                       array( 'addTable', 'valid_tag', 'patch-valid_tag.sql' ),
 
                        // 1.16
-                       array( 'addTable', 'user_properties',                   'patch-user_properties.sql' ),
-                       array( 'addTable', 'log_search',                        'patch-log_search.sql' ),
-                       array( 'addField', 'logging',       'log_user_text',    'patch-log_user_text.sql' ),
-                       array( 'doLogUsertextPopulation' ), # listed separately from the previous update because 1.16 was released without this update
+                       array( 'addTable', 'user_properties', 'patch-user_properties.sql' ),
+                       array( 'addTable', 'log_search', 'patch-log_search.sql' ),
+                       array( 'addField', 'logging', 'log_user_text', 'patch-log_user_text.sql' ),
+                       # listed separately from the previous update because 1.16 was released without this update
+                       array( 'doLogUsertextPopulation' ),
                        array( 'doLogSearchPopulation' ),
-                       array( 'addTable', 'l10n_cache',                        'patch-l10n_cache.sql' ),
-                       array( 'addIndex', 'log_search',    'ls_field_val',     'patch-log_search-rename-index.sql' ),
-                       array( 'addIndex', 'change_tag',    'change_tag_rc_tag', 'patch-change_tag-indexes.sql' ),
-                       array( 'addField', 'redirect',      'rd_interwiki',     'patch-rd_interwiki.sql' ),
+                       array( 'addTable', 'l10n_cache', 'patch-l10n_cache.sql' ),
+                       array( 'addIndex', 'log_search', 'ls_field_val', 'patch-log_search-rename-index.sql' ),
+                       array( 'addIndex', 'change_tag', 'change_tag_rc_tag', 'patch-change_tag-indexes.sql' ),
+                       array( 'addField', 'redirect', 'rd_interwiki', 'patch-rd_interwiki.sql' ),
                        array( 'doUpdateTranscacheField' ),
                        array( 'doUpdateMimeMinorField' ),
 
                        // 1.17
-                       array( 'addTable', 'iwlinks',                           'patch-iwlinks.sql' ),
-                       array( 'addIndex', 'iwlinks', 'iwl_prefix_title_from',  'patch-rename-iwl_prefix.sql' ),
-                       array( 'addField', 'updatelog',     'ul_value',         'patch-ul_value.sql' ),
-                       array( 'addField', 'interwiki',     'iw_api',           'patch-iw_api_and_wikiid.sql' ),
-                       array( 'dropIndex', 'iwlinks',      'iwl_prefix',       'patch-kill-iwl_prefix.sql' ),
-                       array( 'addField', 'categorylinks', 'cl_collation',     'patch-categorylinks-better-collation.sql' ),
+                       array( 'addTable', 'iwlinks', 'patch-iwlinks.sql' ),
+                       array( 'addIndex', 'iwlinks', 'iwl_prefix_title_from', 'patch-rename-iwl_prefix.sql' ),
+                       array( 'addField', 'updatelog', 'ul_value', 'patch-ul_value.sql' ),
+                       array( 'addField', 'interwiki', 'iw_api', 'patch-iw_api_and_wikiid.sql' ),
+                       array( 'dropIndex', 'iwlinks', 'iwl_prefix', 'patch-kill-iwl_prefix.sql' ),
+                       array( 'addField', 'categorylinks', 'cl_collation', 'patch-categorylinks-better-collation.sql' ),
                        array( 'doClFieldsUpdate' ),
                        array( 'doCollationUpdate' ),
-                       array( 'addTable', 'msg_resource',                      'patch-msg_resource.sql' ),
-                       array( 'addTable', 'module_deps',                       'patch-module_deps.sql' ),
-                       array( 'dropIndex', 'archive',      'ar_page_revid',    'patch-archive_kill_ar_page_revid.sql' ),
-                       array( 'addIndex', 'archive',       'ar_revid',         'patch-archive_ar_revid.sql' ),
+                       array( 'addTable', 'msg_resource', 'patch-msg_resource.sql' ),
+                       array( 'addTable', 'module_deps', 'patch-module_deps.sql' ),
+                       array( 'dropIndex', 'archive', 'ar_page_revid', 'patch-archive_kill_ar_page_revid.sql' ),
+                       array( 'addIndex', 'archive', 'ar_revid', 'patch-archive_ar_revid.sql' ),
                        array( 'doLangLinksLengthUpdate' ),
 
                        // 1.18
                        array( 'doUserNewTalkTimestampNotNull' ),
-                       array( 'addIndex', 'user',          'user_email',       'patch-user_email_index.sql' ),
+                       array( 'addIndex', 'user', 'user_email', 'patch-user_email_index.sql' ),
                        array( 'modifyField', 'user_properties', 'up_property', 'patch-up_property.sql' ),
-                       array( 'addTable', 'uploadstash',                       'patch-uploadstash.sql' ),
-                       array( 'addTable', 'user_former_groups',                'patch-user_former_groups.sql'),
+                       array( 'addTable', 'uploadstash', 'patch-uploadstash.sql' ),
+                       array( 'addTable', 'user_former_groups', 'patch-user_former_groups.sql' ),
 
                        // 1.19
-                       array( 'addIndex', 'logging',       'type_action',      'patch-logging-type-action-index.sql'),
-                       array( 'addField', 'revision',      'rev_sha1',         'patch-rev_sha1.sql' ),
+                       array( 'addIndex', 'logging', 'type_action', 'patch-logging-type-action-index.sql' ),
+                       array( 'addField', 'revision', 'rev_sha1', 'patch-rev_sha1.sql' ),
                        array( 'doMigrateUserOptions' ),
-                       array( 'dropField', 'user',         'user_options', 'patch-drop-user_options.sql' ),
-                       array( 'addField', 'archive',       'ar_sha1',          'patch-ar_sha1.sql' ),
-                       array( 'addIndex', 'page', 'page_redirect_namespace_len', 'patch-page_redirect_namespace_len.sql' ),
-                       array( 'addField',      'uploadstash',  'us_chunk_inx',         'patch-uploadstash_chunk.sql' ),
-                       array( 'addfield', 'job',           'job_timestamp',    'patch-jobs-add-timestamp.sql' ),
+                       array( 'dropField', 'user', 'user_options', 'patch-drop-user_options.sql' ),
+                       array( 'addField', 'archive', 'ar_sha1', 'patch-ar_sha1.sql' ),
+                       array( 'addIndex', 'page', 'page_redirect_namespace_len',
+                               'patch-page_redirect_namespace_len.sql' ),
+                       array( 'addField', 'uploadstash', 'us_chunk_inx', 'patch-uploadstash_chunk.sql' ),
+                       array( 'addfield', 'job', 'job_timestamp', 'patch-jobs-add-timestamp.sql' ),
 
                        // 1.20
                        array( 'addIndex', 'revision', 'page_user_timestamp', 'patch-revision-user-page-index.sql' ),
-                       array( 'addField', 'ipblocks',      'ipb_parent_block_id',           'patch-ipb-parent-block-id.sql' ),
-                       array( 'addIndex', 'ipblocks',      'ipb_parent_block_id',           'patch-ipb-parent-block-id-index.sql' ),
-                       array( 'dropField', 'category',     'cat_hidden',       'patch-cat_hidden.sql' ),
+                       array( 'addField', 'ipblocks', 'ipb_parent_block_id', 'patch-ipb-parent-block-id.sql' ),
+                       array( 'addIndex', 'ipblocks', 'ipb_parent_block_id', 'patch-ipb-parent-block-id-index.sql' ),
+                       array( 'dropField', 'category', 'cat_hidden', 'patch-cat_hidden.sql' ),
 
                        // 1.21
-                       array( 'addField',      'revision',     'rev_content_format',           'patch-revision-rev_content_format.sql' ),
-                       array( 'addField',      'revision',     'rev_content_model',            'patch-revision-rev_content_model.sql' ),
+                       array( 'addField', 'revision', 'rev_content_format', 'patch-revision-rev_content_format.sql' ),
+                       array( 'addField', 'revision', 'rev_content_model', 'patch-revision-rev_content_model.sql' ),
                        array( 'addField',      'archive',      'ar_content_format',            'patch-archive-ar_content_format.sql' ),
                        array( 'addField',      'archive',      'ar_content_model',                 'patch-archive-ar_content_model.sql' ),
                        array( 'addField',      'page',     'page_content_model',               'patch-page-page_content_model.sql' ),
+                       array( 'enableContentHandlerUseDB' ),
                        array( 'dropField', 'site_stats',   'ss_admins',        'patch-drop-ss_admins.sql' ),
                        array( 'dropField', 'recentchanges', 'rc_moved_to_title',            'patch-rc_moved.sql' ),
                        array( 'addTable', 'sites',                            'patch-sites.sql' ),
-                       array( 'addField', 'filearchive',   'fa_sha1',          'patch-fa_sha1.sql' ),
-                       array( 'addField', 'job',           'job_token',         'patch-job_token.sql' ),
-                       array( 'addField', 'job',           'job_attempts',       'patch-job_attempts.sql' ),
+                       array( 'addField', 'filearchive', 'fa_sha1', 'patch-fa_sha1.sql' ),
+                       array( 'addField', 'job', 'job_token', 'patch-job_token.sql' ),
+                       array( 'addField', 'job', 'job_attempts', 'patch-job_attempts.sql' ),
                        array( 'doEnableProfiling' ),
-                       array( 'addField', 'uploadstash',      'us_props',      'patch-uploadstash-us_props.sql' ),
+                       array( 'addField', 'uploadstash', 'us_props', 'patch-uploadstash-us_props.sql' ),
                        array( 'modifyField', 'user_groups', 'ug_group', 'patch-ug_group-length-increase-255.sql' ),
-                       array( 'modifyField', 'user_former_groups', 'ufg_group', 'patch-ufg_group-length-increase-255.sql' ),
-                       array( 'addIndex', 'page_props', 'pp_propname_page',  'patch-page_props-propname-page-index.sql' ),
+                       array( 'modifyField', 'user_former_groups', 'ufg_group',
+                               'patch-ufg_group-length-increase-255.sql' ),
+                       array( 'addIndex', 'page_props', 'pp_propname_page',
+                               'patch-page_props-propname-page-index.sql' ),
                        array( 'addIndex', 'image', 'img_media_mime', 'patch-img_media_mime-index.sql' ),
 
                        // 1.22
                        array( 'doIwlinksIndexNonUnique' ),
-                       array( 'addIndex', 'iwlinks', 'iwl_prefix_from_title',  'patch-iwlinks-from-title-index.sql' ),
-                       array( 'addField',      'archive',      'ar_id',                    'patch-archive-ar_id.sql' ),
-                       array( 'addField',      'externallinks',  'el_id',  'patch-externallinks-el_id.sql' ),
+                       array( 'addIndex', 'iwlinks', 'iwl_prefix_from_title',
+                               'patch-iwlinks-from-title-index.sql' ),
+                       array( 'addField', 'archive', 'ar_id', 'patch-archive-ar_id.sql' ),
+                       array( 'addField', 'externallinks', 'el_id', 'patch-externallinks-el_id.sql' ),
+                       array( 'addField', 'recentchanges', 'rc_source', 'patch-rc_source.sql' ),
                );
        }
 
@@ -302,7 +312,11 @@ class MysqlUpdater extends DatabaseUpdater {
                }
 
                $this->applyPatch( 'patch-interwiki.sql', false, 'Creating interwiki table' );
-               $this->applyPatch( "$IP/maintenance/interwiki.sql", true, 'Adding default interwiki definitions' );
+               $this->applyPatch(
+                       "$IP/maintenance/interwiki.sql",
+                       true,
+                       'Adding default interwiki definitions'
+               );
        }
 
        /**
@@ -335,7 +349,13 @@ class MysqlUpdater extends DatabaseUpdater {
                        return;
                }
 
-               if ( $this->applyPatch( 'patch-fix-il_from.sql', false, "Fixing ancient broken imagelinks table." ) ) {
+               $applied = $this->applyPatch(
+                       'patch-fix-il_from.sql',
+                       false,
+                       'Fixing ancient broken imagelinks table.'
+               );
+
+               if ( $applied ) {
                        $this->output( "NOTE: you will have to run maintenance/refreshLinks.php after this." );
                }
        }
@@ -345,7 +365,12 @@ class MysqlUpdater extends DatabaseUpdater {
         */
        function doWatchlistUpdate() {
                $talk = $this->db->selectField( 'watchlist', 'count(*)', 'wl_namespace & 1', __METHOD__ );
-               $nontalk = $this->db->selectField( 'watchlist', 'count(*)', 'NOT (wl_namespace & 1)', __METHOD__ );
+               $nontalk = $this->db->selectField(
+                       'watchlist',
+                       'count(*)',
+                       'NOT (wl_namespace & 1)',
+                       __METHOD__
+               );
                if ( $talk == $nontalk ) {
                        $this->output( "...watchlist talk page rows already present.\n" );
 
@@ -374,10 +399,21 @@ class MysqlUpdater extends DatabaseUpdater {
                $this->output( wfTimestamp( TS_DB ) );
                $this->output( "......checking for duplicate entries.\n" );
 
-               list( $cur, $old, $page, $revision, $text ) = $this->db->tableNamesN( 'cur', 'old', 'page', 'revision', 'text' );
+               list( $cur, $old, $page, $revision, $text ) = $this->db->tableNamesN(
+                       'cur',
+                       'old',
+                       'page',
+                       'revision',
+                       'text'
+               );
 
-               $rows = $this->db->query( "SELECT cur_title, cur_namespace, COUNT(cur_namespace) AS c
-                               FROM $cur GROUP BY cur_title, cur_namespace HAVING c>1", __METHOD__ );
+               $rows = $this->db->query( "
+                       SELECT cur_title, cur_namespace, COUNT(cur_namespace) AS c
+                       FROM $cur
+                       GROUP BY cur_title, cur_namespace
+                       HAVING c>1",
+                       __METHOD__
+               );
 
                if ( $rows->numRows() > 0 ) {
                        $this->output( wfTimestamp( TS_DB ) );
@@ -388,8 +424,13 @@ class MysqlUpdater extends DatabaseUpdater {
                                if ( !isset( $duplicate[$row->cur_namespace] ) ) {
                                        $duplicate[$row->cur_namespace] = array();
                                }
+
                                $duplicate[$row->cur_namespace][] = $row->cur_title;
-                               $this->output( sprintf( "      %-60s %3s %5s\n", $row->cur_title, $row->cur_namespace, $row->c ) );
+                               $this->output( sprintf(
+                                       "      %-60s %3s %5s\n",
+                                       $row->cur_title, $row->cur_namespace,
+                                       $row->c
+                               ) );
                        }
                        $sql = "SELECT cur_title, cur_namespace, cur_id, cur_timestamp FROM $cur WHERE ";
                        $firstCond = true;
@@ -474,7 +515,10 @@ class MysqlUpdater extends DatabaseUpdater {
 
                $this->output( wfTimestamp( TS_DB ) );
                $this->output( "......Locking tables.\n" );
-               $this->db->query( "LOCK TABLES $page WRITE, $revision WRITE, $old WRITE, $cur WRITE", __METHOD__ );
+               $this->db->query(
+                       "LOCK TABLES $page WRITE, $revision WRITE, $old WRITE, $cur WRITE",
+                       __METHOD__
+               );
 
                $maxold = intval( $this->db->selectField( 'old', 'max(old_id)', '', __METHOD__ ) );
                $this->output( wfTimestamp( TS_DB ) );
@@ -495,27 +539,38 @@ class MysqlUpdater extends DatabaseUpdater {
                        $cur_text = 'cur_text';
                        $cur_flags = "''";
                }
-               $this->db->query( "INSERT INTO $old (old_namespace, old_title, old_text, old_comment, old_user, old_user_text,
-                                       old_timestamp, old_minor_edit, old_flags)
-                       SELECT cur_namespace, cur_title, $cur_text, cur_comment, cur_user, cur_user_text, cur_timestamp, cur_minor_edit, $cur_flags
-                       FROM $cur", __METHOD__ );
+               $this->db->query(
+                       "INSERT INTO $old (old_namespace, old_title, old_text, old_comment, old_user,
+                               old_user_text, old_timestamp, old_minor_edit, old_flags)
+                       SELECT cur_namespace, cur_title, $cur_text, cur_comment, cur_user, cur_user_text,
+                               cur_timestamp, cur_minor_edit, $cur_flags
+                       FROM $cur",
+                       __METHOD__
+               );
 
                $this->output( wfTimestamp( TS_DB ) );
                $this->output( "......Setting up revision table.\n" );
-               $this->db->query( "INSERT INTO $revision (rev_id, rev_page, rev_comment, rev_user, rev_user_text, rev_timestamp,
-                               rev_minor_edit)
+               $this->db->query(
+                       "INSERT INTO $revision (rev_id, rev_page, rev_comment, rev_user,
+                               rev_user_text, rev_timestamp, rev_minor_edit)
                        SELECT old_id, cur_id, old_comment, old_user, old_user_text,
                                old_timestamp, old_minor_edit
-                       FROM $old,$cur WHERE old_namespace=cur_namespace AND old_title=cur_title", __METHOD__ );
+                       FROM $old,$cur WHERE old_namespace=cur_namespace AND old_title=cur_title",
+                       __METHOD__
+               );
 
                $this->output( wfTimestamp( TS_DB ) );
                $this->output( "......Setting up page table.\n" );
-               $this->db->query( "INSERT INTO $page (page_id, page_namespace, page_title, page_restrictions, page_counter,
-                       page_is_redirect, page_is_new, page_random, page_touched, page_latest, page_len)
-                       SELECT cur_id, cur_namespace, cur_title, cur_restrictions, cur_counter, cur_is_redirect, cur_is_new,
-                               cur_random, cur_touched, rev_id, LENGTH(cur_text)
+               $this->db->query(
+                       "INSERT INTO $page (page_id, page_namespace, page_title,
+                               page_restrictions, page_counter, page_is_redirect, page_is_new, page_random,
+                               page_touched, page_latest, page_len)
+                       SELECT cur_id, cur_namespace, cur_title, cur_restrictions, cur_counter,
+                               cur_is_redirect, cur_is_new, cur_random, cur_touched, rev_id, LENGTH(cur_text)
                        FROM $cur,$revision
-                       WHERE cur_id=rev_page AND rev_timestamp=cur_timestamp AND rev_id > {$maxold}", __METHOD__ );
+                       WHERE cur_id=rev_page AND rev_timestamp=cur_timestamp AND rev_id > {$maxold}",
+                       __METHOD__
+               );
 
                $this->output( wfTimestamp( TS_DB ) );
                $this->output( "......Unlocking tables.\n" );
@@ -562,7 +617,11 @@ class MysqlUpdater extends DatabaseUpdater {
                        return;
                }
 
-               $this->applyPatch( 'patch-pagelinks.sql', false, "Converting links and brokenlinks tables to pagelinks" );
+               $this->applyPatch(
+                       'patch-pagelinks.sql',
+                       false,
+                       'Converting links and brokenlinks tables to pagelinks'
+               );
 
                global $wgContLang;
                foreach ( MWNamespace::getCanonicalNamespaces() as $ns => $name ) {
@@ -616,14 +675,16 @@ class MysqlUpdater extends DatabaseUpdater {
                        if ( $info->type() == 'int' ) {
                                $oldug = $this->db->tableName( 'user_groups' );
                                $newug = $this->db->tableName( 'user_groups_bogus' );
-                               $this->output( "user_groups table exists but is in bogus intermediate format. Renaming to $newug... " );
+                               $this->output( "user_groups table exists but is in bogus intermediate " .
+                                       "format. Renaming to $newug... " );
                                $this->db->query( "ALTER TABLE $oldug RENAME TO $newug", __METHOD__ );
                                $this->output( "done.\n" );
 
                                $this->applyPatch( 'patch-user_groups.sql', false, "Re-adding fresh user_groups table" );
 
                                $this->output( "***\n" );
-                               $this->output( "*** WARNING: You will need to manually fix up user permissions in the user_groups\n" );
+                               $this->output( "*** WARNING: You will need to manually fix up user " .
+                                       "permissions in the user_groups\n" );
                                $this->output( "*** table. Old 1.5 alpha versions did some pretty funky stuff...\n" );
                                $this->output( "***\n" );
                        } else {
@@ -637,7 +698,11 @@ class MysqlUpdater extends DatabaseUpdater {
 
                if ( !$this->db->tableExists( 'user_rights', __METHOD__ ) ) {
                        if ( $this->db->fieldExists( 'user', 'user_rights', __METHOD__ ) ) {
-                               $this->db->applyPatch( 'patch-user_rights.sql', false, "Upgrading from a 1.3 or older database? Breaking out user_rights for conversion" );
+                               $this->db->applyPatch(
+                                       'patch-user_rights.sql',
+                                       false,
+                                       'Upgrading from a 1.3 or older database? Breaking out user_rights for conversion'
+                               );
                        } else {
                                $this->output( "*** WARNING: couldn't locate user_rights table or field for upgrade.\n" );
                                $this->output( "*** You may need to manually configure some sysops by manipulating\n" );
@@ -684,7 +749,11 @@ class MysqlUpdater extends DatabaseUpdater {
                        return;
                }
 
-               $this->applyPatch( 'patch-watchlist-null.sql', false, "Making wl_notificationtimestamp nullable" );
+               $this->applyPatch(
+                       'patch-watchlist-null.sql',
+                       false,
+                       'Making wl_notificationtimestamp nullable'
+               );
        }
 
        /**
@@ -744,7 +813,8 @@ class MysqlUpdater extends DatabaseUpdater {
                                ), __METHOD__
                        );
                }
-               $this->output( "Done. Please run maintenance/refreshLinks.php for a more thorough templatelinks update.\n" );
+               $this->output( "Done. Please run maintenance/refreshLinks.php for a more " .
+                       "thorough templatelinks update.\n" );
        }
 
        protected function doBacklinkingIndicesUpdate() {
@@ -768,8 +838,16 @@ class MysqlUpdater extends DatabaseUpdater {
                        return;
                }
 
-               $this->applyPatch( 'patch-page_restrictions.sql', false, "Creating page_restrictions table (1/2)" );
-               $this->applyPatch( 'patch-page_restrictions_sortkey.sql', false, "Creating page_restrictions table (2/2)" );
+               $this->applyPatch(
+                       'patch-page_restrictions.sql',
+                       false,
+                       'Creating page_restrictions table (1/2)'
+               );
+               $this->applyPatch(
+                       'patch-page_restrictions_sortkey.sql',
+                       false,
+                       'Creating page_restrictions table (2/2)'
+               );
                $this->output( "done.\n" );
 
                $this->output( "Migrating old restrictions to new table...\n" );
@@ -838,7 +916,11 @@ class MysqlUpdater extends DatabaseUpdater {
                        return true;
                }
 
-               return $this->applyPatch( 'patch-profiling-memory.sql', false, "Adding pf_memory field to table profiling" );
+               return $this->applyPatch(
+                       'patch-profiling-memory.sql',
+                       false,
+                       'Adding pf_memory field to table profiling'
+               );
        }
 
        protected function doFilearchiveIndicesUpdate() {
@@ -858,12 +940,17 @@ class MysqlUpdater extends DatabaseUpdater {
                        return true;
                }
                if ( $this->skipSchema ) {
-                       $this->output( "...skipping schema change (making pl_namespace, tl_namespace and il_to indices UNIQUE).\n" );
+                       $this->output( "...skipping schema change (making pl_namespace, tl_namespace " .
+                               "and il_to indices UNIQUE).\n" );
 
                        return false;
                }
 
-               return $this->applyPatch( 'patch-pl-tl-il-unique.sql', false, "Making pl_namespace, tl_namespace and il_to indices UNIQUE" );
+               return $this->applyPatch(
+                       'patch-pl-tl-il-unique.sql',
+                       false,
+                       'Making pl_namespace, tl_namespace and il_to indices UNIQUE'
+               );
        }
 
        protected function doUpdateMimeMinorField() {
@@ -873,7 +960,11 @@ class MysqlUpdater extends DatabaseUpdater {
                        return;
                }
 
-               $this->applyPatch( 'patch-mime_minor_length.sql', false, "Altering all *_mime_minor fields to 100 bytes in size" );
+               $this->applyPatch(
+                       'patch-mime_minor_length.sql',
+                       false,
+                       'Altering all *_mime_minor fields to 100 bytes in size'
+               );
        }
 
        protected function doClFieldsUpdate() {
@@ -883,7 +974,11 @@ class MysqlUpdater extends DatabaseUpdater {
                        return;
                }
 
-               $this->applyPatch( 'patch-categorylinks-better-collation2.sql', false, 'Updating categorylinks (again)' );
+               $this->applyPatch(
+                       'patch-categorylinks-better-collation2.sql',
+                       false,
+                       'Updating categorylinks (again)'
+               );
        }
 
        protected function doLangLinksLengthUpdate() {
@@ -892,7 +987,11 @@ class MysqlUpdater extends DatabaseUpdater {
                $row = $this->db->fetchObject( $res );
 
                if ( $row && $row->Type == "varbinary(10)" ) {
-                       $this->applyPatch( 'patch-langlinks-ll_lang-20.sql', false, 'Updating length of ll_lang in langlinks' );
+                       $this->applyPatch(
+                               'patch-langlinks-ll_lang-20.sql',
+                               false,
+                               'Updating length of ll_lang in langlinks'
+                       );
                } else {
                        $this->output( "...ll_lang is up-to-date.\n" );
                }
@@ -913,7 +1012,11 @@ class MysqlUpdater extends DatabaseUpdater {
                        return;
                }
 
-               $this->applyPatch( 'patch-user-newtalk-timestamp-null.sql', false, "Making user_last_timestamp nullable" );
+               $this->applyPatch(
+                       'patch-user-newtalk-timestamp-null.sql',
+                       false,
+                       'Making user_last_timestamp nullable'
+               );
        }
 
        protected function doIwlinksIndexNonUnique() {
@@ -929,6 +1032,10 @@ class MysqlUpdater extends DatabaseUpdater {
                        return false;
                }
 
-               return $this->applyPatch( 'patch-iwl_prefix_title_from-non-unique.sql', false, "Making iwl_prefix_title_from index non-UNIQUE" );
+               return $this->applyPatch(
+                       'patch-iwl_prefix_title_from-non-unique.sql',
+                       false,
+                       'Making iwl_prefix_title_from index non-UNIQUE'
+               );
        }
 }
index efa8d00..7757510 100644 (file)
@@ -60,12 +60,22 @@ class OracleInstaller extends DatabaseInstaller {
                        $this->parent->setVar( 'wgDBserver', '' );
                }
 
-               return $this->getTextBox( 'wgDBserver', 'config-db-host-oracle', array(), $this->parent->getHelpBox( 'config-db-host-oracle-help' ) ) .
+               return $this->getTextBox(
+                       'wgDBserver',
+                       'config-db-host-oracle',
+                       array(),
+                       $this->parent->getHelpBox( 'config-db-host-oracle-help' )
+               ) .
                        Html::openElement( 'fieldset' ) .
                        Html::element( 'legend', array(), wfMessage( 'config-db-wiki-settings' )->text() ) .
                        $this->getTextBox( 'wgDBprefix', 'config-db-prefix' ) .
                        $this->getTextBox( '_OracleDefTS', 'config-oracle-def-ts' ) .
-                       $this->getTextBox( '_OracleTempTS', 'config-oracle-temp-ts', array(), $this->parent->getHelpBox( 'config-db-oracle-help' ) ) .
+                       $this->getTextBox(
+                               '_OracleTempTS',
+                               'config-oracle-temp-ts',
+                               array(),
+                               $this->parent->getHelpBox( 'config-db-oracle-help' )
+                       ) .
                        Html::closeElement( 'fieldset' ) .
                        $this->parent->getWarningBox( wfMessage( 'config-db-account-oracle-warn' )->text() ) .
                        $this->getInstallUserBox() .
@@ -81,7 +91,12 @@ class OracleInstaller extends DatabaseInstaller {
 
        public function submitConnectForm() {
                // Get variables from the request
-               $newValues = $this->setVarsFromRequest( array( 'wgDBserver', 'wgDBprefix', 'wgDBuser', 'wgDBpassword' ) );
+               $newValues = $this->setVarsFromRequest(
+                       'wgDBserver',
+                       'wgDBprefix',
+                       'wgDBuser',
+                       'wgDBpassword'
+               );
                $this->parent->setVar( 'wgDBname', $this->getVar( 'wgDBuser' ) );
 
                // Validate them
@@ -318,8 +333,11 @@ class OracleInstaller extends DatabaseInstaller {
         * @return bool Whether the connection string is valid.
         */
        public static function checkConnectStringFormat( $connect_string ) {
+               // @@codingStandardsIgnoreStart Long lines with regular expressions.
+               // @todo Very long regular expression. Make more readable?
                $isValid = preg_match( '/^[[:alpha:]][\w\-]*(?:\.[[:alpha:]][\w\-]*){0,2}$/', $connect_string ); // TNS name
                $isValid |= preg_match( '/^(?:\/\/)?[\w\-\.]+(?::[\d]+)?(?:\/(?:[\w\-\.]+(?::(pooled|dedicated|shared))?)?(?:\/[\w\-\.]+)?)?$/', $connect_string ); // EZConnect
+               // @@codingStandardsIgnoreEnd
                return (bool)$isValid;
        }
 }
index 8484189..7b6f49c 100644 (file)
@@ -38,6 +38,8 @@ class OracleUpdater extends DatabaseUpdater {
 
        protected function getCoreUpdateList() {
                return array(
+                       array( 'disableContentHandlerUseDB' ),
+
                        // 1.17
                        array( 'doNamespaceDefaults' ),
                        array( 'doFKRenameDeferr' ),
@@ -48,13 +50,13 @@ class OracleUpdater extends DatabaseUpdater {
                        array( 'addTable', 'user_former_groups', 'patch-user_former_groups.sql' ),
 
                        //1.18
-                       array( 'addIndex',      'user',          'i02',       'patch-user_email_index.sql' ),
+                       array( 'addIndex', 'user', 'i02', 'patch-user_email_index.sql' ),
                        array( 'modifyField', 'user_properties', 'up_property', 'patch-up_property.sql' ),
                        array( 'addTable', 'uploadstash', 'patch-uploadstash.sql' ),
                        array( 'doRecentchangesFK2Cascade' ),
 
                        //1.19
-                       array( 'addIndex', 'logging',       'i05',      'patch-logging_type_action_index.sql'),
+                       array( 'addIndex', 'logging', 'i05', 'patch-logging_type_action_index.sql' ),
                        array( 'addField', 'revision', 'rev_sha1', 'patch-rev_sha1_field.sql' ),
                        array( 'addField', 'archive', 'ar_sha1', 'patch-ar_sha1_field.sql' ),
                        array( 'doRemoveNotNullEmptyDefaults2' ),
@@ -70,22 +72,26 @@ class OracleUpdater extends DatabaseUpdater {
                        array( 'dropField', 'category', 'cat_hidden', 'patch-cat_hidden.sql' ),
 
                        //1.21
-                       array( 'addField',      'revision',     'rev_content_format',           'patch-revision-rev_content_format.sql' ),
-                       array( 'addField',      'revision',     'rev_content_model',            'patch-revision-rev_content_model.sql' ),
-                       array( 'addField',      'archive',      'ar_content_format',            'patch-archive-ar_content_format.sql' ),
-                       array( 'addField',      'archive',      'ar_content_model',             'patch-archive-ar_content_model.sql' ),
-                       array( 'addField',      'archive',      'ar_id',                        'patch-archive-ar_id.sql' ),
-                       array( 'addField',      'externallinks',        'el_id',                'patch-externallinks-el_id.sql' ),
-                       array( 'addField',      'page',     'page_content_model',               'patch-page-page_content_model.sql' ),
-                       array( 'dropField', 'site_stats', 'ss_admins',  'patch-ss_admins.sql' ),
+                       array( 'addField', 'revision', 'rev_content_format',
+                               'patch-revision-rev_content_format.sql' ),
+                       array( 'addField', 'revision', 'rev_content_model',
+                               'patch-revision-rev_content_model.sql' ),
+                       array( 'addField', 'archive', 'ar_content_format', 'patch-archive-ar_content_format.sql' ),
+                       array( 'addField', 'archive', 'ar_content_model', 'patch-archive-ar_content_model.sql' ),
+                       array( 'addField', 'archive', 'ar_id', 'patch-archive-ar_id.sql' ),
+                       array( 'addField', 'externallinks', 'el_id', 'patch-externallinks-el_id.sql' ),
+                       array( 'addField', 'page', 'page_content_model', 'patch-page-page_content_model.sql' ),
+                       array( 'enableContentHandlerUseDB' ),
+                       array( 'dropField', 'site_stats', 'ss_admins', 'patch-ss_admins.sql' ),
                        array( 'dropField', 'recentchanges', 'rc_moved_to_title', 'patch-rc_moved.sql' ),
-                       array( 'addTable', 'sites',                            'patch-sites.sql' ),
-                       array( 'addField', 'filearchive',   'fa_sha1',          'patch-fa_sha1.sql' ),
-                       array( 'addField', 'job',           'job_token',         'patch-job_token.sql' ),
-                       array( 'addField', 'job',           'job_attempts',       'patch-job_attempts.sql' ),
-                       array( 'addField', 'uploadstash',      'us_props',      'patch-uploadstash-us_props.sql' ),
+                       array( 'addTable', 'sites', 'patch-sites.sql' ),
+                       array( 'addField', 'filearchive', 'fa_sha1', 'patch-fa_sha1.sql' ),
+                       array( 'addField', 'job', 'job_token', 'patch-job_token.sql' ),
+                       array( 'addField', 'job', 'job_attempts', 'patch-job_attempts.sql' ),
+                       array( 'addField', 'uploadstash', 'us_props', 'patch-uploadstash-us_props.sql' ),
                        array( 'modifyField', 'user_groups', 'ug_group', 'patch-ug_group-length-increase-255.sql' ),
-                       array( 'modifyField', 'user_former_groups', 'ufg_group', 'patch-ufg_group-length-increase-255.sql' ),
+                       array( 'modifyField', 'user_former_groups', 'ufg_group',
+                               'patch-ufg_group-length-increase-255.sql' ),
 
                        // KEEP THIS AT THE BOTTOM!!
                        array( 'doRebuildDuplicateFunction' ),
@@ -104,14 +110,22 @@ class OracleUpdater extends DatabaseUpdater {
                        return;
                }
 
-               $this->applyPatch( 'patch_namespace_defaults.sql', false, "Altering namespace fields with default value" );
+               $this->applyPatch(
+                       'patch_namespace_defaults.sql',
+                       false,
+                       'Altering namespace fields with default value'
+               );
        }
 
        /**
         * Uniform FK names + deferrable state
         */
        protected function doFKRenameDeferr() {
-               $meta = $this->db->query( 'SELECT COUNT(*) cnt FROM user_constraints WHERE constraint_type = \'R\' AND deferrable = \'DEFERRABLE\'' );
+               $meta = $this->db->query( '
+                       SELECT COUNT(*) cnt
+                       FROM user_constraints
+                       WHERE constraint_type = \'R\' AND deferrable = \'DEFERRABLE\''
+               );
                $row = $meta->fetchRow();
                if ( $row && $row['cnt'] > 0 ) {
                        return;
@@ -169,7 +183,11 @@ class OracleUpdater extends DatabaseUpdater {
                if ( $meta->isNullable() ) {
                        return;
                }
-               $this->applyPatch( 'patch_remove_not_null_empty_defs.sql', false, "Removing not null empty constraints" );
+               $this->applyPatch(
+                       'patch_remove_not_null_empty_defs.sql',
+                       false,
+                       'Removing not null empty constraints'
+               );
        }
 
        protected function doRemoveNotNullEmptyDefaults2() {
@@ -177,7 +195,11 @@ class OracleUpdater extends DatabaseUpdater {
                if ( $meta->isNullable() ) {
                        return;
                }
-               $this->applyPatch( 'patch_remove_not_null_empty_defs2.sql', false, "Removing not null empty constraints" );
+               $this->applyPatch(
+                       'patch_remove_not_null_empty_defs2.sql',
+                       false,
+                       'Removing not null empty constraints'
+               );
        }
 
        /**
index a7e8462..2cf4156 100644 (file)
@@ -42,8 +42,8 @@ class PostgresInstaller extends DatabaseInstaller {
                '_InstallUser' => 'postgres',
        );
 
-       var $minimumVersion = '8.3';
-       var $maxRoleSearchDepth = 5;
+       public $minimumVersion = '8.3';
+       public $maxRoleSearchDepth = 5;
 
        protected $pgConns = array();
 
@@ -56,12 +56,27 @@ class PostgresInstaller extends DatabaseInstaller {
        }
 
        function getConnectForm() {
-               return $this->getTextBox( 'wgDBserver', 'config-db-host', array(), $this->parent->getHelpBox( 'config-db-host-help' ) ) .
+               return $this->getTextBox(
+                       'wgDBserver',
+                       'config-db-host',
+                       array(),
+                       $this->parent->getHelpBox( 'config-db-host-help' )
+               ) .
                        $this->getTextBox( 'wgDBport', 'config-db-port' ) .
                        Html::openElement( 'fieldset' ) .
                        Html::element( 'legend', array(), wfMessage( 'config-db-wiki-settings' )->text() ) .
-                       $this->getTextBox( 'wgDBname', 'config-db-name', array(), $this->parent->getHelpBox( 'config-db-name-help' ) ) .
-                       $this->getTextBox( 'wgDBmwschema', 'config-db-schema', array(), $this->parent->getHelpBox( 'config-db-schema-help' ) ) .
+                       $this->getTextBox(
+                               'wgDBname',
+                               'config-db-name',
+                               array(),
+                               $this->parent->getHelpBox( 'config-db-name-help' )
+                       ) .
+                       $this->getTextBox(
+                               'wgDBmwschema',
+                               'config-db-schema',
+                               array(),
+                               $this->parent->getHelpBox( 'config-db-schema-help' )
+                       ) .
                        Html::closeElement( 'fieldset' ) .
                        $this->getInstallUserBox();
        }
index f0e4aec..32e7510 100644 (file)
@@ -51,16 +51,16 @@ class PostgresUpdater extends DatabaseUpdater {
                        array( 'renameIndex', 'pagecontent', 'text_pkey', 'pagecontent_pkey' ),
 
                        # renamed sequences
-                       array( 'renameSequence', 'ipblocks_ipb_id_val', 'ipblocks_ipb_id_seq'         ),
-                       array( 'renameSequence', 'rev_rev_id_val',      'revision_rev_id_seq'         ),
-                       array( 'renameSequence', 'text_old_id_val',     'text_old_id_seq'             ),
-                       array( 'renameSequence', 'rc_rc_id_seq',        'recentchanges_rc_id_seq'     ),
-                       array( 'renameSequence', 'log_log_id_seq',      'logging_log_id_seq'          ),
-                       array( 'renameSequence', 'pr_id_val',           'page_restrictions_pr_id_seq' ),
-                       array( 'renameSequence', 'us_id_seq',           'uploadstash_us_id_seq' ),
+                       array( 'renameSequence', 'ipblocks_ipb_id_val', 'ipblocks_ipb_id_seq' ),
+                       array( 'renameSequence', 'rev_rev_id_val', 'revision_rev_id_seq' ),
+                       array( 'renameSequence', 'text_old_id_val', 'text_old_id_seq' ),
+                       array( 'renameSequence', 'rc_rc_id_seq', 'recentchanges_rc_id_seq' ),
+                       array( 'renameSequence', 'log_log_id_seq', 'logging_log_id_seq' ),
+                       array( 'renameSequence', 'pr_id_val', 'page_restrictions_pr_id_seq' ),
+                       array( 'renameSequence', 'us_id_seq', 'uploadstash_us_id_seq' ),
 
                        # since r58263
-                       array( 'renameSequence', 'category_id_seq', 'category_cat_id_seq'),
+                       array( 'renameSequence', 'category_id_seq', 'category_cat_id_seq' ),
 
                        # new sequences if not renamed above
                        array( 'addSequence', 'logging', false, 'logging_log_id_seq' ),
@@ -68,200 +68,216 @@ class PostgresUpdater extends DatabaseUpdater {
                        array( 'addSequence', 'filearchive', 'fa_id', 'filearchive_fa_id_seq' ),
 
                        # new tables
-                       array( 'addTable', 'category',          'patch-category.sql' ),
-                       array( 'addTable', 'page',              'patch-page.sql' ),
-                       array( 'addTable', 'querycachetwo',     'patch-querycachetwo.sql' ),
-                       array( 'addTable', 'page_props',        'patch-page_props.sql' ),
+                       array( 'addTable', 'category', 'patch-category.sql' ),
+                       array( 'addTable', 'page', 'patch-page.sql' ),
+                       array( 'addTable', 'querycachetwo', 'patch-querycachetwo.sql' ),
+                       array( 'addTable', 'page_props', 'patch-page_props.sql' ),
                        array( 'addTable', 'page_restrictions', 'patch-page_restrictions.sql' ),
-                       array( 'addTable', 'profiling',         'patch-profiling.sql' ),
-                       array( 'addTable', 'protected_titles',  'patch-protected_titles.sql' ),
-                       array( 'addTable', 'redirect',          'patch-redirect.sql' ),
-                       array( 'addTable', 'updatelog',         'patch-updatelog.sql' ),
-                       array( 'addTable', 'change_tag',        'patch-change_tag.sql' ),
-                       array( 'addTable', 'tag_summary',       'patch-tag_summary.sql' ),
-                       array( 'addTable', 'valid_tag',         'patch-valid_tag.sql' ),
-                       array( 'addTable', 'user_properties',   'patch-user_properties.sql' ),
-                       array( 'addTable', 'log_search',        'patch-log_search.sql' ),
-                       array( 'addTable', 'l10n_cache',        'patch-l10n_cache.sql' ),
-                       array( 'addTable', 'iwlinks',           'patch-iwlinks.sql' ),
-                       array( 'addTable', 'msg_resource',      'patch-msg_resource.sql' ),
-                       array( 'addTable', 'msg_resource_links','patch-msg_resource_links.sql' ),
-                       array( 'addTable', 'module_deps',       'patch-module_deps.sql' ),
-                       array( 'addTable', 'uploadstash',       'patch-uploadstash.sql' ),
-                       array( 'addTable', 'user_former_groups','patch-user_former_groups.sql' ),
-                       array( 'addTable', 'sites',             'patch-sites.sql' ),
+                       array( 'addTable', 'profiling', 'patch-profiling.sql' ),
+                       array( 'addTable', 'protected_titles', 'patch-protected_titles.sql' ),
+                       array( 'addTable', 'redirect', 'patch-redirect.sql' ),
+                       array( 'addTable', 'updatelog', 'patch-updatelog.sql' ),
+                       array( 'addTable', 'change_tag', 'patch-change_tag.sql' ),
+                       array( 'addTable', 'tag_summary', 'patch-tag_summary.sql' ),
+                       array( 'addTable', 'valid_tag', 'patch-valid_tag.sql' ),
+                       array( 'addTable', 'user_properties', 'patch-user_properties.sql' ),
+                       array( 'addTable', 'log_search', 'patch-log_search.sql' ),
+                       array( 'addTable', 'l10n_cache', 'patch-l10n_cache.sql' ),
+                       array( 'addTable', 'iwlinks', 'patch-iwlinks.sql' ),
+                       array( 'addTable', 'msg_resource', 'patch-msg_resource.sql' ),
+                       array( 'addTable', 'msg_resource_links', 'patch-msg_resource_links.sql' ),
+                       array( 'addTable', 'module_deps', 'patch-module_deps.sql' ),
+                       array( 'addTable', 'uploadstash', 'patch-uploadstash.sql' ),
+                       array( 'addTable', 'user_former_groups', 'patch-user_former_groups.sql' ),
+                       array( 'addTable', 'sites', 'patch-sites.sql' ),
 
                        # Needed before new field
                        array( 'convertArchive2' ),
 
                        # new fields
-                       array( 'addPgField', 'updatelog',     'ul_value',             'TEXT' ),
-                       array( 'addPgField', 'archive',       'ar_deleted',           'SMALLINT NOT NULL DEFAULT 0' ),
-                       array( 'addPgField', 'archive',       'ar_len',               'INTEGER' ),
-                       array( 'addPgField', 'archive',       'ar_page_id',           'INTEGER' ),
-                       array( 'addPgField', 'archive',       'ar_parent_id',         'INTEGER' ),
-                       array( 'addPgField', 'archive',       'ar_content_model',     'TEXT' ),
-                       array( 'addPgField', 'archive',       'ar_content_format',    'TEXT' ),
-                       array( 'addPgField', 'categorylinks', 'cl_sortkey_prefix',    "TEXT NOT NULL DEFAULT ''"),
-                       array( 'addPgField', 'categorylinks', 'cl_collation',         "TEXT NOT NULL DEFAULT 0" ),
-                       array( 'addPgField', 'categorylinks', 'cl_type',              "TEXT NOT NULL DEFAULT 'page'" ),
-                       array( 'addPgField', 'image',         'img_sha1',             "TEXT NOT NULL DEFAULT ''" ),
-                       array( 'addPgField', 'ipblocks',      'ipb_allow_usertalk',   'SMALLINT NOT NULL DEFAULT 0' ),
-                       array( 'addPgField', 'ipblocks',      'ipb_anon_only',        'SMALLINT NOT NULL DEFAULT 0' ),
-                       array( 'addPgField', 'ipblocks',      'ipb_by_text',          "TEXT NOT NULL DEFAULT ''" ),
-                       array( 'addPgField', 'ipblocks',      'ipb_block_email',      'SMALLINT NOT NULL DEFAULT 0' ),
-                       array( 'addPgField', 'ipblocks',      'ipb_create_account',   'SMALLINT NOT NULL DEFAULT 1' ),
-                       array( 'addPgField', 'ipblocks',      'ipb_deleted',          'SMALLINT NOT NULL DEFAULT 0' ),
-                       array( 'addPgField', 'ipblocks',      'ipb_enable_autoblock', 'SMALLINT NOT NULL DEFAULT 1' ),
-                       array( 'addPgField', 'ipblocks',      'ipb_parent_block_id',            'INTEGER DEFAULT NULL REFERENCES ipblocks(ipb_id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED' ),
-                       array( 'addPgField', 'filearchive',   'fa_deleted',           'SMALLINT NOT NULL DEFAULT 0' ),
-                       array( 'addPgField', 'filearchive',   'fa_sha1',              "TEXT NOT NULL DEFAULT ''" ),
-                       array( 'addPgField', 'logging',       'log_deleted',          'SMALLINT NOT NULL DEFAULT 0' ),
-                       array( 'addPgField', 'logging',       'log_id',               "INTEGER NOT NULL PRIMARY KEY DEFAULT nextval('logging_log_id_seq')" ),
-                       array( 'addPgField', 'logging',       'log_params',           'TEXT' ),
-                       array( 'addPgField', 'mwuser',        'user_editcount',       'INTEGER' ),
-                       array( 'addPgField', 'mwuser',        'user_newpass_time',    'TIMESTAMPTZ' ),
-                       array( 'addPgField', 'oldimage',      'oi_deleted',           'SMALLINT NOT NULL DEFAULT 0' ),
-                       array( 'addPgField', 'oldimage',      'oi_major_mime',        "TEXT NOT NULL DEFAULT 'unknown'" ),
-                       array( 'addPgField', 'oldimage',      'oi_media_type',        'TEXT' ),
-                       array( 'addPgField', 'oldimage',      'oi_metadata',          "BYTEA NOT NULL DEFAULT ''" ),
-                       array( 'addPgField', 'oldimage',      'oi_minor_mime',        "TEXT NOT NULL DEFAULT 'unknown'" ),
-                       array( 'addPgField', 'oldimage',      'oi_sha1',              "TEXT NOT NULL DEFAULT ''" ),
-                       array( 'addPgField', 'page',          'page_content_model',   'TEXT' ),
-                       array( 'addPgField', 'page_restrictions', 'pr_id',            "INTEGER NOT NULL UNIQUE DEFAULT nextval('page_restrictions_pr_id_seq')" ),
-                       array( 'addPgField', 'profiling',     'pf_memory',            'NUMERIC(18,10) NOT NULL DEFAULT 0' ),
-                       array( 'addPgField', 'recentchanges', 'rc_deleted',           'SMALLINT NOT NULL DEFAULT 0' ),
-                       array( 'addPgField', 'recentchanges', 'rc_log_action',        'TEXT' ),
-                       array( 'addPgField', 'recentchanges', 'rc_log_type',          'TEXT' ),
-                       array( 'addPgField', 'recentchanges', 'rc_logid',             'INTEGER NOT NULL DEFAULT 0' ),
-                       array( 'addPgField', 'recentchanges', 'rc_new_len',           'INTEGER' ),
-                       array( 'addPgField', 'recentchanges', 'rc_old_len',           'INTEGER' ),
-                       array( 'addPgField', 'recentchanges', 'rc_params',            'TEXT' ),
-                       array( 'addPgField', 'redirect',      'rd_interwiki',         'TEXT NULL' ),
-                       array( 'addPgField', 'redirect',      'rd_fragment',          'TEXT NULL' ),
-                       array( 'addPgField', 'revision',      'rev_deleted',          'SMALLINT NOT NULL DEFAULT 0' ),
-                       array( 'addPgField', 'revision',      'rev_len',              'INTEGER' ),
-                       array( 'addPgField', 'revision',      'rev_parent_id',        'INTEGER DEFAULT NULL' ),
-                       array( 'addPgField', 'revision',      'rev_content_model',    'TEXT' ),
-                       array( 'addPgField', 'revision',      'rev_content_format',   'TEXT' ),
-                       array( 'addPgField', 'site_stats',    'ss_active_users',      "INTEGER DEFAULT '-1'" ),
-                       array( 'addPgField', 'user_newtalk',  'user_last_timestamp',  'TIMESTAMPTZ' ),
-                       array( 'addPgField', 'logging',       'log_user_text',        "TEXT NOT NULL DEFAULT ''" ),
-                       array( 'addPgField', 'logging',       'log_page',             'INTEGER' ),
-                       array( 'addPgField', 'interwiki',     'iw_api',               "TEXT NOT NULL DEFAULT ''"),
-                       array( 'addPgField', 'interwiki',     'iw_wikiid',            "TEXT NOT NULL DEFAULT ''"),
-                       array( 'addPgField', 'revision',      'rev_sha1',             "TEXT NOT NULL DEFAULT ''" ),
-                       array( 'addPgField', 'archive',       'ar_sha1',              "TEXT NOT NULL DEFAULT ''" ),
-                       array( 'addPgField', 'uploadstash',   'us_chunk_inx',         "INTEGER NULL" ),
-                       array( 'addPgField', 'job',           'job_timestamp',        "TIMESTAMPTZ" ),
-                       array( 'addPgField', 'job',           'job_random',           "INTEGER NOT NULL DEFAULT 0" ),
-                       array( 'addPgField', 'job',           'job_attempts',         "INTEGER NOT NULL DEFAULT 0" ),
-                       array( 'addPgField', 'job',           'job_token',            "TEXT NOT NULL DEFAULT ''" ),
-                       array( 'addPgField', 'job',           'job_token_timestamp',  "TIMESTAMPTZ" ),
-                       array( 'addPgField', 'job',           'job_sha1',             "TEXT NOT NULL DEFAULT ''" ),
-                       array( 'addPgField', 'archive',       'ar_id',                "INTEGER NOT NULL PRIMARY KEY DEFAULT nextval('archive_ar_id_seq')" ),
-                       array( 'addPgField', 'externallinks', 'el_id',                "INTEGER NOT NULL PRIMARY KEY DEFAULT nextval('externallinks_el_id_seq')" ),
-
+                       array( 'addPgField', 'updatelog', 'ul_value', 'TEXT' ),
+                       array( 'addPgField', 'archive', 'ar_deleted', 'SMALLINT NOT NULL DEFAULT 0' ),
+                       array( 'addPgField', 'archive', 'ar_len', 'INTEGER' ),
+                       array( 'addPgField', 'archive', 'ar_page_id', 'INTEGER' ),
+                       array( 'addPgField', 'archive', 'ar_parent_id', 'INTEGER' ),
+                       array( 'addPgField', 'archive', 'ar_content_model', 'TEXT' ),
+                       array( 'addPgField', 'archive', 'ar_content_format', 'TEXT' ),
+                       array( 'addPgField', 'categorylinks', 'cl_sortkey_prefix', "TEXT NOT NULL DEFAULT ''" ),
+                       array( 'addPgField', 'categorylinks', 'cl_collation', "TEXT NOT NULL DEFAULT 0" ),
+                       array( 'addPgField', 'categorylinks', 'cl_type', "TEXT NOT NULL DEFAULT 'page'" ),
+                       array( 'addPgField', 'image', 'img_sha1', "TEXT NOT NULL DEFAULT ''" ),
+                       array( 'addPgField', 'ipblocks', 'ipb_allow_usertalk', 'SMALLINT NOT NULL DEFAULT 0' ),
+                       array( 'addPgField', 'ipblocks', 'ipb_anon_only', 'SMALLINT NOT NULL DEFAULT 0' ),
+                       array( 'addPgField', 'ipblocks', 'ipb_by_text', "TEXT NOT NULL DEFAULT ''" ),
+                       array( 'addPgField', 'ipblocks', 'ipb_block_email', 'SMALLINT NOT NULL DEFAULT 0' ),
+                       array( 'addPgField', 'ipblocks', 'ipb_create_account', 'SMALLINT NOT NULL DEFAULT 1' ),
+                       array( 'addPgField', 'ipblocks', 'ipb_deleted', 'SMALLINT NOT NULL DEFAULT 0' ),
+                       array( 'addPgField', 'ipblocks', 'ipb_enable_autoblock', 'SMALLINT NOT NULL DEFAULT 1' ),
+                       array( 'addPgField', 'ipblocks', 'ipb_parent_block_id',
+                               'INTEGER DEFAULT NULL REFERENCES ipblocks(ipb_id) ' .
+                               'ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED' ),
+                       array( 'addPgField', 'filearchive', 'fa_deleted', 'SMALLINT NOT NULL DEFAULT 0' ),
+                       array( 'addPgField', 'filearchive', 'fa_sha1', "TEXT NOT NULL DEFAULT ''" ),
+                       array( 'addPgField', 'logging', 'log_deleted', 'SMALLINT NOT NULL DEFAULT 0' ),
+                       array( 'addPgField', 'logging', 'log_id',
+                               "INTEGER NOT NULL PRIMARY KEY DEFAULT nextval('logging_log_id_seq')" ),
+                       array( 'addPgField', 'logging', 'log_params', 'TEXT' ),
+                       array( 'addPgField', 'mwuser', 'user_editcount', 'INTEGER' ),
+                       array( 'addPgField', 'mwuser', 'user_newpass_time', 'TIMESTAMPTZ' ),
+                       array( 'addPgField', 'oldimage', 'oi_deleted', 'SMALLINT NOT NULL DEFAULT 0' ),
+                       array( 'addPgField', 'oldimage', 'oi_major_mime', "TEXT NOT NULL DEFAULT 'unknown'" ),
+                       array( 'addPgField', 'oldimage', 'oi_media_type', 'TEXT' ),
+                       array( 'addPgField', 'oldimage', 'oi_metadata', "BYTEA NOT NULL DEFAULT ''" ),
+                       array( 'addPgField', 'oldimage', 'oi_minor_mime', "TEXT NOT NULL DEFAULT 'unknown'" ),
+                       array( 'addPgField', 'oldimage', 'oi_sha1', "TEXT NOT NULL DEFAULT ''" ),
+                       array( 'addPgField', 'page', 'page_content_model', 'TEXT' ),
+                       array( 'addPgField', 'page_restrictions', 'pr_id',
+                               "INTEGER NOT NULL UNIQUE DEFAULT nextval('page_restrictions_pr_id_seq')" ),
+                       array( 'addPgField', 'profiling', 'pf_memory', 'NUMERIC(18,10) NOT NULL DEFAULT 0' ),
+                       array( 'addPgField', 'recentchanges', 'rc_deleted', 'SMALLINT NOT NULL DEFAULT 0' ),
+                       array( 'addPgField', 'recentchanges', 'rc_log_action', 'TEXT' ),
+                       array( 'addPgField', 'recentchanges', 'rc_log_type', 'TEXT' ),
+                       array( 'addPgField', 'recentchanges', 'rc_logid', 'INTEGER NOT NULL DEFAULT 0' ),
+                       array( 'addPgField', 'recentchanges', 'rc_new_len', 'INTEGER' ),
+                       array( 'addPgField', 'recentchanges', 'rc_old_len', 'INTEGER' ),
+                       array( 'addPgField', 'recentchanges', 'rc_params', 'TEXT' ),
+                       array( 'addPgField', 'redirect', 'rd_interwiki', 'TEXT NULL' ),
+                       array( 'addPgField', 'redirect', 'rd_fragment', 'TEXT NULL' ),
+                       array( 'addPgField', 'revision', 'rev_deleted', 'SMALLINT NOT NULL DEFAULT 0' ),
+                       array( 'addPgField', 'revision', 'rev_len', 'INTEGER' ),
+                       array( 'addPgField', 'revision', 'rev_parent_id', 'INTEGER DEFAULT NULL' ),
+                       array( 'addPgField', 'revision', 'rev_content_model', 'TEXT' ),
+                       array( 'addPgField', 'revision', 'rev_content_format', 'TEXT' ),
+                       array( 'addPgField', 'site_stats', 'ss_active_users', "INTEGER DEFAULT '-1'" ),
+                       array( 'addPgField', 'user_newtalk', 'user_last_timestamp', 'TIMESTAMPTZ' ),
+                       array( 'addPgField', 'logging', 'log_user_text', "TEXT NOT NULL DEFAULT ''" ),
+                       array( 'addPgField', 'logging', 'log_page', 'INTEGER' ),
+                       array( 'addPgField', 'interwiki', 'iw_api', "TEXT NOT NULL DEFAULT ''" ),
+                       array( 'addPgField', 'interwiki', 'iw_wikiid', "TEXT NOT NULL DEFAULT ''" ),
+                       array( 'addPgField', 'revision', 'rev_sha1', "TEXT NOT NULL DEFAULT ''" ),
+                       array( 'addPgField', 'archive', 'ar_sha1', "TEXT NOT NULL DEFAULT ''" ),
+                       array( 'addPgField', 'uploadstash', 'us_chunk_inx', "INTEGER NULL" ),
+                       array( 'addPgField', 'job', 'job_timestamp', "TIMESTAMPTZ" ),
+                       array( 'addPgField', 'job', 'job_random', "INTEGER NOT NULL DEFAULT 0" ),
+                       array( 'addPgField', 'job', 'job_attempts', "INTEGER NOT NULL DEFAULT 0" ),
+                       array( 'addPgField', 'job', 'job_token', "TEXT NOT NULL DEFAULT ''" ),
+                       array( 'addPgField', 'job', 'job_token_timestamp', "TIMESTAMPTZ" ),
+                       array( 'addPgField', 'job', 'job_sha1', "TEXT NOT NULL DEFAULT ''" ),
+                       array( 'addPgField', 'archive', 'ar_id',
+                               "INTEGER NOT NULL PRIMARY KEY DEFAULT nextval('archive_ar_id_seq')" ),
+                       array( 'addPgField', 'externallinks', 'el_id',
+                               "INTEGER NOT NULL PRIMARY KEY DEFAULT nextval('externallinks_el_id_seq')" ),
 
                        # type changes
-                       array( 'changeField', 'archive',       'ar_deleted',      'smallint', '' ),
-                       array( 'changeField', 'archive',       'ar_minor_edit',   'smallint', 'ar_minor_edit::smallint DEFAULT 0' ),
-                       array( 'changeField', 'filearchive',   'fa_deleted',      'smallint', '' ),
-                       array( 'changeField', 'filearchive',   'fa_height',       'integer',  '' ),
-                       array( 'changeField', 'filearchive',   'fa_metadata',     'bytea',    "decode(fa_metadata,'escape')" ),
-                       array( 'changeField', 'filearchive',   'fa_size',         'integer',  '' ),
-                       array( 'changeField', 'filearchive',   'fa_width',        'integer',  '' ),
-                       array( 'changeField', 'filearchive',   'fa_storage_group', 'text',     '' ),
-                       array( 'changeField', 'filearchive',   'fa_storage_key',  'text',     '' ),
-                       array( 'changeField', 'image',         'img_metadata',    'bytea',    "decode(img_metadata,'escape')" ),
-                       array( 'changeField', 'image',         'img_size',        'integer',  '' ),
-                       array( 'changeField', 'image',         'img_width',       'integer',  '' ),
-                       array( 'changeField', 'image',         'img_height',      'integer',  '' ),
-                       array( 'changeField', 'interwiki',     'iw_local',        'smallint', 'iw_local::smallint' ),
-                       array( 'changeField', 'interwiki',     'iw_trans',        'smallint', 'iw_trans::smallint DEFAULT 0' ),
-                       array( 'changeField', 'ipblocks',      'ipb_auto',        'smallint', 'ipb_auto::smallint DEFAULT 0' ),
-                       array( 'changeField', 'ipblocks',      'ipb_anon_only',   'smallint', "CASE WHEN ipb_anon_only=' ' THEN 0 ELSE ipb_anon_only::smallint END DEFAULT 0" ),
-                       array( 'changeField', 'ipblocks',      'ipb_create_account', 'smallint', "CASE WHEN ipb_create_account=' ' THEN 0 ELSE ipb_create_account::smallint END DEFAULT 1" ),
-                       array( 'changeField', 'ipblocks',      'ipb_enable_autoblock', 'smallint', "CASE WHEN ipb_enable_autoblock=' ' THEN 0 ELSE ipb_enable_autoblock::smallint END DEFAULT 1" ),
-                       array( 'changeField', 'ipblocks',      'ipb_block_email', 'smallint', "CASE WHEN ipb_block_email=' ' THEN 0 ELSE ipb_block_email::smallint END DEFAULT 0" ),
-                       array( 'changeField', 'ipblocks',      'ipb_address',     'text',     'ipb_address::text' ),
-                       array( 'changeField', 'ipblocks',      'ipb_deleted',     'smallint', 'ipb_deleted::smallint DEFAULT 0' ),
-                       array( 'changeField', 'mwuser',        'user_token',      'text',     '' ),
-                       array( 'changeField', 'mwuser',        'user_email_token', 'text',     '' ),
-                       array( 'changeField', 'objectcache',   'keyname',         'text',     '' ),
-                       array( 'changeField', 'oldimage',      'oi_height',       'integer',  '' ),
-                       array( 'changeField', 'oldimage',      'oi_metadata',     'bytea',    "decode(img_metadata,'escape')" ),
-                       array( 'changeField', 'oldimage',      'oi_size',         'integer',  '' ),
-                       array( 'changeField', 'oldimage',      'oi_width',        'integer',  '' ),
-                       array( 'changeField', 'page',          'page_is_redirect', 'smallint', 'page_is_redirect::smallint DEFAULT 0' ),
-                       array( 'changeField', 'page',          'page_is_new',     'smallint', 'page_is_new::smallint DEFAULT 0' ),
-                       array( 'changeField', 'querycache',    'qc_value',        'integer',  '' ),
-                       array( 'changeField', 'querycachetwo', 'qcc_value',       'integer',  '' ),
-                       array( 'changeField', 'recentchanges', 'rc_bot',          'smallint', 'rc_bot::smallint DEFAULT 0' ),
-                       array( 'changeField', 'recentchanges', 'rc_deleted',      'smallint', '' ),
-                       array( 'changeField', 'recentchanges', 'rc_minor',        'smallint', 'rc_minor::smallint DEFAULT 0' ),
-                       array( 'changeField', 'recentchanges', 'rc_new',          'smallint', 'rc_new::smallint DEFAULT 0' ),
-                       array( 'changeField', 'recentchanges', 'rc_type',         'smallint', 'rc_type::smallint DEFAULT 0' ),
-                       array( 'changeField', 'recentchanges', 'rc_patrolled',    'smallint', 'rc_patrolled::smallint DEFAULT 0' ),
-                       array( 'changeField', 'revision',      'rev_deleted',     'smallint', 'rev_deleted::smallint DEFAULT 0' ),
-                       array( 'changeField', 'revision',      'rev_minor_edit',  'smallint', 'rev_minor_edit::smallint DEFAULT 0' ),
-                       array( 'changeField', 'templatelinks', 'tl_namespace',    'smallint', 'tl_namespace::smallint' ),
-                       array( 'changeField', 'user_newtalk',  'user_ip',         'text',     'host(user_ip)' ),
-                       array( 'changeField', 'uploadstash',   'us_image_bits',   'smallint', '' ),
-                       array( 'changeField', 'profiling',     'pf_time',         'float', '' ),
-                       array( 'changeField', 'profiling',     'pf_memory',       'float', '' ),
+                       array( 'changeField', 'archive', 'ar_deleted', 'smallint', '' ),
+                       array( 'changeField', 'archive', 'ar_minor_edit', 'smallint',
+                               'ar_minor_edit::smallint DEFAULT 0' ),
+                       array( 'changeField', 'filearchive', 'fa_deleted', 'smallint', '' ),
+                       array( 'changeField', 'filearchive', 'fa_height', 'integer', '' ),
+                       array( 'changeField', 'filearchive', 'fa_metadata', 'bytea', "decode(fa_metadata,'escape')" ),
+                       array( 'changeField', 'filearchive', 'fa_size', 'integer', '' ),
+                       array( 'changeField', 'filearchive', 'fa_width', 'integer', '' ),
+                       array( 'changeField', 'filearchive', 'fa_storage_group', 'text', '' ),
+                       array( 'changeField', 'filearchive', 'fa_storage_key', 'text', '' ),
+                       array( 'changeField', 'image', 'img_metadata', 'bytea', "decode(img_metadata,'escape')" ),
+                       array( 'changeField', 'image', 'img_size', 'integer', '' ),
+                       array( 'changeField', 'image', 'img_width', 'integer', '' ),
+                       array( 'changeField', 'image', 'img_height', 'integer', '' ),
+                       array( 'changeField', 'interwiki', 'iw_local', 'smallint', 'iw_local::smallint' ),
+                       array( 'changeField', 'interwiki', 'iw_trans', 'smallint', 'iw_trans::smallint DEFAULT 0' ),
+                       array( 'changeField', 'ipblocks', 'ipb_auto', 'smallint', 'ipb_auto::smallint DEFAULT 0' ),
+                       array( 'changeField', 'ipblocks', 'ipb_anon_only', 'smallint',
+                               "CASE WHEN ipb_anon_only=' ' THEN 0 ELSE ipb_anon_only::smallint END DEFAULT 0" ),
+                       array( 'changeField', 'ipblocks', 'ipb_create_account', 'smallint',
+                               "CASE WHEN ipb_create_account=' ' THEN 0 ELSE ipb_create_account::smallint END DEFAULT 1" ),
+                       array( 'changeField', 'ipblocks', 'ipb_enable_autoblock', 'smallint',
+                               "CASE WHEN ipb_enable_autoblock=' ' THEN 0 ELSE ipb_enable_autoblock::smallint END DEFAULT 1" ),
+                       array( 'changeField', 'ipblocks', 'ipb_block_email', 'smallint',
+                               "CASE WHEN ipb_block_email=' ' THEN 0 ELSE ipb_block_email::smallint END DEFAULT 0" ),
+                       array( 'changeField', 'ipblocks', 'ipb_address', 'text', 'ipb_address::text' ),
+                       array( 'changeField', 'ipblocks', 'ipb_deleted', 'smallint', 'ipb_deleted::smallint DEFAULT 0' ),
+                       array( 'changeField', 'mwuser', 'user_token', 'text', '' ),
+                       array( 'changeField', 'mwuser', 'user_email_token', 'text', '' ),
+                       array( 'changeField', 'objectcache', 'keyname', 'text', '' ),
+                       array( 'changeField', 'oldimage', 'oi_height', 'integer', '' ),
+                       array( 'changeField', 'oldimage', 'oi_metadata', 'bytea', "decode(img_metadata,'escape')" ),
+                       array( 'changeField', 'oldimage', 'oi_size', 'integer', '' ),
+                       array( 'changeField', 'oldimage', 'oi_width', 'integer', '' ),
+                       array( 'changeField', 'page', 'page_is_redirect', 'smallint',
+                               'page_is_redirect::smallint DEFAULT 0' ),
+                       array( 'changeField', 'page', 'page_is_new', 'smallint', 'page_is_new::smallint DEFAULT 0' ),
+                       array( 'changeField', 'querycache', 'qc_value', 'integer', '' ),
+                       array( 'changeField', 'querycachetwo', 'qcc_value', 'integer', '' ),
+                       array( 'changeField', 'recentchanges', 'rc_bot', 'smallint', 'rc_bot::smallint DEFAULT 0' ),
+                       array( 'changeField', 'recentchanges', 'rc_deleted', 'smallint', '' ),
+                       array( 'changeField', 'recentchanges', 'rc_minor', 'smallint', 'rc_minor::smallint DEFAULT 0' ),
+                       array( 'changeField', 'recentchanges', 'rc_new', 'smallint', 'rc_new::smallint DEFAULT 0' ),
+                       array( 'changeField', 'recentchanges', 'rc_type', 'smallint', 'rc_type::smallint DEFAULT 0' ),
+                       array( 'changeField', 'recentchanges', 'rc_patrolled', 'smallint',
+                               'rc_patrolled::smallint DEFAULT 0' ),
+                       array( 'changeField', 'revision', 'rev_deleted', 'smallint', 'rev_deleted::smallint DEFAULT 0' ),
+                       array( 'changeField', 'revision', 'rev_minor_edit', 'smallint',
+                               'rev_minor_edit::smallint DEFAULT 0' ),
+                       array( 'changeField', 'templatelinks', 'tl_namespace', 'smallint', 'tl_namespace::smallint' ),
+                       array( 'changeField', 'user_newtalk', 'user_ip', 'text', 'host(user_ip)' ),
+                       array( 'changeField', 'uploadstash', 'us_image_bits', 'smallint', '' ),
+                       array( 'changeField', 'profiling', 'pf_time', 'float', '' ),
+                       array( 'changeField', 'profiling', 'pf_memory', 'float', '' ),
 
                        # null changes
-                       array( 'changeNullableField', 'oldimage', 'oi_bits',       'NULL' ),
-                       array( 'changeNullableField', 'oldimage', 'oi_timestamp',  'NULL' ),
+                       array( 'changeNullableField', 'oldimage', 'oi_bits', 'NULL' ),
+                       array( 'changeNullableField', 'oldimage', 'oi_timestamp', 'NULL' ),
                        array( 'changeNullableField', 'oldimage', 'oi_major_mime', 'NULL' ),
                        array( 'changeNullableField', 'oldimage', 'oi_minor_mime', 'NULL' ),
-                       array( 'changeNullableField', 'image', 'img_metadata', 'NOT NULL'),
-                       array( 'changeNullableField', 'filearchive', 'fa_metadata', 'NOT NULL'),
+                       array( 'changeNullableField', 'image', 'img_metadata', 'NOT NULL' ),
+                       array( 'changeNullableField', 'filearchive', 'fa_metadata', 'NOT NULL' ),
                        array( 'changeNullableField', 'recentchanges', 'rc_cur_id', 'NULL' ),
 
                        array( 'checkOiDeleted' ),
 
                        # New indexes
-                       array( 'addPgIndex', 'archive',       'archive_user_text',      '(ar_user_text)' ),
-                       array( 'addPgIndex', 'image',         'img_sha1',               '(img_sha1)' ),
-                       array( 'addPgIndex', 'ipblocks',      'ipb_parent_block_id',              '(ipb_parent_block_id)' ),
-                       array( 'addPgIndex', 'oldimage',      'oi_sha1',                '(oi_sha1)' ),
-                       array( 'addPgIndex', 'page',          'page_mediawiki_title',   '(page_title) WHERE page_namespace = 8' ),
-                       array( 'addPgIndex', 'pagelinks',     'pagelinks_title',        '(pl_title)' ),
-                       array( 'addPgIndex', 'page_props',    'pp_propname_page',       '(pp_propname, pp_page)' ),
-                       array( 'addPgIndex', 'revision',      'rev_text_id_idx',        '(rev_text_id)' ),
-                       array( 'addPgIndex', 'recentchanges', 'rc_timestamp_bot',       '(rc_timestamp) WHERE rc_bot = 0' ),
-                       array( 'addPgIndex', 'templatelinks', 'templatelinks_from',     '(tl_from)' ),
-                       array( 'addPgIndex', 'watchlist',     'wl_user',                '(wl_user)' ),
-                       array( 'addPgIndex', 'logging',       'logging_user_type_time', '(log_user, log_type, log_timestamp)' ),
-                       array( 'addPgIndex', 'logging',       'logging_page_id_time',   '(log_page,log_timestamp)' ),
-                       array( 'addPgIndex', 'iwlinks',       'iwl_prefix_from_title',  '(iwl_prefix, iwl_from, iwl_title)' ),
-                       array( 'addPgIndex', 'iwlinks',       'iwl_prefix_title_from',  '(iwl_prefix, iwl_title, iwl_from)' ),
-                       array( 'addPgIndex', 'job',           'job_timestamp_idx',      '(job_timestamp)' ),
-                       array( 'addPgIndex', 'job',           'job_sha1',               '(job_sha1)' ),
-                       array( 'addPgIndex', 'job',           'job_cmd_token',          '(job_cmd, job_token, job_random)' ),
-                       array( 'addPgIndex', 'job',           'job_cmd_token_id',       '(job_cmd, job_token, job_id)' ),
-                       array( 'addPgIndex', 'filearchive',   'fa_sha1',                '(fa_sha1)' ),
+                       array( 'addPgIndex', 'archive', 'archive_user_text', '(ar_user_text)' ),
+                       array( 'addPgIndex', 'image', 'img_sha1', '(img_sha1)' ),
+                       array( 'addPgIndex', 'ipblocks', 'ipb_parent_block_id', '(ipb_parent_block_id)' ),
+                       array( 'addPgIndex', 'oldimage', 'oi_sha1', '(oi_sha1)' ),
+                       array( 'addPgIndex', 'page', 'page_mediawiki_title', '(page_title) WHERE page_namespace = 8' ),
+                       array( 'addPgIndex', 'pagelinks', 'pagelinks_title', '(pl_title)' ),
+                       array( 'addPgIndex', 'page_props', 'pp_propname_page', '(pp_propname, pp_page)' ),
+                       array( 'addPgIndex', 'revision', 'rev_text_id_idx', '(rev_text_id)' ),
+                       array( 'addPgIndex', 'recentchanges', 'rc_timestamp_bot', '(rc_timestamp) WHERE rc_bot = 0' ),
+                       array( 'addPgIndex', 'templatelinks', 'templatelinks_from', '(tl_from)' ),
+                       array( 'addPgIndex', 'watchlist', 'wl_user', '(wl_user)' ),
+                       array( 'addPgIndex', 'logging', 'logging_user_type_time',
+                               '(log_user, log_type, log_timestamp)' ),
+                       array( 'addPgIndex', 'logging', 'logging_page_id_time', '(log_page,log_timestamp)' ),
+                       array( 'addPgIndex', 'iwlinks', 'iwl_prefix_from_title', '(iwl_prefix, iwl_from, iwl_title)' ),
+                       array( 'addPgIndex', 'iwlinks', 'iwl_prefix_title_from', '(iwl_prefix, iwl_title, iwl_from)' ),
+                       array( 'addPgIndex', 'job', 'job_timestamp_idx', '(job_timestamp)' ),
+                       array( 'addPgIndex', 'job', 'job_sha1', '(job_sha1)' ),
+                       array( 'addPgIndex', 'job', 'job_cmd_token', '(job_cmd, job_token, job_random)' ),
+                       array( 'addPgIndex', 'job', 'job_cmd_token_id', '(job_cmd, job_token, job_id)' ),
+                       array( 'addPgIndex', 'filearchive', 'fa_sha1', '(fa_sha1)' ),
 
                        array( 'checkIndex', 'pagelink_unique', array(
                                array( 'pl_from', 'int4_ops', 'btree', 0 ),
                                array( 'pl_namespace', 'int2_ops', 'btree', 0 ),
                                array( 'pl_title', 'text_ops', 'btree', 0 ),
                        ),
-                       'CREATE UNIQUE INDEX pagelink_unique ON pagelinks (pl_from,pl_namespace,pl_title)' ),
+                               'CREATE UNIQUE INDEX pagelink_unique ON pagelinks (pl_from,pl_namespace,pl_title)' ),
                        array( 'checkIndex', 'cl_sortkey', array(
                                array( 'cl_to', 'text_ops', 'btree', 0 ),
                                array( 'cl_sortkey', 'text_ops', 'btree', 0 ),
                                array( 'cl_from', 'int4_ops', 'btree', 0 ),
                        ),
-                       'CREATE INDEX cl_sortkey ON "categorylinks" USING "btree" ("cl_to", "cl_sortkey", "cl_from")' ),
+                               'CREATE INDEX cl_sortkey ON "categorylinks" ' .
+                                       'USING "btree" ("cl_to", "cl_sortkey", "cl_from")' ),
                        array( 'checkIndex', 'iwl_prefix_title_from', array(
                                array( 'iwl_prefix', 'text_ops', 'btree', 0 ),
                                array( 'iwl_title', 'text_ops', 'btree', 0 ),
                                array( 'iwl_from', 'int4_ops', 'btree', 0 ),
                        ),
-                       'CREATE INDEX iwl_prefix_title_from ON "iwlinks" USING "btree" ("iwl_prefix", "iwl_title", "iwl_from")' ),
+                       'CREATE INDEX iwl_prefix_title_from ON "iwlinks" ' .
+                               'USING "btree" ("iwl_prefix", "iwl_title", "iwl_from")' ),
                        array( 'checkIndex', 'logging_times', array(
                                array( 'log_timestamp', 'timestamptz_ops', 'btree', 0 ),
                        ),
@@ -271,36 +287,48 @@ class PostgresUpdater extends DatabaseUpdater {
                                array( 'oi_name', 'text_ops', 'btree', 0 ),
                                array( 'oi_archive_name', 'text_ops', 'btree', 0 ),
                        ),
-                       'CREATE INDEX "oi_name_archive_name" ON "oldimage" USING "btree" ("oi_name", "oi_archive_name")' ),
+                       'CREATE INDEX "oi_name_archive_name" ON "oldimage" ' .
+                               'USING "btree" ("oi_name", "oi_archive_name")' ),
                        array( 'checkIndex', 'oi_name_timestamp', array(
                                array( 'oi_name', 'text_ops', 'btree', 0 ),
                                array( 'oi_timestamp', 'timestamptz_ops', 'btree', 0 ),
                        ),
-                       'CREATE INDEX "oi_name_timestamp" ON "oldimage" USING "btree" ("oi_name", "oi_timestamp")' ),
+                       'CREATE INDEX "oi_name_timestamp" ON "oldimage" ' .
+                               'USING "btree" ("oi_name", "oi_timestamp")' ),
                        array( 'checkIndex', 'page_main_title', array(
                                array( 'page_title', 'text_pattern_ops', 'btree', 0 ),
                        ),
-                       'CREATE INDEX "page_main_title" ON "page" USING "btree" ("page_title" "text_pattern_ops") WHERE ("page_namespace" = 0)' ),
+                       'CREATE INDEX "page_main_title" ON "page" ' .
+                               'USING "btree" ("page_title" "text_pattern_ops") WHERE ("page_namespace" = 0)' ),
                        array( 'checkIndex', 'page_mediawiki_title', array(
                                array( 'page_title', 'text_pattern_ops', 'btree', 0 ),
                        ),
-                       'CREATE INDEX "page_mediawiki_title" ON "page" USING "btree" ("page_title" "text_pattern_ops") WHERE ("page_namespace" = 8)' ),
+                       'CREATE INDEX "page_mediawiki_title" ON "page" ' .
+                               'USING "btree" ("page_title" "text_pattern_ops") WHERE ("page_namespace" = 8)' ),
                        array( 'checkIndex', 'page_project_title', array(
                                array( 'page_title', 'text_pattern_ops', 'btree', 0 ),
                        ),
-                       'CREATE INDEX "page_project_title" ON "page" USING "btree" ("page_title" "text_pattern_ops") WHERE ("page_namespace" = 4)' ),
+                       'CREATE INDEX "page_project_title" ON "page" ' .
+                               'USING "btree" ("page_title" "text_pattern_ops") ' .
+                               'WHERE ("page_namespace" = 4)' ),
                        array( 'checkIndex', 'page_talk_title', array(
                                array( 'page_title', 'text_pattern_ops', 'btree', 0 ),
                        ),
-                       'CREATE INDEX "page_talk_title" ON "page" USING "btree" ("page_title" "text_pattern_ops") WHERE ("page_namespace" = 1)' ),
+                       'CREATE INDEX "page_talk_title" ON "page" ' .
+                               'USING "btree" ("page_title" "text_pattern_ops") ' .
+                               'WHERE ("page_namespace" = 1)' ),
                        array( 'checkIndex', 'page_user_title', array(
                                array( 'page_title', 'text_pattern_ops', 'btree', 0 ),
                        ),
-                       'CREATE INDEX "page_user_title" ON "page" USING "btree" ("page_title" "text_pattern_ops") WHERE ("page_namespace" = 2)' ),
+                       'CREATE INDEX "page_user_title" ON "page" ' .
+                               'USING "btree" ("page_title" "text_pattern_ops") WHERE ' .
+                               '("page_namespace" = 2)' ),
                        array( 'checkIndex', 'page_utalk_title', array(
                                array( 'page_title', 'text_pattern_ops', 'btree', 0 ),
                        ),
-                       'CREATE INDEX "page_utalk_title" ON "page" USING "btree" ("page_title" "text_pattern_ops") WHERE ("page_namespace" = 3)' ),
+                       'CREATE INDEX "page_utalk_title" ON "page" ' .
+                               'USING "btree" ("page_title" "text_pattern_ops") ' .
+                               'WHERE ("page_namespace" = 3)' ),
                        array( 'checkIndex', 'ts2_page_text', array(
                                array( 'textvector', 'tsvector_ops', 'gist', 0 ),
                        ),
@@ -320,44 +348,56 @@ class PostgresUpdater extends DatabaseUpdater {
                                array( 'ipb_auto', 'int2_ops', 'btree', 0 ),
                                array( 'ipb_anon_only', 'int2_ops', 'btree', 0 ),
                        ),
-                       'CREATE UNIQUE INDEX ipb_address_unique ON ipblocks (ipb_address,ipb_user,ipb_auto,ipb_anon_only)' ),
+                       'CREATE UNIQUE INDEX ipb_address_unique ' .
+                               'ON ipblocks (ipb_address,ipb_user,ipb_auto,ipb_anon_only)' ),
 
                        array( 'checkIwlPrefix' ),
 
                        # All FK columns should be deferred
-                       array( 'changeFkeyDeferrable', 'archive',          'ar_user',         'mwuser(user_id) ON DELETE SET NULL' ),
-                       array( 'changeFkeyDeferrable', 'categorylinks',    'cl_from',         'page(page_id) ON DELETE CASCADE' ),
-                       array( 'changeFkeyDeferrable', 'externallinks',    'el_from',         'page(page_id) ON DELETE CASCADE' ),
-                       array( 'changeFkeyDeferrable', 'filearchive',      'fa_deleted_user', 'mwuser(user_id) ON DELETE SET NULL' ),
-                       array( 'changeFkeyDeferrable', 'filearchive',      'fa_user',         'mwuser(user_id) ON DELETE SET NULL' ),
-                       array( 'changeFkeyDeferrable', 'image',            'img_user',        'mwuser(user_id) ON DELETE SET NULL' ),
-                       array( 'changeFkeyDeferrable', 'imagelinks',       'il_from',         'page(page_id) ON DELETE CASCADE' ),
-                       array( 'changeFkeyDeferrable', 'ipblocks',         'ipb_by',          'mwuser(user_id) ON DELETE CASCADE' ),
-                       array( 'changeFkeyDeferrable', 'ipblocks',         'ipb_user',        'mwuser(user_id) ON DELETE SET NULL' ),
-                       array( 'changeFkeyDeferrable', 'ipblocks',         'ipb_parent_block_id',       'ipblocks(ipb_id) ON DELETE SET NULL' ),
-                       array( 'changeFkeyDeferrable', 'langlinks',        'll_from',         'page(page_id) ON DELETE CASCADE' ),
-                       array( 'changeFkeyDeferrable', 'logging',          'log_user',        'mwuser(user_id) ON DELETE SET NULL' ),
-                       array( 'changeFkeyDeferrable', 'oldimage',         'oi_name',         'image(img_name) ON DELETE CASCADE ON UPDATE CASCADE' ),
-                       array( 'changeFkeyDeferrable', 'oldimage',         'oi_user',         'mwuser(user_id) ON DELETE SET NULL' ),
-                       array( 'changeFkeyDeferrable', 'pagelinks',        'pl_from',         'page(page_id) ON DELETE CASCADE' ),
-                       array( 'changeFkeyDeferrable', 'page_props',       'pp_page',         'page (page_id) ON DELETE CASCADE' ),
-                       array( 'changeFkeyDeferrable', 'page_restrictions', 'pr_page',         'page(page_id) ON DELETE CASCADE' ),
-                       array( 'changeFkeyDeferrable', 'protected_titles', 'pt_user',         'mwuser(user_id) ON DELETE SET NULL' ),
-                       array( 'changeFkeyDeferrable', 'recentchanges',    'rc_cur_id',       'page(page_id) ON DELETE SET NULL' ),
-                       array( 'changeFkeyDeferrable', 'recentchanges',    'rc_user',         'mwuser(user_id) ON DELETE SET NULL' ),
-                       array( 'changeFkeyDeferrable', 'redirect',         'rd_from',         'page(page_id) ON DELETE CASCADE' ),
-                       array( 'changeFkeyDeferrable', 'revision',         'rev_page',        'page (page_id) ON DELETE CASCADE' ),
-                       array( 'changeFkeyDeferrable', 'revision',         'rev_user',        'mwuser(user_id) ON DELETE RESTRICT' ),
-                       array( 'changeFkeyDeferrable', 'templatelinks',    'tl_from',         'page(page_id) ON DELETE CASCADE' ),
-                       array( 'changeFkeyDeferrable', 'user_groups',      'ug_user',         'mwuser(user_id) ON DELETE CASCADE' ),
-                       array( 'changeFkeyDeferrable', 'user_newtalk',     'user_id',         'mwuser(user_id) ON DELETE CASCADE' ),
-                       array( 'changeFkeyDeferrable', 'user_properties',  'up_user',         'mwuser(user_id) ON DELETE CASCADE' ),
-                       array( 'changeFkeyDeferrable', 'watchlist',        'wl_user',         'mwuser(user_id) ON DELETE CASCADE' ),
+                       array( 'changeFkeyDeferrable', 'archive', 'ar_user', 'mwuser(user_id) ON DELETE SET NULL' ),
+                       array( 'changeFkeyDeferrable', 'categorylinks', 'cl_from', 'page(page_id) ON DELETE CASCADE' ),
+                       array( 'changeFkeyDeferrable', 'externallinks', 'el_from', 'page(page_id) ON DELETE CASCADE' ),
+                       array( 'changeFkeyDeferrable', 'filearchive', 'fa_deleted_user',
+                               'mwuser(user_id) ON DELETE SET NULL' ),
+                       array( 'changeFkeyDeferrable', 'filearchive', 'fa_user', 'mwuser(user_id) ON DELETE SET NULL' ),
+                       array( 'changeFkeyDeferrable', 'image', 'img_user', 'mwuser(user_id) ON DELETE SET NULL' ),
+                       array( 'changeFkeyDeferrable', 'imagelinks', 'il_from', 'page(page_id) ON DELETE CASCADE' ),
+                       array( 'changeFkeyDeferrable', 'ipblocks', 'ipb_by', 'mwuser(user_id) ON DELETE CASCADE' ),
+                       array( 'changeFkeyDeferrable', 'ipblocks', 'ipb_user', 'mwuser(user_id) ON DELETE SET NULL' ),
+                       array( 'changeFkeyDeferrable', 'ipblocks', 'ipb_parent_block_id',
+                               'ipblocks(ipb_id) ON DELETE SET NULL' ),
+                       array( 'changeFkeyDeferrable', 'langlinks', 'll_from', 'page(page_id) ON DELETE CASCADE' ),
+                       array( 'changeFkeyDeferrable', 'logging', 'log_user', 'mwuser(user_id) ON DELETE SET NULL' ),
+                       array( 'changeFkeyDeferrable', 'oldimage', 'oi_name',
+                               'image(img_name) ON DELETE CASCADE ON UPDATE CASCADE' ),
+                       array( 'changeFkeyDeferrable', 'oldimage', 'oi_user', 'mwuser(user_id) ON DELETE SET NULL' ),
+                       array( 'changeFkeyDeferrable', 'pagelinks', 'pl_from', 'page(page_id) ON DELETE CASCADE' ),
+                       array( 'changeFkeyDeferrable', 'page_props', 'pp_page', 'page (page_id) ON DELETE CASCADE' ),
+                       array( 'changeFkeyDeferrable', 'page_restrictions', 'pr_page',
+                               'page(page_id) ON DELETE CASCADE' ),
+                       array( 'changeFkeyDeferrable', 'protected_titles', 'pt_user',
+                               'mwuser(user_id) ON DELETE SET NULL' ),
+                       array( 'changeFkeyDeferrable', 'recentchanges', 'rc_cur_id',
+                               'page(page_id) ON DELETE SET NULL' ),
+                       array( 'changeFkeyDeferrable', 'recentchanges', 'rc_user',
+                               'mwuser(user_id) ON DELETE SET NULL' ),
+                       array( 'changeFkeyDeferrable', 'redirect', 'rd_from', 'page(page_id) ON DELETE CASCADE' ),
+                       array( 'changeFkeyDeferrable', 'revision', 'rev_page', 'page (page_id) ON DELETE CASCADE' ),
+                       array( 'changeFkeyDeferrable', 'revision', 'rev_user', 'mwuser(user_id) ON DELETE RESTRICT' ),
+                       array( 'changeFkeyDeferrable', 'templatelinks', 'tl_from', 'page(page_id) ON DELETE CASCADE' ),
+                       array( 'changeFkeyDeferrable', 'user_groups', 'ug_user', 'mwuser(user_id) ON DELETE CASCADE' ),
+                       array( 'changeFkeyDeferrable', 'user_newtalk', 'user_id', 'mwuser(user_id) ON DELETE CASCADE' ),
+                       array( 'changeFkeyDeferrable', 'user_properties', 'up_user',
+                               'mwuser(user_id) ON DELETE CASCADE' ),
+                       array( 'changeFkeyDeferrable', 'watchlist', 'wl_user', 'mwuser(user_id) ON DELETE CASCADE' ),
 
                        # r81574
                        array( 'addInterwikiType' ),
                        # end
                        array( 'tsearchFixes' ),
+
+                       // 1.22
+                       array( 'addPgField', 'recentchanges', 'rc_source',            "TEXT NOT NULL DEFAULT ''" ),
                );
        }
 
@@ -569,7 +609,8 @@ END;
                        if ( !$skipBothIndexExistWarning
                                && $this->db->indexExists( $table, $old, __METHOD__ )
                        ) {
-                               $this->output( "...WARNING: $old still exists, despite it has been renamed into $new (which also exists).\n" .
+                               $this->output( "...WARNING: $old still exists, despite it has been " .
+                                       "renamed into $new (which also exists).\n" .
                                        "            $old should be manually removed if not needed anymore.\n" );
                        }
 
@@ -682,7 +723,8 @@ END;
        protected function changeFkeyDeferrable( $table, $field, $clause ) {
                $fi = $this->db->fieldInfo( $table, $field );
                if ( is_null( $fi ) ) {
-                       $this->output( "WARNING! Column '$table.$field' does not exist but it should! Please report this.\n" );
+                       $this->output( "WARNING! Column '$table.$field' does not exist but it should! " .
+                               "Please report this.\n" );
 
                        return;
                }
@@ -696,10 +738,13 @@ END;
                        $command = "ALTER TABLE $table DROP CONSTRAINT $conname";
                        $this->db->query( $command );
                } else {
-                       $this->output( "Column '$table.$field' does not have a foreign key constraint, will be added\n" );
+                       $this->output( "Column '$table.$field' does not have a foreign key " .
+                               "constraint, will be added\n" );
                        $conclause = "";
                }
-               $command = "ALTER TABLE $table ADD $conclause FOREIGN KEY ($field) REFERENCES $clause DEFERRABLE INITIALLY DEFERRED";
+               $command =
+                       "ALTER TABLE $table ADD $conclause " .
+                       "FOREIGN KEY ($field) REFERENCES $clause DEFERRABLE INITIALLY DEFERRED";
                $this->db->query( $command );
        }
 
@@ -713,7 +758,11 @@ END;
                                $this->output( "Dropping rule 'archive_delete'\n" );
                                $this->db->query( 'DROP RULE archive_delete ON archive' );
                        }
-                       $this->applyPatch( 'patch-remove-archive2.sql', false, "Converting 'archive2' back to normal archive table" );
+                       $this->applyPatch(
+                               'patch-remove-archive2.sql',
+                               false,
+                               "Converting 'archive2' back to normal archive table"
+                       );
                } else {
                        $this->output( "...obsolete table 'archive2' does not exist\n" );
                }
@@ -723,7 +772,8 @@ END;
                if ( $this->db->fieldInfo( 'oldimage', 'oi_deleted' )->type() !== 'smallint' ) {
                        $this->output( "Changing 'oldimage.oi_deleted' to type 'smallint'\n" );
                        $this->db->query( "ALTER TABLE oldimage ALTER oi_deleted DROP DEFAULT" );
-                       $this->db->query( "ALTER TABLE oldimage ALTER oi_deleted TYPE SMALLINT USING (oi_deleted::smallint)" );
+                       $this->db->query(
+                               "ALTER TABLE oldimage ALTER oi_deleted TYPE SMALLINT USING (oi_deleted::smallint)" );
                        $this->db->query( "ALTER TABLE oldimage ALTER oi_deleted SET DEFAULT 0" );
                } else {
                        $this->output( "...column 'oldimage.oi_deleted' is already of type 'smallint'\n" );
@@ -732,23 +782,32 @@ END;
 
        protected function checkOiNameConstraint() {
                if ( $this->db->hasConstraint( "oldimage_oi_name_fkey_cascaded" ) ) {
-                       $this->output( "...table 'oldimage' has correct cascading delete/update foreign key to image\n" );
+                       $this->output( "...table 'oldimage' has correct cascading delete/update " .
+                               "foreign key to image\n" );
                } else {
                        if ( $this->db->hasConstraint( "oldimage_oi_name_fkey" ) ) {
-                               $this->db->query( "ALTER TABLE oldimage DROP CONSTRAINT oldimage_oi_name_fkey" );
+                               $this->db->query(
+                                       "ALTER TABLE oldimage DROP CONSTRAINT oldimage_oi_name_fkey" );
                        }
                        if ( $this->db->hasConstraint( "oldimage_oi_name_fkey_cascade" ) ) {
-                               $this->db->query( "ALTER TABLE oldimage DROP CONSTRAINT oldimage_oi_name_fkey_cascade" );
+                               $this->db->query(
+                                       "ALTER TABLE oldimage DROP CONSTRAINT oldimage_oi_name_fkey_cascade" );
                        }
                        $this->output( "Making foreign key on table 'oldimage' (to image) a cascade delete/update\n" );
-                       $this->db->query( "ALTER TABLE oldimage ADD CONSTRAINT oldimage_oi_name_fkey_cascaded " .
-                               "FOREIGN KEY (oi_name) REFERENCES image(img_name) ON DELETE CASCADE ON UPDATE CASCADE" );
+                       $this->db->query(
+                               "ALTER TABLE oldimage ADD CONSTRAINT oldimage_oi_name_fkey_cascaded " .
+                               "FOREIGN KEY (oi_name) REFERENCES image(img_name) " .
+                               "ON DELETE CASCADE ON UPDATE CASCADE" );
                }
        }
 
        protected function checkPageDeletedTrigger() {
                if ( !$this->db->triggerExists( 'page', 'page_deleted' ) ) {
-                       $this->applyPatch( 'patch-page_deleted.sql', false, "Adding function and trigger 'page_deleted' to table 'page'" );
+                       $this->applyPatch(
+                               'patch-page_deleted.sql',
+                               false,
+                               "Adding function and trigger 'page_deleted' to table 'page'"
+                       );
                } else {
                        $this->output( "...table 'page' has 'page_deleted' trigger\n" );
                }
@@ -783,13 +842,21 @@ END;
                if ( $this->fkeyDeltype( 'revision_rev_user_fkey' ) == 'r' ) {
                        $this->output( "...constraint 'revision_rev_user_fkey' is ON DELETE RESTRICT\n" );
                } else {
-                       $this->applyPatch( 'patch-revision_rev_user_fkey.sql', false, "Changing constraint 'revision_rev_user_fkey' to ON DELETE RESTRICT" );
+                       $this->applyPatch(
+                               'patch-revision_rev_user_fkey.sql',
+                               false,
+                               "Changing constraint 'revision_rev_user_fkey' to ON DELETE RESTRICT"
+                       );
                }
        }
 
        protected function checkIwlPrefix() {
                if ( $this->db->indexExists( 'iwlinks', 'iwl_prefix' ) ) {
-                       $this->applyPatch( 'patch-rename-iwl_prefix.sql', false, "Replacing index 'iwl_prefix' with 'iwl_prefix_title_from'" );
+                       $this->applyPatch(
+                               'patch-rename-iwl_prefix.sql',
+                               false,
+                               "Replacing index 'iwl_prefix' with 'iwl_prefix_title_from'"
+                       );
                }
        }
 
index 136ca4b..19218c6 100644 (file)
@@ -82,8 +82,17 @@ class SqliteInstaller extends DatabaseInstaller {
        }
 
        public function getConnectForm() {
-               return $this->getTextBox( 'wgSQLiteDataDir', 'config-sqlite-dir', array(), $this->parent->getHelpBox( 'config-sqlite-dir-help' ) ) .
-                       $this->getTextBox( 'wgDBname', 'config-db-name', array(), $this->parent->getHelpBox( 'config-sqlite-name-help' ) );
+               return $this->getTextBox(
+                       'wgSQLiteDataDir',
+                       'config-sqlite-dir', array(),
+                       $this->parent->getHelpBox( 'config-sqlite-dir-help' )
+               ) .
+               $this->getTextBox(
+                       'wgDBname',
+                       'config-db-name',
+                       array(),
+                       $this->parent->getHelpBox( 'config-sqlite-name-help' )
+               );
        }
 
        /**
@@ -132,9 +141,16 @@ class SqliteInstaller extends DatabaseInstaller {
                        if ( !is_writable( dirname( $dir ) ) ) {
                                $webserverGroup = Installer::maybeGetWebserverPrimaryGroup();
                                if ( $webserverGroup !== null ) {
-                                       return Status::newFatal( 'config-sqlite-parent-unwritable-group', $dir, dirname( $dir ), basename( $dir ), $webserverGroup );
+                                       return Status::newFatal(
+                                               'config-sqlite-parent-unwritable-group',
+                                               $dir, dirname( $dir ), basename( $dir ),
+                                               $webserverGroup
+                                       );
                                } else {
-                                       return Status::newFatal( 'config-sqlite-parent-unwritable-nogroup', $dir, dirname( $dir ), basename( $dir ) );
+                                       return Status::newFatal(
+                                               'config-sqlite-parent-unwritable-nogroup',
+                                               $dir, dirname( $dir ), basename( $dir )
+                                       );
                                }
                        }
 
index 0b572f6..6fa22bc 100644 (file)
@@ -31,91 +31,105 @@ class SqliteUpdater extends DatabaseUpdater {
 
        protected function getCoreUpdateList() {
                return array(
+                       array( 'disableContentHandlerUseDB' ),
+
                        // 1.14
                        array( 'addField', 'site_stats',    'ss_active_users',  'patch-ss_active_users.sql' ),
                        array( 'doActiveUsersInit' ),
-                       array( 'addField', 'ipblocks',      'ipb_allow_usertalk', 'patch-ipb_allow_usertalk.sql' ),
+                       array( 'addField', 'ipblocks', 'ipb_allow_usertalk', 'patch-ipb_allow_usertalk.sql' ),
                        array( 'sqliteInitialIndexes' ),
 
                        // 1.15
-                       array( 'addTable', 'change_tag',                        'patch-change_tag.sql' ),
-                       array( 'addTable', 'tag_summary',                       'patch-tag_summary.sql' ),
-                       array( 'addTable', 'valid_tag',                         'patch-valid_tag.sql' ),
+                       array( 'addTable', 'change_tag', 'patch-change_tag.sql' ),
+                       array( 'addTable', 'tag_summary', 'patch-tag_summary.sql' ),
+                       array( 'addTable', 'valid_tag', 'patch-valid_tag.sql' ),
 
                        // 1.16
-                       array( 'addTable', 'user_properties',                   'patch-user_properties.sql' ),
-                       array( 'addTable', 'log_search',                        'patch-log_search.sql' ),
-                       array( 'addField', 'logging',       'log_user_text',    'patch-log_user_text.sql' ),
-                       array( 'doLogUsertextPopulation' ), # listed separately from the previous update because 1.16 was released without this update
+                       array( 'addTable', 'user_properties', 'patch-user_properties.sql' ),
+                       array( 'addTable', 'log_search', 'patch-log_search.sql' ),
+                       array( 'addField', 'logging', 'log_user_text', 'patch-log_user_text.sql' ),
+                       # listed separately from the previous update because 1.16 was released without this update
+                       array( 'doLogUsertextPopulation' ),
                        array( 'doLogSearchPopulation' ),
-                       array( 'addTable', 'l10n_cache',                        'patch-l10n_cache.sql' ),
-                       array( 'addIndex', 'log_search',    'ls_field_val',     'patch-log_search-rename-index.sql' ),
-                       array( 'addIndex', 'change_tag',    'change_tag_rc_tag', 'patch-change_tag-indexes.sql' ),
-                       array( 'addField', 'redirect',      'rd_interwiki',     'patch-rd_interwiki.sql' ),
+                       array( 'addTable', 'l10n_cache', 'patch-l10n_cache.sql' ),
+                       array( 'addIndex', 'log_search', 'ls_field_val', 'patch-log_search-rename-index.sql' ),
+                       array( 'addIndex', 'change_tag', 'change_tag_rc_tag', 'patch-change_tag-indexes.sql' ),
+                       array( 'addField', 'redirect', 'rd_interwiki', 'patch-rd_interwiki.sql' ),
                        array( 'doUpdateTranscacheField' ),
                        array( 'sqliteSetupSearchindex' ),
 
                        // 1.17
-                       array( 'addTable', 'iwlinks',                           'patch-iwlinks.sql' ),
-                       array( 'addIndex', 'iwlinks',   'iwl_prefix_title_from', 'patch-rename-iwl_prefix.sql' ),
-                       array( 'addField', 'updatelog', 'ul_value',             'patch-ul_value.sql' ),
-                       array( 'addField', 'interwiki',     'iw_api',           'patch-iw_api_and_wikiid.sql' ),
-                       array( 'dropIndex', 'iwlinks', 'iwl_prefix',            'patch-kill-iwl_prefix.sql' ),
-                       array( 'addField', 'categorylinks', 'cl_collation',     'patch-categorylinks-better-collation.sql' ),
+                       array( 'addTable', 'iwlinks', 'patch-iwlinks.sql' ),
+                       array( 'addIndex', 'iwlinks', 'iwl_prefix_title_from', 'patch-rename-iwl_prefix.sql' ),
+                       array( 'addField', 'updatelog', 'ul_value', 'patch-ul_value.sql' ),
+                       array( 'addField', 'interwiki', 'iw_api', 'patch-iw_api_and_wikiid.sql' ),
+                       array( 'dropIndex', 'iwlinks', 'iwl_prefix', 'patch-kill-iwl_prefix.sql' ),
+                       array( 'addField', 'categorylinks', 'cl_collation', 'patch-categorylinks-better-collation.sql' ),
                        array( 'doCollationUpdate' ),
-                       array( 'addTable', 'msg_resource',                      'patch-msg_resource.sql' ),
-                       array( 'addTable', 'module_deps',                       'patch-module_deps.sql' ),
-                       array( 'dropIndex', 'archive', 'ar_page_revid',         'patch-archive_kill_ar_page_revid.sql' ),
-                       array( 'addIndex', 'archive', 'ar_revid',               'patch-archive_ar_revid.sql' ),
+                       array( 'addTable', 'msg_resource', 'patch-msg_resource.sql' ),
+                       array( 'addTable', 'module_deps', 'patch-module_deps.sql' ),
+                       array( 'dropIndex', 'archive', 'ar_page_revid', 'patch-archive_kill_ar_page_revid.sql' ),
+                       array( 'addIndex', 'archive', 'ar_revid', 'patch-archive_ar_revid.sql' ),
 
                        // 1.18
-                       array( 'addIndex', 'user',          'user_email',       'patch-user_email_index.sql' ),
-                       array( 'addTable', 'uploadstash',                       'patch-uploadstash.sql' ),
-                       array( 'addTable', 'user_former_groups',                'patch-user_former_groups.sql'),
+                       array( 'addIndex', 'user', 'user_email', 'patch-user_email_index.sql' ),
+                       array( 'addTable', 'uploadstash', 'patch-uploadstash.sql' ),
+                       array( 'addTable', 'user_former_groups', 'patch-user_former_groups.sql' ),
 
                        // 1.19
-                       array( 'addIndex', 'logging',       'type_action',      'patch-logging-type-action-index.sql'),
+                       array( 'addIndex', 'logging', 'type_action', 'patch-logging-type-action-index.sql' ),
                        array( 'doMigrateUserOptions' ),
-                       array( 'dropField', 'user',         'user_options', 'patch-drop-user_options.sql' ),
-                       array( 'addField', 'revision',      'rev_sha1',         'patch-rev_sha1.sql' ),
-                       array( 'addField', 'archive',       'ar_sha1',          'patch-ar_sha1.sql' ),
-                       array( 'addIndex', 'page', 'page_redirect_namespace_len', 'patch-page_redirect_namespace_len.sql' ),
-                       array( 'addField',      'uploadstash',  'us_chunk_inx',         'patch-uploadstash_chunk.sql' ),
-                       array( 'addfield', 'job',           'job_timestamp',    'patch-jobs-add-timestamp.sql' ),
+                       array( 'dropField', 'user', 'user_options', 'patch-drop-user_options.sql' ),
+                       array( 'addField', 'revision', 'rev_sha1', 'patch-rev_sha1.sql' ),
+                       array( 'addField', 'archive', 'ar_sha1', 'patch-ar_sha1.sql' ),
+                       array( 'addIndex', 'page', 'page_redirect_namespace_len',
+                               'patch-page_redirect_namespace_len.sql' ),
+                       array( 'addField', 'uploadstash', 'us_chunk_inx', 'patch-uploadstash_chunk.sql' ),
+                       array( 'addfield', 'job', 'job_timestamp', 'patch-jobs-add-timestamp.sql' ),
 
                        // 1.20
                        array( 'addIndex', 'revision', 'page_user_timestamp', 'patch-revision-user-page-index.sql' ),
                        array( 'addField', 'ipblocks', 'ipb_parent_block_id', 'patch-ipb-parent-block-id.sql' ),
                        array( 'addIndex', 'ipblocks', 'ipb_parent_block_id', 'patch-ipb-parent-block-id-index.sql' ),
-                       array( 'dropField', 'category',     'cat_hidden',       'patch-cat_hidden.sql' ),
+                       array( 'dropField', 'category', 'cat_hidden', 'patch-cat_hidden.sql' ),
 
                        // 1.21
                        array( 'addField', 'revision', 'rev_content_format', 'patch-revision-rev_content_format.sql' ),
-                       array( 'addField', 'revision', 'rev_content_model',  'patch-revision-rev_content_model.sql' ),
+                       array( 'addField', 'revision', 'rev_content_model', 'patch-revision-rev_content_model.sql' ),
                        array( 'addField', 'archive',  'ar_content_format',  'patch-archive-ar_content_format.sql' ),
                        array( 'addField', 'archive',  'ar_content_model',   'patch-archive-ar_content_model.sql' ),
                        array( 'addField', 'page',     'page_content_model', 'patch-page-page_content_model.sql' ),
+                       array( 'enableContentHandlerUseDB' ),
+
                        array( 'dropField', 'site_stats',    'ss_admins',         'patch-drop-ss_admins.sql' ),
                        array( 'dropField', 'recentchanges', 'rc_moved_to_title', 'patch-rc_moved.sql' ),
-                       array( 'addTable', 'sites',                            'patch-sites.sql' ),
-                       array( 'addField', 'filearchive',   'fa_sha1',          'patch-fa_sha1.sql' ),
-                       array( 'addField', 'job',           'job_token',         'patch-job_token.sql' ),
-                       array( 'addField', 'job',           'job_attempts',      'patch-job_attempts.sql' ),
+                       array( 'addTable', 'sites', 'patch-sites.sql' ),
+                       array( 'addField', 'filearchive', 'fa_sha1', 'patch-fa_sha1.sql' ),
+                       array( 'addField', 'job', 'job_token', 'patch-job_token.sql' ),
+                       array( 'addField', 'job', 'job_attempts', 'patch-job_attempts.sql' ),
                        array( 'doEnableProfiling' ),
-                       array( 'addField', 'uploadstash',      'us_props',      'patch-uploadstash-us_props.sql' ),
+                       array( 'addField', 'uploadstash', 'us_props', 'patch-uploadstash-us_props.sql' ),
                        array( 'modifyField', 'user_groups', 'ug_group', 'patch-ug_group-length-increase-255.sql' ),
-                       array( 'modifyField', 'user_former_groups', 'ufg_group', 'patch-ufg_group-length-increase-255.sql' ),
-                       array( 'addIndex', 'page_props', 'pp_propname_page',  'patch-page_props-propname-page-index.sql' ),
+                       array( 'modifyField', 'user_former_groups', 'ufg_group',
+                               'patch-ufg_group-length-increase-255.sql' ),
+                       array( 'addIndex', 'page_props', 'pp_propname_page',
+                               'patch-page_props-propname-page-index.sql' ),
                        array( 'addIndex', 'image', 'img_media_mime', 'patch-img_media_mime-index.sql' ),
-                       array( 'addIndex', 'iwlinks', 'iwl_prefix_from_title',  'patch-iwlinks-from-title-index.sql' ),
+                       array( 'addIndex', 'iwlinks', 'iwl_prefix_from_title', 'patch-iwlinks-from-title-index.sql' ),
                        array( 'addField', 'archive', 'ar_id', 'patch-archive-ar_id.sql' ),
                        array( 'addField', 'externallinks', 'el_id', 'patch-externallinks-el_id.sql' ),
+
+                       // 1.22
+                       array( 'addField', 'recentchanges', 'rc_source', 'patch-rc_source.sql' ),
                );
        }
 
        protected function sqliteInitialIndexes() {
-               // initial-indexes.sql fails if the indexes are already present, so we perform a quick check if our database is newer.
-               if ( $this->updateRowExists( 'initial_indexes' ) || $this->db->indexExists( 'user', 'user_name', __METHOD__ ) ) {
+               // initial-indexes.sql fails if the indexes are already present,
+               // so we perform a quick check if our database is newer.
+               if ( $this->updateRowExists( 'initial_indexes' ) ||
+                       $this->db->indexExists( 'user', 'user_name', __METHOD__ )
+               ) {
                        $this->output( "...have initial indexes\n" );
 
                        return;
@@ -127,7 +141,11 @@ class SqliteUpdater extends DatabaseUpdater {
                $module = DatabaseSqlite::getFulltextSearchModule();
                $fts3tTable = $this->updateRowExists( 'fts3' );
                if ( $fts3tTable && !$module ) {
-                       $this->applyPatch( 'searchindex-no-fts.sql', false, 'PHP is missing FTS3 support, downgrading tables' );
+                       $this->applyPatch(
+                               'searchindex-no-fts.sql',
+                               false,
+                               'PHP is missing FTS3 support, downgrading tables'
+                       );
                } elseif ( !$fts3tTable && $module == 'FTS3' ) {
                        $this->applyPatch( 'searchindex-fts3.sql', false, "Adding FTS3 search capabilities" );
                } else {
index cbceed6..e23edf5 100644 (file)
@@ -660,7 +660,9 @@ class WebInstaller extends Installer {
         */
        public function getInfoBox( $text, $icon = false, $class = false ) {
                $text = $this->parse( $text, true );
-               $icon = ( $icon == false ) ? '../skins/common/images/info-32.png' : '../skins/common/images/' . $icon;
+               $icon = ( $icon == false ) ?
+                       '../skins/common/images/info-32.png' :
+                       '../skins/common/images/' . $icon;
                $alt = wfMessage( 'config-information' )->text();
 
                return Html::infoBox( $text, $icon, $alt, $class, false );
index 1df8f05..f2dc37f 100644 (file)
@@ -104,47 +104,83 @@ class WebInstallerOutput {
 
        /**
         * Get the raw vector CSS, flipping if needed
+        *
+        * @todo Possibly get rid of this function and use ResourceLoader in the manner it was
+        *   designed to be used in, rather than just grabbing a list of filenames from it,
+        *   and not properly handling such details as media types in module definitions.
+        *
         * @param string $dir 'ltr' or 'rtl'
         * @return String
         */
        public function getCSS( $dir ) {
-               $skinDir = dirname( dirname( __DIR__ ) ) . '/skins';
-
-               // All these files will be concatenated in sequence and loaded
-               // as one file.
-               // The string 'images/' in the files' contents will be replaced
-               // by '../skins/$skinName/images/', where $skinName is what appears
-               // before the last '/' in each of the strings.
-               $cssFileNames = array(
-
-                       // Basically the "skins.vector" ResourceLoader module styles
-                       'common/shared.css',
-                       'common/commonElements.css',
-                       'common/commonContent.css',
-                       'common/commonInterface.css',
-                       'vector/screen.css',
-
-                       // mw-config specific
-                       'common/config.css',
+               // All CSS files these modules reference will be concatenated in sequence
+               // and loaded as one file.
+               $moduleNames = array(
+                       'mediawiki.legacy.shared',
+                       'skins.vector',
+                       'mediawiki.legacy.config',
                );
 
+               $prepend = '';
                $css = '';
 
-               wfSuppressWarnings();
-               foreach ( $cssFileNames as $cssFileName ) {
-                       $fullCssFileName = "$skinDir/$cssFileName";
-                       $cssFileContents = file_get_contents( $fullCssFileName );
-                       if ( $cssFileContents ) {
-                               preg_match( "/^(\w+)\//", $cssFileName, $match );
-                               $skinName = $match[1];
-                               $css .= str_replace( 'images/', "../skins/$skinName/images/", $cssFileContents );
-                       } else {
-                               $css .= "/** Your webserver cannot read $fullCssFileName. Please check file permissions. */";
+               $cssFileNames = array();
+               $resourceLoader = new ResourceLoader();
+               foreach ( $moduleNames as $moduleName ) {
+                       $module = $resourceLoader->getModule( $moduleName );
+                       $cssFileNames = $module->getAllStyleFiles();
+
+                       wfSuppressWarnings();
+                       foreach ( $cssFileNames as $cssFileName ) {
+                               if ( !file_exists( $cssFileName ) ) {
+                                       $prepend .= ResourceLoader::makeComment( "Unable to find $cssFileName." );
+                                       continue;
+                               }
+
+                               if ( !is_readable( $cssFileName ) ) {
+                                       $prepend .= ResourceLoader::makeComment( "Unable to read $cssFileName. Please check file permissions." );
+                                       continue;
+                               }
+
+                               try {
+
+                                       if ( preg_match( '/\.less$/', $cssFileName ) ) {
+                                               // Run the LESS compiler for *.less files (bug 55589)
+                                               $compiler = ResourceLoader::getLessCompiler();
+                                               $cssFileContents = $compiler->compileFile( $cssFileName );
+                                       } else {
+                                               // Regular CSS file
+                                               $cssFileContents = file_get_contents( $cssFileName );
+                                       }
+
+                                       if ( $cssFileContents ) {
+                                               // Rewrite URLs, though don't bother embedding images. While static image
+                                               // files may be cached, CSS returned by this function is definitely not.
+                                               $cssDirName = dirname( $cssFileName );
+                                               $css .= CSSMin::remap(
+                                                       /* source */ $cssFileContents,
+                                                       /* local */ $cssDirName,
+                                                       /* remote */ '..' . str_replace(
+                                                               array( $GLOBALS['IP'], DIRECTORY_SEPARATOR ),
+                                                               array( '', '/' ),
+                                                               $cssDirName
+                                                       ),
+                                                       /* embedData */ false
+                                               );
+                                       } else {
+                                               $prepend .= ResourceLoader::makeComment( "Unable to read $cssFileName." );
+                                       }
+
+                               } catch ( Exception $e ) {
+                                       $prepend .= ResourceLoader::formatException( $e );
+                               }
+
+                               $css .= "\n";
                        }
-
-                       $css .= "\n";
+                       wfRestoreWarnings();
                }
-               wfRestoreWarnings();
+
+               $css = $prepend . $css;
 
                if ( $dir == 'rtl' ) {
                        $css = CSSJanus::transform( $css, true );
index 11c0a8e..e30373e 100644 (file)
@@ -704,9 +704,8 @@ class WebInstaller_Name extends WebInstallerPage {
                        ) ) .
                        $this->parent->getTextBox( array(
                                'var' => 'wgMetaNamespace',
-                               'label' => '', //TODO: Needs a label?
-                               'attribs' => array( 'readonly' => 'readonly', 'class' => 'enabledByOther' ),
-
+                               'label' => '', // @todo Needs a label?
+                               'attribs' => array( 'readonly' => 'readonly', 'class' => 'enabledByOther' )
                        ) ) .
                        $this->getFieldSetStart( 'config-admin-box' ) .
                        $this->parent->getTextBox( array(
@@ -865,7 +864,6 @@ class WebInstaller_Name extends WebInstallerPage {
 }
 
 class WebInstaller_Options extends WebInstallerPage {
-
        public function execute() {
                if ( $this->getVar( '_SkipOptional' ) == 'skip' ) {
                        return 'skip';
@@ -1255,7 +1253,9 @@ class WebInstaller_Install extends WebInstallerPage {
        public function startStage( $step ) {
                // Messages: config-install-database, config-install-tables, config-install-interwiki,
                // config-install-stats, config-install-keys, config-install-sysop, config-install-mainpage
-               $this->addHTML( "<li>" . wfMessage( "config-install-$step" )->escaped() . wfMessage( 'ellipsis' )->escaped() );
+               $this->addHTML( "<li>" . wfMessage( "config-install-$step" )->escaped() .
+                       wfMessage( 'ellipsis' )->escaped() );
+
                if ( $step == 'extension-tables' ) {
                        $this->startLiveBox();
                }
@@ -1282,7 +1282,6 @@ class WebInstaller_Install extends WebInstallerPage {
 }
 
 class WebInstaller_Complete extends WebInstallerPage {
-
        public function execute() {
                // Pop up a dialog box, to make it difficult for the user to forget
                // to download the file
index a20ea4a..fa7fee5 100644 (file)
@@ -376,7 +376,7 @@ class JobQueueGroup {
                                                        ++$count;
                                                }
                                        } catch ( JobQueueError $e ) {
-                                               wfDebugLog( 'exception', $e->getLogMessage() );
+                                               MWExceptionHandler::logException( $e );
                                        }
                                }
                        }
index bc2fff1..d611651 100644 (file)
@@ -55,6 +55,17 @@ class FormatJson {
         */
        const ALL_OK = 3;
 
+       /**
+        * Regex that matches whitespace inside empty arrays and objects.
+        *
+        * This doesn't affect regular strings inside the JSON because those can't
+        * have a real line break (\n) in them, at this point they are already escaped
+        * as the string "\n" which this doesn't match.
+        *
+        * @private
+        */
+       const WS_CLEANUP_REGEX = '/(?<=[\[{])\n\s*+(?=[\]}])/';
+
        /**
         * Characters problematic in JavaScript.
         *
@@ -130,6 +141,12 @@ class FormatJson {
                if ( $json === false ) {
                        return false;
                }
+
+               if ( $pretty ) {
+                       // Remove whitespace inside empty arrays/objects; different JSON encoders
+                       // vary on this, and we want our output to be consistent across implementations.
+                       $json = preg_replace( self::WS_CLEANUP_REGEX, '', $json );
+               }
                if ( $escaping & self::UTF8_OK ) {
                        $json = str_replace( self::$badChars, self::$badCharsEscaped, $json );
                }
@@ -213,6 +230,7 @@ class FormatJson {
                                        $buf .= substr( $json, $i, $skip );
                        }
                }
-               return str_replace( "\x01", '\"', preg_replace( '/ +$/m', '', $buf ) );
+               $buf = preg_replace( self::WS_CLEANUP_REGEX, '', $buf );
+               return str_replace( "\x01", '\"', $buf );
        }
 }
diff --git a/includes/libs/ScopedPHPTimeout.php b/includes/libs/ScopedPHPTimeout.php
new file mode 100644 (file)
index 0000000..d1493c3
--- /dev/null
@@ -0,0 +1,84 @@
+<?php
+/**
+ * Expansion of the PHP execution time limit feature for a function call.
+ *
+ * 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 to expand PHP execution time for a function call.
+ * Use this when performing changes that should not be interrupted.
+ *
+ * On construction, set_time_limit() is called and set to $seconds.
+ * If the client aborts the connection, PHP will continue to run.
+ * When the object goes out of scope, the timer is restarted, with
+ * the original time limit minus the time the object existed.
+ */
+class ScopedPHPTimeout {
+       protected $startTime; // float; seconds
+       protected $oldTimeout; // integer; seconds
+       protected $oldIgnoreAbort; // boolean
+
+       protected static $stackDepth = 0; // integer
+       protected static $totalCalls = 0; // integer
+       protected static $totalElapsed = 0; // float; seconds
+
+       /* Prevent callers in infinite loops from running forever */
+       const MAX_TOTAL_CALLS = 1000000;
+       const MAX_TOTAL_TIME = 300; // seconds
+
+       /**
+        * @param $seconds integer
+        */
+       public function __construct( $seconds ) {
+               if ( ini_get( 'max_execution_time' ) > 0 ) { // CLI uses 0
+                       if ( self::$totalCalls >= self::MAX_TOTAL_CALLS ) {
+                               trigger_error( "Maximum invocations of " . __CLASS__ . " exceeded." );
+                       } elseif ( self::$totalElapsed >= self::MAX_TOTAL_TIME ) {
+                               trigger_error( "Time limit within invocations of " . __CLASS__ . " exceeded." );
+                       } elseif ( self::$stackDepth > 0 ) { // recursion guard
+                               trigger_error( "Resursive invocation of " . __CLASS__ . " attempted." );
+                       } else {
+                               $this->oldIgnoreAbort = ignore_user_abort( true );
+                               $this->oldTimeout = ini_set( 'max_execution_time', $seconds );
+                               $this->startTime = microtime( true );
+                               ++self::$stackDepth;
+                               ++self::$totalCalls; // proof against < 1us scopes
+                       }
+               }
+       }
+
+       /**
+        * Restore the original timeout.
+        * This does not account for the timer value on __construct().
+        */
+       public function __destruct() {
+               if ( $this->oldTimeout ) {
+                       $elapsed = microtime( true ) - $this->startTime;
+                       // Note: a limit of 0 is treated as "forever"
+                       set_time_limit( max( 1, $this->oldTimeout - (int)$elapsed ) );
+                       // If each scoped timeout is for less than one second, we end up
+                       // restoring the original timeout without any decrease in value.
+                       // Thus web scripts in an infinite loop can run forever unless we
+                       // take some measures to prevent this. Track total time and calls.
+                       self::$totalElapsed += $elapsed;
+                       --self::$stackDepth;
+                       ignore_user_abort( $this->oldIgnoreAbort );
+               }
+       }
+}
diff --git a/includes/libs/XmlTypeCheck.php b/includes/libs/XmlTypeCheck.php
new file mode 100644 (file)
index 0000000..92ca7d8
--- /dev/null
@@ -0,0 +1,184 @@
+<?php
+/**
+ * XML syntax and type checker.
+ *
+ * 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 XmlTypeCheck {
+       /**
+        * Will be set to true or false to indicate whether the file is
+        * well-formed XML. Note that this doesn't check schema validity.
+        */
+       public $wellFormed = false;
+
+       /**
+        * Will be set to true if the optional element filter returned
+        * a match at some point.
+        */
+       public $filterMatch = false;
+
+       /**
+        * Name of the document's root element, including any namespace
+        * as an expanded URL.
+        */
+       public $rootElement = '';
+
+       /**
+        * @param string $input a filename or string containing the XML element
+        * @param callable $filterCallback (optional)
+        *        Function to call to do additional custom validity checks from the
+        *        SAX element handler event. This gives you access to the element
+        *        namespace, name, and attributes, but not to text contents.
+        *        Filter should return 'true' to toggle on $this->filterMatch
+        * @param boolean $isFile (optional) indicates if the first parameter is a
+        *        filename (default, true) or if it is a string (false)
+        */
+       function __construct( $input, $filterCallback = null, $isFile = true ) {
+               $this->filterCallback = $filterCallback;
+               if ( $isFile ) {
+                       $this->validateFromFile( $input );
+               } else {
+                       $this->validateFromString( $input );
+               }
+       }
+
+       /**
+        * Alternative constructor: from filename
+        *
+        * @param string $fname the filename of an XML document
+        * @param callable $filterCallback (optional)
+        *        Function to call to do additional custom validity checks from the
+        *        SAX element handler event. This gives you access to the element
+        *        namespace, name, and attributes, but not to text contents.
+        *        Filter should return 'true' to toggle on $this->filterMatch
+        * @return XmlTypeCheck
+        */
+       public static function newFromFilename( $fname, $filterCallback = null ) {
+               return new self( $fname, $filterCallback, true );
+       }
+
+       /**
+        * Alternative constructor: from string
+        *
+        * @param string $string a string containing an XML element
+        * @param callable $filterCallback (optional)
+        *        Function to call to do additional custom validity checks from the
+        *        SAX element handler event. This gives you access to the element
+        *        namespace, name, and attributes, but not to text contents.
+        *        Filter should return 'true' to toggle on $this->filterMatch
+        * @return XmlTypeCheck
+        */
+       public static function newFromString( $string, $filterCallback = null ) {
+               return new self( $string, $filterCallback, false );
+       }
+
+       /**
+        * Get the root element. Simple accessor to $rootElement
+        *
+        * @return string
+        */
+       public function getRootElement() {
+               return $this->rootElement;
+       }
+
+       /**
+        * Get an XML parser with the root element handler.
+        * @see XmlTypeCheck::rootElementOpen()
+        * @return resource a resource handle for the XML parser
+        */
+       private function getParser() {
+               $parser = xml_parser_create_ns( 'UTF-8' );
+               // case folding violates XML standard, turn it off
+               xml_parser_set_option( $parser, XML_OPTION_CASE_FOLDING, false );
+               xml_set_element_handler( $parser, array( $this, 'rootElementOpen' ), false );
+               return $parser;
+       }
+
+       /**
+        * @param string $fname the filename
+        */
+       private function validateFromFile( $fname ) {
+               $parser = $this->getParser();
+
+               if ( file_exists( $fname ) ) {
+                       $file = fopen( $fname, "rb" );
+                       if ( $file ) {
+                               do {
+                                       $chunk = fread( $file, 32768 );
+                                       $ret = xml_parse( $parser, $chunk, feof( $file ) );
+                                       if ( $ret == 0 ) {
+                                               $this->wellFormed = false;
+                                               fclose( $file );
+                                               xml_parser_free( $parser );
+                                               return;
+                                       }
+                               } while ( !feof( $file ) );
+
+                               fclose( $file );
+                       }
+               }
+               $this->wellFormed = true;
+
+               xml_parser_free( $parser );
+       }
+
+       /**
+        *
+        * @param string $string the XML-input-string to be checked.
+        */
+       private function validateFromString( $string ) {
+               $parser = $this->getParser();
+               $ret = xml_parse( $parser, $string, true );
+               xml_parser_free( $parser );
+               if ( $ret == 0 ) {
+                       $this->wellFormed = false;
+                       return;
+               }
+               $this->wellFormed = true;
+       }
+
+       /**
+        * @param $parser
+        * @param $name
+        * @param $attribs
+        */
+       private function rootElementOpen( $parser, $name, $attribs ) {
+               $this->rootElement = $name;
+
+               if ( is_callable( $this->filterCallback ) ) {
+                       xml_set_element_handler( $parser, array( $this, 'elementOpen' ), false );
+                       $this->elementOpen( $parser, $name, $attribs );
+               } else {
+                       // We only need the first open element
+                       xml_set_element_handler( $parser, false, false );
+               }
+       }
+
+       /**
+        * @param $parser
+        * @param $name
+        * @param $attribs
+        */
+       private function elementOpen( $parser, $name, $attribs ) {
+               if ( call_user_func( $this->filterCallback, $name, $attribs ) ) {
+                       // Filter hit!
+                       $this->filterMatch = true;
+               }
+       }
+}
index 6c4690e..82197b5 100644 (file)
@@ -6,6 +6,40 @@
 # and is available on most Linux systems. If Perl was distributed with
 # BSD::Resource included, we would happily use that instead, but it isn't.
 
+# Clean up cgroup
+cleanup() {
+       # First we have to move the current task into a "garbage" group, otherwise
+       # the cgroup will not be empty, and attempting to remove it will fail with
+       # "Device or resource busy"
+       if [ -w "$MW_CGROUP"/tasks ]; then
+               GARBAGE="$MW_CGROUP"
+       else
+               GARBAGE="$MW_CGROUP"/garbage-`id -un`
+               if [ ! -e "$GARBAGE" ]; then
+                       mkdir -m 0700 "$GARBAGE"
+               fi
+       fi
+       echo $BASHPID > "$GARBAGE"/tasks
+
+       # Suppress errors in case the cgroup has disappeared due to a release script
+       rmdir "$MW_CGROUP"/$$ 2>/dev/null
+}
+
+updateTaskCount() {
+       # There are lots of ways to count lines in a file in shell script, but this
+       # is one of the few that doesn't create another process, which would
+       # increase the returned number of tasks.
+       readarray < "$MW_CGROUP"/$$/tasks
+       NUM_TASKS=${#MAPFILE[*]}
+}
+
+log() {
+       echo limit.sh: "$*" >&3
+       echo limit.sh: "$*" >&2
+}
+
+MW_INCLUDE_STDERR=
+MW_USE_LOG_PIPE=
 MW_CPU_LIMIT=0
 MW_CGROUP=
 MW_MEM_LIMIT=0
@@ -15,6 +49,14 @@ MW_WALL_CLOCK_LIMIT=0
 # Override settings
 eval "$2"
 
+if [ -n "$MW_INCLUDE_STDERR" ]; then
+       exec 2>&1
+fi
+if [ -z "$MW_USE_LOG_PIPE" ]; then
+       # Open a dummy log FD
+       exec 3>/dev/null
+fi
+
 if [ "$MW_CPU_LIMIT" -gt 0 ]; then
        ulimit -t "$MW_CPU_LIMIT"
 fi
@@ -22,9 +64,11 @@ if [ "$MW_MEM_LIMIT" -gt 0 ]; then
        if [ -n "$MW_CGROUP" ]; then
                # Create cgroup
                if ! mkdir -m 0700 "$MW_CGROUP"/$$; then
-                       echo "limit.sh: failed to create the cgroup." 1>&2
-                       exit 1
+                       log "failed to create the cgroup."
+                       MW_CGROUP=""
                fi
+       fi
+       if [ -n "$MW_CGROUP" ]; then
                echo $$ > "$MW_CGROUP"/$$/tasks
                if [ -n "$MW_CGROUP_NOTIFY" ]; then
                        echo "1" > "$MW_CGROUP"/$$/notify_on_release
@@ -43,43 +87,16 @@ if [ "$MW_FILE_SIZE_LIMIT" -gt 0 ]; then
        ulimit -f "$MW_FILE_SIZE_LIMIT"
 fi
 if [ "$MW_WALL_CLOCK_LIMIT" -gt 0 -a -x "/usr/bin/timeout" ]; then
-       /usr/bin/timeout $MW_WALL_CLOCK_LIMIT /bin/bash -c "$1"
+       /usr/bin/timeout $MW_WALL_CLOCK_LIMIT /bin/bash -c "$1" 3>&-
        STATUS="$?"
        if [ "$STATUS" == 124 ]; then
-               echo "limit.sh: timed out." 1>&2
+               log "timed out executing command \"$1\""
        fi
 else
-       eval "$1"
+       eval "$1" 3>&-
        STATUS="$?"
 fi
 
-# Clean up cgroup
-cleanup() {
-       # First we have to move the current task into a "garbage" group, otherwise
-       # the cgroup will not be empty, and attempting to remove it will fail with
-       # "Device or resource busy"
-       if [ -w "$MW_CGROUP"/tasks ]; then
-               GARBAGE="$MW_CGROUP"
-       else
-               GARBAGE="$MW_CGROUP"/garbage-"$USER"
-               if [ ! -e "$GARBAGE" ]; then
-                       mkdir -m 0700 "$GARBAGE"
-               fi
-       fi
-       echo $BASHPID > "$GARBAGE"/tasks
-
-       # Suppress errors in case the cgroup has disappeared due to a release script
-       rmdir "$MW_CGROUP"/$$ 2>/dev/null
-}
-
-updateTaskCount() {
-       # There are lots of ways to count lines in a file in shell script, but this
-       # is one of the few that doesn't create another process, which would
-       # increase the returned number of tasks.
-       readarray < "$MW_CGROUP"/$$/tasks
-       NUM_TASKS=${#MAPFILE[*]}
-}
-
 if [ -n "$MW_CGROUP" ]; then
        updateTaskCount
 
@@ -92,7 +109,7 @@ if [ -n "$MW_CGROUP" ]; then
                                updateTaskCount
                        done
                        cleanup
-               ) >&/dev/null < /dev/null &
+               ) >&/dev/null < /dev/null 3>&- &
                disown -a
        else
                cleanup
index 09ae3b8..a11dcb7 100644 (file)
  * @ingroup Pager
  */
 class LogPager extends ReverseChronologicalPager {
-       private $types = array(), $performer = '', $title = '', $pattern = '';
+       private $types = array();
+       private $performer = '';
+       private $title = '';
+       private $pattern = '';
        private $typeCGI = '';
        public $mLogEventsList;
 
@@ -35,13 +38,13 @@ class LogPager extends ReverseChronologicalPager {
         * Constructor
         *
         * @param LogEventsList $list
-        * @param string $types or Array: log types to show
+        * @param string|array $types Log types to show
         * @param string $performer the user who made the log entries
         * @param string|Title $title the page title the log entries are for
         * @param string $pattern do a prefix search rather than an exact title match
         * @param array $conds extra conditions for the query
-        * @param int $year The year to start from
-        * @param int $month The month to start from
+        * @param int|bool $year The year to start from. Default: false
+        * @param int|bool $month The month to start from. Default: false
         * @param string $tagFilter tag
         */
        public function __construct( $list, $types = array(), $performer = '', $title = '', $pattern = '',
index d8d0bed..d3fa36d 100644 (file)
@@ -61,16 +61,18 @@ class ExifBitmapHandler extends BitmapHandler {
                                . $metadata['Software'][0][1] . ')';
                }
 
+               $formatter = new FormatMetadata;
+
                // ContactInfo also has to be dealt with specially
                if ( isset( $metadata['Contact'] ) ) {
                        $metadata['Contact'] =
-                               FormatMetadata::collapseContactInfo(
+                               $formatter->collapseContactInfo(
                                        $metadata['Contact'] );
                }
 
                foreach ( $metadata as &$val ) {
                        if ( is_array( $val ) ) {
-                               $val = FormatMetadata::flattenArray( $val, 'ul', $avoidHtml );
+                               $val = $formatter->flattenArrayReal( $val, 'ul', $avoidHtml );
                        }
                }
                $metadata['MEDIAWIKI_EXIF_VERSION'] = 1;
@@ -117,25 +119,32 @@ class ExifBitmapHandler extends BitmapHandler {
         * @return array|bool
         */
        function formatMetadata( $image ) {
-               $metadata = $image->getMetadata();
+               $meta = $this->getCommonMetaArray( $image );
+               if ( count( $meta ) === 0 ) {
+                       return false;
+               }
+
+               return $this->formatMetadataHelper( $meta );
+       }
+
+       public function getCommonMetaArray( File $file ) {
+               $metadata = $file->getMetadata();
                if ( $metadata === self::OLD_BROKEN_FILE ||
                        $metadata === self::BROKEN_FILE ||
-                       $this->isMetadataValid( $image, $metadata ) === self::METADATA_BAD )
+                       $this->isMetadataValid( $file, $metadata ) === self::METADATA_BAD )
                {
                        // So we don't try and display metadata from PagedTiffHandler
                        // for example when using InstantCommons.
-                       return false;
+                       return array();
                }
 
                $exif = unserialize( $metadata );
                if ( !$exif ) {
-                       return false;
+                       return array();
                }
                unset( $exif['MEDIAWIKI_EXIF_VERSION'] );
-               if ( count( $exif ) == 0 ) {
-                       return false;
-               }
-               return $this->formatMetadataHelper( $exif );
+
+               return $exif;
        }
 
        function getMetadataType( $image ) {
old mode 100644 (file)
new mode 100755 (executable)
index 1c5136f..91c4c9a
  * is already a large number of messages using the 'exif' prefix.
  *
  * @ingroup Media
+ * @since 1.23 the class extends ContextSource and various formerly-public internal methods are private
  */
-class FormatMetadata {
+class FormatMetadata extends ContextSource {
+
+       /**
+        * Only output a single language for multi-language fields
+        * @var boolean
+        * @since 1.23
+        */
+       protected $singleLang = false;
+
+       /**
+        * Trigger only outputting single language for multilanguage fields
+        *
+        * @param Boolean $val
+        * @since 1.23
+        */
+       public function setSingleLanguage( $val ) {
+               $this->singleLang = $val;
+       }
 
        /**
         * Numbers given by Exif user agents are often magical, that is they
@@ -52,13 +70,33 @@ class FormatMetadata {
         * value which most of the time are plain integers. This function
         * formats Exif (and other metadata) values into human readable form.
         *
+        * This is the usual entry point for this class.
+        *
         * @param array $tags the Exif data to format ( as returned by
         *                    Exif::getFilteredData() or BitmapMetadataHandler )
+        * @param IContextSource $context Context to use (optional)
         * @return array
         */
-       public static function getFormattedData( $tags ) {
-               global $wgLang;
+       public static function getFormattedData( $tags, $context = false ) {
+               $obj = new FormatMetadata;
+               if ( $context ) {
+                       $obj->setContext( $context );
+               }
+               return $obj->makeFormattedData( $tags );
+       }
 
+       /**
+        * Numbers given by Exif user agents are often magical, that is they
+        * should be replaced by a detailed explanation depending on their
+        * value which most of the time are plain integers. This function
+        * formats Exif (and other metadata) values into human readable form.
+        *
+        * @param array $tags the Exif data to format ( as returned by
+        *                    Exif::getFilteredData() or BitmapMetadataHandler )
+        * @return array
+        * @since 1.23
+        */
+       public function makeFormattedData( $tags ) {
                $resolutionunit = !isset( $tags['ResolutionUnit'] ) || $tags['ResolutionUnit'] == 2 ? 2 : 3;
                unset( $tags['ResolutionUnit'] );
 
@@ -107,7 +145,7 @@ class FormatMetadata {
                                        $time = wfTimestamp( TS_MW, '1971:01:01 ' . $tags[$tag] );
                                        // the 1971:01:01 is just a placeholder, and not shown to user.
                                        if ( $time && intval( $time ) > 0 ) {
-                                               $tags[$tag] = $wgLang->time( $time );
+                                               $tags[$tag] = $this->getLanguage()->time( $time );
                                        }
                                } catch ( TimestampException $e ) {
                                        // This shouldn't happen, but we've seen bad formats
@@ -121,7 +159,7 @@ class FormatMetadata {
                        // instead of the other props which are single
                        // valued (mostly) so handle as a special case.
                        if ( $tag === 'Contact' ) {
-                               $vals = self::collapseContactInfo( $vals );
+                               $vals = $this->collapseContactInfo( $vals );
                                continue;
                        }
 
@@ -133,7 +171,7 @@ class FormatMetadata {
                                        case 1: case 2: case 3: case 4:
                                        case 5: case 6: case 7: case 8:
                                        case 32773: case 32946: case 34712:
-                                               $val = self::msg( $tag, $val );
+                                               $val = $this->exifMsg( $tag, $val );
                                                break;
                                        default:
                                                /* If not recognized, display as is. */
@@ -144,7 +182,7 @@ class FormatMetadata {
                                case 'PhotometricInterpretation':
                                        switch ( $val ) {
                                        case 2: case 6:
-                                               $val = self::msg( $tag, $val );
+                                               $val = $this->exifMsg( $tag, $val );
                                                break;
                                        default:
                                                /* If not recognized, display as is. */
@@ -155,7 +193,7 @@ class FormatMetadata {
                                case 'Orientation':
                                        switch ( $val ) {
                                        case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8:
-                                               $val = self::msg( $tag, $val );
+                                               $val = $this->exifMsg( $tag, $val );
                                                break;
                                        default:
                                                /* If not recognized, display as is. */
@@ -166,7 +204,7 @@ class FormatMetadata {
                                case 'PlanarConfiguration':
                                        switch ( $val ) {
                                        case 1: case 2:
-                                               $val = self::msg( $tag, $val );
+                                               $val = $this->exifMsg( $tag, $val );
                                                break;
                                        default:
                                                /* If not recognized, display as is. */
@@ -179,7 +217,7 @@ class FormatMetadata {
                                        switch ( $val ) {
                                        case 1:
                                        case 2:
-                                               $val = self::msg( $tag, $val );
+                                               $val = $this->exifMsg( $tag, $val );
                                                break;
                                        default:
                                                /* If not recognized, display as is. */
@@ -191,10 +229,10 @@ class FormatMetadata {
                                case 'YResolution':
                                        switch ( $resolutionunit ) {
                                                case 2:
-                                                       $val = self::msg( 'XYResolution', 'i', self::formatNum( $val ) );
+                                                       $val = $this->exifMsg( 'XYResolution', 'i', $this->formatNum( $val ) );
                                                        break;
                                                case 3:
-                                                       $val = self::msg( 'XYResolution', 'c', self::formatNum( $val ) );
+                                                       $val = $this->exifMsg( 'XYResolution', 'c', $this->formatNum( $val ) );
                                                        break;
                                                default:
                                                        /* If not recognized, display as is. */
@@ -210,7 +248,7 @@ class FormatMetadata {
                                case 'ColorSpace':
                                        switch ( $val ) {
                                        case 1: case 65535:
-                                               $val = self::msg( $tag, $val );
+                                               $val = $this->exifMsg( $tag, $val );
                                                break;
                                        default:
                                                /* If not recognized, display as is. */
@@ -221,7 +259,7 @@ class FormatMetadata {
                                case 'ComponentsConfiguration':
                                        switch ( $val ) {
                                        case 0: case 1: case 2: case 3: case 4: case 5: case 6:
-                                               $val = self::msg( $tag, $val );
+                                               $val = $this->exifMsg( $tag, $val );
                                                break;
                                        default:
                                                /* If not recognized, display as is. */
@@ -238,12 +276,12 @@ class FormatMetadata {
                                case 'dc-date':
                                case 'DateTimeMetadata':
                                        if ( $val == '0000:00:00 00:00:00' || $val == '    :  :     :  :  ' ) {
-                                               $val = wfMessage( 'exif-unknowndate' )->text();
+                                               $val = $this->msg( 'exif-unknowndate' )->text();
                                        } elseif ( preg_match( '/^(?:\d{4}):(?:\d\d):(?:\d\d) (?:\d\d):(?:\d\d):(?:\d\d)$/D', $val ) ) {
                                                // Full date.
                                                $time = wfTimestamp( TS_MW, $val );
                                                if ( $time && intval( $time ) > 0 ) {
-                                                       $val = $wgLang->timeanddate( $time );
+                                                       $val = $this->getLanguage()->timeanddate( $time );
                                                }
                                        } elseif ( preg_match( '/^(?:\d{4}):(?:\d\d):(?:\d\d) (?:\d\d):(?:\d\d)$/D', $val ) ) {
                                                // No second field. Still format the same
@@ -251,7 +289,7 @@ class FormatMetadata {
                                                // but second still available in api
                                                $time = wfTimestamp( TS_MW, $val . ':00' );
                                                if ( $time && intval( $time ) > 0 ) {
-                                                       $val = $wgLang->timeanddate( $time );
+                                                       $val = $this->getLanguage()->timeanddate( $time );
                                                }
                                        } elseif ( preg_match( '/^(?:\d{4}):(?:\d\d):(?:\d\d)$/D', $val ) ) {
                                                // If only the date but not the time is filled in.
@@ -260,7 +298,7 @@ class FormatMetadata {
                                                        . substr( $val, 8, 2 )
                                                        . '000000' );
                                                if ( $time && intval( $time ) > 0 ) {
-                                                       $val = $wgLang->date( $time );
+                                                       $val = $this->getLanguage()->date( $time );
                                                }
                                        }
                                        // else it will just output $val without formatting it.
@@ -269,7 +307,7 @@ class FormatMetadata {
                                case 'ExposureProgram':
                                        switch ( $val ) {
                                        case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8:
-                                               $val = self::msg( $tag, $val );
+                                               $val = $this->exifMsg( $tag, $val );
                                                break;
                                        default:
                                                /* If not recognized, display as is. */
@@ -278,13 +316,13 @@ class FormatMetadata {
                                        break;
 
                                case 'SubjectDistance':
-                                       $val = self::msg( $tag, '', self::formatNum( $val ) );
+                                       $val = $this->exifMsg( $tag, '', $this->formatNum( $val ) );
                                        break;
 
                                case 'MeteringMode':
                                        switch ( $val ) {
                                        case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 255:
-                                               $val = self::msg( $tag, $val );
+                                               $val = $this->exifMsg( $tag, $val );
                                                break;
                                        default:
                                                /* If not recognized, display as is. */
@@ -297,7 +335,7 @@ class FormatMetadata {
                                        case 0: case 1: case 2: case 3: case 4: case 9: case 10: case 11:
                                        case 12: case 13: case 14: case 15: case 17: case 18: case 19: case 20:
                                        case 21: case 22: case 23: case 24: case 255:
-                                               $val = self::msg( $tag, $val );
+                                               $val = $this->exifMsg( $tag, $val );
                                                break;
                                        default:
                                                /* If not recognized, display as is. */
@@ -322,15 +360,15 @@ class FormatMetadata {
                                                        continue;
                                                }
                                                $fullTag = $tag . '-' . $subTag;
-                                               $flashMsgs[] = self::msg( $fullTag, $subValue );
+                                               $flashMsgs[] = $this->exifMsg( $fullTag, $subValue );
                                        }
-                                       $val = $wgLang->commaList( $flashMsgs );
+                                       $val = $this->getLanguage()->commaList( $flashMsgs );
                                        break;
 
                                case 'FocalPlaneResolutionUnit':
                                        switch ( $val ) {
                                        case 2:
-                                               $val = self::msg( $tag, $val );
+                                               $val = $this->exifMsg( $tag, $val );
                                                break;
                                        default:
                                                /* If not recognized, display as is. */
@@ -341,7 +379,7 @@ class FormatMetadata {
                                case 'SensingMethod':
                                        switch ( $val ) {
                                        case 1: case 2: case 3: case 4: case 5: case 7: case 8:
-                                               $val = self::msg( $tag, $val );
+                                               $val = $this->exifMsg( $tag, $val );
                                                break;
                                        default:
                                                /* If not recognized, display as is. */
@@ -352,7 +390,7 @@ class FormatMetadata {
                                case 'FileSource':
                                        switch ( $val ) {
                                        case 3:
-                                               $val = self::msg( $tag, $val );
+                                               $val = $this->exifMsg( $tag, $val );
                                                break;
                                        default:
                                                /* If not recognized, display as is. */
@@ -363,7 +401,7 @@ class FormatMetadata {
                                case 'SceneType':
                                        switch ( $val ) {
                                        case 1:
-                                               $val = self::msg( $tag, $val );
+                                               $val = $this->exifMsg( $tag, $val );
                                                break;
                                        default:
                                                /* If not recognized, display as is. */
@@ -374,7 +412,7 @@ class FormatMetadata {
                                case 'CustomRendered':
                                        switch ( $val ) {
                                        case 0: case 1:
-                                               $val = self::msg( $tag, $val );
+                                               $val = $this->exifMsg( $tag, $val );
                                                break;
                                        default:
                                                /* If not recognized, display as is. */
@@ -385,7 +423,7 @@ class FormatMetadata {
                                case 'ExposureMode':
                                        switch ( $val ) {
                                        case 0: case 1: case 2:
-                                               $val = self::msg( $tag, $val );
+                                               $val = $this->exifMsg( $tag, $val );
                                                break;
                                        default:
                                                /* If not recognized, display as is. */
@@ -396,7 +434,7 @@ class FormatMetadata {
                                case 'WhiteBalance':
                                        switch ( $val ) {
                                        case 0: case 1:
-                                               $val = self::msg( $tag, $val );
+                                               $val = $this->exifMsg( $tag, $val );
                                                break;
                                        default:
                                                /* If not recognized, display as is. */
@@ -407,7 +445,7 @@ class FormatMetadata {
                                case 'SceneCaptureType':
                                        switch ( $val ) {
                                        case 0: case 1: case 2: case 3:
-                                               $val = self::msg( $tag, $val );
+                                               $val = $this->exifMsg( $tag, $val );
                                                break;
                                        default:
                                                /* If not recognized, display as is. */
@@ -418,7 +456,7 @@ class FormatMetadata {
                                case 'GainControl':
                                        switch ( $val ) {
                                        case 0: case 1: case 2: case 3: case 4:
-                                               $val = self::msg( $tag, $val );
+                                               $val = $this->exifMsg( $tag, $val );
                                                break;
                                        default:
                                                /* If not recognized, display as is. */
@@ -429,7 +467,7 @@ class FormatMetadata {
                                case 'Contrast':
                                        switch ( $val ) {
                                        case 0: case 1: case 2:
-                                               $val = self::msg( $tag, $val );
+                                               $val = $this->exifMsg( $tag, $val );
                                                break;
                                        default:
                                                /* If not recognized, display as is. */
@@ -440,7 +478,7 @@ class FormatMetadata {
                                case 'Saturation':
                                        switch ( $val ) {
                                        case 0: case 1: case 2:
-                                               $val = self::msg( $tag, $val );
+                                               $val = $this->exifMsg( $tag, $val );
                                                break;
                                        default:
                                                /* If not recognized, display as is. */
@@ -451,7 +489,7 @@ class FormatMetadata {
                                case 'Sharpness':
                                        switch ( $val ) {
                                        case 0: case 1: case 2:
-                                               $val = self::msg( $tag, $val );
+                                               $val = $this->exifMsg( $tag, $val );
                                                break;
                                        default:
                                                /* If not recognized, display as is. */
@@ -462,7 +500,7 @@ class FormatMetadata {
                                case 'SubjectDistanceRange':
                                        switch ( $val ) {
                                        case 0: case 1: case 2: case 3:
-                                               $val = self::msg( $tag, $val );
+                                               $val = $this->exifMsg( $tag, $val );
                                                break;
                                        default:
                                                /* If not recognized, display as is. */
@@ -475,7 +513,7 @@ class FormatMetadata {
                                case 'GPSDestLatitudeRef':
                                        switch ( $val ) {
                                        case 'N': case 'S':
-                                               $val = self::msg( 'GPSLatitude', $val );
+                                               $val = $this->exifMsg( 'GPSLatitude', $val );
                                                break;
                                        default:
                                                /* If not recognized, display as is. */
@@ -487,7 +525,7 @@ class FormatMetadata {
                                case 'GPSDestLongitudeRef':
                                        switch ( $val ) {
                                        case 'E': case 'W':
-                                               $val = self::msg( 'GPSLongitude', $val );
+                                               $val = $this->exifMsg( 'GPSLongitude', $val );
                                                break;
                                        default:
                                                /* If not recognized, display as is. */
@@ -497,16 +535,16 @@ class FormatMetadata {
 
                                case 'GPSAltitude':
                                        if ( $val < 0 ) {
-                                               $val = self::msg( 'GPSAltitude', 'below-sealevel', self::formatNum( -$val, 3 ) );
+                                               $val = $this->exifMsg( 'GPSAltitude', 'below-sealevel', $this->formatNum( -$val, 3 ) );
                                        } else {
-                                               $val = self::msg( 'GPSAltitude', 'above-sealevel', self::formatNum( $val, 3 ) );
+                                               $val = $this->exifMsg( 'GPSAltitude', 'above-sealevel', $this->formatNum( $val, 3 ) );
                                        }
                                        break;
 
                                case 'GPSStatus':
                                        switch ( $val ) {
                                        case 'A': case 'V':
-                                               $val = self::msg( $tag, $val );
+                                               $val = $this->exifMsg( $tag, $val );
                                                break;
                                        default:
                                                /* If not recognized, display as is. */
@@ -517,7 +555,7 @@ class FormatMetadata {
                                case 'GPSMeasureMode':
                                        switch ( $val ) {
                                        case 2: case 3:
-                                               $val = self::msg( $tag, $val );
+                                               $val = $this->exifMsg( $tag, $val );
                                                break;
                                        default:
                                                /* If not recognized, display as is. */
@@ -530,7 +568,7 @@ class FormatMetadata {
                                case 'GPSDestBearingRef':
                                        switch ( $val ) {
                                        case 'T': case 'M':
-                                               $val = self::msg( 'GPSDirection', $val );
+                                               $val = $this->exifMsg( 'GPSDirection', $val );
                                                break;
                                        default:
                                                /* If not recognized, display as is. */
@@ -540,17 +578,17 @@ class FormatMetadata {
 
                                case 'GPSLatitude':
                                case 'GPSDestLatitude':
-                                       $val = self::formatCoords( $val, 'latitude' );
+                                       $val = $this->formatCoords( $val, 'latitude' );
                                        break;
                                case 'GPSLongitude':
                                case 'GPSDestLongitude':
-                                       $val = self::formatCoords( $val, 'longitude' );
+                                       $val = $this->formatCoords( $val, 'longitude' );
                                        break;
 
                                case 'GPSSpeedRef':
                                        switch ( $val ) {
                                        case 'K': case 'M': case 'N':
-                                               $val = self::msg( 'GPSSpeed', $val );
+                                               $val = $this->exifMsg( 'GPSSpeed', $val );
                                                break;
                                        default:
                                                /* If not recognized, display as is. */
@@ -561,7 +599,7 @@ class FormatMetadata {
                                case 'GPSDestDistanceRef':
                                        switch ( $val ) {
                                        case 'K': case 'M': case 'N':
-                                               $val = self::msg( 'GPSDestDistance', $val );
+                                               $val = $this->exifMsg( 'GPSDestDistance', $val );
                                                break;
                                        default:
                                                /* If not recognized, display as is. */
@@ -572,15 +610,15 @@ class FormatMetadata {
                                case 'GPSDOP':
                                        // See http://en.wikipedia.org/wiki/Dilution_of_precision_(GPS)
                                        if ( $val <= 2 ) {
-                                               $val = self::msg( $tag, 'excellent', self::formatNum( $val ) );
+                                               $val = $this->exifMsg( $tag, 'excellent', $this->formatNum( $val ) );
                                        } elseif ( $val <= 5 ) {
-                                               $val = self::msg( $tag, 'good', self::formatNum( $val ) );
+                                               $val = $this->exifMsg( $tag, 'good', $this->formatNum( $val ) );
                                        } elseif ( $val <= 10 ) {
-                                               $val = self::msg( $tag, 'moderate', self::formatNum( $val ) );
+                                               $val = $this->exifMsg( $tag, 'moderate', $this->formatNum( $val ) );
                                        } elseif ( $val <= 20 ) {
-                                               $val = self::msg( $tag, 'fair', self::formatNum( $val ) );
+                                               $val = $this->exifMsg( $tag, 'fair', $this->formatNum( $val ) );
                                        } else {
-                                               $val = self::msg( $tag, 'poor', self::formatNum( $val ) );
+                                               $val = $this->exifMsg( $tag, 'poor', $this->formatNum( $val ) );
                                        }
                                        break;
 
@@ -589,41 +627,41 @@ class FormatMetadata {
                                // the make, model and software name to link to their articles.
                                case 'Make':
                                case 'Model':
-                                       $val = self::msg( $tag, '', $val );
+                                       $val = $this->exifMsg( $tag, '', $val );
                                        break;
 
                                case 'Software':
                                        if ( is_array( $val ) ) {
                                                //if its a software, version array.
-                                               $val = wfMessage( 'exif-software-version-value', $val[0], $val[1] )->text();
+                                               $val = $this->msg( 'exif-software-version-value', $val[0], $val[1] )->text();
                                        } else {
-                                               $val = self::msg( $tag, '', $val );
+                                               $val = $this->exifMsg( $tag, '', $val );
                                        }
                                        break;
 
                                case 'ExposureTime':
                                        // Show the pretty fraction as well as decimal version
-                                       $val = wfMessage( 'exif-exposuretime-format',
-                                               self::formatFraction( $val ), self::formatNum( $val ) )->text();
+                                       $val = $this->msg( 'exif-exposuretime-format',
+                                               $this->formatFraction( $val ), $this->formatNum( $val ) )->text();
                                        break;
                                case 'ISOSpeedRatings':
                                        // If its = 65535 that means its at the
                                        // limit of the size of Exif::short and
                                        // is really higher.
                                        if ( $val == '65535' ) {
-                                               $val = self::msg( $tag, 'overflow' );
+                                               $val = $this->exifMsg( $tag, 'overflow' );
                                        } else {
-                                               $val = self::formatNum( $val );
+                                               $val = $this->formatNum( $val );
                                        }
                                        break;
                                case 'FNumber':
-                                       $val = wfMessage( 'exif-fnumber-format',
-                                               self::formatNum( $val ) )->text();
+                                       $val = $this->msg( 'exif-fnumber-format',
+                                               $this->formatNum( $val ) )->text();
                                        break;
 
                                case 'FocalLength': case 'FocalLengthIn35mmFilm':
-                                       $val = wfMessage( 'exif-focallength-format',
-                                               self::formatNum( $val ) )->text();
+                                       $val = $this->msg( 'exif-focallength-format',
+                                               $this->formatNum( $val ) )->text();
                                        break;
 
                                case 'MaxApertureValue':
@@ -637,9 +675,9 @@ class FormatMetadata {
                                        if ( is_numeric( $val ) ) {
                                                $fNumber = pow( 2, $val / 2 );
                                                if ( $fNumber !== false ) {
-                                                       $val = wfMessage( 'exif-maxaperturevalue-value',
-                                                               self::formatNum( $val ),
-                                                               self::formatNum( $fNumber, 2 )
+                                                       $val = $this->msg( 'exif-maxaperturevalue-value',
+                                                               $this->formatNum( $val ),
+                                                               $this->formatNum( $fNumber, 2 )
                                                        )->text();
                                                }
                                        }
@@ -658,7 +696,7 @@ class FormatMetadata {
                                                case 'sci': case 'soi':
                                                case 'spo': case 'war':
                                                case 'wea':
-                                                       $val = self::msg(
+                                                       $val = $this->exifMsg(
                                                                'iimcategory',
                                                                $val
                                                        );
@@ -670,7 +708,7 @@ class FormatMetadata {
                                        // classification. We decode the
                                        // first 2 digits, which provide
                                        // a broad category.
-                                       $val = self::convertNewsCode( $val );
+                                       $val = $this->convertNewsCode( $val );
                                        break;
                                case 'Urgency':
                                        // 1-8 with 1 being highest, 5 normal
@@ -687,7 +725,7 @@ class FormatMetadata {
                                        }
 
                                        if ( $urgency !== '' ) {
-                                               $val = self::msg( 'urgency',
+                                               $val = $this->exifMsg( 'urgency',
                                                        $urgency, $val
                                                );
                                        }
@@ -700,7 +738,7 @@ class FormatMetadata {
                                case 'PixelYDimension':
                                case 'ImageWidth':
                                case 'ImageLength':
-                                       $val = self::formatNum( $val ) . ' ' . wfMessage( 'unit-pixel' )->text();
+                                       $val = $this->formatNum( $val ) . ' ' . $this->msg( 'unit-pixel' )->text();
                                        break;
 
                                // Do not transform fields with pure text.
@@ -783,7 +821,7 @@ class FormatMetadata {
                                case 'ObjectCycle':
                                        switch ( $val ) {
                                        case 'a': case 'p': case 'b':
-                                               $val = self::msg( $tag, $val );
+                                               $val = $this->exifMsg( $tag, $val );
                                                break;
                                        default:
                                                $val = htmlspecialchars( $val );
@@ -793,20 +831,20 @@ class FormatMetadata {
                                case 'Copyrighted':
                                        switch ( $val ) {
                                        case 'True': case 'False':
-                                               $val = self::msg( $tag, $val );
+                                               $val = $this->exifMsg( $tag, $val );
                                                break;
                                        }
                                        break;
                                case 'Rating':
                                        if ( $val == '-1' ) {
-                                               $val = self::msg( $tag, 'rejected' );
+                                               $val = $this->exifMsg( $tag, 'rejected' );
                                        } else {
-                                               $val = self::formatNum( $val );
+                                               $val = $this->formatNum( $val );
                                        }
                                        break;
 
                                case 'LanguageCode':
-                                       $lang = Language::fetchLanguageName( strtolower( $val ), $wgLang->getCode() );
+                                       $lang = Language::fetchLanguageName( strtolower( $val ), $this->getLanguage()->getCode() );
                                        if ( $lang ) {
                                                $val = htmlspecialchars( $lang );
                                        } else {
@@ -815,17 +853,64 @@ class FormatMetadata {
                                        break;
 
                                default:
-                                       $val = self::formatNum( $val );
+                                       $val = $this->formatNum( $val );
                                        break;
                                }
                        }
                        // End formatting values, start flattening arrays.
-                       $vals = self::flattenArray( $vals, $type );
+                       $vals = $this->flattenArrayReal( $vals, $type );
 
                }
                return $tags;
        }
 
+       /**
+        * Flatten an array, using the content language for any messages.
+        *
+        * @param array $vals array of values
+        * @param string $type Type of array (either lang, ul, ol).
+        *     lang = language assoc array with keys being the lang code
+        *     ul = unordered list, ol = ordered list
+        *     type can also come from the '_type' member of $vals.
+        * @param $noHtml Boolean If to avoid returning anything resembling
+        *     html. (Ugly hack for backwards compatibility with old mediawiki).
+        * @param IContextSource $context
+        * @return String single value (in wiki-syntax).
+        * @since 1.23
+        */
+       public static function flattenArrayContentLang( $vals, $type = 'ul', $noHtml = false, $context = false ) {
+               global $wgContLang;
+               $obj = new FormatMetadata;
+               if ( $context ) {
+                       $obj->setContext( $context );
+               }
+               $context = new DerivativeContext( $obj->getContext() );
+               $context->setLanguage( $wgContLang );
+               $obj->setContext( $context );
+               return $obj->flattenArrayReal( $vals, $type, $noHtml );
+       }
+
+       /**
+        * Flatten an array, using the user language for any messages.
+        *
+        * @param array $vals array of values
+        * @param string $type Type of array (either lang, ul, ol).
+        *     lang = language assoc array with keys being the lang code
+        *     ul = unordered list, ol = ordered list
+        *     type can also come from the '_type' member of $vals.
+        * @param $noHtml Boolean If to avoid returning anything resembling
+        *     html. (Ugly hack for backwards compatibility with old mediawiki).
+        * @param IContextSource $context
+        * @return String single value (in wiki-syntax).
+        */
+       public static function flattenArray( $vals, $type = 'ul', $noHtml = false, $context = false ) {
+               $obj = new FormatMetadata;
+               if ( $context ) {
+                       $obj->setContext( $context );
+               }
+               return $obj->flattenArrayReal( $vals, $type, $noHtml );
+       }
+
        /**
         * A function to collapse multivalued tags into a single value.
         * This turns an array of (for example) authors into a bulleted list.
@@ -834,14 +919,19 @@ class FormatMetadata {
         *
         * @param array $vals array of values
         * @param string $type Type of array (either lang, ul, ol).
-        * lang = language assoc array with keys being the lang code
-        * ul = unordered list, ol = ordered list
-        * type can also come from the '_type' member of $vals.
+        *     lang = language assoc array with keys being the lang code
+        *     ul = unordered list, ol = ordered list
+        *     type can also come from the '_type' member of $vals.
         * @param $noHtml Boolean If to avoid returning anything resembling
-        * html. (Ugly hack for backwards compatibility with old mediawiki).
+        *     html. (Ugly hack for backwards compatibility with old mediawiki).
         * @return String single value (in wiki-syntax).
+        * @since 1.23
         */
-       public static function flattenArray( $vals, $type = 'ul', $noHtml = false ) {
+       public function flattenArrayReal( $vals, $type = 'ul', $noHtml = false ) {
+               if ( !is_array( $vals ) ) {
+                       return $vals; // do nothing if not an array;
+               }
+
                if ( isset( $vals['_type'] ) ) {
                        $type = $vals['_type'];
                        unset( $vals['_type'] );
@@ -862,7 +952,6 @@ class FormatMetadata {
                 * languages, we don't want to show them all by default
                 */
                else {
-                       global $wgContLang;
                        switch ( $type ) {
                        case 'lang':
                                // Display default, followed by ContLang,
@@ -873,7 +962,7 @@ class FormatMetadata {
 
                                $content = '';
 
-                               $cLang = $wgContLang->getCode();
+                               $priorityLanguages = $this->getPriorityLanguages();
                                $defaultItem = false;
                                $defaultLang = false;
 
@@ -888,18 +977,24 @@ class FormatMetadata {
                                        $defaultItem = $vals['x-default'];
                                        unset( $vals['x-default'] );
                                }
-                               // Do contentLanguage.
-                               if ( isset( $vals[$cLang] ) ) {
-                                       $isDefault = false;
-                                       if ( $vals[$cLang] === $defaultItem ) {
-                                               $defaultItem = false;
-                                               $isDefault = true;
-                                       }
-                                       $content .= self::langItem(
-                                               $vals[$cLang], $cLang,
-                                               $isDefault, $noHtml );
+                               foreach( $priorityLanguages as $pLang ) {
+                                       if ( isset( $vals[$pLang] ) ) {
+                                               $isDefault = false;
+                                               if ( $vals[$pLang] === $defaultItem ) {
+                                                       $defaultItem = false;
+                                                       $isDefault = true;
+                                               }
+                                               $content .= $this->langItem(
+                                                       $vals[$pLang], $pLang,
+                                                       $isDefault, $noHtml );
+
+                                               unset( $vals[$pLang] );
 
-                                       unset( $vals[$cLang] );
+                                               if ( $this->singleLang ) {
+                                                       return Html::rawElement( 'span',
+                                                               array( 'lang' => $pLang ), $vals[$pLang] );
+                                               }
+                                       }
                                }
 
                                // Now do the rest.
@@ -908,13 +1003,20 @@ class FormatMetadata {
                                                $defaultLang = $lang;
                                                continue;
                                        }
-                                       $content .= self::langItem( $item,
+                                       $content .= $this->langItem( $item,
                                                $lang, false, $noHtml );
+                                       if ( $this->singleLang ) {
+                                               return Html::rawElement( 'span',
+                                                       array( 'lang' => $lang ), $item );
+                                       }
                                }
                                if ( $defaultItem !== false ) {
-                                       $content = self::langItem( $defaultItem,
+                                       $content = $this->langItem( $defaultItem,
                                                $defaultLang, true, $noHtml ) .
                                                $content;
+                                       if ( $this->singleLang ) {
+                                               return $defaultItem;
+                                       }
                                }
                                if ( $noHtml ) {
                                        return $content;
@@ -947,7 +1049,7 @@ class FormatMetadata {
         * @return string language item (Note: despite how this looks,
         * this is treated as wikitext not html).
         */
-       private static function langItem( $value, $lang, $default = false, $noHtml = false ) {
+       private function langItem( $value, $lang, $default = false, $noHtml = false ) {
                if ( $lang === false && $default === false ) {
                        throw new MWException( '$lang and $default cannot both '
                                . 'be false.' );
@@ -961,13 +1063,12 @@ class FormatMetadata {
                }
 
                if ( $lang === false ) {
+                       $msg = $this->msg( 'metadata-langitem-default', $wrappedValue );
                        if ( $noHtml ) {
-                               return wfMessage( 'metadata-langitem-default',
-                                       $wrappedValue )->text() . "\n\n";
+                               return $msg->text() . "\n\n";
                        } /* else */
                        return '<li class="mw-metadata-lang-default">'
-                               . wfMessage( 'metadata-langitem-default',
-                                       $wrappedValue )->text()
+                               . $msg->text()
                                . "</li>\n";
                }
 
@@ -984,9 +1085,9 @@ class FormatMetadata {
                }
                // else we have a language specified
 
+               $msg = $this->msg( 'metadata-langitem', $wrappedValue, $langName, $lang );
                if ( $noHtml ) {
-                       return '*' . wfMessage( 'metadata-langitem',
-                               $wrappedValue, $langName, $lang )->text();
+                       return '*' . $msg->text();
                } /* else: */
 
                $item = '<li class="mw-metadata-lang-code-'
@@ -995,8 +1096,7 @@ class FormatMetadata {
                        $item .= ' mw-metadata-lang-default';
                }
                $item .= '" lang="' . $lang . '">';
-               $item .= wfMessage( 'metadata-langitem',
-                       $wrappedValue, $langName, $lang )->text();
+               $item .= $msg->text();
                $item .= "</li>\n";
                return $item;
        }
@@ -1010,15 +1110,15 @@ class FormatMetadata {
         * @param string $val the value of the tag
         * @param string $arg an argument to pass ($1)
         * @param string $arg2 a 2nd argument to pass ($2)
-        * @return string A wfMessage of "exif-$tag-$val" in lower case
+        * @return string The text content of "exif-$tag-$val" message in lower case
         */
-       static function msg( $tag, $val, $arg = null, $arg2 = null ) {
+       private function exifMsg( $tag, $val, $arg = null, $arg2 = null ) {
                global $wgContLang;
 
                if ( $val === '' ) {
                        $val = 'value';
                }
-               return wfMessage( $wgContLang->lc( "exif-$tag-$val" ), $arg, $arg2 )->text();
+               return $this->msg( $wgContLang->lc( "exif-$tag-$val" ), $arg, $arg2 )->text();
        }
 
        /**
@@ -1029,15 +1129,14 @@ class FormatMetadata {
         * @param $round float|int|bool digits to round to or false.
         * @return mixed A floating point number or whatever we were fed
         */
-       static function formatNum( $num, $round = false ) {
-               global $wgLang;
+       private function formatNum( $num, $round = false ) {
                $m = array();
                if ( is_array( $num ) ) {
                        $out = array();
                        foreach ( $num as $number ) {
-                               $out[] = self::formatNum( $number );
+                               $out[] = $this->formatNum( $number );
                        }
-                       return $wgLang->commaList( $out );
+                       return $this->getLanguage()->commaList( $out );
                }
                if ( preg_match( '/^(-?\d+)\/(\d+)$/', $num, $m ) ) {
                        if ( $m[2] != 0 ) {
@@ -1049,12 +1148,12 @@ class FormatMetadata {
                                $newNum = $num;
                        }
 
-                       return $wgLang->formatNum( $newNum );
+                       return $this->getLanguage()->formatNum( $newNum );
                } else {
                        if ( is_numeric( $num ) && $round !== false ) {
                                $num = round( $num, $round );
                        }
-                       return $wgLang->formatNum( $num );
+                       return $this->getLanguage()->formatNum( $num );
                }
        }
 
@@ -1066,18 +1165,18 @@ class FormatMetadata {
         * @param $num Mixed: the value to format
         * @return mixed A floating point number or whatever we were fed
         */
-       static function formatFraction( $num ) {
+       private function formatFraction( $num ) {
                $m = array();
                if ( preg_match( '/^(-?\d+)\/(\d+)$/', $num, $m ) ) {
                        $numerator = intval( $m[1] );
                        $denominator = intval( $m[2] );
-                       $gcd = self::gcd( abs( $numerator ), $denominator );
+                       $gcd = $this->gcd( abs( $numerator ), $denominator );
                        if ( $gcd != 0 ) {
                                // 0 shouldn't happen! ;)
-                               return self::formatNum( $numerator / $gcd ) . '/' . self::formatNum( $denominator / $gcd );
+                               return $this->formatNum( $numerator / $gcd ) . '/' . $this->formatNum( $denominator / $gcd );
                        }
                }
-               return self::formatNum( $num );
+               return $this->formatNum( $num );
        }
 
        /**
@@ -1088,7 +1187,7 @@ class FormatMetadata {
         * @return int
         * @private
         */
-       static function gcd( $a, $b ) {
+       private function gcd( $a, $b ) {
                /*
                        // http://en.wikipedia.org/wiki/Euclidean_algorithm
                        // Recursive form would be:
@@ -1119,7 +1218,7 @@ class FormatMetadata {
         * @param string $val The 8 digit news code.
         * @return string The human readable form
         */
-       private static function convertNewsCode( $val ) {
+       private function convertNewsCode( $val ) {
                if ( !preg_match( '/^\d{8}$/D', $val ) ) {
                        // Not a valid news code.
                        return $val;
@@ -1179,8 +1278,8 @@ class FormatMetadata {
                                break;
                }
                if ( $cat !== '' ) {
-                       $catMsg = self::msg( 'iimcategory', $cat );
-                       $val = self::msg( 'subjectnewscode', '', $val, $catMsg );
+                       $catMsg = $this->exifMsg( 'iimcategory', $cat );
+                       $val = $this->exifMsg( 'subjectnewscode', '', $val, $catMsg );
                }
                return $val;
        }
@@ -1193,7 +1292,7 @@ class FormatMetadata {
         * @param string $type latitude or longitude (for if its a NWS or E)
         * @return mixed A floating point number or whatever we were fed
         */
-       static function formatCoords( $coord, $type ) {
+       private function formatCoords( $coord, $type ) {
                $ref = '';
                if ( $coord < 0 ) {
                        $nCoord = -$coord;
@@ -1215,11 +1314,11 @@ class FormatMetadata {
                $min = floor( ( $nCoord - $deg ) * 60.0 );
                $sec = round( ( ( $nCoord - $deg ) - $min / 60 ) * 3600, 2 );
 
-               $deg = self::formatNum( $deg );
-               $min = self::formatNum( $min );
-               $sec = self::formatNum( $sec );
+               $deg = $this->formatNum( $deg );
+               $min = $this->formatNum( $min );
+               $sec = $this->formatNum( $sec );
 
-               return wfMessage( 'exif-coordinate-format', $deg, $min, $sec, $ref, $coord )->text();
+               return $this->msg( 'exif-coordinate-format', $deg, $min, $sec, $ref, $coord )->text();
        }
 
        /**
@@ -1235,8 +1334,9 @@ class FormatMetadata {
         * public.
         *
         * @return String of html-ish looking wikitext
+        * @since 1.23 no longer static
         */
-       public static function collapseContactInfo( $vals ) {
+       public function collapseContactInfo( $vals ) {
                if ( !( isset( $vals['CiAdrExtadr'] )
                        || isset( $vals['CiAdrCity'] )
                        || isset( $vals['CiAdrCtry'] )
@@ -1258,7 +1358,7 @@ class FormatMetadata {
                        foreach ( $vals as &$val ) {
                                $val = htmlspecialchars( $val );
                        }
-                       return self::flattenArray( $vals );
+                       return $this->flattenArrayReal( $vals );
                } else {
                        // We have a real ContactInfo field.
                        // Its unclear if all these fields have to be
@@ -1340,11 +1440,303 @@ class FormatMetadata {
                                        . htmlspecialchars( $vals['CiUrlWork'] )
                                        . '</span>';
                        }
-                       return wfMessage( 'exif-contact-value', $email, $url,
+                       return $this->msg( 'exif-contact-value', $email, $url,
                                $street, $city, $region, $postal, $country,
                                $tel )->text();
                }
        }
+
+       /**
+        * Get a list of fields that are visible by default.
+        *
+        * @return array
+        * @since 1.23
+        */
+       public static function getVisibleFields() {
+               $fields = array();
+               $lines = explode( "\n", wfMessage( 'metadata-fields' )->inContentLanguage()->text() );
+               foreach ( $lines as $line ) {
+                       $matches = array();
+                       if ( preg_match( '/^\\*\s*(.*?)\s*$/', $line, $matches ) ) {
+                               $fields[] = $matches[1];
+                       }
+               }
+               $fields = array_map( 'strtolower', $fields );
+               return $fields;
+       }
+
+       /**
+        * Get an array of extended metadata. (See the imageinfo API for format.)
+        *
+        * @param File $file File to use
+        * @return array [<property name> => ['value' => <value>]], or [] on error
+        * @since 1.23
+        */
+       public function fetchExtendedMetadata( File $file ) {
+               global $wgMemc;
+
+               wfProfileIn( __METHOD__ );
+
+               // If revision deleted, exit immediately
+               if ( $file->isDeleted( File::DELETED_FILE ) ) {
+                       return array();
+               }
+
+               $cacheKey = wfMemcKey(
+                       'getExtendedMetadata',
+                       $this->getLanguage()->getCode(),
+                       (int) $this->singleLang,
+                       $file->getSha1()
+               );
+
+               $cachedValue = $wgMemc->get( $cacheKey );
+               if (
+                       $cachedValue
+                       && wfRunHooks( 'ValidateExtendedMetadataCache', array( $cachedValue['timestamp'], $file ) )
+               ) {
+                       $extendedMetadata = $cachedValue['data'];
+               } else {
+                       $maxCacheTime = ( $file instanceof ForeignAPIFile ) ? 60 * 60 * 12 : 60 * 60 * 24 * 30;
+                       $fileMetadata = $this->getExtendedMetadataFromFile( $file );
+                       $extendedMetadata = $this->getExtendedMetadataFromHook( $file, $fileMetadata, $maxCacheTime );
+                       if ( $this->singleLang ) {
+                               $this->resolveMultilangMetadata( $extendedMetadata );
+                       }
+                       // Make sure the metadata won't break the API when an XML format is used.
+                       // This is an API-specific function so it would be cleaner to call it from
+                       // outside fetchExtendedMetadata, but this way we don't need to redo the
+                       // computation on a cache hit.
+                       $this->sanitizeArrayForXml($extendedMetadata);
+                       $valueToCache = array( 'data' => $extendedMetadata, 'timestamp' => wfTimestampNow() );
+                       $wgMemc->set( $cacheKey, $valueToCache, $maxCacheTime );
+               }
+
+               wfProfileOut( __METHOD__ );
+               return $extendedMetadata;
+       }
+
+       /**
+        * Get file-based metadata in standardized format.
+        *
+        * Note that for a remote file, this might return metadata supplied by extensions.
+        *
+        * @param File $file File to use
+        * @return array [<property name> => ['value' => <value>]], or [] on error
+        * @since 1.23
+        */
+       protected function getExtendedMetadataFromFile( File $file ) {
+               // If this is a remote file accessed via an API request, we already
+               // have remote metadata so we just ignore any local one
+               if ( $file instanceof ForeignAPIFile ) {
+                       // in case of error we pretend no metadata - this will get cached. Might or might not be a good idea.
+                       return $file->getExtendedMetadata() ?: array();
+               }
+
+               wfProfileIn( __METHOD__ );
+
+               $uploadDate = wfTimestamp( TS_ISO_8601, $file->getTimestamp() );
+
+               $fileMetadata = array(
+                       // This is modification time, which is close to "upload" time.
+                       'DateTime' => array(
+                               'value' => $uploadDate,
+                               'source' => 'mediawiki-metadata',
+                       ),
+               );
+
+               $title = $file->getTitle();
+               if ( $title ) {
+                       $text = $title->getText();
+                       $pos = strrpos( $text, '.' );
+
+                       if ( $pos ) {
+                               $name = substr( $text, 0, $pos );
+                       } else {
+                               $name = $text;
+                       }
+
+                       $fileMetadata[ 'ObjectName' ] = array(
+                               'value' => $name,
+                               'source' => 'mediawiki-metadata',
+                       );
+               }
+
+               $common = $file->getCommonMetaArray();
+
+               if ( $common !== false ) {
+                       foreach ( $common as $key => $value ) {
+                               $fileMetadata[$key] = array(
+                                       'value' => $value,
+                                       'source' => 'file-metadata',
+                               );
+                       }
+               }
+
+               wfProfileOut( __METHOD__ );
+               return $fileMetadata;
+       }
+
+       /**
+        * Get additional metadata from hooks in standardized format.
+        *
+        * @param File $file File to use
+        * @param array $extendedMetadata
+        * @param int $maxCacheTime hook handlers might use this parameter to override cache time
+        *
+        * @return array [<property name> => ['value' => <value>]], or [] on error
+        * @since 1.23
+        */
+       protected function getExtendedMetadataFromHook( File $file, array $extendedMetadata, &$maxCacheTime ) {
+               wfProfileIn( __METHOD__ );
+
+               wfRunHooks( 'GetExtendedMetadata', array(
+                       &$extendedMetadata,
+                       $file,
+                       $this->getContext(),
+                       $this->singleLang,
+                       &$maxCacheTime
+               ) );
+
+               $visible = array_flip( self::getVisibleFields() );
+               foreach ( $extendedMetadata as $key => $value ) {
+                       if ( !isset( $visible[ strtolower( $key ) ] ) ) {
+                               $extendedMetadata[$key]['hidden'] = '';
+                       }
+               }
+
+               wfProfileOut( __METHOD__ );
+               return $extendedMetadata;
+       }
+
+       /**
+        * Turns an XMP-style multilang array into a single value.
+        * If the value is not a multilang array, it is returned unchanged.
+        * See mediawiki.org/wiki/Manual:File_metadata_handling#Multi-language_array_format
+        * @param mixed $value
+        * @return mixed value in best language, null if there were no languages at all
+        * @since 1.23
+        */
+       protected function resolveMultilangValue( $value )
+       {
+               if (
+                       !is_array( $value )
+                       || !isset( $value['_type'] )
+                       || $value['_type'] != 'lang'
+               ) {
+                       return $value; // do nothing if not a multilang array
+               }
+
+               // choose the language best matching user or site settings
+               $priorityLanguages = $this->getPriorityLanguages();
+               foreach( $priorityLanguages as $lang ) {
+                       if ( isset( $value[$lang] ) ) {
+                               return $value[$lang];
+                       }
+               }
+
+               // otherwise go with the default language, if set
+               if ( isset( $value['x-default'] ) ) {
+                       return $value['x-default'];
+               }
+
+               // otherwise just return any one language
+               unset($value['_type']);
+               if (!empty($value)) {
+                       return reset($value);
+               }
+
+               // this should not happen; signal error
+               return null;
+       }
+
+       /**
+        * Takes an array returned by the getExtendedMetadata* functions,
+        * and resolves multi-language values in it.
+        * @param array $metadata
+        * @since 1.23
+        */
+       protected function resolveMultilangMetadata( &$metadata ) {
+               if ( !is_array( $metadata ) ) {
+                       return;
+               }
+               foreach ( $metadata as &$field ) {
+                       if ( isset( $field['value'] ) ) {
+                               $field['value'] = $this->resolveMultilangValue( $field['value'] );
+                       }
+               }
+       }
+
+       /**
+        * Makes sure the given array is a valid API response fragment
+        * (can be transformed into XML)
+        * @param array $arr
+        */
+       protected function sanitizeArrayForXml( &$arr ) {
+               if ( !is_array( $arr ) ) {
+                       return;
+               }
+
+               $counter = 1;
+               foreach ( $arr as $key => &$value ) {
+                       $sanitizedKey = $this->sanitizeKeyForXml( $key );
+                       if ( $sanitizedKey !== $key ) {
+                               if ( isset( $arr[$sanitizedKey] ) ) {
+                                       // Make the sanitized keys hopefully unique.
+                                       // To make it definitely unique would be too much effort, given that
+                                       // sanitizing is only needed for misformatted metadata anyway, but
+                                       // this at least covers the case when $arr is numeric.
+                                       $sanitizedKey .= $counter;
+                                       ++$counter;
+                               }
+                               $arr[$sanitizedKey] = $arr[$key];
+                               unset( $arr[$key] );
+                       }
+                       if ( is_array( $value ) ) {
+                               $this->sanitizeArrayForXml( $value );
+                       }
+               }
+       }
+
+       /**
+        * Turns a string into a valid XML identifier.
+        * Used to ensure that keys of an associative array in the
+        * API response do not break the XML formatter.
+        * @param string $key
+        * @return string
+        * @since 1.23
+        */
+       protected function sanitizeKeyForXml( $key ) {
+               // drop all characters which are not valid in an XML tag name
+               // a bunch of non-ASCII letters would be valid but probably won't
+               // be used so we take the easy way
+               $key = preg_replace( '/[^a-zA-z0-9_:.-]/', '', $key );
+               // drop characters which are invalid at the first position
+               $key = preg_replace( '/^[\d-.]+/', '', $key );
+
+               if ( $key == '' ) {
+                       $key = '_';
+               }
+
+               // special case for an internal keyword
+               if ( $key == '_element' ) {
+                       $key = 'element';
+               }
+
+               return $key;
+       }
+
+       /**
+        * Returns a list of languages (first is best) to use when formatting multilang fields,
+        * based on user and site preferences.
+        * @return array
+        * @since 1.23
+        */
+       protected function getPriorityLanguages()
+       {
+               $priorityLanguages = Language::getFallbacksIncludingSiteLanguage( $this->getLanguage()->getCode() );
+               $priorityLanguages = array_merge( (array) $this->getLanguage()->getCode(), $priorityLanguages[0], $priorityLanguages[1] );
+               return $priorityLanguages;
+       }
 }
 
 /** For compatability with old FormatExif class
index 608fb25..19635da 100644 (file)
@@ -47,20 +47,31 @@ class GIFHandler extends BitmapHandler {
         * @return array|bool
         */
        function formatMetadata( $image ) {
+               $meta = $this->getCommonMetaArray( $image );
+               if ( count( $meta ) === 0 ) {
+                       return false;
+               }
+
+               return $this->formatMetadataHelper( $meta );
+       }
+
+       /**
+        * Return the standard metadata elements for #filemetadata parser func.
+        * @param File $image
+        * @return array|bool
+        */
+       public function getCommonMetaArray( File $image ) {
                $meta = $image->getMetadata();
 
                if ( !$meta ) {
-                       return false;
+                       return array();
                }
                $meta = unserialize( $meta );
-               if ( !isset( $meta['metadata'] ) || count( $meta['metadata'] ) <= 1 ) {
-                       return false;
-               }
-
-               if ( isset( $meta['metadata']['_MW_GIF_VERSION'] ) ) {
-                       unset( $meta['metadata']['_MW_GIF_VERSION'] );
+               if ( !isset( $meta['metadata'] ) ) {
+                       return array();
                }
-               return $this->formatMetadataHelper( $meta['metadata'] );
+               unset( $meta['metadata']['_MW_GIF_VERSION'] );
+               return $meta['metadata'];
        }
 
        /**
old mode 100644 (file)
new mode 100755 (executable)
index 779e23c..ddb8efd
@@ -187,6 +187,43 @@ abstract class MediaHandler {
                return self::METADATA_GOOD;
        }
 
+       /**
+        * Get an array of standard (FormatMetadata type) metadata values.
+        *
+        * The returned data is largely the same as that from getMetadata(),
+        * but formatted in a standard, stable, handler-independent way.
+        * The idea being that some values like ImageDescription or Artist
+        * are universal and should be retrievable in a handler generic way.
+        *
+        * The specific properties are the type of properties that can be
+        * handled by the FormatMetadata class. These values are exposed to the
+        * user via the filemetadata parser function.
+        *
+        * Details of the response format of this function can be found at
+        * https://www.mediawiki.org/wiki/Manual:File_metadata_handling
+        * tl/dr: the response is an associative array of
+        * properties keyed by name, but the value can be complex. You probably
+        * want to call one of the FormatMetadata::flatten* functions on the
+        * property values before using them, or call
+        * FormatMetadata::getFormattedData() on the full response array, which
+        * transforms all values into prettified, human-readable text.
+        *
+        * Subclasses overriding this function must return a value which is a
+        * valid API response fragment (all associative array keys are valid
+        * XML tagnames).
+        *
+        * Note, if the file simply has no metadata, but the handler supports
+        * this interface, it should return an empty array, not false.
+        *
+        * @param File $file
+        *
+        * @return Array or false if interface not supported
+        * @since 1.23
+        */
+       public function getCommonMetaArray( File $file ) {
+               return false;
+       }
+
        /**
         * Get a MediaTransformOutput object representing an alternate of the transformed
         * output which will call an intermediary thumbnail assist script.
@@ -436,16 +473,7 @@ abstract class MediaHandler {
         * @access protected
         */
        function visibleMetadataFields() {
-               $fields = array();
-               $lines = explode( "\n", wfMessage( 'metadata-fields' )->inContentLanguage()->text() );
-               foreach ( $lines as $line ) {
-                       $matches = array();
-                       if ( preg_match( '/^\\*\s*(.*?)\s*$/', $line, $matches ) ) {
-                               $fields[] = $matches[1];
-                       }
-               }
-               $fields = array_map( 'strtolower', $fields );
-               return $fields;
+               return FormatMetadata::getVisibleFields();
        }
 
        /**
index 98f1386..d2c17ef 100644 (file)
@@ -52,20 +52,32 @@ class PNGHandler extends BitmapHandler {
         * @return array|bool
         */
        function formatMetadata( $image ) {
+               $meta = $this->getCommonMetaArray( $image );
+               if ( count( $meta ) === 0 ) {
+                       return false;
+               }
+
+               return $this->formatMetadataHelper( $meta );
+       }
+
+       /**
+        * Get a file type independent array of metadata.
+        *
+        * @param $image File
+        * @return array The metadata array
+        */
+       public function getCommonMetaArray( File $image ) {
                $meta = $image->getMetadata();
 
                if ( !$meta ) {
-                       return false;
+                       return array();
                }
                $meta = unserialize( $meta );
-               if ( !isset( $meta['metadata'] ) || count( $meta['metadata'] ) <= 1 ) {
-                       return false;
-               }
-
-               if ( isset( $meta['metadata']['_MW_PNG_VERSION'] ) ) {
-                       unset( $meta['metadata']['_MW_PNG_VERSION'] );
+               if ( !isset( $meta['metadata'] ) ) {
+                       return array();
                }
-               return $this->formatMetadataHelper( $meta['metadata'] );
+               unset( $meta['metadata']['_MW_PNG_VERSION'] );
+               return $meta['metadata'];
        }
 
        /**
index 72a9696..d6f8483 100644 (file)
 class SvgHandler extends ImageHandler {
        const SVG_METADATA_VERSION = 2;
 
+       /**
+        * A list of metadata tags that can be converted
+        * to the commonly used exif tags. This allows messages
+        * to be reused, and consistent tag names for {{#formatmetadata:..}}
+        */
+       private static $metaConversion = array(
+               'originalwidth' => 'ImageWidth',
+               'originalheight' => 'ImageLength',
+               'description' => 'ImageDescription',
+               'title' => 'ObjectName',
+       );
+
        function isEnabled() {
                global $wgSVGConverters, $wgSVGConverter;
                if ( !isset( $wgSVGConverters[$wgSVGConverter] ) ) {
@@ -340,19 +352,11 @@ class SvgHandler extends ImageHandler {
                // Sort fields into visible and collapsed
                $visibleFields = $this->visibleMetadataFields();
 
-               // Rename fields to be compatible with exif, so that
-               // the labels for these fields work and reuse existing messages.
-               $conversion = array(
-                       'originalwidth' => 'imagewidth',
-                       'originalheight' => 'imagelength',
-                       'description' => 'imagedescription',
-                       'title' => 'objectname',
-               );
                $showMeta = false;
                foreach ( $metadata as $name => $value ) {
                        $tag = strtolower( $name );
-                       if ( isset( $conversion[$tag] ) ) {
-                               $tag = $conversion[$tag];
+                       if ( isset( self::$metaConversion[$tag] ) ) {
+                               $tag = strtolower( self::$metaConversion[$tag] );
                        } else {
                                // Do not output other metadata not in list
                                continue;
@@ -368,7 +372,6 @@ class SvgHandler extends ImageHandler {
                return $showMeta ? $result : false;
        }
 
-
        /**
         * @param string $name Parameter name
         * @param $string $value Parameter value
@@ -431,4 +434,29 @@ class SvgHandler extends ImageHandler {
                        'lang' => $params['lang'],
                );
        }
+
+       public function getCommonMetaArray( File $file ) {
+               $metadata = $file->getMetadata();
+               if ( !$metadata ) {
+                       return array();
+               }
+               $metadata = $this->unpackMetadata( $metadata );
+               if ( !$metadata || isset( $metadata['error'] ) ) {
+                       return array();
+               }
+               $stdMetadata = array();
+               foreach ( $metadata as $name => $value ) {
+                       $tag = strtolower( $name );
+                       if ( $tag === 'originalwidth' || $tag === 'originalheight' ) {
+                               // Skip these. In the exif metadata stuff, it is assumed these
+                               // are measured in px, which is not the case here.
+                               continue;
+                       }
+                       if ( isset( self::$metaConversion[$tag] ) ) {
+                               $tag = self::$metaConversion[$tag];
+                               $stdMetadata[$tag] = $value;
+                       }
+               }
+               return $stdMetadata;
+       }
 }
index 5118366..ad913b1 100644 (file)
@@ -39,6 +39,7 @@ if( defined( 'PRETTY_UTF8' ) ) {
 } else {
        /**
         * @ignore
+        * @param string $string
         * @return string
         */
        function pretty( $string ) {
index e1dc42e..135e0e8 100644 (file)
@@ -273,38 +273,6 @@ class RedisBagOStuff extends BagOStuff {
                return $result;
        }
 
-       /**
-        * Non-atomic implementation of incr().
-        *
-        * Probably all callers actually want incr() to atomically initialise
-        * values to zero if they don't exist, as provided by the Redis INCR
-        * command. But we are constrained by the memcached-like interface to
-        * return null in that case. Once the key exists, further increments are
-        * atomic.
-        */
-       public function incr( $key, $value = 1 ) {
-               wfProfileIn( __METHOD__ );
-               list( $server, $conn ) = $this->getConnection( $key );
-               if ( !$conn ) {
-                       wfProfileOut( __METHOD__ );
-                       return false;
-               }
-               if ( !$conn->exists( $key ) ) {
-                       wfProfileOut( __METHOD__ );
-                       return null;
-               }
-               try {
-                       $result = $conn->incrBy( $key, $value );
-               } catch ( RedisException $e ) {
-                       $result = false;
-                       $this->handleException( $server, $conn, $e );
-               }
-
-               $this->logRequest( 'incr', $key, $server, $result );
-               wfProfileOut( __METHOD__ );
-               return $result;
-       }
-
        /**
         * Get a Redis object with a connection suitable for fetching the specified key
         * @return Array (server, RedisConnRef) or (false, false)
index 70a94fe..44c7458 100644 (file)
@@ -100,6 +100,15 @@ class CoreParserFunctions {
                $parser->setFunctionHook( 'subjectpagenamee', array( __CLASS__, 'subjectpagenamee' ), SFH_NO_HASH );
                $parser->setFunctionHook( 'tag',              array( __CLASS__, 'tagObj'           ), SFH_OBJECT_ARGS );
                $parser->setFunctionHook( 'formatdate',       array( __CLASS__, 'formatDate'       ) );
+               $parser->setFunctionHook( 'pageid',           array( __CLASS__, 'pageid'           ), SFH_NO_HASH );
+               $parser->setFunctionHook( 'revisionid',       array( __CLASS__, 'revisionid'       ), SFH_NO_HASH );
+               $parser->setFunctionHook( 'revisionday',      array( __CLASS__, 'revisionday'      ), SFH_NO_HASH );
+               $parser->setFunctionHook( 'revisionday2',     array( __CLASS__, 'revisionday2'     ), SFH_NO_HASH );
+               $parser->setFunctionHook( 'revisionmonth',    array( __CLASS__, 'revisionmonth'    ), SFH_NO_HASH );
+               $parser->setFunctionHook( 'revisionmonth1',   array( __CLASS__, 'revisionmonth1'   ), SFH_NO_HASH );
+               $parser->setFunctionHook( 'revisionyear',     array( __CLASS__, 'revisionyear'     ), SFH_NO_HASH );
+               $parser->setFunctionHook( 'revisiontimestamp', array( __CLASS__, 'revisiontimestamp' ), SFH_NO_HASH );
+               $parser->setFunctionHook( 'revisionuser',     array( __CLASS__, 'revisionuser'     ), SFH_NO_HASH );
 
                if ( $wgAllowDisplayTitle ) {
                        $parser->setFunctionHook( 'displaytitle', array( __CLASS__, 'displaytitle' ), SFH_NO_HASH );
@@ -139,7 +148,7 @@ class CoreParserFunctions {
                $pref = $parser->getOptions()->getDateFormat();
 
                // Specify a different default date format other than the the normal default
-               // iff the user has 'default' for their setting
+               // if the user has 'default' for their setting
                if ( $pref == 'default' && $defaultPref ) {
                        $pref = $defaultPref;
                }
@@ -707,29 +716,15 @@ class CoreParserFunctions {
         * @return string
         */
        static function pagesize( $parser, $page = '', $raw = null ) {
-               static $cache = array();
                $title = Title::newFromText( $page );
 
                if ( !is_object( $title ) ) {
-                       $cache[$page] = 0;
                        return self::formatRaw( 0, $raw );
                }
 
-               # Normalize name for cache
-               $page = $title->getPrefixedText();
-
-               $length = 0;
-               if ( isset( $cache[$page] ) ) {
-                       $length = $cache[$page];
-               } elseif ( $parser->incrementExpensiveFunctionCount() ) {
-                       $rev = Revision::newFromTitle( $title, false, Revision::READ_NORMAL );
-                       $pageID = $rev ? $rev->getPage() : 0;
-                       $revID = $rev ? $rev->getId() : 0;
-                       $length = $cache[$page] = $rev ? $rev->getSize() : 0;
-
-                       // Register dependency in templatelinks
-                       $parser->mOutput->addTemplate( $title, $pageID, $revID );
-               }
+               // fetch revision from cache/database and return the value
+               $rev = self::getCachedRevisionObject( $parser, $title );
+               $length = $rev ? $rev->getSize() : 0;
                return self::formatRaw( $length, $raw );
        }
 
@@ -949,4 +944,204 @@ class CoreParserFunctions {
                );
                return $parser->extensionSubstitution( $params, $frame );
        }
+
+       /**
+        * Fetched the current revision of the given title and return this.
+        * Will increment the expensive function count and
+        * add a template link to get the value refreshed on changes.
+        * For a given title, which is equal to the current parser title,
+        * the revision object from the parser is used, when that is the current one
+        *
+        * @param $parser Parser
+        * @param $title Title
+        * @return Revision
+        * @since 1.23
+        */
+       private static function getCachedRevisionObject( $parser, $title = null ) {
+               static $cache = array();
+
+               if ( is_null( $title ) ) {
+                       return null;
+               }
+
+               // Use the revision from the parser itself, when param is the current page
+               // and the revision is the current one
+               if ( $title->equals( $parser->getTitle() ) ) {
+                       $parserRev = $parser->getRevisionObject();
+                       if ( $parserRev && $parserRev->isCurrent() ) {
+                               // force reparse after edit with vary-revision flag
+                               $parser->getOutput()->setFlag( 'vary-revision' );
+                               wfDebug( __METHOD__ . ": use current revision from parser, setting vary-revision...\n" );
+                               return $parserRev;
+                       }
+               }
+
+               // Normalize name for cache
+               $page = $title->getPrefixedDBkey();
+
+               if ( array_key_exists( $page, $cache ) ) { // cache contains null values
+                       return $cache[$page];
+               }
+               if ( $parser->incrementExpensiveFunctionCount() ) {
+                       $rev = Revision::newFromTitle( $title, false, Revision::READ_NORMAL );
+                       $pageID = $rev ? $rev->getPage() : 0;
+                       $revID = $rev ? $rev->getId() : 0;
+                       $cache[$page] = $rev; // maybe null
+
+                       // Register dependency in templatelinks
+                       $parser->getOutput()->addTemplate( $title, $pageID, $revID );
+
+                       return $rev;
+               }
+               $cache[$page] = null;
+               return null;
+       }
+
+       /**
+        * Get the pageid of a specified page
+        * @param $parser Parser
+        * @param $title string Title to get the pageid from
+        * @since 1.23
+        */
+       public static function pageid( $parser, $title = null ) {
+               $t = Title::newFromText( $title );
+               if ( is_null( $t ) ) {
+                       return '';
+               }
+               // Use title from parser to have correct pageid after edit
+               if ( $t->equals( $parser->getTitle() ) ) {
+                       $t = $parser->getTitle();
+               }
+               // fetch pageid from cache/database and return the value
+               $pageid = $t->getArticleID();
+               return $pageid ? $pageid : '';
+       }
+
+       /**
+        * Get the id from the last revision of a specified page.
+        * @param $parser Parser
+        * @param $title string Title to get the id from
+        * @since 1.23
+        */
+       public static function revisionid( $parser, $title = null ) {
+               $t = Title::newFromText( $title );
+               if ( is_null( $t ) ) {
+                       return '';
+               }
+               // fetch revision from cache/database and return the value
+               $rev = self::getCachedRevisionObject( $parser, $t );
+               return $rev ? $rev->getId() : '';
+       }
+
+       /**
+        * Get the day from the last revision of a specified page.
+        * @param $parser Parser
+        * @param $title string Title to get the day from
+        * @since 1.23
+        */
+       public static function revisionday( $parser, $title = null ) {
+               $t = Title::newFromText( $title );
+               if ( is_null( $t ) ) {
+                       return '';
+               }
+               // fetch revision from cache/database and return the value
+               $rev = self::getCachedRevisionObject( $parser, $t );
+               return $rev ? MWTimestamp::getLocalInstance( $rev->getTimestamp() )->format( 'j' ) : '';
+       }
+
+       /**
+        * Get the day with leading zeros from the last revision of a specified page.
+        * @param $parser Parser
+        * @param $title string Title to get the day from
+        * @since 1.23
+        */
+       public static function revisionday2( $parser, $title = null ) {
+               $t = Title::newFromText( $title );
+               if ( is_null( $t ) ) {
+                       return '';
+               }
+               // fetch revision from cache/database and return the value
+               $rev = self::getCachedRevisionObject( $parser, $t );
+               return $rev ? MWTimestamp::getLocalInstance( $rev->getTimestamp() )->format( 'd' ) : '';
+       }
+
+       /**
+        * Get the month with leading zeros from the last revision of a specified page.
+        * @param $parser Parser
+        * @param $title string Title to get the month from
+        * @since 1.23
+        */
+       public static function revisionmonth( $parser, $title = null ) {
+               $t = Title::newFromText( $title );
+               if ( is_null( $t ) ) {
+                       return '';
+               }
+               // fetch revision from cache/database and return the value
+               $rev = self::getCachedRevisionObject( $parser, $t );
+               return $rev ? MWTimestamp::getLocalInstance( $rev->getTimestamp() )->format( 'm' ) : '';
+       }
+
+       /**
+        * Get the month from the last revision of a specified page.
+        * @param $parser Parser
+        * @param $title string Title to get the month from
+        * @since 1.23
+        */
+       public static function revisionmonth1( $parser, $title = null ) {
+               $t = Title::newFromText( $title );
+               if ( is_null( $t ) ) {
+                       return '';
+               }
+               // fetch revision from cache/database and return the value
+               $rev = self::getCachedRevisionObject( $parser, $t );
+               return $rev ? MWTimestamp::getLocalInstance( $rev->getTimestamp() )->format( 'n' ) : '';
+       }
+
+       /**
+        * Get the year from the last revision of a specified page.
+        * @param $parser Parser
+        * @param $title string Title to get the year from
+        * @since 1.23
+        */
+       public static function revisionyear( $parser, $title = null ) {
+               $t = Title::newFromText( $title );
+               if ( is_null( $t ) ) {
+                       return '';
+               }
+               // fetch revision from cache/database and return the value
+               $rev = self::getCachedRevisionObject( $parser, $t );
+               return $rev ? MWTimestamp::getLocalInstance( $rev->getTimestamp() )->format( 'Y' ) : '';
+       }
+
+       /**
+        * Get the timestamp from the last revision of a specified page.
+        * @param $parser Parser
+        * @param $title string Title to get the timestamp from
+        * @since 1.23
+        */
+       public static function revisiontimestamp( $parser, $title = null ) {
+               $t = Title::newFromText( $title );
+               if ( is_null( $t ) ) {
+                       return '';
+               }
+               // fetch revision from cache/database and return the value
+               $rev = self::getCachedRevisionObject( $parser, $t );
+               return $rev ? MWTimestamp::getLocalInstance( $rev->getTimestamp() )->format( 'YmdHis' ) : '';
+       }
+
+       /**
+        * Get the user from the last revision of a specified page.
+        * @param $parser Parser
+        * @param $title string Title to get the user from
+        * @since 1.23
+        */
+       public static function revisionuser( $parser, $title = null ) {
+               $t = Title::newFromText( $title );
+               if ( is_null( $t ) ) {
+                       return '';
+               }
+               // fetch revision from cache/database and return the value
+               $rev = self::getCachedRevisionObject( $parser, $t );
+               return $rev ? $rev->getUserText() : '';
+       }
 }
index 6fc457d..b629776 100644 (file)
@@ -251,9 +251,8 @@ class LinkHolderArray {
        }
 
        /**
-        * @todo FIXME: Update documentation. makeLinkObj() is deprecated.
         * Replace <!--LINK--> link placeholders with actual links, in the buffer
-        * Placeholders created in Skin::makeLinkObj()
+        *
         * @return array of link CSS classes, indexed by PDBK.
         */
        function replace( &$text ) {
@@ -377,6 +376,10 @@ class LinkHolderArray {
                                $key = "$ns:$index";
                                $searchkey = "<!--LINK $key-->";
                                $displayText = $entry['text'];
+                               if ( isset( $entry['selflink'] ) ) {
+                                       $replacePairs[$searchkey] = Linker::makeSelfLinkObj( $title, $displayText, $query );
+                                       continue;
+                               }
                                if ( $displayText === '' ) {
                                        $displayText = null;
                                }
@@ -455,20 +458,17 @@ class LinkHolderArray {
                // single string to all variants. This would improve parser's performance
                // significantly.
                foreach ( $this->internals as $ns => $entries ) {
+                       if ( $ns == NS_SPECIAL ) {
+                               continue;
+                       }
                        foreach ( $entries as $index => $entry ) {
                                $pdbk = $entry['pdbk'];
                                // we only deal with new links (in its first query)
                                if ( !isset( $colours[$pdbk] ) || $colours[$pdbk] === 'new' ) {
-                                       $title = $entry['title'];
-                                       $titleText = $title->getText();
-                                       $titlesAttrs[] = array(
-                                               'ns' => $ns,
-                                               'key' => "$ns:$index",
-                                               'titleText' => $titleText,
-                                       );
+                                       $titlesAttrs[] = array( $index, $entry['title'] );
                                        // separate titles with \0 because it would never appears
                                        // in a valid title
-                                       $titlesToBeConverted .= $titleText . "\0";
+                                       $titlesToBeConverted .= $entry['title']->getText() . "\0";
                                }
                        }
                }
@@ -479,19 +479,35 @@ class LinkHolderArray {
                foreach ( $titlesAllVariants as &$titlesVariant ) {
                        $titlesVariant = explode( "\0", $titlesVariant );
                }
-               $l = count( $titlesAttrs );
+
                // Then add variants of links to link batch
-               for ( $i = 0; $i < $l; $i ++ ) {
+               $parentTitle = $this->parent->getTitle();
+               foreach ( $titlesAttrs as $i => $attrs ) {
+                       list( $index, $title ) = $attrs;
+                       $ns = $title->getNamespace();
+                       $text = $title->getText();
+
                        foreach ( $allVariantsName as $variantName ) {
                                $textVariant = $titlesAllVariants[$variantName][$i];
-                               if ( $textVariant != $titlesAttrs[$i]['titleText'] ) {
-                                       $variantTitle = Title::makeTitle( $titlesAttrs[$i]['ns'], $textVariant );
-                                       if ( is_null( $variantTitle ) ) {
-                                               continue;
-                                       }
-                                       $linkBatch->addObj( $variantTitle );
-                                       $variantMap[$variantTitle->getPrefixedDBkey()][] = $titlesAttrs[$i]['key'];
+                               if ( $textVariant === $text ) {
+                                       continue;
+                               }
+
+                               $variantTitle = Title::makeTitle( $ns, $textVariant );
+                               if ( is_null( $variantTitle ) ) {
+                                       continue;
+                               }
+
+                               // Self-link checking for mixed/different variant titles. At this point, we
+                               // already know the exact title does not exist, so the link cannot be to a
+                               // variant of the current title that exists as a separate page.
+                               if ( $variantTitle->equals( $parentTitle ) && $title->getFragment() === '' ) {
+                                       $this->internals[$ns][$index]['selflink'] = true;
+                                       continue 2;
                                }
+
+                               $linkBatch->addObj( $variantTitle );
+                               $variantMap[$variantTitle->getPrefixedDBkey()][] = "$ns:$index";
                        }
                }
 
index 221a630..848a1a0 100644 (file)
@@ -115,6 +115,10 @@ class Parser {
        # Marker Suffix needs to be accessible staticly.
        const MARKER_SUFFIX = "-QINU\x7f";
 
+       # Markers used for wrapping the table of contents
+       const TOC_START = '<mw:toc>';
+       const TOC_END = '</mw:toc>';
+
        # Persistent:
        var $mTagHooks = array();
        var $mTransparentTagHooks = array();
@@ -1859,7 +1863,9 @@ class Parser {
                if ( $useLinkPrefixExtension ) {
                        # Match the end of a line for a word that's not followed by whitespace,
                        # e.g. in the case of 'The Arab al[[Razi]]', 'al' will be matched
-                       $e2 = wfMessage( 'linkprefix' )->inContentLanguage()->text();
+                       global $wgContLang;
+                       $charset = $wgContLang->linkPrefixCharset();
+                       $e2 = "/^((?>.*[^$charset]|))(.+)$/sDu";
                }
 
                if ( is_null( $this->mTitle ) ) {
@@ -2113,16 +2119,12 @@ class Parser {
                                }
                        }
 
-                       # Self-link checking
-                       if ( $nt->getFragment() === '' && $ns != NS_SPECIAL ) {
-                               if ( $nt->equals( $this->mTitle ) || ( !$nt->isKnown() && in_array(
-                                       $this->mTitle->getPrefixedText(),
-                                       $this->getConverterLanguage()->autoConvertToAllVariants( $nt->getPrefixedText() ),
-                                       true
-                               ) ) ) {
-                                       $s .= $prefix . Linker::makeSelfLinkObj( $nt, $text, '', $trail );
-                                       continue;
-                               }
+                       # Self-link checking. For some languages, variants of the title are checked in
+                       # LinkHolderArray::doVariants() to allow batching the existence checks necessary
+                       # for linking to a different variant.
+                       if ( $ns != NS_SPECIAL && $nt->equals( $this->mTitle ) && $nt->getFragment() === '' ) {
+                               $s .= $prefix . Linker::makeSelfLinkObj( $nt, $text, '', $trail );
+                               continue;
                        }
 
                        # NS_MEDIA is a pseudo-namespace for linking directly to a file
@@ -2281,13 +2283,13 @@ class Parser {
                $result = $this->closeParagraph();
 
                if ( '*' === $char ) {
-                       $result .= '<ul><li>';
+                       $result .= "<ul>\n<li>";
                } elseif ( '#' === $char ) {
-                       $result .= '<ol><li>';
+                       $result .= "<ol>\n<li>";
                } elseif ( ':' === $char ) {
-                       $result .= '<dl><dd>';
+                       $result .= "<dl>\n<dd>";
                } elseif ( ';' === $char ) {
-                       $result .= '<dl><dt>';
+                       $result .= "<dl>\n<dt>";
                        $this->mDTopen = true;
                } else {
                        $result = '<!-- ERR 1 -->';
@@ -2305,11 +2307,11 @@ class Parser {
         */
        function nextItem( $char ) {
                if ( '*' === $char || '#' === $char ) {
-                       return '</li><li>';
+                       return "</li>\n<li>";
                } elseif ( ':' === $char || ';' === $char ) {
-                       $close = '</dd>';
+                       $close = "</dd>\n";
                        if ( $this->mDTopen ) {
-                               $close = '</dt>';
+                               $close = "</dt>\n";
                        }
                        if ( ';' === $char ) {
                                $this->mDTopen = true;
@@ -2331,15 +2333,15 @@ class Parser {
         */
        function closeList( $char ) {
                if ( '*' === $char ) {
-                       $text = '</li></ul>';
+                       $text = "</li>\n</ul>";
                } elseif ( '#' === $char ) {
-                       $text = '</li></ol>';
+                       $text = "</li>\n</ol>";
                } elseif ( ':' === $char ) {
                        if ( $this->mDTopen ) {
                                $this->mDTopen = false;
-                               $text = '</dt></dl>';
+                               $text = "</dt>\n</dl>";
                        } else {
-                               $text = '</dd></dl>';
+                               $text = "</dd>\n</dl>";
                        }
                } else {
                        return '<!-- ERR 3 -->';
@@ -2466,7 +2468,7 @@ class Parser {
                                $openmatch = preg_match( '/(?:<table|<h1|<h2|<h3|<h4|<h5|<h6|<pre|<tr|<p|<ul|<ol|<dl|<li|<\\/tr|<\\/td|<\\/th)/iS', $t );
                                $closematch = preg_match(
                                        '/(?:<\\/table|<\\/h1|<\\/h2|<\\/h3|<\\/h4|<\\/h5|<\\/h6|' .
-                                       '<td|<th|<\\/?blockquote|<\\/?div|<hr|<\\/pre|<\\/p|' . $this->mUniqPrefix . '-pre|<\\/li|<\\/ul|<\\/ol|<\\/dl|<\\/?center)/iS', $t );
+                                       '<td|<th|<\\/?blockquote|<\\/?div|<hr|<\\/pre|<\\/p|<\\/mw:|' . $this->mUniqPrefix . '-pre|<\\/li|<\\/ul|<\\/ol|<\\/dl|<\\/?center)/iS', $t );
                                if ( $openmatch or $closematch ) {
                                        $paragraphStack = false;
                                        # TODO bug 5718: paragraph closed
@@ -4520,6 +4522,7 @@ class Parser {
                        }
                        $toc = Linker::tocList( $toc, $this->mOptions->getUserLangObj() );
                        $this->mOutput->setTOCHTML( $toc );
+                       $toc = self::TOC_START . $toc . self::TOC_END;
                }
 
                if ( $isMain ) {
@@ -5759,8 +5762,9 @@ class Parser {
         * Get the revision object for $this->mRevisionId
         *
         * @return Revision|null either a Revision object or null
+        * @since 1.23 (public since 1.23)
         */
-       protected function getRevisionObject() {
+       public function getRevisionObject() {
                if ( !is_null( $this->mRevisionObject ) ) {
                        return $this->mRevisionObject;
                }
index 9519de9..502f0fd 100644 (file)
@@ -47,7 +47,8 @@ class ParserOutput extends CacheTime {
                $mEditSectionTokens = false,  # prefix/suffix markers if edit sections were output as tokens
                $mProperties = array(),       # Name/value pairs to be cached in the DB
                $mTOCHTML = '',               # HTML of the TOC
-               $mTimestamp;                  # Timestamp of the revision
+               $mTimestamp,                  # Timestamp of the revision
+               $mTOCEnabled = true;          # Whether TOC should be shown, can't override __NOTOC__
                private $mIndexPolicy = '';       # 'index' or 'noindex'?  Any other value will result in no change.
                private $mAccessedOptions = array(); # List of ParserOptions (stored in the keys)
                private $mSecondaryDataUpdates = array(); # List of DataUpdate, used to save info from the page somewhere else.
@@ -68,11 +69,27 @@ class ParserOutput extends CacheTime {
        }
 
        function getText() {
+               wfProfileIn( __METHOD__ );
+               $text = $this->mText;
                if ( $this->mEditSectionTokens ) {
-                       return preg_replace_callback( ParserOutput::EDITSECTION_REGEX,
-                               array( &$this, 'replaceEditSectionLinksCallback' ), $this->mText );
+                       $text = preg_replace_callback( ParserOutput::EDITSECTION_REGEX,
+                               array( &$this, 'replaceEditSectionLinksCallback' ), $text );
+               } else {
+                       $text = preg_replace( ParserOutput::EDITSECTION_REGEX, '', $text );
+               }
+
+               // If you have an old cached version of this class - sorry, you can't disable the TOC
+               if ( isset( $this->mTOCEnabled ) && $this->mTOCEnabled ) {
+                       $text = str_replace( array( Parser::TOC_START, Parser::TOC_END ), '', $text );
+               } else {
+                       $text = preg_replace(
+                               '#'. preg_quote( Parser::TOC_START ) . '.*?' . preg_quote( Parser::TOC_END ) . '#s',
+                               '',
+                               $text
+                       );
                }
-               return preg_replace( ParserOutput::EDITSECTION_REGEX, '', $this->mText );
+               wfProfileOut( __METHOD__ );
+               return $text;
        }
 
        /**
@@ -123,6 +140,7 @@ class ParserOutput extends CacheTime {
        function getTOCHTML()                { return $this->mTOCHTML; }
        function getTimestamp()              { return $this->mTimestamp; }
        function getLimitReportData()        { return $this->mLimitReportData; }
+       function getTOCEnabled()             { return $this->mTOCEnabled; }
 
        function setText( $text )            { return wfSetVar( $this->mText, $text ); }
        function setLanguageLinks( $ll )     { return wfSetVar( $this->mLanguageLinks, $ll ); }
@@ -134,6 +152,7 @@ class ParserOutput extends CacheTime {
        function setIndexPolicy( $policy )   { return wfSetVar( $this->mIndexPolicy, $policy ); }
        function setTOCHTML( $tochtml )      { return wfSetVar( $this->mTOCHTML, $tochtml ); }
        function setTimestamp( $timestamp )  { return wfSetVar( $this->mTimestamp, $timestamp ); }
+       function setTOCEnabled( $flag )      { return wfSetVar( $this->mTOCEnabled, $flag ); }
 
        function addCategory( $c, $sort )    { $this->mCategories[$c] = $sort; }
        function addLanguageLink( $t )       { $this->mLanguageLinks[] = $t; }
index 0625140..32b16aa 100644 (file)
@@ -61,7 +61,10 @@ class MWTidyWrapper {
 
                // Replace <mw:editsection> elements with placeholders
                $wrappedtext = preg_replace_callback( ParserOutput::EDITSECTION_REGEX,
-                       array( &$this, 'replaceEditSectionLinksCallback' ), $text );
+                       array( &$this, 'replaceCallback' ), $text );
+               // ...and <mw:toc> markers
+               $wrappedtext = preg_replace_callback( '/\<\\/?mw:toc\>/',
+                       array( &$this, 'replaceCallback' ), $wrappedtext );
 
                // Modify inline Microdata <link> and <meta> elements so they say <html-link> and <html-meta> so
                // we can trick Tidy into not stripping them out by including them in tidy's new-empty-tags config
@@ -80,7 +83,7 @@ class MWTidyWrapper {
         *
         * @return string
         */
-       function replaceEditSectionLinksCallback( $m ) {
+       function replaceCallback( $m ) {
                $marker = "{$this->mUniqPrefix}-item-{$this->mMarkerIndex}" . Parser::MARKER_SUFFIX;
                $this->mMarkerIndex++;
                $this->mTokens->setPair( $marker, $m[0] );
diff --git a/includes/rcfeed/RedisPubSubFeedEngine.php b/includes/rcfeed/RedisPubSubFeedEngine.php
new file mode 100644 (file)
index 0000000..4bcc133
--- /dev/null
@@ -0,0 +1,41 @@
+<?php
+class RedisPubSubFeedEngine implements RCFeedEngine {
+       /**
+        * Emit a recent change notification via Redis Pub/Sub
+        *
+        * If the feed URI contains a path component, it will be used to generate a
+        * channel name by stripping the leading slash and replacing any remaining
+        * slashes with '.'. If no path component is present, the channel is set to
+        * 'rc'. If the URI contains a query string, its parameters will be parsed
+        * as RedisConnectionPool options.
+        *
+        * @example $wgRCFeeds['redis'] = array(
+        *      'formatter' => 'JSONRCFeedFormatter',
+        *      'uri'       => "redis://127.0.0.1:6379/rc.$wgDBname",
+        * );
+        *
+        * @since 1.22
+        */
+       public function send( array $feed, $line ) {
+               $parsed = parse_url( $feed['uri'] );
+               $server = $parsed['host'];
+               $options = array( 'serializer' => 'none' );
+               $channel = 'rc';
+
+               if ( isset( $parsed['port'] ) ) {
+                       $server .= ":{$parsed['port']}";
+               }
+               if ( isset( $parsed['query'] ) ) {
+                       parse_str( $parsed['query'], $options );
+               }
+               if ( isset( $parsed['pass'] ) ) {
+                       $options['password'] = $parsed['pass'];
+               }
+               if ( isset( $parsed['path'] ) ) {
+                       $channel = str_replace( '/', '.', ltrim( $parsed['path'], '/' ) );
+               }
+               $pool = RedisConnectionPool::singleton( $options );
+               $conn = $pool->getConnection( $server );
+               $conn->publish( $channel, $line );
+       }
+}
index 19d019d..6380efc 100644 (file)
@@ -592,7 +592,7 @@ class ResourceLoader {
         * and clear out the output buffer. If the client cache is too old then do nothing.
         * @param $context ResourceLoaderContext
         * @param string $mtime The TS_MW timestamp to check the header against
-        * @return bool True iff 304 header sent and output handled
+        * @return bool True if 304 header sent and output handled
         */
        protected function tryRespondLastModified( ResourceLoaderContext $context, $mtime ) {
                // If there's an If-Modified-Since header, respond with a 304 appropriately
@@ -1223,6 +1223,13 @@ class ResourceLoader {
        public static function getLessCompiler() {
                global $wgResourceLoaderLESSFunctions, $wgResourceLoaderLESSImportPaths;
 
+               // When called from the installer, it is possible that a required PHP extension
+               // is missing (at least for now; see bug 47564). If this is the case, throw an
+               // exception (caught by the installer) to prevent a fatal error later on.
+               if ( !function_exists( 'ctype_digit' ) ) {
+                       throw new MWException( 'lessc requires the Ctype extension' );
+               }
+
                $less = new lessc();
                $less->setPreserveComments( true );
                $less->setVariables( self::getLESSVars() );
index 20f6e0b..b38f448 100644 (file)
@@ -41,7 +41,8 @@ class ResourceLoaderStartUpModule extends ResourceLoaderModule {
                        $wgVariantArticlePath, $wgActionPaths, $wgVersion,
                        $wgEnableAPI, $wgEnableWriteAPI, $wgDBname,
                        $wgSitename, $wgFileExtensions, $wgExtensionAssetsPath,
-                       $wgCookiePrefix, $wgResourceLoaderMaxQueryLength;
+                       $wgCookiePrefix, $wgResourceLoaderMaxQueryLength,
+                       $wgResourceLoaderStorageEnabled, $wgResourceLoaderStorageVersion;
 
                $mainPage = Title::newMainPage();
 
@@ -96,6 +97,8 @@ class ResourceLoaderStartUpModule extends ResourceLoaderModule {
                        'wgResourceLoaderMaxQueryLength' => $wgResourceLoaderMaxQueryLength,
                        'wgCaseSensitiveNamespaces' => $caseSensitiveNamespaces,
                        'wgLegalTitleChars' => Title::convertByteClassToUnicodeClass( Title::legalChars() ),
+                       'wgResourceLoaderStorageVersion' => $wgResourceLoaderStorageVersion,
+                       'wgResourceLoaderStorageEnabled' => $wgResourceLoaderStorageEnabled,
                );
 
                wfRunHooks( 'ResourceLoaderGetConfigVars', array( &$vars ) );
diff --git a/includes/search/SearchUpdate.php b/includes/search/SearchUpdate.php
deleted file mode 100644 (file)
index 82a413e..0000000
+++ /dev/null
@@ -1,185 +0,0 @@
-<?php
-/**
- * Search index updater
- *
- * See deferred.txt
- *
- * 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 Search
- */
-
-/**
- * Database independant search index updater
- *
- * @ingroup Search
- */
-class SearchUpdate implements DeferrableUpdate {
-       /**
-        * Page id being updated
-        * @var int
-        */
-       private $id = 0;
-
-       /**
-        * Title we're updating
-        * @var Title
-        */
-       private $title;
-
-       /**
-        * Content of the page (not text)
-        * @var Content|false
-        */
-       private $content;
-
-       /**
-        * Constructor
-        *
-        * @param int $id Page id to update
-        * @param Title|string $title Title of page to update
-        * @param Content|string|false $c Content of the page to update.
-        *  If a Content object, text will be gotten from it. String is for back-compat.
-        *  Passing false tells the backend to just update the title, not the content
-        */
-       public function __construct( $id, $title, $c = false ) {
-               if ( is_string( $title ) ) {
-                       $nt = Title::newFromText( $title );
-               } else {
-                       $nt = $title;
-               }
-
-               if ( $nt ) {
-                       $this->id = $id;
-                       // is_string() check is back-compat for ApprovedRevs
-                       if ( is_string( $c ) ) {
-                               $this->content = new TextContent( $c );
-                       } else {
-                               $this->content = $c ?: false;
-                       }
-                       $this->title = $nt;
-               } else {
-                       wfDebug( "SearchUpdate object created with invalid title '$title'\n" );
-               }
-       }
-
-       /**
-        * Perform actual update for the entry
-        */
-       public function doUpdate() {
-               global $wgDisableSearchUpdate;
-
-               if ( $wgDisableSearchUpdate || !$this->id ) {
-                       return;
-               }
-
-               wfProfileIn( __METHOD__ );
-
-               $page = WikiPage::newFromId( $this->id, WikiPage::READ_LATEST );
-               $indexTitle = Title::indexTitle( $this->title->getNamespace(), $this->title->getText() );
-
-               foreach ( SearchEngine::getSearchTypes() as $type ) {
-                       $search = SearchEngine::create( $type );
-                       if ( !$search->supports( 'search-update' ) ) {
-                               continue;
-                       }
-
-                       $normalTitle = $search->normalizeText( $indexTitle );
-
-                       if ( $page === null ) {
-                               $search->delete( $this->id, $normalTitle );
-                               continue;
-                       } elseif ( $this->content === false ) {
-                               $search->updateTitle( $this->id, $normalTitle );
-                               continue;
-                       }
-
-                       $text = $search->getTextFromContent( $this->title, $this->content );
-                       if ( !$search->textAlreadyUpdatedForIndex() ) {
-                               $text = self::updateText( $text );
-                       }
-
-                       # Perform the actual update
-                       $search->update( $this->id, $normalTitle, $search->normalizeText( $text ) );
-               }
-
-               wfProfileOut( __METHOD__ );
-       }
-
-       /**
-        * Clean text for indexing. Only really suitable for indexing in databases.
-        * If you're using a real search engine, you'll probably want to override
-        * this behavior and do something nicer with the original wikitext.
-        */
-       public static function updateText( $text ) {
-               global $wgContLang;
-
-               # Language-specific strip/conversion
-               $text = $wgContLang->normalizeForSearch( $text );
-               $lc = SearchEngine::legalSearchChars() . '&#;';
-
-               wfProfileIn( __METHOD__ . '-regexps' );
-               $text = preg_replace( "/<\\/?\\s*[A-Za-z][^>]*?>/",
-                       ' ', $wgContLang->lc( " " . $text . " " ) ); # Strip HTML markup
-               $text = preg_replace( "/(^|\\n)==\\s*([^\\n]+)\\s*==(\\s)/sD",
-                       "\\1\\2 \\2 \\2\\3", $text ); # Emphasize headings
-
-               # Strip external URLs
-               $uc = "A-Za-z0-9_\\/:.,~%\\-+&;#?!=()@\\x80-\\xFF";
-               $protos = "http|https|ftp|mailto|news|gopher";
-               $pat = "/(^|[^\\[])({$protos}):[{$uc}]+([^{$uc}]|$)/";
-               $text = preg_replace( $pat, "\\1 \\3", $text );
-
-               $p1 = "/([^\\[])\\[({$protos}):[{$uc}]+]/";
-               $p2 = "/([^\\[])\\[({$protos}):[{$uc}]+\\s+([^\\]]+)]/";
-               $text = preg_replace( $p1, "\\1 ", $text );
-               $text = preg_replace( $p2, "\\1 \\3 ", $text );
-
-               # Internal image links
-               $pat2 = "/\\[\\[image:([{$uc}]+)\\.(gif|png|jpg|jpeg)([^{$uc}])/i";
-               $text = preg_replace( $pat2, " \\1 \\3", $text );
-
-               $text = preg_replace( "/([^{$lc}])([{$lc}]+)]]([a-z]+)/",
-                       "\\1\\2 \\2\\3", $text ); # Handle [[game]]s
-
-               # Strip all remaining non-search characters
-               $text = preg_replace( "/[^{$lc}]+/", " ", $text );
-
-               # Handle 's, s'
-               #
-               #   $text = preg_replace( "/([{$lc}]+)'s /", "\\1 \\1's ", $text );
-               #   $text = preg_replace( "/([{$lc}]+)s' /", "\\1s ", $text );
-               #
-               # These tail-anchored regexps are insanely slow. The worst case comes
-               # when Japanese or Chinese text (ie, no word spacing) is written on
-               # a wiki configured for Western UTF-8 mode. The Unicode characters are
-               # expanded to hex codes and the "words" are very long paragraph-length
-               # monstrosities. On a large page the above regexps may take over 20
-               # seconds *each* on a 1GHz-level processor.
-               #
-               # Following are reversed versions which are consistently fast
-               # (about 3 milliseconds on 1GHz-level processor).
-               #
-               $text = strrev( preg_replace( "/ s'([{$lc}]+)/", " s'\\1 \\1", strrev( $text ) ) );
-               $text = strrev( preg_replace( "/ 's([{$lc}]+)/", " s\\1", strrev( $text ) ) );
-
-               # Strip wiki '' and '''
-               $text = preg_replace( "/''[']*/", " ", $text );
-               wfProfileOut( __METHOD__ . '-regexps' );
-               return $text;
-       }
-}
diff --git a/includes/specials/SpecialBlockme.php b/includes/specials/SpecialBlockme.php
deleted file mode 100644 (file)
index c3d6080..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-<?php
-/**
- * Implements Special:Blockme
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- * @ingroup SpecialPage
- */
-
-/**
- * A special page called by proxyCheck.php to block open proxies
- *
- * @ingroup SpecialPage
- */
-class SpecialBlockme extends UnlistedSpecialPage {
-
-       function __construct() {
-               parent::__construct( 'Blockme' );
-       }
-
-       function execute( $par ) {
-               global $wgBlockOpenProxies, $wgProxyKey;
-
-               $this->setHeaders();
-               $this->outputHeader();
-
-               $ip = $this->getRequest()->getIP();
-               if ( !$wgBlockOpenProxies || $this->getRequest()->getText( 'ip' ) != md5( $ip . $wgProxyKey ) ) {
-                       $this->getOutput()->addWikiMsg( 'proxyblocker-disabled' );
-
-                       return;
-               }
-
-               $user = User::newFromName( $this->msg( 'proxyblocker' )->inContentLanguage()->text() );
-               # FIXME: newFromName could return false on a badly configured wiki.
-               if ( !$user->isLoggedIn() ) {
-                       $user->addToDatabase();
-               }
-
-               $block = new Block();
-               $block->setTarget( $ip );
-               $block->setBlocker( $user );
-               $block->mReason = $this->msg( 'proxyblockreason' )->inContentLanguage()->text();
-
-               $block->insert();
-
-               $this->getOutput()->addWikiMsg( 'proxyblocksuccess' );
-       }
-
-       protected function getGroupName() {
-               return 'other';
-       }
-}
index 129e919..7fcab19 100644 (file)
@@ -85,7 +85,7 @@ class SpecialChangePassword extends UnlistedSpecialPage {
 
                                if ( $user->isLoggedIn() ) {
                                        $this->getOutput()->wrapWikiMsg(
-                                                       "<div class=\"successbox\"><strong>\n$1\n</strong></div>",
+                                                       "<div class=\"successbox\">\n$1\n</div>",
                                                        'changepassword-success'
                                        );
                                        $this->getOutput()->returnToMain();
index 5d9e554..1fe9819 100644 (file)
@@ -227,7 +227,8 @@ class SpecialContributions extends SpecialPage {
         * Generates the subheading with links
         * @param $userObj User object for the target
         * @return String: appropriately-escaped HTML to be output literally
-        * @todo FIXME: Almost the same as getSubTitle in SpecialDeletedContributions.php. Could be combined.
+        * @todo FIXME: Almost the same as getSubTitle in SpecialDeletedContributions.php.
+        * Could be combined.
         */
        protected function contributionsSub( $userObj ) {
                if ( $userObj->isAnon() ) {
@@ -594,9 +595,11 @@ class SpecialContributions extends SpecialPage {
  */
 class ContribsPager extends ReverseChronologicalPager {
        public $mDefaultDirection = true;
-       var $messages, $target;
-       var $namespace = '', $mDb;
-       var $preventClickjacking = false;
+       public $messages;
+       public $target;
+       public $namespace = '';
+       public $mDb;
+       public $preventClickjacking = false;
 
        /**
         * @var array
@@ -679,8 +682,13 @@ class ContribsPager extends ReverseChronologicalPager {
                 * $limit: see phpdoc above
                 * $descending: see phpdoc above
                 */
-               $data = array( $this->mDb->select( $tables, $fields, $conds, $fname, $options, $join_conds ) );
-               wfRunHooks( 'ContribsPager::reallyDoQuery', array( &$data, $pager, $offset, $limit, $descending ) );
+               $data = array( $this->mDb->select(
+                       $tables, $fields, $conds, $fname, $options, $join_conds
+               ) );
+               wfRunHooks(
+                       'ContribsPager::reallyDoQuery',
+                       array( &$data, $pager, $offset, $limit, $descending )
+               );
 
                $result = array();
 
@@ -944,7 +952,11 @@ class ContribsPager extends ReverseChronologicalPager {
                                $chardiff .= Linker::formatRevisionSize( $row->rev_len );
                                $chardiff .= ' <span class="mw-changeslist-separator">. .</span> ';
                        } else {
-                               $parentLen = isset( $this->mParentLens[$row->rev_parent_id] ) ? $this->mParentLens[$row->rev_parent_id] : 0;
+                               $parentLen = 0;
+                               if ( isset( $this->mParentLens[$row->rev_parent_id] ) ) {
+                                       $parentLen = $this->mParentLens[$row->rev_parent_id];
+                               }
+
                                $chardiff = ' <span class="mw-changeslist-separator">. .</span> ';
                                $chardiff .= ChangesList::showCharacterDifference(
                                        $parentLen,
@@ -1001,11 +1013,14 @@ class ContribsPager extends ReverseChronologicalPager {
                        $diffHistLinks = $this->msg( 'parentheses' )
                                ->rawParams( $difftext . $this->messages['pipe-separator'] . $histlink )
                                ->escaped();
-                       $ret = "{$del}{$d} {$diffHistLinks}{$chardiff}{$nflag}{$mflag} {$link}{$userlink} {$comment} {$topmarktext}";
+                       $ret = "{$del}{$d} {$diffHistLinks}{$chardiff}{$nflag}{$mflag} ";
+                       $ret .= "{$link}{$userlink} {$comment} {$topmarktext}";
 
                        # Denote if username is redacted for this edit
                        if ( $rev->isDeleted( Revision::DELETED_USER ) ) {
-                               $ret .= " <strong>" . $this->msg( 'rev-deleted-user-contribs' )->escaped() . "</strong>";
+                               $ret .= " <strong>" .
+                                       $this->msg( 'rev-deleted-user-contribs' )->escaped() .
+                                       "</strong>";
                        }
 
                        # Tags, if any.
index 2cf1730..9b9888a 100644 (file)
  */
 class DeletedContribsPager extends IndexPager {
        public $mDefaultDirection = true;
-       var $messages, $target;
-       var $namespace = '', $mDb;
+       public $messages;
+       public $target;
+       public $namespace = '';
+       public $mDb;
 
        /**
         * @var string Navigation bar with paging links.
@@ -358,9 +360,9 @@ class DeletedContributionsPage extends SpecialPage {
                # If there were contributions, and it was a valid user or IP, show
                # the appropriate "footer" message - WHOIS tools, etc.
                if ( $target != 'newbies' ) {
-                       $message = IP::isIPAddress( $target )
-                               ? 'sp-contributions-footer-anon'
-                               'sp-contributions-footer';
+                       $message = IP::isIPAddress( $target ) ?
+                               'sp-contributions-footer-anon' :
+                               'sp-contributions-footer';
 
                        if ( !$this->msg( $message )->isDisabled() ) {
                                $out->wrapWikiMsg(
index ce7a45b..ecee0bb 100644 (file)
@@ -57,7 +57,7 @@ class SpecialPreferences extends SpecialPage {
 
                if ( $this->getRequest()->getCheck( 'success' ) ) {
                        $out->wrapWikiMsg(
-                               "<div class=\"successbox mw-sp-pref-successbox\">\n$1\n</div>",
+                               "<div class=\"successbox\">\n$1\n</div>",
                                'savedprefs'
                        );
                }
index a42a217..51e7450 100644 (file)
@@ -517,8 +517,8 @@ class SpecialRecentChanges extends IncludableSpecialPage {
                }
 
                if ( $rows->numRows() === 0 ) {
-                       $this->getOutput()->wrapWikiMsg(
-                               "<div class='mw-changeslist-empty'>\n$1\n</div>", 'recentchanges-noresult'
+                       $this->getOutput()->addHtml(
+                               '<div class="mw-changeslist-empty">' . $this->msg( 'recentchanges-noresult' )->parse() . '</div>'
                        );
                } else {
                        $this->getOutput()->addHTML( $rclistOutput );
index a844704..f37ea20 100644 (file)
@@ -72,7 +72,7 @@ class SpecialRecentchangeslinked extends SpecialRecentChanges {
                $outputPage = $this->getOutput();
                $title = Title::newFromURL( $target );
                if ( !$title || $title->getInterwiki() != '' ) {
-                       $outputPage->wrapWikiMsg( "<div class=\"errorbox\">\n$1\n</div>", 'allpagesbadtitle' );
+                       $outputPage->addHtml( '<div class="errorbox">' . $this->msg( 'allpagesbadtitle' )->parse() . '</div>' );
                        return false;
                }
 
index 8609c74..d5a6b29 100644 (file)
@@ -738,7 +738,7 @@ class SpecialSearch extends SpecialPage {
         *
         * @return string
         */
-       protected function showInterwiki( &$matches, $query ) {
+       protected function showInterwiki( $matches, $query ) {
                global $wgContLang;
                wfProfileIn( __METHOD__ );
                $terms = $wgContLang->convertForSearchResult( $matches->termMatches() );
index ca93b6d..fbc8e91 100644 (file)
@@ -42,6 +42,11 @@ class SpecialUnblock extends SpecialPage {
 
                list( $this->target, $this->type ) = SpecialBlock::getTargetAndType( $par, $this->getRequest() );
                $this->block = Block::newFromTarget( $this->target );
+               if ( $this->target instanceof User ) {
+                       # Set the 'relevant user' in the skin, so it displays links like Contributions,
+                       # User logs, UserRights, etc.
+                       $this->getSkin()->setRelevantUser( $this->target );
+               }
 
                $this->setHeaders();
                $this->outputHeader();
index 09facf4..b79aaa9 100644 (file)
@@ -812,7 +812,7 @@ class UploadForm extends HTMLForm {
                $this->mMaxUploadSize['file'] = UploadBase::getMaxUploadSize( 'file' );
                # Limit to upload_max_filesize unless we are running under HipHop and
                # that setting doesn't exist
-               if ( !wfIsHipHop() ) {
+               if ( !wfIsHHVM() ) {
                        $this->mMaxUploadSize['file'] = min( $this->mMaxUploadSize['file'],
                                wfShorthandToInteger( ini_get( 'upload_max_filesize' ) ),
                                wfShorthandToInteger( ini_get( 'post_max_size' ) )
index 90c4c35..5ac3e65 100644 (file)
@@ -221,8 +221,8 @@ class LoginForm extends SpecialPage {
 
                $status = $this->addNewaccountInternal();
                if ( !$status->isGood() ) {
-                       $error = $this->getOutput()->parse( $status->getWikiText() );
-                       $this->mainLoginForm( $error );
+                       $error = $status->getMessage();
+                       $this->mainLoginForm( $error->toString() );
                        return;
                }
 
@@ -257,8 +257,8 @@ class LoginForm extends SpecialPage {
                # Create the account and abort if there's a problem doing so
                $status = $this->addNewAccountInternal();
                if ( !$status->isGood() ) {
-                       $error = $this->getOutput()->parse( $status->getWikiText() );
-                       $this->mainLoginForm( $error );
+                       $error = $status->getMessage();
+                       $this->mainLoginForm( $error->toString() );
                        return false;
                }
 
@@ -447,7 +447,9 @@ class LoginForm extends SpecialPage {
                if ( !wfRunHooks( 'AbortNewAccount', array( $u, &$abortError ) ) ) {
                        // Hook point to add extra creation throttles and blocks
                        wfDebug( "LoginForm::addNewAccountInternal: a hook blocked creation\n" );
-                       return Status::newFatal( new RawMessage( $abortError ) );
+                       $abortError = new RawMessage( $abortError );
+                       $abortError->text();
+                       return Status::newFatal( $abortError );
                }
 
                // Hook point to check for exempt from account creation throttle
index 11632a3..4afa279 100644 (file)
@@ -208,7 +208,21 @@ class SpecialWatchlist extends SpecialPage {
                        $usePage = false;
                } else {
                        # Top log Ids for a page are not stored
-                       $conds[] = 'rc_this_oldid=page_latest OR rc_type=' . RC_LOG;
+                       $nonRevisionTypes = array( RC_LOG );
+                       wfRunHooks( 'SpecialWatchlistGetNonRevisionTypes', array( &$nonRevisionTypes ) );
+                       if ( $nonRevisionTypes ) {
+                               if ( count( $nonRevisionTypes ) === 1 ) {
+                                       // if only one use an equality instead of IN condition
+                                       $nonRevisionTypes = reset( $nonRevisionTypes );
+                               }
+                               $conds[] = $dbr->makeList(
+                                       array(
+                                               'rc_this_oldid=page_latest',
+                                               'rc_type' => $nonRevisionTypes,
+                                       ),
+                                       LIST_OR
+                               );
+                       }
                        $limitWatchlist = 0;
                        $usePage = true;
                }
index 7a4b9f2..4750af9 100644 (file)
@@ -158,9 +158,10 @@ class UsercreateTemplate extends BaseTemplate {
                                                        'id' => 'wpEmail',
                                                        'tabindex' => '6',
                                                        'size' => '20',
+                                                       'required' => $this->data['emailrequired'],
                                                        'placeholder' => $this->getMsg( $this->data['loggedin'] ?
                                                                'createacct-another-email-ph' : 'createacct-email-ph' )->text()
-                                               ) + ( $this->data['emailrequired'] ? array() : array( 'required' => '' ) ) );
+                                               ) );
                                        ?>
                                <?php } ?>
                        </div>
index f5ae353..5eb6094 100644 (file)
@@ -69,10 +69,6 @@ class UserloginTemplate extends BaseTemplate {
                                </label>
                                <?php
                                $extraAttrs = array();
-                               // Set focus to this field if it's blank.
-                               if ( !$this->data['name'] ) {
-                                       $extraAttrs['autofocus'] = '';
-                               }
                                echo Html::input( 'wpName', $this->data['name'], 'text', array(
                                        'class' => 'loginText',
                                        'id' => 'wpName1',
@@ -80,9 +76,11 @@ class UserloginTemplate extends BaseTemplate {
                                        'size' => '20',
                                        // 'required' is blacklisted for now in Html.php due to browser issues.
                                        // Keeping here in case that changes.
-                                       'required',
+                                       'required' => true,
+                                       // Set focus to this field if it's blank.
+                                       'autofocus' => !$this->data['name'],
                                        'placeholder' => $this->getMsg( 'userlogin-yourname-ph' )->text()
-                               ) + $extraAttrs );
+                               ) );
                                ?>
                        </div>
 
@@ -101,18 +99,15 @@ class UserloginTemplate extends BaseTemplate {
                                        ?>
                                </label>
                                <?php
-                               $extraAttrs = array();
-                               // Set focus to this field if username is filled in.
-                               if ( $this->data['name'] ) {
-                                       $extraAttrs['autofocus'] = '';
-                               }
                                echo Html::input( 'wpPassword', null, 'password', array(
                                        'class' => 'loginPassword',
                                        'id' => 'wpPassword1',
                                        'tabindex' => '2',
                                        'size' => '20',
+                                       // Set focus to this field if username is filled in.
+                                       'autofocus' => (bool)$this->data['name'],
                                        'placeholder' => $this->getMsg( 'userlogin-yourpassword-ph' )->text()
-                               ) + $extraAttrs );
+                               ) );
                                ?>
                        </div>
 
index 2260241..b162de2 100644 (file)
@@ -105,7 +105,7 @@ abstract class UploadBase {
                }
 
                # Check php's file_uploads setting
-               return wfIsHipHop() || wfIniGetBool( 'file_uploads' );
+               return wfIsHHVM() || wfIniGetBool( 'file_uploads' );
        }
 
        /**
index 2e0b944..091bd78 100644 (file)
  * @author Michael Dale
  */
 class UploadFromChunks extends UploadFromFile {
-       protected $mOffset, $mChunkIndex, $mFileKey, $mVirtualTempPath;
+       protected $mOffset;
+       protected $mChunkIndex;
+       protected $mFileKey;
+       protected $mVirtualTempPath;
 
        /**
         * Setup local pointers to stash, repo and user (similar to UploadFromStash)
         *
-        * @param $user User
-        * @param $stash UploadStash
-        * @param $repo FileRepo
+        * @param $user User|null Default: null
+        * @param $stash UploadStash|bool Default: false
+        * @param $repo FileRepo|bool Default: false
         */
        public function __construct( $user = null, $stash = false, $repo = false ) {
                // user object. sometimes this won't exist, as when running from cron.
@@ -108,13 +111,14 @@ class UploadFromChunks extends UploadFromFile {
         * @return FileRepoStatus
         */
        public function concatenateChunks() {
+               $chunkIndex = $this->getChunkIndex();
                wfDebug( __METHOD__ . " concatenate {$this->mChunkIndex} chunks:" .
-                       $this->getOffset() . ' inx:' . $this->getChunkIndex() . "\n" );
+                       $this->getOffset() . ' inx:' . $chunkIndex . "\n" );
 
                // Concatenate all the chunks to mVirtualTempPath
-               $fileList = Array();
+               $fileList = array();
                // The first chunk is stored at the mVirtualTempPath path so we start on "chunk 1"
-               for ( $i = 0; $i <= $this->getChunkIndex(); $i++ ) {
+               for ( $i = 0; $i <= $chunkIndex; $i++ ) {
                        $fileList[] = $this->getVirtualChunkLocation( $i );
                }
 
@@ -122,9 +126,12 @@ class UploadFromChunks extends UploadFromFile {
                $ext = FileBackend::extensionFromPath( $this->mVirtualTempPath );
                // Get a 0-byte temp file to perform the concatenation at
                $tmpFile = TempFSFile::factory( 'chunkedupload_', $ext );
-               $tmpPath = $tmpFile
-                       ? $tmpFile->bind( $this )->getPath() // keep alive with $this
-                       : false; // fail in concatenate()
+               $tmpPath = false; // fail in concatenate()
+               if( $tmpFile ) {
+                       // keep alive with $this
+                       $tmpPath = $tmpFile->bind( $this )->getPath();
+               }
+
                // Concatenate the chunks at the temp file
                $tStart = microtime( true );
                $status = $this->repo->concatenate( $fileList, $tmpPath, FileRepo::DELETE_SOURCE );
@@ -134,8 +141,10 @@ class UploadFromChunks extends UploadFromFile {
                }
                wfDebugLog( 'fileconcatenate', "Combined $i chunks in $tAmount seconds.\n" );
 
-               $this->mTempPath = $tmpPath; // file system path
-               $this->mFileSize = filesize( $this->mTempPath ); //Since this was set for the last chunk previously
+               // File system path
+               $this->mTempPath = $tmpPath;
+               // Since this was set for the last chunk previously
+               $this->mFileSize = filesize( $this->mTempPath );
                $ret = $this->verifyUpload();
                if ( $ret['status'] !== UploadBase::OK ) {
                        wfDebugLog( 'fileconcatenate', "Verification failed for chunked upload" );
@@ -321,7 +330,8 @@ class UploadFromChunks extends UploadFromFile {
                                        $error = array( 'unknown', 'no error recorded' );
                                }
                        }
-                       throw new UploadChunkFileException( "error storing file in '$chunkPath': " . implode( '; ', $error ) );
+                       throw new UploadChunkFileException( "Error storing file in '$chunkPath': " .
+                               implode( '; ', $error ) );
                }
                return $storeStatus;
        }
@@ -352,6 +362,11 @@ class UploadFromChunks extends UploadFromFile {
        }
 }
 
-class UploadChunkZeroLengthFileException extends MWException {};
-class UploadChunkFileException extends MWException {};
-class UploadChunkVerificationException extends MWException {};
+class UploadChunkZeroLengthFileException extends MWException {
+}
+
+class UploadChunkFileException extends MWException {
+}
+
+class UploadChunkVerificationException extends MWException {
+}
index cb85fc6..a67fc57 100644 (file)
  * @author Bryan Tong Minh
  */
 class UploadFromStash extends UploadBase {
-       protected $mFileKey, $mVirtualTempPath, $mFileProps, $mSourceType;
+       protected $mFileKey;
+       protected $mVirtualTempPath;
+       protected $mFileProps;
+       protected $mSourceType;
 
        // an instance of UploadStash
        private $stash;
@@ -37,9 +40,9 @@ class UploadFromStash extends UploadBase {
        private $repo;
 
        /**
-        * @param $user User
-        * @param $stash UploadStash
-        * @param $repo FileRepo
+        * @param User|bool $user Default: false
+        * @param UploadStash|bool $stash Default: false
+        * @param FileRepo|bool $repo Default: false
         */
        public function __construct( $user = false, $stash = false, $repo = false ) {
                // user object. sometimes this won't exist, as when running from cron.
@@ -65,7 +68,7 @@ class UploadFromStash extends UploadBase {
        }
 
        /**
-        * @param $key string
+        * @param string $key
         * @return bool
         */
        public static function isValidKey( $key ) {
@@ -74,9 +77,8 @@ class UploadFromStash extends UploadBase {
        }
 
        /**
-        * @param $request WebRequest
-        *
-        * @return Boolean
+        * @param WebRequest $request
+        * @return bool
         */
        public static function isValidRequest( $request ) {
                // this passes wpSessionKey to getText() as a default when wpFileKey isn't set.
@@ -86,8 +88,9 @@ class UploadFromStash extends UploadBase {
        }
 
        /**
-        * @param $key string
-        * @param $name string
+        * @param string $key
+        * @param string $name
+        * @param bool $initTempFile
         */
        public function initialize( $key, $name = 'upload_file', $initTempFile = true ) {
                /**
@@ -110,14 +113,17 @@ class UploadFromStash extends UploadBase {
        }
 
        /**
-        * @param $request WebRequest
+        * @param WebRequest $request
         */
        public function initializeFromRequest( &$request ) {
                // sends wpSessionKey as a default when wpFileKey is missing
                $fileKey = $request->getText( 'wpFileKey', $request->getText( 'wpSessionKey' ) );
 
                // chooses one of wpDestFile, wpUploadFile, filename in that order.
-               $desiredDestName = $request->getText( 'wpDestFile', $request->getText( 'wpUploadFile', $request->getText( 'filename' ) ) );
+               $desiredDestName = $request->getText(
+                       'wpDestFile',
+                       $request->getText( 'wpUploadFile', $request->getText( 'filename' ) )
+               );
 
                $this->initialize( $fileKey, $desiredDestName );
        }
@@ -144,7 +150,7 @@ class UploadFromStash extends UploadBase {
        /**
         * Stash the file.
         *
-        * @param $user User
+        * @param User $user
         * @return UploadStashFile
         */
        public function stashFile( User $user = null ) {
@@ -156,7 +162,7 @@ class UploadFromStash extends UploadBase {
 
        /**
         * This should return the key instead of the UploadStashFile instance, for backward compatibility.
-        * @return String
+        * @return string
         */
        public function stashSession() {
                return $this->stashFile()->getFileKey();
@@ -164,7 +170,7 @@ class UploadFromStash extends UploadBase {
 
        /**
         * Remove a temporarily kept file stashed by saveTempUploadedFile().
-        * @return bool success
+        * @return bool Success
         */
        public function unsaveUploadedFile() {
                return $this->stash->removeFile( $this->mFileKey );
@@ -172,10 +178,10 @@ class UploadFromStash extends UploadBase {
 
        /**
         * Perform the upload, then remove the database record afterward.
-        * @param $comment string
-        * @param $pageText string
-        * @param $watch bool
-        * @param $user User
+        * @param string $comment
+        * @param string $pageText
+        * @param bool $watch
+        * @param User $user
         * @return Status
         */
        public function performUpload( $comment, $pageText, $watch, $user ) {
index ebeb9c1..7db6c64 100644 (file)
@@ -358,7 +358,7 @@ class UploadStash {
                wfDebug( __METHOD__ . " clearing row $key\n" );
 
                // Ensure we have the UploadStashFile loaded for this key
-               $this->getFile( $key );
+               $this->getFile( $key, true );
 
                $dbw = $this->repo->getMasterDb();
 
diff --git a/includes/utils/ArrayUtils.php b/includes/utils/ArrayUtils.php
new file mode 100644 (file)
index 0000000..97a56e1
--- /dev/null
@@ -0,0 +1,68 @@
+<?php
+
+class ArrayUtils {
+       /**
+        * Sort the given array in a pseudo-random order which depends only on the
+        * given key and each element value. This is typically used for load
+        * balancing between servers each with a local cache.
+        *
+        * Keys are preserved. The input array is modified in place.
+        *
+        * Note: Benchmarking on PHP 5.3 and 5.4 indicates that for small
+        * strings, md5() is only 10% slower than hash('joaat',...) etc.,
+        * since the function call overhead dominates. So there's not much
+        * justification for breaking compatibility with installations
+        * compiled with ./configure --disable-hash.
+        *
+        * @param array $array Array to sort
+        * @param string $key
+        * @param string $separator A separator used to delimit the array elements and the
+        *     key. This can be chosen to provide backwards compatibility with
+        *     various consistent hash implementations that existed before this
+        *     function was introduced.
+        */
+       public static function consistentHashSort( &$array, $key, $separator = "\000" ) {
+               $hashes = array();
+               foreach ( $array as $elt ) {
+                       $hashes[$elt] = md5( $elt . $separator . $key );
+               }
+               uasort( $array, function ( $a, $b ) use ( $hashes ) {
+                       return strcmp( $hashes[$a], $hashes[$b] );
+               } );
+       }
+
+       /**
+        * Given an array of non-normalised probabilities, this function will select
+        * an element and return the appropriate key
+        *
+        * @param array $weights
+        * @return bool|int|string
+        */
+       public static function pickRandom( $weights ) {
+               if ( !is_array( $weights ) || count( $weights ) == 0 ) {
+                       return false;
+               }
+
+               $sum = array_sum( $weights );
+               if ( $sum == 0 ) {
+                       # No loads on any of them
+                       # In previous versions, this triggered an unweighted random selection,
+                       # but this feature has been removed as of April 2006 to allow for strict
+                       # separation of query groups.
+                       return false;
+               }
+               $max = mt_getrandmax();
+               $rand = mt_rand( 0, $max ) / $max * $sum;
+
+               $sum = 0;
+               foreach ( $weights as $i => $w ) {
+                       $sum += $w;
+                       # Do not return keys if they have 0 weight.
+                       # Note that the "all 0 weight" case is handed above
+                       if ( $w > 0 && $sum >= $rand ) {
+                               break;
+                       }
+               }
+               return $i;
+       }
+}
diff --git a/includes/utils/Cdb.php b/includes/utils/Cdb.php
new file mode 100644 (file)
index 0000000..81c0afe
--- /dev/null
@@ -0,0 +1,184 @@
+<?php
+/**
+ * Native CDB file reader and writer.
+ *
+ * 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
+ */
+
+/**
+ * Read from a CDB file.
+ * Native and pure PHP implementations are provided.
+ * http://cr.yp.to/cdb.html
+ */
+abstract class CdbReader {
+       /**
+        * Open a file and return a subclass instance
+        *
+        * @param $fileName string
+        *
+        * @return CdbReader
+        */
+       public static function open( $fileName ) {
+               if ( self::haveExtension() ) {
+                       return new CdbReader_DBA( $fileName );
+               } else {
+                       wfDebug( "Warning: no dba extension found, using emulation.\n" );
+                       return new CdbReader_PHP( $fileName );
+               }
+       }
+
+       /**
+        * Returns true if the native extension is available
+        *
+        * @return bool
+        */
+       public static function haveExtension() {
+               if ( !function_exists( 'dba_handlers' ) ) {
+                       return false;
+               }
+               $handlers = dba_handlers();
+               if ( !in_array( 'cdb', $handlers ) || !in_array( 'cdb_make', $handlers ) ) {
+                       return false;
+               }
+               return true;
+       }
+
+       /**
+        * Construct the object and open the file
+        */
+       abstract function __construct( $fileName );
+
+       /**
+        * Close the file. Optional, you can just let the variable go out of scope.
+        */
+       abstract function close();
+
+       /**
+        * Get a value with a given key. Only string values are supported.
+        *
+        * @param $key string
+        */
+       abstract public function get( $key );
+}
+
+/**
+ * Write to a CDB file.
+ * Native and pure PHP implementations are provided.
+ */
+abstract class CdbWriter {
+       /**
+        * Open a writer and return a subclass instance.
+        * The user must have write access to the directory, for temporary file creation.
+        *
+        * @param $fileName string
+        *
+        * @return CdbWriter_DBA|CdbWriter_PHP
+        */
+       public static function open( $fileName ) {
+               if ( CdbReader::haveExtension() ) {
+                       return new CdbWriter_DBA( $fileName );
+               } else {
+                       wfDebug( "Warning: no dba extension found, using emulation.\n" );
+                       return new CdbWriter_PHP( $fileName );
+               }
+       }
+
+       /**
+        * Create the object and open the file
+        *
+        * @param $fileName string
+        */
+       abstract function __construct( $fileName );
+
+       /**
+        * Set a key to a given value. The value will be converted to string.
+        * @param $key string
+        * @param $value string
+        */
+       abstract public function set( $key, $value );
+
+       /**
+        * Close the writer object. You should call this function before the object
+        * goes out of scope, to write out the final hashtables.
+        */
+       abstract public function close();
+}
+
+/**
+ * Reader class which uses the DBA extension
+ */
+class CdbReader_DBA {
+       var $handle;
+
+       function __construct( $fileName ) {
+               $this->handle = dba_open( $fileName, 'r-', 'cdb' );
+               if ( !$this->handle ) {
+                       throw new MWException( 'Unable to open CDB file "' . $fileName . '"' );
+               }
+       }
+
+       function close() {
+               if ( isset( $this->handle ) ) {
+                       dba_close( $this->handle );
+               }
+               unset( $this->handle );
+       }
+
+       function get( $key ) {
+               return dba_fetch( $key, $this->handle );
+       }
+}
+
+/**
+ * Writer class which uses the DBA extension
+ */
+class CdbWriter_DBA {
+       var $handle, $realFileName, $tmpFileName;
+
+       function __construct( $fileName ) {
+               $this->realFileName = $fileName;
+               $this->tmpFileName = $fileName . '.tmp.' . mt_rand( 0, 0x7fffffff );
+               $this->handle = dba_open( $this->tmpFileName, 'n', 'cdb_make' );
+               if ( !$this->handle ) {
+                       throw new MWException( 'Unable to open CDB file for write "' . $fileName . '"' );
+               }
+       }
+
+       function set( $key, $value ) {
+               return dba_insert( $key, $value, $this->handle );
+       }
+
+       function close() {
+               if ( isset( $this->handle ) ) {
+                       dba_close( $this->handle );
+               }
+               if ( wfIsWindows() ) {
+                       unlink( $this->realFileName );
+               }
+               if ( !rename( $this->tmpFileName, $this->realFileName ) ) {
+                       throw new MWException( 'Unable to move the new CDB file into place.' );
+               }
+               unset( $this->handle );
+       }
+
+       function __destruct() {
+               if ( isset( $this->handle ) ) {
+                       $this->close();
+               }
+       }
+}
diff --git a/includes/utils/Cdb_PHP.php b/includes/utils/Cdb_PHP.php
new file mode 100644 (file)
index 0000000..a38b9a8
--- /dev/null
@@ -0,0 +1,493 @@
+<?php
+/**
+ * This is a port of D.J. Bernstein's CDB to PHP. It's based on the copy that
+ * appears in PHP 5.3. Changes are:
+ *    * Error returns replaced with exceptions
+ *    * Exception thrown if sizes or offsets are between 2GB and 4GB
+ *    * Some variables renamed
+ *
+ * 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
+ */
+
+/**
+ * Common functions for readers and writers
+ */
+class CdbFunctions {
+       /**
+        * Take a modulo of a signed integer as if it were an unsigned integer.
+        * $b must be less than 0x40000000 and greater than 0
+        *
+        * @param $a
+        * @param $b
+        *
+        * @return int
+        */
+       public static function unsignedMod( $a, $b ) {
+               if ( $a & 0x80000000 ) {
+                       $m = ( $a & 0x7fffffff ) % $b + 2 * ( 0x40000000 % $b );
+                       return $m % $b;
+               } else {
+                       return $a % $b;
+               }
+       }
+
+       /**
+        * Shift a signed integer right as if it were unsigned
+        * @param $a
+        * @param $b
+        * @return int
+        */
+       public static function unsignedShiftRight( $a, $b ) {
+               if ( $b == 0 ) {
+                       return $a;
+               }
+               if ( $a & 0x80000000 ) {
+                       return ( ( $a & 0x7fffffff ) >> $b ) | ( 0x40000000 >> ( $b - 1 ) );
+               } else {
+                       return $a >> $b;
+               }
+       }
+
+       /**
+        * The CDB hash function.
+        *
+        * @param $s string
+        *
+        * @return
+        */
+       public static function hash( $s ) {
+               $h = 5381;
+               for ( $i = 0; $i < strlen( $s ); $i++ ) {
+                       $h5 = ( $h << 5 ) & 0xffffffff;
+                       // Do a 32-bit sum
+                       // Inlined here for speed
+                       $sum = ( $h & 0x3fffffff ) + ( $h5 & 0x3fffffff );
+                       $h =
+                               (
+                                       ( $sum & 0x40000000 ? 1 : 0 )
+                                       + ( $h & 0x80000000 ? 2 : 0 )
+                                       + ( $h & 0x40000000 ? 1 : 0 )
+                                       + ( $h5 & 0x80000000 ? 2 : 0 )
+                                       + ( $h5 & 0x40000000 ? 1 : 0 )
+                               ) << 30
+                               | ( $sum & 0x3fffffff );
+                       $h ^= ord( $s[$i] );
+                       $h &= 0xffffffff;
+               }
+               return $h;
+       }
+}
+
+/**
+ * CDB reader class
+ */
+class CdbReader_PHP extends CdbReader {
+       /** The filename */
+       var $fileName;
+
+       /** The file handle */
+       var $handle;
+
+       /* number of hash slots searched under this key */
+       var $loop;
+
+       /* initialized if loop is nonzero */
+       var $khash;
+
+       /* initialized if loop is nonzero */
+       var $kpos;
+
+       /* initialized if loop is nonzero */
+       var $hpos;
+
+       /* initialized if loop is nonzero */
+       var $hslots;
+
+       /* initialized if findNext() returns true */
+       var $dpos;
+
+       /* initialized if cdb_findnext() returns 1 */
+       var $dlen;
+
+       /**
+        * @param $fileName string
+        * @throws MWException
+        */
+       function __construct( $fileName ) {
+               $this->fileName = $fileName;
+               $this->handle = fopen( $fileName, 'rb' );
+               if ( !$this->handle ) {
+                       throw new MWException( 'Unable to open CDB file "' . $this->fileName . '".' );
+               }
+               $this->findStart();
+       }
+
+       function close() {
+               if ( isset( $this->handle ) ) {
+                       fclose( $this->handle );
+               }
+               unset( $this->handle );
+       }
+
+       /**
+        * @param $key
+        * @return bool|string
+        */
+       public function get( $key ) {
+               // strval is required
+               if ( $this->find( strval( $key ) ) ) {
+                       return $this->read( $this->dlen, $this->dpos );
+               } else {
+                       return false;
+               }
+       }
+
+       /**
+        * @param $key
+        * @param $pos
+        * @return bool
+        */
+       protected function match( $key, $pos ) {
+               $buf = $this->read( strlen( $key ), $pos );
+               return $buf === $key;
+       }
+
+       protected function findStart() {
+               $this->loop = 0;
+       }
+
+       /**
+        * @throws MWException
+        * @param $length
+        * @param $pos
+        * @return string
+        */
+       protected function read( $length, $pos ) {
+               if ( fseek( $this->handle, $pos ) == -1 ) {
+                       // This can easily happen if the internal pointers are incorrect
+                       throw new MWException(
+                               'Seek failed, file "' . $this->fileName . '" may be corrupted.' );
+               }
+
+               if ( $length == 0 ) {
+                       return '';
+               }
+
+               $buf = fread( $this->handle, $length );
+               if ( $buf === false || strlen( $buf ) !== $length ) {
+                       throw new MWException(
+                               'Read from CDB file failed, file "' . $this->fileName . '" may be corrupted.' );
+               }
+               return $buf;
+       }
+
+       /**
+        * Unpack an unsigned integer and throw an exception if it needs more than 31 bits
+        * @param $s
+        * @throws MWException
+        * @return mixed
+        */
+       protected function unpack31( $s ) {
+               $data = unpack( 'V', $s );
+               if ( $data[1] > 0x7fffffff ) {
+                       throw new MWException(
+                               'Error in CDB file "' . $this->fileName . '", integer too big.' );
+               }
+               return $data[1];
+       }
+
+       /**
+        * Unpack a 32-bit signed integer
+        * @param $s
+        * @return int
+        */
+       protected function unpackSigned( $s ) {
+               $data = unpack( 'va/vb', $s );
+               return $data['a'] | ( $data['b'] << 16 );
+       }
+
+       /**
+        * @param $key
+        * @return bool
+        */
+       protected function findNext( $key ) {
+               if ( !$this->loop ) {
+                       $u = CdbFunctions::hash( $key );
+                       $buf = $this->read( 8, ( $u << 3 ) & 2047 );
+                       $this->hslots = $this->unpack31( substr( $buf, 4 ) );
+                       if ( !$this->hslots ) {
+                               return false;
+                       }
+                       $this->hpos = $this->unpack31( substr( $buf, 0, 4 ) );
+                       $this->khash = $u;
+                       $u = CdbFunctions::unsignedShiftRight( $u, 8 );
+                       $u = CdbFunctions::unsignedMod( $u, $this->hslots );
+                       $u <<= 3;
+                       $this->kpos = $this->hpos + $u;
+               }
+
+               while ( $this->loop < $this->hslots ) {
+                       $buf = $this->read( 8, $this->kpos );
+                       $pos = $this->unpack31( substr( $buf, 4 ) );
+                       if ( !$pos ) {
+                               return false;
+                       }
+                       $this->loop += 1;
+                       $this->kpos += 8;
+                       if ( $this->kpos == $this->hpos + ( $this->hslots << 3 ) ) {
+                               $this->kpos = $this->hpos;
+                       }
+                       $u = $this->unpackSigned( substr( $buf, 0, 4 ) );
+                       if ( $u === $this->khash ) {
+                               $buf = $this->read( 8, $pos );
+                               $keyLen = $this->unpack31( substr( $buf, 0, 4 ) );
+                               if ( $keyLen == strlen( $key ) && $this->match( $key, $pos + 8 ) ) {
+                                       // Found
+                                       $this->dlen = $this->unpack31( substr( $buf, 4 ) );
+                                       $this->dpos = $pos + 8 + $keyLen;
+                                       return true;
+                               }
+                       }
+               }
+               return false;
+       }
+
+       /**
+        * @param $key
+        * @return bool
+        */
+       protected function find( $key ) {
+               $this->findStart();
+               return $this->findNext( $key );
+       }
+}
+
+/**
+ * CDB writer class
+ */
+class CdbWriter_PHP extends CdbWriter {
+       var $handle, $realFileName, $tmpFileName;
+
+       var $hplist;
+       var $numentries, $pos;
+
+       /**
+        * @param $fileName string
+        */
+       function __construct( $fileName ) {
+               $this->realFileName = $fileName;
+               $this->tmpFileName = $fileName . '.tmp.' . mt_rand( 0, 0x7fffffff );
+               $this->handle = fopen( $this->tmpFileName, 'wb' );
+               if ( !$this->handle ) {
+                       $this->throwException(
+                               'Unable to open CDB file "' . $this->tmpFileName . '" for write.' );
+               }
+               $this->hplist = array();
+               $this->numentries = 0;
+               $this->pos = 2048; // leaving space for the pointer array, 256 * 8
+               if ( fseek( $this->handle, $this->pos ) == -1 ) {
+                       $this->throwException( 'fseek failed in file "' . $this->tmpFileName . '".' );
+               }
+       }
+
+       function __destruct() {
+               if ( isset( $this->handle ) ) {
+                       $this->close();
+               }
+       }
+
+       /**
+        * @param $key
+        * @param $value
+        * @return
+        */
+       public function set( $key, $value ) {
+               if ( strval( $key ) === '' ) {
+                       // DBA cross-check hack
+                       return;
+               }
+               $this->addbegin( strlen( $key ), strlen( $value ) );
+               $this->write( $key );
+               $this->write( $value );
+               $this->addend( strlen( $key ), strlen( $value ), CdbFunctions::hash( $key ) );
+       }
+
+       /**
+        * @throws MWException
+        */
+       public function close() {
+               $this->finish();
+               if ( isset( $this->handle ) ) {
+                       fclose( $this->handle );
+               }
+               if ( wfIsWindows() && file_exists( $this->realFileName ) ) {
+                       unlink( $this->realFileName );
+               }
+               if ( !rename( $this->tmpFileName, $this->realFileName ) ) {
+                       $this->throwException( 'Unable to move the new CDB file into place.' );
+               }
+               unset( $this->handle );
+       }
+
+       /**
+        * @throws MWException
+        * @param $buf
+        */
+       protected function write( $buf ) {
+               $len = fwrite( $this->handle, $buf );
+               if ( $len !== strlen( $buf ) ) {
+                       $this->throwException( 'Error writing to CDB file "' . $this->tmpFileName . '".' );
+               }
+       }
+
+       /**
+        * @throws MWException
+        * @param $len
+        */
+       protected function posplus( $len ) {
+               $newpos = $this->pos + $len;
+               if ( $newpos > 0x7fffffff ) {
+                       $this->throwException(
+                               'A value in the CDB file "' . $this->tmpFileName . '" is too large.' );
+               }
+               $this->pos = $newpos;
+       }
+
+       /**
+        * @param $keylen
+        * @param $datalen
+        * @param $h
+        */
+       protected function addend( $keylen, $datalen, $h ) {
+               $this->hplist[] = array(
+                       'h' => $h,
+                       'p' => $this->pos
+               );
+
+               $this->numentries++;
+               $this->posplus( 8 );
+               $this->posplus( $keylen );
+               $this->posplus( $datalen );
+       }
+
+       /**
+        * @throws MWException
+        * @param $keylen
+        * @param $datalen
+        */
+       protected function addbegin( $keylen, $datalen ) {
+               if ( $keylen > 0x7fffffff ) {
+                       $this->throwException( 'Key length too long in file "' . $this->tmpFileName . '".' );
+               }
+               if ( $datalen > 0x7fffffff ) {
+                       $this->throwException( 'Data length too long in file "' . $this->tmpFileName . '".' );
+               }
+               $buf = pack( 'VV', $keylen, $datalen );
+               $this->write( $buf );
+       }
+
+       /**
+        * @throws MWException
+        */
+       protected function finish() {
+               // Hack for DBA cross-check
+               $this->hplist = array_reverse( $this->hplist );
+
+               // Calculate the number of items that will be in each hashtable
+               $counts = array_fill( 0, 256, 0 );
+               foreach ( $this->hplist as $item ) {
+                       ++ $counts[255 & $item['h']];
+               }
+
+               // Fill in $starts with the *end* indexes
+               $starts = array();
+               $pos = 0;
+               for ( $i = 0; $i < 256; ++$i ) {
+                       $pos += $counts[$i];
+                       $starts[$i] = $pos;
+               }
+
+               // Excessively clever and indulgent code to simultaneously fill $packedTables
+               // with the packed hashtables, and adjust the elements of $starts
+               // to actually point to the starts instead of the ends.
+               $packedTables = array_fill( 0, $this->numentries, false );
+               foreach ( $this->hplist as $item ) {
+                       $packedTables[--$starts[255 & $item['h']]] = $item;
+               }
+
+               $final = '';
+               for ( $i = 0; $i < 256; ++$i ) {
+                       $count = $counts[$i];
+
+                       // The size of the hashtable will be double the item count.
+                       // The rest of the slots will be empty.
+                       $len = $count + $count;
+                       $final .= pack( 'VV', $this->pos, $len );
+
+                       $hashtable = array();
+                       for ( $u = 0; $u < $len; ++$u ) {
+                               $hashtable[$u] = array( 'h' => 0, 'p' => 0 );
+                       }
+
+                       // Fill the hashtable, using the next empty slot if the hashed slot
+                       // is taken.
+                       for ( $u = 0; $u < $count; ++$u ) {
+                               $hp = $packedTables[$starts[$i] + $u];
+                               $where = CdbFunctions::unsignedMod(
+                                       CdbFunctions::unsignedShiftRight( $hp['h'], 8 ), $len );
+                               while ( $hashtable[$where]['p'] ) {
+                                       if ( ++$where == $len ) {
+                                               $where = 0;
+                                       }
+                               }
+                               $hashtable[$where] = $hp;
+                       }
+
+                       // Write the hashtable
+                       for ( $u = 0; $u < $len; ++$u ) {
+                               $buf = pack( 'vvV',
+                                       $hashtable[$u]['h'] & 0xffff,
+                                       CdbFunctions::unsignedShiftRight( $hashtable[$u]['h'], 16 ),
+                                       $hashtable[$u]['p'] );
+                               $this->write( $buf );
+                               $this->posplus( 8 );
+                       }
+               }
+
+               // Write the pointer array at the start of the file
+               rewind( $this->handle );
+               if ( ftell( $this->handle ) != 0 ) {
+                       $this->throwException( 'Error rewinding to start of file "' . $this->tmpFileName . '".' );
+               }
+               $this->write( $final );
+       }
+
+       /**
+        * Clean up the temp file and throw an exception
+        *
+        * @param $msg string
+        * @throws MWException
+        */
+       protected function throwException( $msg ) {
+               if ( $this->handle ) {
+                       fclose( $this->handle );
+                       unlink( $this->tmpFileName );
+               }
+               throw new MWException( $msg );
+       }
+}
diff --git a/includes/utils/ConfEditor.php b/includes/utils/ConfEditor.php
new file mode 100644 (file)
index 0000000..67cb87d
--- /dev/null
@@ -0,0 +1,1109 @@
+<?php
+/**
+ * Configuration file editor.
+ *
+ * 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
+ */
+
+/**
+ * This is a state machine style parser with two internal stacks:
+ *   * A next state stack, which determines the state the machine will progress to next
+ *   * A path stack, which keeps track of the logical location in the file.
+ *
+ * Reference grammar:
+ *
+ * file = T_OPEN_TAG *statement
+ * statement = T_VARIABLE "=" expression ";"
+ * expression = array / scalar / T_VARIABLE
+ * array = T_ARRAY "(" [ element *( "," element ) [ "," ] ] ")"
+ * element = assoc-element / expression
+ * assoc-element = scalar T_DOUBLE_ARROW expression
+ * scalar = T_LNUMBER / T_DNUMBER / T_STRING / T_CONSTANT_ENCAPSED_STRING
+ */
+class ConfEditor {
+       /** The text to parse */
+       var $text;
+
+       /** The token array from token_get_all() */
+       var $tokens;
+
+       /** The current position in the token array */
+       var $pos;
+
+       /** The current 1-based line number */
+       var $lineNum;
+
+       /** The current 1-based column number */
+       var $colNum;
+
+       /** The current 0-based byte number */
+       var $byteNum;
+
+       /** The current ConfEditorToken object */
+       var $currentToken;
+
+       /** The previous ConfEditorToken object */
+       var $prevToken;
+
+       /**
+        * The state machine stack. This is an array of strings where the topmost
+        * element will be popped off and become the next parser state.
+        */
+       var $stateStack;
+
+       /**
+        * The path stack is a stack of associative arrays with the following elements:
+        *    name              The name of top level of the path
+        *    level             The level (number of elements) of the path
+        *    startByte         The byte offset of the start of the path
+        *    startToken        The token offset of the start
+        *    endByte           The byte offset of thee
+        *    endToken          The token offset of the end, plus one
+        *    valueStartToken   The start token offset of the value part
+        *    valueStartByte    The start byte offset of the value part
+        *    valueEndToken     The end token offset of the value part, plus one
+        *    valueEndByte      The end byte offset of the value part, plus one
+        *    nextArrayIndex    The next numeric array index at this level
+        *    hasComma          True if the array element ends with a comma
+        *    arrowByte         The byte offset of the "=>", or false if there isn't one
+        */
+       var $pathStack;
+
+       /**
+        * The elements of the top of the pathStack for every path encountered, indexed
+        * by slash-separated path.
+        */
+       var $pathInfo;
+
+       /**
+        * Next serial number for whitespace placeholder paths (\@extra-N)
+        */
+       var $serial;
+
+       /**
+        * Editor state. This consists of the internal copy/insert operations which
+        * are applied to the source string to obtain the destination string.
+        */
+       var $edits;
+
+       /**
+        * Simple entry point for command-line testing
+        *
+        * @param $text string
+        *
+        * @return string
+        */
+       static function test( $text ) {
+               try {
+                       $ce = new self( $text );
+                       $ce->parse();
+               } catch ( ConfEditorParseError $e ) {
+                       return $e->getMessage() . "\n" . $e->highlight( $text );
+               }
+               return "OK";
+       }
+
+       /**
+        * Construct a new parser
+        */
+       public function __construct( $text ) {
+               $this->text = $text;
+       }
+
+       /**
+        * Edit the text. Returns the edited text.
+        * @param array $ops of operations.
+        *
+        * Operations are given as an associative array, with members:
+        *    type:     One of delete, set, append or insert (required)
+        *    path:     The path to operate on (required)
+        *    key:      The array key to insert/append, with PHP quotes
+        *    value:    The value, with PHP quotes
+        *
+        * delete
+        *    Deletes an array element or statement with the specified path.
+        *    e.g.
+        *        array('type' => 'delete', 'path' => '$foo/bar/baz' )
+        *    is equivalent to the runtime PHP code:
+        *        unset( $foo['bar']['baz'] );
+        *
+        * set
+        *    Sets the value of an array element. If the element doesn't exist, it
+        *    is appended to the array. If it does exist, the value is set, with
+        *    comments and indenting preserved.
+        *
+        * append
+        *    Appends a new element to the end of the array. Adds a trailing comma.
+        *    e.g.
+        *        array( 'type' => 'append', 'path', '$foo/bar',
+        *            'key' => 'baz', 'value' => "'x'" )
+        *    is like the PHP code:
+        *        $foo['bar']['baz'] = 'x';
+        *
+        * insert
+        *    Insert a new element at the start of the array.
+        *
+        * @throws MWException
+        * @return string
+        */
+       public function edit( $ops ) {
+               $this->parse();
+
+               $this->edits = array(
+                       array( 'copy', 0, strlen( $this->text ) )
+               );
+               foreach ( $ops as $op ) {
+                       $type = $op['type'];
+                       $path = $op['path'];
+                       $value = isset( $op['value'] ) ? $op['value'] : null;
+                       $key = isset( $op['key'] ) ? $op['key'] : null;
+
+                       switch ( $type ) {
+                       case 'delete':
+                               list( $start, $end ) = $this->findDeletionRegion( $path );
+                               $this->replaceSourceRegion( $start, $end, false );
+                               break;
+                       case 'set':
+                               if ( isset( $this->pathInfo[$path] ) ) {
+                                       list( $start, $end ) = $this->findValueRegion( $path );
+                                       $encValue = $value; // var_export( $value, true );
+                                       $this->replaceSourceRegion( $start, $end, $encValue );
+                                       break;
+                               }
+                               // No existing path, fall through to append
+                               $slashPos = strrpos( $path, '/' );
+                               $key = var_export( substr( $path, $slashPos + 1 ), true );
+                               $path = substr( $path, 0, $slashPos );
+                               // Fall through
+                       case 'append':
+                               // Find the last array element
+                               $lastEltPath = $this->findLastArrayElement( $path );
+                               if ( $lastEltPath === false ) {
+                                       throw new MWException( "Can't find any element of array \"$path\"" );
+                               }
+                               $lastEltInfo = $this->pathInfo[$lastEltPath];
+
+                               // Has it got a comma already?
+                               if ( strpos( $lastEltPath, '@extra' ) === false && !$lastEltInfo['hasComma'] ) {
+                                       // No comma, insert one after the value region
+                                       list( , $end ) = $this->findValueRegion( $lastEltPath );
+                                       $this->replaceSourceRegion( $end - 1, $end - 1, ',' );
+                               }
+
+                               // Make the text to insert
+                               list( $start, $end ) = $this->findDeletionRegion( $lastEltPath );
+
+                               if ( $key === null ) {
+                                       list( $indent, ) = $this->getIndent( $start );
+                                       $textToInsert = "$indent$value,";
+                               } else {
+                                       list( $indent, $arrowIndent ) =
+                                               $this->getIndent( $start, $key, $lastEltInfo['arrowByte'] );
+                                       $textToInsert = "$indent$key$arrowIndent=> $value,";
+                               }
+                               $textToInsert .= ( $indent === false ? ' ' : "\n" );
+
+                               // Insert the item
+                               $this->replaceSourceRegion( $end, $end, $textToInsert );
+                               break;
+                       case 'insert':
+                               // Find first array element
+                               $firstEltPath = $this->findFirstArrayElement( $path );
+                               if ( $firstEltPath === false ) {
+                                       throw new MWException( "Can't find array element of \"$path\"" );
+                               }
+                               list( $start, ) = $this->findDeletionRegion( $firstEltPath );
+                               $info = $this->pathInfo[$firstEltPath];
+
+                               // Make the text to insert
+                               if ( $key === null ) {
+                                       list( $indent, ) = $this->getIndent( $start );
+                                       $textToInsert = "$indent$value,";
+                               } else {
+                                       list( $indent, $arrowIndent ) =
+                                               $this->getIndent( $start, $key, $info['arrowByte'] );
+                                       $textToInsert = "$indent$key$arrowIndent=> $value,";
+                               }
+                               $textToInsert .= ( $indent === false ? ' ' : "\n" );
+
+                               // Insert the item
+                               $this->replaceSourceRegion( $start, $start, $textToInsert );
+                               break;
+                       default:
+                               throw new MWException( "Unrecognised operation: \"$type\"" );
+                       }
+               }
+
+               // Do the edits
+               $out = '';
+               foreach ( $this->edits as $edit ) {
+                       if ( $edit[0] == 'copy' ) {
+                               $out .= substr( $this->text, $edit[1], $edit[2] - $edit[1] );
+                       } else { // if ( $edit[0] == 'insert' )
+                               $out .= $edit[1];
+                       }
+               }
+
+               // Do a second parse as a sanity check
+               $this->text = $out;
+               try {
+                       $this->parse();
+               } catch ( ConfEditorParseError $e ) {
+                       throw new MWException(
+                               "Sorry, ConfEditor broke the file during editing and it won't parse anymore: " .
+                               $e->getMessage() );
+               }
+               return $out;
+       }
+
+       /**
+        * Get the variables defined in the text
+        * @return array( varname => value )
+        */
+       function getVars() {
+               $vars = array();
+               $this->parse();
+               foreach ( $this->pathInfo as $path => $data ) {
+                       if ( $path[0] != '$' ) {
+                               continue;
+                       }
+                       $trimmedPath = substr( $path, 1 );
+                       $name = $data['name'];
+                       if ( $name[0] == '@' ) {
+                               continue;
+                       }
+                       if ( $name[0] == '$' ) {
+                               $name = substr( $name, 1 );
+                       }
+                       $parentPath = substr( $trimmedPath, 0,
+                               strlen( $trimmedPath ) - strlen( $name ) );
+                       if ( substr( $parentPath, -1 ) == '/' ) {
+                               $parentPath = substr( $parentPath, 0, -1 );
+                       }
+
+                       $value = substr( $this->text, $data['valueStartByte'],
+                               $data['valueEndByte'] - $data['valueStartByte']
+                       );
+                       $this->setVar( $vars, $parentPath, $name,
+                               $this->parseScalar( $value ) );
+               }
+               return $vars;
+       }
+
+       /**
+        * Set a value in an array, unless it's set already. For instance,
+        * setVar( $arr, 'foo/bar', 'baz', 3 ); will set
+        * $arr['foo']['bar']['baz'] = 3;
+        * @param $array array
+        * @param string $path slash-delimited path
+        * @param $key mixed Key
+        * @param $value mixed Value
+        */
+       function setVar( &$array, $path, $key, $value ) {
+               $pathArr = explode( '/', $path );
+               $target =& $array;
+               if ( $path !== '' ) {
+                       foreach ( $pathArr as $p ) {
+                               if ( !isset( $target[$p] ) ) {
+                                       $target[$p] = array();
+                               }
+                               $target =& $target[$p];
+                       }
+               }
+               if ( !isset( $target[$key] ) ) {
+                       $target[$key] = $value;
+               }
+       }
+
+       /**
+        * Parse a scalar value in PHP
+        * @return mixed Parsed value
+        */
+       function parseScalar( $str ) {
+               if ( $str !== '' && $str[0] == '\'' ) {
+                       // Single-quoted string
+                       // @todo FIXME: trim() call is due to mystery bug where whitespace gets
+                       // appended to the token; without it we ended up reading in the
+                       // extra quote on the end!
+                       return strtr( substr( trim( $str ), 1, -1 ),
+                               array( '\\\'' => '\'', '\\\\' => '\\' ) );
+               }
+               if ( $str !== '' && $str[0] == '"' ) {
+                       // Double-quoted string
+                       // @todo FIXME: trim() call is due to mystery bug where whitespace gets
+                       // appended to the token; without it we ended up reading in the
+                       // extra quote on the end!
+                       return stripcslashes( substr( trim( $str ), 1, -1 ) );
+               }
+               if ( substr( $str, 0, 4 ) == 'true' ) {
+                       return true;
+               }
+               if ( substr( $str, 0, 5 ) == 'false' ) {
+                       return false;
+               }
+               if ( substr( $str, 0, 4 ) == 'null' ) {
+                       return null;
+               }
+               // Must be some kind of numeric value, so let PHP's weak typing
+               // be useful for a change
+               return $str;
+       }
+
+       /**
+        * Replace the byte offset region of the source with $newText.
+        * Works by adding elements to the $this->edits array.
+        */
+       function replaceSourceRegion( $start, $end, $newText = false ) {
+               // Split all copy operations with a source corresponding to the region
+               // in question.
+               $newEdits = array();
+               foreach ( $this->edits as $edit ) {
+                       if ( $edit[0] !== 'copy' ) {
+                               $newEdits[] = $edit;
+                               continue;
+                       }
+                       $copyStart = $edit[1];
+                       $copyEnd = $edit[2];
+                       if ( $start >= $copyEnd || $end <= $copyStart ) {
+                               // Outside this region
+                               $newEdits[] = $edit;
+                               continue;
+                       }
+                       if ( ( $start < $copyStart && $end > $copyStart )
+                               || ( $start < $copyEnd && $end > $copyEnd )
+                       ) {
+                               throw new MWException( "Overlapping regions found, can't do the edit" );
+                       }
+                       // Split the copy
+                       $newEdits[] = array( 'copy', $copyStart, $start );
+                       if ( $newText !== false ) {
+                               $newEdits[] = array( 'insert', $newText );
+                       }
+                       $newEdits[] = array( 'copy', $end, $copyEnd );
+               }
+               $this->edits = $newEdits;
+       }
+
+       /**
+        * Finds the source byte region which you would want to delete, if $pathName
+        * was to be deleted. Includes the leading spaces and tabs, the trailing line
+        * break, and any comments in between.
+        * @param $pathName
+        * @throws MWException
+        * @return array
+        */
+       function findDeletionRegion( $pathName ) {
+               if ( !isset( $this->pathInfo[$pathName] ) ) {
+                       throw new MWException( "Can't find path \"$pathName\"" );
+               }
+               $path = $this->pathInfo[$pathName];
+               // Find the start
+               $this->firstToken();
+               while ( $this->pos != $path['startToken'] ) {
+                       $this->nextToken();
+               }
+               $regionStart = $path['startByte'];
+               for ( $offset = -1; $offset >= -$this->pos; $offset-- ) {
+                       $token = $this->getTokenAhead( $offset );
+                       if ( !$token->isSkip() ) {
+                               // If there is other content on the same line, don't move the start point
+                               // back, because that will cause the regions to overlap.
+                               $regionStart = $path['startByte'];
+                               break;
+                       }
+                       $lfPos = strrpos( $token->text, "\n" );
+                       if ( $lfPos === false ) {
+                               $regionStart -= strlen( $token->text );
+                       } else {
+                               // The line start does not include the LF
+                               $regionStart -= strlen( $token->text ) - $lfPos - 1;
+                               break;
+                       }
+               }
+               // Find the end
+               while ( $this->pos != $path['endToken'] ) {
+                       $this->nextToken();
+               }
+               $regionEnd = $path['endByte']; // past the end
+               for ( $offset = 0; $offset < count( $this->tokens ) - $this->pos; $offset++ ) {
+                       $token = $this->getTokenAhead( $offset );
+                       if ( !$token->isSkip() ) {
+                               break;
+                       }
+                       $lfPos = strpos( $token->text, "\n" );
+                       if ( $lfPos === false ) {
+                               $regionEnd += strlen( $token->text );
+                       } else {
+                               // This should point past the LF
+                               $regionEnd += $lfPos + 1;
+                               break;
+                       }
+               }
+               return array( $regionStart, $regionEnd );
+       }
+
+       /**
+        * Find the byte region in the source corresponding to the value part.
+        * This includes the quotes, but does not include the trailing comma
+        * or semicolon.
+        *
+        * The end position is the past-the-end (end + 1) value as per convention.
+        * @param $pathName
+        * @throws MWException
+        * @return array
+        */
+       function findValueRegion( $pathName ) {
+               if ( !isset( $this->pathInfo[$pathName] ) ) {
+                       throw new MWException( "Can't find path \"$pathName\"" );
+               }
+               $path = $this->pathInfo[$pathName];
+               if ( $path['valueStartByte'] === false || $path['valueEndByte'] === false ) {
+                       throw new MWException( "Can't find value region for path \"$pathName\"" );
+               }
+               return array( $path['valueStartByte'], $path['valueEndByte'] );
+       }
+
+       /**
+        * Find the path name of the last element in the array.
+        * If the array is empty, this will return the \@extra interstitial element.
+        * If the specified path is not found or is not an array, it will return false.
+        * @return bool|int|string
+        */
+       function findLastArrayElement( $path ) {
+               // Try for a real element
+               $lastEltPath = false;
+               foreach ( $this->pathInfo as $candidatePath => $info ) {
+                       $part1 = substr( $candidatePath, 0, strlen( $path ) + 1 );
+                       $part2 = substr( $candidatePath, strlen( $path ) + 1, 1 );
+                       if ( $part2 == '@' ) {
+                               // Do nothing
+                       } elseif ( $part1 == "$path/" ) {
+                               $lastEltPath = $candidatePath;
+                       } elseif ( $lastEltPath !== false ) {
+                               break;
+                       }
+               }
+               if ( $lastEltPath !== false ) {
+                       return $lastEltPath;
+               }
+
+               // Try for an interstitial element
+               $extraPath = false;
+               foreach ( $this->pathInfo as $candidatePath => $info ) {
+                       $part1 = substr( $candidatePath, 0, strlen( $path ) + 1 );
+                       if ( $part1 == "$path/" ) {
+                               $extraPath = $candidatePath;
+                       } elseif ( $extraPath !== false ) {
+                               break;
+                       }
+               }
+               return $extraPath;
+       }
+
+       /**
+        * Find the path name of first element in the array.
+        * If the array is empty, this will return the \@extra interstitial element.
+        * If the specified path is not found or is not an array, it will return false.
+        * @return bool|int|string
+        */
+       function findFirstArrayElement( $path ) {
+               // Try for an ordinary element
+               foreach ( $this->pathInfo as $candidatePath => $info ) {
+                       $part1 = substr( $candidatePath, 0, strlen( $path ) + 1 );
+                       $part2 = substr( $candidatePath, strlen( $path ) + 1, 1 );
+                       if ( $part1 == "$path/" && $part2 != '@' ) {
+                               return $candidatePath;
+                       }
+               }
+
+               // Try for an interstitial element
+               foreach ( $this->pathInfo as $candidatePath => $info ) {
+                       $part1 = substr( $candidatePath, 0, strlen( $path ) + 1 );
+                       if ( $part1 == "$path/" ) {
+                               return $candidatePath;
+                       }
+               }
+               return false;
+       }
+
+       /**
+        * Get the indent string which sits after a given start position.
+        * Returns false if the position is not at the start of the line.
+        * @return array
+        */
+       function getIndent( $pos, $key = false, $arrowPos = false ) {
+               $arrowIndent = ' ';
+               if ( $pos == 0 || $this->text[$pos - 1] == "\n" ) {
+                       $indentLength = strspn( $this->text, " \t", $pos );
+                       $indent = substr( $this->text, $pos, $indentLength );
+               } else {
+                       $indent = false;
+               }
+               if ( $indent !== false && $arrowPos !== false ) {
+                       $arrowIndentLength = $arrowPos - $pos - $indentLength - strlen( $key );
+                       if ( $arrowIndentLength > 0 ) {
+                               $arrowIndent = str_repeat( ' ', $arrowIndentLength );
+                       }
+               }
+               return array( $indent, $arrowIndent );
+       }
+
+       /**
+        * Run the parser on the text. Throws an exception if the string does not
+        * match our defined subset of PHP syntax.
+        */
+       public function parse() {
+               $this->initParse();
+               $this->pushState( 'file' );
+               $this->pushPath( '@extra-' . ( $this->serial++ ) );
+               $token = $this->firstToken();
+
+               while ( !$token->isEnd() ) {
+                       $state = $this->popState();
+                       if ( !$state ) {
+                               $this->error( 'internal error: empty state stack' );
+                       }
+
+                       switch ( $state ) {
+                       case 'file':
+                               $this->expect( T_OPEN_TAG );
+                               $token = $this->skipSpace();
+                               if ( $token->isEnd() ) {
+                                       break 2;
+                               }
+                               $this->pushState( 'statement', 'file 2' );
+                               break;
+                       case 'file 2':
+                               $token = $this->skipSpace();
+                               if ( $token->isEnd() ) {
+                                       break 2;
+                               }
+                               $this->pushState( 'statement', 'file 2' );
+                               break;
+                       case 'statement':
+                               $token = $this->skipSpace();
+                               if ( !$this->validatePath( $token->text ) ) {
+                                       $this->error( "Invalid variable name \"{$token->text}\"" );
+                               }
+                               $this->nextPath( $token->text );
+                               $this->expect( T_VARIABLE );
+                               $this->skipSpace();
+                               $arrayAssign = false;
+                               if ( $this->currentToken()->type == '[' ) {
+                                       $this->nextToken();
+                                       $token = $this->skipSpace();
+                                       if ( !$token->isScalar() ) {
+                                               $this->error( "expected a string or number for the array key" );
+                                       }
+                                       if ( $token->type == T_CONSTANT_ENCAPSED_STRING ) {
+                                               $text = $this->parseScalar( $token->text );
+                                       } else {
+                                               $text = $token->text;
+                                       }
+                                       if ( !$this->validatePath( $text ) ) {
+                                               $this->error( "Invalid associative array name \"$text\"" );
+                                       }
+                                       $this->pushPath( $text );
+                                       $this->nextToken();
+                                       $this->skipSpace();
+                                       $this->expect( ']' );
+                                       $this->skipSpace();
+                                       $arrayAssign = true;
+                               }
+                               $this->expect( '=' );
+                               $this->skipSpace();
+                               $this->startPathValue();
+                               if ( $arrayAssign ) {
+                                       $this->pushState( 'expression', 'array assign end' );
+                               } else {
+                                       $this->pushState( 'expression', 'statement end' );
+                               }
+                               break;
+                       case 'array assign end':
+                       case 'statement end':
+                               $this->endPathValue();
+                               if ( $state == 'array assign end' ) {
+                                       $this->popPath();
+                               }
+                               $this->skipSpace();
+                               $this->expect( ';' );
+                               $this->nextPath( '@extra-' . ( $this->serial++ ) );
+                               break;
+                       case 'expression':
+                               $token = $this->skipSpace();
+                               if ( $token->type == T_ARRAY ) {
+                                       $this->pushState( 'array' );
+                               } elseif ( $token->isScalar() ) {
+                                       $this->nextToken();
+                               } elseif ( $token->type == T_VARIABLE ) {
+                                       $this->nextToken();
+                               } else {
+                                       $this->error( "expected simple expression" );
+                               }
+                               break;
+                       case 'array':
+                               $this->skipSpace();
+                               $this->expect( T_ARRAY );
+                               $this->skipSpace();
+                               $this->expect( '(' );
+                               $this->skipSpace();
+                               $this->pushPath( '@extra-' . ( $this->serial++ ) );
+                               if ( $this->isAhead( ')' ) ) {
+                                       // Empty array
+                                       $this->pushState( 'array end' );
+                               } else {
+                                       $this->pushState( 'element', 'array end' );
+                               }
+                               break;
+                       case 'array end':
+                               $this->skipSpace();
+                               $this->popPath();
+                               $this->expect( ')' );
+                               break;
+                       case 'element':
+                               $token = $this->skipSpace();
+                               // Look ahead to find the double arrow
+                               if ( $token->isScalar() && $this->isAhead( T_DOUBLE_ARROW, 1 ) ) {
+                                       // Found associative element
+                                       $this->pushState( 'assoc-element', 'element end' );
+                               } else {
+                                       // Not associative
+                                       $this->nextPath( '@next' );
+                                       $this->startPathValue();
+                                       $this->pushState( 'expression', 'element end' );
+                               }
+                               break;
+                       case 'element end':
+                               $token = $this->skipSpace();
+                               if ( $token->type == ',' ) {
+                                       $this->endPathValue();
+                                       $this->markComma();
+                                       $this->nextToken();
+                                       $this->nextPath( '@extra-' . ( $this->serial++ ) );
+                                       // Look ahead to find ending bracket
+                                       if ( $this->isAhead( ")" ) ) {
+                                               // Found ending bracket, no continuation
+                                               $this->skipSpace();
+                                       } else {
+                                               // No ending bracket, continue to next element
+                                               $this->pushState( 'element' );
+                                       }
+                               } elseif ( $token->type == ')' ) {
+                                       // End array
+                                       $this->endPathValue();
+                               } else {
+                                       $this->error( "expected the next array element or the end of the array" );
+                               }
+                               break;
+                       case 'assoc-element':
+                               $token = $this->skipSpace();
+                               if ( !$token->isScalar() ) {
+                                       $this->error( "expected a string or number for the array key" );
+                               }
+                               if ( $token->type == T_CONSTANT_ENCAPSED_STRING ) {
+                                       $text = $this->parseScalar( $token->text );
+                               } else {
+                                       $text = $token->text;
+                               }
+                               if ( !$this->validatePath( $text ) ) {
+                                       $this->error( "Invalid associative array name \"$text\"" );
+                               }
+                               $this->nextPath( $text );
+                               $this->nextToken();
+                               $this->skipSpace();
+                               $this->markArrow();
+                               $this->expect( T_DOUBLE_ARROW );
+                               $this->skipSpace();
+                               $this->startPathValue();
+                               $this->pushState( 'expression' );
+                               break;
+                       }
+               }
+               if ( count( $this->stateStack ) ) {
+                       $this->error( 'unexpected end of file' );
+               }
+               $this->popPath();
+       }
+
+       /**
+        * Initialise a parse.
+        */
+       protected function initParse() {
+               $this->tokens = token_get_all( $this->text );
+               $this->stateStack = array();
+               $this->pathStack = array();
+               $this->firstToken();
+               $this->pathInfo = array();
+               $this->serial = 1;
+       }
+
+       /**
+        * Set the parse position. Do not call this except from firstToken() and
+        * nextToken(), there is more to update than just the position.
+        */
+       protected function setPos( $pos ) {
+               $this->pos = $pos;
+               if ( $this->pos >= count( $this->tokens ) ) {
+                       $this->currentToken = ConfEditorToken::newEnd();
+               } else {
+                       $this->currentToken = $this->newTokenObj( $this->tokens[$this->pos] );
+               }
+               return $this->currentToken;
+       }
+
+       /**
+        * Create a ConfEditorToken from an element of token_get_all()
+        * @return ConfEditorToken
+        */
+       function newTokenObj( $internalToken ) {
+               if ( is_array( $internalToken ) ) {
+                       return new ConfEditorToken( $internalToken[0], $internalToken[1] );
+               } else {
+                       return new ConfEditorToken( $internalToken, $internalToken );
+               }
+       }
+
+       /**
+        * Reset the parse position
+        */
+       function firstToken() {
+               $this->setPos( 0 );
+               $this->prevToken = ConfEditorToken::newEnd();
+               $this->lineNum = 1;
+               $this->colNum = 1;
+               $this->byteNum = 0;
+               return $this->currentToken;
+       }
+
+       /**
+        * Get the current token
+        */
+       function currentToken() {
+               return $this->currentToken;
+       }
+
+       /**
+        * Advance the current position and return the resulting next token
+        */
+       function nextToken() {
+               if ( $this->currentToken ) {
+                       $text = $this->currentToken->text;
+                       $lfCount = substr_count( $text, "\n" );
+                       if ( $lfCount ) {
+                               $this->lineNum += $lfCount;
+                               $this->colNum = strlen( $text ) - strrpos( $text, "\n" );
+                       } else {
+                               $this->colNum += strlen( $text );
+                       }
+                       $this->byteNum += strlen( $text );
+               }
+               $this->prevToken = $this->currentToken;
+               $this->setPos( $this->pos + 1 );
+               return $this->currentToken;
+       }
+
+       /**
+        * Get the token $offset steps ahead of the current position.
+        * $offset may be negative, to get tokens behind the current position.
+        * @return ConfEditorToken
+        */
+       function getTokenAhead( $offset ) {
+               $pos = $this->pos + $offset;
+               if ( $pos >= count( $this->tokens ) || $pos < 0 ) {
+                       return ConfEditorToken::newEnd();
+               } else {
+                       return $this->newTokenObj( $this->tokens[$pos] );
+               }
+       }
+
+       /**
+        * Advances the current position past any whitespace or comments
+        */
+       function skipSpace() {
+               while ( $this->currentToken && $this->currentToken->isSkip() ) {
+                       $this->nextToken();
+               }
+               return $this->currentToken;
+       }
+
+       /**
+        * Throws an error if the current token is not of the given type, and
+        * then advances to the next position.
+        */
+       function expect( $type ) {
+               if ( $this->currentToken && $this->currentToken->type == $type ) {
+                       return $this->nextToken();
+               } else {
+                       $this->error( "expected " . $this->getTypeName( $type ) .
+                               ", got " . $this->getTypeName( $this->currentToken->type ) );
+               }
+       }
+
+       /**
+        * Push a state or two on to the state stack.
+        */
+       function pushState( $nextState, $stateAfterThat = null ) {
+               if ( $stateAfterThat !== null ) {
+                       $this->stateStack[] = $stateAfterThat;
+               }
+               $this->stateStack[] = $nextState;
+       }
+
+       /**
+        * Pop a state from the state stack.
+        * @return mixed
+        */
+       function popState() {
+               return array_pop( $this->stateStack );
+       }
+
+       /**
+        * Returns true if the user input path is valid.
+        * This exists to allow "/" and "@" to be reserved for string path keys
+        * @return bool
+        */
+       function validatePath( $path ) {
+               return strpos( $path, '/' ) === false && substr( $path, 0, 1 ) != '@';
+       }
+
+       /**
+        * Internal function to update some things at the end of a path region. Do
+        * not call except from popPath() or nextPath().
+        */
+       function endPath() {
+               $key = '';
+               foreach ( $this->pathStack as $pathInfo ) {
+                       if ( $key !== '' ) {
+                               $key .= '/';
+                       }
+                       $key .= $pathInfo['name'];
+               }
+               $pathInfo['endByte'] = $this->byteNum;
+               $pathInfo['endToken'] = $this->pos;
+               $this->pathInfo[$key] = $pathInfo;
+       }
+
+       /**
+        * Go up to a new path level, for example at the start of an array.
+        */
+       function pushPath( $path ) {
+               $this->pathStack[] = array(
+                       'name' => $path,
+                       'level' => count( $this->pathStack ) + 1,
+                       'startByte' => $this->byteNum,
+                       'startToken' => $this->pos,
+                       'valueStartToken' => false,
+                       'valueStartByte' => false,
+                       'valueEndToken' => false,
+                       'valueEndByte' => false,
+                       'nextArrayIndex' => 0,
+                       'hasComma' => false,
+                       'arrowByte' => false
+               );
+       }
+
+       /**
+        * Go down a path level, for example at the end of an array.
+        */
+       function popPath() {
+               $this->endPath();
+               array_pop( $this->pathStack );
+       }
+
+       /**
+        * Go to the next path on the same level. This ends the current path and
+        * starts a new one. If $path is \@next, the new path is set to the next
+        * numeric array element.
+        */
+       function nextPath( $path ) {
+               $this->endPath();
+               $i = count( $this->pathStack ) - 1;
+               if ( $path == '@next' ) {
+                       $nextArrayIndex =& $this->pathStack[$i]['nextArrayIndex'];
+                       $this->pathStack[$i]['name'] = $nextArrayIndex;
+                       $nextArrayIndex++;
+               } else {
+                       $this->pathStack[$i]['name'] = $path;
+               }
+               $this->pathStack[$i] =
+                       array(
+                               'startByte' => $this->byteNum,
+                               'startToken' => $this->pos,
+                               'valueStartToken' => false,
+                               'valueStartByte' => false,
+                               'valueEndToken' => false,
+                               'valueEndByte' => false,
+                               'hasComma' => false,
+                               'arrowByte' => false,
+                       ) + $this->pathStack[$i];
+       }
+
+       /**
+        * Mark the start of the value part of a path.
+        */
+       function startPathValue() {
+               $path =& $this->pathStack[count( $this->pathStack ) - 1];
+               $path['valueStartToken'] = $this->pos;
+               $path['valueStartByte'] = $this->byteNum;
+       }
+
+       /**
+        * Mark the end of the value part of a path.
+        */
+       function endPathValue() {
+               $path =& $this->pathStack[count( $this->pathStack ) - 1];
+               $path['valueEndToken'] = $this->pos;
+               $path['valueEndByte'] = $this->byteNum;
+       }
+
+       /**
+        * Mark the comma separator in an array element
+        */
+       function markComma() {
+               $path =& $this->pathStack[count( $this->pathStack ) - 1];
+               $path['hasComma'] = true;
+       }
+
+       /**
+        * Mark the arrow separator in an associative array element
+        */
+       function markArrow() {
+               $path =& $this->pathStack[count( $this->pathStack ) - 1];
+               $path['arrowByte'] = $this->byteNum;
+       }
+
+       /**
+        * Generate a parse error
+        */
+       function error( $msg ) {
+               throw new ConfEditorParseError( $this, $msg );
+       }
+
+       /**
+        * Get a readable name for the given token type.
+        * @return string
+        */
+       function getTypeName( $type ) {
+               if ( is_int( $type ) ) {
+                       return token_name( $type );
+               } else {
+                       return "\"$type\"";
+               }
+       }
+
+       /**
+        * Looks ahead to see if the given type is the next token type, starting
+        * from the current position plus the given offset. Skips any intervening
+        * whitespace.
+        * @return bool
+        */
+       function isAhead( $type, $offset = 0 ) {
+               $ahead = $offset;
+               $token = $this->getTokenAhead( $offset );
+               while ( !$token->isEnd() ) {
+                       if ( $token->isSkip() ) {
+                               $ahead++;
+                               $token = $this->getTokenAhead( $ahead );
+                               continue;
+                       } elseif ( $token->type == $type ) {
+                               // Found the type
+                               return true;
+                       } else {
+                               // Not found
+                               return false;
+                       }
+               }
+               return false;
+       }
+
+       /**
+        * Get the previous token object
+        */
+       function prevToken() {
+               return $this->prevToken;
+       }
+
+       /**
+        * Echo a reasonably readable representation of the tokenizer array.
+        */
+       function dumpTokens() {
+               $out = '';
+               foreach ( $this->tokens as $token ) {
+                       $obj = $this->newTokenObj( $token );
+                       $out .= sprintf( "%-28s %s\n",
+                               $this->getTypeName( $obj->type ),
+                               addcslashes( $obj->text, "\0..\37" ) );
+               }
+               echo "<pre>" . htmlspecialchars( $out ) . "</pre>";
+       }
+}
+
+/**
+ * Exception class for parse errors
+ */
+class ConfEditorParseError extends MWException {
+       var $lineNum, $colNum;
+       function __construct( $editor, $msg ) {
+               $this->lineNum = $editor->lineNum;
+               $this->colNum = $editor->colNum;
+               parent::__construct( "Parse error on line {$editor->lineNum} " .
+                       "col {$editor->colNum}: $msg" );
+       }
+
+       function highlight( $text ) {
+               $lines = StringUtils::explode( "\n", $text );
+               foreach ( $lines as $lineNum => $line ) {
+                       if ( $lineNum == $this->lineNum - 1 ) {
+                               return "$line\n" . str_repeat( ' ', $this->colNum - 1 ) . "^\n";
+                       }
+               }
+               return '';
+       }
+
+}
+
+/**
+ * Class to wrap a token from the tokenizer.
+ */
+class ConfEditorToken {
+       var $type, $text;
+
+       static $scalarTypes = array( T_LNUMBER, T_DNUMBER, T_STRING, T_CONSTANT_ENCAPSED_STRING );
+       static $skipTypes = array( T_WHITESPACE, T_COMMENT, T_DOC_COMMENT );
+
+       static function newEnd() {
+               return new self( 'END', '' );
+       }
+
+       function __construct( $type, $text ) {
+               $this->type = $type;
+               $this->text = $text;
+       }
+
+       function isSkip() {
+               return in_array( $this->type, self::$skipTypes );
+       }
+
+       function isScalar() {
+               return in_array( $this->type, self::$scalarTypes );
+       }
+
+       function isEnd() {
+               return $this->type == 'END';
+       }
+}
diff --git a/includes/utils/HashRing.php b/includes/utils/HashRing.php
new file mode 100644 (file)
index 0000000..930f8c0
--- /dev/null
@@ -0,0 +1,142 @@
+<?php
+/**
+ * Convenience class for weighted consistent hash rings.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @author Aaron Schulz
+ */
+
+/**
+ * Convenience class for weighted consistent hash rings
+ *
+ * @since 1.22
+ */
+class HashRing {
+       /** @var Array (location => weight) */
+       protected $sourceMap = array();
+       /** @var Array (location => (start, end)) */
+       protected $ring = array();
+
+       const RING_SIZE = 268435456; // 2^28
+
+       /**
+        * @param array $map (location => weight)
+        */
+       public function __construct( array $map ) {
+               $map = array_filter( $map, function( $w ) { return $w > 0; } );
+               if ( !count( $map ) ) {
+                       throw new MWException( "Ring is empty or all weights are zero." );
+               }
+               $this->sourceMap = $map;
+               // Sort the locations based on the hash of their names
+               $hashes = array();
+               foreach ( $map as $location => $weight ) {
+                       $hashes[$location] = sha1( $location );
+               }
+               uksort( $map, function ( $a, $b ) use ( $hashes ) {
+                       return strcmp( $hashes[$a], $hashes[$b] );
+               } );
+               // Fit the map to weight-proportionate one with a space of size RING_SIZE
+               $sum = array_sum( $map );
+               $standardMap = array();
+               foreach ( $map as $location => $weight ) {
+                       $standardMap[$location] = (int)floor( $weight / $sum * self::RING_SIZE );
+               }
+               // Build a ring of RING_SIZE spots, with each location at a spot in location hash order
+               $index = 0;
+               foreach ( $standardMap as $location => $weight ) {
+                       // Location covers half-closed interval [$index,$index + $weight)
+                       $this->ring[$location] = array( $index, $index + $weight );
+                       $index += $weight;
+               }
+               // Make sure the last location covers what is left
+               end( $this->ring );
+               $this->ring[key( $this->ring )][1] = self::RING_SIZE;
+       }
+
+       /**
+        * Get the location of an item on the ring
+        *
+        * @param string $item
+        * @return string Location
+        */
+       public function getLocation( $item ) {
+               $locations = $this->getLocations( $item, 1 );
+               return $locations[0];
+       }
+
+       /**
+        * Get the location of an item on the ring, as well as the next clockwise locations
+        *
+        * @param string $item
+        * @param integer $limit Maximum number of locations to return
+        * @return array List of locations
+        */
+       public function getLocations( $item, $limit ) {
+               $locations = array();
+               $primaryLocation = null;
+               $spot = hexdec( substr( sha1( $item ), 0, 7 ) ); // first 28 bits
+               foreach ( $this->ring as $location => $range ) {
+                       if ( count( $locations ) >= $limit ) {
+                               break;
+                       }
+                       // The $primaryLocation is the location the item spot is in.
+                       // After that is reached, keep appending the next locations.
+                       if ( ( $range[0] <= $spot && $spot < $range[1] ) || $primaryLocation !== null ) {
+                               if ( $primaryLocation === null ) {
+                                       $primaryLocation = $location;
+                               }
+                               $locations[] = $location;
+                       }
+               }
+               // If more locations are requested, wrap-around and keep adding them
+               reset( $this->ring );
+               while ( count( $locations ) < $limit ) {
+                       list( $location, ) = each( $this->ring );
+                       if ( $location === $primaryLocation ) {
+                               break; // don't go in circles
+                       }
+                       $locations[] = $location;
+               }
+               return $locations;
+       }
+
+       /**
+        * Get the map of locations to weight (ignores 0-weight items)
+        *
+        * @return array
+        */
+       public function getLocationWeights() {
+               return $this->sourceMap;
+       }
+
+       /**
+        * Get a new hash ring with a location removed from the ring
+        *
+        * @param string $location
+        * @return HashRing|bool Returns false if no non-zero weighted spots are left
+        */
+       public function newWithoutLocation( $location ) {
+               $map = $this->sourceMap;
+               unset( $map[$location] );
+               if ( count( $map ) ) {
+                       return new self( $map );
+               }
+               return false;
+       }
+}
diff --git a/includes/utils/IP.php b/includes/utils/IP.php
new file mode 100644 (file)
index 0000000..73834a5
--- /dev/null
@@ -0,0 +1,761 @@
+<?php
+/**
+ * Functions and constants to play with IP addresses and ranges
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @author Antoine Musso "<hashar at free dot fr>", Aaron Schulz
+ */
+
+// Some regex definition to "play" with IP address and IP address blocks
+
+// An IPv4 address is made of 4 bytes from x00 to xFF which is d0 to d255
+define( 'RE_IP_BYTE', '(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|0?[0-9]?[0-9])' );
+define( 'RE_IP_ADD', RE_IP_BYTE . '\.' . RE_IP_BYTE . '\.' . RE_IP_BYTE . '\.' . RE_IP_BYTE );
+// An IPv4 block is an IP address and a prefix (d1 to d32)
+define( 'RE_IP_PREFIX', '(3[0-2]|[12]?\d)' );
+define( 'RE_IP_BLOCK', RE_IP_ADD . '\/' . RE_IP_PREFIX );
+
+// An IPv6 address is made up of 8 words (each x0000 to xFFFF).
+// However, the "::" abbreviation can be used on consecutive x0000 words.
+define( 'RE_IPV6_WORD', '([0-9A-Fa-f]{1,4})' );
+define( 'RE_IPV6_PREFIX', '(12[0-8]|1[01][0-9]|[1-9]?\d)' );
+define( 'RE_IPV6_ADD',
+       '(?:' . // starts with "::" (including "::")
+               ':(?::|(?::' . RE_IPV6_WORD . '){1,7})' .
+       '|' . // ends with "::" (except "::")
+               RE_IPV6_WORD . '(?::' . RE_IPV6_WORD . '){0,6}::' .
+       '|' . // contains one "::" in the middle (the ^ makes the test fail if none found)
+               RE_IPV6_WORD . '(?::((?(-1)|:))?' . RE_IPV6_WORD . '){1,6}(?(-2)|^)' .
+       '|' . // contains no "::"
+               RE_IPV6_WORD . '(?::' . RE_IPV6_WORD . '){7}' .
+       ')'
+);
+// An IPv6 block is an IP address and a prefix (d1 to d128)
+define( 'RE_IPV6_BLOCK', RE_IPV6_ADD . '\/' . RE_IPV6_PREFIX );
+// For IPv6 canonicalization (NOT for strict validation; these are quite lax!)
+define( 'RE_IPV6_GAP', ':(?:0+:)*(?::(?:0+:)*)?' );
+define( 'RE_IPV6_V4_PREFIX', '0*' . RE_IPV6_GAP . '(?:ffff:)?' );
+
+// This might be useful for regexps used elsewhere, matches any IPv6 or IPv6 address or network
+define( 'IP_ADDRESS_STRING',
+       '(?:' .
+               RE_IP_ADD . '(?:\/' . RE_IP_PREFIX . ')?' . // IPv4
+       '|' .
+               RE_IPV6_ADD . '(?:\/' . RE_IPV6_PREFIX . ')?' . // IPv6
+       ')'
+);
+
+/**
+ * A collection of public static functions to play with IP address
+ * and IP blocks.
+ */
+class IP {
+       /**
+        * Determine if a string is as valid IP address or network (CIDR prefix).
+        * SIIT IPv4-translated addresses are rejected.
+        * Note: canonicalize() tries to convert translated addresses to IPv4.
+        *
+        * @param string $ip possible IP address
+        * @return Boolean
+        */
+       public static function isIPAddress( $ip ) {
+               return (bool)preg_match( '/^' . IP_ADDRESS_STRING . '$/', $ip );
+       }
+
+       /**
+        * Given a string, determine if it as valid IP in IPv6 only.
+        * Note: Unlike isValid(), this looks for networks too.
+        *
+        * @param string $ip possible IP address
+        * @return Boolean
+        */
+       public static function isIPv6( $ip ) {
+               return (bool)preg_match( '/^' . RE_IPV6_ADD . '(?:\/' . RE_IPV6_PREFIX . ')?$/', $ip );
+       }
+
+       /**
+        * Given a string, determine if it as valid IP in IPv4 only.
+        * Note: Unlike isValid(), this looks for networks too.
+        *
+        * @param string $ip possible IP address
+        * @return Boolean
+        */
+       public static function isIPv4( $ip ) {
+               return (bool)preg_match( '/^' . RE_IP_ADD . '(?:\/' . RE_IP_PREFIX . ')?$/', $ip );
+       }
+
+       /**
+        * Validate an IP address. Ranges are NOT considered valid.
+        * SIIT IPv4-translated addresses are rejected.
+        * Note: canonicalize() tries to convert translated addresses to IPv4.
+        *
+        * @param $ip String
+        * @return Boolean: True if it is valid.
+        */
+       public static function isValid( $ip ) {
+               return ( preg_match( '/^' . RE_IP_ADD . '$/', $ip )
+                       || preg_match( '/^' . RE_IPV6_ADD . '$/', $ip ) );
+       }
+
+       /**
+        * Validate an IP Block (valid address WITH a valid prefix).
+        * SIIT IPv4-translated addresses are rejected.
+        * Note: canonicalize() tries to convert translated addresses to IPv4.
+        *
+        * @param $ipblock String
+        * @return Boolean: True if it is valid.
+        */
+       public static function isValidBlock( $ipblock ) {
+               return ( preg_match( '/^' . RE_IPV6_BLOCK . '$/', $ipblock )
+                       || preg_match( '/^' . RE_IP_BLOCK . '$/', $ipblock ) );
+       }
+
+       /**
+        * Convert an IP into a verbose, uppercase, normalized form.
+        * IPv6 addresses in octet notation are expanded to 8 words.
+        * IPv4 addresses are just trimmed.
+        *
+        * @param string $ip IP address in quad or octet form (CIDR or not).
+        * @return String
+        */
+       public static function sanitizeIP( $ip ) {
+               $ip = trim( $ip );
+               if ( $ip === '' ) {
+                       return null;
+               }
+               if ( self::isIPv4( $ip ) || !self::isIPv6( $ip ) ) {
+                       return $ip; // nothing else to do for IPv4 addresses or invalid ones
+               }
+               // Remove any whitespaces, convert to upper case
+               $ip = strtoupper( $ip );
+               // Expand zero abbreviations
+               $abbrevPos = strpos( $ip, '::' );
+               if ( $abbrevPos !== false ) {
+                       // We know this is valid IPv6. Find the last index of the
+                       // address before any CIDR number (e.g. "a:b:c::/24").
+                       $CIDRStart = strpos( $ip, "/" );
+                       $addressEnd = ( $CIDRStart !== false )
+                               ? $CIDRStart - 1
+                               : strlen( $ip ) - 1;
+                       // If the '::' is at the beginning...
+                       if ( $abbrevPos == 0 ) {
+                               $repeat = '0:';
+                               $extra = ( $ip == '::' ) ? '0' : ''; // for the address '::'
+                               $pad = 9; // 7+2 (due to '::')
+                       // If the '::' is at the end...
+                       } elseif ( $abbrevPos == ( $addressEnd - 1 ) ) {
+                               $repeat = ':0';
+                               $extra = '';
+                               $pad = 9; // 7+2 (due to '::')
+                       // If the '::' is in the middle...
+                       } else {
+                               $repeat = ':0';
+                               $extra = ':';
+                               $pad = 8; // 6+2 (due to '::')
+                       }
+                       $ip = str_replace( '::',
+                               str_repeat( $repeat, $pad - substr_count( $ip, ':' ) ) . $extra,
+                               $ip
+                       );
+               }
+               // Remove leading zeros from each bloc as needed
+               $ip = preg_replace( '/(^|:)0+(' . RE_IPV6_WORD . ')/', '$1$2', $ip );
+               return $ip;
+       }
+
+       /**
+        * Prettify an IP for display to end users.
+        * This will make it more compact and lower-case.
+        *
+        * @param $ip string
+        * @return string
+        */
+       public static function prettifyIP( $ip ) {
+               $ip = self::sanitizeIP( $ip ); // normalize (removes '::')
+               if ( self::isIPv6( $ip ) ) {
+                       // Split IP into an address and a CIDR
+                       if ( strpos( $ip, '/' ) !== false ) {
+                               list( $ip, $cidr ) = explode( '/', $ip, 2 );
+                       } else {
+                               list( $ip, $cidr ) = array( $ip, '' );
+                       }
+                       // Get the largest slice of words with multiple zeros
+                       $offset = 0;
+                       $longest = $longestPos = false;
+                       while ( preg_match(
+                               '!(?:^|:)0(?::0)+(?:$|:)!', $ip, $m, PREG_OFFSET_CAPTURE, $offset
+                       ) ) {
+                               list( $match, $pos ) = $m[0]; // full match
+                               if ( strlen( $match ) > strlen( $longest ) ) {
+                                       $longest = $match;
+                                       $longestPos = $pos;
+                               }
+                               $offset = ( $pos + strlen( $match ) ); // advance
+                       }
+                       if ( $longest !== false ) {
+                               // Replace this portion of the string with the '::' abbreviation
+                               $ip = substr_replace( $ip, '::', $longestPos, strlen( $longest ) );
+                       }
+                       // Add any CIDR back on
+                       if ( $cidr !== '' ) {
+                               $ip = "{$ip}/{$cidr}";
+                       }
+                       // Convert to lower case to make it more readable
+                       $ip = strtolower( $ip );
+               }
+               return $ip;
+       }
+
+       /**
+        * Given a host/port string, like one might find in the host part of a URL
+        * per RFC 2732, split the hostname part and the port part and return an
+        * array with an element for each. If there is no port part, the array will
+        * have false in place of the port. If the string was invalid in some way,
+        * false is returned.
+        *
+        * This was easy with IPv4 and was generally done in an ad-hoc way, but
+        * with IPv6 it's somewhat more complicated due to the need to parse the
+        * square brackets and colons.
+        *
+        * A bare IPv6 address is accepted despite the lack of square brackets.
+        *
+        * @param string $both The string with the host and port
+        * @return array
+        */
+       public static function splitHostAndPort( $both ) {
+               if ( substr( $both, 0, 1 ) === '[' ) {
+                       if ( preg_match( '/^\[(' . RE_IPV6_ADD . ')\](?::(?P<port>\d+))?$/', $both, $m ) ) {
+                               if ( isset( $m['port'] ) ) {
+                                       return array( $m[1], intval( $m['port'] ) );
+                               } else {
+                                       return array( $m[1], false );
+                               }
+                       } else {
+                               // Square bracket found but no IPv6
+                               return false;
+                       }
+               }
+               $numColons = substr_count( $both, ':' );
+               if ( $numColons >= 2 ) {
+                       // Is it a bare IPv6 address?
+                       if ( preg_match( '/^' . RE_IPV6_ADD . '$/', $both ) ) {
+                               return array( $both, false );
+                       } else {
+                               // Not valid IPv6, but too many colons for anything else
+                               return false;
+                       }
+               }
+               if ( $numColons >= 1 ) {
+                       // Host:port?
+                       $bits = explode( ':', $both );
+                       if ( preg_match( '/^\d+/', $bits[1] ) ) {
+                               return array( $bits[0], intval( $bits[1] ) );
+                       } else {
+                               // Not a valid port
+                               return false;
+                       }
+               }
+               // Plain hostname
+               return array( $both, false );
+       }
+
+       /**
+        * Given a host name and a port, combine them into host/port string like
+        * you might find in a URL. If the host contains a colon, wrap it in square
+        * brackets like in RFC 2732. If the port matches the default port, omit
+        * the port specification
+        *
+        * @param $host string
+        * @param $port int
+        * @param $defaultPort bool|int
+        * @return string
+        */
+       public static function combineHostAndPort( $host, $port, $defaultPort = false ) {
+               if ( strpos( $host, ':' ) !== false ) {
+                       $host = "[$host]";
+               }
+               if ( $defaultPort !== false && $port == $defaultPort ) {
+                       return $host;
+               } else {
+                       return "$host:$port";
+               }
+       }
+
+       /**
+        * Given an unsigned integer, returns an IPv6 address in octet notation
+        *
+        * @param $ip_int String: IP address.
+        * @return String
+        */
+       public static function toOctet( $ip_int ) {
+               return self::hexToOctet( wfBaseConvert( $ip_int, 10, 16, 32, false ) );
+       }
+
+       /**
+        * Convert an IPv4 or IPv6 hexadecimal representation back to readable format
+        *
+        * @param string $hex number, with "v6-" prefix if it is IPv6
+        * @return String: quad-dotted (IPv4) or octet notation (IPv6)
+        */
+       public static function formatHex( $hex ) {
+               if ( substr( $hex, 0, 3 ) == 'v6-' ) { // IPv6
+                       return self::hexToOctet( substr( $hex, 3 ) );
+               } else { // IPv4
+                       return self::hexToQuad( $hex );
+               }
+       }
+
+       /**
+        * Converts a hexadecimal number to an IPv6 address in octet notation
+        *
+        * @param $ip_hex String: pure hex (no v6- prefix)
+        * @return String (of format a:b:c:d:e:f:g:h)
+        */
+       public static function hexToOctet( $ip_hex ) {
+               // Pad hex to 32 chars (128 bits)
+               $ip_hex = str_pad( strtoupper( $ip_hex ), 32, '0', STR_PAD_LEFT );
+               // Separate into 8 words
+               $ip_oct = substr( $ip_hex, 0, 4 );
+               for ( $n = 1; $n < 8; $n++ ) {
+                       $ip_oct .= ':' . substr( $ip_hex, 4 * $n, 4 );
+               }
+               // NO leading zeroes
+               $ip_oct = preg_replace( '/(^|:)0+(' . RE_IPV6_WORD . ')/', '$1$2', $ip_oct );
+               return $ip_oct;
+       }
+
+       /**
+        * Converts a hexadecimal number to an IPv4 address in quad-dotted notation
+        *
+        * @param $ip_hex String: pure hex
+        * @return String (of format a.b.c.d)
+        */
+       public static function hexToQuad( $ip_hex ) {
+               // Pad hex to 8 chars (32 bits)
+               $ip_hex = str_pad( strtoupper( $ip_hex ), 8, '0', STR_PAD_LEFT );
+               // Separate into four quads
+               $s = '';
+               for ( $i = 0; $i < 4; $i++ ) {
+                       if ( $s !== '' ) {
+                               $s .= '.';
+                       }
+                       $s .= base_convert( substr( $ip_hex, $i * 2, 2 ), 16, 10 );
+               }
+               return $s;
+       }
+
+       /**
+        * Determine if an IP address really is an IP address, and if it is public,
+        * i.e. not RFC 1918 or similar
+        * Comes from ProxyTools.php
+        *
+        * @param $ip String
+        * @return Boolean
+        */
+       public static function isPublic( $ip ) {
+               if ( self::isIPv6( $ip ) ) {
+                       return self::isPublic6( $ip );
+               }
+               $n = self::toUnsigned( $ip );
+               if ( !$n ) {
+                       return false;
+               }
+
+               // ip2long accepts incomplete addresses, as well as some addresses
+               // followed by garbage characters. Check that it's really valid.
+               if ( $ip != long2ip( $n ) ) {
+                       return false;
+               }
+
+               static $privateRanges = false;
+               if ( !$privateRanges ) {
+                       $privateRanges = array(
+                               array( '10.0.0.0', '10.255.255.255' ), # RFC 1918 (private)
+                               array( '172.16.0.0', '172.31.255.255' ), # RFC 1918 (private)
+                               array( '192.168.0.0', '192.168.255.255' ), # RFC 1918 (private)
+                               array( '0.0.0.0', '0.255.255.255' ), # this network
+                               array( '127.0.0.0', '127.255.255.255' ), # loopback
+                       );
+               }
+
+               foreach ( $privateRanges as $r ) {
+                       $start = self::toUnsigned( $r[0] );
+                       $end = self::toUnsigned( $r[1] );
+                       if ( $n >= $start && $n <= $end ) {
+                               return false;
+                       }
+               }
+               return true;
+       }
+
+       /**
+        * Determine if an IPv6 address really is an IP address, and if it is public,
+        * i.e. not RFC 4193 or similar
+        *
+        * @param $ip String
+        * @return Boolean
+        */
+       private static function isPublic6( $ip ) {
+               static $privateRanges = false;
+               if ( !$privateRanges ) {
+                       $privateRanges = array(
+                               array( 'fc00::', 'fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff' ), # RFC 4193 (local)
+                               array( '0:0:0:0:0:0:0:1', '0:0:0:0:0:0:0:1' ), # loopback
+                       );
+               }
+               $n = self::toHex( $ip );
+               foreach ( $privateRanges as $r ) {
+                       $start = self::toHex( $r[0] );
+                       $end = self::toHex( $r[1] );
+                       if ( $n >= $start && $n <= $end ) {
+                               return false;
+                       }
+               }
+               return true;
+       }
+
+       /**
+        * Return a zero-padded upper case hexadecimal representation of an IP address.
+        *
+        * Hexadecimal addresses are used because they can easily be extended to
+        * IPv6 support. To separate the ranges, the return value from this
+        * function for an IPv6 address will be prefixed with "v6-", a non-
+        * hexadecimal string which sorts after the IPv4 addresses.
+        *
+        * @param string $ip quad dotted/octet IP address.
+        * @return String
+        */
+       public static function toHex( $ip ) {
+               if ( self::isIPv6( $ip ) ) {
+                       $n = 'v6-' . self::IPv6ToRawHex( $ip );
+               } else {
+                       $n = self::toUnsigned( $ip );
+                       if ( $n !== false ) {
+                               $n = wfBaseConvert( $n, 10, 16, 8, false );
+                       }
+               }
+               return $n;
+       }
+
+       /**
+        * Given an IPv6 address in octet notation, returns a pure hex string.
+        *
+        * @param string $ip octet ipv6 IP address.
+        * @return String: pure hex (uppercase)
+        */
+       private static function IPv6ToRawHex( $ip ) {
+               $ip = self::sanitizeIP( $ip );
+               if ( !$ip ) {
+                       return null;
+               }
+               $r_ip = '';
+               foreach ( explode( ':', $ip ) as $v ) {
+                       $r_ip .= str_pad( $v, 4, 0, STR_PAD_LEFT );
+               }
+               return $r_ip;
+       }
+
+       /**
+        * Given an IP address in dotted-quad/octet notation, returns an unsigned integer.
+        * Like ip2long() except that it actually works and has a consistent error return value.
+        * Comes from ProxyTools.php
+        *
+        * @param string $ip quad dotted IP address.
+        * @return Mixed: string/int/false
+        */
+       public static function toUnsigned( $ip ) {
+               if ( self::isIPv6( $ip ) ) {
+                       $n = self::toUnsigned6( $ip );
+               } else {
+                       $n = ip2long( $ip );
+                       if ( $n < 0 ) {
+                               $n += pow( 2, 32 );
+                               # On 32-bit platforms (and on Windows), 2^32 does not fit into an int,
+                               # so $n becomes a float. We convert it to string instead.
+                               if ( is_float( $n ) ) {
+                                       $n = (string)$n;
+                               }
+                       }
+               }
+               return $n;
+       }
+
+       /**
+        * @param $ip
+        * @return String
+        */
+       private static function toUnsigned6( $ip ) {
+               return wfBaseConvert( self::IPv6ToRawHex( $ip ), 16, 10 );
+       }
+
+       /**
+        * Convert a network specification in CIDR notation
+        * to an integer network and a number of bits
+        *
+        * @param string $range IP with CIDR prefix
+        * @return array(int or string, int)
+        */
+       public static function parseCIDR( $range ) {
+               if ( self::isIPv6( $range ) ) {
+                       return self::parseCIDR6( $range );
+               }
+               $parts = explode( '/', $range, 2 );
+               if ( count( $parts ) != 2 ) {
+                       return array( false, false );
+               }
+               list( $network, $bits ) = $parts;
+               $network = ip2long( $network );
+               if ( $network !== false && is_numeric( $bits ) && $bits >= 0 && $bits <= 32 ) {
+                       if ( $bits == 0 ) {
+                               $network = 0;
+                       } else {
+                               $network &= ~( ( 1 << ( 32 - $bits ) ) - 1 );
+                       }
+                       # Convert to unsigned
+                       if ( $network < 0 ) {
+                               $network += pow( 2, 32 );
+                       }
+               } else {
+                       $network = false;
+                       $bits = false;
+               }
+               return array( $network, $bits );
+       }
+
+       /**
+        * Given a string range in a number of formats,
+        * return the start and end of the range in hexadecimal.
+        *
+        * Formats are:
+        *     1.2.3.4/24          CIDR
+        *     1.2.3.4 - 1.2.3.5   Explicit range
+        *     1.2.3.4             Single IP
+        *
+        *     2001:0db8:85a3::7344/96                                   CIDR
+        *     2001:0db8:85a3::7344 - 2001:0db8:85a3::7344   Explicit range
+        *     2001:0db8:85a3::7344                                      Single IP
+        * @param string $range IP range
+        * @return array(string, string)
+        */
+       public static function parseRange( $range ) {
+               // CIDR notation
+               if ( strpos( $range, '/' ) !== false ) {
+                       if ( self::isIPv6( $range ) ) {
+                               return self::parseRange6( $range );
+                       }
+                       list( $network, $bits ) = self::parseCIDR( $range );
+                       if ( $network === false ) {
+                               $start = $end = false;
+                       } else {
+                               $start = sprintf( '%08X', $network );
+                               $end = sprintf( '%08X', $network + pow( 2, ( 32 - $bits ) ) - 1 );
+                       }
+               // Explicit range
+               } elseif ( strpos( $range, '-' ) !== false ) {
+                       list( $start, $end ) = array_map( 'trim', explode( '-', $range, 2 ) );
+                       if ( self::isIPv6( $start ) && self::isIPv6( $end ) ) {
+                               return self::parseRange6( $range );
+                       }
+                       if ( self::isIPv4( $start ) && self::isIPv4( $end ) ) {
+                               $start = self::toUnsigned( $start );
+                               $end = self::toUnsigned( $end );
+                               if ( $start > $end ) {
+                                       $start = $end = false;
+                               } else {
+                                       $start = sprintf( '%08X', $start );
+                                       $end = sprintf( '%08X', $end );
+                               }
+                       } else {
+                               $start = $end = false;
+                       }
+               } else {
+                       # Single IP
+                       $start = $end = self::toHex( $range );
+               }
+               if ( $start === false || $end === false ) {
+                       return array( false, false );
+               } else {
+                       return array( $start, $end );
+               }
+       }
+
+       /**
+        * Convert a network specification in IPv6 CIDR notation to an
+        * integer network and a number of bits
+        *
+        * @param $range
+        *
+        * @return array(string, int)
+        */
+       private static function parseCIDR6( $range ) {
+               # Explode into <expanded IP,range>
+               $parts = explode( '/', IP::sanitizeIP( $range ), 2 );
+               if ( count( $parts ) != 2 ) {
+                       return array( false, false );
+               }
+               list( $network, $bits ) = $parts;
+               $network = self::IPv6ToRawHex( $network );
+               if ( $network !== false && is_numeric( $bits ) && $bits >= 0 && $bits <= 128 ) {
+                       if ( $bits == 0 ) {
+                               $network = "0";
+                       } else {
+                               # Native 32 bit functions WONT work here!!!
+                               # Convert to a padded binary number
+                               $network = wfBaseConvert( $network, 16, 2, 128 );
+                               # Truncate the last (128-$bits) bits and replace them with zeros
+                               $network = str_pad( substr( $network, 0, $bits ), 128, 0, STR_PAD_RIGHT );
+                               # Convert back to an integer
+                               $network = wfBaseConvert( $network, 2, 10 );
+                       }
+               } else {
+                       $network = false;
+                       $bits = false;
+               }
+               return array( $network, (int)$bits );
+       }
+
+       /**
+        * Given a string range in a number of formats, return the
+        * start and end of the range in hexadecimal. For IPv6.
+        *
+        * Formats are:
+        *     2001:0db8:85a3::7344/96                                   CIDR
+        *     2001:0db8:85a3::7344 - 2001:0db8:85a3::7344   Explicit range
+        *     2001:0db8:85a3::7344/96                                   Single IP
+        *
+        * @param $range
+        *
+        * @return array(string, string)
+        */
+       private static function parseRange6( $range ) {
+               # Expand any IPv6 IP
+               $range = IP::sanitizeIP( $range );
+               // CIDR notation...
+               if ( strpos( $range, '/' ) !== false ) {
+                       list( $network, $bits ) = self::parseCIDR6( $range );
+                       if ( $network === false ) {
+                               $start = $end = false;
+                       } else {
+                               $start = wfBaseConvert( $network, 10, 16, 32, false );
+                               # Turn network to binary (again)
+                               $end = wfBaseConvert( $network, 10, 2, 128 );
+                               # Truncate the last (128-$bits) bits and replace them with ones
+                               $end = str_pad( substr( $end, 0, $bits ), 128, 1, STR_PAD_RIGHT );
+                               # Convert to hex
+                               $end = wfBaseConvert( $end, 2, 16, 32, false );
+                               # see toHex() comment
+                               $start = "v6-$start";
+                               $end = "v6-$end";
+                       }
+               // Explicit range notation...
+               } elseif ( strpos( $range, '-' ) !== false ) {
+                       list( $start, $end ) = array_map( 'trim', explode( '-', $range, 2 ) );
+                       $start = self::toUnsigned6( $start );
+                       $end = self::toUnsigned6( $end );
+                       if ( $start > $end ) {
+                               $start = $end = false;
+                       } else {
+                               $start = wfBaseConvert( $start, 10, 16, 32, false );
+                               $end = wfBaseConvert( $end, 10, 16, 32, false );
+                       }
+                       # see toHex() comment
+                       $start = "v6-$start";
+                       $end = "v6-$end";
+               } else {
+                       # Single IP
+                       $start = $end = self::toHex( $range );
+               }
+               if ( $start === false || $end === false ) {
+                       return array( false, false );
+               } else {
+                       return array( $start, $end );
+               }
+       }
+
+       /**
+        * Determine if a given IPv4/IPv6 address is in a given CIDR network
+        *
+        * @param string $addr the address to check against the given range.
+        * @param string $range the range to check the given address against.
+        * @return Boolean: whether or not the given address is in the given range.
+        */
+       public static function isInRange( $addr, $range ) {
+               $hexIP = self::toHex( $addr );
+               list( $start, $end ) = self::parseRange( $range );
+               return ( strcmp( $hexIP, $start ) >= 0 &&
+                       strcmp( $hexIP, $end ) <= 0 );
+       }
+
+       /**
+        * Convert some unusual representations of IPv4 addresses to their
+        * canonical dotted quad representation.
+        *
+        * This currently only checks a few IPV4-to-IPv6 related cases.  More
+        * unusual representations may be added later.
+        *
+        * @param string $addr something that might be an IP address
+        * @return String: valid dotted quad IPv4 address or null
+        */
+       public static function canonicalize( $addr ) {
+               // remove zone info (bug 35738)
+               $addr = preg_replace( '/\%.*/', '', $addr );
+
+               if ( self::isValid( $addr ) ) {
+                       return $addr;
+               }
+               // Turn mapped addresses from ::ce:ffff:1.2.3.4 to 1.2.3.4
+               if ( strpos( $addr, ':' ) !== false && strpos( $addr, '.' ) !== false ) {
+                       $addr = substr( $addr, strrpos( $addr, ':' ) + 1 );
+                       if ( self::isIPv4( $addr ) ) {
+                               return $addr;
+                       }
+               }
+               // IPv6 loopback address
+               $m = array();
+               if ( preg_match( '/^0*' . RE_IPV6_GAP . '1$/', $addr, $m ) ) {
+                       return '127.0.0.1';
+               }
+               // IPv4-mapped and IPv4-compatible IPv6 addresses
+               if ( preg_match( '/^' . RE_IPV6_V4_PREFIX . '(' . RE_IP_ADD . ')$/i', $addr, $m ) ) {
+                       return $m[1];
+               }
+               if ( preg_match( '/^' . RE_IPV6_V4_PREFIX . RE_IPV6_WORD .
+                       ':' . RE_IPV6_WORD . '$/i', $addr, $m ) )
+               {
+                       return long2ip( ( hexdec( $m[1] ) << 16 ) + hexdec( $m[2] ) );
+               }
+
+               return null; // give up
+       }
+
+       /**
+        * Gets rid of unneeded numbers in quad-dotted/octet IP strings
+        * For example, 127.111.113.151/24 -> 127.111.113.0/24
+        * @param string $range IP address to normalize
+        * @return string
+        */
+       public static function sanitizeRange( $range ) {
+               list( /*...*/, $bits ) = self::parseCIDR( $range );
+               list( $start, /*...*/ ) = self::parseRange( $range );
+               $start = self::formatHex( $start );
+               if ( $bits === false ) {
+                       return $start; // wasn't actually a range
+               }
+               return "$start/$bits";
+       }
+}
diff --git a/includes/utils/MWCryptRand.php b/includes/utils/MWCryptRand.php
new file mode 100644 (file)
index 0000000..bac018e
--- /dev/null
@@ -0,0 +1,497 @@
+<?php
+/**
+ * A cryptographic random generator class used for generating secret keys
+ *
+ * This is based in part on Drupal code as well as what we used in our own code
+ * prior to introduction of this class.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @author Daniel Friesen
+ * @file
+ */
+
+class MWCryptRand {
+
+       /**
+        * Minimum number of iterations we want to make in our drift calculations.
+        */
+       const MIN_ITERATIONS = 1000;
+
+       /**
+        * Number of milliseconds we want to spend generating each separate byte
+        * of the final generated bytes.
+        * This is used in combination with the hash length to determine the duration
+        * we should spend doing drift calculations.
+        */
+       const MSEC_PER_BYTE = 0.5;
+
+       /**
+        * Singleton instance for public use
+        */
+       protected static $singleton = null;
+
+       /**
+        * The hash algorithm being used
+        */
+       protected $algo = null;
+
+       /**
+        * The number of bytes outputted by the hash algorithm
+        */
+       protected $hashLength = null;
+
+       /**
+        * A boolean indicating whether the previous random generation was done using
+        * cryptographically strong random number generator or not.
+        */
+       protected $strong = null;
+
+       /**
+        * Initialize an initial random state based off of whatever we can find
+        */
+       protected function initialRandomState() {
+               // $_SERVER contains a variety of unstable user and system specific information
+               // It'll vary a little with each page, and vary even more with separate users
+               // It'll also vary slightly across different machines
+               $state = serialize( $_SERVER );
+
+               // To try vary the system information of the state a bit more
+               // by including the system's hostname into the state
+               $state .= wfHostname();
+
+               // Try to gather a little entropy from the different php rand sources
+               $state .= rand() . uniqid( mt_rand(), true );
+
+               // Include some information about the filesystem's current state in the random state
+               $files = array();
+
+               // We know this file is here so grab some info about ourselves
+               $files[] = __FILE__;
+
+               // We must also have a parent folder, and with the usual file structure, a grandparent
+               $files[] = __DIR__;
+               $files[] = dirname( __DIR__ );
+
+               // The config file is likely the most often edited file we know should be around
+               // so include its stat info into the state.
+               // The constant with its location will almost always be defined, as WebStart.php defines
+               // MW_CONFIG_FILE to $IP/LocalSettings.php unless being configured with MW_CONFIG_CALLBACK (eg. the installer)
+               if ( defined( 'MW_CONFIG_FILE' ) ) {
+                       $files[] = MW_CONFIG_FILE;
+               }
+
+               foreach ( $files as $file ) {
+                       wfSuppressWarnings();
+                       $stat = stat( $file );
+                       wfRestoreWarnings();
+                       if ( $stat ) {
+                               // stat() duplicates data into numeric and string keys so kill off all the numeric ones
+                               foreach ( $stat as $k => $v ) {
+                                       if ( is_numeric( $k ) ) {
+                                               unset( $k );
+                                       }
+                               }
+                               // The absolute filename itself will differ from install to install so don't leave it out
+                               if ( ( $path = realpath( $file ) ) !== false ) {
+                                       $state .= $path;
+                               } else {
+                                       $state .= $file;
+                               }
+                               $state .= implode( '', $stat );
+                       } else {
+                               // The fact that the file isn't there is worth at least a
+                               // minuscule amount of entropy.
+                               $state .= '0';
+                       }
+               }
+
+               // Try and make this a little more unstable by including the varying process
+               // id of the php process we are running inside of if we are able to access it
+               if ( function_exists( 'getmypid' ) ) {
+                       $state .= getmypid();
+               }
+
+               // If available try to increase the instability of the data by throwing in
+               // the precise amount of memory that we happen to be using at the moment.
+               if ( function_exists( 'memory_get_usage' ) ) {
+                       $state .= memory_get_usage( true );
+               }
+
+               // It's mostly worthless but throw the wiki's id into the data for a little more variance
+               $state .= wfWikiID();
+
+               // If we have a secret key or proxy key set then throw it into the state as well
+               global $wgSecretKey, $wgProxyKey;
+               if ( $wgSecretKey ) {
+                       $state .= $wgSecretKey;
+               } elseif ( $wgProxyKey ) {
+                       $state .= $wgProxyKey;
+               }
+
+               return $state;
+       }
+
+       /**
+        * Randomly hash data while mixing in clock drift data for randomness
+        *
+        * @param string $data The data to randomly hash.
+        * @return String The hashed bytes
+        * @author Tim Starling
+        */
+       protected function driftHash( $data ) {
+               // Minimum number of iterations (to avoid slow operations causing the loop to gather little entropy)
+               $minIterations = self::MIN_ITERATIONS;
+               // Duration of time to spend doing calculations (in seconds)
+               $duration = ( self::MSEC_PER_BYTE / 1000 ) * $this->hashLength();
+               // Create a buffer to use to trigger memory operations
+               $bufLength = 10000000;
+               $buffer = str_repeat( ' ', $bufLength );
+               $bufPos = 0;
+
+               // Iterate for $duration seconds or at least $minIterations number of iterations
+               $iterations = 0;
+               $startTime = microtime( true );
+               $currentTime = $startTime;
+               while ( $iterations < $minIterations || $currentTime - $startTime < $duration ) {
+                       // Trigger some memory writing to trigger some bus activity
+                       // This may create variance in the time between iterations
+                       $bufPos = ( $bufPos + 13 ) % $bufLength;
+                       $buffer[$bufPos] = ' ';
+                       // Add the drift between this iteration and the last in as entropy
+                       $nextTime = microtime( true );
+                       $delta = (int)( ( $nextTime - $currentTime ) * 1000000 );
+                       $data .= $delta;
+                       // Every 100 iterations hash the data and entropy
+                       if ( $iterations % 100 === 0 ) {
+                               $data = sha1( $data );
+                       }
+                       $currentTime = $nextTime;
+                       $iterations++;
+               }
+               $timeTaken = $currentTime - $startTime;
+               $data = $this->hash( $data );
+
+               wfDebug( __METHOD__ . ": Clock drift calculation " .
+                       "(time-taken=" . ( $timeTaken * 1000 ) . "ms, " .
+                       "iterations=$iterations, " .
+                       "time-per-iteration=" . ( $timeTaken / $iterations * 1e6 ) . "us)\n" );
+               return $data;
+       }
+
+       /**
+        * Return a rolling random state initially build using data from unstable sources
+        * @return string A new weak random state
+        */
+       protected function randomState() {
+               static $state = null;
+               if ( is_null( $state ) ) {
+                       // Initialize the state with whatever unstable data we can find
+                       // It's important that this data is hashed right afterwards to prevent
+                       // it from being leaked into the output stream
+                       $state = $this->hash( $this->initialRandomState() );
+               }
+               // Generate a new random state based on the initial random state or previous
+               // random state by combining it with clock drift
+               $state = $this->driftHash( $state );
+               return $state;
+       }
+
+       /**
+        * Decide on the best acceptable hash algorithm we have available for hash()
+        * @throws MWException
+        * @return String A hash algorithm
+        */
+       protected function hashAlgo() {
+               if ( !is_null( $this->algo ) ) {
+                       return $this->algo;
+               }
+
+               $algos = hash_algos();
+               $preference = array( 'whirlpool', 'sha256', 'sha1', 'md5' );
+
+               foreach ( $preference as $algorithm ) {
+                       if ( in_array( $algorithm, $algos ) ) {
+                               $this->algo = $algorithm;
+                               wfDebug( __METHOD__ . ": Using the {$this->algo} hash algorithm.\n" );
+                               return $this->algo;
+                       }
+               }
+
+               // We only reach here if no acceptable hash is found in the list, this should
+               // be a technical impossibility since most of php's hash list is fixed and
+               // some of the ones we list are available as their own native functions
+               // But since we already require at least 5.2 and hash() was default in
+               // 5.1.2 we don't bother falling back to methods like sha1 and md5.
+               throw new MWException( "Could not find an acceptable hashing function in hash_algos()" );
+       }
+
+       /**
+        * Return the byte-length output of the hash algorithm we are
+        * using in self::hash and self::hmac.
+        *
+        * @return int Number of bytes the hash outputs
+        */
+       protected function hashLength() {
+               if ( is_null( $this->hashLength ) ) {
+                       $this->hashLength = strlen( $this->hash( '' ) );
+               }
+               return $this->hashLength;
+       }
+
+       /**
+        * Generate an acceptably unstable one-way-hash of some text
+        * making use of the best hash algorithm that we have available.
+        *
+        * @param $data string
+        * @return String A raw hash of the data
+        */
+       protected function hash( $data ) {
+               return hash( $this->hashAlgo(), $data, true );
+       }
+
+       /**
+        * Generate an acceptably unstable one-way-hmac of some text
+        * making use of the best hash algorithm that we have available.
+        *
+        * @param $data string
+        * @param $key string
+        * @return String A raw hash of the data
+        */
+       protected function hmac( $data, $key ) {
+               return hash_hmac( $this->hashAlgo(), $data, $key, true );
+       }
+
+       /**
+        * @see self::wasStrong()
+        */
+       public function realWasStrong() {
+               if ( is_null( $this->strong ) ) {
+                       throw new MWException( __METHOD__ . ' called before generation of random data' );
+               }
+               return $this->strong;
+       }
+
+       /**
+        * @see self::generate()
+        */
+       public function realGenerate( $bytes, $forceStrong = false ) {
+               wfProfileIn( __METHOD__ );
+
+               wfDebug( __METHOD__ . ": Generating cryptographic random bytes for " . wfGetAllCallers( 5 ) . "\n" );
+
+               $bytes = floor( $bytes );
+               static $buffer = '';
+               if ( is_null( $this->strong ) ) {
+                       // Set strength to false initially until we know what source data is coming from
+                       $this->strong = true;
+               }
+
+               if ( strlen( $buffer ) < $bytes ) {
+                       // If available make use of mcrypt_create_iv URANDOM source to generate randomness
+                       // On unix-like systems this reads from /dev/urandom but does it without any buffering
+                       // and bypasses openbasedir restrictions, so it's preferable to reading directly
+                       // On Windows starting in PHP 5.3.0 Windows' native CryptGenRandom is used to generate
+                       // entropy so this is also preferable to just trying to read urandom because it may work
+                       // on Windows systems as well.
+                       if ( function_exists( 'mcrypt_create_iv' ) ) {
+                               wfProfileIn( __METHOD__ . '-mcrypt' );
+                               $rem = $bytes - strlen( $buffer );
+                               $iv = mcrypt_create_iv( $rem, MCRYPT_DEV_URANDOM );
+                               if ( $iv === false ) {
+                                       wfDebug( __METHOD__ . ": mcrypt_create_iv returned false.\n" );
+                               } else {
+                                       $buffer .= $iv;
+                                       wfDebug( __METHOD__ . ": mcrypt_create_iv generated " . strlen( $iv ) . " bytes of randomness.\n" );
+                               }
+                               wfProfileOut( __METHOD__ . '-mcrypt' );
+                       }
+               }
+
+               if ( strlen( $buffer ) < $bytes ) {
+                       // If available make use of openssl's random_pseudo_bytes method to attempt to generate randomness.
+                       // However don't do this on Windows with PHP < 5.3.4 due to a bug:
+                       // http://stackoverflow.com/questions/1940168/openssl-random-pseudo-bytes-is-slow-php
+                       // http://git.php.net/?p=php-src.git;a=commitdiff;h=cd62a70863c261b07f6dadedad9464f7e213cad5
+                       if ( function_exists( 'openssl_random_pseudo_bytes' )
+                               && ( !wfIsWindows() || version_compare( PHP_VERSION, '5.3.4', '>=' ) )
+                       ) {
+                               wfProfileIn( __METHOD__ . '-openssl' );
+                               $rem = $bytes - strlen( $buffer );
+                               $openssl_bytes = openssl_random_pseudo_bytes( $rem, $openssl_strong );
+                               if ( $openssl_bytes === false ) {
+                                       wfDebug( __METHOD__ . ": openssl_random_pseudo_bytes returned false.\n" );
+                               } else {
+                                       $buffer .= $openssl_bytes;
+                                       wfDebug( __METHOD__ . ": openssl_random_pseudo_bytes generated " . strlen( $openssl_bytes ) . " bytes of " . ( $openssl_strong ? "strong" : "weak" ) . " randomness.\n" );
+                               }
+                               if ( strlen( $buffer ) >= $bytes ) {
+                                       // openssl tells us if the random source was strong, if some of our data was generated
+                                       // using it use it's say on whether the randomness is strong
+                                       $this->strong = !!$openssl_strong;
+                               }
+                               wfProfileOut( __METHOD__ . '-openssl' );
+                       }
+               }
+
+               // Only read from urandom if we can control the buffer size or were passed forceStrong
+               if ( strlen( $buffer ) < $bytes && ( function_exists( 'stream_set_read_buffer' ) || $forceStrong ) ) {
+                       wfProfileIn( __METHOD__ . '-fopen-urandom' );
+                       $rem = $bytes - strlen( $buffer );
+                       if ( !function_exists( 'stream_set_read_buffer' ) && $forceStrong ) {
+                               wfDebug( __METHOD__ . ": Was forced to read from /dev/urandom without control over the buffer size.\n" );
+                       }
+                       // /dev/urandom is generally considered the best possible commonly
+                       // available random source, and is available on most *nix systems.
+                       wfSuppressWarnings();
+                       $urandom = fopen( "/dev/urandom", "rb" );
+                       wfRestoreWarnings();
+
+                       // Attempt to read all our random data from urandom
+                       // php's fread always does buffered reads based on the stream's chunk_size
+                       // so in reality it will usually read more than the amount of data we're
+                       // asked for and not storing that risks depleting the system's random pool.
+                       // If stream_set_read_buffer is available set the chunk_size to the amount
+                       // of data we need. Otherwise read 8k, php's default chunk_size.
+                       if ( $urandom ) {
+                               // php's default chunk_size is 8k
+                               $chunk_size = 1024 * 8;
+                               if ( function_exists( 'stream_set_read_buffer' ) ) {
+                                       // If possible set the chunk_size to the amount of data we need
+                                       stream_set_read_buffer( $urandom, $rem );
+                                       $chunk_size = $rem;
+                               }
+                               $random_bytes = fread( $urandom, max( $chunk_size, $rem ) );
+                               $buffer .= $random_bytes;
+                               fclose( $urandom );
+                               wfDebug( __METHOD__ . ": /dev/urandom generated " . strlen( $random_bytes ) . " bytes of randomness.\n" );
+                               if ( strlen( $buffer ) >= $bytes ) {
+                                       // urandom is always strong, set to true if all our data was generated using it
+                                       $this->strong = true;
+                               }
+                       } else {
+                               wfDebug( __METHOD__ . ": /dev/urandom could not be opened.\n" );
+                       }
+                       wfProfileOut( __METHOD__ . '-fopen-urandom' );
+               }
+
+               // If we cannot use or generate enough data from a secure source
+               // use this loop to generate a good set of pseudo random data.
+               // This works by initializing a random state using a pile of unstable data
+               // and continually shoving it through a hash along with a variable salt.
+               // We hash the random state with more salt to avoid the state from leaking
+               // out and being used to predict the /randomness/ that follows.
+               if ( strlen( $buffer ) < $bytes ) {
+                       wfDebug( __METHOD__ . ": Falling back to using a pseudo random state to generate randomness.\n" );
+               }
+               while ( strlen( $buffer ) < $bytes ) {
+                       wfProfileIn( __METHOD__ . '-fallback' );
+                       $buffer .= $this->hmac( $this->randomState(), mt_rand() );
+                       // This code is never really cryptographically strong, if we use it
+                       // at all, then set strong to false.
+                       $this->strong = false;
+                       wfProfileOut( __METHOD__ . '-fallback' );
+               }
+
+               // Once the buffer has been filled up with enough random data to fulfill
+               // the request shift off enough data to handle the request and leave the
+               // unused portion left inside the buffer for the next request for random data
+               $generated = substr( $buffer, 0, $bytes );
+               $buffer = substr( $buffer, $bytes );
+
+               wfDebug( __METHOD__ . ": " . strlen( $buffer ) . " bytes of randomness leftover in the buffer.\n" );
+
+               wfProfileOut( __METHOD__ );
+               return $generated;
+       }
+
+       /**
+        * @see self::generateHex()
+        */
+       public function realGenerateHex( $chars, $forceStrong = false ) {
+               // hex strings are 2x the length of raw binary so we divide the length in half
+               // odd numbers will result in a .5 that leads the generate() being 1 character
+               // short, so we use ceil() to ensure that we always have enough bytes
+               $bytes = ceil( $chars / 2 );
+               // Generate the data and then convert it to a hex string
+               $hex = bin2hex( $this->generate( $bytes, $forceStrong ) );
+               // A bit of paranoia here, the caller asked for a specific length of string
+               // here, and it's possible (eg when given an odd number) that we may actually
+               // have at least 1 char more than they asked for. Just in case they made this
+               // call intending to insert it into a database that does truncation we don't
+               // want to give them too much and end up with their database and their live
+               // code having two different values because part of what we gave them is truncated
+               // hence, we strip out any run of characters longer than what we were asked for.
+               return substr( $hex, 0, $chars );
+       }
+
+       /** Publicly exposed static methods **/
+
+       /**
+        * Return a singleton instance of MWCryptRand
+        * @return MWCryptRand
+        */
+       protected static function singleton() {
+               if ( is_null( self::$singleton ) ) {
+                       self::$singleton = new self;
+               }
+               return self::$singleton;
+       }
+
+       /**
+        * Return a boolean indicating whether or not the source used for cryptographic
+        * random bytes generation in the previously run generate* call
+        * was cryptographically strong.
+        *
+        * @return bool Returns true if the source was strong, false if not.
+        */
+       public static function wasStrong() {
+               return self::singleton()->realWasStrong();
+       }
+
+       /**
+        * Generate a run of (ideally) cryptographically random data and return
+        * it in raw binary form.
+        * You can use MWCryptRand::wasStrong() if you wish to know if the source used
+        * was cryptographically strong.
+        *
+        * @param int $bytes the number of bytes of random data to generate
+        * @param bool $forceStrong Pass true if you want generate to prefer cryptographically
+        *                          strong sources of entropy even if reading from them may steal
+        *                          more entropy from the system than optimal.
+        * @return String Raw binary random data
+        */
+       public static function generate( $bytes, $forceStrong = false ) {
+               return self::singleton()->realGenerate( $bytes, $forceStrong );
+       }
+
+       /**
+        * Generate a run of (ideally) cryptographically random data and return
+        * it in hexadecimal string format.
+        * You can use MWCryptRand::wasStrong() if you wish to know if the source used
+        * was cryptographically strong.
+        *
+        * @param int $chars the number of hex chars of random data to generate
+        * @param bool $forceStrong Pass true if you want generate to prefer cryptographically
+        *                          strong sources of entropy even if reading from them may steal
+        *                          more entropy from the system than optimal.
+        * @return String Hexadecimal random data
+        */
+       public static function generateHex( $chars, $forceStrong = false ) {
+               return self::singleton()->realGenerateHex( $chars, $forceStrong );
+       }
+
+}
diff --git a/includes/utils/MWFunction.php b/includes/utils/MWFunction.php
new file mode 100644 (file)
index 0000000..6d11d17
--- /dev/null
@@ -0,0 +1,61 @@
+<?php
+/**
+ * Helper methods to call functions and instance 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
+ */
+
+class MWFunction {
+
+       /**
+        * @deprecated since 1.22; use call_user_func()
+        * @param $callback
+        * @return mixed
+        */
+       public static function call( $callback ) {
+               wfDeprecated( __METHOD__, '1.22' );
+               $args = func_get_args();
+               return call_user_func_array( 'call_user_func', $args );
+       }
+
+       /**
+        * @deprecated since 1.22; use call_user_func_array()
+        * @param $callback
+        * @param $argsarams
+        * @return mixed
+        */
+       public static function callArray( $callback, $argsarams ) {
+               wfDeprecated( __METHOD__, '1.22' );
+               return call_user_func_array( $callback, $argsarams );
+       }
+
+       /**
+        * @param $class
+        * @param $args array
+        * @return object
+        */
+       public static function newObj( $class, $args = array() ) {
+               if ( !count( $args ) ) {
+                       return new $class;
+               }
+
+               $ref = new ReflectionClass( $class );
+               return $ref->newInstanceArgs( $args );
+       }
+
+}
diff --git a/includes/utils/MappedIterator.php b/includes/utils/MappedIterator.php
new file mode 100644 (file)
index 0000000..70d2032
--- /dev/null
@@ -0,0 +1,114 @@
+<?php
+/**
+ * Convenience class for generating iterators from iterators.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @author Aaron Schulz
+ */
+
+/**
+ * Convenience class for generating iterators from iterators.
+ *
+ * @since 1.21
+ */
+class MappedIterator extends FilterIterator {
+       /** @var callable */
+       protected $vCallback;
+       /** @var callable */
+       protected $aCallback;
+       /** @var array */
+       protected $cache = array();
+
+       protected $rewound = false; // boolean; whether rewind() has been called
+
+       /**
+        * Build an new iterator from a base iterator by having the former wrap the
+        * later, returning the result of "value" callback for each current() invocation.
+        * The callback takes the result of current() on the base iterator as an argument.
+        * The keys of the base iterator are reused verbatim.
+        *
+        * An "accept" callback can also be provided which will be called for each value in
+        * the base iterator (post-callback) and will return true if that value should be
+        * included in iteration of the MappedIterator (otherwise it will be filtered out).
+        *
+        * @param Iterator|Array $iter
+        * @param callable $vCallback Value transformation callback
+        * @param array $options Options map (includes "accept") (since 1.22)
+        * @throws MWException
+        */
+       public function __construct( $iter, $vCallback, array $options = array() ) {
+               if ( is_array( $iter ) ) {
+                       $baseIterator = new ArrayIterator( $iter );
+               } elseif ( $iter instanceof Iterator ) {
+                       $baseIterator = $iter;
+               } else {
+                       throw new MWException( "Invalid base iterator provided." );
+               }
+               parent::__construct( $baseIterator );
+               $this->vCallback = $vCallback;
+               $this->aCallback = isset( $options['accept'] ) ? $options['accept'] : null;
+       }
+
+       public function next() {
+               $this->cache = array();
+               parent::next();
+       }
+
+       public function rewind() {
+               $this->rewound = true;
+               $this->cache = array();
+               parent::rewind();
+       }
+
+       public function accept() {
+               $value = call_user_func( $this->vCallback, $this->getInnerIterator()->current() );
+               $ok = ( $this->aCallback ) ? call_user_func( $this->aCallback, $value ) : true;
+               if ( $ok ) {
+                       $this->cache['current'] = $value;
+               }
+               return $ok;
+       }
+
+       public function key() {
+               $this->init();
+               return parent::key();
+       }
+
+       public function valid() {
+               $this->init();
+               return parent::valid();
+       }
+
+       public function current() {
+               $this->init();
+               if ( parent::valid() ) {
+                       return $this->cache['current'];
+               } else {
+                       return null; // out of range
+               }
+       }
+
+       /**
+        * Obviate the usual need for rewind() before using a FilterIterator in a manual loop
+        */
+       protected function init() {
+               if ( !$this->rewound ) {
+                       $this->rewind();
+               }
+       }
+}
diff --git a/includes/utils/README b/includes/utils/README
new file mode 100644 (file)
index 0000000..b5b8ec8
--- /dev/null
@@ -0,0 +1,9 @@
+The classes in this directory are general utilities for use by any part of
+MediaWiki. They do not favour any particular user interface and are not
+constrained to serve any particular feature. This is similar to includes/libs,
+except that some dependency on the MediaWiki framework (such as the use of
+MWException, Status or wfDebug()) disqualifies them from use outside of
+MediaWiki without modification.
+
+Utilities should not use global configuration variables, rather they should rely
+on the caller to configure their behaviour.
diff --git a/includes/utils/ScopedCallback.php b/includes/utils/ScopedCallback.php
new file mode 100644 (file)
index 0000000..ef22e0a
--- /dev/null
@@ -0,0 +1,73 @@
+<?php
+/**
+ * This file deals with RAII style scoped callbacks.
+ *
+ * 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 for asserting that a callback happens when an dummy object leaves scope
+ *
+ * @since 1.21
+ */
+class ScopedCallback {
+       /** @var callable */
+       protected $callback;
+
+       /**
+        * @param callable $callback
+        * @throws MWException
+        */
+       public function __construct( $callback ) {
+               if ( !is_callable( $callback ) ) {
+                       throw new MWException( "Provided callback is not valid." );
+               }
+               $this->callback = $callback;
+       }
+
+       /**
+        * Trigger a scoped callback and destroy it.
+        * This is the same is just setting it to null.
+        *
+        * @param ScopedCallback $sc
+        */
+       public static function consume( ScopedCallback &$sc = null ) {
+               $sc = null;
+       }
+
+       /**
+        * Destroy a scoped callback without triggering it
+        *
+        * @param ScopedCallback $sc
+        */
+       public static function cancel( ScopedCallback &$sc = null ) {
+               if ( $sc ) {
+                       $sc->callback = null;
+               }
+               $sc = null;
+       }
+
+       /**
+        * Trigger the callback when this leaves scope
+        */
+       function __destruct() {
+               if ( $this->callback !== null ) {
+                       call_user_func( $this->callback );
+               }
+       }
+}
diff --git a/includes/utils/StringUtils.php b/includes/utils/StringUtils.php
new file mode 100644 (file)
index 0000000..c1545e6
--- /dev/null
@@ -0,0 +1,606 @@
+<?php
+/**
+ * Methods to play with strings.
+ *
+ * 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
+ */
+
+/**
+ * A collection of static methods to play with strings.
+ */
+class StringUtils {
+
+       /**
+        * Test whether a string is valid UTF-8.
+        *
+        * The function check for invalid byte sequences, overlong encoding but
+        * not for different normalisations.
+        *
+        * This relies internally on the mbstring function mb_check_encoding()
+        * hardcoded to check against UTF-8. Whenever the function is not available
+        * we fallback to a pure PHP implementation. Setting $disableMbstring to
+        * true will skip the use of mb_check_encoding, this is mostly intended for
+        * unit testing our internal implementation.
+        *
+        * @since 1.21
+        * @note In MediaWiki 1.21, this function did not provide proper UTF-8 validation.
+        * In particular, the pure PHP code path did not in fact check for overlong forms.
+        * Beware of this when backporting code to that version of MediaWiki.
+        *
+        * @param string $value String to check
+        * @param boolean $disableMbstring Whether to use the pure PHP
+        * implementation instead of trying mb_check_encoding. Intended for unit
+        * testing. Default: false
+        *
+        * @return boolean Whether the given $value is a valid UTF-8 encoded string
+        */
+       static function isUtf8( $value, $disableMbstring = false ) {
+               $value = (string)$value;
+
+               // If the mbstring extension is loaded, use it. However, before PHP 5.4, values above
+               // U+10FFFF are incorrectly allowed, so we have to check for them separately.
+               if ( !$disableMbstring && function_exists( 'mb_check_encoding' ) ) {
+                       static $newPHP;
+                       if ( $newPHP === null ) {
+                               $newPHP = !mb_check_encoding( "\xf4\x90\x80\x80", 'UTF-8' );
+                       }
+
+                       return mb_check_encoding( $value, 'UTF-8' ) &&
+                               ( $newPHP || preg_match( "/\xf4[\x90-\xbf]|[\xf5-\xff]/S", $value ) === 0 );
+               }
+
+               if ( preg_match( "/[\x80-\xff]/S", $value ) === 0 ) {
+                       // String contains only ASCII characters, has to be valid
+                       return true;
+               }
+
+               // PCRE implements repetition using recursion; to avoid a stack overflow (and segfault)
+               // for large input, we check for invalid sequences (<= 5 bytes) rather than valid
+               // sequences, which can be as long as the input string is. Multiple short regexes are
+               // used rather than a single long regex for performance.
+               static $regexes;
+               if ( $regexes === null ) {
+                       $cont = "[\x80-\xbf]";
+                       $after = "(?!$cont)"; // "(?:[^\x80-\xbf]|$)" would work here
+                       $regexes = array(
+                               // Continuation byte at the start
+                               "/^$cont/",
+
+                               // ASCII byte followed by a continuation byte
+                               "/[\\x00-\x7f]$cont/S",
+
+                               // Illegal byte
+                               "/[\xc0\xc1\xf5-\xff]/S",
+
+                               // Invalid 2-byte sequence, or valid one then an extra continuation byte
+                               "/[\xc2-\xdf](?!$cont$after)/S",
+
+                               // Invalid 3-byte sequence, or valid one then an extra continuation byte
+                               "/\xe0(?![\xa0-\xbf]$cont$after)/",
+                               "/[\xe1-\xec\xee\xef](?!$cont{2}$after)/S",
+                               "/\xed(?![\x80-\x9f]$cont$after)/",
+
+                               // Invalid 4-byte sequence, or valid one then an extra continuation byte
+                               "/\xf0(?![\x90-\xbf]$cont{2}$after)/",
+                               "/[\xf1-\xf3](?!$cont{3}$after)/S",
+                               "/\xf4(?![\x80-\x8f]$cont{2}$after)/",
+                       );
+               }
+
+               foreach ( $regexes as $regex ) {
+                       if ( preg_match( $regex, $value ) !== 0 ) {
+                               return false;
+                       }
+               }
+               return true;
+       }
+
+       /**
+        * Perform an operation equivalent to
+        *
+        *     preg_replace( "!$startDelim(.*?)$endDelim!", $replace, $subject );
+        *
+        * except that it's worst-case O(N) instead of O(N^2)
+        *
+        * Compared to delimiterReplace(), this implementation is fast but memory-
+        * hungry and inflexible. The memory requirements are such that I don't
+        * recommend using it on anything but guaranteed small chunks of text.
+        *
+        * @param $startDelim
+        * @param $endDelim
+        * @param $replace
+        * @param $subject
+        *
+        * @return string
+        */
+       static function hungryDelimiterReplace( $startDelim, $endDelim, $replace, $subject ) {
+               $segments = explode( $startDelim, $subject );
+               $output = array_shift( $segments );
+               foreach ( $segments as $s ) {
+                       $endDelimPos = strpos( $s, $endDelim );
+                       if ( $endDelimPos === false ) {
+                               $output .= $startDelim . $s;
+                       } else {
+                               $output .= $replace . substr( $s, $endDelimPos + strlen( $endDelim ) );
+                       }
+               }
+               return $output;
+       }
+
+       /**
+        * Perform an operation equivalent to
+        *
+        *   preg_replace_callback( "!$startDelim(.*)$endDelim!s$flags", $callback, $subject )
+        *
+        * This implementation is slower than hungryDelimiterReplace but uses far less
+        * memory. The delimiters are literal strings, not regular expressions.
+        *
+        * If the start delimiter ends with an initial substring of the end delimiter,
+        * e.g. in the case of C-style comments, the behavior differs from the model
+        * regex. In this implementation, the end must share no characters with the
+        * start, so e.g. /*\/ is not considered to be both the start and end of a
+        * comment. /*\/xy/*\/ is considered to be a single comment with contents /xy/.
+        *
+        * @param string $startDelim start delimiter
+        * @param string $endDelim end delimiter
+        * @param $callback Callback: function to call on each match
+        * @param $subject String
+        * @param string $flags regular expression flags
+        * @throws MWException
+        * @return string
+        */
+       static function delimiterReplaceCallback( $startDelim, $endDelim, $callback, $subject, $flags = '' ) {
+               $inputPos = 0;
+               $outputPos = 0;
+               $output = '';
+               $foundStart = false;
+               $encStart = preg_quote( $startDelim, '!' );
+               $encEnd = preg_quote( $endDelim, '!' );
+               $strcmp = strpos( $flags, 'i' ) === false ? 'strcmp' : 'strcasecmp';
+               $endLength = strlen( $endDelim );
+               $m = array();
+
+               while ( $inputPos < strlen( $subject ) &&
+                       preg_match( "!($encStart)|($encEnd)!S$flags", $subject, $m, PREG_OFFSET_CAPTURE, $inputPos ) )
+               {
+                       $tokenOffset = $m[0][1];
+                       if ( $m[1][0] != '' ) {
+                               if ( $foundStart &&
+                                       $strcmp( $endDelim, substr( $subject, $tokenOffset, $endLength ) ) == 0 )
+                               {
+                                       # An end match is present at the same location
+                                       $tokenType = 'end';
+                                       $tokenLength = $endLength;
+                               } else {
+                                       $tokenType = 'start';
+                                       $tokenLength = strlen( $m[0][0] );
+                               }
+                       } elseif ( $m[2][0] != '' ) {
+                               $tokenType = 'end';
+                               $tokenLength = strlen( $m[0][0] );
+                       } else {
+                               throw new MWException( 'Invalid delimiter given to ' . __METHOD__ );
+                       }
+
+                       if ( $tokenType == 'start' ) {
+                               # Only move the start position if we haven't already found a start
+                               # This means that START START END matches outer pair
+                               if ( !$foundStart ) {
+                                       # Found start
+                                       $inputPos = $tokenOffset + $tokenLength;
+                                       # Write out the non-matching section
+                                       $output .= substr( $subject, $outputPos, $tokenOffset - $outputPos );
+                                       $outputPos = $tokenOffset;
+                                       $contentPos = $inputPos;
+                                       $foundStart = true;
+                               } else {
+                                       # Move the input position past the *first character* of START,
+                                       # to protect against missing END when it overlaps with START
+                                       $inputPos = $tokenOffset + 1;
+                               }
+                       } elseif ( $tokenType == 'end' ) {
+                               if ( $foundStart ) {
+                                       # Found match
+                                       $output .= call_user_func( $callback, array(
+                                               substr( $subject, $outputPos, $tokenOffset + $tokenLength - $outputPos ),
+                                               substr( $subject, $contentPos, $tokenOffset - $contentPos )
+                                       ));
+                                       $foundStart = false;
+                               } else {
+                                       # Non-matching end, write it out
+                                       $output .= substr( $subject, $inputPos, $tokenOffset + $tokenLength - $outputPos );
+                               }
+                               $inputPos = $outputPos = $tokenOffset + $tokenLength;
+                       } else {
+                               throw new MWException( 'Invalid delimiter given to ' . __METHOD__ );
+                       }
+               }
+               if ( $outputPos < strlen( $subject ) ) {
+                       $output .= substr( $subject, $outputPos );
+               }
+               return $output;
+       }
+
+       /**
+        * Perform an operation equivalent to
+        *
+        *   preg_replace( "!$startDelim(.*)$endDelim!$flags", $replace, $subject )
+        *
+        * @param string $startDelim start delimiter regular expression
+        * @param string $endDelim end delimiter regular expression
+        * @param string $replace replacement string. May contain $1, which will be
+        *                 replaced by the text between the delimiters
+        * @param string $subject to search
+        * @param string $flags regular expression flags
+        * @return String: The string with the matches replaced
+        */
+       static function delimiterReplace( $startDelim, $endDelim, $replace, $subject, $flags = '' ) {
+               $replacer = new RegexlikeReplacer( $replace );
+               return self::delimiterReplaceCallback( $startDelim, $endDelim,
+                       $replacer->cb(), $subject, $flags );
+       }
+
+       /**
+        * More or less "markup-safe" explode()
+        * Ignores any instances of the separator inside <...>
+        * @param string $separator
+        * @param string $text
+        * @return array
+        */
+       static function explodeMarkup( $separator, $text ) {
+               $placeholder = "\x00";
+
+               // Remove placeholder instances
+               $text = str_replace( $placeholder, '', $text );
+
+               // Replace instances of the separator inside HTML-like tags with the placeholder
+               $replacer = new DoubleReplacer( $separator, $placeholder );
+               $cleaned = StringUtils::delimiterReplaceCallback( '<', '>', $replacer->cb(), $text );
+
+               // Explode, then put the replaced separators back in
+               $items = explode( $separator, $cleaned );
+               foreach ( $items as $i => $str ) {
+                       $items[$i] = str_replace( $placeholder, $separator, $str );
+               }
+
+               return $items;
+       }
+
+       /**
+        * Escape a string to make it suitable for inclusion in a preg_replace()
+        * replacement parameter.
+        *
+        * @param string $string
+        * @return string
+        */
+       static function escapeRegexReplacement( $string ) {
+               $string = str_replace( '\\', '\\\\', $string );
+               $string = str_replace( '$', '\\$', $string );
+               return $string;
+       }
+
+       /**
+        * Workalike for explode() with limited memory usage.
+        * Returns an Iterator
+        * @param string $separator
+        * @param string $subject
+        * @return ArrayIterator|ExplodeIterator
+        */
+       static function explode( $separator, $subject ) {
+               if ( substr_count( $subject, $separator ) > 1000 ) {
+                       return new ExplodeIterator( $separator, $subject );
+               } else {
+                       return new ArrayIterator( explode( $separator, $subject ) );
+               }
+       }
+}
+
+/**
+ * Base class for "replacers", objects used in preg_replace_callback() and
+ * StringUtils::delimiterReplaceCallback()
+ */
+class Replacer {
+
+       /**
+        * @return array
+        */
+       function cb() {
+               return array( &$this, 'replace' );
+       }
+}
+
+/**
+ * Class to replace regex matches with a string similar to that used in preg_replace()
+ */
+class RegexlikeReplacer extends Replacer {
+       var $r;
+
+       /**
+        * @param string $r
+        */
+       function __construct( $r ) {
+               $this->r = $r;
+       }
+
+       /**
+        * @param array $matches
+        * @return string
+        */
+       function replace( $matches ) {
+               $pairs = array();
+               foreach ( $matches as $i => $match ) {
+                       $pairs["\$$i"] = $match;
+               }
+               return strtr( $this->r, $pairs );
+       }
+
+}
+
+/**
+ * Class to perform secondary replacement within each replacement string
+ */
+class DoubleReplacer extends Replacer {
+
+       /**
+        * @param $from
+        * @param $to
+        * @param int $index
+        */
+       function __construct( $from, $to, $index = 0 ) {
+               $this->from = $from;
+               $this->to = $to;
+               $this->index = $index;
+       }
+
+       /**
+        * @param array $matches
+        * @return mixed
+        */
+       function replace( $matches ) {
+               return str_replace( $this->from, $this->to, $matches[$this->index] );
+       }
+}
+
+/**
+ * Class to perform replacement based on a simple hashtable lookup
+ */
+class HashtableReplacer extends Replacer {
+       var $table, $index;
+
+       /**
+        * @param $table
+        * @param int $index
+        */
+       function __construct( $table, $index = 0 ) {
+               $this->table = $table;
+               $this->index = $index;
+       }
+
+       /**
+        * @param array $matches
+        * @return mixed
+        */
+       function replace( $matches ) {
+               return $this->table[$matches[$this->index]];
+       }
+}
+
+/**
+ * Replacement array for FSS with fallback to strtr()
+ * Supports lazy initialisation of FSS resource
+ */
+class ReplacementArray {
+       /*mostly private*/ var $data = false;
+       /*mostly private*/ var $fss = false;
+
+       /**
+        * Create an object with the specified replacement array
+        * The array should have the same form as the replacement array for strtr()
+        * @param array $data
+        */
+       function __construct( $data = array() ) {
+               $this->data = $data;
+       }
+
+       /**
+        * @return array
+        */
+       function __sleep() {
+               return array( 'data' );
+       }
+
+       function __wakeup() {
+               $this->fss = false;
+       }
+
+       /**
+        * Set the whole replacement array at once
+        * @param array $data
+        */
+       function setArray( $data ) {
+               $this->data = $data;
+               $this->fss = false;
+       }
+
+       /**
+        * @return array|bool
+        */
+       function getArray() {
+               return $this->data;
+       }
+
+       /**
+        * Set an element of the replacement array
+        * @param string $from
+        * @param string $to
+        */
+       function setPair( $from, $to ) {
+               $this->data[$from] = $to;
+               $this->fss = false;
+       }
+
+       /**
+        * @param array $data
+        */
+       function mergeArray( $data ) {
+               $this->data = array_merge( $this->data, $data );
+               $this->fss = false;
+       }
+
+       /**
+        * @param ReplacementArray $other
+        */
+       function merge( $other ) {
+               $this->data = array_merge( $this->data, $other->data );
+               $this->fss = false;
+       }
+
+       /**
+        * @param string $from
+        */
+       function removePair( $from ) {
+               unset( $this->data[$from] );
+               $this->fss = false;
+       }
+
+       /**
+        * @param array $data
+        */
+       function removeArray( $data ) {
+               foreach ( $data as $from => $to ) {
+                       $this->removePair( $from );
+               }
+               $this->fss = false;
+       }
+
+       /**
+        * @param string $subject
+        * @return string
+        */
+       function replace( $subject ) {
+               if ( function_exists( 'fss_prep_replace' ) ) {
+                       wfProfileIn( __METHOD__ . '-fss' );
+                       if ( $this->fss === false ) {
+                               $this->fss = fss_prep_replace( $this->data );
+                       }
+                       $result = fss_exec_replace( $this->fss, $subject );
+                       wfProfileOut( __METHOD__ . '-fss' );
+               } else {
+                       wfProfileIn( __METHOD__ . '-strtr' );
+                       $result = strtr( $subject, $this->data );
+                       wfProfileOut( __METHOD__ . '-strtr' );
+               }
+               return $result;
+       }
+}
+
+/**
+ * An iterator which works exactly like:
+ *
+ * foreach ( explode( $delim, $s ) as $element ) {
+ *    ...
+ * }
+ *
+ * Except it doesn't use 193 byte per element
+ */
+class ExplodeIterator implements Iterator {
+       // The subject string
+       var $subject, $subjectLength;
+
+       // The delimiter
+       var $delim, $delimLength;
+
+       // The position of the start of the line
+       var $curPos;
+
+       // The position after the end of the next delimiter
+       var $endPos;
+
+       // The current token
+       var $current;
+
+       /**
+        * Construct a DelimIterator
+        * @param string $delim
+        * @param string $subject
+        */
+       function __construct( $delim, $subject ) {
+               $this->subject = $subject;
+               $this->delim = $delim;
+
+               // Micro-optimisation (theoretical)
+               $this->subjectLength = strlen( $subject );
+               $this->delimLength = strlen( $delim );
+
+               $this->rewind();
+       }
+
+       function rewind() {
+               $this->curPos = 0;
+               $this->endPos = strpos( $this->subject, $this->delim );
+               $this->refreshCurrent();
+       }
+
+       function refreshCurrent() {
+               if ( $this->curPos === false ) {
+                       $this->current = false;
+               } elseif ( $this->curPos >= $this->subjectLength ) {
+                       $this->current = '';
+               } elseif ( $this->endPos === false ) {
+                       $this->current = substr( $this->subject, $this->curPos );
+               } else {
+                       $this->current = substr( $this->subject, $this->curPos, $this->endPos - $this->curPos );
+               }
+       }
+
+       function current() {
+               return $this->current;
+       }
+
+       /**
+        * @return int|bool Current position or boolean false if invalid
+        */
+       function key() {
+               return $this->curPos;
+       }
+
+       /**
+        * @return string
+        */
+       function next() {
+               if ( $this->endPos === false ) {
+                       $this->curPos = false;
+               } else {
+                       $this->curPos = $this->endPos + $this->delimLength;
+                       if ( $this->curPos >= $this->subjectLength ) {
+                               $this->endPos = false;
+                       } else {
+                               $this->endPos = strpos( $this->subject, $this->delim, $this->curPos );
+                       }
+               }
+               $this->refreshCurrent();
+               return $this->current;
+       }
+
+       /**
+        * @return bool
+        */
+       function valid() {
+               return $this->curPos !== false;
+       }
+}
diff --git a/includes/utils/UIDGenerator.php b/includes/utils/UIDGenerator.php
new file mode 100644 (file)
index 0000000..963e51a
--- /dev/null
@@ -0,0 +1,337 @@
+<?php
+/**
+ * This file deals with UID generation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @author Aaron Schulz
+ */
+
+/**
+ * Class for getting statistically unique IDs
+ *
+ * @since 1.21
+ */
+class UIDGenerator {
+       /** @var UIDGenerator */
+       protected static $instance = null;
+
+       protected $nodeId32; // string; node ID in binary (32 bits)
+       protected $nodeId48; // string; node ID in binary (48 bits)
+
+       protected $lockFile88; // string; local file path
+       protected $lockFile128; // string; local file path
+
+       /** @var Array */
+       protected $fileHandles = array(); // cache file handles
+
+       const QUICK_RAND = 1; // get randomness from fast and insecure sources
+
+       protected function __construct() {
+               $idFile = wfTempDir() . '/mw-' . __CLASS__ . '-UID-nodeid';
+               $nodeId = is_file( $idFile ) ? file_get_contents( $idFile ) : '';
+               // Try to get some ID that uniquely identifies this machine (RFC 4122)...
+               if ( !preg_match( '/^[0-9a-f]{12}$/i', $nodeId ) ) {
+                       wfSuppressWarnings();
+                       if ( wfIsWindows() ) {
+                               // http://technet.microsoft.com/en-us/library/bb490913.aspx
+                               $csv = trim( wfShellExec( 'getmac /NH /FO CSV' ) );
+                               $line = substr( $csv, 0, strcspn( $csv, "\n" ) );
+                               $info = str_getcsv( $line );
+                               $nodeId = isset( $info[0] ) ? str_replace( '-', '', $info[0] ) : '';
+                       } elseif ( is_executable( '/sbin/ifconfig' ) ) { // Linux/BSD/Solaris/OS X
+                               // See http://linux.die.net/man/8/ifconfig
+                               $m = array();
+                               preg_match( '/\s([0-9a-f]{2}(:[0-9a-f]{2}){5})\s/',
+                                       wfShellExec( '/sbin/ifconfig -a' ), $m );
+                               $nodeId = isset( $m[1] ) ? str_replace( ':', '', $m[1] ) : '';
+                       }
+                       wfRestoreWarnings();
+                       if ( !preg_match( '/^[0-9a-f]{12}$/i', $nodeId ) ) {
+                               $nodeId = MWCryptRand::generateHex( 12, true );
+                               $nodeId[1] = dechex( hexdec( $nodeId[1] ) | 0x1 ); // set multicast bit
+                       }
+                       file_put_contents( $idFile, $nodeId ); // cache
+               }
+               $this->nodeId32 = wfBaseConvert( substr( sha1( $nodeId ), 0, 8 ), 16, 2, 32 );
+               $this->nodeId48 = wfBaseConvert( $nodeId, 16, 2, 48 );
+               // If different processes run as different users, they may have different temp dirs.
+               // This is dealt with by initializing the clock sequence number and counters randomly.
+               $this->lockFile88 = wfTempDir() . '/mw-' . __CLASS__ . '-UID-88';
+               $this->lockFile128 = wfTempDir() . '/mw-' . __CLASS__ . '-UID-128';
+       }
+
+       /**
+        * @return UIDGenerator
+        */
+       protected static function singleton() {
+               if ( self::$instance === null ) {
+                       self::$instance = new self();
+               }
+               return self::$instance;
+       }
+
+       /**
+        * Get a statistically unique 88-bit unsigned integer ID string.
+        * The bits of the UID are prefixed with the time (down to the millisecond).
+        *
+        * These IDs are suitable as values for the shard key of distributed data.
+        * If a column uses these as values, it should be declared UNIQUE to handle collisions.
+        * New rows almost always have higher UIDs, which makes B-TREE updates on INSERT fast.
+        * They can also be stored "DECIMAL(27) UNSIGNED" or BINARY(11) in MySQL.
+        *
+        * UID generation is serialized on each server (as the node ID is for the whole machine).
+        *
+        * @param $base integer Specifies a base other than 10
+        * @return string Number
+        * @throws MWException
+        */
+       public static function newTimestampedUID88( $base = 10 ) {
+               if ( !is_integer( $base ) || $base > 36 || $base < 2 ) {
+                       throw new MWException( "Base must an integer be between 2 and 36" );
+               }
+               $gen = self::singleton();
+               $time = $gen->getTimestampAndDelay( 'lockFile88', 1, 1024 );
+               return wfBaseConvert( $gen->getTimestampedID88( $time ), 2, $base );
+       }
+
+       /**
+        * @param array $time (UIDGenerator::millitime(), clock sequence)
+        * @return string 88 bits
+        */
+       protected function getTimestampedID88( array $info ) {
+               list( $time, $counter ) = $info;
+               // Take the 46 MSBs of "milliseconds since epoch"
+               $id_bin = $this->millisecondsSinceEpochBinary( $time );
+               // Add a 10 bit counter resulting in 56 bits total
+               $id_bin .= str_pad( decbin( $counter ), 10, '0', STR_PAD_LEFT );
+               // Add the 32 bit node ID resulting in 88 bits total
+               $id_bin .= $this->nodeId32;
+               // Convert to a 1-27 digit integer string
+               if ( strlen( $id_bin ) !== 88 ) {
+                       throw new MWException( "Detected overflow for millisecond timestamp." );
+               }
+               return $id_bin;
+       }
+
+       /**
+        * Get a statistically unique 128-bit unsigned integer ID string.
+        * The bits of the UID are prefixed with the time (down to the millisecond).
+        *
+        * These IDs are suitable as globally unique IDs, without any enforced uniqueness.
+        * New rows almost always have higher UIDs, which makes B-TREE updates on INSERT fast.
+        * They can also be stored as "DECIMAL(39) UNSIGNED" or BINARY(16) in MySQL.
+        *
+        * UID generation is serialized on each server (as the node ID is for the whole machine).
+        *
+        * @param $base integer Specifies a base other than 10
+        * @return string Number
+        * @throws MWException
+        */
+       public static function newTimestampedUID128( $base = 10 ) {
+               if ( !is_integer( $base ) || $base > 36 || $base < 2 ) {
+                       throw new MWException( "Base must be an integer between 2 and 36" );
+               }
+               $gen = self::singleton();
+               $time = $gen->getTimestampAndDelay( 'lockFile128', 16384, 1048576 );
+               return wfBaseConvert( $gen->getTimestampedID128( $time ), 2, $base );
+       }
+
+       /**
+        * @param array $info (UIDGenerator::millitime(), counter, clock sequence)
+        * @return string 128 bits
+        */
+       protected function getTimestampedID128( array $info ) {
+               list( $time, $counter, $clkSeq ) = $info;
+               // Take the 46 MSBs of "milliseconds since epoch"
+               $id_bin = $this->millisecondsSinceEpochBinary( $time );
+               // Add a 20 bit counter resulting in 66 bits total
+               $id_bin .= str_pad( decbin( $counter ), 20, '0', STR_PAD_LEFT );
+               // Add a 14 bit clock sequence number resulting in 80 bits total
+               $id_bin .= str_pad( decbin( $clkSeq ), 14, '0', STR_PAD_LEFT );
+               // Add the 48 bit node ID resulting in 128 bits total
+               $id_bin .= $this->nodeId48;
+               // Convert to a 1-39 digit integer string
+               if ( strlen( $id_bin ) !== 128 ) {
+                       throw new MWException( "Detected overflow for millisecond timestamp." );
+               }
+               return $id_bin;
+       }
+
+       /**
+        * Return an RFC4122 compliant v4 UUID
+        *
+        * @param $flags integer Bitfield (supports UIDGenerator::QUICK_RAND)
+        * @return string
+        * @throws MWException
+        */
+       public static function newUUIDv4( $flags = 0 ) {
+               $hex = ( $flags & self::QUICK_RAND )
+                       ? wfRandomString( 31 )
+                       : MWCryptRand::generateHex( 31 );
+
+               return sprintf( '%s-%s-%s-%s-%s',
+                       // "time_low" (32 bits)
+                       substr( $hex, 0, 8 ),
+                       // "time_mid" (16 bits)
+                       substr( $hex, 8, 4 ),
+                       // "time_hi_and_version" (16 bits)
+                       '4' . substr( $hex, 12, 3 ),
+                       // "clk_seq_hi_res (8 bits, variant is binary 10x) and "clk_seq_low" (8 bits)
+                       dechex( 0x8 | ( hexdec( $hex[15] ) & 0x3 ) ) . $hex[16] . substr( $hex, 17, 2 ),
+                       // "node" (48 bits)
+                       substr( $hex, 19, 12 )
+               );
+       }
+
+       /**
+        * Return an RFC4122 compliant v4 UUID
+        *
+        * @param $flags integer Bitfield (supports UIDGenerator::QUICK_RAND)
+        * @return string 32 hex characters with no hyphens
+        * @throws MWException
+        */
+       public static function newRawUUIDv4( $flags = 0 ) {
+               return str_replace( '-', '', self::newUUIDv4( $flags ) );
+       }
+
+       /**
+        * Get a (time,counter,clock sequence) where (time,counter) is higher
+        * than any previous (time,counter) value for the given clock sequence.
+        * This is useful for making UIDs sequential on a per-node bases.
+        *
+        * @param string $lockFile Name of a local lock file
+        * @param $clockSeqSize integer The number of possible clock sequence values
+        * @param $counterSize integer The number of possible counter values
+        * @return Array (result of UIDGenerator::millitime(), counter, clock sequence)
+        * @throws MWException
+        */
+       protected function getTimestampAndDelay( $lockFile, $clockSeqSize, $counterSize ) {
+               // Get the UID lock file handle
+               if ( isset( $this->fileHandles[$lockFile] ) ) {
+                       $handle = $this->fileHandles[$lockFile];
+               } else {
+                       $handle = fopen( $this->$lockFile, 'cb+' );
+                       $this->fileHandles[$lockFile] = $handle ?: null; // cache
+               }
+               // Acquire the UID lock file
+               if ( $handle === false ) {
+                       throw new MWException( "Could not open '{$this->$lockFile}'." );
+               } elseif ( !flock( $handle, LOCK_EX ) ) {
+                       throw new MWException( "Could not acquire '{$this->$lockFile}'." );
+               }
+               // Get the current timestamp, clock sequence number, last time, and counter
+               rewind( $handle );
+               $data = explode( ' ', fgets( $handle ) ); // "<clk seq> <sec> <msec> <counter> <offset>"
+               $clockChanged = false; // clock set back significantly?
+               if ( count( $data ) == 5 ) { // last UID info already initialized
+                       $clkSeq = (int)$data[0] % $clockSeqSize;
+                       $prevTime = array( (int)$data[1], (int)$data[2] );
+                       $offset = (int)$data[4] % $counterSize; // random counter offset
+                       $counter = 0; // counter for UIDs with the same timestamp
+                       // Delay until the clock reaches the time of the last ID.
+                       // This detects any microtime() drift among processes.
+                       $time = $this->timeWaitUntil( $prevTime );
+                       if ( !$time ) { // too long to delay?
+                               $clockChanged = true; // bump clock sequence number
+                               $time = self::millitime();
+                       } elseif ( $time == $prevTime ) {
+                               // Bump the counter if there are timestamp collisions
+                               $counter = (int)$data[3] % $counterSize;
+                               if ( ++$counter >= $counterSize ) { // sanity (starts at 0)
+                                       flock( $handle, LOCK_UN ); // abort
+                                       throw new MWException( "Counter overflow for timestamp value." );
+                               }
+                       }
+               } else { // last UID info not initialized
+                       $clkSeq = mt_rand( 0, $clockSeqSize - 1 );
+                       $counter = 0;
+                       $offset = mt_rand( 0, $counterSize - 1 );
+                       $time = self::millitime();
+               }
+               // microtime() and gettimeofday() can drift from time() at least on Windows.
+               // The drift is immediate for processes running while the system clock changes.
+               // time() does not have this problem. See https://bugs.php.net/bug.php?id=42659.
+               if ( abs( time() - $time[0] ) >= 2 ) {
+                       // We don't want processes using too high or low timestamps to avoid duplicate
+                       // UIDs and clock sequence number churn. This process should just be restarted.
+                       flock( $handle, LOCK_UN ); // abort
+                       throw new MWException( "Process clock is outdated or drifted." );
+               }
+               // If microtime() is synced and a clock change was detected, then the clock went back
+               if ( $clockChanged ) {
+                       // Bump the clock sequence number and also randomize the counter offset,
+                       // which is useful for UIDs that do not include the clock sequence number.
+                       $clkSeq = ( $clkSeq + 1 ) % $clockSeqSize;
+                       $offset = mt_rand( 0, $counterSize - 1 );
+                       trigger_error( "Clock was set back; sequence number incremented." );
+               }
+               // Update the (clock sequence number, timestamp, counter)
+               ftruncate( $handle, 0 );
+               rewind( $handle );
+               fwrite( $handle, "{$clkSeq} {$time[0]} {$time[1]} {$counter} {$offset}" );
+               fflush( $handle );
+               // Release the UID lock file
+               flock( $handle, LOCK_UN );
+
+               return array( $time, ( $counter + $offset ) % $counterSize, $clkSeq );
+       }
+
+       /**
+        * Wait till the current timestamp reaches $time and return the current
+        * timestamp. This returns false if it would have to wait more than 10ms.
+        *
+        * @param array $time Result of UIDGenerator::millitime()
+        * @return Array|bool UIDGenerator::millitime() result or false
+        */
+       protected function timeWaitUntil( array $time ) {
+               do {
+                       $ct = self::millitime();
+                       if ( $ct >= $time ) { // http://php.net/manual/en/language.operators.comparison.php
+                               return $ct; // current timestamp is higher than $time
+                       }
+               } while ( ( ( $time[0] - $ct[0] ) * 1000 + ( $time[1] - $ct[1] ) ) <= 10 );
+
+               return false;
+       }
+
+       /**
+        * @param array $time Result of UIDGenerator::millitime()
+        * @return string 46 MSBs of "milliseconds since epoch" in binary (rolls over in 4201)
+        */
+       protected function millisecondsSinceEpochBinary( array $time ) {
+               list( $sec, $msec ) = $time;
+               $ts = 1000 * $sec + $msec;
+               if ( $ts > pow( 2, 52 ) ) {
+                       throw new MWException( __METHOD__ .
+                               ': sorry, this function doesn\'t work after the year 144680' );
+               }
+               return substr( wfBaseConvert( $ts, 10, 2, 46 ), -46 );
+       }
+
+       /**
+        * @return Array (current time in seconds, milliseconds since then)
+        */
+       protected static function millitime() {
+               list( $msec, $sec ) = explode( ' ', microtime() );
+               return array( (int)$sec, (int)( $msec * 1000 ) );
+       }
+
+       function __destruct() {
+               array_map( 'fclose', $this->fileHandles );
+       }
+}
diff --git a/includes/utils/ZipDirectoryReader.php b/includes/utils/ZipDirectoryReader.php
new file mode 100644 (file)
index 0000000..307efce
--- /dev/null
@@ -0,0 +1,712 @@
+<?php
+/**
+ * ZIP file directories reader, for the purposes of upload verification.
+ *
+ * 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
+ */
+
+/**
+ * A class for reading ZIP file directories, for the purposes of upload
+ * verification.
+ *
+ * Only a functional interface is provided: ZipFileReader::read(). No access is
+ * given to object instances.
+ *
+ */
+class ZipDirectoryReader {
+       /**
+        * Read a ZIP file and call a function for each file discovered in it.
+        *
+        * Because this class is aimed at verification, an error is raised on
+        * suspicious or ambiguous input, instead of emulating some standard
+        * behavior.
+        *
+        * @param string $fileName The archive file name
+        * @param array $callback The callback function. It will be called for each file
+        *   with a single associative array each time, with members:
+        *
+        *      - name: The file name. Directories conventionally have a trailing
+        *        slash.
+        *
+        *      - mtime: The file modification time, in MediaWiki 14-char format
+        *
+        *      - size: The uncompressed file size
+        *
+        * @param array $options An associative array of read options, with the option
+        *    name in the key. This may currently contain:
+        *
+        *      - zip64: If this is set to true, then we will emulate a
+        *        library with ZIP64 support, like OpenJDK 7. If it is set to
+        *        false, then we will emulate a library with no knowledge of
+        *        ZIP64.
+        *
+        *        NOTE: The ZIP64 code is untested and probably doesn't work. It
+        *        turned out to be easier to just reject ZIP64 archive uploads,
+        *        since they are likely to be very rare. Confirming safety of a
+        *        ZIP64 file is fairly complex. What do you do with a file that is
+        *        ambiguous and broken when read with a non-ZIP64 reader, but valid
+        *        when read with a ZIP64 reader? This situation is normal for a
+        *        valid ZIP64 file, and working out what non-ZIP64 readers will make
+        *        of such a file is not trivial.
+        *
+        * @return Status object. The following fatal errors are defined:
+        *
+        *      - zip-file-open-error: The file could not be opened.
+        *
+        *      - zip-wrong-format: The file does not appear to be a ZIP file.
+        *
+        *      - zip-bad: There was something wrong or ambiguous about the file
+        *        data.
+        *
+        *      - zip-unsupported: The ZIP file uses features which
+        *        ZipDirectoryReader does not support.
+        *
+        * The default messages for those fatal errors are written in a way that
+        * makes sense for upload verification.
+        *
+        * If a fatal error is returned, more information about the error will be
+        * available in the debug log.
+        *
+        * Note that the callback function may be called any number of times before
+        * a fatal error is returned. If this occurs, the data sent to the callback
+        * function should be discarded.
+        */
+       public static function read( $fileName, $callback, $options = array() ) {
+               $zdr = new self( $fileName, $callback, $options );
+               return $zdr->execute();
+       }
+
+       /** The file name */
+       var $fileName;
+
+       /** The opened file resource */
+       var $file;
+
+       /** The cached length of the file, or null if it has not been loaded yet. */
+       var $fileLength;
+
+       /** A segmented cache of the file contents */
+       var $buffer;
+
+       /** The file data callback */
+       var $callback;
+
+       /** The ZIP64 mode */
+       var $zip64 = false;
+
+       /** Stored headers */
+       var $eocdr, $eocdr64, $eocdr64Locator;
+
+       var $data;
+
+       /** The "extra field" ID for ZIP64 central directory entries */
+       const ZIP64_EXTRA_HEADER = 0x0001;
+
+       /** The segment size for the file contents cache */
+       const SEGSIZE = 16384;
+
+       /** The index of the "general field" bit for UTF-8 file names */
+       const GENERAL_UTF8 = 11;
+
+       /** The index of the "general field" bit for central directory encryption */
+       const GENERAL_CD_ENCRYPTED = 13;
+
+       /**
+        * Private constructor
+        */
+       protected function __construct( $fileName, $callback, $options ) {
+               $this->fileName = $fileName;
+               $this->callback = $callback;
+
+               if ( isset( $options['zip64'] ) ) {
+                       $this->zip64 = $options['zip64'];
+               }
+       }
+
+       /**
+        * Read the directory according to settings in $this.
+        *
+        * @return Status
+        */
+       function execute() {
+               $this->file = fopen( $this->fileName, 'r' );
+               $this->data = array();
+               if ( !$this->file ) {
+                       return Status::newFatal( 'zip-file-open-error' );
+               }
+
+               $status = Status::newGood();
+               try {
+                       $this->readEndOfCentralDirectoryRecord();
+                       if ( $this->zip64 ) {
+                               list( $offset, $size ) = $this->findZip64CentralDirectory();
+                               $this->readCentralDirectory( $offset, $size );
+                       } else {
+                               if ( $this->eocdr['CD size'] == 0xffffffff
+                                       || $this->eocdr['CD offset'] == 0xffffffff
+                                       || $this->eocdr['CD entries total'] == 0xffff )
+                               {
+                                       $this->error( 'zip-unsupported', 'Central directory header indicates ZIP64, ' .
+                                               'but we are in legacy mode. Rejecting this upload is necessary to avoid ' .
+                                               'opening vulnerabilities on clients using OpenJDK 7 or later.' );
+                               }
+
+                               list( $offset, $size ) = $this->findOldCentralDirectory();
+                               $this->readCentralDirectory( $offset, $size );
+                       }
+               } catch ( ZipDirectoryReaderError $e ) {
+                       $status->fatal( $e->getErrorCode() );
+               }
+
+               fclose( $this->file );
+               return $status;
+       }
+
+       /**
+        * Throw an error, and log a debug message
+        */
+       function error( $code, $debugMessage ) {
+               wfDebug( __CLASS__ . ": Fatal error: $debugMessage\n" );
+               throw new ZipDirectoryReaderError( $code );
+       }
+
+       /**
+        * Read the header which is at the end of the central directory,
+        * unimaginatively called the "end of central directory record" by the ZIP
+        * spec.
+        */
+       function readEndOfCentralDirectoryRecord() {
+               $info = array(
+                       'signature' => 4,
+                       'disk' => 2,
+                       'CD start disk' => 2,
+                       'CD entries this disk' => 2,
+                       'CD entries total' => 2,
+                       'CD size' => 4,
+                       'CD offset' => 4,
+                       'file comment length' => 2,
+               );
+               $structSize = $this->getStructSize( $info );
+               $startPos = $this->getFileLength() - 65536 - $structSize;
+               if ( $startPos < 0 ) {
+                       $startPos = 0;
+               }
+
+               $block = $this->getBlock( $startPos );
+               $sigPos = strrpos( $block, "PK\x05\x06" );
+               if ( $sigPos === false ) {
+                       $this->error( 'zip-wrong-format',
+                               "zip file lacks EOCDR signature. It probably isn't a zip file." );
+               }
+
+               $this->eocdr = $this->unpack( substr( $block, $sigPos ), $info );
+               $this->eocdr['EOCDR size'] = $structSize + $this->eocdr['file comment length'];
+
+               if ( $structSize + $this->eocdr['file comment length'] != strlen( $block ) - $sigPos ) {
+                       $this->error( 'zip-bad', 'trailing bytes after the end of the file comment' );
+               }
+               if ( $this->eocdr['disk'] !== 0
+                       || $this->eocdr['CD start disk'] !== 0 )
+               {
+                       $this->error( 'zip-unsupported', 'more than one disk (in EOCDR)' );
+               }
+               $this->eocdr += $this->unpack(
+                       $block,
+                       array( 'file comment' => array( 'string', $this->eocdr['file comment length'] ) ),
+                       $sigPos + $structSize );
+               $this->eocdr['position'] = $startPos + $sigPos;
+       }
+
+       /**
+        * Read the header called the "ZIP64 end of central directory locator". An
+        * error will be raised if it does not exist.
+        */
+       function readZip64EndOfCentralDirectoryLocator() {
+               $info = array(
+                       'signature' => array( 'string', 4 ),
+                       'eocdr64 start disk' => 4,
+                       'eocdr64 offset' => 8,
+                       'number of disks' => 4,
+               );
+               $structSize = $this->getStructSize( $info );
+
+               $block = $this->getBlock( $this->getFileLength() - $this->eocdr['EOCDR size']
+                       - $structSize, $structSize );
+               $this->eocdr64Locator = $data = $this->unpack( $block, $info );
+
+               if ( $data['signature'] !== "PK\x06\x07" ) {
+                       // Note: Java will allow this and continue to read the
+                       // EOCDR64, so we have to reject the upload, we can't
+                       // just use the EOCDR header instead.
+                       $this->error( 'zip-bad', 'wrong signature on Zip64 end of central directory locator' );
+               }
+       }
+
+       /**
+        * Read the header called the "ZIP64 end of central directory record". It
+        * may replace the regular "end of central directory record" in ZIP64 files.
+        */
+       function readZip64EndOfCentralDirectoryRecord() {
+               if ( $this->eocdr64Locator['eocdr64 start disk'] != 0
+                       || $this->eocdr64Locator['number of disks'] != 0 )
+               {
+                       $this->error( 'zip-unsupported', 'more than one disk (in EOCDR64 locator)' );
+               }
+
+               $info = array(
+                       'signature' => array( 'string', 4 ),
+                       'EOCDR64 size' => 8,
+                       'version made by' => 2,
+                       'version needed' => 2,
+                       'disk' => 4,
+                       'CD start disk' => 4,
+                       'CD entries this disk' => 8,
+                       'CD entries total' => 8,
+                       'CD size' => 8,
+                       'CD offset' => 8
+               );
+               $structSize = $this->getStructSize( $info );
+               $block = $this->getBlock( $this->eocdr64Locator['eocdr64 offset'], $structSize );
+               $this->eocdr64 = $data = $this->unpack( $block, $info );
+               if ( $data['signature'] !== "PK\x06\x06" ) {
+                       $this->error( 'zip-bad', 'wrong signature on Zip64 end of central directory record' );
+               }
+               if ( $data['disk'] !== 0
+                       || $data['CD start disk'] !== 0 )
+               {
+                       $this->error( 'zip-unsupported', 'more than one disk (in EOCDR64)' );
+               }
+       }
+
+       /**
+        * Find the location of the central directory, as would be seen by a
+        * non-ZIP64 reader.
+        *
+        * @return List containing offset, size and end position.
+        */
+       function findOldCentralDirectory() {
+               $size = $this->eocdr['CD size'];
+               $offset = $this->eocdr['CD offset'];
+               $endPos = $this->eocdr['position'];
+
+               // Some readers use the EOCDR position instead of the offset field
+               // to find the directory, so to be safe, we check if they both agree.
+               if ( $offset + $size != $endPos ) {
+                       $this->error( 'zip-bad', 'the central directory does not immediately precede the end ' .
+                               'of central directory record' );
+               }
+               return array( $offset, $size );
+       }
+
+       /**
+        * Find the location of the central directory, as would be seen by a
+        * ZIP64-compliant reader.
+        *
+        * @return array List containing offset, size and end position.
+        */
+       function findZip64CentralDirectory() {
+               // The spec is ambiguous about the exact rules of precedence between the
+               // ZIP64 headers and the original headers. Here we follow zip_util.c
+               // from OpenJDK 7.
+               $size = $this->eocdr['CD size'];
+               $offset = $this->eocdr['CD offset'];
+               $numEntries = $this->eocdr['CD entries total'];
+               $endPos = $this->eocdr['position'];
+               if ( $size == 0xffffffff
+                       || $offset == 0xffffffff
+                       || $numEntries == 0xffff )
+               {
+                       $this->readZip64EndOfCentralDirectoryLocator();
+
+                       if ( isset( $this->eocdr64Locator['eocdr64 offset'] ) ) {
+                               $this->readZip64EndOfCentralDirectoryRecord();
+                               if ( isset( $this->eocdr64['CD offset'] ) ) {
+                                       $size = $this->eocdr64['CD size'];
+                                       $offset = $this->eocdr64['CD offset'];
+                                       $endPos = $this->eocdr64Locator['eocdr64 offset'];
+                               }
+                       }
+               }
+               // Some readers use the EOCDR position instead of the offset field
+               // to find the directory, so to be safe, we check if they both agree.
+               if ( $offset + $size != $endPos ) {
+                       $this->error( 'zip-bad', 'the central directory does not immediately precede the end ' .
+                               'of central directory record' );
+               }
+               return array( $offset, $size );
+       }
+
+       /**
+        * Read the central directory at the given location
+        */
+       function readCentralDirectory( $offset, $size ) {
+               $block = $this->getBlock( $offset, $size );
+
+               $fixedInfo = array(
+                       'signature' => array( 'string', 4 ),
+                       'version made by' => 2,
+                       'version needed' => 2,
+                       'general bits' => 2,
+                       'compression method' => 2,
+                       'mod time' => 2,
+                       'mod date' => 2,
+                       'crc-32' => 4,
+                       'compressed size' => 4,
+                       'uncompressed size' => 4,
+                       'name length' => 2,
+                       'extra field length' => 2,
+                       'comment length' => 2,
+                       'disk number start' => 2,
+                       'internal attrs' => 2,
+                       'external attrs' => 4,
+                       'local header offset' => 4,
+               );
+               $fixedSize = $this->getStructSize( $fixedInfo );
+
+               $pos = 0;
+               while ( $pos < $size ) {
+                       $data = $this->unpack( $block, $fixedInfo, $pos );
+                       $pos += $fixedSize;
+
+                       if ( $data['signature'] !== "PK\x01\x02" ) {
+                               $this->error( 'zip-bad', 'Invalid signature found in directory entry' );
+                       }
+
+                       $variableInfo = array(
+                               'name' => array( 'string', $data['name length'] ),
+                               'extra field' => array( 'string', $data['extra field length'] ),
+                               'comment' => array( 'string', $data['comment length'] ),
+                       );
+                       $data += $this->unpack( $block, $variableInfo, $pos );
+                       $pos += $this->getStructSize( $variableInfo );
+
+                       if ( $this->zip64 && (
+                                  $data['compressed size'] == 0xffffffff
+                               || $data['uncompressed size'] == 0xffffffff
+                               || $data['local header offset'] == 0xffffffff ) )
+                       {
+                               $zip64Data = $this->unpackZip64Extra( $data['extra field'] );
+                               if ( $zip64Data ) {
+                                       $data = $zip64Data + $data;
+                               }
+                       }
+
+                       if ( $this->testBit( $data['general bits'], self::GENERAL_CD_ENCRYPTED ) ) {
+                               $this->error( 'zip-unsupported', 'central directory encryption is not supported' );
+                       }
+
+                       // Convert the timestamp into MediaWiki format
+                       // For the format, please see the MS-DOS 2.0 Programmer's Reference,
+                       // pages 3-5 and 3-6.
+                       $time = $data['mod time'];
+                       $date = $data['mod date'];
+
+                       $year = 1980 + ( $date >> 9 );
+                       $month = ( $date >> 5 ) & 15;
+                       $day = $date & 31;
+                       $hour = ( $time >> 11 ) & 31;
+                       $minute = ( $time >> 5 ) & 63;
+                       $second = ( $time & 31 ) * 2;
+                       $timestamp = sprintf( "%04d%02d%02d%02d%02d%02d",
+                               $year, $month, $day, $hour, $minute, $second );
+
+                       // Convert the character set in the file name
+                       if ( !function_exists( 'iconv' )
+                               || $this->testBit( $data['general bits'], self::GENERAL_UTF8 ) )
+                       {
+                               $name = $data['name'];
+                       } else {
+                               $name = iconv( 'CP437', 'UTF-8', $data['name'] );
+                       }
+
+                       // Compile a data array for the user, with a sensible format
+                       $userData = array(
+                               'name' => $name,
+                               'mtime' => $timestamp,
+                               'size' => $data['uncompressed size'],
+                       );
+                       call_user_func( $this->callback, $userData );
+               }
+       }
+
+       /**
+        * Interpret ZIP64 "extra field" data and return an associative array.
+        * @return array|bool
+        */
+       function unpackZip64Extra( $extraField ) {
+               $extraHeaderInfo = array(
+                       'id' => 2,
+                       'size' => 2,
+               );
+               $extraHeaderSize = $this->getStructSize( $extraHeaderInfo );
+
+               $zip64ExtraInfo = array(
+                       'uncompressed size' => 8,
+                       'compressed size' => 8,
+                       'local header offset' => 8,
+                       'disk number start' => 4,
+               );
+
+               $extraPos = 0;
+               while ( $extraPos < strlen( $extraField ) ) {
+                       $extra = $this->unpack( $extraField, $extraHeaderInfo, $extraPos );
+                       $extraPos += $extraHeaderSize;
+                       $extra += $this->unpack( $extraField,
+                               array( 'data' => array( 'string', $extra['size'] ) ),
+                               $extraPos );
+                       $extraPos += $extra['size'];
+
+                       if ( $extra['id'] == self::ZIP64_EXTRA_HEADER ) {
+                               return $this->unpack( $extra['data'], $zip64ExtraInfo );
+                       }
+               }
+
+               return false;
+       }
+
+       /**
+        * Get the length of the file.
+        */
+       function getFileLength() {
+               if ( $this->fileLength === null ) {
+                       $stat = fstat( $this->file );
+                       $this->fileLength = $stat['size'];
+               }
+               return $this->fileLength;
+       }
+
+       /**
+        * Get the file contents from a given offset. If there are not enough bytes
+        * in the file to satisfy the request, an exception will be thrown.
+        *
+        * @param int $start The byte offset of the start of the block.
+        * @param int $length The number of bytes to return. If omitted, the remainder
+        *    of the file will be returned.
+        *
+        * @return string
+        */
+       function getBlock( $start, $length = null ) {
+               $fileLength = $this->getFileLength();
+               if ( $start >= $fileLength ) {
+                       $this->error( 'zip-bad', "getBlock() requested position $start, " .
+                               "file length is $fileLength" );
+               }
+               if ( $length === null ) {
+                       $length = $fileLength - $start;
+               }
+               $end = $start + $length;
+               if ( $end > $fileLength ) {
+                       $this->error( 'zip-bad', "getBlock() requested end position $end, " .
+                               "file length is $fileLength" );
+               }
+               $startSeg = floor( $start / self::SEGSIZE );
+               $endSeg = ceil( $end / self::SEGSIZE );
+
+               $block = '';
+               for ( $segIndex = $startSeg; $segIndex <= $endSeg; $segIndex++ ) {
+                       $block .= $this->getSegment( $segIndex );
+               }
+
+               $block = substr( $block,
+                       $start - $startSeg * self::SEGSIZE,
+                       $length );
+
+               if ( strlen( $block ) < $length ) {
+                       $this->error( 'zip-bad', 'getBlock() returned an unexpectedly small amount of data' );
+               }
+
+               return $block;
+       }
+
+       /**
+        * Get a section of the file starting at position $segIndex * self::SEGSIZE,
+        * of length self::SEGSIZE. The result is cached. This is a helper function
+        * for getBlock().
+        *
+        * If there are not enough bytes in the file to satisfy the request, the
+        * return value will be truncated. If a request is made for a segment beyond
+        * the end of the file, an empty string will be returned.
+        * @return string
+        */
+       function getSegment( $segIndex ) {
+               if ( !isset( $this->buffer[$segIndex] ) ) {
+                       $bytePos = $segIndex * self::SEGSIZE;
+                       if ( $bytePos >= $this->getFileLength() ) {
+                               $this->buffer[$segIndex] = '';
+                               return '';
+                       }
+                       if ( fseek( $this->file, $bytePos ) ) {
+                               $this->error( 'zip-bad', "seek to $bytePos failed" );
+                       }
+                       $seg = fread( $this->file, self::SEGSIZE );
+                       if ( $seg === false ) {
+                               $this->error( 'zip-bad', "read from $bytePos failed" );
+                       }
+                       $this->buffer[$segIndex] = $seg;
+               }
+               return $this->buffer[$segIndex];
+       }
+
+       /**
+        * Get the size of a structure in bytes. See unpack() for the format of $struct.
+        * @return int
+        */
+       function getStructSize( $struct ) {
+               $size = 0;
+               foreach ( $struct as $type ) {
+                       if ( is_array( $type ) ) {
+                               list( , $fieldSize ) = $type;
+                               $size += $fieldSize;
+                       } else {
+                               $size += $type;
+                       }
+               }
+               return $size;
+       }
+
+       /**
+        * Unpack a binary structure. This is like the built-in unpack() function
+        * except nicer.
+        *
+        * @param string $string The binary data input
+        *
+        * @param array $struct An associative array giving structure members and their
+        *    types. In the key is the field name. The value may be either an
+        *    integer, in which case the field is a little-endian unsigned integer
+        *    encoded in the given number of bytes, or an array, in which case the
+        *    first element of the array is the type name, and the subsequent
+        *    elements are type-dependent parameters. Only one such type is defined:
+        *       - "string": The second array element gives the length of string.
+        *          Not null terminated.
+        *
+        * @param int $offset The offset into the string at which to start unpacking.
+        *
+        * @throws MWException
+        * @return array Unpacked associative array. Note that large integers in the input
+        *    may be represented as floating point numbers in the return value, so
+        *    the use of weak comparison is advised.
+        */
+       function unpack( $string, $struct, $offset = 0 ) {
+               $size = $this->getStructSize( $struct );
+               if ( $offset + $size > strlen( $string ) ) {
+                       $this->error( 'zip-bad', 'unpack() would run past the end of the supplied string' );
+               }
+
+               $data = array();
+               $pos = $offset;
+               foreach ( $struct as $key => $type ) {
+                       if ( is_array( $type ) ) {
+                               list( $typeName, $fieldSize ) = $type;
+                               switch ( $typeName ) {
+                               case 'string':
+                                       $data[$key] = substr( $string, $pos, $fieldSize );
+                                       $pos += $fieldSize;
+                                       break;
+                               default:
+                                       throw new MWException( __METHOD__ . ": invalid type \"$typeName\"" );
+                               }
+                       } else {
+                               // Unsigned little-endian integer
+                               $length = intval( $type );
+
+                               // Calculate the value. Use an algorithm which automatically
+                               // upgrades the value to floating point if necessary.
+                               $value = 0;
+                               for ( $i = $length - 1; $i >= 0; $i-- ) {
+                                       $value *= 256;
+                                       $value += ord( $string[$pos + $i] );
+                               }
+
+                               // Throw an exception if there was loss of precision
+                               if ( $value > pow( 2, 52 ) ) {
+                                       $this->error( 'zip-unsupported', 'number too large to be stored in a double. ' .
+                                               'This could happen if we tried to unpack a 64-bit structure ' .
+                                               'at an invalid location.' );
+                               }
+                               $data[$key] = $value;
+                               $pos += $length;
+                       }
+               }
+
+               return $data;
+       }
+
+       /**
+        * Returns a bit from a given position in an integer value, converted to
+        * boolean.
+        *
+        * @param $value integer
+        * @param int $bitIndex The index of the bit, where 0 is the LSB.
+        * @return bool
+        */
+       function testBit( $value, $bitIndex ) {
+               return (bool)( ( $value >> $bitIndex ) & 1 );
+       }
+
+       /**
+        * Debugging helper function which dumps a string in hexdump -C format.
+        */
+       function hexDump( $s ) {
+               $n = strlen( $s );
+               for ( $i = 0; $i < $n; $i += 16 ) {
+                       printf( "%08X ", $i );
+                       for ( $j = 0; $j < 16; $j++ ) {
+                               print " ";
+                               if ( $j == 8 ) {
+                                       print " ";
+                               }
+                               if ( $i + $j >= $n ) {
+                                       print "  ";
+                               } else {
+                                       printf( "%02X", ord( $s[$i + $j] ) );
+                               }
+                       }
+
+                       print "  |";
+                       for ( $j = 0; $j < 16; $j++ ) {
+                               if ( $i + $j >= $n ) {
+                                       print " ";
+                               } elseif ( ctype_print( $s[$i + $j] ) ) {
+                                       print $s[$i + $j];
+                               } else {
+                                       print '.';
+                               }
+                       }
+                       print "|\n";
+               }
+       }
+}
+
+/**
+ * Internal exception class. Will be caught by private code.
+ */
+class ZipDirectoryReaderError extends Exception {
+       var $errorCode;
+
+       function __construct( $code ) {
+               $this->errorCode = $code;
+               parent::__construct( "ZipDirectoryReader error: $code" );
+       }
+
+       /**
+        * @return mixed
+        */
+       function getErrorCode() {
+               return $this->errorCode;
+       }
+}
index 527f382..dc87bc8 100644 (file)
@@ -3949,6 +3949,16 @@ class Language {
                return self::$dataCache->getItem( $this->mCode, 'linkTrail' );
        }
 
+       /**
+        * A regular expression character set to match legal word-prefixing
+        * characters which should be merged onto a link of the form foo[[bar]].
+        *
+        * @return string
+        */
+       public function linkPrefixCharset() {
+               return self::$dataCache->getItem( $this->mCode, 'linkPrefixCharset' );
+       }
+
        /**
         * @return Language
         */
index 79ddb6a..ccf9b1e 100644 (file)
@@ -1263,7 +1263,10 @@ class ConverterRule {
                $variants = $this->mConverter->mVariants;
                $varsep_pattern = $this->mConverter->getVarSeparatorPattern();
 
+               // Split according to $varsep_pattern, but ignore semicolons from HTML entities
+               $rules = preg_replace( '/(&[#a-zA-Z0-9]+);/', "$1\x01", $rules );
                $choice = preg_split( $varsep_pattern, $rules );
+               $choice = str_replace( "\x01", ';', $choice );
 
                foreach ( $choice as $c ) {
                        $v = explode( ':', $c, 2 );
index 929c513..3848a0b 100644 (file)
        'crh' => 'qırımtatarca',   # Crimean Tatar (multiple scripts - defaults to Latin)
        'crh-latn' => "qırımtatarca (Latin)\xE2\x80\x8E",       # Crimean Tatar (Latin)
        'crh-cyrl' => "къырымтатарджа (Кирилл)\xE2\x80\x8E",       # Crimean Tatar (Cyrillic)
-       'cs' => 'česky',       # Czech
+       'cs' => 'čeština',    # Czech
        'csb' => 'kaszëbsczi', # Cassubian
        'cu' => 'словѣ́ньскъ / ⰔⰎⰑⰂⰡⰐⰠⰔⰍⰟ',      # Old Church Slavonic (ancient language)
        'cv' => 'Чӑвашла',       # Chuvash
index 06c9e60..0038b29 100644 (file)
@@ -164,12 +164,12 @@ $messages = array(
 'tog-hidepatrolled' => 'Peusom neuandam teupatroli bak neuubah paléng barô',
 'tog-newpageshidepatrolled' => 'Peusom ôn teupatroli nibak dapeuta ôn barô',
 'tog-extendwatchlist' => 'Peuhah dapeuta keunalön keu peuleumah ban dum neuubah, kon nyang paléng barô mantöng',
-'tog-usenewrc' => 'Nguy neuleumah neuubah barô tingkat lanjut (peureulèë JavaScript)',
+'tog-usenewrc' => 'Peusaho neuandam bak neuleumah neuubah barô ngon dapeuta keunalon meunurot ôn',
 'tog-numberheadings' => 'Bôh numbô nan keudroë',
-'tog-showtoolbar' => 'Peuleumah <em>toolbar</em> (bateuëng alat) andam',
+'tog-showtoolbar' => 'Peuleumah bateuëng alat andam',
 'tog-editondblclick' => 'Andam ôn ngon duwa go teugon',
 'tog-editsection' => 'Peujeuet andam bideueng rot hubong [andam]',
-'tog-editsectiononrightclick' => 'Peujeuet andam bideueng ngon teugon blah uneun bak nan bideueng (peureulee JavaScript)',
+'tog-editsectiononrightclick' => 'Peujeuet andam bideueng ngon teugon blah uneun bak nan bideueng',
 'tog-showtoc' => 'Peuleumah dapeuta asoe (keu on-on nyang na leubeh nibak 3 boh aneuk ulee)',
 'tog-rememberpassword' => 'Ingat lôn tamong bak peuramban nyoë (keu paleng trep $1 {{PLURAL:$1|uroë|uroë}})',
 'tog-watchcreations' => 'Tamah halaman nyang lonpeugot u dapeuta keunalon',
@@ -187,7 +187,7 @@ $messages = array(
 'tog-shownumberswatching' => 'Peuleumah jumeulah ureueng kalon',
 'tog-oldsig' => 'Tanda jaroe jinoe:',
 'tog-fancysig' => 'Peujeuet tanda jaroe sibagoe naseukah wiki (hana hubong keudroe)',
-'tog-uselivepreview' => 'Nguy peuleumah hase langsong (JavaScript) (baci)',
+'tog-uselivepreview' => 'Nguy peuleumah hase langsong (baci)',
 'tog-forceeditsummary' => 'Peuingat lon meunyo plok neuringkaih neuandam mantong soh',
 'tog-watchlisthideown' => 'Peusöm nyang lôn andam nibak dapeuta keunalön',
 'tog-watchlisthidebots' => 'Peusöm nyang teu andam nibak sagoö nyang bak dapeuta keunalön',
@@ -264,16 +264,25 @@ $messages = array(
 'oct' => 'Siplôh',
 'nov' => 'Siblaih',
 'dec' => 'Duwa Blaih',
-'january-date' => 'Buleuën Sa',
-'february-date' => 'Buleuën Duwa',
-'march-date' => 'Buleuën Lhèë',
+'january-date' => '$1 Buleuën Sa',
+'february-date' => '$1 Buleuën Duwa',
+'march-date' => '$1 Buleuën Lhèë',
+'april-date' => '$1 Buleuën Peuët',
+'may-date' => '$1 Buleuën Limong',
+'june-date' => '$1 Buleuën Nam',
+'july-date' => '$1 Buleuën Tujôh',
+'august-date' => '$1 Buleuën Lapan',
+'september-date' => '$1 Buleuën Sikureuëng',
+'october-date' => '$1 Buleuën Siplôh',
+'november-date' => '$1 Buleuën Siblaih',
+'december-date' => '$1 Buleuën Duwa Blaih',
 
 # Categories related messages
 'pagecategories' => '{{PLURAL:$1|Kawan|Kawan}}',
 'category_header' => 'Teunuléh lam kawan "$1"',
 'subcategories' => 'Aneuk kawan',
 'category-media-header' => 'Peukakaih lam kawan "$1"',
-'category-empty' => "''Kawan nyoë jinoë hat hana teunuléh atawa media.''",
+'category-empty' => "''Kawan nyoë jinoë hat hana halaman atawa media.''",
 'hidden-categories' => '{{PLURAL:$1|Kawan teusom|Kawan teusom}}',
 'hidden-category-category' => 'Kawan teusom',
 'category-subcat-count' => '{{PLURAL:$2|Kawan nyoë  cit na saboh yupkawan nyoë.|Kawan nyoë na {{PLURAL:$1|yupkawan|$1 yupkawan}} nyoë, dari ban dum $2.}}',
@@ -292,7 +301,7 @@ $messages = array(
 'newwindow' => '(peuhah bak tingkap barô)',
 'cancel' => 'Peubateuë',
 'moredotdotdot' => 'Lom...',
-'morenotlisted' => 'Lom...',
+'morenotlisted' => 'Dapeuta nyoe hana leungkap',
 'mypage' => 'Ôn',
 'mytalk' => 'Marit',
 'anontalk' => 'Peugah haba IP nyoë.',
@@ -339,7 +348,7 @@ $messages = array(
 'history_short' => 'Riwayat',
 'updatedmarker' => 'geuubah yoh seunaweue keuneulheueh lon phon kon',
 'printableversion' => 'Seunalén rakam',
-'permalink' => 'Hubông teutap',
+'permalink' => 'Seuneumat teutap',
 'print' => 'Rakam',
 'view' => 'Beuet',
 'edit' => 'Andam',
@@ -365,7 +374,7 @@ $messages = array(
 'articlepage' => 'Eu ôn asoë',
 'talk' => 'Marit',
 'views' => 'Ôn',
-'toolbox' => 'Plôk alat',
+'toolbox' => 'Alat',
 'userpage' => 'Eu on ureueng nguy',
 'projectpage' => 'Eu ôn buët',
 'imagepage' => 'Eu on beureukaih',
@@ -380,7 +389,7 @@ $messages = array(
 'lastmodifiedat' => 'Ôn nyoë seuneulheuëh geuubah bak $2, $1.',
 'viewcount' => 'On nyoe ka geusaweue {{PLURAL:$1|sigo|$sigo}}.<br />',
 'protectedpage' => 'Ôn teupeulindông',
-'jumpto' => 'Lansông u:',
+'jumpto' => 'Grôp u:',
 'jumptonavigation' => 'navigasi',
 'jumptosearch' => 'mita',
 'view-pool-error' => "Meu'ah, server teungoh sibuk jinoe
@@ -395,7 +404,7 @@ $1",
 # All link text and link target definitions of links into project namespace that get used by other message strings, with the exception of user group pages (see grouppage).
 'aboutsite' => 'Bhaih {{SITENAME}}',
 'aboutpage' => 'Project:Bhaih',
-'copyright' => 'Asoë nyang na saban ngön',
+'copyright' => "Asoë na meunurot $1 keucuali meunyo na hay la'en nyang geupeugah",
 'copyrightpage' => '{{ns:project}}:Hak karang',
 'currentevents' => 'Haba barô',
 'currentevents-url' => 'Project:Haba barô',
@@ -476,6 +485,10 @@ Dapeuta on kusuih nyang sah jeuet neu'eu bak [[Special:SpecialPages|{{int:specia
 'error' => 'Seunalah',
 'databaseerror' => 'Kesalahan basis data',
 'databaseerror-text' => 'Saboh salah bak nè data ka teujadi. Nyoë meuhat na nyang han paih bak peukakaih droëneuh',
+'databaseerror-textcl' => 'Teunanyong basis data teungoh kacho',
+'databaseerror-query' => 'Teunanyong: $1',
+'databaseerror-function' => 'Guna: $1',
+'databaseerror-error' => 'Salah: $1',
 'laggedslavemode' => 'Peuneugah: On nyoe kadang hana neuubah baro',
 'readonly' => 'Basis data geurok',
 'enterlockreason' => 'Pasoe daleh neurok ngon pajan jeuet geupeuhah',
@@ -508,10 +521,11 @@ Kadang ka na soe sampoh.',
 'cannotdelete-title' => 'H\'an jeuet sampoh on "$1"',
 'delete-hook-aborted' => "Seunampoh geupeubateue le kaw'et parser.
 Hana jeuneulaih.",
+'no-null-revision' => 'H\'an jeuet peugot revisi null baro keu halaman "$1"',
 'badtitle' => 'Nan hana sah',
 'badtitletext' => 'Nan ôn nyang neulakèë hana sah, soh, atawa nan antarabahsa atawa antarawiki nyang salah sambông.',
-'perfcached' => 'Data di yup nyoe geucok nibak peuniyoh ngon kadang kon data baro. {{PLURAL:$1||}}$1 hase maksimum na bak beuen.',
-'perfcachedts' => 'Data di yup nyoe geupeusom, ngon geupeubaro keuneulheueh bak $1. {{PLURAL:$1||}}$1 hase maksimal na lam beuen.',
+'perfcached' => 'Data di yup nyoe geupeusom ngon kadang kon data baro. {{PLURAL:$1|saboh hase|$1 hase}} maksimum na lam beujana.',
+'perfcachedts' => 'Data di yup nyoe geupeusom, ngon geupeubaro seuneulheueh bak $1. {{PLURAL:$4|saboh hase|$4 hase}} paleng le na lam beujana.',
 'querypage-no-updates' => "Beunaro keu on nyoe hat nyoe teungoh h'an jeuet.
 Data sinoe h'an geupasoe ulang.",
 'wrong_wfQuery_params' => 'Parameter salah u wfQuery()<br />
@@ -522,17 +536,35 @@ Neulakee: $2',
 'actionthrottled' => 'Buet geupeubataih',
 'actionthrottledtext' => 'Sibagoe saboh seunipat lawan-spam, droeneuh geupeubataih nibak neupeulaku buet nyoe le that go lam watee paneuk, ngon droeneuh ka leubeh nibak bataih.
 Neuci lom lam padum minet.',
+'protectedpagetext' => 'Laman nyoe ka geupeulindong mangat bek jeuet geuandam',
 'viewsourcetext' => 'Droëneuh  jeuët neu’eu',
 'viewyourtext' => 'Droëneuh meuidzin kalon ngon neucok nè andam droëneuh u ôn nyoë',
+'protectedinterface' => 'Halaman nyoe na teks muka keu muka keu peukakaih leumiek ngon geupeulindong mangat bek jeuet jipeureuloh.
+Keu neuk tamah atawa ubah teujeumah keu ban dum wiki, neungui [//translatewiki.net/ translatewiki.net], proyek lokalisasi MediaWiki.',
 'ns-specialprotected' => 'Ôn khusuih bèk neuandam',
 'exception-nologin' => 'Hana tamong lom',
 
 # Login and logout pages
+'welcomeuser' => 'Seulamat trôk teuka, $1 !',
+'welcomecreation-msg' => 'Nan droëneuh ka geupeugöt. 
+Bèk tuwo neuatô [[Special:Preferences|geunalak {{SITENAME}}]] droëneuh.',
 'yourname' => 'Ureuëng nguy:',
+'userlogin-yourname' => 'Ureuëng nguy',
 'userlogin-yourname-ph' => 'Peutamöng nan ureuëng nguy droëneuh',
+'createacct-another-username-ph' => 'Pasoë nan ureuëng nguy droëneuh',
 'yourpassword' => 'Lageuëm:',
+'userlogin-yourpassword' => 'Lageuëm rahsia',
+'userlogin-yourpassword-ph' => 'Pasoë lageuëm rahsia droëneuh',
+'createacct-yourpassword-ph' => 'Pasoë lageuëm rahsia',
 'yourpasswordagain' => 'Pasoë lom lageuëm:',
+'createacct-yourpasswordagain' => 'Peunyo lageuëm rahsia',
+'createacct-yourpasswordagain-ph' => 'Pasoë lom lageuëm rahsia',
 'remembermypassword' => 'Ingat lôn tamong bak peuramban nyoë (keu paleng trep $1 {{PLURAL:$1|uroë|uroë}})',
+'userlogin-remembermypassword' => 'Peubiyeuë lôn tamöng',
+'userlogin-signwithsecure' => 'Nguy server aman',
+'yourdomainname' => 'Domain droeneuh:',
+'password-change-forbidden' => 'Droëneuh h‘an jeuët neuubah lageuëm rahsia bak wiki nyoë.',
+'externaldberror' => 'Na seunalah bak peusahèh basis data luwa atawa droëneuh hana geubri idin keu neupeubarô akun luwa droëneuh',
 'login' => 'Tamöng',
 'nav-login-createaccount' => 'Tamöng / dapeuta',
 'loginprompt' => "Droëneuh suwah/payah neupeu’udép ''cookies'' mangat jeuët neutamong u {{SITENAME}}",
@@ -541,12 +573,44 @@ Neuci lom lam padum minet.',
 'logout' => 'Teubiët',
 'userlogout' => 'Teubiët',
 'notloggedin' => 'Hana tamong lom',
+'userlogin-noaccount' => 'Goh lom neudapeuta?',
+'userlogin-joinproject' => 'Neugabông ngön {{SITENAME}}',
 'nologin' => "Goh na nan ureuëng nguy? '''$1'''.",
 'nologinlink' => 'Peudapeuta nan barô',
 'createaccount' => 'Peudapeuta nan barô',
 'gotaccount' => "Ka lheuëh neudapeuta? '''$1'''.",
 'gotaccountlink' => 'Tamong',
 'userlogin-resetlink' => 'Tuwo ngon rincian tamong Droeneuh?',
+'userlogin-resetpassword-link' => 'Peugöt lageuëm rahsia la’én',
+'helplogin-url' => 'Help:Tamong',
+'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|Bantu tamöng]]',
+'userlogin-loggedin' => 'Droëneuh ka neutamöng seubagoë $1. Neunguy blangko di yup keu neutamöng seubagoë ureuëng nguy la’én',
+'userlogin-createanother' => 'Peudapeuta nan barô',
+'createacct-join' => 'Neupasoë keutrangan bhaih droëneuh di yup nyoë',
+'createacct-another-join' => 'Neupasoë keutrangan nan ureuëng nguy barô di yup nyoë',
+'createacct-emailrequired' => 'Alamat surat-e',
+'createacct-emailoptional' => 'Alamat surat-e (hana wajéb)',
+'createacct-email-ph' => 'Neupasoë alamat surat-e droëneuh',
+'createacct-another-email-ph' => 'Pasoë alamat surat-e',
+'createaccountmail' => 'Neunguy lageuëm rahsia beurangkapeuë keu si’at nyoë. Lheuëh nyan neupeu’ét u surat-e nyang droëneuh meuh’eut',
+'createacct-realname' => 'Nan aseuli (hana wajéb)',
+'createaccountreason' => 'Choë:',
+'createacct-reason' => 'Choë:',
+'createacct-reason-ph' => 'Pakön droëneuh neupeugöt nan ureuëng nguy la’én',
+'createacct-captcha' => 'Paréksa aman',
+'createacct-imgcaptcha-ph' => "Pasoë seunurat nyang neu'eu di ateuëh",
+'createacct-submit' => 'Peudapeuta nan barô',
+'createacct-another-submit' => 'Peugöt nan ureuëng nguy la’én',
+'createacct-benefit-heading' => '{{SITENAME}} geupeugöt lé ureuëng lagèë droëneuh.',
+'createacct-benefit-body1' => '{{PLURAL:$1|andam}}',
+'createacct-benefit-body2' => '{{PLURAL:$1|$1 halaman}}',
+'createacct-benefit-body3' => '{{PLURAL:$1|ureuëng tuléh}} seuneulheuëh',
+'badretype' => 'Lageuëm rahsia nyang neupasoë salah.',
+'userexists' => "Nan ureuëng nguy nyang neupasoë ka na soë nguy.
+Neupiléh nan nyang la'én.",
+'loginerror' => 'Salah bak tamong',
+'createacct-error' => 'Peudapeuta nan barô hana meuhasé',
+'createaccounterror' => 'H‘an jeuët peudapeuta nan: $1',
 'loginsuccesstitle' => 'Meuhasé tamong',
 'loginsuccess' => "'''Droëneuh  jinoë ka neutamong di {{SITENAME}} sibagoë \"\$1\".'''",
 'nosuchuser' => 'Hana ureuëng nguy ngön nan "$1".
@@ -622,7 +686,7 @@ Droëneuh jeuët [[Special:Search/{{PAGENAME}}|neumita keu nan ôn nyoë]] bak 
 atawa <span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} neumita log nyang na meuhubông]</span>, tapi Droëneuh hana idin keu neupeugöt ôn nyoë',
 'updated' => '(Seubarô)',
 'note' => "'''Ceunatat:'''",
-'previewnote' => "'''Beu neuingat meunyo ôn nyoë goh lom neukeubah!'''",
+'previewnote' => "'''Beu neuingat meunyo laman nyoë goh lom neukeubah!'''",
 'editing' => 'Andam $1',
 'editingsection' => 'Andam $1 (bideuëng)',
 'copyrightwarning' => "Beu neuingat bahwa ban mandum nyang Droëneuh   tuléh keu {{SITENAME}} geukira geupeuteubiët di yup $2 (ngiëng $1 keu leubèh jeulah). Meunyoë Droëneuh h‘an neutém teunuléh Droëneuh  ji’andam ngön jiba ho ho la’én, bèk neupasoë teunuléh Droëneuh  keunoë.<br />Droëneuh  neumeujanji chit meunyoë teunuléh nyoë nakeuh atra neutuléh keudroë, atawa neucok nibak nè nè atra umôm atawa nè bibeuëh la’én.
@@ -758,22 +822,22 @@ Surat-e droeneuh h'an geupeugah keu ureueng nyan.",
 'rightslog' => 'Log neuubah hak peuhah',
 
 # Associated actions - in the sentence "You do not have permission to X"
-'action-edit' => 'andam ôn nyoë',
+'action-edit' => 'andam laman nyoë',
 
 # Recent changes
-'nchanges' => '$1 {{PLURAL:$1|neu’ubah|neu’ubah}}',
+'nchanges' => '$1 {{PLURAL:$1|neuubah}}',
 'recentchanges' => 'Neuubah barô',
 'recentchanges-legend' => 'Peuniléh neuubah barô',
 'recentchanges-summary' => "Di yup nyoë nakeuh neuubah barô nyang na bak Wikipèdia nyoë.
 Ceunatat: (bida) = neuubah, (riwayat) = riwayat teumuléh, '''B''' = ôn barô, '''u''' = neuandam ubeut, '''b''' = neuandam bot, (± ''bit'') = jumeulah asoë meutamah/meukureuëng, → = neuandam beunagi, ← = mohtasa otomatis.
 ----",
-'recentchanges-feed-description' => 'Peuteumèë neu’ubah barô lam wiki bak umpeuën nyoë.',
+'recentchanges-feed-description' => 'Seutot neuubah barô lam wiki bak umpeuën nyoë.',
 'recentchanges-label-newpage' => 'Neuandam nyoe jipeugot on baro',
 'recentchanges-label-minor' => 'Nyoe neuandam ubeut',
 'recentchanges-label-bot' => 'Neuandam nyoe geupubuet le bot',
 'recentchanges-label-unpatrolled' => 'Neuandam nyoe goh lom geukalon',
 'rcnote' => "Di yup nyoë nakeuh {{PLURAL:$1|nakeuh '''1''' neu’ubah barô |nakeuh '''$1''' neu’ubah barô}} lam {{PLURAL:$2|'''1''' uroë|'''$2''' uroë}} nyoë, trôk ‘an $5, $4.",
-'rcnotefrom' => 'Di yup nyoë nakeuh neu’ubah yôh <strong>$2</strong> (geupeuleumah trôh ‘an <strong>$1</strong> neu’ubah).',
+'rcnotefrom' => 'Di yup nyoë nakeuh neuubah yôh <strong>$2</strong> (geupeudeuh trôh ‘an <strong>$1</strong> neuubah).',
 'rclistfrom' => 'Peuleumah neuubah paléng barô yôh $1 kön',
 'rcshowhideminor' => '$1 andam bacut',
 'rcshowhidebots' => '$1 bot',
@@ -921,7 +985,7 @@ Teuneurang bak [$2 on teuneurangjih] geupeuleumah di yup nyoe.",
 'categories' => 'Dapeuta kawan',
 
 # Special:LinkSearch
-'linksearch' => 'Hubông luwa',
+'linksearch' => 'Mita seuneumat luwa',
 'linksearch-ok' => 'Mita',
 'linksearch-line' => '$1 meusambat nibak $2',
 
@@ -941,7 +1005,7 @@ Teuneurang bak [$2 on teuneurangjih] geupeuleumah di yup nyoe.",
 'watchthispage' => 'Kalön ôn nyoë',
 'unwatch' => 'Bateuë kalön',
 'watchlist-details' => '{{PLURAL:$1|$1 ôn|$1 ôn}} geukalön, hana kira ôn peugah haba.',
-'wlshowlast' => 'Peuleumah $1 jeum $2 uroë $3 keuneulheuëh',
+'wlshowlast' => 'Peudeuh $1 jeum $2 uroë $3 seuneulheuëh',
 'watchlist-options' => 'Peuniléh dapeuta kalön',
 
 # Displayed when you click the "watch" button and it is in the process of watching
@@ -1009,7 +1073,7 @@ Droëneuh jeuët neugantoë tingkat lindông keu ôn nyoë, tapi nyan hana peung
 'month' => 'Yôh buleuën (ngön yôh goh lom nyan)',
 'year' => 'Yôh thôn (ngön yôh goh lom nyan)',
 
-'sp-contributions-newbies' => 'Keu ureuëng ban dapeuta mantöng',
+'sp-contributions-newbies' => 'Peudeuh beuneuri atra ureuëng ban dapeuta mantöng',
 'sp-contributions-newbies-sub' => 'Keu ureuëng nguy barô',
 'sp-contributions-blocklog' => 'Log peutheun',
 'sp-contributions-uploads' => 'peunasoe',
@@ -1025,7 +1089,7 @@ Droëneuh jeuët neugantoë tingkat lindông keu ôn nyoë, tapi nyan hana peung
 'whatlinkshere-title' => 'Ôn nyang na neuhubông u $1',
 'whatlinkshere-page' => 'Ôn:',
 'linkshere' => "Ôn-ôn nyoë meuhubông u '''[[:$1]]''':",
-'nolinkshere' => "Hana ôn nyang teuhubông u '''[[:$1]]'''.",
+'nolinkshere' => "Hana halaman nyang teukaw'et u '''[[:$1]]'''.",
 'isredirect' => 'ôn peupinah',
 'istemplate' => 'ngön seunaleuëk',
 'isimage' => 'hubông beureukaih',
@@ -1035,7 +1099,7 @@ Droëneuh jeuët neugantoë tingkat lindông keu ôn nyoë, tapi nyan hana peung
 'whatlinkshere-hideredirs' => '$1 peuninah',
 'whatlinkshere-hidetrans' => '$1 transklusi',
 'whatlinkshere-hidelinks' => '$1 hubông',
-'whatlinkshere-hideimages' => '$1 hubông beureukaih',
+'whatlinkshere-hideimages' => '$1 seuneumat beureukaih',
 'whatlinkshere-filters' => 'Saréng',
 
 # Block/unblock
@@ -1118,21 +1182,21 @@ Droëneuh jeuët neu’eu nèjih.',
 'tooltip-p-logo' => 'Saweuë ôn keuë',
 'tooltip-n-mainpage' => 'Jak u ôn keuë',
 'tooltip-n-mainpage-description' => 'Saweuë ôn keuë',
-'tooltip-n-portal' => 'Bhaih buët, peuë nyang jeuët neupeulaku, pat tamita sipeuë hay',
+'tooltip-n-portal' => 'Bhaih buët, peuë nyang jeuët neupubuët, pat keu mita sipeuë hay',
 'tooltip-n-currentevents' => 'Mita haba barô',
 'tooltip-n-recentchanges' => 'Dapeuta neuubah baro lam wiki.',
 'tooltip-n-randompage' => 'Peuleumah ôn beurangkari',
 'tooltip-n-help' => 'Bak mita bantu.',
-'tooltip-t-whatlinkshere' => 'Dapeuta ban dum ôn wiki nyang na neuhubông u ôn nyoë',
+'tooltip-t-whatlinkshere' => 'Dapeuta ban dum ôn wiki nyang meuhubông keunoë',
 'tooltip-t-recentchangeslinked' => 'Neuubah barô ôn nyang na seuneumat u ôn nyoë',
 'tooltip-feed-rss' => 'Umpeuën RSS keu ôn nyoë',
 'tooltip-feed-atom' => 'Umpeuën Atom keu ôn nyoë',
 'tooltip-t-contributions' => 'Eu dapeuta nyang ka geutuléh lé ureuëng nguy nyoë',
-'tooltip-t-emailuser' => "Peu'ét surat-e u ureuëng nguy nyoë",
+'tooltip-t-emailuser' => "Peu'ét surat-e keu ureuëng nguy nyoë",
 'tooltip-t-upload' => 'Peutamong beureukaih',
 'tooltip-t-specialpages' => 'Dapeuta ban dum ôn kusuih',
 'tooltip-t-print' => 'Seunalén rakam ôn nyoë',
-'tooltip-t-permalink' => 'Hubông teutap keu geunantoë ôn nyoë',
+'tooltip-t-permalink' => 'Seuneumat teutap keu geunantoë ôn nyoë',
 'tooltip-ca-nstab-main' => 'Eu ôn asoë',
 'tooltip-ca-nstab-user' => 'Eu ôn ureuëng nguy',
 'tooltip-ca-nstab-special' => 'Nyoë nakeuh ôn kusuih nyang h’an jeuët geu’andam.',
@@ -1202,8 +1266,8 @@ Data nyang la'én eunteuk teupeusom keudroë.
 'monthsall' => 'ban dum',
 
 # Watchlist editing tools
-'watchlisttools-view' => 'Peuleumah neuubah meuhubông',
-'watchlisttools-edit' => 'Peuleumah ngön andam dapeuta kaeunalön',
+'watchlisttools-view' => "Peudeuh neuubah meukaw'èt",
+'watchlisttools-edit' => 'Peudeuh ngön andam dapeuta keunalön',
 'watchlisttools-raw' => 'Andam dapeuta keunalön meuntah',
 
 # Core parser functions
index 14285d3..271ba5c 100644 (file)
@@ -219,8 +219,8 @@ $messages = array(
 'tog-extendwatchlist' => 'Brei dophoulys uit om alle wysigings te wys, nie slegs die nuutste nie',
 'tog-usenewrc' => 'Groepeer wysigings per bladsy in onlangse wysigings en dophoulys (benodig JavaScript)',
 'tog-numberheadings' => 'Nommer opskrifte outomaties',
-'tog-showtoolbar' => 'Wys redigeergereedskap (benodig JavaScript)',
-'tog-editondblclick' => 'Dubbelkliek om blaaie te wysig (benodig JavaScript)',
+'tog-showtoolbar' => 'Wys redigeergereedskap',
+'tog-editondblclick' => 'Dubbelkliek om te wysig',
 'tog-editsection' => 'Wys [wysig]-skakels vir elke afdeling',
 'tog-editsectiononrightclick' => 'Wysig afdeling met regskliek op afdeling se titel (JavaScript)',
 'tog-showtoc' => 'Wys inhoudsopgawe (by bladsye met meer as drie opskrifte)',
@@ -666,6 +666,7 @@ Moenie vergeet om u [[Special:Preferences|voorkeure vir {{SITENAME}}]] te stel n
 'userlogin-resetpassword-link' => 'Herstel u wagwoord',
 'helplogin-url' => 'Help:Aanmelding',
 'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|Hulp met aanmelding]]',
+'userlogin-createanother' => "Skep nog 'n rekening",
 'createacct-join' => 'Verskaf u gegewens hieronder.',
 'createacct-another-join' => 'Sleutel die nuwe rekening se inligting hier onder in:',
 'createacct-emailrequired' => 'E-posadres',
@@ -2563,7 +2564,7 @@ $1',
 'contributions' => '{{GENDER:$1|Gebruikersbydraes}}',
 'contributions-title' => '$1 se bydraes',
 'mycontris' => 'Bydraes',
-'contribsub2' => 'Vir $1 ($2)',
+'contribsub2' => 'Vir {{GENDER:$3|$1}} ($2)',
 'nocontribs' => 'Geen veranderinge wat by hierdie kriteria pas, is gevind nie.',
 'uctop' => '(laaste wysiging)',
 'month' => 'Vanaf maand (en vroeër):',
@@ -2722,12 +2723,9 @@ Die blokkade is moontlik reeds opgehef.',
 Die blokkade is 'n onderdeel van die reeks $2, waarvan die blokkade wel opgehef kan word.",
 'ip_range_invalid' => 'Ongeldige IP waardegebied.',
 'ip_range_toolarge' => 'Reeks-blokkades groter as /$1 word nie toegelaat nie.',
-'blockme' => 'Versper my',
 'proxyblocker' => 'Proxyblokker',
-'proxyblocker-disabled' => 'Die funksie is gedeaktiveer.',
 'proxyblockreason' => "U IP-adres is geblokkeer omdat dit van 'n oop instaanbediener (proxy) gebruik maak.
 Kontak asseblief u internet-diensverskaffer of tegniese ondersteuning en lig hulle van hierdie ernstige sekuriteitsprobleem in.",
-'proxyblocksuccess' => 'Voltooi.',
 'sorbsreason' => "U IP-adres is gelys as 'n oop instaanbediener (proxy) in die DNS-swartlys wat op {{SITENAME}} gebruik word.",
 'sorbs_create_account_reason' => "U IP-adres is gelys as 'n oop instaanbediener (proxy) in die DNS-swartlys wat op {{SITENAME}} gebruik word.
 U kan nie 'n rekening skep nie.",
@@ -3072,6 +3070,8 @@ Hierdie situasie was waarskynlik deur 'n skakel na 'n eksterne webtuiste op ons
 'spam_reverting' => 'Besig met terugrol na die laaste weergawe wat nie skakels na $1 bevat nie',
 'spam_blanking' => "Alle weergawes met 'n skakel na $1 word verwyder",
 'spam_deleting' => 'Alle weergawes bevat verwysings na $1. Bladsy verwyder',
+'simpleantispam-label' => "Anti-spam kontrole.
+'''Moenie''' die veld invul nie!",
 
 # Info page
 'pageinfo-title' => 'Inligting oor "$1"',
@@ -3904,7 +3904,10 @@ Saam met die program moes u \'n [{{SERVER}}{{SCRIPTPATH}}/COPYING kopie van van
 'tags-tag' => 'Etiketnaam',
 'tags-display-header' => 'Weergawe in wysigingslyste',
 'tags-description-header' => 'Volledige beskrywing van betekenis',
+'tags-active-header' => 'Aktief?',
 'tags-hitcount-header' => 'Geëtiketteerde veranderings',
+'tags-active-yes' => 'Ja',
+'tags-active-no' => 'Nee',
 'tags-edit' => 'wysig',
 'tags-hitcount' => '$1 {{PLURAL:$1|wysiging|wysigings}}',
 
@@ -4070,8 +4073,8 @@ Anders kan u die eenvoudige vorm hieronder gebruik. U kommentaar sal by die blad
 'limitreport-ppvisitednodes' => 'Aantal nodes besoek tydens voorverwerking:',
 'limitreport-ppgeneratednodes' => 'Aantal nodes geskep tydens voorverwerking:',
 'limitreport-postexpandincludesize' => 'Inklusiegrootte na uitbreiding',
-'limitreport-postexpandincludesize-value' => '$1/$2 grepe',
-'limitreport-templateargumentsize-value' => '$1/$2 grepe',
+'limitreport-postexpandincludesize-value' => '$1/$2 {{PLURAL:$2|greep|grepe}}',
+'limitreport-templateargumentsize-value' => '$1/$2 {{PLURAL:$2|greep|grepe}}',
 'limitreport-expansiondepth' => 'Hoogste uitbreidingsdiepte',
 
 );
index c5c19e2..332eda3 100644 (file)
@@ -1504,6 +1504,9 @@ Lejon dhânien e arsyes në përmbledhje.',
 'siteusers' => '{{SITENAME}} {{PLURAL:$2|përdorues|përdorues}} $1',
 'creditspage' => 'Kreditat e faqes',
 
+# Spam protection
+'simpleantispam-label' => "Anti-spam kontrolloni. A'''''NUK' plotësoni këtë!",
+
 # Browsing diffs
 'previousdiff' => '← Redaktim mâ i vjetër',
 'nextdiff' => 'Redaktim ma i ri →',
index 730af88..da80c4f 100644 (file)
@@ -1848,9 +1848,6 @@ $1',
 'ipb_expiry_invalid' => 'የሚያልቅበት ግዜ አይሆንም።',
 'ipb_already_blocked' => '«$1» ገና ከዚህ በፊት ታግዶ ነው።',
 'ipb-needreblock' => '$1 አሁን ገና ታግዷል። ዝርዝሩን ማስተካከል ፈለጉ?',
-'blockme' => 'ልታገድ',
-'proxyblocker-disabled' => 'ይህ ተግባር እንደማይሠራ ተደርጓል።',
-'proxyblocksuccess' => 'ተደርጓል።',
 'cant-block-while-blocked' => 'እርስዎ እየታገዱ ሌላ ተጠቃሚ ለማገድ አይችሉም።',
 
 # Developer tools
index 10330ed..71ddec7 100644 (file)
@@ -2371,11 +2371,8 @@ Ta más detalles, debaixo s'amuestra o rechistro de supresions:",
 'ipb_blocked_as_range' => "Error: L'adreza IP $1 no s'ha bloqueyato dreitament y por ixo no se puede desbloqueyar. Manimenos, ye bloqueyata por estar parte d'o rango $2, que sí puede desbloqueyar-se de conchunta.",
 'ip_range_invalid' => "O rango d'adrezas IP no ye conforme.",
 'ip_range_toolarge' => 'No se permiten os bloqueyos de rangos más grans que /$1.',
-'blockme' => 'bloqueyar-me',
 'proxyblocker' => 'bloqueyador de proxies',
-'proxyblocker-disabled' => 'Ista función ye desactivata.',
 'proxyblockreason' => "S'ha bloqueyato a suya adreza IP porque ye un proxy ubierto. Por favor, contaute on o suyo furnidor de servicios d'Internet u con o suyo servicio d'asistencia tecnica e informe-les d'iste grau problema de seguridat.",
-'proxyblocksuccess' => 'Feito.',
 'sorbsreason' => 'A suya adreza IP ye en a lista de proxies ubiertos en a DNSBL de {{SITENAME}}.',
 'sorbs_create_account_reason' => 'A suya adreza IP ye en a lista de proxies ubiertos en a DNSBL de {{SITENAME}}. No puede creyar una cuenta',
 'cant-block-while-blocked' => 'No puet bloqueyar a atros usuarios en o tiempo que ye bloqueyato.',
@@ -2721,6 +2718,8 @@ Puede veyer-ne, manimenos, o codigo fuent.',
 'spambot_username' => 'Esporga de spam de MediaWiki',
 'spam_reverting' => "Tornando t'a zaguera versión sin de vinclos ta $1",
 'spam_blanking' => 'Todas as versions teneban vinclos ta $1, se deixa en blanco',
+'simpleantispam-label' => "Preba anti-spam.
+'''NO''' replene esto!",
 
 # Info page
 'pageinfo-title' => 'Información ta «$1»',
index 3c723b7..322dd28 100644 (file)
@@ -370,8 +370,8 @@ Getæl gengra syndrigra trameta cann man findan be [[Special:SpecialPages|þǣm
 'cascadeprotected' => 'Þes trament wæs geborgen wiþ adihtunge, for þǣm þe hē is befangen in þissum {{PLURAL:$1|tramente, þe is| tramentum, þe sind}} geborgen settum wyrcende þǣm cyre "cascading": $2',
 
 # Virus scanner
-'virus-badscanner' => "Јастыра конфигурация: Јарты јок сканер ''$1''",
-'virus-unknownscanner' => 'Јарты јок антивирус:',
+'virus-badscanner' => 'Bad configuration: Unknown virus scanner: $1',
+'virus-unknownscanner' => 'unknown antivirus:',
 
 # Login and logout pages
 'logouttext' => "'''Þū eart nū ūtmeldod.'''
@@ -401,8 +401,8 @@ Cnāw þæt sume trametas mihten gīet wesan geīwde swā þū wǣre gīet inmel
 'logout' => 'Ūtmeldian',
 'userlogout' => 'Ūtmeldian',
 'notloggedin' => 'Nā ingemeldod',
-'userlogin-noaccount' => 'Слерде аккаунт јок по?',
-'userlogin-joinproject' => '{{SITENAME}} кирер',
+'userlogin-noaccount' => "Don't have an account?",
+'userlogin-joinproject' => 'Join {{SITENAME}}',
 'nologin' => 'Næfst þū reccinge? $1',
 'nologinlink' => 'Scieppan reccinge',
 'createaccount' => 'Scieppan reccinge',
@@ -497,12 +497,12 @@ Gif þū hider be misfēnge cōme, cnoca þīnes webbsēcendes '''on bæc''' cn
 'editingold' => "'''WARNUNG: Þū adihtest ealde fadunge þisses trametes.'''
 Gif þū hine hordie, ǣnga andwendunga þā wǣron gedōn æfter þisse fadunge bēoþ sōðes forloren.",
 'yourdiff' => 'Fǣgnessa',
-'copyrightwarning2' => "Bidde behielde þæt man mæȝ ealla forðunga tō {{SITENAME}}
-ādihtan, hƿeorfan, oþþe forniman.
-Ȝif þū ne ƿille man þīn ȝeƿrit ādihtan unmildheorte, þonne hīe hēr ne forþsendan.<br />
-Þū behǣtst ēac þæt þū selfa þis ƿrite, oþþe efenlǣhtest of sumre
-folcliċum āgnunge oþþe ȝelīċum frēom horde (sēo $1 for āscungum).
-'''Ne forþsend efenlǣhtscielded ƿeorc būtan þafunge!'''",
+'copyrightwarning2' => "Bidde behielde þæt man mæg ealla forðunga tō {{SITENAME}}
+ādihtan, hweorfan, oþþe forniman.
+Gif þū ne wille man þīn gewrit ādihtan unmildheorte, þonne hīe hēr ne forþsendan.<br />
+Þū behǣtst ēac þæt þū selfa þis write, oþþe efenlǣhtest of sumre
+folclicum āgnunge oþþe gelīcum frēom horde (sēo $1 for āscungum).
+'''Ne forþsend efenlǣhtscielded weorc būtan þafunge!'''",
 'templatesused' => '{{PLURAL:$1|Þēos bysen is|Þās bysena sind}} gebrocen on þissum tramete:',
 'templatesusedpreview' => '{{PLURAL:$1|Þēos bysen is|Þās bysena sind}} gebrocen on þisre fōrebysene:',
 'template-protected' => '(geborgen)',
@@ -1024,7 +1024,6 @@ Gif se brūcend asifte hine. synderlīce sind ymelan geīwda þǣre þe se brūc
 'contribslink' => 'forðunga',
 'unblocklogentry' => 'unfortȳnde $1',
 'block-log-flags-nocreate' => 'Forbēad tō scieppenne reccinge',
-'proxyblocksuccess' => 'Gedōn.',
 
 # Move page
 'movearticle' => 'Wegan tramet:',
@@ -1168,7 +1167,7 @@ Cēos ōðerne naman lā.',
 'exif-sharpness' => 'Scearpnes',
 'exif-gpslatituderef' => 'Norþ oþþe sūþ brǣdu',
 'exif-gpslatitude' => 'Brǣdu',
-'exif-gpslongituderef' => 'Ēast oþþe ƿest lengu',
+'exif-gpslongituderef' => 'Ēast oþþe west lengu',
 'exif-gpslongitude' => 'Lengu',
 'exif-gpsmeasuremode' => 'Mētungmōd',
 'exif-gpsimgdirection' => 'Rihtung þæs biliðes',
@@ -1177,7 +1176,7 @@ Cēos ōðerne naman lā.',
 'exif-compression-1' => 'Unȝeþrycced',
 
 'exif-meteringmode-0' => 'Uncūþ',
-'exif-meteringmode-1' => 'Ȝeþēaƿisc',
+'exif-meteringmode-1' => 'Geþēawisc',
 'exif-meteringmode-6' => 'Sām',
 'exif-meteringmode-255' => 'Ōðer',
 
@@ -1212,7 +1211,7 @@ Cēos ōðerne naman lā.',
 
 # Pseudotags used for GPSLongitudeRef and GPSDestLongitudeRef
 'exif-gpslongitude-e' => 'Ēast lengu',
-'exif-gpslongitude-w' => 'Ƿest lengu',
+'exif-gpslongitude-w' => 'West lengu',
 
 # Pseudotags used for GPSTrackRef, GPSImgDirectionRef and GPSDestBearingRef
 'exif-gpsdirection-t' => 'Sōþ rihtung',
index 1a1baa2..6679715 100644 (file)
@@ -23,6 +23,7 @@
  * @author Bassem JARKAS
  * @author Chaos
  * @author Ciphers
+ * @author Claw eg
  * @author DRIHEM
  * @author DrFO.Tn
  * @author Elmondo21st
@@ -624,7 +625,7 @@ $messages = array(
 'mypage' => 'صفحة',
 'mytalk' => 'نقاش',
 'anontalk' => 'نقاش عنوان الآي بي',
-'navigation' => 'إبحار',
+'navigation' => 'تصÙ\81Ø­',
 'and' => '&#32;و',
 
 # Cologne Blue skin
@@ -693,7 +694,7 @@ $messages = array(
 'articlepage' => 'اعرض صفحة المحتوى',
 'talk' => 'نقاش',
 'views' => 'معاينة',
-'toolbox' => 'صÙ\86دÙ\88Ù\82 Ø§Ù\84أدÙ\88ات',
+'toolbox' => 'الأدوات',
 'userpage' => 'طالع صفحة المستخدم',
 'projectpage' => 'طالع صفحة المشروع',
 'imagepage' => 'طالع صفحة الملف',
@@ -1005,7 +1006,7 @@ $2',
 من فضلك حاول تسجيل الدخول مرة ثانية بعد استلامها.',
 'blocked-mailpassword' => 'تم منع عنوان الأيبي الخاص بك من التحرير، ولمنع التخريب لا يمكنك أن تستخدم خاصية استرجاع كلمة السر.',
 'eauthentsent' => 'تم إرسال رسالة تأكيد إلكترونية إلى العنوان المسمى.
-حتى ترسل أي رسالة أخرى لذلك الحساب عليك أن تتبع التعليمات الواردة في الرسالة لتأكيد أن هذا الحساب هو لك بالفعل.',
+قبل إرسال أي رسالة أخرى لذلك الحساب، عليك أن تتبع التعليمات الواردة في الرسالة، لتأكيد أن هذا الحساب هو لك بالفعل.',
 'throttled-mailpassword' => 'تم بالفعل إرسال تذكير بكلمة السر، في ال{{PLURAL:$1||ساعة الماضية|ساعتين الماضيتين|$1 ساعات الماضية|$1 ساعة الماضية}}.
 لمنع التخريب، سيتم إرسال تذكير واحد كل {{PLURAL:$1||ساعة|ساعتين|$1 ساعات|$1 ساعة}}.',
 'mailerror' => 'خطأ أثناء إرسال البريد: $1',
@@ -1894,8 +1895,8 @@ $1",
 'action-block' => 'منع هذا المستخدم من التعديل',
 'action-protect' => 'تغيير مستويات الحماية لهذه الصفحة',
 'action-rollback' => 'استرجاع تعديلات آخر مستخدم عدل صفحة معينة سريعا',
-'action-import' => 'استيراد هذه الصفحة من ويكي آخر',
-'action-importupload' => 'استيراد هذه الصفحة من ملف مرفوع',
+'action-import' => 'استيراد صفحات من ويكي آخر',
+'action-importupload' => 'استيراد صفحات من ملف مرفوع',
 'action-patrol' => 'تعليم تعديلات الآخرين بعلامة المراجعة',
 'action-autopatrol' => 'جعل تعديلك معلم عليه كمراجع',
 'action-unwatchedpages' => 'رؤية قائمة الصفحات غير المراقبة',
@@ -2438,8 +2439,9 @@ $1',
 'protectedtitlestext' => 'العناوين التالية محمية ضد الإنشاء',
 'protectedtitlesempty' => 'لا توجد عناوين محمية حاليا بهذه المحددات.',
 'listusers' => 'قائمة الأعضاء',
-'listusers-editsonly' => 'اعرض المستخدمين الذين قاموا بتعديلات فقط',
+'listusers-editsonly' => 'اعرض المستخدمين الذين أجروا تعديلات فقط',
 'listusers-creationsort' => 'رتب حسب تاريخ الإنشاء',
+'listusers-desc' => 'رتب تنازليا',
 'usereditcount' => '{{PLURAL:$1|لا تعديلات|تعديل واحد|تعديلان|$1 تعديلات|$1 تعديلًا|$1 تعديل}}',
 'usercreated' => '{{GENDER:$3|أنشأه|أنشأته}} في $1 الساعة $2',
 'newpages' => 'صفحات جديدة',
@@ -2564,7 +2566,7 @@ $1',
 # Email user
 'mailnologin' => 'لا يوجد عنوان للإرسال',
 'mailnologintext' => 'يجب أن تقوم [[Special:UserLogin|بتسجيل الدخول]] وإدخال بريد إلكتروني صالح في صفحة [[Special:Preferences|التفضيلات]] لتتمكن من إرسال الرسائل لمستخدمين آخرين.',
-'emailuser' => 'إرسال رسالة لهذا المستخدم',
+'emailuser' => 'مراسلة المستخدم',
 'emailuser-title-target' => 'راسل بالبريد الإلكتروني هذا  {{GENDER:$1| المستخدم}}',
 'emailuser-title-notarget' => 'مراسلة المستخدم',
 'emailpage' => 'إرسال رسالة للمستخدم',
@@ -2746,8 +2748,8 @@ $UNWATCHURL
 'modifiedarticleprotection' => 'غير مستوى حماية "[[$1]]"',
 'unprotectedarticle' => 'أزال الحماية من "[[$1]]"',
 'movedarticleprotection' => 'نقل إعدادات الحماية من "[[$2]]" إلى "[[$1]]"',
-'protect-title' => 'ضبط Ù\85ستÙ\88Ù\89 Ø§Ù\84Ø­Ù\85اÙ\8aØ© Ù\84"$1"',
-'protect-title-notallowed' => 'عرض Ù\85ستÙ\88Ù\89 Ø§Ù\84Ø­Ù\85اÙ\8aØ© Ù\84 "$1"',
+'protect-title' => 'ضبط Ù\85ستÙ\88Ù\89 Ø­Ù\85اÙ\8aØ© "$1"',
+'protect-title-notallowed' => 'عرض Ù\85ستÙ\88Ù\89 Ø­Ù\85اÙ\8aØ© "$1"',
 'prot_1movedto2' => 'نُقلت [[$1]] إلى [[$2]]',
 'protect-badnamespace-title' => 'نطاق لا يحمى',
 'protect-badnamespace-text' => 'صفحات هذا النطاق لا يمكن حمايتها',
@@ -2921,7 +2923,7 @@ $1',
 'autoblockid' => 'منع تلقائي #$1',
 'block' => 'امنع المستخدم',
 'unblock' => 'إلغاء منع مستخدم',
-'blockip' => 'منع مستخدم',
+'blockip' => 'منع المستخدم',
 'blockip-title' => 'منع مستخدم',
 'blockip-legend' => 'منع المستخدم',
 'blockiptext' => 'استخدم النموذج التالي لمنع مستخدم، أو عنوان آيبي، معين من التعديل أو إنشاء حسابات جديدة. تُستخدم هذه العملية لمنع التخريب فقط، ويجب أن تتماشى مع [[{{MediaWiki:Policy-url}}|سياسة المنع]]. أدخل تعليلاً واضحًا لسبب المنع في الخانة المخصصة لذلك (مثلاً: ذكر صفحات محددة تمّ تخريبها من قبل المستخدم).',
@@ -3032,12 +3034,9 @@ $1',
 لكنه ممنوع كجزء من النطاق $2، والذي يمكن رفع المنع عنه.',
 'ip_range_invalid' => 'نطاق عناوين الأيبي المدخل غير صحيح.',
 'ip_range_toolarge' => 'لا يسمح بنطاقات المنع الأكبر من /$1',
-'blockme' => 'امنعني',
 'proxyblocker' => 'مانع البروكسي',
-'proxyblocker-disabled' => 'هذه الخاصية معطلة.',
 'proxyblockreason' => 'تم منع عنوان الأيبي الخاص بك لكونه بروكسي مفتوح.
 من فضلك اتصل بمزود خدمة الإنترنت الخاص بك أو الدعم الفني وأعلمهم بهذه المشكلة الأمنية الخطيرة.',
-'proxyblocksuccess' => 'تم.',
 'sorbs' => 'دي إن إس بي إل',
 'sorbsreason' => 'عنوان الأيبي الخاص بك موجود كبروكسي مفتوح في DNSBL المستخدم بواسطة {{SITENAME}}.',
 'sorbs_create_account_reason' => 'عنوان الأيبي الخاص بك موجود كبروكسي مفتوح في DNSBL المستخدم بواسطة {{SITENAME}}.
@@ -3409,6 +3408,8 @@ $2',
 'spam_reverting' => 'استرجاع آخر نسخة ليس بها وصلات إلى $1',
 'spam_blanking' => 'كل النسخ احتوت على وصلات ل $1، إفراغ',
 'spam_deleting' => 'جميع النسخ تحوي رابطا إلى $1، يتم الحذف',
+'simpleantispam-label' => "اختبار ضد السبام.
+'''لا''' تملأ هذا!",
 
 # Info page
 'pageinfo-title' => 'المعلومات عن «$1»',
index a6b49d2..33b463c 100644 (file)
@@ -1461,8 +1461,6 @@ $1',
 'ipb_already_blocked' => '"$1" ܡܚܪܡܐ ܗܘ ܡܢ ܟܕܘ',
 'ipb-needreblock' => '"$1" ܡܚܪܡܐ ܗܘ ܡܢ ܟܕܘ
 Do you want to change the settings?',
-'blockme' => 'ܚܪܘܡ ܠܝ',
-'proxyblocksuccess' => 'ܒܪܐ',
 
 # Move page
 'move-page' => 'ܫܢܝ $1',
index 0281662..51884d4 100644 (file)
@@ -21,8 +21,8 @@ $messages = array(
 # User preference toggles
 'tog-underline' => 'Miñcewirilpe lasun',
 'tog-justify' => 'Xvrvmpe cijkantvkun',
-'tog-showtoolbar' => 'Pengelün kümeelün ñi chemkün (JavaScript duamyengey)',
-'tog-editondblclick' => 'Wirin pakina epu klik mew (JavaScript)',
+'tog-showtoolbar' => 'Pengelün kümeelün ñi chemkün',
+'tog-editondblclick' => 'Wirin pakina epu klik mew',
 'tog-rememberpassword' => 'Amulen tañi nülküwküleael tüfa mew (alürumechi $1 {{PLURAL:$1 antü}})',
 
 'underline-always' => 'Rumel',
@@ -430,7 +430,7 @@ Rulpakünuy feychi kangelkülelu dungu.",
 'powersearch-field' => 'Kintun',
 
 # Preferences page
-'mypreferences' => 'Tami dullin',
+'mypreferences' => 'Dullin',
 'prefs-edits' => 'Rakin Wirin:',
 'prefsnologin' => 'Mülelay Konün',
 'skin-preview' => 'Pen chum müley',
@@ -752,7 +752,6 @@ Fey ñi chumngen mülelu ($2 fey ñi chumngen wülngiñ) pengeli tüfa mew.',
 'blocklogentry' => 'Katrüntukufi [[$1]] $2 antü/ora mew, $3',
 'block-log-flags-nocreate' => 'Pepi dewmangelay konün',
 'block-log-flags-hiddenname' => 'Üy kellufe ellkan',
-'proxyblocksuccess' => 'Dewmangey.',
 
 # Move page
 'move-page' => 'Nengümün $1',
index 1555749..3c31b1f 100644 (file)
@@ -142,8 +142,6 @@ $messages = array(
 'noindex-category' => 'shat mamfhtsach',
 'broken-file-category' => 'ṣfaḫi fiha wṣlat milffaṫ mhrrsa',
 
-'linkprefix' => '/^(.*?)([a-zA-Z\\x80-\\xff]+)$/sD',
-
 'about' => 'ala',
 'article' => 'sfht mohtawa',
 'newwindow' => '(kayṫḫell fe ċerjem weḫdaĥor)',
@@ -1873,10 +1871,7 @@ Imken lek ṫbeddel l-mosṫawa de l-ḫimaya dyal had ṣ-ṣefḫa bla ma i\'e
 'ipb_expiry_temp' => 'L-Blokaj dyal s-smiyyaṫ dyal l-mosṫeĥdimin l-mĥebbyin ĥaṣṣo ybqa dima.',
 'ipb_already_blocked' => '"$1" rah fayeṫ mbloki',
 'ipb-otherblocks-header' => 'Blokaj {{PLURAL:$1|weḫdaĥor|weḫdaĥrin}}',
-'blockme' => 'blokini',
 'proxyblocker' => 'blokør dl-proksi',
-'proxyblocker-disabled' => 'had l-ĥaṣṣiyya ma mtloqa-ċ',
-'proxyblocksuccess' => 'ṣafi.',
 'sorbs' => 'DNSBL',
 
 # Developer tools
index 5d4a353..a7d03e9 100644 (file)
@@ -2423,12 +2423,9 @@ $1',
 بس هو، على الرغم من كدا،ممنوع لانه جزء من النطاق $2، و اللى ممكن رفع المنع عنه.',
 'ip_range_invalid' => 'نطاق عناوين الأيبى مش صحيح.',
 'ip_range_toolarge' => 'حدود المنع اللى اكبر من /$1 مش مسموح بيها.',
-'blockme' => 'امنعنى',
 'proxyblocker' => 'مانع البروكسي',
-'proxyblocker-disabled' => 'الخاصية دى متعطلة.',
 'proxyblockreason' => 'عنوان الأيبى بتاعك اتمنع لانه بروكسى مفتوح.
 لو سمحت تتصل بمزود خدمة الإنترنت بتاعك أو الدعم الفنى و قولهم على المشكلة الامنية الخطيرة دي.',
-'proxyblocksuccess' => 'خلاص.',
 'sorbs' => 'دى إن إس بى إل',
 'sorbsreason' => 'عنوان الأيبى بتاعك موجود كبروكسى مفتوح فى DNSBL اللى بيستعمله{{SITENAME}}.',
 'sorbs_create_account_reason' => 'عنوان الأيبى بتاعك موجود كبروكسى مفتوح فى ال DNSBL اللى بيستعمله {{SITENAME}}.
@@ -2739,6 +2736,8 @@ $1',
 'spambot_username' => 'تنظيف سبام ميدياويكى',
 'spam_reverting' => 'ترجيع آخر نسخة مافيهاش لينكات لـ $1',
 'spam_blanking' => 'كل النسخ فيها لينكات ل $1، فضيها',
+'simpleantispam-label' => "اختبار انتي-سبام.
+'''ماتعبيش''' دا!",
 
 # Skin names
 'skinname-cologneblue' => 'كولون بلو',
index 2c044f9..b56ee1a 100644 (file)
@@ -2676,12 +2676,9 @@ $1ৰ অৱৰোধৰ কাৰণ: "$2"',
 কিন্তু এইটো $2 পৰিসীমাৰ অন্তৰ্গত যাৰ বাধা আঁতৰাব পাৰি ।',
 'ip_range_invalid' => 'অবৈধ আই.পি. পৰিসৰ ।',
 'ip_range_toolarge' => '/$1তকৈ ডাঙৰ প্ৰতিবন্ধক পৰিসৰ অনুমোদিত নহয় ।',
-'blockme' => 'মোক বাৰণ কৰক',
 'proxyblocker' => 'প্ৰক্সী অৱৰোধকাৰী',
-'proxyblocker-disabled' => 'এই ফাংচনটো নিষ্ক্ৰিয়',
 'proxyblockreason' => 'আপোনাৰ আই.পি. ঠিকনা অৱৰোধ কৰা হৈছে কাৰণ এইটো এটা মুক্ত প্ৰক্সী ।
 অনুগ্ৰহ কৰি আপোনাৰ ইণ্টাৰনেট সেৱা প্ৰদানকাৰী বা কাৰিকৰী সহায়কৰ্তাৰ লগত যোগাযোগ কৰক আৰু এই গুৰুতৰ সুৰক্ষা সমস্যাৰ বিষয়ে জনাওক ।',
-'proxyblocksuccess' => 'সম্পন্ন কৰা হ’ল ।',
 'sorbsreason' => '{{SITENAME}}ত ব্যৱহাৰ কৰা DNSBLত আপোনাৰ আই.পি. ঠিকনা মুক্ত প্ৰক্সী হিছাপে তালিকাভুক্ত হৈ আছে ।',
 'sorbs_create_account_reason' => '{{SITENAME}}ত ব্যৱহাৰ কৰা DNSBLত আপোনাৰ আই.পি. ঠিকনা মুক্ত প্ৰক্সী হিছাপে তালিকাভুক্ত হৈ আছে ।
 আপুনি একাউণ্ট সৃষ্টি কৰিব নোৱাৰে',
@@ -3028,6 +3025,8 @@ $2',
 'spam_reverting' => '$1লৈ সংযোগ নথকা সৰ্বশেষ পুনৰীক্ষনলে উভতাই নিয়া হৈছে',
 'spam_blanking' => 'সকলো পুনৰীক্ষনৰ $1লৈ সংযোগ আছিল, ৰিক্ত কৰা হৈছে',
 'spam_deleting' => 'সকলো পুনৰীক্ষণৰ $1লৈ সংযোগ আছিল, বিলোপ কৰা হৈছে',
+'simpleantispam-label' => "এণ্টি-স্পাম পৰীক্ষা।
+এইখন পূৰণ '''নকৰিব'''!",
 
 # Info page
 'pageinfo-title' => '"$1" ৰ তথ্য',
index b2782db..671c716 100644 (file)
@@ -596,7 +596,7 @@ usando la contraseña antigua.",
 Por favor vuelvi a aniciar sesión depués de recibila.',
 'blocked-mailpassword' => 'Ta bloquiada la edición dende la to direición IP, polo que pa evitar abusos nun se pue usar la función de recuperación de contraseña.',
 'eauthentsent' => "Unvióse un corréu electrónicu de confirmación a la direición indicada.
-Enantes de que s'unvie nengún otru corréu a la cuenta, has de siguir les instrucciones del corréu electrónicu pa confirmar que la cuenta ye de to.",
+Enantes de que s'unvie nengún otru corréu a la cuenta, has de siguir les instrucciones d'esi corréu pa confirmar que la cuenta ye daveres de to.",
 'throttled-mailpassword' => "Yá s'unvió un corréu de reaniciu la clave {{PLURAL:$1|na postrer hora|nes postreres $1 hores}}.
 Pa evitar abusos, namái s'unviará un corréu de reaniciu cada {{PLURAL:$1|hora|$1 hores}}.",
 'mailerror' => 'Fallu al unviar el corréu: $1',
@@ -2616,11 +2616,8 @@ Pa ver los bloqueos qu'hai agora mesmo, mira na [[Special:BlockList|llista de bl
 'ipb_blocked_as_range' => 'Error: La IP $1 nun ta bloquiada direutamente, polo que nun pue ser desloquiada. Sicasí, foi bloquiada como parte del intervalu $2, que pue ser desbloquiáu.',
 'ip_range_invalid' => 'Rangu IP non válidu.',
 'ip_range_toolarge' => 'Nun se permiten bloqueos mayores de /$1.',
-'blockme' => 'Blóquiame',
 'proxyblocker' => 'Bloquiador de proxys',
-'proxyblocker-disabled' => 'Esta función ta desactivada.',
 'proxyblockreason' => "La to direición IP foi bloquiada porque ye un proxy abiertu. Por favor contauta col to proveedor de serviciones d'Internet o col to servicio d'asistencia téunica y infórmalos d'esti seriu problema de seguridá.",
-'proxyblocksuccess' => 'Fecho.',
 'sorbsreason' => 'La to direición IP sal na llista de proxys abiertos en DNSBL usada por {{SITENAME}}.',
 'sorbs_create_account_reason' => 'La to direición IP sal na llista de proxys abiertos en DNSBL usada por {{SITENAME}}. Nun pues crear una cuenta',
 'xffblockreason' => "Una direición IP presente na testera X-Forwarded-For, o suya o d'un sirvidor proxy que ta usando, ta bloquiada. El motivu orixinal del bloquéu foi: $1",
@@ -2984,6 +2981,8 @@ Probablemente tea causao por un enllaz a un sitiu esternu de la llista prieta.',
 'spam_reverting' => 'Revirtiendo a la cabera versión que nun contién enllaces a $1',
 'spam_blanking' => 'Toles revisiones teníen enllaces a $1; dexando en blanco',
 'spam_deleting' => 'Toles revisiones teníen enllaces a $1, desaniciando',
+'simpleantispam-label' => "Comprobación anti-spam.
+¡'''NUN''' rellenes esto!",
 
 # Info page
 'pageinfo-title' => 'Información sobro "$1"',
index 2a7fc88..3259115 100644 (file)
@@ -1716,11 +1716,8 @@ male abdion elekayane IP mane.',
 'ipb_expiry_invalid' => "temps d'expiration invalide.",
 'ipb_already_blocked' => '"$1" ixam tir elekan',
 'ip_range_invalid' => 'IP elega mewadafa.',
-'blockme' => 'Zo eleká !',
 'proxyblocker' => 'Elekasiki va proxy',
-'proxyblocker-disabled' => 'Bati fli tir metegirafi.',
 'proxyblockreason' => "Votre ip a été bloquée car c'est un proxy ouvert. Merci de contacter votre fournisseur d'accès internet ou votre support technique et de l'informer de ce problème de sécurité.",
-'proxyblocksuccess' => 'Tenuweyes.',
 'sorbsreason' => "Rinafe IP mane wetce fenkunafi 'proxy' koe DNSBL faveni gan {{SITENAME}} zo vexalar.",
 
 # Developer tools
index e393e7d..a39894c 100644 (file)
@@ -206,8 +206,6 @@ $messages = array(
 'noindex-category' => 'İndeksləşdirilməyən səhifələr',
 'broken-file-category' => 'İşləməyən fayl keçidləri olan səhifələr',
 
-'linkprefix' => '/^(.*?)([a-zA-Z\\x80-\\xff]+)$/sD',
-
 'about' => 'Haqqında',
 'article' => 'Mündəricat',
 'newwindow' => '(yeni pəncərədə açılır)',
@@ -2028,10 +2026,7 @@ Bloklama şərtlərini dəyişmək istəyirsiniz?',
 'unblock-hideuser' => 'İstifadəçi adı gizli olduğu üçün, bi bloku götürə bilməzsiniz.',
 'ipb_cant_unblock' => 'Xəta: Bloklama IDsi $1 tapılmadı. Bloklamanın götürülməsi mümkündür.',
 'ip_range_invalid' => 'Yanlış IP',
-'blockme' => 'Məni blokla',
 'proxyblocker' => 'Proksi bloklayıcı',
-'proxyblocker-disabled' => 'Bu funksiya əngəlləndi.',
-'proxyblocksuccess' => 'Oldu.',
 'sorbs' => 'DNSBL',
 
 # Developer tools
index b845a59..e9423fb 100644 (file)
@@ -2542,12 +2542,9 @@ $1 آدلی ایستیفاده‌چی‌نین باغلانما سببی: "$2"',
 آنجاق، بو عنوان $2 آرا‌لیغینین پارچاسی اولا‌راق مانعه تؤردیلمیش، دئکابر مانعه تؤرتمه‌سینی قال‌دیرا.',
 'ip_range_invalid' => 'یانلیش ای پی',
 'ip_range_toolarge' => '/ $1 بلوک‌دان داها بؤیوک بازه باغلانمالارینا ایجازه وئریلمیر.',
-'blockme' => 'منی باغلا',
 'proxyblocker' => 'پروکسی باغلییان',
-'proxyblocker-disabled' => 'بو ایش باغلانیب دیر.',
 'proxyblockreason' => 'ای پی آدرئسینیز آچیق بیر پروکسی اولدوغو اوچون مانعه تؤردیلدی.
 خاهیش ائدیریک اینتئرنئت سئویش تعمین ایله یا دا تئکنیکی دستک ایله علاقه قورون و بو جدی تهلوکه‌سیزلیک پروبلئمین‌دن خبردار ائدین.',
-'proxyblocksuccess' => 'اولدو.',
 'sorbsreason' => 'ای پی عنوانینیز، {{SITENAME}} سایتی طرفین‌دن ایستیفاده ائدیلن DNSBL آچیق پروکسی اولا‌راق اولونموش.',
 'sorbs_create_account_reason' => 'ایپ اونوانینیز {{SITENAME}} سایتی طرفین‌دن ایستیفاده ائدیلن DNSBL آچیق پروکسی اولا‌راق اولونموش.
 حساب میدانا گتیره بیلمز',
index 3197a42..61c065a 100644 (file)
@@ -8,6 +8,7 @@
  * @file
  *
  * @author AiseluRB
+ * @author Alfiya55
  * @author Assele
  * @author Comp1089
  * @author Haqmar
@@ -155,12 +156,12 @@ $messages = array(
 'tog-hidepatrolled' => 'Һуңғы үҙгәртеүҙәр исемлегендә тикшерелгән үҙгәртеүҙәрҙе йәшер',
 'tog-newpageshidepatrolled' => 'Яңы биттәр исемлегендә тикшерелгән үҙгәртеүҙәрҙе йәшер',
 'tog-extendwatchlist' => 'Барлыҡ үҙгәртеүҙәрҙе үҙ эсенә алған, киңәйтелгән күҙәтеү исемлеге',
-'tog-usenewrc' => 'Һуңғы төҙәтеүҙәр һәм күҙәтеү исемлегендәге үҙгәрештерҙе төркөмдәргә булергә (JavaScript)',
-'tog-numberheadings' => 'Башисемдәрҙе автоматик рәүештә номерландыр',
-'tog-showtoolbar' => 'Мөхәррирләү ваҡытында өҫкө ҡоралдар панелен күрһәтергә (JavaScript кәрәкле)',
-'tog-editondblclick' => 'Биттәрҙе ике сиртеү менән мөхәррирләргә (JavaScript кәрәкле)',
+'tog-usenewrc' => 'Һуңғы төҙәтеүҙәр һәм күҙәтеү исемлегендәге үҙгәрештәрҙе төркөмдәргә бүлергә',
+'tog-numberheadings' => 'Башисемдәрҙе автоматик рәүештә номерлаe',
+'tog-showtoolbar' => 'Мөхәррирләгән ваҡытта өҫкө ҡоралдар панелен күрһәтергә (JavaScript кәрәк)',
+'tog-editondblclick' => 'Биттәрҙе ике сиртеү менән мөхәррирләргә',
 'tog-editsection' => 'Һәр бүлек өсөн «үҙгәртеү» һылтанмаһын күрһәтергә',
-'tog-editsectiononrightclick' => 'Ð\91үлекÑ\82Ó\99Ñ\80Ò\99е Ð¸Ñ\81емдÓ\99Ñ\80енÓ\99 Ñ\81Ñ\8bÑ\81ҡан Ð¼ÐµÐ½Ó\99н Ñ\81иÑ\80Ñ\82еп Ò¯Ò\99гÓ\99Ñ\80Ñ\82еÑ\80гÓ\99 (JavaScript ÐºÓ\99Ñ\80Ó\99кле)',
+'tog-editsectiononrightclick' => 'Ð\91үлекÑ\82Ó\99Ñ\80Ò\99е Ð¸Ñ\81емдÓ\99Ñ\80енÓ\99 Ñ\82Ó©Ñ\80Ñ\82көнөң Ñ\83Ò£ Ñ\8fÒ\93Ñ\8bна Ñ\81иÑ\80Ñ\82еп Ò¯Ò\99гÓ\99Ñ\80Ñ\82еÑ\80гÓ\99',
 'tog-showtoc' => 'Эстәлек күрһәтелһен (3-тән күп башлығы булған биттәрҙә)',
 'tog-rememberpassword' => 'Был браузерҙа (иң күбендә $1 {{PLURAL:$1|көнгә}}) иҫәп яҙыуым хәтерләнһен',
 'tog-watchcreations' => 'Мин төҙөгән биттәрҙе һәм күсергән файлдарҙы күҙәтеү исемлегенә өҫтәргә',
@@ -191,6 +192,7 @@ $messages = array(
 'tog-showhiddencats' => 'Йәшерен категорияларҙы күрһәтергә',
 'tog-norollbackdiff' => 'Кире ҡайтарыуҙан һуң версия айырмалары күрһәтелмәһен',
 'tog-useeditwarning' => 'Мөхәррирләү битенән үҙгәртеүҙәрҙе һаҡламайынса сыҡҡан ваҡытта мине киҫәтергә',
+'tog-prefershttps' => 'Системаға танытылғандан һуң һәр ваҡыт һаҡланыулы тоташыу ҡулланырға',
 
 'underline-always' => 'Һәр ваҡыт',
 'underline-never' => 'Бер ҡасан да',
@@ -291,7 +293,7 @@ $messages = array(
 'newwindow' => '(яңы биттә)',
 'cancel' => 'Бөтөрөргә',
 'moredotdotdot' => 'Дауамы...',
-'morenotlisted' => 'Башҡа бер нимә лә юҡ...',
+'morenotlisted' => 'Был исемлек тулы түгел',
 'mypage' => 'Бит',
 'mytalk' => 'Әңгәмә',
 'anontalk' => 'Был IP-адресының фекер алышыу бите',
@@ -347,6 +349,7 @@ $messages = array(
 'create-this-page' => 'Был битте яһарға',
 'delete' => 'Юҡ  итергә',
 'deletethispage' => 'Был битте юйырға',
+'undeletethispage' => 'Юйылған был битте ҡабат тергеҙеү',
 'undelete_short' => '$1 {{PLURAL:$1|үҙгәртеүҙе}} тергеҙергә',
 'viewdeleted_short' => '{{PLURAL:$1|1 юйылған үҙгәртеүҙе|$1 юйылған үҙгәртеүҙе}} ҡарау',
 'protect' => 'Һаҡларға',
@@ -393,7 +396,7 @@ $1',
 # All link text and link target definitions of links into project namespace that get used by other message strings, with the exception of user group pages (see grouppage).
 'aboutsite' => '{{SITENAME}} тураһында',
 'aboutpage' => 'Project:Тасуирлама',
-'copyright' => '$1 ярашлы эстәлеге менән һәр кем файҙалана ала.',
+'copyright' => '$1 лицензияһына ярашлы, эстәлеге менән һәр кем файҙалана ала (башҡаһы күрһәтелмәһә)',
 'copyrightpage' => '{{ns:project}}:Авторлыҡ хоҡуҡтары',
 'currentevents' => 'Ағымдағы ваҡиғалар',
 'currentevents-url' => 'Project:Ағымдағы ваҡиғалар',
@@ -477,6 +480,12 @@ $1',
 # General errors
 'error' => 'Хата',
 'databaseerror' => 'Мәғлүмәттәр базаһы хатаһы',
+'databaseerror-text' => 'Бирелмәләр базаһында хата киткән.
+Был программа тәьминәтендә хата барлығы күрһәткесе булырға мөмкин.',
+'databaseerror-textcl' => 'Бирелмәләр базаһында хата бар.',
+'databaseerror-query' => 'Һоратыу: $1',
+'databaseerror-function' => 'Функция:$1',
+'databaseerror-error' => 'Хата: $1',
 'laggedslavemode' => "'''Иғтибар:''' биттә һуңғы үҙгәртеүҙәр күрһәтелмәгән булырға мөмкин.",
 'readonly' => 'Мәғлүмәттәр базаһы бикләнгән',
 'enterlockreason' => 'Ябылыу сәбәбен һәм ваҡытын белдерегеҙ.',
@@ -511,6 +520,7 @@ $1',
 'cannotdelete-title' => '"$1" битен юйып булмай',
 'delete-hook-aborted' => 'Үҙгәртеүҙе махсус-процедура кире ҡаҡты.
 Өҫтәмә аңлатма килтерелмәй.',
+'no-null-revision' => '«$1» бите өсөн яңы нулле төҙәтеү яһап булманы',
 'badtitle' => 'Ярамаған исем',
 'badtitletext' => 'Биттең һоратылған исеме дөрөҫ түгел, буш йәки телдәр араһы йәки интервики исеме яңылыш күрһәтелгән. Исемдә тыйылған символдар булыуы ла мөмкин.',
 'perfcached' => 'Был мәғлүмәттәр кэштан алынған, уларҙа һуңғы үҙгәртеүҙәр булмаҫҡа мөмкин. Кэшта иң күбе {{PLURAL:$1|язма}} һаҡлана.',
@@ -537,6 +547,10 @@ $2',
 'namespaceprotected' => '«$1» исем арауығындағы биттәрҙе мөхәррирләү өсөн хоҡуҡтарығыҙ юҡ.',
 'customcssprotected' => 'Был CSS-битте үҙгәртеү хоҡуғығыҙ юҡ, сөнки унда башҡа ҡулланыусының шәхси көйләүҙәре бар.',
 'customjsprotected' => 'Был JavaScript-битте үҙгәртеү хоҡуғығыҙ юҡ, сөнки унда башҡа ҡулланыусының шәхси көйләүҙәре бар.',
+'mycustomcssprotected' => 'Биттең был CSS-ын  мөхәррирләргә хоҡуғығыҙ юҡ.',
+'mycustomjsprotected' => 'Был биттәге  JavaScript-ты мөхәррирләргә хоҡуғығыҙ юҡ.',
+'myprivateinfoprotected' => 'Һеҙгә шәхси мәғлүмәттәрегеҙҙе үҙгәртергә рөхсәт юҡ',
+'mypreferencesprotected' => 'Һеҙҙең көйләүҙәрегеҙҙе мөхәррирләргә хоҡуғығыҙ юҡ.',
 'ns-specialprotected' => '«{{ns:special}}» исем арауығындағы биттәрҙе үҙгәртеп булмай.',
 'titleprotected' => "Был исем менән бит яһау [[User:$1|$1]] тарафынан тыйылған.
 Белдерелгән сәбәп: ''$2''.",
@@ -554,21 +568,26 @@ $2',
 'virus-unknownscanner' => 'беленмәгән антивирус:',
 
 # Login and logout pages
-'logouttext' => "'''Һеҙ иҫәп яҙыуығыҙҙан сыҡтығыҙ.'''
+'logouttext' => "'''Һеҙ эш сеансын тамамланығыҙ.'''
 
-Һеҙ {{SITENAME}} проектында аноним рәүештә дауам итә йәки <span class='plainlinks'>[$1 яңынан таныла]</span> алаһығыҙ (үҙ йәки башҡа исем менән).
-Ҡайһы бер биттәр һеҙ системала танылған һымаҡ күренергә мөмкин, уны бөтөрөү өсөн браузер кэшын таҙартығыҙ.",
+Ҡайһы бер биттәр һеҙ системаға танылмаған кеүек күренеүен дауам итер. Быны бөтөрөү өсөн браузер кэшын таҙартығыҙ.",
 'welcomeuser' => 'Рәхим итегеҙ $1!',
 'welcomecreation-msg' => 'Иҫәп яҙыуығыҙ яһалды.
 Шәхси [[Special:Preferences|{{SITENAME}} көйләүҙәрен]] үҙегеҙгә уңайлы итеп үҙгәртергә онотмағыҙ.',
 'yourname' => 'Ҡатнашыусы исеме',
 'userlogin-yourname' => 'Ҡулланыусы исеме',
+'userlogin-yourname-ph' => 'Иҫәп яҙмағыҙҙың исемен яҙығыҙ',
+'createacct-another-username-ph' => 'Иҫәп яҙмағыҙҙың исемен яҙығыҙ',
 'yourpassword' => 'Серһүҙ',
 'userlogin-yourpassword' => 'Серһүҙ',
 'userlogin-yourpassword-ph' => 'Яңы серһүҙҙе яҙығыҙ',
+'createacct-yourpassword-ph' => 'Серһүҙҙе яҙығыҙ',
 'yourpasswordagain' => 'Серһүҙҙе ҡабаттан яҙыу',
-'remembermypassword' => 'Был компьютерҙа серһүҙемде иҫләргә ($1 {{PLURAL:$1|көндән|көндән}} күп түгел)',
+'createacct-yourpasswordagain' => 'Серһүҙҙе раҫлағыҙ',
+'createacct-yourpasswordagain-ph' => 'Серһүҙҙе тағы бер тапҡыр яҙығыҙ',
+'remembermypassword' => 'Был браузерҙа (иң күбендә $1 {{PLURAL:$1|көнгә}}) иҫәп яҙыуым хәтерләнһен',
 'userlogin-remembermypassword' => 'Системала ҡалырға',
+'userlogin-signwithsecure' => 'Һаҡланыулы тоташыу',
 'yourdomainname' => 'Һеҙҙең домен',
 'password-change-forbidden' => 'Был викила серһүҙегеҙҙе үҙгәртә алмайһығыҙ.',
 'externaldberror' => 'Тышҡы мәғлүмәт базаһы менән танылғанда хата барлыҡҡа килде йәки тышҡы үҙ көйләүҙәрегеҙҙе үҙгәртер өсөн хоҡуҡтарығыҙ етәрле түгел.',
@@ -580,19 +599,43 @@ $2',
 'logout' => 'Тамамлау',
 'userlogout' => 'Тамамлау',
 'notloggedin' => 'Танылмағанһығыҙ',
+'userlogin-noaccount' => 'Иҫәп яҙмағыҙ юҡмы?',
+'userlogin-joinproject' => 'Проектҡа ҡушылырға',
 'nologin' => "Һеҙ теркәлмәгәнһегеҙме әле? '''$1'''.",
 'nologinlink' => 'Иҫәп яҙыуын булдырырға',
 'createaccount' => 'Яңы ҡатнашыусыны теркәү',
 'gotaccount' => "Әгәр Һеҙ теркәлеү үткән булһағыҙ? '''$1'''.",
 'gotaccountlink' => 'Үҙегеҙ менән таныштырығыҙ',
 'userlogin-resetlink' => 'Танылыу мәғлүмәттәрен оноттоғоҙмо?',
-'createaccountmail' => 'эл. почта буйынса',
+'userlogin-resetpassword-link' => 'Серһүҙҙе ҡабул итмәү',
+'helplogin-url' => 'Help:Системаға танылыу',
+'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|Help with logging in]]Системаға инеүҙә ярҙам',
+'userlogin-loggedin' => ' Һеҙ {{GENDER:$1|$1}} булараҡ индегеҙ инде. Башҡа файҙаланыусы булып инер өсөн аҫтағы ҡалыпты ҡулланығыҙ.',
+'userlogin-createanother' => 'Башҡа иҫәп яҙмаһын булдырырға',
+'createacct-join' => 'Аҫта мәғлүмәттәрегеҙҙе яҙығыҙ.',
+'createacct-another-join' => 'Аҫта яңы иҫәп яҙмағыҙҙың мәғлүмәттәрен яҙығыҙ.',
+'createacct-emailrequired' => 'Электрон почта адресы',
+'createacct-emailoptional' => 'Электрон почта адресы (мотлаҡ түгел)',
+'createacct-email-ph' => 'Электрон почта адресығыҙҙы яҙығыҙ',
+'createacct-another-email-ph' => 'Электрон почта адресығыҙҙы яҙығыҙ',
+'createaccountmail' => 'Осраҡлы рәүештә хасил ителгән ваҡытлыса серһүҙҙе файҙаланырға һәм уны миңә ошо электрон почтаһы адресына ебәрергә',
+'createacct-realname' => 'Ысын исемегеҙ (мотлаҡ түгел)',
 'createaccountreason' => 'Сәбәп:',
+'createacct-reason' => 'Сәбәп',
+'createacct-reason-ph' => 'Икенсе иҫәп яҙмаһы һеҙгә ни өсөн кәрәк?',
 'createacct-captcha' => 'Һаҡлылыҡты тикшереү',
+'createacct-imgcaptcha-ph' => 'Өҫтәге тексты индерегеҙ',
+'createacct-submit' => 'Иҫәп яҙмаһын булдырырға',
+'createacct-another-submit' => 'Тағы бер иҫәп яҙмаһын булдырырға',
+'createacct-benefit-heading' => '{{SITENAME}} һеҙҙең кеүек үк кешеләр тарафынан булдырылған',
+'createacct-benefit-body1' => '{{PLURAL:$1|үҙгәртеү}}',
+'createacct-benefit-body2' => '{{PLURAL:$1|мәҡәлә|мәҡәлә|мәҡәләнең}}',
+'createacct-benefit-body3' => 'һуңғы ваҡытта {{PLURAL:$1|ҡатнашыусы|}}',
 'badretype' => 'Һеҙ кереткән серһүҙҙәр тап килмәй.',
 'userexists' => 'Керетелгән исем ҡулланыла инде.
 Зинһар, башҡа исем һайлағыҙ.',
 'loginerror' => 'Танылыу хатаһы',
+'createacct-error' => 'Иҫәп яҙмаһын булдырғандағы хата',
 'createaccounterror' => 'Иҫәп яҙыуын яһап булмай: $1',
 'nocookiesnew' => 'Иҫәп яҙыуы яһалды, ләкин һеҙ танылмағанһығыҙ. {{SITENAME}} ҡатнашыусыны таныу өсөн «cookies» ҡуллана. Һеҙҙә «cookies» тыйылған. Зинһар, уларға рөхсәт бирегеҙ, шунан яңынан ҡатнашыусы исеме һәм серһүҙ менән танылығыҙ.',
 'nocookieslogin' => '{{SITENAME}} ҡатнашыусыны таныу өсөн «cookies» ҡуллана. Һеҙҙә «cookies» тыйылған. Зинһар, уға рөхсәт бирегеҙ һәм яңынан керегеҙ.',
@@ -623,7 +666,7 @@ $2',
 
 Зинһар, серһүҙҙе алғас, системаға яңынан керегеҙ.',
 'blocked-mailpassword' => 'Һеҙҙең IP-адресығыҙҙан мөхәррирләү тыйылған, шул сәбәпле серһүҙ тергеҙеү ғәмәле лә блокланған.',
-'eauthentsent' => 'Күрһәтелгән электрон почта адресына адресты үҙгәртеүҙе раҫлауығыҙ өсөн хат ебәрелде. Хатта, был адрес һеҙҙеке булғанын раҫлау өсөн ниндәй ғәмәлдәрҙе үтәү кәрәкле икәне тураһында мәғлүмәт бар.',
+'eauthentsent' => 'Күрһәтелгән электрон почта адресына адресты үҙгәртеүҙе раҫлауығыҙ өсөн хат ебәрелде. Хатта был адрес һеҙҙеке булғанын раҫлау өсөн ниндәй ғәмәлдәрҙе үтәү кәрәклеге тураһында мәғлүмәт бар.',
 'throttled-mailpassword' => 'Серһүҙҙе иҫләтеү ғәмәле {{PLURAL:$1|һуңғы $1 сәғәт}} эсенде ҡулланылды инде.
 Насар ниәтле ҡулланыуҙарға ҡаршы, Серһүҙ иҫләтеү ғәмәлен {{PLURAL:$1|сәғәт|$1 сәғәт}} эсендә бер тапҡыр ғына ҡулланырға була.',
 'mailerror' => 'Хат ебәреү хатаһы: $1',
@@ -637,21 +680,24 @@ $2',
 'cannotchangeemail' => 'Иҫәп яҙыуы электрон почта адрестарын был викила үҙгәртеп булмай.',
 'emaildisabled' => 'Был сайт электрон почта хәберҙәрен ебәрә алмай',
 'accountcreated' => 'Иҫәп яҙыуы яһалды',
-'accountcreatedtext' => '$1 исемле ҡулланыусы өсөн исәп яҙыуы яһалды.',
+'accountcreatedtext' => '[[{{ns:User}}:$1|$1]]([[{{ns:User talk}}:$1|msj]])   өсөн иҫәп яҙмаһы булдырылды.',
 'createaccount-title' => '{{SITENAME}}: теркәлеү',
 'createaccount-text' => 'Кемдер, электрон почта адресығыҙҙы күрһәтеп, {{SITENAME}} ($4) проектында «$3» пароле менән «$2» исемле иҫәп яҙыуы теркәне. Һеҙҙең кереүегеҙ һәм серһүҙегеҙҙе алмаштырыуығыҙ кәрәк.
 
 Иҫәп яҙыуы яңылыш яһалһа, был хатҡа иғтибар итмәгеҙ.',
 'usernamehasherror' => 'Ҡулланыусы исемендә "#" символы була алмай',
-'login-throttled' => 'Һеҙ системала артыҡ күп танылырға тырыштығыҙ.
\97инһаÑ\80, Ò¡Ð°Ð±Ð°Ñ\82ламаҫÑ\82ан Ð°Ð»Ð´Ð° Ð±ÐµÑ\80аÒ\99 көтөгөҙ.',
+'login-throttled' => 'Һеҙ системаға ҡат-ҡат танылырға тырыштығыҙ.
¢Ð°Ò\93Ñ\8b Ð±ÐµÑ\80 Ñ\82анÑ\8bлÑ\8bÑ\80Ò\99ан Ð°Ð»Ð´Ð°, Ð·Ð¸Ð½Ò»Ð°Ñ\80, $1 көтөгөҙ.',
 'login-abort-generic' => 'Танылыу уңышһыҙ тамамланды',
 'loginlanguagelabel' => 'Тел: $1',
 'suspicious-userlogout' => 'Һеҙҙең сеансты тамамлау тураһында һорауығыҙ кире ҡағылды, сөнки ул төҙөк булмаған браузер йәки кэшлаусы прокси тарафынан ебәрелгән һорауға оҡшаған.',
+'createacct-another-realname-tip' => 'Ысын исемегеҙ (мотлаҡ түгел).
+Уны яҙып ҡуйһағыҙ, ул биткә кем төҙәтеү индергәнен күрһәтеү өсөн ҡулланыласаҡ.',
 
 # Email sending
 'php-mail-error-unknown' => 'PHP-ның mail() функцияһында билдәһеҙ хата',
 'user-mail-no-addy' => 'Электрон почта адресы булмайынса электрон хәбәр ебәреп ҡараны',
+'user-mail-no-body' => 'Буш йә мәғәнәһеҙ йөкмәткеле ҡыҫҡа электрон хат ебәрергә тырышҡан.',
 
 # Change password dialog
 'resetpass' => 'Серһүҙҙе үҙгәртеү',
@@ -661,7 +707,7 @@ $2',
 'newpassword' => 'Яңы серһүҙ:',
 'retypenew' => 'Серһүҙҙе яңынан керетегеҙ:',
 'resetpass_submit' => 'Серһүҙ ҡуйырға һәм танышырға',
-'changepassword-success' => 'Серһүҙегеҙ уңышлы үҙгәртелде! Системала танышыу бара...',
+'changepassword-success' => 'Серһүҙегеҙ уңышлы үҙгәртелде!',
 'resetpass_forbidden' => 'Серһүҙҙе үҙгәртеп булмай',
 'resetpass-no-info' => 'Был битте туранан ҡарау өсөн һеҙгә системала танылырға кәрәк.',
 'resetpass-submit-loggedin' => 'Серһүҙҙе үҙгәртергә',
@@ -669,11 +715,15 @@ $2',
 'resetpass-wrong-oldpass' => 'Хаталы ваҡытлыса йәки ағымдағы серһүҙ.
 Һеҙ, бәлки, серһүҙегеҙҙе алмаштырғанһығыҙ йәки яңы серһүҙ һоратҡанһығыҙ.',
 'resetpass-temp-password' => 'Ваҡытлыса серһүҙ',
+'resetpass-abort-generic' => 'Серһүҙҙе үҙгәртеү киңәйеү тарафынан өҙөлдө.',
 
 # Special:PasswordReset
 'passwordreset' => 'Серһүҙҙе ташлатыу',
+'passwordreset-text-one' => 'Серһүҙегеҙҙе ташлар өсөн ош ҡалыпты тултырығыҙ.',
+'passwordreset-text-many' => '{{PLURAL:$1|Серһүҙҙе ташлар өсөн яландарҙың береһен тултырығыҙ.}}',
 'passwordreset-legend' => 'Серһүҙҙе ташлатыу',
 'passwordreset-disabled' => 'Был викила серһүҙҙе ташлатыу ғәмәлдә түгел',
+'passwordreset-emaildisabled' => 'Был викиҙа электрон почта функцияһы һүндерелгән.',
 'passwordreset-username' => 'Ҡулланыусы исеме:',
 'passwordreset-domain' => 'Домен:',
 'passwordreset-capture' => 'Хәбәрҙең һуңғы хәлен ҡарарғамы?',
@@ -697,9 +747,9 @@ $2
 Әгәр, һеҙ быны һоратмаған булһағыҙ йәки элекке серһүҙегеҙҙе киренән иҫләһәгеҙ һәм уны үҙгәртергә теләмәһәгеҙ, был хатҡа иғтибар итмәгеҙ һәм элекке серһүҙеҙҙе ҡулланыуҙы дауам итегеҙ.',
 'passwordreset-emailelement' => 'Ҡулланыусы исеме: $1
 Ваҡытлыса серһүҙ: $2',
-'passwordreset-emailsent' => 'ЭлекÑ\82Ñ\80он Ð¿Ð¾Ñ\87Ñ\82а Ð°Ñ\88а Ð¸Ò«Ð»Ó\99Ñ\82еү Ñ\85аÑ\82Ñ\8b ебәрелде.',
-'passwordreset-emailsent-capture' => 'Ð\95бÓ\99Ñ\80елгÓ\99н Ñ\85Ó\99Ñ\82еÑ\80лÓ\99Ñ\82еү Ñ\85Ó\99бÓ\99Ñ\80е Ñ\82үбÓ\99ндÓ\99 ÐºÒ¯Ñ\80Ò»Ó\99лгÓ\99н.',
-'passwordreset-emailerror-capture' => 'Ð\9aилеп Ñ\81Ñ\8bҡҡан Ñ\85Ó\99Ñ\82еÑ\80лÓ\99Ñ\82еү Ñ\85Ó\99бÓ\99Ñ\80е Ñ\82үбÓ\99ндÓ\99 ÐºÒ¯Ñ\80Ò»Ó\99Ñ\82елгÓ\99н, Ñ\82ик Ñ\83нÑ\8b ÐµÐ±Ó\99Ñ\80еү Ñ\83Ò£Ñ\8bÑ\88Ò»Ñ\8bÒ\99 Ñ\82амамландÑ\8b. Ð¡Ó\99бÓ\99бе:$1',
+'passwordreset-emailsent' => 'СеÑ\80Ò»Ò¯Ò\99Ò\99е Ñ\82аÑ\88лаÑ\83 Ñ\82Ñ\83Ñ\80аһÑ\8bндаÒ\93Ñ\8b Ð¼Ó\99Ò\93лүмÓ\99Ñ\82 Ð¼ÐµÐ½Ó\99н Ñ\8dлекÑ\82Ñ\80он Ð¿Ð¾Ñ\87Ñ\82а Ð°Ñ\88а Ñ\85аÑ\82 ебәрелде.',
+'passwordreset-emailsent-capture' => 'СеÑ\80Ò»Ò¯Ò\99Ò\99е Ñ\82аÑ\88лаÑ\83 Ñ\82Ñ\83Ñ\80аһÑ\8bндаÒ\93Ñ\8b Ð¼Ó\99Ò\93лүмÓ\99Ñ\82 Ð¼ÐµÐ½Ó\99н Ñ\8dлекÑ\82Ñ\80он Ñ\85аÑ\82 ÐµÐ±Ó\99Ñ\80елде, Ñ\83нÑ\8bÒ£ Ñ\82екÑ\81Ñ\8b Ñ\82үбÓ\99ндÓ\99 Ð±Ð¸Ñ\80елÓ\99:',
+'passwordreset-emailerror-capture' => 'СеÑ\80Ò»Ò¯Ò\99Ò\99е Ñ\82аÑ\88лаÑ\83 Ñ\82Ñ\83Ñ\80аһÑ\8bнда Ñ\85Ó\99бÓ\99Ñ\80 Ð¸Ñ\82еүÑ\81е Ñ\8dлекÑ\82Ñ\80он Ñ\85аÑ\82 Ð±Ñ\83лдÑ\8bÑ\80Ñ\8bлÒ\93айнÑ\8b, Ð»Ó\99кин Ñ\83нÑ\8b  {{GENDER:$2|kullanıcıya}} Ñ\82үбÓ\99ндÓ\99ге Ñ\81Ó\99бÓ\99п Ð°Ñ\80ҡаһÑ\8bнда ÐµÐ±Ó\99Ñ\80еп Ð±Ñ\83лманÑ\8b$1',
 
 # Special:ChangeEmail
 'changeemail' => 'Электрон почта адресын үҙгәртергә',
@@ -713,6 +763,19 @@ $2
 'changeemail-submit' => 'Адресты үҙгәртергә',
 'changeemail-cancel' => 'Кире алырға',
 
+# Special:ResetTokens
+'resettokens' => 'Токендарҙы ташларға',
+'resettokens-text' => 'Иҫәп яҙмағыҙ менән бәйләнгән ҡайһы бер шәхси мәғлүмәттәрегеҙгә инеүгә юл асыусы токендарҙы ташлай алаһығыҙ.
+
+Яңылыштан уларҙы берәйһе менән уртаҡлашҡан  йәки аккаунтығыҙ ваттырылған осраҡта быны эшләү мотлаҡ.',
+'resettokens-no-tokens' => 'Ташлар өсөн токендар юҡ.',
+'resettokens-legend' => 'Токендарҙы ташларға',
+'resettokens-tokens' => 'Токендар:',
+'resettokens-token-label' => '$1 (ағымдағы мәғәнә: $2)',
+'resettokens-watchlist-token' => ' [[Special:Watchlist|күҙәтеүҙәрегеҙ исемлегендә биттәрҙең үҙгәрештәре]] веб-каналы өсөн токен(Atom/RSS)',
+'resettokens-done' => 'Токендар ташланды.',
+'resettokens-resetbutton' => 'Һайланған токендарҙы ташларға',
+
 # Edit page toolbar
 'bold_sample' => 'Ҡалын яҙылыш',
 'bold_tip' => 'Ҡалын яҙылыш',
@@ -789,9 +852,9 @@ $2
 'loginreqlink' => 'танылыу',
 'loginreqpagetext' => 'Башҡа биттәрҙе ҡарау өсөн $1 кәрәк.',
 'accmailtitle' => 'Серһүҙ ебәрелде.',
-'accmailtext' => "[[User talk:$1|$1]] ҡулланыусыһы өсөн яһалған серһүҙ $2 адресына ебәрелде.
+'accmailtext' => "[[User talk:$1|$1]] өсөн осраҡлы яһалған серһүҙ $2 адресына ебәрелде.
 
¡Ð¸Ñ\81Ñ\82емала Ñ\82анÑ\8bлÒ\93андан Ò»Ñ\83Ò£ ''[[Special:ChangePassword|Ñ\81еÑ\80Ò»Ò¯Ò\99егеÒ\99Ò\99е үҙгәртә алаһығыҙ]]''.",
¢Ð°Ð½Ñ\8bлÒ\93андан Ò»Ñ\83Ò£ Ð±Ñ\8bл Ð¸Ò«Ó\99п Ñ\8fÒ\99маһÑ\8b Ó©Ñ\81өн Ñ\81еÑ\80Ò»Ò¯Ò\99Ò\99е ''[[Special:ChangePassword|Ñ\81еÑ\80Ò»Ò¯Ò\99Ò\99е Ò¯Ò\99гÓ\99Ñ\80Ñ\82еү Ó©Ñ\81өн Ð¼Ð°Ñ\85Ñ\81Ñ\83Ñ\81 Ð±Ð¸Ñ\82Ñ\82Ó\99 үҙгәртә алаһығыҙ]]''.",
 'newarticle' => '(Яңы)',
 'newarticletext' => "Һеҙ һылтанма буйынса әлегә яһалмаған биткә күстегеҙ.
 Яңы бит яһар өсөн аҫтағы тәҙрәгә текст керетегеҙ (тулыраҡ мәғлүмәт өсөн [[{{MediaWiki:Helppage}}|ярҙам битен]] ҡарағыҙ).
@@ -891,7 +954,7 @@ $2
 'nocreate-loggedin' => 'Яңы биттәр яһау хоҡуғығыҙ юҡ.',
 'sectioneditnotsupported-title' => 'Бүлектәрҙә мөхәррирләү терәкләнмәй',
 'sectioneditnotsupported-text' => 'Был биттә бүлектәрҙе мөхәррирләү терәкләнмәй.',
-'permissionserrors' => 'Ирешеү хоҡуҡтары хаталары',
+'permissionserrors' => 'Инеү хоҡуғы хатаһы',
 'permissionserrorstext' => 'Түбәндәге {{PLURAL:$1|сәбәп|сәбәптәр}} буйынса һеҙҙең был ғәмәлде үтәү хоҡуғығыҙ юҡ:',
 'permissionserrorstext-withaction' => "«'''$2'''» ғәмәлен башҡара алмайһығыҙ. {{PLURAL:$1|Сәбәбе|Сәбәптәре}}:",
 'recreate-moveddeleted-warn' => "'''Иғтибар: Һеҙ, элек юйылған битте яңынан яһарға теләйһегеҙ.'''
@@ -907,6 +970,7 @@ $2
 Бәлки ул юйылғандыр.',
 'edit-conflict' => 'Төҙәтеүҙәр конфликты',
 'edit-no-change' => 'Текста үҙгәртеүҙер булмау сәбәпле үҙгәртеүегеҙгә иғтибар ителмәне.',
+'postedit-confirmation' => 'Үҙгәртеүегеҙ һаҡланды.',
 'edit-already-exists' => 'Яңы бит яһап булмай.
 Ул былай ҙа бар.',
 'defaultmessagetext' => 'Алдан билдәләнгән яҙма',
@@ -949,6 +1013,7 @@ $2
 'undo-failure' => 'Ара үҙгәртеүҙәр тура килмәү сәбәпле төҙәтеүҙе кире алып булмай.',
 'undo-norev' => 'Үҙгәртеүҙе кире алып булмай, сөнки юҡ йәки юйылған.',
 'undo-summary' => '[[Special:Contributions/$2|$2]] ҡулланыусыһының ([[User talk:$2|фекер алышыу]]) $1 үҙгәртеүенән баш тартыу',
+'undo-summary-username-hidden' => 'Исеме йәшерелгән ҡатнашыусының төҙәтеүен  $1 кире ҡағыу',
 
 # Account creation failure
 'cantcreateaccounttitle' => 'Иҫәп яҙыуын яһап булмай',
@@ -975,8 +1040,8 @@ $3 белдергән сәбәп: ''$2''",
 Аңлатмалар: '''({{int:cur}})''' — хәҙерге версиянан айырма, '''({{int:last}})''' — алдағы версиянан айырма, '''{{int:minoreditletter}}''' — әҙ үҙгәреш яһалған.",
 'history-fieldset-title' => 'Тарихты ҡарарға',
 'history-show-deleted' => 'Юйылғандар ғына',
-'histfirst' => 'Иң иҫке',
-'histlast' => 'Һуңғы',
+'histfirst' => 'Иң иҫкеләр',
+'histlast' => 'Иң һуңғылар',
 'historysize' => '($1 {{PLURAL:$1|байт}})',
 'historyempty' => '(буш)',
 
@@ -1128,6 +1193,7 @@ $1",
 'compareselectedversions' => 'Һайланған версияларҙы сағыштырыу',
 'showhideselectedversions' => 'Һайланған версияларҙы күрһәтергә/йәшерергә',
 'editundo' => 'кире алыу',
+'diff-empty' => '(айырмалар юҡ)',
 'diff-multi' => '({{PLURAL:$2|$2 ҡатнашыусының}} {{PLURAL:$1|ваҡытлы версияһы}} күрһәтелмәгән)',
 'diff-multi-manyusers' => '(Кәмендә {{PLURAL:$2|$2 ҡатнашыусының}} {{PLURAL:$1|ваҡытлы версияһы}} күрһәтелмәгән)',
 'difference-missing-revision' => '$1 айырмаһының {{PLURAL:$2|бер өлгөһө|$2 өлгөһө}} табылманы.
@@ -1176,7 +1242,7 @@ $1",
 'search-interwiki-default' => '$1 һөҙөмтә:',
 'search-interwiki-more' => '(тағы)',
 'search-relatedarticle' => 'Ҡағылышлы',
-'mwsuggest-disable' => 'AJAX-тәҡдимдәрен ябырға',
+'mwsuggest-disable' => 'Эҙләү өйрәтмәләрен һүндерергә',
 'searcheverything-enable' => 'Бар исем арауыҡтарында эҙләргә',
 'searchrelated' => 'ҡағылышлы',
 'searchall' => 'барыһы',
@@ -1197,6 +1263,7 @@ $1",
 'searchdisabled' => '{{SITENAME}} эҙләүе ябыҡ.
 Хәҙергә эҙләүҙе Google менән үтәй алаһығыҙ.
 Тик унда {{SITENAME}} өсөн индекслауҙың иҫке булыуы мөмкинлеген онотмағыҙ.',
+'search-error' => 'Эҙләүҙә хата китте: $1',
 
 # Preferences page
 'preferences' => 'Көйләүҙәр',
@@ -1228,7 +1295,7 @@ $1",
 'prefs-rendering' => 'Күренеш',
 'saveprefs' => 'Һаҡларға',
 'resetprefs' => 'Һаҡланмаған үҙгәрештерҙе таҙартырға',
-'restoreprefs' => 'Ғәҙәттәге бар көйләүҙәргә ҡайтырға',
+'restoreprefs' => 'Алдан ҡуйылған көйләүҙәрҙе тергеҙергә',
 'prefs-editing' => 'Мөхәррирләү',
 'rows' => 'Юлдар:',
 'columns' => 'Бағаналар:',
@@ -1240,6 +1307,8 @@ $1",
 'recentchangesdays-max' => 'Иң күбендә $1 {{PLURAL:$1|көн}}',
 'recentchangescount' => 'Ғәҙәттә күрһәтелгән үҙгәртеүҙәр һаны:',
 'prefs-help-recentchangescount' => 'Һуңғы үҙгәртеүҙәрҙе, биттәр тарихын, журналдарҙы үҙ эсенә ала.',
+'prefs-help-watchlist-token2' => 'Был - күҙәтеүҙәрегеҙ исемлегенең веб-каналы өсөн йәшерен асҡыс.
+Уны белеүселәр күҙәтеүҙәрегеҙ исемлеген уҡый аласаҡ, шуға уны бер кемгә лә әйтмәгеҙ. [[Special:ResetTokens|Уны ташларға теләһәгеҙ, ошонда баҫығыҙ]].',
 'savedprefs' => 'Һеҙҙең көйләүҙәрегеҙ һаҡланды.',
 'timezonelegend' => 'Ваҡыт бүлкәте:',
 'localtime' => 'Урындағы ваҡыт:',
@@ -1283,12 +1352,13 @@ $1",
 'prefs-help-signature' => 'Әңгәмә биттәрендәге хәбәрҙәрегеҙ һеҙҙең имзағыҙға һәм ваҡытҡа әйләнәсәк "<nowiki>~~~~</nowiki>" тамғаларын өҫтәү юлы менән имзаланырға тейеш.',
 'badsig' => 'Хаталы имза. HTML-тегдарҙың дөрөҫлөгөн тикшерегеҙ.',
 'badsiglength' => 'Бигерәк оҙон имза. Имза оҙонлоғо $1 {{PLURAL:$1|символдан}} артыҡ булмаҫҡа тейеш.',
-'yourgender' => 'Зат:',
-'gender-unknown' => 'күрһәтелмәгән',
-'gender-male' => 'Ир-егет',
-'gender-female' => 'Ҡатын-ҡыҙ',
-'prefs-help-gender' => 'Теләк буйынса: ҡатнашыусының затына бәйле ҡайһы бер программа хәбәрҙәрендә ҡулланыла.
-Был дөйөм мәғлүмәт буласаҡ.',
+'yourgender' => 'Ҡайһы тасуирлама һеҙгә ҡулайыраҡ?',
+'gender-unknown' => 'Күрһәткем килмәй',
+'gender-male' => 'Ул вики биттәрен мөхәррирләй',
+'gender-female' => 'Ул вики биттәрен мөхәррирләй',
+'prefs-help-gender' => 'Был көйләүҙе ҡуйыу мотлаҡ түгел.
+Программа тәьминәте был мәғлүмәтте һеҙгә грамматика йәһәтенән дөрөҫ мөрәжәғәт итеү өсөн ҡулланасаҡ. 
+Был мәғлүмәт бөтәһенә лә күренәсәк.',
 'email' => 'Электрон почта',
 'prefs-help-realname' => 'Ысын исемегеҙ (теләк буйынса).
 Әгәр уны күрһәтһәгеҙ, битте кемдең төҙәткәнен күрһәткәндә ҡулланыласаҡ.',
@@ -1302,7 +1372,9 @@ $1",
 'prefs-signature' => 'Имза',
 'prefs-dateformat' => 'Ваҡыт форматы',
 'prefs-timeoffset' => 'Ваҡыт күсереү:',
-'prefs-advancedediting' => 'Киңәйтелгән көйләүҙәр',
+'prefs-advancedediting' => 'Дөйөм көйләүҙәр',
+'prefs-editor' => 'мөхәррир',
+'prefs-preview' => 'алдан байҡау',
 'prefs-advancedrc' => 'Киңәйтелгән көйләүҙәр',
 'prefs-advancedrendering' => 'Киңәйтелгән көйләүҙәр',
 'prefs-advancedsearchoptions' => 'Киңәйтелгән көйләүҙәр',
@@ -1311,6 +1383,7 @@ $1",
 'prefs-displaysearchoptions' => 'Күренеш көйләүҙәре',
 'prefs-displaywatchlist' => 'Күренеш көйләүҙәре',
 'prefs-diffs' => 'Айырмалар',
+'prefs-help-prefershttps' => 'Был көйләү системаға киләһе танылыуҙан һуң ҡулланыласаҡ.',
 
 # User preference: email validation using jQuery
 'email-address-validity-valid' => 'E-mail адрес дөрөҫ булғанға оҡшаған',
@@ -1334,9 +1407,11 @@ $1",
 'userrights-no-interwiki' => 'Һеҙҙең башҡа вики-проекттарҙа ҡатнашыусыларҙың хоҡуҡтарын үҙгәртергә хоҡуҡтарығыҙ юҡ.',
 'userrights-nodatabase' => '$1 базаһы юҡ йәки урындағы (локаль) база түгел.',
 'userrights-nologin' => 'Ҡатнашыусыларҙың хоҡуҡтарын билдәләр өсөн, һеҙ хаким хоҡуҡтары менән [[Special:UserLogin|танылырға]] тейешһегеҙ.',
-'userrights-notallowed' => 'Һеҙҙең иҫәп яҙыуығыҙҙан ҡатнашыусыларҙың хоҡуҡтарын өҫтәү йәки алыу рөхсәт ителмәгән.',
+'userrights-notallowed' => 'Һеҙгә ҡатнашыусыларҙың хоҡуҡтарын өҫтәргә йәки юҡ итергә рөхсәт ителмәгән.',
 'userrights-changeable-col' => 'Һеҙ үҙгәртә алған төркөмдәр',
 'userrights-unchangeable-col' => 'Һеҙ үҙгәртә алмаған төркөмдәр',
+'userrights-conflict' => 'Ҡатнашыусының хоҡуҡтарын үҙгәртеү яраманы! Зинһар, үҙгәрештәрҙе тикшерегеҙ һәм яңынан индерегеҙ.',
+'userrights-removed-self' => 'Һеҙ үҙ хоҡуҡтарығыҙҙы уңышлы юҡ иттегеҙ. Шулай итеп, был биткә башҡаса инә алмаясаҡһығыҙ.',
 
 # Groups
 'group' => 'Төркөм:',
@@ -1380,7 +1455,7 @@ $1",
 'right-reupload-shared' => 'Дөйөм һаҡлағыстағы файлды урындағы (локаль) файл менән алыштырыу',
 'right-upload_by_url' => 'Файлдарҙы URL адрестан күсереү',
 'right-purge' => 'Биттәрҙең кэшын раҫлауһыҙ юйыу',
-'right-autoconfirmed' => 'Үҙгәртеүҙән ярым-һаҡланған биттәрҙе мөхәррирләү',
+'right-autoconfirmed' => 'IP-адресҡа тиҙлек сикләүе юҡ',
 'right-bot' => 'Үҙенән-үҙе башҡарыла торған эш тип иҫәпләнеү',
 'right-nominornewtalk' => 'Фекер алышыу битендә кереткән әҙ үҙгәрештәр яңы хәбәр тураһында белдереү булдырмай',
 'right-apihighlimits' => 'API-һорауҙарҙы башҡарыуға сикләүҙәр аҙыраҡ',
@@ -1400,13 +1475,22 @@ $1",
 'right-hideuser' => 'Ҡатнашыусы исемен тыйыу һәм йәшереү',
 'right-ipblock-exempt' => 'IP адрестарҙы бикләүҙе, авто-бикләүҙәрҙе, арауыҡтарҙы бикләүҙе урап үтеү',
 'right-proxyunbannable' => 'Прокси серверҙарҙы авто-бикләүҙе урап үтеү',
-'right-unblockself' => 'Үҙҙәренең биген асыу',
-'right-protect' => 'Биттәрҙең һаҡланыу дәрәжәһен үҙгәртеү һәм һаҡланған биттәрҙе мөхәррирләү',
-'right-editprotected' => 'Һаҡланған биттәрҙе мөхәррирләү(эҙмә-эҙлекле һаҡлауһыҙ)',
+'right-unblockself' => 'Үҙ бигеңде асырға',
+'right-protect' => 'Биттәрҙең һаҡланыу кимәлен үҙгәртеү һәм баҫҡыслап һаҡланған биттәрҙе төҙәтеү',
+'right-editprotected' => '"{{int:protect-level-sysop}}" булараҡ һаҡланған биттәрҙе төҙәтеү',
+'right-editsemiprotected' => '"{{int:protect-level-autoconfirmed}}" булараҡ һаҡланған биттәрҙе төҙәтеү',
 'right-editinterface' => 'Ҡулланыусы интерфейсын үҙгәртеү',
 'right-editusercssjs' => 'Башҡа ҡатнашыусыларҙың CSS һәм JS файлдарын мөхәррирләү',
 'right-editusercss' => 'Башҡа ҡатнашыусыларҙың CSS файлдарын мөхәррирләү',
 'right-edituserjs' => 'Башҡа ҡатнашыусыларҙың JS файлдарын мөхәррирләү',
+'right-editmyusercss' => 'Файҙаланыусының CSS файлдарын мөхәррирләү',
+'right-editmyuserjs' => 'Үҙеңдең файҙаланыуҙағы JavaScript-файлдарын мөхәррирләргә',
+'right-viewmywatchlist' => 'Үҙеңдең күҙәтеү исемлеген ҡарарға',
+'right-editmywatchlist' => 'Үҙеңдең күҙәтеү исемлеген мөхәррирләргә.
+Ҡайһы бер ғәмәлдәрҙең, быға хоҡуғы булмаһа ла, биттәр өҫтәүенә иғтибар итегеҙ.',
+'right-viewmyprivateinfo' => 'Үҙеңдең шәхси мәғлүмәттәреңде (мәҫәлән, электрон почта адресын, ысын исемеңде) байҡау',
+'right-editmyprivateinfo' => 'Үҙ шәхси мәғлүмәттәреңде (мәҫәлән, электрон почта адресын, ысын исемеңде) төҙәтеү',
+'right-editmyoptions' => 'Үҙ өҫтөнлөктәреңде мөхәррирләргә',
 'right-rollback' => 'Ниндәйҙер битте мөхәррирләгән һуңғы ҡатнашыусының үҙгәртеүҙәрен тиҙ кире алыу',
 'right-markbotedits' => 'Кире алынған үҙгәртеүҙәрҙе бот үҙгәртеүе тип билдәләү',
 'right-noratelimit' => 'Тиҙлек сикләнмәгән',
@@ -1458,8 +1542,8 @@ $1",
 'action-block' => 'Был ҡатнашыусыға мөхәррирләүҙе тыйыу',
 'action-protect' => 'Был биттең һаҡланыу дәрәжәһен үҙгәртеү',
 'action-rollback' => 'битте мөхәррирләгән һуңғы ҡатнашыусының үҙгәртеүҙәрен тиҙ кире алыу',
-'action-import' => 'Ð\91Ñ\8bл Ð±Ð¸Ñ\82Ñ\82е Ð±Ð°Ñ\88ҡа Ð²Ð¸ÐºÐ¸-пÑ\80оекÑ\82Ñ\82ан күсереү',
-'action-importupload' => 'Был битте файл күсереү аша тейәү',
+'action-import' => 'баÑ\88ҡа Ð²Ð¸ÐºÐ¸-пÑ\80оекÑ\82Ñ\82ан Ð±Ð¸Ñ\82Ñ\82Ó\99Ñ\80Ò\99е күсереү',
+'action-importupload' => 'тейәлгән файлдан биттәрҙе күсереү',
 'action-patrol' => 'Башҡаларҙың үҙгәртеүҙәрен тикшерелгән тип билдәләү',
 'action-autopatrol' => 'Үҙ үҙгәртеүҙәрен тикшерелгән тип билдәләү',
 'action-unwatchedpages' => 'Күҙәтелмәгән биттәр исемлеген ҡарау',
@@ -1468,12 +1552,19 @@ $1",
 'action-userrights-interwiki' => 'Ҡатнашыусыларҙың башҡа Викиларҙағы хоҡуҡтарын үҙгәртеү',
 'action-siteadmin' => 'Мәғлүмәттәр базаһын асыу һәм ябыу',
 'action-sendemail' => 'электрон хат ебәреү',
+'action-editmywatchlist' => 'һеҙҙең күҙәтеүҙәр исемелеген мөхәррирләү',
+'action-viewmywatchlist' => 'һеҙҙең күҙәтеүҙәр исемлеген байҡау',
+'action-viewmyprivateinfo' => 'һеҙҙең шәхси мәғлүмәтте байҡау',
+'action-editmyprivateinfo' => 'һеҙҙең шәхси мәғлүмәтте мөхәррирләү',
 
 # Recent changes
 'nchanges' => '$1 {{PLURAL:$1|үҙгәртеү|үҙгәртеү}}',
+'enhancedrc-since-last-visit' => '$1 {{PLURAL:$1|һеҙҙең һуңғы визит}}',
+'enhancedrc-history' => 'тарих',
 'recentchanges' => 'Һуңғы үҙгәртеүҙәр',
 'recentchanges-legend' => 'Һуңғы үҙгәртеүҙәр көйләүҙәре',
 'recentchanges-summary' => 'Төрлө биттәрҙә эшләнгән һуңғы үҙгәртеүҙәр исемлеге',
+'recentchanges-noresult' => 'Был осорҙа тейешле шарттарға тап килгән үҙгәрештәр юҡ.',
 'recentchanges-feed-description' => 'Был таҫмалағы һуңғы үҙгәртеүҙәрҙе күҙәтеп барырға',
 'recentchanges-label-newpage' => 'Был үҙгәртеү яңы бит яһаны',
 'recentchanges-label-minor' => 'Был әҙ үҙгәреш',
@@ -1501,7 +1592,7 @@ $1",
 'rc_categories_any' => 'Һәр',
 'rc-change-size-new' => 'Үҙгәртештән һуң $1 {{PLURAL:$1|байт|байт}}',
 'newsectionsummary' => '/* $1 */ яңы бүлек',
-'rc-enhanced-expand' => 'Ваҡлыҡтарҙы күрһәтергә (JavaScript кәрәкле)',
+'rc-enhanced-expand' => 'Ваҡ-төйәгенә тиклем күрһәтергә',
 'rc-enhanced-hide' => 'Ваҡлыҡтарҙы йәшерергә',
 'rc-old-title' => 'төп нөхсә исеме "$1"',
 
@@ -1521,7 +1612,7 @@ $1",
 'reuploaddesc' => 'Тейәү формаһына кире ҡайтырға',
 'upload-tryagain' => 'Файлдың үҙгәртелгән  тасуирламаһын ебәрергә',
 'uploadnologin' => 'Танылмағанһығыҙ',
-'uploadnologintext' => 'ФайлдаÑ\80 Ñ\82ейÓ\99Ò¯ Ó©Ñ\81өн, Ò»ÐµÒ\99гÓ\99 [[Special:UserLogin|Ñ\82анÑ\8bлÑ\8bÑ\80Ò\93а]] ÐºÓ\99Ñ\80Ó\99к.',
+'uploadnologintext' => 'СеÑ\80веÑ\80Ò\93а Ñ\84айлдаÑ\80 Ñ\82ейÓ\99Ò¯ Ó©Ñ\81өн Ò»ÐµÒ\99 Ñ\82ейеÑ\88һегеÒ\99 $1',
 'upload_directory_missing' => 'Тейәү өсөн директория ($1) юҡ йәки веб-сервер уны булдыра алмай.',
 'upload_directory_read_only' => 'Тейәү өсөн директорияға ($1) веб-сервер яҙҙыра алмай.',
 'uploaderror' => 'Тейәү хатаһы',
@@ -1760,8 +1851,7 @@ $1',
 'upload_source_file' => '(һеҙҙең компьютерҙағы файл)',
 
 # Special:ListFiles
-'listfiles-summary' => 'Был махсус бит бөтә тейәлгән файлдарҙы күрһәтә.
-Ҡулланыусыға күрә һайланһа, был ҡулланыусының һуңғы файл өҫтәүҙәре генә күрһәтелә.',
+'listfiles-summary' => 'Был ярҙамсы бит бөтә тейәлгән файлдарҙы күрһәтә.',
 'listfiles_search_for' => 'Файл исеме буйынса эҙләү:',
 'imgfile' => 'файл',
 'listfiles' => 'Файлдар исемлеге',
@@ -1772,6 +1862,10 @@ $1',
 'listfiles_size' => 'Күләм',
 'listfiles_description' => 'Тасуирлау',
 'listfiles_count' => 'Версиялар',
+'listfiles-show-all' => 'Рәсемдәрҙең иҫке версияларын индерергә',
+'listfiles-latestversion' => 'Ағымдағы версия',
+'listfiles-latestversion-yes' => 'Эйе',
+'listfiles-latestversion-no' => 'Юҡ',
 
 # File description page
 'file-anchor-link' => 'Файл',
@@ -1868,6 +1962,13 @@ $1',
 'randompage' => 'Осраҡлы мәҡәлә',
 'randompage-nopages' => 'Түбәндәге {{PLURAL:$2|исемдәр арауығында|исемдәр арауыҡтарында}} биттәр юҡ: $1.',
 
+# Random page in category
+'randomincategory' => 'Категориялағы осраҡлы бит',
+'randomincategory-invalidcategory' => '$1 тигән категория юҡ.',
+'randomincategory-nopages' => '[[:Category:$1|$1]] категорияһында биттәр юҡ.',
+'randomincategory-selectcategory' => '$1 $2 категорияһынан осраҡлы биткә күсергә.',
+'randomincategory-selectcategory-submit' => 'Күсергә',
+
 # Random redirect
 'randomredirect' => 'Осраҡлы биткә күсеү',
 'randomredirect-nopages' => '"$1" исемдәр арауығында йүнәлтеүҙәр юҡ.',
@@ -1893,6 +1994,14 @@ $1',
 'statistics-users-active-desc' => 'Һуңғы {{PLURAL:$1|көндә|$1 көндә}} ниндәйҙер эшмәкәрлек башҡарған ҡатнашыусылар',
 'statistics-mostpopular' => 'Иң күп ҡаралған биттәр',
 
+'pageswithprop' => 'Үҙенсәлектәре ҡайтанан билдәләнгән биттәр',
+'pageswithprop-legend' => 'Үҙенсәлектәре ҡайтанан билдәләнгән биттәр',
+'pageswithprop-text' => 'Бында айырым үҙенсәлектәре ҡулдан яңыртып билдәләнгән биттәр һанала.',
+'pageswithprop-prop' => 'Үҙенсәлектең атамаһы:',
+'pageswithprop-submit' => 'Табырға',
+'pageswithprop-prophidden-long' => 'Текст үҙенсәлегенең оҙон мәғәнәһе йәшерелгән ($1)',
+'pageswithprop-prophidden-binary' => 'ике тармаҡлы үҙенсәлектең мәғәнәһе йәшерелгән ($1)',
+
 'doubleredirects' => 'Икеле йүнәлтеүҙәр',
 'doubleredirectstext' => 'Был биттә икенсе йүнәлтеү биттәренә йүнәлткән биттәр исемлеге килтерелгән.
 Һәр юл беренсе һәм икенсе йүнәлтеүгә һылтанманан, шулай уҡ икенсе һылтанма йүнәлткән һәм беренсе йүнәлтмә һылтанма яһарға тейеш булған биттән  тора.
@@ -1950,6 +2059,7 @@ $1',
 'mostrevisions' => 'Иң күп үҙгәртеү яһалған биттәр',
 'prefixindex' => 'Исемдәре башында ҡушымта торған биттәр',
 'prefixindex-namespace' => 'Префикслы бар биттәр ( $1 исемдәр арауығы)',
+'prefixindex-strip' => 'Һөҙөмтәләр исемлегендә префиксты йәшерергә',
 'shortpages' => 'Ҡыҫҡа биттәр',
 'longpages' => 'Оҙон биттәр',
 'deadendpages' => 'Көрсөк биттәр',
@@ -1965,6 +2075,7 @@ $1',
 'listusers' => 'Ҡатнашыусылар исемлеге',
 'listusers-editsonly' => 'Кәмендә бер үҙгәртеү индергән ҡатнашыусыларҙы ғына күрһәтергә',
 'listusers-creationsort' => 'Булдырыу көнө буйынса тәртипкә килтерергә',
+'listusers-desc' => 'Кәмеү буйынса айырырға',
 'usereditcount' => '$1 {{PLURAL:$1|үҙгәртеү}}',
 'usercreated' => '$3 ҡулланыусыһының теркәлеү ваҡыты: $1 $2',
 'newpages' => 'Яңы биттәр',
@@ -2045,8 +2156,8 @@ $1',
 'linksearch-ns' => 'Исемдәр арауығы:',
 'linksearch-ok' => 'Эҙләү',
 'linksearch-text' => '<code>*.wikipedia.org</code> һымаҡ төркөм билдәләрен ҡулланырға була.
-Кәмендә, өҫкө кимәл домен кәрәк. Мәҫәлән, <code>*.org</code><br />
-Мөмкин булған протоколдар: <code>$1</code> (бер протокол да күрһәтелмәһә, http:// ҡулланыла)',
+Кәмендә өҫкө кимәл домен кәрәк, мәҫәлән, <code>*.org</code><br />
+Мөмкин булған{{PLURAL:$2|протокол|протоколдар}}: <code>$1</code> (башҡа протокол өҫтәлмәһә, алдан бирелгәне индерелә http://).',
 'linksearch-line' => '$1 адресына $2 битенән һылтанма яһалған',
 'linksearch-error' => 'Төркөм билдәләре URL адрестың башында ғына ҡулланыла ала.',
 
@@ -2059,7 +2170,7 @@ $1',
 # Special:ActiveUsers
 'activeusers' => 'Әүҙем ҡатнашыусылар исемлеге',
 'activeusers-intro' => 'Был — һуңғы $1 {{PLURAL:$1|көн}} эсендә ниҙер башҡарған ҡатнашыусылар исемлеге.',
-'activeusers-count' => 'һуңғы $3 {{PLURAL:$3|көн}} эсендә $1 {{PLURAL:$1|үҙгәртеү}}',
+'activeusers-count' => 'һуңғы $3 {{PLURAL:$3|көн}} эсендәге һуңғы көндә $1 {{PLURAL:$1|үҙгәртеү}}',
 'activeusers-from' => 'Ошондай хәрефтәрҙән башланған ҡатнашыусыларҙы күрһәтергә:',
 'activeusers-hidebots' => 'Боттарҙы йәшерергә',
 'activeusers-hidesysops' => 'Хакимдәрҙе йәшерергә',
@@ -2069,7 +2180,8 @@ $1',
 'listgrouprights' => 'Ҡатнашыусылар төркөмө хоҡуҡтары',
 'listgrouprights-summary' => 'Түбәндә был вики-проектта билдәләнгән ҡатнашыусы төркөмдәре килтерелгән һәм уларҙың хоҡуҡтары күрһәтелгән.
 Шәхси хоҡуҡтар тураһында [[{{MediaWiki:Listgrouprights-helppage}}|өҫтәмә мәғлүмәт]] булыуы мөмкин.',
-'listgrouprights-key' => '* <span class="listgrouprights-granted">Бирелгән хоҡуҡтар</span>
+'listgrouprights-key' => 'Легенда:
+* <span class="listgrouprights-granted">Бирелгән хоҡуҡтар</span>
 * <span class="listgrouprights-revoked">Алынған хоҡуҡтар</span>',
 'listgrouprights-group' => 'Төркөм',
 'listgrouprights-rights' => 'Хоҡуҡтар',
@@ -2140,8 +2252,8 @@ $1',
 'unwatchthispage' => 'Күҙәтеүҙе туҡтатырға',
 'notanarticle' => 'Мәҡәлә түгел',
 'notvisiblerev' => 'Башҡа ҡатнашыусы тарафынан керетелгән һуңғы өлгө юйылған',
-'watchlist-details' => 'Һеҙҙең күҙәтеү исемлегегеҙҙә, фекерләшеү биттәрен һанамағанда, {{PLURAL:$1|$1 бит|$1 бит}} бар.',
-'wlheader-enotif' => 'Электрон почта аша белдереү һайланған',
+'watchlist-details' => 'Һеҙҙең күҙәтеү исемлегегеҙҙә, фекерләшеү биттәрен һанамағанда, {{PLURAL:$1|$1 бит}} бар.',
+'wlheader-enotif' => 'Электрон почта аша белдереү индерелгән.',
 'wlheader-showupdated' => "Һеҙҙең аҙаҡҡы кереүегеҙҙән һуң үҙгәргән биттәр '''ҡалын''' шрифт менән күрһәтелгән.",
 'watchmethod-recent' => 'күҙәтелгән биттәр өсөн аҙаҡҡы үҙгәртеүҙәрҙе ҡарау',
 'watchmethod-list' => 'аҙаҡҡы үҙгәртеүҙәр өсөн күҙәтелгән биттәрҙе ҡарау',
@@ -2226,9 +2338,11 @@ $UNWATCHURL
 'deleteotherreason' => 'Башҡа/өҫтәмә сәбәп:',
 'deletereasonotherlist' => 'Башҡа сәбәп',
 'deletereason-dropdown' => '* Ғәҙәттәге юйыу сәбәптәре
-** Автор һорауы буйынса
-** Авторлыҡ хоҡуҡтарын боҙоу
-** Вандаллыҡ',
+**спам
+**емереү
+**авторлыҡ хоҡуҡтарын боҙоу
+**автор үтенесе буйынса
+**эшләмәгән ҡайтанан йүнәлтеү',
 'delete-edit-reasonlist' => 'Сәбәптәр исемлеген мөхәррирләргә',
 'delete-toobig' => 'Был биттең үҙгәртеүҙәр тарихы бик оҙон, $1 {{PLURAL:$1|өлгөнән}} күберәк.
 {{SITENAME}} проектының эшмәкәрлеге боҙолмауы маҡсатында бындай биттәрҙе юйыу тыйылған.',
@@ -2248,7 +2362,7 @@ $UNWATCHURL
 Һуңғы үҙгәртеүҙәрҙе [[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]] өлгөһөнә ҡайтарылды',
-'revertpage-nouser' => '(Ҡатнашыусының исеме юйылған) уҙгәртеүҙәре [[User:$1|$1]] өлгөһөнә ҡайтарылды',
+'revertpage-nouser' => '(Ҡатнашыусының исеме йәшерелгән) үҙгәртеүҙәре {{GENDER:$1|[[User:$1|$1]]}}өлгөһөнә ҡайтарылды',
 'rollback-success' => '$1 уҙгәртеүҙәре кире алдынды;
 $2 өлгөһөнә ҡайтыу.',
 
@@ -2271,6 +2385,8 @@ $2 өлгөһөнә ҡайтыу.',
 'prot_1movedto2' => '[[$1]] битенең исемен [[$2]] тип үҙгәрткән',
 'protect-badnamespace-title' => 'Һаҡланмаған исемдәр арауығы',
 'protect-badnamespace-text' => 'Был исемдәр арауығындағы биттәрҙе һаҡлап булмай.',
+'protect-norestrictiontypes-text' => 'Был бит һаҡлана алмай, сөнки уға сикләүҙәрҙең рөхсәт ителгән төрҙәре юҡ.',
+'protect-norestrictiontypes-title' => 'Һаҡланмаған бит',
 'protect-legend' => 'Битте һаҡлауҙы раҫлау',
 'protectcomment' => 'Сәбәп:',
 'protectexpiry' => 'Тамамлана:',
@@ -2285,8 +2401,8 @@ $2 өлгөһөнә ҡайтыу.',
 'protect-locked-access' => "Биттең һаҡлау дәрәжеһен үҙгәртер өсөн иҫәп яҙыуығыҙҙың хоҡуҡтары етәрле түгел. '''$1''' битенең хәҙерге һаҡлау көйләүҙәре:",
 'protect-cascadeon' => 'Был бит һаҡланған, сөнки ул эҙмә-эҙлекле һаҡлау ҡуйылған {{PLURAL:$1|биткә|биттәргә}} керә. Һеҙ был биттең һаҡлау дәрәжәһен үҙгәртә алаһығыҙ, ләкин был эҙмә-эҙлекле һаҡлауға йоғонто яһамаясаҡ.',
 'protect-default' => 'Бар ҡулланыусыларға рөхсәт бирергә',
-'protect-fallback' => '«$1» Ñ\80Ó©Ñ\85Ñ\81Ó\99Ñ\82е ÐºÓ\99Ñ\80Ó\99к',
-'protect-level-autoconfirmed' => 'Яңы һәм теркәлмәгән ҡулланыусыларҙан һаҡларға',
+'protect-fallback' => '«$1» Ñ\85оҡÑ\83ҡлÑ\8b Ò¡Ð°Ñ\82наÑ\88Ñ\8bÑ\83Ñ\81Ñ\8bлаÑ\80Ò\93а Ò\93Ñ\8bна Ñ\80Ó©Ñ\85Ñ\81Ó\99Ñ\82е Ð¸Ñ\82елгÓ\99н',
+'protect-level-autoconfirmed' => 'Үҙенән-үҙе раҫланған ҡатнашыусыларға ғына рөхсәт ителгән',
 'protect-level-sysop' => 'Хакимдәр генә',
 'protect-summary-cascade' => 'эҙмә-эҙлекле',
 'protect-expiring' => '$1 бөтә (UTC)',
@@ -2390,9 +2506,9 @@ $1',
 'contributions' => '{{GENDER:$1|Ҡатнашыусы}} өлөшө',
 'contributions-title' => '$1 исемле ҡулланыусының кереткән өлөшө',
 'mycontris' => 'Өлөш',
-'contribsub2' => '$1 ($2) өсөн',
+'contribsub2' => '{{GENDER:$3|$1}} өлөшө ($2)',
 'nocontribs' => 'Күрһәтелгән шарттарға яуап биргән үҙгәртеүҙәр табылманы.',
-'uctop' => '(аÒ\99аҡҡы)',
+'uctop' => '(аÒ\93Ñ\8bмдаÒ\93ы)',
 'month' => 'Айҙан башлап (һәм элегерәк):',
 'year' => 'Йылдан башлап (һәм элегерәк):',
 
@@ -2551,15 +2667,13 @@ $1 ҡатнашыусыһын бикләү сәбәбе: "$2"',
 Әммә ул $2 бикләү арауығына керә һәм был арауыҡтың биге алына ала.',
 'ip_range_invalid' => 'IP адрестар арауығы дөрөҫ түгел.',
 'ip_range_toolarge' => '/$1 арауығынан ҙурыраҡ адрестар арауығын бикләү рөхсәт ителмәй.',
-'blockme' => 'Мине биклә',
 'proxyblocker' => 'Проксины бикләү',
-'proxyblocker-disabled' => 'Был мөмкинлек һүндерелгән.',
 'proxyblockreason' => 'Һеҙҙең IP адресығыҙ бикләнгән, сөнки ул — асыҡ прокси.
 Зинһар, Интернет менән тәъмин итеүсегеҙгә йәки ярҙам хеҙмәтенә мөрәжәғәт итегеҙ һәм уларға был едти хәүефһеҙлек хатаһы тураһында хәбәр итегеҙ.',
-'proxyblocksuccess' => 'Үтәлде',
 'sorbsreason' => 'Һеҙҙең IP адресығыҙ {{SITENAME}} проекты ҡулланған DNSBL исемлегендә асыҡ прокси тип иҫәпләнә.',
 'sorbs_create_account_reason' => 'Һеҙҙең IP адресығыҙ {{SITENAME}} проекты ҡулланған DNSBL исемлегендә асыҡ прокси тип иҫәпләнә.
 Һеҙ иҫәп яҙмаһы булдыра алмайһығыҙ.',
+'xffblockreason' => 'X-Forwarded-For атамаһы эсенә ингән һәм һеҙҙекеме, һеҙ ҡулланған прокси-серверҙыҡымы булған IP-адрес бикләнде. Бикләүҙең тәүсәбәбе ошо ине: $1',
 'cant-block-while-blocked' => 'Үҙегеҙ бикләнгән ваҡытта һеҙ башҡа ҡатнашыусыларҙы бикләй алмайһығыҙ.',
 'cant-see-hidden-user' => 'Һеҙ бикләргә тырышҡан ҡатнашыусы әлеге ваҡытта бикләнгән һәм йәшерелгән.
 Ҡатнашыусыларҙы йәшереү хоҡуғығыҙ булмағанға күрә, һеҙ был бикләүҙе ҡарай йәки үҙгәртә алмайһығыҙ.',
@@ -2591,18 +2705,18 @@ $1 ҡатнашыусыһын бикләү сәбәбе: "$2"',
 # Move page
 'move-page' => '$1 — исемен үҙгәртеү',
 'move-page-legend' => 'Биттең исемен үҙгәртеү',
-'movepagetext' => "Аҫтағы Ñ\84оÑ\80манÑ\8b Ò¡Ñ\83лланÑ\8bÑ\83 Ð±Ð¸Ñ\82Ñ\82ең Ð¸Ñ\81емен Ò¯Ò\99гÓ\99Ñ\80Ñ\82Ó\99 Ò»Ó\99м Ñ\83нÑ\8bÒ£ Ò¯Ò\99гÓ\99Ñ\80Ñ\82еүÒ\99Ó\99Ñ\80 Ñ\8fÒ\99маһÑ\8bн Ñ\8fÒ£Ñ\8b Ñ\83Ñ\80Ñ\8bнÒ\93а ÐºÒ¯Ñ\81еÑ\80Ó\99.
+'movepagetext' => "Аҫтағы Ò¡Ð°Ð»Ñ\8bпÑ\82Ñ\8b Ò¡Ñ\83лланÑ\8bп, Ð±Ð¸Ñ\82Ñ\82ең Ð¸Ñ\81емен Ò¯Ò\99гÓ\99Ñ\80Ñ\82Ó\99 Ò»Ó\99м Ñ\83нÑ\8bÒ£ Ò¯Ò\99гÓ\99Ñ\80Ñ\82еүÒ\99Ó\99Ñ\80 Ð¶Ñ\83Ñ\80налÑ\8bн Ñ\8fÒ£Ñ\8b Ñ\83Ñ\80Ñ\8bнÒ\93а ÐºÒ¯Ñ\81еÑ\80Ó\99 Ð°Ð»Ð°Ò»Ñ\8bÒ\93Ñ\8bÒ\99.
 Биттең элекке исеме яңы биткә йүнәлтеү булып ҡаласаҡ.
-Һеҙ элекке исемгә булған йүнәлтеүҙәрҙе автоматик рәүештә яңы исемгә күсерә алаһығыз.
-Әгәр быны эшләмәһәгеҙ, [[Special:DoubleRedirects|икеле]] һәм [[Special:BrokenRedirects|өҙөлгән йүнәлтеүҙәрҙе]] тикшерегеҙ.
-Һылтанмаларҙың кәрәкле урынға күрһәтеүҙәренең дауам итеүе өсөн һеҙ яуаплы.
+Һеҙ элекке исемгә булған йүнәлтеүҙәрҙе автоматик рәүештә яңы исемгә күсерә алаһығыҙ.
+Әгәр быны эшләмәһәгеҙ, [[Special:DoubleRedirects|икеле]] һәм [[Special:BrokenRedirects|өҙөлгән йүнәлтеүҙәр]] барлығын тикшерегеҙ.
+Һылтанмаларҙың кәрәкле урынға күрһәтеүен дауам итеүе өсөн һеҙ яуаплы.
 
-Иғтибар итегеҙ, әгәр яңы исемле бит бар икән, биттең исеме '''үҙгәртелмәйәсәк'''; элекке бит йүнәлтеү, буш һәм үҙгәртеү тарихына эйә булмаған осраҡтарҙан башҡа.
\91Ñ\8bл Ñ\88Ñ\83нÑ\8b Ð°Ò£Ð»Ð°Ñ\82а: Ð±Ð¸Ñ\82 Ð¸Ñ\81емен Ñ\8fÒ£Ñ\8bлÑ\8bÑ\88 Ò¯Ò\99гÓ\99Ñ\80Ñ\82Ò»Ó\99геÒ\99, Ð±Ð¸Ñ\82Ñ\82е кире ҡайтара алаһығыҙ, ләкин булған битте юя алмайһығыҙ.
+Иғтибар итегеҙ: әгәр яңы һайланған исемдәге тағы бер бит бар икән, биттең исеме '''үҙгәртелмәйәсәк'''; ул бит йүнәлтеүсе  йәки буш булһа һәм төҙәтеүҙәр тарихына эйә булмаһа ғына,  был мөмкин.
¢Ð¸Ð¼Ó\99к, Ð±Ð¸Ñ\82Ñ\82ең Ð¸Ñ\81емен Ñ\8fÒ£Ñ\8bлÑ\8bÑ\88 Ò¯Ò\99гÓ\99Ñ\80Ñ\82Ò»Ó\99геÒ\99, Ð±Ð¸Ñ\82Ñ\82е Ñ\8dлекке Ð¸Ñ\81еменÓ\99 кире ҡайтара алаһығыҙ, ләкин булған битте юя алмайһығыҙ.
 
-'''Ð\98Ò\93Ñ\82ибаÑ\80!'''
-Популяр биттәрҙең исемен үҙгәртеү көтмәгән һөҙөмтәләргә килтерергә мөмкин.
\94аÑ\83ам Ð¸Ñ\82еÑ\80Ò\99Ó\99н Ð°Ð»Ð´Ð°, Ð±Ó©Ñ\82Ó\99 Ð±Ñ\83лаÑ\81аҡ Ò»Ó©Ò\99өмÑ\82Ó\99лÓ\99Ñ\80Ò\99е Ð°Ò£Ð»Ð°Ñ\83Ñ\8bÒ\93Ñ\8bÒ\99Ò\99Ñ\8b Ñ\83йлағыҙ.",
+'''Ð\98ҫкÓ\99Ñ\80Ñ\82еү!'''
+\"Популяр\" биттәрҙең исемен үҙгәртеү күләмле һәм көтөлмәгән һөҙөмтәләргә килтерергә мөмкин.
\94аÑ\83ам Ð¸Ñ\82еÑ\80Ò\99Ó\99н Ð°Ð»Ð´Ð°, Ð¸Ñ\85Ñ\82имал Ð±Ñ\83лÒ\93ан Ò»Ó©Ò\99өмÑ\82Ó\99лÓ\99Ñ\80Ò\99е Ð°Ò£Ð»Ð°Ñ\83Ñ\8bÒ\93Ñ\8bÒ\99Ò\93а Ñ\8bÑ\88анÑ\8bғыҙ.",
 'movepagetext-noredirectfixer' => "Аҫтағы форманы ҡулланыу биттең исемен үҙгәртә һәм уның үҙгәртеүҙәр яҙмаһын яңы урынға күсерә.
 Биттең элекке исеме яңы биткә йүнәлтеү булып ҡаласаҡ.
 Һеҙ элекке исемгә булған йүнәлтеүҙәрҙе автоматик рәүештә яңы исемгә күсерә алаһығыз.
@@ -2721,6 +2835,8 @@ $1 ҡатнашыусыһын бикләү сәбәбе: "$2"',
 'thumbnail-more' => 'Ҙурайтырға',
 'filemissing' => 'Файл юҡ',
 'thumbnail_error' => 'Шартлы рәсем булдырыу хатаһы: $1',
+'thumbnail_error_remote' => '$1 хата тураһында хәбәр итә:
+$2',
 'djvu_page_error' => 'DjVu битенең һаны биттәр һанынан ашҡан',
 'djvu_no_xml' => 'DjVu файлы өсөн XML сығарып булмай',
 'thumbnail-temp-create' => 'Эскиздың ваҡытлыса файлын яһап булмай',
@@ -2897,6 +3013,8 @@ The wiki server cannot provide data in a format your client can read.',
 'spam_reverting' => '$1 һылтанмаһыҙ һуңғы өлгөгә ҡайтарыу',
 'spam_blanking' => 'Бөтә өлгөләрҙә лә $1 һылтанмаһы бар, таҙартыу',
 'spam_deleting' => 'Бөтә өлгөләрҙә лә $1 һылтанма бар, таҙартыу бара',
+'simpleantispam-label' => "Спамға ҡаршы тикшереү.
+Быны '''ТУЛТЫРМАҒЫҘ'''!",
 
 # Info page
 'pageinfo-title' => '«$1» буйынса мәғлүмәт',
@@ -2910,12 +3028,13 @@ The wiki server cannot provide data in a format your client can read.',
 'pageinfo-length' => 'Бит оҙонлоғо (байттарҙа)',
 'pageinfo-article-id' => 'Бит идентификаторы',
 'pageinfo-language' => 'Бит эстәлегенең теле',
-'pageinfo-robot-policy' => 'ЭÒ\99лÓ\99Ò¯ Ñ\85еÒ\99мÓ\99Ñ\82Ñ\82Ó\99Ñ\80е Ñ\81Ñ\82аÑ\82Ñ\83Ñ\81Ñ\8b',
-'pageinfo-robot-index' => 'Ð\98ндекÑ\81лана',
-'pageinfo-robot-noindex' => 'Ð\98ндекÑ\81ланмай',
+'pageinfo-robot-policy' => 'ЭÒ\99лÓ\99Ò¯ Ñ\80обоÑ\82Ñ\82аÑ\80Ñ\8b Ñ\82аÑ\80аÑ\84Ñ\8bнан Ð¸Ð½Ð´ÐµÐºÑ\81аÑ\86иÑ\8fланÑ\8bÑ\83',
+'pageinfo-robot-index' => 'РөÑ\85Ñ\81Ó\99Ñ\82 Ð¸Ñ\82елгÓ\99н',
+'pageinfo-robot-noindex' => 'РөÑ\85Ñ\81Ó\99Ñ\82 Ð¸Ñ\82елмÓ\99й',
 'pageinfo-views' => 'Ҡарау һаны',
 'pageinfo-watchers' => 'Битте күҙәтеүселәр һаны',
-'pageinfo-redirects-name' => 'Был биткә йүнәлтеүҙәр',
+'pageinfo-few-watchers' => 'Күп тигәндә $1 {{PLURAL:$1|күҙәтеүсе}}',
+'pageinfo-redirects-name' => 'Был биткә йүнәлтеүҙәр һаны',
 'pageinfo-subpages-name' => 'Был биттең эске биттәре',
 'pageinfo-subpages-value' => '$1 ($2 {{PLURAL:$2|йүнәлтеү}}; $3 {{PLURAL:$3|ябай}})',
 'pageinfo-firstuser' => 'Битте яһаусы',
@@ -2929,6 +3048,7 @@ The wiki server cannot provide data in a format your client can read.',
 'pageinfo-magic-words' => 'Тылсымлы {{PLURAL:$1|һүҙ|һүҙҙәр}} ($1)',
 'pageinfo-hidden-categories' => 'Йәшерен {{PLURAL:$1|категория|категориялар}} ($1)',
 'pageinfo-templates' => 'Ҡулланылған {{PLURAL:$1|ҡалып|ҡалыптар}} ($1)',
+'pageinfo-transclusions' => '{{PLURAL:$1|Индерелгән биттәр}} ($1)',
 'pageinfo-toolboxlink' => 'Бит мәғлүмәттәре',
 'pageinfo-redirectsto' => 'Йүнәлтеү',
 'pageinfo-redirectsto-info' => 'мәғлүмәт',
@@ -2937,6 +3057,10 @@ The wiki server cannot provide data in a format your client can read.',
 'pageinfo-protect-cascading' => 'Бынан башлап һикәлтәле һаҡлау',
 'pageinfo-protect-cascading-yes' => 'Эйе',
 'pageinfo-protect-cascading-from' => 'Бынан башлап һикәлтәле һаҡлау',
+'pageinfo-category-info' => 'Категория тураһында мәғлүмәт',
+'pageinfo-category-pages' => 'Биттәр һаны',
+'pageinfo-category-subcats' => 'Категория бүлемдәре һаны',
+'pageinfo-category-files' => 'Файлдар һаны',
 
 # Skin names
 'skinname-cologneblue' => 'Кёльн һағышы',
@@ -3019,9 +3143,25 @@ $1',
 'minutes' => '{{PLURAL:$1|$1 минут|$1 минут}}',
 'hours' => '{{PLURAL:$1|$1 сәғәт|$1 сәғәт}}',
 'days' => '{{PLURAL:$1|$1 көн|$1 көн}}',
+'weeks' => '{{PLURAL:$1|$1 аҙна|$1 аҙна|}}',
+'months' => '{{PLURAL:$1|$1 ай}}',
+'years' => '{{PLURAL:$1|$1 йыл}}',
 'ago' => '$1 элек',
 'just-now' => 'яңы ғына',
 
+# Human-readable timestamps
+'hours-ago' => '$1 {{PLURAL:$1|сәғәт}} элек',
+'minutes-ago' => '$1 {{PLURAL:$1|минут}} элек',
+'seconds-ago' => '$1 {{PLURAL:$1|секунд}} элек',
+'monday-at' => 'дүшәмбе $1',
+'tuesday-at' => 'шишәмбе $1',
+'wednesday-at' => 'шаршамбы $1',
+'thursday-at' => 'кесе йома $1',
+'friday-at' => 'йома $1',
+'saturday-at' => 'шәмбе $1',
+'sunday-at' => 'йәкшәмбе $1',
+'yesterday-at' => 'Кисә $1',
+
 # Bad image list
 'bad_image_list' => 'Формат киләһе рәүештә булырға тейеш:
 
@@ -3234,7 +3374,7 @@ $1',
 'exif-compression-4' => 'CCITT Group 4, факслы кодлау',
 
 'exif-copyrighted-true' => 'Авторлыҡ хоҡуҡтары менән һаҡлана',
-'exif-copyrighted-false' => 'Ð\94өйөм Ð¼Ð¸Ð»ÐµÐº',
+'exif-copyrighted-false' => 'Ð\90вÑ\82оÑ\80лÑ\8bÒ¡-Ñ\85оҡÑ\83ҡи Ñ\81Ñ\82аÑ\82Ñ\83Ñ\81 Ð¸Ð½Ð´ÐµÑ\80елмÓ\99гÓ\99н',
 
 'exif-unknowndate' => 'Билдәһеҙ көн',
 
@@ -3498,11 +3638,10 @@ $3
 $5
 
 Был раҫлау коды $4 ғәмәлдән сыға.',
-'confirmemail_body_set' => 'Кемдер, бәлки һеҙҙер, $1 IP адресынан 
-{{SITENAME}}  проектында "$2" иҫәп яҙмаһының электрон почта адресын ошо адрес итеп билдәләгән.
+'confirmemail_body_set' => 'Кемдер (бәлки, һеҙҙер) $1 IP-адресынан 
+{{SITENAME}}  проектында "$2" иҫәп яҙмаһының электрон почта адресы итеп ошо адресты күрһәткән.
 
-Был иҫәп яҙмаһы ысынлап та һеҙҙеке икәнен раҫлау өсөн һәм
-{{SITENAME}} проектында электрон почта мөмкинлектәрен яңынан тоҡандырыу өсөн, браузерығыҙҙа түбәндәге һылтанманы асығыҙ:
+Был иҫәп яҙмаһы ысынлап та һеҙҙеке икәнен раҫлау һәм {{SITENAME}} сайтынан хат ебәреү мөмкинлектәрен яңынан тоҡандырыу өсөн, браузерығыҙҙа түбәндәге һылтанманы асығыҙ:
 
 $3
 
@@ -3628,6 +3767,7 @@ $5
 'version-license' => 'Рөхсәтнамә',
 'version-poweredby-credits' => "Был вики проект '''[//www.mediawiki.org/ MediaWiki]''' нигеҙендә эшләй, copyright © 2001-$1 $2.",
 'version-poweredby-others' => 'башҡалар',
+'version-poweredby-translators' => 'translatewiki.net тәржемәселәре',
 'version-credits-summary' => '[[Special:Version|MediaWiki]] үҫешенә өлөш индергәндәре өсөн киләһе ҡатнашыусыларға рәхмәт әйтәбеҙ.',
 'version-license-info' => 'MediaWiki — ирекле программа, һеҙ уны Ирекле программалар фонды тарафынан баҫтырылған GNU General Public License рөхсәтнамәһенә ярашлы тарата һәм/йәки үҙгәртә алаһығыҙ (рөхсәтнамәнең йә исенсе өлгөһө, йә унан һуңғы өлгөләре).
 
@@ -3641,6 +3781,18 @@ MediaWiki файҙалы булыр, тигән өмөттә, ләкин БЕР
 'version-entrypoints-header-entrypoint' => 'Инеш урыны',
 'version-entrypoints-header-url' => 'URL',
 
+# Special:Redirect
+'redirect' => 'Файлдан, файҙаланыусынан йә версияның тиңләштереүсеһенән артабан йүнәлтеү',
+'redirect-legend' => 'Файлға йәки биткә йүнәлтеү',
+'redirect-summary' => 'Был махсус бит файлға (файлдың исеменән), биткә (версияның тиңләштереүсеһенән) йәки ҡатнашыусының битенә (ҡатнашыусының һанлы тиңләштереүсеһенән) йүнәлтә.',
+'redirect-submit' => 'Күсергә',
+'redirect-lookup' => 'Эҙләү',
+'redirect-value' => 'Мәғәнәһе:',
+'redirect-user' => 'Ҡатнашыусының тиңләштереүсеһе',
+'redirect-revision' => 'Биттең версияһы',
+'redirect-file' => 'Файлдың исеме',
+'redirect-not-exists' => 'Мәғәнәһе табылманы',
+
 # Special:FileDuplicateSearch
 'fileduplicatesearch' => 'Бер иш файлдарҙы эҙләү',
 'fileduplicatesearch-summary' => 'Бер иш файлдарҙы хэш-кодтары буйынса эҙләү.',
@@ -3667,7 +3819,7 @@ MediaWiki файҙалы булыр, тигән өмөттә, ләкин БЕР
 'specialpages-group-highuse' => 'Йыш ҡулланылған биттәр',
 'specialpages-group-pages' => 'Биттәр исемлеге',
 'specialpages-group-pagetools' => 'Биттәр өсөн ҡоралдар',
-'specialpages-group-wiki' => 'Ð\92ики Ð¼Ó\99Ò\93лүмÓ\99Ñ\82 һәм ҡоралдар',
+'specialpages-group-wiki' => 'Ð\9cÓ\99Ò\93лүмÓ\99Ñ\82Ñ\82Ó\99Ñ\80 һәм ҡоралдар',
 'specialpages-group-redirects' => 'Йүнәлтеүсе махсус биттәр',
 'specialpages-group-spam' => 'Спамға ҡаршы ҡоралдар',
 
@@ -3689,12 +3841,16 @@ MediaWiki файҙалы булыр, тигән өмөттә, ләкин БЕР
 'tags' => 'Ҡулланылған үҙгәртеү билдәләре',
 'tag-filter' => '[[Special:Tags|Билдәләрҙе]] һайлау:',
 'tag-filter-submit' => 'Һайлау',
+'tag-list-wrapper' => '([[Special:Tags|{{PLURAL:$1|Тамғалар}}]]: $2)',
 'tags-title' => 'Билдәләр',
 'tags-intro' => 'Был биттә программа үҙгәртеүҙәрҙе билдәләү өсөн ҡулланған билдәләр һәм уларҙың мәғәнәләре исемлеге килтерелгән.',
 'tags-tag' => 'Билдә исеме',
 'tags-display-header' => 'Үҙгәртеүҙәр исемлегендә күрһәтеү',
 'tags-description-header' => 'Мәғәнәһенең тулы тасуирламаһы',
+'tags-active-header' => 'Әүҙемме?',
 'tags-hitcount-header' => 'Билдәләнгән үҙгәртеүҙәр',
+'tags-active-yes' => 'Эйе',
+'tags-active-no' => 'Юҡ',
 'tags-edit' => 'үҙгәртергә',
 'tags-hitcount' => '$1 {{PLURAL:$1|үҙгәртеү|үҙгәртеү}}',
 
@@ -3715,6 +3871,7 @@ MediaWiki файҙалы булыр, тигән өмөттә, ләкин БЕР
 'dberr-problems' => 'Ғәфү итегеҙ! Был сайтта техник ҡыйынлыҡтар тыуҙы.',
 'dberr-again' => 'Битте бер нисә минуттан яңыртып ҡарағыҙ.',
 'dberr-info' => '(Мәғлүмәттәр базаһы серверы менән тоташтырылып булмай: $1)',
+'dberr-info-hidden' => '(Мәғлүмәт базаларының серверына тоташып булмай)',
 'dberr-usegoogle' => 'Әлегә һеҙ Google ярҙамында эҙләп ҡарай алһығыҙ.',
 'dberr-outofdate' => 'Әммә уның индекстары иҫекргән булыуы мөмкинлеген күҙ уңында тотоғоҙ.',
 'dberr-cachederror' => 'Түбәндә һоралған биттең кэшта һаҡланған өлгөһө күрһәтелгән, унда аҙаҡҡы үҙгәртеүҙәр булмауы мөмкин.',
@@ -3730,23 +3887,26 @@ MediaWiki файҙалы булыр, тигән өмөттә, ләкин БЕР
 'htmlform-submit' => 'Ебәрергә',
 'htmlform-reset' => 'Үҙгәртеүҙәрҙе кире алырға',
 'htmlform-selectorother-other' => 'Башҡа',
+'htmlform-no' => 'Юҡ',
+'htmlform-yes' => 'Эйе',
+'htmlform-chosen-placeholder' => 'Вариант һайлағыҙ',
 
 # SQLite database support
 'sqlite-has-fts' => '$1, тулы текст буйынса эҙләү мөмкинлеге менән',
 'sqlite-no-fts' => '$1, тулы текст буйынса эҙләү мөмкинлекһеҙ',
 
 # New logging system
-'logentry-delete-delete' => '$1 $3 битен юйҙы',
-'logentry-delete-restore' => '$1 $3 битен тергеҙҙе',
-'logentry-delete-event' => '$1 {{PLURAL:$5|$5 журнал яҙмаһының|$5 журнал яҙмаһының}} күренеүсәнлеген $3 битендә үҙгәртте: $4',
-'logentry-delete-revision' => '$1 {{PLURAL:$5|$5 версияның|$5 версияның}} күренеүсәнлеген $3 битендә үҙгәртте: $4',
-'logentry-delete-event-legacy' => '$1 $3 журнал яҙмаһының күренеүсәнлеген үҙгәртте',
-'logentry-delete-revision-legacy' => '$1 $3 битендә версияларҙың күренеүсәнлеген үҙгәртте',
-'logentry-suppress-delete' => '$1 $3 битен йәшерҙе',
-'logentry-suppress-event' => '$1 {{PLURAL:$5|$5 журнал яҙмаһының|$5 журнал яҙмаһының}} күренеүсәнлеген $3 битендә йәшерен үҙгәртте: $4',
-'logentry-suppress-revision' => '$1 {{PLURAL:$5|$5 версияның|$5 версияның}} күренеүсәнлеген $3 битендә йәшерен үҙгәртте: $4',
-'logentry-suppress-event-legacy' => '$1 $3 журнал яҙмаһының күренеүсәнлеген йәшерен үҙгәртте',
-'logentry-suppress-revision-legacy' => '$1 $3 битендә версияларҙың күренеүсәнлеген йәшерен үҙгәртте',
+'logentry-delete-delete' => '$1 $3 битен {{GENDER:$2|юйҙы}}',
+'logentry-delete-restore' => '$1 $3 битен {{GENDER:$2|тергеҙҙе}}',
+'logentry-delete-event' => '$1 журналдағы {{PLURAL:$5|яҙманы}} $3: $4 {{GENDER:$2|үҙгәртте}}',
+'logentry-delete-revision' => '$1 {{PLURAL:$5|$5 версияның}} күренеүсәнлеген   $3: $4 битендә {{GENDER:$2|үҙгәртте}}',
+'logentry-delete-event-legacy' => '$1  $3 журналы яҙмаларының күренеүсәнлеген {{GENDER:$2|үҙгәртте}}',
+'logentry-delete-revision-legacy' => '$1  $3 битендә версияларҙың күренеүсәнлеген {{GENDER:$2|үҙгәртте}}',
+'logentry-suppress-delete' => '$1 $3 битен {{GENDER:$2|баҫырылдырҙы}}',
+'logentry-suppress-event' => '$1 журналдағы {{PLURAL:$5|$5 яҙманың}} күренеүсәнлеген $3 битендә йәшерен {{GENDER:$2|}}үҙгәртте: $4',
+'logentry-suppress-revision' => '$1 {{PLURAL:$5|$5 версияның}} күренеүсәнлеген $3 битендә йәшерен үҙгәртте: $4',
+'logentry-suppress-event-legacy' => '$1  журнал яҙмаларының күренеүсәнлеген йәшерен  {{GENDER:$2|үҙгәртте}}$3',
+'logentry-suppress-revision-legacy' => '$1 $3 битендә версияларҙың күренеүсәнлеген йәшерен {{GENDER:$2|}}',
 'revdelete-content-hid' => 'эстәлек йәшерелгән',
 'revdelete-summary-hid' => 'төҙәтеү аңлатмаһы йәшерелде',
 'revdelete-uname-hid' => 'ҡатнашыусы исеме йәшерелгән',
@@ -3755,19 +3915,20 @@ MediaWiki файҙалы булыр, тигән өмөттә, ләкин БЕР
 'revdelete-uname-unhid' => 'ҡатнашыусы исеме күрһәтелде',
 'revdelete-restricted' => 'хакимдәргә ҡаршы ҡулланылған сикләүҙәр',
 'revdelete-unrestricted' => 'хакимдәрҙән алынған сикләүҙәр',
-'logentry-move-move' => '$1 $3 битенең исемен үҙгәртте. Яңы исеме: $4',
-'logentry-move-move-noredirect' => '$1 $3 битенең исемен йүнәлтеү ҡуймайынса үҙгәртте. Яңы исеме: $4',
-'logentry-move-move_redir' => '$1 $3 битенең исемен йүнәлтеү өҫтөнән үҙгәртте. Яңы исеме: $4',
-'logentry-move-move_redir-noredirect' => '$1 $3 битенең исемен йүнәлтеү ҡуймайынса һәм йүнәлтеү өҫтөнән үҙгәртте. Яңы исеме: $4',
-'logentry-patrol-patrol' => '$1 $3 битенең $4 версияһын билдәләне.',
-'logentry-patrol-patrol-auto' => '$1 $3 битенең $4 версияһын автоматик рәүештә билдәләне.',
-'logentry-newusers-newusers' => '$1 ҡатнашыусыһының иҫәп яҙмаһы булдырылды',
-'logentry-newusers-create' => '$1 ҡатнашыусыһының иҫәп яҙмаһы булдырылды',
-'logentry-newusers-create2' => '$3 ҡатнашыусыһының иҫәп яҙмаһы $1 тарафынан булдырылды',
-'logentry-newusers-autocreate' => 'Автоматик рәүештә $1 иҫәп яҙыуы яһалды',
-'logentry-rights-rights' => '$1 $3 ҡулланыусыһының төркөмдәрҙәге ағзалығын $4 икән, $5 тип үҙгәртте',
-'logentry-rights-rights-legacy' => '$1 $3 ҡулланыусыһының төркөм ағзалығын үҙгәртте',
-'logentry-rights-autopromote' => '$1 автоматик рәүештә $2 икән, $3 ителде.',
+'logentry-move-move' => '$1  $3 битенең исемен {{GENDER:$2| үҙгәртте}}. Яңы исеме: $4',
+'logentry-move-move-noredirect' => '$1 $3 битенең исемен йүнәлтеү ҡуймайынса {{GENDER:$2|үҙгәртте}}. Яңы исеме: $4',
+'logentry-move-move_redir' => '$1 $3 битенең исемен йүнәлтеү өҫтөнән {{GENDER:$2|үҙгәртте}}. Яңы исеме: $4',
+'logentry-move-move_redir-noredirect' => '$1 $3 битенең исемен йүнәлтеү ҡуймайынса һәм йүнәлтеү өҫтөнән {{GENDER:$2|үҙгәртте}}. Яңы исеме: $4',
+'logentry-patrol-patrol' => '$1 $3 битенең $4 версияһын {{GENDER:$2|тикшерҙе}}.',
+'logentry-patrol-patrol-auto' => '$1 $3 битенең $4 версияһын автоматик рәүештә {{GENDER:$2|тикшерҙе}}.',
+'logentry-newusers-newusers' => ' {{GENDER:$2|ҡатнашыусы}} $1 иҫәп яҙмаһы булдырҙы',
+'logentry-newusers-create' => '{{GENDER:$2|ҡатнашыусы}} $1 иҫәп яҙмаһы булдырҙы.',
+'logentry-newusers-create2' => '$1 {{GENDER:$2|ҡатнашыусы}} $3 иҫәп яҙмаһын булдырҙы',
+'logentry-newusers-byemail' => '$1 {{GENDER:$2|}} $3 иҫәп яҙмаһын булдырҙы һәм серһүҙ электрон почта аша ебәрелде',
+'logentry-newusers-autocreate' => 'Автоматик рәүештә {{GENDER:$2| ҡатнашыусының}} $1 иҫәп яҙмаһы яһалды',
+'logentry-rights-rights' => '$1  $3 файҙаланыусының төркөмдәрҙәге ағзалығын $4 урынына $5 тип {{GENDER:$2|үҙгәртте}}',
+'logentry-rights-rights-legacy' => '$1  $3 өсөн төркөмдәрҙәге ағзалыҡты {{GENDER:$2|үҙгәртте}}',
+'logentry-rights-autopromote' => '$1 {{GENDER:$2|}} автоматик рәүештә {{GENDER:$2|}} $4 урынына $5 ителде.',
 'rightsnone' => '(юҡ)',
 
 # Feedback
@@ -3822,6 +3983,7 @@ MediaWiki файҙалы булыр, тигән өмөттә, ләкин БЕР
 'api-error-ok-but-empty' => 'Эске хата: серверҙан яуап юҡ.',
 'api-error-overwrite' => 'Булған файлды алыштырыу рөхсәт ителмәй.',
 'api-error-stashfailed' => 'Эске хата: сервер ваҡытлыса файлды һаҡлай алманы.',
+'api-error-publishfailed' => 'Эске хата: сервер ваҡытлыса файлды һаҡлай алманы.',
 'api-error-timeout' => 'Көтөлгән ваҡыт эсендә сервер яуып бирмәне.',
 'api-error-unclassified' => 'Билдәһеҙ хата барлыҡҡа килде.',
 'api-error-unknown-code' => 'Билдәһеҙ хата: «$1»',
@@ -3842,4 +4004,22 @@ MediaWiki файҙалы булыр, тигән өмөттә, ләкин БЕР
 'duration-centuries' => '$1 {{PLURAL:$1|быуат|быуаттар}}',
 'duration-millennia' => '$1 {{PLURAL:$1|меңйыллыҡ|меңйыллыҡтар}}',
 
+# Image rotation
+'rotate-comment' => 'Рәсем сәғәт йөрөшө буйынса $1{{PLURAL:$1|}} градусҡа боролдо',
+
+# Limit report
+'limitreport-title' => 'Анализатор мәғлүмәттәре:',
+'limitreport-cputime' => 'Процессорҙың ваҡытын ҡулланыу',
+'limitreport-cputime-value' => '$1 {{PLURAL:$1|секунд}}',
+'limitreport-walltime' => 'Ғәмәлдәге ваҡыт режимында ҡулланыу',
+'limitreport-walltime-value' => '$1 {{PLURAL:$1|секунд}}',
+'limitreport-ppvisitednodes' => 'Процессор инеп ҡараған төйөндәр һаны',
+'limitreport-ppgeneratednodes' => 'Процессор эшләп сығарған төйөндәр һаны',
+'limitreport-postexpandincludesize' => 'Асылған өлөштәр һаны',
+'limitreport-postexpandincludesize-value' => '$1/$2 {{PLURAL:$2|байт}}',
+'limitreport-templateargumentsize' => 'Ҡалып аргументының үлсәмдәре',
+'limitreport-templateargumentsize-value' => '$1/$2 {{PLURAL:$2|байт}}',
+'limitreport-expansiondepth' => 'Киңәйеүҙең иң ҙур тәрәнлеге',
+'limitreport-expensivefunctioncount' => 'Анализаторҙың "ҡиммәтле" функцияларының һаны',
+
 );
index 7a7c0de..22094fb 100644 (file)
@@ -7,6 +7,7 @@
  * @ingroup Language
  * @file
  *
+ * @author Ebraminio
  * @author Huji
  * @author Kaganer
  * @author Mostafadaneshvar
@@ -2079,12 +2080,9 @@ $1',
 'ipb_blocked_as_range' => 'حطا: ای پی  $1 مستقیما محدود نهنت و نه تونیت رفع محدودیت بیت.
 بله آی جزی چه محدوده  $2 محدود بوتت که تونیت رفع محدودیت بیت.',
 'ip_range_invalid' => 'نامعتبر محدوده آی پی',
-'blockme' => 'مناء محدود کن',
 'proxyblocker' => 'محدود کننده ی پروکسی',
-'proxyblocker-disabled' => 'ای عمگر غیرفعالنت.',
 'proxyblockreason' => 'شمی آدرس آی پی محدود بوتت په چی که ایء یک پچین پروکسی ات.
 لطفا گون وتی اینترنتی شرکت تماس گریت یا حمایت تکنیکی و آیانا چی ای مشکل امنیتی شدید سهی کنیت.',
-'proxyblocksuccess' => 'انجام بوت.',
 'sorbs' => 'دی ان اس بی ال',
 'sorbsreason' => 'شمی آدرس آی پی لیست بوتت په داب پچین پروکسی ته  DNSBL که استفاده بیت گون {{SITENAME}}.',
 'sorbs_create_account_reason' => 'شمی آدرس آی پی لیست بوتت په داب پچین پروکسی ته  دی ان ای بی ال که استفاده بیت گون {{SITENAME}}.
@@ -2372,6 +2370,8 @@ $1',
 'spambot_username' => 'اسپم پاک کنوک مدیا وی کی',
 'spam_reverting' => 'عوض کتن په آهری نسحه که شامل لینکان می بیت په $1',
 'spam_blanking' => 'کل بازبینی آن شامل لینکان په $1, بوتت  هالیکی',
+'simpleantispam-label' => "کنترل ضد اسپم.
+ای شیء پر ''مکن''",
 
 # Skin names
 'skinname-cologneblue' => 'نیلی کولاجن',
index d76944c..ec7eb60 100644 (file)
@@ -314,7 +314,7 @@ $messages = array(
 'articlepage' => 'Tanawon an laog kan pahina',
 'talk' => 'Orolayan',
 'views' => 'Mga Tanawon',
-'toolbox' => 'Kagamitang kahon',
+'toolbox' => 'Mga gagamiton:',
 'userpage' => 'Tanawon an pahina kan parágamit',
 'projectpage' => 'Tanawon an pahina kan proyekto',
 'imagepage' => 'Hilngón an pahina nin sagunson (file)',
@@ -344,7 +344,7 @@ $1',
 # All link text and link target definitions of links into project namespace that get used by other message strings, with the exception of user group pages (see grouppage).
 'aboutsite' => 'Dapít sa {{SITENAME}}',
 'aboutpage' => 'Project:Mapanonongód',
-'copyright' => 'An kalamnan manunumpungan sa laog kan $1.',
+'copyright' => 'An kalamnan manunumpungan sa laog kan $1 o baya notado na ining laen.',
 'copyrightpage' => '{{ns:project}}:Mga Katanosang pansurat',
 'currentevents' => 'Sa ngunyan na mga pangyayari',
 'currentevents-url' => 'Project:Mga pangyayari sa ngunyán',
@@ -558,6 +558,9 @@ Dae malingaw na liwaton an saimong [[Special:Preferences|{{SITENAME}} mga kamuya
 'userlogin-resetpassword-link' => 'Pakibaguha an saimong sekretong panlaog',
 'helplogin-url' => 'Help:Paglalaog',
 'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|Tabang sa paglalaog]]',
+'userlogin-loggedin' => 'Ika nakalaog na tabi bilang si {{GENDER:$1|$1}}.
+Gamita an porma sa ibaba sa paglaog bilang ibang paragamit.',
+'userlogin-createanother' => 'Magmukna nin ibang panindog',
 'createacct-join' => 'Pakikaag an saimong impormasyon sa ibaba.',
 'createacct-another-join' => 'Ikaag an impormasyon kan baguhong panindog sa ibaba.',
 'createacct-emailrequired' => 'Estada kan e-surat',
@@ -620,8 +623,8 @@ Kun ibang tawo an naghimo kaining kahagadan, o kun saimo nang nagiromdoman an sa
 'passwordsent' => 'Sarong baguhon na sekretong panlaog an ipinadara sa e-koreong address na nakarehistro para ki "$1".
 Tabi maglaog giraray matapos mong maresibe ini.',
 'blocked-mailpassword' => 'An saimong IP address pinagkubkob na magliwat, asin kaya dae tinutugutan na gumamit kan pambawi nin sekretong panlaog na punksyon tanganing makalikay sa abuso.',
-'eauthentsent' => 'Sarong e-koreong pankumpirmasyon an ipinadara sa nominadong e-koreong address.
-Bago an ibang e-koreo ipinadara sa panindog, ika igwang pagsunudong na mga instruksyon na yaon sa e-koreo, tanganing kumpirmaron na an panindog tunay talagang saimo.',
+'eauthentsent' => 'Sarong pankumpirmasyon na e-surat an ipinadara sa isinambit na estada nin e-surat.
+Bago an ibang e-surat ipinapadara sa panindog, ika igwang susunudon na mga instruksyon na yaon sa e-surat, tanganing kumpirmaron na an panindog tunay talagang saimo.',
 'throttled-mailpassword' => 'Sarong e-surat sa pagliliwat kan sekretong panlaog an ipinadara na, sa laog nin {{PLURAL:$1|hour|$1 hours}}.
 Tangarig malikayan an abuso, saro sanang e-surat sa pagliliwat kan sekretong panlaog an ipinapadara sa lambang {{PLURAL:$1|hour|$1 hours}}.',
 'mailerror' => 'Salâ an pagpadará kan koreo: $1',
@@ -1062,15 +1065,15 @@ An ibang administrador sa {{SITENAME}} puwede pa man makagamit sa pinagtagong la
 *Bakong angay an personal na impormasyon
 *: ''mga address kan harong asin mga numero kan telepono, sosyal na seguridad, iba pa.''",
 'revdelete-legend' => 'Ilapat an mga restriksyon sa bisibilidad',
-'revdelete-hide-text' => 'Tagoon an teksto kan pagpakaraháy',
+'revdelete-hide-text' => 'Teksto nin rebisyon',
 'revdelete-hide-image' => 'Tagoon an laog kan file',
 'revdelete-hide-name' => 'Tagoon an aksyon asin target',
-'revdelete-hide-comment' => 'Tagoon an komento sa paghirá',
-'revdelete-hide-user' => 'Tagoon an pangaran kan editor/IP',
+'revdelete-hide-comment' => 'Liwaton an sumaryo',
+'revdelete-hide-user' => 'Paraliwat na ngaran-paragamit/IP na estada',
 'revdelete-hide-restricted' => 'Ilubog an mga datos gikan sa mga administrador asin man kan iba',
 'revdelete-radio-same' => '(dae pagribayan)',
-'revdelete-radio-set' => 'Iyo tabi',
-'revdelete-radio-unset' => 'Bako tabi',
+'revdelete-radio-set' => 'Namamansayan',
+'revdelete-radio-unset' => 'Itinago',
 'revdelete-suppress' => 'Dai ipahilíng an mga datos sa mga sysops asin sa mga iba pa',
 'revdelete-unsuppress' => 'Halîon an mga restriksyón sa mga ibinalík na pagpakarhay',
 'revdelete-log' => 'Rason:',
@@ -1500,8 +1503,8 @@ An saimong e-surat na adres dae ipagbuyagyag kunsoarin na an ibang paragamit mak
 'action-block' => 'kubkubon ining paragamit gikan sa pagliliwat',
 'action-protect' => 'ribayan an kurit nin proteksyon para sa pahinang ini',
 'action-rollback' => 'hidaling ipagbalik an mga pagliwat kan huring paragamit na pinagliwat an sarong partikular na pahina',
-'action-import' => 'importaron ining pahina gikan sa ibang wiki',
-'action-importupload' => 'importaron ining pahina gikan sa sarong ikinargang sagunson',
+'action-import' => 'importaron an mga pahina gikan sa ibang wiki',
+'action-importupload' => 'importaron an mga pahina gikan sa sarong ikinargang sagunson',
 'action-patrol' => 'markahan an pagliwat kan iba bilang patrolyado',
 'action-autopatrol' => 'Giboha na an saimong pagliwat markado bilang patrolyado',
 'action-unwatchedpages' => 'tanawon an listahan kan mayong bantay na mga pahina',
@@ -2025,6 +2028,7 @@ Ini ngunyan minatukdo-liwat pasiring sa [[$2]].',
 'listusers' => 'Lista nin paragamit',
 'listusers-editsonly' => 'Ipahiling sana an mga paragamit na igwang mga pinagliwat',
 'listusers-creationsort' => 'Salansanon sa paagi kan petsa nin pagmukna',
+'listusers-desc' => 'Salansanon sa paibabang pasurunod',
 'usereditcount' => '$1 {{PLURAL:$1|pigliwat|mga pigliwat}}',
 'usercreated' => '{{GENDER:$3|Minukna}} kan $1 sa $2',
 'newpages' => 'Mga bàguhong pahina',
@@ -2289,10 +2293,12 @@ Hilingón tabì an $2 para mahiling an lista nin mga kaaagi pa sanang pagparà.'
 'deletecomment' => 'Rason:',
 'deleteotherreason' => 'Iba/dugang na rason:',
 'deletereasonotherlist' => 'Ibang rason',
-'deletereason-dropdown' => '*Pirmehang rason nin pagpupura
-** Kahagadan nin Awtor/Parasurat
-** Kalapasan sa Copyright
-** Bandalismo',
+'deletereason-dropdown' => '*Kumon na mga rason nin pagpura
+**Ispam
+**Bandalismo
+**Kalapasan sa Katanosan nin pagsadiri
+**Kahagadan nin Awtor
+**Parasa na panlikwat',
 'delete-edit-reasonlist' => 'Pagliwat kan mga rason nin pagpupura',
 'delete-toobig' => 'Ining pahina igwa nin dakulaong historiya sa pagliwat, minasobrang $1 {{PLURAL:$1|rebisyon|mga rebisyon}}.
 An pagpupura kan nasambit na mga pahina dae pinagtutugot tanganing maiwasan an aksidenteng pagka-antala kan {{SITENAME}}.',
@@ -2313,7 +2319,7 @@ may iba na tabing nagliwat o nagbalik kan pahina.
 An huring pagliwat sa pahina ginibo ni [[User:$3|$3]] ([[User talk:$3|olay]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).',
 'editcomment' => "An sumaryo kan pagliwat: \"''\$1''\".",
 'revertpage' => 'Ibinalik na mga pagliwat ni [[Special:Contributions/$2|$2]] ([[User talk:$2|talk]]) sagkod sa huring rebisyon ni [[User:$1|$1]]',
-'revertpage-nouser' => 'Binalikwat na mga pagliliwat kan sarong nakatagong paragamit sa huring rebisyon ni [[User:$1|$1]]',
+'revertpage-nouser' => 'Binalikwat na mga pagliliwat kan sarong nakatagong paragamit sa huring rebisyon ni {{GENDER:$1|[[User:$1|$1]]}}',
 'rollback-success' => 'Binawî na mga paghirá ni $1; pigbalik sa dating bersyón ni $2.',
 
 # Edit tokens
@@ -2453,7 +2459,7 @@ $1",
 'contributions' => '{{GENDER:$1|Paragamit}} na mga kaambagan',
 'contributions-title' => 'Mga kontribusyon kan paragamit para sa $1',
 'mycontris' => 'Mga Kaarambagan',
-'contribsub2' => 'Para ki $1($2)',
+'contribsub2' => 'Para ki {{GENDER:$3|$1}} ($2)',
 'nocontribs' => 'Mayong mga pagbabago na nahanap na kapadis sa ining mga criteria.',
 'uctop' => '(sa ngunyan)',
 'month' => 'Poon bulan (asin mas amay):',
@@ -2611,11 +2617,8 @@ Hilngon sa [[Special:BlockList|listahan nin kubkob]] para sa listahan kan presen
 Ini, baya, pinagkubkob bilang parte kan hidwas $2, na mapuwedeng daemakukubkob.',
 'ip_range_invalid' => 'Dai pwede ining serye nin IP.',
 'ip_range_toolarge' => 'An hidwas kan mga kubkob dakulaon kesa /$1 dae pinagtutugutan.',
-'blockme' => 'Kubkuba ako',
 'proxyblocker' => 'Parabagát na karibay',
-'proxyblocker-disabled' => 'Ining punksyon pinag-untok.',
 'proxyblockreason' => 'Binagat an saimong direccion nin IP ta ini sarong bukas na proxy. Apodon tabi an saimong Internet service provider o tech support asin ipaaram sainda ining seriosong problema nin seguridad.',
-'proxyblocksuccess' => 'Tapos.',
 'sorbsreason' => 'An saimong IP na estada pinaglista bilang sarong bukas na proksi sa lang kan DNSBL na ginagamit kan {{SITENAME}}.',
 'sorbs_create_account_reason' => 'An saimong IP na estada pinaglista bilang sarong bukas na proksi sa laog kan DNSBL na ginagamit kan {{SITENAME}}.
 Ika dae makakamukna nin sarong panindog.',
@@ -2963,6 +2966,8 @@ Ini hurot na pinagkausa nin sarong sugpunan pasiring sa sarong pinagbawal na pan
 'spam_reverting' => 'Mabalik sa huring bersion na mayong takod sa $1',
 'spam_blanking' => 'An gabos na mga pahirá na may takod sa $1, pigblablanko',
 'spam_deleting' => 'An gabos na mga rebisyon na igwang mga kasugpunan sa $1, pinupura',
+'simpleantispam-label' => "Rikisa nin Kontra-Ispam.
+Giboha na '''DAE''' paglaogan digde!",
 
 # Info page
 'pageinfo-title' => 'Impormasyon para sa "$1"',
@@ -3731,7 +3736,7 @@ Ika dapat na nakapagresibe na kan [{{SERVER}}{{SCRIPTPATH}}/COPYING sarong kopya
 # Special:Redirect
 'redirect' => 'Palikwatong sa paagi nin sagunson, paragamit, or rebisyon kan ID',
 'redirect-legend' => 'Palikwatong pasiring sa sarong sagunson o pahina',
-'redirect-summary' => 'Ining espesyal na pahina minalikwat pasiring sa sarong sagunson (ipinagtao an ngaran kan sagunson), sarong pahina (ipinagtao an sarong rebisyon kan ID), o sarong pahina nin paragamit (ipinagtao an numerikong ID nin paragamit).',
+'redirect-summary' => 'Ining espesyal na pahina minalikwat pasiring sa sarong sagunson (ipinagtao an ngaran kan sagunson), sarong pahina (ipinagtao an sarong rebisyon kan ID), o sarong pahina nin paragamit (ipinagtao an numerikong ID nin paragamit). Pinaggamitan: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/revision/328429]], or [[{{#Special:Redirect}}/user/101]].',
 'redirect-submit' => 'Dumani',
 'redirect-lookup' => 'Hanapon mo',
 'redirect-value' => 'Halaga:',
@@ -3793,7 +3798,10 @@ Ika dapat na nakapagresibe na kan [{{SERVER}}{{SCRIPTPATH}}/COPYING sarong kopya
 'tags-tag' => 'Ngarang panmarka',
 'tags-display-header' => 'Kinaluwasan sa listahan nin kaliwatan',
 'tags-description-header' => 'Bilog na deskripsyon nin kahulugan',
+'tags-active-header' => 'Aktibo?',
 'tags-hitcount-header' => 'Pinagmarkahan na mga kaliwatan',
+'tags-active-yes' => 'Iyo',
+'tags-active-no' => 'Dae',
 'tags-edit' => 'liwatón',
 'tags-hitcount' => '$1 {{PLURAL:$1|kaliwatan|mga kaliwatan}}',
 
@@ -3959,9 +3967,9 @@ Kun bako man, ika makakagamit nin sayon na porma sa ibaba. An saimong komento id
 'limitreport-ppvisitednodes' => 'Bilang kan tagapagsumpay sa pangenot na tagapagprosesong pinagbisita',
 'limitreport-ppgeneratednodes' => 'Bilang kan tagapagsumpay sa pangenot na tagapagprosesong pinagpuyos',
 'limitreport-postexpandincludesize' => 'Bigak kan paskil kabali an sukol',
-'limitreport-postexpandincludesize-value' => '$1/$2 mga bayta',
+'limitreport-postexpandincludesize-value' => '$1/$2 {{PLURAL:$2|bayta|mga bayta}}',
 'limitreport-templateargumentsize' => 'Sukol kan argumentong panguyog',
-'limitreport-templateargumentsize-value' => '$1/$2 mga bayta',
+'limitreport-templateargumentsize-value' => '$1/$2{{PLURAL:$2|bayta|mga bayta}}',
 'limitreport-expansiondepth' => 'Kinatugmadan kan pinakahalangkaw na kahiwasan',
 'limitreport-expensivefunctioncount' => 'Bilang kan hiro nin mamahalon na parabangay',
 
index 1f49571..85dab07 100644 (file)
@@ -2363,12 +2363,9 @@ $1',
 'ipb_blocked_as_range' => 'Нельга зняць блок з IP-адрасу $1, таму што ён заблакаваны не наўпрост, але як частка абсягу $2; той абсяг, у сваю чаргу, можна разблакоўваць.',
 'ip_range_invalid' => 'Няправільны абсяг IP.',
 'ip_range_toolarge' => 'Блакіроўкі дыяпазонаў звыш /$1 забаронены.',
-'blockme' => 'Заблакаваць сябе',
 'proxyblocker' => 'Блакіратар проксі',
-'proxyblocker-disabled' => 'Гэта функцыя выключаная.',
 'proxyblockreason' => "Ваш адрас IP заблакаваны, таму што ён належыць да ліку адкрытых проксі.
 Гэта сур'ёзная праблема бяспекі; паведамце пра гэта свайму Інтэрнет-правайдэру або ў службу тэхнічнай падтрымкі.",
-'proxyblocksuccess' => 'Зроблена.',
 'sorbsreason' => 'Ваш адрас IP знаходзіцца ў спісе забароненых адкрытых проксі, якім карыстаецца {{SITENAME}}.',
 'sorbs_create_account_reason' => 'Ваш адрас IP знаходзіцца ў спісе забароненых адкрытых проксі, якім карыстаецца {{SITENAME}}.
 Вы не можаце рэгістравацца',
index fdf9f84..54097af 100644 (file)
@@ -783,7 +783,7 @@ $2',
 Калі ласка, увайдзіце ў сыстэму пасьля яго атрыманьня.',
 'blocked-mailpassword' => 'З Вашага IP-адрасу забароненыя рэдагаваньні, а таму таксама для прадухіленьня шкоды недаступная функцыя аднаўленьня паролю.',
 'eauthentsent' => 'Пацьверджаньне было дасланае на пазначаны адрас электроннай пошты.
-У лісьце ўтрымліваюцца інструкцыі, па выкананьні якіх, Вы зможаце пацьвердзіць, што адрас сапраўды належыць Вам, і на гэты адрас будзе дасылацца пошта адсюль.',
+У лісьце ўтрымліваюцца інструкцыі, па выкананьні якіх Вы зможаце пацьвердзіць, што адрас сапраўды належыць Вам, і на гэты адрас будзе дасылацца пошта адсюль.',
 'throttled-mailpassword' => 'Ліст пра скіданьне паролю ўжо было даслана за {{PLURAL:$1|$1 апошнюю гадзіну|$1 апошнія гадзіны|$1 апошніх гадзінаў}}.
 Для прадухіленьня злоўжываньняў напамін будзе дасылацца не часьцей як аднойчы ў $1 {{PLURAL:$1|гадзіну|гадзіны|гадзінаў}}.',
 'mailerror' => 'Памылка пры адпраўцы электроннай пошты: $1',
@@ -2450,9 +2450,11 @@ $UNWATCHURL
 'deleteotherreason' => 'Іншая/дадатковая прычына:',
 'deletereasonotherlist' => 'Іншая прычына',
 'deletereason-dropdown' => '* Агульныя прычыны выдаленьня
-** Запыт аўтара/аўтаркі
+** Спам
+** Вандалізм
 ** Парушэньне аўтарскіх правоў
-** Вандалізм',
+** Запыт аўтара
+** Кепскае перанакіраваньне',
 'delete-edit-reasonlist' => 'Рэдагаваць прычыны выдаленьня',
 'delete-toobig' => 'Гэтая старонка мае доўгую гісторыю рэдагаваньняў, болей за $1 {{PLURAL:$1|вэрсію|вэрсіі|вэрсіяў}}.
 Выдаленьне такіх старонак было забароненае, каб пазьбегнуць праблемаў у працы {{GRAMMAR:родны|{{SITENAME}}}}.',
@@ -2771,12 +2773,9 @@ $1',
 Тым ня менш, ён належыць да дыяпазону $2, які можа быць разблякаваны.',
 'ip_range_invalid' => 'Некарэктны дыяпазон IP-адрасоў.',
 'ip_range_toolarge' => 'Блякаваньні дыяпазонаў, большых за /$1, не дазволеныя.',
-'blockme' => 'Заблякуйце мяне',
 'proxyblocker' => 'Блякаваньне проксі',
-'proxyblocker-disabled' => 'Гэта функцыя выключаная.',
 'proxyblockreason' => "Ваш IP-адрас быў заблякаваны таму што ён належыць адкрытаму проксі.
 Калі ласка, зьвяжыцеся з Вашым Інтэрнэт-правайдарам альбо са службай тэхнічнай падтрымкі і паведаміце ім пра гэтую сур'ёзную праблему бясьпекі.",
-'proxyblocksuccess' => 'Зроблена.',
 'sorbsreason' => 'Ваш IP-адрас знаходзіцца ў сьпісе адкрытых проксі ў DNSBL, якім карыстаецца {{SITENAME}}.',
 'sorbs_create_account_reason' => 'Ваш IP-адрас знаходзіцца ў сьпісе адкрытых проксі ў DNSBL, якім карыстаецца {{SITENAME}}.
 Вы ня зможаце стварыць рахунак',
@@ -3121,6 +3120,8 @@ $2',
 'spam_reverting' => 'Адкат да апошняй вэрсіі без спасылак на $1',
 'spam_blanking' => 'Усе вэрсіі ўтрымліваюць спасылкі на $1, чыстка',
 'spam_deleting' => 'Усе вэрсіі ўтрымлівалі спасылкі на $1, выдаляем',
+'simpleantispam-label' => "Праверка анты-спаму.
+'''НЕ''' запаўняйце тут нічога!",
 
 # Info page
 'pageinfo-title' => 'Інфармацыя пра «$1»',
@@ -3906,7 +3907,7 @@ MediaWiki распаўсюджваецца з надзеяй, што будзе
 # Special:Redirect
 'redirect' => 'Перанакіраваньне да файла, удзельніка або вэрсіі старонкі',
 'redirect-legend' => 'Перанакіраваньне да файла або старонкі',
-'redirect-summary' => 'Гэтая спэцыяльная старонка перанакіруе да файла (паводле імя файла), старонкі (паводле нумара вэрсіі) або старонкі ўдзельніка (паводле нумара ўдзельніка).',
+'redirect-summary' => 'Гэтая спэцыяльная старонка перанакіруе да файла (паводле імя файла), старонкі (паводле нумара вэрсіі) або старонкі ўдзельніка (паводле нумара ўдзельніка). Ужываньне: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/revision/328429]] або [[{{#Special:Redirect}}/user/101]].',
 'redirect-submit' => 'Перайсьці',
 'redirect-lookup' => 'Шукаць паводле:',
 'redirect-value' => 'Значэньне:',
index d421698..f25f067 100644 (file)
@@ -246,7 +246,7 @@ $messages = array(
 'tog-hidepatrolled' => 'Скриване на патрулираните редакции от списъка с последните промени',
 'tog-newpageshidepatrolled' => 'Скриване на патрулираните редакции от списъка на новите страници',
 'tog-extendwatchlist' => 'Разширяване на списъка, така че да показва всички промени, не само най-скорошните',
-'tog-usenewrc' => 'Ð\93Ñ\80Ñ\83пиÑ\80ане Ð½Ð° Ð¿Ð¾Ñ\81ледниÑ\82е Ð¿Ñ\80омени Ð¸ Ñ\81пиÑ\81Ñ\8aка Ð·Ð° Ð½Ð°Ð±Ð»Ñ\8eдение Ð¿Ð¾ Ñ\81Ñ\82Ñ\80аниÑ\86и (изиÑ\81ква Ð\94жаваÑ\81кÑ\80ипÑ\82)',
+'tog-usenewrc' => 'Ð\93Ñ\80Ñ\83пиÑ\80ане Ð¿Ð¾ Ñ\81Ñ\82Ñ\80аниÑ\86и Ð½Ð° Ð¿Ñ\80омениÑ\82е Ð¸ Ñ\81пиÑ\81Ñ\8aка Ð·Ð° Ð½Ð°Ð±Ð»Ñ\8eдение',
 'tog-numberheadings' => 'Номериране на заглавията',
 'tog-showtoolbar' => 'Показване на инструментите за редактиране',
 'tog-editondblclick' => 'Редактиране на страниците чрез двойно щракване',
@@ -572,6 +572,7 @@ $1',
 'databaseerror' => 'Грешка при работа с базата от данни',
 'databaseerror-text' => 'Възникна грешка при заявката за базата данни.
 Това може да означава бъг в софтуера.',
+'databaseerror-textcl' => 'Възникна грешка при заявка за базата данни.',
 'databaseerror-query' => 'Заявка: $1',
 'databaseerror-function' => 'Функция: $1',
 'databaseerror-error' => 'Грешка: $1',
@@ -632,6 +633,7 @@ $2',
 'customjsprotected' => 'Нямате права за редактиране на тази Джаваскрипт страница, защото тя съдържа чужди потребителски настройки.',
 'mycustomcssprotected' => 'Нямате права за редактиране на тази CSS страница.',
 'mycustomjsprotected' => 'Нямате права за редактиране на тази JavaScript страница.',
+'myprivateinfoprotected' => 'Нямате права да редактирате личната си информация.',
 'mypreferencesprotected' => 'Нямате права да редактирате настройките си.',
 'ns-specialprotected' => 'Специалните страници не могат да бъдат редактирани.',
 'titleprotected' => "Тази страница е била защитена срещу създаване от [[User:$1|$1]].
@@ -657,13 +659,14 @@ $2',
 'yourname' => 'Потребителско име:',
 'userlogin-yourname' => 'Потребителско име',
 'userlogin-yourname-ph' => 'Въведете вашето потребителско име',
+'createacct-another-username-ph' => 'Въвежда се потребителското име',
 'yourpassword' => 'Парола:',
 'userlogin-yourpassword' => 'Парола',
 'userlogin-yourpassword-ph' => 'Въведете вашата парола',
 'createacct-yourpassword-ph' => 'Въведете парола',
 'yourpasswordagain' => 'Парола (повторно):',
 'createacct-yourpasswordagain' => 'Потвърждаване на паролата',
-'createacct-yourpasswordagain-ph' => 'Ð\92Ñ\8aведеÑ\82е Ð¿Ð°Ñ\80олаÑ\82а Ð¾Ñ\82ново',
+'createacct-yourpasswordagain-ph' => 'Ð\92Ñ\8aвежда Ñ\81е Ð¿Ð°Ñ\80олаÑ\82а (повÑ\82оÑ\80но)',
 'remembermypassword' => 'Запомняне на паролата на този компютър (най-много за $1 {{PLURAL:$1|ден|дни}})',
 'userlogin-signwithsecure' => 'Използване на защитена връзка',
 'yourdomainname' => 'Домейн:',
@@ -690,14 +693,20 @@ $2',
 'userlogin-resetpassword-link' => 'Възстановяване на паролата',
 'helplogin-url' => 'Help:Влизане',
 'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|Помощ за влизане]] в системата',
+'userlogin-loggedin' => 'Вече сте влезли в системата като {{GENDER:$1|$1}}.
+Чрез формуляра по-долу можете да влезете като друг потребител.',
 'userlogin-createanother' => 'Създаване на друга сметка',
 'createacct-join' => 'Въведете своите данни по-долу.',
+'createacct-another-join' => 'Попълване на информацията за новата сметка',
 'createacct-emailrequired' => 'Адрес за електронна поща',
 'createacct-emailoptional' => 'Адрес за електронна поща (незадължително)',
-'createaccountmail' => 'Използване на временна парола, която се изпраща по електронната поща, посочена по-долу',
+'createacct-another-email-ph' => 'Въвежда се електронна поща',
+'createaccountmail' => 'Използване на случайна временна парола, която се изпраща на електронната поща, посочена по-долу',
 'createacct-realname' => 'Истинско име (незадължително)',
 'createaccountreason' => 'Причина:',
 'createacct-reason' => 'Причина',
+'createacct-reason-ph' => 'Защо създавате друга сметка',
+'createacct-captcha' => 'Проверка за сигурност',
 'createacct-imgcaptcha-ph' => 'Въведете текста, който виждате по-горе',
 'createacct-submit' => 'Създаване на сметката',
 'createacct-another-submit' => 'Създаване на друга сметка',
@@ -780,7 +789,7 @@ $2',
 'newpassword' => 'Нова парола:',
 'retypenew' => 'Нова парола повторно:',
 'resetpass_submit' => 'Избиране на парола и влизане',
-'changepassword-success' => 'Паролата ви беше сменена! Сега влизате…',
+'changepassword-success' => 'Паролата ви беше променена успешно!',
 'resetpass_forbidden' => 'Не е разрешена смяна на паролата',
 'resetpass-no-info' => 'За да достъпвате тази страница директно, необходимо е да влезете в системата.',
 'resetpass-submit-loggedin' => 'Промяна на паролата',
@@ -792,9 +801,11 @@ $2',
 
 # Special:PasswordReset
 'passwordreset' => 'Възстановяване на парола',
+'passwordreset-text-one' => 'Попълването на формуляра ще доведе до възстановяване на паролата.',
 'passwordreset-text-many' => '{{PLURAL:$1|За възстановяване на паролата е необходимо да се попълни едно от полетата.}}',
 'passwordreset-legend' => 'Възстановяване на парола',
 'passwordreset-disabled' => 'Възстановяването на паролата е изключено в това уики.',
+'passwordreset-emaildisabled' => 'Функционалността за електронна поща е изключена в това уики.',
 'passwordreset-username' => 'Потребителско име:',
 'passwordreset-domain' => 'Домейн:',
 'passwordreset-capture' => 'Преглеждане на електронното писмо?',
@@ -821,7 +832,7 @@ $2
 Временна парола: $2',
 'passwordreset-emailsent' => 'На електронната поща беше испратено писмо за възстановяване на паролата.',
 'passwordreset-emailsent-capture' => 'По-долу е показано електронното писмо за възстановяване на паролата, което беше изпратено.',
-'passwordreset-emailerror-capture' => 'По-долу е показано създадено електронно писмо за възстановяване на паролата, което не беше изпратено на потребителя: $1',
+'passwordreset-emailerror-capture' => 'По-долу е показано създадено електронно писмо за възстановяване на паролата, което не беше изпратено на {{GENDER:$2|потребителя}}: $1',
 
 # Special:ChangeEmail
 'changeemail' => 'Промяна на адреса за е-поща',
@@ -914,9 +925,7 @@ $2
 'loginreqlink' => 'влизане',
 'loginreqpagetext' => 'Необходимо е $1, за да можете да разглеждате други страници.',
 'accmailtitle' => 'Паролата беше изпратена.',
-'accmailtext' => "Случайно генерирана парола за [[User talk:$1|$1]] беше изпратена на $2.
-
-Паролата за тази нова потребителска сметка може да бъде променена от специалната страница ''[[Special:ChangePassword|„Промяна на парола“]]'' след влизане в системата.",
+'accmailtext' => "Случайно генерирана парола за [[User talk:$1|$1]] беше изпратена на $2. Паролата може да бъде променена от страницата ''[[Special:ChangePassword|„Промяна на паролата“]]'' след влизане в системата.",
 'newarticle' => '(нова)',
 'newarticletext' => 'Последвахте препратка към страница, която все още не съществува.
 За да я създадете, просто започнете да пишете в долната текстова кутия
@@ -1014,7 +1023,7 @@ $2
 'nocreate-loggedin' => 'Нямате необходимите права да създавате нови страници.',
 'sectioneditnotsupported-title' => 'Не се поддържа редактиране на раздели',
 'sectioneditnotsupported-text' => 'Не се поддържа редактиране на раздели на тази страница.',
-'permissionserrors' => 'Ð\93Ñ\80еÑ\88ки при правата на достъп',
+'permissionserrors' => 'Ð\93Ñ\80еÑ\88ка при правата на достъп',
 'permissionserrorstext' => 'Нямате правата да извършите това действие по {{PLURAL:$1|следната причина|следните причини}}:',
 'permissionserrorstext-withaction' => 'Нямате разрешение за $2 поради {{PLURAL:$1|следната причина|следните причини}}:',
 'recreate-moveddeleted-warn' => "'''Внимание: Създавате страница, която по-рано вече е била изтрита.'''
@@ -1344,7 +1353,7 @@ $1",
 'prefs-rendering' => 'Облик',
 'saveprefs' => 'Съхраняване',
 'resetprefs' => 'Отмяна на текущите промени',
-'restoreprefs' => 'Възстановяване на всички настройки по подразбиране',
+'restoreprefs' => 'Възстановяване на всички настройки по подразбиране (за всички раздели)',
 'prefs-editing' => 'Редактиране',
 'rows' => 'Редове:',
 'columns' => 'Колони:',
@@ -1584,9 +1593,10 @@ $1",
 
 # Recent changes
 'nchanges' => '$1 {{PLURAL:$1|промяна|промени}}',
+'enhancedrc-since-last-visit' => '$1 {{PLURAL:$1|от последното посещение}}',
 'enhancedrc-history' => 'история',
 'recentchanges' => 'Последни промени',
-'recentchanges-legend' => 'Ð\9eпÑ\86ии на списъка с последни промени',
+'recentchanges-legend' => 'Ð\9dаÑ\81Ñ\82Ñ\80ойки на списъка с последни промени',
 'recentchanges-summary' => "Проследяване на последните промени в {{SITENAME}}.
 
 Легенда: '''тек''' = разлика на текущата версия,
@@ -2588,11 +2598,8 @@ $1',
 'ipb_blocked_as_range' => 'Грешка: IP-адресът $1 не може да бъде разблокиран, тъй като е част от блокирания регистър $2. Можете да разблокирате адреса, като разблокирате целия регистър.',
 'ip_range_invalid' => 'Невалиден интервал за IP-адреси.',
 'ip_range_toolarge' => 'Забранено е блокиране на диапазони от IP адреси по-големи от /$1.',
-'blockme' => 'Самоблокиране',
 'proxyblocker' => 'Блокировач на проксита',
-'proxyblocker-disabled' => 'Тази функция е деактивирана.',
 'proxyblockreason' => 'IP-адресът ви беше блокиран, тъй като е анонимно достъпен междинен сървър. Свържете се с доставчика ви на интернет и го информирайте за този сериозен проблем в сигурността.',
-'proxyblocksuccess' => 'Готово.',
 'sorbsreason' => 'IP-адресът ви е записан като анонимно достъпен междинен сървър в DNSBL на {{SITENAME}}.',
 'sorbs_create_account_reason' => 'IP-адресът ви е записан като анонимно достъпен междинен сървър в DNSBL на {{SITENAME}}. Не може да създадете сметка.',
 'cant-block-while-blocked' => 'Не можете да блокирате други потребители, докато сам(а) сте блокиран(а).',
@@ -2751,6 +2758,8 @@ $1',
 'thumbnail-more' => 'Увеличаване',
 'filemissing' => 'Липсващ файл',
 'thumbnail_error' => 'Грешка при създаване на миникартинка: $1',
+'thumbnail_error_remote' => 'Съобщение за грешка от $1:
+$2',
 'djvu_page_error' => 'Номерът на DjVu-страницата е извън обхвата',
 'djvu_no_xml' => 'Не е възможно вземането на XML за DjVu-файла',
 'thumbnail-temp-create' => 'Временния файл с миникартинка не може да бъде създаден.',
@@ -2919,9 +2928,12 @@ $1',
 'spam_reverting' => 'Връщане на последната версия, несъдържаща препратки към $1',
 'spam_blanking' => 'Всички версии, съдържащи препратки към $1, изчистване',
 'spam_deleting' => 'Всички версии съдържат препратки към $1, изтриване',
+'simpleantispam-label' => "Проверка за спам.
+Необходимо е да '''НЕ''' попълвате това поле!",
 
 # Info page
 'pageinfo-title' => 'Информация за "$1"',
+'pageinfo-not-current' => 'За съжаление тази информация не може да бъде предоставена за стари версии.',
 'pageinfo-header-basic' => 'Основна информация',
 'pageinfo-header-edits' => 'История на редакциите',
 'pageinfo-header-restrictions' => 'Защита на страницата',
@@ -3669,6 +3681,7 @@ MediaWiki се разпространява с надеждата, че ще б
 'tags' => 'Валидни етикети за промени',
 'tag-filter' => 'Филтър на [[Special:Tags|етикета]]:',
 'tag-filter-submit' => 'Филтриране',
+'tag-list-wrapper' => '([[Special:Tags|{{PLURAL:$1|Етикет|Етикети}}]]: $2)',
 'tags-title' => 'Етикети',
 'tags-intro' => 'Тук са изброени всички етикети, които могат да се ползват за отбелязване на редакциите, както и тяхното значение.',
 'tags-tag' => 'Име на етикета',
@@ -3697,6 +3710,7 @@ MediaWiki се разпространява с надеждата, че ще б
 'dberr-problems' => 'Съжаляваме! Сайтът изпитва технически затруднения.',
 'dberr-again' => 'Изчакайте няколко минути и опитайте да презаредите.',
 'dberr-info' => '(Няма достъп до сървъра с базата данни: $1)',
+'dberr-info-hidden' => '(Няма връска със сървъра на базата данни)',
 'dberr-usegoogle' => 'Междувременно опитайте да потърсите в Google.',
 'dberr-outofdate' => 'Имайте предвид, че индексираното от Гугъл наше съдържание може вече да е неактуално.',
 'dberr-cachederror' => 'Следва складирано копие на поисканата страница. Възможно е складираното копие да не е актуално.',
index 420b898..fe44b3a 100644 (file)
@@ -2509,12 +2509,9 @@ Janaki [[Special:BlockList|daptar diblukir]] gasan daptar uprasi dibabat wan pam
 Ngini, kayapa pun, diblukir sawagai palihan wilayah $2, nang kawa-ai dilapas-blukirnya.',
 'ip_range_invalid' => 'Jarak IP kada sah.',
 'ip_range_toolarge' => 'Jarak blukir taganal pada /$1 kada dibulihakan.',
-'blockme' => 'Blokir ulun',
 'proxyblocker' => 'Pamblukir pruksi',
-'proxyblocker-disabled' => 'Pungsi ngini dipajahakan.',
 'proxyblockreason' => 'Alamat IP Pian diblukir karana ngini sabuah pruksi tabuka.
 Muhun hubungi Panyadia Layan Internet Pian atawa sukungan tiknik wan padahi sidin pasal masalah ka-amanan sarius ngini.',
-'proxyblocksuccess' => 'Sudah.',
 'sorbsreason' => 'Alamat IP Pian tadaptar sawagai pruksi tabuka dalam DNSBL dipuruk ulih {{SITENAME}}.',
 'sorbs_create_account_reason' => 'Alamat IP Pian tadaptar sawagai pruksi tabuka dalam DNSBL dipuruk ulih {{SITENAME}}.
 Pian kada kawa maulah sabuah akun',
index 66db1f2..f62a548 100644 (file)
@@ -113,7 +113,7 @@ $messages = array(
 'tog-diffonly' => 'পার্থক্যের নিচে পাতার বিষয়বস্তু না দেখানো হোক',
 'tog-showhiddencats' => 'লুকায়িত বিষয়শ্রেণীসমূহ দেখাও',
 'tog-norollbackdiff' => 'রোলব্যাকের পরে পার্থক্য দেখিও না',
-'tog-useeditwarning' => 'অসংরক্ষিত পরিবর্তন সহ কোনো পাতা ত্যাগের সময় সাবধান করো',
+'tog-useeditwarning' => 'অসংরক্ষিত পরিবর্তনসহ কোনো পাতা ত্যাগের সময় সাবধান করো',
 'tog-prefershttps' => 'যখনই প্রবেশ করবেন সবসময় নিরাপদ সংযোগ ব্যবহার করুন',
 
 'underline-always' => 'সব সময়',
@@ -271,7 +271,7 @@ $messages = array(
 'create-this-page' => 'পাতাটি তৈরি করো',
 'delete' => 'অপসারণ',
 'deletethispage' => 'এই পাতাটি মুছে ফেলুন',
-'undeletethispage' => 'à¦\8fà¦\87 à¦ªà¦¾à¦¤à¦¾à¦\9fি à¦®à§\81à¦\9bà§\8b à¦¨à¦¾',
+'undeletethispage' => 'পাতাà¦\9fি à¦ªà§\81নরà§\81দà§\8dধার à¦\95রà§\8b',
 'undelete_short' => 'পুনঃস্থাপন {{PLURAL:$1|১টি সম্পাদনা|$1টি সম্পাদনাসমূহ}}',
 'viewdeleted_short' => '{{PLURAL:$1| টি অপসারিত সম্পাদনা|$1 টি অপসারিত সম্পাদনা}} দেখাও',
 'protect' => 'সুরক্ষা',
@@ -318,7 +318,7 @@ $1',
 # All link text and link target definitions of links into project namespace that get used by other message strings, with the exception of user group pages (see grouppage).
 'aboutsite' => '{{SITENAME}} বৃত্তান্ত',
 'aboutpage' => 'Project:বৃত্তান্ত',
-'copyright' => '$1 à¦\8fর à¦\86à¦\93তায় à¦ªà§\8dরাপà§\8dয।',
+'copyright' => '$1 à¦\8fর à¦\86à¦\93তায় à¦ªà§\8dরà¦\95াশিত à¦¯à¦¦à¦¿ à¦\85নà§\8dয à¦\95িà¦\9bà§\81 à¦¨à¦¿à¦°à§\8dধারিত à¦¨à¦¾ à¦¥à¦¾à¦\95à§\87।',
 'copyrightpage' => '{{ns:project}}:কপিরাইট',
 'currentevents' => 'সমসাময়িক ঘটনা',
 'currentevents-url' => 'Project:সমসাময়িক ঘটনাসমূহ',
@@ -335,7 +335,7 @@ $1',
 'privacypage' => 'Project:গোপনীয়তার নীতি',
 
 'badaccess' => 'অনুমোদন ত্রুটি',
-'badaccess-group0' => 'আপনি যে কাজের জন্য অনুরোধ করেছেন, যে কাজটি সম্পন্ন করার অনুমতি নেই',
+'badaccess-group0' => 'আপনি যে কাজের জন্য অনুরোধ করেছেন, যে কাজটি সম্পন্ন করার অনুমতি নেই',
 'badaccess-groups' => 'আপনি যে কাজটি করতে চাচ্ছেন তা কেবল {{PLURAL:$2|এই দলের|এই দলগুলির যেকোন একটির}} একজন সদস্য ব্যবহারকারী সম্পাদন করতে পারেন: $1।',
 
 'versionrequired' => 'মিডিয়াউইকির $1 সংস্করণ প্রয়োজন',
@@ -529,6 +529,8 @@ $2',
 'userlogin-resetpassword-link' => 'শব্দচাবি পুনরায় ধার্য করুন',
 'helplogin-url' => 'Help:প্রবেশ',
 'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|লগইন সংক্রান্ত সাহায্য]]',
+'userlogin-loggedin' => 'আপনি বর্তমানে {{GENDER:$1|$1}} হিসাবে লগইন আছেন।
+অন্য ব্যবহারকারী নামে লগইন করতে চাইলে নিচের ফর্মটি ব্যবহার করুন।',
 'userlogin-createanother' => 'আরেকটি অ্যাকাউন্ট তৈরি করুন',
 'createacct-join' => 'আপনার সম্পর্কিত তথ্য নিচে যোগ করুন।',
 'createacct-another-join' => 'নিচে আপনার নতুন অ্যাকাউন্টের তথ্য দিন।',
@@ -1456,8 +1458,8 @@ $1",
 'action-block' => 'এই ব্যবহারকারীকে সম্পাদনা করতে বাঁধা দাও',
 'action-protect' => 'এই পাতার সুরক্ষার মাত্রা পরিবর্তন করো',
 'action-rollback' => 'একটি নির্দিষ্ট পাতার সর্বশেষ ব্যবহারকারীর সম্পদনা পূর্বাবস্থায় ফিরিয়ে আনুন',
-'action-import' => 'à¦\85নà§\8dয à¦\89à¦\87à¦\95ি à¦¥à§\87à¦\95à§\87 à¦\8fà¦\87 à¦ªà¦¾à¦¤à¦¾à¦\9fি আমদানী করো',
-'action-importupload' => 'ফাà¦\87ল à¦\86পলà§\8bড à¦¥à§\87à¦\95à§\87 à¦\8fà¦\87 à¦ªà¦¾à¦¤à¦¾à¦\9fি আমদানী করো',
+'action-import' => 'à¦\85নà§\8dয à¦\89à¦\87à¦\95ি à¦¥à§\87à¦\95à§\87 à¦ªà¦¾à¦¤à¦¾ আমদানী করো',
+'action-importupload' => 'ফাà¦\87ল à¦\86পলà§\8bড à¦¥à§\87à¦\95à§\87 à¦ªà¦¾à¦¤à¦¾ আমদানী করো',
 'action-patrol' => 'অন্যদের সম্পাদনা পরীক্ষিত বলে চিহ্নিত করো',
 'action-autopatrol' => 'পরীক্ষিত বলে চিহ্নিত কি আপনি সম্পাদনা করেছেন',
 'action-unwatchedpages' => 'নজরতালিকা বহির্ভূত পাতাগুলির তালিকা দেখাও',
@@ -1980,6 +1982,7 @@ Maybe you want to edit the description on its [$2 file description page] there.'
 'listusers' => 'ব্যবহারকারীর তালিকা',
 'listusers-editsonly' => 'শুধুমাত্র এমন ব্যবহারকারীদের দেখাও যাদের অবদান আছে',
 'listusers-creationsort' => 'তৈরির তারিখ অনুসারে সাজাও',
+'listusers-desc' => 'বড় থেকে ছোট ক্রম অনুযায়ী সাজাও',
 'usereditcount' => '$1 {{PLURAL:$1|সম্পাদনা|সম্পাদনা}}',
 'usercreated' => '{{GENDER:$3|তৈরি হয়েছে}} $1 তারিখ, সময়: $2',
 'newpages' => 'নতুন পাতাসমূহ',
@@ -2075,7 +2078,7 @@ Maybe you want to edit the description on its [$2 file description page] there.'
 # Special:ActiveUsers
 'activeusers' => 'সক্রিয় ব্যবহারকারী তালিকা',
 'activeusers-intro' => 'এটি ব্যবহারকারী তালিকা যাদের $1 {{PLURAL:$1|দিনে|দিনে}} যেকোন কর্মকান্ড রয়েছে।',
-'activeusers-count' => 'à¦\97ত {{PLURAL:$3|দিনà§\87}} à¦¸à¦°à§\8dবমà§\8bà¦\9f {{PLURAL:$1|পদ}} à¦¸à¦\82à¦\96à§\8dযা $1',
+'activeusers-count' => 'à¦\97ত {{PLURAL:$3|à¦\95ালà§\87|$3 à¦¦à¦¿à¦¨à§\87}} à¦¸à¦°à§\8dবমà§\8bà¦\9f {{PLURAL:$1|à¦\95রà§\8dমà§\87র}} à¦¸à¦\82à¦\96à§\8dযা $1à¦\9fি',
 'activeusers-from' => 'ব্যবহারকারী দেখাও যাদের নাম এই অক্ষর দিয়ে শুরু:',
 'activeusers-hidebots' => 'বট লুকাও',
 'activeusers-hidesysops' => 'প্রশাসক লুকাও',
@@ -2241,9 +2244,11 @@ $UNWATCHURL
 'deleteotherreason' => 'অন্য/অতিরিক্ত কারণ:',
 'deletereasonotherlist' => 'অন্য কারণ',
 'deletereason-dropdown' => '*মুছে ফেলার সাধারণ কারণগুলি
-** লেখকের অনুরোধ
+** স্প্যাম
+** ধ্বংসপ্রবণতা
 ** কপিরাইট ভঙ্গ
-** ধ্বংসপ্রবণতা',
+** লেখকের অনুরোধ
+** অকার্যকর পুনঃনির্দেশ',
 'delete-edit-reasonlist' => 'অপসারণের কারণ সম্পাদনা',
 'delete-toobig' => 'এই পাতার সম্পাদনার ইতিহাস অনেক বড়, যা $1টি {{PLURAL:$1|সংস্করণের|সংস্করণের}} বেশি।
 {{SITENAME}}-এর দূর্ঘটনাজনিত সমস্যা এড়াতে এই ধরনের পাতা মুছার ব্যপারে সীমাবদ্ধতা আরোপিত হয়েছে।',
@@ -2560,11 +2565,8 @@ $1',
 'ipb_blocked_as_range' => 'ত্রুটি: $1 আইপি ঠিকানাটিকে সরাসরি বাধা দেওয়া হয়নি এবং বাধা তুলে নেওয়া যাবে না। তবে ঠিকানাটি $2 সীমার অন্তর্ভুক্ত এবং সেটি থেকে বাধা তুলে নেওয়া সম্ভব।',
 'ip_range_invalid' => 'অবৈধ আইপি শ্রেণী',
 'ip_range_toolarge' => '/$1 এর বড় রেঞ্জব্লক ব্যবহার অনুমদিত নয়।',
-'blockme' => 'আমাকে বাধা দেওয়া হোক',
 'proxyblocker' => 'প্রক্সি বাধাদানকারী',
-'proxyblocker-disabled' => 'এই ফাংশনটি নিষ্ক্রিয়।',
 'proxyblockreason' => 'আপনার আইপি ঠিকানাকে বাধা দেয়া হয়েছে কারণ এটি একটি উন্মুক্ত প্রক্সি। অনুগ্রহ করে আপনার ইন্টারনেট সেবা প্রদানকারী কোম্পানির সাথে কারিগরি সহায়তার ব্যাপারে যোগাযোগ করুন এবং এই গুরুত্বপূর্ণ নিরাপত্তা সমস্যার ব্যাপারে তাদেরকে অবহিত করুন।',
-'proxyblocksuccess' => 'নিষ্পন্ন হয়েছে।',
 'sorbsreason' => 'আপনার আইপি ঠিকানাটি {{SITENAME}}-এর ব্যবহার করা DNSBL-এ উন্মুক্ত প্রক্সি হিসেবে তালিকাভুক্ত আছে।',
 'sorbs_create_account_reason' => 'আপনার আইপি ঠিকানাটি {{SITENAME}}-এর ব্যবহার করা DNSBL-এ উন্মুক্ত প্রক্সি হিসেবে তালিকাভুক্ত আছে। আপনি কোন অ্যাকাউন্ট সৃষ্টি করতে পারবেন না।',
 'xffblockreason' => 'X-Forwarded-For হেডারে থাকা আইপি ঠিকানাটি ব্লক করা হয়েছে, হয় এটি আপনার নিজের অথবা আপনার ব্যবহৃত প্রক্সি সার্ভারের আইপি ঠিকানা। ব্লক করার কারণ হল: $1',
@@ -2899,6 +2901,8 @@ $2',
 'spam_reverting' => '$1-এর প্রতি কোন সংযোগ নেই, এমন সর্বশেষ সংস্করণে ফেরত নেওয়া হচ্ছে।',
 'spam_blanking' => '$1-এর প্রতি সংযোগ অন্তর্ভুক্ত আছে এমন সমস্ত সংশোধন খালি করা হচ্ছে',
 'spam_deleting' => '$1-এর প্রতি সংযোগ অন্তর্ভুক্ত আছে এমন সমস্ত সংশোধন অপসারণ করা হচ্ছে',
+'simpleantispam-label' => "এন্টি-স্প্যাম যাচাই।
+এটা পূরণ করবেন '''না'''!",
 
 # Info page
 'pageinfo-title' => '"$1" এর তথ্য',
index 1dd3e15..9191af5 100644 (file)
@@ -2647,7 +2647,7 @@ Setu aze an abeg(où) m\'eo bet stanket $1 : "\'\'$2\'\'"',
 'blocklogpage' => 'Roll ar stankadennoù',
 'blocklog-showlog' => "Stanket eo bet an implijer-mañ c'hoazh. A-is emañ marilh ar stankadennoù, d'ho titouriñ :",
 'blocklog-showsuppresslog' => "Stanket ha kuzhet eo bet an implijer-mañ c'hoazh. A-is emañ marilh ar diverkadennoù, d'ho titouriñ :",
-'blocklogentry' => 'en/he deus stanket [[$1]] betek an $2 $3',
+'blocklogentry' => 'en/he deus stanket [[$1]] gant ur pad termen a $2 $3',
 'reblock-logentry' => "en deus kemmet an arventennoù stankañ evit [[$1]] gant un termen d'an $2 $3",
 'blocklogtext' => "Setu roud stankadennoù ha distankadennoù an implijerien. N'eo ket bet rollet ar chomlec'hioù IP bet stanket ent emgefre. Sellet ouzh [[Special:BlockList|roll an implijerien stanket]] evit gwelet piv zo stanket e gwirionez.",
 'unblocklogentry' => 'distanket "$1"',
@@ -2670,11 +2670,8 @@ Setu aze an abeg(où) m\'eo bet stanket $1 : "\'\'$2\'\'"',
 'ipb_blocked_as_range' => "Fazi : N'eo ket bet stanket ar chomlec'h IP $1 war-eeun, setu n'hall ket bezañ distanket. Stanket eo bet dre al live $2 avat, hag a c'hall bezañ distanket.",
 'ip_range_invalid' => 'Stankañ IP direizh.',
 'ip_range_toolarge' => "N'eo ket aotreet stankañ pajennoù brasoc'h evit /$1.",
-'blockme' => "Stankit ac'hanon",
 'proxyblocker' => 'Stanker proksi',
-'proxyblocker-disabled' => "Diweredekaet eo an arc'hwel-mañ.",
 'proxyblockreason' => "Stanket eo bet hoc'h IP rak ur proksi digor eo. Trugarez da gelaouiñ ho pourvezer moned ouzh ar Genrouedad pe ho skoazell deknikel eus ar gudenn surentez-mañ.",
-'proxyblocksuccess' => 'Echu.',
 'sorbsreason' => "Rollet eo ho chomlec'h IP evel ur proksi digor en DNSBL implijet gant {{SITENAME}}.",
 'sorbs_create_account_reason' => "Rollet eo ho chomlec'h IP evel ur proksi digor war an DNSBL implijet gant {{SITENAME}}. N'hallit ket krouiñ ur gont",
 'cant-block-while-blocked' => "N'hallit ket stankañ implijerien all ma'z oc'h stanket c'hwi hoc'h-unan.",
@@ -3030,6 +3027,8 @@ Sur a-walc'h abalamour d'ul liamm enni a gas d'ul lec'hienn ziavaez berzet.",
 'spam_reverting' => "Distreiñ d'ar stumm diwezhañ hep liamm davet $1",
 'spam_blanking' => 'Diverkañ an holl stummoù enno liammoù davet $1',
 'spam_deleting' => 'An holl stummoù enno liammoù war-zu $1, o tiverkañ',
+'simpleantispam-label' => "Taol gwiriañ eneb-strob.
+'''Arabat''' merkañ tra pe dra amañ !",
 
 # Info page
 'pageinfo-title' => 'Titouroù evit "$1"',
index a5d49ac..dc57112 100644 (file)
@@ -498,7 +498,7 @@ $messages = array(
 'articlepage' => 'Pogledaj članak',
 'talk' => 'Razgovor',
 'views' => 'Pregledi',
-'toolbox' => 'Traka sa alatima',
+'toolbox' => 'Alati',
 'userpage' => 'Pogledaj korisničku stranicu',
 'projectpage' => 'Pogledaj stranicu projekta',
 'imagepage' => 'Pogledajte stranicu datoteke',
@@ -528,7 +528,7 @@ $1',
 # All link text and link target definitions of links into project namespace that get used by other message strings, with the exception of user group pages (see grouppage).
 'aboutsite' => 'O projektu {{SITENAME}}',
 'aboutpage' => 'Project:O_projektu_{{SITENAME}}',
-'copyright' => 'Svi sadržaji podliježu "$1" licenci.',
+'copyright' => 'Sadržaj je dostupan pod licencom $1 osim ako je drugačije navedeno.',
 'copyrightpage' => '{{ns:project}}:Autorska_prava',
 'currentevents' => 'Trenutni događaji',
 'currentevents-url' => 'Project:Novosti',
@@ -733,6 +733,7 @@ Ne zaboravite da prilagodite sebi svoja [[Special:Preferences|{{SITENAME}} pode
 'userlogin-resetpassword-link' => 'Resetirajte svoju šifru/lozinku',
 'helplogin-url' => 'Help:Prijavljivanje',
 'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|Pomoć pri prijavljivanju]]',
+'userlogin-createanother' => 'Napravi još jedan račun',
 'createacct-join' => 'Unesite svoje podatke ispod.',
 'createacct-another-join' => 'Unesite informacije o novom računu ispod.',
 'createacct-emailrequired' => 'Adresa e-pošte',
@@ -1232,15 +1233,15 @@ Drugi administratori projekta {{SITENAME}} će i dalje moći pristupiti sakriven
 * Osjetljive korisničke informacije
 *: ''kućne adrese, brojevi telefona, brojevi bankovnih kartica itd.''",
 'revdelete-legend' => 'Postavi ograničenja vidljivosti',
-'revdelete-hide-text' => 'Sakrij tekst revizije',
+'revdelete-hide-text' => 'Tekst revizije',
 'revdelete-hide-image' => 'Sakrij sadržaj datoteke',
 'revdelete-hide-name' => 'Sakrij akciju i cilj',
 'revdelete-hide-comment' => 'Sakrij izmjene komentara',
-'revdelete-hide-user' => 'Sakrij korisničko ime urednika/IP',
+'revdelete-hide-user' => 'Korisničko ime urednika/IP',
 'revdelete-hide-restricted' => 'Ograniči podatke za administratore kao i za druge korisnike',
 'revdelete-radio-same' => '(ne mijenjaj)',
-'revdelete-radio-set' => 'Da',
-'revdelete-radio-unset' => 'Ne',
+'revdelete-radio-set' => 'Vidljivo',
+'revdelete-radio-unset' => 'Sakriveno',
 'revdelete-suppress' => 'Sakrij podatke od administratora kao i od drugih',
 'revdelete-unsuppress' => 'Ukloni ograničenja na vraćenim revizijama',
 'revdelete-log' => 'Razlog:',
@@ -1678,6 +1679,7 @@ Ako izaberete da date ime, biće korišteno za pripisivanje za vaš rad.',
 
 # Recent changes
 'nchanges' => '$1 {{PLURAL:$1|promjena|promjene|promjena}}',
+'enhancedrc-since-last-visit' => '$1 {{PLURAL:$1|izmjena od vaše posljedne posjete}}',
 'enhancedrc-history' => 'historija',
 'recentchanges' => 'Nedavne izmjene',
 'recentchanges-legend' => 'Postavke nedavnih izmjena',
@@ -2610,7 +2612,7 @@ $1',
 'contributions' => 'Doprinosi {{GENDER:$1|korisnika|korisnice|korisnika}}',
 'contributions-title' => 'Doprinosi korisnika $1',
 'mycontris' => 'Doprinos',
-'contribsub2' => 'Za $1 ($2)',
+'contribsub2' => 'Za {{GENDER:$3|$1}} ($2)',
 'nocontribs' => 'Nisu nađene promjene koje zadovoljavaju ove uslove.',
 'uctop' => '(trenutno)',
 'month' => 'Od mjeseca (i ranije):',
@@ -2767,11 +2769,8 @@ Možda je već deblokirana.',
 Međutim, možda je blokirana kao dio bloka $2, koji se ne može deblokirati.',
 'ip_range_invalid' => 'Netačan raspon IP adresa.',
 'ip_range_toolarge' => 'Nisu dopuštene blokade veće od /$1.',
-'blockme' => 'Blokiraj me',
 'proxyblocker' => 'Bloker proksija',
-'proxyblocker-disabled' => 'Ova funkcija je onemogućena.',
 'proxyblockreason' => 'Vaša IP adresa je blokirana jer je ona otvoreni proksi.  Molimo vas da kontaktirate vašeg davatelja internetskih usluga (Internet Service Provider-a) ili tehničku podršku i obavijestite ih o ovom ozbiljnom sigurnosnom problemu.',
-'proxyblocksuccess' => 'Proksi uspješno blokiran.',
 'sorbsreason' => 'Vaša IP adresa je prikazana kao otvoreni proxy u DNSBL koji koristi {{SITENAME}}.',
 'sorbs_create_account_reason' => 'Vaša IP adresa je prikazana kao otvoreni proxy u DNSBL korišten od {{SITENAME}}.
 Ne možete napraviti račun',
@@ -3130,6 +3129,8 @@ Ovo je vjerovatno izazvao vezom ka vanjskoj nepoželjnoj stranici.',
 'spam_reverting' => 'Vraćanje na zadnju verziju koja ne sadrži linkove ka $1',
 'spam_blanking' => 'Sve revizije koje sadrže linkove ka $1, očisti',
 'spam_deleting' => 'Sve revizije koje sadrže linkove na $1, brišem',
+'simpleantispam-label' => "Provjera protiv spama.
+'''NE''' popunjavaj ovo!",
 
 # Info page
 'pageinfo-title' => 'Informacije za "$1"',
index ee209f9..6e58102 100644 (file)
@@ -373,11 +373,11 @@ $messages = array(
 'vector-action-undelete' => 'Restaura',
 'vector-action-unprotect' => 'Desprotegeix',
 'vector-simplesearch-preference' => 'Activar la barra de cerca simplificada (només aparença Vector)',
-'vector-view-create' => 'Inicia',
+'vector-view-create' => 'Crea',
 'vector-view-edit' => 'Modifica',
 'vector-view-history' => "Mostra l'historial",
 'vector-view-view' => 'Mostra',
-'vector-view-viewsource' => 'Mostra la font',
+'vector-view-viewsource' => 'Mostra el codi',
 'actions' => 'Accions',
 'namespaces' => 'Espais de noms',
 'variants' => 'Variants',
@@ -581,7 +581,7 @@ No ha donat cap explicació.',
 'wrong_wfQuery_params' => 'Paràmetres incorrectes per a wfQuery()<br />
 Funció: $1<br />
 Consulta: $2',
-'viewsource' => 'Mostra la font',
+'viewsource' => 'Mostra el codi',
 'viewsource-title' => 'Mostra la font per a $1',
 'actionthrottled' => 'Acció limitada',
 'actionthrottledtext' => "Com a mesura per a prevenir la propaganda indiscriminada (spam), no podeu fer aquesta acció tantes vegades en un període de temps tan curt. Torneu-ho a intentar d'ací uns minuts.",
@@ -716,7 +716,7 @@ la vostra antiga contrasenya.",
 'passwordsent' => "S'ha enviat una nova contrasenya a l'adreça electrònica registrada per «$1».
 Inicieu una sessió després que la rebeu.",
 'blocked-mailpassword' => 'La vostra adreça IP ha estat blocada. Se us ha desactivat la funció de recuperació de contrasenya per a prevenir abusos.',
-'eauthentsent' => "S'ha enviat un correu electrònic a la direcció especificada. Abans no s'envïi cap altre correu electrònic a aquesta adreça, cal verificar que és realment vostra. Per tant, cal que seguiu les instruccions presents en el correu electrònic que se us ha enviat.",
+'eauthentsent' => "S'ha enviat un correu electrònic a la direcció especificada. Abans no s'env cap altre correu electrònic a aquesta adreça, cal verificar que és realment vostra. Per tant, cal que seguiu les instruccions presents en el correu electrònic que se us ha enviat.",
 'throttled-mailpassword' => "Ja se us ha enviat un correu electrònic de reinicialització de contrasenya en {{PLURAL:$1|l'última hora|les últimes $1 hores}}.
 Per a prevenir abusos, només s'envia un correu electrònic de reinicialització de contrasenya cada {{PLURAL:$1|hora|$1 hores}}.",
 'mailerror' => "S'ha produït un error en enviar el missatge: $1",
@@ -1155,15 +1155,15 @@ Els altres administradors de {{SITENAME}} encara podran accedir al contingut ama
 * Informació personal inapropiada
 *: ''adreces personals, números de telèfon, números de la seguretat social, etc.''",
 'revdelete-legend' => 'Defineix restriccions en la visibilitat',
-'revdelete-hide-text' => 'Amaga el text de revisió',
+'revdelete-hide-text' => 'Text de la revisió',
 'revdelete-hide-image' => 'Amaga el contingut del fitxer',
 'revdelete-hide-name' => "Acció d'amagar i objectiu",
-'revdelete-hide-comment' => "Amaga el comentari de l'edició",
-'revdelete-hide-user' => "Amaga el nom d'usuari o la IP de l'editor",
+'revdelete-hide-comment' => 'Modifica el resum',
+'revdelete-hide-user' => "Nom d'usuari / adreça IP de l'editor",
 'revdelete-hide-restricted' => 'Suprimir les dades als administradors així com a la resta.',
 'revdelete-radio-same' => '(no modificar)',
-'revdelete-radio-set' => 'Si',
-'revdelete-radio-unset' => 'No',
+'revdelete-radio-set' => 'Visible',
+'revdelete-radio-unset' => 'Oculta',
 'revdelete-suppress' => 'Suprimeix també les dades dels administradors',
 'revdelete-unsuppress' => 'Suprimir les restriccions de les revisions restaurades',
 'revdelete-log' => 'Motiu:',
@@ -2633,12 +2633,12 @@ l'accés a l'escriptura a una adreça IP o un usuari prèviament bloquejat.",
 'blocklist-userblocks' => 'Amaga bloquejos de compte',
 'blocklist-tempblocks' => 'Amaga bloquejos temporals',
 'blocklist-addressblocks' => "Amaga bloquejos d'una sola IP",
-'blocklist-rangeblocks' => 'Amaga els blocatges de rang',
+'blocklist-rangeblocks' => 'Amaga els bloquejos de rang',
 'blocklist-timestamp' => 'Marca horària',
 'blocklist-target' => 'Usuari blocat',
 'blocklist-expiry' => 'Caduca',
-'blocklist-by' => 'Administrador del blocatge',
-'blocklist-params' => 'Paràmetres del blocatge',
+'blocklist-by' => 'Administrador que ha blocat',
+'blocklist-params' => 'Paràmetres del bloqueig',
 'blocklist-reason' => 'Motiu',
 'ipblocklist-submit' => 'Cerca',
 'ipblocklist-localblock' => 'Bloqueig local',
@@ -2687,11 +2687,8 @@ Per més detalls, a sota es mostra el registre de supressions:',
 'ipb_blocked_as_range' => "Error: L'adreça IP $1 no està blocada directament i per tant no pot ésser desbloquejada. Ara bé, sí que ho està per formar part del rang $2 que sí que pot ser desblocat.",
 'ip_range_invalid' => 'Rang de IP no vàlid.',
 'ip_range_toolarge' => 'No estan permesos el bloquejos de rangs més grans que /$1.',
-'blockme' => "Bloca'm",
 'proxyblocker' => 'Bloqueig de proxy',
-'proxyblocker-disabled' => "S'ha inhabilitat la funció.",
 'proxyblockreason' => "La vostra adreça IP ha estat bloquejada perquè és un proxy obert. Si us plau contactau el vostre proveïdor d'Internet o servei tècnic i informau-los d'aquest seriós problema de seguretat.",
-'proxyblocksuccess' => 'Fet.',
 'sorbsreason' => "La vostra adreça IP està llistada com a servidor intermediari (''proxy'') obert dins la llista negra de DNS que fa servir el projecte {{SITENAME}}.",
 'sorbs_create_account_reason' => "La vostra adreça IP està llistada com a servidor intermediari (''proxy'') obert a la llista negra de DNS que utilitza el projecte {{SITENAME}}. No podeu crear-vos-hi un compte",
 'xffblockreason' => "Una adreça IP present en la capçalera X-Forwarded-For, ja sigui vostra o la d'un servidor proxy que esteu utilitzant, ha estat blocada. El motiu inicial del bloqueig és: $1",
@@ -3039,6 +3036,8 @@ Això deu ser degut per un enllaç a un lloc extern inclòs a la llista negra.',
 'spam_reverting' => 'Es reverteix a la darrera versió que no conté enllaços a $1',
 'spam_blanking' => "Totes les revisions contenien enllaços $1, s'està deixant en blanc",
 'spam_deleting' => "S'estan suprimint totes les revisions que contenien enllaços a $1",
+'simpleantispam-label' => "Comprovació antispam.
+'''NO''' ho ompliu!",
 
 # Info page
 'pageinfo-title' => 'Informació de «$1»',
@@ -3811,7 +3810,7 @@ Amb aquest programa heu d'haver rebut [{{SERVER}}{{SCRIPTPATH}}/COPYING una còp
 # Special:Redirect
 'redirect' => 'Redirigeix per fitxer, usuari o ID de la revisió',
 'redirect-legend' => 'Redirigeix a un fitxer o a una pàgina',
-'redirect-summary' => "Aquesta pàgina especial redirigeix a un fitxer (donat el nom del fitxer), una pàgina (donada un ID de la revisió), o a una pàgina d'usuari (donat un ID numèric d'usuari).",
+'redirect-summary' => "Aquesta pàgina especial redirigeix a un fitxer (donat el nom del fitxer), una pàgina (donada un ID de la revisió), o a una pàgina d'usuari (donat un ID numèric d'usuari). Ús: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/revision/328429]], or [[{{#Special:Redirect}}/user/101]].",
 'redirect-submit' => 'Vés-hi',
 'redirect-lookup' => 'Consulta:',
 'redirect-value' => 'Valor:',
index 80ae8e2..0a133cb 100644 (file)
@@ -329,7 +329,7 @@ $messages = array(
 'tog-numberheadings' => 'Ша шех хlитто терахь корташна',
 'tog-showtoolbar' => 'Гайта лакхара гlирсан дакъа нисйеш аттон оц тадар чохь (JavaScript)',
 'tog-editondblclick' => 'Нисйе агlонаш шозза тlетаlийча (JavaScript)',
-'tog-editsection' => 'Ð\93айÑ\82а Ñ\85Ñ\8cажоÑ\80иг Â«Ð½Ð¸Ñ\81йе» Ð°Ñ\8cлла Ñ\85lоÑ\80а Ð°Ð³lона',
+'tog-editsection' => 'Ð\93айÑ\82а Ñ\85Ñ\8cажоÑ\80аг Â«Ð½Ð¸Ñ\81йе» Ð°Ñ\8cлла Ñ\85Ó\80оÑ\80а Ð°Ð³Ó\80она',
 'tog-editsectiononrightclick' => 'Нисде дакъа шозза бакъехьар дахка тlетаlийча оцу кортан (JavaScript)',
 'tog-showtoc' => 'Гойти коьртнаш (оцу агlонашна лаххара 3 коьртнашца)',
 'tog-rememberpassword' => 'Даглаца сан дӀаяздар хӀокху браузеран тӀяхь (цхьан $1 {{PLURAL:$1|де|ден|динахь}})',
@@ -347,7 +347,7 @@ $messages = array(
 'tog-enotifrevealaddr' => 'Гайта сан зlе оцу хаамаш барехь',
 'tog-shownumberswatching' => 'Гайта декъашхойн терахь, агlо латийна болу шай тергаме могlам юкъа',
 'tog-oldsig' => 'Хьалххьажар долучу куьгтаlорна:',
-'tog-fancysig' => 'Шен вики-къастаман куьгтаlдар (ша шех хьажориг йоцуш)',
+'tog-fancysig' => 'Шен вики-къастаман куьгтаӀдар (ша шех хьажораг йоцуш)',
 'tog-uselivepreview' => 'Лелайа чехка хьалха хьажа (JavaScript, муха ю хьажарна)',
 'tog-forceeditsummary' => 'Дага даийта, нагахь нисйарх лаьцна чохь язйина яцахь',
 'tog-watchlisthideown' => 'Къайлаяха ас нисйинарш оцу тергаме могlам чура',
@@ -359,9 +359,10 @@ $messages = array(
 'tog-ccmeonemails' => 'Дlадахьийта суна исанна кехат, аса дохьуьйтуш долу кхечу декъашхошна.',
 'tog-diffonly' => 'Ма гайта агlон чулацам шина башхонца цхьатерра йолуш',
 'tog-showhiddencats' => 'Гайта къайлаха йолу категореш',
-'tog-noconvertlink' => 'Хааман Ñ\85Ñ\8cажоÑ\80иг ÐºÑ\85Ñ\83ллÑ\83 Ð³lиÑ\80Ñ\81 Ð´lабайа',
+'tog-noconvertlink' => 'Хааман Ñ\85Ñ\8cажоÑ\80аг ÐºÑ\85Ñ\83ллÑ\83 Ð³Ó\80иÑ\80Ñ\81 Ð´Ó\80абайа',
 'tog-norollbackdiff' => 'Юха яккхиначул тӀаьхьа ма гайта версешан башхо',
 'tog-useeditwarning' => 'Хаамбе бина хийцамаш дӀаязцабеш аса болх дӀатосучу хенахь',
+'tog-prefershttps' => 'Даима лела йе лардина системин чудалар',
 
 'underline-always' => 'Даимна',
 'underline-never' => 'Цкъа а',
@@ -456,9 +457,7 @@ $messages = array(
 'index-category' => 'Меттигтерахьйо агlонаш',
 'noindex-category' => 'ДӀахьушйоцу агӀонаш',
 'broken-file-category' => '{{#switch:{{NAMESPACE}}
- |{{ns:0}}=Болх цабеш файланши хьажоригаш йолу агӀонаш}}',
-
-'linkprefix' => '/^(.*?)([a-zA-Z\\x80-\\xff]+)$/sD',
+ |{{ns:0}}=Болх цабеш файлийн хьажорагаш йолу агӀонаш}}',
 
 'about' => 'Цуьнах лаьцна',
 'article' => 'Яззам',
@@ -506,13 +505,13 @@ $messages = array(
 'help' => 'ГӀо',
 'search' => 'Лаха',
 'searchbutton' => 'Лаха',
-'go' => 'Дехьа хьажа',
-'searcharticle' => 'Дехьа хьажа',
+'go' => 'Дехьа гӀо',
+'searcharticle' => 'Дехьа гӀо',
 'history' => 'Истори',
 'history_short' => 'Истори',
 'updatedmarker' => 'Керла яккхина сона гинчултӀаьхьа',
 'printableversion' => 'Зорба туху верси',
-'permalink' => 'Ð\94аиман Ð¹Ð¾Ð»Ñ\83 Ñ\85Ñ\8cажоÑ\80иг',
+'permalink' => 'Ð\94аиман Ð¹Ð¾Ð»Ñ\83 Ñ\85Ñ\8cажоÑ\80аг',
 'print' => 'Зорба тоха',
 'view' => 'Хьажа',
 'edit' => 'Нисйé',
@@ -553,7 +552,7 @@ $messages = array(
 'lastmodifiedat' => 'ХӀокху агӀон тӀаьххьаралера хийцам: $2, $1.',
 'viewcount' => 'ХӀокху агӀонга хьойсина $1 {{PLURAL:$1|за|за|за}}.',
 'protectedpage' => 'ГӀароллийца йолу агӀо',
-'jumpto' => 'ДехьагӀо оцу:',
+'jumpto' => 'Дехьа гӀо:',
 'jumptonavigation' => 'Навигаци',
 'jumptosearch' => 'лаха',
 'view-pool-error' => 'Бехк цабиллар доьха, хӀинц гӀулкхдириг йоьттина ю.
@@ -655,11 +654,11 @@ $1',
 'laggedslavemode' => 'Тергам бе: агӀона чохь керла йаьхинарш ца хила мега.',
 'readonly' => 'Блоктоьхна дӀайаздар хаамийн бухе',
 'enterlockreason' => 'Билгал де блоктохаран бахьна а и чекх йолу хан а.',
-'missing-article' => 'Хlокху чохь кароезаш йолу хьан дехарца йозан агlонаш цакарийна «$1» $2.
+'missing-article' => 'ХӀокху чохь кароезаш йолу хьан дехарца йозан агӀонаш цакарийна «$1» $2.
 
\98Ñ\88Ñ\82наÑ\80г Ð½Ð°Ð³Ð³Ð°Ñ\85Ñ\8c Ñ\85Ñ\83Ñ\8cлÑ\83 Ñ\85Ñ\8cажоÑ\80иг Ð´lайаÑ\8cккÑ\85ина Ð¹Ð°Ð»Ñ\85Ñ\8c Ð¹Ð° Ñ\85ийÑ\86ам Ð±Ð¸Ð½Ð° Ñ\82иÑ\88а Ñ\85Ñ\8cажоÑ\80игÑ\86а Ð´ÐµÑ\85Ñ\8cа Ð²Ð°Ð»Ð° Ð³lоьртича.
\98Ñ\88Ñ\82наÑ\80г Ð½Ð°Ð³Ð³Ð°Ñ\85Ñ\8c Ñ\85Ñ\83Ñ\8cлÑ\83 Ñ\85Ñ\8cажоÑ\80аг Ð´Ó\80аÑ\8fÑ\8cккÑ\85ина ÐµÐ»Ð°Ñ\85Ñ\8c Ñ\8f Ñ\85ийÑ\86ам Ð±Ð¸Ð½Ð° Ñ\82иÑ\88а Ñ\85Ñ\8cажоÑ\80агÑ\86а Ð´ÐµÑ\85Ñ\8cа Ð³Ó\80о Ð³Ó\80оьртича.
 
-Нагахьсан гlулкх цуьнах доьзна дацахь, хьуна карийна гlирс латточехь гlалат.
+Нагахьсан гӀулкх цуьнах доьзна дацахь, хьуна карийна гӀирс латточехь гӀалат.
 Дехар до, хаам бе оцуьнах [[Special:ListUsers/sysop|куьйгалхога]], гойтуш URL.',
 'missingarticle-rev' => '(верси № $1)',
 'missingarticle-diff' => '(тейп тайпнара: $1, $2)',
@@ -684,6 +683,8 @@ $1',
 'badtitletext' => 'Дехарца йолу агlонан цlе нийса яц, йаьсса ю, хила мега нийса ца хlоттийна меттаюкъар йа юкъарвики цlе. Хила мега, цlарца цамагош йолу саберг.',
 'perfcached' => 'Лахара хаам схьаэца кэша чура цундела тӀехьарлаьра хийцамаш гойтуш бац. Кэша чохь латтаё оцул $1  кӀезиг {{PLURAL:$1|дӀаяздар|дӀаяздарш}}.',
 'perfcachedts' => 'Лахара хаам схьаэца кэша чура иза тӀаьхьара карла ялла $1. Кэша чохь латта до оцул $4 кӀезиг {{PLURAL:$4|дӀаяздар|дӀаяздарш}}.',
+'querypage-no-updates' => 'ХӀинца хӀара агӀо карлаякхар дӀадайина ду.
+Кхузахь гайтина болу хаамаш карла боккхур бац.',
 'wrong_wfQuery_params' => 'Хилийта йиш йоцу параметраш хӀокху функцин wfQuery()<br />
 Функци: $1<br />
 Жоп дехар: $2',
@@ -693,6 +694,9 @@ $1',
 'protectedpagetext' => 'ХӀара агӀо дӀакъойлина йу рé цадаккхийта.',
 'viewsourcetext' => 'Хьоьга далундерг хьажар а дезахь хlокху агlон чура йоза хьаэцар:',
 'protectedinterface' => 'ХӀара схьгайтарна гӀирса хаамаш латтош йолу агӀо ю. Куьйгалхошна бен иза хийца цало.',
+'editinginterface' => "'''Тергам бе:''' Ахьа таеш ю интерфейсан йоза долу агӀо програмин латторан.
+Цуна бина хийцам хьокху википедин кхечу декъашхошна гур бу.
+Хьокху хаамийн гочдар тӀетоха я хийца лела йе сайт MediaWiki [//translatewiki.net/ translatewiki.net].",
 'namespaceprotected' => 'ХӀан бакъо яц анна цӀераш чохь тадарш да «$1».',
 'customcssprotected' => 'Хьан бакъо яц хӀара CSS-агӀо тая, иза кхечу декъашхочун гӀерс болу дера.',
 'customjsprotected' => 'Хьан бакъо яц хӀара JavaScript-агӀо тая, иза кхечу декъашхочун гӀерс болу дера.',
@@ -743,6 +747,9 @@ $1',
 'userlogin-resetpassword-link' => 'Пароль кхоссар',
 'helplogin-url' => 'Help:Системин довзийтар',
 'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|Системин чудаха гӀодар]]',
+'userlogin-loggedin' => 'Хьо {{GENDER:$1|$1}} цӀарца чохь ву/ю.
+Лахара форманца кхин цӀарца чугӀо.',
+'userlogin-createanother' => 'Кхолла декъашхочун кхин дӀаяздар',
 'createacct-join' => 'ДӀаязбе лахахь хай хаам.',
 'createacct-emailrequired' => 'Электронни почтан адрес',
 'createacct-emailoptional' => 'Электронни почтан адрес (ца яздича мега)',
@@ -812,20 +819,37 @@ $1',
 
 # Special:ChangeEmail
 'changeemail' => 'Хийца электрони почт',
+'changeemail-header' => 'Электрони почтан адрес хийцар',
+'changeemail-text' => 'Юза хӀара форма хьайн электрони почтан адрес хуьйцуш. Ахьа хийцар бакъдан язъян еза пароль.',
 'changeemail-no-info' => 'ХӀара агӀо лело системин чугӀо.',
+'changeemail-oldemail' => 'Карара электронни почтан адрес:',
+'changeemail-newemail' => 'Электрони почтан керла адрес:',
 'changeemail-none' => '(яц)',
+'changeemail-password' => 'Хьан пароль «{{SITENAME}}» проектан:',
 'changeemail-submit' => 'Хийца email',
 'changeemail-cancel' => 'Цаоьшу',
 
+# Special:ResetTokens
+'resettokens' => 'Токенаш кхоссар',
+'resettokens-text' => 'Хьан йиш ю токенаш кхосса, цара йиш хуьлуьйту цхьаболу долара хаамашна тӀекхача, уьш ю хьан дӀаяздар ца вовшахтесна. 
+
+Хьона иза оьшу, ахьа хьай токенаш цхьам гучу яьхна елахь я хьан аккаунт йохийна елахь.',
+'resettokens-legend' => 'Токенаш кхоссар',
+'resettokens-tokens' => 'Токенаш:',
+'resettokens-token-label' => '$1 (карара маьӀна: $2)',
+'resettokens-watchlist-token' => 'Веб-каналан (Atom/RSS) токен  [[Special:Watchlist|хьан тергаме могӀам чура агӀонашна хийцамаш бар]]',
+'resettokens-done' => 'Токенаш кхиссина.',
+'resettokens-resetbutton' => 'Къастина токенаш кхоссар',
+
 # Edit page toolbar
 'bold_sample' => 'Дерстино до йоза',
 'bold_tip' => 'Дерстино до йоза',
 'italic_sample' => 'Сеттан до йоза',
 'italic_tip' => 'Сеттан до йоза',
 'link_sample' => 'Хьажориган коьрта могlа',
-'link_tip' => 'ЧоÑ\8cÑ\85Ñ\8cа Ñ\85Ñ\8cажоÑ\80иг',
-'extlink_sample' => 'http://www.example.com Ñ\85Ñ\8cажоÑ\80иг корта',
-'extlink_tip' => 'Арахьа хьажориг (йиц ма йе хlотталушерг http://)',
+'link_tip' => 'ЧоÑ\8cÑ\85Ñ\8cа Ñ\85Ñ\8cажоÑ\80аг',
+'extlink_sample' => 'http://www.example.com Ñ\85Ñ\8cажоÑ\80аг корта',
+'extlink_tip' => 'Арахьара хьажораг (йиц ма йе хӀотталушерг http://)',
 'headline_sample' => 'Йозан корта',
 'headline_tip' => 'Корта 2-гlа локхаллийца',
 'nowiki_sample' => 'Чудиллийша кхузе барамхlоттонза йоза.',
@@ -833,7 +857,7 @@ $1',
 'image_sample' => 'Example.jpg',
 'image_tip' => 'Чохь йолу файл',
 'media_sample' => 'Example.ogg',
-'media_tip' => 'Ð¥Ñ\8cажоÑ\80иг медиа-файлан тӀе',
+'media_tip' => 'Ð¥Ñ\8cажоÑ\80аг медиа-файлан тӀе',
 'sig_tip' => 'Хьан куьгтаlор аъ хlоттина хан',
 'hr_tip' => 'Ана сиз (сих сиха ма леладайша)',
 
@@ -850,6 +874,8 @@ $1',
 'summary-preview' => 'Цуьнах лаьцна хирду:',
 'blockedtitle' => 'Декъашхочун блоктоьхана',
 'nosuchsectiontitle' => 'Дакъа каро йиш яц.',
+'nosuchsectiontext' => 'Хьо гӀерта дуцу дакъа тадан.
+Хьо хӀокху агӀоне хьоьжучу хенахь иза кхечухьа деккхина я дӀадаьккхина хела тарло.',
 'loginreqtitle' => 'Хьай цӀарца чугӀо',
 'loginreqlink' => 'Логин',
 'accmailtitle' => 'Пароль дlаяхьийтина.',
@@ -921,7 +947,7 @@ $1',
 'template-semiprotected' => '(дуьззина доцуш гlаролла)',
 'hiddencategories' => 'ХӀара агӀо чуйогӀуш ю оцу $1 {{PLURAL:$1|къайлаха категори чу|къайлаха категореш чу}}:',
 'edittools' => '<!-- Кхузе буха диллина йоза гуш хир ду редоккхуче бухахь а хlума чуйоккхуче бухахь. -->',
-'permissionserrors' => 'ТӀекхачарехь гӀалат',
+'permissionserrors' => 'ТӀекхачаре бакъона гӀалат',
 'permissionserrorstext' => 'Хьан бакъо яц кхочуш хилийта хийцам оцу {{PLURAL:$1|шолгlа бахьанца|шолгlа бахьанашца}}:',
 'permissionserrorstext-withaction' => "Хьан бакъо яц хlумда «'''$2'''» оцу {{PLURAL:$1|шолгlа бахьанца|шолгlа бахьанашца}}:",
 'recreate-moveddeleted-warn' => "'''Тидам бе. Ахьа кхуллуш ю, хьалхо дӀаяккхина йолу агӀо.'''
@@ -1011,6 +1037,7 @@ $1',
 
 # History merging
 'mergehistory-from' => 'Дуьххьарлера агӀоно',
+'mergehistory-fail' => 'АгӀонийн истореш вовшахтоха цаделира, дехар до агӀона параметаршка а хене а хьажа.',
 'mergehistory-invalid-source' => 'Хьостан нийса корта хила еза.',
 'mergehistory-invalid-destination' => 'Юзийна агӀона нийса корта хила еза.',
 'mergehistory-reason' => 'Бахьан:',
@@ -1026,6 +1053,7 @@ $1',
 'compareselectedversions' => 'Хаьржина версеш муха ю хьажа',
 'showhideselectedversions' => 'Гайта/къайлайаха хаьржина башхонаш',
 'editundo' => 'цаоьшу',
+'diff-empty' => '(башхалла яц)',
 'diff-multi' => '({{PLURAL:$1|гайтина яц $1 юккъера верси|гайтина яц $1 юккъера версеш}} {{PLURAL:$2|$2 декъашхочун|$2 декъашхой}})',
 
 # Search results
@@ -1109,17 +1137,20 @@ $1',
 'prefs-email' => 'Электронан почтан параметраш',
 'prefs-rendering' => 'Арахьара хатl',
 'saveprefs' => 'lалашдан',
+'resetprefs' => 'Кхоссар',
 'restoreprefs' => 'МеттахӀоттабе гӀирс Iад битарца',
 'prefs-editing' => 'Тадар',
 'rows' => 'МогӀанаш:',
 'columns' => 'БӀогӀамаш:',
 'searchresultshead' => 'Лаха',
 'resultsperpage' => 'Карийначу дӀаяздаршан дукхалла:',
-'stub-threshold' => 'Ð\9aеÑ\87 Ñ\8fÑ\80ан Ð´Ð¾Ð·Ð° <a href="#" class="stub">коÑ\8cÑ\80Ñ\82амогÓ\80амна Ñ\85Ñ\8cажоÑ\80игаш</a> (байташках):',
+'stub-threshold' => 'Ð\9aеÑ\87 Ñ\8fÑ\80ан Ð´Ð¾Ð·Ð° <a href="#" class="stub">коÑ\8cÑ\80Ñ\82амогÓ\80амна Ñ\85Ñ\8cажоÑ\80агаш</a> (байташках):',
 'recentchangesdays' => 'Керла нисдар гайта динахь:',
 'recentchangesdays-max' => 'Къезиг  $1 {{PLURAL:$1|дена}}',
 'recentchangescount' => 'Iад йитарца гойтуш долу нисдаршан дукхалла',
 'prefs-help-recentchangescount' => 'Гойту керла нисдарш, агӀонашан истори, тептарш.',
+'prefs-help-watchlist-token2' => 'Иза хьан тергаме могӀан къайла догӀа ду.
+Муьлха и хуучунна йиш ю хьан тергаме могӀам беша, цундела ма хаийта иза кхечаьрга. [[Special:ResetTokens|ТӀетаӀа йе кхуза и хьайга кхосса лууш делахь]].',
 'savedprefs' => 'Хьан гӀирс Ӏалашбина.',
 'timezonelegend' => 'Сахьатан аса:',
 'localtime' => 'Меттигера хан:',
@@ -1222,21 +1253,73 @@ $1',
 'grouppage-suppress' => '{{ns:project}}:Ревизораш',
 
 # Rights
+'right-read' => 'агӀонашка хьажар',
 'right-edit' => 'АгӀоаш нисяр',
 'right-createpage' => 'АгӀонаш кхоллар (дийцарш дац)',
 'right-createtalk' => 'Дийцаре агӀонаш кхоллар',
 'right-createaccount' => 'декъашхошна керла дӀаяздарш кхоллар',
+'right-minoredit' => '«къезиг хийцам» аьлла билгало хӀоттор',
 'right-move' => 'АгӀонашан цӀераш хийцар',
 'right-move-subpages' => 'АгӀонашан цӀераш хийцар цера бухара агӀонашцан',
+'right-move-rootuserpages' => 'декъашхочун ораман агӀонийн цӀераш хийцар',
 'right-movefile' => 'Файланши цӀе хийцар',
+'right-suppressredirect' => 'агӀона цӀе хуьйцуш ширчу цӀарах ма кхолла дӀасахьажаяр',
 'right-upload' => 'Файлаш чуйаьхар',
+'right-reupload' => 'йолуш йолу чера тӀехула файлаш дӀаязъяр',
+'right-reupload-own' => 'тохарлеррачу декъашхочо файлаш юху дӀаязъяр',
+'right-reupload-shared' => 'юкъахь йолу проекташкара файлаш чоьхьарачу файлашца хийцар',
+'right-upload_by_url' => 'URL адресца файлаш чуяхар',
+'right-purge' => 'бакъдеш йолу агӀо йиссалц, агӀонийн кэш цӀанъян',
+'right-autoconfirmed' => 'IP-адресан чехкалин доза дац',
+'right-bot' => 'автоматически процес сана лара',
+'right-nominornewtalk' => 'агӀонашкахь къезиг нисдарш цахиларо хуьлуьйту керла хаамийн хӀоттам',
+'right-apihighlimits' => 'API-дехарш кхочушдан кӀезиг дихкар',
+'right-writeapi' => 'дӀаяздеш лелойо API',
 'right-delete' => 'агӀош дӀаяхар',
 'right-bigdelete' => 'еха хийцаман истори йолу агӀонаш дӀаяхар',
+'right-deletelogentry' => 'тептар чура билгала дӀаяздарш дӀадахар а меттахӀиттадар а.',
+'right-deleterevision' => 'агӀонийн билгала версеш дӀаяхар а меттахӀиттаяр а',
+'right-deletedhistory' => 'дӀаяхна агӀонийн исторега хьажар дӀадаьккхина йоза тӀекхочехь доцуш',
+'right-deletedtext' => 'дӀадаьккхина йозане а хийцамашка а хьажар агӀонийн дӀаяхна версин юккъахь',
 'right-browsearchive' => 'ДӀаяхна агӀонаш лахар',
 'right-undelete' => 'АгӀонаш меттахӀоттор',
+'right-suppressrevision' => 'куьйгалхойх хьулйина йолу агӀонийн версеш меттахӀиттаяр а хьажар а',
+'right-suppressionlog' => 'долара тептаршка хьажар',
+'right-block' => 'кхечу декъашхошка тадарш ца дайта дехкар хӀоттор',
 'right-blockemail' => 'Цамагдо декъашхошка хааман кехаташ кхехьийта',
+'right-hideuser' => 'декъашхочун цӀе а и лечкъо а цамагор',
+'right-ipblock-exempt' => 'IP блоктохаршна чекхбовлар, диапазонийн шаблоктохаршна а блоктохаршна а',
+'right-proxyunbannable' => 'проксен автоматически блоктохаран чекхбовлар',
 'right-unblockself' => 'ша шин блокдӀаяккхар',
 'right-protect' => 'АгӀона гӀоралла хийцар а гӀоралла дина агӀо нисяр а',
+'right-editprotected' => '«{{int:protect-level-sysop}}» бахьанца гӀоралла дина агӀонаш нисяр',
+'right-editsemiprotected' => '«{{int:protect-level-autoconfirmed}}» бахьанца гӀоралла дина агӀонаш нисяр',
+'right-editinterface' => 'лелош йолу интерфейсан хийцам бар',
+'right-editusercssjs' => 'кхечу декъашхойн CSS- а JS- а файлаш нисяр',
+'right-editusercss' => 'кхечу декъашхойн CSS-файлаш нсяр',
+'right-edituserjs' => 'кхечу декъашхойн JavaScript-файлаш нисяр',
+'right-editmyusercss' => 'Декъашхочун CSS файлаш таяр',
+'right-editmyuserjs' => 'Лелош йолу шен JavaScript-файлаш таяр',
+'right-viewmywatchlist' => 'Шен тергаме могӀане хьажар',
+'right-editmywatchlist' => 'Хьайн тергаме могӀам табар. Тидам бе, цхьадолу динарш могӀам юкъа шаш кхетар ду.',
+'right-viewmyprivateinfo' => 'Хьан долара хаамашка хьажар (масала, электронан адрес, бакъ цӀе)',
+'right-editmyprivateinfo' => 'Хьан долара хаамаш нисбар (масала, электронан адрес, бакъ цӀе)',
+'right-editmyoptions' => 'Тае хьайн гӀоли хетарг',
+'right-rollback' => 'билгала агӀона тӀехьарчу декъашхочо дина нисдарш сиха юхадахар',
+'right-markbotedits' => 'юхудохучу нисдаршан шаболх бечунна нисдарш аьлла билгало ян',
+'right-noratelimit' => 'чехкалин доза дац',
+'right-import' => 'кхечу википедешкара агӀонаш импорт ян',
+'right-importupload' => 'файлаш чуяхарца агӀонаш импорт ян',
+'right-patrol' => 'нисдарш хьаьжна сана билгалдар',
+'right-patrolmarks' => 'керла нисдарийн хьаьжна билгалонашка хьажар',
+'right-unwatchedpages' => 'тергамехь йоцу агӀонийн могӀане хьажар',
+'right-mergehistory' => 'агӀонаш вовшахтохар',
+'right-userrights' => 'декъашхойн массо бакъонаш хийцар',
+'right-userrights-interwiki' => 'кхечу вики сайташкара декъашхойн бакъонаш хийцар',
+'right-siteadmin' => 'хаамийн гуламан блоктохар а блокдӀаяккхар а',
+'right-override-export-depth' => 'агӀонаш экспорт ян, 5 кхаччалц къорга агӀонаш цхьан',
+'right-sendemail' => 'кхечу декъашхошка электронан хаамаш кхехьийта',
+'right-passwordreset' => 'пароль хийцарца электроннан хаамашка хьажар',
 
 # Special:Log/newusers
 'newuserlogpage' => 'Декъашхой дlабазбина тептар',
@@ -1248,9 +1331,13 @@ $1',
 'action-read' => 'хӀара агӀо ешар',
 'action-edit' => 'нисйа хlара агlо',
 'action-move' => 'хӀокху агӀон цӀе хийца',
+'action-move-rootuserpages' => 'декъашхочун ораман агӀонийн цӀераш хийцар',
 'action-delete' => 'дӀаяккха хӀара агӀо',
 'action-deletedhistory' => 'хӀокху агӀона дӀаяккхинцу исторега хьажар',
 'action-undelete' => 'хӀара агӀо меттахӀоттор',
+'action-patrol' => 'кхечера нисдарш хьаьжна сана билгалдар',
+'action-autopatrol' => 'шен нисдарш хьаьжна сана билгалдар',
+'action-siteadmin' => 'хаамийн гуламан блоктохар а блокдӀаяккхар а',
 
 # Recent changes
 'nchanges' => '$1 {{PLURAL:$1|хийцам|хийцамаш|хийцамаш}}',
@@ -1294,7 +1381,7 @@ $1',
 'recentchangeslinked-summary' => "Хlара хийцам биначу агlонашан могlам бу, тlетовжар долуш хьагучу агlон (йа хьагойтуш йолучу категорена).
 Агlонаш юькъайогlуш йолу хьан [[Special:Watchlist|тергаме могlам чохь]] '''къастийна йу'''.",
 'recentchangeslinked-page' => 'Агlон цlе:',
-'recentchangeslinked-to' => 'Кхечу агlор, гайта хийцамаш агlонашца, хlоттийначу агlонтlе хьажориг йолуш',
+'recentchangeslinked-to' => 'Кхечу агӀор, гайта хийцамаш агӀонашца, хӀоттийначу агӀонтӀе хьажораг йолуш',
 
 # Upload
 'upload' => 'Файл чуяккхар',
@@ -1302,7 +1389,7 @@ $1',
 'reuploaddesc' => 'Юху гӀо файл чуйоккху агӀоне',
 'upload-tryagain' => 'ДӀадахьийта хийцина файлах лаьцнарг',
 'uploadnologintext' => 'Серверан чу файлаш яха хьо $1.',
-'upload-permitted' => 'Ð\9cагийна Ñ\84айлаÑ\88ан тайпанаш: $1.',
+'upload-permitted' => 'Ð\9cагийна Ñ\84айлийн тайпанаш: $1.',
 'uploadlogpage' => 'Чуяхаран тéптар',
 'uploadlogpagetext' => 'Лахахьа гойтуш бу могlам тlаьххьара чуяхна файлаши. Ишта хьажа. [[Special:ImageList|файлаши могlам]] йа [[Special:NewImages|галеларе файлаши]].',
 'filename' => 'Файлан цӀе',
@@ -1312,6 +1399,7 @@ $1',
 'filesource' => 'Хьост:',
 'ignorewarning' => 'ХӀума дац чуяккха файл',
 'ignorewarnings' => 'ДӀахедар тергал ца дан',
+'badfilename' => 'Файлан цӀе оцу $1.',
 'emptyfile' => 'Ахьа чуйоккхуш йолу файл еса хийла там бу. Иза гӀалат хийла мега файлан цӀе нийса язйина йоцу дела. Дехар до хьажа бакъалла и юьй ахьа чуйоккхуш йолу файл.',
 'file-deleted-duplicate' => 'Иштта файл ([[:$1]]) хӀинцале дӀаяьккхина хилла. Дехар до, юху файл чуяккхале файл дӀаяккхаран историга хьажа.',
 'uploadwarning' => 'Дlахьедар',
@@ -1352,7 +1440,7 @@ PICT # тайп тайпан
 Декъашхо къастичи, цун керла файлаш гойту.',
 'listfiles_search_for' => 'Лаха хIуман цIарца:',
 'imgfile' => 'файл',
-'listfiles' => 'ФайлаÑ\88и могӀам',
+'listfiles' => 'Файлийн могӀам',
 'listfiles_date' => 'Терахь',
 'listfiles_name' => 'Файлан цӀе',
 'listfiles_user' => 'Декъашхо',
@@ -1374,7 +1462,7 @@ PICT # тайп тайпан
 'filehist-dimensions' => 'Файлан барам',
 'filehist-filesize' => 'Файлан барам',
 'filehist-comment' => 'Билгалдаккхар',
-'imagelinks' => 'Ð¥Ñ\8cажоÑ\80игаÑ\88 Ð¾Ñ\86Ñ\83 Ñ\84айлан',
+'imagelinks' => 'Файл Ð»ÐµÐ»Ð¾Ñ\80',
 'linkstoimage' => '{{PLURAL:$1|ТӀаьхьайогӀу $1 агӀо тӀетойжина|ТӀаьхьайогӀу $1 агӀонаш тӀетойжина|ТӀаьхьайогlу $1 агӀонаш тӀетойжина}} хӀокху файлан:',
 'nolinkstoimage' => 'АгӀонашчохь файл лелош яц.',
 'sharedupload' => 'Хlара хlума оцун $1 чура ю иза хила мега лелош кхечу кхолламашкахь.',
@@ -1407,6 +1495,7 @@ PICT # тайп тайпан
 
 # MIME search
 'mimesearch' => 'MIME хула лаха',
+'mimesearch-summary' => 'ХӀокху агӀоно йиш хуьлуьйту MIME-тайпан файлаш харжа. Яздеш долу формат: чулацаман тайп/бухара тайп, масала  <code>image/jpeg</code>.',
 'mimetype' => 'MIME-тайп:',
 
 # Unwatched pages
@@ -1417,6 +1506,8 @@ PICT # тайп тайпан
 
 # Unused templates
 'unusedtemplates' => 'Лелош доцу кепаш',
+'unusedtemplatestext' => 'Кхузахь дагар йина «{{ns:template}}» цӀерийн меттиган агӀонаш, кхечу агӀонийн юкъа тоьхна йоцу.
+Диц ма делахь хьажа кеп агӀонашкахь лелош юй.',
 
 # Random page
 'randompage' => 'Цахууш нисйелла агӀо',
@@ -1424,6 +1515,7 @@ PICT # тайп тайпан
 # Random page in category
 'randomincategory' => 'Категори чу цахууш нийса елла агӀо',
 'randomincategory-selectcategory' => 'Категори чу цахууш нийса елла агӀона чу гӀо: $1 $2.',
+'randomincategory-selectcategory-submit' => 'Дехьа гӀо',
 
 # Random redirect
 'randomredirect' => 'Цахууш нисделла дIасахьажор',
@@ -1448,14 +1540,18 @@ PICT # тайп тайпан
 'pageswithprop-text' => 'Кхузахь гойтуш ю агӀонаш цхьадолу къастамаш куьйга юху билгал даьхнарш.',
 'pageswithprop-prop' => 'Къастаман цӀе:',
 
-'doubleredirects' => 'ШалгIа дIасахьажийнарш',
+'doubleredirects' => 'Шалха дIасахьажийнарш',
+'doubleredirectstext' => 'ХӀокху агӀонехь ю дӀасахьажорашан тӀе хьажийна йолу дӀасахьажораш.
+<del>ТӀехула сиз хаькхна </del>нисйина чарна.',
 'double-redirect-fixed-move' => 'Агlон [[$1]] цlе хийцна, хlинца иза дlахьажийна оцу [[$2]]',
 
 'brokenredirects' => 'ДIахаьдна долу дIасахьажораш',
+'brokenredirectstext' => 'Лахара дӀасахьажийнарш ю йоцучу агӀонийн тӀе хьажийна:',
 'brokenredirects-edit' => 'нисйé',
 'brokenredirects-delete' => 'дӀаяккха',
 
-'withoutinterwiki' => 'Кхечу меттанашан хьажориг йоцу агIонаш',
+'withoutinterwiki' => 'Юкъарвики-хьажорагаш йоцу агӀонаш',
+'withoutinterwiki-summary' => 'Лахара агӀонийн юкъарвики-хьажорагаш яц:',
 'withoutinterwiki-submit' => 'Гайта',
 
 'fewestrevisions' => 'ЧIогIа кIезиг башхонаш йолу агIонаш',
@@ -1463,13 +1559,14 @@ PICT # тайп тайпан
 # Miscellaneous special pages
 'nbytes' => '$1 {{PLURAL:$1|байт|байташ|байт}}',
 'ncategories' => '$1 {{PLURAL:$1|категори|категореш|категореш}}',
-'ninterwikis' => '$1 {{PLURAL:$1|Ñ\8eкÑ\8aаÑ\80вики-Ñ\85Ñ\8cажоÑ\80иг|Ñ\8eкÑ\8aаÑ\80вики-Ñ\85Ñ\8cажоÑ\80игаш}}',
-'nlinks' => '$1 {{PLURAL:$1|Ñ\85Ñ\8cажоÑ\80иг|Ñ\85Ñ\8cажоÑ\80игаш}}',
+'ninterwikis' => '$1 {{PLURAL:$1|Ñ\8eкÑ\8aаÑ\80вики-Ñ\85Ñ\8cажоÑ\80аг|Ñ\8eкÑ\8aаÑ\80вики-Ñ\85Ñ\8cажоÑ\80агаш}}',
+'nlinks' => '$1 {{PLURAL:$1|Ñ\85Ñ\8cажоÑ\80аг|Ñ\85Ñ\8cажоÑ\80агаш}}',
 'nmembers' => '$1 {{PLURAL:$1|хӀума|хӀумнаш}}',
 'nimagelinks' => 'Лелош ю $1 {{PLURAL:$1|агӀоначохь|агӀонашкахь|агӀонашкахь}}',
 'ntransclusions' => 'лелош ю $1 {{PLURAL:$1|агӀоначохь|агӀонашкахь|агӀонашкахь}}',
 'specialpage-empty' => 'Дехаро хӀумма ца елла.',
 'lonelypages' => 'Байлахь йисина агIонаш',
+'lonelypagestext' => 'Кхузахь ю {{grammar:genitive|{{SITENAME}}}} кхечу агӀонашкахь тӀе хьажийна хьажорагаш йоцу агӀонаш.',
 'uncategorizedpages' => 'Категореш йоцу агIонаш',
 'uncategorizedcategories' => 'Категореш йоцу категореш',
 'uncategorizedimages' => 'Категореш йоцу файлаш',
@@ -1479,16 +1576,20 @@ PICT # тайп тайпан
 'wantedcategories' => 'Оьшуш йолу категореш',
 'wantedpages' => 'Оьшуш йолу агIонаш',
 'wantedfiles' => 'Оьшуш йолу файлаш',
+'wantedfiletext-cat' => 'Лахара йоцу файлаш лело гӀерта. Оцу могӀам юкъа ца хууш файлаш кхета там бу, кхечу проекташ чохь йолу. Ишта ца хууш юкъа нийса елачарна тӀехула <del>сиз</del> хира ду.
+Кхин йоцу файлаш гойту [[:$1]] чохь',
+'wantedfiletext-nocat' => 'Лахара йоцу файлаш лело гӀерта. Оцу могӀам юкъа ца хууш файлаш кхета там бу, кхечу проекташ чохь йолу. Ишта ца хууш юкъа нийса елачарна тӀехула <del>сиз</del> хира ду.',
 'wantedtemplates' => 'Оьшуш долу кепаш',
-'mostlinked' => 'Ð\94Ñ\83ккÑ\85а Ñ\85Ñ\8cажоÑ\80игаш тIе тоьхна йолу агIонаш',
+'mostlinked' => 'Ð\94Ñ\83ккÑ\85а Ñ\85Ñ\8cажоÑ\80агаш тIе тоьхна йолу агIонаш',
 'mostlinkedcategories' => 'Дуккха тӀе хьажораш йолу категореш',
 'mostlinkedtemplates' => 'Массарел дуккха а леладо кепаш',
 'mostcategories' => 'Дуккха категореш тӀе тоьхна йолу агӀонаш',
 'mostimages' => 'Массарел дуккха лелайо файлаш',
-'mostinterwikis' => 'Ð\94Ñ\83ккÑ\85а Ñ\8eкÑ\8aаÑ\80вики Ñ\85Ñ\8cажоÑ\80игаш тӀе тоьхна йолу агӀонаш',
+'mostinterwikis' => 'Ð\94Ñ\83ккÑ\85а Ñ\8eкÑ\8aаÑ\80вики Ñ\85Ñ\8cажоÑ\80агаш тӀе тоьхна йолу агӀонаш',
 'mostrevisions' => 'Сих сиха нисйина йолу агIонаш',
 'prefixindex' => 'Хьалха агlонашан цlераш хlотто йеза',
 'prefixindex-namespace' => 'Хьалха агӀонашан цӀераш хӀотто еза («{{ns:$1}}»)',
+'prefixindex-strip' => 'Хиламийн могӀам чура префикс къайлаяккха',
 'shortpages' => 'Боцоа яззамаш',
 'longpages' => 'Беха яззамаш',
 'deadendpages' => 'Дика йоцу агIонаш',
@@ -1497,13 +1598,14 @@ PICT # тайп тайпан
 'listusers' => 'Декъашхой могlам',
 'listusers-editsonly' => 'Цхаъ мукъане а хийцам бина декъашхой гайта',
 'listusers-creationsort' => 'Кхолларан хене хьаьжна нисъяр',
+'listusers-desc' => 'Харжа къезиг хиларца',
 'usercreated' => '{{GENDER:$3|дӀавазвелла|дӀаязелла}} $1 $2',
 'newpages' => 'Керла агlонаш',
 'newpages-username' => 'Декъашхо:',
 'ancientpages' => 'Яззамаш оцу терахьца тӀаьххьара тадар дина долу',
 'move' => 'Цlе хийца',
 'movethispage' => 'Хlокху агlон цlе хийца',
-'unusedimagestext' => 'Дехар до, тидаме эца, кхин йолу дуьнана машан-меттигаш а лелош хила мега нисса йогlу хьажориг (URL) хlокху хlуман, хlокху могlаме йогlуш ялахь яцахь а иза хила мега жигара лелош.',
+'unusedimagestext' => 'Дехар до, тидаме эца, кхин йолу дуьнана машан-меттигаш а лелош хила мега нийсса йогӀу хьажораг (URL) хӀокху хӀуман, хӀокху могӀаме йогӀуш ялахь яцахь а иза хила мега жигара лелош.',
 'notargettitle' => 'Ӏалашо билгал йина яц',
 'nopagetitle' => 'Ишта агӀо яц',
 'nopagetext' => 'Ишта агӀо яц.',
@@ -1553,9 +1655,13 @@ PICT # тайп тайпан
 'sp-deletedcontributions-contribs' => 'къинхьегам',
 
 # Special:LinkSearch
-'linksearch' => 'Арахьа хьажориг',
+'linksearch' => 'Арахьара хьажораг',
+'linksearch-pat' => 'Лаха кеп:',
 'linksearch-ok' => 'Лаха',
-'linksearch-line' => '$2 — хьажориг кху $1',
+'linksearch-text' => 'Лело мега хӀоттош йолу символаш, масала, <code>*.wikipedia.org</code>.
+Лакхара даржан домен мукъа хила еза , масала<code>*.org</code><br />
+Ловш йолу {{PLURAL:$2|протокол|протоколаш}}: <code>$1</code> (Iад йитарца http://, протокол бакъалла язъен яцахь).',
+'linksearch-line' => '$2 — хьажораг кху $1',
 
 # Special:ListUsers
 'listusersfrom' => 'Гучé баха декъашхой, болалуш болу тӀера:',
@@ -1578,6 +1684,7 @@ PICT # тайп тайпан
 'listgrouprights-members' => '(тобан могlам)',
 
 # Email user
+'mailnologintext' => 'Электронан кехаташ кхехьийта йиш хилийта [[Special:UserLogin|системин чугӀо]] кхин декъашхошка хаамаш кхехьийта хьа [[Special:Preferences|гӀирса чохь]] бакъалла долу электронан почтан адрес хила деза.',
 'emailuser' => 'Декъашхочун хааман кехат',
 'emailuser-title-target' => 'Декъашхочунга кехат яздар',
 'emailuser-title-notarget' => 'Декъашхочунга кехат яздар',
@@ -1628,16 +1735,7 @@ PICT # тайп тайпан
 'delete-confirm' => '$1 — дӀаяккхар',
 'delete-legend' => 'ДӀаяккхар',
 'historywarning' => "'''Тергам:''' хӀокху агӀона герггарчу хьесапехь $1 {{PLURAL:$1|версеш|верси|верси}} ю:",
-'confirmdeletetext' => "<div id=\"confirmdeletetext\">
-Хьо гӀерта '''[[Википеди:АгӀонаш дӀаяхар|хӀара агӀо дӀаяккха]]'''; '''дехар до''', хьажа [[Special:Whatlinkshere/{{FULLPAGENAMEE}}|хьажориг юй кхузе хьажийна]], дӀаяккхале хьалха уьш нисйа деза.
-{{#switch:{{NAMESPACE}}|{{ns:File talk}}=
-<br />Хила мега, хӀара дийцаре агӀо
-{{#ifexist:Media:{{PAGENAME}}
-|{{#ifexist:File:{{PAGENAME}}|цигара файлан.|оц [[ВикидӀайуьллуче]]ра.}}
-|йоцуш йолу файлан]]
-}}
-}}
-</div>",
+'confirmdeletetext' => "Хьо гӀерта агӀо я файл дӀаяккха '''дехар до''', дӀаяккхале хьалха хьажа [[{{MediaWiki:Policy-url}}|кхуза]].",
 'actioncomplete' => 'Дешдерг кхочушди',
 'actionfailed' => 'Кхочушъ дина дац',
 'deletedtext' => '«$1» дӀаяккхина яра.
@@ -1729,6 +1827,7 @@ PICT # тайп тайпан
 'undeletebtn' => 'МеттахӀоттае',
 'undeletelink' => 'хьажа/меттахӀоттае',
 'undeleteviewlink' => 'хьажа',
+'undeletereset' => 'ЦӀанъян',
 'undeleteinvert' => 'Къастае массо',
 'undeletecomment' => 'Бахьан:',
 'undeletedrevisions' => '$1 {{PLURAL:$1|хийцамаш|хийцамаш|хийцамаш}} меттахӀоттайина',
@@ -1771,22 +1870,22 @@ PICT # тайп тайпан
 'sp-contributions-submit' => 'Лаха',
 
 # What links here
-'whatlinkshere' => 'Ð¥Ñ\8cажоÑ\80игаш кхузе',
-'whatlinkshere-title' => 'Ð\90гlонаÑ\88, Ñ\85Ñ\8cажоÑ\80игÑ\86а Ð¾Ñ\86Ñ\83 Â«$1»',
+'whatlinkshere' => 'Ð¥Ñ\8cажоÑ\80агаш кхузе',
+'whatlinkshere-title' => 'Ð¥Ó\80окÑ\85Ñ\83нÑ\86а Â«$1» Ð¹Ð¾Ð»Ñ\83 Ð°Ð³Ó\80онаÑ\88',
 'whatlinkshere-page' => 'Агlо:',
-'linkshere' => "Тlаьхьайогlу агlонаш хьажоригца ю оцу '''[[:$1]]''':",
-'nolinkshere' => "ХӀокху '''[[:$1]]''' агӀона тӀе кхечу агӀонашчохь хьажоригаш яц",
+'linkshere' => "ТӀаьхьайогӀу агӀонаш оцу '''[[:$1]]''': хьажорагца ю",
+'nolinkshere' => "ХӀокху '''[[:$1]]''' агӀона тӀе кхечу агӀонашкахь хьажорагаш яц.",
 'nolinkshere-ns' => "Хаьржинчу анахь яц '''[[:$1]]''' цӀе йолу агӀонаш",
 'isredirect' => 'агlо-дlасахьажайар',
 'istemplate' => 'лата йe',
-'isimage' => 'Ð\9eÑ\86Ñ\83 Ñ\81Ñ\83Ñ\8cÑ\80Ñ\82ан Ñ\85Ñ\8cажоÑ\80иг',
+'isimage' => 'Файлан Ñ\85Ñ\8cажоÑ\80аг',
 'whatlinkshere-prev' => '{{PLURAL:$1|хьалхайодарг|хьалхайодарш|хьалхайодарш}} $1',
 'whatlinkshere-next' => '{{PLURAL:$1|тlаьхьайогlург|тlаьхьайогlурш|тlаьхьайогlурш}} $1',
-'whatlinkshere-links' => 'â\86\90 Ñ\85Ñ\8cажоÑ\80игаш',
+'whatlinkshere-links' => 'â\86\90 Ñ\85Ñ\8cажоÑ\80агаш',
 'whatlinkshere-hideredirs' => '$1 дlасахьажйар',
 'whatlinkshere-hidetrans' => '$1 латораш',
-'whatlinkshere-hidelinks' => '$1 Ñ\85Ñ\8cажоÑ\80игаш',
-'whatlinkshere-hideimages' => '$1 Ñ\84айлаÑ\88ан Ñ\85Ñ\8cажоÑ\80игаш',
+'whatlinkshere-hidelinks' => '$1 Ñ\85Ñ\8cажоÑ\80агаш',
+'whatlinkshere-hideimages' => '$1 Ñ\84айлийн Ñ\85Ñ\8cажоÑ\80агаш',
 'whatlinkshere-filters' => 'Литтарш',
 
 # Block/unblock
@@ -1876,18 +1975,19 @@ PICT # тайп тайпан
 # Move page
 'move-page' => '$1 — цlе хийцар',
 'move-page-legend' => 'ЦӀe хийца яр',
-'movepagetext' => "Леладан лахар хатlаьхь, хьо агlон цlе хуьйцуш ву, цхьатерра дехьа а докхуш цуьнан хийцаман тептар.
-Тиша цlе хира ю дlасахьажйарехь керлачун тlе хьажийна.
-Хьега далур ду ша шех дlасахьажор керла яккхар, хьалхалерачуьна метта йиллина йолу.
-Нагахь ахьа иза цадинехь, дехар до, хьажа йуйла [[Special:DoubleRedirects|шалгlа]] а [[Special:BrokenRedirects|хадийначу дlасахьажориш]].
-Ахьа жоп лур ду кхин дlа а хьажориг хьажийна хилийта, хила йезаче.
-
-Тергамбеш хила, иза агlо '''хира яц''' цlе хийцина, нагахь иза цlе йолуш керла агlо йалахь, цхьа йолу хенахь, нагахь иза йалахь цхьан тlе хьажийна йа йаьсса а нисйарца истори йоцуш.
-Иза бохург ду, хьега хийцалур ю оцу агlон цlе оцу цlарца, хlинц цуьна хилла йолу, нагахь ахьа гlалатонца цlе хийцанехь, йолуш йолу агlо цахууш йа мега хьа.
-
-'''ДlАХЬЕДАР!'''
-Цlе хийцарца хила тарло барамашкахь а цамётту хийцам ''гlар йойлачу'' агlонашна.
-Дехар до, кхин дlа хьо вахале, дика ойла йе, хьо кхеташ хиларехь тlаьхьа хиндолучунах.",
+'movepagetext' => "Бухахь йолу форманца агӀон цӀе хийцало. Цул совнах цуьна хийцаман журнал кхоьчу метте доккха. Хьалхалера цӀарахь хиръю керла кхоьллина агӀонан хьажораг.
+
+Хьовсалаш [[Special:DoubleRedirects|шалха]] а [[Special:BrokenRedirects|йохна хьажорагаш]] юй техь аьлла.
+
+Шу жоьпехь ду хьажорагаш нийса некъ гойтуш хиларан.
+
+Тидам бе хьалхалера агӀон цӀе ‘’’хийцалур яц’’’ иштта цӀе йолу агӀо йолуш елахь. Юкъардаккхар: йолуш йолу агӀо кхоьчухьа хьажораг елахь, я еса елахь а, цуьна хьийцаме истори яцахь а.
+
+И бохург ду шун агӀонан цӀе юха а хьалха хилларгчунтӀе хийца йиш ю, амма йолуш йолу агӀо дӀаяккха йиш яц.
+
+'''ДӀАХЬЕДАР!'''
+
+ЦӀе хийцар бахьнехь гӀаръяьлла агӀонашна дукха дагахь боцу хийцамаш хила тарло. Цундела цӀе хийцале шеш хила тарлучу тӀехьонашах кхета аьлла тешна хила.",
 'movepagetext-noredirectfixer' => "Бухахь йолу форманца агӀон цӀе хийцало. Цул совнах цуьна хийцаман журнал кхоьчу метте доккха. Хьалхалера цӀарахь хиръю керла кхоьллина агӀонан хьажораг.
 
 Хьовсалаш [[Special:DoubleRedirects|шалха]] а [[Special:BrokenRedirects|йохна хьажорагаш]] юй техь аьлла.
@@ -1926,6 +2026,7 @@ PICT # тайп тайпан
 'movepage-page-moved' => 'АгӀона $1 цӀе хийцина оцу $2.',
 'movelogpage' => 'Цlераш хийцаран тептар',
 'movesubpage' => '{{PLURAL:$1|Бухара агӀо|Бухара агӀонаш}}',
+'movesubpagetext' => 'ХӀокху агӀона $1 {{PLURAL:$1|бухара агӀо ю|бухара агӀонаш ю}}.',
 'movenosubpage' => 'ХӀокху агӀона бухара агӀонаш яц.',
 'movereason' => 'Бахьан:',
 'revertmove' => 'юхаяккха',
@@ -1946,11 +2047,11 @@ PICT # тайп тайпан
 
 # Export
 'export' => 'АгӀонаш араяхар',
-'exporttext' => 'Шуьга далур ду кхечу меттера чудахарш, йоза а хийцаме тептарш билгалла йолу агlонаш йа гулдина йолу агlонаш хlокх XML барамца, йуха тlяхьа чура [[Special:Import|хьаэцалурдолш]] кхечу вики-хьалхен, болх беш йолу хlокху MediaWiki гlирсаца.
+'exporttext' => 'Шуьга далур ду кхечу меттера чудахарш, йоза а хийцаме тептарш билгалла йолу агӀонаш йа гулдина йолу агӀонаш хӀокх XML барамца, юха тӀяхьа чура [[Special:Import|хьаэцалурдолш]] кхечу вики-хьалхен, болх беш йолу хlокху MediaWiki гlирсаца.
 
-Кхечу меттера яззамаш чуйаха, чуязйе цlе редокхчу метте, цlхьа могlан цlе могlаршкахь, йуха харжа лаьи шуна Кхечу меттер чуйаха массо яззамашна истори хийцамбарш йа тlяхьаралера яззамна башхо.
+Кхечу меттера яззамаш чуйаха, чуязйе цӀе тадечу метте, цӀхьа могӀан цӀе могӀаршкахь, юха харжа лаьи шуна Кхечу меттер чуйаха массо яззамашна истори хийцамбарш йа тӀяхьаралера яззамна башхо.
 
-ШÑ\83Ñ\8cга ÐºÑ\85и Ð´Ð°Ð»Ð°Ð½Ð´ÐµÑ\80г, Ð»ÐµÐ»Ð°ÐµÑ\88 Ð¹Ð¾Ð»Ñ\83 Ð¼ÐµÑ\82Ñ\82иг ÐºÑ\8aаÑ\81Ñ\82аман Ð¼Ð°Ñ\88ан Ñ\85Ñ\8cажоÑ\80иг ÐºÑ\85еÑ\87Ñ\83 Ð¼ÐµÑ\82Ñ\82еÑ\80 Ñ\87Ñ\83даÑ\85а Ñ\82\8fÑ\85Ñ\8cаÑ\80леÑ\80а Ð±Ð°Ñ\88Ñ\85он Ñ\8fззамаÑ\88. Ð\9cаÑ\81Ñ\81ала Ð¾Ñ\86Ñ\83 Ñ\8fззамна [[{{MediaWiki:Mainpage}}]] Ñ\85lаÑ\80а Ñ\85иÑ\80а Ð¹Ñ\83 Ñ\85Ñ\8cажоÑ\80иг [[{{#Special:Export}}/{{MediaWiki:Mainpage}}]].',
+ШÑ\83Ñ\8cга ÐºÑ\85и Ð´Ð°Ð»Ð°Ð½Ð´ÐµÑ\80г, Ð»ÐµÐ»Ð°ÐµÑ\88 Ð¹Ð¾Ð»Ñ\83 Ð¼ÐµÑ\82Ñ\82иг ÐºÑ\8aаÑ\81Ñ\82аман Ð¼Ð°Ñ\88ан Ñ\85Ñ\8cажоÑ\80аг ÐºÑ\85еÑ\87Ñ\83 Ð¼ÐµÑ\82Ñ\82еÑ\80 Ñ\87Ñ\83даÑ\85а Ñ\82Ó\80аÑ\8cÑ\85Ñ\8cаÑ\80леÑ\80аÑ\87Ñ\83 Ð±Ð°Ñ\88Ñ\85он Ñ\8fззамаÑ\88. Ð\9cаÑ\81ала Ð¾Ñ\86Ñ\83 Ñ\8fззамна [[{{MediaWiki:Mainpage}}]] Ñ\85Ó\80аÑ\80а Ñ\85иÑ\80а Ñ\8e Ñ\85Ñ\8cажоÑ\80аг [[{{#Special:Export}}/{{MediaWiki:Mainpage}}]].',
 'exportcuronly' => 'Карара верси бен юкъа ма тоха, юзийна хьалхалерра истори йоцуш',
 'export-submit' => 'Экспорт ян',
 'export-addcattext' => 'ТӀетоха агӀонаш категори чура:',
@@ -1962,6 +2063,8 @@ PICT # тайп тайпан
 'allmessagesname' => 'Хаам',
 'allmessagesdefault' => 'Шаьшха йоза',
 'allmessagescurrent' => 'Карарчу хенан йоза',
+'allmessagestext' => 'ХӀара «MediaWiki» цӀерийн меттигера системан хаамийн могӀа бу.
+Хьайна MediaWiki тая лууш делахь, дехар до, проект [//translatewiki.net translatewiki.net] [//www.mediawiki.org/wiki/Localisation юьйцучу хьажа].',
 'allmessages-filter-legend' => 'Литтар',
 'allmessages-filter' => 'Литтар оцу хьола хийцамца:',
 'allmessages-filter-unmodified' => 'Хийцан йоцурш',
@@ -1969,7 +2072,7 @@ PICT # тайп тайпан
 'allmessages-filter-modified' => 'Хийцнарш',
 'allmessages-prefix' => 'Литтар оцу дешахьалхе:',
 'allmessages-language' => 'Мотт:',
-'allmessages-filter-submit' => 'Ð\94еÑ\85Ñ\8cа Ð²Ð°Ð»Ð°',
+'allmessages-filter-submit' => 'Ð\94еÑ\85Ñ\8cа Ð³Ó\80о',
 
 # Thumbnails
 'thumbnail-more' => 'Доккха де',
@@ -1980,6 +2083,7 @@ PICT # тайп тайпан
 'import-interwiki-source' => 'Вики-хьост/агlо:',
 'import-interwiki-templates' => 'Лата де массо кепаш',
 'import-upload-filename' => 'Файлан цӀе:',
+'import-comment' => 'Билгалдаккхар:',
 'importnosources' => 'Юкъаравики-импортан хьост хаьржина яцара, дуьхьала хийцамашан истори чуяккхар дӀадайина ду.',
 
 # Import log
@@ -2005,7 +2109,7 @@ PICT # тайп тайпан
 'tooltip-ca-watch' => 'Тlетоха хlара агlо сан тергаме могlам юкъа',
 'tooltip-ca-unwatch' => 'Дlайаккха хlара агlо хьай тергаме могlам юкъар',
 'tooltip-search' => 'Лаха иза дош',
-'tooltip-search-go' => 'Билгала и санна цlе йолучу агlон чу дехьа вала',
+'tooltip-search-go' => 'Билгала и санна цӀе йолучу агӀон чу дехьа гӀо',
 'tooltip-search-fulltext' => 'Лаха агlонаш ше чулацамехь хlара йоза долуш',
 'tooltip-p-logo' => 'Коьрта агӀона дехьа гӀо',
 'tooltip-n-mainpage' => 'Коьрта агӀона дехьа гӀо',
@@ -2024,7 +2128,7 @@ PICT # тайп тайпан
 'tooltip-t-upload' => 'Чуйаха файлаш',
 'tooltip-t-specialpages' => 'Белха агlонаши могlам',
 'tooltip-t-print' => 'Хlокху агlонна зорба туху башхо',
-'tooltip-t-permalink' => 'Ð\94аимна Ð¹Ð¾Ð»Ñ\83 Ñ\85Ñ\8cажоÑ\80иг Ñ\85lокÑ\85Ñ\83 Ð±Ð°Ñ\88Ñ\85а Ð°Ð³lонна',
+'tooltip-t-permalink' => 'Ð\94аима Ð¹Ð¾Ð»Ñ\83 Ñ\85Ñ\8cажоÑ\80аг Ñ\85Ó\80окÑ\85Ñ\83 Ð±Ð°Ñ\88Ñ\85а Ð°Ð³Ó\80онна',
 'tooltip-ca-nstab-main' => 'Яззамна чулацам',
 'tooltip-ca-nstab-user' => 'ХӀора декъашхочун долахь йолу агӀо ю',
 'tooltip-ca-nstab-media' => 'Медиа-файл',
@@ -2063,7 +2167,7 @@ PICT # тайп тайпан
 # Spam protection
 'spamprotectiontitle' => 'Совбиларна литтар',
 'spamprotectiontext' => 'Хьо дӀаязъян гӀерта агӀо спам-литтаро дӀакъоьвлина.
-ЦÑ\83на Ð±Ð°Ñ\85Ñ\8cна Ñ\85ила Ñ\82ам Ð±Ñ\83 Ð°Ð³Ó\80она Ñ\87оÑ\85Ñ\8c Ð·Ñ\83лам Ð»Ð¸Ñ\82Ñ\82аÑ\80ан Ñ\87Ñ\83Ñ\82оÑ\8cÑ\85на Ð¹Ð¾Ð»Ñ\83 Ñ\85Ñ\8cажоÑ\80иг хилар.',
+ЦÑ\83на Ð±Ð°Ñ\85Ñ\8cна Ñ\85ила Ñ\82ам Ð±Ñ\83 Ð°Ð³Ó\80она Ñ\87оÑ\85Ñ\8c Ð·Ñ\83лам Ð»Ð¸Ñ\82Ñ\82аÑ\80ан Ñ\87Ñ\83Ñ\82оÑ\8cÑ\85на Ð¹Ð¾Ð»Ñ\83 Ñ\85Ñ\8cажоÑ\80аг хилар.',
 
 # Info page
 'pageinfo-header-basic' => 'Коьрта хаам',
@@ -2103,21 +2207,28 @@ PICT # тайп тайпан
 'show-big-image-size' => '$1 × $2 пикселш',
 
 # Special:NewFiles
-'newimages' => 'Ð\9aеÑ\80лаÑ\87Ñ\83 Ñ\84айланÑ\88ан Ð³Ð°Ð»ÐµÑ\80ий',
+'newimages' => 'Ð\9aеÑ\80лаÑ\87Ñ\83 Ñ\84айлийн Ð³Ð°Ð»ÐµÑ\80ей',
 'newimages-summary' => 'ХӀокху белхан агӀона чохь гойтуш ю дукха хан йоццуш чуйаьхна файлаш.',
 'newimages-legend' => 'Литтар',
+'showhidebots' => '$1 шабелхалой',
 'ilsubmit' => 'Лаха',
 'sp-newimages-showfrom' => 'Гайта керла файлаш $2, $1 тӀера дуьйна',
 
 # Video information, used by Language::formatTimePeriod() to format lengths in the above messages
 'seconds-abbrev' => '$1оцу',
+'days' => '{{PLURAL:$1|$1 де}}',
+'ago' => '$1 хьалха',
+
+# Human-readable timestamps
+'hours-ago' => '$1 {{PLURAL:$1|сахьат}} хьалха',
+'yesterday-at' => 'селхана $1 даьлча',
 
 # Bad image list
 'bad_image_list' => 'Барам хила беза ишта:
 
-Лораш хира йу могlамяхь йолу хlумнаш (могlийн, йола луш йолу сабол тlира *).
\94Ñ\83Ñ\8cÑ\85Ñ\8cаÑ\80алеÑ\80а Ñ\85Ñ\8cажоÑ\80иг Ð¼Ð°Ð³lаÑ\80Ñ\88и Ñ\85ила Ð±ÐµÐ·Ð° Ñ\85Ñ\8cажоÑ\80иг кху цамагдо сурт дуьлаче.
lяхьа йогlуш йолу хьажориг оцу могlарехь хира йу магóш, билгалла аьлча яззамаш долуче, сурт хьаллаточехь.',
+Лораш хира ю могӀамяхь йолу хӀумнаш (могӀийн, йола луш йолу символ тӀира *).
\94Ñ\83Ñ\8cÑ\85Ñ\85Ñ\8cаÑ\80алеÑ\80а Ñ\85Ñ\8cажоÑ\80аг Ð¼Ð°Ð³Ó\80аÑ\80Ñ\88и Ñ\85ила Ð±ÐµÐ·Ð° Ñ\85Ñ\8cажоÑ\80аг кху цамагдо сурт дуьлаче.
Ӏяхьа йогӀуш йолу хьажораг оцу могӀарехь хира ю магóш, билгалла аьлча яззамаш долуче, сурт хьаллаточехь.',
 
 # Metadata
 'metadata' => 'Метахаамаш',
@@ -2168,6 +2279,7 @@ PICT # тайп тайпан
 'exif-gpsaltitude' => 'Локхалла',
 'exif-gpsdestlatitude' => 'Объектан дохалла',
 'exif-gpsdatestamp' => 'Терахь',
+'exif-jpegfilecomment' => 'JPEG-файлан билгалдаккхар',
 'exif-keywords' => 'Коьрта дешнаш',
 'exif-objectname' => 'Йоцца цӀе',
 'exif-specialinstructions' => 'Къаьсттина тӀехьажор',
@@ -2179,6 +2291,8 @@ PICT # тайп тайпан
 'exif-originaltransmissionref' => 'ДӀадолалун меттиган код',
 'exif-label' => 'Билгало',
 'exif-datetimemetadata' => 'ТӀехьара метахаамаш хийцина терахь',
+'exif-pngfilecomment' => 'PNG-файлан билгалдаккхар',
+'exif-giffilecomment' => 'GIF-файлан билгалдаккхар',
 
 # Exif attributes
 'exif-compression-1' => 'ТIеIовдан яц',
@@ -2281,11 +2395,17 @@ PICT # тайп тайпан
 'redirect' => 'Декъашхочун файлан тӀера дӀасхьажор',
 'redirect-legend' => 'Файлан я агӀона тӀера дӀасхьажор',
 'redirect-summary' => 'ХӀара агӀо лело йиш ю файлан я агӀона тӀера дӀасхьажош.',
+'redirect-submit' => 'Дехьа гӀо',
+'redirect-lookup' => 'Лаха:',
 'redirect-value' => 'МаьӀна:',
 'redirect-user' => 'Декъашхочун ID',
+'redirect-revision' => 'АгӀона верси',
+'redirect-file' => 'Файлан цӀе',
 
 # Special:FileDuplicateSearch
 'fileduplicatesearch' => 'Лаха цхьатера йолу файлаш',
+'fileduplicatesearch-summary' => 'Лаха цхьатера йолу файлаш хэш-кодаца.',
+'fileduplicatesearch-legend' => 'Цхьатера ерш лахар',
 'fileduplicatesearch-filename' => 'Файлан цӀе:',
 'fileduplicatesearch-submit' => 'Лаха',
 'fileduplicatesearch-info' => '$1 × $2 {{PLURAL:$2|пиксель|пикселш|пикселш}}<br />Файлан барам: $3<br />MIME-тайп: $4',
@@ -2349,7 +2469,7 @@ PICT # тайп тайпан
 'htmlform-selectorother-other' => 'Кхин',
 
 # New logging system
-'logentry-delete-delete' => '$1 {{GENDER:$2|дӀаякхина|дӀаякхина}} агӀо $3',
+'logentry-delete-delete' => '$1 {{GENDER:$2|дӀаяьккхина}} агӀо $3',
 'logentry-delete-restore' => '$1 {{GENDER:$2|меттахӀоттайина|меттахӀоттайина}} агӀо $3',
 'logentry-move-move' => '$1 {{GENDER:$2|цӀе хийцина|цӀе хийцина}} $3 оцу $4',
 'logentry-move-move-noredirect' => '$1 {{GENDER:$2|цӀе хийцина|цӀе хийцина}} $3 оцу $4 дӀасахьажийнарг цаюьтуш',
@@ -2357,6 +2477,7 @@ PICT # тайп тайпан
 'logentry-move-move_redir-noredirect' => '$1 {{GENDER:$2|цӀе хийцина|цӀе хийцина}} $3 оцу $4 дӀасахьажоран тӀохул а дӀасахьажийнарг цаюьтуш а',
 'logentry-newusers-newusers' => '{{GENDER:$2|ДӀавазвелла|ДӀаязелла}} керла декъашхо $1',
 'logentry-newusers-create' => '{{GENDER:$2|ДӀавазвелла|ДӀаязелла}} керла декъашхо $1',
+'logentry-newusers-autocreate' => 'Автоматически кхоьллина {{GENDER:$2|декъашхочун}} $1 дӀаяздар',
 'logentry-rights-rights' => '$1 {{GENDER:$2|хийцина}} хӀокхуна $3 бакъо $4 → $5',
 'logentry-rights-rights-legacy' => '$1 {{GENDER:$2|хийцина}} хӏокхуна $3 бакъо',
 'rightsnone' => '(яц)',
@@ -2367,6 +2488,9 @@ PICT # тайп тайпан
 # Search suggestions
 'searchsuggest-search' => 'Лаха',
 
+# Durations
+'duration-days' => '$1 {{PLURAL:$1|де}}',
+
 # Limit report
 'limitreport-title' => 'АгӀона хӀоттам къасторан хаамаш:',
 'limitreport-cputime' => 'Процессоран хан лелор',
index 3090f3f..d083eb5 100644 (file)
@@ -1182,7 +1182,7 @@ $1",
 'search-result-size' => '$1 ({{PLURAL:$2|یەک وشە|$2 وشە}})',
 'search-result-category-size' => '{{PLURAL:$1|١ ئەندام|$1 ئەندام}} ({{PLURAL:$2|١ ژێرپۆل|$2 ژێرپۆل}}, {{PLURAL:$3|١ پەڕگە|$3 پەڕگە}})',
 'search-result-score' => 'پەیوەندی: $1%',
-'search-redirect' => '(ئاڵوگۆڕ $1)',
+'search-redirect' => '(ڕەوانەکەر $1)',
 'search-section' => '(بەشی $1)',
 'search-suggest' => 'ئایا مەبەستت ئەمە بوو: $1',
 'search-interwiki-caption' => 'پرۆژە خوشکەکان',
@@ -2461,12 +2461,9 @@ $1',
 'ipb_blocked_as_range' => 'هەڵە: ئای‌پی $1 ڕاستەوخۆ بەربەست نەکراوە بۆیە ناکڕێت لە بەربەست لای‌ بەیت.
 ئەوە وەک بەشێک لە زنجیرە ئای‌پیی $2 بەربەست کراوە و هەر بەو شێوە دەکرێ لە بەربەست دەرچێ.',
 'ip_range_invalid' => 'زنجیرە ئای‌پی نەگونجاو.',
-'blockme' => 'بەربەست‌کردنی من',
 'proxyblocker' => 'بەربەست‌کەری پرۆکسی',
-'proxyblocker-disabled' => 'ئەم فەنکشێنە لەکار خستراوە.',
 'proxyblockreason' => 'ناونیشانی ئای‌پی تۆ بەربەست‌کراوە لەبەر ئەوەی پرۆکسیەکی کراوەیە.
 تکایە پەیوەندی بکە بە دابینکەری خزمەتی ئینتەرنەتی خۆت یان پاڵپشتی تەکنیکی و ئاگادریان کەوە لەو کێشە ئەمنیە گرینگە.',
-'proxyblocksuccess' => 'جێ‌بەجێ‌کرا.',
 'sorbsreason' => 'ناونیشانی ئای‌پی تۆ لە DNSBLدا کە {{SITENAME}} کەڵکی لێ‌وەر دەگرێ، وەک پرۆکسیەکی کراوە لیست کراوە.',
 'sorbs_create_account_reason' => 'ناونیشانی ئای‌پی تۆ لە DNSBLدا کە {{SITENAME}} کەڵکی لێ‌وەر دەگرێ، وەک پرۆکسیەکی کراوە لیست کراوە.
 بۆیە ناتوانی هەژمارە درووست‌بکەی.',
index a582b18..c07de24 100644 (file)
@@ -103,6 +103,7 @@ $dateFormats = array(
 
 $separatorTransformTable = array( ','  => '.', '.' => ',' );
 $linkTrail = '/^([a-zâçğıñöşüа-яё“»]+)(.*)$/sDu';
+$linkPrefixCharset = 'a-zâçğıñöşüA-ZÂÇĞİÑÖŞÜa-яёА-ЯЁ«„';
 
 $messages = array(
 # User preference toggles
@@ -230,8 +231,6 @@ $messages = array(
 'noindex-category' => 'Индекссиз саифелер',
 'broken-file-category' => 'Ичинде бозукъ файл багълантылары олгъан саифелер',
 
-'linkprefix' => '/^(.*?)([a-zâçğıñöşüA-ZÂÇĞİÑÖŞÜa-яёА-ЯЁ«„]+)$/sDu',
-
 'about' => 'Акъкъында',
 'article' => 'Саифе',
 'newwindow' => '(янъы бир пенджереде ачылыр)',
index bd6306f..6bb6a4c 100644 (file)
@@ -97,6 +97,7 @@ $dateFormats = array(
 
 $separatorTransformTable = array( ',' => '.', '.' => ',' );
 $linkTrail = '/^([a-zâçğıñöşüа-яё“»]+)(.*)$/sDu';
+$linkPrefixCharset = 'a-zâçğıñöşüA-ZÂÇĞİÑÖŞÜa-яёА-ЯЁ«„';
 
 $messages = array(
 # User preference toggles
@@ -224,8 +225,6 @@ $messages = array(
 'noindex-category' => 'İndekssiz saifeler',
 'broken-file-category' => 'İçinde bozuq fayl bağlantıları olğan saifeler',
 
-'linkprefix' => '/^(.*?)([a-zâçğıñöşüA-ZÂÇĞİÑÖŞÜa-яёА-ЯЁ«„]+)$/sDu',
-
 'about' => 'Aqqında',
 'article' => 'Saife',
 'newwindow' => '(yañı bir pencerede açılır)',
index 81ba616..55ad052 100644 (file)
@@ -1,5 +1,5 @@
 <?php
-/** Czech (česky)
+/** Czech (čeština)
  *
  * See MessagesQqq.php for message documentation incl. usage of parameters
  * To improve a translation please visit http://translatewiki.net
@@ -1317,15 +1317,15 @@ pokud nebyla nastavena další omezení.",
 * Nevhodné osobní údaje
 *: ''adresy bydliště a telefonní čísla, rodná čísla apod.''",
 'revdelete-legend' => 'Nastavit omezení k revizi',
-'revdelete-hide-text' => 'Skrýt text revize',
+'revdelete-hide-text' => 'Text revize',
 'revdelete-hide-image' => 'Skrýt obsah souboru',
 'revdelete-hide-name' => 'Skrýt událost a cíl',
-'revdelete-hide-comment' => 'Skrýt editační komentář',
-'revdelete-hide-user' => 'Skrýt uživatelské jméno/IP adresu',
+'revdelete-hide-comment' => 'Editační komentář',
+'revdelete-hide-user' => 'Uživatelské jméno / IP adresa',
 'revdelete-hide-restricted' => 'Utajit data i před správci',
 'revdelete-radio-same' => '(neměnit)',
-'revdelete-radio-set' => 'Ano',
-'revdelete-radio-unset' => 'Ne',
+'revdelete-radio-set' => 'Viditelný',
+'revdelete-radio-unset' => 'Skrytý',
 'revdelete-suppress' => 'Utajit data i před správci',
 'revdelete-unsuppress' => 'Odstranit omezení na vrácené verze',
 'revdelete-log' => 'Důvod:',
@@ -2857,11 +2857,8 @@ Vizte též [[Special:BlockList|seznam všech probíhajících bloků]].',
 'ipb_blocked_as_range' => 'Chyba: IP adresa $1 není blokována přímo a tak ji nelze odblokovat. Je částí zablokovaného rozsahu $2, který může být odblokován.',
 'ip_range_invalid' => 'Neplatný IP rozsah.',
 'ip_range_toolarge' => 'Blokování rozsahů větších než /$1 není dovoleno.',
-'blockme' => 'Zablokuj mě',
 'proxyblocker' => 'Blokování proxy serverů',
-'proxyblocker-disabled' => 'Tato funkce je vypnuta.',
 'proxyblockreason' => 'Vaše IP adresa byla zablokována, protože funguje jako otevřený proxy server. Kontaktujte svého poskytovatele internetového připojení nebo technickou podporu a informujte je o tomto vážném bezpečnostním problému.',
-'proxyblocksuccess' => 'Hotovo.',
 'sorbsreason' => 'Vaše IP adresa je uvedena na seznamu DNSBL jako otevřený proxy server.',
 'sorbs_create_account_reason' => 'Vaše IP adresa je uvedena na seznamu DNSBL jako otevřený proxy server. Z této adresy si nemůžete založit účet',
 'xffblockreason' => 'IP adresa uvedená v hlavičce X-Forwarded-For, ať už vaše, nebo patřící proxy serveru, který používáte, byla zablokována. Zdůvodnění tohoto zablokování: $1',
@@ -3215,6 +3212,8 @@ Uložte jej na svůj disk a nahrajte ho sem.',
 'spam_reverting' => 'Revert na poslední verzi neobsahující odkazy na $1',
 'spam_blanking' => 'Všechny verze obsahovaly odkazy na $1, vyprázdněno',
 'spam_deleting' => 'Všechny verze obsahovaly odkazy na $1, smazáno',
+'simpleantispam-label' => "Antispamová kontrola.
+'''NEVYPLŇUJTE''' následující!",
 
 # Info page
 'pageinfo-title' => 'Informace o stránce „$1“',
@@ -3880,7 +3879,7 @@ Opravdu si přejete znovu tuto stránku založit?',
 
 # Separators for various lists, etc.
 'ellipsis' => '…',
-'percent' => '$1&nbsp;%',
+'percent' => '$1&#160;%',
 
 # Multipage image navigation
 'imgmultipageprev' => '← předchozí stránka',
@@ -4045,7 +4044,7 @@ MediaWiki je distribuována v naději, že bude užitečná, avšak BEZ JAKÉKOL
 # Special:Redirect
 'redirect' => 'Přesměrování podle souboru, uživatele nebo ID revize',
 'redirect-legend' => 'Přesměrování na soubor či stránku',
-'redirect-summary' => 'Tato speciální stránka přesměrovává na soubor (podle názvu), stránku (podle ID revize) nebo uživatele (podle číselného uživatelského ID).',
+'redirect-summary' => 'Tato speciální stránka přesměrovává na soubor (podle názvu), stránku (podle ID revize) nebo uživatele (podle číselného uživatelského ID). Použití: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/revision/328429]] nebo [[{{#Special:Redirect}}/user/101]].',
 'redirect-submit' => 'Přejít',
 'redirect-lookup' => 'Najít:',
 'redirect-value' => 'Hodnota:',
index 67b8657..2d2d4bf 100644 (file)
@@ -1041,7 +1041,6 @@ Biéj do [[Special:BlockList|lëstë zascëgónëch adresów IP]] abë òbaczëc
 'blocklogentry' => 'zablokòwôł [[$1]], czas blokadë: $2 $3',
 'unblocklogentry' => 'òdblokòwôł $1',
 'block-log-flags-nocreate' => 'blokada ùsôdzaniô kònta',
-'proxyblocksuccess' => 'Fertich.',
 
 # Developer tools
 'lockbtn' => 'Zascëgôj bazã pòdôwków',
index e9c7e05..706051c 100644 (file)
@@ -97,6 +97,7 @@ $dateFormats = array(
 );
 
 $linkTrail = '/^([a-zабвгдеєжѕзїіıићклмнопсстѹфхѡѿцчшщъыьѣюѥѧѩѫѭѯѱѳѷѵґѓђёјйљњќуўџэ҄я“»]+)(.*)$/sDu';
+$linkPrefixCharset = '„«';
 
 $messages = array(
 # User preference toggles
@@ -180,8 +181,6 @@ $messages = array(
 'category-subcat-count' => '{{PLURAL:$2|Сѥи катигорїи тъкъмо сꙗ подъкатигорїꙗ ѥстъ|Сѥи катигорїи {{PLURAL:$1|ѥдина подъкатигорїꙗ ѥстъ|2 подъкатигорїи ѥстє|$1 подъкатигорїѩ сѫтъ}} · а вьсѩ жє подъкатигорїѩ число $2 ѥстъ}}',
 'listingcontinuesabbrev' => '· вѧщє',
 
-'linkprefix' => '/^(.*?)(„|«)$/sD',
-
 'about' => 'опьсаниѥ',
 'article' => 'члѣнъ',
 'newwindow' => '(иномь окънѣ)',
index ef5f4f1..7a93e72 100644 (file)
@@ -42,6 +42,7 @@ $namespaceGenderAliases = array();
 
 $linkPrefixExtension = true;
 $linkTrail = '/^([a-zа-яĕçăӳ"»]+)(.*)$/sDu';
+$linkPrefixCharset = 'a-zA-Z"\\x{80}-\\x{10ffff}';
 
 $messages = array(
 # User preference toggles
@@ -153,8 +154,6 @@ $messages = array(
 'category-file-count-limited' => 'Ку категоринче $1 файл.',
 'listingcontinuesabbrev' => '(малалли)',
 
-'linkprefix' => '/^(.*?)([a-zA-Z\\x80-\\xff«"]+)$/sD',
-
 'about' => 'Ăнлантаркăч',
 'article' => 'Статья',
 'newwindow' => '(çĕнĕ чӳречере)',
index 5ffe6a2..5d564fb 100644 (file)
@@ -159,7 +159,7 @@ $messages = array(
 'tog-diffonly' => "Peidio â dangos cynnwys y dudalen islaw'r gymhariaeth ar dudalennau cymharu",
 'tog-showhiddencats' => 'Dangos categorïau cuddiedig',
 'tog-norollbackdiff' => 'Hepgor dangos cymhariaeth ar ôl gwrthdroi golygiad',
-'tog-useeditwarning' => "Tynnwch fy sylw pan wyf ar fin gadael tudalen olygu heb roi'r newidiadau ar gadw",
+'tog-useeditwarning' => "Tynnu fy sylw pan wyf ar fin gadael tudalen olygu heb roi'r newidiadau ar gadw",
 'tog-prefershttps' => 'Defnyddio cysylltiad diogel bob amser tra fy mod wedi mewngofnodi',
 
 'underline-always' => 'Bob amser',
@@ -301,7 +301,7 @@ $messages = array(
 'tagline' => 'Oddi ar {{SITENAME}}',
 'help' => 'Cymorth',
 'search' => 'Chwilio',
-'searchbutton' => 'Chwilio',
+'searchbutton' => 'Chwilier',
 'go' => 'Eler',
 'searcharticle' => 'Mynd',
 'history' => 'Hanes y dudalen',
@@ -334,7 +334,7 @@ $messages = array(
 'articlepage' => 'Dangos tudalen bwnc',
 'talk' => 'Sgwrs',
 'views' => 'Golygon',
-'toolbox' => 'Blwch offer',
+'toolbox' => 'Offer',
 'userpage' => 'Gweld tudalen y defnyddiwr',
 'projectpage' => 'Gweld tudalen y wici',
 'imagepage' => 'Gweld tudalen y ffeil',
@@ -399,7 +399,7 @@ $1',
 'youhavenewmessagesmulti' => 'Mae negeseuon newydd gennych ar $1',
 'editsection' => 'golygu',
 'editold' => 'golygu',
-'viewsourceold' => 'dangos y tarddiad',
+'viewsourceold' => 'dangos côd y dudalen',
 'editlink' => 'golygu',
 'viewsourcelink' => 'dangos côd y dudalen',
 'editsectionhint' => "Golygu'r adran: $1",
@@ -558,16 +558,16 @@ Sylwer y bydd rhai tudalennau yn parhau i ymddangos fel ag yr oeddent pan oeddec
 'loginprompt' => "Mae'n rhaid galluogi cwcis er mwyn mewngofnodi i {{SITENAME}}.",
 'userlogin' => 'Mewngofnodi / creu cyfrif',
 'userloginnocreate' => 'Mewngofnodi',
-'logout' => 'Allgofnodi',
+'logout' => 'Allgofnoder',
 'userlogout' => 'Allgofnodi',
 'notloggedin' => 'Nid ydych wedi mewngofnodi',
 'userlogin-noaccount' => 'Dim cyfrif gennych?',
 'userlogin-joinproject' => 'Ymuno â {{SITENAME}}',
-'nologin' => "Dim cyfrif gennych? '''$1'''.",
+'nologin' => 'Dim cyfrif gennych? $1.',
 'nologinlink' => 'Crëwch gyfrif',
 'createaccount' => 'Creu cyfrif newydd',
-'gotaccount' => "Oes cyfrif gennych eisoes? '''$1'''.",
-'gotaccountlink' => 'Mewngofnodwch',
+'gotaccount' => 'Oes cyfrif gennych eisoes? $1.',
+'gotaccountlink' => 'Mewngofnodi',
 'userlogin-resetlink' => 'Ydych chi wedi anghofio eich manylion mewngofnodi?',
 'userlogin-resetpassword-link' => 'Ailosod eich cyfrinair',
 'helplogin-url' => 'Help:Mewngofnodi',
@@ -629,7 +629,7 @@ Os mai rhywun arall a holodd am y cyfrinair, ynteu eich bod wedi cofio\'r hen gy
 'passwordsent' => 'Mae cyfrinair newydd wedi\'i ddanfon at gyfeiriad e-bost cofrestredig "$1". Mewngofnodwch eto ar ôl i chi dderbyn y cyfrinair, os gwelwch yn dda.',
 'blocked-mailpassword' => 'Gan fod eich cyfeiriad IP wedi ei atal rhag golygu, ni ellir adfer y cyfrinair.',
 'eauthentsent' => 'Anfonwyd e-bost o gadarnhâd at y cyfeiriad a benwyd.
-Cyn y gellir anfon unrhywbeth arall at y cyfeiriad hwnnw rhaid i chi ddilyn y cyfarwyddiadau yn yr e-bost hwnnw er mwyn cadarnhau bod y cyfeiriad yn un dilys.',
+Cyn y gellir anfon unrhywbeth arall at y cyfeiriad hwnnw rhaid i chi ddilyn y cyfarwyddiadau yn yr e-bost er mwyn cadarnhau mai chi sydd berchen y cyfeiriad hwnnw.',
 'throttled-mailpassword' => "Anfonwyd e-bost atoch eisoes i'ch atgoffa o'ch cyfrinair, a hynny yn ystod y $1 {{PLURAL:$1|awr}} diwethaf.
 Er mwyn rhwystro camddefnydd, dim ond un e-bost i'ch atgoffa o'ch cyfrinair gaiff ei anfon bob yn $1 {{PLURAL:$1|awr}}.",
 'mailerror' => 'Gwall wrth ddanfon yr e-bost: $1',
@@ -722,7 +722,7 @@ Y cyfrinair dros dro: $2",
 'changeemail-none' => '(dim)',
 'changeemail-password' => 'Eich cyfrinair ar {{SITENAME}}:',
 'changeemail-submit' => 'Newidier y cyfeiriad e-bost',
-'changeemail-cancel' => 'Dileer',
+'changeemail-cancel' => 'Diddymer',
 
 # Special:ResetTokens
 'resettokens' => 'Ailosod tocynnau',
@@ -898,7 +898,7 @@ Cynigiodd y gweinyddwr a glodd y gronfa ddata y rheswm hwn dros ei chloi: $1",
 Dyma'r cofnod lòg diweddaraf, er gwybodaeth:",
 'semiprotectedpagewarning' => "'''Sylwer:''' Mae'r dudalen hon wedi ei chloi; dim ond defnyddwyr cofrestredig a allant ei golygu.
 Dyma'r cofnod lòg diweddaraf, er gwybodaeth:",
-'cascadeprotectedwarning' => "'''Dalier sylw:''' Mae'r dudalen hon wedi ei diogelu fel nad ond defnyddwyr â galluoedd gweinyddwyr sy'n gallu ei newid, oherwydd ei bod yn rhan o'r {{PLURAL:$1|dudalen ganlynol|dudalen ganlynol|tudalennau canlynol|tudalennau canlynol|tudalennau canlynol|tudalennau canlynol}} sydd wedi {{PLURAL:$1|ei|ei|eu|eu|eu|eu}} sgydol-ddiogelu.",
+'cascadeprotectedwarning' => "'''Dalier sylw:''' Mae'r dudalen hon wedi ei diogelu fel nad ond defnyddwyr â galluoedd gweinyddwyr sy'n gallu ei newid, oherwydd ei bod yn rhan o'r {{PLURAL:$1|dudalen ganlynol|dudalen ganlynol|tudalennau canlynol}} sydd wedi {{PLURAL:$1|ei sgydol-ddiogelu|ei sgydol-ddiogelu|eu sgydol-diogelu}}.",
 'titleprotectedwarning' => "'''RHYBUDD:  Mae'r dudalen hon wedi ei chloi; dim ond rhai defnyddwyr sydd â'r [[Special:ListGroupRights|gallu]] i'w chreu.'''
 Dyma'r cofnod lòg diweddaraf, er gwybodaeth:",
 'templatesused' => 'Defnyddir y {{PLURAL:$1|nodyn hwn|nodyn hwn|nodiadau hyn|nodiadau hyn|nodiadau hyn|nodiadau hyn}} yn y dudalen hon:',
@@ -1062,15 +1062,15 @@ Fe fydd gweinyddwyr eraill {{SITENAME}} o hyd yn gallu gweld yr hyn a guddiwyd.
 * Gwybodaeth bersonol anaddas
 *: ''cyfeiriad cartref, rhif ffôn, rhif yswiriant cenedlaethol, ayb.''",
 'revdelete-legend' => 'Gosod cyfyngiadau ar y gallu i weld',
-'revdelete-hide-text' => 'Cuddio testun y diwygiad',
+'revdelete-hide-text' => 'Testun y diwygiad',
 'revdelete-hide-image' => 'Cuddio cynnwys y ffeil',
 'revdelete-hide-name' => "Cuddio'r weithred a'r targed",
-'revdelete-hide-comment' => 'Cuddio sylwad golygu',
-'revdelete-hide-user' => 'Cuddio enw defnyddiwr/IP y golygydd',
+'revdelete-hide-comment' => 'Sylw golygu',
+'revdelete-hide-user' => 'Enw defnyddiwr/IP y golygydd',
 'revdelete-hide-restricted' => 'Gosod y cyfyngiadau gweld data ar weinyddwyr yn ogystal ag eraill',
 'revdelete-radio-same' => '(peidier â newid)',
-'revdelete-radio-set' => 'Cuddier',
-'revdelete-radio-unset' => 'Na chuddier',
+'revdelete-radio-set' => 'Gweladwy',
+'revdelete-radio-unset' => 'Cudd',
 'revdelete-suppress' => 'Atal data oddi wrth Weinyddwyr yn ogystal ag eraill',
 'revdelete-unsuppress' => "Tynnu'r cyfyngiadau ar y golygiadau a adferwyd",
 'revdelete-log' => 'Rheswm:',
@@ -2611,11 +2611,8 @@ Gallwch weld rhestr y rhwystrau a'r gwaharddiadau sydd yn weithredol ar hyn o br
 'ipb_blocked_as_range' => "Gwall: Nid yw'r cyfeiriad IP $1 wedi'n rwystro'n uniongyrchol ac felly ni ellir ei ddadrwystro. Wedi dweud hynny, y mae'n rhan o'r amrediad $2 sydd wedi'i rwystro; gellir dadrwystro'r amrediad.",
 'ip_range_invalid' => 'Ystod IP annilys.',
 'ip_range_toolarge' => "Ni chaniateir rhwystrau ystod sy'n fwy na /$1.",
-'blockme' => 'Rhwystro fi',
 'proxyblocker' => 'Dirprwy-flociwr',
-'proxyblocker-disabled' => 'Analluogwyd y swyddogaeth hon.',
 'proxyblockreason' => "Mae eich cyfeiriad IP wedi'i rwystro gan ei fod yn ddirprwy agored. Cysylltwch â'ch Cyflenwr Gwasanaeth Rhyngrwyd neu gymorth technegol er mwyn eu hysbysu am y broblem ddiogelwch ddifrifol yma.",
-'proxyblocksuccess' => 'Gwnaethpwyd.',
 'sorbsreason' => 'Mae eich cyfeiriad IP wedi cael ei osod ymhlith y dirprwyon agored ar y Rhestr DNS Gwaharddedig a ddefnyddir gan {{SITENAME}}.',
 'sorbs_create_account_reason' => 'Mae eich cyfeiriad IP wedi cael ei osod ymhlith y dirprwyon agored ar y Rhestr DNS Gwaharddedig a ddefnyddir gan {{SITENAME}}.
 Ni allwch greu cyfrif',
@@ -2960,6 +2957,8 @@ Achos hyn yn fwy na thebyg yw presenoldeb cysylltiad i wefan ar y rhestr wahardd
 'spam_reverting' => 'Wedi adfer y diwygiad diweddaraf na sydd yn cynnwys cysylltiadau i $1',
 'spam_blanking' => 'Roedd cysylltiadau i $1 gan bob golygiad, felly gwacawyd y dudalen',
 'spam_deleting' => 'Roedd pob diwygiad yn cynnwys cysylltiadau â $1, felly fe ddilëwyd y dudalen',
+'simpleantispam-label' => "Prawf gwrth-sbam.
+'''Peidiwch''' â llenwi hwn!",
 
 # Info page
 'pageinfo-title' => 'Manylion "$1"',
@@ -3717,7 +3716,9 @@ Dylech fod wedi derbyn [{{SERVER}}{{SCRIPTPATH}}/COPYING gopi o GNU General Publ
 # Special:Redirect
 'redirect' => 'Ailgyfeirio yn ôl enw ffeil, ID defnyddiwr neu ID diwygiad tudalen',
 'redirect-legend' => 'Ailgyfeirio i ffeil neu dudalen',
-'redirect-summary' => "Mae'r dudalen arbennig hon yn arwain at ffeil (o roi enw'r ffeil), at dudalen (o roi ID rhyw ddidwygiad o'r dudalen), neu at dudalen defnyddiwr (o roi rhif y defnyddiwr).",
+'redirect-summary' => "Mae'r dudalen arbennig hon yn ailgyfeirio at ffeil (o roi enw'r ffeil), at dudalen (o roi ID rhyw ddiwygiad o'r dudalen), neu at dudalen defnyddiwr (o roi rhif ID y defnyddiwr).
+Defnydd: 
+[[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/revision/328429]], neu [[{{#Special:Redirect}}/user/101]].",
 'redirect-submit' => 'Ati',
 'redirect-lookup' => 'Chwilio drwy:',
 'redirect-value' => 'Chwilio am:',
index 04e3d8e..162fee0 100644 (file)
@@ -35,6 +35,7 @@
  * @author Morten LJ
  * @author Najami
  * @author Nghtwlkr
+ * @author Overlaet
  * @author Palnatoke
  * @author Peter Alberti
  * @author Peter Andersen
@@ -219,7 +220,7 @@ $messages = array(
 'tog-enotifwatchlistpages' => 'Send mig en e-mail ved ændringer til en side eller fil på min overvågningsliste',
 'tog-enotifusertalkpages' => 'Send mig en e-mail når min brugerdiskussionsside ændres',
 'tog-enotifminoredits' => 'Send mig også en e-mail ved mindre ændringer af sider og filer på min overvågningsliste',
-'tog-enotifrevealaddr' => 'Vis min e-mail-adresse i mails med besked om ændringer',
+'tog-enotifrevealaddr' => 'Vis min e-mailadresse i e-mails med besked om ændringer',
 'tog-shownumberswatching' => 'Vis antal brugere, der overvåger',
 'tog-oldsig' => 'Nuværende signatur:',
 'tog-fancysig' => 'Behandl signatur som wikitekst uden automatisk henvisning',
@@ -361,7 +362,7 @@ $messages = array(
 'vector-action-move' => 'Flyt',
 'vector-action-protect' => 'Beskyt',
 'vector-action-undelete' => 'Gendan',
-'vector-action-unprotect' => 'Ændr beskyttelse',
+'vector-action-unprotect' => 'Ændre beskyttelse',
 'vector-simplesearch-preference' => 'Aktivér forenklet søgefelt (kun Vector-udseendet)',
 'vector-view-create' => 'Opret',
 'vector-view-edit' => 'Redigér',
@@ -383,7 +384,7 @@ $messages = array(
 'searcharticle' => 'Gå til',
 'history' => 'Historik',
 'history_short' => 'Historik',
-'updatedmarker' => '(ændret)',
+'updatedmarker' => 'opdateret siden sidste besøg',
 'printableversion' => 'Udskriftsvenlig udgave',
 'permalink' => 'Permanent henvisning',
 'print' => 'Udskriv',
@@ -398,10 +399,10 @@ $messages = array(
 'undelete_short' => 'Fortryd sletning af {{PLURAL:$1|én version|$1 versioner}}',
 'viewdeleted_short' => 'Vis {{PLURAL:$1|en slettet redigering|$1 slettede redigeringer}}',
 'protect' => 'Beskyt',
-'protect_change' => 'ændr',
+'protect_change' => 'ændre',
 'protectthispage' => 'Beskyt side',
-'unprotect' => 'Ændr beskyttelse',
-'unprotectthispage' => 'Ændr beskyttelsen af denne side',
+'unprotect' => 'Ændre beskyttelse',
+'unprotectthispage' => 'Ændre beskyttelsen af denne side',
 'newpage' => 'Ny side',
 'talkpage' => 'Diskussion',
 'talkpagelinktext' => 'diskussion',
@@ -532,9 +533,9 @@ Dette kan indikere en fejl i softwaren.',
 'laggedslavemode' => "'''Bemærk:''' Den viste side indeholder muligvis ikke de nyeste ændringer.",
 'readonly' => 'Databasen er skrivebeskyttet',
 'enterlockreason' => 'Skriv en begrundelse for skrivebeskyttelsen, med samt en vurdering af, hvornår skrivebeskyttelsen ophæves igen',
-'readonlytext' => 'Databasen er midlertidigt skrivebeskyttet. Forsøg venligst senere.
+'readonlytext' => 'Databasen er i øjeblikket låst for nye poster og andre ændringer, sandsynligvis for rutinemæssig databasevedligeholdelse, hvorefter den vil være tilbage til normal.
 
-Årsag til spærringen: $1',
+Den administrator som har låst, gav denne forklaring: $1',
 'missing-article' => 'Databasen burde indeholde siden "$1" $2, men det gør den ikke.
 
 Den sandsynlige årsag er at du har fulgt et forældet link til en forskel eller en gammel version af en side der er blevet slettet.
@@ -590,10 +591,10 @@ $2',
 'customjsprotected' => 'Du har ikke tilladelse til at redigere denne JavaScript-side, da den indeholder en anden brugers personlige indstillinger.',
 'mycustomcssprotected' => 'Du har ikke rettigheder til at redigere denne CSS-side.',
 'mycustomjsprotected' => 'Du har ikke rettigheder til at redigere denne JavaScript-side.',
-'myprivateinfoprotected' => 'Du har ikke tilladelse til at redigere dine private oplysninger.',
-'mypreferencesprotected' => 'Du har ikke tilladelse til at redigere dine præferencer.',
+'myprivateinfoprotected' => 'Du har ikke rettigheder til at redigere dine private oplysninger.',
+'mypreferencesprotected' => 'Du har ikke rettigheder til at redigere dine indstillinger.',
 'ns-specialprotected' => 'Sider i navnerummet {{ns:special}} kan ikke redigeres.',
-'titleprotected' => "Dette sidenavn er beskyttet mod oprettelse af [[User:$1|$1]]. Begrundelsen for beskyttelsen er ''$2''.",
+'titleprotected' => 'Dette sidenavn er blevet beskyttet mod oprettelse af [[User:$1|$1]]. Begrundelsen for beskyttelsen er "\'\'$2\'\'".',
 'filereadonlyerror' => 'Ude af stand til at redigere filen "$1", fordi fildatabasen "$2" er skrivebeskyttet.
 
 Administratoren, som skrivebeskyttede den, gav følgende begrundelse: "$3".',
@@ -621,10 +622,10 @@ Glem ikke at ændre dine [[Special:Preferences|{{SITENAME}} indstillinger]].',
 'yourpassword' => 'Din adgangskode:',
 'userlogin-yourpassword' => 'Adgangskode',
 'userlogin-yourpassword-ph' => 'Indtast din adgangskode',
-'createacct-yourpassword-ph' => 'Indtast kodeord',
+'createacct-yourpassword-ph' => 'Indtast en adgangskode',
 'yourpasswordagain' => 'Gentag adgangskode',
-'createacct-yourpasswordagain' => 'Bekræft kodeord',
-'createacct-yourpasswordagain-ph' => 'Indtast kodeord igen',
+'createacct-yourpasswordagain' => 'Bekræft adgangskode',
+'createacct-yourpasswordagain-ph' => 'Indtast adgangskode igen',
 'remembermypassword' => 'Husk mit brugernavn i denne browser (højst $1 {{PLURAL:$1|dag|dage}})',
 'userlogin-remembermypassword' => 'Husk mig',
 'userlogin-signwithsecure' => 'Brug sikker forbindelse',
@@ -655,19 +656,19 @@ Brug formularen nedenfor til at logge på som en anden bruger.',
 'userlogin-createanother' => 'Opret en anden konto',
 'createacct-join' => 'Indtast dine oplysninger nedenfor.',
 'createacct-another-join' => 'Angiv den nye kontos oplysninger nedenfor.',
-'createacct-emailrequired' => 'Mailadresse',
-'createacct-emailoptional' => 'Mailadresse (valgfri)',
-'createacct-email-ph' => 'Indtast din mailadresse',
-'createacct-another-email-ph' => 'Indtast e-mail-adresse',
+'createacct-emailrequired' => 'E-mailadresse',
+'createacct-emailoptional' => 'E-mailadresse (valgfri)',
+'createacct-email-ph' => 'Indtast din e-mailadresse',
+'createacct-another-email-ph' => 'Indtast e-mailadresse',
 'createaccountmail' => 'Brug en midlertidig tilfældig adgangskode og send den til den angivne e-mailadresse',
-'createacct-realname' => 'Dit rigtige navn',
+'createacct-realname' => 'Dit rigtige navn (valgfrit)',
 'createaccountreason' => 'Begrundelse:',
 'createacct-reason' => 'Årsag',
-'createacct-reason-ph' => 'Hvorfor vil du oprette endnu en konto',
+'createacct-reason-ph' => 'Hvorfor du vil oprette endnu en konto',
 'createacct-captcha' => 'Sikkerhedskontrol',
 'createacct-imgcaptcha-ph' => 'Indtast venligst ovenstående tekst',
 'createacct-submit' => 'Opret din konto',
-'createacct-another-submit' => 'Oprette en anden konto',
+'createacct-another-submit' => 'Opret en anden konto',
 'createacct-benefit-heading' => '{{SITENAME}} laves af mennesker som dig.',
 'createacct-benefit-body1' => '{{PLURAL:$1|redigering|redigeringer}}',
 'createacct-benefit-body2' => '{{PLURAL:$1|side|sider}}',
@@ -678,7 +679,7 @@ Vælg venligst et andet brugernavn.',
 'loginerror' => 'Logon mislykket',
 'createacct-error' => 'Fejl ved kontooprettelse',
 'createaccounterror' => 'Kunne ikke oprette brugerkonto: $1',
-'nocookiesnew' => 'Din brugerkonto er nu oprettet, men du er ikke logget på. {{SITENAME}} bruger cookies til at logge brugere på. Du har slået cookies fra. Vær venlig at slå cookies til, og derefter kan du logge på med dit nye brugernavn og kodeord.',
+'nocookiesnew' => 'Din brugerkonto er nu oprettet, men du er ikke logget på. {{SITENAME}} bruger cookies til at logge brugere på. Du har slået cookies fra. Vær venlig at slå cookies til, og derefter kan du logge på med dit nye brugernavn og adgangskode.',
 'nocookieslogin' => '{{SITENAME}} bruger cookies til at logge brugere på. Du har slået cookies fra. Slå dem venligst til og prøv igen.',
 'nocookiesfornew' => 'Denne brugerkonto er ikke oprettet, da vi ikke kunne bekræfte dens kilde.
 Sørg for, at du har aktivereret cookies, genindlæs siden og prøv igen.',
@@ -690,47 +691,47 @@ Der skelnes mellem store og bogstaver i brugernavne.
 Kontrollér stavemåden, eller [[Special:UserLogin/signup|opret en ny konto]].',
 'nosuchusershort' => 'Der er ingen bruger ved navn "$1". Tjek din stavning.',
 'nouserspecified' => 'Angiv venligst et brugernavn.',
-'login-userblocked' => 'Denne bruger er blokeret. Login er ikke tilladt',
+'login-userblocked' => 'Denne bruger er blokeret. Det er ikke tilladt at logge på.',
 'wrongpassword' => 'Den indtastede adgangskode var forkert. Prøv igen.',
-'wrongpasswordempty' => 'Du glemte at indtaste password. Prøv igen.',
+'wrongpasswordempty' => 'Du glemte at indtaste adgangskode. Prøv igen.',
 'passwordtooshort' => 'Adgangskoden skal mindst være på $1 {{PLURAL:$1|tegn|tegn}}.',
-'password-name-match' => 'Kodeordet må ikke være det samme som brugernavnet.',
+'password-name-match' => 'Adgangskoden må ikke være det samme som brugernavnet.',
 'password-login-forbidden' => 'Brugen af dette brugernavn og adgangskode er blevet forbudt.',
-'mailmypassword' => 'Send nyt password',
-'passwordremindertitle' => 'Nyt password til {{SITENAME}}',
+'mailmypassword' => 'Send ny adgangskode',
+'passwordremindertitle' => 'Ny midlertidig adgangskode til {{SITENAME}}',
 'passwordremindertext' => 'Nogen (sandsynligvis dig, fra IP-adressen $1)
 har bedt om at vi sender dig en ny adgangskode til at logge på {{SITENAME}} ($4).
 En midlertidig adgangskode for bruger "$2" er blevet lavet, den er "$3".
-Hvis dette var din mening, skal du logge ind og vælge en ny adgangskode nu.
+Hvis dette var din mening, skal du logge  og vælge en ny adgangskode nu.
 Din midlertidige adgangskode vil udløbe om {{PLURAL:$5|en dag|$5 dage}}.
 
 Hvis en anden har bestilt den nye adgangskode, eller hvis du er kommet i tanke om din gamle adgangskode og ikke længere vil ændre den,
 kan du bare ignorere denne e-mail og fortsætte med at bruge din gamle adgangskode.',
-'noemail' => 'Der er ikke oplyst en e-mail-adresse for bruger "$1".',
+'noemail' => 'Der er ikke oplyst en e-mailadresse for bruger "$1".',
 'noemailcreate' => 'Du skal angive en gyldig e-mailadresse',
-'passwordsent' => 'En ny adgangskode er sendt til e-mail-adressen,
-som er registreret for "$1".
+'passwordsent' => 'En ny adgangskode er sendt til e-mailadressen, som er registreret for "$1".
 Du bør logge på og ændre din adgangskode straks efter du har modtaget e-mailen.',
-'blocked-mailpassword' => 'Din IP-adresse er spærret for ændring af sider. For at forhindre misbrug, er det heller ikke muligt, at bestille et nyt password.',
-'eauthentsent' => 'En bekræftelsesmail er sendt til den angivne e-mail-adresse.
+'blocked-mailpassword' => 'Din IP-adresse er spærret for ændring af sider. For at forhindre misbrug, er det heller ikke muligt, at bestille en ny adgangskode.',
+'eauthentsent' => 'En bekræftelsesmail er sendt til den angivne e-mailadresse.
 
 Før en e-mail kan modtages af andre brugere af {{SITENAME}}-mailfunktionen, skal adressen og dens tilhørsforhold til denne bruger bekræftes. Følg venligst anvisningerne i denne mail.',
 'throttled-mailpassword' => 'Indenfor {{PLURAL:$1|den sidste time|de sidste $1 timer}} er der allerede sendt en ny adgangskode. For at forhindre misbrug af funktionen, kan der kun bestilles en ny adgangskode én gang for hver {{PLURAL:$1|time|$1 timer}}.',
 'mailerror' => 'Fejl ved afsendelse af e-mail: $1',
 'acct_creation_throttle_hit' => 'Besøgende med samme IP-adresse som dig har oprettet {{PLURAL:$1|en konto|$1 kontoer}} det sidste døgn, og det er ikke tilladt at oprette flere.
 Derfor kan besøgende ikke oprette flere kontoer fra denne IP-adresse i øjeblikket.',
-'emailauthenticated' => 'Din e-mail-adresse blev bekræftet $2 $3.',
-'emailnotauthenticated' => 'Din e-mail-adresse er endnu ikke bekræftet og de avancerede e-mail-funktioner er slået fra indtil bekræftelse har fundet sted (d.u.a.). Log ind med den midlertidige adgangskode, der er blevet sendt til dig, for at bekræfte, eller bestil et nyt på loginsiden.',
-'noemailprefs' => 'Angiv en e-mail-adresse, så følgende funktioner er til rådighed.',
-'emailconfirmlink' => 'Bekræft e-mail-adressen (autentificering).',
-'invalidemailaddress' => 'E-mail-adressen kan ikke accepteres da den tilsyneladende har et ugyldigt format. Skriv venligst en e-mail-adresse med et korrekt format eller tøm feltet.',
-'cannotchangeemail' => 'De email-adresser, der er tilknyttet brugerkontoer, kan ikke ændres på denne wiki.',
-'emaildisabled' => 'Denne hjemmeside kan ikke sende emails.',
+'emailauthenticated' => 'Din e-mailadresse blev bekræftet $2 $3.',
+'emailnotauthenticated' => 'Din e-mailadresse er endnu ikke bekræftet.
+Ingen e-mail vil blive sendt for de følgende funktioner.',
+'noemailprefs' => 'Angiv en e-mailadresse, så følgende funktioner er til rådighed.',
+'emailconfirmlink' => 'Bekræft din e-mailadresse',
+'invalidemailaddress' => 'E-mailadressen kan ikke accepteres da den tilsyneladende har et ugyldigt format. Skriv venligst en e-mailadresse med et korrekt format eller tøm feltet.',
+'cannotchangeemail' => 'De e-mailadresser, der er tilknyttet brugerkontoer, kan ikke ændres på denne wiki.',
+'emaildisabled' => 'Denne hjemmeside kan ikke sende e-mails.',
 'accountcreated' => 'Brugerkonto oprettet',
 'accountcreatedtext' => 'Brugerkontoen for [[{{ns:User}}:$1|$1]] ([[{{ns:User talk}}:$1|diskussion]]) er oprettet.',
 'createaccount-title' => 'Opret brugerkonto på {{SITENAME}}',
-'createaccount-text' => 'Nogen har oprettet en konto for din e-post-adresse på {{SITENAME}} ($4) med navnet "$2". Adgangskoden er "$3".
-Du opfordres til at logge ind og ændre adgangskoden med det samme.
+'createaccount-text' => 'Nogen har oprettet en konto for din e-mailadresse på {{SITENAME}} ($4) med navnet "$2". Adgangskoden er "$3".
+Du opfordres til at logge  og ændre adgangskoden med det samme.
 
 Du kan ignorere denne besked hvis kontoen blev oprettet ved en fejl.',
 'usernamehasherror' => 'Brugernavn må ikke indeholde #',
@@ -738,18 +739,18 @@ Du kan ignorere denne besked hvis kontoen blev oprettet ved en fejl.',
 Vent venligst $1, før du prøver igen.',
 'login-abort-generic' => 'Det lykkedes dig ikke at logge på - afbrudt',
 'loginlanguagelabel' => 'Sprog: $1',
-'suspicious-userlogout' => 'Din anmodning om at logge ud blev nægtet, fordi det ser ud som den blev sendt af en ødelagt browser eller caching proxy.',
+'suspicious-userlogout' => 'Din anmodning om at logge af blev nægtet, fordi det ser ud som den blev sendt af en ødelagt browser eller caching proxy.',
 'createacct-another-realname-tip' => 'Angivelse af rigtigt navn er valgfrit.
 Hvis du vælger at oplyse dit navn, vil det blive brugt til at tilskrive dig dit arbejde.',
 
 # Email sending
-'php-mail-error-unknown' => "Ukendt fejl i PHP's mail()-funtion",
-'user-mail-no-addy' => 'Forsøgte at sende email uden en email-adresse',
+'php-mail-error-unknown' => 'Ukendt fejl i PHP funktionen mail()',
+'user-mail-no-addy' => 'Forsøgte at sende e-mail uden en e-mailadresse',
 'user-mail-no-body' => 'Forsøgte at sende en e-mail med tomt eller urimeligt kort indhold.',
 
 # Change password dialog
 'resetpass' => 'Skift adgangskode',
-'resetpass_announce' => 'Log på med den via e-mail tilsendte password. For at afslutte tilmeldingen, skal du nu vælge et nyt password.',
+'resetpass_announce' => 'Du loggede på med den via e-mail tilsendte adgangskode. For at afslutte tilmeldingen, skal du nu vælge en ny adgangskode.',
 'resetpass_text' => '<!-- Tilføj tekst her -->',
 'resetpass_header' => 'Skift adgangskode',
 'oldpassword' => 'Gammel adgangskode:',
@@ -764,33 +765,33 @@ Hvis du vælger at oplyse dit navn, vil det blive brugt til at tilskrive dig dit
 'resetpass-wrong-oldpass' => 'Ugyldig midlertidig eller gældende adgangskode.
 Du har muligvis allerede skiftet din adgangskode eller anmodet om en ny midlertidig kode.',
 'resetpass-temp-password' => 'Midlertidig adgangskode',
-'resetpass-abort-generic' => 'Ændring af kodeord er blevet afbrudt af udvidelse',
+'resetpass-abort-generic' => 'Ændring af adgangskode er blevet afbrudt af en udvidelse',
 
 # Special:PasswordReset
 'passwordreset' => 'Nulstil adgangskode',
 'passwordreset-text-one' => 'Udfyld denne formular for at nulstille din adgangskode.',
-'passwordreset-text-many' => '{{PLURAL:$1|Udfyld en af felterne nedenfor for at nulstille din adgangskode.}}',
+'passwordreset-text-many' => '{{PLURAL:$1|Udfyld et af felterne nedenfor for at nulstille din adgangskode.}}',
 'passwordreset-legend' => 'Nulstil adgangskode',
-'passwordreset-disabled' => 'Nulstilling af kodeord er slået fra på denne wiki.',
+'passwordreset-disabled' => 'Nulstilling af adgangskode er slået fra på denne wiki.',
 'passwordreset-emaildisabled' => 'E-mailfunktioner er slået fra på denne wiki.',
 'passwordreset-username' => 'Brugernavn:',
 'passwordreset-domain' => 'Domæne:',
-'passwordreset-capture' => 'Se den resulterende email?',
+'passwordreset-capture' => 'Se den resulterende e-mail?',
 'passwordreset-capture-help' => 'Hvis du krydser dette felt af, vil emailen (med den midlertidige adgangskode) blive vist til dig i tillæg til at blive sendt til brugeren.',
-'passwordreset-email' => 'E-mail adresse:',
+'passwordreset-email' => 'E-mailadresse:',
 'passwordreset-emailtitle' => 'Kontooplysninger på {{SITENAME}}',
-'passwordreset-emailtext-ip' => 'Nogen (sandsynligvis dig, fra IP-adressen $1) har anmodet om at få nulstillet din adgangskode til {{SITENAME}} ($4). {{PLURAL:$3|Den følgende brugerkonto er associeret|De følgende brugerkonti er associerede}} med denne e-mail-adresse:
+'passwordreset-emailtext-ip' => 'Nogen (sandsynligvis dig, fra IP-adressen $1) har anmodet om at få nulstillet din adgangskode til {{SITENAME}} ($4). {{PLURAL:$3|Den følgende brugerkonto er associeret|De følgende brugerkonti er associerede}} med denne e-mailadresse:
 
 $2
 
 {{PLURAL:$3|Denne midlertidige adgangskode|Disse midlertidige adgangskoder}} vil udløbe om {{PLURAL:$5|en dag|$5 dage}}.
-Du bør logge på og vælge en ny adgangskode nu. Hvis en anden end dig har gjort denne anmodning, eller hvis du er kommet i tanke om din oprindelig adgangskode og ikke længere ønsker at ændre den, kan du ignorere denne meddelelse og fortsætte med at bruge din gamle adgangskode.',
-'passwordreset-emailtext-user' => 'Brugeren $1 på {{SITENAME}} har anmodet om at få nulstillet din adgangskode til {{SITENAME}} ($4). {{PLURAL:$3|Den følgende brugerkonto er associeret|De følgende brugerkonti er associerede}} med denne e-mail-adresse:
+Du bør logge på og vælge en ny adgangskode nu. Hvis en anden end dig har lavet denne anmodning, eller hvis du er kommet i tanke om din oprindelig adgangskode og ikke længere ønsker at ændre den, kan du ignorere denne meddelelse og fortsætte med at bruge din gamle adgangskode.',
+'passwordreset-emailtext-user' => 'Brugeren $1 på {{SITENAME}} har anmodet om at få nulstillet din adgangskode til {{SITENAME}} ($4). {{PLURAL:$3|Den følgende brugerkonto er associeret|De følgende brugerkonti er associerede}} med denne e-mailadresse:
 
 $2
 
 {{PLURAL:$3|Denne midlertidige adgangskode|Disse midlertidige adgangskoder}} vil udløbe om {{PLURAL:$5|en dag|$5 dage}}.
-Du bør logge på og vælge en ny adgangskode nu. Hvis en anden end dig har gjort denne anmodning, eller hvis du er kommet i tanke om din oprindelig adgangskode og ikke længere ønsker at ændre den, kan du ignorere denne meddelelse og fortsætte med at bruge din gamle adgangskode.',
+Du bør logge på og vælge en ny adgangskode nu. Hvis en anden end dig har lavet denne anmodning, eller hvis du er kommet i tanke om din oprindelig adgangskode og ikke længere ønsker at ændre den, kan du ignorere denne meddelelse og fortsætte med at bruge din gamle adgangskode.',
 'passwordreset-emailelement' => 'Brugernavn: $1
 Midlertidig adgangskode: $2',
 'passwordreset-emailsent' => 'En e-mail om nulstilling af adgangskode er blevet sendt.',
@@ -798,15 +799,15 @@ Midlertidig adgangskode: $2',
 'passwordreset-emailerror-capture' => 'En mail om nulstilling af adgangskode, som vist nedenfor, blev genereret, men det lykkedes ikke at sende den til {{GENDER:$2|bruger}}: $1',
 
 # Special:ChangeEmail
-'changeemail' => 'Ændr email-adresse',
-'changeemail-header' => 'Ændr kontoens email-adresse',
-'changeemail-text' => 'Udfyld denne formular for at ændre din email-adresse. Du skal indtaste dit kodeord for at bekræfte denne ændring.',
+'changeemail' => 'Ændre e-mailadresse',
+'changeemail-header' => 'Ændre kontoens e-mailadresse',
+'changeemail-text' => 'Udfyld denne formular for at ændre din e-mailadresse. Du skal indtaste din adgangskode for at bekræfte denne ændring.',
 'changeemail-no-info' => 'Du skal være logget på for at komme direkte til denne side.',
-'changeemail-oldemail' => 'Nuværende email-adresse:',
-'changeemail-newemail' => 'Ny email-adresse:',
+'changeemail-oldemail' => 'Nuværende e-mailadresse:',
+'changeemail-newemail' => 'Ny e-mailadresse:',
 'changeemail-none' => '(ingen)',
 'changeemail-password' => 'Din adgangskode til {{SITENAME}}:',
-'changeemail-submit' => 'Ændr email',
+'changeemail-submit' => 'Ændre e-mail',
 'changeemail-cancel' => 'Afbryd',
 
 # Special:ResetTokens
@@ -817,7 +818,7 @@ Du bør gøre det, hvis du ved et uheld deler dem med nogen, eller hvis din kont
 'resettokens-no-tokens' => 'Der er ingen nøgler at nulstille.',
 'resettokens-legend' => 'Nulstil nøgler',
 'resettokens-tokens' => 'Nøgler:',
-'resettokens-token-label' => '$1(aktuel værdi: $2)',
+'resettokens-token-label' => '$1 (aktuel værdi: $2)',
 'resettokens-watchlist-token' => 'Nøgle for web-feed (Atom/RSS) af [[Special:Watchlist|ændringer af sider på din overvågningsliste]]',
 'resettokens-done' => 'Nøgler er nulstillet.',
 'resettokens-resetbutton' => 'Nulstil valgte nøgler',
@@ -829,16 +830,16 @@ Du bør gøre det, hvis du ved et uheld deler dem med nogen, eller hvis din kont
 'italic_tip' => 'Kursiv tekst',
 'link_sample' => 'Henvisning',
 'link_tip' => 'Intern henvisning',
-'extlink_sample' => 'http://www.example.com Titel på henvisning',
+'extlink_sample' => 'http://www.example.com titel på henvisning',
 'extlink_tip' => 'Ekstern henvisning (husk http:// præfiks)',
 'headline_sample' => 'Tekst til overskrift',
 'headline_tip' => 'Type 2-overskrift',
 'nowiki_sample' => 'Indsæt tekst her som ikke skal wikiformateres',
 'nowiki_tip' => 'Ignorer wikiformatering',
 'image_sample' => 'Eksempel.jpg',
-'image_tip' => 'Indlejret billede',
+'image_tip' => 'Indlejret fil',
 'media_sample' => 'Eksempel.ogg',
-'media_tip' => 'Henvisning til multimediefil',
+'media_tip' => 'Henvisning til fil',
 'sig_tip' => 'Din signatur med tidsstempel',
 'hr_tip' => 'Horisontal linje (brug den sparsomt)',
 
@@ -852,11 +853,11 @@ Du bør gøre det, hvis du ved et uheld deler dem med nogen, eller hvis din kont
 'showpreview' => 'Forhåndsvisning',
 'showlivepreview' => 'Live-forhåndsvisning',
 'showdiff' => 'Vis ændringer',
-'anoneditwarning' => 'Du arbejder uden at være logget på. Istedet for brugernavn vises din IP-adresse i versionshistorikken.',
-'anonpreviewwarning' => "''Du er ikke logget ind. Hvis du gemmer, registreres din IP-adresse i versionshistorikken.''",
-'missingsummary' => "'''Påmindelse:''' Du har ikke angivet en redigeringsbeskrivelse. Hvis du igen trykker på \"Gem\", gemmes ændringerne uden en beskrivelse.",
-'missingcommenttext' => 'Indtast venligst et resume.',
-'missingcommentheader' => "'''Bemærk:''' Du har ikke angivet en overskrift i feltet „Emne:“ for denne kommentar. Hvis du trykker „Gem side“ én gang til, gemmes dine ændringer uden overskrift.",
+'anoneditwarning' => "'''Advarsel:''' Du er ikke logget på. I stedet for brugernavn vises din IP-adresse i versionshistorikken.",
+'anonpreviewwarning' => "''Du er ikke logget . Hvis du gemmer, registreres din IP-adresse i versionshistorikken.''",
+'missingsummary' => "'''Bemærk:''' Du har ikke angivet en redigeringsbeskrivelse. Hvis du igen trykker på \"{{int:savearticle}}\", gemmes ændringerne uden en beskrivelse.",
+'missingcommenttext' => 'Skriv venligst en kommentar nedenfor.',
+'missingcommentheader' => "'''Bemærk:''' Du har ikke angivet en overskrift/emne for denne kommentar. Hvis du trykker \"{{int:savearticle}}\" én gang til, gemmes dine ændringer uden overskrift/emne.",
 'summary-preview' => 'Forhåndsvisning af beskrivelsen:',
 'subject-preview' => 'Forhåndsvisning af emnet:',
 'blockedtitle' => 'Brugeren er blokeret',
@@ -886,15 +887,15 @@ Begrundelsen for det er:
 
 Du kan kontakte $1 eller en af de andre [[{{MediaWiki:Grouppage-sysop}}|administratorer]] for at diskutere blokeringen.
 
-Bemærk at du ikke kan bruge funktionen "e-mail til denne bruger" medmindre du har en gyldig e-mail-adresse registreret i din [[Special:Preferences|brugerindstilling]], og du ikke er blevet blokeret fra at bruge den.
+Bemærk at du ikke kan bruge funktionen "e-mail til denne bruger" medmindre du har en gyldig e-mailadresse registreret i din [[Special:Preferences|brugerindstilling]], og du ikke er blevet blokeret fra at bruge den.
 
 Din nuværende IP-adresse er $3, og blokerings-id\'et er #$5.
 Angiv venligst alle de ovenstående detaljer ved eventuelle henvendelser.',
 'blockednoreason' => 'ingen begrundelse givet',
 'whitelistedittext' => 'Du skal $1 for at kunne redigere sider.',
-'confirmedittext' => 'Du skal først bekræfte e-mail-adressen, før du kan lave ændringer. Udfyld og bekræft din e-mail-adresse i dine [[Special:Preferences|Indstillinger]].',
+'confirmedittext' => 'Du skal først bekræfte e-mailadressen, før du kan lave ændringer. Udfyld og bekræft din e-mailadresse i dine [[Special:Preferences|indstillinger]].',
 'nosuchsectiontitle' => 'Kan ikke finde afsnittet',
-'nosuchsectiontext' => 'Du forsøgte at ændre et ikke-eksisterende afsnit. Det kan være flyttet eller slettet, siden du hentede siden.',
+'nosuchsectiontext' => 'Du forsøgte at ændre et afsnit der ikke findes. Det kan være flyttet eller slettet, siden du hentede siden.',
 'loginreqtitle' => 'Log på nødvendigt',
 'loginreqlink' => 'logge på',
 'loginreqpagetext' => 'Du skal $1 for at se andre sider.',
@@ -1152,7 +1153,7 @@ Andre administratorer på {{SITENAME}} vil fortsat være i stand til at se det s
 'revdelete-hide-comment' => 'Skjul ændringskommentar',
 'revdelete-hide-user' => 'Skjul brugerens brugernavn/IP',
 'revdelete-hide-restricted' => 'Skjul også informationen for administratorer',
-'revdelete-radio-same' => '(ændr ikke)',
+'revdelete-radio-same' => '(ikke ændre)',
 'revdelete-radio-set' => 'Ja',
 'revdelete-radio-unset' => 'Nej',
 'revdelete-suppress' => 'Skjul også informationen for administratorer',
@@ -1331,8 +1332,8 @@ Du kan prøve at bruge \"all:\" som præfiks for at søge i alt indhold (inkl. d
 'prefs-watchlist-token' => 'Overvågningslistenøgle:',
 'prefs-misc' => 'Forskelligt',
 'prefs-resetpass' => 'Skift adgangskode',
-'prefs-changeemail' => 'Ændr email',
-'prefs-setemail' => 'Angiv en email-adresse',
+'prefs-changeemail' => 'Ændre e-mailadresse',
+'prefs-setemail' => 'Angiv en e-mailadresse',
 'prefs-email' => 'Indstillinger for e-mail',
 'prefs-rendering' => 'Udseende',
 'saveprefs' => 'Gem indstillinger',
@@ -1405,9 +1406,9 @@ Informationen vil være offentlig.',
 'email' => 'E-mail',
 'prefs-help-realname' => 'Angivelse af rigtigt navn er valgfrit.
 Hvis du vælger at oplyse dit navn, vil det blive brugt til at tilskrive dig dit arbejde.',
-'prefs-help-email' => 'Angivelse af e-mail-adresse er valgfrit. Det gør det muligt at sende dig en ny adgangskode hvis du glemmer den.',
+'prefs-help-email' => 'Angivelse af e-mailadresse er valgfrit, men den gør det muligt at sende dig en ny adgangskode hvis du glemmer den.',
 'prefs-help-email-others' => 'Du kan også vælge at lade andre kontakte dig gennem din bruger eller diskussions side uden at behøve at afsløre din identitet.',
-'prefs-help-email-required' => 'E-mail-adresse er krævet.',
+'prefs-help-email-required' => 'E-mailadresse er krævet.',
 'prefs-info' => 'Grundlæggende information',
 'prefs-i18n' => 'Internationalisering:',
 'prefs-signature' => 'Signatur',
@@ -1429,7 +1430,7 @@ Hvis du vælger at oplyse dit navn, vil det blive brugt til at tilskrive dig dit
 
 # User preference: email validation using jQuery
 'email-address-validity-valid' => 'E-mailadressen ser ud til at være gyldig',
-'email-address-validity-invalid' => 'Indtast en gyldig e-mail adresse',
+'email-address-validity-invalid' => 'Indtast en gyldig e-mailadresse',
 
 # User rights
 'userrights' => 'Håndtering af brugerrettigheder',
@@ -1530,8 +1531,8 @@ Vær venlig at gennemse og bekræft dine ændringer.',
 'right-editmyuserjs' => 'Redigere dine egne JavaScript-filer',
 'right-viewmywatchlist' => 'Se din egen overvågningsliste',
 'right-editmywatchlist' => 'Redigere din egen overvågningsliste. Bemærk nogle handlinger tilføjer sider selv uden denne rettelse.',
-'right-viewmyprivateinfo' => 'Se dine egen private data (f.eks. e-mail-adresse, rigtige navn)',
-'right-editmyprivateinfo' => 'Redigere din egen private data (f.eks. e-mail-adresse, rigtige navn)',
+'right-viewmyprivateinfo' => 'Se dine egne private data (f.eks. e-mailadresse, rigtige navn)',
+'right-editmyprivateinfo' => 'Redigere dine egne private data (f.eks. e-mailadresse, rigtige navn)',
 'right-editmyoptions' => 'Redigere dine egne indstillinger',
 'right-rollback' => 'Hurtig gendannelse af alle redigeringer foretaget af den seneste bruger',
 'right-markbotedits' => 'Markere gendannelser som ændringer foretaget af en robot',
@@ -1548,7 +1549,7 @@ Vær venlig at gennemse og bekræft dine ændringer.',
 'right-siteadmin' => 'Låse og frigive databasen',
 'right-override-export-depth' => 'Eksportere sider inkl. henviste sider op til en dybde på 5',
 'right-sendemail' => 'Sende e-mail til andre brugere',
-'right-passwordreset' => 'Se emails til nulstilling af adgangskoder',
+'right-passwordreset' => 'Se e-mails til nulstilling af adgangskoder',
 
 # Special:Log/newusers
 'newuserlogpage' => 'Brugeroprettelseslog',
@@ -1593,7 +1594,7 @@ Vær venlig at gennemse og bekræft dine ændringer.',
 'action-userrights' => 'ændre alle brugerrettigheder',
 'action-userrights-interwiki' => 'ændre brugerrettigheder for brugere på andre wikier',
 'action-siteadmin' => 'låse eller låse databasen op',
-'action-sendemail' => 'sende email',
+'action-sendemail' => 'sende e-mail',
 'action-editmywatchlist' => 'rediger din overvågningsliste',
 'action-viewmywatchlist' => 'se din overvågningsliste',
 'action-viewmyprivateinfo' => 'se din private information',
@@ -2243,17 +2244,17 @@ Der findes muligvis [[{{MediaWiki:Listgrouprights-helppage}}|yderligere informat
 'mailnologin' => 'Du er ikke logget på',
 'mailnologintext' => 'Du skal være [[Special:UserLogin|logget på]] og have en gyldig e-mailadresse sat i dine [[Special:Preferences|indstillinger]] for at sende e-mail til andre brugere.',
 'emailuser' => 'E-mail til denne bruger',
-'emailuser-title-target' => 'Send email til denne {{GENDER:$1|bruger}}',
-'emailuser-title-notarget' => 'Send email til en bruger',
+'emailuser-title-target' => 'Send e-mail til denne {{GENDER:$1|bruger}}',
+'emailuser-title-notarget' => 'Send e-mail til en bruger',
 'emailpage' => 'E-mail bruger',
 'emailpagetext' => 'Du kan bruge formularen nedenfor til at sende en e-mail til denne {{GENDER:$1|bruger}}.
 Den e-mail-adresse, du har angivet i [[Special:Preferences|dine indstillinger]], vil dukke op i "fra"-feltet på e-mailen, så modtageren kan svare dig.',
 'usermailererror' => 'E-mail-modulet returnerede en fejl:',
-'defemailsubject' => '{{SITENAME}}-email fra brugeren "$1"',
+'defemailsubject' => '{{SITENAME}}-e-mail fra brugeren "$1"',
 'usermaildisabled' => 'Bruger-e-mail deaktiveret',
 'usermaildisabledtext' => 'Du kan ikke sende e-mails til andre brugere på denne wiki',
-'noemailtitle' => 'Ingen e-mail-adresse',
-'noemailtext' => 'Denne bruger har ikke angivet en gyldig e-mail-adresse.',
+'noemailtitle' => 'Ingen e-mailadresse',
+'noemailtext' => 'Denne bruger har ikke angivet en gyldig e-mailadresse.',
 'nowikiemailtitle' => 'E-mail er ikke tilladt',
 'nowikiemailtext' => 'Denne bruger har valgt ikke at modtage e-mail fra andre brugere.',
 'emailnotarget' => 'Ikke-eksisterende eller ugyldigt brugernavn for modtageren.',
@@ -2378,11 +2379,12 @@ Bekræft venligst at du virkelig vil gøre dette, at du forstår konsekvenserne,
 'deletecomment' => 'Begrundelse:',
 'deleteotherreason' => 'Anden/uddybende begrundelse:',
 'deletereasonotherlist' => 'Anden begrundelse',
-'deletereason-dropdown' => '
-*Hyppige sletningsårsager
-** Efter forfatters ønske
+'deletereason-dropdown' => '* Hyppige sletningsårsager
+** Spam
+** Hærværk
 ** Overtrædelse af ophavsret
-** Hærværk',
+** Efter forfatters ønske
+** Brudt omdirigering',
 'delete-edit-reasonlist' => 'Rediger sletningsårsager',
 'delete-toobig' => 'Denne side har en stor historik, over {{PLURAL:$1|en version|$1 versioner}}. Sletning af sådanne sider er begrænset, for at forhindre utilsigtet forstyrrelse af {{SITENAME}}.',
 'delete-warning-toobig' => 'Denne side har en stor historik, over {{PLURAL:$1|en version|$1 versioner}} versioner, slettes den kan det forstyrre driften af {{SITENAME}}, gå forsigtigt frem.',
@@ -2417,7 +2419,7 @@ Se [[Special:ProtectedPages|listen over beskyttede sider]] for listen over sideb
 'modifiedarticleprotection' => 'ændrede beskyttelsen af "[[$1]]"',
 'unprotectedarticle' => 'fjernede beskyttelse af "[[$1]]"',
 'movedarticleprotection' => 'flyttede beskyttelsesindstillinger fra "[[$2]]" til "[[$1]]"',
-'protect-title' => 'Ændr beskyttelse af "$1"',
+'protect-title' => 'Ændre beskyttelse af "$1"',
 'protect-title-notallowed' => 'Få vist beskyttelsesniveauet af "$1"',
 'prot_1movedto2' => '$1 flyttet til $2',
 'protect-badnamespace-title' => 'Navnerum, der ikke kan beskyttes',
@@ -2662,7 +2664,7 @@ Se [[Special:BlockList|blokeringslisten]] for alle blokeringer.',
 'ipblocklist-no-results' => 'Den angivene IP-addresse eller brugernavn er ikke blokeret.',
 'blocklink' => 'bloker',
 'unblocklink' => 'ophæv blokering',
-'change-blocklink' => 'ændr blokering',
+'change-blocklink' => 'ændre blokering',
 'contribslink' => 'bidrag',
 'emaillink' => 'send e-mail',
 'autoblocker' => 'Du er automatisk blokeret, fordi du deler IP-adresse med "[[User:$1|$1]]".
@@ -2697,11 +2699,8 @@ Se [[Special:BlockList|blokeringslisten]] for den nuværende liste med aktuelle
 'ipb_blocked_as_range' => 'Fejl: IP-adressen $1 er ikke direkte blokeret. Derfor kan en blokering ikke ophæves. Adressen er blokeret som en del af intervallet $2. Denne blokering kan ophæves.',
 'ip_range_invalid' => 'Ugyldigt IP-interval.',
 'ip_range_toolarge' => 'Blokeringer af IP-serier større end /$1 er ikke tilladte.',
-'blockme' => 'Bloker mig',
 'proxyblocker' => 'Proxy-blokering',
-'proxyblocker-disabled' => 'Denne funktion er ikke i brug.',
 'proxyblockreason' => "Din IP-adresse er blevet blokeret fordi den er en såkaldt ''åben proxy''. Kontakt din Internet-udbyder eller tekniske hotline og oplyse dem om dette alvorlige sikkerhedsproblem.",
-'proxyblocksuccess' => 'Færdig.',
 'sorbsreason' => 'IP-adressen er opført i DNSBL på {{SITENAME}} som åben PROXY.',
 'sorbs_create_account_reason' => 'IP-adressen er opført i DNSBL på {{SITENAME}} som åben PROXY. Oprettelse af nye brugere er ikke mulig.',
 'xffblockreason' => 'En IP-adresse der er indeholdt i X-Fremsendt-Til hovedet, enten din egen eller en på en proxy-server, du bruger, er blevet blokeret. Den oprindelige grund til blokeringen var:$1',
@@ -2958,7 +2957,7 @@ Alle Transwiki import-aktioner protokolleres i [[Special:Log/import|import-logge
 Du kan se på kildeteksten.',
 'tooltip-ca-history' => 'Tidligere versioner af denne side',
 'tooltip-ca-protect' => 'Beskyt denne side',
-'tooltip-ca-unprotect' => 'Ændr beskyttelsen af denne side',
+'tooltip-ca-unprotect' => 'Ændre beskyttelsen af denne side',
 'tooltip-ca-delete' => 'Slet denne side',
 'tooltip-ca-undelete' => 'Gendan de redigeringer der blev lavet på denne side før den blev slettet',
 'tooltip-ca-move' => 'Flyt denne side',
@@ -3043,6 +3042,8 @@ Dette skyldes sandsynligvis en henvisning til et sortlistet eksternt websted.',
 'spam_reverting' => 'Sidste version uden henvisning til $1 gendannet.',
 'spam_blanking' => 'Alle versioner, som indeholdt henvisninger til $1, er renset.',
 'spam_deleting' => 'Alle versioner indeholder henvisninger til $1, sletter',
+'simpleantispam-label' => "Anti-spam tjek.
+Udfyld '''IKKE''' dette!",
 
 # Info page
 'pageinfo-title' => 'Information om "$1"',
@@ -3617,37 +3618,39 @@ Kun indholdet af lister (linjer startende med *) bliver brugt. Den første henvi
 'limitall' => 'alle',
 
 # Email address confirmation
-'confirmemail' => 'Bekræft e-mail-adressen',
-'confirmemail_noemail' => 'Du har ikke angivet en gyldig e-mail-adresse i din [[Special:Preferences|brugerprofil]].',
-'confirmemail_text' => '{{SITENAME}} kræver, at du bekræfter en e-mail-adresse (autentificering), før du kan bruge de udvidede e-mail-funktioner. Med et klik på kontrolfeltet forneden sendes en e-mail til dig. Denne e-mail indeholder et link med en bekræftelseskode. Med et klik på dette link bekræftes, at e-mail-adressen er gyldig.',
+'confirmemail' => 'Bekræft e-mailadresse',
+'confirmemail_noemail' => 'Du har ikke angivet en gyldig e-mailadresse i din [[Special:Preferences|brugerprofil]].',
+'confirmemail_text' => '{{SITENAME}} kræver, at du bekræfter en e-mailadresse (autentificering), før du kan bruge de udvidede e-mailfunktioner. Med et klik på kontrolfeltet forneden sendes en e-mail til dig. Denne e-mail indeholder et link med en bekræftelseskode. Med et klik på dette link bekræftes, at e-mailadressen er gyldig.',
 'confirmemail_pending' => 'En bekræftelsesmail er allerede sendt til dig. Hvis du først for nylig har oprettet brugerkontoen, vent da et par minutter på denne e-mail, før du bestiller en ny kode.',
 'confirmemail_send' => 'Send bekræftelseskode',
 'confirmemail_sent' => 'Bekræftelses-e-mail afsendt.',
-'confirmemail_oncreate' => 'En bekræftelseskode er sendt til din e-mail-adresse. Denne kode skal ikke bruges til anmeldelsen, den kræves dog til aktiveringen af e-mail-funktionerne indenfor Wikien.',
-'confirmemail_sendfailed' => 'Bekræftelsesmailen kunne ikke afsendes. Kontroller at e-mail-adressen er korrekt.
+'confirmemail_oncreate' => 'En bekræftelseskode er sendt til din e-mailadresse. Denne kode skal ikke bruges til at logge på, den kræves til aktivering af e-mailfunktionerne i Wikien.',
+'confirmemail_sendfailed' => '{{SITENAME}} kunne ikke afsende din bekræftelsesmail.
+Kontroller at e-mailadressen er korrekt.
 
-Svarbesked fra mailserveren: $1',
+Besked fra mailserveren: $1',
 'confirmemail_invalid' => 'Ugyldig bekræftelseskode. Kodens gyldighed er muligvis udløbet.',
-'confirmemail_needlogin' => 'Du skal $1 for at bekræfte e-mail-adressen.',
-'confirmemail_success' => 'E-mail-adressen er nu bekræftet. Du kan nu logge på.',
-'confirmemail_loggedin' => 'E-mail-adressen er nu bekræftet.',
-'confirmemail_error' => 'Der skete en fejl ved bekræftelsen af e-mail-adressen.',
-'confirmemail_subject' => '[{{SITENAME}}] - bekræftelse af e-mail-adressen',
+'confirmemail_needlogin' => 'Du skal $1 for at bekræfte din e-mailadresse.',
+'confirmemail_success' => 'E-mailadressen er blevet bekræftet.
+Du kan nu [[Special:UserLogin|logge på]].',
+'confirmemail_loggedin' => 'Din e-mailadresse er nu bekræftet.',
+'confirmemail_error' => 'Der skete en fejl under lagring af din bekræftelse.',
+'confirmemail_subject' => '[{{SITENAME}}] - bekræftelse af e-mailadresse',
 'confirmemail_body' => 'Hej,
 
-Nogen med IP-adresse $1, sandsynligvis dig, har bestilt en bekræftelse af denne e-mail-adresse til brugerkontoen "$2" på {{SITENAME}}.
+Nogen med IP-adresse $1, sandsynligvis dig, har bestilt en bekræftelse af denne e-mailadresse til brugerkontoen "$2" på {{SITENAME}}.
 
-For at aktivere e-mail-funktionen for {{SITENAME}} (igen) og for at bekræfte, at denne brugerkonto virkelig hører til din e-mail-adresse og dermed til dig, bedes du åbne det følgende link i din browser: $3
+For at aktivere e-mailfunktionen for {{SITENAME}} og for at bekræfte, at denne brugerkonto virkelig hører til din e-mailadresse og dermed til dig, bedes du åbne det følgende link i din browser: $3
 
 Bekræftelseskoden er gyldig indtil følgende tidspunkt: $4
 
-Hvis denne e-mail-adresse *ikke* hører til den anførte brugerkonto, skal du i stedet åbne dette link i din browser: $5
+Hvis denne e-mailadresse *ikke* hører til den anførte brugerkonto, skal du i stedet åbne dette link i din browser: $5
 
 --
 {{SITENAME}}: {{fullurl:{{Mediawiki:mainpage}}}}',
-'confirmemail_body_changed' => 'Der er nogen, sandsynligvis dig, fra ip-adressen $1, der har ændret emailadressen for kontoen "$2" til denne adresse på {{SITENAME}}.
+'confirmemail_body_changed' => 'Der er nogen, sandsynligvis dig, fra ip-adressen $1, der har ændret e-mailadressen for kontoen "$2" til denne adresse på {{SITENAME}}.
 
-For at bekræfte, at denne konto virkeligt tilhører dig og for at genaktivere emailfunktionerne på {{SITENAME}}, bedes du åbne følgende link i en browser:
+For at bekræfte, at denne konto virkeligt tilhører dig og for at genaktivere e-mailfunktionerne på {{SITENAME}}, bedes du åbne følgende link i en browser:
 
 $3
 
index fad1fea..e373c86 100644 (file)
@@ -1377,15 +1377,15 @@ Andere Administratoren auf {{SITENAME}} haben Zugriff auf den versteckten Inhalt
 * Unangebrachte persönliche Informationen
 *: ''Adressen, Telefonnummern, Sozialversicherungsnummern etc.''",
 'revdelete-legend' => 'Setzen der Sichtbarkeitseinschränkungen',
-'revdelete-hide-text' => 'Text der Version verstecken',
+'revdelete-hide-text' => 'Text der Version',
 'revdelete-hide-image' => 'Dateiinhalt verstecken',
 'revdelete-hide-name' => 'Logbuchaktion und Ziel verstecken',
-'revdelete-hide-comment' => 'Bearbeitungszusammenfassung verstecken',
-'revdelete-hide-user' => 'Benutzername/IP-Adresse des Bearbeiters verstecken',
+'revdelete-hide-comment' => 'Bearbeitungszusammenfassung',
+'revdelete-hide-user' => 'Benutzername/IP-Adresse des Bearbeiters',
 'revdelete-hide-restricted' => 'Daten sowohl vor Administratoren als auch anderen Benutzern unterdrücken',
 'revdelete-radio-same' => '(nicht ändern)',
-'revdelete-radio-set' => 'Ja',
-'revdelete-radio-unset' => 'Nein',
+'revdelete-radio-set' => 'Sichtbar',
+'revdelete-radio-unset' => 'Versteckt',
 'revdelete-suppress' => 'Grund der Löschung auch vor Administratoren verstecken',
 'revdelete-unsuppress' => 'Einschränkungen für wiederhergestellte Versionen aufheben',
 'revdelete-log' => 'Grund:',
@@ -2919,11 +2919,8 @@ Siehe die [[Special:BlockList|Liste der gesperrten IP-Adressen und Benutzernamen
 'ipb_blocked_as_range' => 'Fehler: Die IP-Adresse $1 wurde als Teil der Bereichssperre $2 indirekt gesperrt. Eine Entsperrung von $1 alleine ist nicht möglich.',
 'ip_range_invalid' => 'Ungültiger IP-Adressbereich.',
 'ip_range_toolarge' => 'Adressbereiche, die größer als /$1 sind, sind nicht erlaubt.',
-'blockme' => 'Sperre mich',
 'proxyblocker' => 'Proxy blocker',
-'proxyblocker-disabled' => 'Diese Funktion ist deaktiviert.',
 'proxyblockreason' => 'Deine IP-Adresse wurde gesperrt, da sie ein offener Proxy ist. Bitte kontaktiere deinen Internet-Provider oder deine Systemadministratoren und informiere sie über dieses mögliche Sicherheitsproblem.',
-'proxyblocksuccess' => 'Erledigt.',
 'sorbsreason' => 'Die IP-Adresse ist in der DNSBL von {{SITENAME}} als offener PROXY gelistet.',
 'sorbs_create_account_reason' => 'Die IP-Adresse ist in der DNSBL von {{SITENAME}} als offener PROXY gelistet. Das Anlegen neuer Benutzer ist nicht möglich.',
 'xffblockreason' => 'Eine IP-Adresse im X-Forwarded-For-Header wurde gesperrt, entweder deine oder die des benutzten Proxyservers. Der ursprüngliche Sperrgrund war: $1',
@@ -3285,6 +3282,8 @@ Das liegt wahrscheinlich an einem Link auf eine externe Seite.',
 'spam_reverting' => 'Letzte Version ohne Links zu $1 wiederhergestellt.',
 'spam_blanking' => 'Alle Versionen mit einem Link zu $1 wurden bereinigt.',
 'spam_deleting' => 'Alle Versionen mit einem Link zu $1 wurden gelöscht.',
+'simpleantispam-label' => "Spamschutzprüfung.
+Hier '''NICHTS''' eintragen!",
 
 # Info page
 'pageinfo-title' => 'Informationen zu „$1“',
@@ -4075,7 +4074,7 @@ Eine [{{SERVER}}{{SCRIPTPATH}}/COPYING Kopie der ''GNU General Public License'']
 # Special:Redirect
 'redirect' => 'Weiterleitung auf Benutzerseite, Seitenversion oder Datei',
 'redirect-legend' => 'Weiterleitung auf eine Benutzerseite, Seitenversion oder Datei',
-'redirect-summary' => 'Diese Spezialseite leitet auf eine Benutzerseite (numerische Benutzerkennung angegeben), Seitenversion (Versionskennung angegeben) oder Datei (Dateiname angegeben) weiter.',
+'redirect-summary' => 'Diese Spezialseite leitet auf eine Benutzerseite (numerische Benutzerkennung angegeben), Seitenversion (Versionskennung angegeben) oder Datei (Dateiname angegeben) weiter. Benutzung: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/revision/328429]] oder [[{{#Special:Redirect}}/user/101]].',
 'redirect-submit' => 'Los',
 'redirect-lookup' => 'Suchen:',
 'redirect-value' => 'Kennung oder Dateiname:',
index d87a09c..ab87bab 100644 (file)
@@ -458,8 +458,6 @@ $messages = array(
 'broken-file-category' => 'Peleye ke gıreyê dosyeyanê ğeletan muhtewa kenê',
 'categoryviewer-pagedlinks' => '($1) ($2)',
 
-'linkprefix' => "'''MediaWiki niya ro.'''",
-
 'about' => 'Heqa cı de',
 'article' => 'Wesiqe',
 'newwindow' => '(pençereyê newey de beno a)',
@@ -778,6 +776,7 @@ Wexta ke verhafızayê cıgerayoxê şıma pak beno no benate de taye peli de he
 'userlogin-resetpassword-link' => 'Parolaya xo reset ke',
 'helplogin-url' => 'Help:Qeydbiyayış',
 'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|Desteg be qeydbiyayış ra]]',
+'userlogin-createanother' => 'Zewbi hesab vıraz',
 'createacct-join' => 'Cêr melumatê xo cı ke',
 'createacct-emailrequired' => 'Adresa e-postey',
 'createacct-emailoptional' => 'Adresa e-postey (mecburi niya)',
@@ -835,8 +834,8 @@ Eke vurnayişê parolayi, şıma nêwaşt ya zi parolayê şıma ameyo şıma vi
 'noemailcreate' => 'Şıma gani yew parolayo meqbul peda bıkeri',
 'passwordsent' => '"$1" No name de yew e-posta erşawiya (ruşya). hesabê xo, şıma wext mesaj gırewt u çax akere.',
 'blocked-mailpassword' => 'Cıkewetışê na keyepel de şıma qedexe biye, ey ra newe yew şifre nêerşawyeno.',
-'eauthentsent' => 'Adreso ke şıma dayo ma, ma yew e-posta rışt uca, o e-posta de kodê araşt kerdış esto.
-Heta ke şıma o e-postaaraşt nêkeri ma yewna e-posta şıma ri nêrışêno.',
+'eauthentsent' => 'Adresok şıma qeyd kerdo wıcayré e-posta rışiyé.
+Hetana şıma ne e-posta néwweyniyé, şımaé zewbi e-posta do nérışiyo.',
 'throttled-mailpassword' => 'Eyarkerdışê parola xora zerreyê {{PLURAL:$1|yew saete|$1 saetan}} erşawiya.
 Seba xırabgurenayışê xızmete ra, her {{PLURAL:$1|yew saete|$1 saetan}} de rey tenya yew eyarkerdışê parola erşawiyeno.',
 'mailerror' => 'Erşawıtışe xetayê e-posta: $1',
@@ -892,6 +891,7 @@ Bıne vındere u newe ra dest pê bıkere.',
 'passwordreset-text-many' => '{{PLURAL:$1|Qande parola reset kerdışi cayanra taynın pırkeri}}',
 'passwordreset-legend' => 'Parola reset ke',
 'passwordreset-disabled' => 'Parola reset kerdış ena viki sera qefılneyayo.',
+'passwordreset-emaildisabled' => 'Na wikid hısusiyeté e-posta dewera vıcyayé',
 'passwordreset-username' => 'Nameyê karberi:',
 'passwordreset-domain' => 'Domain:',
 'passwordreset-capture' => 'neticey e-postay bımocne?',
@@ -1638,6 +1638,7 @@ Kaberê bini ke şıma de kewti irtıbat, adresa e-postey şıma eşkera nêbena
 'right-editusercss' => 'Dosyanê CSSiê karberanê binan sero bıgureye',
 'right-edituserjs' => 'Dosyanê JSiê karberanê binan sero bıgureye',
 'right-viewmywatchlist' => 'Lista seyr de xo bıvin',
+'right-editmyoptions' => 'Tercihané ğo bıvırn',
 'right-rollback' => 'Lez/herbi vurnayışanê karberê peyêni tekrar bıke, oyo ke yew be yew pelê sero gureyao',
 'right-markbotedits' => 'Vurnayışanê peyd ameyan, vurnayışê boti deye nışan kerê',
 'right-noratelimit' => 'Sinoranê xızi (rate limit) ra tesir nêbi',
@@ -1711,6 +1712,7 @@ Kaberê bini ke şıma de kewti irtıbat, adresa e-postey şıma eşkera nêbena
 'recentchanges' => 'Vurnayışê peyêni',
 'recentchanges-legend' => 'Tercihê vurnayışanê peyênan',
 'recentchanges-summary' => 'Ena pele de wiki sero vurnayışanê peyênan teqib ke.',
+'recentchanges-noresult' => 'Zey kiterandé şıma vırnayış névineya',
 'recentchanges-feed-description' => 'Ena feed dı vurnayişanê tewr peniyan teqip bık.',
 'recentchanges-label-newpage' => 'Enê vurnayışi pelaya newi vıraşt',
 'recentchanges-label-minor' => 'Eno yew vurnayışo qıckeko',
@@ -2012,6 +2014,7 @@ keyepel nıka zaf meşğulo yew dema herayi de newe ra tesel bıkerê.',
 'listfiles_size' => 'Gırdiye',
 'listfiles_description' => 'Sılasnayış',
 'listfiles_count' => 'Versiyoni',
+'listfiles-show-all' => 'Asayışa versiyonandé verénan',
 'listfiles-latestversion' => 'Versiyono verin',
 'listfiles-latestversion-yes' => 'E',
 'listfiles-latestversion-no' => 'Nê',
@@ -2201,6 +2204,7 @@ gıreyê her satıri de gıreyi; raş motışê yewın u dıyıni esto.
 'mostrevisions' => 'Pelan ke tewr zaf revizyonî biyê.',
 'prefixindex' => 'Veroleya peley pêro',
 'prefixindex-namespace' => 'Peleyê Veroleyıni ($1 cay nami)',
+'prefixindex-strip' => 'Listeya réz bıyayışi',
 'shortpages' => 'Pelê kılmeki',
 'longpages' => 'Peleyê dergeki',
 'deadendpages' => 'Pelê nêgıredayey',
@@ -2481,10 +2485,12 @@ Qe qeydê wedarnayışi, $2 bevinin.',
 'deletecomment' => 'Sebeb:',
 'deleteotherreason' => 'Sebebo bin:',
 'deletereasonotherlist' => 'Sebebo bin',
-'deletereason-dropdown' => '*sebebê hewnakerdışê pêroyî
-** talebê nuştekari
-** ihlalê heqê telifi
-** Vandalizm',
+'deletereason-dropdown' => '*Sebebé esterıti
+** Spam
+** Vandalizm
+** İhlala heqdé telifi
+** Waştışé nustoği
+** Xırab hetenayış',
 'delete-edit-reasonlist' => 'Sebebê vurnayışan bıvurne',
 'delete-toobig' => 'no pel, pê $1 {{PLURAL:$1|tene vuriyayiş|tene vuriyayiş}}i wayirê yew tarixo kehen o.
 qey hewna nêşiyayişi wina pelani u {{SITENAME}}nêxerebnayişê keyepeli yew hed niyaya ro.',
@@ -2805,11 +2811,8 @@ belka ver-grewtış wedariyayo.',
 labele parçeya benateyê na $2 adresibi u ey ra ver-geryayo u şıma eşkeni no wedari.',
 'ip_range_invalid' => 'Rêza IPi nêvêrena.',
 'ip_range_toolarge' => 'Menzilan ke /$1 ra girdêrê inan rê izin nidano.',
-'blockme' => 'Mi blok bik',
 'proxyblocker' => 'blokarê proxyi',
-'proxyblocker-disabled' => 'Eno fonksiyon nêxebetiyeno.',
 'proxyblockreason' => 'IPadresa şıma yew proxyo akerdeyo u ey ra verniyê ey geriya.',
-'proxyblocksuccess' => 'Qeyd ke.',
 'sorbs' => 'DNSBL',
 'sorbsreason' => 'IP adresa şıma, hetê no {{SITENAME}} keyepeli ra  DNSBL de proxy hesibyayo u liste biyo.',
 'sorbs_create_account_reason' => 'IP adresa şıma, hetê no translatewiki.net keyepeli ra DNSBL de proxy hesibyayo u liste biyo.
@@ -3155,6 +3158,8 @@ Tı eşkeno yew sebeb bınus.',
 'spam_reverting' => 'agêriyeno revizyon o ke tawayê $1 ıney piya çiniyo',
 'spam_blanking' => 'Revizyonê gredê $1 vineyay, wa weng kero',
 'spam_deleting' => 'Revizyonê gredê $1 vineyay, wa besterneyê',
+'simpleantispam-label' => "Cerbnayışa anti-spami.
+Ney '''Mefiyé de'''!",
 
 # Info page
 'pageinfo-title' => 'Heq tê "$1"\'i',
@@ -4210,6 +4215,7 @@ satır ê ke pê ney # # destpêkenê zey mışore/mıjore muamele vineno.
 'dberr-problems' => 'Mayê muxulêm! Ena sita dı newke xırabiya teknik esta.',
 'dberr-again' => 'Yew di dekika vinder u hin bar bike.',
 'dberr-info' => '(Erzmelumati ra xızmetkari nêreseno: $1)',
+'dberr-info-hidden' => '(Ardendé erz malumatiya gredayışo nébeno)',
 'dberr-usegoogle' => 'Ti eşkeno hem zi ser Google de bigêre.',
 'dberr-outofdate' => 'Ekê raten da ma deyê belki zi newen niyo qandê coy diqet kerê.',
 'dberr-cachederror' => 'Pel ke ti wazeno yew kopyayê cacheyî ay esto, ay belki rocaniyeyo.',
@@ -4350,6 +4356,9 @@ satır ê ke pê ney # # destpêkenê zey mışore/mıjore muamele vineno.
 'limitreport-cputime-value' => '$1 {{PLURAL:$1|saniye|saniyeyan}}',
 'limitreport-walltime' => 'Raştay demdı bıkarn',
 'limitreport-walltime-value' => '$1 {{PLURAL:$1|saniye|saniyeyan}}',
+'limitreport-ppvisitednodes' => 'Amariya ziyaretda gozgıreya verkarkerdoği',
+'limitreport-ppgeneratednodes' => 'Amariya vıraştışda gozgırandé vekarkerdoği',
+'limitreport-postexpandincludesize' => 'Ebata herayina rışteri dahil a.',
 'limitreport-postexpandincludesize-value' => '$1/$2 {{PLURAL:$2|bayt|bayti}}',
 'limitreport-templateargumentsize' => 'Ebata hacetandi şablonan',
 'limitreport-templateargumentsize-value' => '$1/$2 {{PLURAL:$2|bayt|bayti}}',
index c87a1e4..d53ebe1 100644 (file)
@@ -2508,11 +2508,8 @@ Glědaj do [[Special:BlockList|lisćiny blokěrowanjow]], aby blokěrowanja pśe
 'ipb_blocked_as_range' => 'Zmólka: IP-adresa $1 njejo direktnje blokěrowana a njeda se wótblokěrowaś. Jo pak ako źěl wobcerka $2 blokěrowana, kótaryž da se wótblokěrowaś.',
 'ip_range_invalid' => 'Njepłaśecy wobłuk IP-adresow.',
 'ip_range_toolarge' => 'Wobcerkowe bloki, kótarež su wětše ako /$1, njejsu dowólone.',
-'blockme' => 'blokěruj mě',
 'proxyblocker' => 'Blokěrowanje proxy',
-'proxyblocker-disabled' => 'Toś ta funkcija jo znjemóžnjona.',
 'proxyblockreason' => 'Twója IP-adresa jo se blokěrowała, dokulaž jo wócynjony proxy. Pšosym kontaktěruj swójogo seśowego providera abo swóje systemowe administratory a informěruj je wó toś tom móžnem wěstotnem problemje.',
-'proxyblocksuccess' => 'Gótowe.',
 'sorbsreason' => 'Twója IP-adresa jo w DNSBL we {{GRAMMAR:lokatiw|{{SITENAME}}}} zapisana ako wócynjony proxy.',
 'sorbs_create_account_reason' => 'Twója IP-adresa jo w DNSBL {{GRAMMAR:genitiw|{{SITENAME}}}} ako wócynjony proxy zapisana. Njejo móžno, nowe wužywarske konta załožowaś.',
 'xffblockreason' => 'IP-adresa w header X-Forwarded-For, pak twója pak ta proksy-serwera, kótaryž wužywaš, jo se zablokěrowała. Spócetna pśicyna za blokěrowanje jo była: $1',
@@ -2850,6 +2847,8 @@ W zespominanju dajo se pśicyna pódaś.',
 'spam_reverting' => 'Nawrośijo se slědna wersija, kótaraž njejo wopśimjeła wótkaz na $1.',
 'spam_blanking' => 'Wšykne wersije su wopśimowali wótkaze na $1, do rěcha spórane.',
 'spam_deleting' => 'Wšykne wersije z wótkazami do $1 so lašuju',
+'simpleantispam-label' => "Antispamowa kontrola.
+How '''NIC''' zapisaś!",
 
 # Info page
 'pageinfo-title' => 'Informacije za bok "$1"',
index 04b7ff0..87c5aeb 100644 (file)
@@ -734,7 +734,6 @@ Also see [[Special:WantedCategories|wanted categories]].',
 'blocklink' => 'ފިޔަވަޅުއަޅުއްވާ',
 'unblocklink' => 'ފިޔަވަޅުއެޅުން ބަދަލުކުރައްވާ',
 'contribslink' => 'ޙިއްޞާ',
-'proxyblocksuccess' => 'ފުރިހަމަވެއްޖެ.',
 
 # Developer tools
 'lockdb' => 'ކޮށާރު ބަންދުކުރައްވާ',
index 22e7b4e..410d7d9 100644 (file)
@@ -579,7 +579,7 @@ $messages = array(
 'articlepage' => 'Εμφάνιση σελίδας περιεχομένου',
 'talk' => 'Συζήτηση',
 'views' => 'Προβολές',
-'toolbox' => 'Î\95Ï\81γαλειοθήκη',
+'toolbox' => 'Î\95Ï\81γαλεία',
 'userpage' => 'Προβολή σελίδας χρήστη',
 'projectpage' => 'Προβολή σελίδας εγχειρήματος',
 'imagepage' => 'Προβολή σελίδας αρχείου',
@@ -2828,11 +2828,8 @@ $1',
 'ipb_blocked_as_range' => 'Σφάλμα! Η φραγή της διεύθυνσης IP $1 δεν είναι άμεση και δεν μπορεί να αρθεί. Όμως αποτελεί μέρος της περιοχής $2, της οποίας η φραγή μπορεί να αρθεί.',
 'ip_range_invalid' => 'Το εύρος των διευθύνσεων IP δεν είναι έγκυρο.',
 'ip_range_toolarge' => 'Φραγές range μεγαλύτερων από /$1 δεν επιτρέπονται.',
-'blockme' => 'Φραγή σε μένα',
 'proxyblocker' => 'Εργαλείο φραγής διακομιστών (proxy blocker)',
-'proxyblocker-disabled' => 'Η λειτουργία αυτή έχει απενεργοποιηθεί.',
 'proxyblockreason' => 'Η διεύθυνση IP σας έχει υποστεί φραγή γιατί είναι open proxy. Παρακαλούμε επικοινωνείστε με τον παροχέα υπηρεσιών Διαδικτύου που χρησιμοποιείτε ή με την τεχνική υποστήριξη, για να θέσετε υπ΄ όψη τους αυτό το σοβαρό θέμα ασφάλειας.',
-'proxyblocksuccess' => 'Ολοκληρώθηκε!',
 'sorbsreason' => 'Η διεύθυνση IP σας έχει χαρακτηρισθεί ως open proxy στο DNSBL.',
 'sorbs_create_account_reason' => 'Η διεύθυνση IP σας έχει χαρακτηρισθεί open proxy στο DNSBL. Δεν μπορείτε να δημιουργήσετε λογαριασμό χρήστη.',
 'cant-block-while-blocked' => 'Δεν μπορείτε να φράξετε άλλους χρήστες ενώ είστε φραγμένος/η.',
@@ -2909,7 +2906,7 @@ $1',
 'movepage-moved' => '\'\'\'"$1" μεταφέρθηκε στο "$2"\'\'\'',
 'movepage-moved-redirect' => 'Δημιουργήθηκε μια ανακατεύθυνση.',
 'movepage-moved-noredirect' => 'Η δημιουργία ανακατεύθυνσης παρεμποδίστηκε.',
-'articleexists' => 'Υπάρχει ήδη σελίδα με αυτό το όνομα. Παρακαλούμε δώστε άλλο όνομα στη σελίδα.',
+'articleexists' => 'Υπάρχει ήδη σελίδα με αυτό το όνομα, ή το όνομα που επιλέξατε δεν είναι αποδεκτό. Παρακαλούμε δώστε άλλο όνομα στη σελίδα.',
 'cantmove-titleprotected' => "Δεν μπορείτε να μετακινήσετε μια σελίδα σ' αυτή τη θέση διότι έχει απαγορευθεί η δημιουργία αυτού του τίτλου",
 'talkexists' => "Η ίδια η σελίδα μετακινήθηκε επιτυχώς αλλά όχι και η σελίδα συζήτησης, λόγω του ότι υπάρχει ήδη άλλη σελίδα συζήτησης κάτω από το νέο τίτλο. Παρακαλούμε ενοποιήστε τις δύο σελίδες με 'αντιγραφή-και-επικόλληση'.",
 'movedto' => 'Μετακινήθηκε στο',
@@ -3183,6 +3180,8 @@ $2',
 'spam_reverting' => 'Επαναφορά στην τελευταία έκδοση που δεν περιέχει συνδέσμους στο $1',
 'spam_blanking' => 'Όλες οι αναθεωρήσεις περιείχαν συνδέσμους προς το $1, εξάλειψη',
 'spam_deleting' => 'Διαγραφή όλων των αναθεωρήσεων που περιείχαν συνδέσμους προς το $1',
+'simpleantispam-label' => "Έλεγχος anti-spam.
+'''ΜΗΝ''' το συμπληρώσετε αυτό!",
 
 # Info page
 'pageinfo-title' => 'Πληροφορίες για "$1"',
index e796087..7b62d29 100644 (file)
@@ -392,7 +392,6 @@ $specialPageAliases = array(
        'Badtitle'                  => array( 'Badtitle' ),
        'Blankpage'                 => array( 'BlankPage' ),
        'Block'                     => array( 'Block', 'BlockIP', 'BlockUser' ),
-       'Blockme'                   => array( 'BlockMe' ),
        'Booksources'               => array( 'BookSources' ),
        'BrokenRedirects'           => array( 'BrokenRedirects' ),
        'Categories'                => array( 'Categories' ),
@@ -495,6 +494,12 @@ $specialPageAliases = array(
  */
 $linkTrail = '/^([a-z]+)(.*)$/sD';
 
+/**
+ * Regular expression charset matching the "link prefix", e.g. "foo" in
+ * foo[[bar]]. UTF-8 characters may be used.
+ */
+$linkPrefixCharset = 'a-zA-Z\\x{80}-\\x{10ffff}';
+
 /**
  * List of filenames for some ui images that can be overridden per language
  * basis if needed.
@@ -791,8 +796,6 @@ future releases. Also note that since each list value is wrapped in a unique
 'broken-file-category'           => 'Pages with broken file links',
 'categoryviewer-pagedlinks'      => '($1) ($2)', # only translate this message to other languages if you have to change it
 
-'linkprefix' => '/^(.*?)([a-zA-Z\\x80-\\xff]+)$/sD', # only translate this message to other languages if you have to change it
-
 'about'         => 'About',
 'article'       => 'Content page',
 'newwindow'     => '(opens in new window)',
@@ -1212,7 +1215,7 @@ continue using your old password.',
 'passwordsent'                    => 'A new password has been sent to the email address registered for "$1".
 Please log in again after you receive it.',
 'blocked-mailpassword'            => 'Your IP address is blocked from editing, and so is not allowed to use the password recovery function to prevent abuse.',
-'eauthentsent'                    => 'A confirmation email has been sent to the nominated email address.
+'eauthentsent'                    => 'A confirmation email has been sent to the specified email address.
 Before any other email is sent to the account, you will have to follow the instructions in the email, to confirm that the account is actually yours.',
 'throttled-mailpassword'          => 'A password reset email has already been sent, within the last {{PLURAL:$1|hour|$1 hours}}.
 To prevent abuse, only one password reset email will be sent per {{PLURAL:$1|hour|$1 hours}}.',
@@ -1707,15 +1710,15 @@ Other administrators on {{SITENAME}} will still be able to access the hidden con
 * Inappropriate personal information
 *: ''home addresses and telephone numbers, social security numbers, etc.''",
 'revdelete-legend'            => 'Set visibility restrictions',
-'revdelete-hide-text'         => 'Hide revision text',
+'revdelete-hide-text'         => 'Revision text',
 'revdelete-hide-image'        => 'Hide file content',
 'revdelete-hide-name'         => 'Hide action and target',
-'revdelete-hide-comment'      => 'Hide edit summary',
-'revdelete-hide-user'         => "Hide editor's username/IP address",
+'revdelete-hide-comment'      => 'Edit summary',
+'revdelete-hide-user'         => "Editor's username/IP address",
 'revdelete-hide-restricted'   => 'Suppress data from administrators as well as others',
 'revdelete-radio-same'        => '(do not change)',
-'revdelete-radio-set'         => 'Yes',
-'revdelete-radio-unset'       => 'No',
+'revdelete-radio-set'         => 'Visible',
+'revdelete-radio-unset'       => 'Hidden',
 'revdelete-suppress'          => 'Suppress data from administrators as well as others',
 'revdelete-unsuppress'        => 'Remove restrictions on restored revisions',
 'revdelete-log'               => 'Reason:',
@@ -3400,12 +3403,9 @@ See the [[Special:BlockList|block list]] for the list of currently operational b
 It is, however, blocked as part of the range $2, which can be unblocked.',
 'ip_range_invalid'                => 'Invalid IP range.',
 'ip_range_toolarge'               => 'Range blocks larger than /$1 are not allowed.',
-'blockme'                         => 'Block me',
 'proxyblocker'                    => 'Proxy blocker',
-'proxyblocker-disabled'           => 'This function is disabled.',
 'proxyblockreason'                => 'Your IP address has been blocked because it is an open proxy.
 Please contact your Internet service provider or technical support of your organization and inform them of this serious security problem.',
-'proxyblocksuccess'               => 'Done.',
 'sorbs'                           => 'DNSBL', # only translate this message to other languages if you have to change it
 'sorbsreason'                     => 'Your IP address is listed as an open proxy in the DNSBL used by {{SITENAME}}.',
 'sorbs_create_account_reason'     => 'Your IP address is listed as an open proxy in the DNSBL used by {{SITENAME}}.
@@ -3855,6 +3855,8 @@ This is probably caused by a link to a blacklisted external site.',
 'spam_reverting'      => 'Reverting to last revision not containing links to $1',
 'spam_blanking'       => 'All revisions contained links to $1, blanking',
 'spam_deleting'       => 'All revisions contained links to $1, deleting',
+'simpleantispam-label' => "Anti-spam check.
+Do '''NOT''' fill this in!",
 
 # Info page
 'pageinfo-header'                 => '-', # do not translate or duplicate this message to other languages
@@ -4872,7 +4874,7 @@ You should have received [{{SERVER}}{{SCRIPTPATH}}/COPYING a copy of the GNU Gen
 'redirect'            => 'Redirect by file, user, or revision ID',
 'redirect-legend'     => 'Redirect to a file or page',
 'redirect-text'       => '', # do not translate or duplicate this message to other languages
-'redirect-summary'    => 'This special page redirects to a file (given the file name), a page (given a revision ID), or a user page (given a numeric user ID).',
+'redirect-summary'    => 'This special page redirects to a file (given the file name), a page (given a revision ID), or a user page (given a numeric user ID). Usage: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/revision/328429]], or [[{{#Special:Redirect}}/user/101]].',
 'redirect-submit'     => 'Go',
 'redirect-lookup'     => 'Lookup:',
 'redirect-value'      => 'Value:',
@@ -4958,8 +4960,7 @@ You should have received [{{SERVER}}{{SCRIPTPATH}}/COPYING a copy of the GNU Gen
 
 # Database error messages
 'dberr-header'      => 'This wiki has a problem',
-'dberr-problems'    => 'Sorry!
-This site is experiencing technical difficulties.',
+'dberr-problems'    => 'Sorry! This site is experiencing technical difficulties.',
 'dberr-again'       => 'Try waiting a few minutes and reloading.',
 'dberr-info'        => '(Cannot contact the database server: $1)',
 'dberr-info-hidden' => '(Cannot contact the database server)',
index fb5be50..48979e9 100644 (file)
@@ -38,6 +38,7 @@
  * @author Smeira
  * @author ThomasPusch
  * @author Tlustulimu
+ * @author Umbert'
  * @author Urhixidur
  * @author Yekrats
  * @author Александр Сигачёв
@@ -570,7 +571,7 @@ $1',
 # All link text and link target definitions of links into project namespace that get used by other message strings, with the exception of user group pages (see grouppage).
 'aboutsite' => 'Pri {{SITENAME}}',
 'aboutpage' => 'Project:Enkonduko',
-'copyright' => 'La enhavo estas disponebla laŭ $1.',
+'copyright' => 'La enhavo estas disponebla laŭ $1, se ne estas alia indiko.',
 'copyrightpage' => '{{ns:project}}:Aŭtorrajto',
 'currentevents' => 'Aktualaĵoj',
 'currentevents-url' => 'Project:Aktualaĵoj',
@@ -653,6 +654,7 @@ Listo de validaj specialaj paĝoj estas trovebla ĉe [[Special:SpecialPages|{{in
 # General errors
 'error' => 'Eraro',
 'databaseerror' => 'Datumbaza eraro',
+'databaseerror-error' => 'Eraro: $1',
 'laggedslavemode' => 'Avertu: la paĝo eble ne enhavas lastatempajn ĝisdatigojn.',
 'readonly' => 'Datumaro ŝlosita, nurlega',
 'enterlockreason' => 'Bonvolu klarigi, kial oni ŝlosas la datumaron, kaj
@@ -778,7 +780,7 @@ Ne forgesu ŝanĝi viajn [[Special:Preferences|{{SITENAME}}-preferojn]]',
 'createacct-emailrequired' => 'Retpoŝta adreso',
 'createacct-emailoptional' => 'Retpoŝta adreso (nedeviga)',
 'createacct-email-ph' => 'Enigu vian retpoŝtan adreson',
-'createaccountmail' => 'Uzi provizoran hazardsignan pasvorton kaj sendi ĝin al la retpoŝto suben',
+'createaccountmail' => 'Uzi provizoran hazardsignan pasvorton kaj sendi ĝin al la retpoŝta adreso ĉi-suba',
 'createacct-realname' => 'Vera nomo (nedeviga)',
 'createaccountreason' => 'Kialo:',
 'createacct-reason' => 'Kialo',
@@ -795,6 +797,7 @@ Ne forgesu ŝanĝi viajn [[Special:Preferences|{{SITENAME}}-preferojn]]',
 'userexists' => 'Salutnomo enigita jam estas uzata.
 Bonvolu elekti alian nomon.',
 'loginerror' => 'Ensaluta eraro',
+'createacct-error' => 'Eraro pri kreado de konto',
 'createaccounterror' => 'Ne eblis krei konton: $1',
 'nocookiesnew' => 'La uzantokonto estis kreita sed vi ne estas ensalutinta. *** E-igo lcfirst {{SITENAME}} uzas kuketojn por akcepti uzantojn. Kuketoj esta malaktivigitaj ĉe vi. Bonvolu aktivigi ilin kaj ensalutu per viaj novaj salutnomo kaj pasvorto.',
 'nocookieslogin' => '{{SITENAME}} uzas kuketojn por akcepti uzantojn. Kuketoj esta malaktivigitaj ĉe vi. Bonvolu aktivigi ilin kaj provu denove.',
@@ -830,7 +833,7 @@ registrita por "$1".
 Bonvolu saluti denove ricevinte ĝin.',
 'blocked-mailpassword' => 'Via IP adreso estas forbarita de redaktado, kaj tial
 ne rajtas uzi la pasvorto-rekovran funkcion por malebligi misuzon.',
-'eauthentsent' => 'Konfirma retmesaĝo estas sendita al la nomita retadreso. Antaŭ ol iu ajn alia mesaĝo estos sendita al la konto, vi devos sekvi la instrukciojn en la mesaĝo por konfirmi ke la konto ja estas la via.',
+'eauthentsent' => 'Konfirma retmesaĝo estis sendita al la nomita retadreso. Antaŭ ol iu ajn alia mesaĝo estos sendita al la konto, vi devos sekvi la instrukciojn en la mesaĝo por konfirmi ke la konto ja estas via.',
 'throttled-mailpassword' => 'Retpoŝto kun reŝargita pasvorto estis jam sendita ene de la {{PLURAL:$1|lasta horo|lastaj $1 horoj}}.
 Por preventi misuzon, nur unu reŝargita pasvorto estos sendita dum {{PLURAL:$1|horo|$1 horoj}}.',
 'mailerror' => 'Okazis eraro sendante retpoŝtaĵon: $1',
@@ -852,11 +855,13 @@ Enigi bone formatita adreso aŭ malplenigi tiun kampon.',
 
 Vi povas ignori ĉi mesaĝon, se ĉi konto estis kreita erare.',
 'usernamehasherror' => 'Salutnomo ne povas enhavi kriphaketaĵajn signojn',
-'login-throttled' => 'Vi tro ofte provis ensaluti.
-Bonvolu ĝisatendi antaŭ retrovi.',
+'login-throttled' => 'Vi ĵus tro ofte provis ensaluti.
+Bonvolu ĝisatendi $1 antaŭ reprovi.',
 'login-abort-generic' => 'Via ensaluto malsukcesis - Ĉesigita',
 'loginlanguagelabel' => 'Lingvo: $1',
 'suspicious-userlogout' => 'Via peto por elsaluti estis malpermesita ĉar verŝajne ĝi estis sendita de trompita retumilo aŭ kaŝiganta proksima servilo.',
+'createacct-another-realname-tip' => 'La vera nomo estas nenecesa.
+Se vi decidas indiki ĝin, ĝi estos uzata por montri atribuadon de viaj kontribuoj.',
 
 # Email sending
 'php-mail-error-unknown' => 'Nekonata eraro en la funkcio mail() de PHP',
@@ -872,7 +877,7 @@ Bonvolu ĝisatendi antaŭ retrovi.',
 'newpassword' => 'Nova pasvorto',
 'retypenew' => 'Retajpi novan pasvorton',
 'resetpass_submit' => 'Fari pasvorton kaj ensaluti',
-'changepassword-success' => 'Via pasvorto estis sukcese ŝanĝita! Nun ensalutanta vin...',
+'changepassword-success' => 'Via pasvorto estis sukcese ŝanĝita!',
 'resetpass_forbidden' => 'Pasvortoj ne estas ŝanĝeblaj',
 'resetpass-no-info' => 'Vi devas ensaluti por atingi ĉi tiun paĝon rekte.',
 'resetpass-submit-loggedin' => 'Ŝanĝi pasvorton',
@@ -884,6 +889,7 @@ Vi eble jam ŝanĝis vian pasvorton aŭ petis novan provizoran pasvorton.',
 
 # Special:PasswordReset
 'passwordreset' => 'Restarigo de pasvorto',
+'passwordreset-text-one' => 'Plenigu ĉi tiun formularon por renovigi vian pasvorton.',
 'passwordreset-legend' => 'Refari pasvorton',
 'passwordreset-disabled' => 'Pasvortaj restarigoj estis malŝaltitaj en ĉi tiu vikio.',
 'passwordreset-emaildisabled' => 'Retpoŝtaj funkcioj estas malfunkciigitaj en tiu ĉi vikio.',
@@ -915,7 +921,7 @@ aŭ se vi memoris vian originalan pasvorton, kaj vi ne plu volas ŝanĝi
 Provizora pasvorto: $2',
 'passwordreset-emailsent' => 'Renovigita pasvorto estis retpoŝte sendita.',
 'passwordreset-emailsent-capture' => 'Retpoŝto kun renovigita pasvorto estis sendita, kiu estas montrata sube.',
-'passwordreset-emailerror-capture' => 'Retpoŝto kun renovigita pasvorto estis generita, montrata sube, sed sendado al uzanto malsukcesis: $1',
+'passwordreset-emailerror-capture' => 'Retpoŝto kun renovigita pasvorto estis generita, montrata sube, sed sendado al la {{GENDER:$2|uzanto}} malsukcesis: $1',
 
 # Special:ChangeEmail
 'changeemail' => 'Ŝanĝi retpoŝtadreson',
@@ -929,6 +935,14 @@ Provizora pasvorto: $2',
 'changeemail-submit' => 'Ŝanĝi retpoŝtadreson',
 'changeemail-cancel' => 'Nuligi',
 
+# Special:ResetTokens
+'resettokens-no-tokens' => 'Ne estas ŝlosiloj renovigeblaj.',
+'resettokens-legend' => 'Renovigi ŝlosilojn',
+'resettokens-tokens' => 'Ŝlosiloj:',
+'resettokens-token-label' => '$1 (nuna valoro: $2)',
+'resettokens-done' => 'Ŝlosiloj renovigitaj.',
+'resettokens-resetbutton' => 'Renovigi elektitajn ŝlosilojn',
+
 # Edit page toolbar
 'bold_sample' => 'Grasa teksto',
 'bold_tip' => 'Grasa teksto',
@@ -1135,7 +1149,7 @@ Verŝajne ĝi estis forigita.',
 'content-failed-to-parse' => 'Oni malsukcesis analizi $2-entenon laŭ la $1-modelo: $3',
 'invalid-content-data' => 'Enhavo estas malvalida',
 'content-not-allowed-here' => 'Enhavo de $1 ne estas permesita en paĝo [[$2]]',
-'editwarning-warning' => 'Forlasante ĉi tiun paĝon kaŭzos al vi perdi iun ajn ŝanĝojn kiujn vi faris.
+'editwarning-warning' => 'Forlaso de ĉi tiu paĝo kaŭzos al vi perdi iun ajn ŝanĝojn kiujn vi faris.
 Se vi ensalutas, vi povas malŝalti ĉi tiun averton en la sekcio "{{int:prefs-editing}}" de viaj preferoj.',
 
 # Content models
@@ -1172,6 +1186,7 @@ Bonvolu konfirmi la jenan komparaĵon por verigi ĉi tiel vi volas, kaj konservi
 'undo-failure' => 'Ne povis nuligi redakton pro konfliktaj intermezaj redaktoj.',
 'undo-norev' => 'La redakto ne eblis esti malfarita ĉar ĝi aŭ ne ekzistas aŭ estis forigita.',
 'undo-summary' => 'Nuligis version $1 de [[Special:Contributions/$2|$2]] ([[User talk:$2|Diskuto]] | [[Special:Contributions/$2|{{MediaWiki:Contribslink}}]])',
+'undo-summary-username-hidden' => 'Malfari ŝanĝon $1 de kaŝita uzulo',
 
 # Account creation failure
 'cantcreateaccounttitle' => 'Ne povas krei konton',
@@ -1269,8 +1284,8 @@ Aliaj administrantoj ĉe {{SITENAME}} plu povos aliri la kaŝitan entenon kaj re
 'revdelete-hide-user' => 'Kaŝi nomon aŭ IP-adreson de redaktinto',
 'revdelete-hide-restricted' => 'Subpremi ĉi tiujn datenojn de administrantoj kaj ankaŭ aliaj',
 'revdelete-radio-same' => '(ne ŝanĝi)',
-'revdelete-radio-set' => 'Jes',
-'revdelete-radio-unset' => 'Ne',
+'revdelete-radio-set' => 'Videbla',
+'revdelete-radio-unset' => 'Kaŝita',
 'revdelete-suppress' => 'Subpremi datenojn de kaj administrantoj kaj aliaj',
 'revdelete-unsuppress' => 'Forigi limigojn al restarigitaj versioj',
 'revdelete-log' => 'Kialo:',
@@ -1535,6 +1550,7 @@ indekso pro troŝarĝita servilo. Intertempe, vi povas serĉi per <i>guglo</i> a
 'prefs-displaywatchlist' => 'Montraj opcioj',
 'prefs-tokenwatchlist' => 'Ĵetono',
 'prefs-diffs' => 'Diferencoj',
+'prefs-help-prefershttps' => 'Ĉi tiu agordo ekefikos je via sekva ensaluto.',
 
 # User preference: email validation using jQuery
 'email-address-validity-valid' => 'Ŝajnas ke la retpoŝtadreso estas valida',
@@ -1561,6 +1577,7 @@ indekso pro troŝarĝita servilo. Intertempe, vi povas serĉi per <i>guglo</i> a
 'userrights-notallowed' => 'Via konto ne rajtas doni aŭ forigi uzanto-rajtojn.',
 'userrights-changeable-col' => 'Grupoj kiujn vi povas ŝanĝi',
 'userrights-unchangeable-col' => 'Grupoj kiujn vi ne povas ŝanĝi',
+'userrights-removed-self' => 'Vi sukcese nuligis viajn proprajn rajtojn. Do vi ne plu rajtas aliri ĉi tiun paĝon.',
 
 # Groups
 'group' => 'Grupo:',
@@ -1634,6 +1651,8 @@ indekso pro troŝarĝita servilo. Intertempe, vi povas serĉi per <i>guglo</i> a
 'right-editmyusercss' => 'Redakti viajn proprajn CSS-dosierojn',
 'right-editmyuserjs' => 'Redakti viajn proprajn JavaScript-dosierojn',
 'right-viewmywatchlist' => 'Rigardi vian atentaron',
+'right-viewmyprivateinfo' => 'Vidi viajn proprajn privatajn informojn (ekz. retpoŝtan adreson, veran nomon)',
+'right-editmyprivateinfo' => 'Redakti viajn proprajn privatajn informojn (ekz. retpoŝtan adreson, veran nomon)',
 'right-rollback' => 'Tuj malfari la redaktojn de la lasta uzanto kiu redaktis specifan paĝon',
 'right-markbotedits' => 'Marki restarigitajn redaktojn kiel robotajn redaktojn',
 'right-noratelimit' => 'Ne influita de po-limoj',
@@ -1685,7 +1704,7 @@ indekso pro troŝarĝita servilo. Intertempe, vi povas serĉi per <i>guglo</i> a
 'action-block' => 'forari ĉi tiun uzanton de redaktado',
 'action-protect' => 'ŝanĝi la protektan nivelon por ĉi tiu paĝo',
 'action-rollback' => 'tuj malfari la redaktojn de la lasta uzanto kiu redaktis specifan paĝon',
-'action-import' => 'importi ĉi tiun paĝon de alia vikio',
+'action-import' => 'enporti paĝojn de alia vikio',
 'action-importupload' => 'importi ĉi tiun paĝon de dosiera alŝuto',
 'action-patrol' => 'marki redakton de alia persono kiel patrolitan',
 'action-autopatrol' => 'fari vian redakton markitan kiel patrolitan',
@@ -1695,12 +1714,18 @@ indekso pro troŝarĝita servilo. Intertempe, vi povas serĉi per <i>guglo</i> a
 'action-userrights-interwiki' => 'redakti la rajtojn de uzantoj en aliaj vikioj',
 'action-siteadmin' => 'ŝlosi aŭ malŝlosi la datumbazon',
 'action-sendemail' => 'sendi retpoŝtojn',
+'action-editmywatchlist' => 'modifi vian atento-liston',
+'action-viewmywatchlist' => 'vidi vian atento-liston',
+'action-viewmyprivateinfo' => 'vidi viajn privatajn informojn',
+'action-editmyprivateinfo' => 'redakti viajn privatajn informojn',
 
 # Recent changes
 'nchanges' => '$1 {{PLURAL:$1|ŝanĝo|ŝanĝoj}}',
+'enhancedrc-history' => 'historio',
 'recentchanges' => 'Lastaj ŝanĝoj',
 'recentchanges-legend' => 'Opcioj pri lastaj ŝanĝoj',
 'recentchanges-summary' => 'Per ĉi tiu paĝo vi povas sekvi la plej lastajn ŝanĝojn en la {{SITENAME}}.',
+'recentchanges-noresult' => 'En la donita tempo ne estis ŝanĝoj, kiuj konformas al la kriterioj.',
 'recentchanges-feed-description' => 'Sekvi la plej lastatempajn ŝanĝojn al la vikio en ĉi tiu fonto.',
 'recentchanges-label-newpage' => 'Ĉi tiu redakto kreis novan paĝon',
 'recentchanges-label-minor' => 'Ĉi tiu estas eta redakto',
@@ -2764,11 +2789,8 @@ La kialo donita por la forbaro de $1 estis: "$2"',
 'ipb_blocked_as_range' => 'Eraro: La IP-adreso $1 ne estas forbarita rekte kaj ne povas esti malforbarita. Tamen ĝi estas forbarita kiel parto de la intervalo $2, kiu ne povas esti malforbarita.',
 'ip_range_invalid' => 'Malvalida IP-adresa intervalo.',
 'ip_range_toolarge' => 'IP-adresaj intervaloj pli grandaj ol /$1 estas malpermesataj.',
-'blockme' => 'Forbari min',
 'proxyblocker' => 'Forbarilo por prokuriloj.',
-'proxyblocker-disabled' => 'Ĉi tiu funkcio estas malŝaltita.',
 'proxyblockreason' => 'Via IP-adreso estis forbarita ĉar ĝi estas malferma prokurilo. Bonvolu kontakti vian provizanto de retservo aŭ komputika helpisto kaj informu ilin de ĉi serioza problemo pri sekureco.',
-'proxyblocksuccess' => 'Farita.',
 'sorbsreason' => 'Via IP-adreso estas listigita kiel malferma prokurilo en la DNSBL uzata de {{SITENAME}}.',
 'sorbs_create_account_reason' => 'Via IP-adreso estas listigita kiel malferma prokurilo en la DNSBL uzata de {{SITENAME}}. Vi ne rajtas krei konton.',
 'cant-block-while-blocked' => 'Vi ne povas forbari aliajn uzantojn dum vi estas forbarita.',
@@ -3135,6 +3157,8 @@ Datoj de versioj kaj nomoj de redaktantoj estos preservitaj.
 'spam_reverting' => 'Restarigo de lasta versio ne entenante ligilojn al $1',
 'spam_blanking' => 'Forviŝo de ĉiuj versioj entenantaj ligilojn al $1',
 'spam_deleting' => 'Ĉiuj versioj enhavis ligilojn al $1 - forigante',
+'simpleantispam-label' => 'Kontrolo kontraŭ spamo.
+NE ENIGU ion ajn!',
 
 # Info page
 'pageinfo-title' => 'Informoj por "$1"',
index be82bc2..34455a1 100644 (file)
@@ -2893,11 +2893,8 @@ Consulta la [[Special:BlockList|lista de bloqueos]] para ver la lista de bloqueo
 Sin embargo, está bloqueada como parte del rango $2, que puede ser desbloqueado.',
 'ip_range_invalid' => 'El rango de IP no es válido.',
 'ip_range_toolarge' => 'Los bloqueos de rango superiores a /$1 no están permitidos.',
-'blockme' => 'Bloquearme',
 'proxyblocker' => 'Bloqueador de proxies',
-'proxyblocker-disabled' => 'Esta función está desactivada.',
 'proxyblockreason' => 'Su dirección IP ha sido bloqueada porque es un proxy abierto. Por favor, contacte con su proveedor de servicios de Internet o con su servicio de asistencia técnica e infórmeles de este grave problema de seguridad.',
-'proxyblocksuccess' => 'Hecho.',
 'sorbsreason' => 'Su dirección IP está listada como proxy abierto en DNSBL.',
 'sorbs_create_account_reason' => 'Su dirección IP está listada como proxy abierto en DNSBL. No puede crear una cuenta',
 'xffblockreason' => 'Una dirección IP presente en la cabecera X-Forwarded-For, tuya o del servidor proxy que estás usando, ha sido bloqueada. El motivo original del bloqueo fue: $1',
@@ -3262,6 +3259,8 @@ Esto podría estar causado por un enlace a un sitio externo incluido en la lista
 'spam_reverting' => 'Revirtiendo a la última versión que no contenga enlaces a $1',
 'spam_blanking' => 'Todas las revisiones contienen enlaces a $1, blanqueando',
 'spam_deleting' => 'Todas las revisiones que contienen enlaces a $1, en proceso de eliminación',
+'simpleantispam-label' => 'Comprobación anti-spam
+¡NO rellenes esto!',
 
 # Info page
 'pageinfo-title' => 'Información para «$1»',
@@ -4036,7 +4035,7 @@ Has recibido [{{SERVER}}{{SCRIPTPATH}}/COPYING una copia de la Licencia Pública
 # Special:Redirect
 'redirect' => 'Redirigir por archivo, usuario o ID de revisión',
 'redirect-legend' => 'Redirigir a un archivo o página',
-'redirect-summary' => 'Esta página especial redirige a un fichero (dado un nombre de fichero), a una página (dado un identificador de revisión) o a una página de usuario (dado en identificador numérico de usuario).',
+'redirect-summary' => 'Esta página especial redirige a un fichero (dado un nombre de fichero), a una página (dado un identificador de revisión) o a una página de usuario (dado en identificador numérico de usuario). Uso: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/revision/328429]], o [[{{#Special:Redirect}}/user/101]].',
 'redirect-submit' => 'Ir',
 'redirect-lookup' => 'Buscar:',
 'redirect-value' => 'Valor:',
index 74f1332..ecdae41 100644 (file)
@@ -749,6 +749,9 @@ Pane tähele, et seni kuni sa pole oma võrgulehitseja puhvrit tühjendanud, võ
 'userlogin-resetpassword-link' => 'Lähtesta oma parool',
 'helplogin-url' => 'Help:Sisselogimine',
 'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|Sisselogimisabi]]',
+'userlogin-loggedin' => 'Oled juba sisse logitud nimega {{GENDER:$1|$1}}.
+Kasuta allolevat vormi, et logida sisse teise kasutajaga.',
+'userlogin-createanother' => 'Loo teine konto',
 'createacct-join' => 'Sisesta allapoole oma andmed.',
 'createacct-another-join' => 'Sisesta allpool uue konto andmed.',
 'createacct-emailrequired' => 'E-posti aadress',
@@ -1230,10 +1233,10 @@ Saad seda muudatust vaadata. [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAG
 'revdelete-no-file' => 'Faili ei ole.',
 'revdelete-show-file-confirm' => 'Kas oled kindel, et soovid häha faili "<nowiki>$1</nowiki>" kustutatud redaktsiooni, mis tehti $2 kell $3?',
 'revdelete-show-file-submit' => 'Jah',
-'revdelete-selected' => "'''{{PLURAL:$2|Valitud versioon|Valitud versioonid}} artiklist [[:$1]]:'''",
+'revdelete-selected' => "'''Valitud {{PLURAL:$2|redaktsioon|redaktsioonid}} leheküljest [[:$1]]:'''",
 'logdelete-selected' => "'''Valitud {{PLURAL:$1|logisissekanne|logisissekanded}}:'''",
-'revdelete-text' => "'''Kustutatud redaktsioonid kajastuvad endiselt lehe ajaloos ja logides, kuid osa nende sisust pole tavakasutajatele nähtav.'''
-{{GRAMMAR:genitive|{{SITENAME}}}} administraatorid saavad varjatud sisu siiski vaadata ning seda vajadusel taastada, kui see pole just täiendavalt ära keelatud.",
+'revdelete-text' => "'''Kustutatud redaktsioonid ja sündmused kajastuvad endiselt lehekülje ajaloos ja logides, kuid osa nende sisust pole avalikult nähtav.'''
+{{GRAMMAR:genitive|{{SITENAME}}}} administraatorid saavad peidetud sisu siiski vaadata ning seda vajadusel selle liidese kaudu taastada, kui see pole just täiendavalt keelatud.",
 'revdelete-confirm' => 'Kinnita, et soovid tõesti seda teha ning et saad aru tagajärgedest ja tegevus on kooskõlas [[{{MediaWiki:Policy-url}}|siinsete kokkulepetega]].',
 'revdelete-suppress-text' => "Andmed tuleks varjata '''ainult''' järgnevatel juhtudel:
 * Sobimatu isiklik teave
@@ -2452,10 +2455,12 @@ Palun kinnita, et tahad seda tõepoolest teha, et sa mõistad tagajärgi ja et s
 'deletecomment' => 'Põhjus:',
 'deleteotherreason' => 'Muu või täiendav põhjus:',
 'deletereasonotherlist' => 'Muu põhjus',
-'deletereason-dropdown' => '*Harilikud kustutamise põhjused
-** Autori palve
+'deletereason-dropdown' => '* Harilikud kustutamise põhjused
+** Rämpspostitus
+** Vandalism
 ** Autoriõiguse rikkumine
-** Vandalism',
+** Autori palve
+** Katkine ümbersuunamine',
 'delete-edit-reasonlist' => 'Redigeeri kustutamise põhjuseid',
 'delete-toobig' => 'See lehekülg on pika redigeerimisajalooga – üle {{PLURAL:$1|ühe muudatuse|$1 muudatuse}}.
 Selle kustutamine on keelatud, et ära hoida ekslikku {{GRAMMAR:genitive|{{SITENAME}}}} töö häirimist.',
@@ -2776,11 +2781,8 @@ Blokeering võib juba eemaldatud olla.',
 See kuulub aga blokeeritud IP-vahemikku $2, mille blokeeringut saab eemaldada.',
 'ip_range_invalid' => 'Vigane IP-vahemik.',
 'ip_range_toolarge' => 'Suuremad aadressiblokid kui /$1 pole lubatud.',
-'blockme' => 'Blokeeri mind',
 'proxyblocker' => 'Proksiblokeerija',
-'proxyblocker-disabled' => 'See funktsioon ei toimi.',
 'proxyblockreason' => 'Sinu IP-aadress on blokeeritud, sest see on avatud proksi. Palun võta ühendust oma internetiteenuse pakkujaga või tehnilise toega ja teata neile sellest probleemist.',
-'proxyblocksuccess' => 'Tehtud.',
 'sorbsreason' => 'Sinu IP-aadress on {{GRAMMAR:genitive|{{SITENAME}}}} kasutatavas DNS-põhises mustas nimekirjas märgitud kui avatud proksi.',
 'sorbs_create_account_reason' => 'Sinu IP-aadress on {{GRAMMAR:genitive|{{SITENAME}}}} kasutatavas DNS-põhises mustas nimekirjas märgitud kui avatud proksi.
 Sa ei saa kasutajakontot luua.',
@@ -3128,6 +3130,8 @@ See on ilmselt põhjustatud linkimisest mustas nimekirjas olevasse välisvõrguk
 'spam_reverting' => 'Taastan viimase versiooni, mis ei sisalda linke aadressile $1.',
 'spam_blanking' => 'Kõik versioonid sisaldasid linke veebilehele $1. Lehekülg tühjendatud.',
 'spam_deleting' => 'Kustutatud kõik redaktsioonid, mis viitasid aadressile $1.',
+'simpleantispam-label' => "Rämpspostikontroll.
+'''ÄRA''' täida seda välja!",
 
 # Info page
 'pageinfo-title' => 'Teave lehekülje "$1" kohta',
index 825466d..ff723a9 100644 (file)
@@ -1069,6 +1069,7 @@ Ezin duzu atzitu.',
 'revdelete-no-change' => "'''Abisua:''' $1 $2 data duen elementuak jadanik bazituen eskatutako ikusgaitasun ezarpenak.",
 'revdelete-concurrent-change' => 'Errorea, $1 $2 data duen elementua aldatzean: badirudi haren egoera aldatu duela nor edo nork, zu aldatzen saiatzen ari zinela.
 Begira itzazu erregistroak.',
+'revdelete-only-restricted' => '$2 data duen $1 elementua ezkutatzen arazoa: ezin dira kendu adminstratzaileen ikuskaritzatik elementuak ez badago beste ikusgarritasun aukerarik hautatua.',
 'revdelete-reason-dropdown' => '*Ezabatzeko ohiko arrazoiak
 ** Egile eskubideak urratzea
 ** Informazio pertsonal edo iruzkin desegokia
@@ -1117,7 +1118,7 @@ Kontura zaitez nabigazio loturek, zutabea ezabatu dezakela.',
 
 # Diffs
 'history-title' => '"$1" orrialdearen historia berrikuspena',
-'difference-title' => '"$1"-en berrikuspenen arteko aldaketa',
+'difference-title' => '«$1»: berrikuspenen arteko aldeak',
 'difference-title-multipage' => '"$1" eta "$2" orrialdeen arteko ezberditasunak',
 'difference-multipage' => '(Orrialdeen arteko ezberdintasunak)',
 'lineno' => '$1. lerroa:',
@@ -1271,11 +1272,13 @@ Saia zaitez zure eskeraren aurretik ''all:'' jartzen eduki guztien artean bilatz
 'badsig' => 'Baliogabeko sinadura; egiaztatu HTML etiketak.',
 'badsiglength' => 'Zure sinadura luzeegia da.
 $1 {{PLURAL:$1|karakteretik|karakteretik}} behera izan behar ditu.',
-'yourgender' => 'Generoa:',
-'gender-unknown' => 'Zehaztugabea',
-'gender-male' => 'Gizona',
-'gender-female' => 'Emakumea',
-'prefs-help-gender' => 'Hautazkoa: softwareak generoa zehazteko erabilia. Informazio hau publikoa da.',
+'yourgender' => 'Nola nahiagu duzu deskribatua izatea?',
+'gender-unknown' => 'Nahiago dut ez esatea',
+'gender-male' => 'Wiki orrialdeak editatzen dituen gizona',
+'gender-female' => 'Wiki orrialdeak editatzen dituen emakumea',
+'prefs-help-gender' => 'Hobespen hau jartzea aukerazkoa da.
+Softwareak bere balioak erabiltzen ditu zu aipatzeko eta beste batzuek genero gramatikala erabiltzeko aukera izan dezaten.
+Informazio hau publikoa da.',
 'email' => 'E-posta',
 'prefs-help-realname' => '* Benetako izena (aukerakoa): zehaztea erabakiz gero, zure lanarentzako atribuzio bezala balioko du.',
 'prefs-help-email' => 'E-posta helbidea aukerakoa da, baina zure pasahitza ahaztekotan berriro zure e-postara bidaltzeko aukera ematen dizu.',
@@ -1286,7 +1289,7 @@ $1 {{PLURAL:$1|karakteretik|karakteretik}} behera izan behar ditu.',
 'prefs-signature' => 'Sinadura',
 'prefs-dateformat' => 'Data-formatua',
 'prefs-timeoffset' => 'Denbora ezberdintasuna',
-'prefs-advancedediting' => 'Aukera aurreratuak',
+'prefs-advancedediting' => 'Genero aukerak',
 'prefs-advancedrc' => 'Aukera aurreratuak',
 'prefs-advancedrendering' => 'Aukera aurreratuak',
 'prefs-advancedsearchoptions' => 'Aukera aurreratuak',
@@ -1486,7 +1489,7 @@ $1 {{PLURAL:$1|karakteretik|karakteretik}} behera izan behar ditu.',
 'rc_categories_any' => 'Edozein',
 'rc-change-size-new' => '{{PLURAL:$1|Byte 1|$1 byte}} aldaketaren ostean',
 'newsectionsummary' => '/* $1 */ atal berria',
-'rc-enhanced-expand' => 'Erakutsi xehetasunak (JavaScript beharrezkoa da)',
+'rc-enhanced-expand' => 'Erakutsi xehetasunak',
 'rc-enhanced-hide' => 'Xehetasunak ezkutatu',
 'rc-old-title' => 'hasiera batean "$1" gisa sortua',
 
@@ -1616,6 +1619,7 @@ $1',
 'upload-too-many-redirects' => 'URLak birzuzenketa gehiegi zituen',
 'upload-unknown-size' => 'Tamaina ezezaguna',
 'upload-http-error' => 'HTTP errorea gertatu da: $1',
+'upload-copy-upload-invalid-domain' => 'Domeinu honetan ezin dira igoerak kopiatu.',
 
 # File backend
 'backend-fail-stream' => 'Ezin izan da "$1" fitxategiaren stream egin.',
@@ -1625,6 +1629,7 @@ $1',
 'backend-fail-notsame' => 'Berdina ez den beste fitxategi bat dago "$1"n',
 'backend-fail-invalidpath' => '"$1" ez da gordetzeko helbide baliagarria.',
 'backend-fail-delete' => 'Ezin izan da ezabatu "$1" fitxategia.',
+'backend-fail-describe' => 'Ezin dira "$1" fitxategiaren metadatuak aldatu.',
 'backend-fail-alreadyexists' => '"$1" fitxategia jadanik badago.',
 'backend-fail-store' => 'Ezin izan da gorde "$1" fitxategia "$2" helbidean.',
 'backend-fail-copy' => 'Ezin izan da kopiatu "$1" fitxategia "$2" helbidean.',
@@ -1639,11 +1644,15 @@ $1',
 # Lock manager
 'lockmanager-notlocked' => 'Ezin izan da "$1" askatu; ez dago itxita.',
 'lockmanager-fail-closelock' => 'Ezin izan da "$1" fitxategiaren giltza itxi.',
+'lockmanager-fail-deletelock' => 'Ezin izan da "$1" fitxategia desblokeatu.',
+'lockmanager-fail-acquirelock' => 'Ezin izan da "$1" blokeoa eskuratu.',
+'lockmanager-fail-openlock' => 'Ezin izan da "$1" blokeo fitxategia ireki.',
 
 # ZipDirectoryReader
 'zip-wrong-format' => 'Zehaztutako fitxategia ez zen ZIP motakoa.',
 
 # Special:UploadStash
+'uploadstash' => 'Gordailu bat igo',
 'uploadstash-refresh' => 'Fitxategien zerrenda eguneratu',
 
 # img_auth script messages
@@ -2288,7 +2297,7 @@ $1',
 'contributions' => '{{GENDER:$1|Lankidearen}} ekarpenak',
 'contributions-title' => '$1(r)entzat lankidearen ekarpenak',
 'mycontris' => 'Ekarpenak',
-'contribsub2' => '$1 ($2)',
+'contribsub2' => '{{GENDER:$3|$1(r)entzat}} ($2)',
 'nocontribs' => 'Ez da ezaugarri horiekin bat datorren aldaketarik aurkitu.',
 'uctop' => '(azken aldaketa)',
 'month' => 'Hilabetea (eta lehenagokoak):',
@@ -2438,11 +2447,8 @@ Ikus [[Special:BlockList|blokeoen zerrenda]] aktibo dauden blokeoak eta debekuak
 Hala ere, $2-(r)en parte denez, blokeoa kendu daiteke.',
 'ip_range_invalid' => 'Baliogabeko IP eremua.',
 'ip_range_toolarge' => '/$1 baino handiagoak diren blokeo eremuak ezin dira eskuratu.',
-'blockme' => 'Blokea nazazu',
 'proxyblocker' => 'Proxy blokeatzailea',
-'proxyblocker-disabled' => 'Funtzio hau ez-gaitua dago.',
 'proxyblockreason' => 'Zure IP helbidea blokeatu egin da proxy ireki baten zaudelako. Mesedez, zure Interneteko Zerbitzu Hornitzailearekin harremanetan jar zaitez segurtasun arazo honetaz ohartarazteko.',
-'proxyblocksuccess' => 'Egina.',
 'sorbsreason' => 'Zure IP helbidea proxy ireki bezala zerrendatuta dago DNSBLan.',
 'sorbs_create_account_reason' => 'Zure IP helbidea proxy ireki bezala zerrendatuta dago DNSBLan. Ezin duzu kontua sortu.',
 'cant-block-while-blocked' => 'Blokeatuta zauden bitartean ezin dituzu beste lankideak blokeatu.',
@@ -2510,7 +2516,7 @@ Kasu horietan orrialdea eskuz mugitu edo bestearekin bateratu beharko duzu.",
 'move-subpages' => 'Azpiorrialde guztiak ($1-tik gora) mugitu',
 'move-talk-subpages' => 'Azpiorrialdeen eztabaida orrialde guztiak ($1-tik gora) mugitu',
 'movepage-page-exists' => '$1 orrialdea jada badago eta ezin da automatikoki gainetik idatzi.',
-'movepage-page-moved' => '$1 orria $2 izenera aldatu da.',
+'movepage-page-moved' => '«$1» orria «$2» izenera aldatu da.',
 'movepage-page-unmoved' => '$1 orrialdea ezin da $2(e)ra mugitu.',
 'movepage-max-pages' => '$1 {{PLURAL:$1|orrialderen|orrialdeen}} maximoa mugitu da eta jada ez dira gehiago mugituko modu automatikoan.',
 'movelogpage' => 'Mugimendu erregistroa',
@@ -2554,6 +2560,7 @@ Horrez gain, lotura zuzena ere erabil dezakezu; adibidez, [[{{#Special:Export}}/
 'exportcuronly' => 'Oraingo berrikuspena bakarrik hartu, ez historia guztia',
 'exportnohistory' => "----
 '''Oharra:''' Formulario honen bitartez orrialdeen historia osoak esportatzeko aukera ezgaitu egin da, errendimendua dela-eta.",
+'exportlistauthors' => 'Orrialde bakoitzaren lankideen zerrenda osoa sartu',
 'export-submit' => 'Esportatu',
 'export-addcattext' => 'Orrialdeak gehitu kategoria honetatik:',
 'export-addcat' => 'Gehitu',
@@ -2586,6 +2593,7 @@ Mesedez bisitatu [//www.mediawiki.org/wiki/Localisation MediaWiki] eta [//transl
 'thumbnail_error' => 'Errorea irudi txikia sortzerakoan: $1',
 'djvu_page_error' => 'DjVu orrialdea eremuz kanpo',
 'djvu_no_xml' => 'Ezinezkoa izan da DjVu fitxategiaren XML lortzea',
+'thumbnail-temp-create' => 'Ezin izan da behin-behineko iruditxoa sortu',
 'thumbnail-dest-create' => 'Ezin izan da iruditxoa gorde helburuan',
 'thumbnail_invalid_params' => 'Irudi txikiaren ezarpenak ez dira baliagarriak',
 'thumbnail_dest_directory' => 'Ezinezkoa izan da helburu direktorioa sortu',
@@ -2744,6 +2752,9 @@ Baliteke zerrenda beltzean dagoen kanpo lotura batek sortzea arazo hori.',
 'spambot_username' => 'MediaWikiren spam garbiketa',
 'spam_reverting' => '$1(e)rako loturarik ez daukan azken bertsiora itzultzen',
 'spam_blanking' => 'Berrikuspen guztiek $1(e)rako lotura zeukaten, husten',
+'spam_deleting' => '$1(e)ra loturak dituzten errebisio guztiak ezabatzen',
+'simpleantispam-label' => "Anti-spam egiaztapena.
+Atal hau '''EZ''' bete!",
 
 # Info page
 'pageinfo-title' => '"$1"(r)entzako informazioa',
@@ -3048,6 +3059,7 @@ Zerrenda elementuak (hasieran * duten lerroak) baino ez dira kontuan hartzen. Le
 'exif-source' => 'Jatorria',
 'exif-editstatus' => 'Irudiaren egoera editoriala',
 'exif-urgency' => 'Larrialdia',
+'exif-fixtureidentifier' => 'Konpontzearen izena',
 'exif-locationdest' => 'Agertzen den lekua',
 'exif-locationdestcode' => 'Agertzen den lekuaren kodea',
 'exif-objectcycle' => 'Media hau baliagarria den egunaren ordua',
@@ -3059,11 +3071,14 @@ Zerrenda elementuak (hasieran * duten lerroak) baino ez dira kontuan hartzen. Le
 'exif-iimsupplementalcategory' => 'Kategoria gehigarriak',
 'exif-datetimeexpires' => 'Ez erabili data hau pasata:',
 'exif-datetimereleased' => 'Ekoizpen data:',
+'exif-originaltransmissionref' => 'Trasmisio originalaren kokapen kodea',
 'exif-identifier' => 'Identifikatzailea',
 'exif-lens' => 'Erabilitako lentea',
 'exif-serialnumber' => 'Kameraren serie-zenbakia',
 'exif-cameraownername' => 'Kameraren jabea',
 'exif-label' => 'Etiketa',
+'exif-datetimemetadata' => 'Datuaren metadata azken aldiz aldatu da',
+'exif-nickname' => 'Irudiaren izen ez-formala',
 'exif-rating' => 'Balorazioa (5 arte)',
 'exif-rightscertificate' => 'Eskubideen kudeaketa ziurtagiria',
 'exif-copyrighted' => 'Copyright egoera',
@@ -3090,6 +3105,7 @@ Zerrenda elementuak (hasieran * duten lerroak) baino ez dira kontuan hartzen. Le
 
 # Exif attributes
 'exif-compression-1' => 'Konprimatu gabe',
+'exif-compression-2' => 'CCITT Group 3 1-Dimensional Modified Huffman kodetzea abiatu da',
 'exif-compression-6' => 'JPEG',
 
 'exif-copyrighted-true' => 'Copyrightduna',
@@ -3584,9 +3600,9 @@ Halaber [[Special:EditWatchlist|aldatzaile estandarra]] erabil dezakezu.',
 'revdelete-uname-unhid' => 'lankide ezkutua erakutsi',
 'revdelete-restricted' => 'administratzaileentzako mugak ezarri dira',
 'revdelete-unrestricted' => 'administratzaileentzako mugak kendu dira',
-'logentry-move-move' => '$1 {{GENDER:$2|wikilariak}} $3 orria $4 izenera aldatu du',
+'logentry-move-move' => '$1 {{GENDER:$2|wikilariak}} «$3» orria «$4» izenera aldatu du',
 'logentry-move-move-noredirect' => '$1 {{GENDER:$2|wikilariak}} $3 orria $4 izenera aldatu du, birzuzenketarik utzi gabe',
-'logentry-move-move_redir' => '$1 {{GENDER:wikilariak}} «$3» orria «$4» izenera aldatu du, birzuzenketaren gainetik',
+'logentry-move-move_redir' => '$1 {{GENDER:$2|wikilariak}} «$3» orria «$4» izenera aldatu du, birzuzenketaren gainetik',
 'logentry-move-move_redir-noredirect' => '$1 {{GENDER:wikilariak}} «$3» orria «$4» izenera aldatu du, birzuzenketa bat gainidatzita, birzuzenketarik utzi gabe',
 'logentry-patrol-patrol' => '$1(e)k $3 orrialdearen $4 berrikuzpena patruilatutzat {{GENDER:$2|markatu}} du',
 'logentry-newusers-newusers' => '$1 erabiltzaile kontua sortu da',
@@ -3599,7 +3615,9 @@ Halaber [[Special:EditWatchlist|aldatzaile estandarra]] erabil dezakezu.',
 'feedback-message' => 'Mezua:',
 'feedback-cancel' => 'Utzi',
 'feedback-submit' => 'Feedbacka bidali',
+'feedback-error1' => 'Akatsa: APIaren emaitza ez ezagunak',
 'feedback-error2' => 'Akatsa: Aldaketa ez da egin',
+'feedback-error3' => 'Akatsa: APIaren erantzunik gabe',
 'feedback-close' => 'Egina',
 'feedback-bugnew' => 'Txekeatu dut. Bug berria bidaliko',
 
@@ -3610,11 +3628,25 @@ Halaber [[Special:EditWatchlist|aldatzaile estandarra]] erabil dezakezu.',
 # API errors
 'api-error-badaccess-groups' => 'Ez duzu baimendik fitxategi hauek wiki honetara igotzeko.',
 'api-error-badtoken' => 'Barne akatsa: token okerra.',
+'api-error-empty-file' => 'Bidali duzun fitxategia hutsik dago.',
+'api-error-emptypage' => 'Berria sortzerako garaian orrialde hutsak ezin dira erabili.',
+'api-error-fetchfileerror' => 'Barne akatsa: zerbait gaizki joan da fitxategia eskuratzerakoan.',
+'api-error-file-too-large' => 'Bidali duzun fitxategia handiegia zen.',
 'api-error-filename-tooshort' => 'Fitxategiaren izena laburregia da.',
 'api-error-filetype-banned' => 'Mota horretako fitxategiak debekatuta daude.',
+'api-error-filetype-missing' => 'Fitxategiak ez zuen luzapenik.',
 'api-error-illegal-filename' => 'Fitxategiaren izena ez da onartzen.',
+'api-error-mustbeloggedin' => 'Fitxategiak igotzeko izena emanda eduki behar duzu.',
+'api-error-mustbeposted' => 'Barne arazoa: HTTP POST beharrezkoa da.',
+'api-error-noimageinfo' => 'Igoera ondo egin da, baina zerbitzariak ez digu informaziorik eman zerbitzariaren inguruan.',
+'api-error-nomodule' => 'Barne arazoa: igoera modulurik ez dago.',
+'api-error-ok-but-empty' => 'Barne arazoa: zerbitzariaren erantzunik ez.',
+'api-error-overwrite' => 'Existitzen den fitxategi bat gain-idaztea ez da posible.',
+'api-error-stashfailed' => 'Barne arazoa: Zerbitzariak ezin izan du behin-behineko fitxategia gorde',
+'api-error-timeout' => 'Zerbitzariak ez du erantzun espero zitekeen denboran.',
 'api-error-unclassified' => 'Ezezaguna den errorea gertatu da.',
 'api-error-unknown-code' => 'Akats ezezaguna: "$1".',
+'api-error-unknown-error' => 'Barne arazoa: fitxategia igotzen saiatzerakoan zerbait gaizki egon da.',
 'api-error-unknown-warning' => 'Ohartarazpen ezezaguna: "$1".',
 'api-error-unknownerror' => 'Akats ezezaguna: "$1".',
 'api-error-uploaddisabled' => 'Wiki honetan ezin dira igoerak egin.',
index 6b0eefb..cf6a54d 100644 (file)
@@ -1663,11 +1663,8 @@ Escrebi una razón concreta embahu (pol sabulugal, almientandu páhinas qu'aigan
 'ipb_cant_unblock' => "Marru: Nu s'á alcuentrau el tarugu con ID $1. Es posibri que ya aiga siu desatarugau.",
 'ipb_blocked_as_range' => "Marru: La IP $1 nu s'alcuentra atarugá diretamenti, polo que nu puei sel desatarugá. Nu ostanti, hue atarugá cumu parti el intervalu $2, que puei sel desatarugau.",
 'ip_range_invalid' => "Rangu d'IP nu premitiu.",
-'blockme' => 'Atarugami',
 'proxyblocker' => 'Tarugaol de proxys',
-'proxyblocker-disabled' => "Esta hunción s'alcuentra desativá.",
 'proxyblockreason' => "La tu direción IP á siu atarugá polque es un proxy abiertu. Pol favol, contauta con el tu proveol de sirvicius d'Internet u con el tu sirviciu d'asisténcia télefónica i enhórmalus desti gravi pobrema e seguráncia.",
-'proxyblocksuccess' => 'Hechu.',
 'sorbsreason' => 'La tu direción IP apaici ena lista e proxys abiertus en DNSBL gastá pol {{SITENAME}}.',
 'sorbs_create_account_reason' => 'La tu direción IP apaici ena lista e proxys abiertus en DNSBL gastá pol {{SITENAME}}. Nu se te premiti crial una cuenta',
 
index ca5e467..f579209 100644 (file)
@@ -606,7 +606,7 @@ $messages = array(
 'vector-action-protect' => 'محافظت',
 'vector-action-undelete' => 'احیا',
 'vector-action-unprotect' => 'تغییر سطح حفاظت',
-'vector-simplesearch-preference' => 'فعال کردن نوار جستجوی ساده‌شده (فقط در پوستهٔ برداری)',
+'vector-simplesearch-preference' => 'فعالکردن نوار جستجوی ساده‌شده (فقط در پوستهٔ برداری)',
 'vector-view-create' => 'ایجاد',
 'vector-view-edit' => 'ویرایش',
 'vector-view-history' => 'نمایش تاریخچه',
@@ -655,7 +655,7 @@ $messages = array(
 'articlepage' => 'نمایش مقاله',
 'talk' => 'بحث',
 'views' => 'بازدیدها',
-'toolbox' => 'جعبÙ\87â\80\8cابزار',
+'toolbox' => 'ابزارÙ\87ا',
 'userpage' => 'نمایش صفحهٔ کاربر',
 'projectpage' => 'دیدن صفحهٔ پروژه',
 'imagepage' => 'نمایش صفحهٔ پرونده',
@@ -761,11 +761,11 @@ $1',
 
 # Main script and global functions
 'nosuchaction' => 'چنین عملی وجود ندارد',
-'nosuchactiontext' => 'عمل مشخص‌شده در نشانی اینترنتی غیرمجاز است.
+'nosuchactiontext' => 'عمل مشخص‌شده در نشانی اینترنتی نامجاز است.
 ممکن است نشانی اینترنتی را اشتباه وارد کرده باشید یا پیوند مشکل‌داری را دنبال کرده باشید.
 همچنین ممکن است ایرادی در نرم‌افزار استفاده‌شده در {{SITENAME}} وجود داشته باشد.',
 'nosuchspecialpage' => 'چنین صفحهٔ ویژه‌ای وجود ندارد',
-'nospecialpagetext' => '<strong>شما یک صفحهٔ ویژهٔ غیرمجاز را درخواست کرده‌اید.</strong>
+'nospecialpagetext' => '<strong>شما یک صفحهٔ ویژهٔ نامجاز را درخواست کرده‌اید.</strong>
 
 فهرستی از صفحه‌های ویژهٔ مجاز در [[Special:SpecialPages|{{int:specialpages}}]] وجود دارد.',
 
@@ -1053,7 +1053,7 @@ $2
 شما باید هم‌اکنون وارد شده و یک گذرواژهٔ جدید برگزینید. اگر شخص دیگری این درخواست را داده است، یا اگر گذرواژهٔ اصلی‌تان را به خاطر آوردید و دیگر نمی‌خواهید آن را تغییر دهید، می‌توانید این پیغام را نادیده بگیرید و به استفاده از گذرواژهٔ قبلی‌تان ادامه دهید.',
 'passwordreset-emailelement' => 'نام کاربری: $1
 گذرواژهٔ موقت: $2',
-'passwordreset-emailsent' => 'یک نامهٔ بازنشانی گذرواژه فرستاده شده است.',
+'passwordreset-emailsent' => 'یک نامهٔ بازنشانی گذرواژه فرستاده شدهاست.',
 'passwordreset-emailsent-capture' => 'یک رایانامهٔ بازنشانی که در پایین نمایش داده شده، فرستاده شده است.',
 'passwordreset-emailerror-capture' => 'رایانامهٔ بازنشانی، که در زیر نمایش داده شده، ایجاد شد، ولی ارسال آن به {{GENDER:$2|کاربر}} موفقیت‌آمیز نبود: $1',
 
@@ -1077,7 +1077,7 @@ $2
 'resettokens-legend' => 'بازنشانی شناساننده‌ها',
 'resettokens-tokens' => 'شناساننده‌ها:',
 'resettokens-token-label' => '$1 (مقدار کنونی: $2)',
-'resettokens-watchlist-token' => 'شناساننده Ø¨Ø±Ø§Û\8c Ø®Ù\88راک Ù\88بÙ\90 [[Special:Watchlist|تغÛ\8cÛ\8cرات ØµÙ\81Ø­Ù\87â\80\8cÙ\87اÛ\8cÛ\8c Ú©Ù\87 Ù¾Û\8cÚ¯Û\8cرÛ\8c Ù\85Û\8câ\80\8cÚ©Ù\86Û\8cد]] (اتÙ\85/آراسâ\80\8cاس)',
+'resettokens-watchlist-token' => 'شناسانندهÙ\94 Ø®Ù\88راک Ù\88بÙ\90Û\8c [[Special:Watchlist|تغÛ\8cÛ\8cرات ØµÙ\81Ø­Ù\87â\80\8cÙ\87اÛ\8cÛ\8c Ú©Ù\87 Ù¾Û\8câ\80\8cÚ¯Û\8cرÛ\8c Ù\85Û\8câ\80\8cÚ©Ù\86Û\8cد]] (اتÙ\85/آراسâ\80\8cاس)',
 'resettokens-done' => 'بازنشانی شناساننده‌ها.',
 'resettokens-resetbutton' => 'بازشناسی شناساننده‌های گزیده‌شده.',
 
@@ -1402,11 +1402,11 @@ $2
 'rev-delundel' => 'نمایش/نهفتن',
 'rev-showdeleted' => 'نمایش',
 'revisiondelete' => 'حذف/احیای نسخه‌ها',
-'revdelete-nooldid-title' => 'نسخه هدف غیرمجاز',
+'revdelete-nooldid-title' => 'نسخهٔ هدف نامجاز',
 'revdelete-nooldid-text' => 'شما نسخه‌های هدف را برای انجام این عمل مشخص نکرده‌اید یا این نسخه‌ها وجود ندارند، یا این که شما می‌خواهید آخرین نسخه را پنهان کنید.',
 'revdelete-nologtype-title' => 'نوع سیاهه مشخص نشده‌است',
 'revdelete-nologtype-text' => 'شما هیچ نوع سیاهه‌ای را برای این کار مشخص نکردید.',
-'revdelete-nologid-title' => 'مورد غیرمجاز در سیاهه',
+'revdelete-nologid-title' => 'مدخل نامجاز سیاهه',
 'revdelete-nologid-text' => 'شما یا رویدادی را در سیاههٔ هدف مشخص نکردید یا موردی را مشخص کردید که وجود ندارد.',
 'revdelete-no-file' => 'پروندهٔ مشخص شده وجود ندارد.',
 'revdelete-show-file-confirm' => 'آیا مطمئن هستید که می‌خواهید یک نسخهٔ حذف شده از پروندهٔ «<nowiki>$1</nowiki>» مورخ $2 ساعت $3 را ببینید؟',
@@ -1421,15 +1421,15 @@ $2
 * اطلاعات نامناسب شخصی
 *: ''نشانی منزل، شماره تلفن، شماره تامین اجتماعی و غیره.''",
 'revdelete-legend' => 'تنظیم محدودیت‌های پیدایی',
-'revdelete-hide-text' => 'Ù\86Ù\87Ù\81تÙ\86 Ù\85تÙ\86 Ù\86سخÙ\87',
+'revdelete-hide-text' => 'متن نسخه',
 'revdelete-hide-image' => 'نهفتن محتویات پرونده',
 'revdelete-hide-name' => 'نهفتن عمل و هدف',
-'revdelete-hide-comment' => 'نهفتن توضیح ویرایش',
-'revdelete-hide-user' => 'نام کاربری/نشانی آی‌پی ویراستار پنهان شود',
+'revdelete-hide-comment' => 'خلاصهٔ ویرایش',
+'revdelete-hide-user' => 'نام کاربری/نشانی آی‌پی',
 'revdelete-hide-restricted' => 'فرونشانی اطلاعات برای مدیران به همراه دیگران',
 'revdelete-radio-same' => '(بدون تغییر)',
-'revdelete-radio-set' => 'بله',
-'revdelete-radio-unset' => 'خیر',
+'revdelete-radio-set' => 'نمایان',
+'revdelete-radio-unset' => 'مخفی',
 'revdelete-suppress' => 'از دسترسی مدیران به داده نیز مانند سایر کاربران جلوگیری به عمل آید.',
 'revdelete-unsuppress' => 'خاتمهٔ محدودیت‌ها در مورد نسخه‌های انتخاب شده',
 'revdelete-log' => 'دلیل:',
@@ -1665,8 +1665,8 @@ $1",
 'yourvariant' => 'گویش زبان محتوا:',
 'prefs-help-variant' => 'گویش انتخابی شما برای نمایش محتوای صفحه‌ها در این ویکی.',
 'yournick' => 'امضای جدید:',
-'prefs-help-signature' => 'نظرهای نوشته شده در صفحهٔ بحث باید با «<nowiki>~~~~</nowiki>» امضا شوند؛ این علامت به طور خودکار به امضای شما و مهر تاریخ تبدیل خواهد شد.',
-'badsig' => 'امضای خام غیرمجاز.
+'prefs-help-signature' => 'نظرهای نوشته‌شده در صفحهٔ بحث باید با «<nowiki>~~~~</nowiki>» امضا شوند؛ این علامت به‌صورت خودکار به امضای شما و مهر تاریخ تبدیل خواهد شد.',
+'badsig' => 'امضای خام نامجاز.
 لطفاً برچسب‌های اچ‌تی‌ام‌ال را بررسی کنید.',
 'badsiglength' => 'امضای شما بیش از اندازه طولانی است.
 امضا باید کمتر از $1 {{PLURAL:$1|نویسه}} طول داشته باشد.',
@@ -1675,7 +1675,7 @@ $1",
 'gender-male' => 'مرد',
 'gender-female' => 'زن',
 'prefs-help-gender' => 'انجام این تنظیم اختیاری است.
-نرم‌افزار از این مقدار برای اشارهٔ صحیح به جنسیت شما و ذکر شما برای دیگران با استفاده از قوائد درست دستور زبان استفاده می‌کند.
+نرم‌افزار از این مقدار برای اشارهٔ صحیح به جنسیت و ذکر شما برای دیگران با استفاده از دستور زبان درست استفاده می‌کند.
 این اطلاعات عمومی خواهند بود.',
 'email' => 'رایانامه',
 'prefs-help-realname' => 'نام واقعی اختیاری است.
@@ -1794,7 +1794,7 @@ $1",
 'right-ipblock-exempt' => 'تاثیر نپذیرفتن از قطع دسترسی‌های آی‌پی، خودکار یا فاصله‌ای',
 'right-proxyunbannable' => 'تاثیر نپذیرفتن از قطع دسترسی خودکار پروکسی‌ها',
 'right-unblockself' => 'بازکردن دسترسی خود',
-'right-protect' => 'تغییر میزان محافظت صفحه‌ها و ویرایش صفحه‌های محافظت شده آبشاری',
+'right-protect' => 'تغییر میزان محافظت صفحه‌ها و ویرایش صفحه‌های محافظتشده آبشاری',
 'right-editprotected' => 'ویرایش صفحه‌های محافظت شده به عنوان "{{int:protect-level-sysop}}"',
 'right-editsemiprotected' => 'ویرایش صفحه حفاظت‌شده به عنوان "{{int:protect-level-autoconfirmed}}"',
 'right-editinterface' => 'ویرایش واسط کاربری',
@@ -1811,12 +1811,12 @@ $1",
 'right-rollback' => 'واگردانی سریع ویرایش‌های آخرین کاربری که یک صفحه را ویرایش کرده‌است',
 'right-markbotedits' => 'علامت‌زدن ویرایش‌های واگردانی‌شده به‌عنوان ویرایش ربات',
 'right-noratelimit' => 'تاثیر نپذیرفتن از محدودیت سرعت',
-'right-import' => 'وارد کردن صفحه از ویکی‌های دیگر',
-'right-importupload' => 'وارد کردن صفحه از طریق بارگذاری پرونده',
+'right-import' => 'واردکردن صفحه از ویکی‌های دیگر',
+'right-importupload' => 'واردکردن صفحه از طریق بارگذاری پرونده',
 'right-patrol' => 'گشت زدن به ویرایش‌های دیگران',
 'right-autopatrol' => 'گشن زدن خودکار به ویرایش‌های خودش',
 'right-patrolmarks' => 'مشاهدهٔ برچسب گشت تغییرات اخیر',
-'right-unwatchedpages' => 'مشاهدهٔ فهرست صفحه‌هایی که پیگیری نمی‌شوند',
+'right-unwatchedpages' => 'مشاهدهٔ فهرست صفحه‌هایی که پیگیری نمی‌شوند',
 'right-mergehistory' => 'ادغام تاریخچهٔ صفحه‌ها',
 'right-userrights' => 'ویرایش تمام اختیارات کاربرها',
 'right-userrights-interwiki' => 'ویرایش اختیارات کاربرهای ویکی‌های دیگر',
@@ -1852,14 +1852,14 @@ $1",
 'action-delete' => 'حذف این صفحه',
 'action-deleterevision' => 'حذف این نسخه',
 'action-deletedhistory' => 'مشاهدهٔ تاریخچهٔ حذف شدهٔ این صفحه',
-'action-browsearchive' => 'جستجوی صفحه‌های حذف شده',
+'action-browsearchive' => 'جستجوی صفحه‌های حذفشده',
 'action-undelete' => 'احیای این صفحه',
 'action-suppressrevision' => 'مشاهده و احیای ویرایش‌های حذف شده',
 'action-suppressionlog' => 'مشاهدهٔ این سیاههٔ خصوصی',
-'action-block' => 'قطع دسترسی این کاربر از ویرایش کردن',
+'action-block' => 'قطع دسترسی این کاربر از ویرایشکردن',
 'action-protect' => 'تغییر سطح محافظت این صفحه',
 'action-rollback' => 'واگردانی سریع ویرایش‌های آخرین کاربری که یک صفحه را ویرایش کرده‌است',
-'action-import' => 'وارد کردن صفحه از ویکی های دیگر',
+'action-import' => 'واردکردن صفحه از ویکی های دیگر',
 'action-importupload' => 'واردکردن صفحه از طریق بارگذاری پرونده',
 'action-patrol' => 'گشت زدن ویرایش دیگران',
 'action-autopatrol' => 'گشت زدن ویرایش خودتان',
@@ -1945,7 +1945,7 @@ $1",
 *'''<code><nowiki>[[</nowiki>{{ns:media}}<nowiki>:File.ogg]]</nowiki></code>''' برای ایجاد یک پیونده مستقیم به پرونده بدون نمایش پرونده",
 'upload-permitted' => 'انواع مجاز پرونده‌ها: $1.',
 'upload-preferred' => 'انواع ترجیح‌داده شده پرونده‌ها: $1.',
-'upload-prohibited' => 'انواع غیرمجاز پرونده‌ها: $1.',
+'upload-prohibited' => 'انواع نامجاز پرونده‌ها: $1.',
 'uploadlog' => 'سیاههٔ بارگذاری‌ها',
 'uploadlogpage' => 'سیاههٔ بارگذاری‌ها',
 'uploadlogpagetext' => 'فهرست زیر فهرستی از آخرین بارگذاری پرونده‌ها است.
@@ -1966,11 +1966,11 @@ $1",
 'badfilename' => 'نام پرونده به «$1» تغییر کرد.',
 'filetype-mime-mismatch' => 'پسوند پرونده «$1.‎» با نوع MIME آن ($2) مطابقت ندارد.',
 'filetype-badmime' => 'پرونده‌هایی که نوع MIME آن‌ها $1 باشد برای بارگذاری مجاز نیستند.',
-'filetype-bad-ie-mime' => 'این پرونده را نمی‌توانید بارگذاری کنید زیرا اینترنت اکسپلورر آن را به عنوان «$1» تشخیص می‌دهد، که یک نوع پروندهٔ غیرمجاز و احتمالاً خطرناک است.',
+'filetype-bad-ie-mime' => 'این پرونده را نمی‌توانید بارگذاری کنید زیرا اینترنت اکسپلورر آن را به‌عنوان «$1» تشخیص می‌دهد، که یک نوع پروندهٔ نامجاز و احتمالاً خطرناک است.',
 'filetype-unwanted-type' => "'''«‎.‎$1»''' یک نوع پرونده ناخواسته است.
 {{PLURAL:$3|نوع پرونده ترجیح داده شده|انواع پرونده ترجیح داده شده}} از این قرار است: $2 .",
-'filetype-banned-type' => '&lrm;\'\'\'".$1"\'\'\' {{PLURAL:$4|یک نوع پرونده غیرمجاز است|انواعی پرونده غیرمجاز هستند}}.
-{{PLURAL:$3|نوع پرونده مجاز|انواع پرونده مجاز}} از این قرار است: $2 .',
+'filetype-banned-type' => '&lrm;\'\'\'".$1"\'\'\' {{PLURAL:$4|یک نوع پروندهٔ نامجاز است|انواعی پروندهٔ نامجاز هستند}}.
+{{PLURAL:$3|نوع پروندهٔ مجاز|انواع پروندهٔ مجاز}} از این قرار است: $2.',
 'filetype-missing' => 'این پرونده پسوند (مثلاً «‎.jpg») ندارد.',
 'empty-file' => 'پرونده‌ای که ارسال کردید خالی بود.',
 'file-too-large' => 'پرونده‌ای که ارسال کردید بیش از اندازه بزرگ بود.',
@@ -2125,7 +2125,7 @@ $1',
 'uploadstash-nofiles' => 'شما هیچ پروندهٔ انبارشده‌ای ندارید.',
 'uploadstash-badtoken' => 'انجام این اقدام ناموفق بود، احتمالاً به این دلیل که اعتبار ویرایش شما به اتمام رسیده است. دوباره امتحان کنید.',
 'uploadstash-errclear' => 'پاک کردن پرونده‌ها ناموفق بود.',
-'uploadstash-refresh' => 'تازه کردن فهرست پرونده‌ها',
+'uploadstash-refresh' => 'تازهکردن فهرست پرونده‌ها',
 'invalid-chunk-offset' => 'جابجایی نامعتبر قطعه',
 
 # img_auth script messages
@@ -2267,7 +2267,7 @@ https://www.mediawiki.org/wiki/Manual:Image_Authorization را ببینید.',
 'mimesearch-summary' => 'با کمک این صفحه شما می‌توانید پرونده‌هایی که یک نوع MIME به خصوص دارند را پیدا کنید.
 ورودی: به صورت contenttype/subtype ، نظیر <code>image/jpeg</code>.',
 'mimetype' => 'نوع MIME:',
-'download' => 'بارگÛ\8cرÛ\8c',
+'download' => 'درÛ\8cاÙ\81ت',
 
 # Unwatched pages
 'unwatchedpages' => 'صفحه‌های پی‌گیری‌نشده',
@@ -2306,7 +2306,7 @@ https://www.mediawiki.org/wiki/Manual:Image_Authorization را ببینید.',
 'statistics-articles' => 'صفحه‌های محتوایی',
 'statistics-pages' => 'صفحه‌ها',
 'statistics-pages-desc' => 'تمام صفحه‌های این ویکی، از جمله صفحه‌های بحث، تغییرمسیر و غیره',
-'statistics-files' => 'پرونده‌های بارگذاری شده',
+'statistics-files' => 'پرونده‌های بارگذاریشده',
 'statistics-edits' => 'ویرایش صفحه‌ها از هنگامی که {{SITENAME}} راه‌اندازی شده',
 'statistics-edits-average' => 'متوسط ویرایش‌ها به ازای هر صفحه',
 'statistics-views-total' => 'مجموع بازدیدها',
@@ -2367,10 +2367,10 @@ https://www.mediawiki.org/wiki/Manual:Image_Authorization را ببینید.',
 'popularpages' => 'صفحه‌های محبوب',
 'wantedcategories' => 'رده‌های مورد نیاز',
 'wantedpages' => 'صفحه‌های مورد نیاز',
-'wantedpages-badtitle' => 'عنوان غیرمجاز در مجموعهٔ نتایج: $1',
+'wantedpages-badtitle' => 'عنوان نامجاز در مجموعهٔ نتایج: $1',
 'wantedfiles' => 'پرونده‌های مورد نیاز',
 'wantedfiletext-cat' => 'پرونده‌های زیر استفاده می‌شوند اما موجود نیستند. همچنین ممکن است پرونده‌های مخازن خارجی با وجود موجود بودن در اینجا فهرست شوند. هرگونه رتبه مثبت کاذب <del>خط خواهد خورد.</del> علاوه بر این، صفحاتی که پرونده‌هایی ناموجود را در خود جای داده‌اند در [[:$1]] فهرست شده‌اند.',
-'wantedfiletext-nocat' => 'پرونده‌های زیر استفاده می‌شوند اما موجود نیستند. همچنین ممکن است پرونده‌های مخازن خارجی با وجود موجود بودن در اینجا فهرست شوند. هرگونه رتبه مثبت کاذب <del>خط خواهد خورد.</del>',
+'wantedfiletext-nocat' => 'پرونده‌های زیر استفاده می‌شوند اما موجود نیستند. همچنین ممکن است پرونده‌های مخازن خارجی با وجود موجود بودن در اینجا فهرست شوند. هرگونه رتبهٔ مثبت کاذب <del>خط خواهد خورد.</del>',
 'wantedtemplates' => 'الگوهای مورد نیاز',
 'mostlinked' => 'صفحه‌هایی که بیشتر از همه به آن‌ها پیوند داده شده‌است',
 'mostlinkedcategories' => 'رده‌هایی که بیشتر از همه به آن‌ها پیوند داده شده‌است',
@@ -2583,7 +2583,7 @@ https://www.mediawiki.org/wiki/Manual:Image_Authorization را ببینید.',
 'iteminvalidname' => 'مشکل با مورد «$1»، نام نامعتبر است...',
 'wlnote' => 'در زیر {{PLURAL:$1|تغییری|$1 تغییری}} که در {{PLURAL:$2|ساعت|$2 ساعت}} گذشته انجام شده موجود است، تاریخ آخرین بازیابی: $3، $4',
 'wlshowlast' => 'نمایش آخرین $1 ساعت $2 روز $3',
-'watchlist-options' => 'گزینه‌های پیگیری',
+'watchlist-options' => 'گزینه‌های پیگیری',
 
 # Displayed when you click the "watch" button and it is in the process of watching
 'watching' => 'پی‌گیری...',
@@ -2802,7 +2802,7 @@ $1',
 
 برای دیدن سیاههٔ حذف‌ها و احیاهای اخیر به  [[Special:Log/delete|سیاههٔ حذف]] رجوع کنید.",
 'undelete-header' => 'برای دیدن صفحه‌های حذف‌شدهٔ اخیر [[Special:Log/delete|سیاههٔ حذف]] را ببینید.',
-'undelete-search-title' => 'جستجوی صفحه‌های حذف شده',
+'undelete-search-title' => 'جستجوی صفحه‌های حذفشده',
 'undelete-search-box' => 'جستجوی صفحه‌های حذف‌شده.',
 'undelete-search-prefix' => 'نمایش صفحه‌ها با شروع از:',
 'undelete-search-submit' => 'برو',
@@ -2907,11 +2907,11 @@ $1',
 'ipbotheroption' => 'دیگر',
 'ipbotherreason' => 'دلیل دیگر/اضافی:',
 'ipbhidename' => 'نهفتن نام کاربری از ویرایش‌ها و فهرست‌ها',
-'ipbwatchuser' => 'پیگیری صفحهٔ کاربری و بحث این کاربر',
+'ipbwatchuser' => 'پیگیری صفحهٔ کاربری و بحث این کاربر',
 'ipb-disableusertalk' => 'جلوگیری از ویرایشی صفحهً بحث توسط خود کاربر در زمانی که بسته است',
 'ipb-change-block' => 'بستن دوبارهٔ کاربر با این تنظیم‌ها',
 'ipb-confirm' => 'تأیید بستن',
-'badipaddress' => 'نشانی آی‌پی غیر مجاز',
+'badipaddress' => 'نشانی آی‌پی نامجاز',
 'blockipsuccesssub' => 'بستن با موفقیت انجام شد',
 'blockipsuccesstext' => '[[Special:Contributions/$1|$1]] بسته شد.<br />
 برای بررسی بسته‌شده‌ها [[Special:BlockList|فهرست بسته‌شده‌ها]] را ببینید.',
@@ -2991,12 +2991,9 @@ $1',
 این نشانی به همراه بازه $2 بسته شده که قابل باز شدن است.',
 'ip_range_invalid' => 'بازهٔ آی‌پی نامعتبر.',
 'ip_range_toolarge' => 'قطع دسترسی بازه‌های بزرگتر از /$1 مجاز نیست.',
-'blockme' => 'دسترسی مرا قطع کن',
 'proxyblocker' => 'مسدود کننده پروکسی',
-'proxyblocker-disabled' => 'این عملکرد غیرفعال شده‌است.',
 'proxyblockreason' => 'نشانی آی‌پی شما بسته شده است چون متعلق به یک پروکسی باز است.
 لطفاً با ارائه دهندهً خدمات اینترنت خود یا پشتیبانی فنی تماس بگیرید و آنها را از این مشکل امنیتی جدی آگاه کنید.',
-'proxyblocksuccess' => 'انجام شد.',
 'sorbsreason' => 'نشانی آی‌پی شما توسط DNSBL مورد استفاده {{SITENAME}} به عنوان یک پروکسی باز گزارش شده‌است.',
 'sorbs_create_account_reason' => 'نشانی آی‌پی شما توسط DNSBL مورد استفاده {{SITENAME}} به عنوان یک پروکسی باز گزارش شده‌است.
 شما اجازهٔ ساختن حساب کاربری ندارید.',
@@ -3112,7 +3109,7 @@ $1',
 'imagenocrossnamespace' => 'امکان انتقال تصویر به فضای نام غیر پرونده وجود ندارد',
 'nonfile-cannot-move-to-file' => 'امکان انتقال محتوای غیر پرونده به فضای نام پرونده وجود ندارد',
 'imagetypemismatch' => 'پسوند پرونده جدید با نوع آن سازگار نیست',
-'imageinvalidfilename' => 'نام پروندهٔ هدف غیرمجاز است',
+'imageinvalidfilename' => 'نام پروندهٔ هدف نامجاز است',
 'fix-double-redirects' => 'به روز کردن تمامی تغییرمسیرهایی که به مقالهٔ اصلی اشاره می‌کنند',
 'move-leave-redirect' => 'بر جا گذاشتن یک تغییرمسیر',
 'protectedpagemovewarning' => "'''هشدار:''' این صفحه قفل شده‌است به طوری که تنها کاربران با دسترسی مدیریت می‌توانند آن را انتقال دهند.
@@ -3173,7 +3170,7 @@ $2',
 'djvu_no_xml' => 'امکان پیدا کردن پروندهٔ XML برای استفادهٔ DjVu وجود نداشت.',
 'thumbnail-temp-create' => 'نمی‌توان پروندهٔ بندانگشتی موقت را ساخت',
 'thumbnail-dest-create' => 'نمی‌توان تصویر بندانگشتی را در مقصد ذخیره کرد',
-'thumbnail_invalid_params' => 'پارامترهای غیرمجاز در تصویر بندانگشتی (thumbnail)',
+'thumbnail_invalid_params' => 'پارامترهای نامجاز در تصویر بندانگشتی (thumbnail)',
 'thumbnail_dest_directory' => 'اشکال در ایجاد پوشهٔ مقصد',
 'thumbnail_image-type' => 'تصویر از نوع پشتیبانی نشده',
 'thumbnail_gd-library' => 'تنظیمات ناقص کتابخانهٔ GD: عملکرد $1 وجود ندارد',
@@ -3223,7 +3220,7 @@ $2',
 'import-error-edit' => 'صفحهٔ «$1» وارد نمی‌شود، چون شما مجاز به ویرایش آن نیستید.',
 'import-error-create' => 'صفحهٔ «$1» وارد نمی‌شود، چون شما مجاز به ایجاد آن نیستید.',
 'import-error-interwiki' => 'صفحه «$1» وارد نشد. چون نام آن برای پیوند خارجی (interwiki) رزرو شده‌است.',
-'import-error-special' => 'صفحه «$1» درون‌ریزی نشد، چرا که متعلق به فضای نام غیرمجاز است.',
+'import-error-special' => 'صفحهٔ «$1» درون‌ریزی نشد، چرا که متعلق به فضای نام نامجاز است.',
 'import-error-invalid' => 'صفحه "$1" به دلیل نامعتبر بودن نامش وارد نمی‌شود.',
 'import-error-unserialize' => 'امکان خارج کردن نسخهٔ $2 از صفحهٔ «$1» از حالت سریال‌شده وجود نداشت. گزارش شد که نسخه از مدل محتوای $3 استفاده می‌کند که به صورت $4 سریال شده‌است.',
 'import-options-wrong' => '{{PLURAL:$2|جزئیات|جزئیات}} اشتباه: <nowiki>$1</nowiki>',
@@ -3243,7 +3240,7 @@ $2',
 'javascripttest-title' => 'در حال اجرای آزمایش‌های $1',
 'javascripttest-pagetext-noframework' => 'این صفحه برای اجرای آزمایش‌های جاوا اسکریپت کنار گذاشته شده‌است.',
 'javascripttest-pagetext-unknownframework' => 'چارچوب آزمایشی ناشناخته «$1».',
-'javascripttest-pagetext-frameworks' => 'لطفاً یکی از فریم‌ورک‌های آزمایشی زیر را انتخاب کنید: $1',
+'javascripttest-pagetext-frameworks' => 'لطفاً یکی از چارچوب‌های آزمایش زیر را انتخاب کنید: $1',
 'javascripttest-pagetext-skins' => 'پوسته‌ای را برای اجرای آزمایش‌ها انتخاب کنید:',
 'javascripttest-qunit-intro' => '[$1 مستندات آزمایش] را در mediawiki.org ببینید.',
 'javascripttest-qunit-heading' => 'مجموعه آزمایش QUnit جاوااسکریپت برای مدیاویکی',
@@ -3346,6 +3343,8 @@ $2',
 'spam_reverting' => 'واگردانی به آخرین نسخه‌ای که پیوندی به $1 ندارد.',
 'spam_blanking' => 'تمام نسخه‌ها حاوی پیوند به $1 بود، در حال خالی کردن',
 'spam_deleting' => 'تمام نسخه‌ها حاوی پیوند به $1 بود، در حال حذف',
+'simpleantispam-label' => "بررسی ضدهرزنگاری.
+این قسمت را پر '''نکنید'''!",
 
 # Info page
 'pageinfo-title' => 'اطلاعات در مورد «$1»',
@@ -3361,7 +3360,7 @@ $2',
 'pageinfo-language' => 'زبان محتوای صفحه',
 'pageinfo-robot-policy' => '‌فهرست‌کردن توسط ربات‌ها',
 'pageinfo-robot-index' => 'مجاز',
-'pageinfo-robot-noindex' => 'غیر مجاز',
+'pageinfo-robot-noindex' => 'نامجاز',
 'pageinfo-views' => 'شمار بازدیدها',
 'pageinfo-watchers' => 'شمار پی‌گیری‌کنندگان صفحه',
 'pageinfo-few-watchers' => 'کمتر از  $1 {{PLURAL:$1| پی‌گیر|پی‌گیر}}',
@@ -3436,7 +3435,7 @@ $1',
 'mediawarning' => "'''هشدار''': این پرونده ممکن است حاوی کدهای مخرب باشد.
 با اجرای آن رایانهٔ شما ممکن است آسیب ببیند.",
 'imagemaxsize' => "محدودیت ابعاد تصویر:<br />''(برای صفحه‌های توصیف پرونده)''",
-'thumbsize' => 'اندازهٔ Thumbnail:',
+'thumbsize' => 'اندازهٔ بنداگشتی:',
 'widthheight' => '$1 در $2',
 'widthheightpage' => '$1×$2، $3 {{PLURAL:$3|صفحه|صفحه}}',
 'file-info' => 'اندازهٔ پرونده: $1، نوع  MIME $2',
@@ -3445,7 +3444,7 @@ $1',
 'file-nohires' => 'تفکیک‌پذیری بالاتری در دسترس نیست.',
 'svg-long-desc' => 'پروندهٔ اس‌وی‌جی، با ابعاد <span dir="ltr">$1 × $2</span> پیکسل، اندازهٔ پرونده: $3',
 'svg-long-desc-animated' => 'پروندهٔ اس‌وی‌جی متحرک، با ابعاد <span dir="ltr">$1 × $2</span> پیکسل، اندازهٔ پرونده: $3',
-'svg-long-error' => 'پرونده SVG غیرمجاز: $1',
+'svg-long-error' => 'پرونده SVG نامجاز: $1',
 'show-big-image' => 'تصویر با تفکیک‌پذیری بالاتر',
 'show-big-image-preview' => 'اندازهٔ این پیش‌نمایش: $1.',
 'show-big-image-other' => '{{PLURAL:$2|کیفیت|کیفیت‌های}} دیگر: $1.',
@@ -3471,6 +3470,10 @@ $1',
 'sp-newimages-showfrom' => 'نشان‌دادن تصویرهای جدید از $2، $1 به بعد',
 
 # Video information, used by Language::formatTimePeriod() to format lengths in the above messages
+'seconds-abbrev' => '$1 ثانیه',
+'minutes-abbrev' => '$1 دقیقه',
+'hours-abbrev' => '$1 ساعت',
+'days-abbrev' => '$1 روز',
 'seconds' => '{{PLURAL:$1|$1ثانیه| $1  ثانیه}}',
 'minutes' => '{{PLURAL: $1|دقیقه|دقیقه}}',
 'hours' => '{{PLURAL: $1|ساعت|ساعت}}',
@@ -3880,7 +3883,7 @@ $1',
 
 'exif-dc-contributor' => 'مشارکت‌کنندگان',
 'exif-dc-coverage' => 'محدوده مکانی و یا زمانی رسانه',
-'exif-dc-date' => 'تاریخ (ها)',
+'exif-dc-date' => 'تاریخ(ها)',
 'exif-dc-publisher' => 'ناشر',
 'exif-dc-relation' => 'رسانه‌های مرتبط',
 'exif-dc-rights' => 'حقوق',
@@ -4059,7 +4062,7 @@ $5
 'watchlistedit-noitems' => 'فهرست پی‌گیری‌های شما خالی است.',
 'watchlistedit-normal-title' => 'ویرایش فهرست پی‌گیری‌ها',
 'watchlistedit-normal-legend' => 'حذف عنوان‌ها از فهرست پی‌گیری‌ها',
-'watchlistedit-normal-explain' => 'عنوان‌های موجود در فهرست پیگیری شما در زیر نشان داده شده‌اند.
+'watchlistedit-normal-explain' => 'عنوان‌های موجود در فهرست پیگیری شما در زیر نشان داده شده‌اند.
 برای حذف هر عنوان جعبهٔ کنار آن را علامت بزنید و دکمهٔ «{{int:Watchlistedit-normal-submit}}» را بفشارید.
 شما همچنین می‌توانید [[Special:EditWatchlist/raw|فهرست خام را ویرایش کنید]].',
 'watchlistedit-normal-submit' => 'حذف عنوان‌ها',
@@ -4184,7 +4187,7 @@ $5
 # Special:Redirect
 'redirect' => 'تغییرمسیر توسط پرونده، کاربر یا شناسهٔ نسخه',
 'redirect-legend' => 'تغییرمسیر به یک پرونده یا صفحه',
-'redirect-summary' => 'این صفحهٔ ویژه به پرونده (نام پرونده داده‌شده)، صفحه (شماره شناسهٔ صفحه داده‌شده) یا صفحهٔ کاربری (شناسهٔ عددی کاربری داده‌شده) تغییرمسیر می‌یابد',
+'redirect-summary' => 'این صفحهٔ ویژه به پرونده (نام پرونده داده‌شده)، صفحه (شماره شناسهٔ صفحه داده‌شده) یا صفحهٔ کاربری (شناسهٔ عددی کاربری داده‌شده) تغییرمسیر می‌یابد. طرز استفاده: [[{{#Special:Redirect}}/file/Example.jpg]]، [[{{#Special:Redirect}}/revision/328429]] یا [[{{#Special:Redirect}}/user/101]].',
 'redirect-submit' => 'برو',
 'redirect-lookup' => 'جستجو:',
 'redirect-value' => 'مقدار:',
@@ -4297,7 +4300,7 @@ $5
 # New logging system
 'logentry-delete-delete' => '$1 صفحهٔ $3 را {{GENDER:$2|حذف کرد}}',
 'logentry-delete-restore' => '$1 صفحهٔ $3 را {{GENDER:$2|احیا کرد}}',
-'logentry-delete-event' => '$1 پیدایی {{PLURAL:$5|یک مورد سیاهه|$5 مورد سیاهه}} را در $3 {{GENDER:$2|تغییر داد}} : $4',
+'logentry-delete-event' => '$1 پیدایی {{PLURAL:$5|یک مورد سیاهه|$5 مورد سیاهه}} را در $3 {{GENDER:$2|تغییر داد}}: $4',
 'logentry-delete-revision' => '$1 پیدایی {{PLURAL:$5|یک نسخه|$5 نسخه}} صفحه $3 را {{GENDER:$2|تغییر داد}}: $4',
 'logentry-delete-event-legacy' => '$1 پیدایی موارد سیاهه را در $3 {{GENDER:$2|تغییر داد}}',
 'logentry-delete-revision-legacy' => '$1 پیدایی نسخه‌های $3 را {{GENDER:$2|تغییر داد}}',
@@ -4319,7 +4322,7 @@ $5
 'logentry-move-move_redir' => '$1 صفحهٔ $3 را به $4 که تغییرمسیر بود {{GENDER:$2|منتقل کرد}}',
 'logentry-move-move_redir-noredirect' => '$1 صفحهٔ $3 را بدون برجای‌گذاشتن تغییرمسیر به $4 که تغییرمسیر بود {{GENDER:$2|منتقل کرد}}',
 'logentry-patrol-patrol' => '$1 نسخه $4 صفحه $3 را به عنوان گشت خورده {{GENDER:$2|علامت زد}}',
-'logentry-patrol-patrol-auto' => '$1 نسخه $4 صفحه $3 را به طور خودکار به عنوان گشت خورده {{GENDER:$2|علامت زد}}',
+'logentry-patrol-patrol-auto' => '$1 نسخهٔ $4 صفحهٔ $3 را به‌طور خودکار به‌عنوان گشت‌خورده {{GENDER:$2|علامت زد}}',
 'logentry-newusers-newusers' => 'حساب کاربری $1 {{GENDER:$2|ایجاد شد}}',
 'logentry-newusers-create' => 'حساب کاربری $1 {{GENDER:$2|ایجاد شد}}',
 'logentry-newusers-create2' => 'حساب کاربری $3 توسط $1 {{GENDER:$2|ایجاد شد}}',
@@ -4365,7 +4368,7 @@ $5
 'api-error-file-too-large' => 'پرونده‌ای که شما ارسال کردید بیش از اندازه بزرگ بود.',
 'api-error-filename-tooshort' => 'نام پرونده بیش از اندازه کوتاه است.',
 'api-error-filetype-banned' => 'این نوع پرونده ممنوع است.',
-'api-error-filetype-banned-type' => '&lrm;$1 {{PLURAL:$4|یک نوع پرونده غیرمجاز است|انواعی پرونده غیرمجاز هستند}}. {{PLURAL:$3|نوع پرونده مجاز|انواع پرونده مجاز}} از این قرار است: $2 .',
+'api-error-filetype-banned-type' => '&lrm;$1 {{PLURAL:$4|یک نوع پروندهٔ نامجاز است|انواع پروندهٔ نامجاز هستند}}. {{PLURAL:$3|نوع پروندهٔ مجاز|انواع پروندهٔ مجاز}} از این قرار است: $2.',
 'api-error-filetype-missing' => 'پرونده فرمت ندارد.',
 'api-error-hookaborted' => 'اصلاحیه‌ای که شما سعی در ایجاد آن بودید توسط افزونه‌ای به دام افتاد.',
 'api-error-http' => 'خطای داخلی: قادر به اتصال به سرور نیست.',
index 804552b..8a62105 100644 (file)
@@ -360,7 +360,7 @@ $messages = array(
 'tog-noconvertlink' => 'Älä muunna linkkien otsikoita toiseen kirjoitusjärjestelmään',
 'tog-norollbackdiff' => 'Älä näytä eroavaisuuksia, kun olet palauttanut muokkauksen palauta-työkalulla',
 'tog-useeditwarning' => 'Varoita minua, kun poistun muokkaussivulta tallentamatta muutoksia',
-'tog-prefershttps' => 'Käytä aina turvallista yhteyttä kun olet kirjautunut sisään',
+'tog-prefershttps' => 'Käytä aina suojattua yhteyttä, kun olet kirjautunut sisään',
 
 'underline-always' => 'Aina',
 'underline-never' => 'Ei koskaan',
@@ -809,7 +809,7 @@ Salli evästeiden käyttö, ja sen jälkeen kirjaudu sisään juuri luomallasi k
 'nocookiesfornew' => 'Käyttäjätunnusta ei luotu, koska sen lähdettä ei kyetty varmistamaan. Varmista, että selaimessasi on käytössä evästeet, päivitä tämä sivu ja yritä uudelleen.',
 'noname' => 'Et ole määritellyt kelvollista käyttäjänimeä.',
 'loginsuccesstitle' => 'Sisäänkirjautuminen onnistui',
-'loginsuccess' => "'''Olet nyt kirjautunut sivustolle {{SITENAME}} käyttäjänä $1.'''",
+'loginsuccess' => "'''Olet kirjautunut sivustolle {{SITENAME}} käyttäjänä $1.'''",
 'nosuchuser' => 'Käyttäjää ”$1” ei ole olemassa. Nimet ovat kirjainkoosta riippuvaisia. Tarkista kirjoititko nimen oikein, tai [[Special:UserLogin/signup|luo uusi käyttäjätunnus]].',
 'nosuchusershort' => 'Käyttäjää nimeltä ”$1” ei ole. Kirjoititko nimen oikein?',
 'nouserspecified' => 'Käyttäjätunnusta ei ole määritelty.',
@@ -824,12 +824,13 @@ Salli evästeiden käyttö, ja sen jälkeen kirjaudu sisään juuri luomallasi k
 'passwordremindertext' => 'Joku IP-osoitteesta $1 pyysi {{GRAMMAR:partitive|{{SITENAME}}}} ($4) lähettämään uuden salasanan. Väliaikainen salasana käyttäjälle $2 on nyt $3. Kirjaudu sisään ja vaihda salasana. Väliaikainen salasana vanhenee {{PLURAL:$5|yhden päivän|$5 päivän}} kuluttua.
 
 Jos joku muu on tehnyt tämän pyynnön, tai jos olet muistanut salasanasi ja et halua vaihtaa sitä, voit jättää tämän viestin huomiotta ja jatkaa vanhan salasanan käyttöä.',
-'noemail' => 'Käyttäjälle "$1" ei ole määritelty sähköpostiosoitetta.',
+'noemail' => 'Käyttäjälle $1 ei ole määritelty sähköpostiosoitetta.',
 'noemailcreate' => 'Sinun on annettava voimassa oleva sähköpostiosoite',
 'passwordsent' => 'Uusi salasana on lähetetty käyttäjän <b>$1</b> sähköpostiosoitteeseen.
 Ole hyvä ja kirjaudu sisään kun olet saanut sen.',
 'blocked-mailpassword' => 'Osoitteellesi on asetettu muokkausesto, joka estää käyttämästä salasanamuistutustoimintoa.',
-'eauthentsent' => 'Varmennussähköposti on lähetetty annettuun sähköpostiosoitteeseen. Muita viestejä ei lähetetä, ennen kuin olet toiminut viestin ohjeiden mukaan ja varmistanut, että sähköpostiosoite kuuluu sinulle.',
+'eauthentsent' => 'Varmennussähköposti on lähetetty annettuun sähköpostiosoitteeseen.
+Muita viestejä ei lähetetä, ennen kuin olet toiminut viestin ohjeiden mukaan ja varmistanut, että sähköpostiosoite kuuluu sinulle.',
 'throttled-mailpassword' => 'Salasananpalautusviesti on lähetetty {{PLURAL:$1|kuluvan|kuluvien $1}} tunnin aikana. Salasananpalautusviestejä lähetetään enintään {{PLURAL:$1|tunnin|$1 tunnin}} välein.',
 'mailerror' => 'Virhe lähetettäessä sähköpostia: $1',
 'acct_creation_throttle_hit' => 'IP-osoitteestasi on luotu tähän wikiin jo {{PLURAL:$1|yksi tunnus|$1 tunnusta}} päivän aikana, joka suurin sallittu määrä tälle ajalle.
@@ -854,8 +855,8 @@ Odota $1 ennen kuin yrität uudelleen.',
 'login-abort-generic' => 'Kirjautuminen epäonnistui – keskeytetty',
 'loginlanguagelabel' => 'Kieli: $1',
 'suspicious-userlogout' => 'Pyyntösi kirjautua ulos evättiin, koska se näytti rikkinäisen selaimen tai välimuistipalvelimen lähettämältä.',
-'createacct-another-realname-tip' => 'Oikea nimi on vapaaehtoinen.
-Jos kuitenkin kerrot sen, nimeä käytetään jotta voidaan kertoa kuka sisältöä on tuottanut.',
+'createacct-another-realname-tip' => 'Vapaaehtoinen.
+Nimesi näytetään käyttäjätunnuksesi sijasta sivun tekijäluettelossa.',
 
 # Email sending
 'php-mail-error-unknown' => 'Tuntematon virhe PHP:n mail()-funktiossa',
@@ -871,7 +872,7 @@ Jos kuitenkin kerrot sen, nimeä käytetään jotta voidaan kertoa kuka sisält
 'newpassword' => 'Uusi salasana:',
 'retypenew' => 'Uusi salasana uudelleen:',
 'resetpass_submit' => 'Aseta salasana ja kirjaudu sisään',
-'changepassword-success' => 'Salasanasi vaihtaminen onnistui.',
+'changepassword-success' => 'Salasanan vaihto onnistui.',
 'resetpass_forbidden' => 'Salasanoja ei voi vaihtaa.',
 'resetpass-no-info' => 'Et voi nähdä tätä sivua kirjautumatta sisään.',
 'resetpass-submit-loggedin' => 'Muuta salasana',
@@ -879,14 +880,14 @@ Jos kuitenkin kerrot sen, nimeä käytetään jotta voidaan kertoa kuka sisält
 'resetpass-wrong-oldpass' => 'Virheellinen väliaikainen tai nykyinen salasana.
 Olet saattanut jo onnistuneesti vaihtaa salasanasi tai pyytää uutta väliaikaista salasanaa.',
 'resetpass-temp-password' => 'Väliaikainen salasana:',
-'resetpass-abort-generic' => 'Lisäosa hylkäsi salasanan vaihdon.',
+'resetpass-abort-generic' => 'Laajennus keskeytti salasanan vaihdon.',
 
 # Special:PasswordReset
-'passwordreset' => 'Salasanan vaihto',
-'passwordreset-text-one' => 'Täytä tämä lomake vaihtaaksesi salasanasi.',
-'passwordreset-text-many' => '{{PLURAL:$1|Täytä yksi kentistä nollataksesi salasanasi.}}',
-'passwordreset-legend' => 'Salasanan vaihto',
-'passwordreset-disabled' => 'Salasanojen vaihtaminen ei ole mahdollista tässä wikissä.',
+'passwordreset' => 'Salasanan uudistus',
+'passwordreset-text-one' => 'Täytä tämä lomake uudistaaksesi salasanasi.',
+'passwordreset-text-many' => '{{PLURAL:$1|Täytä yksi kentistä uudistaaksesi salasanasi.}}',
+'passwordreset-legend' => 'Salasanan uudistus',
+'passwordreset-disabled' => 'Salasanojen uudistaminen ei ole mahdollista tässä wikissä.',
 'passwordreset-emaildisabled' => 'Sähköpostitoiminnot on poistettu käytöstä tässä wikissä.',
 'passwordreset-username' => 'Käyttäjätunnus:',
 'passwordreset-domain' => 'Verkkotunnus:',
@@ -932,15 +933,15 @@ Väliaikainen salasana: $2',
 'changeemail-cancel' => 'Peruuta',
 
 # Special:ResetTokens
-'resettokens' => 'Uudista tunnisteet',
-'resettokens-text' => 'Tällä sivulla voit uudistaa tunnisteesi, joiden avulla hallitaan pääsyä käyttäjätiliisi liittyviin yksityisiin tietoihin.
+'resettokens' => 'Uudista avaimet',
+'resettokens-text' => 'Tällä sivulla voit uudistaa avaimesi, jotka mahdollistavat pääsyn käyttäjätunnukseesi liittyviin tiettyihin yksityisiin tietoihin.
 
-Sinun pitää tehdä tämä jos olet vahingossa jakanut ne jonkun kanssa tai käyttäjätiliisi on saatettu kajota.',
-'resettokens-no-tokens' => 'Tunnisteita ei löydy uudistettavaksi.',
-'resettokens-legend' => 'Uudista tunnisteet',
-'resettokens-tokens' => 'Tunnisteet:',
+Sinun 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.',
+'resettokens-legend' => 'Uudista avaimet',
+'resettokens-tokens' => 'Avaimet:',
 'resettokens-token-label' => '$1 (nykyinen arvo: $2)',
-'resettokens-watchlist-token' => 'Tarkkailulistan verkkosyötteen tunniste',
+'resettokens-watchlist-token' => '[[Special:Watchlist|Tarkkailulistan]] verkkosyötteen (Atom tai RSS) avain',
 'resettokens-done' => 'Avaimet on uudistettu.',
 'resettokens-resetbutton' => 'Uudista valitut avaimet',
 
@@ -1281,11 +1282,12 @@ $1",
 'revdel-restore-visible' => 'näkyvät versiot',
 'pagehist' => 'Sivun muutoshistoria',
 'deletedhist' => 'Poistettujen versioiden historia',
-'revdelete-hide-current' => 'Virhe tapahtui $2, $1 päivätyn kohteen piilottamisessa: tämä on nykyinen versio. Sitä ei voi piilottaa.',
+'revdelete-hide-current' => 'Virhe piilotettaessa $1 kello $2 päivättyä kohdetta: Tämä on uusin versio.
+Sitä ei voi piilottaa.',
 'revdelete-show-no-access' => 'Virhe näyttäessä kohtaa $2 kello $1: kohta on merkitty ”rajoitetuksi”.
 Sinulla ei ole oikeutta siihen.',
 'revdelete-modify-no-access' => 'Virhe tapahtui $2, $1 kohteen muokkauksessa: tämä kohde on merkitty "rajoitetuksi". Sinulla ei ole oikeuksia sen muokkaukseen.',
-'revdelete-modify-missing' => 'Virhe muuttaessa kohdetta, jonka tunnus on $1: Se puuttuu tietokannasta.',
+'revdelete-modify-missing' => 'Virhe muuttaessa kohdetta, jonka tunniste on $1: Se puuttuu tietokannasta.',
 'revdelete-no-change' => "'''Varoitus:''' kohteessa $2 kello $1 on jo valmiiksi haluamasi näkyvyysasetukset.",
 'revdelete-concurrent-change' => 'Virhe $2, $1 päivätyn kohteen muokkauksessa: sen tilan on näköjään muuttanut joku sillä aikaa kun yritit muokata sitä. Ole hyvä ja tarkista lokit.',
 'revdelete-only-restricted' => 'Virhe piilotettaessa $1 kello $2 päivättyä kohdetta: Et voi poistaa kohteita ylläpitäjien näkyviltä valitsematta myös jotain muuta näkyvyysasetusta.',
@@ -1445,7 +1447,7 @@ Kokeile lisätä haun alkuun ''all:'', niin haku kohdistuu kaikkeen sisältöön
 'prefs-rendering' => 'Ulkoasu',
 'saveprefs' => 'Tallenna asetukset',
 'resetprefs' => 'Tyhjennä tallentamattomat muutokset',
-'restoreprefs' => 'Palauta kaikki oletusasetuksiin (kaikissa asetusten osastoissa)',
+'restoreprefs' => 'Palauta kaikki oletusasetuksiin (kaikissa osioissa)',
 'prefs-editing' => 'Muokkaus',
 'rows' => 'Rivejä',
 'columns' => 'Sarakkeita',
@@ -1457,8 +1459,10 @@ Kokeile lisätä haun alkuun ''all:'', niin haku kohdistuu kaikkeen sisältöön
 'recentchangesdays-max' => 'Enintään $1 {{PLURAL:$1|päivä|päivää}}',
 'recentchangescount' => 'Näytettävien muutoksien määrä oletuksena',
 'prefs-help-recentchangescount' => 'Tämä sisältää tuoreet muutokset, muutoshistoriat ja lokit.',
-'prefs-help-watchlist-token2' => 'Tämä on salainen avain tarkkailulistasi verkkosyötteeseen. Kuka tahansa joka tietää sen voi lukea tarkkailulistaasi, joten älä paljasta sitä. [[Special:ResetTokens|Napsauta tästä jos sinun pitää uudistaa se]].',
-'savedprefs' => 'Asetuksesi tallennettiin onnistuneesti.',
+'prefs-help-watchlist-token2' => 'Tämä on salainen avain tarkkailulistasi verkkosyötteeseen.
+Kuka tahansa, joka tietää sen voi lukea tarkkailulistaasi, joten älä paljasta sitä.
+[[Special:ResetTokens|Napsauta tästä, jos sinun pitää uudistaa se]].',
+'savedprefs' => 'Asetuksesi on tallennettu.',
 'timezonelegend' => 'Aikavyöhyke',
 'localtime' => 'Paikallinen aika',
 'timezoneuseserverdefault' => 'Käytä oletusta ($1)',
@@ -1502,8 +1506,8 @@ Kokeile lisätä haun alkuun ''all:'', niin haku kohdistuu kaikkeen sisältöön
 'badsiglength' => 'Allekirjoitus on liian pitkä – sen on oltava alle $1 {{PLURAL:$1|merkki|merkkiä}}.',
 'yourgender' => 'Mikä seuraavista kuvaa sinua?',
 'gender-unknown' => 'En halua määritellä',
-'gender-male' => 'Hän on miespuolinen käyttäjä',
-'gender-female' => 'Hän on naispuolinen käyttäjä',
+'gender-male' => 'Mies',
+'gender-female' => 'Nainen',
 'prefs-help-gender' => 'Tämän asetuksen määrittäminen on vapaaehtoista.
 Ohjelmisto käyttää annettua arvoa viitaten sinuun oikealla kieliopillisella suvulla.
 Tämä tieto on julkinen.',
@@ -1545,10 +1549,10 @@ Tämä tieto on julkinen.',
 'saveusergroups' => 'Tallenna nämä käyttäjäryhmät',
 'userrights-groupsmember' => 'Jäsenenä ryhmissä:',
 'userrights-groupsmember-auto' => 'Automaattisesti jäsenenä ryhmissä:',
-'userrights-groups-help' => 'Voit muuttaa ryhmiä, joissa tämä käyttäjä on:
+'userrights-groups-help' => 'Voit muuttaa ryhmiä, joissa tämä käyttäjä on.
 * Merkattu valintaruutu tarkoittaa, että käyttäjä on kyseisessä ryhmässä.
 * Merkkaamaton valintaruutu tarkoittaa, että käyttäjä ei ole kyseisessä ryhmässä.
-* <nowiki>*</nowiki> tarkoittaa, että et pysty poistamaan käyttäjää tästä ryhmästä kun olet hänet siihen lisännyt tai päinvastoin',
+* <nowiki>*</nowiki> tarkoittaa, että et pysty poistamaan käyttäjää tästä ryhmästä, kun olet hänet siihen lisännyt tai päinvastoin',
 'userrights-reason' => 'Syy:',
 'userrights-no-interwiki' => 'Sinulla ei ole oikeutta muokata käyttöoikeuksia muissa wikeissä.',
 'userrights-nodatabase' => 'Tietokantaa $1 ei ole tai se ei ole paikallinen.',
@@ -1629,9 +1633,9 @@ Tämä tieto on julkinen.',
 'right-editusercssjs' => 'Muokata toisten käyttäjien CSS- ja JavaScript-tiedostoja',
 'right-editusercss' => 'Muokata toisten käyttäjien CSS-tiedostoja',
 'right-edituserjs' => 'Muokata toisten käyttäjien JavaScript-tiedostoja',
-'right-editmyusercss' => 'Muokata omia CSS-tiedostojaan',
-'right-editmyuserjs' => 'Muokata omia JavaScript-tiedostojaan',
-'right-viewmywatchlist' => 'Katsoa tarkkailulistaasi',
+'right-editmyusercss' => 'Muokata omia CSS-tiedostoja',
+'right-editmyuserjs' => 'Muokata omia JavaScript-tiedostoja',
+'right-viewmywatchlist' => 'Tarkastella tarkkailulistaasi',
 'right-editmywatchlist' => 'Muokata tarkkailulistaasi. Huomaa, että jotkin toiminnot lisäävät yhä sivuja listallesi riippumatta tästä oikeudesta.',
 'right-viewmyprivateinfo' => 'Nähdä omat yksityiset tietosi (esim. sähköpostiosoite, oikea nimi)',
 'right-editmyprivateinfo' => 'Muokata omia yksityisiä tietojasi (esim. sähköpostiosoite, oikea nimi)',
@@ -1698,7 +1702,7 @@ Tämä tieto on julkinen.',
 'action-siteadmin' => 'lukita tai avata tietokantaa',
 'action-sendemail' => 'lähettää sähköpostia',
 'action-editmywatchlist' => 'muokata tarkkailulistaasi',
-'action-viewmywatchlist' => 'katsoa tarkkailulistaasi',
+'action-viewmywatchlist' => 'tarkastella tarkkailulistaasi',
 'action-viewmyprivateinfo' => 'katsoa omia yksityisiä tietojasi',
 'action-editmyprivateinfo' => 'muokata omia yksityisiä tietojasi',
 
@@ -2097,11 +2101,11 @@ Syöte: sisältötyyppi/alatyyppi, esimerkiksi <code>image/jpeg</code>.',
 'randompage-nopages' => '{{PLURAL:$2|Nimiavaruudessa|Nimiavaruuksissa}} $1 ei ole sivuja.',
 
 # Random page in category
-'randomincategory' => 'Satunnainen sivu, joka kuuluu luokkaan',
-'randomincategory-invalidcategory' => '" $1 " ei ole kelvollinen luokan nimi.',
-'randomincategory-nopages' => 'Luokassa [[:Category:$1]] ei ole sivuja.',
-'randomincategory-selectcategory' => 'Etsi satunnainen sivu luokasta: $1 $2.',
-'randomincategory-selectcategory-submit' => 'Etsi',
+'randomincategory' => 'Satunnainen sivu luokasta',
+'randomincategory-invalidcategory' => '$1 ei ole kelvollinen luokan nimi.',
+'randomincategory-nopages' => 'Luokassa [[:Category:$1|$1]] ei ole sivuja.',
+'randomincategory-selectcategory' => 'Hae satunnainen sivu luokasta: $1 $2',
+'randomincategory-selectcategory-submit' => 'Hae',
 
 # Random redirect
 'randomredirect' => 'Satunnainen ohjaus',
@@ -2314,7 +2318,8 @@ Vaaditaan vähintään ylätason verkkotunnus, esimerkiksi "*.org".<br />
 'listgrouprights' => 'Käyttäjäryhmien oikeudet',
 'listgrouprights-summary' => 'Tämä lista sisältää tämän wikin käyttäjäryhmät sekä ryhmiin liitetyt käyttöoikeudet.
 Lisätietoa yksittäisistä käyttäjäoikeuksista saattaa löytyä [[{{MediaWiki:Listgrouprights-helppage}}|erilliseltä ohjesivulta]].',
-'listgrouprights-key' => '* <span class="listgrouprights-granted">Myönnetyt oikeudet</span>
+'listgrouprights-key' => 'Selite:
+* <span class="listgrouprights-granted">Myönnetyt oikeudet</span>
 * <span class="listgrouprights-revoked">Kumotut oikeudet</span>',
 'listgrouprights-group' => 'Ryhmä',
 'listgrouprights-rights' => 'Oikeudet',
@@ -2467,10 +2472,12 @@ Sivulla $2 on lista viimeaikaisista poistoista.',
 'deletecomment' => 'Syy',
 'deleteotherreason' => 'Muu syy tai tarkennus',
 'deletereasonotherlist' => 'Muu syy',
-'deletereason-dropdown' => '*Yleiset poistosyyt
-** Tekijän poistopyyntö
+'deletereason-dropdown' => '* Yleiset poistosyyt
 ** Tekijänoikeusrikkomus
-** Vandalismi',
+** Tekijän poistopyyntö
+** Testisivu
+** Vandalismi
+** Virheellinen ohjaus',
 'delete-edit-reasonlist' => 'Muokkaa poistosyitä',
 'delete-toobig' => 'Tällä sivulla on pitkä muutoshistoria – yli $1 {{PLURAL:$1|versio|versiota}}. Näin suurien muutoshistorioiden poistamista on rajoitettu suorituskykysyistä.',
 'delete-warning-toobig' => 'Tällä sivulla on pitkä muutoshistoria – yli $1 {{PLURAL:$1|versio|versiota}}. Näin suurien muutoshistorioiden poistaminen voi haitata sivuston suorituskykyä.',
@@ -2537,7 +2544,7 @@ Viimeisimmän muokkauksen on tehnyt käyttäjä [[User:$3|$3]] ([[User talk:$3|k
 'protect-otherreason-op' => 'Muu syy',
 'protect-dropdown' => '*Yleiset suojaussyyt
 ** Jatkuva vandalismi
-** Jatkuva roskalinkkien lisääminen
+** Jatkuva mainoslinkkien lisääminen
 ** Muokkaussota
 ** Suuri näkyvyys',
 'protect-edit-reasonlist' => 'Muokkaa suojaussyitä',
@@ -2776,11 +2783,8 @@ Alla on ote häivytyslokista.',
 'ipb_blocked_as_range' => 'IP-osoite $1 on estetty välillisesti ja sen estoa ei voi poistaa. Se on estetty osana verkkoaluetta $2, jonka eston voi poistaa',
 'ip_range_invalid' => 'Virheellinen IP-alue.',
 'ip_range_toolarge' => 'Suuremmat osoitealue-estot kuin /$1 eivät ole sallittuja.',
-'blockme' => 'Estä minut',
 'proxyblocker' => 'Välityspalvelinesto',
-'proxyblocker-disabled' => 'Tämä toiminto ei ole käytössä.',
 'proxyblockreason' => 'IP-osoitteestasi on estetty muokkaukset, koska se on avoin välityspalvelin. Ota yhteyttä Internet-palveluntarjoajaasi tai tekniseen tukeen ja kerro heille tästä tietoturvaongelmasta.',
-'proxyblocksuccess' => 'Valmis.',
 'sorbsreason' => 'IP-osoitteesi on listattu avoimena välityspalvelimena DNSBL:n mustalla listalla sivustolla {{SITENAME}}.',
 'sorbs_create_account_reason' => 'IP-osoitteesi on listattu avoimena välityspalvelimena DNSBL:n mustalla listalla sivustolla {{SITENAME}}. 
 Et voi luoda käyttäjätunnusta.',
@@ -2996,7 +3000,7 @@ Tallenna tiedot koneellesi ja tuo ne tällä sivulla.',
 'import-error-interwiki' => 'Sivua $1 ei voitu tuoda, koska sen nimi on varattu ulkoisen linkittämisen (interwiki).',
 'import-error-special' => 'Sivua $1 ei tuoda, koska se kuuluu nimitilaan, joka ei salli sivuja.',
 'import-error-invalid' => 'Sivua $1 ei tuoda, koska sen nimi ei kelpaa.',
-'import-error-unserialize' => 'Revisiota $2 sivusta "$1" ei voida jakaa osiin. Revision kerrottiin käyttävän sisältömallia $3 ja sarjoitusmuotoilua $4.',
+'import-error-unserialize' => 'Versiota $2 sivusta $1 ei voida jakaa osiin. Version ilmoitettiin käyttävän sisältömallia $3 ja sarjoitusmuotoilua $4.',
 'import-options-wrong' => '{{PLURAL:$2|Väärä asetus|Väärät asetukset}}: <nowiki>$1</nowiki>',
 'import-rootpage-invalid' => 'Annettu sivun nimi ei kelpaa.',
 'import-rootpage-nosubpage' => 'Annetun sivun nimiavaruus $1 ei salli alasivuja.',
@@ -3043,9 +3047,9 @@ Voit katsella sivun lähteenä olevaa wikitekstiä.',
 'tooltip-ca-move' => 'Siirrä tämä sivu',
 'tooltip-ca-watch' => 'Lisää tämä sivu tarkkailulistallesi',
 'tooltip-ca-unwatch' => 'Poista tämä sivu tarkkailulistaltasi',
-'tooltip-search' => 'Etsi {{GRAMMAR:elative|{{SITENAME}}}}',
+'tooltip-search' => 'Hae {{GRAMMAR:elative|{{SITENAME}}}}',
 'tooltip-search-go' => 'Siirry sivulle, joka on tarkalleen tällä nimellä',
-'tooltip-search-fulltext' => 'Etsi sivuilta tätä tekstiä',
+'tooltip-search-fulltext' => 'Hae sivuilta tätä tekstiä',
 'tooltip-p-logo' => 'Etusivu',
 'tooltip-n-mainpage' => 'Siirry etusivulle',
 'tooltip-n-mainpage-description' => 'Siirry etusivulle',
@@ -3136,6 +3140,8 @@ Voit katsella sivun lähteenä olevaa wikitekstiä.',
 'spam_reverting' => 'Palautettu viimeisimpään versioon, joka ei sisällä linkkejä kohteeseen $1.',
 'spam_blanking' => 'Kaikki versiot sisälsivät linkkejä kohteeseen $1. Sivu tyhjennetty.',
 'spam_deleting' => 'Kaikki versiot sisälsivät linkkejä kohteeseen $1, poistetaan',
+'simpleantispam-label' => "Mainostenvastainen varmistus.
+'''ÄLÄ''' täytä tätä!",
 
 # Info page
 'pageinfo-title' => 'Tietoja sivusta $1',
@@ -3469,7 +3475,7 @@ Kaikki muut linkit ovat poikkeuksia eli toisin sanoen sivuja, joissa tiedostoa s
 'exif-copyrightowner' => 'Tekijänoikeuden haltija',
 'exif-usageterms' => 'Käyttöehdot',
 'exif-webstatement' => 'Verkossa oleva tekijänoikeustieto',
-'exif-originaldocumentid' => 'Alkuperäisen asiakirjan tunnusnumero',
+'exif-originaldocumentid' => 'Alkuperäisen asiakirjan tunniste',
 'exif-licenseurl' => 'Tekijänoikeuslisenssin URL',
 'exif-morepermissionsurl' => 'Vaihtoehtoiset lisenssitiedot',
 'exif-attributionurl' => 'Kun kuvaa käytetään, linkitä tähän osoitteeseen',
@@ -3792,7 +3798,7 @@ Varmista, että haluat luoda sivun uudelleen.",
 'confirm-unwatch-top' => 'Poistetaanko tämä sivu tarkkailulistaltasi?',
 
 # Separators for various lists, etc.
-'percent' => '$1&nbsp;%',
+'percent' => '$1&#160;%',
 
 # Multipage image navigation
 'imgmultipageprev' => '← edellinen sivu',
@@ -3885,7 +3891,7 @@ Voit myös muokata listaa [[Special:EditWatchlist|tavalliseen tapaan]].',
 'version-license' => 'Lisenssi',
 'version-poweredby-credits' => "Tämä wiki käyttää '''[//www.mediawiki.org/ MediaWikiä]'''. Copyright © 2001–$1 $2.",
 'version-poweredby-others' => 'muut',
-'version-poweredby-translators' => 'translatewiki.net kääntäjät',
+'version-poweredby-translators' => 'translatewiki.net-kääntäjät',
 'version-credits-summary' => 'Haluaisimme kiittää seuraavia henkilöitä heidän panoksestaan [[Special:Version|MediaWiki-ohjelmistoon]].',
 'version-license-info' => 'MediaWiki on vapaa ohjelmisto – voit levittää sitä ja/tai muokata sitä Free Software Foundationin GNU General Public Licensen ehdoilla, joko version 2 tai halutessasi minkä tahansa myöhemmän version mukaisesti.
 
@@ -3900,13 +3906,13 @@ Sinun olisi pitänyt saada [{{SERVER}}{{SCRIPTPATH}}/COPYING kopio GNU General P
 'version-entrypoints-header-url' => 'URL',
 
 # Special:Redirect
-'redirect' => 'Ohjaus tiedostonimen, käyttäjänumeron tai versionumeron mukaan',
-'redirect-legend' => 'Uudelleenohjaa tiedostoon tai sivulle',
-'redirect-summary' => 'Tämä toimintosivu ohjaa tiedostoon (tiedoston nimen mukaan), sivulle (sivun versionumeron mukaan) tai käyttäjäsivulle (käyttäjätunnuksen numeron mukaan).',
+'redirect' => 'Ohjaus tiedostonimen, käyttäjätunnisteen tai versiotunnisteen mukaan',
+'redirect-legend' => 'Ohjaus tiedostoon tai sivulle',
+'redirect-summary' => 'Tämä toimintosivu ohjaa tiedostoon (tiedostonimen mukaan), sivulle (versiotunnisteen mukaan) tai käyttäjäsivulle (käyttäjätunnisteen mukaan). Käyttö: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/revision/328429]] tai [[{{#Special:Redirect}}/user/101]].',
 'redirect-submit' => 'Siirry',
 'redirect-lookup' => 'Hae:',
 'redirect-value' => 'Arvo:',
-'redirect-user' => 'Käyttäjän tunnusnumero',
+'redirect-user' => 'Käyttäjätunniste',
 'redirect-revision' => 'Sivun versio',
 'redirect-file' => 'Tiedostonimi',
 'redirect-not-exists' => 'Arvoa ei löytynyt',
@@ -4103,9 +4109,9 @@ Muussa tapauksessa voit käyttää alla olevaa helpompaa lomaketta. Kommenttisi
 'api-error-publishfailed' => 'Sisäinen virhe: Väliaikaisen tiedoston julkaiseminen epäonnistui.',
 'api-error-timeout' => 'Palvelin ei vastannut odotetun ajan kuluessa.',
 'api-error-unclassified' => 'Tapahtui tuntematon virhe.',
-'api-error-unknown-code' => 'Tuntematon virhe: $1',
+'api-error-unknown-code' => 'Tuntematon virhe: $1.',
 'api-error-unknown-error' => 'Sisäinen virhe: Jotain meni vikaan kun tiedostosi yritettiin tallentaa.',
-'api-error-unknown-warning' => 'Tuntematon varoitus: $1',
+'api-error-unknown-warning' => 'Tuntematon varoitus: $1.',
 'api-error-unknownerror' => 'Tuntematon virhe: $1.',
 'api-error-uploaddisabled' => 'Tiedostojen tallentaminen ei ole käytössä.',
 'api-error-verification-error' => 'Tiedosto voi olla vioittunut, tai sillä saattaa olla väärä tiedostopääte.',
index 2cda462..794d86d 100644 (file)
@@ -329,7 +329,7 @@ $messages = array(
 'articlepage' => 'Vís síðu við innihaldi',
 'talk' => 'Kjak',
 'views' => 'Skoðanir',
-'toolbox' => 'Amboðskassi',
+'toolbox' => 'Tól',
 'userpage' => 'Vís brúkarasíðu',
 'projectpage' => 'Vís verkætlanarsíðu',
 'imagepage' => 'Vís fílusíðuna',
@@ -1486,8 +1486,8 @@ Tín t-post adressa verður ikki avdúkað, tá aðrir brúkarir seta seg í sam
 'action-block' => 'noktað hesum brúkara at rætta',
 'action-protect' => 'broyt verjustøðuna hjá hesi síðu',
 'action-rollback' => 'rulla skjótt aftur rættingarnar hjá tí seinasta brúkaranum, sum rættaði eina ávísa síðu',
-'action-import' => 'innflyt hesa síðu frá aðrari wiki',
-'action-importupload' => 'innflyt hesa síðuna frá einari fílu sum er løgd út',
+'action-import' => 'innflyt síður frá aðrari wiki',
+'action-importupload' => 'innflyt síður frá einari fílu sum er løgd út',
 'action-patrol' => 'markað rætting hjá øðrum sum eftirhugda',
 'action-autopatrol' => 'fá tina rætting merkta sum eftirhugda',
 'action-unwatchedpages' => 'Síggj listan yvir síður sum ikki eru eftiransaðar',
@@ -1503,6 +1503,7 @@ Tín t-post adressa verður ikki avdúkað, tá aðrir brúkarir seta seg í sam
 
 # Recent changes
 'nchanges' => '$1 {{PLURAL:$1|broyting|broytingar}}',
+'enhancedrc-history' => 'søga',
 'recentchanges' => 'Seinastu broytingar',
 'recentchanges-legend' => 'Nýligar broytingar møguleikar',
 'recentchanges-summary' => 'Á hesi síðu kanst tú fylgja teimum nýggjastu broytingunum á hesi wiki.',
@@ -2205,10 +2206,12 @@ Sí $2 fyri fulla skráseting av strikingum.',
 'deletecomment' => 'Orsøk:',
 'deleteotherreason' => 'Onnur orsøk:',
 'deletereasonotherlist' => 'Onnur orsøk',
-'deletereason-dropdown' => '*Vanligar orsøkir til striking
-** Umbøn frá høvunda
+'deletereason-dropdown' => '* Vanligar orsøkir til striking
+** Spamm
+** Herverk (Vandalisma)
 ** Brot á upphavsrættin
-** Herverk (Vandalisma)',
+** Umbøn frá høvunda
+** Brotin víðaristilling',
 'delete-edit-reasonlist' => 'Rætta orsøkir til striking',
 'delete-toobig' => 'Henda síðan hevur eina langa rættingar søgu, meira enn $1 {{PLURAL:$1|versjón|versjónir}}. 
 Striking av slíkum síðum er avmarkað fyri at forða fyri at onkur av óvart kemur til at forstýra {{SITENAME}}.',
@@ -2228,7 +2231,7 @@ onkur annar hevur longu rættað ella rullað síðuna aftur.
 Seinasta broytingin á síðuni var av [[User:$3|$3]] ([[User talk:$3|kjak]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).',
 'editcomment' => "Rættingarfrágreiðingin var: \"''\$1''\".",
 'revertpage' => 'Tók burtur rættingar hjá [[Special:Contributions/$2|$2]] ([[User talk:$2|kjak]]) til seinastu versjón hjá [[User:$1|$1]]',
-'revertpage-nouser' => 'Tók burtur rættingar hjá einum fjaldum brúkara til seinastu versjón hjá [[User:$1|$1]]',
+'revertpage-nouser' => 'Tók burtur rættingar hjá einum fjaldum brúkara til seinastu versjón hjá  {{GENDER:$1|[[User:$1|$1]]}}',
 'rollback-success' => 'Tók burtur rættingar hjá $1;
 broytti tað aftur til seinastu versjón hjá $2.',
 
@@ -2341,7 +2344,7 @@ Sí [[Special:Log/delete|slettingarloggin]] fyri at síggja seinastu strikingar
 'contributions' => '{{GENDER:$1|Brúkaraíkøst}}',
 'contributions-title' => 'Brúkaraíkøst fyri $1',
 'mycontris' => 'Íkøst',
-'contribsub2' => 'Eftir $1 ($2)',
+'contribsub2' => 'Fyri {{GENDER:$3|$1}} ($2)',
 'nocontribs' => 'Ongar broytingar vóru funnar, sum samsvaraðu hesar treytirnar.',
 'uctop' => '(verandi)',
 'month' => 'Frá mánaði (og áðrenn):',
@@ -2484,10 +2487,7 @@ Fjalingarloggurin er vístur niðanfyri til kunningar:',
 'ipb-otherblocks-header' => '{{PLURAL:$1|Onnur sperring|Aðrar sperringar}}',
 'unblock-hideuser' => 'Tú kanst ikki taka burtur sperringina hjá hesum brúkara, eftirsum brúkaranavnið hjá viðkomandi er fjalt.',
 'ipb_cant_unblock' => 'Feilur: Sperring ID $1 ikki funnin. Tað er møguligt, at sperringin longu er tikin burtur.',
-'blockme' => 'Sperra meg',
 'proxyblocker' => 'Proxy sperring',
-'proxyblocker-disabled' => 'Henda funksjónin er ikki virkin.',
-'proxyblocksuccess' => 'Liðugt.',
 'sorbsreason' => 'Tín IP adressa er merkt sum ein open proxy í DNSBL sum {{SITENAME}} brúkar.',
 'sorbs_create_account_reason' => 'Tín IP adressa er merkt sum ein open proxy í DNSBL sum {{SITENAME}} brúkar.
 Tú kanst ikki upprætta eina konto.',
index 1206c92..07f3e9b 100644 (file)
@@ -608,7 +608,7 @@ $messages = array(
 'articlepage' => 'Voir la page de contenu',
 'talk' => 'Discussion',
 'views' => 'Affichages',
-'toolbox' => 'Boîte à outils',
+'toolbox' => 'Outils',
 'userpage' => 'Page utilisateur',
 'projectpage' => 'Page méta',
 'imagepage' => 'Voir la page du fichier',
@@ -909,8 +909,8 @@ pouvez ignorer ce message et continuer à utiliser votre ancien mot de passe.",
 'noemailcreate' => 'Vous devez fournir une adresse de courriel valide',
 'passwordsent' => "Un nouveau mot de passe a été envoyé à l'adresse de courriel de l'utilisateur « $1 ». Veuillez vous reconnecter après l'avoir reçu.",
 'blocked-mailpassword' => 'Votre adresse IP est bloquée en écriture, la fonction de rappel du mot de passe est donc désactivée pour éviter les abus.',
-'eauthentsent' => "Un courriel de confirmation a été envoyé à l'adresse indiquée.
-Avant qu'un autre courriel ne soit envoyé à ce compte, vous devrez suivre les instructions du courriel et confirmer que le compte est bien le vôtre.",
+'eauthentsent' => 'Un courriel de confirmation a été envoyé à l’adresse indiquée.
+Avant qu’un autre courriel ne soit envoyé à ce compte, vous devrez suivre les instructions du courriel et confirmer que le compte est bien le vôtre.',
 'throttled-mailpassword' => "Un courriel de réinitialisation de votre mot de passe a déjà été envoyé durant {{PLURAL:$1|la dernière heure|les $1 dernières heures}}. Afin d'éviter les abus, un seul courriel de réinitialisation de votre mot de passe sera envoyé par {{PLURAL:$1|heure|intervalle de $1 heures}}.",
 'mailerror' => "Erreur lors de l'envoi du courriel : $1",
 'acct_creation_throttle_hit' => "Quelqu'un utilisant votre adresse IP a créé {{PLURAL:$1|un compte|$1 comptes}} au cours des dernières 24 heures, ce qui constitue la limite autorisée dans cet intervalle de temps.
@@ -1342,15 +1342,15 @@ Les autres administrateurs de {{SITENAME}} pourront toujours accéder au contenu
 * Informations personnelles inappropriées
 *: ''adresse, numéro de téléphone, numéro de sécurité sociale, …''",
 'revdelete-legend' => 'Mettre en place des restrictions de visibilité :',
-'revdelete-hide-text' => 'Masquer le texte de la version',
+'revdelete-hide-text' => 'Texte de la révision',
 'revdelete-hide-image' => 'Masquer le contenu du fichier',
 'revdelete-hide-name' => "Masquer l'action et la cible",
-'revdelete-hide-comment' => 'Masquer le commentaire de modification',
-'revdelete-hide-user' => "Masquer le pseudo ou l'adresse IP du contributeur.",
+'revdelete-hide-comment' => 'Modifier le résumé',
+'revdelete-hide-user' => 'Nom d’utilisateur/Adresse IP de l’éditeur',
 'revdelete-hide-restricted' => "Supprimer ces données aux administrateurs ainsi qu'aux autres",
 'revdelete-radio-same' => '(ne pas changer)',
-'revdelete-radio-set' => 'Oui',
-'revdelete-radio-unset' => 'Non',
+'revdelete-radio-set' => 'Visible',
+'revdelete-radio-unset' => 'Masqué',
 'revdelete-suppress' => 'Masquer également les données pour les administrateurs',
 'revdelete-unsuppress' => 'Enlever les restrictions sur les versions restaurées',
 'revdelete-log' => 'Motif :',
@@ -2907,12 +2907,9 @@ Il est possible qu'un déblocage ait déjà été effectué.",
 Elle fait cependant partie de la plage $2 qui, elle, peut être débloquée.",
 'ip_range_invalid' => 'Plage IP incorrecte.',
 'ip_range_toolarge' => 'Les blocages de plages plus grandes que /$1 ne sont pas autorisées.',
-'blockme' => 'Bloquez-moi',
 'proxyblocker' => 'Bloqueur de mandataires',
-'proxyblocker-disabled' => 'Cette fonction est désactivée.',
 'proxyblockreason' => "Votre adresse IP a été bloquée car il s'agit d'un mandataire ouvert.
 Veuillez contacter votre fournisseur d'accès Internet ou votre support technique et l'informer de ce sérieux problème de sécurité.",
-'proxyblocksuccess' => 'Fait.',
 'sorbsreason' => 'Votre adresse IP est listée comme mandataire ouvert dans le DNSBL utilisé par {{SITENAME}}.',
 'sorbs_create_account_reason' => 'Votre adresse IP est listée comme mandataire ouvert dans le DNSBL utilisé par {{SITENAME}}.
 Vous ne pouvez pas créer un compte.',
@@ -3273,6 +3270,8 @@ Vous pouvez toutefois en visualiser la source.',
 'spam_reverting' => 'Rétablissement de la dernière version ne contenant pas de lien vers $1',
 'spam_blanking' => 'Toutes les versions contenant des liens vers $1 sont blanchies',
 'spam_deleting' => 'Toutes les versions contenaient des liens vers $1, suppression',
+'simpleantispam-label' => "Vérification anti-pourriel.
+Ne '''RIEN''' inscrire ici !",
 
 # Info page
 'pageinfo-title' => 'Informations pour « $1 »',
@@ -3953,7 +3952,7 @@ Veuillez confirmer que vous désirez réellement recréer cette page.",
 # Separators for various lists, etc.
 'semicolon-separator' => '&nbsp;;&#32;',
 'colon-separator' => '&nbsp;:&#32;',
-'percent' => '$1&nbsp;%',
+'percent' => '$1&#160;%',
 
 # Multipage image navigation
 'imgmultipageprev' => '← page précédente',
@@ -4127,7 +4126,7 @@ Vous devriez avoir reçu [{{SERVER}}{{SCRIPTPATH}}/COPYING une copie de la Licen
 # Special:Redirect
 'redirect' => 'Redirigé par fichier, utilisateur, ou ID de révision',
 'redirect-legend' => 'Rediriger vers une page ou un fichier',
-'redirect-summary' => "Cette page spéciale redirige vers un fichier (nom donné au fichier), une page (ID attribuée à la révision) ou une page d'utilisateur (identifiant numérique attribué à l'utilisateur).",
+'redirect-summary' => 'Cette page spéciale redirige vers un fichier (nom de fichier fourni), une page (ID de révision fourni) ou une page d’utilisateur (identifiant numérique de l’utilisateur fourni). Utilisation : [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/revision/328429]], ou [[{{#Special:Redirect}}/user/101]].',
 'redirect-submit' => 'Valider',
 'redirect-lookup' => 'Recherche :',
 'redirect-value' => 'Valeur :',
index 58df14c..e83aa9a 100644 (file)
@@ -2832,12 +2832,9 @@ O est possiblo qu’un dèblocâjo èye ja étâ fêt.',
 Portant, el est avouéc la plage $2 que pôt étre dèblocâ.',
 'ip_range_invalid' => 'Plage d’adrèces IP fôssa.',
 'ip_range_toolarge' => 'Los blocâjos de plages d’adrèces IP ples grantes que /$1 sont pas ôtorisâs.',
-'blockme' => 'Blocâd-mè',
 'proxyblocker' => "Bloquior de sèrvors mandatèros (''proxies'')",
-'proxyblocker-disabled' => 'Cela fonccion est dèsactivâ.',
 'proxyblockreason' => "Voutra adrèce IP at étâ blocâ perce qu’o est un sèrvor mandatèro (''proxy'') uvèrt.
 Vos volyéd veriér vers voutron fornissor d’accès u Malyâjo ou ben voutra assistance tècnica et l’enformar de cél problèmo de sècuritât sèriox.",
-'proxyblocksuccess' => 'Chavonâ.',
 'sorbsreason' => "Voutra adrèce IP est listâ coment sèrvor mandatèro (''proxy'') uvèrt dens lo DNSBL utilisâ per {{SITENAME}}.",
 'sorbs_create_account_reason' => "Voutra adrèce IP est listâ coment sèrvor mandatèro (''proxy'') uvèrt dens lo DNSBL utilisâ per {{SITENAME}}.
 Vos pouede pas fâre un compto.",
@@ -3197,6 +3194,8 @@ O est probâblament diu a un lim de vers un seto de defôr qu’aparêt sur la l
 'spam_reverting' => 'Rètablissement de la dèrriére vèrsion que contint gins de lim de vers $1',
 'spam_blanking' => 'Totes les vèrsions que contegnont des lims de vers $1 sont blanchies',
 'spam_deleting' => 'Totes les vèrsions que contegnont des lims de vers $1 sont suprimâs',
+'simpleantispam-label' => "Contrôlo anti-spame.
+Enscrîde '''REN''' ique !",
 
 # Info page
 'pageinfo-title' => 'Enformacions por « $1 »',
@@ -3846,7 +3845,7 @@ Volyéd confirmar que vos voléd franc refâre cela pâge.",
 # Separators for various lists, etc.
 'semicolon-separator' => '&nbsp;;&#32;',
 'colon-separator' => '&nbsp;:&#32;',
-'percent' => '$1&nbsp;%',
+'percent' => '$1&#160;%',
 
 # Multipage image navigation
 'imgmultipageprev' => '← pâge devant',
index 689339a..53d89d2 100644 (file)
@@ -267,7 +267,7 @@ $1',
 # All link text and link target definitions of links into project namespace that get used by other message strings, with the exception of user group pages (see grouppage).
 'aboutsite' => 'Auer {{SITENAME}}',
 'aboutpage' => 'Project:Auer',
-'copyright' => 'Det stäänt oner det lisens $1.',
+'copyright' => 'Det sidj as tu fun oner $1 , wan diar niks ööders stäänt.',
 'copyrightpage' => '{{ns:project}}:Copyrights',
 'currentevents' => 'Aktuels',
 'currentevents-url' => 'Project:Aktuels',
@@ -482,6 +482,9 @@ Ferjid det ei, an aachte üüb din [[Special:Preferences|{{SITENAME}} iinstelang
 'userlogin-resetpassword-link' => 'Paaswurd turagsaat',
 'helplogin-url' => 'Help:Uunmelde',
 'userlogin-helplink' => "[[{{MediaWiki:helplogin-url}}|Halep bi't uunmeldin]]",
+'userlogin-loggedin' => 'Du beest al üs {{GENDER:$1|$1}} uunmeldet.
+Brük det formulaar diar oner, am di mä en öödern nööm uuntumeldin.',
+'userlogin-createanother' => 'En ööder brükerkonto iinracht',
 'createacct-join' => 'Du oner din dooten iin.',
 'createacct-another-join' => "Skriiw oner a dooten för't nei brükerkonto hen",
 'createacct-emailrequired' => 'E-mail adres',
@@ -975,15 +978,15 @@ Dü könst di ferskeel uunluke. Wan dü muar wed wel, luke iin uun't [{{fullurl:
 * Persöönelk informatsjuunen, diar näämen wat uungung
 *: ''Adresen, Tilefoonnumern, Ferseekerangsnumern an sowat''",
 'revdelete-legend' => 'Iinstelangen, hüföl tu sen wees skal',
-'revdelete-hide-text' => 'Tekst faan det werjuun fersteeg',
+'revdelete-hide-text' => 'Tekst faan det werjuun',
 'revdelete-hide-image' => 'Fersteeg, wat uun det datei stäänt',
 'revdelete-hide-name' => 'Logbuk-aktjuun fersteeg',
-'revdelete-hide-comment' => 'Tuupfaadet beskriiwang fersteeg',
-'revdelete-hide-user' => 'Brükernööm/IP-adres faan di brüker fersteeg',
+'revdelete-hide-comment' => 'Tuupfaadet beskriiwang',
+'revdelete-hide-user' => 'Brükernööm/IP-adres faan di brüker',
 'revdelete-hide-restricted' => 'Dooten uk för administratooren an öödern fersteeg',
 'revdelete-radio-same' => '(ei feranre)',
-'revdelete-radio-set' => 'Ja',
-'revdelete-radio-unset' => 'Naan',
+'revdelete-radio-set' => 'Tu sen',
+'revdelete-radio-unset' => 'Ferbürgen',
 'revdelete-suppress' => "Grünj för't striken uk för administratooren an öödern fersteeg",
 'revdelete-unsuppress' => 'Weder iinsteld werjuunen luasmaage',
 'revdelete-log' => 'Grünj:',
@@ -1408,7 +1411,7 @@ Arken koon det lees.',
 'action-protect' => 'det seekerhaid faan sidjen tu feranrin',
 'action-rollback' => 'feranrangen faan di leetst brüker gau turagtusaaten',
 'action-import' => 'sidjen faan en ööder Wiki tu importiarin',
-'action-importupload' => 'sidjen auer det huuchschüüren faan datein tu importiarin',
+'action-importupload' => 'sidjen auer det huuchschüüren faan en datei tu importiarin',
 'action-patrol' => 'det werk faan ööder brükern üs kontroliaret tu kääntiaknin',
 'action-autopatrol' => 'aanj feranrangen üs kontroliaret tu kääntiaknin',
 'action-unwatchedpages' => 'det list faan sidjen uuntulukin, diar näämen üüb aachtet',
@@ -1712,6 +1715,7 @@ För a seekerhaid as img_auth.php ei aktiwiaret.',
 'listfiles_count' => 'Werjuunen',
 'listfiles-show-all' => 'Ual bilwerjuunen mä iinslütj',
 'listfiles-latestversion' => 'Aktuel werjuun',
+'listfiles-latestversion-yes' => 'Ja',
 'listfiles-latestversion-no' => 'Naan',
 
 # File description page
@@ -1920,6 +1924,7 @@ Uun arke rä stun ferwisangen tu't iarst an ööder widjerfeerang an uk tu det s
 'listusers' => 'Brükerfertiaknis',
 'listusers-editsonly' => 'Wise bluas aktiif brükern',
 'listusers-creationsort' => 'Sortiare efter dootem',
+'listusers-desc' => 'Sortiare amdeel',
 'usereditcount' => '{{PLURAL:$1|feranrang|$1 feranrangen}}',
 'usercreated' => '{{GENDER:$3|Maaget}} di $1 am a klook $2',
 'newpages' => 'Nei sidjen',
@@ -2172,9 +2177,11 @@ Halep an muar diartu: {{canonicalurl:{{MediaWiki:Helppage}}}}',
 'deleteotherreason' => 'Ööder/noch en grünj:',
 'deletereasonotherlist' => 'Ööder grünj',
 'deletereason-dropdown' => "*Algemian grünjer för't striken
-** Di skriiwer wul det so
+** Spam / dom tjüch
+** Wandaalen onerwais
 ** Copyright as ei beaachtet
-** Wandaalen onerwais",
+** Di skriiwer wul det so
+** Widjerfeerang uunstaken",
 'delete-edit-reasonlist' => "Grünjer för't striken bewerke",
 'delete-toobig' => 'Detdiar sidj hää muar üs $1 {{PLURAL:$1|werjuun|werjuunen}} . Sok sidjen kön ei so gau stregen wurd, ööders san a servers plaat.',
 'delete-warning-toobig' => "Detdiar sidj hää muar üs $1 {{PLURAL:$1|werjuun|werjuunen}} . Det striken koon komer maage bi't dootenbeenk.",
@@ -2192,7 +2199,7 @@ Halep an muar diartu: {{canonicalurl:{{MediaWiki:Helppage}}}}',
 Det leetst feranrang as faan [[User:$3|$3]] ([[User talk:$3|Diskusjuun]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).',
 'editcomment' => "Tuupfaadet feranrang: ''„$1“''.",
 'revertpage' => 'Feranrangen faan [[Special:Contributions/$2|$2]] ([[User talk:$2|Diskusjuun]]) san üüb di leetst stant faan [[User:$1|$1]] turagsaat wurden.',
-'revertpage-nouser' => 'Feranrangen faan en ferbürgenen brüker turagsaat an det leetst werjuun faan [[User:$1|$1]] weder iinsteld.',
+'revertpage-nouser' => 'Feranrangen faan en ferbürgenen brüker turagsaat an det leetst werjuun faan {{GENDER:$1|[[User:$1|$1]]}} weder iinsteld.',
 'rollback-success' => 'Feranrangen faan $1 turagsaat an det leetst werjuun faan $2 weder iinsteld.',
 
 # Edit tokens
@@ -2332,7 +2339,7 @@ $1",
 'contributions' => '{{GENDER:$1|Brüker}} bidracher',
 'contributions-title' => 'Brükerbidracher för "$1"',
 'mycontris' => 'Bidracher',
-'contribsub2' => 'För $1 ($2)',
+'contribsub2' => 'För {{GENDER:$3|$1}} ($2)',
 'nocontribs' => 'Diar wiar nian paasin brükerbidracher',
 'uctop' => '(aktuel)',
 'month' => 'faan muun (of iarer):',
@@ -2487,12 +2494,9 @@ Luke bi't [[Special:BlockList|sperlist]] för aal jo aktuel speren.",
 'ipb_blocked_as_range' => 'Feeler: Det IP-adres $1 as auer det widjloftag sper $2 speret. Det sper faan $1 alian koon ei apheewen wurd.',
 'ip_range_invalid' => 'Ferkiard IP-adresrüm',
 'ip_range_toolarge' => 'Adresrümen mut ei grater üs /$1 wees.',
-'blockme' => 'Spere mi',
 'proxyblocker' => 'Proxy blocker',
-'proxyblocker-disabled' => 'Detdiar funktjuun as ei aktiif',
 'proxyblockreason' => 'Din IP-adres as speret wurden, auer det tu en eebenen proxy hiart.
 Fertel det dan ISP of dan süsteemsiinst. Eeben proxys stel det seekerhaid uun fraag.',
-'proxyblocksuccess' => 'Klaar.',
 'sorbsreason' => 'Din IP-adres as uun det DNSBL faan {{SITENAME}} üs eeben proxy apfeerd.',
 'sorbs_create_account_reason' => 'Din IP-adres as uun det DNSBL faan {{SITENAME}} üs eeben proxy apfeerd. Dü könst nian brükerkonto maage.',
 'xffblockreason' => 'En IP-adres uun di X-Forwarded-For-Header as speret wurden, det as din aanj of det faan dan proxy server. Di spergrünj as: $1',
@@ -2821,6 +2825,8 @@ Dü könst di kweltekst uunluke.',
 'spam_reverting' => 'Leetst werjuun saner ferwisangen tu $1 weder iinsteld.',
 'spam_blanking' => 'Aal a werjuunen mä en ferwisang tu $1 san apklaaret wurden.',
 'spam_deleting' => 'Aal a werjuunen mä en ferwisung tu $1 san stregen wurden.',
+'simpleantispam-label' => "Anti-spam preew.
+Heer '''NIKS''' iindreeg!",
 
 # Info page
 'pageinfo-title' => 'Informatjuun tu „$1“',
@@ -3574,7 +3580,8 @@ You should have received [{{SERVER}}{{SCRIPTPATH}}/COPYING a copy of the GNU Gen
 # Special:Redirect
 'redirect' => 'Widjerfeerang üüb en brükersidj, sidjenwerjuun of datei.',
 'redirect-legend' => 'Widjerfeerang üüb en sidjenwerjuun of datei.',
-'redirect-summary' => 'Det spezial-sidj feert widjer üüb en brükersidj, sidjenwerjuun of datei.',
+'redirect-summary' => 'Detdiar spezial-sidj feert widjer üüb en brükersidj, sidjenwerjuun of datei.
+An det woort so brükt: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/revision/328429]], of [[{{#Special:Redirect}}/user/101]].',
 'redirect-submit' => 'Widjer',
 'redirect-lookup' => 'Schük:',
 'redirect-value' => 'Käänang of dateinööm:',
@@ -3637,7 +3644,10 @@ You should have received [{{SERVER}}{{SCRIPTPATH}}/COPYING a copy of the GNU Gen
 'tags-tag' => 'Kääntiaken-nööm',
 'tags-display-header' => 'Nööm üüb feranrangslisten',
 'tags-description-header' => 'Widjloftag beskriiwang',
+'tags-active-header' => 'Aktiif?',
 'tags-hitcount-header' => 'Kääntiakent feranrangen',
+'tags-active-yes' => 'Ja',
+'tags-active-no' => 'Naan',
 'tags-edit' => 'bewerke',
 'tags-hitcount' => '$1 {{PLURAL:$1|feranrang|feranrangen}}',
 
@@ -3804,9 +3814,9 @@ You should have received [{{SERVER}}{{SCRIPTPATH}}/COPYING a copy of the GNU Gen
 'limitreport-ppvisitednodes' => 'Taal faan ferbinjangsknooter för di föörproseser',
 'limitreport-ppgeneratednodes' => 'Faan di föörproseser bereegent ferbinjangsknooter',
 'limitreport-postexpandincludesize' => "Grate faan iinbinjangen efter't ütjwidjin",
-'limitreport-postexpandincludesize-value' => '$1/$2 bytes',
+'limitreport-postexpandincludesize-value' => '$1/$2 {{PLURAL:$2|byte|bytes}}',
 'limitreport-templateargumentsize' => "Grate faan't föörlaagenargument",
-'limitreport-templateargumentsize-value' => '$1/$2 bytes',
+'limitreport-templateargumentsize-value' => '$1/$2 {{PLURAL:$2|byte|bytes}}',
 'limitreport-expansiondepth' => 'Maksimaal ütjwidjangsjipde',
 'limitreport-expensivefunctioncount' => 'Taal faan apwendag parser-funktjuunen',
 
index 4fdeab1..db3b20d 100644 (file)
@@ -1913,7 +1913,6 @@ Meld de krekte reden! Neam bygelyks de siden dy't oantaaste waarden.",
 'ipb_expiry_invalid' => 'Tiid fan ferrinnen is net goed.',
 'ipb_already_blocked' => '"$1" is al útsluten',
 'ipb_cant_unblock' => 'Flater: It útsluten fan ID $1 kin net fûn wurde. It is miskien al net mear útsluten.',
-'proxyblocksuccess' => 'Dien.',
 
 # Developer tools
 'lockdb' => "Meitsje de database 'Net-skriuwe'",
index 52e6280..c8172dc 100644 (file)
@@ -218,8 +218,6 @@ $messages = array(
 'listingcontinuesabbrev' => 'ar lean.',
 'index-category' => 'Leathanaigh innéacsaithe',
 
-'linkprefix' => '/^(.*?)([a-zA-Z\\x80-\\xff]+)$/sD',
-
 'about' => 'Maidir leis',
 'article' => 'Leathanach ábhair',
 'newwindow' => '(a osclófar i bhfuinneog nua)',
@@ -1551,7 +1549,6 @@ liosta a fháil de coisc atá i bhfeidhm faoi láthair.',
 'proxyblockreason' => "Coisceadh do sheoladh IP dá bharr gur seachfhreastalaí
 neamhshlándála is ea é. Déan teagmháil le do chomhlacht idirlín nó le do lucht cabhrach teicneolaíochta
 go mbeidh 'fhios acu faoin fadhb slándála tábhachtach seo.",
-'proxyblocksuccess' => 'Rinneadh.',
 'sorbsreason' => 'Liostalaítear do sheoladh IP mar sheachfhreastalaí oscailte sa DNSBL.',
 
 # Developer tools
index b8a07a8..bef6c2f 100644 (file)
@@ -1051,7 +1051,7 @@ $2',
 'unwatchedpages' => '冇眏到𠮶页面',
 
 # List redirects
-'listredirects' => '重定向页面列表',
+'listredirects' => '重定向列表',
 
 # Unused templates
 'unusedtemplates' => '冇使用𠮶模板',
@@ -1086,7 +1086,7 @@ $2',
 'statistics-users-active-desc' => '头$1日操作过𠮶用户',
 'statistics-mostpopular' => '眵𠮶人最多𠮶页面',
 
-'doubleredirects' => '双重重定向页面',
+'doubleredirects' => '双重重定向',
 'doubleredirectstext' => '底下𠮶重定向链接到别只重定向页面:',
 'double-redirect-fixed-move' => '[[$1]]拕移动正,佢个下拕重定向到[[$2]]。',
 'double-redirect-fixer' => '重定向𠮶修正器',
@@ -1515,11 +1515,8 @@ $1',
 'ipb_already_blocked' => '锁到嘞"$1"',
 'ipb_cant_unblock' => '错误: 冇发现Block ID $1。个只IP话伓定拖解封喽。',
 'ip_range_invalid' => '冇用𠮶IP范围。',
-'blockme' => '封吥偶去',
 'proxyblocker' => '代理封锁器',
-'proxyblocker-disabled' => '个只功能用伓正喽。',
 'proxyblockreason' => '倷𠮶IP系一只公开𠮶代理,佢拖封到嘞。请联络倷𠮶Internet服务提供商或技术帮助再告诵佢俚个只严重𠮶安全问题。',
-'proxyblocksuccess' => '扤正啰。',
 'sorbsreason' => '{{SITENAME}}用𠮶 DNSBL 查到倷𠮶IP地址系只公开代理服务器。',
 'sorbs_create_account_reason' => '{{SITENAME}}用𠮶 DNSBL 检查到倷𠮶IP地址系只公开代理服务器,倷也就新开伓正帐户。',
 
index f9b3f76..4628ab3 100644 (file)
@@ -1537,11 +1537,8 @@ $1',
 'ipb_already_blocked' => '鎖到哩"$1"',
 'ipb_cant_unblock' => '錯誤: 冇發現Block ID $1。箇隻IP話伓定拕解封哩。',
 'ip_range_invalid' => '冇用嗰IP範圍。',
-'blockme' => '封吥我去',
 'proxyblocker' => '代理封鎖器',
-'proxyblocker-disabled' => '箇隻功能用伓正哩。',
 'proxyblockreason' => '汝嗰IP係一隻公開嗰代理,佢拕封到哩。請聯絡汝嗰Internet服務提供商或技術幫助再告誦佢俚箇隻嚴重嗰安全問題。',
-'proxyblocksuccess' => '舞正哩。',
 'sorbsreason' => '{{SITENAME}}用嗰 DNSBL 查到汝嗰IP地址係隻公開代理服務器。',
 'sorbs_create_account_reason' => '{{SITENAME}}用嗰 DNSBL 檢查到汝嗰IP地址係隻公開代理服務器,汝也就新開伓正帳戶。',
 
index 04d2471..00a058c 100644 (file)
@@ -42,12 +42,12 @@ $messages = array(
 'tog-hidepatrolled' => 'Falaich mùthaidhean fo fhaire ann an liosta nam mùthaidhean ùra',
 'tog-newpageshidepatrolled' => 'Falaich duilleagan fo fhaire ann an liosta nan duilleagan ùra',
 'tog-extendwatchlist' => "Leudaich an clàr-faire gus an seall e gach mùthadh 's chan ann an fheadhainn as ùire a-mhàin",
-'tog-usenewrc' => "Buidhnich na h-atharraichean a-rèir duilleige sna mùthaidhean ùra agus air a' chlàr-fhaire (feumaidh seo JavaScript)",
+'tog-usenewrc' => "Buidhnich na h-atharraichean a-rèir duilleige sna mùthaidhean ùra agus air a' chlàr-fhaire",
 'tog-numberheadings' => 'Cuir àireamhan ri ceann-sgrìobhaidhean leis fhèin',
-'tog-showtoolbar' => 'Seall am bàr-inneil deasachaidh (feumaidh seo JavaScript)',
-'tog-editondblclick' => 'Tòisich air deasachadh duilleige le briogadh dùbailt (feumaidh seo JavaScript)',
+'tog-showtoolbar' => 'Seall am bàr-inneal deasachaidh',
+'tog-editondblclick' => 'Tòisich air deasachadh duilleige le briogadh dùbailte',
 'tog-editsection' => 'Cuir am comas deasachadh earainn le ceanglaichean [deasaich]',
-'tog-editsectiononrightclick' => "Cuir an comas deasachadh earainn le briogadh deas air tiotal de dh'earrainn (feumaidh seo JavaScript)",
+'tog-editsectiononrightclick' => "Cuir an comas deasachadh earrainn le briogadh deas air tiotal de dh'earrainn",
 'tog-showtoc' => 'Seall an clàr-innse (air duilleagan air a bheil barrachd air 3 ceann-sgrìobhaidhean)',
 'tog-rememberpassword' => "Cuimhnich gu bheil mi air logadh a-steach air a' choimpiutair seo (suas gu $1 {{PLURAL:$1|latha|latha|làithean|latha}})",
 'tog-watchcreations' => "Cuir duilleagan a chruthaicheas mi air a' chlàr-fhaire agam",
@@ -65,7 +65,7 @@ $messages = array(
 'tog-shownumberswatching' => "Nochd àireamh nan cleachdaichean a tha a' cumail sùil air",
 'tog-oldsig' => 'An t-earr-sgrìobhadh làithreach:',
 'tog-fancysig' => 'Làimhsich an t-earr-sgrìobhadh mar wikitext (gun cheangal leis fhèin)',
-'tog-uselivepreview' => 'Cleachd an ro-shealladh beò (feumaidh seo JavaScript) (deuchainneach)',
+'tog-uselivepreview' => 'Cleachd an ro-shealladh beò (deuchainneach)',
 'tog-forceeditsummary' => "Cuir ceist nuair a dh'fhàgas mi gearr-chunntas an deasachaidh bàn",
 'tog-watchlisthideown' => 'Falaich mo mhùthaidhean fhèin air mo chlàr-faire',
 'tog-watchlisthidebots' => 'Falaich mùthaidhean nam bot air mo chlàr-faire',
@@ -78,6 +78,7 @@ $messages = array(
 'tog-showhiddencats' => 'Seall na roinnean falaichte',
 'tog-norollbackdiff' => 'Na dèan diof às dèidh roiligeadh air ais',
 'tog-useeditwarning' => 'Thoir rabhadh dhomh ma bhios mi an impis duilleag deasachaidh fhàgail mus do shàbhail mi na mùthaidhean agam',
+'tog-prefershttps' => 'Cleachd ceangal tèarainte an-còmhnaidh nuair a bhios mi clàraichte a-staigh',
 
 'underline-always' => 'An-còmhnaidh',
 'underline-never' => 'Na dèan seo idir',
@@ -141,6 +142,18 @@ $messages = array(
 'oct' => 'Dàmh',
 'nov' => 'Samh',
 'dec' => 'Dùbh',
+'january-date' => '$1 dhen Fhaoilleach',
+'february-date' => '$1 dhen Ghearran',
+'march-date' => '$1 dhen Mhàrt',
+'april-date' => '$1 dhen Ghiblean',
+'may-date' => '$1 dhen Chèitean',
+'june-date' => '$1 dhen Ògmhios',
+'july-date' => '$1 dhen Iuchar',
+'august-date' => '$1 dhen Lùnastal',
+'september-date' => '$1 dhen t-Sultain',
+'october-date' => '$1 dhen Dàmhair',
+'november-date' => '$1 dhen t-Samhain',
+'december-date' => '$1 dhen Dùbhlachd',
 
 # Categories related messages
 'pagecategories' => '{{PLURAL:$1|Roinn-seòrsa|Roinn-seòrsa|Roinnean-seòrsa|Roinn-seòrsa}}',
@@ -166,7 +179,7 @@ $messages = array(
 'newwindow' => "(a' fosgladh ann an uinneag ùr)",
 'cancel' => 'Sguir dheth',
 'moredotdotdot' => 'Barrachd...',
-'morenotlisted' => 'Barrachd nach eil air an liosta...',
+'morenotlisted' => 'Chan eil an liosta seo iomlan.',
 'mypage' => 'Duilleag',
 'mytalk' => 'Deasbaireachd',
 'anontalk' => 'Conaltradh airson an IP seo',
@@ -222,6 +235,7 @@ $messages = array(
 'create-this-page' => 'Cruthaich an duilleag seo',
 'delete' => 'Sguab às',
 'deletethispage' => 'Sguab às an duilleag seo',
+'undeletethispage' => 'Neo-dhèan sguabadh às na duilleige seo',
 'undelete_short' => "Neo-dhèan sguabadh às de {{PLURAL:$1|dh'aon deasachadh|$1 dheasachadh|$1 deasachaidhean|$1 deasachadh}}",
 'viewdeleted_short' => 'Seall {{PLURAL:$1|aon deasachadh|$1 dheasachadh|$1 deasachaidhean|$1 deasachadh}} a chaidh a sguabadh às',
 'protect' => 'Dìon',
@@ -238,7 +252,7 @@ $messages = array(
 'articlepage' => 'Seall duilleag na susbainte',
 'talk' => 'Deasbaireachd',
 'views' => 'Tadhalan',
-'toolbox' => 'Bogsa-innealan',
+'toolbox' => 'Innealan',
 'userpage' => "Seall duilleag a' chleachdaiche",
 'projectpage' => "Seall duilleag a' phròiseict",
 'imagepage' => 'Seall duilleag an fhaidhle',
@@ -268,7 +282,7 @@ $1",
 # All link text and link target definitions of links into project namespace that get used by other message strings, with the exception of user group pages (see grouppage).
 'aboutsite' => 'Mu dhèidhinn {{SITENAME}}',
 'aboutpage' => 'Project:Mu dhèidhinn',
-'copyright' => 'Tha susbaint ri làimh fo $1.',
+'copyright' => "Tha susbaint ri làimh fo $1 mur eil an caochladh 'ga innse.",
 'copyrightpage' => '{{ns:project}}:Còraichean lethbhric',
 'currentevents' => 'Cùisean an latha',
 'currentevents-url' => 'Project:Cùisean an latha',
@@ -352,6 +366,12 @@ Gheibh thu liosta nan duilleagan sònraichte 's dligheach aig [[Special:SpecialP
 # General errors
 'error' => 'Mearachd',
 'databaseerror' => 'Mearachd an stòir-dhàta',
+'databaseerror-text' => "Thachair mearachd leis a' cheist a chuireadh dhan stòr-dàta.
+Dh'fhaoidte gu bheil buga sa bhathar-bhog.",
+'databaseerror-textcl' => "Thachair mearachd leis a' cheist a chuireadh dhan stòr-dàta.",
+'databaseerror-query' => 'Ceist: $1',
+'databaseerror-function' => 'Foincsean: $1',
+'databaseerror-error' => 'Mearachd: $1',
 'laggedslavemode' => "'''Rabhadh:''' Faodaidh nach eil ùrachaidhean a rinneadh o chionn ghoirid a' nochdadh san duilleag.",
 'readonly' => 'Stòr-dàta glaiste',
 'enterlockreason' => "Cuir a-steach adhbhar a' ghlais, a' gabhail a-steach tuairmeas air fuasgladh a' ghlais.",
@@ -385,6 +405,7 @@ Faodaidh gun deach a sguabadh às le cuideigin eile mu thràth.',
 'cannotdelete-title' => 'Cha ghabh an duilleag "$1" a sguabadh às',
 'delete-hook-aborted' => 'Sguireadh dhen sguabadh às ri linn dubhain.
 Cha deach adhbhar a thoirt seachad.',
+'no-null-revision' => 'Cha b\' urrainn dhuinn lèirmheas neoinitheach ùr a chruthachadh dhan duilleag "$1"',
 'badtitle' => 'Droch thiotal',
 'badtitletext' => "Bha an duilleag a dh'iarr thu mì-dhligheach, falamh no le tiotal eadar-chànanach no eadar-uici air a dhroch cheangal.
 Faodaidh gu bheil aon no barrachd charactairean ann nach urrainn dhut a chleachdadh ann an tiotalan.",
@@ -412,6 +433,10 @@ $2',
 'namespaceprotected' => "Chan eil cead agad duilleagan san namespace '''$1''' a dheasachadh.",
 'customcssprotected' => "Chan eil cead agad an duilleag CSS seo a dheasachadh a chionn 's gu bheil na roghainnean pearsanta aig cleachdaiche eile innte.",
 'customjsprotected' => "Chan eil cead agad an duilleag JavaScript seo a dheasachadh a chionn 's gu bheil na roghainnean pearsanta aig cleachdaiche eile innte.",
+'mycustomcssprotected' => 'Chan eil cead agad an duilleag CSS seo a dheasachadh.',
+'mycustomjsprotected' => 'Chan eil cead agad an duilleag JavaScript seo a dheasachadh.',
+'myprivateinfoprotected' => 'Chan eil cead agad am fiosrachadh prìobhaideach agad a dheasachadh.',
+'mypreferencesprotected' => 'Chan eil cead agad na roghainnean agad a dheasachadh.',
 'ns-specialprotected' => 'Chan ghabh duilleagan sònraichte a dheasachadh.',
 'titleprotected' => 'Chaidh an duilleag seo a dhìon o chruthachadh le [[User:$1|$1]].
 Seo am mìneachadh: "\'\'$2\'\'".',
@@ -428,16 +453,26 @@ Thug an rianaire a ghlais e seachad an t-adhbhar a leanas: "$3".',
 'virus-unknownscanner' => 'sganair bhìorasan neo-aithnichte:',
 
 # Login and logout pages
-'logouttext' => "'''Chaidh do logadh a-mach.'''
-'S urrainn dhut leantainn air adhart a' cleachdadh {{SITENAME}} a chleachdadh gun urra no 's urrainn dhut <span class='plainlinks'>[$1 logadh a-steach a-rithist]</span> mar an dearbh-chleachdaiche no mar chleachdaiche eile.
-Thoir an aire gum bi coltas air cuide dhe na duilleagan mar gum biodh tu air logadh a-steach gus am falamhaich thu tasgadan a' bhrabhsair agad.",
+'logouttext' => "'''Chaidh do chlàradh a-mach.'''
+
+Thoir an aire gum bi coltas air cuid dhe na duilleagan mar gum biodh tu air clàradh a-steach gus am falamhaich thu tasgadan a' bhrabhsair agad.",
 'welcomeuser' => 'Fàilte ort, $1',
 'welcomecreation-msg' => 'Chaidh an cunntas agad a chruthachadh.
 Na dìochuimhnich na [[Special:Preferences|roghainnean agad air {{SITENAME}}]] a ghleusadh dhut fhèin.',
 'yourname' => 'Ainm-cleachdaiche:',
+'userlogin-yourname' => 'Ainm-cleachdaiche',
+'userlogin-yourname-ph' => 'Cuir a-steach an t-ainm-cleachdaiche agad',
+'createacct-another-username-ph' => 'Cuir a-steach an t-ainm-cleachdaiche',
 'yourpassword' => 'Am facal-faire agad',
+'userlogin-yourpassword' => 'Facal-faire',
+'userlogin-yourpassword-ph' => 'Cuir a-steach am facal-faire agad',
+'createacct-yourpassword-ph' => 'Cuir a-steach facal-faire',
 'yourpasswordagain' => 'Ath-sgrìobh facal-faire',
+'createacct-yourpasswordagain' => 'Dearbh am facal-faire',
+'createacct-yourpasswordagain-ph' => 'Cuir a-steach am facal-faire a-rithist',
 'remembermypassword' => "Cuimhnich gu bheil mi air logadh a-steach air a' choimpiutair seo (suas gu $1 {{PLURAL:$1|latha|latha|làithean|latha}})",
+'userlogin-remembermypassword' => 'Cum clàraichte a-staigh mi',
+'userlogin-signwithsecure' => 'Cleachd ceangal tèarainte',
 'yourdomainname' => 'An àrainn-lìn agad:',
 'password-change-forbidden' => 'Chan urrainn dhut faclan-faire atharrachadh air an uicipeid seo.',
 'externaldberror' => 'Thachair mearachd le dearbhadh an stòir-dhàta air neo chan eil cead agad an cunntas agad air an taobh a-muigh ùrachadh.',
@@ -449,18 +484,44 @@ Na dìochuimhnich na [[Special:Preferences|roghainnean agad air {{SITENAME}}]] a
 'logout' => 'Log a-mach',
 'userlogout' => 'Log a-mach',
 'notloggedin' => 'Chan eil thu air logadh a-steach',
+'userlogin-noaccount' => 'Nach eil cunntas agad?',
+'userlogin-joinproject' => 'Gabh pàirt ann an {{SITENAME}}',
 'nologin' => 'Nach eil cunntas agad fhathast? $1.',
 'nologinlink' => 'Cruthaich cunntas',
 'createaccount' => 'Cruthaich cunntas ùr',
 'gotaccount' => 'A bheil cunntas agad mu thràth? $1.',
 'gotaccountlink' => 'Log a-steach',
 'userlogin-resetlink' => "Na dhìochuimhnich thu d' ainm is facal-faire?",
+'userlogin-resetpassword-link' => 'Ath-shuidhich am facal-faire agad',
+'helplogin-url' => "Help:A' clàradh a-steach",
+'userlogin-helplink' => "[[{{MediaWiki:helplogin-url}}|Cobhair leis a' chlàradh a-steach]]",
+'userlogin-loggedin' => 'Chaidh do chlàradh mar {{GENDER:$1|$1}} mu thràth.
+Cleachd am foirm gu h-ìosal airson clàradh a-steach mar chleachdaiche eile.',
+'userlogin-createanother' => 'Cruthaich cunntas eile',
+'createacct-join' => 'Cuir a-steach am fiosrachadh agad gu h-ìosal.',
+'createacct-another-join' => "Cuir a-steach fiosrachadh a' chunntais ùir gu h-ìosal.",
+'createacct-emailrequired' => 'Seòladh puist-d',
+'createacct-emailoptional' => 'Seòladh puist-d (roghainneil)',
+'createacct-email-ph' => 'Cuir a-steach an seòladh puist-d agad',
+'createacct-another-email-ph' => 'Cuir a-steach seòladh puist-d',
 'createaccountmail' => "Cleachd facal-faire sealach air thuaiream agus cuir e dhan phost-d a tha 'ga shònrachadh gu h-ìosal",
+'createacct-realname' => 'Fìor-ainm (roghainneil)',
 'createaccountreason' => 'Adhbhar:',
+'createacct-reason' => 'Adhbhar',
+'createacct-reason-ph' => "Carson a tha thu a' cruthachadh cunntas eile?",
+'createacct-captcha' => 'Sgrùdadh tèarainteachd',
+'createacct-imgcaptcha-ph' => 'Cuir a-steach an teacsa a chì thu gu h-àrd',
+'createacct-submit' => 'Cruthaich an cunntas agad',
+'createacct-another-submit' => 'Cruthaich cunntas eile',
+'createacct-benefit-heading' => "Tha {{SITENAME}} 'ga chruthachadh le daoine mar thu fhèin.",
+'createacct-benefit-body1' => '{{PLURAL:$1|deasachadh|dheasachadh|deasachaidhean|deasachadh}}',
+'createacct-benefit-body2' => '{{PLURAL:$1|duilleag|dhuilleag|duilleagan|duilleag}}',
+'createacct-benefit-body3' => '{{PLURAL:$1|chom-pàirtiche|chom-pàirtiche|com-pàirtichean|com-pàirtiche}} o chionn goirid',
 'badretype' => "Chan eil an dà fhacal-faire a chuir thu a-steach a' freagairt ri chèile.",
 'userexists' => "Tha an t-ainm-cleachdaiche a chuir thu a-steach 'ga chleachdadh mu thràth.
 Nach tagh thu ainm eile?",
 'loginerror' => 'Mearachd log a-steach',
+'createacct-error' => "Mearachd le cruthachadh a' chunntais",
 'createaccounterror' => 'Cha do ghabh an cunntas a leanas a chruthachadh: $1',
 'nocookiesnew' => "Chaidh an cunntas a chruthachadh ach cha do rinn thu logadh a-steach.
 Tha {{SITENAME}} a' cleachdadh briosgaidean gus daoine a logadh a-steach.
@@ -503,7 +564,7 @@ agus leantainn ort leis an t-seann fhacal-faire.',
 Clàraich a-steach a-rithist nuair a gheibh thu e.',
 'blocked-mailpassword' => "Chaidh bacadh a chur air an t-seòladh IP agad 's chan eil cead deasachaidh agad agus chan urrainn dhut an gleus a chum aiseag an fhacail-fhaire a chleachdadh gus casg a chur air mì-ghnàthachadh.",
 'eauthentsent' => 'Chaidh post-d dearbhaidh a chur dhan phost-d a chaidh ainmeachadh.
-Mus dèid post-d sam bith eile a chur dhan chunntas, feumaidh tu leantainn ris an treòrachadh sa phost-d mar dhearbhadh gur ann agadsa a tha an cunntas.',
+Mus dèid post-d sam bith eile a chur dhan chunntas, feumaidh tu leantainn ris an stiùireadh sa phost-d mar dhearbhadh gur ann agadsa a tha an cunntas.',
 'throttled-mailpassword' => 'Chaidh post-d a chur airson ath-shuidheachadh facail-fhaire mu thràth {{PLURAL:$1|uair|$1 uair|$1 uairean|$1 uair}} a thìde air ais.
 Gus casg a chur air mì-ghnàthachadh, cha chuir sinn ach aon chuimhneachan facail-fhaire gach {{PLURAL:$1|uair|$1 uair|$1 uairean|$1 uair}} a thìde.',
 'mailerror' => "Mearachd a' cur post: $1",
@@ -518,7 +579,7 @@ Cuir a-steach seòladh san fhòrmat cheart no falamhaich an raon sin.",
 'cannotchangeemail' => 'Cha ghabh na puist-d a tha co-cheangailte ri cunntas atharrachadh air an uicipeid seo.',
 'emaildisabled' => 'Chan urrainn dhut puist-d a chur air an làrach seo.',
 'accountcreated' => 'Cunntas cruthaichte',
-'accountcreatedtext' => 'Chaidh an cunntas cleachdaiche airson $1 a chruthachadh.',
+'accountcreatedtext' => 'Chaidh an cunntas cleachdaiche airson [[{{ns:User}}:$1|$1]] ([[{{ns:User talk}}:$1|talk]]) a chruthachadh.',
 'createaccount-title' => 'Cruthachadh cunntais airson {{SITENAME}}',
 'createaccount-text' => 'Chruthaich cuideigin cunntas airson a\' phost-d agad air {{SITENAME}} ($4) air a bheil "$2", leis an fhacal-fhaire "$3".
 Bu chòir dhut clàradh a-steach agus am facal-faire agad atharrachadh gu h-ìosal an-dràsta.
@@ -526,10 +587,12 @@ Bu chòir dhut clàradh a-steach agus am facal-faire agad atharrachadh gu h-ìos
 \'S urrainn dhut an teachdaireachd seo a leigeil seachad ma chaidh an cunntas a chruthachadh air mhearachd.',
 'usernamehasherror' => 'Chan fhaod hais a bhith ann an ainm cleachdaiche',
 'login-throttled' => "Dh'fheuch thu ri clàradh a-steach ro thric o chionn ghoirid.
-Fuirich ort mus feuch thu ris a-rithist.",
+Fuirich ort $1 mus feuch thu ris a-rithist.",
 'login-abort-generic' => "Cha do shoirbhich leat leis a' chlàradh a-steach - Chaidh sgur dheth",
 'loginlanguagelabel' => 'Cànan: $1',
 'suspicious-userlogout' => "Chaidh d' iarrtas airson clàradh a-mach a dhiùltadh a chionn 's gu bheil coltas gun deach a chur le brabhsair briste no le progsaidh tasglannaidh.",
+'createacct-another-realname-tip' => 'Cha leig thu leas innse dè am fìor-ainm a tha ort.
+Ma bheir thu seachad e, thèid seo a chleachdadh gus urram a thoirt dha na h-ùghdaran airson an cuid obrach.',
 
 # Email sending
 'php-mail-error-unknown' => 'Mearachd neo-aithichte san fheart mail() aig PHP.',
@@ -545,8 +608,7 @@ Gus an clàradh a-steach a choileadh, tha agad ri facal-faire ùr a shuidheachad
 'newpassword' => 'Facal-faire ùr',
 'retypenew' => 'Ath-sgrìobh am facal-faire ùr',
 'resetpass_submit' => "Suidhich am facal-faire 's clàraich a-steach",
-'changepassword-success' => "Chaidh am facal-faire agad atharrachadh!
-'Gad chlàradh a-steach an-dràsta...",
+'changepassword-success' => 'Chaidh am facal-faire agad atharrachadh!',
 'resetpass_forbidden' => 'Cha ghabh na faclan-faire atharrachadh',
 'resetpass-no-info' => 'Feumaidh tu clàradh a-steach mus dèan thu inntrigeadh dìreach dhan duilleag seo.',
 'resetpass-submit-loggedin' => 'Atharraich am facal-faire',
@@ -554,11 +616,15 @@ Gus an clàradh a-steach a choileadh, tha agad ri facal-faire ùr a shuidheachad
 'resetpass-wrong-oldpass' => "Tha am facal-faire sealach no làithreach mì-dhligheach.
 Saoil an do dh'atharraich thu am facal-faire agad mu thràth no an do dh'iarr thu facal-faire sealach ùr?",
 'resetpass-temp-password' => 'Facal-faire sealach:',
+'resetpass-abort-generic' => 'Chuir leudachan crìoch air atharrachadh an fhacail-fhaire.',
 
 # Special:PasswordReset
 'passwordreset' => 'Ath-shuidhich am facal-faire',
+'passwordreset-text-one' => 'Lìon am foirm seo gus am facal-faire agad ath-shuidheachadh.',
+'passwordreset-text-many' => '{{PLURAL:$1|Lìon aon dhe na raointean gus am facal-faire agad ath-shuidheachadh.}}',
 'passwordreset-legend' => 'Ath-shuidhich am facal-faire',
 'passwordreset-disabled' => 'Chaidh ath-shuidheachadh nam faclan-faire a chur à comas air an uicipeid seo.',
+'passwordreset-emaildisabled' => "Chaidh feartan a' phuist-d a chur à comas san uicipeid seo.",
 'passwordreset-username' => 'Ainm-cleachdaiche:',
 'passwordreset-domain' => 'Àrainn-lìn:',
 'passwordreset-capture' => "A bheil thu airson coimhead air a' phost-d?",
@@ -581,7 +647,7 @@ Bu chòir dhut clàradh a-steach agus facal-faire ùr a thaghadh an-dràsta. Ma
 Facal-faire sealach: $2',
 'passwordreset-emailsent' => 'Chaidh post-d airson ath-shuidheachadh an fhacail-fhaire a chur.',
 'passwordreset-emailsent-capture' => 'Chaidh post-d a chum ath-shuidheachadh an fhacail-fhaire a chur agus chì thu sin gu h-ìosal.',
-'passwordreset-emailerror-capture' => "Chaidh post-d a chum ath-shuidheachadh an fhacail-fhaire a ghintinn agus chì thu sin gu h-ìosal ach cha b' urrainn dhuinn a chur dhan chleachdaiche: $1",
+'passwordreset-emailerror-capture' => "Chaidh post-d a chum ath-shuidheachadh an fhacail-fhaire a ghintinn agus chì thu sin gu h-ìosal ach cha b' urrainn dhuinn a chur dhan chleachdaiche {{GENDER:$2|user}}: $1",
 
 # Special:ChangeEmail
 'changeemail' => 'Atharraich am post-d',
@@ -595,6 +661,19 @@ Facal-faire sealach: $2',
 'changeemail-submit' => 'Atharraich am post-d',
 'changeemail-cancel' => 'Sguir dheth',
 
+# Special:ResetTokens
+'resettokens' => 'Ath-shuidhich na tòcanan',
+'resettokens-text' => "'S urrainn dhut tòcanan ath-shuidheachadh a bheir cothrom dhut air cuid a dhàta prìobhaideach a tha co-cheangailte ris a' chunntas agad.
+
+Bu chòir dhut seo a dhèanamh ma thug thu do chuideigin e air mhearachd no ma bhris cuideigin a-steach air a' chunntas agad.",
+'resettokens-no-tokens' => 'Chan eil tòcan ann a ghabhas ath-shuidheachadh.',
+'resettokens-legend' => 'Ath-shuidhich na tòcanan',
+'resettokens-tokens' => 'Tòcanan:',
+'resettokens-token-label' => "$1 ('s e $2 an luach làithreach)",
+'resettokens-watchlist-token' => "Tòcan airson an inbhir-lìn (Atom/RSS) a sheallas dhut [[Special:Watchlist|atharraichean air duilleagan a tha air a' chlàr-fhaire agad]]",
+'resettokens-done' => 'Chaidh na tòcanan ath-shuidheachadh.',
+'resettokens-resetbutton' => 'Ath-shuidhich na tòcanan a chaidh a thaghadh',
+
 # Edit page toolbar
 'bold_sample' => 'Teacs trom',
 'bold_tip' => 'Teacs trom',
@@ -677,7 +756,7 @@ Dh'fhaoidte gun deach a ghluasad no a sguabadh às fhad 's a bha thu a' coimhead
 'accmailtitle' => 'Facal-faire air a chur.',
 'accmailtext' => "Chaidh facal-faire a chruthachadh air thuaiream airson [[User talk:$1|$1]] 's a chur gu $2.
 
-Gabhaidh am facal-faire airson a' chunntais ùir seo atharrachadh air an fo ''[[Special:ChangePassword|atharraich facal-faire]]'' as dèidh do chleachdaiche logadh a-steach.",
+Gabhaidh am facal-faire airson a' chunntais ùir seo atharrachadh air an fo ''[[Special:ChangePassword|atharraich facal-faire]]'' as dèidh dhan chleachdaiche clàradh a-steach.",
 'newarticle' => '(Ùr)',
 'newarticletext' => "Lean thu ri ceangal gu duilleag nach eil ann fhathast.
 Cuir teacs sa bhogsa gu h-ìosal gus an duilleag seo a chruthachadh (seall air [[{{MediaWiki:Helppage}}|duilleag na cobharach]] airson barrachd fiosrachaidh).
@@ -786,7 +865,7 @@ Seo an rud mu dheireadh san loga mar fhiosrachadh dhut:",
 'nocreate-loggedin' => 'Chan eil cead agad duilleagan ùra a chruthachadh.',
 'sectioneditnotsupported-title' => 'Chan eil taic ri deasachadh earrannan',
 'sectioneditnotsupported-text' => 'Chan eil taic ri deasachadh earrannan air an duilleag seo.',
-'permissionserrors' => "Meareachd leis a' chead",
+'permissionserrors' => "Mearachd leis a' chead",
 'permissionserrorstext' => 'Chan eil cead agad sin a dhèanamh air sgàth {{PLURAL:$1|an adhbhair|an $1 adhbhar|nan $1 adhbharan|nan $1 adhbhar}} a leanas:',
 'permissionserrorstext-withaction' => 'Chan eil cead agad airson "$2" air sgàth {{PLURAL:$1|an $1 adhbhair|an $1 adhbhar|nan $1 adhbharan|nan $1 adhbhar}} a leanas:',
 'recreate-moveddeleted-warn' => "'''Rabhadh: Tha thu gu bhith ath-chruthachadh duilleag a chaidh a sguabadh às roimhe.'''
@@ -802,6 +881,7 @@ Cha deach adhbhar a thoirt seachad.',
 Tha coltas gun deach a sguabadh às.",
 'edit-conflict' => 'Còmhstri deasachaidh.',
 'edit-no-change' => "Chaidh an obair-dheasachaidh agad a leigeil seachad a chionn 's nach do dh'atharraich thu dad.",
+'postedit-confirmation' => 'Chaidh na dheasaich thu a shàbhaladh.',
 'edit-already-exists' => "Cha b' urrainn dhuinn an duilleag ùr a chruthachadh.
 Tha e ann mu thràth.",
 'defaultmessagetext' => 'Teacsa bunaiteach na teachdaireachd',
@@ -829,10 +909,29 @@ Cha dèid cuid dhith a ghabhail a-steach.",
 Chaidh na h-argamaidean sinn a leigeil seachad.",
 'post-expand-template-argument-category' => 'Duilleagan air an deach argamaidean teamplaidean fhàgail às',
 'parser-template-loop-warning' => 'Mhothaicheadh do lùb teamplaid: [[$1]]',
+'parser-template-recursion-depth-warning' => 'Chaidh thu thairis air crìoch doimhne nan ath-chùrsaidhean teamplaid ($1)',
+'language-converter-depth-warning' => 'Chaidh thu thairis air crìoch doimhne an iompachair chànain ($1)',
+'node-count-exceeded-category' => 'Duilleagan far an deachas thairis air cunntas nan nòdan',
+'node-count-exceeded-warning' => 'Chaidh an duilleag thairis air cunntas nan nòdan',
+'expansion-depth-exceeded-category' => "Duilleagan far an deachas thairis air a' chrìoch leudachaidh",
+'expansion-depth-exceeded-warning' => 'Chaidh an duilleag thairis air an doimhne leudachaidh',
 'parser-unstrip-loop-warning' => 'Mhothaich sinn do lùb unstrip',
+'parser-unstrip-recursion-limit' => 'Chaidheas thairis air crìoch unstrip recursion ($1)',
+'converter-manual-rule-error' => 'Mhothaich sinn do mhearachd san riaghailt iompachadh làimhe airson cànan',
+
+# "Undo" feature
+'undo-success' => "Gabhaidh an deasachadh seo a neo-dhèanamh.
+Thoir sùil air a' choimeas gu h-ìosal is dearbh gur e sin a tha fa-near dhut agus sàbhail na h-atharraichean gu h-ìosal gus neo-dhèanamh an deasachaidh a choileanadh.",
+'undo-failure' => "Cha b' urrainn dhuinn an deasachadh a neo-dhèanamh air sgàth 's gun robh deasachaidhean eile sa mheadhan.",
+'undo-norev' => "Cha b' urrainn dhuinn an deasachadh a neo-dhèanamh a chionn 's nach robh e ann no gun deach a sguabadh às.",
+'undo-summary' => 'Neo-dhèan mùthadh $1 le [[Special:Contributions/$2|$2]] ([[User talk:$2|Deasbaireachd]])',
+'undo-summary-username-hidden' => 'Neo-dhèan am mùthadh $1 le cleachdaiche falaichte',
 
 # Account creation failure
 'cantcreateaccounttitle' => 'Cha ghabh an cunntas a chruthachadh',
+'cantcreateaccount-text' => "Chuir [[User:$3|$3]] bacadh air cruthachadh chunntasan on t-seòladh IP seo ('''$1''').
+
+Dh'innise $3 gun do rinn iad seo air sgàth: ''$2''",
 
 # History pages
 'viewpagelogs' => 'Seall logaichean na duilleige seo',
@@ -870,20 +969,70 @@ Feuch is [[Special:Search|lorg duilleagan ùra iomachaidh air an uici]]",
 'rev-deleted-comment' => '(chaidh gearr-chunntas an deasachaidh a thoirt air falbh)',
 'rev-deleted-user' => '(chaidh an t-ainm-cleachdaiche a thoirt air falbh)',
 'rev-deleted-event' => '(chaidh gnìomh an loga a thoirt air falbh)',
+'rev-deleted-user-contribs' => '[chaidh an t-ainm-cleachdaiche no an seòladh IP a thoirt air falbh - chan fhaic na com-pàirtichean an deasachadh]',
+'rev-deleted-text-permission' => "Chaidh mùthadh na duilleige seo '''a sguabadh às'''.
+Gheibh thu mion-fhiosrachadh air [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} ann an loga nan rudan a chaidh a sguabadh às].",
+'rev-deleted-text-unhide' => "Chaidh mùthadh na duilleige seo '''a sguabadh às'''.
+Gheibh thu mion-fhiosrachadh air [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} ann an loga nan rudan a chaidh a sguabadh às].
+'S urrainn dhut [$1 am mùthadh seo fhaicinn fhathast] ma tha thu airson leantainn air adhart.",
+'rev-suppressed-text-unhide' => "Chaidh mùthadh na duilleige seo '''a mhùchadh'''.
+Gheibh thu mion-fhiosrachadh air [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} ann an loga nan rudan a chaidh a mhùchadh].
+'S urrainn dhut [$1 am mùthadh seo fhaicinn fhathast] ma tha thu airson leantainn air adhart.",
+'rev-deleted-text-view' => "Chaidh mùthadh na duilleige seo '''a sguabadh às'''.
+'S urrainn dhut coimhead air, gheibh thu mion-fhiosrachadh air [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} ann an loga nan rudan a chaidh a sguabadh às].",
+'rev-suppressed-text-view' => "Chaidh mùthadh na duilleige seo '''a mhùchadh'''.
+'S urrainn dhut coimhead air, gheibh thu mion-fhiosrachadh air [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} ann an loga nan rudan a chaidh a mhùchadh].",
+'rev-deleted-no-diff' => "Chan fhaic thu an diff seo a chionn 's gun deach aon dhe na mùthaidhean '''a sguabadh às'''.
+Gheibh thu mion-fhiosrachadh air [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} ann an loga nan rudan a chaidh a sguabadh às].",
+'rev-suppressed-no-diff' => "Chan fhaic thu an diff seo a chionn 's gun deach aon dhe na mùthaidhean '''a sguabadh às'''.",
+'rev-deleted-unhide-diff' => "Chaidh mùthadh dhen diff seo '''a sguabadh às'''.
+Gheibh thu mion-fhiosrachadh air [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} ann an loga nan rudan a chaidh a sguabadh às].
+'S urrainn dhut [$1 coimhead air an diff seo fhathast] ma tha thu airson leantainn air adhart.",
+'rev-suppressed-unhide-diff' => "Chaidh mùthadh an diff seo '''a mhùchadh'''.
+Gheibh thu mion-fhiosrachadh air [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} ann an loga nan rudan a chaidh a mhùchadh].
+'S urrainn dhut [$1 coimhead air an diff seo fhathast] ma tha thu airson leantainn air adhart.",
+'rev-deleted-diff-view' => "Chaidh mùthadh an diff seo '''a sguabadh às'''.
+'S urrainn dhut coimhead air an diff seo, gheibh thu mion-fhiosrachadh air [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} ann an loga nan rudan a chaidh a sguabadh às].",
+'rev-suppressed-diff-view' => "Chaidh mùthadh an diff seo '''a mhùchadh'''.
+'S urrainn dhut coimhead air an diff seo, gheibh thu mion-fhiosrachadh air [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} ann an loga nan rudan a chaidh a mhùchadh].",
 'rev-delundel' => 'seall/falaich',
 'rev-showdeleted' => 'seall',
+'revisiondelete' => 'Sguab às/neo-dhèan sguabadh às mhùthaidhean',
+'revdelete-nooldid-title' => 'Tha am mùthadh seo mì-dhligheach',
+'revdelete-nooldid-text' => "Cha do shònraich thu mùthadh airson seo a dhèanamh, chan eil e ann no tha thu a' feuchainn ris am mùthadh làithreach a chur am falach.",
+'revdelete-nologtype-title' => 'Cha deach seòrsa an loga a shònrachadh',
+'revdelete-nologtype-text' => 'Cha do shònraich thu seòrsa an loga air an dèanar seo.',
+'revdelete-nologid-title' => 'Innteart mì-dhligheach an loga',
+'revdelete-nologid-text' => 'Cha do shònraich thu tachartas loga targaide gus seo a dhèanamh no chan eil an t-innteart seo ann.',
+'revdelete-no-file' => 'Chan eil am faidhle a shònraich thu ann.',
+'revdelete-show-file-confirm' => 'A bheil thu cinnteach gu bheil thu airson coimhead air mùthadh an fhaidhle "<nowiki>$1</nowiki>" a chaidh a sguabadh às $2 aig $3?',
+'revdelete-show-file-submit' => 'Tha',
 'revdelete-selected' => "'''{{PLURAL:$2|Lèirmheas|Lèirmheasan}} de [[:$1]] a thagh thu:'''",
 'logdelete-selected' => "'''{{PLURAL:$1|An tachartas loga|Na tachartasan loga}} a thagh thu:'''",
-'revdelete-hide-user' => 'Falaich ainm-cleachdaiche/seòladh IP an deasaiche',
+'revdelete-hide-text' => "Teacsa a' mhùthaidh",
+'revdelete-hide-image' => 'Falaich susbaint an fhaidhle',
+'revdelete-hide-name' => 'Falaich an gnìomh agus an targaid',
+'revdelete-hide-comment' => 'Gearr-chunntas an deasachaidh',
+'revdelete-hide-user' => 'Ainm-cleachdaiche/seòladh IP an deasaiche',
+'revdelete-hide-restricted' => 'Mùch dàta o rianairean agus càch',
 'revdelete-radio-same' => '(na atharraich)',
-'revdelete-radio-set' => 'Dèan seo',
-'revdelete-radio-unset' => 'Na dèan seo',
+'revdelete-radio-set' => 'Ri fhaicinn',
+'revdelete-radio-unset' => 'Falaichte',
+'revdelete-suppress' => 'Mùch dàta o rianairean agus càch',
+'revdelete-unsuppress' => 'Thoir air falbh na bacaidhean air mùthaidhean a chaidh aiseag',
 'revdelete-log' => 'Adhbhar:',
 'revdelete-submit' => 'Cuir air {{PLURAL:$1|an lèirmheas|na lèirmheasan}} a thagh thu',
+'revdelete-success' => "'''Chaidh so-fhaicsinneachd a' mhùthaidh ùrachadh.'''",
+'revdelete-failure' => "'''Cha b' urrainn dhuinn so-fhaicsinneachd a' mhùthaidh ùrachadh:'''
+$1",
+'logdelete-success' => "'''Chaidh faicsinneachd an loga a shuidheachadh.'''",
+'logdelete-failure' => "'''Cha b' urrainn dhuinn faicsinneachd an loga a shuidheachadh:'''
+$1",
 'revdel-restore' => 'mùth follaiseachd',
 'revdel-restore-deleted' => 'mùthaidhean a chaidh a sguabadh às',
 'revdel-restore-visible' => 'mùthaidhean faicsinneach',
 'pagehist' => 'Eachdraidh na duilleige',
+'deletedhist' => 'Eachdraidh a chaidh a sguabadh às',
 'revdelete-otherreason' => 'Adhbhar eile/a bharrachd:',
 'revdelete-reasonotherlist' => 'Adhbhar eile',
 'revdelete-edit-reasonlist' => 'Deasaich adhbharan an sguabaidh às',
@@ -891,9 +1040,12 @@ Feuch is [[Special:Search|lorg duilleagan ùra iomachaidh air an uici]]",
 
 # History merging
 'mergehistory-from' => 'An duilleag thùsail:',
+'mergehistory-autocomment' => 'Chaidh [[:$1]] a cho-aonadh dha [[:$2]]',
+'mergehistory-comment' => 'Chaidh [[:$1]] a cho-aonadh dha [[:$2]]: $3',
 'mergehistory-reason' => 'Adhbhar:',
 
 # Merge log
+'mergelog' => "Loga a' cho-aonaidh",
 'revertmerge' => 'Dì-aontaich',
 
 # Diffs
@@ -903,6 +1055,7 @@ Feuch is [[Special:Search|lorg duilleagan ùra iomachaidh air an uici]]",
 'compareselectedversions' => 'Dèan coimeas eadar na mùthaidhean a thagh thu',
 'showhideselectedversions' => 'Seall/Falaich na lèirmheasan a thagh thu',
 'editundo' => 'neo-dhèan',
+'diff-empty' => '(Gun diofar eatarra)',
 'diff-multi' => '({{PLURAL:$1|Aon lèirmheas eadar-mheadhanach||$1 lèirmheasan eadar-mheadhanach|$1 lèirmheas eadar-mheadhanach}} le {{PLURAL:$2|aon chleachdaiche|$2 chleachdaiche|$2 cleachdaichean|$2 cleachdaiche}} gun sealltainn)',
 'diff-multi-manyusers' => '({{PLURAL:$1|Aon lèirmheas eadar-mheadhanach||$1 lèirmheasan eadar-mheadhanach|$1 lèirmheas eadar-mheadhanach}} le {{PLURAL:$2|aon chleachdaiche|$2 chleachdaiche|$2 cleachdaichean|$2 cleachdaiche}} gun sealltainn)',
 
@@ -988,6 +1141,9 @@ Faodaidh gum bi inneacsan susbaint {{SITENAME}} tuilleadh 's sean ge-tà.",
 'prefs-watchlist' => 'An clàr-faire',
 'prefs-watchlist-days' => "Co mheud latha a sheallar air a' chlàr-fhaire:",
 'prefs-watchlist-days-max' => "{{PLURAL:$1|latha|latha|làithean|latha}} air a' char as motha",
+'prefs-watchlist-edits-max' => 'Àireamh as motha: 1000',
+'prefs-watchlist-token' => "Tòcan a' chlàir-fhaire:",
+'prefs-misc' => 'Measgachadh',
 'prefs-resetpass' => 'Atharraich am facal-faire',
 'prefs-changeemail' => 'Atharraich am post-d',
 'prefs-setemail' => 'Suidhich seòladh puist-d',
@@ -995,16 +1151,20 @@ Faodaidh gum bi inneacsan susbaint {{SITENAME}} tuilleadh 's sean ge-tà.",
 'prefs-rendering' => 'Coltas',
 'saveprefs' => 'Sàbhail',
 'resetprefs' => 'Falamhaich atharrachaidhean nach deach a shàbhaladh fhathast',
-'restoreprefs' => 'Aisig na roghainnean bunaiteach uile',
+'restoreprefs' => 'Aisig na roghainnean bunaiteach uile (anns gach earrann)',
 'prefs-editing' => "A' deasachadh",
 'rows' => 'Sreathan',
 'columns' => 'Colbhan',
 'searchresultshead' => 'Lorg',
 'stub-threshold-disabled' => 'À comas',
+'recentchangesdays-max' => "{{PLURAL:$1|latha|latha|làithean|latha}} air a' char as motha",
+'recentchangescount' => 'Uiread a dheasachaidhean a thèid a shealltainn a ghnàth:',
 'savedprefs' => 'Tha na roghainnean agad air an sàbhaladh.',
 'timezonelegend' => 'Roinn-tìde:',
 'localtime' => 'An t-àm ionadail:',
+'timezoneuseserverdefault' => 'Cleachd bun-roghainn na h-Uicipeid ($1)',
 'servertime' => 'Àm an fhrithealaichte:',
+'guesstimezone' => 'Lìon on bhrabhsair',
 'timezoneregion-africa' => 'Afraga',
 'timezoneregion-america' => 'Aimeireaga',
 'timezoneregion-antarctica' => 'An Antartaig',
@@ -1015,6 +1175,8 @@ Faodaidh gum bi inneacsan susbaint {{SITENAME}} tuilleadh 's sean ge-tà.",
 'timezoneregion-europe' => 'An Roinn-Eòrpa',
 'timezoneregion-indian' => 'An Cuan Innseanach',
 'timezoneregion-pacific' => 'An Cuan Sèimh',
+'allowemail' => 'Ceadaich post-d o chleachdaichean eile',
+'prefs-searchoptions' => 'Lorg',
 'prefs-namespaces' => 'Namespaces',
 'default' => 'an roghainn bhunaiteach',
 'prefs-files' => 'Faidhlichean',
@@ -1038,10 +1200,10 @@ Faodaidh gum bi inneacsan susbaint {{SITENAME}} tuilleadh 's sean ge-tà.",
 Thoir sùil air na tagaichean HTML.',
 'badsiglength' => 'Tha an t-earr-sgrìobhadh agad ro fhada.
 Chan fhaod e a bhith nas fhaide na $1 {{PLURAL:$1|charactar|charactar|caractaran|caractar}}.',
-'yourgender' => 'Gnè:',
-'gender-unknown' => 'Gun innse',
-'gender-male' => 'Fireann',
-'gender-female' => 'Boireann',
+'yourgender' => "Dè a' ghnè a tha annad:",
+'gender-unknown' => "B' fhearr leam gun a bhith 'ga leigeil ris",
+'gender-male' => 'Deasaichidh e duilleagan na h-Uicipeid',
+'gender-female' => 'Deasaichidh i duilleagan na h-Uicipeid',
 'email' => 'Post-d:',
 'prefs-help-email' => "Chan leig thu leas post-dealain a chur ann ach bidh feum air ma dhìochuimhnicheas tu am facal-faire agad 's ma dh'iarras tu fear ùr.",
 'prefs-help-email-others' => "'S urrainn dhut leigeil le daoine eile post-dealain a chur thugad tro cheangal air an duilleag agad.
@@ -1052,7 +1214,9 @@ Chan fhaicear an seòladh fhèin nuair a chuireas cuideigin post-dealain thugad.
 'prefs-signature' => 'Earr-sgrìobhadh',
 'prefs-dateformat' => "Fòrmat a' chinn-là",
 'prefs-timeoffset' => 'Diofar ama',
-'prefs-advancedediting' => 'Roghainnean adhartach',
+'prefs-advancedediting' => 'Roghainnean coitcheann',
+'prefs-editor' => 'Deasaiche',
+'prefs-preview' => 'Ro-shealladh',
 'prefs-advancedrc' => 'Roghainnean adhartach',
 'prefs-advancedrendering' => 'Roghainnean adhartach',
 'prefs-advancedsearchoptions' => 'Roghainnean adhartach',
@@ -1060,6 +1224,7 @@ Chan fhaicear an seòladh fhèin nuair a chuireas cuideigin post-dealain thugad.
 'prefs-displayrc' => 'Roghainnean taisbeanaidh',
 'prefs-displaysearchoptions' => 'Roghainnean taisbeanaidh',
 'prefs-displaywatchlist' => 'Roghainnean taisbeanaidh',
+'prefs-tokenwatchlist' => 'Tòcan',
 'prefs-diffs' => 'Diffs',
 
 # User preference: email validation using jQuery
@@ -1151,7 +1316,7 @@ Chan fhaicear an seòladh fhèin nuair a chuireas cuideigin post-dealain thugad.
 'minoreditletter' => 'b',
 'newpageletter' => 'Ù',
 'boteditletter' => 'bt',
-'rc-enhanced-expand' => 'Seall am mion-fhiosrachadh (feumaidh seo JavaScript)',
+'rc-enhanced-expand' => 'Seall am mion-fhiosrachadh',
 'rc-enhanced-hide' => 'Cuir am mion-fhiosrachadh am falach',
 
 # Recent changes linked
@@ -1335,9 +1500,11 @@ Seall air $2 airson clàr de dhuilleagan a chaidh a sguabadh às o chionn ghoiri
 'deleteotherreason' => 'Adhbhar eile/a bharrachd:',
 'deletereasonotherlist' => 'Adhbhar eile',
 'deletereason-dropdown' => "*Adhbharan cumanta airson sguabadh às
-** Dh'iarr an t-ùghdar e
+** Spama
+** Milleadh
 ** Tha e a' briseadh na còrach-lethbhreac
-** Milleadh",
+** Dh'iarr an t-ùghdar e
+** Ath-threòrachadh briste",
 'delete-edit-reasonlist' => 'Deasaich adhbharan sguabadh às',
 
 # Rollback
@@ -1395,7 +1562,7 @@ Seo roghainnean làithreach na duilleige '''$1''':",
 'contributions' => "Mùthaidhean a' {{GENDER:$1|chleachdaiche}}",
 'contributions-title' => 'Mùthaidhean a rinn $1',
 'mycontris' => 'Mùthaidhean',
-'contribsub2' => 'Do $1 ($2)',
+'contribsub2' => 'Airson {{GENDER:$3|$1}} ($2)',
 'uctop' => '(làithreach)',
 'month' => 'On mhìos (agus na bu tràithe):',
 'year' => 'On bhliadhna (agus na bu tràithe):',
@@ -1450,7 +1617,6 @@ Seo roghainnean làithreach na duilleige '''$1''':",
 'block-log-flags-nocreate' => 'cruthachadh de chunntasan ùra à comas',
 'ipb_expiry_invalid' => 'Tha an t-àm-crìochnachaidh mì-dhligheach.',
 'ip_range_invalid' => 'Raon IP neo-iomchaidh.',
-'proxyblocksuccess' => 'Dèanta.',
 
 # Developer tools
 'lockdb' => 'Glais an stòr-dàta',
index 3683045..40c829b 100644 (file)
@@ -471,7 +471,7 @@ $messages = array(
 'articlepage' => 'Ver a páxina de contido',
 'talk' => 'Conversa',
 'views' => 'Vistas',
-'toolbox' => 'Caixa de ferramentas',
+'toolbox' => 'Ferramentas',
 'userpage' => 'Ver a páxina {{GENDER:{{BASEPAGENAME}}|do usuario|da usuaria}}',
 'projectpage' => 'Ver a páxina do proxecto',
 'imagepage' => 'Ver a páxina do ficheiro',
@@ -784,7 +784,7 @@ continuar a utilizar o seu contrasinal vello.',
 'passwordsent' => 'Enviouse un contrasinal novo ao enderezo de correo electrónico rexistrado de "$1".
 Por favor, acceda ao sistema de novo tras recibilo.',
 'blocked-mailpassword' => 'O seu enderezo IP está bloqueado e ten restrinxida a edición de artigos. Tampouco se lle permite usar a función de recuperación do contrasinal para evitar abusos do sistema.',
-'eauthentsent' => 'Envióuselle un correo electrónico de confirmación ao enderezo mencionado.
+'eauthentsent' => 'Envióuselle un correo electrónico de confirmación ao enderezo especificado.
 Antes de que se lle envíe calquera outro correo a esta conta terá que seguir as instrucións que aparecen nesa mensaxe para confirmar que a conta é realmente súa.',
 'throttled-mailpassword' => 'Enviouse un correo electrónico de restablecemento do contrasinal {{PLURAL:$1|na última hora|nas últimas $1 horas}}.
 Para evitar o abuso do sistema só se enviará unha mensaxe de restablecemento cada {{PLURAL:$1|hora|$1 horas}}.',
@@ -2810,12 +2810,9 @@ Olle a [[Special:BlockList|lista de bloqueos]] para comprobar os bloqueos vixent
 'ipb_blocked_as_range' => 'Erro: O enderezo IP $1 non está bloqueado directamente e non se pode desbloquear. Porén, está bloqueado por estar no rango $2, que si se pode desbloquear.',
 'ip_range_invalid' => 'Rango IP non válido.',
 'ip_range_toolarge' => 'Non están permitidos os rangos de bloqueo maiores que /$1.',
-'blockme' => 'Bloquearme',
 'proxyblocker' => 'Bloqueador de proxy',
-'proxyblocker-disabled' => 'Esta función está desactivada.',
 'proxyblockreason' => 'O seu enderezo IP foi bloqueado porque é un proxy aberto.
 Por favor, contacte co seu fornecedor de acceso á Internet ou co seu soporte técnico e informe deste grave problema de seguridade.',
-'proxyblocksuccess' => 'Feito.',
 'sorbsreason' => 'O seu enderezo IP está rexistrado como un proxy aberto na lista DNSBL usada por {{SITENAME}}.',
 'sorbs_create_account_reason' => 'O seu enderezo IP está rexistrado como un proxy aberto na lista DNSBL usada por {{SITENAME}}.
 Polo tanto, non pode crear unha conta',
@@ -3179,6 +3176,8 @@ Isto, probabelmente, se debe a unha ligazón cara a un sitio externo que está n
 'spam_reverting' => 'Revertida á última edición sen ligazóns a "$1"',
 'spam_blanking' => 'Limpáronse todas as revisións con ligazóns a "$1"',
 'spam_deleting' => 'Borráronse todas as revisións con ligazóns a "$1"',
+'simpleantispam-label' => "Comprobación antispam.
+'''NON''' encha isto!",
 
 # Info page
 'pageinfo-title' => 'Información sobre "$1"',
@@ -3959,7 +3958,7 @@ Debería recibir [{{SERVER}}{{SCRIPTPATH}}/COPYING unha copia da licenza públic
 # Special:Redirect
 'redirect' => 'Redirixir por nome de ficheiro, ID de usuario ou ID de revisión',
 'redirect-legend' => 'Redirixir a un ficheiro ou unha páxina',
-'redirect-summary' => 'Esta páxina especial redirixe cara a un ficheiro (dado o nome), unha páxina (dado o ID dunha revisión) ou unha páxina de usuario (dado o ID dun usuario).',
+'redirect-summary' => 'Esta páxina especial redirixe cara a un ficheiro (dado o nome), unha páxina (dado o ID dunha revisión) ou unha páxina de usuario (dado o ID dun usuario). Utilización: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/revision/328429]], or [[{{#Special:Redirect}}/user/101]].',
 'redirect-submit' => 'Continuar',
 'redirect-lookup' => 'Procurar:',
 'redirect-value' => 'Valor:',
index 876608d..f1161fd 100644 (file)
@@ -1811,10 +1811,7 @@ $1',
 'ipb_cant_unblock' => 'Σφάλμα: Ἡ φραγὴ τῆς ID $1 οὐχ εὑρέθη.
 Εἰκότως ἀποπεφραγμένη ἤδη ἐστίν.',
 'ip_range_invalid' => 'Ἄκυρον IP-εὖρος.',
-'blockme' => 'Φράξον με',
 'proxyblocker' => 'Ἐργαλεῖον φραγῆς διακομιστῶν',
-'proxyblocker-disabled' => 'Ἥδε ἡ ἐνέργεια κατεσταλμένη εστίν.',
-'proxyblocksuccess' => 'Γενομένη.',
 'cant-block-while-blocked' => 'Οὐκ ἔξεστί σοι φράττειν ἑτέρους χρωμένους ἐν ὅσῳ πεφραγμένος εἶ.',
 
 # Developer tools
index 042bc74..6716266 100644 (file)
@@ -335,7 +335,7 @@ $messages = array(
 'articlepage' => 'Syte',
 'talk' => 'Diskussion',
 'views' => 'Wievylmol agluegt',
-'toolbox' => 'Wärkzügkäschtli',
+'toolbox' => 'Wärchzyyg',
 'userpage' => 'Benutzersyte',
 'projectpage' => 'Projektsyte azeige',
 'imagepage' => 'Dateisyte',
@@ -937,8 +937,8 @@ Erklärig: (aktuell) = Underschid zu jetz,
 (vorane) = Underschid zur alte Version, <strong>K</strong> = chlyni Änderig',
 'history-fieldset-title' => 'Suech in dr Versionsgschicht',
 'history-show-deleted' => 'nume gleschti Versione',
-'histfirst' => 'Eltischti',
-'histlast' => 'ischti',
+'histfirst' => 'eltischti',
+'histlast' => 'neischti',
 'historysize' => '({{PLURAL:$1|1 Byte|$1 Bytes}})',
 'historyempty' => '(läär)',
 
@@ -1452,7 +1452,7 @@ Des cha nimmi ruckgängig gmacht wäre.',
 'rc_categories_any' => 'Alli',
 'rc-change-size-new' => '$1 {{PLURAL:$1|Byte|Byte}} no dr Änderig',
 'newsectionsummary' => 'Neje Abschnitt /* $1 */',
-'rc-enhanced-expand' => 'Detail aazeige (brucht JavaScript)',
+'rc-enhanced-expand' => 'Detail aazeige',
 'rc-enhanced-hide' => 'Detail verstecke',
 'rc-old-title' => 'urspringlig aaglait as „$1“',
 
@@ -2309,7 +2309,7 @@ $1',
 'contributions' => '{{GENDER:$1|Benutzer-Byträg}}',
 'contributions-title' => 'Benutzerbyytreg vu „$1“',
 'mycontris' => 'Myyni Byyträg',
-'contribsub2' => 'Für $1 ($2)',
+'contribsub2' => 'Vu {{GENDER:$3|$1}} ($2)',
 'nocontribs' => 'S sin keini Benutzerbyytreg mit däne Kriterie gfunde wore.',
 'uctop' => '(aktuell)',
 'month' => 'u Monet:',
@@ -2462,11 +2462,8 @@ Go d Sperri ufhebe lueg d [[Special:BlockList|Lisch vu allene aktive Sperrine]].
 'ipb_blocked_as_range' => 'Fähler: D IP-Adräss $1 isch as Teil vu dr Beryychssperri $2 indirekt gsperrt. S isch nit megli, nume $1 z entsperre.',
 'ip_range_invalid' => 'Uugiltige IP-Adrässberyych.',
 'ip_range_toolarge' => 'Adrässberyych, wu greßer sin wie /$1, sin nit erlaubt.',
-'blockme' => 'Sperr mi',
 'proxyblocker' => 'Proxy blocker',
-'proxyblocker-disabled' => 'Die Funktion isch deaktiviert.',
 'proxyblockreason' => 'Dyni IP-Adrässe isch gsperrt wore, wel si ne ufige Proxy isch. Bitte kontaktier Dyyn Internet-Provider oder Dyni Systemadministratore un informier si iber des Sicherheitsproblem.',
-'proxyblocksuccess' => 'Fertig.',
 'sorbsreason' => 'D IP-Adräss isch in dr DNSBL vu {{SITENAME}} as uffige PROXY glischtet.',
 'sorbs_create_account_reason' => 'D IP-Adräss isch in dr DNSBL vu {{SITENAME}} as uffige PROXY glischtet. S Aalege vu neije Benutzer isch nit megli.',
 'cant-block-while-blocked' => 'Du derfsch kei anderi Benutzer sperre, derwylscht Du sälber gsperrt bisch.',
@@ -2781,6 +2778,7 @@ Die uf em lokale Rächner spychere un derno do uffelade.',
 'spam_reverting' => 'Letschti Version ohni Links zue $1 widerhärgstellt.',
 'spam_blanking' => 'In allene Versione het s Links zue $1 gha, sufer gmacht.',
 'spam_deleting' => 'Alli Versione mit eme Link zue $1 sin glescht woret.',
+'simpleantispam-label' => "Spamschutz-Priefig. Do '''nyt''' yytrage!",
 
 # Info page
 'pageinfo-title' => 'Informatione zue „$1“',
index 100e2ad..864ffa5 100644 (file)
@@ -153,7 +153,7 @@ $linkTrail = "/^([\x{0A80}-\x{0AFF}]+)(.*)$/sDu";
 $messages = array(
 # User preference toggles
 'tog-underline' => 'કડીઓની નીચે લીટી (અંડરલાઇન):',
-'tog-justify' => 'ફàª\95રà«\8b લાઇનસર કરો',
+'tog-justify' => 'ફàª\95રાàª\93 લાઇનસર કરો',
 'tog-hideminor' => 'હાલમાં થયેલા ફેરફારમાં નાના ફેરફારો છુપાવો',
 'tog-hidepatrolled' => 'હાલના સલામતી માટે કરવામાં આવેલાં થયેલા ફેરફારો છુપાવો.',
 'tog-newpageshidepatrolled' => 'નવાં પાનાંની યાદીમાંથી દેખરેખ હેઠળનાં પાનાં છુપાવો',
@@ -175,9 +175,9 @@ $messages = array(
 'tog-previewonfirst' => 'પ્રથમ ફેરફાર વખતે પૂર્વાલોકન બતાવો',
 'tog-nocache' => 'બ્રાઉઝરનું પેજ કેશિંગ અસક્રિય કરો',
 'tog-enotifwatchlistpages' => 'મારી ધ્યાનસૂચિમાંનું પાનુ અને ફાઇલમાં ફેરફાર થાય ત્યારે મને ઇ-મેલ મોકલો',
-'tog-enotifusertalkpages' => 'મારી ચર્ચાનાં પાનામાં ફેરફાર થાય ત્યારે મને ઇ-મેલ મોકલો',
-'tog-enotifminoredits' => 'પાનાં અને ફાઇલ્સમાં નાનાં ફેરફાર થાય તો પણ મને ઇ-મેલ મોકલો',
-'tog-enotifrevealaddr' => 'નà«\8bàª\9fà«\80ફà«\80àª\95à«\87શનના àª\87મà«\87લમાàª\82 àª®àª¾àª°à«\82 àª\87મà«\87લ àª\8fડà«\8dરà«\87સ બતાવો',
+'tog-enotifusertalkpages' => 'મારી ચર્ચાનાં પાનામાં ફેરફાર થાય ત્યારે મને ઇમેલ મોકલો',
+'tog-enotifminoredits' => 'પાનાં અને ફાઇલ્સમાં નાનાં ફેરફાર થાય તો પણ મને ઇમેલ મોકલો',
+'tog-enotifrevealaddr' => 'નà«\8bàª\9fà«\80ફà«\80àª\95à«\87શનના àª\87મà«\87લમાàª\82 àª®àª¾àª°à«\82 àª\87મà«\87લ àª¸àª°àª¨àª¾àª®à«\81àª\82 બતાવો',
 'tog-shownumberswatching' => 'ધ્યાન રાખતા સભ્યોની સંખ્યા બતાવો',
 'tog-oldsig' => 'હાલના હસ્તાક્ષર:',
 'tog-fancysig' => 'હસ્તાક્ષરનો વિકિલખાણ તરીકે ઉપયોગ કરો (સ્વચાલિત કડી વગર)',
@@ -187,10 +187,10 @@ $messages = array(
 'tog-watchlisthidebots' => 'ધ્યાનસુચીમાં બોટ દ્વારા થયેલા ફેરફાર સંતાડો.',
 'tog-watchlisthideminor' => "'મારી ધ્યાનસુચી'માં નાનાં ફેરફારો છુપાવો",
 'tog-watchlisthideliu' => 'લોગ થયેલા સભ્ય દ્વારા કરવામાં આવેલ ફેરફાર ધ્યાનસુચીમાં છુપાવો.',
-'tog-watchlisthideanons' => 'અજાણ્યાસભ્ય દ્વારા થયેલ ફેરફાર મારી ધ્યાનસુચીમાં છુપાવો.',
-'tog-watchlisthidepatrolled' => 'સુરક્ષા કાજે કરવામાં આવેલ ફેરફાર મારી ધ્યાનસુચીમાં છુપાવો.',
-'tog-ccmeonemails' => 'મે અન્યોને મોકલેલા ઇ-મà«\87àª\87લનà«\80 àª¨àª\95લ àª®àª¨à«\87 àª®à«\8bàª\95લà«\8b',
-'tog-diffonly' => 'તફાવતની નીચે લેખ ન બતાવશો.',
+'tog-watchlisthideanons' => 'અજાણ્યા સભ્ય દ્વારા થયેલ ફેરફાર મારી ધ્યાનસુચીમાં છુપાવો',
+'tog-watchlisthidepatrolled' => 'સુરક્ષા કાજે કરવામાં આવેલ ફેરફાર મારી ધ્યાનસુચીમાં છુપાવો',
+'tog-ccmeonemails' => 'મે અન્યોને મોકલેલા ઇમà«\87લનà«\80 àª¨àª\95લ àª®àª¨à«\87 àª®à«\8bàª\95લà«\8b',
+'tog-diffonly' => 'તફાવતની નીચે લેખ ન બતાવશો',
 'tog-showhiddencats' => 'છુપી શ્રેણીઓ દર્શાવો',
 'tog-noconvertlink' => 'Disable link title conversion',
 'tog-norollbackdiff' => 'રોલબેક કર્યા પછીના તફાવતો છુપાવો',
@@ -291,8 +291,6 @@ $messages = array(
 'noindex-category' => 'અનુક્રમણિકા નહી બનાવેલા પાનાં',
 'broken-file-category' => 'ફાઇલોની ત્રૂટક કડીઓવાળાં પાનાં',
 
-'linkprefix' => '/^(.*?)((?:[a-zA-Z\\x80-\\xff]|ક્|ખ્|ગ્|ઘ્|ચ્|છ્|જ્|ઝ્|ટ્|ઠ્|ડ્|ઢ્|ણ્|ત્|થ્|દ્|ધ્|ન્|પ્|ફ્|બ્|ભ્|મ્|ય્|ર્|લ્|વ્|સ્|શ્|ષ્|હ્|ળ્|ક્ષ્|જ્ઞ્|અ|આ|ઇ|ઈ|ઉ|ઊ|એ|ઐ|ઓ|ઔ|અં|અઃ|અઁ|ઍ|ઑ|ઋ|ઁ|઼|।|્|ા|િ|ી|ુ|ૂ|ે|ૈ|ો|ૌ|ં|ઃ|ૅ|ૉ|ૃ)+)$/sD',
-
 'about' => 'વિષે',
 'article' => 'લેખનું પાનું',
 'newwindow' => '(નવા પાનામાં ખુલશે)',
@@ -341,7 +339,7 @@ $messages = array(
 'search' => 'શોધો',
 'searchbutton' => 'શોધો',
 'go' => 'જાઓ',
-'searcharticle' => 'àª\9cાવ',
+'searcharticle' => 'àª\9cાàª\93',
 'history' => 'પાનાનો ઇતિહાસ',
 'history_short' => 'ઇતિહાસ',
 'updatedmarker' => 'મારી ગઇ મુલાકાત પછીના બદલાવ',
@@ -352,7 +350,7 @@ $messages = array(
 'edit' => 'ફેરફાર કરો',
 'create' => 'બનાવો',
 'editthispage' => 'આ પાનામાં ફેરફાર કરો',
-'create-this-page' => 'આ પાનું બનાવો.',
+'create-this-page' => 'આ પાનું બનાવો',
 'delete' => 'રદ કરો',
 'deletethispage' => 'આ પાનું હટાવો',
 'undeletethispage' => 'આ પાનું પુનર્જીવીત કરો',
@@ -360,7 +358,7 @@ $messages = array(
 'viewdeleted_short' => '{{PLURAL:$1|ભૂંસી નાખેલો એક|ભૂંસી નાખેલા $1}} ફેરફાર જુઓ',
 'protect' => 'સુરક્ષિત કરો',
 'protect_change' => 'બદલો',
-'protectthispage' => 'આ પાનું સુરક્ષિત કરો.',
+'protectthispage' => 'આ પાનું સુરક્ષિત કરો',
 'unprotect' => 'સુરક્ષા બદલો',
 'unprotectthispage' => 'આ પાનાનું સુરક્ષા  બદલો',
 'newpage' => 'નવું પાનું',
@@ -372,7 +370,7 @@ $messages = array(
 'articlepage' => 'લેખનું પાનું જુઓ',
 'talk' => 'ચર્ચા',
 'views' => 'દેખાવ',
-'toolbox' => 'સાધન પેટી',
+'toolbox' => 'સાધન',
 'userpage' => 'સભ્યનું પાનું જુઓ',
 'projectpage' => 'પ્રકલ્પનું પાનું જુઓ',
 'imagepage' => 'ફાઇલનું પાનું જુઓ',
@@ -387,7 +385,7 @@ $messages = array(
 'lastmodifiedat' => 'આ પાનામાં છેલ્લો ફેરફાર $1ના રોજ $2 વાગ્યે થયો.',
 'viewcount' => 'આ પાનું {{PLURAL:$1|એક|$1}} વખત જોવામાં આવ્યું છે.',
 'protectedpage' => 'સંરક્ષિત પાનું',
-'jumpto' => 'સà«\80ધા àª\86ના àªªàª° àª\9cાવ:',
+'jumpto' => 'આના પર જાવ:',
 'jumptonavigation' => 'ભ્રમણ',
 'jumptosearch' => 'શોધો',
 'view-pool-error' => 'માફ કરશો, આ સમયે સર્વર અતિબોજા હેઠળ છે.
@@ -398,7 +396,7 @@ $messages = array(
 
 $1',
 'pool-timeout' => 'સમય સમાપ્ત -  સ્થગિતતા પ્રતિક્ષીત',
-'pool-queuefull' => '(Pool) કતાર પૂરી ભરેલી',
+'pool-queuefull' => '(Pool) કતાર પૂરી ભરેલી છે',
 'pool-errorunknown' => 'અજ્ઞાત ત્રુટિ',
 
 # All link text and link target definitions of links into project namespace that get used by other message strings, with the exception of user group pages (see grouppage).
@@ -432,18 +430,18 @@ $1',
 'ok' => 'મંજૂર',
 'retrievedfrom' => '"$1"થી લીધેલું',
 'youhavenewmessages' => 'તમારા માટે $1 ($2).',
-'newmessageslink' => 'નવીન સંદેશ',
+'newmessageslink' => 'નવીન સંદેશાઓ',
 'newmessagesdifflink' => 'છેલ્લો ફેરફાર',
 'youhavenewmessagesfromusers' => 'આપને માટે {{PLURAL:$3|અન્ય સભ્ય|$3 અન્ય સભ્યો}} તરફથી $1 છે. ($2).',
-'youhavenewmessagesmanyusers' => 'આપને માટે $1 છે. ($2)',
+'youhavenewmessagesmanyusers' => 'આપને માટે ઘણાં સભ્યો તરફથી $1 છે ($2).',
 'newmessageslinkplural' => '{{PLURAL:$1|નવો સંદેશો|નવા સંદેશા}}',
 'newmessagesdifflinkplural' => 'છેલ્લા {{PLURAL:$1|ફેરફાર|ફેરફારો}}',
-'youhavenewmessagesmulti' => '$1 ઉપર તમારા માટે નવો સંદેશ છે.',
+'youhavenewmessagesmulti' => 'તમારા માટે $1 ઉપર નવા સંદેશાઓ છે',
 'editsection' => 'ફેરફાર કરો',
 'editold' => 'ફેરફાર કરો',
 'viewsourceold' => 'સ્રોત જુઓ',
 'editlink' => 'ફેરફાર',
-'viewsourcelink' => 'સ્રોત જુઓ.',
+'viewsourcelink' => 'સ્રોત જુઓ',
 'editsectionhint' => 'ફેરફાર કરો - પરિચ્છેદ: $1',
 'toc' => 'અનુક્રમણિકા',
 'showtoc' => 'બતાવો',
@@ -456,10 +454,10 @@ $1',
 'feedlinks' => 'ફીડ:',
 'feed-invalid' => 'અયોગ્ય સબસ્ક્રીપ્સન ફીડ પ્રકાર.',
 'feed-unavailable' => ' સંલગ્ન માહિતીની અપૂરાતિ મોજૂદ નથી',
-'site-rss-feed' => '$1 RSS Feed',
-'site-atom-feed' => '$1 Atom Feed',
-'page-rss-feed' => '"$1" RSS Feed',
-'page-atom-feed' => '"$1" àª\8fàª\9fà«\8bમ àª«à«\80ડ',
+'site-rss-feed' => '$1 RSS ફીડ',
+'site-atom-feed' => '$1 એટમ ફીડ',
+'page-rss-feed' => '"$1" RSS ફીડ',
+'page-atom-feed' => '"$1" એટમ ફીડ',
 'red-link-title' => '$1 (પાનું અસ્તિત્વમાં નથી)',
 'sort-descending' => 'ઉતરતા ક્રમમાં ગોઠવો',
 'sort-ascending' => 'ચડતા ક્રમમાં ગોઠવો',
@@ -477,7 +475,7 @@ $1',
 'nstab-category' => 'શ્રેણી',
 
 # Main script and global functions
-'nosuchaction' => 'આવી કોઇ ક્રિયા નથી.',
+'nosuchaction' => 'આવી કોઇ ક્રિયા નથી',
 'nosuchactiontext' => 'આ URL દ્વારા દર્શાવેલી ક્રિયા અયોગ્ય છે.
 તમે કદાચ ખોટો URL છાપ્યો હશે અથવા ખોટી કડીથી અહીં આવ્યા હશો.
 તમે સોફ્ટવેરની આ ખામી {{SITENAME}} પર દર્શાવી શકો છો.',
@@ -495,7 +493,7 @@ $1',
 'databaseerror-query' => 'પ્રશ્ન: $1',
 'databaseerror-function' => 'વિધેય: $1',
 'databaseerror-error' => 'ક્ષતિ: $1',
-'laggedslavemode' => 'ચેતવણી: પાનું તાજેતરના ફેરફાર ધરાવતું નથી.',
+'laggedslavemode' => '"ચેતવણી:" પાનું તાજેતરના ફેરફાર ધરાવતું નથી.',
 'readonly' => 'ડેટાબેઝ સ્થગિત',
 'enterlockreason' => 'સ્થગિતતા ક્યારે દુર કરાશે તેના અંદાજ શાથે,સ્થગિત કરવાનું કારણ આપો',
 'readonlytext' => 'નવી નોંધો અને ફેરફારો માટે ડેટાબેઝ હાલમાં સ્થગિત કરાયેલ છે,કદાચ નિયમિત ડેટાબેઝ સારસંભાળ માટે,તે પછી આ ફરી સામાન્ય થશે.
@@ -517,7 +515,7 @@ $1',
 'filecopyerror' => '"$1" થી "$2"માં નકલ નિષ્ફળ.',
 'filerenameerror' => '"$1" નું નામ બદલીને "$2" કરવામાં નિષ્ફળ.',
 'filedeleteerror' => '"$1" ફાઇલ હટાવી ન શકાઇ.',
-'directorycreateerror' => 'ડà«\80રેક્ટરી "$1" ન બનાવી શકાઇ.',
+'directorycreateerror' => 'ડિરેક્ટરી "$1" ન બનાવી શકાઇ.',
 'filenotfound' => 'ફાઇલ "$1" ન મળી.',
 'fileexistserror' => 'ફાઇલ "$1"માં ન લખી શકાયું : ફાઇલ અસ્તિત્વ ધરાવે છે.',
 'unexpected' => 'અણધારી કિંમત: "$1"="$2".',
@@ -528,7 +526,7 @@ $1',
 'cannotdelete-title' => '"$1" પાનું કાઢી શકતા નથી',
 'delete-hook-aborted' => 'દૂર કરવાનું હૂક વડે રોકી રાખવામાં આવ્યું.
 તે કોઇ કારણ આપતું નથી.',
-'badtitle' => 'àª\96રાબ àª¨àª¾àª®',
+'badtitle' => 'àª\96રાબ àª¶àª¿àª°à«\8dષàª\95',
 'badtitletext' => 'આપનું ઈચ્છિત શીર્ષક અમાન્ય છે, ખાલી છે, અથવાતો અયોગ્ય રીતે આંતર-ભાષિય કે આંતર-વિકિ સાથે જોડાયેલું શીર્ષક છે.
 શક્ય છે કે તેમાં એક કે વધુ એવા અક્ષર કે ચિહ્નો છે કે જે પાનાનાં શીર્ષક માટે અવૈધ છે.',
 'perfcached' => 'નીચે દર્શાવેલી માહિતી જૂના સંગ્રહમાંથી લીધેલી છે અને શક્ય છે કે તે હાલની પરિસ્થિતિમાં સચોટ ના હોય. વધુમાં વધુ {{PLURAL:$1|એક પરિણામ|$1 પરિણામો}} આ સંગ્રહમાં ઉપલબ્ધ છે.',
@@ -582,12 +580,12 @@ $2',
 'yourname' => 'સભ્ય નામ:',
 'userlogin-yourname' => 'સભ્ય નામ',
 'userlogin-yourname-ph' => 'તમારૂં સભ્ય નામ દાખલ કરો',
-'createacct-another-username-ph' => 'તમારà«\82àª\82 àª¸àª­à«\8dયનામ àª¦àª¾àª\96લ àª\95રà«\8b',
+'createacct-another-username-ph' => 'સભ્યનામ દાખલ કરો',
 'yourpassword' => 'ગુપ્ત સંજ્ઞા:',
 'userlogin-yourpassword' => 'ગુપ્ત સંજ્ઞા',
-'userlogin-yourpassword-ph' => 'àª\97à«\81પà«\8dત àª¸àª\82àª\9cà«\8dàª\9eા લખો',
+'userlogin-yourpassword-ph' => 'તમારà«\80 àª\97à«\81પà«\8dત àª¸àª\82àª\9cà«\8dàª\9eા (પાસવરà«\8dડ) લખો',
 'createacct-yourpassword-ph' => 'પાસવર્ડ દાખલ કરો',
-'yourpasswordagain' => 'ગુપ્ત સંજ્ઞા (પાસવર્ડ) ફરી લખો',
+'yourpasswordagain' => 'ગુપ્ત સંજ્ઞા (પાસવર્ડ) ફરી લખો:',
 'createacct-yourpasswordagain' => 'પાસવર્ડની ખાતરી કરો',
 'createacct-yourpasswordagain-ph' => 'પાસવર્ડ ફરીથી દાખલ કરો',
 'remembermypassword' => 'આ કોમ્યૂટર પર મારી લૉગ ઇન વિગતો ધ્યાનમાં રાખો (વધુમાં વધુ $1 {{PLURAL:$1|દિવસ|દિવસ}} માટે)',
@@ -604,7 +602,7 @@ $2',
 'logout' => 'બહાર નીકળો',
 'userlogout' => 'બહાર નીકળો/લૉગ આઉટ',
 'notloggedin' => 'પ્રવેશ કરેલ નથી',
-'userlogin-noaccount' => 'શું તમારૂં ખાતું નથી ?',
+'userlogin-noaccount' => 'શું તમારૂં ખાતું નથી?',
 'userlogin-joinproject' => '{{SITENAME}} સાથે જોડાવ',
 'nologin' => "શું તમારૂં ખાતું નથી? તો નવું '''$1'''.",
 'nologinlink' => 'ખાતું ખોલો',
@@ -615,6 +613,8 @@ $2',
 'userlogin-resetpassword-link' => 'તમારી ગુપ્તસંજ્ઞા બદલો',
 'helplogin-url' => 'Help:પ્રવેશ માટે',
 'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|પ્રવેશવા માટેની મદદ]]',
+'userlogin-loggedin' => 'તમે પહેલેથી {{GENDER:$1|$1}} તરીકે પ્રવેશ કરેલો જ છે.
+બીજા સભ્ય તરીકે પ્રવેશ કરવા માટે નીચેનું ફોર્મ વાપરો.',
 'userlogin-createanother' => 'બીજું ખાતું બનાવો',
 'createacct-join' => 'તમારી માહિતી નીચે દાખલ કરો.',
 'createacct-another-join' => 'નવા ખાતાંની માહિતી નીચે દાખલ કરો.',
@@ -669,20 +669,19 @@ $2',
 'passwordtooshort' => 'ગુપ્ત સંજ્ઞામાં ઓછામાં {{PLURAL:$1|ઓછો એક અક્ષર હોવો |ઓછા $1 અક્ષર હોવા}} જોઇએ.',
 'password-name-match' => 'તમારી ગુપ્તસંજ્ઞા તમારા સભ્યનામ કરતાં અલગ જ હોવી જોઇએ.',
 'password-login-forbidden' => 'આ સભ્યનામ અને ગુપ્તસંજ્ઞા વાપરવા પર પ્રતિબંધ છે.',
-'mailmypassword' => 'પાસવરà«\8dડ àª\87-મેલમાં મોકલો',
+'mailmypassword' => 'નવà«\8b àªªàª¾àª¸àªµàª°à«\8dડ àª\87મેલમાં મોકલો',
 'passwordremindertitle' => '{{SITENAME}} માટેની નવી કામચલાઉ ગુપ્ત સંજ્ઞા',
 'passwordremindertext' => 'કોઇકે (કદાચ તમે IP એડ્રેસ $1 પરથી) {{SITENAME}} ($4) માટે નવી ગુપ્ત સજ્ઞા (પાસવર્ડ) માટે વિનંતી કરેલ છે.
 હંગામી ધોરણે સભ્ય "$2" માટે ગુપ્ત સંજ્ઞા બની છે અને તે "$3". જો તમે જ આ વિનંતી કરી હોય અને તમે ગુપ્ત સંજ્ઞા બદલવા માંગતા હો તો તમારે પ્રવેશ કરવો પડશે અને નવી ગુપ્ત સંજ્ઞા પસંદ કરવી પડશે. હંગામી ગુપ્ત સંજ્ઞાની અવધિ {{PLURAL:$5|એક દિવસ|$5 દિવસો}} છે ત્યાર બાદ તે કામ નહીં કરે.
 
 જો બીજા કોઇએ આ વિનંતી કરી હોય અથવા તમને તમારી જુની ગુપ્ત સંજ્ઞા યાદ આવી ગઇ હોય અને તમે તે બદલવા ન માંગતા હો તો આ સંદેશ અવગણીને તમારી જુની ગુપ્ત સંજ્ઞા વાપરવાનું ચાલુ રાખો.',
 'noemail' => 'સભ્ય "$1"નું કોઇ ઇ-મેલ સરનામું નોંધાયેલું નથી.',
-'noemailcreate' => 'વà«\88ધ àª\87-મà«\87લ àª\86પશà«\8b',
+'noemailcreate' => 'તમારà«\87 àªµà«\88ધ àª\87મà«\87લ àª\86પવાનà«\80 àª\9cરà«\82ર àª\9bà«\87.',
 'passwordsent' => '"$1" ની નવી ગુપ્તસંજ્ઞા (પાસવર્ડ) આપના ઇમેઇલ પર મોકલવામાં આવ્યો છે.
 કૃપા કરી તે મળ્યા બાદ ફરી લોગ ઇન કરો.',
-'blocked-mailpassword' => 'Your IP address is blocked from editing, and so is not allowed to use the password recovery function to prevent abuse.
-ફેરફાર કરવા માટે તમારું IP એડ્રેસ  સ્થગિત કરી દેવાયું છે તેથી દૂરુપયોગ ટાળવા માટે તમને ગુપ્તસંજ્ઞા રીકવરી કરવાની છૂટ નથી.',
-'eauthentsent' => 'પુષ્ટિ કરવા માટે તમે આપેલા સરનામાં પર ઇમેઇલ મોકલવામાં આવ્યો છે.
-એ જ સરનામે બીજો ઇમેઇલ થતાં પહેલાં તમારે ઇમેઇલમાં લખેલી સૂચનાઓ પ્રમાણે કરવું પડશે જેથી એ પુષ્ટિ થઇ શકે કે આપેલું સરનામું તમારું છે.',
+'blocked-mailpassword' => 'ફેરફાર કરવા માટે તમારું IP એડ્રેસ સ્થગિત કરી દેવાયું છે, તેથી દૂરુપયોગ ટાળવા માટે તમને ગુપ્તસંજ્ઞા ફરી મેળવવાની છૂટ નથી.',
+'eauthentsent' => 'પુષ્ટિ કરવા માટે તમે આપેલા સરનામાં પર ઇમેલ મોકલવામાં આવ્યો છે.
+એ જ સરનામે બીજો ઇમેલ થતાં પહેલાં તમારે ઇમેલમાં લખેલી સૂચનાઓ પ્રમાણે કરવું પડશે જેથી એ પુષ્ટિ થઇ શકે કે આપેલું સરનામું તમારું છે.',
 'throttled-mailpassword' => 'ગુપ્ત સંજ્ઞા યાદ અપાવતી ઇમેઇલ છેલ્લા {{PLURAL:$1|કલાકમાં|$1 કલાકોમાં}} મોકલેલી છે.
 દૂરુપયોગ રોકવા માટે, {{PLURAL:$1|કલાકમાં|$1 કલાકોમાં}} ફક્ત એક જ આવી મેઇલ કરવામાં આવે છે.',
 'mailerror' => 'મેઇલ મોકલવામાં ત્રુટિ: $1',
@@ -733,23 +732,23 @@ $2',
 'resetpass-no-info' => 'બારોબાર આ પાનું જોવા માટે પ્રવેશ કરવો આવશ્યક છે.',
 'resetpass-submit-loggedin' => 'ગુપ્તસંજ્ઞા બદલો',
 'resetpass-submit-cancel' => 'રદ કરો',
-'resetpass-wrong-oldpass' => 'àª\85વà«\88ધ àª¹àª\82àª\97ામà«\87 àª\95à«\87 àª\95ાયમી ગુપ્તસંજ્ઞા.
-કદાચ તમે પહેલેથી સફળતા પૂર્વક તમારી ગુપ્ત સંજ્ઞા બદલી દીધી હોય કે નવી ગુપ્ત સંંજ્ઞામાટે વિનંતિ કરી હોય',
+'resetpass-wrong-oldpass' => 'àª\85યà«\8bàª\97à«\8dય àª¹àª\82àª\97ામà«\80 àª\95à«\87 àª¹àª¾àª²àª¨ી ગુપ્તસંજ્ઞા.
+કદાચ તમે પહેલેથી સફળતાપૂર્વક તમારી ગુપ્ત સંજ્ઞા બદલી દીધી હશે કે નવી ગુપ્ત સંજ્ઞા માટે વિનંતિ કરી હશે.',
 'resetpass-temp-password' => 'કામચલાઉ ગુપ્તસંજ્ઞા:',
 'resetpass-abort-generic' => 'વિસ્તારક વડે પાસવર્ડ બદલવાનું રોકી રખાયું છે.',
 
 # Special:PasswordReset
-'passwordreset' => 'પાસવરà«\8dડ àª°à«\80સà«\87àª\9f àª\95રો',
+'passwordreset' => 'àª\97à«\81પà«\8dત àª¸àª\82àª\9cà«\8dàª\9eા àª«àª°à«\80 àª\97à«\8bઠવો',
 'passwordreset-text-one' => 'તમારો પાસવર્ડ બદલવા માટે આ ફોર્મ પૂરુ કરો.',
-'passwordreset-legend' => 'પાસવરà«\8dડ àª°à«\80સà«\87àª\9f àª\95રો',
-'passwordreset-disabled' => 'àª\86 àªµàª¿àª\95à«\80 àªªàª° àªªàª¾àª¸àªµàª°à«\8dડ àª°à«\80સà«\87àª\9f àª\95રવા પર પ્રતિબંધ છે.',
+'passwordreset-legend' => 'àª\97à«\81પà«\8dત àª¸àª\82àª\9cà«\8dàª\9eા àª«àª°à«\80 àª\97à«\8bઠવો',
+'passwordreset-disabled' => 'àª\86 àªµàª¿àª\95à«\80 àªªàª° àª\97à«\81પà«\8dત àª¸àª\82àª\9cà«\8dàª\9eા àª«àª°à«\80 àª\97à«\8bઠવવા પર પ્રતિબંધ છે.',
 'passwordreset-emaildisabled' => 'આ વિકિ પર ઇમેઇલ સગવડ બંધ છે.',
 'passwordreset-username' => 'સભ્ય નામ:',
 'passwordreset-domain' => 'ડોમેઈન:',
-'passwordreset-capture' => 'પરિણામી ઈ મેલ જોવો છે ?',
+'passwordreset-capture' => 'પરિણામી ઈમેલ જોવો છે?',
 'passwordreset-capture-help' => 'જો તમે આ ઓપ્શન સિલેક્ટ કરશો, તો તમને અને યુઝર ને ઈ મેલ (કામચલાઉ પાસવર્ડ સાથે) દેખાડવામાં આવશે.',
-'passwordreset-email' => 'ઇ મેલ સરનામું:',
-'passwordreset-emailtitle' => '{{SITENAME}} àª®àª¾àª\9fà«\87 àª\96ાતà«\81 àª¬àª¨àª¾àªµà«\8dયà«\81àª\82',
+'passwordreset-email' => 'ઇમેલ સરનામું:',
+'passwordreset-emailtitle' => '{{SITENAME}} àªªàª° àª\96ાતાનà«\80 àª®àª¾àª¹àª¿àª¤à«\80',
 'passwordreset-emailtext-ip' => 'કોઈકે (કદાચ તમોએ , $1 IP એડ્રેસ થી) તમારી વેબસાઈટ {{SITENAME}}  ($4) નો પાસવર્ડ રિસેટ કરવાની રજૂઆત કરી છે. આ ઈમેઈલ એડ્રેસ સાથે {{PLURAL:$3|નું ખાતું|ના ખાતા}} જોડાયેલા છે.
 .
 .
@@ -769,15 +768,15 @@ $2
 'passwordreset-emailerror-capture' => 'પાસવર્ડ ફરી ગોઠવવા માટેનો ઇમેલ બનાવવામાં આવ્યો છે, જે નીચે પ્રમાણે છે, પરંતુ તે {{GENDER:$2|સભ્ય}}ને મોકલવામાં નિષ્ફળ થયો છે: $1',
 
 # Special:ChangeEmail
-'changeemail' => 'àª\88 àª®à«\87લ àª\96ાતà«\81 àª¬àª¦àª²àªµàª¾ àª®àª¾àª\9fà«\87',
-'changeemail-header' => 'તમારા àª\96ાતાનà«\81àª\82 àª\88-મà«\87àª\88લ સરનામું બદલો',
+'changeemail' => 'àª\87મà«\87લ àª¸àª°àª¨àª¾àª®à«\81àª\82 àª¬àª¦àª²à«\8b',
+'changeemail-header' => 'તમારા àª\96ાતાનà«\81àª\82 àª\87મà«\87લ સરનામું બદલો',
 'changeemail-text' => 'તમારું ઈ-મેઈલ સરનામું બદલવા માટે આ ફોર્મ ભરો. આ ફેરફાર કાયમ કરવા માટે તમારે પાસવર્ડ ભરવાની જરૂર પડશે.',
 'changeemail-no-info' => 'બારોબાર આ પાનું જોવા માટે પ્રવેશ કરવો આવશ્યક છે.',
-'changeemail-oldemail' => 'હાલ નું ઈ મેલ ખાતુ:',
-'changeemail-newemail' => 'નવું ઈ-મેલ સરનામું',
+'changeemail-oldemail' => 'હાલનું ઈમેલ સરનામું:',
+'changeemail-newemail' => 'નવું ઈમેલ સરનામું:',
 'changeemail-none' => '(કંઈ નહી)',
 'changeemail-password' => 'તમારો {{SITENAME}} પાસવર્ડ:',
-'changeemail-submit' => 'ઈ મેલ બદલો',
+'changeemail-submit' => 'ઈમેલ બદલો',
 'changeemail-cancel' => 'રદ કરો',
 
 # Special:ResetTokens
@@ -811,13 +810,13 @@ $2
 # Edit pages
 'summary' => 'સારાંશ:',
 'subject' => 'વિષય/શીર્ષક:',
-'minoredit' => 'આ એક નાનો સુધારો છે.',
+'minoredit' => 'આ એક નાનો સુધારો છે',
 'watchthis' => 'આ પાનાને ધ્યાનમાં રાખો',
-'savearticle' => 'સાચવો',
+'savearticle' => 'પાનà«\81àª\82 àª¸àª¾àª\9aવà«\8b',
 'preview' => 'પૂર્વાવલોકન',
-'showpreview' => 'ઝલક',
+'showpreview' => 'ઝલક દર્શાવો',
 'showlivepreview' => 'જીવંત પૂર્વાવલોકન',
-'showdiff' => 'ફેરફારો',
+'showdiff' => 'ફેરફારો દર્શાવો',
 'anoneditwarning' => "'''ચેતવણી:''' તમે તમારા સભ્ય નામથી પ્રવેશ કર્યો નથી.
 આ પાનાનાં ઇતિહાસમાં તમારૂં આઇ.પી. (IP) એડ્રેસ નોંધવામાં આવશે.",
 'anonpreviewwarning' => 'તમે સભ્યનામથી પ્રવેશ કર્યો નથી,આ પાનું ઈતિહાસમાંતમારા IP સરનામાના નામે  સાચવવામાં આવશે',
@@ -934,7 +933,7 @@ $2
 તમારે તમારા ફેરફારો વિહરમાન હયાત લેખમાં વિલિન કરવો પડશે. 
  જો તમે  \"{{int:savearticle}}\" આ બાન દબાવશો તો '''ફક્ત''' ઉપરનો લેખ સચવાશે.",
 'yourtext' => 'તમારું લખાણ',
-'storedversion' => 'રàª\95à«\8dષિત પુનરાવર્તન',
+'storedversion' => 'સàª\82àª\97à«\8dરહà«\87લ પુનરાવર્તન',
 'nonunicodebrowser' => "'''ચેતવણી: તમારું બ્રાઉઝર યુનિકોડ ઉકેલવા સક્ષમ નથી.'''
 અહીં તમે સુરક્ષિત રીતે ફેરફારો નહીં કરી શકો: ASCII સિવાયના અક્ષરો સંપાદન ચોકઠામાં હેક્સાડેસિમલ સ્વરૂપે દેખાશે.",
 'editingold' => "'''ચેતવણી: તમે આ પાનાની ખૂબ જૂની આવૃત્તિમાં ફેરફાર કરી રહ્યાં છો.'''
@@ -1039,7 +1038,7 @@ $3 દ્વારા અપાયેલ કારણ છે ''$2''",
 'currentrev-asof' => '$1એ જોઈ શકાતી હાલની આવૃત્તિ',
 'revisionasof' => '$1 સુધીનાં પુનરાવર્તન',
 'revision-info' => '$2 દ્વારા $1 સુધીમાં કરવામાં આવેલાં ફેરફારો',
-'previousrevision' => '←જુના ફેરફારો',
+'previousrevision' => '← જુના ફેરફારો',
 'nextrevision' => 'આ પછીનું પુનરાવર્તન→',
 'currentrevisionlink' => 'વર્તમાન આવૃત્તિ',
 'cur' => 'વર્તમાન',
@@ -1194,7 +1193,7 @@ $1",
 # Merge log
 'mergelog' => 'લોગ વિલિન કરો',
 'pagemerge-logentry' => '[[$1]] ને  [[$2]]માં વિલિન કરાયું ( $3 સુધી ના પુનરાવર્તનો)',
-'revertmerge' => 'છુટુ પાડો',
+'revertmerge' => 'છુટુ પાડો',
 'mergelogpagetext' => 'તાજેતરમાં એક બીજા સાથે વિલિન થયેલ ઇતિહાસ પાનાની યાદી',
 
 # Diffs
@@ -1277,7 +1276,7 @@ $1",
 
 # Preferences page
 'preferences' => 'પસંદ',
-'mypreferences' => 'પસંદ',
+'mypreferences' => 'પસંદગીઓ',
 'prefs-edits' => 'સંપાદનોની સંખ્યા',
 'prefsnologin' => 'પ્રવેશ કરેલ નથી',
 'prefsnologintext' => 'સભ્યના અધિકારો બદલવા તમે <span class="plainlinks">[{{fullurl:{{#Special:UserLogin}}|returnto=$1}} logged in]</span> પ્રવેશ કરેલો હોવો જોઈએ',
@@ -1873,10 +1872,10 @@ https://www.mediawiki.org/wiki/Manual:Image_Authorization. જુઓ',
 'filehist-current' => 'વર્તમાન',
 'filehist-datetime' => 'તારીખ/સમય',
 'filehist-thumb' => 'લઘુચિત્ર',
-'filehist-thumbtext' => '$1àª\8d હતું તે સંસ્કરણનું લઘુચિત્ર',
-'filehist-nothumb' => 'થમà«\8dબનà«\87àª\87લ નથી',
+'filehist-thumbtext' => '$1àª\8f હતું તે સંસ્કરણનું લઘુચિત્ર',
+'filehist-nothumb' => 'લàª\98à«\81àª\9aિતà«\8dર નથી',
 'filehist-user' => 'સભ્ય',
-'filehist-dimensions' => 'પરિમાણ',
+'filehist-dimensions' => 'પરિમાણ',
 'filehist-filesize' => 'ફાઇલનું કદ',
 'filehist-comment' => 'ટિપ્પણી',
 'filehist-missing' => 'ફાઇલ ગાયબ',
@@ -1917,8 +1916,8 @@ https://www.mediawiki.org/wiki/Manual:Image_Authorization. જુઓ',
 'filedelete-intro' => "તમે '''[[Media:$1|$1]]'' ફાઇલ અને તેની સાથે સંલગ્ન ઇતિહાસ ભુંસી રહ્યા છો.",
 'filedelete-intro-old' => "તમે '''[[Media:$1|$1]]'''નું આ [$4 $3, $2] વર્ઝન ભુસી રહ્યા છો.",
 'filedelete-comment' => 'કારણ:',
-'filedelete-submit' => 'ભà«\81àª\82સો',
-'filedelete-success' => "'''$1'''નà«\87 àª­à«\82àª\82સà«\80 àª¨àª¾àª\82àª\96વામાં આવ્યું છે.",
+'filedelete-submit' => 'દà«\82ર àª\95રો',
+'filedelete-success' => "'''$1'''નà«\87 àª¦à«\82ર àª\95રવામાં આવ્યું છે.",
 'filedelete-success-old' => "'''[[Media:$1|$1]]'''નું $3, $2ના રોજનું  સંસ્કરણ ભુંસી નાખ્યું છે.",
 'filedelete-nofile' => "'''$1'''નું અસ્તિત્વ નથી.",
 'filedelete-nofile-old' => "'''$1'''નું  આપે જણાવેલ ખાસિયતવાળું સંગ્રહિત સંસ્કરણ અસ્તિત્વમાં નથી.",
@@ -1955,6 +1954,10 @@ https://www.mediawiki.org/wiki/Manual:Image_Authorization. જુઓ',
 'randompage-nopages' => 'આ {{PLURAL:$2|નામસ્થળ|નામસ્થળો}}માં કોઇ પાના નથી: $1.',
 
 # Random page in category
+'randomincategory' => 'શ્રેણીમાં ગમે તે પાનું',
+'randomincategory-invalidcategory' => '"$1" એ યોગ્ય શ્રેણી નામ નથી.',
+'randomincategory-nopages' => '[[:Category:$1|$1]] વર્ગમાં કોઇ પાનું નથી.',
+'randomincategory-selectcategory' => 'વર્ગમાંથી ગમે તે પાનું મેળવો: $1 $2.',
 'randomincategory-selectcategory-submit' => 'જાઓ',
 
 # Random redirect
@@ -2010,6 +2013,7 @@ https://www.mediawiki.org/wiki/Manual:Image_Authorization. જુઓ',
 # Miscellaneous special pages
 'nbytes' => '$1 {{PLURAL:$1|બાઇટ|બાઇટ્સ}}',
 'ncategories' => '$1 {{PLURAL:$1|શ્રેણી|શ્રેણીઓ}}',
+'ninterwikis' => '$1 {{PLURAL:$1|આંતરવિકિ|આંતરવિકિઓ}}',
 'nlinks' => '$1 {{PLURAL:$1|કડી|કડીઓ}}',
 'nmembers' => '$1 {{PLURAL:$1|સદસ્ય|સદસ્યો}}',
 'nrevisions' => '$1 {{PLURAL:$1|પુનરાવર્તન|પુનરાવર્તનો}}',
@@ -2057,6 +2061,7 @@ https://www.mediawiki.org/wiki/Manual:Image_Authorization. જુઓ',
 'listusers' => 'સભ્યોની યાદી',
 'listusers-editsonly' => 'માત્ર સંપાદન કરનારા સભ્યો બતાવો',
 'listusers-creationsort' => 'તારીખ અનુસાર ગોઠવો',
+'listusers-desc' => 'ઉતરતા ક્રમમાં ગોઠવો',
 'usereditcount' => '$1 {{PLURAL:$1|ફેરફાર|ફેરફારો}}',
 'usercreated' => '$1 તારીખે $2 વાગ્યે {{GENDER:$3|બનાવ્યું}}',
 'newpages' => 'નવાં પાનાં',
@@ -2102,7 +2107,7 @@ https://www.mediawiki.org/wiki/Manual:Image_Authorization. જુઓ',
 'prevpage' => 'પાછળનું પાનું ($1)',
 'allpagesfrom' => 'આનાથી શરૂ થતા પાના દર્શાવો:',
 'allpagesto' => 'આનાથી અંત થતા પાના દર્શાવો:',
-'allarticles' => 'બધા àª²à«\87àª\96',
+'allarticles' => 'બધા àªªàª¾àª¨àª¾àª\82àª\93',
 'allinnamespace' => 'બધા પાના  ($1 નમાવકાશ)',
 'allnotinnamespace' => 'બધા પાના  ($1 નમાવકાશમાંના હોય)',
 'allpagesprev' => 'પહેલાનું',
@@ -2317,10 +2322,12 @@ $UNWATCHURL
 'deletecomment' => 'કારણ:',
 'deleteotherreason' => 'અન્ય/વધારાનું કારણ:',
 'deletereasonotherlist' => 'અન્ય કારણ',
-'deletereason-dropdown' => '* હટાવવાનાં સામાન્ય કારણો 
-** લેખકની વિનંતી
+'deletereason-dropdown' => '* દૂર કરવાના સામાન્ય કારણો
+** સ્પામ
+** ભાંગફોડીયા પ્રવૃત્તિ
 ** પ્રકાશનાધિકાર ભંગ 
-** ભાંગફોડીયા પ્રવૃત્તિ',
+** લેખકની વિનંતી
+** ભાંગેલ વળાંક',
 'delete-edit-reasonlist' => 'ભુંસવાનું કારણ બદલો.',
 'delete-toobig' => 'આ પાનાના ફેરફારોનો ઇતિહાસ ખૂબ લાંબો છે , $1 {{PLURAL:$1|ફેરફાર|ફેરફારો}}થી પણ વધારે.
 {{SITENAME}}ને અક્સ્માતે ખોરવાતું અટકાવવા આવા પાનાને હટાવવા પર પ્રતિબંધ છે.',
@@ -2531,7 +2538,7 @@ $1',
 'whatlinkshere-hidetrans' => '$1 આરપાર સમાવેશનો',
 'whatlinkshere-hidelinks' => 'કડીઓ $1',
 'whatlinkshere-hideimages' => '$1 ફાઇલની કડીઓ',
-'whatlinkshere-filters' => 'ચાળણી',
+'whatlinkshere-filters' => 'ચાળણી',
 
 # Block/unblock
 'autoblockid' => 'ઓટોબ્લોક #$1',
@@ -2588,7 +2595,7 @@ $1',
 'unblocked-range' => '$1  અનાવરોધિત કરવામાં આવ્યું છે',
 'unblocked-id' => ' $1 નો પ્રતિબંધ હટાવાયો',
 'blocklist' => 'પ્રતિબંધિત સભ્યો',
-'ipblocklist' => 'àª\85વરà«\8bધિત વપરાશકર્તાઓ',
+'ipblocklist' => 'પà«\8dરતિબàª\82ધિત વપરાશકર્તાઓ',
 'ipblocklist-legend' => 'પ્રતિબંધિત સભ્ય શોધો',
 'blocklist-userblocks' => 'એકાઉન્ટ બ્લોકો છુપાવો',
 'blocklist-tempblocks' => 'કામચલાઉ બ્લોકો છુપાવો',
@@ -2652,12 +2659,9 @@ $1',
 જો કે આને સામૂહિક $2 રોક લગાવાઇ હોવાથી તે સમૂ હની રોક હટાવી શકાશે.',
 'ip_range_invalid' => 'અવૈધ IP શ્રેણી',
 'ip_range_toolarge' => '/$1થી મોટા વિસ્તાર પ્રતિબંધની પરવાનગી નથી.',
-'blockme' => 'મને પ્રતિબંધિત કરો',
 'proxyblocker' => 'અવેજી (પ્રોક્સી) રોક લગાડનાર',
-'proxyblocker-disabled' => 'આ સુવિધા નિષ્ક્રીય કરી છે.',
 'proxyblockreason' => 'તમારા IP સરનામા પરા રોક લગાડાઈ છે કેમકે તેએક ખુલ્લી પ્રોક્સી છે.
 કૃપયા તમારા ઇંટરનેટ સેવા પ્રદાતા કે તકનીકી સહાય વિભાગનો સંપર્ક કરી જણાવો કે આ એક ભયંકર સુરક્ષા મામલો છે.',
-'proxyblocksuccess' => 'સંપન્ન',
 'sorbsreason' => '{{SITENAME}} દ્વારા વપરાયેલા DNSBL માં તમારું IP સરનામું એક ખુલ્લી પ્રોક્સી તરીકે નોંધાયું છે.',
 'sorbs_create_account_reason' => '{{SITENAME}} માં વપરાતા DNSBL દ્વારા તમારા IP  સરનામાને ખુલી પ્રોક્સી જણાવાઇ છે.
 તમે ખાતાની રચના નહીં કરી શકો.',
@@ -2826,6 +2830,8 @@ $1',
 'thumbnail-more' => 'વિસ્તૃત કરો',
 'filemissing' => 'ફાઇલ ગાયબ',
 'thumbnail_error' => 'નાની છબી (થંબનેઇલ-thumbnail) બનાવવામાં ત્રુટિ: $1',
+'thumbnail_error_remote' => '$1 તરફથી ક્ષતિ સંદેશ:
+$2',
 'djvu_page_error' => 'DjVu પાનું સીમાની બહાર',
 'djvu_no_xml' => 'DjVu ફાઇલ માટે XML લાવવા અસમર્થ',
 'thumbnail-temp-create' => 'હંગામી થમ્બનેલ ફાઈલ ન બનાવી શકાઈ',
@@ -2907,7 +2913,7 @@ $1',
 'tooltip-pt-anonuserpage' => 'IP સરનામું માટેના સભ્ય પાનામાં તમે ફેરફાર કરી રહ્યાં છો.',
 'tooltip-pt-mytalk' => 'તમારૂં ચર્ચાનું પાનું',
 'tooltip-pt-anontalk' => 'આ IP સરનામા દ્વારા થયેલ ફેરફારની ચર્ચા',
-'tooltip-pt-preferences' => 'મારà«\80 àªªàª¸àª\82દ',
+'tooltip-pt-preferences' => 'તમારà«\80 àªªàª¸àª\82દàª\97à«\80àª\93',
 'tooltip-pt-watchlist' => 'તમે દેખરેખ રાખી રહ્યાં હોવ તેવા પાનાઓની યાદી',
 'tooltip-pt-mycontris' => 'તમારા યોગદાનની યાદી',
 'tooltip-pt-login' => 'આપને લોગ ઇન કરવા ભલામણ કરવામાં આવે છે, જોકે તે આવશ્યક નથી',
@@ -2933,9 +2939,9 @@ $1',
 'tooltip-n-mainpage-description' => 'મુખ્ય પાના પર જાઓ',
 'tooltip-n-portal' => 'પરિયોજના વિષે, આપ શું કરી શકો અને વસ્તુઓ ક્યાં શોધશો',
 'tooltip-n-currentevents' => 'પ્રસ્તુત ઘટનાની પૃષ્ઠભૂમિની માહિતિ શોધો',
-'tooltip-n-recentchanges' => 'વિકિમાં હાલમા થયેલા ફેરફારોની સૂચિ.',
+'tooltip-n-recentchanges' => 'વિકિમાં હાલમાં થયેલ ફેરફારોની સૂચિ',
 'tooltip-n-randompage' => 'કોઇ પણ એક લેખ બતાવો',
-'tooltip-n-help' => 'શોધવા માટેની જગ્યા.',
+'tooltip-n-help' => 'શોધવા માટેની જગ્યા',
 'tooltip-t-whatlinkshere' => 'અહીં જોડાતા બધાં વિકિ પાનાઓની યાદી',
 'tooltip-t-recentchangeslinked' => 'આ પાના પરની કડીઓ વાળા લેખોમાં તાજેતરમાં થયેલા ફેરફારો',
 'tooltip-feed-rss' => 'આ પાના માટે આર.એસ.એસ. ફીડ',
@@ -2995,6 +3001,8 @@ $1',
 'spambot_username' => 'મિડિયાવિકી સ્પેમ સફાઇ',
 'spam_reverting' => ' $1 પર કડી ન ધરાવતા છેલ્લા ફેરેફાર પર પુનઃ સ્થાપન કરાય છે',
 'spam_blanking' => 'બધા ફેરફારોમાં  $1 પર કડી હતી, આને હટાવી દેવામાં આવે છે',
+'simpleantispam-label' => "સ્પામ-વિરોધી તપાસ.
+આને '''ના''' ભરશો!",
 
 # Info page
 'pageinfo-title' => ' "$1" માટે માહિતી',
@@ -3824,6 +3832,7 @@ $5
 'dberr-problems' => 'દિલગીરી! આ સાઇટ તકનિકી અડચણ અનુભવી રહી છે.',
 'dberr-again' => 'થોડી વાર રાહ જોઈને ફરી પેજ લોડ કરવાનો પ્રયત્ન કરો.',
 'dberr-info' => '(માહિતી સંચય સર્વર : $1નો સંપર્ક નથી કરી શકાયો)',
+'dberr-info-hidden' => '(ડેટાબેઝ સર્વર સાથે જોડાણ થઇ શકતું નથી)',
 'dberr-usegoogle' => 'તેસમયા દરમ્યાન તમે ગુગલ દ્વારા શોધી શકો',
 'dberr-outofdate' => 'આપણી માહિતી સંબંધી તેમની સૂચિ કાલાતિત હોઇ શકે.',
 'dberr-cachederror' => 'વિનંતિ કરેલ પાનાની આ એક સંગ્રહીત પ્રત માત્ર છે અને તે અધ્યતન ન પણ હોય.',
@@ -3871,8 +3880,8 @@ $5
 'logentry-move-move-noredirect' => '$1 એ દિશાનિર્દેશન છોડ્યા વગર પાના $3ને $4 પર {{GENDER:$2|વાળ્યું}}',
 'logentry-move-move_redir' => '$1એ દિશાનિર્દેશન કરીને પાના $3ને $4 પર {{GENDER:$2|ખસેડ્યું}}',
 'logentry-move-move_redir-noredirect' => '$1એ દિશાનિર્દેશન કરીને પાના $3ને $4 પર {{GENDER:$2|વાળ્યું}} પણ પાછળ દિશાનિર્દેશન છોડ્યું નહી',
-'logentry-patrol-patrol' => '$1 આવૃત્તિ ચિહ્નિત થયેલ પાનાં $4 $3 ચોકી કરવા ફરવા નીકળવું',
-'logentry-patrol-patrol-auto' => '$1 આપોઆપ ચિહ્નિત ચોકી પહેરો કરવા લાગ્યા આવૃત્તિ પાનું $4 $3',
+'logentry-patrol-patrol' => 'પાનાં $3 ની આવૃત્તિ $4 ને $1 વડે ચોકી કરવા માટે {{GENDER:$2|નિશાનીત}} કરેલ છે',
+'logentry-patrol-patrol-auto' => 'પાનાં $3 ની આવૃત્તિ $4 ને $1 એ આપમેળે ચોકી કરવા માટે {{GENDER:$2|નિશાનીત}} કરેલ છે',
 'logentry-newusers-newusers' => 'સભ્ય ખાતું $1 {{GENDER:$2|બનાવવામાં આવ્યું}}',
 'logentry-newusers-create' => 'સભ્ય ખાતું $1 {{GENDER:$2|બનાવવામાં આવ્યું}}',
 'logentry-newusers-create2' => 'સભ્ય ખાતું $3 $1 વડે {{GENDER:$2|બનાવવામાં આવ્યું હતું}}',
index cfd1c48..1e2aafe 100644 (file)
@@ -1186,7 +1186,6 @@ Shoh ny reaghaghyn roie da'n duillag '''$1''':",
 'unblocklogentry' => '$1 er ny neughlassey magh',
 'block-log-flags-anononly' => 'ymmydeyryn neuenmyssit ynrican',
 'block-log-flags-nocreate' => 'gyn kiart coontyssyn y chroo',
-'proxyblocksuccess' => 'Jeant.',
 
 # Move page
 'move-page' => '$1 y scughey',
index 3176244..770bd89 100644 (file)
@@ -1560,7 +1560,6 @@ Also see [[Special:WantedCategories|wanted categories]].',
 'ip_range_invalid' => '無效嘅IP範圍。',
 'proxyblocker' => '代理封鎖器',
 'proxyblockreason' => '汝嘅IP地址係一隻開放嘅代理,其已經分封鎖。請聯繫汝嘅網際網路服務提供商或技術支援者並講佢兜聽邇隻嚴重嘅安全問題。',
-'proxyblocksuccess' => '完成。',
 'sorbsreason' => 'Ngì-ke IP chhô-vi pûn DNSBL lie̍t-vi su̍k-yî khôi-fong thoi-lî fu̍k-vu-khí.',
 'sorbs_create_account_reason' => 'Ngì-ke IP chhô-vi pûn DNSBL lie̍t-vi su̍k-yî khôi-fong thoi-lî fu̍k-vu-khí. Só-yî ngì mò-fap kien-li̍p chong-ho.',
 
index 23c203f..b0084fa 100644 (file)
@@ -736,7 +736,6 @@ E ʻike iā $2 no ka papa o nā kāpae ʻana hou.',
 'unblocklink' => 'mai pale',
 'change-blocklink' => 'hoʻololi ka palena',
 'contribslink' => 'nā ha‘awina',
-'blockme' => 'E ke‘a ia‘u',
 
 # Move page
 'move-page-legend' => 'Hoʻoneʻe i ka ʻaoʻao',
index d26e1b7..492d702 100644 (file)
@@ -579,7 +579,7 @@ $messages = array(
 'articlepage' => 'צפייה בדף התוכן',
 'talk' => 'שיחה',
 'views' => 'צפיות',
-'toolbox' => 'ת×\99×\91ת ×\9b×\9c×\99×\9d',
+'toolbox' => 'כלים',
 'userpage' => 'צפייה בדף המשתמש',
 'projectpage' => 'צפייה בדף המיזם',
 'imagepage' => 'צפייה בדף הקובץ',
@@ -890,7 +890,7 @@ $2',
 'passwordsent' => 'סיסמה חדשה נשלחה לכתובת הדואר האלקטרוני הרשומה עבור "$1".
 אנא היכנסו חזרה לאתר אחרי שתקבלו אותה.',
 'blocked-mailpassword' => 'כתובת ה־IP שלכם חסומה מעריכה, ולפיכך אינכם מורשים להשתמש באפשרות שחזור הסיסמה כדי למנוע ניצול לרעה של התכונה.',
-'eauthentsent' => '×\93×\95×\90\9c ×\90×\99×\9e×\95ת × ×©×\9c×\97 ×\9c×\9bת×\95×\91ת ×\94×\93×\95×\90\9c ×©×§×\91עת.
+'eauthentsent' => '×\93×\95×\90\9c ×\90×\99×\9e×\95ת × ×©×\9c×\97 ×\9c×\9bת×\95×\91ת ×\94×\93×\95×\90\9c ×©×¦×\95×\99× ×\94.
 לפני שדברי דוא"ל אחרים יישלחו לחשבון הזה, יהיה עליך לפעול לפי ההוראות בדוא"ל, כדי לאשר שהחשבון אכן שייך לך.',
 'throttled-mailpassword' => 'כבר נשלח דוא"ל לאיפוס הסיסמה ב{{PLURAL:$1|שעה האחרונה|שעתיים האחרונות|־$1 השעות האחרונות}}.
 כדי למנוע ניצול לרעה, יכול להישלח רק דוא"ל אחד כזה בכל {{PLURAL:$1|שעה|שעתיים|$1 שעות}}.',
@@ -986,7 +986,7 @@ $2
 
 # Special:ChangeEmail
 'changeemail' => 'שינוי כתובת דוא"ל',
-'changeemail-header' => 'שינוי כתוב דוא"ל של חשבון',
+'changeemail-header' => 'שינוי כתובת הדואר האלקטרוני בחשבון',
 'changeemail-text' => 'מלאו טופס זה כדי לשנות את כתובת הדואר האלקטרוני שלכם. יהיה עליכם למלא סיסמה כדי לאשר את השינוי.',
 'changeemail-no-info' => 'עליכם להיכנס לחשבון כדי לגשת לדף זה ישירות.',
 'changeemail-oldemail' => 'כתובת דוא"ל נוכחית:',
@@ -1334,15 +1334,15 @@ $2
 * חשיפת מידע אישי
 *: '''כתובות בתים ומספרי טלפון, מספרי ביטוח לאומי, וכדומה'''",
 'revdelete-legend' => 'הגדרת הגבלות התצוגה',
-'revdelete-hide-text' => '×\94סתרת ×ª×\95×\9b×\9f ×\94×\92רס×\94',
+'revdelete-hide-text' => 'תוכן הגרסה',
 'revdelete-hide-image' => 'הסתרת תוכן הקובץ',
 'revdelete-hide-name' => 'הסתרת הפעולה ודף היעד',
-'revdelete-hide-comment' => '×\94סתרת ×ª×§×¦×\99ר ×\94ער×\99×\9b×\94',
-'revdelete-hide-user' => '×\94סתרת ×©×\9d ×\94×\9eשת×\9eש ×\90×\95 ×\9bת×\95×\91ת ×\94Ö¾IP ×©×\9c ×\94×¢×\95ר×\9a',
+'revdelete-hide-comment' => 'תקציר העריכה',
+'revdelete-hide-user' => 'שם המשתמש או כתובת ה־IP של העורך',
 'revdelete-hide-restricted' => 'הסתרת המידע גם ממפעילי המערכת',
 'revdelete-radio-same' => '(ללא שינוי)',
-'revdelete-radio-set' => '×\9b×\9f',
-'revdelete-radio-unset' => '×\9c×\90',
+'revdelete-radio-set' => '×\92×\9c×\95×\99',
+'revdelete-radio-unset' => '×\9e×\95סתר',
 'revdelete-suppress' => 'הסתרת המידע גם ממפעילי המערכת',
 'revdelete-unsuppress' => 'הסרת הגבלות בגרסאות המשוחזרות',
 'revdelete-log' => 'סיבה:',
@@ -2248,7 +2248,8 @@ $1',
 'doubleredirectstext' => 'בדף הזה מופיעה רשימת דפי הפניה שמפנים לדפי הפניה אחרים.
 כל שורה מכילה קישור לשתי ההפניות הראשונות, וכן את היעד של ההפניה השנייה, שהיא לרוב היעד ה"אמיתי" של ההפניה, שההפניה הראשונה אמורה להצביע אליו.
 פריטים <del>מחוקים</del> כבר תוקנו.',
-'double-redirect-fixed-move' => '[[$1]] הועבר. כעת הוא הפניה לדף [[$2]].',
+'double-redirect-fixed-move' => '[[$1]] הועבר.
+כעת זו הפניה לדף [[$2]].',
 'double-redirect-fixed-maintenance' => 'תיקון הפניה כפולה מ[[$1]] ל[[$2]].',
 'double-redirect-fixer' => 'מתקן הפניות',
 
@@ -2900,12 +2901,9 @@ $1',
 'ipb_blocked_as_range' => 'שגיאה: כתובת ה־IP $1 אינה חסומה ישירות ולכן לא ניתן לשחרר את חסימתה. עם זאת, היא חסומה כחלק מהטווח $2, שניתן לשחרר את חסימתו.',
 'ip_range_invalid' => 'טווח IP שגוי.',
 'ip_range_toolarge' => 'לא ניתן לחסום טווחים גדולים מ־<span dir="ltr">/$1</span>.',
-'blockme' => 'חסום אותי',
 'proxyblocker' => 'חוסם פרוקסי',
-'proxyblocker-disabled' => 'אפשרות זו מבוטלת.',
 'proxyblockreason' => 'כתובת ה־IP שלכם נחסמה משום שהיא כתובת של שרת פרוקסי פתוח.
 אנא צרו קשר עם ספק האינטרנט שלכם או עם התמיכה הטכנית של הארגון שלכם והודיעו להם על בעיית האבטחה החמורה הזאת.',
-'proxyblocksuccess' => 'בוצע.',
 'sorbsreason' => 'כתובת ה־IP שלכם רשומה ככתובת פרוקסי פתוחה ב־DNSBL שאתר זה משתמש בו.',
 'sorbs_create_account_reason' => 'כתובת ה־IP שלכם רשומה ככתובת פרוקסי פתוחה ב־DNSBL שאתר זה משתמש בו. אינכם יכולים ליצור חשבון.',
 'xffblockreason' => 'כתובת IP הנמצאת בכותרת X-Forwarded-For, בין אם שלכם או של שרת פרוקסי שאתם משתמשים בו, נחסמה. סיבת החסימה המקורית הייתה: $1',
@@ -3271,6 +3269,8 @@ $2',
 'spam_reverting' => 'שחזור לגרסה אחרונה שלא כוללת קישורים ל־$1',
 'spam_blanking' => 'כל הגרסאות כוללות קישורים ל־$1, מרוקן את הדף',
 'spam_deleting' => 'כל הגרסאות כוללות קישורים ל־$1, מוחק את הדף',
+'simpleantispam-label' => "בדיקת אנטי־ספאם.
+'''אל''' תמלאו שדה זה!",
 
 # Info page
 'pageinfo-title' => 'מידע על "$1"',
@@ -4108,7 +4108,7 @@ $5
 # Special:Redirect
 'redirect' => 'הפניה לפי שם קובץ, מספר משתמש או מספר גרסה',
 'redirect-legend' => 'הפניה לקובץ או לדף',
-'redirect-summary' => 'דף מיוחד זה מפנה לקובץ (בהינתן שם הקובץ), לדף (בהינתן מספר גרסה), או לדף משתמש (בהינתן מספר משתמש).',
+'redirect-summary' => 'דף מיוחד זה מפנה לקובץ (בהינתן שם הקובץ), לדף (בהינתן מספר גרסה), או לדף משתמש (בהינתן מספר משתמש). דוגמאות לשימוש: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/revision/328429]], או [[{{#Special:Redirect}}/user/101]].',
 'redirect-submit' => 'מעבר',
 'redirect-lookup' => 'סוג:',
 'redirect-value' => 'ערך:',
index 4019ee7..9787c39 100644 (file)
@@ -2734,12 +2734,9 @@ $1 को बाध्य करने का कारण है: "$2"',
 फिर भी, $2 प्रकार को बाध्य किया जा सकता है, जिनको अबाध्य किया जा सकता है।',
 'ip_range_invalid' => 'गलत आईपी रेंज',
 'ip_range_toolarge' => '/$1 से अधिक बड़े रेञ्ज ब्लॉकों की अनुमति नहीं है।',
-'blockme' => 'मुझे ब्लॉक करो',
 'proxyblocker' => 'प्रॉक्सी ब्लॉकर',
-'proxyblocker-disabled' => 'यह कार्य रद्द कर दिया गया हैं।',
 'proxyblockreason' => 'आपका IP पता बाधित किया जा चुका है क्योंकि यह एक मुक्त प्रतिनिधि है।
 कृपया आप अपने इंटरनेट सेवा प्रदान करने वाले से या तकनीकी सहायक से सम्पर्क करें अथवा उन्हें इस भयावह सुरक्षा समस्या के बारे में सूचित करें।',
-'proxyblocksuccess' => 'हो गया।',
 'sorbsreason' => '{{SITENAME}} द्वारा इस्तेमालमें लाये जाने वाले DNSBL में आपके आईपी एड्रेसको ओपन प्रॉक्सीमें दर्शाया गया हैं।',
 'sorbs_create_account_reason' => '{{SITENAME}} के DNSBL ने आपका आईपी एड्रेस ओपन प्रोक्सी करके सूचित किया हैं। आप खाता खोल नहीं सकतें।',
 'cant-block-while-blocked' => 'आप खुद ही अवरोधित हैं इसलिए इस समय आप औरों को अवरोधित नहीं कर सकते हैं।',
@@ -3090,6 +3087,8 @@ $1 को बाध्य करने का कारण है: "$2"',
 'spam_reverting' => '$1 को कड़ी ना होने वाले पुराने अवतरण को पुनर्स्थापित कर रहें हैं',
 'spam_blanking' => 'सभी अवतरणोंमें $1 को कड़ियां हैं, पूरा पाठ निकाल रहें हैं',
 'spam_deleting' => 'सभी अवतरणों में $1 की कड़ी थी, हटाया जा रहा है',
+'simpleantispam-label' => "ऍन्टी-स्पैम जाँच.
+इसे भरें '''नहीं'''!",
 
 # Info page
 'pageinfo-title' => '"$1" के लिये जानकारी',
index d27923c..ac292cb 100644 (file)
@@ -2558,12 +2558,9 @@ Saait iske pahile khol dewa gais hoi.',
 Lekin iske, as part of the range $2, block karaa gais hai, jiske unblock karaa jaawe sake hai.',
 'ip_range_invalid' => 'IP ke range me galti hai.',
 'ip_range_toolarge' => '/$1 se barraa range blocks ke ijajat nai hae.',
-'blockme' => 'Ham ke roko',
 'proxyblocker' => 'Proxy roke waala',
-'proxyblocker-disabled' => 'Ii function pe rukawat hai.',
 'proxyblockreason' => 'Aap ke IP address ke block kar dewa gais hai kahe ki ii ek open proxy hai.
 Meharbaani kar ke aap aapan Internet service provider, nai to tech support, ke contact kar ke ii serious security problem ke baare me batao.',
-'proxyblocksuccess' => 'Hoe gais hai.',
 'sorbsreason' => 'DNSBL used by {{SITENAME}} me aap ke IP address ke as an open proxy list karaa gais hai.',
 'sorbs_create_account_reason' => 'DNSBL used by {{SITENAME}} me aap ke IP address ke as an open proxy list karaa gais hai.
 Aap ke ek account banae ke ijajat nai hai',
index 0869055..8fb7446 100644 (file)
@@ -1443,6 +1443,7 @@ Više informacija možete pronaći u [{{fullurl:{{#Special:Log}}/delete|page={{F
 'recentchangesdays-max' => '(maksimalno $1 {{PLURAL:$1|dan|dana}})',
 'recentchangescount' => 'Zadani broj izmjena koje se prikazuju:',
 'prefs-help-recentchangescount' => 'Ovo uključuje nedavne promjene, stare izmjene, i evidencije.',
+'prefs-help-watchlist-token2' => 'Ovo je tajni ključ prema sažetku Vašeg popisa praćenja. Svaki suradnik kojem je poznat, moći će čitati Vaš popis praćenih stranica. Ne dijelite ga ni s kim. [[Special:ResetTokens|Kliknite ovdje ako ga želite ponovo postaviti]].',
 'savedprefs' => 'Vaše postavke su sačuvane.',
 'timezonelegend' => 'Vremenska zona:',
 'localtime' => 'Lokalno vrijeme:',
@@ -2590,7 +2591,7 @@ $1',
 'contributions' => 'Doprinosi {{GENDER:$1|suradnika|suradnice}}',
 'contributions-title' => 'Suradnički doprinosi za $1',
 'mycontris' => 'Moji doprinosi',
-'contribsub2' => 'Za $1 ($2)',
+'contribsub2' => 'Za {{GENDER:$3|$1}} ($2)',
 'nocontribs' => 'Nema promjena koje udovoljavaju ovim kriterijima.',
 'uctop' => '(vrh)',
 'month' => 'Od mjeseca (i ranije):',
@@ -2748,11 +2749,8 @@ Za popis trenutačnih zabrana i blokiranja vidi [[Special:BlockList|popis blokir
 'ipb_blocked_as_range' => 'Pogreška: IP adresa $1 nije blokirana direktno te stoga ne može biti odblokirana. Blokirana je kao dio opsega $2, koji može biti odblokiran.',
 'ip_range_invalid' => 'Raspon IP adresa nije valjan.',
 'ip_range_toolarge' => 'Opsezi blokiranja veći od /$1 nisu dozvoljeni.',
-'blockme' => 'Blokiraj me',
 'proxyblocker' => 'Zaštita od otvorenih posrednika (proxyja)',
-'proxyblocker-disabled' => 'Ova funkcija je onemogućena.',
 'proxyblockreason' => 'Vaša je IP adresa blokirana jer se radi o otvorenom posredniku (proxyju). Molimo stupite u vezu s Vašim davateljem internetskih usluga (ISP-om) ili službom tehničke podrške i obavijestite ih o ovom ozbiljnom sigurnosnom problemu.',
-'proxyblocksuccess' => 'Napravljeno.',
 'sorbsreason' => 'Vaša IP adresa je na popisu otvorenih posrednika na poslužitelju DNSBL.',
 'sorbs_create_account_reason' => 'Vaša IP adresa je na popisu otvorenih posrednika na poslužitelju DNSBL. Ne možete otvoriti račun.',
 'cant-block-while-blocked' => 'Ne možete blokirati druge suradnike dok ste blokirani.',
@@ -3094,6 +3092,8 @@ Razlog je vjerojatno vanjska poveznica koja se nalazi na crnom popisu.',
 'spam_reverting' => 'Vraćam na zadnju inačicu koja ne sadrži poveznice na $1',
 'spam_blanking' => 'Sve inačice sadrže poveznice na $1, brišem cjelokupni sadržaj',
 'spam_deleting' => 'Sve inačice sadržale su poveznice na $1, brišem cjelokupni sadržaj',
+'simpleantispam-label' => "Anti-spam provjera.
+'''Ne''' ispunjavajte ovo!",
 
 # Info page
 'pageinfo-title' => 'Podatci o stranici "$1"',
@@ -3958,6 +3958,7 @@ Trebali ste primiti [{{SERVER}}{{SCRIPTPATH}}/COPYING kopiju GNU opće javne lic
 'tags' => 'Valjane oznake izmjena',
 'tag-filter' => 'Filtar [[Special:Tags|oznaka]]:',
 'tag-filter-submit' => 'Filtar',
+'tag-list-wrapper' => '([[Special:Tags|{{PLURAL:$1|Oznaka|Oznake}}]]: $2)',
 'tags-title' => 'Oznake',
 'tags-intro' => 'Ova je stranica popis oznaka s kojima softver može označiti promjenu te njihovo značenje.',
 'tags-tag' => 'Naziv oznake',
index f362223..42eeeb5 100644 (file)
@@ -2569,11 +2569,8 @@ Hlej [[Special:BlockList|lisćinu blokowanjow]], zo by zablokowanjow pruwował.'
 'ipb_blocked_as_range' => 'Zmylk: IP $1 njeje direktnje zablokowana a njeda so wublokować. Blokuje so wšak jako dźěl wobwoda $2, kotryž da so wublokować.',
 'ip_range_invalid' => 'Njepłaciwy wobłuk IP-adresow.',
 'ip_range_toolarge' => 'Wobłukowe bloki, kotrež su wjetše hač /$1, njejsu dowolene.',
-'blockme' => 'Blokować',
 'proxyblocker' => 'Awtomatiske blokowanje wotewrjenych proksy-serwerow',
-'proxyblocker-disabled' => 'Tuta funkcija je deaktiwizowana.',
 'proxyblockreason' => 'Twoja IP-adresa bu zablokowana, dokelž je wotewrjeny proksy. Prošu skontaktuj swojeho prowidera abo syćoweho administratora a informuj jeho wo tutym chutnym wěstotnym problemje.',
-'proxyblocksuccess' => 'Dokónčene.',
 'sorbs' => 'SORBS DNSbl',
 'sorbsreason' => 'Twoja IP-adresa je jako wotewrjeny proksy na DNSBL {{GRAMMAR:genitiw|{{SITENAME}}}} zapisana.',
 'sorbs_create_account_reason' => 'Twoja IP-adresa je jako wotewrjeny proksy na DNSBL {{GRAMMAR:genitiw|{{SITENAME}}}} zapisana. Njemóžeš konto wutworić.',
@@ -2904,6 +2901,8 @@ $2',
 'spam_reverting' => 'wróćo na poslednju wersiju, kotraž wotkazy na $1 njewobsahuje',
 'spam_blanking' => 'Wšě wersije z wotkazami do $1 so porjedźeja',
 'spam_deleting' => 'Wšě wersije z wotkazami do $1 so zhašeja',
+'simpleantispam-label' => "Kontrola přećiwo spamej.
+Tu '''ničo''' njezapisać!",
 
 # Info page
 'pageinfo-title' => 'Informacije za stronu "$1"',
index a6e681f..0795f8f 100644 (file)
@@ -2746,11 +2746,8 @@ Add meg a blokkolás okát is (például idézd a blokkolandó személy által v
 'ipb_blocked_as_range' => 'Hiba: a(z) $1 IP-cím nem blokkolható közvetlenül, és nem lehet feloldani. A(z) $2 tartomány részeként van blokkolva, amely feloldható.',
 'ip_range_invalid' => 'Érvénytelen IP-tartomány.',
 'ip_range_toolarge' => 'Nem engedélyezettek azok a tartományblokkok, melyek nagyobbak mint /$1.',
-'blockme' => 'Saját magam blokkolása',
 'proxyblocker' => 'Proxyblokkoló',
-'proxyblocker-disabled' => 'Ez a funkció le van tiltva.',
 'proxyblockreason' => "Az IP-címeden ''nyílt proxy'' üzemel. Amennyiben nem használsz proxyt, vedd fel a kapcsolatot egy informatikussal vagy az internetszolgáltatóddal ezen súlyos biztonsági probléma ügyében.",
-'proxyblocksuccess' => 'Kész.',
 'sorbsreason' => 'Az IP-címed nyitott proxyként szerepel e webhely által használt DNSBL listán.',
 'sorbs_create_account_reason' => 'Az IP-címed nyitott proxyként szerepel e webhely által használt DNSBL listán. Nem hozhatsz létre fiókot.',
 'cant-block-while-blocked' => 'Nem blokkolhatsz más szerkesztőket, miközben te magad blokkolva vagy.',
@@ -3116,6 +3113,8 @@ Ez valószínűleg egy olyan link miatt van, ami egy feketelistán lévő oldalr
 'spam_reverting' => 'Visszatérés a $1 lapra mutató hivatkozásokat nem tartalmazó utolsó változathoz',
 'spam_blanking' => 'Az összes változat tartalmazott a $1 lapra mutató hivatkozásokat, kiürítés',
 'spam_deleting' => 'Minden változat tartalmazott $1-re mutató hivatkozást, törlöm',
+'simpleantispam-label' => "Spam elleni ellenőrzés.
+'''NE''' töltsd ezt ki!",
 
 # Info page
 'pageinfo-title' => 'Információk a(z) „$1” lapról',
index e358141..aa7b289 100644 (file)
@@ -2163,7 +2163,6 @@ $1',
 'ip_range_invalid' => 'IP-հասցեների անթույլատրելի լայնույթ։',
 'proxyblocker' => 'Փոխանորդի արգելափակում',
 'proxyblockreason' => 'Ձեր IP-հասցեն արգելափակվել է, քանի որ այն պատկանում է հանրային միջնորդ (պրոքսի) սեռվերին։ Խնդրում ենք կապվել ձեր ցանցային կամ տեխնիկական ծառայության տրամադրողի հետ և տեղեկացնել այս լուրջ անվտանգության խնդրի մասին։',
-'proxyblocksuccess' => 'Արված է։',
 'sorbsreason' => 'Ձեր IP-հասցեն հաշվված է որպես ազատ օգտագործման փոխանորդ DNSBL ցանկում։',
 'sorbs_create_account_reason' => 'Ձեր IP-հասցեն հաշվված է որպես ազատ օգտագործման փոխանորդ DNSBL ցանկում։ Դուք չեք կարող ստեղծել մասնակցային հաշիվ։',
 'ipbnounblockself' => 'Դուք չեք կարող արգելափակել ինքներդ ձեզ',
index cb7b4d2..54d9372 100644 (file)
@@ -362,7 +362,7 @@ $messages = array(
 'articlepage' => 'Vider pagina de contento',
 'talk' => 'Discussion',
 'views' => 'Representationes',
-'toolbox' => 'Instrumentario',
+'toolbox' => 'Instrumentos',
 'userpage' => 'Vider pagina del usator',
 'projectpage' => 'Vider pagina de projecto',
 'imagepage' => 'Vider le pagina del file',
@@ -674,8 +674,8 @@ e continuar a usar le contrasigno original.',
 'passwordsent' => 'Un nove contrasigno ha essite inviate al adresse de e-mail registrate pro "$1".
 Per favor aperi session de novo post reciper lo.',
 'blocked-mailpassword' => 'Tu adresse IP es blocate de facer modificationes, e pro impedir le abuso, le uso del function pro recuperar contrasignos es equalmente blocate.',
-'eauthentsent' => 'Un e-mail de confirmation ha essite inviate al adresse de e-mail specificate.
-Pro poter reciper altere e-mail a iste conto, tu debe sequer le instructiones in iste e-mail pro confirmar que le conto es realmente tue.',
+'eauthentsent' => 'Un message de confirmation ha essite inviate al adresse de e-mail specificate.
+Pro permitter que le systema invia altere messages a iste adresse, tu debe sequer le instructiones in iste message pro confirmar que le adresse es realmente tue.',
 'throttled-mailpassword' => 'Un message pro le reinitialisation del contrasigno ha jam essite inviate intra le ultime {{PLURAL:$1|hora|$1 horas}}.
 Pro prevenir le abuso, solmente un message pro le reinitialisation del contrasigno essera inviate per {{PLURAL:$1|hora|$1 horas}}.',
 'mailerror' => 'Error de inviar e-mail: $1',
@@ -1139,15 +1139,15 @@ Altere administratores in {{SITENAME}} continuara a poter acceder al contento ce
 * Informationes personal inappropriate
 *: ''adresses de domicilio e numeros de telephono, numeros de securitate social, etc.''",
 'revdelete-legend' => 'Definir restrictiones de visibilitate',
-'revdelete-hide-text' => 'Celar le texto del version',
+'revdelete-hide-text' => 'Texto del version',
 'revdelete-hide-image' => 'Celar le contento del file',
 'revdelete-hide-name' => 'Celar action e objectivo',
-'revdelete-hide-comment' => 'Celar le summario del modification',
-'revdelete-hide-user' => 'Celar le nomine de usator o adresse IP del modificator',
+'revdelete-hide-comment' => 'Summario del modification',
+'revdelete-hide-user' => 'Nomine de usator o adresse IP del modificator',
 'revdelete-hide-restricted' => 'Supprimer le datos a administratores assi como a alteres',
 'revdelete-radio-same' => '(non cambiar)',
-'revdelete-radio-set' => 'Si',
-'revdelete-radio-unset' => 'No',
+'revdelete-radio-set' => 'Visibile',
+'revdelete-radio-unset' => 'Celate',
 'revdelete-suppress' => 'Supprimer le datos a administratores assi como a alteres',
 'revdelete-unsuppress' => 'Eliminar restrictiones super versiones restaurate',
 'revdelete-log' => 'Motivo:',
@@ -2392,10 +2392,12 @@ Tote le horas es in le fuso horari del servitor.',
 'deletecomment' => 'Motivo:',
 'deleteotherreason' => 'Motivo altere/additional:',
 'deletereasonotherlist' => 'Altere motivo',
-'deletereason-dropdown' => '*Motivos habitual pro deler paginas
-** Requesta del autor
+'deletereason-dropdown' => '*Motivos commun pro deler
+** Spam
+** Vandalismo
 ** Violation de copyright
-** Vandalismo',
+** Requesta del autor
+** Redirection rupte',
 'delete-edit-reasonlist' => 'Modificar le motivos pro deletion',
 'delete-toobig' => 'Iste pagina ha un grande historia de modificationes con plus de $1 {{PLURAL:$1|version|versiones}}.
 Le deletion de tal paginas ha essite restringite pro impedir le disruption accidental de {{SITENAME}}.',
@@ -2725,12 +2727,9 @@ Vide le [[Special:BlockList|lista de blocadas]] pro le lista de bannimentos e bl
 Illo es, nonobstante, blocate como parte del intervallo $2, le qual pote esser disblocate.',
 'ip_range_invalid' => 'Intervallo de adresses IP invalide.',
 'ip_range_toolarge' => 'Non es permittite blocar un gamma de adresses IP plus grande que /$1.',
-'blockme' => 'Blocar me',
 'proxyblocker' => 'Blocator de proxy',
-'proxyblocker-disabled' => 'Iste function es disactivate.',
 'proxyblockreason' => 'Tu adresse IP ha essite blocate proque illo es un proxy aperte.
 Per favor contacta tu providitor de servicio internet o supporto technic e informa les de iste problema grave de securitate.',
-'proxyblocksuccess' => 'Succedite.',
 'sorbsreason' => 'Tu adresse IP es listate como proxy aperte in le DNSBL usate per {{SITENAME}}.',
 'sorbs_create_account_reason' => 'Tu adresse IP es listate como proxy aperte in le DNSBL usate per {{SITENAME}}.
 Tu non pote crear un conto',
@@ -3092,6 +3091,8 @@ Le causa es probabilemente un ligamine verso un sito externe que es presente in
 'spam_reverting' => 'Revertite al ultime version que non contine ligamines a $1',
 'spam_blanking' => 'Tote le versiones contineva ligamines a $1. Le pagina es vacuate.',
 'spam_deleting' => 'Tote le versiones contineva ligamines a $1. Le pagina es delite.',
+'simpleantispam-label' => "Verification anti-spam.
+'''NON''' completa isto!",
 
 # Info page
 'pageinfo-title' => 'Informationes pro "$1"',
@@ -3867,7 +3868,7 @@ Vos deberea haber recipite [{{SERVER}}{{SCRIPTPATH}}/COPYING un exemplar del Lic
 # Special:Redirect
 'redirect' => 'Rediriger per nomine de file, ID de usator o ID de version',
 'redirect-legend' => 'Rediriger a un file o pagina',
-'redirect-summary' => 'Iste pagina special redirige a un file (si es date le nomine de un file), a un pagina (si es date un ID de version) o a un pagina de usator (si es date un ID de usator numeric).',
+'redirect-summary' => 'Iste pagina special redirige a un file (si es date le nomine de un file), a un pagina (si es date un ID de version) o a un pagina de usator (si es date un ID de usator numeric). Usage: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/revision/328429]] o [[{{#Special:Redirect}}/user/101]].',
 'redirect-submit' => 'Va',
 'redirect-lookup' => 'Cercar:',
 'redirect-value' => 'Valor:',
index b070bde..dd1a209 100644 (file)
@@ -481,8 +481,6 @@ $messages = array(
 'broken-file-category' => 'Halaman dengan gambar rusak',
 'categoryviewer-pagedlinks' => '($1) ($2)',
 
-'linkprefix' => '/^(.*?)([a-zA-Z\\x80-\\xff]+)$/sD',
-
 'about' => 'Tentang',
 'article' => 'Halaman isi',
 'newwindow' => '(buka di jendela baru)',
@@ -2856,11 +2854,8 @@ Lihat [[Special:BlockList|daftar pemblokiran]] untuk semua pengguna yang saat in
 'ipb_blocked_as_range' => 'Kesalahan: IP $1 tidak diblok secara langsung dan tidak dapat dilepaskan. IP $1 diblok sebagai bagian dari pemblokiran kelompok IP $2, yang dapat dilepaskan.',
 'ip_range_invalid' => 'Blok IP tidak sah.',
 'ip_range_toolarge' => 'Rentang blok lebih besar dari /$1 tidak diperbolehkan.',
-'blockme' => 'Blokir saya',
 'proxyblocker' => 'Pemblokir proxy',
-'proxyblocker-disabled' => 'Fitur ini sedang tidak diakfifkan.',
 'proxyblockreason' => 'Alamat IP Anda telah diblokir karena alamat IP Anda adalah proxy terbuka. Silakan hubungi penyedia jasa internet Anda atau dukungan teknis dan beritahukan mereka masalah keamanan serius ini.',
-'proxyblocksuccess' => 'Selesai.',
 'sorbs' => 'DNSBL',
 'sorbsreason' => 'Alamat IP anda terdaftar sebagai proxy terbuka di DNSBL.',
 'sorbs_create_account_reason' => 'Alamat IP anda terdaftar sebagai proxy terbuka di DNSBL. Anda tidak dapat membuat akun.',
@@ -3221,6 +3216,8 @@ Ini mungkin disebabkan oleh pranala ke situs luar yang termasuk dalam daftar hit
 'spam_reverting' => 'Membatalkan ke versi terakhir yang tak memiliki pranala ke $1',
 'spam_blanking' => 'Semua revisi yang memiliki pranala ke $1, kosong',
 'spam_deleting' => 'Semua revisi yang memiliki pranala ke $1, penghapusan',
+'simpleantispam-label' => "Pemeriksaan anti-spam.
+Masukan ini '''DILARANG'''!",
 
 # Info page
 'pageinfo-title' => 'Informasi untuk "$1"',
index 2d5a126..d80520c 100644 (file)
@@ -1285,8 +1285,6 @@ Ngá bu ihe hé mèkwàrà nà ihü '''$1''':",
 'unblocklogentry' => 'àkwáchị gị $1',
 'block-log-flags-nocreate' => "Í ké ọ'bànifé bàchìrì",
 'block-log-flags-noemail' => 'ha kwàchịrị e-mail',
-'blockme' => 'Kwàchím',
-'proxyblocksuccess' => 'Ọméchá.',
 
 # Developer tools
 'lockdb' => 'Gbàchí uche nsónùsòrò',
index d87b481..1d502cb 100644 (file)
@@ -252,7 +252,7 @@ $messages = array(
 'articlepage' => 'Kitaen ti naglaon a panid',
 'talk' => 'Pagtungtungan',
 'views' => 'Dagiti pangkitaan',
-'toolbox' => 'Kahon ti ramit',
+'toolbox' => 'Ramramit',
 'userpage' => 'Kitaen ti panid ti agar-aramat',
 'projectpage' => 'Kitaen ti panid ti gandat',
 'imagepage' => 'Kitaen ti panid ti papeles',
@@ -498,6 +498,9 @@ Dimo liplipatan a sukatan dagiti kakaykayatam idiay [[Special:Preferences|{{SITE
 'userlogin-resetpassword-link' => 'Iyasentar manen ti kontrasenias',
 'helplogin-url' => 'Help:Panagserrek',
 'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|Tulong iti panagserrek]]',
+'userlogin-loggedin' => 'Nakastrekkan a kas ni {{GENDER:$1|$1}}.
+Usaren ti porma dita baba tapno sumrek a kas sabali nga agar-aramat.',
+'userlogin-createanother' => 'Agaramid pay ti sabali a pakabilangan',
 'createacct-join' => 'Ikabil ti pakaammom dita baba.',
 'createacct-another-join' => 'Ikabil ti pakaammo ti baro a pakabilangan dita baba.',
 'createacct-emailrequired' => 'Esurat a pagtaengan',
@@ -2265,10 +2268,12 @@ Kitaen ti $2 para iti pannakrehistro dagiti naudi a naikkat.',
 'deletecomment' => 'Rason:',
 'deleteotherreason' => 'Sabali/maipatinayon a rason:',
 'deletereasonotherlist' => 'Sabali a rason',
-'deletereason-dropdown' => '*Kadawyan a rasrason ti panagikkat
-** Kiddaw ti mannurat
+'deletereason-dropdown' => '* Kadawyan a rasrason ti panagikkat
+** Spam
+** Bandalismo
 ** Panaglabsing iti karbengan ti panagipablaak
-** Bandalismo',
+** Kiddaw ti mannurat
+** Naputed a baw-ing',
 'delete-edit-reasonlist' => 'Urnosen dagiti rason ti panagikkat',
 'delete-toobig' => 'Daytoy a panid ket dakkel ti pakasaritaanna, sumurok a  $1 {{PLURAL:a panagbaliwan|dagiti panagbaliwan}}.
 Ti panagikkat ti kastoy a pammpanid ket naparitan tapno mapawilan ti saan nga inkarkaro a pannakadadael ti {{SITENAME}}.',
@@ -2596,12 +2601,9 @@ Kitaen ti [[Special:BlockList|Listaan ti lapden nga IP]] para iti listaan kadagi
 Ngem, nupay kasta, naserran a kas paset ti sakup ti $2, a mabalin a malukatan ti serrana.',
 'ip_range_invalid' => 'Imbalido a sakup ti IP.',
 'ip_range_toolarge' => 'Dagiti serra a nasakup a dakdakkel ngem /$1 ket saan a maipalubos.',
-'blockme' => 'Serraannak',
 'proxyblocker' => 'Pannakbagi a panagserra',
-'proxyblocker-disabled' => 'Daytoy a panagaramid ket nabaldado.',
 'proxyblockreason' => 'Ti IP a pagtaengam ket naserraan ngamin ket daytoy ket nakalukat a panakbagi.
 Pangngaasi ta kontakem ti agit-ited ti serbisio ti Internetmo wenno teknikal a suporta ti kaurnusam ken ibagam kaniada ti nakaro a parikut ti seguridad.',
-'proxyblocksuccess' => 'Nalpasen.',
 'sorbsreason' => 'Ti IP a pagtaengam ket nakalista a kasla "nalukatan a pannakbagi" idiay DNSBL nga inusar ti {{SITNAME}}.',
 'sorbs_create_account_reason' => 'Ti IP a pagtaengam ket nakalista a kasla "nalukatan a pannakbagi" idiay DNSBL nga inusar ti {{SITNAME}}.
 Saanka a makaaramid ti pakabilangan',
@@ -2948,6 +2950,8 @@ Daytoy ket mabalin a gapuanan babaen ti silpo a naiparit ti akin ruar a pagsaada
 'spam_reverting' => 'Ipasubli ti kinaudi a panagbaliw nga awan dagiti linaon a silpo idiay $1',
 'spam_blanking' => 'Dagiti amin a panagbaliw ket aglaon kadagiti silpo idiay $1, iblanko',
 'spam_deleting' => 'Dagiti amin a panagbaliw ket naglaon kadagiti silpo idiay $1, ik-ikkaten',
+'simpleantispam-label' => "Kontra-spam a panagkita.
+ '''Saan''' mo a suratan daytoy!",
 
 # Info page
 'pageinfo-title' => 'Pakaammo para iti "$1"',
index 7ef3b09..0626ea7 100644 (file)
@@ -182,8 +182,8 @@ $messages = array(
 'help' => 'Куцтохкам',
 'search' => 'Лахаp',
 'searchbutton' => 'Хьалаха',
-'go' => 'Дехьавала',
-'searcharticle' => 'Дехьавала',
+'go' => 'Дехьа гӀо',
+'searcharticle' => 'Дехьа гӀо',
 'history' => 'искар',
 'history_short' => 'Искар',
 'updatedmarker' => 'Со ханача денца хувцамаш хиннaд',
@@ -228,7 +228,7 @@ $messages = array(
 'lastmodifiedat' => 'Укх оагӀув тӀехьара  хувцам: $2, $1.',
 'viewcount' => 'Укх оагӀув тӀа бӀаргтасса хиннад {{PLURAL:$1|цхьазза|$1 шозза}}.',
 'protectedpage' => 'Лорама оагӀув',
-'jumpto' => 'Укхаза дехьавала/яла:',
+'jumpto' => 'Укхаза дехьа гӀо:',
 'jumptonavigation' => 'никътохкарг',
 'jumptosearch' => 'леха',
 'pool-timeout' => 'ЧIегатохара сабаран ха чакхаяьннай',
@@ -909,8 +909,6 @@ $messages = array(
 'blocklogentry' => '[[$1]] чIега белаб,  $2 $3 ха ялалца',
 'unblocklogentry' => '$1 юха яста я',
 'block-log-flags-nocreate' => 'ЛархIамий дагарчена цIи яьккхар пурам янза я.',
-'blockme' => 'ЧIега бола сона',
-'proxyblocksuccess' => 'Хьадаьд.',
 
 # Move page
 'move-page-legend' => 'ОагIува цIи хувца',
@@ -956,7 +954,7 @@ $messages = array(
 'allmessagesdefault' => 'Сатийна улла яздам',
 'allmessages-filter-all' => 'Дерригаш',
 'allmessages-language' => 'Мотт:',
-'allmessages-filter-submit' => 'Дехьавала/яла',
+'allmessages-filter-submit' => 'Дехьа гӀо',
 
 # Thumbnails
 'thumbnail-more' => 'Хьадоккхаде',
@@ -1102,7 +1100,7 @@ $messages = array(
 'confirm_purge_button' => 'ХIаа',
 
 # Multipage image navigation
-'imgmultigo' => 'Дехьавала/яла!',
+'imgmultigo' => 'Дехьа гӀо!',
 'imgmultigoto' => '$1 оагIув тIа дехьавала',
 
 # Table pager
index e93d0c2..599890c 100644 (file)
@@ -1246,7 +1246,6 @@ Videz [[Special:BlockList|IP-blokuslisto]] por revizor blokusadi.',
 'unblocklogentry' => 'desblokusis "$1"',
 'ipb_expiry_invalid' => 'Nevalida expiro-tempo.',
 'ip_range_invalid' => 'Nevalida IP-rango.',
-'proxyblocksuccess' => 'Facita.',
 
 # Developer tools
 'lockdb' => 'Blokusar datumaro',
index 2eb6224..54f931c 100644 (file)
@@ -231,6 +231,7 @@ $specialPageAliases = array(
 $separatorTransformTable = array( ',' => '.', '.' => ',' );
 $linkPrefixExtension = true;
 $linkTrail = '/^([áðéíóúýþæöa-z-–]+)(.*)$/sDu';
+$linkPrefixCharset = 'áÁðÐéÉíÍóÓúÚýÝþÞæÆöÖA-Za-z–-';
 
 $messages = array(
 # User preference toggles
@@ -371,14 +372,12 @@ $messages = array(
 'noindex-category' => 'Óraðaðar skrár',
 'broken-file-category' => 'Síður með brotna myndatengla',
 
-'linkprefix' => '/^(.*?)([áÁðÐéÉíÍóÓúÚýÝþÞæÆöÖA-Za-z-–]+)$/sDu',
-
 'about' => 'Um',
 'article' => 'Efnissíða',
 'newwindow' => '(opnast í nýjum glugga)',
 'cancel' => 'Hætta við',
 'moredotdotdot' => 'Meira...',
-'morenotlisted' => 'fleiri ekki skráð...',
+'morenotlisted' => 'Þessi listi er ekki tæmandi.',
 'mypage' => 'Síða',
 'mytalk' => 'Spjall',
 'anontalk' => 'Spjallsíða þessa vistfangs.',
@@ -481,7 +480,7 @@ $1',
 # All link text and link target definitions of links into project namespace that get used by other message strings, with the exception of user group pages (see grouppage).
 'aboutsite' => 'Um {{SITENAME}}',
 'aboutpage' => 'Project:Um verkefnið',
-'copyright' => 'Efni má nota samkvæmt $1.',
+'copyright' => 'Efni má nota samkvæmt $1 nema kemur fram annars.',
 'copyrightpage' => '{{ns:project}}:Höfundarréttur',
 'currentevents' => 'Potturinn',
 'currentevents-url' => 'Project:Potturinn',
@@ -563,6 +562,7 @@ Sjá [[Special:Version|útgáfusíðuna]].',
 # General errors
 'error' => 'Villa',
 'databaseerror' => 'Gagnagrunnsvilla',
+'databaseerror-error' => 'Villa: $1',
 'laggedslavemode' => 'Viðvörun: Síðan inniheldur ekki nýjustu uppfærslur.',
 'readonly' => 'Gagnagrunnur læstur',
 'enterlockreason' => 'Gefðu fram ástæðu fyrir læsingunni, og einnig áætlun
@@ -651,6 +651,7 @@ Ekki gleyma að breyta [[Special:Preferences|{{SITENAME}} stillingunum]] þínum
 'yourname' => 'Notandanafn:',
 'userlogin-yourname' => 'Notandanafn',
 'userlogin-yourname-ph' => 'Skrifaðu inn notendanafnið þitt',
+'createacct-another-username-ph' => 'Skrifaðu inn notendanafnið',
 'yourpassword' => 'Lykilorð:',
 'userlogin-yourpassword' => 'Lykilorð',
 'userlogin-yourpassword-ph' => 'Skrifaðu niður lykilorðið þitt',
@@ -683,10 +684,15 @@ Ekki gleyma að breyta [[Special:Preferences|{{SITENAME}} stillingunum]] þínum
 'userlogin-resetpassword-link' => 'Endursetja lykilorð',
 'helplogin-url' => 'Help:Innskráning',
 'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|Hjálp við innskráningu]]',
+'userlogin-loggedin' => 'Þú ert búin(n) að skrá þig inn sem {{GENDER:$1|$1}}.
+Notaðu eyðablaðið fyrir neðan til að skrá þig inn sem annar notandi.',
+'userlogin-createanother' => 'Stofna annan aðgang',
 'createacct-join' => 'Sláðu inn þínar upplýsingar fyrir neðan.',
+'createacct-another-join' => 'Skrifaðu upplýsingar um nýja aðganginn fyrir neðan.',
 'createacct-emailrequired' => 'Netfang',
 'createacct-emailoptional' => 'Netfang (valfrjálst)',
 'createacct-email-ph' => 'Skrifaðu niður netfangið þitt',
+'createacct-another-email-ph' => 'Skrifaðu netfang',
 'createaccountmail' => 'Nota handahófsvalið bráðabirgðalykilorð og senda það á netfangið sem er tilgreint hér fyrir neðan',
 'createacct-realname' => 'Raunverulegt nafn (valfrjálst)',
 'createaccountreason' => 'Ástæða:',
@@ -695,6 +701,7 @@ Ekki gleyma að breyta [[Special:Preferences|{{SITENAME}} stillingunum]] þínum
 'createacct-captcha' => 'Öryggis athugun',
 'createacct-imgcaptcha-ph' => 'Sláðu inn textann að ofan',
 'createacct-submit' => 'Búa til aðganginn',
+'createacct-another-submit' => 'Stofna annan aðgang',
 'createacct-benefit-heading' => '{{SITENAME}} er skrifuð af fólki eins og þér.',
 'createacct-benefit-body1' => '{{PLURAL:$1|breyting|breytingar}}',
 'createacct-benefit-body2' => '{{PLURAL:$1|síða|síður}}',
@@ -764,10 +771,11 @@ Gjörðu svo vel og settu inn netfang á gildu formi eða tæmdu reitinn.',
 Þú getur hunsað þessi skilaboð, ef villa hefur átt sér stað.',
 'usernamehasherror' => 'Notendanöfn mega ekki innihalda kassa (#)',
 'login-throttled' => 'Þér hefur mistekist að skrá þig inn undir þessu notendanafni of oft.
-Vinsamlegast reynið aftur síðar.',
+Vinsamlegast bíðið $1 áður en þú reynir aftur.',
 'login-abort-generic' => 'Innskráningin misheppnaðist - hætt var við hana.',
 'loginlanguagelabel' => 'Tungumál: $1',
 'suspicious-userlogout' => 'Beiðni um útskráningu hafnað því hún var líklegast send frá biluðum vafra eða vefseli sem hefur vistað vefsíðuna í flýtiminni.',
+'createacct-another-realname-tip' => 'Alvöru nafn er valfrjálst. Ef þú kýst að gefa það upp, verður það notað til að gefa þér heiður af verkum þínum.',
 
 # Email sending
 'php-mail-error-unknown' => 'Óþekkt villa í PHP mail() aðgerð.',
@@ -784,7 +792,7 @@ Til að klára að skrá þig inn, verður þú að endurstilla lykilorðið hé
 'newpassword' => 'Nýja lykilorðið',
 'retypenew' => 'Endurtaktu nýja lykilorðið:',
 'resetpass_submit' => 'Skrifaðu aðgangsorðið og skráðu þig inn',
-'changepassword-success' => 'Aðgangsorðinu þínu hefur verið breytt! Skráir þig inn...',
+'changepassword-success' => 'Það tókst að breyta lykilorðinu þínu!',
 'resetpass_forbidden' => 'Ekki er hægt að breyta lykilorðum',
 'resetpass-no-info' => 'Þú verður að vera skráð(ur) inn til að hafa aðgang að þessari síðu.',
 'resetpass-submit-loggedin' => 'Breyta lykilorði',
@@ -839,6 +847,18 @@ Tímabundið lykilorð: $2',
 'changeemail-submit' => 'Breyta netfangi',
 'changeemail-cancel' => 'Hætta við',
 
+# Special:ResetTokens
+'resettokens' => 'Endurstilla lykla',
+'resettokens-text' => 'Hér getur þú endurstillt lykla sem veita þér aðgang að ákveðnum persónuupplýsingum um aðganginn þinn.
+
+Þú átt að gera það ef þú ert búin(n) að deila þeim með einhverjum öðrum óviljandi eða ef búið er að brjóta inn í aðganginn þinn.',
+'resettokens-no-tokens' => 'Það eru engir lyklar að endurstilla.',
+'resettokens-legend' => 'Endurstilla lykla',
+'resettokens-tokens' => 'Lyklar:',
+'resettokens-token-label' => '$1 (núverandi gildi: $2)',
+'resettokens-done' => 'Lyklarnir hafa verið endurstilltir.',
+'resettokens-resetbutton' => 'Endurstilla valda lykla',
+
 # Edit page toolbar
 'bold_sample' => 'Feitletraður texti',
 'bold_tip' => 'Feitletraður texti',
@@ -1018,7 +1038,7 @@ Verndunarskrá síðunnar er gefin fyrir neðan til tilvísunar.",
 'nocreate-loggedin' => 'Þú hefur ekki leyfi til að skapa nýjar síður.',
 'sectioneditnotsupported-title' => 'Hlutabreyting er ekki virk',
 'sectioneditnotsupported-text' => 'Hlutabreyting er ekki virk á þessari síðu.',
-'permissionserrors' => 'Leyfisvillur',
+'permissionserrors' => 'Leyfisvilla',
 'permissionserrorstext' => 'Þú hefur ekki leyfi til að gera þetta, af eftirfarandi {{PLURAL:$1|ástæðu|ástæðum}}:',
 'permissionserrorstext-withaction' => 'Þú hefur ekki réttindi til að $2, af eftirfarandi {{PLURAL:$1|ástæðu|ástæðum}}:',
 'recreate-moveddeleted-warn' => "'''Viðvörun: Þú ert að endurskapa síðu sem áður hefur verið eytt.'''
@@ -1077,6 +1097,7 @@ Hluti sniðsins verður ekki með.",
 'undo-failure' => 'Breytinguna var ekki hægt að taka tilbaka vegna breytinga í millitíðinni.',
 'undo-norev' => 'Ekki var hægt að taka breytinguna aftr því að hún er ekki til eða henni var eytt.',
 'undo-summary' => 'Tek aftur breytingu $1 frá [[Special:Contributions/$2|$2]] ([[User talk:$2|spjall]])',
+'undo-summary-username-hidden' => 'Afturkalla breytingu $1 eftir faldan notanda',
 
 # Account creation failure
 'cantcreateaccounttitle' => 'Ekki hægt að búa til aðgang',
@@ -1105,7 +1126,7 @@ Skýringartexti: (nú) = skoðanamunur á núverandi útgáfu,
 'history-fieldset-title' => 'Skoða breytingaskrá',
 'history-show-deleted' => 'Eingöngu eyddar breytingar',
 'histfirst' => 'elstu',
-'histlast' => 'yngstu',
+'histlast' => 'nýjustu',
 'historysize' => '({{PLURAL:$1|1 bæti|$1 bæti}})',
 'historyempty' => '(tóm)',
 
@@ -1169,15 +1190,15 @@ Frekari upplýsingar eru í [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGE
 * Óviðeigandi persónulegar upplýsingar
 *: ''heimilisfang, símanúmer, kennitala, osfrv.''",
 'revdelete-legend' => 'Setja sjáanlegar hamlanir',
-'revdelete-hide-text' => 'Fela breytingatexta',
+'revdelete-hide-text' => 'Breytingatexti',
 'revdelete-hide-image' => 'Fela efni skráar',
 'revdelete-hide-name' => 'Fela aðgerð og mark',
-'revdelete-hide-comment' => 'Fela breytingarágrip',
-'revdelete-hide-user' => 'Fela notandanafn/vistfang',
+'revdelete-hide-comment' => 'Breytingarágrip',
+'revdelete-hide-user' => 'Notandanafn/vistfang',
 'revdelete-hide-restricted' => 'Dylja gögn frá stjórnendum og öðrum',
 'revdelete-radio-same' => '(ekki breyta)',
-'revdelete-radio-set' => '',
-'revdelete-radio-unset' => 'Nei',
+'revdelete-radio-set' => 'Sjáanlegt',
+'revdelete-radio-unset' => 'Falið',
 'revdelete-suppress' => 'Dylja gögn frá stjórnendum og öðrum',
 'revdelete-unsuppress' => 'Fjarlægja takmarkanir á endurvöktum breytingum',
 'revdelete-log' => 'Ástæða:',
@@ -1259,6 +1280,7 @@ Athugaðu að með því að nota flakktenglana er þessi dálkur endurstilltur.
 'compareselectedversions' => 'Bera saman valdar útgáfur',
 'showhideselectedversions' => 'Sýna/fela valdar breytingar',
 'editundo' => 'Taka aftur þessa breytingu',
+'diff-empty' => '(Enginn munur)',
 'diff-multi' => '({{PLURAL:$1|Ein millibreyting ekki sýnd|$1 millibreytingar ekki sýndar}} frá {{PLURAL:$2|notanda|$2 notendum}}.)',
 'diff-multi-manyusers' => '({{PLURAL:$1|Ein millibreyting ekki sýnd|$1 millibreytingar ekki sýndar}} frá fleiri en {{PLURAL:$2|einum notanda|$2 notendum}}.)',
 'difference-missing-revision' => '{{PLURAL:$2|Ein útgáfa|$2 útgáfur}} samanburðarins ($1) {{PLURAL:$2|fannst|fundust}} ekki.
@@ -1360,7 +1382,7 @@ Athugaðu að skrár þeirra yfir {{SITENAME}}-efni kunna að vera úreltar.',
 'prefs-rendering' => 'Útlit',
 'saveprefs' => 'Vista',
 'resetprefs' => 'Endurstilla valmöguleika',
-'restoreprefs' => 'Endurheimta allar stillingar',
+'restoreprefs' => 'Endurstilla allar sjálfgefnar stillingar (í öllum hlutum)',
 'prefs-editing' => 'Breytingarflipinn',
 'rows' => 'Raðir',
 'columns' => 'Dálkar',
@@ -1416,10 +1438,10 @@ Ekki er hægt að taka þessa breytingu til baka.',
 'badsig' => 'Ógild hrá undirskrift. Athugaðu HTML-kóða.',
 'badsiglength' => 'Undirskriftin er of löng.
 Hún þarf að vera færri en $1 {{PLURAL:$1|stafur|stafir}}.',
-'yourgender' => 'Kyn:',
-'gender-unknown' => 'Ã\93skilgreint',
-'gender-male' => 'Karl',
-'gender-female' => 'Kona',
+'yourgender' => 'Hvernig vilt þú helst lýsa þér?',
+'gender-unknown' => 'Ã\89g vil heldur ekki gefa upp',
+'gender-male' => 'Hann breytir wikisíðum',
+'gender-female' => 'Hún breytir wikisíðum',
 'prefs-help-gender' => 'Valfrjálst: notað til að aðgreina kynin í meldingum hugbúnaðarins. Þessar upplýsingar verða aðgengilegar öllum.',
 'email' => 'Tölvupóstur',
 'prefs-help-realname' => 'Alvöru nafn er valfrjálst.
@@ -1433,7 +1455,9 @@ Tölvupóstfang þitt er ekki gefið upp þegar aðrir notendur hafa samband vi
 'prefs-signature' => 'Undirskrift',
 'prefs-dateformat' => 'Dagasnið',
 'prefs-timeoffset' => 'Tímamismunur',
-'prefs-advancedediting' => 'Háþróaðir möguleikar',
+'prefs-advancedediting' => 'Almennir valkostir',
+'prefs-editor' => 'Ritsjóri',
+'prefs-preview' => 'Forskoðun',
 'prefs-advancedrc' => 'Háþróaðir möguleikar',
 'prefs-advancedrendering' => 'Háþróaðir möguleikar',
 'prefs-advancedsearchoptions' => 'Háþróaðir möguleikar',
@@ -1441,7 +1465,9 @@ Tölvupóstfang þitt er ekki gefið upp þegar aðrir notendur hafa samband vi
 'prefs-displayrc' => 'Útlitsmöguleikar',
 'prefs-displaysearchoptions' => 'Útlitsmöguleikar',
 'prefs-displaywatchlist' => 'Útlitsmöguleikar',
+'prefs-tokenwatchlist' => 'Lykill',
 'prefs-diffs' => 'Breytingar',
+'prefs-help-prefershttps' => 'Þessi stilling tekur gildi í næsta skiptið sem þú skráir inn.',
 
 # User preference: email validation using jQuery
 'email-address-validity-valid' => 'Netfang virðist vera virkt.',
@@ -1465,9 +1491,11 @@ Tölvupóstfang þitt er ekki gefið upp þegar aðrir notendur hafa samband vi
 'userrights-no-interwiki' => 'Þú hefur ekki leyfi til að breyta notandaréttindum á öðrum wiki-síðum.',
 'userrights-nodatabase' => 'Gagnagrunnurinn $1 er ekki til eða ekki staðbundinn.',
 'userrights-nologin' => 'Þú verður að [[Special:UserLogin|innskrá]] þig á möppudýraaðgang til að geta útdeilt notandaréttindum.',
-'userrights-notallowed' => 'Þinn aðgangur hefur ekki réttindi til að útdeila notandaréttindum.',
+'userrights-notallowed' => 'Þú hefur ekki réttindi til að útdeila eða draga til baka notandaréttini.',
 'userrights-changeable-col' => 'Hópar sem þú getur breytt',
 'userrights-unchangeable-col' => 'Hópar sem þú getur ekki breytt',
+'userrights-conflict' => 'Árekstur í að breyta notandaréttindum! Vinsamlegast skoðaðu aftur og staðfestu breytingar þínar.',
+'userrights-removed-self' => 'Þér hefur tekist að fjarlægja þín eigin réttindi. Vegna þess mátt þú ekki lengur skoða þessa síðu.',
 
 # Groups
 'group' => 'Hópur:',
@@ -1533,13 +1561,19 @@ Tölvupóstfang þitt er ekki gefið upp þegar aðrir notendur hafa samband vi
 'right-proxyunbannable' => 'Sneiða hjá sjálfvirkum proxy-bönnum',
 'right-unblockself' => 'Afbanna sjálfan sig',
 'right-protect' => 'Breyta verndunarstigi og breyta vernduðum síðum',
-'right-editprotected' => 'Breyta verndaðar síður (án keðjuverndunar)',
+'right-editprotected' => 'Breyta síðum vernduðum sem „{{int:protect-level-sysop}}“',
+'right-editsemiprotected' => 'Breyta síðum vernduðum sem „{{int:protect-level-autoconfirmed}}“',
 'right-editinterface' => 'Breyta notandaviðmótinu',
 'right-editusercssjs' => 'Breyta CSS- og JS-skrám annarra',
 'right-editusercss' => 'Breyta CSS-skrám annarra',
 'right-edituserjs' => 'Breyta JS-skrám annarra',
 'right-editmyusercss' => 'Breyta þinni eigin CSS-notandaskrá',
 'right-editmyuserjs' => 'Breyta þinni eigin JavaScript-notandaskrá',
+'right-viewmywatchlist' => 'Skoða þinn eigin vaktlista',
+'right-editmywatchlist' => 'Breyta þínum eigin vaktlista. Athugið að nokkrar aðgerðir bæta enn við síður án þessa réttindis.',
+'right-viewmyprivateinfo' => 'Skoða þínar eigin persónuupplýsingar (t.d. netfang, alvörunafn)',
+'right-editmyprivateinfo' => 'Breyta þínum eigin persónuupplýsingum (t.d. netfangi, alvörunafni)',
+'right-editmyoptions' => 'Breyta þínum eigin stillingum',
 'right-rollback' => 'Taka snögglega aftur breytingar síðasta notanda sem breytti síðunni',
 'right-markbotedits' => 'Merkja endurtektar breytingar sem vélmennabreytingar',
 'right-noratelimit' => 'Sneiða hjá takmörkunum',
@@ -1591,8 +1625,8 @@ Tölvupóstfang þitt er ekki gefið upp þegar aðrir notendur hafa samband vi
 'action-block' => 'Banna notandanum að gera breytingar',
 'action-protect' => 'breyta verndunarstigum fyrir þessa síðu',
 'action-rollback' => 'Taka snögglega aftur breytingar síðasta notanda sem breytti ákveðinni síðu',
-'action-import' => 'Flytja inn þessa skrá frá öðrum wiki',
-'action-importupload' => 'Flytja inn þessa síðu frá skráar upphali',
+'action-import' => 'flytja inn síður frá öðrum wiki',
+'action-importupload' => 'flytja inn síður frá skráarupphali',
 'action-patrol' => 'Merkja breytingar annara sem yfirfarnar',
 'action-autopatrol' => 'Merkja eigin breytingu sem yfirfarna',
 'action-unwatchedpages' => 'Skoða lista yfir óvaktaðar síður',
@@ -1601,12 +1635,19 @@ Tölvupóstfang þitt er ekki gefið upp þegar aðrir notendur hafa samband vi
 'action-userrights-interwiki' => 'breyta notandaréttindum annarra notenda á öðrum wiki-verkefnum',
 'action-siteadmin' => 'læsa eða opna gagnagrunninn',
 'action-sendemail' => 'senda tölvupósta',
+'action-editmywatchlist' => 'breyta vaktlistanum þínum',
+'action-viewmywatchlist' => 'skoða vaktlistann þinn',
+'action-viewmyprivateinfo' => 'skoða persónuupplýsingar þínar',
+'action-editmyprivateinfo' => 'breyta persónuupplýsingum þínum',
 
 # Recent changes
 'nchanges' => '$1 {{PLURAL:$1|breyting|breytingar}}',
+'enhancedrc-since-last-visit' => '$1 {{PLURAL:$1|síðan síðustu heimsókn}}',
+'enhancedrc-history' => 'breytingaskrá',
 'recentchanges' => 'Nýlegar breytingar',
 'recentchanges-legend' => 'Stillingar nýlegra breytinga',
 'recentchanges-summary' => 'Hér geturðu fylgst með nýjustu breytingunum.',
+'recentchanges-noresult' => 'Engar breytingar í uppgefna tímabilinu sem passa við þessa mælikvarða.',
 'recentchanges-feed-description' => 'Hér er hægt að fylgjast með nýlegum breytingum á {{SITENAME}}.',
 'recentchanges-label-newpage' => 'Þessi breyting skapaði nýja síðu',
 'recentchanges-label-minor' => 'Þetta er minniháttar breyting',
@@ -1634,7 +1675,7 @@ Tölvupóstfang þitt er ekki gefið upp þegar aðrir notendur hafa samband vi
 'rc_categories_any' => 'Alla',
 'rc-change-size-new' => '$1 {{PLURAL:$1|bæt|bæti}} eftir breytingu',
 'newsectionsummary' => 'Nýr hluti: /* $1 */',
-'rc-enhanced-expand' => 'Sýna upplýsingar (þarfnast JavaScript)',
+'rc-enhanced-expand' => 'Sýna upplýsingar',
 'rc-enhanced-hide' => 'Fela ítarefni',
 'rc-old-title' => 'Upphaflega búin til undir nafninu "$1"',
 
@@ -1654,8 +1695,7 @@ Síður á [[Special:Watchlist|vaktlistanum þínum]] eru '''feitletraðar'''.",
 'reuploaddesc' => 'Aftur á innhlaðningarformið.',
 'upload-tryagain' => 'Sendu breytta myndlýsingu',
 'uploadnologin' => 'Óinnskráð(ur)',
-'uploadnologintext' => 'Þú verður að vera [[Special:UserLogin|skráð(ur) inn]]
-til að hlaða inn skrám.',
+'uploadnologintext' => 'Þú verður $1 til að hala upp skrár.',
 'upload_directory_missing' => 'Mappa upphlaða ($1) er týnd og vefþjónninn gat ekki búið hana til.',
 'upload_directory_read_only' => 'Mistókst að skrifa í möppu upphlaða ($1) á vefþjóni.',
 'uploaderror' => 'Villa í innhlaðningu',
@@ -1894,8 +1934,7 @@ Athugaðu hvort síðan sé aðgengileg, bíddu í smástund og reyndu aftur.
 'upload_source_file' => '(skrá á tölvunni þinni)',
 
 # Special:ListFiles
-'listfiles-summary' => 'Þessi kerfissíða sýnir allar upphlaðnar skrár.
-Þegar hún er síuð ákveðnu notendanafni birtast eingöngu myndir frá honum.',
+'listfiles-summary' => 'Þessi kerfissíða sýnir allar upphlaðnar skrár.',
 'listfiles_search_for' => 'Leita að miðilsnafni:',
 'imgfile' => 'skrá',
 'listfiles' => 'Skráalisti',
@@ -1906,6 +1945,10 @@ Athugaðu hvort síðan sé aðgengileg, bíddu í smástund og reyndu aftur.
 'listfiles_size' => 'Stærð (bæti)',
 'listfiles_description' => 'Lýsing',
 'listfiles_count' => 'Útgáfur',
+'listfiles-show-all' => 'Taka með gamlar útgáfur af myndum',
+'listfiles-latestversion' => 'Núverandi útgáfa',
+'listfiles-latestversion-yes' => 'Já',
+'listfiles-latestversion-no' => 'Nei',
 
 # File description page
 'file-anchor-link' => 'Skrá',
@@ -2002,6 +2045,13 @@ Leitarstrengurinn á að vera á þessu formi: efnistag/myndasnið, t.d. <code>i
 'randompage' => 'Handahófsvalin grein',
 'randompage-nopages' => 'Það eru engar síður í {{PLURAL:$2|nafnrýminu|nafnrýmunum}}: $1.',
 
+# Random page in category
+'randomincategory' => 'Handhófsvalin síða í flokki',
+'randomincategory-invalidcategory' => '„$1“ er ekki gilt flokkarheiti',
+'randomincategory-nopages' => 'Það eru engar síður í flokkinum [[:Category:$1|$1]].',
+'randomincategory-selectcategory' => 'Fá handhófsvalda síðu úr flokkinum: $1 $2.',
+'randomincategory-selectcategory-submit' => 'Fara',
+
 # Random redirect
 'randomredirect' => 'Handahófsvalin tilvísun',
 'randomredirect-nopages' => 'Það eru engar tilvísanir í nafnrýminu „$1“.',
@@ -2027,6 +2077,10 @@ Leitarstrengurinn á að vera á þessu formi: efnistag/myndasnið, t.d. <code>i
 'statistics-users-active-desc' => 'Notendur sem hafa framkvæmt aðgerð {{PLURAL:$1|síðastliðin dag|síðastliðna $1 daga}}',
 'statistics-mostpopular' => 'Mest skoðuðu síður',
 
+'pageswithprop' => 'Síður með eiginleika',
+'pageswithprop-legend' => 'Síður með síðueiginleika',
+'pageswithprop-text' => 'Á þessari síðu er listi yfir síður sem hafa ákveðna síðueiginleika.',
+'pageswithprop-prop' => 'Heiti eiginleika:',
 'pageswithprop-submit' => 'Áfram',
 
 'doubleredirects' => 'Tvöfaldar tilvísanir',
@@ -2086,6 +2140,7 @@ Hún er tilvísun á [[$2]].',
 'mostrevisions' => 'Síður eftir fjölda breytinga',
 'prefixindex' => 'Allar síður með forskeyti',
 'prefixindex-namespace' => 'Allar síður með forskeyti ($1 nafnrými)',
+'prefixindex-strip' => 'Fjarlægja forskeyti í listanum',
 'shortpages' => 'Stuttar síður',
 'longpages' => 'Langar síður',
 'deadendpages' => 'Botnlangar',
@@ -2101,6 +2156,7 @@ Hún er tilvísun á [[$2]].',
 'listusers' => 'Notendalisti',
 'listusers-editsonly' => 'Sýna eingöngu notendur með breytingar',
 'listusers-creationsort' => 'Raða eftir stofndegi',
+'listusers-desc' => 'Raða í lækkandi röð',
 'usereditcount' => '$1 {{PLURAL:$1|breyting|breytingar}}',
 'usercreated' => '{{GENDER:$3|Stofnað|}} $1 $2',
 'newpages' => 'Nýjustu greinar',
@@ -2530,7 +2586,7 @@ $1',
 'contributions' => 'Framlög {{GENDER:$1|notanda}}',
 'contributions-title' => 'Framlög notanda $1',
 'mycontris' => 'Framlög',
-'contribsub2' => 'Eftir $1 ($2)',
+'contribsub2' => 'Eftir {{GENDER:$3|$1}} ($2)',
 'nocontribs' => 'Engar breytingar fundnar sem passa við þessa viðmiðun.',
 'uctop' => '(núverandi)',
 'month' => 'Frá mánuðinum (og fyrr):',
@@ -2689,12 +2745,9 @@ Sjá [[Special:BlockList|ítarlegri lista]] fyrir öll núgildandi bönn.',
 Vistfangið var bannað sem hluti af fjöldabanninu $2, sem er hægt að afbanna.',
 'ip_range_invalid' => 'Ógilt vistfangasvið.',
 'ip_range_toolarge' => 'Fjöldabönn stærri en /$1 eru óheimil.',
-'blockme' => 'Banna mig',
 'proxyblocker' => 'Vefsels bann',
-'proxyblocker-disabled' => 'Þessi virkni er óvirk.',
 'proxyblockreason' => 'Vistfangið þitt hefur verið bannað því það er opið vefsel.
 Vinsamlegast hafðu samband við internetþjónustuaðilann þinn eða netstjóra félagsins og láttu þá vita af þessu alvarlegu öryggisvandamáli.',
-'proxyblocksuccess' => 'Búinn.',
 'sorbsreason' => 'Vistfangið þitt er á lista yfir opin vefsel í DNSBL sem er í notkun á {{SITENAME}}.',
 'sorbs_create_account_reason' => 'Vistfangið þitt er á lista yfir opin vefsel í DNSBL sem er notað af {{SITENAME}}.
 Þú getur ekki stofnað aðgang.',
@@ -3039,6 +3092,8 @@ Vinsamlegast reyndu aftur.',
 'spam_reverting' => 'Tek aftur síðustu breytingu sem inniheldur ekki tengil á $1',
 'spam_blanking' => 'Allar útgáfur innihéldu tengla á $1, tæmi síðuna',
 'spam_deleting' => 'Allar útgáfur innihéldu tengla á $1, eyði síðunni',
+'simpleantispam-label' => "Kæfuvörn.
+'''EKKI''' fylla þetta út!",
 
 # Info page
 'pageinfo-title' => 'Upplýsingar um $1',
@@ -3053,12 +3108,12 @@ Vinsamlegast reyndu aftur.',
 'pageinfo-article-id' => 'Einkennisnúmer síðunnar',
 'pageinfo-language' => 'Tungumál síðunnar',
 'pageinfo-robot-policy' => 'Leitarvélastaða',
-'pageinfo-robot-index' => 'Skráanleg',
-'pageinfo-robot-noindex' => 'Óskráanleg',
+'pageinfo-robot-index' => 'Heimilað',
+'pageinfo-robot-noindex' => 'Ekki heimilað',
 'pageinfo-views' => 'Fjöldi innlita',
 'pageinfo-watchers' => 'Fjöldi notenda, sem vakta síðuna',
 'pageinfo-few-watchers' => 'Vöktuð af færri en $1 {{PLURAL:$1|notanda|notendum}}',
-'pageinfo-redirects-name' => 'Tilvísanir til þessarar síðu',
+'pageinfo-redirects-name' => 'Fjöldi tilvísana til þessarar síðu',
 'pageinfo-subpages-name' => 'Undirsíður þessarar síðu',
 'pageinfo-subpages-value' => '$1 ($2 {{PLURAL:$2|tilvísun|tilvísanir}}; $3 {{PLURAL:$3|ekki tilvísun|ekki tilvísanir}})',
 'pageinfo-firstuser' => 'Stofnandi síðunnar',
@@ -3364,6 +3419,7 @@ Ef skránni hefur verið breytt, kann að vera að einhverjar upplýsingar eigi
 'exif-disclaimer' => 'Fyrirvari',
 'exif-contentwarning' => 'Viðvörun innihalds myndar',
 'exif-giffilecomment' => 'GIF athugasemd',
+'exif-intellectualgenre' => 'Tegund hlutar',
 'exif-scenecode' => 'IPTC kóði myndefnis',
 'exif-event' => 'Lýsir viðburðinum',
 'exif-organisationinimage' => 'Lýsir félaginu',
@@ -3806,7 +3862,10 @@ MediaWiki er útgefin í þeirri von að hann sé gagnlegur, en ÁN ALLRAR ÁBYR
 'tags-tag' => 'Nafn tags',
 'tags-display-header' => 'Útlit í breytingarskrá',
 'tags-description-header' => 'Tæmandi merkingarlýsing',
+'tags-active-header' => 'Virkt?',
 'tags-hitcount-header' => 'Merktar breytingar',
+'tags-active-yes' => 'Já',
+'tags-active-no' => 'Nei',
 'tags-edit' => 'breyta',
 'tags-hitcount' => '$1 {{PLURAL:$1|breyting|breytingar}}',
 
@@ -3827,6 +3886,7 @@ MediaWiki er útgefin í þeirri von að hann sé gagnlegur, en ÁN ALLRAR ÁBYR
 'dberr-problems' => 'Því miður!Tæknilegir örðugleikar eru á þessari síðu.',
 'dberr-again' => 'Reyndu að bíða í nokkrar mínútur og endurhladdu síðan síðuna.',
 'dberr-info' => '(Mistókst að hafa samband við gagnaþjón: $1)',
+'dberr-info-hidden' => '(Mistókst að hafa samband við gagnaþjón)',
 'dberr-usegoogle' => 'Þú getur notað Google til að leita á meðan.',
 'dberr-outofdate' => 'Athugaðu að afrit þeirra gætu verið úreld.',
 'dberr-cachederror' => 'Þetta er afritað eintak af umbeðinni síðu og gæti verið úreld.',
@@ -3962,4 +4022,8 @@ Ef ekki, þá getur þú notað einfalt eyðublað hér fyrir neðan. Athugasemd
 # Image rotation
 'rotate-comment' => 'Myndinni var snúið um $1 {{PLURAL:$1|gráðu|gráður}} réttsælis',
 
+# Limit report
+'limitreport-walltime' => 'Rauntímanotkun',
+'limitreport-walltime-value' => '$1 {{PLURAL:$1|sekúnda|sekúndur}}',
+
 );
index a625b42..658d105 100644 (file)
@@ -311,7 +311,7 @@ $messages = array(
 'tog-watchdeletion' => 'Aggiungi le pagine e i file cancellati agli osservati speciali',
 'tog-minordefault' => 'Indica ogni modifica come minore (solo come predefinito)',
 'tog-previewontop' => "Mostra l'anteprima sopra la casella di modifica e non sotto",
-'tog-previewonfirst' => "Mostra l'anteprima per la prima modifica",
+'tog-previewonfirst' => "Mostra l'anteprima almeno una volta prima di salvare",
 'tog-nocache' => 'Disabilita la cache delle pagine del browser',
 'tog-enotifwatchlistpages' => 'Inviami una email quando viene modificata una pagina o un file presente tra gli osservati speciali',
 'tog-enotifusertalkpages' => 'Segnalami via e-mail le modifiche alla mia pagina di discussione',
@@ -805,8 +805,8 @@ Se non sei stato tu a fare la richiesta, oppure hai ritrovato la password e non
 'passwordsent' => 'Una nuova password è stata inviata all\'indirizzo e-mail registrato per l\'utente "$1".
 Per favore, effettua un accesso non appena la ricevi.',
 'blocked-mailpassword' => 'Per prevenire abusi, non è consentito usare la funzione "Invia nuova password" da un indirizzo IP bloccato.',
-'eauthentsent' => "Un messaggio e-mail di conferma è stato spedito all'indirizzo indicato.
-Per abilitare l'invio di messaggi e-mail per questo accesso è necessario seguire le istruzioni che vi sono indicate, in modo da confermare che si è i legittimi proprietari dell'indirizzo",
+'eauthentsent' => "Un messaggio email di conferma è stato spedito all'indirizzo indicato.
+Per abilitare l'invio di messaggi email per questo utente è necessario seguire le istruzioni che vi sono indicate, in modo da confermare che si è i legittimi proprietari dell'indirizzo.",
 'throttled-mailpassword' => 'Una email di reimpostazione della password è già stata inviata da meno di {{PLURAL:$1|1 ora|$1 ore}}.
 Per prevenire abusi, la funzione di reimpostazione della password può essere usata solo una volta ogni {{PLURAL:$1|ora|$1 ore}}.',
 'mailerror' => "Errore nell'invio del messaggio: $1",
@@ -1237,15 +1237,15 @@ Gli altri amministratori di {{SITENAME}} potranno accedere comunque ai contenuti
 * Dati personali inopportuni
 *: ''indirizzi, numeri di telefono, codici fiscali, ecc.''",
 'revdelete-legend' => 'Imposta le seguenti limitazioni sulle versioni cancellate:',
-'revdelete-hide-text' => 'Nascondi il testo della versione',
+'revdelete-hide-text' => 'Testo della versione',
 'revdelete-hide-image' => 'Nascondi i contenuti del file',
 'revdelete-hide-name' => 'Nascondi azione e oggetto della stessa',
-'revdelete-hide-comment' => "Nascondi l'oggetto della modifica o la motivazione dell'azione",
-'revdelete-hide-user' => "Nascondi il nome o l'indirizzo IP dell'autore",
+'revdelete-hide-comment' => "Oggetto della modifica o motivazione dell'azione",
+'revdelete-hide-user' => "Nome o indirizzo IP dell'autore",
 'revdelete-hide-restricted' => 'Nascondi le informazioni indicate anche agli amministratori',
 'revdelete-radio-same' => '(non cambiare)',
-'revdelete-radio-set' => '',
-'revdelete-radio-unset' => 'No',
+'revdelete-radio-set' => 'Visibile',
+'revdelete-radio-unset' => 'Nascosto',
 'revdelete-suppress' => 'Nascondi le informazioni anche agli amministratori',
 'revdelete-unsuppress' => 'Elimina le limitazioni sulle revisioni ripristinate',
 'revdelete-log' => 'Motivo:',
@@ -2767,11 +2767,8 @@ Consultare l'[[Special:BlockList|elenco dei blocchi]] per l'elenco dei bandi o b
 'ipb_blocked_as_range' => "Errore: L'indirizzo IP $1 non è soggetto a blocco individuale e non può essere sbloccato. Il blocco è invece attivo a livello dell'intervallo $2, che può essere sbloccato.",
 'ip_range_invalid' => 'Intervallo di indirizzi IP non valido.',
 'ip_range_toolarge' => 'Non è possibile bloccare range superiori al /$1',
-'blockme' => 'Bloccami',
 'proxyblocker' => 'Blocco dei proxy aperti',
-'proxyblocker-disabled' => 'Questa funzione è disabilitata.',
 'proxyblockreason' => 'Questo indirizzo IP è stato bloccato perché risulta essere un proxy aperto. Si prega di contattare il proprio fornitore di accesso a Internet o il supporto tecnico e informarli di questo grave problema di sicurezza.',
-'proxyblocksuccess' => 'Fatto.',
 'sorbsreason' => 'Questo indirizzo IP è elencato come proxy aperto nella blacklist DNSBL utilizzata da {{SITENAME}}.',
 'sorbs_create_account_reason' => 'Non è possibile creare nuovi accessi da questo indirizzo IP perché è elencato come proxy aperto nella blacklist DNSBL utilizzata da {{SITENAME}}.',
 'xffblockreason' => "Un indirizzo IP presente nell'intestazione X-Forwarded-For, tuo o del server proxy che stai utilizzando, è stato bloccato. La motivazione originale del blocco è: $1",
@@ -3118,6 +3115,8 @@ Tutte le operazioni di importazione trans-wiki sono registrate nel [[Special:Log
 'spam_reverting' => "Ripristinata l'ultima versione priva di collegamenti a $1",
 'spam_blanking' => 'Pagina svuotata, tutte le versioni contenevano collegamenti a $1',
 'spam_deleting' => 'Pagina cancellata, tutte le versioni contenevano collegamenti a $1',
+'simpleantispam-label' => "Controllo anti-spam.
+'''NON''' riempire!",
 
 # Info page
 'pageinfo-title' => 'Informazioni per "$1"',
@@ -3785,7 +3784,7 @@ Per favore, conferma che vuoi veramente ricreare questa pagina.",
 'confirm-unwatch-top' => 'Elimina questa pagina dalla tua lista degli osservati speciali?',
 
 # Separators for various lists, etc.
-'percent' => '$1&nbsp;%',
+'percent' => '$1&#160;%',
 
 # Multipage image navigation
 'imgmultipageprev' => '← pagina precedente',
@@ -3901,7 +3900,8 @@ Questo programma deve essere distribuito assieme ad [{{SERVER}}{{SCRIPTPATH}}/CO
 # Special:Redirect
 'redirect' => 'Reindirizzamento da file, utente o ID versione',
 'redirect-legend' => 'Reindirizza a un file o una pagina',
-'redirect-summary' => 'Questa pagina speciale reindirizza a un file (specificando il nome del file), a una pagina (specificando un ID di versione) o a un utente (specificando un ID utente numerico).',
+'redirect-summary' => 'Questa pagina speciale reindirizza a un file (specificando il nome del file), a una pagina (specificando un ID di versione) o a un utente (specificando un ID utente numerico).
+Esempi: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/revision/328429]], or [[{{#Special:Redirect}}/user/101]].',
 'redirect-submit' => 'Vai',
 'redirect-lookup' => 'Ricerca:',
 'redirect-value' => 'Valore:',
index fddfca2..849cf8e 100644 (file)
@@ -585,7 +585,7 @@ $messages = array(
 'articlepage' => '本文を表示',
 'talk' => '議論',
 'views' => '表示',
-'toolbox' => 'ツールボックス',
+'toolbox' => 'ツール',
 'userpage' => '利用者ページを表示',
 'projectpage' => 'プロジェクトのページを表示',
 'imagepage' => 'ファイルのページを表示',
@@ -841,7 +841,7 @@ $2',
 'createacct-emailoptional' => 'メールアドレス (省略可能)',
 'createacct-email-ph' => 'メールアドレスを入力',
 'createacct-another-email-ph' => 'メールアドレスを入力',
-'createaccountmail' => 'ä¸\80æ\99\82ç\9a\84ã\81§ã\83©ã\83³ã\83\80ã\83 ã\81ªã\83\91ã\82¹ã\83¯ã\83¼ã\83\89ã\82\92ç\94\9fæ\88\90ã\81\97ã\81¦ã\80\81æ\8c\87å®\9aã\81\97ã\81\9fã\83¡ã\83¼ã\83«ã\82¢ã\83\89ã\83¬ã\82¹ã\81«é\80\81ä¿¡ã\81\99ã\82\8b',
+'createaccountmail' => 'ä¸\80æ\99\82ç\9a\84ã\81ªç\84¡ä½\9cç\82ºã\81®ã\83\91ã\82¹ã\83¯ã\83¼ã\83\89ã\82\92ç\94\9fæ\88\90ã\81\97ã\81¦ã\80\81æ\8c\87å®\9aã\81\97ã\81\9fã\83¡ã\83¼ã\83«ã\82¢ã\83\89ã\83¬ã\82¹ã\81«é\80\81ä¿¡',
 'createacct-realname' => '本名 (省略可能)',
 'createaccountreason' => '理由:',
 'createacct-reason' => '理由',
@@ -1367,15 +1367,15 @@ $3が示した理由: ''$2''",
 * 非公開個人情報
 *: ''自宅の住所、電話番号、社会保障番号など''",
 'revdelete-legend' => '閲覧レベル制限を設定',
-'revdelete-hide-text' => '版の本文を隠す',
+'revdelete-hide-text' => '版の本文',
 'revdelete-hide-image' => 'ファイル内容を隠す',
 'revdelete-hide-name' => '操作および対象を隠す',
-'revdelete-hide-comment' => '編集の要約を隠す',
-'revdelete-hide-user' => '投稿者の利用者名またはIPを隠す',
+'revdelete-hide-comment' => '編集の要約',
+'revdelete-hide-user' => '投稿者の利用者名/IPアドレス',
 'revdelete-hide-restricted' => '他の利用者と同様に管理者からもデータを隠す',
 'revdelete-radio-same' => '(変更しない)',
-'revdelete-radio-set' => 'はい',
-'revdelete-radio-unset' => 'いいえ',
+'revdelete-radio-set' => '表示',
+'revdelete-radio-unset' => '非表示',
 'revdelete-suppress' => '他の利用者と同様に管理者からもデータを隠す',
 'revdelete-unsuppress' => '復元版に対する制限を除去',
 'revdelete-log' => '理由:',
@@ -1560,7 +1560,7 @@ $1",
 'prefs-rendering' => '表示',
 'saveprefs' => '保存',
 'resetprefs' => '保存していない変更を破棄',
-'restoreprefs' => 'すべて初期設定に戻す',
+'restoreprefs' => 'すべて初期設定に戻す (すべての節について)',
 'prefs-editing' => '編集',
 'rows' => '行数:',
 'columns' => '列数:',
@@ -2969,12 +2969,9 @@ $1 のブロックの理由は「''$2''」です。",
 ただし、$2の範囲でブロックされており、こちらのブロックは別途解除できます。',
 'ip_range_invalid' => 'IP範囲が無効です。',
 'ip_range_toolarge' => '/$1より広範囲の範囲ブロックは許可されていません。',
-'blockme' => '自分をブロック',
 'proxyblocker' => 'プロキシブロック係',
-'proxyblocker-disabled' => 'この機能は無効になっています。',
 'proxyblockreason' => 'このIPアドレスは公開プロキシであるためブロックされています。
 ご使用中のインターネットサービスプロバイダーまたは所属組織の技術担当者に連絡して、これが深刻なセキュリティ問題であることを伝えてください。',
-'proxyblocksuccess' => '完了。',
 'sorbs' => 'DNSBL',
 'sorbsreason' => 'ご使用中のIPアドレスが、{{SITENAME}}の使用しているDNSBLに公開プロキシとして記載されています。',
 'sorbs_create_account_reason' => 'ご使用中のIPアドレスが、{{SITENAME}}の使用しているDNSBLに公開プロキシとして記載されています。
@@ -3347,6 +3344,8 @@ $2',
 'spam_reverting' => '$1へのリンクを含まない最新の版に差し戻し',
 'spam_blanking' => 'すべての版が$1へのリンクを含んでいます。白紙化します。',
 'spam_deleting' => 'すべての版が$1へのリンクを含んでいます。削除します。',
+'simpleantispam-label' => "スパム攻撃防止用のチェックです。
+ここに値を決して入力'''しない'''でください。",
 
 # Info page
 'pageinfo-title' => '「$1」の情報',
@@ -3360,7 +3359,7 @@ $2',
 'pageinfo-length' => 'ページの長さ (バイト単位)',
 'pageinfo-article-id' => 'ページ ID',
 'pageinfo-language' => 'ページ本文の言語',
-'pageinfo-robot-policy' => 'ロボットによるインデックス',
+'pageinfo-robot-policy' => 'ロボットによるインデックス作成',
 'pageinfo-robot-index' => '許可',
 'pageinfo-robot-noindex' => '不許可',
 'pageinfo-views' => '閲覧回数',
@@ -4263,7 +4262,7 @@ MediaWikiは、有用であることを期待して配布されていますが
 # Special:Redirect
 'redirect' => 'ファイル名、利用者ID、版IDでの転送',
 'redirect-legend' => 'ファイルまたはページヘの転送',
-'redirect-summary' => 'この特別ページは、ファイル (ファイル名を指定)、ページ (版 ID を指定)、利用者ページ (利用者 ID を整数で指定) に転送されます。',
+'redirect-summary' => 'この特別ページは、ファイル (ファイル名を指定)、ページ (版 ID を指定)、利用者ページ (利用者 ID を整数で指定) に転送されます。使用例: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/revision/328429]], or [[{{#Special:Redirect}}/user/101]]',
 'redirect-submit' => '実行',
 'redirect-lookup' => '検索の種類:',
 'redirect-value' => '値:',
index ca273a2..82b4075 100644 (file)
@@ -2396,12 +2396,9 @@ Mangga mirsani [[Special:BlockList|daftar panganggo sing diblokir]] kanggo dafta
 'ipb_blocked_as_range' => 'Kaluputan: IP $1 ora diblokir sacara langsung lan ora bisa dijabel blokiré. IP $1 diblokir mawa bagéyan saka pamblokiran kelompok IP $2, sing bisa dijabel pamblokirané.',
 'ip_range_invalid' => 'Blok IP ora absah.',
 'ip_range_toolarge' => 'Jangkahé blokiran luwih gedhé saka /$1 ora dililakaké.',
-'blockme' => 'Blokiren aku',
 'proxyblocker' => 'Pamblokir proxy',
-'proxyblocker-disabled' => 'Fungsi iki saiki lagi dipatèni.',
 'proxyblockreason' => "Alamat IP panjenengan wis diblokir amerga alamat IP panjenengan iku ''open proxy''.
 Mangga ngubungi sing nyedyakaké dines internèt panjenengan utawa pitulungan tèknis lan aturana masalah kaamanan sérius iki.",
-'proxyblocksuccess' => 'Bubar.',
 'sorbsreason' => "Alamat IP panjenengan didaftar minangka ''open proxy'' ing DNSBL.",
 'sorbs_create_account_reason' => "Alamat IP panjenengan didaftar minangka ''open proxy'' ing DNSBL. Panjenengan ora bisa nggawé akun utawa rékening.",
 'cant-block-while-blocked' => 'Panjenengan ora bisa mblokir panganggo liya nalika panjenengan dhéwé pinuju diblokir.',
@@ -2731,6 +2728,8 @@ Mbokmanawa iki disebabaké anané pranala jaba sing klebu daftar ireng.',
 'spam_reverting' => 'Mbalèkaké menyang vèrsi pungkasan sing ora ana pranalané menyang $1',
 'spam_blanking' => 'Kabèh révisi sing duwé pranala menyang $1, pangosongan',
 'spam_deleting' => 'Kabèh benahan sing nduwé pranala nèng $1, dibusaki',
+'simpleantispam-label' => "Pamariksan anti-spam.
+'''Aja''' diisèkaké!",
 
 # Info page
 'pageinfo-title' => 'Inpormasi kanggo "$1"',
index b43f299..dfcdb94 100644 (file)
@@ -15,6 +15,7 @@
  * @author Dawid Deutschland
  * @author ITshnik
  * @author Kaganer
+ * @author MIKHEIL
  * @author Malafaya
  * @author Nemo bis
  * @author Nodar Kherkheulidze
@@ -175,7 +176,7 @@ $messages = array(
 'tog-hidepatrolled' => 'დამალეთ შესწორებული რედაქტირებები ბოლო ცვლილებებში',
 'tog-newpageshidepatrolled' => 'დამალეთ შემოწმებული გვერდები ახალი გვერდების სიიდან',
 'tog-extendwatchlist' => 'გავრცობილი კონტროლის სია ყველა დაკავშირებული ცვლილების ჩვენების ჩათვლით',
-'tog-usenewrc' => 'ბოლო ცვლილებების და კონტროლის სიის ცვლილებების დაჯგუფება (საჭიროა ჯავასკრიპტი)',
+'tog-usenewrc' => 'ბოლო ცვლილებების და კონტროლის სიის ცვლილებების დაჯგუფება',
 'tog-numberheadings' => 'ავტომატურად დანომრე ქვესათაურები',
 'tog-showtoolbar' => 'რედაქტირების პანელის ჩვენება',
 'tog-editondblclick' => 'გვერდების რედაქტირება ორმაგი დაწკაპუნებით',
@@ -309,8 +310,6 @@ $messages = array(
 'broken-file-category' => 'გვერდები ფაილების არასწორი ბმულებით',
 'categoryviewer-pagedlinks' => '($1) ($2)',
 
-'linkprefix' => '/^(.*?)([a-zA-Z\\x80-\\xff]+)$/sD',
-
 'about' => 'შესახებ',
 'article' => 'სტატია',
 'newwindow' => '(ახალ ფანჯარაში)',
@@ -419,7 +418,7 @@ $1',
 # All link text and link target definitions of links into project namespace that get used by other message strings, with the exception of user group pages (see grouppage).
 'aboutsite' => '{{SITENAME}}-ის შესახებ',
 'aboutpage' => 'Project:შესახებ',
-'copyright' => 'შინაარსი წარმოდგენილია $1 პირობებით.',
+'copyright' => 'შინაარსი წარმოდგენილია $1 პირობებით (თუ სხვა არ არის მითითებული).',
 'copyrightpage' => '{{ns:project}}:საავტორო უფლებები',
 'currentevents' => 'მიმდინარე მოვლენები',
 'currentevents-url' => 'Project:მიმდინარე მოვლენები',
@@ -587,11 +586,9 @@ $2',
 'virus-unknownscanner' => 'უცნობი ანტივირუსი:',
 
 # Login and logout pages
-'logouttext' => "'''á\83\97á\83¥á\83\95á\83\94á\83\9c á\83\90á\83\9bá\83\9fá\83\90á\83\9bá\83\90á\83\93 á\83¡á\83\98á\83¡á\83¢á\83\94á\83\9bá\83\98á\83\93á\83\90á\83\9c á\83\92á\83\90á\83¡á\83£á\83\9aá\83\98 á\83®á\83\90á\83 á\83\97.'''
+'logouttext' => "'''á\83\97á\83¥á\83\95á\83\94á\83\9c á\83\90á\83\9bá\83\9fá\83\90á\83\9bá\83\90á\83\93 á\83\92á\83\90á\83¡á\83£á\83\9aá\83\98 á\83®á\83\90á\83 á\83\97 á\83¡á\83\98á\83¡á\83¢á\83\94á\83\9bá\83\98á\83\93á\83\90á\83\9c.'''
 
-შეგიძლიათ გამოიყენოთ {{SITENAME}} ანონიმურად, ან შეგიძლიათ
-<span class='plainlinks'>[$1 შეხვიდეთ ისევ]</span> როგორც იგივე ან სხვა მომხმარებელი.
-შენიშნეთ, რომ ზოგიერთ გვერდზე შესაძლოა ისევ უჩვენებდეს რომ შესული ხართ სანამ თქვენი ბრაუზერის მეხსიერებას არ გაწმენდთ.",
+ზოგიერთმა გვერდმა შესაძლოა ისევ ისე გააგრძელოს ჩვენება თითქოს თქვენ ჯერ კიდევ სისტემაში იყოთ. ამის მოსაგვარებლად საჭიროა თქვენი ბრაუზერის მეხსიერების გაწმენდა.",
 'welcomeuser' => 'მოგესალმებით, $1!',
 'welcomecreation-msg' => 'თქვენი ანგარიში შექმნილია.
 არ დაგავიწყდეთ თქვენი [[Special:Preferences|{{SITENAME}}-ის კონფიგურაციის]] შეცვლა.',
@@ -636,7 +633,7 @@ $2',
 'createacct-emailoptional' => 'ელ. ფოსტის მისამართი (არასავალდებულო)',
 'createacct-email-ph' => 'შეიყვანეთ თქვენი ელ. ფოსტის მისამართი',
 'createacct-another-email-ph' => 'შეიყვანეთ ელ.ფოსტის მისამართი',
-'createaccountmail' => 'á\83\92á\83\90á\83\9bá\83\9dá\83\98á\83§á\83\94á\83\9cá\83\94á\83\97 á\83¨á\83\94á\83\9bá\83\97á\83®á\83\95á\83\94á\83\95á\83\98á\83\97á\83\9dá\83\91á\83\98á\83¡ á\83\9bá\83\94á\83\97á\83\9dá\83\93á\83\98á\83\97 á\83¨á\83\94á\83 á\83©á\83\94á\83£á\83\9aá\83\98 á\83\93á\83 á\83\9dá\83\94á\83\91á\83\98á\83\97á\83\98 á\83\9eá\83\90á\83 á\83\9dá\83\9aá\83\98 á\83\93á\83\90 á\83\9bá\83\98á\83¡á\83\98 á\83\92á\83\90á\83\92á\83\96á\83\90á\83\95á\83\9cá\83\90 á\83¥á\83\95á\83\94á\83\9bá\83\9dá\83\97 á\83\9bá\83\98á\83\97á\83\98á\83\97á\83\94á\83\91á\83£á\83\9a á\83\94á\83\9a. á\83¤á\83\9dá\83¡á\83¢á\83\98á\83¡ á\83\9bá\83\98á\83¡á\83\90á\83\9bá\83\90á\83 á\83\97á\83\96á\83\94:',
+'createaccountmail' => 'á\83\92á\83\90á\83\9bá\83\9dá\83\98á\83§á\83\94á\83\9cá\83\94á\83\97 á\83¨á\83\94á\83\9bá\83\97á\83®á\83\95á\83\94á\83\95á\83\98á\83\97á\83\9dá\83\91á\83\98á\83¡ á\83\9bá\83\94á\83\97á\83\9dá\83\93á\83\98á\83\97 á\83¨á\83\94á\83 á\83©á\83\94á\83£á\83\9aá\83\98 á\83\93á\83 á\83\9dá\83\94á\83\91á\83\98á\83\97á\83\98 á\83\9eá\83\90á\83 á\83\9dá\83\9aá\83\98 á\83\93á\83\90 á\83\9bá\83\98á\83¡á\83\98 á\83\92á\83\90á\83\90á\83\92á\83\96á\83\90á\83\95á\83\9cá\83\94á\83\97 á\83\9bá\83\98á\83\97á\83\98á\83\97á\83\94á\83\91á\83£á\83\9a á\83\94á\83\9a\83¤á\83\9dá\83¡á\83¢á\83\98á\83¡ á\83\9bá\83\98á\83¡á\83\90á\83\9bá\83\90á\83 á\83\97á\83\96á\83\94',
 'createacct-realname' => 'ნამდვილი სახელი (არააუცილებელი)',
 'createaccountreason' => 'მიზეზი:',
 'createacct-reason' => 'მიზეზი',
@@ -879,8 +876,9 @@ $2
 'loginreqlink' => 'შესვლა',
 'loginreqpagetext' => 'თქვენ უნდა $1 სხვა გვერდები აჩვენოთ.',
 'accmailtitle' => 'პაროლი გაგზავნილია.',
-'accmailtext' => "მომხმარებელი [[User talk:$1|$1]]-სთვის შექმნილია ახალი პაროლი, შექმნილი შემთხვევითი სიმბოლოებისგან გაიგზავნა $2 ელექტრონულ მისამართზე.
-სისტემისადმი თავის წარდგენის შემდეგ თქვენ შეგიძლლიათ ''[[Special:ChangePassword|შეცვალოთ პაროლი]]''.",
+'accmailtext' => "შემთხვევითი მეთოდით შექმნილი პაროლი მომხმარებლისათვის [[User talk:$1|$1]] გაგზავნილია მისამართზე $2.
+
+ავტორიზაციის გავლის შემდეგ შესაძლებელი იქნება ამ ანგარიშის  ''[[Special:ChangePassword|პაროლის შეცვლა]]'' ანგარიშში შესვლის გვერდზე.",
 'newarticle' => '(ახალი)',
 'newarticletext' => 'ბმულის მეშვეობით თქვენ მოხვდით გვერდზე, რომელიც ჯერ არ არსებობს.
 გვერდის შესაქმნელად შეიყვანეთ ინფორმაცია ქვემო ფანჯარაში
@@ -1322,7 +1320,7 @@ $1",
 'prefs-rendering' => 'იერსახე',
 'saveprefs' => 'შენახვა',
 'resetprefs' => 'გადატვირთვა',
-'restoreprefs' => 'á\83\99á\83\9dá\83\9cá\83¤á\83\98á\83\92á\83£á\83 á\83\90á\83ªá\83\98á\83\98á\83¡ á\83¡á\83\90á\83¬á\83§á\83\98á\83¡á\83\96á\83\94 á\83\93á\83\90á\83\91á\83 á\83£á\83\9cá\83\94á\83\91á\83\90',
+'restoreprefs' => 'á\83§á\83\95á\83\94á\83\9aá\83\90 á\83¡á\83\90á\83¬á\83§á\83\98á\83¡á\83\98 á\83\9eá\83\90á\83 á\83\90á\83\9bá\83\94á\83¢á\83 á\83\98á\83¡ á\83\90á\83¦á\83\93á\83\92á\83\94á\83\9cá\83\90 (á\83§á\83\95á\83\94á\83\9aá\83\90 á\83¡á\83\94á\83¥á\83ªá\83\98á\83\90á\83¨á\83\98)',
 'prefs-editing' => 'რედაქტირება',
 'rows' => 'რიგები:',
 'columns' => 'სვეტები',
@@ -1383,8 +1381,9 @@ $1",
 'gender-unknown' => 'მითითებას არ ვთვლი საჭიროდ',
 'gender-male' => 'ის (მამრობითი) არედაქტირებს ვიკი-გვერდებს',
 'gender-female' => 'ის (მდედრობითი) არედაქტირებს ვიკი-გვერდებს',
-'prefs-help-gender' => 'არასავალდებულო ველი: გამოიყენება პროგრამული უზრუნველყოფის იმ შეტყობინებებისთვის, რომლებიც ადამიანის სქესზეა დამოკიდებული.
-ეს ინფორმაცია საზოგადოებრივი  იქნება.',
+'prefs-help-gender' => 'ამ პარამეტრის დაყენება არასავალდებულოა.
+პროგრამული უზრუნველყოფა ამ ინფორმაციას იყენებს მხოლოდ სწორი გრამატიკული სქესით მომართვისათვის.
+ეს ინფორმაცია საჯარო იქნება ყველასათვის.',
 'email' => 'ელ. ფოსტა',
 'prefs-help-realname' => 'ნამდვილი სახელის მითითება აუცილებელი არ არის, მაგრამ თუ მიუთითებთ ის გამოყენებული იქნება თქვენი ნამუშევრის აღსანიშნავად.',
 'prefs-help-email' => 'ელ. ფოსტის მისამართი არ არის სავალდებულო, მაგრამ მისი მითითება იძლევა ახალი პაროლის გამოგზავნის საშუალებას თქვენი პაროლის დავიწყების შემთხვევაში.',
@@ -1480,7 +1479,7 @@ $1",
 'right-reupload-shared' => 'ფაილთა შეცვლა საერთო საცავიდან ლოკალურებით',
 'right-upload_by_url' => 'ფაილის ატვირთვა URL-დან',
 'right-purge' => 'ქეშის გაწმენდა დადასტურების გვერდის გარეშე',
-'right-autoconfirmed' => 'á\83\9cá\83\90á\83¬á\83\98á\83\9aá\83\9dá\83\91á\83 á\83\98á\83\95 á\83\93á\83\90á\83ªá\83£á\83\9aá\83\98 á\83\92á\83\95á\83\94á\83 á\83\93á\83\94á\83\91á\83\98á\83¡ á\83 á\83\94á\83\93á\83\90á\83¥á\83¢á\83\98á\83 á\83\94á\83\91á\83\90',
+'right-autoconfirmed' => 'á\83¡á\83\98á\83©á\83¥á\83\90á\83 á\83\98á\83¡ á\83¨á\83\94á\83\96á\83¦á\83£á\83\93á\83\95á\83\90 IP á\83\9bá\83\98á\83¡á\83\90á\83\9bá\83\90á\83 á\83\97á\83\96á\83\94 á\83\90á\83  á\83\90á\83 á\83\98á\83¡',
 'right-bot' => 'ჩაითვალო ავტომატურ პროცესად',
 'right-nominornewtalk' => 'მცირე რედაქტირებების არ ქონის შემთხვევაში ჩაირთვება ახალ შეტყობინებათა რეჟიმი',
 'right-apihighlimits' => 'ნაკლები შეზღუდვა API-მოთხოვნებზე',
@@ -1501,7 +1500,7 @@ $1",
 'right-ipblock-exempt' => 'IP ბლოკის, ავტობლოკის და დიაპაზონთა ბლოკის გასვლა',
 'right-proxyunbannable' => 'პროქსის ავტობლოკის გადასვლა',
 'right-unblockself' => 'საკუთარი თავის განბლოკვა',
-'right-protect' => 'გვერდების დაცვის დონის შეცვლა და დაცული გვერდების რედაქტირება',
+'right-protect' => 'á\83\92á\83\95á\83\94á\83 á\83\93á\83\94á\83\91á\83\98á\83¡ á\83\93á\83\90á\83ªá\83\95á\83\98á\83¡ á\83\93á\83\9dá\83\9cá\83\98á\83¡ á\83¨á\83\94á\83ªá\83\95á\83\9aá\83\90 á\83\93á\83\90 á\83\99á\83\90á\83¡á\83\99á\83\90á\83\93á\83£á\83 á\83\90á\83\93 á\83\93á\83\90á\83ªá\83£á\83\9aá\83\98 á\83\92á\83\95á\83\94á\83 á\83\93á\83\94á\83\91á\83\98á\83¡ á\83 á\83\94á\83\93á\83\90á\83¥á\83¢á\83\98á\83 á\83\94á\83\91á\83\90',
 'right-editprotected' => 'გვერდების რედაქტირება რომლებიც დაცულია როგორც „{{int:protect-level-sysop}}“',
 'right-editsemiprotected' => 'გვერდების რედაქტირება რომლებიც დაცულია როგორც „{{int:protect-level-autoconfirmed}}“',
 'right-editinterface' => 'მომხმარებლის ინტერფეისის შეცვლა',
@@ -1583,7 +1582,7 @@ $1",
 'enhancedrc-history' => 'ისტორია',
 'recentchanges' => 'ბოლო ცვლილებები',
 'recentchanges-legend' => 'ბოლო ცვლილებების პარამეტრები',
-'recentchanges-summary' => 'á\83£á\83\97á\83\95á\83\90á\83\9aá\83\97á\83\95á\83\90á\83\9aá\83\94á\83\97 á\83\95á\83\98á\83\99á\83\98á\83¨á\83\98 ბოლო ცვლილებებს ამ გვერდზე.',
+'recentchanges-summary' => 'á\83£á\83\97á\83\95á\83\90á\83\9aá\83\97á\83\95á\83\90á\83\9aá\83\94á\83\97 á\83\95á\83\98á\83\99á\83\98á\83¡ ბოლო ცვლილებებს ამ გვერდზე.',
 'recentchanges-feed-description' => 'ვიკის უახლესი ცვლილებების მეთვალყურეობა ამ არხში.',
 'recentchanges-label-newpage' => 'ამ რედაქტირებით შეიქმნა ახალი გვერდი',
 'recentchanges-label-minor' => 'ეს არის მცირე შესწორება',
@@ -1864,8 +1863,7 @@ $1',
 'upload_source_file' => ' (ფაილი შენს კომპიუტერზე)',
 
 # Special:ListFiles
-'listfiles-summary' => 'ეს სპეციალური გვერდი აჩვენებს ყველა ატვირთულ ფაილს.
-მომხმარებლების მიხედვით გაფილტვრისას, ნაჩვენები იქნება ამ მომხმარებლის მხოლოდ ახალი ატვირთვები.',
+'listfiles-summary' => 'ეს სპეციალური გვერდი აჩვენებს ყველა ატვირთულ ფაილს.',
 'listfiles_search_for' => 'ძიება სურათის სახელის მიხედვით:',
 'imgfile' => 'ფაილი',
 'listfiles' => 'სურათების სია',
@@ -1877,6 +1875,7 @@ $1',
 'listfiles_description' => 'აღწერილობა',
 'listfiles_count' => 'ვერსიები',
 'listfiles-show-all' => 'სურათების ძველი ვერსიების ჩართვა',
+'listfiles-latestversion' => 'მიმდინარე ვერსია',
 'listfiles-latestversion-yes' => 'დიახ',
 'listfiles-latestversion-no' => 'არა',
 
@@ -1976,6 +1975,7 @@ $1',
 'randompage-nopages' => '{{PLURAL:$2|სახელთა შემდეგი სივრცე|სახელთა შემდეგ სივრცეში}} "$1" არ არის გვერდები.',
 
 # Random page in category
+'randomincategory-invalidcategory' => 'კატეგორია „$1“ არ არსებობს.',
 'randomincategory-selectcategory-submit' => 'მიდი',
 
 # Random redirect
@@ -2348,9 +2348,11 @@ $UNWATCHURL
 'deleteotherreason' => 'სხვა/დამატებითი მიზეზი:',
 'deletereasonotherlist' => 'სხვა მიზეზი',
 'deletereason-dropdown' => '* წაშლის ხშირი მიზეზები
-** ავტორის თხოვნით
+** სპამი
+** ვანდალიზმი
 ** საავტორო უფლების დარღვევა
-** ვანდალიზმი',
+** ავტორის მოთხოვნით
+** გატეხილი გადამისამართება',
 'delete-edit-reasonlist' => 'წაშლის მიზეზების რედაქტირება',
 'delete-toobig' => 'ამ გვერდს ძალიან გრძელი ისტორია გააჩნია,  $1 {{PLURAL:$1|ვერსიაზე|ვერსიიებზე|ვერსიებზე}} მეტი. მისი წაშლა აიკრძალა {{SITENAME}}-ის კორექტურად მუშაობის უზრუნველყოფისთვის.',
 'delete-warning-toobig' => 'ამ გვერდს ძალიან გრძელი ისტორია გააჩნია,  $1 {{PLURAL:$1|ვერსიაზე|ვერსიიებზე|ვერსიებზე}} მეტი.
@@ -2371,7 +2373,7 @@ $UNWATCHURL
 ბოლო ცვლილებები შეიტანა  [[User:$3|$3]] ([[User talk:$3|განხილვა]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).',
 'editcomment' => "რედაქტირება განმარტებული იყო როგორც: \"''\$1''\".",
 'revertpage' => '[[Special:Contributions/$2|$2]]-ის რედაქტირება გაუქმდა; აღდგა ბოლოს [[User:$1|$1]]-ის მიერ რედაქტირებული ვერსია',
-'revertpage-nouser' => 'á\83\9bá\83\9dá\83\9bá\83®á\83\9bá\83\90á\83 á\83\94á\83\91á\83\94á\83\9a (á\83\9bá\83\9dá\83\9bá\83®á\83\9bá\83\90á\83 á\83\94á\83\91á\83\9aá\83\98á\83¡ á\83¡á\83\90á\83®á\83\94á\83\9aá\83\98 á\83\93á\83\90á\83\9bá\83\90á\83\9aá\83£á\83\9aá\83\98á\83\90)-á\83\98á\83¡ á\83 á\83\94á\83\93á\83\90á\83¥á\83¢á\83\98á\83 á\83\94á\83\91á\83\90 á\83\93á\83\90á\83\91á\83 á\83£á\83\9cá\83\94á\83\91á\83£á\83\9aá\83\98á\83\90 á\83\95á\83\94á\83 á\83¡á\83\98á\83\90á\83\96á\83\94 [[User:$1|$1]]',
+'revertpage-nouser' => 'á\83\9bá\83\9dá\83\9bá\83®á\83\9bá\83\90á\83 á\83\94á\83\91á\83\9aá\83\98á\83¡ (á\83\9bá\83\9dá\83\9bá\83®á\83\9bá\83\90á\83 á\83\94á\83\91á\83\9aá\83\98á\83¡ á\83¡á\83\90á\83®á\83\94á\83\9aá\83\98 á\83\93á\83\90á\83\9bá\83\90á\83\9aá\83£á\83\9aá\83\98á\83\90) á\83ªá\83\95á\83\9aá\83\98á\83\9aá\83\94á\83\91á\83\94á\83\91á\83\98 á\83\93á\83\90á\83\91á\83 á\83£á\83\9cá\83\94á\83\91á\83£á\83\9aá\83\98á\83\90 á\83\95á\83\94á\83 á\83¡á\83\98á\83\90á\83\96á\83\94 {{GENDER:$1|[[User:$1|$1]]}}',
 'rollback-success' => 'გაუქმდა შესწორება $1; დაბრუნება ვერსიაზე $2.',
 
 # Edit tokens
@@ -2513,7 +2515,7 @@ $1',
 'contributions' => '{{GENDER:$1|მომხმარებელი}} წვლილი',
 'contributions-title' => 'მომხმარებლის წვლილი $1',
 'mycontris' => 'წვლილი',
-'contribsub2' => '$1 ($2) თვის',
+'contribsub2' => 'მომხმარებელი {{GENDER:$3|$1}} წვლილი ($2)',
 'nocontribs' => 'ძებნისას მითითებული პარამეტრების შესაბამისი არც ერთი ცვლილება ნაპოვნი არ არის',
 'uctop' => '(მიმდინარე)',
 'month' => 'თვე:',
@@ -2672,11 +2674,8 @@ $1',
 'ipb_blocked_as_range' => 'შეცდომა:  IP-მისამართი $1 არ იყო პირდაპირ დაბლოკილი, შესაბამისად ვერ მოხდება მისი განბლოკვა.თუმცა იგი ეკუთვნის დიაპაზონს $2, რომლის განბლოკვა შესაძლებელია.',
 'ip_range_invalid' => 'არასწორი IP მისამართი',
 'ip_range_toolarge' => 'დაბლოკვა /$1 დიაპაზონზე ზემოთ აკრძალულია.',
-'blockme' => 'დამბლოკე',
 'proxyblocker' => 'პროქსის ბლოკირება',
-'proxyblocker-disabled' => 'ეს ფუნქცია გაუქმებულია.',
 'proxyblockreason' => 'თქვენი IP მისამართი დაიბლოკა, ვინაიდან ის ღია პროქსია. გთხოვთ დაუკავშირდეთ თქვენ ინტერნეტ პროვაიდერს ან ტექ. სამსახურს და აცნობოთ მათ ამ სერიოზული უსაფრთხოების პრობლემის შესახებ.',
-'proxyblocksuccess' => 'შესრულებულია.',
 'sorbs' => 'DNSBL',
 'sorbsreason' => 'თქვენი IP-მისამართი მიჩნევა ღია პროქსიდ DNSBL-ის თანახმად.',
 'sorbs_create_account_reason' => 'თქვენი IP-მისამართი ითვლება ღია პროქსიდ DNSBL-ის ანახმად. თქვენ ვერ შექმნით ანგარიშს.',
@@ -3024,6 +3023,8 @@ $2',
 'spam_reverting' => 'დაბრუნება ბოლო ვერსიასთან, რომელიც არ შეიცავს ბმულს $1-თან',
 'spam_blanking' => 'ყველა გვერდი შეიცავს ბმულს $1-გვერდზე. გასუფთავება',
 'spam_deleting' => 'ყველა ვერსია შეიცავდა ბმულს $1-ზე, მიმდინარეობს წაშლა',
+'simpleantispam-label' => "სპამის საწინააღმდეგო შემოწმება.
+ეს '''არ''' შეავსოთ!",
 
 # Info page
 'pageinfo-title' => 'ინფორმაცია „$1“-თვის',
@@ -3037,7 +3038,7 @@ $2',
 'pageinfo-length' => 'გვერდის სიგრძე (ბაიტებში)',
 'pageinfo-article-id' => 'გვერდის ID',
 'pageinfo-language' => 'გვერდის შინაარსის ენა',
-'pageinfo-robot-policy' => 'á\83¡á\83\90á\83«á\83\98á\83\94á\83\91á\83\9d á\83¡á\83\98á\83¡á\83¢á\83\94á\83\9bá\83\98á\83¡ á\83¡á\83¢á\83\90á\83¢á\83£á\83¡á\83\98',
+'pageinfo-robot-policy' => 'á\83\98á\83\9cá\83\93á\83\94á\83¥á\83¡á\83\90á\83ªá\83\98á\83\90 á\83¡á\83\90á\83«á\83\98á\83\94á\83\91á\83\9d á\83 á\83\9dá\83\91á\83\9dá\83¢á\83\94á\83\91á\83\98á\83\97',
 'pageinfo-robot-index' => 'დაშვებულია',
 'pageinfo-robot-noindex' => 'არ არის დაშვებული',
 'pageinfo-views' => 'ხილვების რაოდენობა',
index 5800a8b..b1314d1 100644 (file)
@@ -151,6 +151,7 @@ $dateFormats = array(
 );
 
 $linkTrail = "/^((?:[a-zıʼ’“»]|'(?!'))+)(.*)$/sDu";
+$linkPrefixCharset = 'a-zıA-Zİ\\x80-\\xff';
 
 $messages = array(
 # User preference toggles
@@ -264,8 +265,6 @@ $messages = array(
 'category-file-count-limited' => "Usı kategoriyada to'mendegi {{PLURAL:$1|fayl|$1 fayl}} bar.",
 'listingcontinuesabbrev' => 'dawamı',
 
-'linkprefix' => '/^(.*?)([a-zıA-Zİ\\x80-\\xff]+)$/sDu',
-
 'about' => 'Haqqında',
 'article' => "Mag'lıwmat beti",
 'newwindow' => "(jan'a aynada)",
@@ -1580,8 +1579,6 @@ Basqa bloklawlar ushın [[Special:BlockList|IP bloklaw dizimin]] ko'rip shıg'ı
 'block-log-flags-noemail' => "e-mail bloklang'an",
 'ipb_expiry_invalid' => "Ku'shin joytıw waqtı nadurıs.",
 'ipb_already_blocked' => '"$1" a\'lle qashan bloklang\'an',
-'proxyblocker-disabled' => "Bul funktsiya o'shirilgen.",
-'proxyblocksuccess' => 'Tamamlandı.',
 
 # Developer tools
 'lockdb' => "Mag'lıwmatlar bazasın qulpla",
index 23e206a..27bff4d 100644 (file)
@@ -2239,7 +2239,6 @@ Asekcem aneggaru n useklas n ikyafen yella ddaw agi :',
 'block-log-flags-anononly' => 'Imseqdacen udrigen kan',
 'block-log-flags-nocreate' => 'asnulfu n umiḍan yessegdel',
 'proxyblockreason' => 'Tansa n IP inek teɛkel axaṭer nettat "open proxy". G leɛnayek, meslay akk d provider inek.',
-'proxyblocksuccess' => 'D ayen.',
 'sorbsreason' => 'Tansa IP inek/inem tella deg yiwen umuɣ am "open proxy" deg DNSBL yettuseqdac deg {{SITENAME}}.',
 'sorbs_create_account_reason' => 'Tansa IP inek/inem tella deg yiwen umuɣ am "open proxy" deg DNSBL yettuseqdac deg {{SITENAME}}.
 Ur tezmireḍ ara ad snulfuḍ amiḍan.',
index 8f18ce2..36c0437 100644 (file)
@@ -177,8 +177,6 @@ $messages = array(
 'noindex-category' => 'Pelê bêendeksıni',
 'broken-file-category' => 'Peli be gıreunê dosyeunê sıkıtau',
 
-'linkprefix' => '/^(.*?)([a-zA-Z\\x80-\\xff]+)$/sD',
-
 'about' => 'Heqa',
 'article' => 'Pela tedeesteyu',
 'newwindow' => '(zerrê pençerê dê newey de beno ra)',
index 906b356..0e98531 100644 (file)
@@ -2015,12 +2015,9 @@ $1 بۇعاتتاۋى ٴۇشىن كەلتىرىلگەن سەبەبى: «$2».',
 'ipb_blocked_as_range' => 'قاتەلىك: IP $1 تىكەلەي بۇعاتتالماعان جانە بۇعاتتاۋى وشىرىلمەيدى.
 بىراق, بۇل بۇعاتتاۋى ٴوشىرىلۋى مۇمكىن $2 اۋقىمى بولىگى بوپ بۇعاتتالعان.',
 'ip_range_invalid' => 'IP مەكەنجاي اۋقىمى جارامسىز.',
-'blockme' => 'وزدىكتىك_بۇعاتتاۋ',
 'proxyblocker' => 'پروكسىي سەرۆەرلەردى بۇعاتتاۋىش',
-'proxyblocker-disabled' => 'بۇل جەتە وشىرىلگەن.',
 'proxyblockreason' => 'IP مەكەنجايىڭىز اشىق پروكسىي سەرۆەرگە جاتاتىندىقتان بۇعاتتالعان.
 ىينتەرنەت قىزمەتىن جابدىقتاۋشىڭىزبەن, نە تەحنىيكالىق قولداۋ قىزمەتىمەن قاتىناسىڭىز, جانە ولارعا وسى وتە كۇردەلى قاۋىپسىزدىك شاتاق تۋرالى اقپارات بەرىڭىز.',
-'proxyblocksuccess' => 'ٴبىتتى.',
 'sorbsreason' => 'IP مەكەنجايىڭىز {{SITENAME}} تورابىندا قولدانىلعان DNSBL قارا تىزىمىندەگى اشىق پروكسىي-سەرۆەر دەپ تابىلادى.',
 'sorbs_create_account_reason' => 'IP مەكەنجايىڭىز {{SITENAME}} تورابىندا قولدانىلعان DNSBL قارا تىزىمىندەگى اشىق پروكسىي-سەرۆەر دەپ تابىلادى.
 جاڭا تىركەلگى جاساي المايسىز.',
index 23528c1..8163914 100644 (file)
@@ -2619,12 +2619,9 @@ $1 бұғаттауы үшін келтірілген себебі: «$2».',
 'ipb_blocked_as_range' => 'Қателік: IP $1 тікелей бұғатталмаған және бұғаттауы өшірілмейді.
 Бірақ, бұл бұғаттауы өшірілуі мүмкін $2 ауқымы бөлігі боп бұғатталған.',
 'ip_range_invalid' => 'IP мекенжай ауқымы жарамсыз.',
-'blockme' => 'Тіркелгімді бұғатта',
 'proxyblocker' => 'Прокси серверлерді бұғаттауыш',
-'proxyblocker-disabled' => 'Бұл жете өшірілген.',
 'proxyblockreason' => 'IP мекенжайыңыз ашық прокси серверге жататындықтан бұғатталған.
 Интернет қызметін жабдықтаушыңызбен, не техникалық қолдау қызметімен қатынасыңыз, және оларға осы оте күрделі қауыпсіздік шатақ туралы ақпарат беріңіз.',
-'proxyblocksuccess' => 'Орындалды.',
 'sorbsreason' => 'IP мекенжайыңыз {{SITENAME}} торабында қолданылған DNSBL қара тізіміндегі ашық прокси-сервер деп табылады.',
 'sorbs_create_account_reason' => 'IP мекенжайыңыз {{SITENAME}} торабында қолданылған DNSBL қара тізіміндегі ашық прокси-сервер деп табылады.
 Жаңа тіркелгі жасай алмайсыз.',
@@ -3673,7 +3670,9 @@ $5
 'tags-tag' => 'Тег атауы',
 'tags-display-header' => 'Өзгеріс тізіміндегі көрінісі',
 'tags-description-header' => 'Толық сипаттама мәні',
-'tags-hitcount-header' => 'Телгіленген өзгерістер',
+'tags-active-header' => 'Белсенді ме?',
+'tags-hitcount-header' => 'Тегтелген өзгерістер',
+'tags-active-yes' => 'Иә',
 'tags-edit' => 'өңдеу',
 'tags-hitcount' => '$1 {{PLURAL:$1|өзгеріс|өзгеріс}}',
 
@@ -3713,7 +3712,7 @@ $5
 'logentry-move-move_redir' => '$1 $3 бетін $4 деген айдатқыш үстіне {{GENDER:$2|жылжытты}}',
 'logentry-move-move_redir-noredirect' => '$1 $3 бетін $4 деген айдатқыш үстіне {{GENDER:$2|жылжытты}} (айдатқыш қалдырылмады)',
 'logentry-newusers-newusers' => '$1 жаңадан қатысушы тіркелгісін {{GENDER:$2|жасады}}',
-'logentry-newusers-create' => '$1 жаңадан қатысушы тіркелгісі {{GENDER:$2|жасады}}',
+'logentry-newusers-create' => '$1 жаңадан аккаунт тіркеді',
 'logentry-newusers-create2' => '$1 $3 деген аккаунт тіркеді',
 'logentry-newusers-autocreate' => '$1 қатысушы аккаунтын автоматты түрде {{GENDER:$2|тіркеді}}',
 'logentry-rights-rights' => '$1 $3 үшін топ мүшелігін $4 дегеннен $5 дегенге {{GENDER:$2|өзгерті}}',
index 22622eb..67e7cf7 100644 (file)
@@ -1978,12 +1978,9 @@ Ağımdağı belsendi tïımdar men buğattawlardı [[{{#special:Ipblocklist}}|I
 'ipb_blocked_as_range' => 'Qatelik: IP $1 tikeleý buğattalmağan jäne buğattawı öşirilmeýdi.
 Biraq, bul buğattawı öşirilwi mümkin $2 awqımı böligi bop buğattalğan.',
 'ip_range_invalid' => 'IP mekenjaý awqımı jaramsız.',
-'blockme' => 'Özdiktik_buğattaw',
 'proxyblocker' => 'Proksï serverlerdi buğattawış',
-'proxyblocker-disabled' => 'Bul jete öşirilgen.',
 'proxyblockreason' => 'IP mekenjaýıñız aşıq proksï serverge jatatındıqtan buğattalğan.
 Ïnternet qızmetin jabdıqtawşıñızben, ne texnïkalıq qoldaw qızmetimen qatınasıñız, jäne olarğa osı ote kürdeli qawıpsizdik şataq twralı aqparat beriñiz.',
-'proxyblocksuccess' => 'Bitti.',
 'sorbsreason' => 'IP mekenjaýıñız {{SITENAME}} torabında qoldanılğan DNSBL qara tizimindegi aşıq proksï-server dep tabıladı.',
 'sorbs_create_account_reason' => 'IP mekenjaýıñız {{SITENAME}} torabında qoldanılğan DNSBL qara tizimindegi aşıq proksï-server dep tabıladı.
 Jaña tirkelgi jasaý almaýsız.',
index b962cdd..41360e7 100644 (file)
@@ -308,7 +308,7 @@ $messages = array(
 'tog-prefershttps' => 'ប្រើប្រាស់ការតភ្ជាប់មានសុវត្ថិភាពជានិច្ចពេលកត់ឈ្មោះចូល',
 
 'underline-always' => 'ជានិច្ច',
-'underline-never' => 'á\9e\80á\9e»á\9f\86á\9e¢á\9f\84យសោះ',
+'underline-never' => 'á\9e\80á\9e»á\9f\86á\9e²á\9f\92យសោះ',
 'underline-default' => 'តាមលំនាំដើមនៃ​កម្មវិធី​រុករក​',
 
 # Font style option in Special:Preferences
@@ -402,8 +402,6 @@ $messages = array(
 'broken-file-category' => 'ទំព័រទាំងឡាយដែលដាច់តំណភ្ជាប់',
 'categoryviewer-pagedlinks' => '($1) ($2)',
 
-'linkprefix' => '/^(.*?)([a-zA-Z\\x80-\\xff]+)$/sD',
-
 'about' => 'អំពី',
 'article' => 'មាតិកាអត្ថបទ',
 'newwindow' => '(បើក​លើ​បង្អួច​ថ្មី)',
@@ -482,7 +480,7 @@ $messages = array(
 'articlepage' => 'មើលខ្លឹមសារទំព័រ​',
 'talk' => 'ការពិភាក្សា',
 'views' => 'គំហើញ',
-'toolbox' => 'ប្រអប់​ឧបករណ៍',
+'toolbox' => '​ឧបករណ៍',
 'userpage' => 'មើលទំព័រអ្នកប្រើប្រាស់',
 'projectpage' => 'មើល​ទំព័រគម្រោង',
 'imagepage' => 'មើល​ទំព័រ​ឯកសារ',
@@ -602,6 +600,7 @@ $1',
 # General errors
 'error' => 'មានបញ្ហា',
 'databaseerror' => 'មូលដ្ឋានទិន្នន័យមានបញ្ហា',
+'databaseerror-error' => 'កំហុស៖ $1',
 'laggedslavemode' => "'''ប្រយ័ត្ន៖''' ទំព័រនេះ​ប្រហែលជាគ្មានព័ត៌មានទាន់សម័យទេ។",
 'readonly' => 'មូលដ្ឋានទិន្នន័យត្រូវបានចាក់សោ',
 'enterlockreason' => 'សូមផ្ដល់ហេតុផលសម្រាប់ការជាប់សោ ព្រមទាំងកាលបរិច្ឆេទដោះសោវិញ',
@@ -724,6 +723,7 @@ $2',
 'userlogin-resetpassword-link' => 'ស្ដារពាក្យសម្ងាត់របស់អ្នក',
 'helplogin-url' => 'Help:ការកត់ឈ្មោះចូល',
 'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|ជំនួយក្នុងការកត់ឈ្មោះចូល]]',
+'userlogin-createanother' => 'បង្កើតគណនីមួយទៀត',
 'createacct-join' => 'បំពេញព័ត៌មានរបស់អ្នកខាងក្រោម។',
 'createacct-another-join' => 'បញ្ចូលព័ត៌មានគណនីថ្មីខាងក្រោម។',
 'createacct-emailrequired' => 'អាសយដ្ឋានអ៊ីមែល',
@@ -2732,13 +2732,10 @@ $1',
 
 វាប្រហែលជាត្រូវបានគេដកការហាមឃាត់ហើយ។',
 'ip_range_invalid' => 'ដែនកំណត់ IP គ្មានសុពលភាព។',
-'blockme' => 'ហាមឃាត់ខ្ញុំ',
 'proxyblocker' => 'កម្ម​វិធី​​រាំង​ផ្ទប់​ប្រូកស៊ី (Proxy)',
-'proxyblocker-disabled' => 'មុខងារនេះត្រូវបានអសកម្ម។',
 'proxyblockreason' => 'អាសយដ្ឋាន IP របស់អ្នកត្រូវបានរាំងខ្ទប់ហើយ ពីព្រោះវាជាប្រុកស៊ី(proxy)ចំហ។
 
 សូមទំនាក់ទំនងអ្នកផ្ដល់សេវាអ៊ីនធឺណិតឬអ្នកបច្ចេកទេសរបស់អ្នក ហើយប្រាប់ពួកគេពីបញ្ហាសុវត្ថិភាពដ៏សំខាន់នេះ។',
-'proxyblocksuccess' => 'រួចរាល់ជាស្ថាពរ។',
 'sorbsreason' => 'អាសយដ្ឋាន IP របស់អ្នកមានឈ្មោះក្នុងបញ្ជីប្រុកស៊ី(proxy)ចំហ នៅក្នុង DNSBL របស់ {{SITENAME}}។',
 'sorbs_create_account_reason' => 'អាសយដ្ឋាន IP របស់អ្នកមានឈ្មោះក្នុងបញ្ជីប្រុកស៊ី(proxy)ចំហ នៅក្នុង DNSBL របស់ {{SITENAME}}។
 
index 71ac4a7..060371e 100644 (file)
@@ -1711,8 +1711,6 @@ $2',
 'block-log-flags-nocreate' => 'ಖಾತೆ ಸೃಷ್ಟಿ ತಡೆಹಿಡಿಯಲಾಗಿದೆ',
 'block-log-flags-noemail' => 'ಇ-ಅಂಚೆ ತಡೆಹಿಡಿಯಲಾಗಿದೆ',
 'ipb_already_blocked' => '"$1" ಆಗಲೆ ತಡೆ ಹಿಡಿಯಲಾಗಿದೆ',
-'blockme' => 'ನನ್ನನ್ನು ತಡೆಹಿಡಿ',
-'proxyblocksuccess' => 'ಮುಗಿಯಿತು.',
 
 # Developer tools
 'lockdb' => 'ಡೇಟಾಬೇಸ್ ಅನ್ನು ಮುಚ್ಚು',
index 9497dc9..18e8ba8 100644 (file)
@@ -569,7 +569,7 @@ $messages = array(
 'articlepage' => '문서 보기',
 'talk' => '토론',
 'views' => '보기',
-'toolbox' => '도구모음',
+'toolbox' => '도구',
 'userpage' => '사용자 문서 보기',
 'projectpage' => '프로젝트 문서 보기',
 'imagepage' => '파일 문서 보기',
@@ -881,7 +881,7 @@ $2',
 비밀번호를 받고 다시 로그인해 주세요.',
 'blocked-mailpassword' => '당신의 IP 주소는 편집을 할 수 없게 차단되어 있어서 악용하지 못하도록 비밀번호 되살리기 기능 사용이 금지됩니다.',
 'eauthentsent' => '입력한 이메일로 확인 이메일을 보냈습니다.
-ê²\8cì \95ì\97\90ì\84\9c ë\8b¤ë¥¸ ì\9d´ë©\94ì\9d¼ë¡\9c ë³´ë\82´ê¸° ì \84ì\97\90 ì\9d´ë©\94ì\9d¼ ë\82´ì\9a©ì\9d\98 ì§\80ì\8b\9cë\8c\80ë¡\9c ê³\84ì \95 í\99\95ì\9d¸ ì \88차를 ì\8b¤í\96\89í\95´ ì£¼ì\8b­ì\8b\9cì\98¤.',
+ë\8b¤ë¥¸ ëª¨ë\93  í\98\95í\83\9cì\9d\98 ì\9d´ë©\94ì\9d¼ì\9d\84 ë\8b¹ì\8b ì\9d\98 ê³\84ì \95ì\9c¼ë¡\9c ë³´ë\82´ê¸° ì \84ì\97\90, ê³\84ì \95ì\9d´ ì \95ë§\90 ë\8b¹ì\8b ì\9d\98 ê²\83ì\9d¸ì§\80 í\99\95ì\9d¸í\95\98기 ì\9c\84í\95´ ì\9d´ë©\94ì\9d¼ ë\82´ì\9a©ì\9d\98 ì§\80ì\8b\9cë\8c\80ë¡\9c ê³\84ì \95 í\99\95ì\9d¸ ì \88차를 ì\8b¤í\96\89í\95´ ì£¼ì\85\94ì\95¼ í\95©ë\8b\88ë\8b¤.',
 'throttled-mailpassword' => '비밀번호 재설정 이메일을 이미 최근 {{PLURAL:$1|$1시간}} 안에 보냈습니다.
 악용을 방지하기 위해 비밀번호 재설정 메일은 {{PLURAL:$1|$1시간}}마다 오직 하나씩만 보낼 수 있습니다.',
 'mailerror' => '메일 보내기 오류: $1',
@@ -1339,15 +1339,15 @@ $2개 보다 적게 {{PLURAL:$2|써야}} 하지만 {{PLURAL:$1|지금은 $1개
 * 부적절한 개인 정보
 *: 집 주소, 전화번호, 주민등록번호 등",
 'revdelete-legend' => '보이기 제한을 설정',
-'revdelete-hide-text' => '판의 내용을 숨기기',
+'revdelete-hide-text' => '판 내용',
 'revdelete-hide-image' => '파일을 숨기기',
 'revdelete-hide-name' => '기록 내용과 대상을 숨기기',
-'revdelete-hide-comment' => '편집 요약을 숨기기',
-'revdelete-hide-user' => '편집자의 사용자 이름/IP를 숨기기',
+'revdelete-hide-comment' => '편집 요약',
+'revdelete-hide-user' => '편집자의 사용자 이름/IP 주소',
 'revdelete-hide-restricted' => '관리자도 보지 못하게 숨기기',
 'revdelete-radio-same' => '(바꾸지 않음)',
-'revdelete-radio-set' => '',
-'revdelete-radio-unset' => 'ì\95\84ë\8b\88ì\98¤',
+'revdelete-radio-set' => '보이기',
+'revdelete-radio-unset' => 'ì\88¨ê¸°ê¸°',
 'revdelete-suppress' => '문서 내용을 관리자에게도 보이지 않게 숨기기',
 'revdelete-unsuppress' => '되살린 판에 대한 제한을 해제',
 'revdelete-log' => '이유:',
@@ -2929,12 +2929,9 @@ $1 사용자가 차단된 이유는 다음과 같습니다: "$2"',
 하지만 $2로 광역 차단되었기 때문에, 광역 차단 해제로 차단을 해제할 수 있습니다.',
 'ip_range_invalid' => 'IP 범위가 잘못되었습니다.',
 'ip_range_toolarge' => '/$1보다 넓은 범위의 광역 차단을 할 수 없습니다.',
-'blockme' => '자가 차단',
 'proxyblocker' => '프록시 차단',
-'proxyblocker-disabled' => '이 기능은 비활성되어 있습니다.',
 'proxyblockreason' => '당신의 IP 주소는 공개 프록시로 밝혀져 자동으로 차단됩니다.
 만약 인터넷 사용에 문제가 있다면 인터넷 서비스 공급자나 기술 지원팀에게 문의해주세요.',
-'proxyblocksuccess' => '완료.',
 'sorbsreason' => '당신의 IP 주소는 {{SITENAME}}에서 사용하는 DNSBL 공개 프록시 목록에 들어 있습니다.',
 'sorbs_create_account_reason' => '당신의 IP 주소는 {{SITENAME}}에서 사용하는 DNSBL 공개 프록시 목록에 들어 있습니다.
 계정을 만들 수 없습니다.',
@@ -3305,6 +3302,8 @@ $2',
 'spam_reverting' => '$1(을)를 포함하지 않는 최신 버전으로 되돌림',
 'spam_blanking' => '모든 버전에 $1 링크를 포함하고 있어 차단함',
 'spam_deleting' => '모든 버전에 $1 링크를 포함하고 있어 삭제함',
+'simpleantispam-label' => "스팸 방지 검사입니다.
+이것을 입력하지 '''마세요'''!",
 
 # Info page
 'pageinfo-title' => '"$1" 문서에 대한 정보',
@@ -4095,7 +4094,7 @@ $5
 # Special:Redirect
 'redirect' => '파일, 사용자나 판 ID별 넘겨주기',
 'redirect-legend' => '파일이나 문서로 넘겨주기',
-'redirect-summary' => '이 특수 문서는 파일(파일 이름을 지정), 문서(판 ID를 지정)나 사용자 문서(사용자 ID를 정수로 지정)로 넘겨줍니다.',
+'redirect-summary' => '이 특수 문서는 파일(파일 이름을 지정), 문서(판 ID를 지정)나 사용자 문서(사용자 ID를 정수로 지정)로 넘겨줍니다. 사용법: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/revision/328429]], 혹은 [[{{#Special:Redirect}}/user/101]].',
 'redirect-submit' => '찾기',
 'redirect-lookup' => '찾을 종류:',
 'redirect-value' => '값:',
index 171fd70..b886dce 100644 (file)
@@ -2438,12 +2438,9 @@ $1',
 Алай  а, бу  $2 диапазонну кесеги кибик тыйылгъанды, диапазонну блокдан чыгъарыргъа боллукъсуз.',
 'ip_range_invalid' => 'Джараусуз IP диапазон.',
 'ip_range_toolarge' => '/$1 кёб диапазонланы блокга салыргъа джарамайды.',
-'blockme' => 'Мени блокга сал',
 'proxyblocker' => 'Проксилени тыйыу',
-'proxyblocker-disabled' => 'Бу функция джукълатылыбды.',
 'proxyblockreason' => 'IP-адресигиз ачыкъ прокси болгъаны ючюн тыйылдыгъыз.
 Тилейбиз, кесигизни интернет-провайдеригиз бла, неда дагъан болгъан къуллукъла бла байланыб, къоркъуусузлукъну бу проблемасын билдиригиз.',
-'proxyblocksuccess' => 'Тындырылды.',
 'sorbsreason' => 'IP-адресигиз, {{SITENAME}} сайтда  хайырланнган DNSBL-де ачыкъ прокси кибик саналады.',
 'sorbs_create_account_reason' => 'IP-адресигиз, translatewiki.net сайтда хайырланнган DNSBL-де ачыкъ прокси кибик саналады. Тергеу джазыу къураяллыкъ тюлсюз.',
 'cant-block-while-blocked' => 'Сиз кесигиз блокда заманда, башха къошулуучуланы блок этеллик тюлсюз.',
index 649567c..8d41d92 100644 (file)
@@ -431,7 +431,7 @@ $messages = array(
 'articlepage' => 'Aanluure wat op dä Sigg drop steiht',
 'talk' => 'Klaafe',
 'views' => 'Aansichte',
-'toolbox' => 'Werkzüch',
+'toolbox' => 'Wärkzüsch',
 'userpage' => 'Däm Metmaacher sing Sigg aanluure',
 'projectpage' => 'De Projeksigg aanluure',
 'imagepage' => 'De Sigg övver die Dattei aanluure',
@@ -461,7 +461,7 @@ $1',
 # All link text and link target definitions of links into project namespace that get used by other message strings, with the exception of user group pages (see grouppage).
 'aboutsite' => 'Övver {{GRAMMAR:Akkusativ|{{ucfirst:{{SITENAME}}}}}}',
 'aboutpage' => 'Project:Övver {{GRAMMAR:Akkusativ|{{ucfirst:{{SITENAME}}}}}}',
-'copyright' => 'Dä Enhald steiht unger de $1.',
+'copyright' => 'Dä Enhald steiht unger dä Lezänz $1, ußer wann ußdröklesch jäd anders jesaad es.',
 'copyrightpage' => '{{ns:project}}:Lizenz',
 'currentevents' => 'Et Neuste',
 'currentevents-url' => 'Project:Et Neuste',
@@ -685,7 +685,9 @@ Wann De wells, künnts De Ding [[Special:Preferences|Enschtällonge aanpaße]].'
 'userlogin-resetpassword-link' => 'Paßwoot verjäße?',
 'helplogin-url' => 'Help:Övver et Enlogge',
 'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|Hölp bem Enlogge]]',
+'userlogin-createanother' => 'Donn ene zohsäzlejje Zohjang aanlääje',
 'createacct-join' => 'Jiv Ding Daate en:',
+'createacct-another-join' => 'Maach de nüüdeje Aanjaabe för dä neue Zohjaang.',
 'createacct-emailrequired' => 'Ding Addräß för de <i lang="en">e-mail</i>',
 'createacct-emailoptional' => 'Ding Addräß för de <i lang="en">e-mail</i>, kann fott bliive',
 'createacct-email-ph' => 'Jiv Ding Addräß för de <i lang="en">e-mail</i> en!',
@@ -772,7 +774,7 @@ Ene schöne Jroß vun {{GRAMMAR:Dat|{{SITENAME}}}}.
 'noemailcreate' => 'Do moß en jöltijje Adräß för Ding <i lang="en">e-mail</i> aanjävve',
 'passwordsent' => 'E neu Passwood es aan de E-Mail Adress vun däm Metmaacher „$1“ ungerwähs. Meld dich domet aan, wann De et häs. Dat ahle Passwood bliev erhalde un kann och noch jebruch wääde, bes dat De Dich et eetste Mol met däm Neue enjelogg häs.',
 'blocked-mailpassword' => 'Ding IP Adress es blockeet.',
-'eauthentsent' => 'En <i lang="en">e-mail</i> es jäz ungerwähs aan di Adräß, di en de Enschtällonge schteiht. Ih dat <i lang="en">e-mails</i> övver {{GRAMMAR:Genitiv iere male|{{ucfirst:{{SITENAME}}}}}} <i lang="en">e-mail</i>-Knopp verscheck wääde künne, moß de <i lang="en">e-mail</i>-Adräß eets ens beschtäätesch woode sin. Wat mer doför maache moß, schteiht en dä <i lang="en">e-mail</i> dren, di jrad avjescheck woode es.',
+'eauthentsent' => 'En <i lang="en">e-mail</i> es jäz ungerwähs aan di Adräß en de Enschtällonge. Ih dat mieh <i lang="en">e-mails</i> verscheck wääde künne, moß mer maache, wat en dä <i lang="en">e-mail</i> dren schteiht, öm ze beschtääteje, dat di Adräß schtemmp.',
 'throttled-mailpassword' => 'En Erennerung för di Passwood es alld ongerwähs, un mieh wi eimol en {{PLURAL:$1|der Schtond|$1 Schtonde|nidd ens ener Schtond}} dommer kein schecke.',
 'mailerror' => 'Fähler beim E-Mail Verschecke: $1.',
 'acct_creation_throttle_hit' => '<b>Schad.</b>
@@ -806,6 +808,9 @@ Waad e Wielsche ävver $1, ih dat De et wider versöhks.',
 'loginlanguagelabel' => 'Sproch: $1',
 'suspicious-userlogout' => "Do bes '''nit''' ußjelogg.
 Et süht us, wi wann ene kappodde Brauser udder <i lang=\"en\">proxy</i>ẞööver met Zwescheschpeischer noh däm Ußlogge jefrooch hät.",
+'createacct-another-realname-tip' => 'Dä reschteje Nahme kam_mer fott lohße.
+
+Wann dä aanjejovve es, weet_e jebruch, öm öffentlesch de Schriiver för Beidrääsch ze nänne.',
 
 # Email sending
 'php-mail-error-unknown' => 'Nit bekannte Fähler met dä Funxjohn <code lang="en">mail()</code> vum PHP',
@@ -821,7 +826,7 @@ Et süht us, wi wann ene kappodde Brauser udder <i lang=\"en\">proxy</i>ẞööv
 'newpassword' => 'Et neue Passwood:',
 'retypenew' => 'Noch ens dat neue Passwood:',
 'resetpass_submit' => 'E neu Zweschepasswood övvermeddele un aanmellde',
-'changepassword-success' => 'Passwood jeändert. Jetz küdd_et Enlogge&nbsp;…',
+'changepassword-success' => 'Et Paßwood es jeändert.',
 'resetpass_forbidden' => 'E Passwoot kann nit jeändert wääde.',
 'resetpass-no-info' => 'Do mööts ad enjelogg sin, öm tiräk op di Sigg jonn ze dörve',
 'resetpass-submit-loggedin' => 'Passwood tuusche',
@@ -834,6 +839,8 @@ Do häs Der enzwesche e neu Zweschepaßwood jehollt.',
 
 # Special:PasswordReset
 'passwordreset' => 'Et Paßwoot zeröck säze',
+'passwordreset-text-one' => 'Föll dat Fommolaa uß, öm Ding Paßwoot ze ändere.',
+'passwordreset-text-many' => '{{PLURAL:$1|Föll ei Fäld en däm Fommolaa uß, öm Ding Paßwoot ze ändere.}}',
 'passwordreset-legend' => 'Et Paßwoot zeröck säze',
 'passwordreset-disabled' => 'Et Paßwoot zeröck ze säze es heh em Wiki afjeschalldt.',
 'passwordreset-emaildisabled' => 'Heh dat Wiki määt nix met <i lang="en">e-mail</i>!',
@@ -886,6 +893,9 @@ Do moß Ding Paßwoot enjävve, öm Ding Änderong ze bschtäätejje.',
 'changeemail-submit' => 'Lohß jonn!',
 'changeemail-cancel' => 'Ophüre',
 
+# Special:ResetTokens
+'resettokens-token-label' => '$1 (Em Momang es et: $2)',
+
 # Edit page toolbar
 'bold_sample' => 'Fätte Schreff',
 'bold_tip' => 'Fätte Schreff',
@@ -1129,13 +1139,14 @@ Ene Jrond weße mer nit.',
 'edit-gone-missing' => 'Kunnt di Sigg nit änndere. Se schingk verschwunde un weed fottjeschemeße woode sin.',
 'edit-conflict' => 'Dubbelt beärbeit.',
 'edit-no-change' => 'Do häs ja nix aan dä Sigg jeändert, do dom_mer och nix domet.',
+'postedit-confirmation' => 'Ding Änderunge sin nit faßjehallde.',
 'edit-already-exists' => 'Kunnt kei neu Sigg aanlääje. Di Sigg jidd_et ald.',
 'defaultmessagetext' => 'Dä standaadmäßije Tex',
 'content-failed-to-parse' => 'Et wohr nit müjjelesch, dä Enhalld met däm <i lang="en">MIME-Typ</i> $2 för en Dattei met $1 dren ze verwooschte: $3.',
 'invalid-content-data' => 'Di Daate en dä Sigg sen onjöltesch.',
 'content-not-allowed-here' => 'Ene Enhalld vun dä Zoot „$1“ es op dä Sigg „[[$2]]“ nit zohjelohße.',
-'editwarning-warning' => 'Wann de vun hee dä Sigg fott jeihß, doh künnte all Ding Änderunge aan dä Sigg verschött jonn.
-Do kanns heh di Warnung affschallde, wann de aanjemelldt un enjelogg bes, dann kriß de se nieh mieh wider. Jangk doför en dä Affschnett „{{int:prefs-editing}}“ en Dinge Enshtellunge.',
+'editwarning-warning' => 'Wann de vun hee dä Sigg fott jeihß, doh künnte all Ding Änderonge aan dä Sigg verschött jonn.
+Do kanns heh di Warnung affschallde, wann de aanjemelldt un enjelogg bes, dann kriß de se nieh mieh wider. Jangk doför en dä Afschnett „{{int:prefs-editing}}“ en Dinge Enschtellonge.',
 
 # Content models
 'content-model-wikitext' => 'Wikitäx',
@@ -1155,7 +1166,7 @@ Do kanns heh di Warnung affschallde, wann de aanjemelldt un enjelogg bes, dann k
 'parser-template-loop-warning' => 'Schablon roofe sesch em Kringel op: [[$1]]',
 'parser-template-recursion-depth-warning' => 'Schablone refe sesch zo öff sellver op ($1)',
 'language-converter-depth-warning' => 'Zoh vill Verschachtelunge (övver $1) beim Täx-Ömwandelle vun ein Shprooch en andere.',
-'node-count-exceeded-category' => 'Sigge, woh dä  node-count övverschredde es',
+'node-count-exceeded-category' => 'Sigge, woh dä <i lang="en" xml:lang="en">node-count</i> övverschredde es',
 'node-count-exceeded-warning' => 'Heh di Sigg hät dä <i lang="en" xml:lang="en">node-count</i> övverschredde',
 'expansion-depth-exceeded-category' => 'Sigge, woh de <i lang="en" xml:lang="en">expansion depth</i> övverschredde es',
 'expansion-depth-exceeded-warning' => 'Heh di Sigg hät de <i lang="en" xml:lang="en">expansion depth</i> övverschredde',
@@ -1348,6 +1359,7 @@ Donn de Version makeere bes wohen (inklusive) dat övverdraare wäde sull. Donn
 'compareselectedversions' => 'Dun de markeete Version verjliche',
 'showhideselectedversions' => 'De ußjewählte Versione aanzeije udder vershteiche',
 'editundo' => 'De letzte Änderung zeröck nämme',
+'diff-empty' => '(Keine Ongerscheid)',
 'diff-multi' => '(Mer don hee {{PLURAL:$1|eij Version|$1 Versione|keij Version}} dozwesche beim Verjliesche översprenge. Di sin vun jesamp {{PLURAL:$2|einem Metmaacher|$2 Metmaachere|keinem Metmaacher}} jemaat woode)',
 'diff-multi-manyusers' => '({{PLURAL:$1|Ein Version|$1 Versione|kei Version}} dozwesche vun mieh wi {{PLURAL:$2|einem Metmaacher|$2 Metmaachere|keinem Metmaacher}} wääde nit jezeish)',
 'difference-missing-revision' => '{{PLURAL:$2|Ein Version|$2 Versione}} vun heh däm Verjlisch zwesche Versione ($1) {{PLURAL:$2|hammer}} nit jefonge.
@@ -1637,6 +1649,10 @@ dat dänne ehr Daate topaktoell sin,
 'right-editusercssjs' => 'Anderlücks CSS- un JS-Dateie ändere',
 'right-editusercss' => 'Anderlücks CSS-Dateie ändere',
 'right-edituserjs' => 'Anderlücks JS-Dateie ändere',
+'right-editmyusercss' => 'De eije <i lang="en" xml:lang="en">CSS</i> Datteije aanlääje un ändere',
+'right-editmyuserjs' => 'Eije JaavaSkrepp-Datteije aanlääje un ändere',
+'right-viewmywatchlist' => 'De eije Oppaßleß beloore',
+'right-editmyoptions' => 'De eije Enschtällonge ändere',
 'right-rollback' => 'All de letzte Änderunge fom letzte Metmaacher aan ene Sigg retur maache',
 'right-markbotedits' => 'Retur jemaate Änderonge als Bot-Änderung makeere',
 'right-noratelimit' => 'Kein Beschränkunge dorch Jrenze (<i lang="en">[http://www.mediawiki.org/wiki/Manual:%24wgRateLimits $wgRateLimits]</i>)',
@@ -1688,8 +1704,8 @@ dat dänne ehr Daate topaktoell sin,
 'action-block' => 'hee dämm Metmaacher et Sigge Ändere ze verbeede',
 'action-protect' => 'hee dä Sigg iere Sigge-Schotz ze ändere',
 'action-rollback' => 'all de letzte Änderunge fom letzte Metmaacher aan ene beshtemmpte Sigg flöck retur ze maache',
-'action-import' => 'hee di Sigg uss enem andere Wiki ze empotteere',
-'action-importupload' => 'hee di Sigg uss ene huhjelaade Datei ze impotteere',
+'action-import' => 'Sigge uss_enem andere Wiki ze empotteere',
+'action-importupload' => 'Sigge uss_ene huhjelaade Dattei ze empotteere',
 'action-patrol' => 'anderlüx Änderunge als „nohjeloort“ ze makeere',
 'action-autopatrol' => 'Ding eije Änderunge sälver als „nohjeloort“ ze makeere',
 'action-unwatchedpages' => 'de Leß met de Sigg en kei Oppassleß aanzeloore',
@@ -1698,14 +1714,19 @@ dat dänne ehr Daate topaktoell sin,
 'action-userrights-interwiki' => 'dä Metmaacher fun ander Wikis ier Rääschte ze ändere',
 'action-siteadmin' => 'de Datebank ze sperre udder widder freizejävve',
 'action-sendemail' => '<i lang="en">e-mails</i> ze verschecke',
+'action-editmywatchlist' => 'de eije Oppaßleß ze ändere',
+'action-viewmywatchlist' => 'de eije Oppaßleß ze belooere',
+'action-viewmyprivateinfo' => 'de eije päsöönlesche Aanjaabe ze belooere',
 'action-editmyprivateinfo' => 'Ding päsöönlesche Aanjaabe ze ändere',
 
 # Recent changes
 'nchanges' => '{{PLURAL:$1|Ein Änderong|$1 Änderonge|Kein Änderong}}',
+'enhancedrc-since-last-visit' => '{{PLURAL:$1|Ein|$1|Kein}} zigg_em läzde Aanloore',
 'enhancedrc-history' => 'Väsjohne',
 'recentchanges' => 'Neuste Änderonge',
 'recentchanges-legend' => 'Enstellunge',
 'recentchanges-summary' => 'Op dä Sigg hee sin de neuste Änderunge am Wiki opjeliss.',
+'recentchanges-noresult' => 'Nit verändert en dä Zigg met de aanjejovve Beschrängkonge.',
 'recentchanges-feed-description' => 'Op dämm Abonnomang-Kannal (<i lang="en">Feed</i>) kannze de {{int:recentchanges}} aam Wiki en Laif un en Färve metloore.',
 'recentchanges-label-newpage' => 'Heh di Sigg es neu dobei jekumme met dä Änderung',
 'recentchanges-label-minor' => 'Heh dat es en Mini-Änderung',
@@ -2538,10 +2559,12 @@ Do kanns hee noh Hölp luure:
 'deletecomment' => 'Aanlaß odder Jrund:',
 'deleteotherreason' => 'Ander Jrund oder Zosätzlich:',
 'deletereasonotherlist' => 'Ander Jrund',
-'deletereason-dropdown' => '* Alljemein Jrönde
-** dä Schriever wollt et esu
+'deletereason-dropdown' => '* Alljemein Jrönde för et Fottschmiiße
+** SPAM
+** et wohd jät kapott jemaat
 ** wohr jäje et Urhävverrääsch
-** et wohd jet kapott jemaat',
+** dä Schriever wolld et esu
+** kappodde Ömleidong',
 'delete-edit-reasonlist' => 'De Jrönde för et Fottschmieße beärbeide',
 'delete-toobig' => 'Di Sigg hät {{PLURAL:$1|ein Version|$1 Versione|jaa kein Version}}. Dat sinn_er ärsch fill. Domet unsere ẞööver do nit draan en de Kneen jeit, dom_mer esu en Sigg nit fottschmieße.',
 'delete-warning-toobig' => 'Di Sigg hät {{PLURAL:$1|ein Version|$1 Versione|jakein Version}}. Dat sinn_er ärsch fill. Wann De die all fottschmieße wells, dat kann dem Wiki sing Datenbangk schwer ußbremse.',
@@ -2860,14 +2883,11 @@ Automattesch jesperrte <i lang="en>IP</i>-Addräße sin nit heh, ävver en de [[
 'ipb_blocked_as_range' => 'Dat jeit nit. De IP-Adress „$1“ es nit tirek jesperrt. Se es ävver en däm jesperrte Bereich „$2“ dren. Die Sperr kam_mer ophevve. Donoh kam_mer och kleiner Aandeile fun däm Bereich widder neu sperre. Di Adress alleins kam_mer ävver nit freijevve.',
 'ip_range_invalid' => 'Dä Bereich vun IP_Adresse es nit en Oodnung.',
 'ip_range_toolarge' => 'Berette övver /$1 ze sperre is nit zohjelohße, bloß kleiner.',
-'blockme' => 'Open_Proxy_Blocker',
 'proxyblocker' => 'Open_Proxy_Blocker',
-'proxyblocker-disabled' => 'Di Funxjon es ußjeschalldt.',
 'proxyblockreason' => 'Unger Ding IP_Adress läuf ene offe Proxy.
 Dröm kanns De heh em Wiki nix maache.
 Schwaad met Dingem System-Minsch udder Netzwerk-Techniker udder ISP (<i lang="en">Internet Service Provider</i>)
 un verzäll dänne vun däm ärrje Risiko för de Secherheit fun dänne ehr Rääschnere!',
-'proxyblocksuccess' => 'Jedonn.',
 'sorbs' => '<i lang="en">DNSBL</i>',
 'sorbsreason' => 'Ding IP-Adress weed en de DNSbl als ene offe Proxy jeliss. Schwaad met Dingem System-Minsch oder Netzwerk-Techniker (ISP Internet Service Provider) drüvver, un verzäll dänne vun däm Risiko för ehr Secherheit!',
 'sorbs_create_account_reason' => 'Ding IP-Adress weed en de DNSbl als ene offe Proxy jeliss. Dröm kanns De Dich heh em Wiki nit als ene neue Metmaacher aanmelde. Schwaad met Dingem System-Minsch oder Netzwerk-Techniker oder (ISP Internet Service Provider) drüvver, un verzäll dänne vun däm Risiko för ehr Secherheit!',
@@ -3240,6 +3260,7 @@ Esu kam_mer noch en Aanmärkong en „{{int:summary}}“ maache.',
 'spam_reverting' => 'De letzte Version ohne de Links op „$1“ widder zerröckjehollt.',
 'spam_blanking' => 'All die Versione hatte Links op „$1“, die sin jetz erus jemaht.',
 'spam_deleting' => 'All di Versione met Lenks op „$1“ wääde fott jeschmeße',
+'simpleantispam-label' => 'SPAMschotz — donn hee nix endraare!',
 
 # Info page
 'pageinfo-title' => 'Övver di Sigg: „$1“',
index 2a8eca3..09da332 100644 (file)
@@ -1711,8 +1711,6 @@ Sedemekê binivîse!",
 'ipb_already_blocked' => '"$1" berê hatîye astengkirin',
 'ipb-needreblock' => '$1 berê hatiye astengkirin. Tu dixwazî eyaran biguherînî?',
 'ipb_cant_unblock' => "Şaşbûn: ID'ya astengkirinê $1 nehate dîtin. Astengkirinê xwe niha belkî hatîye rakirin.",
-'blockme' => 'Min astengbike',
-'proxyblocksuccess' => 'Çêbû.',
 'sorbsreason' => "Adrêsa IP ya te ji DNSBL'a {{SITENAME}} wek proxy'eka vekirî tê naskirin.",
 'sorbs_create_account_reason' => "Adrêsa IP ya te ji DNSBL'a {{SITENAME}} wek proxy'eka vekirî tê naskirin. Tu nikarê account'ekê ji xwe ra çêkê.",
 
index 45e12ea..bac3319 100644 (file)
@@ -1309,7 +1309,6 @@ To include a file in a page, use a link in one of the following forms:
 'block-log-flags-nocreate' => 'эсеп жазуусун жаратуу өчүрүлгөн',
 'block-log-flags-noemail' => 'кат жөнөтүүгө тыюу салынган',
 'block-log-flags-hiddenname' => 'колдонуучу аты жашырылган',
-'blockme' => 'Мени бөгөттө',
 'proxyblocker' => 'Проксини блокировкалоо',
 
 # Developer tools
index ff7a910..d7a4bbc 100644 (file)
@@ -824,12 +824,13 @@ Titulus: '''({{int:cur}})''' = dissimilis ab emendatione novissima,
 'revdelete-show-file-submit' => 'Sic',
 'revdelete-selected' => "'''{{PLURAL:$2|Emendatio selecta|Emendationes selectae}} paginae [[:$1]]:'''",
 'revdelete-legend' => 'Modificare cohibitiones visibilitatis',
-'revdelete-hide-text' => 'Celare textum emendationis',
+'revdelete-hide-text' => 'Textus emendationis',
 'revdelete-hide-image' => 'Celare contentum fasciculi',
-'revdelete-hide-comment' => 'Celare summarium emendationis',
+'revdelete-hide-comment' => 'Summarium emendationis',
+'revdelete-hide-user' => 'Nomen usoris/locus IP',
 'revdelete-radio-same' => 'non mutare',
-'revdelete-radio-set' => 'Ita vero',
-'revdelete-radio-unset' => 'Minime',
+'revdelete-radio-set' => 'Visibiles/visibilia',
+'revdelete-radio-unset' => 'Non visibiles/non visibilia',
 'revdelete-log' => 'Causa:',
 'revdel-restore' => 'visibilitatem mutare',
 'revdel-restore-deleted' => 'Recensiones deletae',
@@ -897,6 +898,7 @@ Titulus: '''({{int:cur}})''' = dissimilis ab emendatione novissima,
 'searchprofile-articles-tooltip' => 'Quaerere in $1',
 'searchprofile-project-tooltip' => 'Quaerere in $1',
 'searchprofile-images-tooltip' => 'Fasciculos quaerere',
+'searchprofile-everything-tooltip' => 'Omnia perscrutari (etiam paginae disputationis)',
 'searchprofile-advanced-tooltip' => 'In spatiis nominalibus accommotis quaerere',
 'search-result-size' => '$1 ({{PLURAL:$2|1 verbum|$2 verba}})',
 'search-result-score' => 'Gravitas: $1%',
@@ -1145,6 +1147,7 @@ Si vis id dare, opera tua tibi ascribentur.',
 'recentchanges-label-newpage' => 'Haec recensio paginam novam creavit',
 'recentchanges-label-minor' => 'Haec est recensio minor',
 'recentchanges-label-bot' => 'Hanc emendationem automaton fecit',
+'recentchanges-label-unpatrolled' => 'Haec recensio nondum est examinata',
 'rcnote' => "Subter {{PLURAL:$1|est '''1''' nuper mutatum|sunt '''$1''' nuperrime mutata}} in {{PLURAL:$2|die proximo|'''$2''' diebus proximis}} ex $5, $4.",
 'rcnotefrom' => "Subter sunt '''$1''' nuperrime mutata in proxima '''$2''' die.",
 'rclistfrom' => 'Monstrare mutata nova incipiens ab $1',
@@ -1473,6 +1476,7 @@ Vide etiam [[Special:WantedCategories|categorias desideratas]].',
 'linksearch-pat' => 'Quaerere per exemplar:',
 'linksearch-ns' => 'Spatium nominale:',
 'linksearch-ok' => 'Quaerere',
+'linksearch-line' => '$1 necta est a $2',
 
 # Special:ListUsers
 'listusers-submit' => 'Monstrare',
@@ -1833,10 +1837,7 @@ Commodule notatio obstructionum subter datur.',
 'ipb_already_blocked' => '"$1" iam obstructus est',
 'ipb-needreblock' => '$1 iam obstructus est. Visne obstructionem modificare?',
 'ip_range_invalid' => 'Latitudo IP irrita.',
-'blockme' => 'Usor obstructus',
 'proxyblocker' => 'Instrumentum obstructionis moderatorum',
-'proxyblocker-disabled' => 'Haec functio prohibita est.',
-'proxyblocksuccess' => 'Factum.',
 'cant-block-while-blocked' => 'Dum obstructus es, non potes usores alios obstruere.',
 
 # Developer tools
@@ -2428,6 +2429,7 @@ Quaesumus, adfirma ut iterum hanc paginam crees.",
 
 # Special:Tags
 'tags' => 'Affixa mutationum validarum',
+'tag-filter' => '[[Special:Tags|Tag]] Colum:',
 'tag-filter-submit' => 'Filtrum',
 'tags-title' => 'Affixa',
 'tags-edit' => 'recensere',
index 73cc82c..51ffbd1 100644 (file)
@@ -403,7 +403,7 @@ $messages = array(
 'articlepage' => 'Säit',
 'talk' => 'Diskussioun',
 'views' => 'Affichagen',
-'toolbox' => 'Geschirkëscht',
+'toolbox' => 'Geschir (Tools)',
 'userpage' => 'Benotzersäit',
 'projectpage' => 'Meta-Text',
 'imagepage' => 'Billersäit kucken',
@@ -517,8 +517,8 @@ All Spezialsäiten déi et gëtt, sinn op der [[Special:SpecialPages|{{int:speci
 # General errors
 'error' => 'Feeler',
 'databaseerror' => 'Datebank Feeler',
-'databaseerror-text' => 'Et ass ee Feeler bäi enger Ufro un Datebank geschitt. Dat deit op e Feeler an der Software.',
-'databaseerror-textcl' => "Et ass e Feeler bäi enger Ufro un d'Datebank geschitt.",
+'databaseerror-text' => 'Et ass ee Feeler bei enger Ufro un Datebank geschitt. Dat deit op e Feeler an der Software.',
+'databaseerror-textcl' => "Et ass e Feeler bei enger Ufro un d'Datebank geschitt.",
 'databaseerror-query' => 'Ufro: $1',
 'databaseerror-function' => 'Funktioun: $1',
 'databaseerror-error' => 'Feeler: $1',
@@ -540,7 +540,7 @@ Mellt dëst w.e.g. bei engem [[Special:ListUsers/sysop|Administrateur]] a vergie
 'internalerror' => 'Interne Feeler',
 'internalerror_info' => 'Interne Feeler: $1',
 'fileappenderrorread' => '"$1" konnt während dem Derbäisetze net gelies ginn.',
-'fileappenderror' => '"$1" konnt net bäi "$2" derbäigesat ginn.',
+'fileappenderror' => '"$1" konnt net bei "$2" derbäigesat ginn.',
 'filecopyerror' => 'De Fichier "$1" konnt net op "$2" kopéiert ginn.',
 'filerenameerror' => 'De Fichier "$1" konnt net op "$2" ëmbenannt ginn.',
 'filedeleteerror' => 'De Fichier "$1" konnt net geläscht ginn.',
@@ -597,7 +597,7 @@ Den Administrateur den d\'Schreiwe gespaart huet, huet dës Erklärung uginn: "$
 'exception-nologin-text' => 'Dës Säit oder Aktioun erfuerdert datt Dir op dëser Wiki ageloggt sidd.',
 
 # Virus scanner
-'virus-badscanner' => "Schlecht Configuratioun: onbekannte  Virescanner: ''$1''",
+'virus-badscanner' => "Schlecht Konfiguratioun: onbekannte Virescanner: ''$1''",
 'virus-scanfailed' => 'De Scan huet net funktionéiert (Code $1)',
 'virus-unknownscanner' => 'onbekannten Antivirus:',
 
@@ -634,7 +634,7 @@ Vergiesst net fir Är [[Special:Preferences|{{SITENAME}} Astellungen]] z'ännere
 'userlogout' => 'Ausloggen',
 'notloggedin' => 'Net ageloggt',
 'userlogin-noaccount' => 'Hutt Dir kee Benotzerkont?',
-'userlogin-joinproject' => 'Maacht mat bäi {{SITENAME}}',
+'userlogin-joinproject' => 'Maacht mat bei {{SITENAME}}',
 'nologin' => 'Hutt Dir kee Benotzerkont? $1.',
 'nologinlink' => 'Neie Benotzerkont maachen',
 'createaccount' => 'Neie Kont opmaachen',
@@ -707,7 +707,8 @@ Wann een aneren dës Ufro sollt gemaach hunn oder wann Dir Iech an der Zwëschen
 'passwordsent' => 'Een neit Passwuert gouf un déi fir de Benotzer "$1" gespäichert E-Mailadress geschéckt.
 Mellt Iech w.e.g. domat un, soubal Dir et kritt hutt.',
 'blocked-mailpassword' => "Déi vun Iech benotzten IP-Adress ass fir d'Ännere vu Säite gespaart. Fir Mëssbrauch ze verhënneren, gouf d'Méiglechkeet fir een neit Passwuert unzefroen och gespaart.",
-'eauthentsent' => "Eng Confirmatiouns-E-Mail gouf un déi Adress geschéckt déi Dir uginn hutt.<br />
+'eauthentsent' => "Eng Confirmatiouns-E-Mail gouf un déi Adress geschéckt déi Dir uginn hutt.
+
 Ier iergendeng E-Mail vun anere Benotzer op dee Kont geschéckt ka ginn, musst Dir als éischt d'Instructiounen an der Confirmatiouns-E-Mail befollegen, fir ze bestätegen datt de Kont wierklech Ären eegenen ass.",
 'throttled-mailpassword' => "An {{PLURAL:$1|der leschter Stonn|de leschte(n) $1 Stonnen}} eng E-Mail verschéckt fir d'Passwuert zréckzesetzen.
 Fir de Mëssbrauch vun dëser Funktioun ze verhënneren kann nëmmen all {{PLURAL:$1|Stonn|$1 Stonnen}} sou eng Mail verschéckt ginn.",
@@ -718,7 +719,7 @@ Dofir kënne Visiteure déi dës IP-Adress benotzen den Ament keng Benotzerkonte
 'emailnotauthenticated' => 'Är E-Mail Adress gouf <strong>nach net confirméiert</strong>.<br />
 Dowéinst ass et bis ewell net méiglech, fir déi folgend Funktiounen E-Mailen ze schécken oder ze kréien.',
 'noemailprefs' => 'Gitt eng E-Mailadress bei Ären Astellungen un, fir datt déi Funktioune funktionéieren.',
-'emailconfirmlink' => 'Confirméiert Ã¤r E-Mailadress w.e.g..',
+'emailconfirmlink' => 'Confirméiert Ã\84r E-Mailadress w.e.g.',
 'invalidemailaddress' => 'Dës E-Mail-Adress gëtt net akzeptéiert well se en ongëltegt Format (z. B. ongëlteg Zeechen) ze hu schéngt.
 Gitt eng valabel E-Mail-Adress an oder loosst dëst Feld eidel.',
 'cannotchangeemail' => 'Mailadresse vu Benotzerkonte kënnen op dëser Wiki net geännert ginn.',
@@ -735,7 +736,7 @@ Wann dëse Benotzerkont ongewollt ugeluecht gouf, kënnt Dir dës Noriicht einfa
 Waart w.e.g. $1 ier Dir et nach eng Kéier probéiert.',
 'login-abort-generic' => 'Dir sidd net ageloggt - Aloggen ofgebrach',
 'loginlanguagelabel' => 'Sprooch: $1',
-'suspicious-userlogout' => 'Är Ufro fir Iech auszeloggen gouf refuséiert well et sou ausgesäit wéi wa se vun engem Futtise Browser oder Proxy-Tëschespäicher kënnt.',
+'suspicious-userlogout' => 'Är Ufro fir Iech auszeloggen gouf refuséiert well et sou ausgesäit wéi wa se vun engem futtise Browser oder Proxy-Tëschespäicher kënnt.',
 'createacct-another-realname-tip' => "De richtegen Numm ass fakultativ.
 
 Wann Dir en ugitt, gëtt e benotzt fir d'Benotzerattributiounen fir Är Aarbecht zouzeuerdnen.",
@@ -996,7 +997,7 @@ Den Administrateur den d'Datebank gespaart huet, huet dës Erklärung ginn: $1",
 'template-protected' => '(gespaart)',
 'template-semiprotected' => '(gespaart fir net-ugemellten an nei Benotzer)',
 'hiddencategories' => 'Dës Säit gehéiert zu {{PLURAL:$1|1 verstoppter Kategorie|$1 verstoppte Kategorien}}:',
-'edittools' => '<!-- Dësen Text gëtt ënner dem "Ännere"Formulaire esouwéi dem "Eropluede"-Formulaire ugewisen. -->',
+'edittools' => '<!-- Dësen Text gëtt ënner dem "Ännere"-Formulaire souwéi dem "Eropluede"-Formulaire ugewisen. -->',
 'nocreatetext' => "Op {{SITENAME}} gouf d'Schafe vun neie Säite limitéiert. Dir kënnt Säiten déi scho bestinn änneren oder Iech [[Special:UserLogin|umellen]].",
 'nocreate-loggedin' => 'Dir hutt keng Berechtigung fir nei Säiten unzeleeën.',
 'sectioneditnotsupported-title' => 'Ännere vum Abschnitt gëtt net ënnerstëtzt',
@@ -1144,15 +1145,15 @@ Aner {{SITENAME}}-Administrateure kënnen de geläschten Inhalt oder aner geläs
 * Net ubruechte perséinlechen Informatiounen
 *: ''Adressen, Telefonsnummeren, Sozialversécherungsnummeren asw.''",
 'revdelete-legend' => "Limitatioune fir d'Sichtbarkeet festleeën",
-'revdelete-hide-text' => 'Text vun der Versioun verstoppen',
+'revdelete-hide-text' => 'Text vun der Versioun',
 'revdelete-hide-image' => 'Bildinhalt verstoppen',
 'revdelete-hide-name' => 'Logbuch-Aktioun verstoppen',
-'revdelete-hide-comment' => 'Bemierkung verstoppen',
-'revdelete-hide-user' => 'Dem Auteur säi Benotzernumm/IP verstoppen',
+'revdelete-hide-comment' => 'Resumé vun der Ännerung',
+'revdelete-hide-user' => 'Dem Auteur säi Benotzernumm/IP-Adress',
 'revdelete-hide-restricted' => 'Donnéeën och fir Administrateuren suppriméieren geneesou wéi fir déi Aner',
 'revdelete-radio-same' => '(net änneren)',
-'revdelete-radio-set' => 'Jo',
-'revdelete-radio-unset' => 'Neen',
+'revdelete-radio-set' => 'Visibel',
+'revdelete-radio-unset' => 'Verstoppt',
 'revdelete-suppress' => 'Grond vum Läschen och fir Administrateure verstoppt',
 'revdelete-unsuppress' => 'Limitatiounen fir restauréiert Versiounen ophiewen',
 'revdelete-log' => 'Grond:',
@@ -1449,7 +1450,7 @@ Dës Informatioun ass ëffentlech.",
 'userrights-notallowed' => 'Dir hutt net déi néideg Rechter fir Rechter vun anere Benotzer derbäizesetzen oder ewechzehuelen.',
 'userrights-changeable-col' => 'Gruppen déi Dir ännere kënnt',
 'userrights-unchangeable-col' => 'Gruppen déi Dir net ännere kënnt',
-'userrights-conflict' => 'Konflikt bäi de Benotzerrechter! Kuckt Är Ännerunge w.e.g. no a maacht se w.e.g. nach eng Kéier.',
+'userrights-conflict' => 'Konflikt bei de Benotzerrechter! Kuckt Är Ännerunge w.e.g. no a maacht se w.e.g. nach eng Kéier.',
 'userrights-removed-self' => 'Dir hutt Är eege Rechter ewechgeholl. Dofir kënnt Dir net méi op dës Säit zougräifen.',
 
 # Groups
@@ -1850,7 +1851,7 @@ Kuckt  https://www.mediawiki.org/wiki/Manual:Image_Authorization',
 Nèemmen Datenofruff ass erlaabt.',
 'img-auth-streaming' => '"$1" lueden.',
 'img-auth-public' => "D'Funktioun img_auth.php erlaabt et fir Fichieren vun enger privater Wiki erauszeginn.
-Dës Wiki ass als ëffentlech Wiki configuréiert.
+Dës Wiki ass als ëffentlech Wiki konfiguréiert.
 Fir eng optimal Sécherheet ass img_auth.php ausgeschalt.",
 'img-auth-noread' => 'De Benotzer hut keen Zougang fir "$1" ze liesen',
 'img-auth-bad-query-string' => "D'URL huet eng net valabel Rei vun Zeechen.",
@@ -1862,7 +1863,7 @@ Fir eng optimal Sécherheet ass img_auth.php ausgeschalt.",
 'http-read-error' => 'HTTP-Feeler beim Liesen.',
 'http-timed-out' => 'HTTP-Ufro huet ze laang gebraucht (time out).',
 'http-curl-error' => 'Feeler beim Ofruff vun der URL: $1',
-'http-bad-status' => 'Et gouf e Problem bäi der HTTP-Ufro: $1 $2',
+'http-bad-status' => 'Et gouf e Problem bei der HTTP-Ufro: $1 $2',
 
 # Some likely curl errors. More could be added from <http://curl.haxx.se/libcurl/c/libcurl-errors.html>
 'upload-curl-error6' => "URL ass net z'erreechen",
@@ -2214,9 +2215,9 @@ Et ginn [[{{MediaWiki:Listgrouprights-helppage}}|zousätzlech Informatiounen]] i
 'listgrouprights-removegroup' => 'Kann {{PLURAL:$2|dëse Gruppe|dës Gruppen}} ewechhuelen: $1',
 'listgrouprights-addgroup-all' => 'Kann all Gruppen derbäisetzen',
 'listgrouprights-removegroup-all' => 'Ka Benotzer aus alle Gruppen eraushuelen',
-'listgrouprights-addgroup-self' => "Däerf {{PLURAL:$2|de Grupp|d'Gruppe}} bäi säin eegene Benotzerkont derbäisetzen: $1",
+'listgrouprights-addgroup-self' => "Däerf {{PLURAL:$2|de Grupp|d'Gruppe}} bei säin eegene Benotzerkont derbäisetzen: $1",
 'listgrouprights-removegroup-self' => "Däerf {{PLURAL:$2|de Grupp|d'Gruppe}} vu sengem eegene Benotzerkont ewechhuelen: $1",
-'listgrouprights-addgroup-self-all' => 'däerf all Gruppe bäi säin eegene Benotzerkont derbäisetzen',
+'listgrouprights-addgroup-self-all' => 'däerf all Gruppe bei säin eegene Benotzerkont derbäisetzen',
 'listgrouprights-removegroup-self-all' => 'Däerf all Gruppe vu sengem eegene Benotzerkont ewechhuelen',
 
 # Email user
@@ -2281,7 +2282,7 @@ All weider Ännerungen op dëser Säit an der associéierter Diskussiounssäit g
 'watchmethod-recent' => 'Rezent Ännerunge ginn op iwwerwaacht Säiten iwwerpréift',
 'watchmethod-list' => 'Iwwerwaachte Säite ginn op rezent Ännerungen iwwerpréift',
 'watchlistcontains' => 'Op ärer Iwwerwaachungslëscht $1 {{PLURAL:$1|steet $1 Säit|stinn $1 Säiten}}.',
-'iteminvalidname' => "Problem mat dem Objet '$1', ongëltegen Numm ...",
+'iteminvalidname' => "Problem mam Element '$1', ongëltegen Numm ...",
 'wlnote' => "Hei {{PLURAL:$1|ass déi lescht Ännerung|sinn déi lescht '''$1''' Ännerunge}} vun {{PLURAL:$2|der leschter Stonn|de leschte(n) '''$2''' Stonnen}}, Stand: $3 ëm $4 Auer.",
 'wlshowlast' => "D'Ännerunge vun de leschte(n) $1 Stonnen, $2 Deeg oder $3 (an de leschten 30 Deeg) weisen.",
 'watchlist-options' => 'Optioune vun der Iwwerwaachungslëscht',
@@ -2396,9 +2397,9 @@ Déi lescht Ännerung vun der Säit ass vum [[User:$3|$3]] ([[User talk:$3|Disku
 
 # Edit tokens
 'sessionfailure-title' => 'Setzungsfeeler',
-'sessionfailure' => 'Et schéngt e Problem mat Ã¤rer Loginséance ze ginn;
-Dës Aktioun gouf aus Sécherheetsgrënn ofgebrach, fir ze verhënneren datt Ã¤r Séance piratéiert ka ginn.
-Klickt w.e.g. op "Zréck" a lued déi Säit vun där Dir komm sidd nei, a versicht et dann nach eng Kéier.',
+'sessionfailure' => 'Et schéngt e Problem mat Ã\84rer Loginseance ze ginn;
+Dës Aktioun gouf aus Sécherheetsgrënn ofgebrach, fir ze verhënneren datt Ã\84r Seance piratéiert ka ginn.
+Klickt w.e.g. op "Zréck" a luet déi Säit vun där Dir komm sidd nei, a versicht et dann nach eng Kéier.',
 
 # Protect
 'protectlogpage' => 'Protektiounslogbuch',
@@ -2685,11 +2686,8 @@ Kuckt d'[[Special:BlockList|Spärlëscht]] fir déi aktuell Spären.",
 Si ass awer als Deel vun der Rei $2 gespaart, an dës Spär kann opgehuewe ginn.",
 'ip_range_invalid' => 'Ongëltegen IP Block.',
 'ip_range_toolarge' => 'Späre vu Beräicher déi méi grouss wéi /$1 si sinn net erlaabt.',
-'blockme' => 'Spär mech',
 'proxyblocker' => 'Proxy blocker',
-'proxyblocker-disabled' => 'Dës Funktioun ass ausgeschalt.',
 'proxyblockreason' => 'Är IP-Adress gouf gespaart, well si een oppene Proxy ass. Kontaktéiert w.e.g. ären Internet-Provider oder ärs Systemadministrateuren und informéiert si iiwwer dëses méigleche Sécherheetsprobleem.',
-'proxyblocksuccess' => 'Gemaach.',
 'sorbsreason' => 'Är IP Adress steet als oppene Proxy an der schwaarzer Lëscht (DNSBL) déi vu {{SITENAME}} benotzt gëtt.',
 'sorbs_create_account_reason' => 'Är IP-Adress steet als oppene Proxy an der schwaarzer Lëscht déi op {{SITENAME}} benotzt gëtt. DIr kënnt keen neie Benotzerkont opmaachen.',
 'xffblockreason' => 'Eng IP-Adress am X-Forwarded-For-Header gouf gespaart, entweder Är oder déi vum Proxyserver deen Dir benotzt. De Grond vun der Spär war: $1',
@@ -2965,7 +2963,7 @@ Späichert en op Ärem Computer of a luet en hei nees erop.',
 'tooltip-n-mainpage' => "Besicht d'Haaptsäit",
 'tooltip-n-mainpage-description' => "Besicht d'Haaptsäit",
 'tooltip-n-portal' => 'Iwwer de Portal, wat Dir maache kënnt, wou wat ze fannen ass',
-'tooltip-n-currentevents' => "D'Aktualitéit a wat derhanner ass",
+'tooltip-n-currentevents' => "D'Aktualitéit a wat dohanner ass",
 'tooltip-n-recentchanges' => 'Lëscht vun de rezenten Ännerungen op {{SITENAME}}.',
 'tooltip-n-randompage' => 'Zoufälleg Säit',
 'tooltip-n-help' => 'Hëllefsäiten weisen.',
@@ -3039,6 +3037,8 @@ Dëst wahrscheinlech duerch en externe Link den op der schwaarzer Lëscht (black
 'spam_reverting' => 'Déi lescht Versioun ouni Linken op $1 restauréieren.',
 'spam_blanking' => 'An alle Versioune ware Linken op $1, et ass elo alles gebotzt.',
 'spam_deleting' => 'All Versioune mat Linken op $1 gi geläscht',
+'simpleantispam-label' => "Anti-Spam Kontroll.
+Fëllt dëst '''NET''' aus!",
 
 # Info page
 'pageinfo-title' => 'Informatioun iwwer "$1"',
@@ -3319,7 +3319,7 @@ Déi aner sinn am Standard verstoppt.
 'exif-gpsdestlongitude' => 'Längt',
 'exif-gpsdestbearingref' => "Referenz fir d'Motivrichtung",
 'exif-gpsdestbearing' => 'Richtung vum Motiv',
-'exif-gpsdestdistanceref' => "Referenz fir d'Distanz bis bäi den Objet (vun der Foto)",
+'exif-gpsdestdistanceref' => "Referenz fir d'Distanz bis bei den Objet (vun der Foto)",
 'exif-gpsdestdistance' => 'Motivdistanz',
 'exif-gpsprocessingmethod' => 'Numm vun der GPS-Prozedur-Method',
 'exif-gpsareainformation' => 'Numm vun der GPS-Géigend',
@@ -3787,7 +3787,8 @@ Dir misst eng [{{SERVER}}{{SCRIPTPATH}}/COPYING Kopie vun der GNU General Public
 # Special:Redirect
 'redirect' => 'Viruleedung duerch e Fichier, Benotzer oder Versiouns-ID',
 'redirect-legend' => 'Viruleedung op ee Fichier oder eng Säit',
-'redirect-summary' => 'Dës Spezialsäit ass eng Viruleedung op e Fichier (Fichiersnumm uginn), eng Säit (Versiounsnummer uginn) oder eng Benotzersäit (numeresch Benotzeridentifikatioun uginn).',
+'redirect-summary' => 'Dës Spezialsäit ass eng Viruleedung op e Fichier (Fichiersnumm uginn), eng Säit (Versiounsnummer uginn) oder eng Benotzersäit (numeresch Benotzeridentifikatioun uginn).
+Gebrauch: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/revision/328429]], oder [[{{#Special:Redirect}}/user/101]].',
 'redirect-submit' => 'Lass',
 'redirect-lookup' => 'Nosichen:',
 'redirect-value' => 'Wäert:',
@@ -3941,7 +3942,7 @@ Soss kënnt Dir den einfache Formulär hei drënner benotzen. Är Bemierkung gë
 'feedback-message' => 'Message:',
 'feedback-cancel' => 'Ofbriechen',
 'feedback-submit' => 'Feedback schécken',
-'feedback-adding' => "Feedback gëtt bäi d'Säit derbäigesat...",
+'feedback-adding' => "Feedback gëtt bei d'Säit derbäigesat...",
 'feedback-error1' => 'Feeler: Resultat vum API gouf net erkannt',
 'feedback-error2' => "Feeler: D'Ännerung gouf net gespäichert",
 'feedback-error3' => 'Feeler: Keng Äntwert vum API',
index 95b479f..f038432 100644 (file)
@@ -141,7 +141,7 @@ $messages = array(
 'cancel' => 'Cansela',
 'moredotdotdot' => 'Plu...',
 'mypage' => 'Me paje',
-'mytalk' => 'Me discutes',
+'mytalk' => 'Discutes',
 'anontalk' => 'Discutes per esta IP',
 'navigation' => 'Naviga',
 'and' => '&#32;e',
@@ -444,7 +444,7 @@ La arcivo de sutraes per esta paje es asi per conveni:",
 
 # Preferences page
 'preferences' => 'Preferis',
-'mypreferences' => 'Me preferis',
+'mypreferences' => 'Preferis',
 'skin-preview' => 'Previde',
 'saveprefs' => 'Fisa',
 'rows' => 'Linias:',
@@ -473,6 +473,7 @@ La arcivo de sutraes per esta paje es asi per conveni:",
 'gender-female' => 'Fema',
 'email' => 'Eposta',
 'prefs-help-realname' => 'Tu nom vera no es obligada, ma si tu vole dona tu nom vera, el va es usada per onora tu per tu labora.',
+'prefs-signature' => 'Suscrive',
 
 # User rights
 'userrights' => 'Dirije de la diretos de usores',
@@ -518,7 +519,7 @@ La arcivo de sutraes per esta paje es asi per conveni:",
 'newpageletter' => 'N',
 'boteditletter' => 'b',
 'rc_categories_any' => 'Cualce',
-'rc-enhanced-expand' => 'Mostra detalias (JavaScript es nesesada)',
+'rc-enhanced-expand' => 'Mostra detalias',
 'rc-enhanced-hide' => 'Asconde detalias',
 
 # Recent changes linked
@@ -554,7 +555,7 @@ Pajes a [[Special:Watchlist|tu lista de pajes oservada]] es en leteras '''forte'
 'filehist-dimensions' => 'Mesuras',
 'filehist-filesize' => 'Grandia de fix',
 'filehist-comment' => 'Comenta',
-'imagelinks' => 'Lias de fix',
+'imagelinks' => 'Usas de fix',
 'linkstoimage' => 'Esta {{PLURAL:$1|paje|pajes}} lia a esta fix:',
 'nolinkstoimage' => 'Es no pajes ce lia a esta fix.',
 'sharedupload' => 'Esta fix es parte de $1 e pote es usada par otra projetas.',
@@ -663,7 +664,7 @@ Also see [[Special:WantedCategories|wanted categories]].',
 
 # Watchlist
 'watchlist' => 'Pajes oservada',
-'mywatchlist' => 'Me lista de pajes oservada',
+'mywatchlist' => 'Lista de pajes oservada',
 'nowatchlist' => 'Tu ave no cosas en tu lista oservada',
 'addedwatchtext' => "La paje \"[[:\$1]]\" ia es juntada a tu [[Special:Watchlist|lista de pajes oservada]].
 Cambias future a esta paje e se paje de discutes va es listada ala, e la paje va apera en leteras '''forte''' en la [[Special:RecentChanges|lista de cambias resente]] per es plu fasil oservada.
index 222c7fd..3c042be 100644 (file)
@@ -2450,11 +2450,8 @@ Wils se de instellinge wiezige?',
 'ipb_blocked_as_range' => "Fout: 't IP-adres $1 is neet direct geblokkeerd en de blokkade kan neet opgeheve waere. De blokkade is ongerdeil van de reeks $2, wovan de blokkade waal opgeheve kan waere.",
 'ip_range_invalid' => 'Ongeldige IP-reeks',
 'ip_range_toolarge' => 'Reeksblokkades groeater es /$1 kènne neet.',
-'blockme' => 'Blokkeer mich',
 'proxyblocker' => 'Proxyblokker',
-'proxyblocker-disabled' => 'Deze functie is oetgesjakeld.',
 'proxyblockreason' => "Dien IP-adres is geblokkeerd ómdat 't 'n aope proxy is. Contacteer estebleef diene internet service provider of technische óngersjteuning en informeer ze euver dit serjeus veiligheidsprebleem.",
-'proxyblocksuccess' => 'Klaor.',
 'sorbsreason' => 'Dien IP-adres is opgenaome in de DNS-blacklist es open proxyserver, dae {{SITENAME}} gebroek.',
 'sorbs_create_account_reason' => 'Dien IP-adres is opgenómme in de DNS-blacklist es open proxyserver, dae {{SITENAME}} gebroek. De kèns gein gebroekersaccount aanmake.',
 'cant-block-while-blocked' => 'De kins anger gebroekers neet blokkere terwiel se zelf geblokkeerd bös.',
@@ -2780,6 +2777,8 @@ Meistal wörd dit door 'ne zwarte externe link veroorzaak.",
 'spam_reverting' => 'Bezig mit trökdrèjje nao de letste versie die gein verwiezing haet nao $1',
 'spam_blanking' => "Alle wieziginge mit 'ne link nao $1 waere verwiederd",
 'spam_deleting' => 'Alle wieziginge hawwe links nao $1, wuuertj gewösj',
+'simpleantispam-label' => "Antispemcontrole.
+Vol dit veld '''NEET''' in!",
 
 # Info page
 'pageinfo-title' => 'Informatie euver "$1"',
index 6adb664..0fafbb0 100644 (file)
@@ -837,7 +837,6 @@ Petohoni di petulo '''$1''' sa:",
 'contribslink' => 'afina',
 'blocklogpage' => 'Desu di bolok',
 'blocklogentry' => 'sa bolok [[$1]] e simpekile sa $2 $3',
-'proxyblocksuccess' => 'Afi.',
 
 # Move page
 'move-page-legend' => 'Nyanganyisize petulo',
index 0efe4a3..1c977f1 100644 (file)
@@ -2600,11 +2600,8 @@ Jei norite pamatyti dabar blokuojamus adresus, žiūrėkite [[Special:BlockList|
 'ipb_blocked_as_range' => 'Klaida: IP $1 nebuvo užblokuotas tiesiogiai, tad negali būti atblokuotas. Tačiau jis buvo užblokuotas kaip srities $2 dalis, kuri gali būti atblokuota.',
 'ip_range_invalid' => 'Neleistina IP sritis.',
 'ip_range_toolarge' => 'Didesni nei /$1 blokai neleidžiami.',
-'blockme' => 'Užblokuoti mane',
 'proxyblocker' => 'Tarpinių serverių („proxy“) blokavimo priemonė',
-'proxyblocker-disabled' => 'Ši funkcija yra išjungta.',
 'proxyblockreason' => 'Jūsų IP adresas yra užblokuotas, nes jis yra atvirasis tarpinis serveris. Prašome susisiekti su savo interneto paslaugų tiekėju ar technine pagalba ir praneškite jiems apie šią svarbią saugumo problemą.',
-'proxyblocksuccess' => 'Atlikta.',
 'sorbsreason' => 'Jūsų IP adresas yra įtrauktas į atvirųjų tarpinių serverių DNSBL sąrašą, naudojamą šios svetainės.',
 'sorbs_create_account_reason' => 'Jūsų IP adresas yra įtrauktas į atvirųjų tarpinių serverių DNSBL sąrašą, naudojamą šios svetainės. Jūs negalite sukurti paskyros',
 'cant-block-while-blocked' => 'Jūs negalite blokuoti kitų naudotojų, pats būdamas užblokuotas.',
@@ -2960,6 +2957,8 @@ Leidžia pridėti atmetimo priežastį komentaruose',
 'spam_reverting' => 'Atkuriama į ankstesnę versiją, neturinčios nuorodų į $1',
 'spam_blanking' => 'Visos versijos turėjo nuorodų į $1, išvaloma',
 'spam_deleting' => 'Visos versijos turėjo nuorodų į $1, ištrinama',
+'simpleantispam-label' => "Anti-spam patikra.
+'''NE'''pildykite!",
 
 # Info page
 'pageinfo-title' => '„$1“ informacija',
index 1b4b7f5..558d6c0 100644 (file)
@@ -105,8 +105,6 @@ $messages = array(
 'category-article-count' => '{{PLURAL:$2|Itymā kategorejā ir vīn dūtuo puslopa.|{{PLURAL:$1|Paruodeita $1 puslopa|Paruodeitys $1 puslopys}} nu $2.}}',
 'listingcontinuesabbrev' => '(tuoļuojums)',
 
-'linkprefix' => '/^(.*?)([a-zA-Z\\x80-\\xff]+)$/sD',
-
 'about' => 'Aproksts',
 'article' => 'Rakstīņs',
 'newwindow' => '(atdareišona jaunuo puslopā)',
index c97461d..f636b64 100644 (file)
@@ -1939,10 +1939,12 @@ Papildinformācija:
 'deletecomment' => 'Iemesls:',
 'deleteotherreason' => 'Cits/papildu iemesls:',
 'deletereasonotherlist' => 'Cits iemesls',
-'deletereason-dropdown' => '*Izplatīti dzēšanas iemesli
-** Autora pieprsījums
+'deletereason-dropdown' => '* Izplatīti dzēšanas iemesli
+** Spams
+** Vandālisms
 ** Autortiesību pārkāpums
-** Vandālisms',
+** Autora pieprasījums
+** Nederīga pāradresācija',
 'delete-edit-reasonlist' => 'Izmainīt dzēšanas iemeslus',
 'delete-toobig' => 'Šai lapai ir liela izmaiņu hronoloģija, vairāk nekā $1 {{PLURAL:$1|versija|versijas}}.
 Šādu lapu dzēšana ir atslēgta, lai novērstu nejaušus traucējumus {{grammar:lokatīvs|{{SITENAME}}}}.',
@@ -2079,6 +2081,7 @@ $1',
 # Namespace form on various pages
 'namespace' => 'Vārdtelpa:',
 'invert' => 'Izvēlēties pretēji',
+'namespace_association' => 'Saistītā vārdtelpa',
 'blanknamespace' => '(Pamatlapa)',
 
 # Contributions
@@ -2231,10 +2234,7 @@ Tas, iespējams, jau ir atbloķēts.',
 'ipb_blocked_as_range' => 'Kļūda: IP $1 nav bloķēta tieši, tāpēc to nevar atbloķēt.
 Tā ir bloķēta kā daļa no IP adrešu diapazona $2, kuru var atbloķēt.',
 'ip_range_invalid' => 'Nederīgs IP diapazons',
-'blockme' => 'Bloķēt mani',
 'proxyblocker' => 'Starpniekservera bloķētājs',
-'proxyblocker-disabled' => 'Šī funkcija ir atspējota.',
-'proxyblocksuccess' => 'Darīts.',
 'cant-block-while-blocked' => 'Tu nevari bloķēt citus lietotājus, kamēr pats esi bloķēts.',
 'ipbblocked' => 'Tu nevar bloķēt vai atbloķēt lietotājus, jo Tu pats esi bloķēts',
 'ipbnounblockself' => 'Tev nav atļauts sevi atbloķēt',
@@ -2515,6 +2515,8 @@ To visticamāk izraisīja ārēja saite uz melnajā sarakstā esošu interneta v
 'spamprotectionmatch' => 'Spama filtram radās iebildumi pret šo tekstu: $1',
 'spambot_username' => 'MediaWiki surogātpasta tīrīšana',
 'spam_reverting' => 'Atjauno iepriekšējo versiju, kas nesatur saiti uz $1',
+'simpleantispam-label' => "Pretspama pārbaude. 
+ '''NEAIZPILDĪT!'''",
 
 # Info page
 'pageinfo-title' => 'Informācija par "$1"',
@@ -2641,6 +2643,7 @@ Pārējie lauki, pēc noklusējuma, būs paslēpti.
 'exif-imagelength' => 'augstums',
 'exif-bitspersample' => 'biti komponentē',
 'exif-compression' => 'Saspiešanas veids',
+'exif-photometricinterpretation' => 'Pikseļu sastāvs',
 'exif-orientation' => 'Orientācija',
 'exif-samplesperpixel' => 'Komponentu skaits',
 'exif-planarconfiguration' => 'Datu izkārtojums',
@@ -3197,7 +3200,7 @@ Var arī lietot [[Special:EditWatchlist|standarta izmainīšanas lapu]].',
 'logentry-newusers-newusers' => 'Lietotāja konts $1 tika {{GENDER:$2|izveidots}}',
 'logentry-newusers-create' => 'Lietotāja konts $1 tika {{GENDER:$2|izveidots}}',
 'logentry-newusers-create2' => '$1 {{GENDER:$2|izveidoja}} lietotāja kontu $3',
-'logentry-newusers-autocreate' => 'Konts $1 tika {GENDER:$2|izveidots}} automātiski',
+'logentry-newusers-autocreate' => 'Lietotaja konts $1 tika {{GENDER:$2|izveidots}} automātiski',
 'rightsnone' => '(nav)',
 
 # Feedback
index 1af650a..c57b0a5 100644 (file)
@@ -2212,9 +2212,7 @@ $1',
 'ipb_blocked_as_range' => '錯:該IP $1 無直禁也,無赦之。唯它在 $2 之範禁內,其範可赦之。',
 'ip_range_invalid' => 'IP址圍不格',
 'ip_range_toolarge' => '大於 /$1 之禁段乃無容也。',
-'blockme' => '自禁',
 'proxyblocker' => '禁Proxy',
-'proxyblocksuccess' => '成矣。',
 'cant-block-while-blocked' => '爾然被禁,勿施於人。',
 'cant-see-hidden-user' => '簿禁或藏矣。
 爾無藏之權,無視纂禁也。',
index 187fbcb..70e2146 100644 (file)
@@ -2300,12 +2300,9 @@ $1 एकर प्रतिबन्धक कारण अछि : "$2"',
 ई, मुदा, क्षेत्र $2 क अन्दर प्रतिबन्धित अछि, जे अप्रतिबन्धित कएल जा सकैए।',
 'ip_range_invalid' => 'अमान्य अनिकेत क्षेत्र।',
 'ip_range_toolarge' => 'समूह खण्ड पैघ अछि /$1 सभकेँ अनुमति नै छै।',
-'blockme' => 'हमरा प्रतिबन्धित करू',
 'proxyblocker' => 'दोसराइत प्रतिबन्धक',
-'proxyblocker-disabled' => 'ई प्रकार्य प्रतिबन्धित अछि।',
 'proxyblockreason' => 'अहाँक अनिकेत पता प्रतिबन्धित भेल अछि कारण ई सोझे-सोझ दोसराइत अछि।
 अहाँ अपन अन्तर्जाल सेवा दाता वा तकनीकी सहायकसँ सम्पर्क करू आ ऐ गम्भीर सुरक्षा समस्याक सूचना दिअ।',
-'proxyblocksuccess' => 'भेल।',
 'sorbsreason' => 'अहाँक अनिकेत सूचित अछि सोझे-सोझ दोसराइतक रूपमे {{जालस्थल}} क डी.एन.एस.बी.एल.मे।',
 'sorbs_create_account_reason' => 'अहाँक अनिकेत एतए सूचित अछि खुजल दोसराइत सन डी.एन.बी.एस.एल. मे जे प्रयोग कएल जाइए {{अन्तर्जाल}} द्वारा।',
 'cant-block-while-blocked' => 'अहाँ जाधरि स्वयं प्रतिबन्धित छी दोसराकेँ प्रतिबन्धित नै कऽ सकै छी।',
index 2468aab..746a2e1 100644 (file)
@@ -2029,12 +2029,9 @@ $1',
 'ipb_blocked_as_range' => 'Эльбятькс: IP $1 аф кардаф видеста ди соннеда аш кода кардафкссь валхтомс.
 Сон, интай улема, кардафоль кода  $2-нь потмонц пакшец, конань эзда ули кода кардафксонь валхтомс.',
 'ip_range_invalid' => 'Аф кондясти IP потмоц.',
-'blockme' => 'Кардамань монь',
 'proxyblocker' => 'Ётка якаень сёлгома',
-'proxyblocker-disabled' => 'Тя функциесь аф тиеви.',
 'proxyblockreason' => 'Тонь IP адрес пякстафоль сонь панжада прокси-серверонь эзда.
 Тондейть эряви сотомс тонь интернет ладяйть эли текникань нежедемать мархта ди пачфтемс тя прябалать колга.',
-'proxyblocksuccess' => 'Тиф.',
 'sorbsreason' => 'Тонь IP адресце лувови панжада ётка якай сервероннекс DNSBL-са, конась нолдаф тевс {{SITENAME}}са.',
 'sorbs_create_account_reason' => 'Тонь IP адресце лувови панжада ётка якань сервероннекс DNSBL-са конась нолнезь тевс {{SITENAME}}са.
 Тондейть аш кода сёрматфтомать тиемс',
index 5e71254..5386a66 100644 (file)
@@ -464,7 +464,7 @@ $1',
 # All link text and link target definitions of links into project namespace that get used by other message strings, with the exception of user group pages (see grouppage).
 'aboutsite' => 'Mombamomba ny {{SITENAME}}',
 'aboutpage' => 'Project:Mombamomba',
-'copyright' => '$1 no mifehy ny fampiasana ny votoatin-kevitra eto.',
+'copyright' => 'Ny lisansa $1 no mamehy ny fampiasana ity voatoatiny ity.',
 'copyrightpage' => '{{ns:project}}:Copyright',
 'currentevents' => 'Ny vaovao',
 'currentevents-url' => 'Project:Vaovao',
@@ -680,6 +680,7 @@ Mila manaiky cookies ianao raha te hiditra amin'ny {{SITENAME}}.",
 'userlogin-resetpassword-link' => 'Hamerina ny tenimiafinao',
 'helplogin-url' => 'Help:Fidirana',
 'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|Fanoroana mikasika ny fidirana]]',
+'userlogin-createanother' => 'Hamorona kaonty hafa',
 'createacct-join' => 'Atsofohy eo ambany ny fampahalalana momba anao.',
 'createacct-another-join' => "Atsofohy eo ambany ny fampahalalana vaovaon'ny kaonty",
 'createacct-emailrequired' => 'Adiresy mailaka :',
@@ -1428,7 +1429,7 @@ Tokony mba manana lohavy ambanimbany kokoa non'ny $1",
 'gender-unknown' => 'Tsy tia hanome ny antsipirihany aho',
 'gender-male' => 'Manova pejy wiki izy (lehilahy)',
 'gender-female' => 'Manova pejy wiki izy (vehivavy)',
-'prefs-help-gender' => "Ankifidy : Ampiasaina mba hifanaraka amin'ny lahi-vavy. Ho sarababem-bahoaka io fampahalalàna io.",
+'prefs-help-gender' => "Ankifidy : ampiasaina ho an'ny fifandraisan'ny rindrankajy aminao. Ho sarababem-bahoaka ity fampahalalana ity.",
 'email' => 'Imailaka',
 'prefs-help-realname' => "Anarana marina (afaka tsy fenoina): raha fenoinao ity dia hampiasaina hanomezana anao tambin'ny asa izay efainao eto.",
 'prefs-help-email' => 'Azo tsy omena ny adiresy imailaka, fa ilaina izy io raha sendra hadino ny tenimiafinao.',
@@ -1610,8 +1611,8 @@ Tsy haseho ny adiresy imailakao rehefa manoratra any aminao ny mpikambana hafa."
 'action-block' => 'manakana am-panoratana ny mpikambana iray',
 'action-protect' => "manova ny fanovàn'ity pejy ity",
 'action-rollback' => "Manafoana haingana ny fanovan'ny mpikambana farany nanova pejy iray",
-'action-import' => 'mampiditra ity pejy ity avy amina wiki hafa',
-'action-importupload' => 'hampiditra ity pejy ity avy amina rakitra nampidirina',
+'action-import' => "hampiditra ity pejy ity avy amin'ny wiki hafa",
+'action-importupload' => "hampiditra ity pejy ity amin'ny fampidirana rakitra",
 'action-patrol' => 'marihana ho hita ity version ity',
 'action-autopatrol' => 'manana ny fanovanao voamarina',
 'action-unwatchedpages' => 'hijery ny lisitry ny pejy tsy arahina',
@@ -1915,6 +1916,7 @@ Rehefa sivanin'ny mpikambana iray izy ity, ny rakitra izay ahitana santiôna vao
 'listfiles_size' => 'Habe',
 'listfiles_description' => 'Visavisa',
 'listfiles_count' => 'Version',
+'listfiles-latestversion' => 'Filaza ankehitriny',
 'listfiles-latestversion-yes' => 'Eny',
 'listfiles-latestversion-no' => 'Tsia',
 
@@ -1941,8 +1943,12 @@ Rehefa sivanin'ny mpikambana iray izy ity, ny rakitra izay ahitana santiôna vao
 'morelinkstoimage' => "Hijery [[Special:WhatLinksHere/$1|rohy fanampiny]] makany amin'io rakitra io.",
 'linkstoimage-redirect' => '$1 (fihodinana) $2',
 'sharedupload' => "Mety ho rakitra itambarana amin'ny tetikasa hafa ny rakitra $1.",
+'sharedupload-desc-there' => "Avy amin'i $1 ity rakitr aity ary mety ampiasaina any amin'ny tetikasa hafa.
+Jereo [$2 ny pejy famisavisana ilay rakitra] ho an'ny fampahalalana fanampiny.",
 'sharedupload-desc-here' => "Avy amin'i $1 io rakitra io ary mety ampiasain'ny tetikasa hafa.
 Aseho eo ambany ny [$2 famisavisana ilay rakitra].",
+'sharedupload-desc-edit' => "Avy amin'i $1 ity rakitra ity ka mety ampiasaina any amina tetikasa hafa.
+Mety tia hanova ny famisavisany ianao any amin'ny [$2 pejy famisavisana rakitra] any.",
 'filepage-nofile' => 'Tsy nahitana rakitra mitondra io anarana io.',
 'filepage-nofile-link' => 'Tsy misy rakitra mitondra io anarana io, fa afaka [$1 mampiditra azy ianao].',
 'uploadnewversion-linktext' => "Andefa version vaovao n'ity rakitra ity",
@@ -2379,7 +2385,7 @@ ataovy am-pitandremana ity tao ity.",
 Efa nataon'i [[User:$3|$3]] ([[User talk:$3|dinika]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]) ny fanovana farany.",
 'editcomment' => "Toy izao no fanamarihana momba io fanovana io: \"''\$1''\".",
 'revertpage' => "Voafafa ny fanovana ny [[Special:Contributions/$2|$2]] ([[User talk:$2|Dinika]]); voaverina amin'ny votoatiny teo aloha nataon'i [[User:$1|$1]]",
-'revertpage-nouser' => "Manala ny fanovana (nataon'ny anaram-pikambana nesorina), miverina any amin'ny santiona farany nataon'i  [[User:$1|$1]]",
+'revertpage-nouser' => "Manala ny fanovana (nataom-pikambana voaafina), miverina any amin'ny filaza faran'i  [[User:$1|$1]]",
 'rollback-success' => "Fanalàna ny fanovana nataon'i $1 ;
 miverina any amin'ny santiôna farany nataon'i $2.",
 
@@ -2398,7 +2404,7 @@ Ho ann'y fanazavana fanampiny, jereo [[Special:ProtectedPages|ny lisitry ny pejy
 'unprotectedarticle' => "nanala ny fiarovana an'i « [[$1]] »",
 'movedarticleprotection' => 'nanova ny safidim-piarovana : « [[$2]] » lasa « [[$1]] »',
 'protect-title' => "Hanova ny lentam-piarovana ho an'i « $1 »",
-'protect-title-notallowed' => "Hijery ny lentam-piarovana ho an'i «[[$1]]»",
+'protect-title-notallowed' => "Hijery ny lentam-piarovana ho an'i «$1»",
 'prot_1movedto2' => '[[$1]] voaova anarana ho [[$2]]',
 'protect-badnamespace-title' => 'Anaran-tsehatra tsy azo arovana',
 'protect-badnamespace-text' => "Tsy afaka arovana ny pejy ao amin'io anaran-tsehatra io.",
@@ -2519,7 +2525,7 @@ $1',
 'contributions' => "Fandraisan'anjaran'ny mpikambana{{GENDER:$1}}",
 'contributions-title' => "Fandraisan'anjaran'i $1",
 'mycontris' => "Fandraisan'anjara",
-'contribsub2' => "ho an'ny $1 ($2)",
+'contribsub2' => "Ho an'ny $1 ($2)",
 'nocontribs' => "Tsy misy fanovana mifanaraka amin'ireo critères ireo.",
 'uctop' => '(ankehitriny)',
 'month' => "Tamin'ny volana (sy teo aloha) :",
@@ -2594,8 +2600,8 @@ Fenoy etsy ambany ny antony manokana (ohatra, mitanisà pejy nosomparana).",
 'ipb-confirm' => 'Sakanana marina',
 'badipaddress' => 'Tsy mety ny adiresy IP (invalid)',
 'blockipsuccesssub' => 'Vita soa aman-tsara ny sakana',
-'blockipsuccesstext' => 'Voasakana i [[Special:Contributions/$1|$1]].
-<br />Jereo ny [[Special:BlockList|lisitry ny IP voasakana]] raha hanala ny sakana efa misy.',
+'blockipsuccesstext' => 'Voasakana i [[Special:Contributions/$1|$1]].<br />
+Jereo ny [[Special:BlockList|lisitry ny voasakana]] raha hanala ny sakana efa misy.',
 'ipb-blockingself' => 'Hanakana ny kaontinao ianao ! Tena hanao izany ve ?',
 'ipb-confirmhideuser' => "Eo ampanakanana mpikambana miaraka amin'ny \"fanakonana mpikambana\" ampiasaina. Izany dia mamafa ny anaran'ilay mpikambana amin'ny listra ary amin'ny iditra laogy. Tena hanao izany ve ianao?",
 'ipb-edit-dropdown' => 'Hanova ny antony fanakanana tsipalotra',
@@ -2648,9 +2654,9 @@ Eo ambany ny laogim-panakanana.',
 Eo ambany ny laogim-pamafana.',
 'blocklogentry' => 'voasakana i "[[$1]]" mandritra ny $2 ; antony : $3',
 'reblock-logentry' => "nanova ny parametatry ny sakan'i [[$1]], ary tapitra amin'ny $2. Ny antony dia ''$3''",
-'blocklogtext' => "Eto no ahitana ny tantaran'ny hetsika momba ny fisakanana sy ny fanafoanana fisakanana mpandray anjara.
-Ireo adiresy IP voasakana ho azy dia tsy miseho eto. Jereo ao amin'ny [[Special:BlockList|lisitry ny IP voasakana]]
-ny lisitry ny fisakanana sy fandrarana na tanteraka misy ankehitriny.",
+'blocklogtext' => "Eto no ahitana ny tantaran'ny hetsika momba ny fisakanana sy ny famoanana ny fisakanana mpandray anjara.
+Tsy aseho eto ny adiresy IP voasakana ho azy.
+Jereo ao amin'ny [[Special:BlockList|lisitry ny sakana]] hahitana ny lisitry ny sakana mihatra amin'izao fotoana izao",
 'unblocklogentry' => "voaaisotra ny sakana an'i $1",
 'block-log-flags-anononly' => 'mpikambana tsy nisoratra anarana ihany',
 'block-log-flags-nocreate' => 'tsy mahazo manokatra kaonty',
@@ -2673,11 +2679,8 @@ Mety efa natao angamba ny fanalana sakana.',
 Ao amin'ny laharana $2 izay afaka alàna sakana anefa izy io.",
 'ip_range_invalid' => 'Tsy mety io IP io.',
 'ip_range_toolarge' => 'Ny fanidiana laharana IP ngeza nohonny /$1 dia tsy azo atao.',
-'blockme' => 'Sakano ahy',
 'proxyblocker' => 'Mpisakana proxy',
-'proxyblocker-disabled' => 'Tsy mandeha io asa io.',
 'proxyblockreason' => "Voasakana ny adiresy IP-nao satria adiresy proxy malalaka izy io. Azafady mba lazao amin'ny mpanome internet anao io olana io.",
-'proxyblocksuccess' => 'Vita.',
 'sorbsreason' => "Voasokokajin'ny DNSBL ho ao anatin'ny proxy midanadana ny adiresy IP-nao.",
 'sorbs_create_account_reason' => "Voasokajy ho isan'ny proxy midanadana ao amin'ny DNSBL ny adiresy IP-nao. Ireo IP ireo dia ahiana ho fitaovana azon'ny mpandefa spam ampiasaina. Tsy afaka manokatra kaonty ianao.",
 'cant-block-while-blocked' => 'Tsy azo sakananao ny mpikambana hafa raha mbola voasakana ianao.',
@@ -2718,15 +2721,15 @@ Mba hahafahany manidy na mamoha ny banky angona, mila azo soratan'ny lohamilin-t
 # Move page
 'move-page' => "Hanova anarana an'i $1",
 'move-page-legend' => 'Afindrao toerana ny pejy',
-'movepagetext' => "Ampiasao ilay formulaire eo ambany eo mba hamindra azy toerana, voakisaka any amin'ny anarany ankehitriny ny tantarany. Lasa pejy-na redirection ilay pejy taloha, (manondro makany amin'ny anarany ankehitriny ilay pejy).
-Afaka manao ''update'' ny redirect ianao.
+'movepagetext' => "Ampiasao ilay fôrmiolera eo ambany eo mba hamindra azy toerana, voakisaka any amin'ny anarany ankehitriny ny tantarany. Lasa pejy fihodinana ilay pejy taloha, (manondro makany amin'ny anarany ankehitriny ilay pejy).
+
+Afaka manavao ho azy ny fihodinana mankany amin'ny lohateny taloha ianao. Raha tsy fidinao ny manao izany, marino tsara ny fisian'ireo [[Special:DoubleRedirects|fihodinana roa]] na [[Special:BrokenRedirects|fihodinana tapaka]]. Ianao no manana andrikitra amin'ny fanamarinana ny tsi-fitapahan'ireo rohy.
 
 Jereo koa fa '''tsy afaka''' akisaka ilay pejy ra mitovy anarana amin'ny pejy efa misy ny anarana ny anarana vaovaon'ilay pejy tianao akisaka, fa mety atao ihany io asa io ra tsy misy nininona ilay pejy. Afaka manolo anarana pejy efa manondro ny fihisiny taloha ianao ra diso ianao, fa tsy afaka ataonao no manitsaka pejy efa misy.
 
 '''TANDREMO'''
 
-Mety fanom-panona ihany ny mpitsidika ra be mpitsidika io pejy ovainao anarana io ;
-Tsy maintsy fantatrao tsara ny vokany aloha no mitohy.",
+Mety ho fiovana lehibe ary tsy ampoizina ny fanaovana izany ho an'ny pejy voatsidika mateetika ; fantaro tsara ny fiantraika alohan'ny manao izany.",
 'movepagetalktext' => "Voasikaka koa ny pejin-dresak'ity pejy ity '''ra''' :
 
 * Efa misy pejin-dresaka efa misy votoatiny amin'ilay anarana vaovao, na
@@ -2906,6 +2909,9 @@ Avereno fanindroany.',
 # JavaScriptTest
 'javascripttest' => 'Fanandramana JavaScript',
 'javascripttest-title' => 'Mandefa fanandramana $1',
+'javascripttest-pagetext-skins' => 'Mifidia skin hanaovana ny fanandramana:',
+'javascripttest-qunit-intro' => "Jereo ny [$1 fanoroana mikasika ny andrana] eo amin'i mediawiki.org.",
+'javascripttest-qunit-heading' => "Tohin'andrana QUnit an'i Javascript eo amin'i MediaWiki",
 
 # Tooltip help for the actions
 'tooltip-pt-userpage' => 'Ny pejinao',
@@ -2925,7 +2931,7 @@ Ampesao ny topi-maso aloha no mihatiry.",
 'tooltip-ca-viewsource' => 'Voaaro ilay pejy. Fa afaka itanao ny voatotiny.',
 'tooltip-ca-history' => "Ny revision natao tamin'ity pejy ity",
 'tooltip-ca-protect' => 'Arovy ity pejy ity',
-'tooltip-ca-unprotect' => "Hanala ny fiarovan'ity pejy ity",
+'tooltip-ca-unprotect' => "Hanova ny lentam-piarovan'ity pejy ity",
 'tooltip-ca-delete' => 'Fafao ity pejy ity',
 'tooltip-ca-undelete' => "Hamerina ny fanovana natao tamin'ity pejy ity talohan'ny famafany",
 'tooltip-ca-move' => 'Ovay anarana ilay pejy',
@@ -2992,7 +2998,8 @@ Mamerina ny version taloha io asa io ary afaka manometraka ny antony anatin'ny a
 'othercontribs' => "Mifototra amin'ny asan'i $1.",
 'others' => 'hafa',
 'siteusers' => '{{SITENAME}} mpikambana $1 miisa $2{{PLURAL:}}',
-'anonusers' => "Ny mpikambana tsy nisoratra anarana $1 ao amin'i {{SITENAME}}",
+'anonusers' => "Ny mpikambana tsy nisoratra anarana $1 ao amin'i {{SITENAME}}{{PLURAL:$2}}",
+'creditspage' => "Fisaorana ho an'ny pejy",
 
 # Spam protection
 'spamprotectiontitle' => "Sivana mpiaro amin'ny spam",
@@ -3012,11 +3019,12 @@ Mamerina ny version taloha io asa io ary afaka manometraka ny antony anatin'ny a
 'pageinfo-length' => 'Halavam-pejy (oktety)',
 'pageinfo-article-id' => 'Laharam-pejy',
 'pageinfo-language' => "Tenin'ny votoatiny",
-'pageinfo-robot-policy' => "Satan'ny motera fikarohana",
-'pageinfo-robot-index' => 'Azo tondroina',
+'pageinfo-robot-policy' => "Fanondroana ataon'ny rôbô",
+'pageinfo-robot-index' => 'Azo atao',
 'pageinfo-robot-noindex' => 'Tsy azo tondroina',
 'pageinfo-views' => "Isan'ny jery",
 'pageinfo-watchers' => "Isan'ny mpandray anjara manaraka",
+'pageinfo-few-watchers' => 'Mpanaraka latsaky ny $1{{PLURAL:}}',
 'pageinfo-redirects-name' => "Fihodinana manketo amin'ity pejy ity",
 'pageinfo-subpages-name' => "Zana-pejin'ity pejy ity",
 'pageinfo-firstuser' => 'Mpamorona ilay pejy',
@@ -3029,7 +3037,7 @@ Mamerina ny version taloha io asa io ary afaka manometraka ny antony anatin'ny a
 'pageinfo-recent-authors' => "Isa vao haingan'ny mpanoratra misongadina",
 'pageinfo-hidden-categories' => 'Sokajy nafenina{{PLURAL:$1}} ($1)',
 'pageinfo-templates' => 'Endrika natsofoka{{PLURAL:$1}} ($1)',
-'pageinfo-transclusions' => "Pejy natsofoka tanatin'i ($1)",
+'pageinfo-transclusions' => "Pejy natsofoka tanatin'i ($1){{PLURAL:}}",
 'pageinfo-toolboxlink' => 'Fampahalalana mikasika ny pejy',
 'pageinfo-redirectsto' => "Fihdinana mankany amin'ny",
 'pageinfo-redirectsto-info' => 'fampahalalana',
@@ -3054,6 +3062,7 @@ Mamerina ny version taloha io asa io ary afaka manometraka ny antony anatin'ny a
 'markedaspatrollederrortext' => 'Tsy maintsy misafidy santiôna iray ianao mba hahafahanao manamarika azy ho voamarina.',
 'markedaspatrollederror-noautopatrol' => 'Tsy azonao marihana ho voamarina ny fanovanao.',
 'markedaspatrollednotify' => "Voamarika ho hita ny fanovana natao tamin'i $1.",
+'markedaspatrollederrornotify' => 'Tsy nahamarika azy ho voaara-maso.',
 
 # Patrol log
 'patrol-log-page' => "Laogin'ny fanovana voamarina",
@@ -3089,7 +3098,7 @@ Raha alefanao ilay izy, mety ho simban'io renifango io ny solosainao.",
 'svg-long-error' => 'Rakitra SVG tsy ekena : $1',
 'show-big-image' => "Hijery ny tena haben'ny sary",
 'show-big-image-preview' => "Haben'ny topi-maso: $1.",
-'show-big-image-other' => 'Habe hafa: $1{{PLURAL:$1}}',
+'show-big-image-other' => 'Habe hafa: $1{{PLURAL:$2}}',
 'show-big-image-size' => '$1 × $2 teboka',
 'file-info-gif-looped' => 'miverimberina',
 'file-info-gif-frames' => 'sary{{PLURAL:$1}} $1',
@@ -3224,7 +3233,12 @@ Tokony sary tsy misy na sary tsy izy ny rohy voalohany anaty andalana iray .
 'exif-gpsdestlatitude' => 'Laharam-pehintany tanjona',
 'exif-gpsareainformation' => 'Anaram-paritra GPS',
 'exif-gpsdatestamp' => 'Daty GPS',
+'exif-worldregioncreated' => 'Faritany nangalana ity ilay sary',
 'exif-countrycreated' => 'Firenena nangalana ilay sary',
+'exif-countrycodecreated' => 'Kaontim-pirenena nangalana ilay sary',
+'exif-provinceorstatecreated' => 'Faritany nangalana ilay sary',
+'exif-citycreated' => 'Tanàna nangalana ilay sary',
+'exif-sublocationcreated' => 'Fari-tanàna nangalana ilay sary',
 'exif-worldregiondest' => 'Faritany aseho',
 'exif-countrydest' => 'Firenena aseho',
 'exif-countrycodedest' => 'Kaodim-pirenena aseho',
@@ -3245,6 +3259,7 @@ Tokony sary tsy misy na sary tsy izy ny rohy voalohany anaty andalana iray .
 'exif-contact' => 'Fampahalalana mikasika ny fifandraisana',
 'exif-writer' => 'Mpanoratra',
 'exif-languagecode' => 'Fiteny',
+'exif-iimversion' => 'filaza IIM',
 'exif-iimcategory' => 'Sokajy',
 'exif-iimsupplementalcategory' => 'Sokajy fanampiny',
 'exif-datetimeexpires' => 'Asa ampiasaina aoriany',
@@ -3268,9 +3283,15 @@ Tokony sary tsy misy na sary tsy izy ny rohy voalohany anaty andalana iray .
 'exif-attributionurl' => "Rehefa mampiasa ity asa ity dia asio rohy mankany amin'i",
 'exif-preferredattributionname' => 'Rehefa mampiasa ilay asa, isaory',
 'exif-pngfilecomment' => "Famoahan-kevitra momban'ilay rakitra PNG",
+'exif-disclaimer' => 'Fampitanremana',
 'exif-contentwarning' => 'Fampitandremana mikasika ny votoatiny',
 'exif-giffilecomment' => 'Famoahan-kevitry ny rakirta GIF',
 'exif-intellectualgenre' => 'Karazan-javatra',
+'exif-subjectnewscode' => "Kaodin'ny lohahevitra",
+'exif-event' => 'Zava-mitranga azo sary',
+'exif-organisationinimage' => 'Fikambanana azo sary',
+'exif-personinimage' => 'Olona azo sary',
+'exif-originalimageheight' => "Haambon-tsary talohan'ny nanovana azy",
 
 'exif-copyrighted-true' => "Iharan'ny zom-pamorona",
 'exif-copyrighted-false' => "Toetran'ny zom-pamorona tsy voafaritra",
@@ -3278,10 +3299,20 @@ Tokony sary tsy misy na sary tsy izy ny rohy voalohany anaty andalana iray .
 'exif-unknowndate' => 'Daty tsy fantatra',
 
 'exif-orientation-1' => 'Tsotra',
+'exif-orientation-3' => 'Ahodina 180°',
+'exif-orientation-4' => 'Navadika ambony ambany',
+'exif-orientation-5' => 'Navadika 90° miankavia ary navadika ambony ambany',
+'exif-orientation-6' => 'Navadika 90° miankavia',
+'exif-orientation-7' => 'Navadika 90° miankavanana ary navadika ambony ambany',
+'exif-orientation-8' => 'Navadika 90° miankavanana',
+
+'exif-planarconfiguration-2' => 'Data misaraka',
 
 'exif-componentsconfiguration-0' => 'tsy nahitana',
 
 'exif-exposureprogram-0' => 'Tsy nolazaina',
+'exif-exposureprogram-1' => 'Natao tanana',
+'exif-exposureprogram-2' => 'Fandaharana ara-dalàna',
 
 'exif-subjectdistance-value' => '$1 metatra',
 
@@ -3298,8 +3329,16 @@ Tokony sary tsy misy na sary tsy izy ny rohy voalohany anaty andalana iray .
 'exif-lightsource-9' => "Toetr'andro mazava",
 'exif-lightsource-10' => "Toetr'andro mandrahona",
 'exif-lightsource-11' => 'Haloka',
+'exif-lightsource-17' => 'Jiro manara-penitra A',
+'exif-lightsource-18' => 'Jiro manara-penitra B',
+'exif-lightsource-19' => 'Jiro manara-penitra C',
+'exif-lightsource-24' => "Tangistenina ISO an'ny studio",
+'exif-lightsource-255' => 'Loharanon-kazavana hafa',
 
 # Flash modes
+'exif-flash-fired-0' => 'Tsy nirehitra ny flash',
+'exif-flash-fired-1' => 'Nirehitra ny flash',
+'exif-flash-return-0' => 'Tsy misy stirôbôskôpy mamerina lefa fahitana',
 'exif-flash-mode-3' => 'Toetra aotômatika',
 
 'exif-focalplaneresolutionunit-2' => 'Posy',
@@ -3354,7 +3393,9 @@ raha vao nanokatra kaonty ianao, dia miandrasa minitra vitsivitsy mba ho tonga i
 'confirmemail_sent' => 'Lasa ny fanamarinana ny imailaka.',
 'confirmemail_oncreate' => "Nalefa tany amin'ny adiresy imailakao ny kaody fanamarinana.
 Tsy ilaina ampaisaina io tenimiafina io rehefa hiditra eto amin'ity wiki ity ianao, fa tsy maintsy omenao ilay izy rehefa mampiasa tao mifototra amin'ny imailaka.",
-'confirmemail_sendfailed' => 'Tsy lasa ny fanamarinana ny imailaka. Hamarino ny adiresy fandrao misy litera tsy mety.',
+'confirmemail_sendfailed' => "Tsy lasa ny fanamarinana ny imailaka. Hamarino ny adiresy fandrao misy litera tsy mety.
+
+Ity no naverin'ny mpandefa mailaka : $1",
 'confirmemail_invalid' => 'Tsy mety ilay fango fanamarinana. Angamba efa lany daty?',
 'confirmemail_needlogin' => 'Mila $1 ianao raha hanamarina ny adiresy imailakao.',
 'confirmemail_success' => 'Voamarina ny adiresy imailakao. Afaka [[Special:UserLogin|miditra]] ianao ankehitriny ary mankafia ny wiki.',
@@ -3390,6 +3431,11 @@ Azafady hamafiso fa tena irinao averina hoforonina tokoa ity lahatsoratra ity.",
 'confirm_purge_button' => 'Eka',
 'confirm-purge-top' => "Fafana ve ny cache-n'ity pejy ity?",
 
+# action=watch/unwatch
+'confirm-watch-top' => 'Hanaraka ity pejy ity?',
+'confirm-unwatch-button' => 'OK',
+'confirm-unwatch-top' => "Hanala ity pejy ity amin'ny lisitry ny pejy arahinao?",
+
 # Multipage image navigation
 'imgmultipageprev' => '← pejy nialoha',
 'imgmultipagenext' => 'pejy manaraka →',
@@ -3404,6 +3450,7 @@ Azafady hamafiso fa tena irinao averina hoforonina tokoa ity lahatsoratra ity.",
 'table_pager_first' => 'Pejy voalohany',
 'table_pager_last' => 'Pejy farany',
 'table_pager_limit' => 'Haneho zavatra $1 isaky ny pejy',
+'table_pager_limit_label' => 'Valiny isam-pejy:',
 'table_pager_limit_submit' => 'Hitsidika',
 'table_pager_empty' => 'Tsy nahitana valiny',
 
@@ -3430,7 +3477,11 @@ Andramo ny topi-maso tsotra',
 'watchlistedit-noitems' => 'Tsy misy lohateny ny lisitrao.',
 'watchlistedit-normal-title' => 'Hanova ny lisitra ny pejy arahako maso',
 'watchlistedit-normal-legend' => "Hanala lohateny ao amin'ny lisitra",
+'watchlistedit-normal-explain' => "Aseho eo ambany ny lohateny ao amin'ny lisitry ny pejy arahanao.
+Tsindrio ny boaty eo akaikiny ary tsindrio  « {{int:Watchlistedit-normal-submit}} ».
+Azonao atao ihany koa ny [[Special:EditWatchlist/raw|manova ilay lisitra amin'ny akorany]].",
 'watchlistedit-normal-submit' => 'Hanala ireo lohateny nosafidiana ireo',
+'watchlistedit-normal-done' => "Afaka tamin'ny lisitry ny pejy arahanao ny lohateny $1{{PLURAL:}}",
 'watchlistedit-raw-title' => "Hanova ny lisitra ny pejy arahako maso amin'ny fomba akorany",
 'watchlistedit-raw-legend' => "Fanovana ilay lisitry ny pejy arahina maso amin'ny fomba akorany",
 'watchlistedit-raw-titles' => 'Lohateny :',
@@ -3455,6 +3506,7 @@ Andramo ny topi-maso tsotra',
 'version-hook-subscribedby' => "Nalefan'i",
 'version-version' => '(Santiôna $1)',
 'version-license' => 'Lisansy',
+'version-poweredby-others' => 'hafa',
 'version-software' => 'Rindrankahy voapetraka',
 'version-software-product' => 'Vokatra',
 'version-software-version' => 'Santiôna',
@@ -3481,7 +3533,7 @@ Andramo ny topi-maso tsotra',
 'specialpages-group-highuse' => 'Pejy ampiasaina mafy',
 'specialpages-group-pages' => 'Lisitra ny pejy',
 'specialpages-group-pagetools' => "Fitaovna ho an'ny pejy",
-'specialpages-group-wiki' => "Datan'ny wiki sy fitaovana",
+'specialpages-group-wiki' => 'Data sy fitaovana',
 'specialpages-group-redirects' => 'Pejy manokana voaodina',
 'specialpages-group-spam' => 'Fitaovana fanalana spam',
 
@@ -3546,13 +3598,13 @@ Andramo ny topi-maso tsotra',
 'revdelete-restricted' => "nametraka fanerena ho an'ny mpandrindra",
 'revdelete-unrestricted' => "fanerena nesorina tamin'ny mpandrindra",
 'logentry-move-move' => "nanova ny anaran'i $3 ho $4 i $1",
-'logentry-newusers-newusers' => 'Noforonina ny kaontim-pikambana $1',
-'logentry-newusers-create' => 'Noforonina ny kaontim-pikambana $1',
-'logentry-newusers-create2' => "Noforonin'i $1 ny kaomtim-pikambana $3",
-'logentry-newusers-autocreate' => 'Noforonina ho azy ny kaontim-pikambana $1',
-'logentry-rights-rights' => "$1 dia nanova ny sokajim-pikambana isian'i $3 avy amin'ny $4 lasa $5",
-'logentry-rights-rights-legacy' => "$1 nanova ny vonodrom-pikambana isian'i $3",
-'logentry-rights-autopromote' => 'Lasa $5 ho azy i $1 izay $4 taloha',
+'logentry-newusers-newusers' => '{{GENDER:$2|Noforonina}} ny kaontim-pikambana $1',
+'logentry-newusers-create' => '{{GENDER:$2|Noforonina}} ny kaontim-pikambana $1',
+'logentry-newusers-create2' => "{{GENDER:$2|Noforonin}}'i $1 ny kaomtim-pikambana $3",
+'logentry-newusers-autocreate' => '{{GENDER:$2|Noforonina}} ho azy ny kaontim-pikambana $1',
+'logentry-rights-rights' => "$1 dia nanova ny sokajim-pikambana isian'i $3 avy amin'ny $4 lasa $5{{GENDER:$2}}",
+'logentry-rights-rights-legacy' => "{{GENDER:$2}}$1 nanova ny vonodrom-pikambana isian'i $3",
+'logentry-rights-autopromote' => '{{GENDER:$2}}Lasa $5 ho azy i $1 izay $4 taloha',
 'rightsnone' => '(tsy misy)',
 
 # Feedback
index d215781..1d804d0 100644 (file)
@@ -2058,9 +2058,7 @@ Caliak [[Special:BlockList|daftar sakek]] untuak kasado pangguno nan kini kanai
 'ipb_already_blocked' => '"$1" alah disakek',
 'ipb-needreblock' => '$1 alah tasakek. Apo nio diubah pangaturannyo?',
 'ipb-otherblocks-header' => '{{PLURAL:$1|Sakek}} lain',
-'blockme' => 'Sakek denai',
 'proxyblocker' => 'Sakek proksi',
-'proxyblocker-disabled' => 'Fungsi ko dimatian',
 
 # Developer tools
 'lockdb' => 'Kunci basis data',
@@ -2255,6 +2253,8 @@ Sanak hanyo buliah mancaliak sumbernyo sajo',
 # Spam protection
 'spam_blanking' => 'Sado revisi nan ado pautan ka $1, kosong',
 'spam_deleting' => 'Sado revisi nan ado pautan ka $1, dihapuih',
+'simpleantispam-label' => "Pamarisoan anti-spam.
+Masukan ko '''DILARANG'''!",
 
 # Info page
 'pageinfo-title' => 'Informasi untuak "$1"',
index d467be4..1b1b2db 100644 (file)
@@ -489,8 +489,6 @@ $messages = array(
 'noindex-category' => 'Неиндексирани страници',
 'broken-file-category' => 'Страници со прекинати врски до податотеки',
 
-'linkprefix' => '/^(.*?)([a-zA-Z\\x80-\\xff]+)$/sD',
-
 'about' => 'За {{SITENAME}}',
 'article' => 'Статија',
 'newwindow' => '(се отвора во нов прозорец)',
@@ -569,7 +567,7 @@ $messages = array(
 'articlepage' => 'Преглед на содржината',
 'talk' => 'Разговор',
 'views' => 'Посети',
-'toolbox' => 'Ð\90лаÑ\82ник',
+'toolbox' => 'Ð\90лаÑ\82ки',
 'userpage' => 'Преглед на корисничката страница',
 'projectpage' => 'Преглед на проектната страница',
 'imagepage' => 'Преглед на страницата на податотеката',
@@ -1340,15 +1338,15 @@ $2
 * Несоодветни лични информации
 *: ''домашни адреси и телефонски броеви, матични броеви, и.т.н.''",
 'revdelete-legend' => 'Постави ограничувања за видливост',
-'revdelete-hide-text' => 'СкÑ\80иÑ\98 Ð³Ð¾ Ñ\82екÑ\81Ñ\82от на ревизијата',
+'revdelete-hide-text' => 'ТекÑ\81т на ревизијата',
 'revdelete-hide-image' => 'Скриј содржина на податотека',
 'revdelete-hide-name' => 'Скриј го дејството и неговата одредница',
-'revdelete-hide-comment' => 'СкÑ\80иÑ\98 Ð³Ð¾ Ð¾Ð¿Ð¸Ñ\81оÑ\82 на уредувањето',
-'revdelete-hide-user' => 'СкÑ\80иÑ\98 ÐºÐ¾Ñ\80иÑ\81ниÑ\87ко Ð¸Ð¼Ðµ/IP-адÑ\80еÑ\81а Ð½Ð° Ð°Ð²Ñ\82оÑ\80от',
+'revdelete-hide-comment' => 'Ð\9eпиÑ\81 на уредувањето',
+'revdelete-hide-user' => 'Ð\9aоÑ\80иÑ\81ниÑ\87ко Ð¸Ð¼Ðµ/IP-адÑ\80еÑ\81а Ð½Ð° Ñ\83Ñ\80едникот',
 'revdelete-hide-restricted' => 'Постави ограничувања и за администратори на ист начин како и за останатите',
 'revdelete-radio-same' => '(не менувај)',
-'revdelete-radio-set' => 'Ð\94а',
-'revdelete-radio-unset' => 'Ð\9dе',
+'revdelete-radio-set' => 'Ð\92идлива',
+'revdelete-radio-unset' => 'СкÑ\80иена',
 'revdelete-suppress' => 'Притајувај податоци и од администраторите',
 'revdelete-unsuppress' => 'Отстрани ограничувања на обновени ревизии',
 'revdelete-log' => 'Причина:',
@@ -2924,12 +2922,9 @@ $1',
 Таа е блокирана како дел од блокот адреси $2, кој не може да се деблокира.',
 'ip_range_invalid' => 'Неважечки IP дијапазон на адреси.',
 'ip_range_toolarge' => 'Не се дозволени опсежни блокирања поголеми од /$1.',
-'blockme' => 'Блокирај ме',
 'proxyblocker' => 'Блокер на застапници (proxy)',
-'proxyblocker-disabled' => 'Оваа функција е оневозможена.',
 'proxyblockreason' => 'Вашата IP-адреса е блокирана бидејќи претставува отворен застапник (proxy).
 Ве молиме контактирајте со вашиот доставувач на Интернет услуги или техничката поддршка и информирајте ги за овој сериозен безбедносен проблем.',
-'proxyblocksuccess' => 'Готово.',
 'sorbs' => 'DNSBL',
 'sorbsreason' => 'Вашата IP-адреса е запишана како отворен застапник (proxy) во DNSBL кој го користи {{SITENAME}}..',
 'sorbs_create_account_reason' => 'Вашата IP-адреса е наведена како отворен застапникот (proxy) во DNSBL користена од {{SITENAME}}.
@@ -3299,6 +3294,8 @@ $2',
 'spam_reverting' => 'Враќам на последната верзија што не содржи врска до $1',
 'spam_blanking' => 'Сите ревизии содржеа врски до $1. Чистам',
 'spam_deleting' => 'Сите ревизии содржеа врски до $1. Бришам',
+'simpleantispam-label' => "Проверка против спам.
+'''НЕ''' пополнувајте го ова!",
 
 # Info page
 'pageinfo-title' => 'Информации за „$1“',
@@ -4037,7 +4034,7 @@ $5
 'confirm-unwatch-top' => 'Да ја отстранам страницава од списокот на набљудувања?',
 
 # Separators for various lists, etc.
-'percent' => '$1 %',
+'percent' => '$1&#160;%',
 
 # Multipage image navigation
 'imgmultipageprev' => '&larr; претходна страница',
@@ -4229,7 +4226,7 @@ $5
 # Special:Redirect
 'redirect' => 'Пренасочување по податотека, корисник или назнака на ревизија',
 'redirect-legend' => 'Пренасочување кон податотека или страница',
-'redirect-summary' => 'Оваа специјална страница пренасочува кон податотека (се задава името), страница (се задава назнаката на ревизијата) или корисничка странца (се задава бројчената назнака на корисникот).',
+'redirect-summary' => 'Оваа специјална страница пренасочува кон податотека (се задава името), страница (се задава назнаката на ревизијата) или корисничка странца (се задава бројчената назнака на корисникот). Употреба: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/revision/328429]] или [[{{#Special:Redirect}}/user/101]].',
 'redirect-submit' => 'Оди',
 'redirect-lookup' => 'Пребарај:',
 'redirect-value' => 'Вредност:',
index 6da9f87..a229938 100644 (file)
@@ -558,7 +558,7 @@ $messages = array(
 'articlepage' => 'ലേഖനം കാണുക',
 'talk' => 'സംവാദം',
 'views' => 'ദർശനീയത',
-'toolbox' => 'പണിസà´\9eàµ\8dà´\9aà´¿',
+'toolbox' => 'à´\89à´ªà´\95à´°à´£à´\99àµ\8dà´\99ൾ',
 'userpage' => 'ഉപയോക്താവിന്റെ താൾ കാണുക',
 'projectpage' => 'പദ്ധതി താൾ കാണുക',
 'imagepage' => 'പ്രമാണ താൾ കാണുക',
@@ -855,7 +855,7 @@ $2',
 'noemailcreate' => 'താങ്കൾ സാധുവായ ഇമെയിൽ വിലാസം നൽകേണ്ടതാണ്',
 'passwordsent' => '‘$1” എന്ന അംഗത്വത്തിനായി രജിസ്റ്റർ ചെയ്യപ്പെട്ടിട്ടുള്ള ഇമെയിൽ വിലാസത്തിലേക്ക് ഒരു പുതിയ രഹസ്യവാക്ക് അയച്ചിട്ടുണ്ട്. അത് ലഭിച്ചശേഷം ദയവായി ലോഗിൻ ചെയ്യുക.',
 'blocked-mailpassword' => 'താങ്കളുടെ ഐ.പി. വിലാസത്തെ ഈ വിക്കി തിരുത്തുന്നതിൽ നിന്നു തടഞ്ഞിട്ടുള്ളതാണ്‌. അതിനാൽ രഹസ്യവാക്ക് വീണ്ടെടുക്കുവാനുള്ള സജ്ജീകരണം ഉപയോഗിക്കുന്നതിനു താങ്കൾക്ക് അവകാശമില്ല.',
-'eauthentsent' => 'താà´\99àµ\8dà´\95ൾ à´µà´¿à´\95àµ\8dà´\95ിയിൽ à´\95àµ\8dà´°à´®àµ\80à´\95à´°à´¿à´\9aàµ\8dà´\9aà´¿à´\9fàµ\8dà´\9fàµ\81à´³àµ\8dà´³ à´\87à´®àµ\86യിൽ à´µà´¿à´²à´¾à´¸à´¤àµ\8dതിലàµ\87à´\95àµ\8dà´\95àµ\8d à´¸àµ\8dഥിരàµ\80à´\95രണതàµ\8dതിനായി à´\92à´°àµ\81 à´®àµ\86യിൽ à´\85à´¯à´\9aàµ\8dà´\9aà´¿à´\9fàµ\8dà´\9fàµ\81à´£àµ\8dà´\9fàµ\8d. à´\87വിà´\9fàµ\86 à´¨à´¿à´¨àµ\8dà´¨àµ\8d à´\86 à´\87à´®àµ\86യിൽ à´µà´¿à´²à´¾à´¸à´¤àµ\8dതിലàµ\87à´\95àµ\8dà´\95àµ\8d à´®à´±àµ\8dà´±àµ\8aà´°àµ\81 à´®àµ\86യിൽ à´\95àµ\82à´\9fà´¿ à´\85à´¯à´\95àµ\8dà´\95àµ\81à´¨àµ\8dനതിനàµ\81 à´®àµ\81ൻപായി, à´\85à´\82à´\97à´¤àµ\8dà´µà´\82 à´¤à´¾à´\99àµ\8dà´\95à´³àµ\81à´\9fàµ\87à´¤àµ\81 à´¤à´¨àµ\8dà´¨àµ\86 à´\8eà´¨àµ\8dà´¨àµ\81 à´\89റപàµ\8dà´ªàµ\81 à´µà´°àµ\81à´¤àµ\8dà´¤àµ\81à´¨àµ\8dനതിനായി, à´\87à´ªàµ\8dà´ªàµ\8bൾ à´\85à´¯à´\9aàµ\8dà´\9aà´¿à´\9fàµ\8dà´\9fàµ\81à´³àµ\8dà´³ മെയിലിലെ നിർദ്ദേശങ്ങൾ താങ്കൾ പാലിക്കേണ്ടതാണ്.',
+'eauthentsent' => 'താà´\99àµ\8dà´\95ൾ à´¨àµ½à´\95ിയിà´\9fàµ\8dà´\9fàµ\81à´³àµ\8dà´³ à´\87à´®àµ\86യിൽ à´µà´¿à´²à´¾à´¸à´¤àµ\8dതിലàµ\87à´\95àµ\8dà´\95àµ\8d à´¸àµ\8dഥിരàµ\80à´\95രണതàµ\8dതിനായി à´\92à´°àµ\81 à´\87à´®àµ\86യിൽ à´\85à´¯à´\9aàµ\8dà´\9aà´¿à´\9fàµ\8dà´\9fàµ\81à´£àµ\8dà´\9fàµ\8d. à´\86 à´µà´¿à´²à´¾à´¸à´¤àµ\8dതിലàµ\87à´\95àµ\8dà´\95àµ\8d à´®à´±àµ\8dà´±àµ\8aà´°àµ\81 à´\87à´®àµ\86യിൽ à´\85à´¯à´\95àµ\8dà´\95àµ\81à´¨àµ\8dനതിനàµ\81 à´®àµ\81ൻപായി, à´\85à´\82à´\97à´¤àµ\8dà´µà´\82 à´¤à´¾à´\99àµ\8dà´\95à´³àµ\81à´\9fàµ\87à´¤àµ\81 à´¤à´¨àµ\8dà´¨àµ\86 à´\8eà´¨àµ\8dà´¨àµ\81 à´\89റപàµ\8dà´ªàµ\81 à´µà´°àµ\81à´¤àµ\8dà´¤àµ\81à´¨àµ\8dനതിനàµ\8d, à´\87à´ªàµ\8dà´ªàµ\8bൾ à´\85à´¯à´\9aàµ\8dà´\9aà´¿à´\9fàµ\8dà´\9fàµ\81à´³àµ\8dà´³ à´\87മെയിലിലെ നിർദ്ദേശങ്ങൾ താങ്കൾ പാലിക്കേണ്ടതാണ്.',
 'throttled-mailpassword' => 'കഴിഞ്ഞ {{PLURAL:$1|മണിക്കൂറിനുള്ളിൽ |$1 മണിക്കൂറുകൾക്കുള്ളിൽ}} രഹസ്യവാക്ക് പുനർസജ്ജീകരിക്കാനുള്ള ഒരു ഇമെയിൽ അയച്ചിട്ടുണ്ട്. ദുരുപയോഗം ഒഴിവാക്കാൻ {{PLURAL:$1|ഒരു മണിക്കൂറിനുള്ളിൽ |$1 മണിക്കൂറുകൾക്കുള്ളിൽ}} രഹസ്യവാക്ക് പുനർസജ്ജീകരിക്കാനുള്ള ഒരു ഇമെയിൽ മാത്രമേ അയയ്ക്കുകയുള്ളു.',
 'mailerror' => 'മെയിൽ അയയ്ക്കുന്നതിൽ പിഴവ്: $1',
 'acct_creation_throttle_hit' => 'കഴിഞ്ഞ ഒരു ദിവസത്തിനുള്ളിൽ താങ്കളുടെ ഐ.പി. വിലാസത്തിൽ നിന്നുമുള്ള സന്ദർശകർ {{PLURAL:$1|1 അംഗത്വം|$1 അംഗത്വങ്ങൾ}} എടുത്തിട്ടുണ്ട്, പ്രസ്താവിത സമയത്തിനുള്ളിൽ എടുക്കാവുന്ന ഏറ്റവും കൂടിയ പരിധിയാണിത്.
@@ -1285,15 +1285,15 @@ $3 അതിനു കാണിച്ചിരിക്കുന്ന കാര
 * അനുയോജ്യമല്ലാത്ത വ്യക്തി വിവരങ്ങൾ
 *: ''വീട്ടുവിലാസങ്ങൾ, ടെലിഫോൺ നമ്പറുകൾ, സാമൂഹിക സുരക്ഷാ നമ്പരുകൾ, തുടങ്ങിയവ.''",
 'revdelete-legend' => 'നാൾപ്പതിപ്പിന്റെ ദർശനീയത സജ്ജീകരിക്കുക',
-'revdelete-hide-text' => 'മാറàµ\8dà´±à´\82 à´µà´¨àµ\8dà´¨ à´\8eà´´àµ\81à´¤àµ\8dà´¤àµ\8d à´®à´±à´¯àµ\8dà´\95àµ\8dà´\95àµ\81à´\95',
+'revdelete-hide-text' => 'നാൾപàµ\8dപതിപàµ\8dപിലàµ\86 à´\8eà´´àµ\81à´¤àµ\8dà´¤àµ\8d',
 'revdelete-hide-image' => 'പ്രമാണത്തിന്റെ ഉള്ളടക്കം മറയ്ക്കുക',
 'revdelete-hide-name' => 'പ്രവൃത്തിയും ലക്ഷ്യവും മറയ്ക്കുക',
-'revdelete-hide-comment' => 'തിരàµ\81à´¤àµ\8dതലിനàµ\8dà´±àµ\86 à´\85à´­à´¿à´ªàµ\8dരായà´\82 à´®à´±à´¯àµ\8dà´\95àµ\8dà´\95àµ\81à´\95',
-'revdelete-hide-user' => 'തിരുത്തുന്ന ആളുടെ ഉപയോക്തൃനാമം/ഐ.പി. വിലാസം മറയ്ക്കുക',
+'revdelete-hide-comment' => 'തിരàµ\81à´¤àµ\8dതലിനàµ\8dà´±àµ\86 à´\9aàµ\81à´°àµ\81à´\95àµ\8dà´\95à´\82',
+'revdelete-hide-user' => 'തിരുത്തുന്ന ആളുടെ ഉപയോക്തൃനാമം/ഐ.പി. വിലാസം',
 'revdelete-hide-restricted' => 'വിവരങ്ങളുടെ നിയന്ത്രണം മറ്റുള്ളവരെ പോലെ കാര്യനിർവാഹകർക്കും ബാധകമാക്കുക',
 'revdelete-radio-same' => '(മാറ്റം വരുത്തരുത്)',
-'revdelete-radio-set' => 'à´µàµ\87ണം',
-'revdelete-radio-unset' => 'à´µàµ\87à´£àµ\8dà´\9f',
+'revdelete-radio-set' => 'à´\95ാണണം',
+'revdelete-radio-unset' => 'മറയàµ\8dà´\95àµ\8dà´\95à´£à´\82',
 'revdelete-suppress' => 'സിസോപ്പുകളിൽ നിന്നും മറ്റുള്ളവരിൽ നിന്നും ഈ ഡാറ്റാ മറച്ചു വെക്കുക',
 'revdelete-unsuppress' => 'പുനഃസ്ഥാപിച്ച പതിപ്പുകളിലുള്ള നിയന്ത്രണങ്ങൾ ഒഴിവാക്കുക',
 'revdelete-log' => 'കാരണം:',
@@ -2506,9 +2506,11 @@ $UNWATCHURL
 'deleteotherreason' => 'മറ്റ്/കൂടുതൽ കാരണങ്ങൾ:',
 'deletereasonotherlist' => 'മറ്റു കാരണങ്ങൾ',
 'deletereason-dropdown' => '*മായ്ക്കാനുള്ള സാധാരണ കാരണങ്ങൾ
-** സ്രഷ്ടാവ് ആവശ്യപ്പെട്ടതു പ്രകാരം
+** പാഴെഴുത്ത്
+** നശീകരണ പ്രവർത്തനം
 ** പകർപ്പവകാശ ലംഘനം
-** നശീകരണ പ്രവർത്തനം',
+** സ്രഷ്ടാവ് ആവശ്യപ്പെട്ടതു പ്രകാരം
+** പൊട്ടിയ തിരിച്ചുവിടൽ',
 'delete-edit-reasonlist' => 'മായ്ക്കലിന്റെ കാരണം തിരുത്തുക',
 'delete-toobig' => 'ഈ താളിനു വളരെ വിപുലമായ തിരുത്തൽ ചരിത്രമുണ്ട്. $1 മേൽ {{PLURAL:$1|പതിപ്പുണ്ട്|പതിപ്പുകളുണ്ട്}}. ഇത്തരം താളുകൾ മായ്ക്കുന്നതു {{SITENAME}} സം‌രംഭത്തിന്റെ നിലനില്പ്പിനെ തന്നെ ബാധിക്കുമെന്നതിനാൽ ഈ താൾ മായ്ക്കുന്നതിനുള്ള അവകാശം പരിമിതപ്പെടുത്തിയിരിക്കുന്നു.',
 'delete-warning-toobig' => 'ഈ താളിനു വളരെ വിപുലമായ തിരുത്തൽ ചരിത്രമുണ്ട്. അതായത്, ഇതിനു് $1 മേൽ {{PLURAL:$1|പതിപ്പുണ്ട്|പതിപ്പുകളുണ്ട്}}. ഇത്തരം താളുകൾ മായ്ക്കുന്നതു {{SITENAME}} സം‌രംഭത്തിന്റെ ഡാറ്റാബേസ് ഓപ്പറേഷനെ ബാധിച്ചേക്കാം. അതിനാൽ വളരെ ശ്രദ്ധാപൂർവ്വം തുടർനടപടികളിലേക്കു നീങ്ങുക.',
@@ -2823,11 +2825,8 @@ $1',
 'ipb_blocked_as_range' => 'പിഴവ്:  $1 എന്ന ഐ.പി.യെ നേരിട്ടല്ല തടഞ്ഞിട്ടുള്ളത്. അതിനാൽ തടയൽ നീക്കം ചെയ്യുവാൻ സാദ്ധ്യമല്ല. അതിനെ $2ന്റെ ഭാഗമായുള്ള റേഞ്ചിൽ ആണ്‌ തടഞ്ഞിട്ടുള്ളത്. അത് ഒഴിവാക്കാവുന്നതാണ്.',
 'ip_range_invalid' => 'അസാധുവായ ഐ.പി. റേഞ്ച്.',
 'ip_range_toolarge' => 'പരിധി നിശ്ചയിച്ചുള്ള തടയലുകൾ /$1 എന്നതിലും കൂടുതലാകാൻ അനുവദിക്കുന്നില്ല.',
-'blockme' => 'എന്നെ തടയുക',
 'proxyblocker' => 'പ്രോക്സി തടയൽ',
-'proxyblocker-disabled' => 'ഈ പ്രക്രിയ സജ്ജമാക്കിയിട്ടില്ല.',
 'proxyblockreason' => 'ഓപ്പൺ പ്രോക്സി ആയതിനാൽ താങ്കളുടെ ഐ.പി. വിലാസത്തെ തടഞ്ഞിരിക്കുന്നു. ഇതു എന്തെങ്കിലും പിഴവ് മൂലം സംഭവിച്ചതാണെങ്കിൽ താങ്കളുടെ ഇന്റർനെറ്റ് സേവന ദാതാവിനെ സമീപിച്ചു ഈ സുരക്ഷാ പ്രശ്നത്തെ കുറിച്ച് ബോധിപ്പിക്കുക.',
-'proxyblocksuccess' => 'ചെയ്തു കഴിഞ്ഞു.',
 'sorbsreason' => '{{SITENAME}} ഉപയോഗിക്കുന്ന DNSBL ൽ താങ്കളുടെ ഐ.പി. വിലാസം ഒരു ഓപ്പൺ പ്രോക്സിയായാണു രേഖപ്പെടുത്തിട്ടുള്ളത്.',
 'sorbs_create_account_reason' => '{{SITENAME}} ഉപയോഗിക്കുന്ന DNSBL ൽ താങ്കളുടെ ഐ.പി. വിലാസം ഒരു ഓപ്പൺ പ്രോക്സിയായാണു രേഖപ്പെടുത്തിട്ടുള്ളത്. താങ്കൾക്ക് അംഗത്വമെടുക്കാൻ സാദ്ധ്യമല്ല.',
 'xffblockreason' => 'എക്സ്-ഫോർവേഡഡ്-ഫോർ ഹെഡറിലെ ഒരു ഐ.പി. വിലാസം, താങ്കളുടേതോ താങ്കൾ ഉപയോഗിക്കുന്ന പ്രോക്സി സെർവറിലേതോ ആകാം, തടയപ്പെട്ടിരിക്കുന്നതാണ്. തടയലിന്റെ കാരണം: $1',
@@ -3191,6 +3190,8 @@ $1',
 'spam_reverting' => '$1 എന്നതിലേയ്ക്കുള്ള കണ്ണികളില്ലാത്ത അവസാന നാൾപ്പതിപ്പിലേയ്ക്ക് മുൻപ്രാപനം ചെയ്യുന്നു',
 'spam_blanking' => '$1 എന്നതിലേയ്ക്ക് കണ്ണികളുള്ള എല്ലാ നാൾപ്പതിപ്പുകളും ശൂന്യമാക്കുന്നു',
 'spam_deleting' => '$1 എന്നതിലേയ്ക്ക് കണ്ണികളുള്ള എല്ലാ നാൾപ്പതിപ്പുകളും മായ്ക്കുന്നു',
+'simpleantispam-label' => "പാഴെഴുത്ത് വിരുദ്ധ പരിശോധന.
+ഇത് '''പൂരിപ്പിക്കരുത്'''!",
 
 # Info page
 'pageinfo-title' => '"$1" എന്ന താളിന്റെ വിവരങ്ങൾ',
index b158fe3..04734d6 100644 (file)
@@ -2398,12 +2398,9 @@ $1',
 Харин энэ нь $2 хэсгийн хэсэг болж түгжигдсэн байгаа бөгөөд үүнийг тайлах боломжтой.',
 'ip_range_invalid' => 'Хүчингүй IP-н хүрээ.',
 'ip_range_toolarge' => '/$1-с том хэмжээтэй түгжээ нь хориотой.',
-'blockme' => 'Намайг түгж',
 'proxyblocker' => 'Түгжигч прокси',
-'proxyblocker-disabled' => 'Энэ функцийг хаасан байна.',
 'proxyblockreason' => 'Таны IP хаяг нь чөлөөт прокси учраас түгжигдсэн байна.
 Та өөрийн ISP-тэйгээ холбоо барьж техникийн зөвлөгөө авч энэ асуудлынхаа тухай хэлнэ үү.',
-'proxyblocksuccess' => 'Гүйцэтгэсэн.',
 'sorbsreason' => '{{SITENAME}}-н хэрэглэдэг DNSBL-д таны IP хаягийг чөлөөт прокси хэмээн тодорхойлсон байна.',
 'sorbs_create_account_reason' => '{{SITENAME}}-н хэрэглэдэг DNSBL-д таны IP хаягийг чөлөөт прокси гэж тэмдэглэсэн байна.
 Та бүртгэл үүсгэх боломжгүй.',
index d5a7690..7a462ba 100644 (file)
@@ -541,7 +541,7 @@ $messages = array(
 'articlepage' => 'लेख पृष्ठ',
 'talk' => 'चर्चा',
 'views' => 'दृष्ये',
-'toolbox' => 'साधनपà¥\87à¤\9fà¥\80',
+'toolbox' => 'साधनà¥\87',
 'userpage' => 'सदस्य पृष्ठ',
 'projectpage' => 'प्रकल्प पान पहा',
 'imagepage' => 'संचिका पृष्ठ पहा',
@@ -657,6 +657,9 @@ $1',
 'databaseerror-text' => 'विदागार पृच्छा त्रूटी घडलेली आहे.
 ते संचेतनात गणकदोष असण्याची शक्यता निर्देशित करते.',
 'databaseerror-textcl' => 'विदागार पृच्छा त्रूटी घडलेली आहे.',
+'databaseerror-query' => 'पृच्छा:$1',
+'databaseerror-function' => 'क्रिया: $1',
+'databaseerror-error' => 'त्रुटी: $1',
 'laggedslavemode' => "'''सुचना:''' पानावर अद्ययावत बदल नसतील.",
 'readonly' => 'विदागारास (डाटाबेस) ताळे आहे.',
 'enterlockreason' => 'विदागारास ताळे ठोकण्याचे कारण, ताळे उघडले जाण्याच्या अदमासे कालावधीसहीत द्या.',
@@ -777,6 +780,8 @@ $2',
 'userlogin-resetpassword-link' => 'परवलीचा शब्द पुन्हा जुळवा (रिसेट)',
 'helplogin-url' => 'Help:सनोंद प्रवेशासाठी(लॉगिंग-ईन)',
 'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|सनोंद-प्रवेशासाठी(लॉग-ईन) सहाय्य]]',
+'userlogin-loggedin' => 'आपण पुर्वीच {{GENDER:$1|$1}} म्हणून सनोंद प्रवेशित आहात.वेगळ्या सदस्यनावाने सनोंद प्रवेशासाठी खालील आवेदन वापरा.',
+'userlogin-createanother' => 'दुसरे नवीन खाते तयार करा',
 'createacct-join' => 'खाली आपली माहिती भरा',
 'createacct-another-join' => 'नविन खात्याबाबतची माहिती येथे खाली टाका.',
 'createacct-emailrequired' => 'विपत्र पत्ता(ई-मेल)',
@@ -1515,6 +1520,7 @@ $1",
 'prefs-displaywatchlist' => 'दर्शन पर्याय',
 'prefs-tokenwatchlist' => 'ओळखचिन्ह',
 'prefs-diffs' => 'फरक',
+'prefs-help-prefershttps' => 'हा पसंतीक्रम आपल्या पुढील सनोंद प्रवेशानंतर कार्यान्वित होईल.',
 
 # User preference: email validation using jQuery
 'email-address-validity-valid' => 'विपत्रपत्ता वैध दिसत आहे',
@@ -1672,8 +1678,8 @@ $1",
 'action-block' => 'या सदस्यास संपादन करण्यापासून प्रतिबंधित करा',
 'action-protect' => 'या पानाचा सुरक्षास्तर बदला',
 'action-rollback' => 'या आधीच्या सदस्याने नुकतेच संपादन केलेले एखादे विशिष्ट पानाचे बदल लवकर पूर्वस्थितीत न्या',
-'action-import' => 'दà¥\81सऱà¥\8dया à¤µà¤¿à¤\95à¥\80वरà¥\81न à¤¹à¥\87 à¤ªà¤¾à¤¨ आयात करा',
-'action-importupload' => 'अपभारीत संचिकेतून पान आयात करा',
+'action-import' => 'दà¥\81सऱà¥\8dया à¤µà¤¿à¤\95à¥\80वरà¥\81न à¤ªà¤¾à¤¨à¥\87 आयात करा',
+'action-importupload' => 'अपभारीत संचिकेतून पान आयात करा',
 'action-patrol' => "इतरांची संपादनांवर 'पहारा दिला' म्हणून खूण करा",
 'action-autopatrol' => 'आपल्या संपादनांवर पहारा दिल्याची खूण करा',
 'action-unwatchedpages' => 'पहारा न दिलेल्या पानांची यादी पहा',
@@ -1689,6 +1695,7 @@ $1",
 
 # Recent changes
 'nchanges' => '$1 {{PLURAL:$1|बदल}}',
+'enhancedrc-since-last-visit' => '$1 {{PLURAL:$1|मागील भेटीनंतर}}',
 'enhancedrc-history' => 'इतिहास',
 'recentchanges' => 'अलीकडील बदल',
 'recentchanges-legend' => 'अलीकडील बदलाएवजी पर्याय',
@@ -1972,6 +1979,8 @@ https://www.mediawiki.org/wiki/Manual:Image_Authorization कृपया हे
 'listfiles_size' => 'आकार (बाईट्स)',
 'listfiles_description' => 'वर्णन',
 'listfiles_count' => 'आवृत्त्या',
+'listfiles-show-all' => 'या चित्राच्या जून्या आवृत्त्या अंतर्भूत करा.',
+'listfiles-latestversion' => 'सध्याची आवृत्ती',
 'listfiles-latestversion-yes' => 'हो',
 'listfiles-latestversion-no' => 'नाही',
 
@@ -2178,6 +2187,7 @@ Input:contenttype/subtype, e.g. <code>image/jpeg</code>.',
 'listusers' => 'सदस्यांची यादी',
 'listusers-editsonly' => 'फक्त संपादनांसहित सदस्य दाखवा',
 'listusers-creationsort' => 'निर्मितीच्या तारखेप्रमाणे लावा',
+'listusers-desc' => 'उतरत्या क्रमाने निवडा',
 'usereditcount' => '$1 {{PLURAL:$1|संपादन|संपादने}}',
 'usercreated' => 'दि. $1 ला, $2 वाजता, सदस्य खाते{{GENDER:$3|द्वारे बनविल्या गेले}}',
 'newpages' => 'नवीन पाने',
@@ -2436,9 +2446,11 @@ $UNWATCHURL
 'deleteotherreason' => 'दुसरे/अतिरिक्त कारण:',
 'deletereasonotherlist' => 'दुसरे कारण',
 'deletereason-dropdown' => '* वगळण्याची सामान्य कारणे
-** लेखकाची(लेखिकेची) विनंती
+** स्पॅम
+** उत्पात
 ** प्रताधिकार उल्लंघन
-** उत्पात',
+** लेखकाची(लेखिकेची) विनंती
+** तुटकी पुनर्निर्देशने',
 'delete-edit-reasonlist' => 'वगळण्याची कारणे संपादित करा',
 'delete-toobig' => 'या पानाला खूप मोठी इतिहास यादी आहे, तसेच हे पान $1 {{PLURAL:$1|पेक्षा|पेक्षा}}पेक्षा जास्त वेळा बदलण्यात आलेले आहे. अशी पाने वगळणे हे {{SITENAME}} ला धोकादायक ठरू नये म्हणून शक्य केलेले नाही.',
 'delete-warning-toobig' => 'या पानाला खूप मोठी इतिहास यादी आहे, तसेच हे पान $1 {{PLURAL:$1|पेक्षा|पेक्षा}} पेक्षा जास्त वेळा बदलण्यात आलेले आहे.
@@ -2458,7 +2470,7 @@ $UNWATCHURL
 शेवटचे संपादन [[User:$3|$3]] ([[User talk:$3|Talk]] [[Special:Contributions/$3|{{int:contribslink}}]])-चे होते.',
 'editcomment' => "संपादन सारांश \"''\$1''\" होता.",
 'revertpage' => '[[Special:Contributions/$2|$2]] ([[User talk:$2|चर्चा]]) यांनी केलेले बदल [[User:$1|$1]] यांच्या आवृत्तीकडे पूर्वपदास नेले.',
-'revertpage-nouser' => '(सदस्यनाम लपवले) यांनी केलेले बदल उलटवून [[User:$1|$1]] यांच्या आवृत्तीप्रमाणे पूर्ववत केले.',
+'revertpage-nouser' => 'लपविलेल्या सदस्याची संपादने उलटवून {{GENDER:$1|[[सदस्य:$1|$1]]}} यांच्या आवृत्तीप्रमाणे पूर्ववत केले.',
 'rollback-success' => '$1 ने उलटवलेली संपादने;$2 च्या आवृत्तीस परत नेली.',
 
 # Edit tokens
@@ -2590,7 +2602,7 @@ $1',
 'contributions' => '{{GENDER:$1|सदस्य}} योगदान',
 'contributions-title' => '$1 साठी सदस्य-योगदान',
 'mycontris' => 'योगदान',
-'contribsub2' => '$1 ($2) साठी',
+'contribsub2' => '{{GENDER:$3|$1}} ($2) साठी',
 'nocontribs' => 'या मानदंडाशी जुळणारे बदल सापडले नाहीत.',
 'uctop' => '(सद्य)',
 'month' => 'या महिन्यापासून (आणि पूर्वीचे):',
@@ -2746,11 +2758,8 @@ $1',
 'ipb_blocked_as_range' => 'त्रूटी:अंकपत्ता IP $1 हा प्रत्यक्षपणे प्रतिबंधित केलेला नाही आणि अप्रतिबंधीत करता येत नाही.तो,अर्थात,$2पल्ल्याचा भाग म्हाणून तो प्रतिबंधित केलेला आहे,जो की अप्रतिबंधीत करता येत नाही.',
 'ip_range_invalid' => 'अंकपत्ता अयोग्य टप्प्यात.',
 'ip_range_toolarge' => '/$1 पेक्षा मोठ्या Range प्रतिबंधनाची परवानगी नाह् are not allowed.',
-'blockme' => 'मला प्रतिबंधित करा',
 'proxyblocker' => 'प्रातिनिधी(प्रॉक्झी)प्रतिबंधक',
-'proxyblocker-disabled' => 'हे कार्य अवरूद्ध केले आहे.',
 'proxyblockreason' => 'तुमचा अंकपत्ता प्रतिबंधित केला आहे कारण तो उघड-उघड प्रतिनिधी आहे.कृपया तुमच्या आंतरजाल सेवा दात्यास किंवा तंत्रज्ञास पाचारण संपर्क करा आणि त्यांचे या गंभीर सुरक्षाप्रश्ना कडे लक्ष वेधा.',
-'proxyblocksuccess' => 'झाले.',
 'sorbsreason' => '{{SITENAME}}ने वापरलेल्या DNSBL मध्ये तुमच्या अंकपत्त्याची नोंद उघड-उघड प्रतिनिधी म्हणून सूचित केली आहे.',
 'sorbs_create_account_reason' => '{{SITENAME}}च्या DNSBLने तुमचा अंकपत्ता उघड-उघड प्रतिनिधी म्हणून सूचित केला आहे.तुम्ही खाते उघडू शकत नाही',
 'xffblockreason' => '(X-Forwarded-For header) मधील अंकपत्ता,आपला किंवा आपण वापरत असलेल्या सर्व्हरचा,प्रतिबंधित केल्या गेला आहे.प्रतिबंधित करण्याचे मुळ कारण होते:$1',
@@ -3090,6 +3099,8 @@ $1',
 'spam_reverting' => '$1शी दुवे नसलेल्या गेल्या आवर्तनाकडे परत उलटवत आहे',
 'spam_blanking' => '$1शी दुवे असलेली सर्व आवर्तने,रिक्त केली जात आहेत',
 'spam_deleting' => 'यातील सर्व आवृत्त्यांचे $1शी दुवे आहेत.गाळत आहे',
+'simpleantispam-label' => "चिखलणी विरोधक तपासणी.
+हे भरू '''नका'''!",
 
 # Info page
 'pageinfo-title' => '"$1" च्याबद्दल माहिती',
@@ -3913,7 +3924,10 @@ $5
 'tags-tag' => 'खूण नाव',
 'tags-display-header' => 'बदल सुचीवर कसे दिसेल',
 'tags-description-header' => 'अर्थाची पूर्ण माहिती',
+'tags-active-header' => 'सक्रिय?',
 'tags-hitcount-header' => 'खुणा केलेले बदल',
+'tags-active-yes' => 'होय',
+'tags-active-no' => 'नाही',
 'tags-edit' => 'संपादन करा',
 'tags-hitcount' => '$1 {{PLURAL:$1|बदल|बदल}}',
 
@@ -3934,6 +3948,7 @@ $5
 'dberr-problems' => 'माफ करा, हे संकेतस्थळ सध्या तांत्रिक अडचणींना सामोरे जात आहे.',
 'dberr-again' => 'थोडा वेळ थांबून पुन्हा पहा.',
 'dberr-info' => '( विदादाताशी संपर्क साधण्यात  असमर्थ : $1)',
+'dberr-info-hidden' => '( विदादात्याशी संपर्क साधण्यात  असमर्थ)',
 'dberr-usegoogle' => 'तोपर्यंत गूगलवर शोधून पहा',
 'dberr-outofdate' => 'लक्षात घ्या, आमच्या मजकुराबाबत त्यांची सूची कालबाह्य असू शकते',
 'dberr-cachederror' => 'ही मागवलेल्या पानाची सयीतील प्रत आहे, ती अद्ययावत नसण्याची शक्यता आहे.',
@@ -4072,7 +4087,7 @@ $5
 'limitreport-cputime' => 'CPU वापराचा वेळ',
 'limitreport-cputime-value' => '$1 {{PLURAL:$1|सेकंद}}',
 'limitreport-walltime-value' => '$1 {{PLURAL:$1|सेकंद}}',
-'limitreport-postexpandincludesize-value' => '$1/$2 बाईटस्',
-'limitreport-templateargumentsize-value' => '$1/$2 बाईटस्',
+'limitreport-postexpandincludesize-value' => '$1/$2 {{PLURAL:$2|बाइट|बाइट्स}}',
+'limitreport-templateargumentsize-value' => '$1/$2 {{PLURAL:$2|बाइट|बाइट्स}}',
 
 );
index fea4ebe..dfef63f 100644 (file)
@@ -338,8 +338,6 @@ $messages = array(
 'noindex-category' => 'Laman tak diindeks',
 'broken-file-category' => 'Laman yang ada pautan fail yang terputus',
 
-'linkprefix' => '/^(.*?)([a-zA-Z\\x80-\\xff]+)$/sD',
-
 'about' => 'Perihal',
 'article' => 'Laman kandungan',
 'newwindow' => '(dibuka di tetingkap baru)',
@@ -2699,12 +2697,9 @@ Sila lihat juga [[Special:BlockList|senarai sekatan]] untuk senarai larangan dan
 'ipb_blocked_as_range' => 'Ralat: IP $1 tidak boleh dinyahsekat kerana ia tidak disekat secara langsung. Sebaliknya, ia disekat kerana merupakan sebahagian daripada sekatan julat $2, yang mana boleh dinyahsekat.',
 'ip_range_invalid' => 'Julat IP tidak sah.',
 'ip_range_toolarge' => 'Sekatan julat yang lebih luas daripada /$1 adalah tidak dibenarkan.',
-'blockme' => 'Sekat saya',
 'proxyblocker' => 'Penyekat proksi',
-'proxyblocker-disabled' => 'Fungsi ini dimatikan.',
 'proxyblockreason' => 'Alamat IP anda telah disekat kerana ia merupakan proksi terbuka.
 Sila hubungi penyedia perkhidmatan Internet anda atau pihak sokongan teknikal dan beritahu mereka mengenai masalah keselamatan yang berat ini.',
-'proxyblocksuccess' => 'Berjaya.',
 'sorbsreason' => 'Alamat IP anda telah disenaraikan sebagai proksi terbuka dalam DNSBL yang digunakan oleh {{SITENAME}}.',
 'sorbs_create_account_reason' => 'Alamat IP anda telah disenaraikan sebagai proksi terbuka dalam DNSBL yang digunakan oleh {{SITENAME}}. Oleh itu, anda tidak dibenarkan membuka akaun baru.',
 'xffblockreason' => 'Alamat IP yang terdapat dalam pengepala X-Forwarded-For, sama ada milik anda ataupun pelayan proksi yang anda gunakan, telah disekat. Sebab asal sekatan adalah: $1',
@@ -3053,6 +3048,7 @@ Simpan dalam komputer anda dan muat naiknya di sini.',
 'spam_reverting' => 'Membalikkan kepada versi terakhir yang tidak mengandungi pautan ke $1',
 'spam_blanking' => 'Mengosongkan semua semakan yang mengandungi pautan ke $1',
 'spam_deleting' => 'Menghapuskan semua semakan yang mengandungi pautan ke $1',
+'simpleantispam-label' => "Pemeriksaan anti-spam. '''JANGAN''' isi ruangan ini!",
 
 # Info page
 'pageinfo-title' => 'Maklumat untuk "$1"',
index 360f61d..a1bea2d 100644 (file)
@@ -271,6 +271,8 @@ $magicWords = array(
        'formatdate'                => array( '0', 'formatdata', 'dataformat', 'formatdate', 'dateformat' ),
 );
 
+$linkPrefixCharset = 'A-\\x{10ffff}';
+
 $messages = array(
 # User preference toggles
 'tog-underline' => 'Ħoloq sottolinjati:',
@@ -411,8 +413,6 @@ $messages = array(
 'noindex-category' => 'Paġni mhux indiċizzati',
 'broken-file-category' => "Paġni b'ħoloq lejn fajls miksura",
 
-'linkprefix' => '/^(.*?)([a-żA-Ż\\x80-\\xff]+)$/sD',
-
 'about' => 'Dwar',
 'article' => 'artiklu',
 'newwindow' => "(tinfetaħ f'tieqa ġdida)",
@@ -2608,11 +2608,8 @@ Ara l-[[Special:BlockList|lista tal-blokki]] sabiex tara l-blokki attivi.',
 'ipb_blocked_as_range' => "Problema: L-Indirizz tal-IP $1 ma jistax jiġi blokkat waħdu u ma jistax jiġi sblokkat. L-Imblokk huwa attiv però f'livell ta' interval $2, li jista' jkun sblokkat.",
 'ip_range_invalid' => "Interval ta' indirizzi ta' IP mhux validi.",
 'ip_range_toolarge' => "Mhumiex permessi firxa ta' blokki ikbar minn /$1.",
-'blockme' => 'Imblukkani',
 'proxyblocker' => "Blokki ta' proxy miftuħa",
-'proxyblocker-disabled' => 'Din il-funzjoni mhijiex attivata.',
 'proxyblockreason' => "L-indirizz IP tiegħek ġie imblukkat peress li huwa proxy miftuħ. Jekk jogħġbok, ikkuntattja lill-provdituri tas-servizz tal-internet (ISP) jew lis-''support'' tekniku tiegħek u infurmahom b'din il-problema serja ta' sigurtà.",
-'proxyblocksuccess' => 'Blokk esegwit.',
 'sorbsreason' => 'L-indirizz IP tiegħek huwa mniżżel bħala proxy miftuħ fid-DNSBL użat minn {{SITENAME}}.',
 'sorbs_create_account_reason' => 'L-indirizz IP tiegħek huwa mniżżel bħala proxy miftuħ fid-DNSBL użat minn {{SITENAME}}. Ma tistax toħloq kont.',
 'cant-block-while-blocked' => 'Ma tistax timblokka lil utenti oħra waqt li inti mblukkat.',
index 3fe13d1..8a3fb50 100644 (file)
@@ -246,7 +246,7 @@ $messages = array(
 'articlepage' => 'Ber páigina de cuntenido',
 'talk' => 'Çcusson',
 'views' => 'Besitas',
-'toolbox' => 'Caixa de Ferramientas',
+'toolbox' => 'Ferramientas',
 'userpage' => 'Ber páigina de outelizador',
 'imagepage' => 'Ber páigina de fexeiro',
 'mediawikipage' => 'Ber páigina de mensaiges',
index 227cad4..4edacf7 100644 (file)
@@ -1509,8 +1509,6 @@ Your e-mail address is not revealed when other users contact you.
 'block-log-flags-hiddenname' => 'အသုံးပြုသူအမည် ဝှက်ထားသည်',
 'ipb_expiry_invalid' => 'သက်တမ်းကုန်လွန်မည့် အချိန်သည် တရားမဝင်ပါ။',
 'ipb_already_blocked' => '"$1" ကို အစကတည်းက ပိတ်ထားသည်',
-'blockme' => 'ကျွန်ုပ်ကို ပိတ်ရန်',
-'proxyblocksuccess' => 'ပြီးပါပြီ။',
 
 # Move page
 'move-page' => '$1 ကို ရွှေ့ရန်',
index 9fad6c7..e24ad45 100644 (file)
@@ -1638,8 +1638,6 @@ IP-тешкстэть — $3, саймас совавтоманть ID-сь —
 'block-log-flags-noemail' => 'е-сёрма озавтозь саймес',
 'block-log-flags-hiddenname' => 'лисиенть-совиенть лемезэ кекшезь',
 'ipb_already_blocked' => '"$1" уш саймас саезь',
-'blockme' => 'Озавтомак саймес',
-'proxyblocksuccess' => 'Озавтовсь.',
 
 # Developer tools
 'lockdb' => 'Сёлгомс датабазанть',
index 77b0acf..a9224e7 100644 (file)
@@ -1173,8 +1173,6 @@ Xiquitta $2 ic yancuīc tlapololiztli.',
 'change-blocklink' => 'Ticpatlaz tlatzacualli',
 'contribslink' => 'tlapatlaliztli',
 'blocklogpage' => 'Tlatequitiltilīlli ōmotzacuili',
-'blockme' => 'Timitzcuilīz',
-'proxyblocksuccess' => 'Ōmochīuh.',
 
 # Move page
 'move-page' => 'Ticzacāz $1',
index 91c0d6e..cd16f34 100644 (file)
@@ -2810,11 +2810,8 @@ Skjulingsloggen vises nedenfor.',
 'ipb_blocked_as_range' => 'Feil: IP-en $1 er ikke blokkert direkte, og kan ikke avblokkeres. Den er imidlertid blokkert som del av blokkeringa av IP-rangen $2, som kan avblokkeres.',
 'ip_range_invalid' => 'Ugyldig IP-rad.',
 'ip_range_toolarge' => 'Blokkering av IP-serier større enn /$1 er ikke tillatt.',
-'blockme' => 'Blokker meg',
 'proxyblocker' => 'Proxyblokker',
-'proxyblocker-disabled' => 'Denne funksjonen er slått av.',
 'proxyblockreason' => 'IP-adressen din ble blokkert fordi den er en åpen proxy. Kontakt internettleverandøren din eller teknisk støtte og informer dem om dette alvorlige sikkerhetsproblemet.',
-'proxyblocksuccess' => 'Utført.',
 'sorbsreason' => 'Din IP-adresse angis som en åpen proxy i DNSBL-en brukt av {{SITENAME}}.',
 'sorbs_create_account_reason' => 'Din IP-adresse angis som en åpen proxy i DNSBL-en brukt av {{SITENAME}}. Du kan ikke opprette en konto',
 'xffblockreason' => 'En IP-adresse som er tilstede i X-Forwarded-For-headeren, enten din eller en som tilhører en proxyserver du bruker, har blitt blokkert. Den opprinnelige blokkeringsgrunnen var: $1',
@@ -3167,6 +3164,8 @@ Dette er sannsynligvis forårsaket av en lenke til et svartelistet eksternt nett
 'spam_reverting' => 'Tilbakestiller til siste versjon uten lenke til $1',
 'spam_blanking' => 'Alle revisjoner inneholdt lenke til $1, tømmer siden',
 'spam_deleting' => 'Sletter alle revisjoner med lenker til $1',
+'simpleantispam-label' => "Antispamsjekk.
+'''IKKE''' fyll inn dette feltet!",
 
 # Info page
 'pageinfo-title' => 'Informasjon om «$1»',
index 10cc0d0..b981c5a 100644 (file)
@@ -2194,12 +2194,9 @@ Kiek [[Special:BlockList|IP-Blocklist]] för en List vun den blockten Brukern.',
 'ipb_cant_unblock' => 'Fehler: Block-ID $1 nich funnen. De Sperr is villicht al wedder ophoven.',
 'ipb_blocked_as_range' => 'Fehler: De IP-Adress $1 is as Deel vun de IP-Reeg $2 indirekt sperrt worrn. De Sperr trüchnehmen för $1 alleen geiht nich.',
 'ip_range_invalid' => 'Ungüllig IP-Addressrebeet.',
-'blockme' => 'Sperr mi',
 'proxyblocker' => 'Proxyblocker',
-'proxyblocker-disabled' => 'Disse Funkschoon is afstellt.',
 'proxyblockreason' => 'Dien IP-Adress is blockt, vun wegen dat se en apenen Proxy is.
 Kontakteer dien Provider oder diene Systemtechnik un informeer se över dat möögliche Sekerheitsproblem.',
-'proxyblocksuccess' => 'Trech.',
 'sorbsreason' => 'Diene IP-Adress steiht in de DNSBL vun {{SITENAME}} as apen PROXY.',
 'sorbs_create_account_reason' => 'Diene IP-Adress steiht in de DNSBL vun {{SITENAME}} as apen PROXY. Du kannst keen Brukerkonto nee opstellen.',
 'cant-block-while-blocked' => 'Du kannst kene annern Brukers sperren, wenn du sülvst sperrt büst.',
@@ -2480,6 +2477,7 @@ All Transwiki-Import-Akschonen staht later ok in dat [[Special:Log/import|Import
 'spambot_username' => 'MediaWiki Spam-Oprümen',
 'spam_reverting' => 'Trüchdreiht na de letzte Version ahn Lenken na $1.',
 'spam_blanking' => 'All Versionen harrn Lenken na $1, rein maakt.',
+'simpleantispam-label' => "Antispam-Kuntrull. Hier '''nix''' indragen!",
 
 # Info page
 'pageinfo-title' => 'Informatschoon för "$1"',
index 4c6ba0e..a7dd270 100644 (file)
@@ -2483,10 +2483,12 @@ Bevestig hieronder dat dit inderdaod de bedoeling is, da'j de gevolgen begriepen
 'deletecomment' => 'Reden:',
 'deleteotherreason' => 'Aandere/extra reden:',
 'deletereasonotherlist' => 'Aandere reden',
-'deletereason-dropdown' => '*Redens veur t vortdoon van ziejen
+'deletereason-dropdown' => '* Redens veur t vortdoon van ziejen
+** Spam
+** Vandalisme
+** Schending van auteursrechten
 ** Op verzeuk van de auteur
-** Schending van auteursrecht
-** Vandalisme',
+** Ebreuken deurverwiezing',
 'delete-edit-reasonlist' => 'Redens veur t vortdoon bewarken',
 'delete-toobig' => 'Disse zied hef n lange bewarkingsgeschiedenisse, meer as $1 {{PLURAL:$1|versie|versies}}.
 t Vortdoon van dit soort ziejen is mit rechten bepark um t per ongelok versteuren van de warking van {{SITENAME}} te veurkoemen.',
@@ -2802,11 +2804,8 @@ Wi'j de instellingen wiezigen?",
 De blokkering is onderdeel van de reeks $2, waorvan de blokkering wel op-eheven kan wörden.',
 'ip_range_invalid' => 'Ongeldige IP-reeks',
 'ip_range_toolarge' => 'Groeps-IP-adressen die groter bin as /$1, bin niet toe-estaon.',
-'blockme' => 'Mien blokkeren',
 'proxyblocker' => 'Proxyblokker',
-'proxyblocker-disabled' => 'Disse funksie is uutezet.',
 'proxyblockreason' => "Dit is n automatiese preventieve blokkering umda'j gebruukmaken van n open proxyserver.",
-'proxyblocksuccess' => 'Suksesvol.',
 'sorbsreason' => "Joew IP-adres is op-eneumen as open proxyserver in de zwarte lieste van DNS die'w veur {{SITENAME}} gebruken.",
 'sorbs_create_account_reason' => "Joew IP-adres is op-eneumen as open proxyserver in de zwarte lieste van DNS, die'w veur {{SITENAME}} gebruken.
 Je kunnen gien gebrukerszied anmaken.",
@@ -3146,6 +3145,8 @@ Meestentieds kömp dit deur n uutgaonde verwiezing die op de zwarte lieste steet
 'spam_reverting' => 'Bezig mit t weerummezetten naor de leste versie die gien verwiezing hef naor $1',
 'spam_blanking' => 'Alle wiezigingen mit n verwiezing naor $1 wörden vortehaold',
 'spam_deleting' => 'In alle versies staon verwiezingen naor $1. Zied vortedaon',
+'simpleantispam-label' => "Antispamkontraole.
+Hier '''NIKS''' invullen!",
 
 # Info page
 'pageinfo-title' => 'Informasie over "$1"',
@@ -3947,7 +3948,7 @@ Samen mit dit programma heur je n [{{SERVER}}{{SCRIPTPATH}}/COPYING kopie van de
 'specialpages-group-pagetools' => 'Ziedhulpmiddels',
 'specialpages-group-wiki' => 'Gegevens en hulpmiddels',
 'specialpages-group-redirects' => 'Deurverwiezende spesiale ziejen',
-'specialpages-group-spam' => 'Hulpmiddels tegen ongewunste bewarkingen',
+'specialpages-group-spam' => 'Spam-hulpmiddels',
 
 # Special:BlankPage
 'blankpage' => 'Lege zied',
index cd42746..99a16bf 100644 (file)
@@ -193,8 +193,6 @@ $messages = array(
 'noindex-category' => 'क्रमांकन नगरिएका पृष्ठहरु',
 'broken-file-category' => 'टुटेको फाइल लिंकसितको पृष्ठ',
 
-'linkprefix' => '/^(.*?)([a-zA-Z\\x80-\\xff]+)$/sD',
-
 'about' => 'बारेमा',
 'article' => 'सामाग्री पृष्ठ',
 'newwindow' => '(नयाँ विन्डोमा खुल्छ)',
@@ -273,7 +271,7 @@ $messages = array(
 'articlepage' => 'कन्टेन्ट पृष्ठ हेर्नुहोस्',
 'talk' => 'वार्तालाप',
 'views' => 'अवलोकनहरू',
-'toolbox' => 'à¤\94à¤\9cारबà¤\9fà¥\8dà¤\9fा',
+'toolbox' => 'à¤\94à¤\9cारहरà¥\82',
 'userpage' => 'प्रयोगकर्ता पृष्ठ हेर्ने',
 'projectpage' => 'प्रोजेक्ट पृष्ठ हेर्ने',
 'imagepage' => 'फाइल पृष्ठ हेर्नुहोस्',
@@ -336,6 +334,9 @@ $1',
 'newmessageslink' => 'नयाँ सन्देशहरू',
 'newmessagesdifflink' => 'आखिरी परिवर्तन',
 'youhavenewmessagesfromusers' => 'तपाईंको लागि  {{PLURAL:$3|प्रयोगकर्ता|$3 प्रयोगकर्ताहरु}} ($2) बाट $1',
+'youhavenewmessagesmanyusers' => 'तपाईँलाई धेरै प्रयोगकर्ताहरू($2) बाट $1 छ ।',
+'newmessageslinkplural' => '{{PLURAL:$1|नयाँ सन्देश|नयाँ सन्देशहरू}}',
+'newmessagesdifflinkplural' => 'अन्तिम {{PLURAL:$1|सम्पादन|सम्पादनहरू}}',
 'youhavenewmessagesmulti' => 'तपाईंको लागि $1 मा  नयाँ सन्देशहरू छन्',
 'editsection' => 'सम्पादन',
 'editold' => 'सम्पादन गर्ने',
@@ -389,6 +390,12 @@ $1',
 # General errors
 'error' => 'त्रुटि',
 'databaseerror' => 'डेटावेस त्रुटि',
+'databaseerror-text' => 'डेटाबेस क्वेरीमा खराबी देखा पर्‌यो ।
+यसले सफ्टवेयरमा त्रुटी रहेको इङ्गित गर्न सक्छ ।',
+'databaseerror-textcl' => 'डेटावेस क्वेरीमा खराबी देखियो ।',
+'databaseerror-query' => 'क्वेरी: $1',
+'databaseerror-function' => 'फङ्सन : $1',
+'databaseerror-error' => 'खराबी: $1',
 'laggedslavemode' => "'''चेतावनी:''' पृष्ठमा हालका अद्यतनहरु नहुनसक्छन् ।",
 'readonly' => 'डेटाबेस बन्द गरिएको छ',
 'enterlockreason' => 'ताल्चा मार्नुको कारण दिनुहोस्, साथै ताल्चा हटाउने समयको अवधि अनुमान लगाउनुहोस्।',
@@ -419,6 +426,8 @@ $1',
 'cannotdelete' => '"$1" पृष्ठ वा फ़ाइल मेट्नसकिएन।
 यो अघिबाट नैं मेटिएको हुनुपर्छ।',
 'cannotdelete-title' => 'पृष्ठ  "$1" लाई मेट्न सकिएन',
+'delete-hook-aborted' => 'हुकले सम्पादनकार्य बन्द गरिदियो ।
+कुनै कारण दिइएन ।',
 'badtitle' => 'गलत शीर्षक',
 'badtitletext' => 'अनुरोध गरेको पृष्ठ शीर्षक अमान्य, खाली वा गलत रुपमा अन्तर भाषा वा अन्तर विकी सम्बन्ध गरिएको थियो।  यसमा शीर्षकमा प्रयोग गर्न नमिल्ने एक वा बढी अक्षरहरू रहेका हुनसक्छन् ।',
 'perfcached' => 'तलको डाटाहरु क्याचमा रहेका कुराहरु हुन्। अपटुडेट नहुनपनि सक्छन्।अधिकतम {{PLURAL:$1|नतिजा|$1 नतिजाहरू}} क्यासमा उपलब्ध छ।',
@@ -433,10 +442,11 @@ $1',
 'actionthrottled' => 'कार्य रोकियो',
 'actionthrottledtext' => 'स्पामबाट बच्ने तरिकाको रुपमा , तपाईँलाई यो कार्य थोरै समयमा धेरै पटक गर्नबाट सिमित गरिएको छ, र तपाईले आफ्नो सिमा पार गरिसक्नु भयो ।
 कृपया केही मिनेटहरु पछि पुन: प्रयास गर्नुहोस्  ।',
-'protectedpagetext' => 'यà¥\8b à¤ªà¥\83षà¥\8dठ à¤¸à¤®à¥\8dपादन à¤¹à¥\81नबाà¤\9f à¤¬à¤\9aाà¤\89न à¤¸à¤®à¥\8dपादनमा à¤°à¥\8bà¤\95  लगाइएको छ।',
+'protectedpagetext' => 'यà¥\8b à¤ªà¥\83षà¥\8dठ à¤¸à¤®à¥\8dपादन à¤¹à¥\81नबाà¤\9f à¤¬à¤\9aाà¤\89न à¤¸à¤®à¥\8dपादनमा à¤¤à¤¥à¤¾ à¤\85नà¥\8dयà¤\95ारà¥\8dयमा à¤°à¥\8bà¤\95 लगाइएको छ।',
 'viewsourcetext' => 'तपाईँले यस पृष्ठको स्रोत हेर्न र प्रतिलिपी गर्न सक्नुहुन्छ ।',
 'viewyourtext' => "यस पृष्ठमा रहेका '''तपाईँका सम्पादनहरु''' हेर्न या प्रतिलिपी गर्न सक्नुहुन्छ :",
-'protectedinterface' => 'यो पृष्ठले सफ्टवेयरको लागि अन्तरमोहडा पाठ प्रदान गर्दछ , र यसलाई दुरुपयोग हुनबाट बचाउन ताल्चा मारिएको छ।',
+'protectedinterface' => 'यो पृष्ठले सफ्टवेयरको लागि अन्तरमोहडा पाठ प्रदान गर्दछ , र यसलाई दुरुपयोग हुनबाट बचाउन सुरक्षा प्रादन गरिएको छ।
+सम्पूर्ण विकिहरूका लागि अनुवादमा परिवर्तन गर्नको लागि [//translatewiki.net/ translatewiki.net], प्रयोग गर्नुहोस् ,  मिडियाविकि स्थानियकरण परियोजना ।',
 'editinginterface' => "'''चेतावनी:''' तपाईं यस्तो पृष्ठलाई सम्पादन गर्नुहुँदैछ, जसले सफ्टवेयरको लागि अन्तरमोहोड़ा (interface) पाठ प्रदान गर्दछ।
 यसको परिवर्तनले यस विकिमा अरु प्रयोगकर्ताको अन्तरमोहोड़ाको प्रदर्शनमा प्रभाव पार्छ।
 सबै विकिका निम्ति अनुवाद जोड्न अथवा परिबर्तन गर्न कृपया यहाँ जानुहोस् [//translatewiki.net/ translatewiki.net], मीडियाविकि स्थानीयकरण पारियोजना।",
@@ -448,9 +458,9 @@ $2',
 'ns-specialprotected' => 'विशेष पृष्ठ सम्पादन गर्न सकिदैन ।',
 'titleprotected' => ' [[User:$1|$1]]द्वारा यो शीर्षक निर्माणहुनबाट जोगाइएको छ।
 कारण   "\'\'$2\'\'" हो ।',
-'filereadonlyerror' => 'फाà¤\87ल "$1" à¤²à¤¾à¤\88 à¤ªà¤°à¤¿à¤µà¤°à¥\8dतन à¤\97रà¥\8dन à¤¸à¤\95िà¤\82दà¥\88न à¤\95िन à¤­à¤¨à¥\87à¤\82 फाइल भण्डार  "$2" केवल पढ्ने स्थिति (read-only mode)मा छ।
+'filereadonlyerror' => 'फाà¤\87ल "$1" à¤²à¤¾à¤\88 à¤ªà¤°à¤¿à¤µà¤°à¥\8dतन à¤\97रà¥\8dन à¤¸à¤\95िà¤\81दà¥\88न à¤\95िन à¤­à¤¨à¥\87 फाइल भण्डार  "$2" केवल पढ्ने स्थिति (read-only mode)मा छ।
 
-à¤\95ारण à¤¯à¥\8b à¤¦à¤¿à¤\8fà¤\95à¥\8bà¤\9b: "\'\'$3\'\'"।',
+यसलाà¤\88 à¤¸à¥\81रà¤\95à¥\8dषित à¤\97रà¥\8dनà¥\87 à¤ªà¥\8dरवनà¥\8dधà¤\95लà¥\87  à¤¯à¥\8b à¤\95ारण à¤¦à¤¿à¤\8fà¤\95ाà¤\9bनà¥\8d : \'\'$3\'\'।',
 'exception-nologin' => 'प्रवेश (लग ईन) नगरिएको',
 
 # Virus scanner
@@ -460,14 +470,25 @@ $2',
 
 # Login and logout pages
 'logouttext' => "'''तपाईं अहिले बाहिर निस्कनु भएको छ।'''
-तपाईंले नाम/खाताविनै पनि {{SITENAME}}मा प्रयोग गर्न सक्नुहुन्छ, अथवा अघिकै वा अर्कै कुनै नामको खाताबाट <span class='plainlinks'>[$1 फेरि प्रवेश गर्न]</span> पनि सक्नुहुन्छ।
-याद à¤°à¤¾à¤\96à¥\8dनà¥\81हà¥\8bसà¥\8d à¤¤à¤ªà¤¾à¤\88à¤\82लà¥\87 à¤¬à¥\8dराà¤\89à¤\9cरà¤\95à¥\8b à¤¸à¥\8dमरण à¤­à¤£à¥\8dडार खालि नगर्दासम्म कुनै पृष्ठहरूमा तपाईं अझै प्रवेश गरिराखेको देखाउन सक्छ।",
+
+याद à¤°à¤¾à¤\96à¥\8dनà¥\81हà¥\8bसà¥\8d à¤¤à¤ªà¤¾à¤\88à¤\82लà¥\87 à¤¬à¥\8dराà¤\89à¤\9cरà¤\95à¥\8b à¤\95à¥\8dयाश खालि नगर्दासम्म कुनै पृष्ठहरूमा तपाईं अझै प्रवेश गरिराखेको देखाउन सक्छ।",
 'welcomeuser' => '$1जी स्वागत छ!',
 'yourname' => 'प्रयोगकर्ता नाम:',
+'userlogin-yourname' => 'प्रयोगकर्ता नाम',
+'userlogin-yourname-ph' => 'तपाईँको प्रयोगकर्तानाम लेख्नुहोस्',
+'createacct-another-username-ph' => 'प्रयोगकर्तानाम लेख्नुहोस्',
 'yourpassword' => 'पासवर्ड',
+'userlogin-yourpassword' => 'पासवर्ड',
+'userlogin-yourpassword-ph' => 'तपाईँको पासवर्ड लेख्नुहोस्',
+'createacct-yourpassword-ph' => 'पासवर्ड लेख्नुहोस्',
 'yourpasswordagain' => 'पासवर्ड फेरि टाईप गर्नुहोस्',
+'createacct-yourpasswordagain' => 'पासवर्ड निश्चित गर्नुहोस्',
+'createacct-yourpasswordagain-ph' => 'फेरि पासवर्ड लेख्नुहोस्',
 'remembermypassword' => 'यो कम्प्युटरमा मेरो प्रवेश याद गर । (धेरैमा $1 {{PLURAL:$1|दिन|दिनहरु}})',
+'userlogin-remembermypassword' => 'मलाई प्रवेश गराइराख्ने',
+'userlogin-signwithsecure' => 'सुक्षित जडान प्रयोग गर्ने',
 'yourdomainname' => 'तपाईंको ज्ञानक्षेत्र(डोमेन)',
+'password-change-forbidden' => 'यो विकिमा पासवर्ड परिवर्तन गर्न सक्नुहुन्न ।',
 'externaldberror' => 'यहाँ प्रमाणिकरण डेटाबेस त्रुटि भयो या त तपाईंलाई आफ्नो बाहिरी खाता अद्यतन गर्ने अनुमति छैन।',
 'login' => 'प्रवेश',
 'nav-login-createaccount' => 'प्रवेश गर्ने/नयाँ खाता बनाउने',
@@ -477,14 +498,33 @@ $2',
 'logout' => 'निर्गमन',
 'userlogout' => 'निर्गमन (लग आउउ)',
 'notloggedin' => 'प्रवेश (लग ईन) नगरिएको',
+'userlogin-noaccount' => 'के खाता छैन ?',
+'userlogin-joinproject' => '{{SITENAME}} मा खाता खोल्नुहोस् ।',
 'nologin' => 'तपाईको खाता छैन? $1 ।',
 'nologinlink' => 'नयाँ खाता खोल्नुहोस्',
 'createaccount' => 'खाता खोल्नुहोस्',
 'gotaccount' => "के तपाईँसँग पहिले देखि नै खाता छ ? '''$1''' ।",
 'gotaccountlink' => 'लग इन',
 'userlogin-resetlink' => 'प्रवेश सम्बन्धी विवरणहरु बिर्सनु भयो?',
-'createaccountmail' => 'इ-मेलबाट',
+'userlogin-resetpassword-link' => 'पासवर्ड परिवर्तन गर्नुहोस्',
+'userlogin-createanother' => 'अर्को खाता खोल्नुहोस्',
+'createacct-join' => 'तपाईँका जानकारीहरू तल थप्नुहोस् ।',
+'createacct-another-join' => 'नयाँ खाताको जानकारी तल थप्नुहोस ।',
+'createacct-emailrequired' => 'इमेल ठेगाना',
+'createacct-emailoptional' => 'इमेल ठेगाना (ऐच्छिक)',
+'createacct-email-ph' => 'तपाईँको इमेल ठेगाना भर्नुहोस्',
+'createacct-another-email-ph' => 'इमेल ठेगाना भर्नुहोस्',
+'createaccountmail' => 'कुनै अस्थाई र श्रिजित पासवर्ड प्रयोग गर्ने र खुलाईएको इमेलमा पठाउने',
+'createacct-realname' => 'वास्तविक नाम(ऐच्छिक)',
 'createaccountreason' => 'कारण :',
+'createacct-reason' => 'कारण',
+'createacct-reason-ph' => 'किन नयाँ खाता खोलिरहनु भएको हो ?',
+'createacct-captcha' => 'सुरक्षा जाँच',
+'createacct-imgcaptcha-ph' => 'माथि देखिए अनुसारको पाठ भर्नुहोस्',
+'createacct-submit' => 'तपाईँको खाता सिर्जना गर्नुहोस',
+'createacct-another-submit' => 'अर्को खाता सिर्जना गर्नुहोस्',
+'createacct-benefit-heading' => '{{SITENAME}} तपाईँ जस्तै मानिसहरूद्वारा सिर्जना गरिएको हो ।',
+'createacct-benefit-body1' => '{{PLURAL:$1|सम्पादन|सम्पादनहरू}}',
 'badretype' => 'तपाईंले दिनुभएको पासवर्ड मिल्दैन।',
 'userexists' => 'तपाईले प्रविष्ट गर्नुभएको प्रयोगकर्ताको नाम पहिले देखिनै प्रयोगमा छ ।
 कृपया फरक नाम छान्नुहोस् ।',
@@ -541,12 +581,11 @@ $2',
 'cannotchangeemail' => 'यस विकिमा तपाईको खातासँग सम्बन्धित इमेल ठेगाना परिवर्तन गर्न सकिन्न ।',
 'emaildisabled' => 'यो साइटले इमेलहरु पठाउन सक्तैन।',
 'accountcreated' => 'खाता खोलियो',
-'accountcreatedtext' => '$1 कोलागि प्रयोगकर्ता खाता खोलियो।',
+'accountcreatedtext' => '[[{{ns:User}}:$1|$1]] ([[{{ns:User talk}}:$1|वार्ता]])$1 प्रयोगकर्ताको लागि खाता खोलिएको छ ।',
 'createaccount-title' => '{{SITENAME}}कोलागि खाता खोल्ने काम',
 'createaccount-text' => 'कसैले तपाईको इमेल ठेगानालाई {{SITENAME}} ($4) मा "$2" नामको खाता बनाएको छ, जसको पासवर्ड "$3" छ।',
 'usernamehasherror' => 'प्रयोगकर्तानाममा ह्यास अक्षरहरु राख्न मिल्दैन।',
-'login-throttled' => 'तपाईंले भर्खरै धेरै पल्ट प्रवेशको निम्ति प्रयास गर्नुभयो।
-कृपया पर्खेर केही समयपछि मात्र प्रयास गर्नुहोस्।',
+'login-throttled' => 'तपाईंले भर्खरै धेरै पल्ट प्रवेशको निम्ति प्रयास गर्नुभएको छ ,कृपया $1 पर्खेर मात्र प्रयास गर्नुहोस्।',
 'login-abort-generic' => 'तपाईंको प्रवेश असफल भयो - छोड़ियो',
 'loginlanguagelabel' => 'भाषा: $1',
 'suspicious-userlogout' => 'तपाईंको निर्गमन अनुरोध अस्विकार गरिन्छ किन कि यो खराब ब्राउजर वा क्यासिङ प्रोक्सिले पठाएको जस्तो देखिन्छ।',
@@ -565,7 +604,7 @@ $2',
 'newpassword' => 'नयाँ पासवर्ड:',
 'retypenew' => 'प्रवेश शव्द पुन: दिनुहोस् :',
 'resetpass_submit' => 'पासवर्ड व्यवस्थित गरी र प्रवेशगर्ने',
-'changepassword-success' => 'तपाà¤\88à¤\81à¤\95à¥\8b à¤ªà¥\8dरवà¥\87शशवà¥\8dद à¤¸à¤«à¤²à¤¤à¤¾à¤ªà¥\82रà¥\8dवà¤\95 à¤ªà¤°à¤¿à¤µà¤°à¥\8dतन à¤­à¤¯à¥\8b ! à¤¤à¤ªà¤¾à¤\88लाà¤\88 à¤ªà¥\8dरवà¥\87श à¤\97राà¤\87à¤\81दà¥\88à¤\9b ...',
+'changepassword-success' => 'तपाà¤\88à¤\81à¤\95à¥\8b à¤ªà¤¾à¤¸à¤µà¤°à¥\8dड à¤¸à¤«à¤²à¤¤à¤¾à¤ªà¥\82रà¥\8dवà¤\95 à¤ªà¤°à¤¿à¤µà¤°à¥\8dतन à¤­à¤¯à¥\8b !',
 'resetpass_forbidden' => 'प्रवेशशव्द परिवर्तन गर्न मिल्दैन',
 'resetpass-no-info' => 'यो पृष्ठ सिधै हेर्नको लागि तपाईँले प्रवेश गर्नुपर्छ ।',
 'resetpass-submit-loggedin' => 'प्रवेशशव्द परिवर्तन गर्ने',
@@ -598,8 +637,8 @@ $2
 तपाईंले प्रवेश गरेर अहिले नैं नयाँ पासवर्ड चुन्नुहोस्। यदि अरु कसैले अनुरोध गरेको भए अथवा यदि तपाईंलाई मूल पासवर्ड याद भए अनि यसलाई परिवर्तन गर्न चाहनु हुन्न भनें, तपाईंले यस सन्देशलाई अनदेखा गर्नुहोस् र पुरानै पासवर्डलाई चालू राख्नुहोस्।',
 'passwordreset-emailelement' => 'प्रयोगकर्ताको नाम: $1
 अस्थाई पासवर्ड: $2',
-'passwordreset-emailsent' => 'à¤\8fà¤\89à¤\9fा à¤\85नà¥\81सà¥\8dमारà¤\95 à¤\87मà¥\87ल à¤ªà¤ à¤¾à¤\87यà¥\8b।',
-'passwordreset-emailsent-capture' => 'à¤\85नà¥\81सà¥\8dमारà¤\95 à¤\87मà¥\87ल à¤ªà¤ à¤¾à¤\87यà¥\8b, à¤\9cà¥\8b तल देखाइएकोछ।',
+'passwordreset-emailsent' => 'पासवरà¥\8dड à¤ªà¤°à¤¿à¤µà¤°à¥\8dतनà¤\95à¥\8b à¤²à¤¾à¤\97ि à¤\87मà¥\87ल à¤ªà¤ à¤¾à¤\87à¤\8fà¤\95à¥\8b à¤\9b।',
+'passwordreset-emailsent-capture' => 'पासवरà¥\8dड à¤ªà¤°à¤¿à¤µà¤°à¥\8dतनà¤\95à¥\8b à¤²à¤¾à¤\97ि à¤\87मà¥\87ल à¤ªà¤ à¤¾à¤\87यà¥\8b, à¤\9cà¥\81न तल देखाइएकोछ।',
 
 # Special:ChangeEmail
 'changeemail' => 'इमेल ठेगाना परिवर्तन गर्नुहोस',
@@ -856,8 +895,8 @@ $2
 लिजेंड: (चालू): '''({{int:cur}})''' = अवतरणको बीचमा अंतर, '''({{int:last}})''' = पहिलाका अवतरणको बीचमा अंतर, '''{{int:minoreditletter}}''' = सानो परिवर्तन।",
 'history-fieldset-title' => 'इतिहासको विचरण गर्ने',
 'history-show-deleted' => 'मेटिएका मात्र',
-'histfirst' => 'पहिलो',
-'histlast' => 'à¤\85नà¥\8dतिम',
+'histfirst' => 'पà¥\81रानो',
+'histlast' => 'नयाà¤\81',
 'historysize' => '({{PLURAL:$1|१ बाइट |$1 बाइटहरु}})',
 'historyempty' => '(खाली)',
 
@@ -1040,7 +1079,7 @@ $1",
 'search-interwiki-default' => '$1 नतिजाहरु:',
 'search-interwiki-more' => '(अझै)',
 'search-relatedarticle' => 'सम्बन्धित',
-'mwsuggest-disable' => 'AJAX सुझाव निस्क्रिय पार्नुहोस्',
+'mwsuggest-disable' => 'खोज सुझावहरु अक्षम पार्ने',
 'searcheverything-enable' => 'सबै नेमस्पेसेजहरुमा खोज्नुहोस्',
 'searchrelated' => 'सम्बन्धित',
 'searchall' => 'सबै',
@@ -1092,7 +1131,7 @@ $1",
 'prefs-rendering' => 'स्वरुप',
 'saveprefs' => 'संग्रह',
 'resetprefs' => 'संग्रह नगरिएका परिवर्तनहरु सफागर्ने',
-'restoreprefs' => 'सबै पूर्वनिर्धारित स्थिती कायम गर्ने',
+'restoreprefs' => 'सबै पूर्वनिर्धारित स्थिती कायम गर्ने(सबै खण्डहरूमा)',
 'prefs-editing' => 'सम्पादन',
 'rows' => 'हरफहरु :',
 'columns' => 'स्तम्भहरु :',
@@ -1136,8 +1175,8 @@ $1",
 'prefs-emailconfirm-label' => 'इ-मेल एकिन प्रक्रिया :',
 'youremail' => 'ईमेल',
 'username' => '{{GENDER:$1|प्रयोगकर्ता नाम}}:',
-'uid' => 'प्रोगकर्ता आइडी:',
-'prefs-memberingroups' => 'निम्न {{PLURAL:$1|समूह | समूहहरू}}को सदस्य :',
+'uid' => '{{GENDER:$1|प्रयोगकर्ता}} ID:',
+'prefs-memberingroups' => 'निम्न {{PLURAL:$1|समूह | समूहहरू}}को {{GENDER:$2|सदस्य}} :',
 'prefs-memberingroups-type' => '$1',
 'prefs-registration' => 'दर्ता समय:',
 'prefs-registration-date-time' => '$1',
@@ -1150,10 +1189,10 @@ $1",
 HTML ट्यागहरु जाँच्नुहोस् ।',
 'badsiglength' => 'तपाईको दस्तखत धेरै लामो छ।
 यो $1 {{PLURAL:$1|अक्षर|अक्षरहरू}} भन्दा लामो हुनु हुँदैन ।',
-'yourgender' => 'लिà¤\99à¥\8dà¤\97:',
-'gender-unknown' => 'नà¤\96à¥\81लà¥\87à¤\95à¥\8b',
-'gender-male' => 'पà¥\81रà¥\82ष',
-'gender-female' => 'महिला',
+'yourgender' => 'à¤\95सरà¥\80 à¤µà¤¤à¤¾à¤\89न à¤\9aाहनà¥\81हà¥\81नà¥\8dà¤\9b ?',
+'gender-unknown' => 'म à¤\96à¥\81लाà¤\89न à¤\9aाहनà¥\8dन',
+'gender-male' => 'à¤\89सलà¥\87 à¤µà¤¿à¤\95ि à¤ªà¥\83षà¥\8dठहरà¥\82 à¤¸à¤®à¥\8dपादन à¤\97रà¥\8dà¤\9b',
+'gender-female' => 'à¤\89नलà¥\87 à¤µà¤¿à¤\95ि à¤ªà¥\83षà¥\8dठ à¤¸à¤®à¥\8dपादन à¤\97रà¥\8dà¤\9bिन',
 'prefs-help-gender' => 'वैकल्पिक: सफ्टवेयरले लिङगानुसार सम्बोधन गर्नको लागि प्रयोग गरिन्छ ।
 यो जानकारी सार्वजनिक हुनेछ ।',
 'email' => 'ईमेल',
@@ -1167,7 +1206,7 @@ HTML ट्यागहरु जाँच्नुहोस् ।',
 'prefs-signature' => 'हस्ताक्षर',
 'prefs-dateformat' => 'मिति ढाँचा',
 'prefs-timeoffset' => 'समय अफसेट',
-'prefs-advancedediting' => 'सामान्य',
+'prefs-advancedediting' => 'सामान्य विकल्पहरू',
 'prefs-advancedrc' => 'उन्नत विकल्पहरू',
 'prefs-advancedrendering' => 'उन्नत विकल्पहरु',
 'prefs-advancedsearchoptions' => 'उन्नत विकल्पहरू',
@@ -2242,12 +2281,9 @@ $1को बन्देजको कारण : "$2" हो',
 यो एक रेन्ज रोक $2, को अन्तर्गत रहेको छ जसलाई रोक खोल्न मिल्छ ।',
 'ip_range_invalid' => 'IP क्षेत्र अमान्य ।',
 'ip_range_toolarge' => ' /$1 भन्दा ठूलो रेन्ज रोक लगाउन पाइदैन ।.',
-'blockme' => 'मलाई निषेध गर्ने',
 'proxyblocker' => 'प्रोक्सी निषेध गर्ने',
-'proxyblocker-disabled' => 'यो कार्य निष्कृय पारिएको छ।',
 'proxyblockreason' => 'तपाईको IP ठेगानामा रोक लगाइएको छ किनकी यो खुला प्रोक्सी हो ।
 कृपया तपाईको इन्टरनेट सेवा प्रदायक या प्राविधिक सहायतालाई सम्पर्क गरी यस सुरक्षा समस्याको बारेमा जानकारी गराउनुहोस् ।',
-'proxyblocksuccess' => 'सकियो.',
 'sorbs' => 'DNSBL',
 'sorbsreason' => 'तपाईको IP ठेगाना खुल्ला प्रोक्सीको रुपमा  DNSBL मा सुचीकरण गरिएको छ यसलाई{{SITENAME}}ले प्रयोगमा ल्याएको छ।',
 'sorbs_create_account_reason' => 'तपाईको IP ठेगाना खुल्ला प्रोक्सीको रुपमा  DNSBL मा सुचीकरण गरिएको छ यसलाई{{SITENAME}}ले प्रयोगमा ल्याएको छ।
index 5e17a4d..4ec34de 100644 (file)
@@ -16,6 +16,7 @@
  * @author Erwin
  * @author Erwin85
  * @author Extended by Hendrik Maryns <hendrik.maryns@uni-tuebingen.de>, March 2007.
+ * @author Flightmare
  * @author Fryed-peach
  * @author Galwaygirl
  * @author Geitost
@@ -1637,7 +1638,7 @@ Als u deze opgeeft, kan deze naam gebruikt worden om u erkenning te geven voor u
 'prefs-signature' => 'Ondertekening',
 'prefs-dateformat' => 'Datumopmaak:',
 'prefs-timeoffset' => 'Tijdverschil',
-'prefs-advancedediting' => 'Algemene opties',
+'prefs-advancedediting' => 'Algemene instellingen',
 'prefs-editor' => 'Tekstverwerker',
 'prefs-preview' => 'Voorvertoning',
 'prefs-advancedrc' => 'Gevorderde instellingen',
@@ -2243,7 +2244,7 @@ Invoer: inhoudstype/subtype, bijvoorbeeld <code>image/jpeg</code>.',
 # Unused templates
 'unusedtemplates' => 'Ongebruikte sjablonen',
 'unusedtemplatestext' => 'Deze pagina geeft alle pagina\'s weer in de naamruimte {{ns:template}} die op geen enkele pagina gebruikt worden.
-Vergeet niet de "Koppelingen naar deze pagina" te controleren alvorens deze sjabloon te verwijderen.',
+Vergeet niet de "Koppelingen naar deze pagina" te controleren alvorens dit sjabloon te verwijderen.',
 'unusedtemplateswlh' => 'andere koppelingen',
 
 # Random page
@@ -2961,12 +2962,9 @@ Misschien is de blokkade al opgeheven.',
 De blokkade is onderdeel van de reeks $2, waarvan de blokkade wel opgeheven kan worden.',
 'ip_range_invalid' => 'Ongeldige IP-reeks.',
 'ip_range_toolarge' => 'Reeksblokkades groter dan /$1 zijn niet toegestaan.',
-'blockme' => 'Mij blokkeren',
 'proxyblocker' => 'Proxyblocker',
-'proxyblocker-disabled' => 'Deze functie is uitgeschakeld.',
 'proxyblockreason' => 'Uw IP-adres is geblokkeerd, omdat u gebruik maakt van een open proxyserver.
 Neem contact op met uw internetprovider of uw helpdesk en stel die op de hoogte van dit ernstige beveiligingsprobleem.',
-'proxyblocksuccess' => 'Afgerond.',
 'sorbsreason' => 'Uw IP-adres staat bekend als open proxyserver in de DNS-blacklist die {{SITENAME}} gebruikt.',
 'sorbs_create_account_reason' => 'Uw IP-adres staat bekend als open proxyserver in de DNS-blacklist die {{SITENAME}} gebruikt.
 U kunt geen gebruiker registreren.',
@@ -3338,6 +3336,8 @@ Meestal wordt dit door een externe koppeling op een zwarte lijst veroorzaakt.',
 'spam_reverting' => 'Teruggedraaid naar de laatste versie die geen koppeling bevat naar $1',
 'spam_blanking' => 'Alle versies bevatten een koppeling naar $1. Pagina leeggemaakt',
 'spam_deleting' => 'Alle versies bevatten koppelingen naar $1. Pagina verwijderd',
+'simpleantispam-label' => "Antispamcontrole.
+Vul dit veld '''NIET''' in!",
 
 # Info page
 'pageinfo-title' => 'Informatie over "$1"',
index 74eddc5..47bc87c 100644 (file)
@@ -457,8 +457,6 @@ $messages = array(
 'noindex-category' => 'Ikkje-indekserte sider',
 'broken-file-category' => 'Sider med brotne fillenkjer',
 
-'linkprefix' => '/^(.*?)([a-zA-Z\\x80-\\xff]+)$/sD',
-
 'about' => 'Om',
 'article' => 'Innhaldsside',
 'newwindow' => '(vert opna i eit nytt vindauge)',
@@ -2721,11 +2719,8 @@ IP-adresser som blir automatisk blokkerte er ikkje lista her. Sjå [[Special:Blo
 'ipb_blocked_as_range' => 'Feil: IP-en $1 er ikkje direkte blokkert og kan ikkje opphevast. Adressa er blokkert som ein del av blokkeringa av IP-intervallet $2. Denne blokkeringa kan opphevast.',
 'ip_range_invalid' => 'Ugyldig IP-adresseserie.',
 'ip_range_toolarge' => 'Blokkering av IP-seriar større enn /$1 er ikkje tillate.',
-'blockme' => 'Blokker meg',
 'proxyblocker' => 'Proxy-blokkerar',
-'proxyblocker-disabled' => 'Denne funksjonen er slått av.',
 'proxyblockreason' => 'Du er blokkert frå å endre fordi IP-adressa di tilhøyrer ein open mellomtenar (proxy). Du bør kontakte internettleverandøren din eller kundesørvis og gje dei beskjed, ettersom dette er eit alvorleg sikkerheitsproblem.',
-'proxyblocksuccess' => 'Utført.',
 'sorbsreason' => 'IP-adressa di er lista som ein open mellomtenar i DNSBL.',
 'sorbs_create_account_reason' => 'IP-adressa di er lista som ein open mellomtenar i DNSBL, og difor får du ikkje registrert deg.',
 'xffblockreason' => 'Ei IP-adresse i X-Forwarded-For-tittelen, anten di eller den som høyrer til ein proksytenar du nyttar, er blokkert. Den opphavlege blokkeringsgrunnen var: $1',
@@ -3063,6 +3058,8 @@ Vitja [//www.mediawiki.org/wiki/Localisation MediaWiki Localisation] og [//trans
 'spam_reverting' => 'Attenderullar til siste versjon utan lenkje til $1',
 'spam_blanking' => 'Alle versjonar inneheldt lenkje til $1, tømmer sida',
 'spam_deleting' => 'Alle versjonane inneheldt lenkjer til $1, slettar.',
+'simpleantispam-label' => "Antispam-kontroll.
+'''IKKJE''' fyll ut dette feltet!",
 
 # Info page
 'pageinfo-title' => 'Informasjon om «$1»',
index 114fb92..99cea76 100644 (file)
@@ -1028,7 +1028,6 @@ Seemo sa go lota ga letlakala '''$1''':",
 'unblocklogentry' => 'Gago thibelo $1',
 'block-log-flags-nocreate' => 'Go hloma tšhupaleloko gago dumelege',
 'block-log-flags-noemail' => 'e-mail e thibilwe',
-'proxyblocksuccess' => 'Phetilwe.',
 
 # Move page
 'move-page-legend' => 'Huduša letlakala',
index d53a578..caa64ac 100644 (file)
@@ -314,7 +314,7 @@ $messages = array(
 'tog-minordefault' => 'Considerar mas modificacions coma menoras per defaut',
 'tog-previewontop' => 'Far veire la previsualizacion al dessús de la zòna de modificacion',
 'tog-previewonfirst' => 'Far veire la previsualizacion al moment de la primièra edicion',
-'tog-nocache' => "Desactivar l'amagatal de paginas",
+'tog-nocache' => "Desactivar l'escondedor de las paginas pel navigador",
 'tog-enotifwatchlistpages' => 'M’avertir per corrièr electronic quand una pagina o un fichièr de ma lista de seguiment es modificat',
 'tog-enotifusertalkpages' => 'M’avertir per corrièr electronic en cas de modificacion de ma pagina de discussion',
 'tog-enotifminoredits' => 'M’avertir per corrièr electronic quitament en cas de modificacions menoras de las paginas o dels fichièrs',
@@ -510,7 +510,7 @@ $messages = array(
 'articlepage' => "Vejatz l'article",
 'talk' => 'Discussion',
 'views' => 'Afichatges',
-'toolbox' => "Bóstia d'aisinas",
+'toolbox' => 'Aisinas',
 'userpage' => "Pagina d'utilizaire",
 'projectpage' => 'Pagina meta',
 'imagepage' => 'Veire la pagina del fichièr',
@@ -540,7 +540,7 @@ $1",
 # All link text and link target definitions of links into project namespace that get used by other message strings, with the exception of user group pages (see grouppage).
 'aboutsite' => 'A prepaus de {{SITENAME}}',
 'aboutpage' => 'Project:A prepaus',
-'copyright' => 'Lo contengut es disponible segon los tèrmes de la licéncia $1.',
+'copyright' => 'Lo contengut es disponible jos licéncia $1 levat mencion contrària.',
 'copyrightpage' => '{{ns:project}}:Copyright',
 'currentevents' => 'Actualitats',
 'currentevents-url' => 'Project:Actualitats',
@@ -623,6 +623,8 @@ Una lista de las paginas especialas pòt èsser trobada sus [[Special:SpecialPag
 # General errors
 'error' => 'Error',
 'databaseerror' => 'Error de la banca de donadas',
+'databaseerror-text' => "Una error de requèsta de banca de donadas s'es producha. Aquò pòt provenir d'un bug dins lo logicial.",
+'databaseerror-textcl' => "Una error de requèsta de banca de donadas s'es produsida.",
 'databaseerror-query' => 'Requèsta : $1',
 'databaseerror-function' => 'Foncion : $1',
 'databaseerror-error' => 'Error : $1',
@@ -746,6 +748,9 @@ Doblidetz pas de modificar [[Special:Preferences|vòstras preferéncias per {{SI
 'userlogin-resetpassword-link' => 'Reïnicializar lo senhal',
 'helplogin-url' => 'Help:Connexion',
 'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|Ajuda a la connexion]]',
+'userlogin-loggedin' => 'Sètz ja connectat en tant que {{GENDER:$1|$1}}.
+Utilizatz lo formulari çaijós per vos connectar amb un autre utilizaire.',
+'userlogin-createanother' => 'Crear un autre compte',
 'createacct-join' => 'Entratz vòstras informacions çaijós.',
 'createacct-another-join' => 'Picar las informacions del novèl compte çaijós.',
 'createacct-emailrequired' => 'Adreça electronica',
@@ -806,7 +811,7 @@ podètz ignorar aqueste messatge e contunhar d'utilizar vòstre senhal ancian.",
 Identificatz-vos tre que l'aurètz recebut.",
 'blocked-mailpassword' => 'Vòstra adreça IP es blocada en edicion, la foncion de rapèl del senhal es doncas desactivada per evitar los abuses.',
 'eauthentsent' => 'Un corrièr de confirmacion es estat mandat a l’adreça indicada.
-Abans qu’un autre corrièr sià mandat a aqueste compte, vos caldrà seguir las instruccions donadas dins lo messatge per confirmar que sètz plan lo titular.',
+Abans qu’un autre corrièr sià mandat a aqueste compte, vos caldrà seguir las instruccions donadas dins lo messatge per confirmar que lo compte es plan vòstre.',
 'throttled-mailpassword' => 'Un corrièr electronic de reïnicializacion de vòstre senhal es ja estat mandat durant {{PLURAL:$1|la darrièra ora|las $1 darrièras oras}}. Per evitar los abuses, un sol corrièr de reïnicializacion de vòstre senhal serà pas mandat per {{PLURAL:$1|ora|interval de $1 oras}}.',
 'mailerror' => 'Error en mandant lo corrièr electronic : $1',
 'acct_creation_throttle_hit' => "De visitors d'aqueste wiki qu'utilizan vòstra adreça IP an creat $1 {{PLURAL:$1|compte|comptes}} lo jorn darrièr, aquò es lo limit maximum autorizat pendent aqueste periòde.
@@ -1230,15 +1235,15 @@ D’autres administrators sus {{SITENAME}} poiràn totjorn accedir al contengut
 * Informacions personalas inapropriadas
 *: ''adreça, numèro de telefòn, numèro de seguretat sociala, ...''",
 'revdelete-legend' => 'Metre en plaça de restriccions de version :',
-'revdelete-hide-text' => 'Amagar lo tèxte de la version',
+'revdelete-hide-text' => 'Tèxte de la revision',
 'revdelete-hide-image' => 'Amagar lo contengut del fichièr',
 'revdelete-hide-name' => 'Amagar l’accion e la cibla',
-'revdelete-hide-comment' => 'Amagar lo comentari de modificacion',
-'revdelete-hide-user' => 'Amagar lo pseudonim o l’adreça IP del contributor.',
+'revdelete-hide-comment' => 'Modificar lo resumit',
+'revdelete-hide-user' => 'Nom d’utilizaire/Adreça IP de l’editor',
 'revdelete-hide-restricted' => 'Suprimir aquestas donadas als administrators e mai als autres',
 'revdelete-radio-same' => '(cambiar pas)',
-'revdelete-radio-set' => 'Òc',
-'revdelete-radio-unset' => 'Non',
+'revdelete-radio-set' => 'Visible',
+'revdelete-radio-unset' => 'Amagat',
 'revdelete-suppress' => 'Suprimir las donadas dels administrators e tanben dels autres utilizaires',
 'revdelete-unsuppress' => 'Levar las restriccions sus las versions restablidas',
 'revdelete-log' => 'Motiu :',
@@ -1505,6 +1510,7 @@ Tanben podètz causir de permetre a d’autres de vos contactar per vòstra pagi
 'prefs-displaywatchlist' => "Opcions d'afichatge",
 'prefs-tokenwatchlist' => 'Geton',
 'prefs-diffs' => 'Diferéncias',
+'prefs-help-prefershttps' => 'Aquesta preferéncia serà efectiva al moment de vòstra connexion que ven.',
 
 # User preference: email validation using jQuery
 'email-address-validity-valid' => "L'adreça electronica sembla bona",
@@ -1531,6 +1537,7 @@ Tanben podètz causir de permetre a d’autres de vos contactar per vòstra pagi
 'userrights-notallowed' => "Avètz pas la permission d'apondre o suprimir de dreches d'utilizaire.",
 'userrights-changeable-col' => 'Los gropes que podètz cambiar',
 'userrights-unchangeable-col' => 'Los gropes que podètz pas cambiar',
+'userrights-conflict' => "Conflicte de modificacion de dreches d'utilizaire ! Relegissètz e confirmatz vòstras modificacions.",
 
 # Groups
 'group' => 'Grop :',
@@ -1659,8 +1666,8 @@ Tanben podètz causir de permetre a d’autres de vos contactar per vòstra pagi
 'action-block' => 'blocar aqueste utilizaire a l’edicion',
 'action-protect' => 'modificar los nivèls de proteccion per aquesta pagina',
 'action-rollback' => "anullar rapidament las modificacions del darrièr utilizaire qu'a modificat una pagina donada",
-'action-import' => 'importar aquesta pagina a partir d’un autre wiki',
-'action-importupload' => 'importar aquesta pagina e partir de l’impòrt d’un fichièr',
+'action-import' => 'importar de paginas dempuèi un autre wiki',
+'action-importupload' => 'importar de paginas dempuèi un fichièr telecargat',
 'action-patrol' => 'marcar la modificacion dels autres coma patrolhada',
 'action-autopatrol' => 'aver vòstra modificacion marcada coma patrolhada',
 'action-unwatchedpages' => 'veire la lista de las paginas pas susvelhadas',
@@ -2175,6 +2182,7 @@ Las entradas <del>barradas</del> son estadas resolgudas.',
 'listusers' => 'Lista dels participants',
 'listusers-editsonly' => "Far veire sonque los utilizaires qu'an al mens una contribucion",
 'listusers-creationsort' => 'Triar per data de creacion',
+'listusers-desc' => 'Triar en òrdre descendent',
 'usereditcount' => '$1 {{PLURAL:$1|cambiament|cambiaments}}',
 'usercreated' => '{{GENDER:$3|Creat}} lo $1 a $2',
 'newpages' => 'Paginas novèlas',
@@ -2435,10 +2443,12 @@ Vejatz $2 per una lista de las supressions recentas.',
 'deletecomment' => 'Motiu :',
 'deleteotherreason' => 'Motius suplementaris o autres :',
 'deletereasonotherlist' => 'Autre motiu',
-'deletereason-dropdown' => "*Motius de supression mai corrents
-** Demanda de l'autor
-** Violacion dels dreches d'autor
-** Vandalisme",
+'deletereason-dropdown' => '* Motius de supression los mai corrents
+** Corrièrs indesirables
+** Vandalisme
+** Violacion dels dreches d’autor
+** Demanda de l’autor
+** Redireccion copada',
 'delete-edit-reasonlist' => 'Modifica los motius de la supression',
 'delete-toobig' => "Aquesta pagina dispausa d'un istoric important, depassant {{PLURAL:$1|revision|revisions}}.
 La supression de talas paginas es estada limitada per evitar de perturbacions accidentalas de {{SITENAME}}.",
@@ -2460,7 +2470,7 @@ qualqu’un mai ja a modificat o revocat la pagina.
 La darrièra modificacion es estada efectuada per [[User:$3|$3]] ([[User talk:$3|Discutir]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
 'editcomment' => "Lo resumit de la modificacion èra : « ''$1'' ».",
 'revertpage' => 'Anullacion de las modificacions de [[Special:Contributions/$2|$2]] ([[User talk:$2|Discussion]]) cap a la darrièra version de [[User:$1|$1]]',
-'revertpage-nouser' => 'Revocacion de las modificacions per un d’utilizaire amagat a la darrièra version per [[User:$1|$1]]',
+'revertpage-nouser' => 'Revocacion de las modificacions per un utilizaire amagat a la darrièra version per {{GENDER:$1|[[User:$1|$1]]}}',
 'rollback-success' => 'Anullacion de las modificacions de $1 ; retorn a la version de $2.',
 
 # Edit tokens
@@ -2481,6 +2491,7 @@ Consultatz la [[Special:ProtectedPages|lista de las paginas protegidas]] per la
 'protect-title-notallowed' => 'Veire lo nivèl de proteccion de « $1 »',
 'prot_1movedto2' => 'a renomenat [[$1]] en [[$2]]',
 'protect-badnamespace-title' => 'Espaci de noms pas protegible',
+'protect-badnamespace-text' => 'Las paginas dins aqueste espaci de noms pòdon pas èsser protegidas.',
 'protect-norestrictiontypes-title' => 'Pagina pas protegibla',
 'protect-legend' => 'Confirmar la proteccion',
 'protectcomment' => 'Rason :',
@@ -2593,7 +2604,7 @@ $1",
 'contributions' => "Contribucions de l'{{GENDER:$1|utilizaire|utilizaira}}",
 'contributions-title' => 'Lista de las contribucions de l’utilizaire $1',
 'mycontris' => 'Contribucions',
-'contribsub2' => 'Lista de las contribucions de $1 ($2). Las paginas que son estadas escafadas son pas afichadas.',
+'contribsub2' => 'Per {{GENDER:$3|$1}} ($2)',
 'nocontribs' => 'Cap de modificacion correspondenta a aquestes critèris es pas estada trobada.',
 'uctop' => '(actual)',
 'month' => 'A partir del mes (e precedents) :',
@@ -2745,11 +2756,8 @@ Consultatz la [[Special:BlockList|lista dels utilizaires blocats]] per veire los
 'ipb_blocked_as_range' => "Error : L'adreça IP $1 es pas estada blocada dirèctament e doncas pòt pas èsser deblocada. Çaquelà, es estada blocada per la plaja $2 la quala pòt èsser deblocada.",
 'ip_range_invalid' => 'Plaja IP incorrècta.',
 'ip_range_toolarge' => 'Los blocatges de plajas mai grandas que /$1 son pas autorizadas.',
-'blockme' => 'Blocatz-me',
 'proxyblocker' => 'Blocaire de mandatari (proxy)',
-'proxyblocker-disabled' => 'Aquesta foncion es desactivada.',
 'proxyblockreason' => "Vòstra ip es estada blocada perque s’agís d’un proxy dobèrt. Mercé de contactar vòstre fornidor d’accès internet o vòstre supòrt tecnic e de l’informar d'aqueste problèma de seguretat.",
-'proxyblocksuccess' => 'Acabat.',
 'sorbsreason' => 'Vòstra adreça IP es listada en tant que mandatari (proxy) dobèrt DNSBL per {{SITENAME}}.',
 'sorbs_create_account_reason' => 'Vòstra adreça IP es listada en tant que mandatari (proxy) dobèrt DNSBL per {{SITENAME}}.
 Podètz pas crear un compte',
@@ -2845,6 +2853,7 @@ Lo volètz suprimir per permetre lo cambiament de nom ?',
 'immobile-source-page' => 'Aquesta pagina se pòt pas tornar nomenar.',
 'immobile-target-page' => 'Es pas possible de desplaçar la pagina cap a aqueste títol.',
 'imagenocrossnamespace' => 'Pòt pas desplaçar un imatge cap a un espaci de nomenatge que siá pas un imatge.',
+'nonfile-cannot-move-to-file' => "Impossible de renomenar quicòm mai qu'un fichièr cap a l'espaci de noms fichièr.",
 'imagetypemismatch' => "L'extension novèla d'aqueste fichièr reconeis pas aqueste format.",
 'imageinvalidfilename' => 'Lo nom del fichièr cibla es incorrècte',
 'fix-double-redirects' => 'Metre a jorn las redireccions que puntant cap al títol ancian',
@@ -2867,6 +2876,7 @@ Dins aqueste darrièr cas, podètz tanben utilizar un ligam, coma [[{{#Special:E
 'exportcuronly' => 'Exportar unicament la version correnta sens l’istoric complet',
 'exportnohistory' => "----
 '''Nòta :''' l’exportacion completa de l’istoric de las paginas amb l’ajuda d'aqueste formulari es estada desactivada per de rasons de performàncias.",
+'exportlistauthors' => 'Inclure una lista completa dels contributors per cada pagina',
 'export-submit' => 'Exportar',
 'export-addcattext' => 'Apondre las paginas de la categoria :',
 'export-addcat' => 'Apondre',
@@ -2901,6 +2911,8 @@ Visitatz la [//www.mediawiki.org/wiki/Localisation Localizacion MediaWiki] e [//
 $2",
 'djvu_page_error' => 'Pagina DjVu fòra limits',
 'djvu_no_xml' => "Impossible d’obténer l'XML pel fichièr DjVu",
+'thumbnail-temp-create' => 'Impossible de crear lo fichièr de vinheta temporari',
+'thumbnail-dest-create' => "Impossible d'enregistrar la vinheta sus la destinacion",
 'thumbnail_invalid_params' => 'Paramètres de la miniatura invalids',
 'thumbnail_dest_directory' => 'Impossible de crear lo repertòri de destinacion',
 'thumbnail_image-type' => 'Tipe d’imatge pas suportat',
@@ -2946,7 +2958,10 @@ Salvatz-lo sus vòstre disc dur puèi importatz-lo aicí.",
 'import-upload' => "Impòrt d'un fichier XML",
 'import-token-mismatch' => 'Pèrda de las donadas de sesilha. Tornatz ensajar.',
 'import-invalid-interwiki' => "Impossible d'importar dempuèi lo wiki especificat.",
+'import-error-edit' => 'La pagina « $1 » es pas estada importada perque sètz pas autorizat a la modificar.',
+'import-error-create' => 'La pagina « $1 » es pas estada importada perque sètz pas autorizat a la crear.',
 'import-options-wrong' => '{{PLURAL:$2|Marrida opcion|Marridas opcions}} : <nowiki>$1</nowiki>',
+'import-rootpage-invalid' => 'La pagina raiç provesida es un títol invalid.',
 
 # Import log
 'importlogpage' => 'Istoric de las importacions de paginas',
@@ -2959,6 +2974,7 @@ Salvatz-lo sus vòstre disc dur puèi importatz-lo aicí.",
 # JavaScriptTest
 'javascripttest' => 'Tèst de JavaScript',
 'javascripttest-title' => 'Execucion dels tèsts $1',
+'javascripttest-pagetext-noframework' => "Aquesta pagina es reservada per l'execucion dels tèsts JavaScript.",
 'javascripttest-pagetext-unknownframework' => 'Estructura « $1 » desconeguda.',
 'javascripttest-pagetext-frameworks' => 'Causissètz una de las estructuras de tèst seguentas : $1',
 'javascripttest-pagetext-skins' => 'Causissètz un abilhatge amb lo qual cal aviar los tèsts :',
@@ -3073,6 +3089,8 @@ Aquò es probablament causat per un ligam sus lista negra que punta cap a un sit
 'spam_reverting' => 'Restabliment de la darrièra version que conten pas de ligam cap a $1',
 'spam_blanking' => 'Totas las versions que contenon de ligams cap a $1 son blanquidas',
 'spam_deleting' => 'Totas las versions contenonián de ligams cap a $1, supression',
+'simpleantispam-label' => "Verificacion antispam.
+Inscriviscatz '''PAS RES''' dedins !",
 
 # Info page
 'pageinfo-title' => 'Informacions per « $1 »',
@@ -3899,7 +3917,10 @@ Ensajatz la previsualizacion normala.',
 'tags-tag' => 'Nom de la balisa',
 'tags-display-header' => 'Aparéncia dins las listas de modificacions',
 'tags-description-header' => 'Descripcion completa de la balisa',
+'tags-active-header' => 'Actiu ?',
 'tags-hitcount-header' => 'Modificacions balisadas',
+'tags-active-yes' => 'Òc',
+'tags-active-no' => 'Non',
 'tags-edit' => 'modificar',
 'tags-hitcount' => '$1 {{PLURAL:$1|cambiament|cambiaments}}',
 
@@ -3920,6 +3941,7 @@ Ensajatz la previsualizacion normala.',
 'dberr-problems' => 'O planhèm ! Aqueste site rencontra de dificultats tecnicas.',
 'dberr-again' => "Ensajatz d'esperar qualques minutas e tornatz cargar.",
 'dberr-info' => '(Se pòt pas connectar al servidor de la banca de donadas : $1)',
+'dberr-info-hidden' => '(Connexion al servidor de la banca de donadas impossibla)',
 'dberr-usegoogle' => 'Podètz ensajar de cercar amb Google pendent aqueste temps.',
 'dberr-outofdate' => 'Notatz que lors indèxes de nòstre contengut pòdon èsser depassats.',
 'dberr-cachederror' => 'Aquò es una còpia amagada de la pagina demandada e pòt èsser depassada.',
@@ -4011,8 +4033,12 @@ Ensajatz la previsualizacion normala.',
 'api-error-filetype-missing' => "L'extension del fichièr es mancanta.",
 'api-error-hookaborted' => "La modificacion qu'avètz ensajat de realizar es estada anullada per una extension.",
 'api-error-illegal-filename' => 'Lo nom del fichièr es pas autorizat.',
+'api-error-mustbeloggedin' => 'Vos cal èsser connectat per telecargar de fichièrs.',
+'api-error-mustbeposted' => 'Error intèrna : aquesta requèsta necessita lo metòde HTTP POST.',
 'api-error-nomodule' => 'Error intèrna : cap de modul de versament pas definit.',
 'api-error-ok-but-empty' => 'Error intèrna : Lo servidor a pas respondut.',
+'api-error-overwrite' => 'Espotir un fichièr existent es pas autorizat.',
+'api-error-stashfailed' => 'Error intèrna : lo servidor a pas pogut enregistrar lo fichièr temporari.',
 'api-error-unclassified' => "Una error desconeguda s'es producha.",
 'api-error-unknown-code' => 'Error desconeguda : « $1 »',
 'api-error-unknown-warning' => 'Avertiment desconegut : $1',
@@ -4042,9 +4068,9 @@ Ensajatz la previsualizacion normala.',
 'limitreport-ppvisitednodes' => 'Nombre de nosèls de preprocessor visitats',
 'limitreport-ppgeneratednodes' => 'Nombre de nosèls de preprocessor generats',
 'limitreport-postexpandincludesize' => 'Talha d’inclusion aprèp espandiment',
-'limitreport-postexpandincludesize-value' => '$1/$2 octets',
+'limitreport-postexpandincludesize-value' => '$1/$2 {{PLURAL:$2|octet|octets}}',
 'limitreport-templateargumentsize' => 'Talha de l’argument del modèl',
-'limitreport-templateargumentsize-value' => '$1/$2 octets',
+'limitreport-templateargumentsize-value' => '$1/$2 {{PLURAL:$2|octet|octets}}',
 'limitreport-expansiondepth' => 'Mai granda prigondor d’espandiment',
 'limitreport-expensivefunctioncount' => 'Nombre de foncions d’analisi costosas',
 
index cb687eb..4d8a75b 100644 (file)
@@ -2732,12 +2732,9 @@ $1ର ଅଟକ ପାଇଁ ଦିଆଯାଇଥିବା କାରଣଟି 
 ଏହା, $2 ଭିତରେ ଥିବାରୁ ତାହାକୁ ଅଟକରୁ ଛାଡ଼ କରାଯାଇପାରିବ ନାହିଁ ।',
 'ip_range_invalid' => 'ଅଚଳ IP ସୀମା ।',
 'ip_range_toolarge' => '/$1 ଠାରୁ ବଡ଼ ସୀମା ଅଟକ ଅନୁମୋଦିତ ନୁହେଁ ।',
-'blockme' => 'ମୋତେ ଅଟକାଇବେ',
 'proxyblocker' => 'ପ୍ରକ୍ସି ଅଟକ',
-'proxyblocker-disabled' => 'ଏହି କାମଟି ଅଚଳ କରାଯାଇଅଛି ।',
 'proxyblockreason' => 'ଏକ ଖୋଲା ପ୍ରକ୍ସି ହୋଇଥିବାରୁ ଆପଣଙ୍କ IP ଠିକଣାଟିକୁ ଅଟକାଇଦିଆଗଲା ।
 ଦୟାକରି ଆପଣଙ୍କ ଇଣ୍ଟରନେଟ ସେବାପ୍ରଦାନକାରୀ, କାରିଗରି ସହଯୋଗ କିମ୍ବା ସଙ୍ଗଠନ ସହିତ କଥା ହୋଇ ଏହି ବିରାଟ ଅସୁବିଧା ବାବଦରେ ବତାଇଦିଅନ୍ତୁ ।',
-'proxyblocksuccess' => 'ଶେଷ ହେଲା ।',
 'sorbsreason' => '{{SITENAME}} ଦେଇ ଆପଣଙ୍କ IP ଠିକଣାଟି DNSBL ଭିତରେ ଏକ ଖୋଲା ପ୍ରକ୍ସି ଭାବରେ ନଥିଭୁକ୍ତ ହୋଇଅଛି ।',
 'sorbs_create_account_reason' => '{{SITENAME}} ଦେଇ ଆପଣଙ୍କ IP ଠିକଣାଟି DNSBL ଭିତରେ ଏକ ଖୋଲା ପ୍ରକ୍ସି ଭାବରେ ନଥିଭୁକ୍ତ ହୋଇଅଛି ।
 ଆପଣ ନୂଆ ଖାତାଟିଏ ଖୋଲି ପାରିବେ ନାହିଁ',
@@ -3083,6 +3080,8 @@ MediaWiki ବ୍ୟବହାର କରି [[Special:Import|ପୃଷ୍ଠା 
 'spam_reverting' => '$1 ସହ ଯୋଡ଼ା ନଥିବା ଶେଷ ସଂସ୍କରଣକୁ ଲେଉଟାଇ ଦେଉଅଛୁଁ',
 'spam_blanking' => '$1 ସହ ଯୋଡ଼ାଥିବା ସବୁଯାକ ସଂସ୍କରଣ ଖାଲି କରିଦିଆଗଲା',
 'spam_deleting' => '$1 ସହ ଯୋଡ଼ାଥିବା ସବୁଯାକ ସଂସ୍କରଣ ଖାଲି କରିଦିଆଗଲା',
+'simpleantispam-label' => "ସ୍ପାମ-ବିରୋଧି ପରଖ ।
+ଏହାକୁ ଭରନ୍ତୁ '''ନାହିଁ''' !",
 
 # Info page
 'pageinfo-title' => '"$1"ର ବିବରଣୀ',
index 270fa0d..171475b 100644 (file)
@@ -698,8 +698,8 @@ $2',
 'emailnotauthenticated' => 'ਤੁਹਾਡਾ ਈਮੇਲ ਪਤਾ ਹਾਲੇ ਤਸਕਦੀਕ ਨਹੀਂ ਹੋਇਆ। ਹੇਠ ਦਿੱਤੇ ਫੀਚਰਾਂ ਲਈ ਕੋਈ ਵੀ ਈਮੇਲ ਨਹੀਂ ਭੇਜੀ ਜਾਵੇਗੀ।',
 'noemailprefs' => 'ਇਹਨਾਂ ਸਹੂਲਤਾਂ ਦੀ ਵਰਤੋਂ ਲਈ ਆਪਣੀਆਂ ਪਸੰਦਾਂ ਵਿਚ ਇਕ ਈ-ਮੇਲ ਪਤਾ ਦਿਓ।',
 'emailconfirmlink' => 'ਆਪਣਾ ਈਮੇਲ ਪਤਾ ਤਸਦੀਕ ਕਰਾਓ',
-'invalidemailaddress' => 'ਈ-ਮੇਲ ਪਤਾ ਕਬੂਲ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ ਹੈ ਕਿਉਂਕਿ ਇਹ ਸਹੀ ਅੰਦਾਜ਼ ਵਿਚ ਲਿਖਿਆ ਨਹੀਂ ਜਾਪਦਾ ਹੈ।
-ਸਹà©\80 à¨\85ੰਦਾà¨\9c਼ à¨µà¨¿à¨\9a à¨¦à¨¿à¨\93 ਜਾਂ ਇਹ ਖ਼ਾਨਾ ਖ਼ਾਲੀ ਛੱਡ ਦਿਓ।',
+'invalidemailaddress' => 'ਈਮੇਲ ਪਤਾ ਕਬੂਲ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ ਹੈ ਕਿਉਂਕਿ ਇਹ ਸਹੀ ਅੰਦਾਜ਼ ਵਿਚ ਲਿਖਿਆ ਨਹੀਂ ਜਾਪਦਾ ਹੈ।
+ਸਹà©\80 à¨\85ੰਦਾà¨\9c਼ à¨µà¨¿à¨\9a à¨²à¨¿à¨\96à©\8b ਜਾਂ ਇਹ ਖ਼ਾਨਾ ਖ਼ਾਲੀ ਛੱਡ ਦਿਓ।',
 'cannotchangeemail' => 'ਇਸ ਵਿਕੀ ਤੇ ਈ-ਮੇਲ ਪਤੇ ਬਦਲੇ ਨਹੀਂ ਜਾ ਸਕਦੇ।',
 'emaildisabled' => 'ਇਹ ਸਾਈਟ ਈ-ਮੇਲਾਂ ਨਹੀਂ ਭੇਜ ਸਕਦੀ।',
 'accountcreated' => 'ਖਾਤਾ ਬਣਾਇਆ',
@@ -941,7 +941,7 @@ $1 ਲੁਕਵੀਆਂ ਸ਼੍ਰੇਣੀਆਂ}} ਦਾ ਮੈਂਬਰ 
 'post-expand-template-inclusion-category' => 'ਉਹ ਸਫ਼ੇ ਜਿੱਥੇ ਫਰਮੇ ਸ਼ਾਮਲ ਕਰਨ ਦਾ ਅਕਾਰ ਹੱਦੋਂ ਵੱਧ ਗਿਆ ਹੈ',
 'post-expand-template-argument-warning' => "'''ਚੇਤਾਵਨੀ:'''
 ਇਸ ਪੰਨੇ ਤੇ ਘੱਟੋ ਘੱਟ ਇੱਕ ਐਸੀ ਸਾਂਚਾ ਬਹਿਸ ਹੈ ਜਿਸ ਦਾ ਅਕਾਰ ਬਹੁਤ ਵੱਡਾ ਹੈ। ਅਜਿਹੀਆਂ ਬਹਿਸਾਂ ਨੂੰ ਛੱਡ ਦਿੱਤਾ ਗਿਆ ਹੈ।",
-'post-expand-template-argument-category' => 'à¨\90ਸà©\87 à¨ªà©°à¨¨à©\87 à¨\9cਿਨà©\8dਹਾà¨\82 à¨µà¨¿à©±à¨\9a à¨¸à¨¾à¨\82à¨\9aà©\87 à¨¦à©\87 à¨¸à¨\81à¨\98à¨\9fà¨\95 à¨\9bà©\81ੱà¨\9f à¨\97à¨\8f à¨¹à¨¨ ।',
+'post-expand-template-argument-category' => 'à¨\90ਸà©\87 à¨¸à¨«à¨¼à©\87 à¨\9cਿਨà©\8dਹਾà¨\82 à¨µà¨¿à©±à¨\9a à¨«à¨°à¨®à©\87 à¨¦à©\87 à¨\86ਰà¨\97à©\82ਮà©\88à¨\82à¨\9f à¨\9bà©\81ੱà¨\9f à¨\97à¨\8f à¨¹à¨¨।',
 'parser-template-loop-warning' => 'ਫਰਮੇ ਦਾ ਲੂਪ ਲੱਭਿਆ: [[$1]]',
 
 # "Undo" feature
@@ -1240,7 +1240,7 @@ HTML ਟੈਗ ਚੈੱਕ ਕਰੋ।',
 'gender-female' => 'ਉਹ ਵਿਕੀ ਸਫ਼ੇ ਸੋਧਦੀ ਹੈ',
 'email' => 'ਈਮੇਲ',
 'prefs-help-realname' => 'ਅਸਲੀ ਨਾਂ ਚੋਣਵਾਂ ਹੈ, ਅਤੇ ਜੇ ਤੁਸੀਂ ਇਹ ਦਿੱਤਾ ਹੈ ਤਾਂ ਤੁਹਾਡੇ ਕੰਮ ਵਾਸਤੇ ਗੁਣ ਦੇ ਤੌਰ ਉੱਤੇ ਵਰਤਿਆ ਜਾਵੇਗਾ।',
-'prefs-help-email' => 'ਤà©\81ਹਾਡà©\80 à¨®à¨°à¨\9cà©\80 à¨¹à©\88 à¨\88ਮà©\87ਲ à¨ªà¨¤à¨¾ à¨¦à¨¿à¨\93 à¨\9cਾà¨\82 à¨¨à¨¾ à¨¦à¨¿à¨\93 à¨ªà¨° à¨ªà¨¾à¨¸à¨µà¨°à¨¡ à¨­à©\81ੱਲ à¨\9cਾਣ à¨¤à©\87 à¨¨à¨µà¨¾à¨\82 à¨ªà¨¾à¨¸à¨µà¨°à¨¡ à¨¹à¨¾à¨¸à¨² à¨\95ਰਨ à¨²à¨\88 à¨\87ਹ à¨\9cਰੂਰੀ ਹੈ।',
+'prefs-help-email' => 'à¨\88ਮà©\87ਲ à¨ªà¨¤à¨¾ à¨¦à©\87ਣਾ à¨¤à©\81ਹਾਡà©\80 à¨®à¨°à¨\9c਼à©\80 à¨¹à©\88, à¨¦à¨¿à¨\93 à¨\9cਾà¨\82 à¨¨à¨¾ à¨¦à¨¿à¨\93 à¨ªà¨° à¨ªà¨¾à¨¸à¨µà¨°à¨¡ à¨­à©\81ੱਲ à¨\9cਾਣ à¨¤à©\87 à¨¨à¨µà¨¾à¨\82 à¨ªà¨¾à¨¸à¨µà¨°à¨¡ à¨¹à¨¾à¨¸à¨² à¨\95ਰਨ à¨²à¨\88 à¨\87ਹ à¨\9c਼ਰੂਰੀ ਹੈ।',
 'prefs-help-email-others' => 'ਤੁਸੀਂ ਇਹ ਵੀ ਚੁਣ ਸਕਦੇ ਹੋ ਕਿ ਤੁਹਾਡੇ ਵਰਤੋਂਕਾਰ ਜਾਂ ਚਰਚਾ ਪੰਨੇ ਤੋਂ ਹੋਰ ਵਰਤੋਂਕਾਰ ਤੁਹਾਨੂੰ ਈ-ਮੇਲ ਭੇਜ ਸਕਣ?
 ਜਦੋਂ ਹੋਰ ਵਰਤੋਂਕਾਰ ਤੁਹਾਨੂੰ ਈ-ਮੇਲ ਭੇਜਦੇ ਹਨ ਤਾਂ ਤੁਹਾਡਾ ਈ-ਮੇਲ ਪਤਾ ਜ਼ਾਹਰ ਨਹੀਂ ਕੀਤਾ ਜਾਂਦਾ।',
 'prefs-help-email-required' => 'ਈ-ਮੇਲ ਪਤਾ ਚਾਹੀਦਾ ਹੈ।',
@@ -2081,8 +2081,6 @@ $1|ਤਬਦੀਲੀ ਹੋਈ|'''$1''' ਤਬਦੀਲੀਆਂ ਹੋਈਆ
 'ipb-otherblocks-header' => 'ਹੋਰ {{PLURAL:$1|ਪਾਬੰਦੀ|ਪਾਬੰਦੀਆਂ}}',
 'unblock-hideuser' => 'ਤੁਸੀਂ ਇਸ ਮੈਂਬਰ ’ਤੇ ਪਾਬੰਦੀ ਨਹੀਂ ਲਾ ਸਕਦੇ ਕਿਉਂਕਿ ਇਸਦਾ ਮੈਂਬਰ-ਨਾਂ ਲੁਕਾਇਆ ਹੋਇਆ ਹੈ।',
 'ipb_cant_unblock' => 'ਗ਼ਲਤੀ: ਪਾਬੰਦੀ ਪਤਾ $1 ਨਹੀਂ ਲੱਭਿਆ। ਸ਼ਾਇਦ ਇਹ ਪਹਿਲਾਂ ਹੀ ਪਾਬੰਦੀ-ਮੁਕਤ ਹੋ ਚੁੱਕਾ ਹੈ।',
-'blockme' => 'ਮੇਰੇ ’ਤੇ ਪਾਬੰਦੀ ਲਾਓ',
-'proxyblocksuccess' => 'ਪੂਰਾ ਹੋਇਆ',
 'cant-block-while-blocked' => 'ਤੁਸੀਂ ਦੂਜੇ ਮੈਂਬਰਾਂ ’ਤੇ ਪਾਬੰਦੀ ਨਹੀਂ ਲਾ ਸਕਦੇ ਜਦੋਂ ਤੁਸੀਂ ਖ਼ੁਦ ਪਾਬੰਦੀਸ਼ੁਦਾ ਹੋ।',
 'ipbblocked' => 'ਤੁਸੀਂ ਦੂਜੇ ਮੈਂਬਰਾਂ ਨੂੰ ਪਾਬੰਦੀਸ਼ੁਦਾ ਜਾਂ ਪਾਬੰਦੀ-ਮੁਕਤ ਨਹੀਂ ਕਰ ਸਕਦੇ ਕਿਉਂਕਿ ਤੁਸੀਂ ਖ਼ੁਦ ਪਾਬੰਦੀਸ਼ੁਦਾ ਹੋ',
 'ipbnounblockself' => 'ਤੁਹਾਨੂੰ ਖ਼ੁਦ ਨੂੰ ਪਾਬੰਦੀ-ਮੁਕਤ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਹੈ',
@@ -2562,6 +2560,8 @@ $1|ਤਬਦੀਲੀ ਹੋਈ|'''$1''' ਤਬਦੀਲੀਆਂ ਹੋਈਆ
 'confirmemail_send' => 'ਇੱਕ ਤਸਦੀਕੀ ਕੋਡ ਭੇਜੋ',
 'confirmemail_sent' => 'ਤਸਦੀਕੀ ਈਮੇਲ ਭੇਜੀ ਗਈ।',
 'confirmemail_invalid' => 'ਗਲਤ ਪੁਸ਼ਟੀ ਕੋਡ ਹੈ। ਕੋਡ ਦੀ ਮਿਆਦ ਪੁੱਗੀ ਹੋ ਸਕਦੀ ਹੈ।',
+'confirmemail_success' => 'ਤੁਹਾਡਾ ਈਮੇਲ ਪਤਾ ਤਸਦੀਕ ਹੋ ਚੁੱਕਾ ਹੈ।
+ਤੁਸੀਂ ਹੁਣ [[Special:UserLogin|ਲਾਗਇਨ]] ਕਰ ਕੇ ਵਿਕੀ ਦਾ ਮਜ਼ਾ ਸਕਦੇ ਹੋ।',
 'confirmemail_loggedin' => 'ਤੁਹਾਡਾ ਈ-ਮੇਲ ਪਤਾ ਹੁਣ ਤਸਦੀਕ ਹੋ ਚੁੱਕਾ ਹੈ।',
 'confirmemail_subject' => '{{SITENAME}} ਈ-ਮੇਲ ਪਤਾ ਤਸਦੀਕ',
 'confirmemail_invalidated' => 'ਈਮੇਲ ਪਤੇ ਦੀ ਤਸਦੀਕੀ ਰੱਦ ਕੀਤੀ ਗਈ',
@@ -2613,17 +2613,21 @@ $1|ਤਬਦੀਲੀ ਹੋਈ|'''$1''' ਤਬਦੀਲੀਆਂ ਹੋਈਆ
 ਸਧਾਰਨ ਝਲਕ ਦੀ ਕੋਸ਼ਿਸ਼ ਕਰੋ।',
 
 # Watchlist editor
-'watchlistedit-noitems' => 'ਤੁਹਾਡੀ ਨਿਗਰਾਨੀ-ਲਿਸਟ ਵਿਚ ਕੋਈ ਸਿਰਲੇਖ ਨਹੀਂ ਹਨ।',
+'watchlistedit-numitems' => 'ਗੱਲ-ਬਾਤ ਸਫ਼ਿਆਂ ਤੋਂ ਬਿਨਾਂ, ਤੁਹਾਡੀ ਨਿਗਰਾਨੀ-ਲਿਸਟ ਵਿੱਚ {{PLURAL:$1|1 ਸਿਰਲੇਖ ਹੈ|$1 ਸਿਰਲੇਖ ਹਨ}}।',
+'watchlistedit-noitems' => 'ਤੁਹਾਡੀ ਨਿਗਰਾਨੀ-ਲਿਸਟ ਵਿੱਚ ਕੋਈ ਸਿਰਲੇਖ ਨਹੀਂ ਹਨ।',
 'watchlistedit-normal-title' => 'ਨਿਗਰਾਨੀ-ਲਿਸਟ ਸੋਧੋ',
-'watchlistedit-normal-legend' => 'ਨਿà¨\97ਰਾਨà©\80-ਲਿਸà¨\9f à¨¤à©\8bà¨\82 à¨¸à¨¿à¨°à¨²à©\87à¨\96 ਹਟਾਓ',
+'watchlistedit-normal-legend' => 'ਸਿਰਲà©\87à¨\96ਾà¨\82 à¨¨à©\82à©° à¨¨à¨¿à¨\97ਰਾਨà©\80-ਲਿਸà¨\9f à¨µà¨¿à©±à¨\9aà©\8bà¨\82 ਹਟਾਓ',
 'watchlistedit-normal-submit' => 'ਸਿਰਲੇਖ ਹਟਾਓ',
+'watchlistedit-normal-done' => 'ਤੁਹਾਡੀ ਨਿਗਰਾਨੀ-ਲਿਸਟ ਵਿੱਚੋਂ {{PLURAL:$1|1 ਸਿਰਲੇਖ ਹਟਾਇਆ ਗਿਆ|$1 ਸਿਰਲੇਖ ਹਟਾਏ ਗਏ}}:',
 'watchlistedit-raw-title' => 'ਕੱਚੀ ਨਿਗਰਾਨ-ਸੂਚੀ ਸੋਧੋ',
-'watchlistedit-raw-legend' => 'ਕੱਚੀ ਨਿਗਰਾਨ-ਸੂਚੀ ਸੋਧੋ',
+'watchlistedit-raw-legend' => 'ਕੱਚੀ ਨਿਗਰਾਨੀ-ਲਿਸਟ ਸੋਧੋ',
+'watchlistedit-raw-explain' => 'ਤੁਹਾਡੀ ਨਿਗਰਾਨੀ-ਲਿਸਟ ਵਿੱਚ ਮੌਜੂਦ ਸਫ਼ੇ ਹਟਾਏ ਜਾਂ ਹੋਰ ਜੋੜੇ ਜਾ ਸਕਦੇ ਹਨ। ਹਟਾਉਣ ਜਾਂ ਜੋੜਨ ਤੋਂ ਬਾਅਦ "{{int:Watchlistedit-raw-submit}}" ’ਤੇ ਕਲਿੱਕ ਕਰੋ।
+ਤੁਸੀਂ [[Special:EditWatchlist|ਮਿਆਰੀ ਐਡੀਟਰ]] ਵੀ ਵਰਤ ਸਕਦੇ ਹੋ।',
 'watchlistedit-raw-titles' => 'ਸਿਰਲੇਖ:',
 'watchlistedit-raw-submit' => 'ਨਿਗਰਾਨੀ-ਲਿਸਟ ਤਾਜ਼ੀ ਕਰੋ',
-'watchlistedit-raw-done' => 'ਤੁਹਾਡੀ ਨਿਗਰਾਨ-ਸੂਚੀ ਅੱਪਡੇਟ ਹੋ ਗਈ ਹੈ।',
-'watchlistedit-raw-added' => '{{PLURAL:$1|1 title was|$1 titles were}} ਸ਼ਾਮਲ:',
-'watchlistedit-raw-removed' => '{{PLURAL:$1|1 title was|$1 titles were}} ਹਟਾਓ:',
+'watchlistedit-raw-done' => 'ਤੁਹਾਡੀ ਨਿਗਰਾਨੀ-ਲਿਸਟ ਅੱਪਡੇਟ ਹੋ ਗਈ ਹੈ।',
+'watchlistedit-raw-added' => '{{PLURAL:$1|1 ਸਿਰਲੇਖ ਸ਼ਾਮਲ ਕੀਤਾ|$1 ਸਿਰਲੇਖ ਸ਼ਾਮਲ ਕੀਤੇ}}:',
+'watchlistedit-raw-removed' => '{{PLURAL:$1|1 ਸਿਰਲੇਖ ਹਟਾਇਆ|$1 ਸਿਰਲੇਖ ਹਟਾਏ}}:',
 
 # Watchlist editing tools
 'watchlisttools-view' => 'ਸਬੰਧਤ ਤਬਦੀਲੀਆਂ ਵੇਖੋ',
index 572a001..0788b05 100644 (file)
@@ -1655,12 +1655,9 @@ Lon me ing [[Special:BlockList|IP block list]] para king tala da reng kasalungsu
 'ipb_blocked_as_range' => 'Mali: E diretsung makasabat ing IP $1, at e maliaring ilako pangasabat.
 Pero makasabat ya antimong kayabe king range $2, a maliaring ilako pangasabat.',
 'ip_range_invalid' => 'E matatanggap a IP range.',
-'blockme' => 'Sabatan muku',
 'proxyblocker' => 'Maniabat a proxy',
-'proxyblocker-disabled' => 'Makapatda (disabled) ya ing gamit (function) a ini.',
 'proxyblockreason' => 'Mesabat ya ing kekang IP address uling metung yang open proxy.
 Pakiyaus me ing kekang Internet service provider o tech support at pabaluan me kaniting mabayat a prublema king seguridad.',
-'proxyblocksuccess' => 'Merapat na.',
 'sorbsreason' => 'Makalista ya ing kekang IP address antimong open proxy king DNSBL a gagamitan ning {{SITENAME}}.',
 'sorbs_create_account_reason' => 'Makalista yang open proxy king DNSBL a gagamitan ning {{SITENAME}} ing kekang IP address.
 E ka maliaring maglalang account.',
index 25ddedf..4d12a58 100644 (file)
@@ -790,7 +790,6 @@ Guck $2 fer e Lischt vun de letscht Leschunge.',
 'infiniteblock' => 'fer immer',
 'blocklink' => 'Aabinne',
 'contribslink' => 'Ardickele',
-'proxyblocksuccess' => 'Geduh.',
 
 # Move page
 'move-page' => '„$1“ ziehe',
@@ -935,7 +934,7 @@ Guck $2 fer e Lischt vun de letscht Leschunge.',
 
 # Separators for various lists, etc.
 'ellipsis' => '…',
-'percent' => '$1&nbsp;%',
+'percent' => '$1&#160;%',
 
 # Multipage image navigation
 'imgmultipageprev' => '← letscht Blatt',
index 839037b..54fe1db 100644 (file)
@@ -769,6 +769,9 @@ Nie zapomnij dostosować [[Special:Preferences|preferencji]].',
 'userlogin-resetpassword-link' => 'Nie pamiętasz hasła?',
 'helplogin-url' => 'Help:Logowanie',
 'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|Pomoc z logowaniem]]',
+'userlogin-loggedin' => 'Zalogowano jako {{GENDER:$1|$1}}.
+Użyj poniższego formularza, aby zalogować się jako inny użytkownik.',
+'userlogin-createanother' => 'Załóż nowe konto',
 'createacct-join' => 'Wpisz poniżej swoje dane.',
 'createacct-another-join' => 'Wprowadź szczegóły nowego konta poniżej.',
 'createacct-emailrequired' => 'Adres e‐mail',
@@ -1278,7 +1281,7 @@ wybrana wersja nie istnieje lub próbowano ukryć wersję bieżącą.',
 'logdelete-selected' => "'''Zaznaczone {{PLURAL:$1|zdarzenie|zdarzenia}} z rejestru:'''",
 'revdelete-text' => "'''Usunięte wersje i czynności będą nadal widoczne w historii strony i rejestrach, ale ich treść nie będzie publicznie dostępna.'''
 Inni administratorzy {{GRAMMAR:D.lp|{{SITENAME}}}} nadal będą mieć dostęp do ukrytych treści oraz będą mogli je odtworzyć używając standardowych mechanizmów, chyba że nałożono dodatkowe ograniczenia.",
-'revdelete-confirm' => 'Potwierdź, że chcesz to zrobić, rozumiesz konsekwencje oraz że robisz to zgodnie z [[{{MediaWiki:Policy-url}}|zasadami]].',
+'revdelete-confirm' => 'Potwierdź, że chcesz to zrobić zgodnie z [[{{MediaWiki:Policy-url}}|zasadami]] i że rozumiesz konsekwencje.',
 'revdelete-suppress-text' => "Ukrywanie powinno być używane '''wyłącznie''' w sytuacji:
 * Ujawnienie danych osobowych
 *: ''adres domowy, numer telefonu, numer PESEL itp''",
@@ -2687,7 +2690,7 @@ $1',
 'contributions' => 'Wkład {{GENDER:$1|użytkownika|użytkowniczki}}',
 'contributions-title' => 'Wkład {{GENDER:$1|użytkownika|użytkowniczki}} $1',
 'mycontris' => 'Edycje',
-'contribsub2' => 'Dla {{GENDER:$3|użytkownika|użytkowniczki}}$1 ($2)',
+'contribsub2' => 'Dla {{GENDER:$3|użytkownika|użytkowniczki}} $1 ($2)',
 'nocontribs' => 'Brak zmian odpowiadających tym kryteriom.',
 'uctop' => '(ostatnia)',
 'month' => 'Do miesiąca (włącznie)',
@@ -2845,12 +2848,9 @@ By przejrzeć listę obecnie aktywnych blokad, przejdź na stronę [[Special:Blo
 Należy on do zablokowanego zakresu adresów $2. Odblokować można tylko cały zakres.',
 'ip_range_invalid' => 'Niepoprawny zakres adresów IP.',
 'ip_range_toolarge' => 'Zakresy IP większe niż /$1 są niedozwolone.',
-'blockme' => 'Zablokuj mnie',
 'proxyblocker' => 'Blokowanie proxy',
-'proxyblocker-disabled' => 'Ta funkcja jest wyłączona.',
 'proxyblockreason' => 'Twój adres IP został zablokowany, ponieważ jest to adres otwartego proxy.
 O tym poważnym problemie dotyczącym bezpieczeństwa należy poinformować dostawcę Internetu lub pomoc techniczną.',
-'proxyblocksuccess' => 'Wykonano.',
 'sorbsreason' => 'Twój adres IP znajduje się na liście serwerów open proxy w DNSBL, używanej przez {{GRAMMAR:B.lp|{{SITENAME}}}}.',
 'sorbs_create_account_reason' => 'Twój adres IP znajduje się na liście serwerów open proxy w DNSBL, używanej przez {{GRAMMAR:B.lp|{{SITENAME}}}}.
 Nie możesz utworzyć konta',
@@ -3212,6 +3212,8 @@ Najprawdopodobniej zostało to spowodowane przez link do zewnętrznej strony int
 'spam_reverting' => 'Przywracanie ostatniej wersji nie zawierającej linków do $1',
 'spam_blanking' => 'Wszystkie wersje zawierały odnośniki do $1. Czyszczenie strony.',
 'spam_deleting' => 'Wszystkie wersje zawierały linki do $1, usuwam.',
+'simpleantispam-label' => "Filtr antyspamowy.
+'''NIE''' wpisuj tu nic!",
 
 # Info page
 'pageinfo-title' => 'Informacje o „$1“',
@@ -3891,7 +3893,7 @@ Czy na pewno chcesz ją ponownie utworzyć?",
 'confirm-unwatch-top' => 'Usunąć tę stronę z listy obserwowanych?',
 
 # Separators for various lists, etc.
-'percent' => '$1&nbsp;%',
+'percent' => '$1&#160;%',
 
 # Multipage image navigation
 'imgmultipageprev' => '← poprzednia strona',
@@ -4041,7 +4043,7 @@ Powinieneś otrzymać [{{SERVER}}{{SCRIPTPATH}}/COPYING kopię licencji GNU Gene
 # Special:Redirect
 'redirect' => 'Przekierowanie według pliku, użytkownika albo identyfikatora wersji',
 'redirect-legend' => 'Przekieruj do pliku lub strony',
-'redirect-summary' => 'Ta strona specjalna przekierowuje do pliku (wg nazwy pliku), do strony (wg numeru wersji) albo do strony użytkownika (wg liczbowego identyfikatora użytkownika)',
+'redirect-summary' => 'Ta strona specjalna przekierowuje do: pliku (o podanej nazwie), do strony (o podanym numerze wersji) albo do strony użytkownika (o podanym identyfikatorze numerycznym). Sposób użycia: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/revision/328429]] albo [[{{#Special:Redirect}}/user/103]].',
 'redirect-submit' => 'Przejdź',
 'redirect-lookup' => 'Wyszukaj:',
 'redirect-value' => 'Wartość:',
@@ -4099,7 +4101,7 @@ Powinieneś otrzymać [{{SERVER}}{{SCRIPTPATH}}/COPYING kopię licencji GNU Gene
 'tag-filter-submit' => 'Filtr',
 'tag-list-wrapper' => '([[Special:Tags|{{PLURAL:$1|Znacznik|Znaczniki}}]]: $2)',
 'tags-title' => 'Znaczniki',
-'tags-intro' => 'Na tej stronie znajduje się lista wzorców tekstu, dla których oprogramowanie może oznaczyć edycje, dodatkowo wskazując ich znaczenie.',
+'tags-intro' => 'Na tej stronie znajduje się lista znaczników, którymi oprogramowanie może oznaczyć edycje, oraz ich opisów.',
 'tags-tag' => 'Nazwa znacznika',
 'tags-display-header' => 'Wystąpienia na listach zmian',
 'tags-description-header' => 'Pełny opis znaczenia',
index 008c59d..bdd48cd 100644 (file)
@@ -260,7 +260,7 @@ $messages = array(
 'articlepage' => 'Vëdde la pàgina ëd contnù',
 'talk' => 'Discussion',
 'views' => 'Vìsite',
-'toolbox' => "Bòita dj'utiss",
+'toolbox' => 'Utiss',
 'userpage' => 'Che a varda la pàgina Utent',
 'projectpage' => 'Che a varda la pàgina ëd proget',
 'imagepage' => "Vëdde la pàgina dl'archivi",
@@ -818,66 +818,65 @@ test ch'a-i é già. Sossì dle vire a riva quand un a deuvra un servent anònim
 'yourtext' => 'Sò test',
 'storedversion' => 'La version memorisà',
 'nonunicodebrowser' => "'''A L'EUJ! Sò programa ëd navigassion a marcia pa giust con lë stàndard Unicode. I soma obligà a dovré dij truschin përchè a peula salvesse sò artìcoj sensa problema: ij caràter che a son nen ASCII a jë s-ciairerà ant ël quàder ëd modìfica dël test coma còdes esadecimaj.'''",
-'editingold' => "'''CHE A FASA MACH ATENSION: che a sta fasend-je dle modìfiche a na version nen agiornà dl'artìcol.<br />
+'editingold' => "'''CHE A FASA MACH ATENSION: che a l'é an camin ch'a modìfica na version nen agiornà dl'artìcol.<br />
 Se a la salva parèj, lòn che a l'era stàit fàit dapress a sta revision-sì as perdrà d'autut.'''",
 'yourdiff' => 'Diferense',
-'copyrightwarning' => "Che a ten-a për piasì present che tute le contribussion a {{SITENAME}} as consìdero dàite sota a na licensa ëd la sòrt $2 (che a varda $1 për avèj pì 'd detaj).
-Se a veul nen che sò test a peula esse modificà e distribuì da qualsëssìa përson-a sensa gnun-a limitassion ëd gnun-a sòrt, che a lo buta pa ansima a {{SITENAME}}, ma pitòst che as lo pùblica ansima a un sò sit përsonal.<br />
-Ën mandand ës test-sì chiel (chila) as fa garant sota soa responsabilità che ël test a l'ha scrivusslo despërchiel (daspërchila) coma original, ò pura che a l'ha tracopialo da na sorgiss ëd pùblich domini, ò da n'àutra sorgiss dla midema sòrt, ò pura che chiel (chila) a l'ha arseivù autorisassion scrita a dovré sto test e che sòn a peul dimostrelo.<br />
-'''DOVRÉ PA MAI DËL MATERIAL COATÀ DA DRIT D'AUTOR (c) SENSA AVÈJ N'AUTORISASSION SCRITA PËR FELO!!!'''",
-'copyrightwarning2' => "Për piasì, che a ten-a present che tute le contribussion a {{SITENAME}} a peulo esse modificà ò scancelà da dj'àutri contributor. Se a veul nen che lòn che a scriv a ven-a modificà sensa limitassion ëd gnun-a sòrt, che a lo manda nen ambelessì.<br />
-Ant l'istess temp, ën mandand dël material un as pija la responsabilità dë dì che a l'ha scrivusslo daspërchiel (ò daspërchila), ò pura che a l'ha copialo da na sorgiss ëd domini pùblich, ò pura da 'nt n'àutra sorgiss dla midema sòrt (che a varda $1 për avèj pì d'anformassion).
-'''CHE A MANDA PA DËL MATERIAL COATÀ DA DRIT D'AUTOR SENSA AVÈJ AVÙ ËL PËRMESS SCRIT DË FELO!'''",
-'longpageerror' => "'''EROR: Ël test che a l'ha mandà a l'é longh {{PLURAL:$1|un kilobyte|$1 kilobyte}} , che a resta pì che ël
-lìmit màssim ëd {{PLURAL:$2|un kilobyte|$2 kilobyte}}. Parèj as peul pa salvesse.",
-'readonlywarning' => "'''Avis: La base dat a l'é stàita blocà për manutension, e donca a podrà pa salvesse soe modìfiche tut sùbit.'''
+'copyrightwarning' => "Che a ten-a për piasì da ment che tute le contribussion a {{SITENAME}} as consìdero dàite sota a na licensa ëd la sòrt $2 (che a varda $1 për avèj pì 'd detaj).
+Se a veul nen che sò test a peula esse modificà e distribuì da qualsëssìa përson-a sensa gnun-a limitassion ëd gnun-a sòrt, che a lo buta pa ambelessì.<br />
+Ën mandand ës test-sì chiel as fa garant sota soa responsabilità che ël test a l'ha scrivusslo despërchiel, ò pura che a l'ha tracopialo da na sorgiss ëd pùblich domini, ò da n'àutra sorgiss dla midema sòrt.
+'''Anserì mai dël material coatà da drit d'autor sensa avèj n'autorisassion për felo!'''",
+'copyrightwarning2' => "Për piasì, che a ten-a da ment che tute le contribussion a {{SITENAME}} a peulo esse modificà ò scancelà da d'àutri contributor. Se a veul nen che lòn che a scriv a ven-a modificà sensa limitassion ëd gnun-a sòrt, che a lo manda nen ambelessì.<br />
+Ant l'istess temp, ën mandand dël material un as pija la responsabilità dë dì che a l'ha scrivusslo daspërchiel, ò pura che a l'ha copialo da na sorgiss ëd domini pùblich, ò pura da 'nt n'àutra sorgiss dla midema sòrt (che a varda $1 për avèj pì d'anformassion).
+'''Che a manda pa dël material coata da drit d'autor sensa avèj avù ël përmess ëd felo!'''",
+'longpageerror' => "'''EROR: Ël test che a l'ha mandà a l'é longh {{PLURAL:$1|un kilobyte|$1 kilobytes}}, che a resta pì che ël lìmit màssim {{PLURAL:$2|d'un kilobyte|ëd $2 kilobyte}}.''' Parèj as peul pa salvesse.",
+'readonlywarning' => "'''Avis: La base ëd dat a l'é stàita blocà për manutension, e donca a podrà pa salvesse soe modìfiche tut sùbit.'''
 A peul esse che a-j ven-a còmod copiesse via sò test e ancoless-lo an n'archivi ëd test e goernelo për pi tard.
 
 L'aministrator che a l'ha fàit ël blocagi a l'ha dàit costa spiegassion: $1",
-'protectedpagewarning' => "'''Avis: costa pàgina-sì a l'é stàita blocà an manera che mach j'utent con la qualìfica da aministrator a peulo feje dle modìfiche.'''
+'protectedpagewarning' => "'''Avis: costa pàgina-sì a l'é stàita protegiùa an manera che mach j'utent con la qualìfica da aministrator a peulo feje dle modìfiche.'''
 L'ùltima vos dël registr a l'é smonùa sì-sota për arferiment:",
 'semiprotectedpagewarning' => "'''Nòta:''' Costa pàgina-sì a l'é stàita blocà an manera che mach j'utent registrà a peulo modifichela.
 L'ùltima vos dël registr a l'é smonùa sì-sota për arferiment:",
-'cascadeprotectedwarning' => "'''Tension:''' sta pàgina-sì a l'é stàita blocà an manera che mach j'utent con la qualìfica da aministrator a peulo modifichela, për via che {{PLURAL:\$1|a l'é proteta|a-i intra ant le pàgine protete}} col sistema \"a cascada\":",
+'cascadeprotectedwarning' => "'''Tension:''' Sta pàgina a l'é stàita blocà an manera che mach j'utent con la qualìfica da aministrator a peulo modifichela, për via che a l'é comprèisa an {{PLURAL:$1|costa pàgina-sì|an coste pàgine-sì}} ch'a l'han ël sistema ëd protession a cascada:",
 'titleprotectedwarning' => "'''Avis: sta pàgina-sì a l'é stàita blocà an manera che a-i é dabzògn ëd [[Special:ListGroupRights|drit specìfich]] për creela.'''
 L'ùltima vos dël registr a l'é smonùa sì-sota për arferiment:",
-'templatesused' => '{{PLURAL:$1|Stamp|Stamp}} dovrà dzora a sta pàgina-sì:',
-'templatesusedpreview' => '{{PLURAL:$1|Stamp|Stamp}} dovrà ant sta preuva-sì:',
-'templatesusedsection' => '{{PLURAL:$1|Stamp|Stamp}} dovrà ant sta session-sì:',
+'templatesused' => '{{PLURAL:$1|Stamp}} dovrà da costa pàgina-sì:',
+'templatesusedpreview' => '{{PLURAL:$1|Stamp}} dovrà an costa preuva:',
+'templatesusedsection' => '{{PLURAL:$1|Stamp}} dovrà an costa session-sì:',
 'template-protected' => '(protet)',
 'template-semiprotected' => '(mes-protet)',
 'hiddencategories' => 'Sta pàgina-sì a fa part ëd {{PLURAL:$1|na categorìa|$1 categorìe}} stërmà:',
-'edittools' => "<!-- Test ch'a së s-ciàira sot a ij mòduj ëd mòdifica e 'd càrich d'archivi. -->",
-'nocreatetext' => "Cost sit-sì a l'ha limità la possibilità ëd creé dle pàgine neuve.
+'edittools' => "<!-- Test ch'a së s-ciàira sot a ij mòduj ëd modìfica e 'd carie d'archivi. -->",
+'nocreatetext' => "{{SITENAME}} a l'ha limità la possibilità ëd creé dle pàgine neuve.
 A peul torné andaré e modifiché na pàgina che a-i é già, ò pura [[Special:UserLogin|rintré ant ël sistema ò deurb-se un cont]].",
-'nocreate-loggedin' => "A l'ha pa ij përmess për creé dle pàgine neuve.",
-'sectioneditnotsupported-title' => "La modìfica dla session a l'é nen prevëdùa",
-'sectioneditnotsupported-text' => "La modìfica dla session a l'é nen prevëdùa an costa pàgina ëd modìfica.",
+'nocreate-loggedin' => "A l'ha pa ël përmess ëd creé dle pàgine neuve.",
+'sectioneditnotsupported-title' => "La modìfica ëd session a l'é nen mantnùa",
+'sectioneditnotsupported-text' => "La modìfica ëd na session a l'é nen mantnùa për costa pàgina.",
 'permissionserrors' => 'Eror ant ij përmess',
-'permissionserrorstext' => "A l'ha pa ij përmess dont a fa da manca për {{PLURAL:$1|via che|via che}}:",
-'permissionserrorstext-withaction' => "It l'has nen ij përmess për $2, për {{PLURAL:$1|cost motiv|costi motiv}}:",
-'recreate-moveddeleted-warn' => "A l'é an camin ch'a crea torna na pàgina ch'a l'era stàita scancelà.'''
+'permissionserrorstext' => "A l'ha pa ij përmess dont a fa da manca për {{PLURAL:$1|via che}}:",
+'permissionserrorstext-withaction' => "A l'ha nen ij përmess për $2, për {{PLURAL:$1|cost motiv|costi motiv}}:",
+'recreate-moveddeleted-warn' => "'''Atension: a l'é an camin ch'a crea torna na pàgina ch'a l'era stàita scancelà.'''
 
 Ch'a varda d'esse sigur ch'a vala la pen-a ëd travajé an sna pàgina parèj.
 Për soa comodità i-j mostroma la lista djë scancelament ch'a toco sta pàgina-sì:",
 'moveddeleted-notice' => "Sta pàgina-sì a l'é stàita scancelà.
-Ël registr ëd le scancelassion e dij tramud a l'é arportà sota për arferiment.",
-'log-fulllog' => 'Varda tut ël registr',
+Ël registr ëd le scancelassion e dij tramud a l'é arportà sì-sota për arferiment.",
+'log-fulllog' => 'Vëdde tut ël registr',
 'edit-hook-aborted' => "Modìfica anulà da n'estension.
-A-i é pa gnun-e spiegassion.",
-'edit-gone-missing' => 'As peul nen modifiché la pàgina.
+A-i é pa  spiegassion.",
+'edit-gone-missing' => 'As peul nen agiornesse la pàgina.
 A smija che a sia stàita scancelà.',
-'edit-conflict' => "Conflit d'edission.",
-'edit-no-change' => "Toa modìfica a l'é stàita ignorà, përchè a l'é pa stàit fàit gnun cambiament al test.",
-'postedit-confirmation' => "Soa modìfica a l'é stàita salvà!",
-'edit-already-exists' => 'As peul nen creesse la pàgina.
-A esist già.',
+'edit-conflict' => 'Conflit ëd modìfiche.',
+'edit-no-change' => "Soa modìfica a l'é stàita ignorà, përchè gnun cambiament a l'é stàit fàit al test.",
+'postedit-confirmation' => "Soa modìfica a l'é stàita salvà.",
+'edit-already-exists' => "La neuva pàgina a l'ha nen podù creesse.
+A esist già.",
 'defaultmessagetext' => "Test che a-i sarìa se a-i fusso pa 'd modìfiche",
 'content-failed-to-parse' => "Faliment ëd l'anàlisi dël contnù ëd $2 për ël model $1: $3",
 'invalid-content-data' => 'Dat dël contnù pa bon',
 'content-not-allowed-here' => "Ël contnù «$1» a l'é nen autorisà an sla pàgina [[$2]]",
 'editwarning-warning' => "Chité sta pàgina-sì a peul feje perde tute le modìfiche ch'a l'ha fàit.
-S'a l'é rintrà ant ël sistema, a peul disabilité st'avis ant la session «Modìfiche» dij sò gust.",
+S'a l'é rintrà ant ël sistema, a peul disabilité st'avis ant la session «Modìfica» dij sò gust.",
 
 # Content models
 'content-model-wikitext' => 'test wiki',
@@ -886,21 +885,21 @@ S'a l'é rintrà ant ël sistema, a peul disabilité st'avis ant la session «Mo
 'content-model-css' => 'CSS',
 
 # Parser/template warnings
-'expensive-parserfunction-warning' => "'''Atension:''' Costa pàgina a l'ha tròpe ciamà costose a le fonsions ëd parser.
+'expensive-parserfunction-warning' => "'''Atension:''' Costa pàgina a l'ha tròpe ciamà costose a le fonsions d'anàlisi sintàtica.
 
-A dovrìa essnie men che {{PLURAL:$2|$2|$2}}, adess a-i na j'é {{PLURAL:$1|$1|$1}}.",
-'expensive-parserfunction-category' => 'Pàgine con tròpe ciamà costose a le fonsion parser',
-'post-expand-template-inclusion-warning' => "'''Atension:''' La dimension djë stamp anserì a l'é tròp gròssa.
+A dovrìa essnie men che {{PLURAL:$2|$2}}, adess a-i na j'é {{PLURAL:$1|$1}}.",
+'expensive-parserfunction-category' => "Pàgine con tròpe ciamà costose ëd fonsion ëd l'analisator sintàtich",
+'post-expand-template-inclusion-warning' => "'''Atension:''' La dimension dj'anseriment dë stamp a l'é tròp gròssa.
 Chèich stamp a saran nen anserì.",
-'post-expand-template-inclusion-category' => "Pàgine andoa la dimension djë stamp anserì a l'é tròpa",
+'post-expand-template-inclusion-category' => 'Pàgine con tròpe anclusion dë stamp',
 'post-expand-template-argument-warning' => "'''Atension:''' Costa pàgina a conten almanch un paràmeter dë stamp che a l'ha n'espansion tròp gròssa.
-Costi paràmeter a son stàit lassà fòra.",
-'post-expand-template-argument-category' => 'Pàgine contenente stamp con paràmeter mancant',
+Costi paràmeter a son stàit ignorà.",
+'post-expand-template-argument-category' => 'Pàgine contenente djë stamp con paràmeter mancant',
 'parser-template-loop-warning' => 'Trovà na liassa dlë stamp: [[$1]]',
 'parser-template-recursion-depth-warning' => 'Passà ël lìmit ëd ricorsion dlë stamp ($1)',
-'language-converter-depth-warning' => 'Passà lìmit ëd profondità dël convertidor ëd lenghe ($1)',
-'node-count-exceeded-category' => "Pàgine anté che ël nùmer ëd grop a l'é sorpassà",
-'node-count-exceeded-warning' => "La pàgina a l'ha sorpassà ël nùmer ëd grop",
+'language-converter-depth-warning' => 'Lìmit ëd profondità dël convertidor ëd lenga sorpassà ($1)',
+'node-count-exceeded-category' => "Pàgine anté che ël nùmer ëd neu a l'é sorpassà",
+'node-count-exceeded-warning' => "La pàgina a l'ha sorpassà ël nùmer ëd neu",
 'expansion-depth-exceeded-category' => "Pàgine anté che la profondeur d'espansion a l'é sorpassà",
 'expansion-depth-exceeded-warning' => "La pàgina a l'ha sorpassà la profondità d'espansion",
 'parser-unstrip-loop-warning' => 'Trovà un sicl nen dësmontàbil',
@@ -1004,15 +1003,15 @@ J'àutri aministrator dzora a {{SITENAME}} a saran ancó sempe bon a s-ciairé 
 * Anformassion përsonaj nen aproprià
 *: ''adrësse ëd ca e nùmer ëd teléfon, còdes fiscaj, e via fòrt''",
 'revdelete-legend' => 'But-je coste limitassion-sì a le version scancelà:',
-'revdelete-hide-text' => 'Stërma ël test dla revision',
+'revdelete-hide-text' => 'Test dla revision',
 'revdelete-hide-image' => "Stërma ël contnù dl'archivi",
 'revdelete-hide-name' => 'Stërma assion e oget',
-'revdelete-hide-comment' => 'Stërma ël coment a la modìfica',
-'revdelete-hide-user' => "Stërma lë stranòm ò l'adrëssa IP dël contributor",
+'revdelete-hide-comment' => 'Resumé dla modìfica',
+'revdelete-hide-user' => "Stranòm/adrëssa IP dl'utent",
 'revdelete-hide-restricted' => "Stërmé j'anformassion a j'aministrator tan-me a j'àutri",
 'revdelete-radio-same' => '(cambia pa)',
-'revdelete-radio-set' => 'É!',
-'revdelete-radio-unset' => '',
+'revdelete-radio-set' => 'Visìbil',
+'revdelete-radio-unset' => 'Stërmà',
 'revdelete-suppress' => "Smon-je pa ij dat gnanca a j'aministrator",
 'revdelete-unsuppress' => "Gava le limitassion da 'nt le version ciapà andaré",
 'revdelete-log' => 'Rason:',
@@ -2569,12 +2568,9 @@ coj che sio ij blocagi ativ al dì d'ancheuj.",
 'ipb_blocked_as_range' => "Eror: L'adrëssa IP $1 a l'ha gnun blocagi diret ansima e donca a peul pa esse dësblocà. A resta blocà mach për via ch'a l'é ciapà andrinta al ragg $2, e lolì as peul pa dësblochesse.",
 'ip_range_invalid' => 'Nùmer IP nen bon.',
 'ip_range_toolarge' => "Ij blocagi d'antërvaj pi gròss che /$1 a son pa përmëttù.",
-'blockme' => 'Blòch-me',
 'proxyblocker' => "Blocador dj'arpetitor",
-'proxyblocker-disabled' => "Sta funsion-sì a l'é pa abilità.",
 'proxyblockreason' => "Soa adrëssa IP a l'é stàita blocà përchè a l'é cola ëd n'arpetitor duvèrt.
 Për piasì che a contata sò fornitor ëd conession e che a lo anforma. As trata d'un problema ëd sigurëssa motobin serios.",
-'proxyblocksuccess' => 'Bele fàit.',
 'sorbsreason' => "Soa adrëssa IP a l'é listà coma arpetitor duvert (open proxy) ansima al DNSBL dovrà da {{SITENAME}}.",
 'sorbs_create_account_reason' => "Soa adrëssa IP a l'é listà coma arpetitor duvèrt (open proxy) ansima al DNSBL dovrà da {{SITENAME}}. A peul nen creésse un cont.",
 'xffblockreason' => "N'adrëssa IP ant l'antestassion X-Forwarded-For, la soa o cola d'un servent fantasma che chiel a deuvra, a l'é stàita blocà. La rason dël blocagi inissial a l'era: $1",
@@ -2928,6 +2924,8 @@ Sòn a l'é motobin belfé che a sia rivà përchè a-i era n'anliura a un sit e
 'spam_reverting' => "Butà andaré a l'ùltima version che a l'avèissa pa andrinta dj'anliure a $1",
 'spam_blanking' => "Pàgina dësvujdà, che tute le version a l'avìo andrinta dj'anliure a $1",
 'spam_deleting' => 'Scancelà, dagià che tute le revision a contnisìo dle liure a $1',
+'simpleantispam-label' => "Contròl contra la rumenta.
+Compilé '''NEN''' sòn!",
 
 # Info page
 'pageinfo-title' => 'Anformassion për «$1»',
@@ -3719,7 +3717,7 @@ A dovrìa avèj arseivù [{{SERVER}}{{SCRIPTPATH}}/COPYING na còpia dla Licensa
 # Special:Redirect
 'redirect' => 'Ridirigiù da archivi, utent o ID ëd revision',
 'redirect-legend' => "Ridirige a n'archivi o na pàgina",
-'redirect-summary' => "Costa pàgina special a ponta a n'archivi (dàit un nòm d'archivi), na pàgina (dàita n'ID a la revision) o na pàgina d'utent (dàit n'identificativ numérich a l'utent).",
+'redirect-summary' => "Costa pàgina special a ponta a n'archivi (dàit ël nòm dl'archivi), na pàgina (dàita n'ID a la revision) o na pàgina d'utent (dàit n'identificativ numérich a l'utent). Usagi: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/revision/328429]], or [[{{#Special:Redirect}}/user/101]].",
 'redirect-submit' => 'Andé',
 'redirect-lookup' => 'Arserca:',
 'redirect-value' => 'Valor:',
index fceb4c7..01de94d 100644 (file)
@@ -2330,12 +2330,9 @@ $1',
 اینوں $2 دی رینج چ روکیا گیا، جینوں کھولیا جاسکدا اے۔',
 'ip_range_invalid' => 'ناں منی جان والی آئی پی رینج۔',
 'ip_range_toolarge' => 'رینج روکاں /$1 توں وڈیاں دی اجازت نئیں۔',
-'blockme' => 'مینوں روکو',
 'proxyblocker' => 'دوروں روکن والا',
-'proxyblocker-disabled' => 'اس کم نوں روک دتا گیا اے۔',
 'proxyblockreason' => 'تواڈا آئی پی پتہ تے روک لگادتی گئی جے کیوں جے اے اک کھلا پراکسی اے۔
 مہربانی کرکے اپنے انٹرنٹ سروس دین والے نال  یا تکنیکی مدد دین والے نال تے اوناں ایس بچاؤ خطرے بارے دسو۔',
-'proxyblocksuccess' => 'ہوگیا۔',
 'sorbsreason' => 'تیرا آئی پی پتہ اک کھلی پراکسی وانگوں دتا گیا اے ڈی این ایس بی ایل چ {{سائیٹناں}} نے۔',
 'sorbs_create_account_reason' => 'تواڈا پتہ اک کھلا پراکسی لسٹ چ اے ڈی این ایس بی ایل نال {{سائیٹناں}} چ۔
 تسیں اک کھاتہ نئیں کھول سکدے۔',
@@ -2667,6 +2664,8 @@ $1',
 'spambot_username' => 'میڈیاوکی سپام سفائی',
 'spam_reverting' => 'آخری ریوین ول جیدے چ $1 دے جوڑ ناں ہون۔',
 'spam_blanking' => 'سارے ریوین جناں چ $1 نوں جوڑ نیں، طاف کیتا جاریا اے۔',
+'simpleantispam-label' => 'سپام روک پھاٹک
+ایدے تے ناں لکھو۔',
 
 # Info page
 'pageinfo-title' => '"$1" لئی جانکاری',
index 1d8834f..ad44e5a 100644 (file)
@@ -1102,8 +1102,6 @@ $messages = array(
 'blocklogentry' => 'εσπάλισεν [[$1]] για $2 $3',
 'unblocklogentry' => 'άνοιγμαν ασπαλιγματί τη $1',
 'block-log-flags-nocreate' => "ποίσιμον λογαρίας 'κ ίνεται",
-'blockme' => 'Ασπάλισον με',
-'proxyblocksuccess' => 'Εγέντον.',
 
 # Developer tools
 'lockdb' => 'Ασπάλιγμαν βάσης δογμενίων',
index 51ec75c..ebbde80 100644 (file)
@@ -1855,10 +1855,7 @@ Jaīs en [[Special:BlockList|IP blōkisenin listin]] ki widālai wissans tēnti
 Sta ast, šlāit, blōkitan kāigi delīks stesse $2 ebīmtan, kawīdan mazzi būtwei etblōkitan.',
 'ip_range_invalid' => 'Nitikrōmiskas IP ebīmtan.',
 'ip_range_toolarge' => 'Ebīmtas blōkisenei mūiseisan nikāi /$1 ni ast preiēminan.',
-'blockme' => 'Blōkis min',
 'proxyblocker' => 'Proxy blōkisna',
-'proxyblocker-disabled' => 'Šī funkciōni ast izklaūtan.',
-'proxyblocksuccess' => 'Segītan.',
 'sorbsreason' => 'Twajā IP adressi ast en listei stēisan open proxy sērwerin en DBSBL, tērpautan pra {{SITENAME}}.',
 'sorbs_create_account_reason' => 'Twajā IP adressi ast en listei stēisan open proxy sērwerin en DBSBL, tērpautan pra {{SITENAME}}.
 Tū ni mazzi teīktun rekkenan',
index 378334f..e55b989 100644 (file)
@@ -652,7 +652,7 @@ $1',
 'mailerror' => 'د برېښليک د لېږلو ستونزه: $1',
 'acct_creation_throttle_hit' => 'د همدې ويکي کارنانو په وروستيو ورځو کې ستاسې د IP پتې په کارولو سره {{PLURAL:$1|1 گڼون|$1 گڼونونه}} جوړ کړي، چې دا په همدې مودې کې د گڼونونو د جوړولو تر ټولو ډېر شمېر دی چې اجازه يې ورکړ شوې.
 نو په همدې خاطر د اوس لپاره د همدې IP پتې کارنان نه شي کولای چې نور گڼونونه جوړ کړي.',
-'emailauthenticated' => 'ستاسÛ\90 Ø¨Ø±Û\90Ú\9aÙ\84Ù\8aÚ© Ù¾ØªÙ\87 Ù¾Ù\87 $2 Ù\86Û\90Ù¼Ù\87 Ù¾Ù\87 $3 Ø¨Ø¬Ù\88 Ø¯ Ù\85Ù\86Ù\84Ù\88 Ù\88Ú\93 Ù\88Ú«رځېده.',
+'emailauthenticated' => 'ستاسÛ\90 Ø¨Ø±Û\90Ú\9aÙ\84Ù\8aÚ© Ù¾ØªÙ\87 Ù¾Ù\87 $2 Ù\86Û\90Ù¼Ù\87 Ù¾Ù\87 $3 Ø¨Ø¬Ù\88 Ø¯ Ù\85Ù\86Ù\84Ù\88 Ù\88Ú\93 Ù\88Ú¯رځېده.',
 'emailnotauthenticated' => 'لا تر اوسه ستاسې برېښليک پته د منلو وړ نه ده ګرځېدلې. د لاندې ځانګړتياو لپاره به تاسې ته هېڅ کوم برېښليک و نه لېږل شي.',
 'noemailprefs' => 'ددې لپاره چې دا کړنې کار وکړي نو تاسو يو برېښليک وټاکۍ.',
 'emailconfirmlink' => 'د خپل د برېښليک پتې پخلی وکړی',
@@ -1483,6 +1483,9 @@ $1',
 'randompage' => 'ناټاکلی مخ',
 'randompage-nopages' => 'په لانديني {{PLURAL:$2|نوم-تشيال|نوم-تشيالونو}} کې هېڅ کوم مخ نشته: $1.',
 
+# Random page in category
+'randomincategory-selectcategory' => 'يو ناټاکلی مخ له وېشنيزې موندل: $1 $2.',
+
 # Random redirect
 'randomredirect' => 'ناټاکلی ورگرځېدنه',
 
@@ -1684,7 +1687,7 @@ $1',
 'emailsubject' => 'سکالو:',
 'emailmessage' => 'پيغام:',
 'emailsend' => 'لېږل',
-'emailccme' => 'زÙ\85ا Ø¯ Ù¾Ù\8aغاÙ\85 Ù\8aÙ\88Ù\87 Ø¨Û\90Ù\84Ú«ه دې ماته هم برېښليک شي.',
+'emailccme' => 'زÙ\85ا Ø¯ Ù¾Ù\8aغاÙ\85 Ù\8aÙ\88Ù\87 Ø¨Û\90Ù\84Ú¯ه دې ماته هم برېښليک شي.',
 'emailccsubject' => '$1 ته ستاسو د پيغام لمېسه: $2',
 'emailsent' => 'برېښليک مو ولېږل شو',
 'emailsenttext' => 'ستاسو برېښليکي پيغام ولېږل شو.',
@@ -2002,8 +2005,6 @@ $UNWATCHURL  نه ليدنه وکړۍ
 'ipb-needreblock' => 'پر $1 د پخوا نه بنديز لگېدلی.
 آيا تاسې د امستنو بدلول غواړۍ؟',
 'ipb-otherblocks-header' => '{{PLURAL:$1|بل بنديز|نور بنديزونه}}',
-'blockme' => 'پر ما بنديز لگول',
-'proxyblocksuccess' => 'ترسره شو.',
 
 # Developer tools
 'lockdb' => 'توکبنسټ تړل',
@@ -2332,7 +2333,7 @@ $1',
 'exif-pixelxdimension' => 'د انځور جګوالی',
 'exif-usercomment' => 'د کارن تبصرې',
 'exif-relatedsoundfile' => 'اړونده غږيزه دوتنه',
-'exif-datetimedigitized' => 'د Ú«ڼياليز کېدنې وخت او نېټه',
+'exif-datetimedigitized' => 'د Ú¯ڼياليز کېدنې وخت او نېټه',
 'exif-fnumber' => 'F شمېره',
 'exif-lightsource' => 'د رڼا سرچينه',
 'exif-flash' => 'فلش',
@@ -2564,7 +2565,7 @@ $5
 # Size units
 'size-bytes' => '$1 بايټ',
 'size-kilobytes' => '$1 کيلوبايټ',
-'size-megabytes' => '$1 Ù\85Û\90Ú«ابايټ',
+'size-megabytes' => '$1 Ù\85Û\90Ú¯ابايټ',
 'size-gigabytes' => '$1 ګېګابايټ',
 'size-terabytes' => '$1 ټېرابايټ',
 'size-petabytes' => '$1 پېبي بايټ',
index f9fe2b2..5b26b1b 100644 (file)
@@ -655,6 +655,7 @@ Encontra uma lista das páginas especiais válidas em [[Special:SpecialPages|{{i
 Isto pode indicar um defeito no programa.',
 'databaseerror-textcl' => 'Ocorreu um erro na consulta à base de dados.',
 'databaseerror-query' => 'Consulta:$1',
+'databaseerror-function' => 'Função: $1',
 'databaseerror-error' => 'Erro: $1',
 'laggedslavemode' => "'''Aviso:''' A página pode não conter as atualizações mais recentes.",
 'readonly' => 'Base de dados bloqueada (limitada a leituras)',
@@ -1301,15 +1302,15 @@ Outros administradores da {{SITENAME}} continuarão a poder aceder ao conteúdo
 * Informação pessoal imprópria
 *: ''endereços de domicílio e números de telefone, números da segurança social, etc''",
 'revdelete-legend' => 'Definir restrições de visibilidade',
-'revdelete-hide-text' => 'Ocultar texto da edição',
+'revdelete-hide-text' => 'Revisão do texto',
 'revdelete-hide-image' => 'Ocultar conteúdo do ficheiro',
 'revdelete-hide-name' => 'Ocultar operação e destino',
-'revdelete-hide-comment' => 'Ocultar resumo da edição',
-'revdelete-hide-user' => 'Ocultar nome de utilizador/IP',
+'revdelete-hide-comment' => 'Resumo da edição',
+'revdelete-hide-user' => 'Nome de utilizador/endereço de IP',
 'revdelete-hide-restricted' => 'Ocultar dados dos administradores e de todos os outros',
 'revdelete-radio-same' => '(manter)',
-'revdelete-radio-set' => 'Sim',
-'revdelete-radio-unset' => 'o',
+'revdelete-radio-set' => 'Visível',
+'revdelete-radio-unset' => 'Escondido',
 'revdelete-suppress' => 'Ocultar dados dos administradores e de todos os outros',
 'revdelete-unsuppress' => 'Remover restrições das revisões restauradas',
 'revdelete-log' => 'Motivo:',
@@ -1391,6 +1392,7 @@ Note que, se usar os links de navegação, os botões de opção voltarão aos v
 'compareselectedversions' => 'Comparar as versões selecionadas',
 'showhideselectedversions' => 'Mostrar/ocultar versões selecionadas',
 'editundo' => 'desfazer',
+'diff-empty' => '(Sem diferenças)',
 'diff-multi' => '({{PLURAL:$1|Uma edição intermédia|$1 edições intermédias}} de {{PLURAL:$2|um utilizador|$2 utilizadores}} {{PLURAL:$1|não apresentada|não apresentadas}})',
 'diff-multi-manyusers' => '({{PLURAL:$1|Uma edição intermédia|$1 edições intermédias}} de mais de {{PLURAL:$2|um utilizador|$2 utilizadores}} não {{PLURAL:$1|apresentada|apresentadas}})',
 'difference-missing-revision' => '{{PLURAL:$2|Uma revisão|$2 revisões}} desta diferença ($1) não {{PLURAL:$2|foi encontrada|foram encontradas}}.
@@ -1572,6 +1574,7 @@ Se optar por revelá-lo, ele será utilizado para atribuir-lhe crédito pelo seu
 'prefs-dateformat' => 'Formato de data',
 'prefs-timeoffset' => 'Desvio horário',
 'prefs-advancedediting' => 'Opções gerais',
+'prefs-editor' => 'Editor',
 'prefs-preview' => 'Antevisão',
 'prefs-advancedrc' => 'Opções avançadas',
 'prefs-advancedrendering' => 'Opções avançadas',
@@ -1582,6 +1585,7 @@ Se optar por revelá-lo, ele será utilizado para atribuir-lhe crédito pelo seu
 'prefs-displaywatchlist' => 'Opções de apresentação',
 'prefs-tokenwatchlist' => 'Chave',
 'prefs-diffs' => 'Diferenças',
+'prefs-help-prefershttps' => 'Esta preferência terá efeito no seu próximo início de sessão.',
 
 # User preference: email validation using jQuery
 'email-address-validity-valid' => 'Parece válido',
@@ -1608,7 +1612,7 @@ Se optar por revelá-lo, ele será utilizado para atribuir-lhe crédito pelo seu
 'userrights-notallowed' => 'A sua conta não tem permissão para adicionar ou remover privilégios a utilizadores.',
 'userrights-changeable-col' => 'Grupos que pode alterar',
 'userrights-unchangeable-col' => 'Grupos que não pode alterar',
-'userrights-conflict' => 'Conflito com os privilégios dos utilizadores! Por favor, aplique as suas mudanças novamente.',
+'userrights-conflict' => 'Conflito entre alterações de privilégios de utilizador! Por favor, revise e confirme as suas mudanças.',
 'userrights-removed-self' => 'Você removeu com sucesso os seus privilégios. Como resultado disso, já não consegue aceder a esta página.',
 
 # Groups
@@ -1676,12 +1680,18 @@ Se optar por revelá-lo, ele será utilizado para atribuir-lhe crédito pelo seu
 'right-unblockself' => 'Desbloquearem-se a si próprios',
 'right-protect' => 'Mudar níveis de proteção e editar páginas protegidas em cascata',
 'right-editprotected' => 'Editar páginas protegidas como "{{int:protect-level-sysop}}"',
+'right-editsemiprotected' => 'Editar páginas protegidas como "{{int:protect-level-autoconfirmed}}"',
 'right-editinterface' => 'Editar a interface de utilizador',
 'right-editusercssjs' => 'Editar os ficheiros CSS e JS de outros utilizadores',
 'right-editusercss' => 'Editar os ficheiros CSS de outros utilizadores',
 'right-edituserjs' => 'Editar os ficheiros JS de outros utilizadores',
 'right-editmyusercss' => 'Editar os seus próprios ficheiros CSS de utilizador',
 'right-editmyuserjs' => 'Editar os seus próprios ficheiros JavaScript de utilizador',
+'right-viewmywatchlist' => 'Ver sua própria lista de páginas vigiadas',
+'right-editmywatchlist' => 'Editar sua própria lista de páginas vigiadas. Observe que algumas ações seguirão adicionando páginas, mesmo sem este direito.',
+'right-viewmyprivateinfo' => 'Ver os seus próprios dados privados (ex.: endereço de e-mail, nome real)',
+'right-editmyprivateinfo' => 'Editar os seus próprios dados privados (ex.: endereço de e-mail, nome real)',
+'right-editmyoptions' => 'Editar as suas próprias preferências',
 'right-rollback' => 'Reverter rapidamente as edições do último utilizador que editou uma página em particular',
 'right-markbotedits' => 'Marcar edições revertidas como edições de bot',
 'right-noratelimit' => 'Não ser afetado pelos limites de velocidade de operação',
@@ -1733,8 +1743,8 @@ Se optar por revelá-lo, ele será utilizado para atribuir-lhe crédito pelo seu
 'action-block' => 'impedir este utilizador de editar',
 'action-protect' => 'alterar os níveis de proteção desta página',
 'action-rollback' => 'reverter rapidamente as edições do último utilizador que editou uma dada página',
-'action-import' => 'importar esta página a partir de outra wiki',
-'action-importupload' => 'importar esta página a partir de um ficheiro xml',
+'action-import' => 'importar páginas a partir de outra wiki',
+'action-importupload' => 'importar páginas por meio do envio de um ficheiro',
 'action-patrol' => 'marcar as edições de outros utilizadores como patrulhadas',
 'action-autopatrol' => 'marcar como patrulhadas as suas próprias edições',
 'action-unwatchedpages' => 'ver a lista de páginas não-vigiadas',
@@ -1743,9 +1753,15 @@ Se optar por revelá-lo, ele será utilizado para atribuir-lhe crédito pelo seu
 'action-userrights-interwiki' => 'editar privilégios de utilizadores de outras wikis',
 'action-siteadmin' => 'bloquear ou desbloquear a base de dados',
 'action-sendemail' => 'enviar e-mails',
+'action-editmywatchlist' => 'Editar sua lista de páginas vigiadas',
+'action-viewmywatchlist' => 'Ver sua lista de páginas vigiadas',
+'action-viewmyprivateinfo' => 'Ver sua informação privada',
+'action-editmyprivateinfo' => 'Editar sua informação privada',
 
 # Recent changes
 'nchanges' => '$1 {{PLURAL:$1|alteração|alterações}}',
+'enhancedrc-since-last-visit' => '$1 {{PLURAL:$1|desde a última visita}}',
+'enhancedrc-history' => 'histórico',
 'recentchanges' => 'Mudanças recentes',
 'recentchanges-legend' => 'Opções das mudanças recentes',
 'recentchanges-summary' => 'Acompanhe nesta página as mudanças mais recentes da wiki.',
@@ -2061,6 +2077,10 @@ Verifique se o endereço está correto e o site disponível, por favor.',
 'listfiles_size' => 'Tamanho',
 'listfiles_description' => 'Descrição',
 'listfiles_count' => 'Versões',
+'listfiles-show-all' => 'Incluir versões antigas de imagens',
+'listfiles-latestversion' => 'Versão atual',
+'listfiles-latestversion-yes' => 'Sim',
+'listfiles-latestversion-no' => 'Não',
 
 # File description page
 'file-anchor-link' => 'Ficheiro',
@@ -2156,6 +2176,8 @@ Talvez queira editar a descrição na [$2 página original de descrição do fic
 'randompage-nopages' => 'Não há páginas {{PLURAL:$2|no seguinte espaço nominal|nos seguintes espaços nominais}}: $1.',
 
 # Random page in category
+'randomincategory-nopages' => 'Não há páginas na categoria [[:Category:$1|$1]].',
+'randomincategory-selectcategory' => 'Obter página aleatória da categoria: $1 $2',
 'randomincategory-selectcategory-submit' => 'Ir',
 
 # Random redirect
@@ -2188,8 +2210,8 @@ Talvez queira editar a descrição na [$2 página original de descrição do fic
 'pageswithprop-text' => 'Esta página lista páginas que usam uma propriedade em particular.',
 'pageswithprop-prop' => 'Nome da propriedade:',
 'pageswithprop-submit' => 'Avançar',
-'pageswithprop-prophidden-long' => 'foi ocultado o valor da propriedade por ser um texto muito longo ($1 kilobytes)',
-'pageswithprop-prophidden-binary' => 'foi ocultado o valor da propriedade por ser binário ($1 kilobytes)',
+'pageswithprop-prophidden-long' => 'foi ocultado o valor da propriedade por ser um texto muito longo ($1)',
+'pageswithprop-prophidden-binary' => 'foi ocultado o valor da propriedade por ser binário ($1)',
 
 'doubleredirects' => 'Redirecionamentos duplos',
 'doubleredirectstext' => 'Esta página lista todas as páginas que redirecionam para outras páginas de redirecionamento.
@@ -2247,7 +2269,7 @@ Agora redirecciona para [[$2]].',
 'mostinterwikis' => 'Páginas com mais interwikis',
 'mostrevisions' => 'Páginas com mais revisões',
 'prefixindex' => 'Todas as páginas iniciadas por',
-'prefixindex-namespace' => 'Todas as páginas com prefixo (domínio $1)',
+'prefixindex-namespace' => 'Todas as páginas com prefixo (espaço nominal $1)',
 'prefixindex-strip' => 'Remover prefixo',
 'shortpages' => 'Páginas curtas',
 'longpages' => 'Páginas longas',
@@ -2264,6 +2286,7 @@ Agora redirecciona para [[$2]].',
 'listusers' => 'Utilizadores',
 'listusers-editsonly' => 'Mostrar apenas utilizadores com edições',
 'listusers-creationsort' => 'Ordenar por data de criação',
+'listusers-desc' => 'Ordenar de forma decrescente',
 'usereditcount' => '$1 {{PLURAL:$1|edição|edições}}',
 'usercreated' => '{{GENDER:$3|Criado|Criada}} em $1 às $2',
 'newpages' => 'Páginas recentes',
@@ -2850,12 +2873,9 @@ Consulte a [[Special:BlockList|lista de bloqueios]] para obter a lista de bloque
 'ipb_blocked_as_range' => 'Erro: O IP $1 não se encontra bloqueado de forma direta e não pode ser desbloqueado deste modo. No entanto, está bloqueado como parte da gama $2, a qual pode ser desbloqueada.',
 'ip_range_invalid' => 'Gama de IPs inválida.',
 'ip_range_toolarge' => 'Não são permitidas gamas de IPs maiores do que /$1.',
-'blockme' => 'Bloquear-me',
 'proxyblocker' => 'Bloqueador de proxies',
-'proxyblocker-disabled' => 'Esta função foi impossibilitada.',
 'proxyblockreason' => "O seu endereço IP foi bloqueado por ser um ''proxy'' público.
 Contacte o seu fornecedor de internet ou o serviço de apoio técnico e informe-os deste grave problema de segurança, por favor.",
-'proxyblocksuccess' => 'Feito.',
 'sorbsreason' => "O seu endereço IP encontra-se listado como ''proxy'' aberto na DNSBL utilizada pela {{SITENAME}}.",
 'sorbs_create_account_reason' => "O seu endereço IP encontra-se listado como ''proxy'' aberto na DNSBL utilizada pela {{SITENAME}}. Não pode criar uma conta",
 'xffblockreason' => 'Um endereço IP presente no cabeçalho X-Forwarded-For, seja seu ou de um servidor de proxy que estiver a usar, foi bloqueado. A razão do bloqueio original foi: $1',
@@ -3214,6 +3234,8 @@ Este bloqueio foi provavelmente causado por um link para um site externo que con
 'spam_reverting' => 'A reverter para a última revisão que não contém links para $1',
 'spam_blanking' => 'Todas as revisões continham links para $1; a esvaziar',
 'spam_deleting' => 'Todas as revisões continham links para $1; a eliminar',
+'simpleantispam-label' => "Verificação contra spam
+'''NÃO''' preencha isto!",
 
 # Info page
 'pageinfo-title' => 'Informações sobre "$1"',
@@ -3991,7 +4013,7 @@ Em conjunto com este programa deve ter recebido [{{SERVER}}{{SCRIPTPATH}}/COPYIN
 # Special:Redirect
 'redirect' => 'Redirecionar pelo ID do ficheiro, utilizador ou revisão',
 'redirect-legend' => 'Redirecionar para um ficheiro ou página',
-'redirect-summary' => 'Esta página especial redireciona a um ficheiro (dado o nome do ficheiro), a uma página (dado um ID de revisão) ou a uma página de utilizador (dado o ID do utilizador).',
+'redirect-summary' => 'Esta página especial redireciona a um ficheiro (dado o nome do ficheiro), a uma página (dado um ID de revisão) ou a uma página de utilizador (dado o ID do utilizador). Utilização: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/revision/328429]] ou [[{{#Special:Redirect}}/user/101]].',
 'redirect-submit' => 'Ir',
 'redirect-lookup' => 'Pesquisa:',
 'redirect-value' => 'Valor:',
@@ -4053,7 +4075,10 @@ Em conjunto com este programa deve ter recebido [{{SERVER}}{{SCRIPTPATH}}/COPYIN
 'tags-tag' => 'Nome da etiqueta',
 'tags-display-header' => 'Aparência nas listas de modificações',
 'tags-description-header' => 'Descrição completa do significado',
+'tags-active-header' => 'Ativa?',
 'tags-hitcount-header' => 'Modificações etiquetadas',
+'tags-active-yes' => 'Sim',
+'tags-active-no' => 'Não',
 'tags-edit' => 'editar',
 'tags-hitcount' => '$1 {{PLURAL:$1|modificação|modificações}}',
 
@@ -4074,6 +4099,7 @@ Em conjunto com este programa deve ter recebido [{{SERVER}}{{SCRIPTPATH}}/COPYIN
 'dberr-problems' => 'Desculpe! Este site está com dificuldades técnicas.',
 'dberr-again' => 'Experimente esperar alguns minutos e atualizar.',
 'dberr-info' => '(Não foi possível contactar o servidor da base de dados: $1)',
+'dberr-info-hidden' => '(Não foi possível contactar o servidor de base de dados)',
 'dberr-usegoogle' => 'Pode tentar pesquisar no Google entretanto.',
 'dberr-outofdate' => 'Note que os seus índices relativos ao nosso conteúdo podem estar desatualizados.',
 'dberr-cachederror' => 'A seguinte página é uma cópia em cache da página pedida e pode não estar atualizada.',
@@ -4210,8 +4236,11 @@ Caso contrário, pode facilmente usar o formulário abaixo. O seu comentário se
 'rotate-comment' => 'Imagem rodada em $1 {{PLURAL:$1|grau|graus}} no sentido dos ponteiros do relógio',
 
 # Limit report
+'limitreport-title' => 'Dados de perfis do analisador:',
 'limitreport-cputime-value' => '$1 {{PLURAL:$1|segundo|segundos}}',
+'limitreport-walltime-value' => '$1 {{PLURAL:$1|segundo|segundos}}',
 'limitreport-postexpandincludesize-value' => '$1/$2 {{PLURAL:$2|byte|bytes}}',
+'limitreport-templateargumentsize' => 'Tamanho dos argumentos da predefinição',
 'limitreport-templateargumentsize-value' => '$1/$2 {{PLURAL:$2|byte|bytes}}',
 
 );
index c7d1660..16b0d6f 100644 (file)
@@ -61,6 +61,7 @@
  * @author Sir Lestaty de Lioncourt
  * @author Teles
  * @author TheGabrielZaum
+ * @author Titoncio
  * @author Urhixidur
  * @author Vivaelcelta
  * @author Vuln
@@ -2862,11 +2863,8 @@ Consulte a [[Special:BlockList|lista de bloqueios]] para obter a lista de bloque
 'ipb_blocked_as_range' => 'Erro: O IP $1 não se encontra bloqueado de forma direta, não podendo ser desbloqueado deste modo. Se encontra bloqueado como parte do "range" $2, o qual pode ser desbloqueado.',
 'ip_range_invalid' => 'Gama de IPs inválida.',
 'ip_range_toolarge' => 'Intervalos de bloqueio maiores do que /$1 não são permitidos',
-'blockme' => 'Bloquear-me',
 'proxyblocker' => 'Bloqueador de proxy',
-'proxyblocker-disabled' => 'Esta função está desabilitada.',
 'proxyblockreason' => 'O seu endereço de IP foi bloqueado por ser um proxy público. Por favor contacte o seu fornecedor do serviço de Internet ou o apoio técnico e informe-os deste problema de segurança grave.',
-'proxyblocksuccess' => 'Concluído.',
 'sorbsreason' => 'O seu endereço IP encontra-se listado como proxy aberto pela DNSBL utilizada por {{SITENAME}}.',
 'sorbs_create_account_reason' => 'O seu endereço de IP encontra-se listado como proxy aberto na DNSBL utilizada por {{SITENAME}}. Você não pode criar uma conta',
 'xffblockreason' => 'Um endereço IP presente no cabeçalho X-Forwarded-For, seu ou do servidor proxy que está usando, foi bloqueado. O motivo original do bloqueio foi: $1',
@@ -3215,6 +3213,8 @@ Tal bloqueio foi provavelmente causado por uma ligação para um ''website'' ext
 'spam_reverting' => 'Revertendo para a última versão que não contém links para $1',
 'spam_blanking' => 'Todas revisões contendo links para $1, limpando',
 'spam_deleting' => 'Eliminada por todas as suas edições conterem links para $1',
+'simpleantispam-label' => "Verificação contra spam
+'''NÃO''' preencha isto!",
 
 # Info page
 'pageinfo-title' => 'Informações sobre "$1"',
index 74d13ac..cb7d38a 100644 (file)
@@ -426,8 +426,6 @@ The pagination links in category viewer. Parameters:
 * $1 - the previous link, uses {{msg-mw|Prevn}}
 * $2 - the next link, uses {{msg-mw|Nextn}}',
 
-'linkprefix' => '{{optional}}',
-
 'about' => '{{Identical|About}}',
 'article' => "A 'content page' is a page that forms part of the purpose of the wiki. It includes the main page and pages in the main namespace and any other namespaces that are included when the wiki is customised. For example on Wikimedia Commons 'content pages' include pages in the file and category namespaces. On Wikinews 'content pages' include pages in the Portal namespace. For technical definition of 'content namespaces' see [[mw:Manual:Using_custom_namespaces#Content_namespaces|MediaWiki]].
 
@@ -653,7 +651,7 @@ See also:
 \'\'\'Note:\'\'\' This is "views" as in "appearances"/"representations", \'\'\'not\'\'\' as in "visits"/"accesses".
 {{Identical|View}}',
 'toolbox' => 'The title of the toolbox below the search menu.
-{{Identical|Toolbox}}',
+{{Identical|Tool}}',
 'userpage' => '',
 'projectpage' => 'Used as link text in Talk page of project page.',
 'imagepage' => 'Used as link text in Talk page of file page.',
@@ -2448,14 +2446,14 @@ There are three radio buttons in each row, and the captions above each column re
 * {{msg-mw|Revdelete-radio-same}}
 * {{msg-mw|Revdelete-radio-set}}
 * {{msg-mw|Revdelete-radio-unset}}
-{{Identical|Yes}}',
+{{Identical|Visible}}',
 '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.
 [[File:RevDelete Special-RevisionDelete (r60428).png|frame|center|Screenshot of the interface]]
 There are three radio buttons in each row, and the captions above each column read:
 * {{msg-mw|Revdelete-radio-same}}
 * {{msg-mw|Revdelete-radio-set}}
 * {{msg-mw|Revdelete-radio-unset}}
-{{Identical|No}}',
+{{Identical|Hidden}}',
 'revdelete-suppress' => 'Option for oversight; used in [[Special:RevisionDelete]].
 
 See also:
@@ -4508,7 +4506,8 @@ Example: [[:Image:Addon-icn.png]]',
 'filehist-dimensions' => 'Used as label in file description page.
 
 Followed by length, filesize, and width x height. e.g. "1.5 s (13 KB)".',
-'filehist-filesize' => 'In image description page',
+'filehist-filesize' => 'Used in image description page.
+{{Identical|File size}}',
 'filehist-comment' => 'In file description page
 
 {{Identical|Comment}}',
@@ -6617,33 +6616,18 @@ See also:
 * {{msg-mw|Range block disabled}}
 * {{msg-mw|Ip range invalid}}
 * {{msg-mw|Ip range toolarge}}',
-'blockme' => '{{doc-special|BlockMe|unlisted=1}}
-This feature is disabled by default.',
 'proxyblocker' => 'Used in [[Special:BlockMe]].
 
 See also:
 * {{msg-mw|proxyblocker-disabled}}
 * {{msg-mw|proxyblockreason}}
 * {{msg-mw|proxyblocksuccess}}',
-'proxyblocker-disabled' => 'Used in [[Special:BlockMe]].
-
-See also:
-* {{msg-mw|proxyblocker}}
-* {{msg-mw|proxyblockreason}}
-* {{msg-mw|proxyblocksuccess}}',
 'proxyblockreason' => 'Used as explanation of the reason in [[Special:BlockMe]].
 
 See also:
 * {{msg-mw|proxyblocker-disabled}}
 * {{msg-mw|proxyblocker}}
 * {{msg-mw|proxyblocksuccess}}',
-'proxyblocksuccess' => 'Used in [[Special:BlockMe]].
-
-See also:
-* {{msg-mw|proxyblocker-disabled}}
-* {{msg-mw|proxyblocker}}
-* {{msg-mw|proxyblockreason}}
-{{Identical|Done}}',
 'sorbs' => '{{optional}}',
 'sorbsreason' => 'See also:
 * {{msg-mw|Sorbsreason}}
@@ -8004,6 +7988,9 @@ Used when a page is deleted because all revisions contained a particular link.
 
 Parameters:
 * $1 - a spammed domain name',
+'simpleantispam-label' => 'Used as label for the input box in "Edit" page.
+
+The label and the input box are always hidden.',
 
 # Info page
 'pageinfo-title' => 'Page title for action=info. Parameters:
index 7da7374..4d7ca96 100644 (file)
@@ -272,12 +272,12 @@ $messages = array(
 'tog-hidepatrolled' => "Patrullasqa llamk'apusqakunata ñaqha hukchasqapi pakay",
 'tog-newpageshidepatrolled' => "Patrullasqa llamk'apusqakunata musuq p'anqakunapi pakay",
 'tog-extendwatchlist' => "Watiqana sutisuyuta tukuy rurachinalla hukchaykunaman mast'ay, ama lliwmanta aswan ñaqhallachu",
-'tog-usenewrc' => "Huñu hukchasqakuna p'anqallakama ñaqha hukchasqakunapi watiqasqakunapipas (JavaScript nisqallawanmi llamk'an)",
+'tog-usenewrc' => "Huñu hukchasqakuna p'anqallakama ñaqha hukchasqakunapi watiqasqakunapipas",
 'tog-numberheadings' => "Uma siq'ikunata kikinmanta yupay",
 'tog-showtoolbar' => "Llamk'apuna sillwita rikuchiy",
-'tog-editondblclick' => "P'anqakunata llamk'apuy iskaylla ñit'iywan (JavaScript)",
+'tog-editondblclick' => "P'anqakunata llamk'apuy iskaylla ñit'iywan",
 'tog-editsection' => "Rakirilla llamk'apuyta saqillay [qillqay] t'inkiwan",
-'tog-editsectiononrightclick' => "Rakirilla llamk'apuyta saqillay paña butunta rakirip sutinpi ñit'ispa (JavaScript)",
+'tog-editsectiononrightclick' => "Rakirilla llamk'apuyta saqillay paña butunta rakirip sutinpi ñit'ispa",
 'tog-showtoc' => "Yuyarina wachuchasqata rikuchiy (kimsamanta aswan uma siq'iyuq p'anqakunapaq)",
 'tog-rememberpassword' => "Ruraqpa sutiyta yaykuna rimaytapas yuyaykuy kay llika wamp'unapi ({{PLURAL:$1|huk p'unchawkama|$1 p'unchawkama}})",
 'tog-watchcreations' => "Qallarisqay p'anqakunata churkusqay willañiqikunatapas watiqay",
@@ -295,7 +295,7 @@ $messages = array(
 'tog-shownumberswatching' => "Rikuchiy hayk'a watiqaq ruraqkuna",
 'tog-oldsig' => "Kachkaqña silq'uy:",
 'tog-fancysig' => "Silq'uyta wiki qillqa hinata llamk'achiy (mana kikinmanta t'inkichaq silq'uy)",
-'tog-uselivepreview' => "''Live preview'' nisqa ñawpaq qhawayta llamk'achiy (JavaScript) (llamiy aknaraq)",
+'tog-uselivepreview' => "''Live preview'' nisqa ñawpaq qhawayta llamk'achiy (llamiy aknaraq)",
 'tog-forceeditsummary' => "Ch'usaq llamk'apuy waqaychasqa kachkaptinqa ch'itiyay.",
 'tog-watchlisthideown' => "Watiqasqaykunapiqa ñuqap llamk'apusqaykunata pakay",
 'tog-watchlisthidebots' => "Watiqasqaykunapiqa rurana antachakunap llamk'apusqankunata pakay",
@@ -309,6 +309,7 @@ $messages = array(
 'tog-noconvertlink' => "T'inki suti t'ikrayman ama niy",
 'tog-norollbackdiff' => 'Ruraqpa hukchasqankunata kutichispa ama wakin kayta willaychu',
 'tog-useeditwarning' => "Yuyampaway p'anqata saqiptiy manaraq rurarqusqay hukchasqakunata waqaychaspay.",
+'tog-prefershttps' => "Yaykurqaspaqa hayk'appas takyasqa t'inkiwan llamk'ay",
 
 'underline-always' => "Hayk'appas",
 'underline-never' => "Mana hayk'appas",
@@ -409,7 +410,7 @@ $messages = array(
 'newwindow' => '(Musuq wintanam kichakun)',
 'cancel' => 'Ama niy',
 'moredotdotdot' => 'Aswan...',
-'morenotlisted' => 'Aswanqa sutisuyupi manam kanchu...',
+'morenotlisted' => "Kay sutisuyuqa manaraqmi hunt'asqachu.",
 'mypage' => "P'anqay",
 'mytalk' => 'Rimachinay',
 'anontalk' => 'Kay IP huchhapaq rimanakuy',
@@ -512,7 +513,7 @@ $1",
 # All link text and link target definitions of links into project namespace that get used by other message strings, with the exception of user group pages (see grouppage).
 'aboutsite' => '{{SITENAME}}manta',
 'aboutpage' => 'Project:{{SITENAME}}manta',
-'copyright' => "Ch'aqtasqakunataqa llamk'achinkiman <i>$1</i> nisqap ruraq hayñinkama",
+'copyright' => "Samiqwanqa llamk'ankiman $1 nisqa ruraq hayñikama, mana wakin hina willaptinqa.",
 'copyrightpage' => '{{ns:project}}:Ruraqpa hayñin',
 'currentevents' => 'Kunan pacha',
 'currentevents-url' => 'Project:Kunan pacha',
@@ -595,6 +596,11 @@ Allin sapaq p'anqakunataqa tarinki [[Special:SpecialPages|Sapaq p'anqakuna]] nis
 # General errors
 'error' => 'Pantasqa',
 'databaseerror' => 'Willañiqintin pantasqa',
+'databaseerror-text' => "Willañiqintin maskana pantasqam tukurqan. Llamp'u kaqpi pantasqachá kachkan.",
+'databaseerror-textcl' => 'Willañiqintin maskana pantasqam tukurqan.',
+'databaseerror-query' => 'Maskana: $1',
+'databaseerror-function' => 'Ruray paqtachi: $1',
+'databaseerror-error' => 'Pantasqa: $1',
 'laggedslavemode' => "'''Paqtataq''': Kay p'anqapiqa manaraqchá kachkanchu aswan qayna musuqchasqakuna.",
 'readonly' => "Willañiqintinqa hark'asqam",
 'enterlockreason' => "Qillqamuy imarayku hark'asqa karqan, hayk'appas manañachá hark'asqachu kanqa",
@@ -628,6 +634,7 @@ P\'anqaqa pipapas qullusqanñachá.',
 'cannotdelete-title' => 'Manam atinichu "$1" sutiyuq p\'anqata qulluyta',
 'delete-hook-aborted' => "Ch'iwinam qulluyta t'ipirqan.
 Manam nirqanchu imarayku.",
+'no-null-revision' => 'Manam atinichu "$1" p\'anqapaq musuq ch\'usaq musuqchasqata kamariyta.',
 'badtitle' => "P'anqap sutinqa manam allinchu",
 'badtitletext' => "Kay p'anqap sutinqa manam allinchu, mana allin interwiki t'inkichá icha ch'usaqchá, p'anqa sutipaq mana saqillasqa sananchayuqchá.",
 'perfcached' => "Kay qatiq willakunaqa ''cache'' nisqa pakasqa hallch'apim kachkan, chayrayku manañachá musuqchasqachu. {{PLURAL:$1|Huklla|$1-lla}} taripasqam pakasqa hallch'api aypalla kachkan, manam aswanchu.",
@@ -655,6 +662,8 @@ $2",
 'customjsprotected' => "Manam saqillasunkichu kay JavaScript p'anqata llamk'apuyta, huk ruraqpa kikin tiyachisqankunayuq kaptinmi.",
 'mycustomcssprotected' => "Kay CSS p'anqataqa manam llamk'apuyta atinkichu.",
 'mycustomjsprotected' => "Kay JavaScript p'anqataqa manam llamk'apuyta atinkichu.",
+'myprivateinfoprotected' => "Manam saqillasqachu kanki kikiykip akuna willaykikunata llamk'apunaykipaq.",
+'mypreferencesprotected' => "Manam saqillasqachu kanki allinkachinaykikunata llamk'apunaykipaq.",
 'ns-specialprotected' => "{{ns:special}} suti k'itipi p'anqakunaqa manam llamk'apunallachu.",
 'titleprotected' => "Kay p'anqa sutitaqa [[User:$1|$1]] sutiyuq ruraq kamariymanta hark'arqanmi, kayraykum nispa: ''$2''.",
 'filereadonlyerror' => 'Manam atinichu "$1" sutiyuq willañiqita hukchayta, "$2" sutiyuq willañiqi churamuna ñawirillanapaq kachkaptinmi.
@@ -673,13 +682,14 @@ Amachaq kamachiqqa kayrayku amachani nispa nirqanmi: "$3".',
 # Login and logout pages
 'logouttext' => "'''Llamk'apuy tiyayniykiqa puchukasqañam.'''
 
-Sutinnaq kaspaykipas {{SITENAME}}pi wamp'uytam atinki. Mana hinataq munaspaykiqa, <span class='plainlinks'>[$1 musuqmanta yaykuy]</span> ñawpaq icha huk sutiwan. Huk p'anqakunaqa kaqllam rikch'akunqa, ''cache'' nisqa pakasqa hallch'ata mana ch'usaqchaptiykiqa.",
+Huk p'anqakunaqa kaqllam rikch'akunqa, ''cache'' nisqa pakasqa hallch'ata mana ch'usaqchaptiykiqa.",
 'welcomeuser' => 'Allinmi hamusqayki, $1!',
 'welcomecreation-msg' => 'Rakiqunaykiqa kamarisqañam.
 Ama qunqaychu [[Special:Preferences|{{SITENAME}} allinkachinaykikunata]] hukchayta.',
 'yourname' => 'Ruraq sutiyki:',
 'userlogin-yourname' => 'Ruraqpa sutin',
 'userlogin-yourname-ph' => 'Ruraqpa sutiykita yaykuchiy',
+'createacct-another-username-ph' => 'Ruraqpa sutinta yaykuchiy',
 'yourpassword' => 'Yaykuna rimayki',
 'userlogin-yourpassword' => 'Yaykuna rima',
 'userlogin-yourpassword-ph' => 'Yaykuna rimaykita yaykuchiy',
@@ -712,11 +722,13 @@ Ama qunqaychu [[Special:Preferences|{{SITENAME}} allinkachinaykikunata]] hukchay
 'userlogin-resetpassword-link' => 'Yaykuna rimaykita kutichiy',
 'helplogin-url' => 'Help:Yaykuy',
 'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|Yaykunapaq yanapa]]',
+'userlogin-createanother' => 'Huk rakiqunata kamariy',
 'createacct-join' => 'Kay qatiqpi willaykita yaykuchiy.',
 'createacct-emailrequired' => 'E-chaski imamayta',
 'createacct-emailoptional' => 'E-chaski imamayta (munaspayki)',
 'createacct-email-ph' => 'E-chaski imamaytaykita yaykuchiy',
-'createaccountmail' => "Kikinmanta tukusqa mit'alla yaykuna rimata llamk'achispa kay qatiqpi kaq e-chaski imamaytaman kachay",
+'createacct-another-email-ph' => 'E-chaski imamaytata yaykuchiy',
+'createaccountmail' => "Kikinmanta tukusqa mit'alla yaykuna rimata llamk'achispa akllasqa e-chaski imamaytaman kachay",
 'createacct-realname' => 'Chiqap suti (munaspayki)',
 'createaccountreason' => 'Kayrayku:',
 'createacct-reason' => 'Kayrayku',
@@ -724,6 +736,7 @@ Ama qunqaychu [[Special:Preferences|{{SITENAME}} allinkachinaykikunata]] hukchay
 'createacct-captcha' => 'Amachana llanchiy',
 'createacct-imgcaptcha-ph' => 'Hanaqpi rikusqayki qillqata yaykuchiy',
 'createacct-submit' => 'Rakiqunaykita kamariy',
+'createacct-another-submit' => 'Huk rakiqunata kamariy',
 'createacct-benefit-heading' => '{{SITENAME}}taqa qam hina runakunam ruran.',
 'createacct-benefit-body1' => "{{PLURAL:$1|llamk'apusqa|llamk'apusqakuna}}",
 'createacct-benefit-body2' => "{{PLURAL:$1|p'anqa|p'anqakuna}}",
@@ -783,10 +796,11 @@ Ama hina kaspa, chaskispaykiqa ruraqpa sutiykita nispa musuqmanta yaykuy.',
 
 Kay willay pantasqa kaptinqa, qhawarparillay.',
 'usernamehasherror' => 'Ruraqpa sutinqa ama iskaychakana (<nowiki>#</nowiki>) sananchayuqchu kachun',
-'login-throttled' => 'Nisyu kutitachá kay rakiqunapaq yaykuna rimawan ñaqha yaykuykachanki. Ama hina kaspa, suyariy manaraq musuqmanta yaykuykachaspa.',
+'login-throttled' => 'Nisyu kutitachá kay rakiqunapaq yaykuna rimawan ñaqha yaykuykachanki. Ama hina kaspa, $1 suyariy manaraq musuqmanta yaykuykachaspa.',
 'login-abort-generic' => 'Yaykuykachaspayki manam ayparqankichu - Allqasqa',
 'loginlanguagelabel' => 'Rimay: $1',
 'suspicious-userlogout' => "Lluqsiy mañakuyniykiqa mananchasqam karqan, waqllisqa wamp'unamanta icha pakaq proksimanta kachasqa kaspanchá.",
+'createacct-another-realname-tip' => "* Chiqap sutiqa munanallapaqmi. Quwaptiykiqa, llamk'apusqakunam paywan sananchasqa kanqa.",
 
 # Email sending
 'php-mail-error-unknown' => 'Mana riqsisqa pantasqa PHP mail() rurananpi',
@@ -802,7 +816,7 @@ Kay willay pantasqa kaptinqa, qhawarparillay.',
 'newpassword' => 'Musuq yaykuna rima:',
 'retypenew' => 'Musuq yaykuna rimaykita takyachiy:',
 'resetpass_submit' => 'Yaykuna rimata hukchaspa yaykuy',
-'changepassword-success' => 'Yaykuna rimaykiqa hukchasqañam. Yaykamuchkankim...',
+'changepassword-success' => 'Yaykuna rimaykiqa aypalla hukchasqañam.',
 'resetpass_forbidden' => 'Manam saqillanchu yaykuna rimata hukchayta',
 'resetpass-no-info' => "Yaykunaykim tiyan kay p'anqata chiqalla aypanaykipaq.",
 'resetpass-submit-loggedin' => 'Yaykuna rimata hukchay',
@@ -815,7 +829,7 @@ Yaykuna rimaykitaqa aypalla hukcharqunkiñachá icha huk mit'alla yaykuna rimata
 # Special:PasswordReset
 'passwordreset' => 'Yaykuna rimata kutichiy',
 'passwordreset-text-one' => "Kay hunt'ana p'anqata hunt'ay, yaykuna rimaykita kutichinaykipaq.",
-'passwordreset-text-many' => '{{PLURAL:$1|Kay willa rakikunamanta hukta yaykuchiy, yaykuna rimaykita kutichinaykipaq.}}',
+'passwordreset-text-many' => "{{PLURAL:$1|Kay k'itichakunamanta hukta hunt'achiy, yaykuna rimaykita kutichinaykipaq.}}",
 'passwordreset-legend' => 'Yaykuna rimata kutichiy',
 'passwordreset-disabled' => 'Kay wikipiqa yaykuna rimata manam kutichiyta atinkichu.',
 'passwordreset-emaildisabled' => "Kay wikipiqa e-chaski llamk'anakunaman ama nisqam.",
@@ -863,6 +877,15 @@ Mit'alla yaykuna rima: $2",
 'changeemail-submit' => 'E-chaskita wakinchay',
 'changeemail-cancel' => 'Ama niy',
 
+# Special:ResetTokens
+'resettokens' => 'Llawikunata kutichiy',
+'resettokens-no-tokens' => 'Manam kanchu kutichina llawikuna.',
+'resettokens-legend' => 'Llawikunata kutichiy',
+'resettokens-tokens' => 'Llawikuna:',
+'resettokens-token-label' => '$1 (kunan chani: $2)',
+'resettokens-done' => 'Llawikunaqa kutichimusqañam.',
+'resettokens-resetbutton' => 'Akllasqa llawikunata kutichimuy',
+
 # Edit page toolbar
 'bold_sample' => 'Yanasapa qillqa',
 'bold_tip' => 'Yanasapa qillqa',
@@ -935,7 +958,7 @@ Astasqachá icha qullusqachá qhawachkaptiyki.',
 'accmailtitle' => 'Yaykuna rimaqa kachasqañam.',
 'accmailtext' => "Kikinmanta kamarisqa [[User talk:$1|$1]]-paq yaykuna rimaqa $2-manmi kachasqaña.
 
-Yaykurqaspaqa ''[[Special:ChangePassword|yaykuna rima hukchana]]'' p'anqapi kay musuq rakiqunapaq yaykuna rimata hukchaytam atinki.",
+Yaykurqaspaqa ''[[Special:ChangePassword|yaykuna rima hukchana]]'' p'anqapi kay yaykuna rimata hukchaytam atinki.",
 'newarticle' => '(Musuq)',
 'newarticletext' => "Manaraq kachkaq p'anqatam llamk'apuchkanki. Musuq p'anqata kamariyta munaspaykiqa, qillqarillay. Astawan ñawiriyta munaspaykiqa, [[{{MediaWiki:Helppage}}|yanapana p'anqata]] qhaway. Mana munaspaykitaq, ñawpaq p'anqaman ripuy.",
 'anontalkpagetext' => "---- ''Kayqa huk sutinnaq icha mana sutinta llamk'achiq ruraqpa rimanakuyninmi. IP huchhantam hallch'asunchik payta sutinchanapaq. Achka ruraqkunam huklla IP huchhanta llamk'achiyta atin. Sutinnaq ruraq kaspaykiqa, mana qampa rurasqaykimanta willamusqakunata rikuspaykiqa, ama hina kaspa [[Special:UserLogin/signup|rakiqunaykita kamariy]] icha [[Special:UserLogin|yaykuy]] huk sutinnaq ruraqkunawan ama pantasqa kanaykipaq.''",
@@ -1027,7 +1050,7 @@ Hallch'api qhipaq kaq yaykuchisqataqa kay qatiqpim rikunki willasunaykipaq:",
 'nocreate-loggedin' => "Manam saqillasunkichu musuq p'anqakunata kamariyta.",
 'sectioneditnotsupported-title' => "Raki allichayqa manam q'imisqachu",
 'sectioneditnotsupported-text' => "Raki allichayqa kay p'anqapi manam q'imisqachu.",
-'permissionserrors' => 'Saqillay pantasqakuna',
+'permissionserrors' => 'Saqillay pantasqa',
 'permissionserrorstext' => 'Manam saqillasunkichu, {{PLURAL:$1|kayraykum|kayraykum}}:',
 'permissionserrorstext-withaction' => 'Manam saqillasunkichu $2-ta, {{PLURAL:$1|kayraykum|kayraykum}}:',
 'recreate-moveddeleted-warn' => "'''Paqtataq: Ñawpaqta qullusqaña p'anqatam musuqmanta kamarichkanki.'''
@@ -1086,6 +1109,7 @@ Chay niykunaqa manam chaninchasqachu.",
 'undo-failure' => "Manam atinichu llamk'apusqata kutichiyta, huk ruraqtaq musuqta llamk'apurquptinñam.",
 'undo-norev' => "Manam atinichu llamk'apusqata kutichiyta, mana kaptinmi icha qullusqa kaptinmi.",
 'undo-summary' => '[[Special:Contributions/$2|$2]]-pa $1 hukchasqanta kutichisqa ([[User talk:$2|rimay]])',
+'undo-summary-username-hidden' => 'Pakasqa ruraqpa $1 nisqa musuqchasqata kutichiy',
 
 # Account creation failure
 'cantcreateaccounttitle' => 'Manam atinichu rakiqunata kichayta',
@@ -1112,8 +1136,8 @@ $3-qa nirqan kayraykum: ''$2''",
 (ñawpaq) = ñawpaq kachkasqanwan huk kaykuna, a = aslla hukchasqa",
 'history-fieldset-title' => 'Wiñay kawsaypi maskay',
 'history-show-deleted' => 'Qullusqalla',
-'histfirst' => 'Ã\91awpaqkuna',
-'histlast' => 'Qhipaqkuna',
+'histfirst' => 'ñawpaqkuna',
+'histlast' => 'qhipaqkuna',
 'historysize' => '({{PLURAL:$1|1 byte|$1 byte}})',
 'historyempty' => "(ch'usaq)",
 
@@ -1263,6 +1287,7 @@ Takyachikuy kay hukchayqa allin wiñay kawsay ñiqita ama waqllichunchu chaylla.
 'compareselectedversions' => "Akllasqa llamk'apusqakunata wakichay",
 'showhideselectedversions' => 'Akllasqa musuqchasqakunata rikuchiy/pakay',
 'editundo' => 'kutichiy',
+'diff-empty' => '(Manam wak hina kanchu)',
 'diff-multi' => "({{PLURAL:$2|Huk ruraqpa|$2 ruraqpa}} {{PLURAL:$1|chawpipi huk llamk'apusqanqa manam rikuchisqachu|chawpipi $1 llamk'apusqankunaqa manam rikuchisqachu}})",
 'diff-multi-manyusers' => "({{PLURAL:$2|Hukmanta|$2-manta}} aswan ruraqkunap {{PLURAL:$1|chawpipi huk llamk'apusqanqa manam rikuchisqachu|chawpipi $1 llamk'apusqankunaqa manam rikuchisqachu}})",
 'difference-missing-revision' => "Kay wakin kaymanta ($1) {{PLURAL:$2|huk musuqchasqa|$2 musuqchasqakuna}} manam tarisqachu.
@@ -1362,7 +1387,7 @@ Imaymanata [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} qulluy ha
 'prefs-rendering' => "Rikch'akuynin",
 'saveprefs' => 'Allinkachinakunata waqaychay',
 'resetprefs' => 'Mana waqaychasqa hukchasqakunaman ama niy',
-'restoreprefs' => 'Tukuy kikinmanta allinkachinakunata kutichimuy',
+'restoreprefs' => 'Tukuy kikinmanta allinkachinakunata kutichimuy (tukuy rakirikunapi)',
 'prefs-editing' => "Llamk'apusqa",
 'rows' => 'Sinrukuna:',
 'columns' => 'Wachukuna:',
@@ -1418,11 +1443,11 @@ Chaytataq manam kutichiyta atinkichu.",
 'badsig' => "Chawa silq'usqaykiqa manam allinchu; HTML sananchakunata llanchiy.",
 'badsiglength' => 'Chutu sutiykiqa nisyu sunim.
 $1 {{PLURAL:$1|sanampamanta|sanampakunamanta}} aswan pisi kananmi.',
-'yourgender' => 'Qhari icha warmi:',
-'gender-unknown' => 'Mana riqsisqa',
-'gender-male' => 'Qhari',
-'gender-female' => 'Warmi',
-'prefs-help-gender' => "Munaspaykiqa: llamp'u kaqpa allinlla warmi icha qhari nispa napaykusunaykipaq. Kay willayqa sapsim kanqa.",
+'yourgender' => 'Ima hina nisunkikutaq munanki?',
+'gender-unknown' => 'Manam willayta munanichu',
+'gender-male' => "Qharim wikita llamk'apun",
+'gender-female' => "Warmim wikita llamk'apun",
+'prefs-help-gender' => "Munaspaykiqa: llamp'u kaqpa allinlla warmi icha qhari nispa napaykusunaykipaq huk runakunaman willananpaqpas. Kay willayqa sapsim kanqa.",
 'email' => 'E-chaski',
 'prefs-help-realname' => "* Chiqap sutiyki (munaspaqa): quwaptiykiqa, llamk'apusqaykikunam paywan sananchasqa kanqa.",
 'prefs-help-email' => 'E-chaskita munaspayki akllayta atinki. Arí nispaykiqa, yaykuna rimata qunqaspayki musuq yaykuna rimata e-chaski imamaytaykiman kachachikamuyta atinki.',
@@ -1433,7 +1458,9 @@ $1 {{PLURAL:$1|sanampamanta|sanampakunamanta}} aswan pisi kananmi.',
 'prefs-signature' => "Silq'uy",
 'prefs-dateformat' => "P'unchaw rikch'ay",
 'prefs-timeoffset' => 'Pacha wakinyay',
-'prefs-advancedediting' => 'Ñawparikusqa akllanakuna',
+'prefs-advancedediting' => 'Sapsi akllanakuna',
+'prefs-editor' => "P'anqachaq",
+'prefs-preview' => 'Ñawpaqta qhawallay',
 'prefs-advancedrc' => 'Ñawparikusqa akllanakuna',
 'prefs-advancedrendering' => 'Ñawparikusqa akllanakuna',
 'prefs-advancedsearchoptions' => 'Ñawparikusqa akllanakuna',
@@ -1441,6 +1468,7 @@ $1 {{PLURAL:$1|sanampamanta|sanampakunamanta}} aswan pisi kananmi.',
 'prefs-displayrc' => 'Akllanakunata rikuchiy',
 'prefs-displaysearchoptions' => 'Akllanakunata rikuchiy',
 'prefs-displaywatchlist' => 'Akllanakunata rikuchiy',
+'prefs-tokenwatchlist' => 'Llawi',
 'prefs-diffs' => 'Wakin kaykuna',
 
 # User preference: email validation using jQuery
@@ -1468,7 +1496,7 @@ $1 {{PLURAL:$1|sanampamanta|sanampakunamanta}} aswan pisi kananmi.',
 'userrights-notallowed' => 'Qampa rakiqunaykiwanqa manam ruraqkunap hayñinkunata yapayta icha qichuyta atinkichu.',
 'userrights-changeable-col' => 'Hukchanayki huñukuna',
 'userrights-unchangeable-col' => 'Mana hukchanayki huñukuna',
-'userrights-conflict' => 'Ruraqpa hayñin tupanakuymi. Ama hina kaspa, hukchasqaykikunata musuqmanta quy.',
+'userrights-conflict' => 'Ruraqpa hayñin hukchay tupanakuymi. Ama hina kaspa, hukchasqaykikunata musuqmanta quy.',
 
 # Groups
 'group' => 'Huñu:',
@@ -1512,7 +1540,7 @@ $1 {{PLURAL:$1|sanampamanta|sanampakunamanta}} aswan pisi kananmi.',
 'right-reupload-shared' => 'Rakinakusqa midya waqaychanallapi kaq willañiqikunata huknachay',
 'right-upload_by_url' => 'URL tiyaymanta willañiqita churkuy',
 'right-purge' => "''Cache'' nisqa pakasqa hallch'ata ch'usaqchay mana takyachina p'anqawan",
-'right-autoconfirmed' => "Kuskan amachasqa p'anqakunata llamk'apuy",
+'right-autoconfirmed' => 'IP-pi tiksisqa achura saywakunapa manam saywachasqan kanqachu',
 'right-bot' => 'Rurana antachap ruraykachasqanta hina hatalliy',
 'right-nominornewtalk' => 'Kikinpa rimachinanpi uchuylla hukchasqakunata "musuq willaykuna" nisqapi mana rikuy',
 'right-apihighlimits' => "API maskanakunapi aswan hanaq saywakunata llamk'achiy",
@@ -1533,14 +1561,20 @@ $1 {{PLURAL:$1|sanampamanta|sanampakunamanta}} aswan pisi kananmi.',
 'right-ipblock-exempt' => "IP hark'ayta, kikinmanta hark'ayta, tawqa hark'aytapas pulqaspa pasay",
 'right-proxyunbannable' => "Kikinmanta ''proxy'' nisqa sirwiq hark'ayta pulqaspa pasay",
 'right-unblockself' => "Kikinta hark'asqamanta qispikuy",
-'right-protect' => "Amachasqa kachkayta hukchay, amachasqa p'anqakunata llamk'apuy",
-'right-editprotected' => "Amachasqa p'anqakunata llamk'apuy (mana phaqcha amachasqa)",
+'right-protect' => "Amachasqa kachkayta hukchay, ch'aqtasqa amachasqa p'anqakunata llamk'apuy",
+'right-editprotected' => 'Amachasqa p\'anqakunata "{{int:protect-level-sysop}}" hina llamk\'apuy',
+'right-editsemiprotected' => '"{{int:protect-level-autoconfirmed}}" hina amachasqa p\'anqakunata llamk\'apuy',
 'right-editinterface' => "Ruraqpaq uyapurata llamk'apuy",
 'right-editusercssjs' => "Huk ruraqkunap CSS, JS willañiqinkunata llamk'apuy",
 'right-editusercss' => "Huk ruraqkunap CSS willañiqinkunata llamk'apuy",
 'right-edituserjs' => "Huk ruraqkunap JS willañiqinkunata llamk'apuy",
 'right-editmyusercss' => "Kikiykip ruraqpaq CSS willañiqiykikunata llamk'apuy",
 'right-editmyuserjs' => "Kikiykip ruraqpaq JavaScript willañiqiykikunata llamk'apuy",
+'right-viewmywatchlist' => 'Kikiykip watiqasqayki sutisuyuykita qhaway',
+'right-editmywatchlist' => "Kikiykip watiqasqayki sutisuyuykita llamk'apuy. Paqtataq, huk ruranakunaqa p'anqakunata yapanqaraqmi kay hañi mana kaptinpas.",
+'right-viewmyprivateinfo' => 'Kikiykip akuna willaykikunata qhaway (ahinataq e-chaski imamaytayki, chiqap sutiyki)',
+'right-editmyprivateinfo' => "Kikiykip akuna willaykikunata llamk'apuy (ahinataq e-chaski imamaytayki, chiqap sutiyki)",
+'right-editmyoptions' => "Kikiykip allinkachinaykikunata llamk'apuy",
 'right-rollback' => "Huk p'anqapi qhipaq llamk'apuqpa hukchasqankunata utqaylla kutichiy",
 'right-markbotedits' => "Kutichisqa llamk'apusqakunata rurana antachap llamk'apusqankunata hina sananchay",
 'right-noratelimit' => 'Achura saywakunap manam chayachisqanchu',
@@ -1592,8 +1626,8 @@ $1 {{PLURAL:$1|sanampamanta|sanampakunamanta}} aswan pisi kananmi.',
 'action-block' => "kay ruraqta llamk'apuymanta hark'ay",
 'action-protect' => "kay p'anqapaq amachana kamachisqakunata hukchay",
 'action-rollback' => "huk p'anqapi qhipaq llamk'apuqpa hukchasqankunata utqaylla kutichiy",
-'action-import' => "kay p'anqata hawa wikimanta chaskimuy",
-'action-importupload' => "kay p'anqata willañiqi churkusqamanta chaskimuy",
+'action-import' => "p'anqakunata hawa wikimanta chaskimuy",
+'action-importupload' => "p'anqakunata willañiqi churkusqamanta chaskimuy",
 'action-patrol' => "huk ruraqpa llamk'apusqanta patrullasqa nispa sananchay",
 'action-autopatrol' => "kikiykip llamk'apusqaykita patrullasqa nispa sananchakuy",
 'action-unwatchedpages' => "mana watiqasqa p'anqa sutisuyuta qhaway",
@@ -1602,12 +1636,19 @@ $1 {{PLURAL:$1|sanampamanta|sanampakunamanta}} aswan pisi kananmi.',
 'action-userrights-interwiki' => "hawa wikikunapi ruraqkunap hayñinkunata llamk'apuy",
 'action-siteadmin' => "willañiqintinta hark'ay icha paskay",
 'action-sendemail' => 'e-chaskikunata kachay',
+'action-editmywatchlist' => "watiqasqayki sutisuyuta llamk'apuy",
+'action-viewmywatchlist' => 'watiqasqayki sutisuyuta qhaway',
+'action-viewmyprivateinfo' => 'kikiykip akuna willaykikunata qhaway',
+'action-editmyprivateinfo' => "kikiykip akuna willaykikunata llamk'apuy",
 
 # Recent changes
 'nchanges' => '$1 {{PLURAL:$1|hukchasqa|hukchasqakuna}}',
+'enhancedrc-since-last-visit' => '$1 {{PLURAL:$1|qhipaq watukusqamantapacha}}',
+'enhancedrc-history' => 'wiñay kawsay',
 'recentchanges' => 'Ñaqha hukchasqa',
 'recentchanges-legend' => 'Ñaqha hukchasqapaq allinkachinakuna',
 'recentchanges-summary' => "Kay p'anqapiqa aswan qhipaq ñaqha hukchasqakunam.",
+'recentchanges-noresult' => "Kay taripanakama hukchasqakunaqa akllasqa mit'api manam kanchu.",
 'recentchanges-feed-description' => 'Kay mikhuchinapi wikipi qhipaq ñaqha hukchasqakunata qatiy.',
 'recentchanges-label-newpage' => "Kayta llamk'apuptiykim musuq p'anqam tukukurqun",
 'recentchanges-label-minor' => "Kayqa aslla llamk'apuymi",
@@ -1635,7 +1676,7 @@ $1 {{PLURAL:$1|sanampamanta|sanampakunamanta}} aswan pisi kananmi.',
 'rc_categories_any' => 'Imallapas',
 'rc-change-size-new' => '$1 {{PLURAL:$1|byte|byte}} hukchasqa kaptinña',
 'newsectionsummary' => 'Musuq raki: /* $1 */',
-'rc-enhanced-expand' => 'Imaymanachakunata rikuchiy (JavaScript kananmi)',
+'rc-enhanced-expand' => 'Imaymanachakunata rikuchiy',
 'rc-enhanced-hide' => 'Imaymanachakunata pakay',
 'rc-old-title' => 'ñawpaqta "$1" sutiwan kamarisqa',
 
@@ -1654,7 +1695,7 @@ $1 {{PLURAL:$1|sanampamanta|sanampakunamanta}} aswan pisi kananmi.',
 'reuploaddesc' => "Churkuna hunt'ana p'anqaman kutimuy.",
 'upload-tryagain' => 'Hukchasqa willañiqimanta willaykunata kachay',
 'uploadnologin' => 'Manaraqmi yaykurqunkichu',
-'uploadnologintext' => '[[Special:UserLogin|Yaykunaykim]] tiyan willañiqikunata churkunaykipaq.',
+'uploadnologintext' => '$1 tiyan willañiqikunata churkunaykipaq.',
 'upload_directory_missing' => 'Churkuna willañiqi churanaqa ($1) manam kanchu. Llika sirwiqpas manam atinchu churkuna willañiqi churanata kamariyta.',
 'upload_directory_read_only' => "Llika sirwiqqa manam atinchu churkuna hallch'aman ($1) qillqayta.",
 'uploaderror' => 'Willañiqita churkunayaptiyki pantasqam tukurqan',
@@ -1873,8 +1914,7 @@ Lliwmanta aswan alliku kanapaqqa, img_auth.php manam atinchu.',
 'upload_source_file' => ' (antañiqiqniykipi willañiqi)',
 
 # Special:ListFiles
-'listfiles-summary' => "Kay sapaq p'anqapiqa tukuy churkusqa willañiqikunatam rikunki.
-Ruraqkama ch'illchispaykiqa, chay ruraq qhipaq churkuq kaptillan willañiqikunatam sutisuyup patanpi rikunki.",
+'listfiles-summary' => "Kay sapaq p'anqapiqa tukuy churkusqa willañiqikunatam rikunki.",
 'listfiles_search_for' => 'Rikchap sutinta maskay:',
 'imgfile' => 'willañiqi',
 'listfiles' => 'Rikchakuna',
@@ -1885,6 +1925,10 @@ Ruraqkama ch'illchispaykiqa, chay ruraq qhipaq churkuq kaptillan willañiqikunat
 'listfiles_size' => 'Hatun kay',
 'listfiles_description' => "T'iktuna",
 'listfiles_count' => 'Musuqchasqakuna',
+'listfiles-show-all' => "Rikchakunamanta mawk'a musuqchasqakunata ch'aqtay",
+'listfiles-latestversion' => 'Kunan musuqchasqa',
+'listfiles-latestversion-yes' => 'Arí',
+'listfiles-latestversion-no' => 'Mana',
 
 # File description page
 'file-anchor-link' => 'Rikcha',
@@ -1979,6 +2023,13 @@ Ama hina kaspa, [$2 willañiqi ch'uyanchana p'anqata] qhaway astawan willachikun
 'randompage' => "Mayninpi p'anqa",
 'randompage-nopages' => "Manam ima p'anqapas kanchu kay suti {{PLURAL:$2|k'itipi|k'itikunapi}}: $1.",
 
+# Random page in category
+'randomincategory' => "Katiguriyapi kikinmanta p'anqa",
+'randomincategory-invalidcategory' => '"$1" nisqaqa katiguriyapaq manam allin sutinchu.',
+'randomincategory-nopages' => "[[:Category:$1|$1]] katiguriyapiqa manam p'anqakuna kanchu.",
+'randomincategory-selectcategory' => "Katiguriyamanta kikinmanta p'anqata chaskiy: $1 $2.",
+'randomincategory-selectcategory-submit' => 'Riy',
+
 # Random redirect
 'randomredirect' => "Mayninpi pusapuna p'anqa",
 'randomredirect-nopages' => 'Manam kanchu "$1" nisqa suti k\'itipi pusapuna p\'anqakuna.',
@@ -2184,7 +2235,8 @@ Q\'imichisqa tantari {{PLURAL:$2|qillqa|qillqakuna}}: <code>$1</code> (mana mayq
 'listgrouprights' => 'Ruraq huñup hayñinkuna',
 'listgrouprights-summary' => "Kay qatiq sutisuyupiqa kay wikipi sut'ichasqa ruraq huñukunatam, kikinpa chayamuna hayñinkunatawan rikunki.
 Chay kikinkunap hayñinkunamanta astawan ñawirinaykipaqqa [[{{MediaWiki:Listgrouprights-helppage}}|kaypi qhaway]].",
-'listgrouprights-key' => '* <span class="listgrouprights-granted">Qusqa hayñi</span>
+'listgrouprights-key' => 'T\'iktuna:
+* <span class="listgrouprights-granted">Qusqa hayñi</span>
 * <span class="listgrouprights-revoked">Qichusqa hayñi</span>',
 'listgrouprights-group' => 'Huñu',
 'listgrouprights-rights' => 'Hayñikuna',
@@ -2339,9 +2391,11 @@ $2 nisqa p\'anqata qhaway ñaqha qullusqakunata rikunaykipaq.',
 'deleteotherreason' => 'Huk rayku:',
 'deletereasonotherlist' => 'Huk rayku',
 'deletereason-dropdown' => "*Qulluypaq sapsi raykukuna
-** Kikin kamariqpa mañakusqan
+** Spam nisqa millay rurasqa
+** Wandaluchasqa
 ** Ruraqpa hayñinta k'irisqa
-** Wandaluchasqa",
+** Kikin kamariqpa mañakusqan
+** P'itisqa pusapuna",
 'delete-edit-reasonlist' => "Qullusqapaq raykukunata llamk'apuy",
 'delete-toobig' => "Kay p'anqaqa ancha wiñay kawsaysapa, $1-manta aswan {{PLURAL:$1|musuqchasqayuq|musuqchasqayuq}}. Kay hina p'anqakunata qulluyqa saywachasqam, {{SITENAME}}ta mana waqllinapaq.",
 'delete-warning-toobig' => "Kay p'anqaqa ancha wiñay kawsaysapa, $1-manta aswan {{PLURAL:$1|musuqchasqayuq|musuqchasqayuq}}. Kay hina p'anqata qulluspaykiqa, {{SITENAME}}ta waqllinkimanchá. Kay ruraymanta anchata yuyaychakuspa hamut'ay.",
@@ -2359,7 +2413,7 @@ $2 nisqa p\'anqata qhaway ñaqha qullusqakunata rikunaykipaq.',
 Qhipaq kaq llamk'apusqaqa [[User:$3|$3]]-pa ([[User talk:$3|rimanakuy]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]) rurasqanmi.",
 'editcomment' => "Llamk'apusqakunamanta pisichasqaqa kay hinam: \"''\$1''\".",
 'revertpage' => '[[Special:Contributions/$2|$2]] ([[User talk:$2|rimachina]]) sutiyuq ruraqpa hukchasqankunaqa kutichisqam [[User:$1|$1]]-pa ñawpaq hukchasqanman',
-'revertpage-nouser' => "Ruraqpa hukchasqankunaqa (sutinqa qichusqam) kutichisqañam [[User:$1|$1]]-pa ñawpaq llamk'apusqanta paqarichispa",
+'revertpage-nouser' => "Ruraqpa hukchasqankunaqa (sutinqa qichusqam) kutichisqañam {{GENDER:$1|[[User:$1|$1]]}}-pa ñawpaq llamk'apusqanta paqarichispa",
 'rollback-success' => "$1-pa hukchasqankunaqa kutichisqañam $2-pa ñawpaq llamk'apusqanta paqarichispa.",
 
 # Edit tokens
@@ -2493,7 +2547,7 @@ $1',
 'contributions' => "{{GENDER:$1|Ruraqpa}} llamk'apusqankuna",
 'contributions-title' => "$1 sutiyuq ruraqpa llamk'apusqankuna",
 'mycontris' => "Llamk'apusqaykuna",
-'contribsub2' => '$1 ($2)',
+'contribsub2' => '{{GENDER:$3|$1}}paq ($2)',
 'nocontribs' => 'Manam kay hina hukchasqakuna kanchu.',
 'uctop' => '(qhipaq hukchasqa)',
 'month' => 'Kay killamanta (ñawpaqmantapas):',
@@ -2644,11 +2698,8 @@ Willariy imaraykum hark'anki (ahinataq: sapaq wandaluchasqa p'anqakunamanta will
 'ipb_blocked_as_range' => "Pantasqa: IP $1 huchhaqa manam chiqallachu hark'asqa kaptinmi manam paskanallachu. Chaywanpas, $2 patayayku kaspataq hark'asqam kachkan. Chay patayaykuqa hark'asqamanta paskanallam.",
 'ip_range_invalid' => "IP huchha k'itiqa manam chanichkanchu.",
 'ip_range_toolarge' => "/$1-manta aswan hatun k'iti hark'aykunaqa manam saqillasqachu.",
-'blockme' => "Hark'away",
 'proxyblocker' => "Proxy hark'aq",
-'proxyblocker-disabled' => 'Kay ruranamanqa ama nisqam.',
 'proxyblockreason' => "IP huchhaykiqa hark'asqam kichasqa proxy kaptinmi. Ama hina kaspa, internet mink'aqniykiman icha allwiya yanapaqniykiman kay hatun qasi sasachakuymanta willay.",
-'proxyblocksuccess' => 'Rurasqañam.',
 'sorbsreason' => 'IP huchhaykiqa kichasqa proxy nispa {{SITENAME}}pi DNSBL nisqapi qillqasqam.',
 'sorbs_create_account_reason' => 'IP huchhaykiqa kichasqa proxy nispa {{SITENAME}}pi DNSBL nisqapi qillqasqam. Manam atinkichu rakiqunata kichayta',
 'cant-block-while-blocked' => "Kikiyki hark'asqa kaspaykiqa, manam huk ruraqkunata hark'ayta atinkichu.",
@@ -3000,13 +3051,13 @@ Tukuy hawa wikimanta chaskisqakunaqa [[Special:Log/import|hawamanta chaskiy hall
 'pageinfo-length' => "P'anqap chhikan (byte)",
 'pageinfo-article-id' => "P'anqap ID-nin",
 'pageinfo-language' => "P'anqap rimaynin",
-'pageinfo-robot-policy' => 'Maskana kuyuchinap kachkaynin',
-'pageinfo-robot-index' => 'Maskana yuyarinapaqpas',
-'pageinfo-robot-noindex' => 'Mana maskana yuyarinapaq',
+'pageinfo-robot-policy' => 'Maskana kuyuchinam yuyarinachan',
+'pageinfo-robot-index' => 'Saqillasqa',
+'pageinfo-robot-noindex' => 'Mana saqillasqa',
 'pageinfo-views' => "Hayk'a qhawaykuna",
 'pageinfo-watchers' => "P'anqata hayk'a watiqaqkuna",
 'pageinfo-few-watchers' => '$1-manta aswan pisi {{PLURAL:$1|qhawaq|qhawaqkuna}}',
-'pageinfo-redirects-name' => "Kay p'anqaman pusampuqkuna",
+'pageinfo-redirects-name' => "Kay p'anqaman hayk'a pusampuqkuna",
 'pageinfo-subpages-name' => "Kay p'anqap urin p'anqankuna",
 'pageinfo-subpages-value' => '$1 ($2 {{PLURAL:$2|pusapuna|pusapunakuna}}; $3 {{PLURAL:$3|mana pusapuna|mana pusapunakuna}})',
 'pageinfo-firstuser' => "P'anqap kamariqnin",
@@ -3339,7 +3390,7 @@ Kikin siq'ipi ima qatiq t'inkillapas sapaqllatam hamut'arisqa, ahinataq siq'ipi
 'exif-compression-4' => 'CCITT Huñu 4 tilifaks llawiy',
 
 'exif-copyrighted-true' => 'Iskaychay hayñi kan',
-'exif-copyrighted-false' => 'Sapsi kapuy',
+'exif-copyrighted-false' => "Ruraqpa iskaychay hayñin kachkayqa mana sut'ichasqachu",
 
 'exif-unknowndate' => "Mana riqsisqa p'unchaw",
 
@@ -3597,7 +3648,7 @@ Kay takyachina tuyruqa $4 pachapim puchukanqa.',
 'confirmemail_body_set' => 'Pipas, qamchiki, $1 IP huchhayuq tiyaymanta,
 hukcharqan {{SITENAME}}pi "$2" sutiyuq rakiqunapaq e-chaski imamaytatam kay imamaytaman.
 
-Kay rakiquna chiqapta qamman kapuptinqa, kay t\'inkita qatiy {{SITENAME}}pi e-chaski ruranaykita musuqmanta takyachinaykipaq:
+Kay rakiquna chiqapta qamman kapuptinqa, kay t\'inkita qatiy {{SITENAME}}pi e-chaski ruranaykita takyachinaykipaq:
 
 $3
 
@@ -3794,7 +3845,10 @@ MediaWikitaqa mast'ariyku runakunata yanapanapaqmi, ichataq MANAM FIYAKUYTA ATIY
 'tags-tag' => 'Unanchachap sutin',
 'tags-display-header' => "Hukchasqakunamanta sutisuyup rikch'akuynin",
 'tags-description-header' => "Sut'inmanta hunt'a ch'uyanchaynin",
+'tags-active-header' => 'Ruraqllachu?',
 'tags-hitcount-header' => 'Unanchasqa hukchasqakuna',
+'tags-active-yes' => 'Arí',
+'tags-active-no' => 'Mana',
 'tags-edit' => "llamk'apuy",
 'tags-hitcount' => '$1 {{PLURAL:$1|hukchasqa|hukchasqakuna}}',
 
@@ -3950,4 +4004,10 @@ Mana chayqa, kay qatiqpi kaq hunt'ana p'anqatam llamk'achiyta atinki. Willapuyni
 # Image rotation
 'rotate-comment' => "Rikch'aqa pacha rikuchiqwan $1 {{PLURAL:$1|k'atma}} muyusqam",
 
+# Limit report
+'limitreport-cputime-value' => '$1 {{PLURAL:$1|sikundu|sikundukuna}}',
+'limitreport-walltime-value' => '$1 {{PLURAL:$1|sikundu|sikundukuna}}',
+'limitreport-postexpandincludesize-value' => '$1/$2 {{PLURAL:$2|byte}}',
+'limitreport-templateargumentsize-value' => '$1/$2 {{PLURAL:$2|byte}}',
+
 );
index a83bdc3..f944b96 100644 (file)
@@ -2452,12 +2452,9 @@ Eventualmain è ella gia vegnida annulada.",
 Ella e bloccada en la zona d'adressas IP $2 che po vegnir debloccà.",
 'ip_range_invalid' => "Zona d'adressas IP nunvalida.",
 'ip_range_toolarge' => "Zonas da bloccadas pli grondas che /$1 n'èn betg lubidas.",
-'blockme' => 'Bloccar mai',
 'proxyblocker' => 'Bloccar proxys',
-'proxyblocker-disabled' => 'Questa funcziun è deactivada.',
 'proxyblockreason' => "Tia adressa IP è vegnida bloccada perquai ch'ella è in proxy avert. 
 Contactescha tes provider dals survetschs d'internet u ils administraturs dal sistem ed als infurmescha davart quest problem da segirezza pussaivel.",
-'proxyblocksuccess' => 'Terminà.',
 'sorbsreason' => 'Tia adressa IP fa part da la glista da proxys averts da DNSBL che vegn utilisada da {{SITENAME}}.',
 'sorbs_create_account_reason' => "Tia adressa IP fa part da la glista da proxys averts da DNSBL che vegn utilisada da {{SITENAME}}.
 Ti na pos betg crear in conto d'utilisader.",
index 81d6ae0..e2765c6 100644 (file)
@@ -438,8 +438,6 @@ $messages = array(
 'noindex-category' => 'Pagini neindexate',
 'broken-file-category' => 'Pagini cu legături invalide către fișiere',
 
-'linkprefix' => '/^(.*?)([a-zA-Z\\x80-\\xff]+)$/sD',
-
 'about' => 'Despre',
 'article' => 'Articol',
 'newwindow' => '(se deschide într-o fereastră nouă)',
@@ -518,7 +516,7 @@ $messages = array(
 'articlepage' => 'Vedeți articolul',
 'talk' => 'Discuție',
 'views' => 'Vizualizări',
-'toolbox' => 'Trusa de unelte',
+'toolbox' => 'Unelte',
 'userpage' => 'Vizualizați pagina utilizatorului',
 'projectpage' => 'Vizualizați pagina proiectului',
 'imagepage' => 'Vizualizați pagina fișierului',
@@ -822,7 +820,8 @@ să folosiți vechea parolă.',
 'noemailcreate' => 'Trebuie oferită o adresă e e-mail validă.',
 'passwordsent' => 'O nouă parolă a fost trimisă la adresa de e-mail a utilizatorului "$1". Te rugăm să te autentifici pe {{SITENAME}} după ce o primești.',
 'blocked-mailpassword' => 'Această adresă IP este blocată la editare, și deci nu este permisă utilizarea funcției de recuperare a parolei pentru a preveni abuzul.',
-'eauthentsent' => 'Un email de confirmare a fost trimis adresei nominalizate. Înainte de a fi trimis orice alt email acestui cont, trebuie să urmați intrucțiunile din email, pentru a confirma că acest cont este într-adevăr al dvs.',
+'eauthentsent' => 'Un e-mail de confirmare a fost trimis către adresa specificată.
+Înainte ca orice alt e-mail să mai fie trimis către acel cont, trebuie să urmați instrucțiunile prezente în e-mail pentru a confirma că acest cont este într-adevăr al dumneavoastră.',
 'throttled-mailpassword' => 'Un e-mail pentru resetarea parolei a fost deja trimis în {{PLURAL:$1|ultima oră|ultimele $1 ore|ultimele $1 de ore}}. Pentru a preveni abuzul, se va trimite doar un e-mail de resetare a parolei la un interval de o {{PLURAL:$1|o oră|$1 ore|$1 de ore}}.',
 'mailerror' => 'Eroare la trimitere e-mail: $1',
 'acct_creation_throttle_hit' => 'De la această adresă IP, vizitatorii sitului au creat {{PLURAL:$1|1 cont|$1 conturi|$1 de conturi}} de utilizator în ultimele zile, acest număr de noi conturi fiind maximul admis în această perioadă de timp.
@@ -1267,15 +1266,15 @@ funcție, fie versiunea specificată nu există, ori sunteți pe cale să ascund
 * Informații personale inadecvate
 *: ''adrese și numere de telefon personale, CNP, numere de securitate socială, etc.''",
 'revdelete-legend' => 'Restricții de afișare',
-'revdelete-hide-text' => 'Șterge textul versiunii',
+'revdelete-hide-text' => 'Textul versiunii',
 'revdelete-hide-image' => 'Șterge conținutul fișierului',
 'revdelete-hide-name' => 'Șterge operația și obiectul',
-'revdelete-hide-comment' => 'Șterge descrierea modificării',
-'revdelete-hide-user' => 'Șterge numele de utilizator sau adresa IP',
+'revdelete-hide-comment' => 'Descrierea modificării',
+'revdelete-hide-user' => 'Numele de utilizator sau adresa IP',
 'revdelete-hide-restricted' => 'Ascunde informațiile față de administratori și față de alți utilizatori',
 'revdelete-radio-same' => '(nu schimba)',
-'revdelete-radio-set' => 'Da',
-'revdelete-radio-unset' => 'Nu',
+'revdelete-radio-set' => 'Vizibil',
+'revdelete-radio-unset' => 'Ascuns',
 'revdelete-suppress' => 'Ascunde versiunile și față de administratori',
 'revdelete-unsuppress' => 'Anulează restricțiile la versiunile restaurate',
 'revdelete-log' => 'Motivul ștergerii:',
@@ -2824,12 +2823,9 @@ Jurnalul suprimărilor este indicat mai jos:',
 Face parte din area de blocare $2, care nu poate fi deblocată.',
 'ip_range_invalid' => 'Serie IP invalidă.',
 'ip_range_toolarge' => 'Blocările mai mari de /$1 nu sunt permise.',
-'blockme' => 'Blochează-mă',
 'proxyblocker' => 'Blocaj de proxy',
-'proxyblocker-disabled' => 'Această funcție este dezactivată.',
 'proxyblockreason' => 'Adresa dumneavoastră IP a fost blocată pentru că este un proxy deschis.
 Vă rugăm să vă contactați furnizorul de servicii Internet sau tehnicienii IT și să-i informați asupra acestei probleme serioase de securitate.',
-'proxyblocksuccess' => 'Realizat.',
 'sorbsreason' => 'Adresa dumneavoastră IP este listată ca un proxy deschis în DNSBL.',
 'sorbs_create_account_reason' => 'Adresa dumneavoastră IP este listată ca un proxy deschis în lista neagră DNS.
 Nu vă puteți crea un cont',
@@ -3184,6 +3180,8 @@ Permite adăugarea unui motiv în descrierea modificărilor',
 'spam_reverting' => 'Revenire la ultima versiune care nu conține legături către $1',
 'spam_blanking' => 'Toate versiunile conținând legături către $1 au fost golite',
 'spam_deleting' => 'Toate versiunile conținând legături către $1 au fost șterse',
+'simpleantispam-label' => "Verificare antispam.
+'''NU''' completați!",
 
 # Info page
 'pageinfo-title' => 'Informații pentru „$1”',
@@ -3950,7 +3948,7 @@ MediaWiki este distribuit în speranța că va fi folositor, dar FĂRĂ VREO GAR
 # Special:Redirect
 'redirect' => 'Redirecționare după fișier, utilizator sau ID-ul versiunii',
 'redirect-legend' => 'Redirecționare către un fișier sau o pagină',
-'redirect-summary' => 'Această pagină specială vă redirecționează către un fișier (dat fiind un nume de fișier), o pagină (dat fiind ID-ul unei versiuni) sau o pagină de utilizator (dat fiind un ID numeric al utilizatorului).',
+'redirect-summary' => 'Această pagină specială vă redirecționează către un fișier (dat fiind un nume de fișier), o pagină (dat fiind ID-ul unei versiuni) sau o pagină de utilizator (dat fiind un ID numeric al utilizatorului). Utilizare: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/revision/328429]] sau [[{{#Special:Redirect}}/user/101]].',
 'redirect-submit' => 'Du-te',
 'redirect-lookup' => 'Căutare:',
 'redirect-value' => 'Valoare:',
index 393d036..0203dd1 100644 (file)
@@ -165,8 +165,6 @@ $messages = array(
 'broken-file-category' => 'Pàggene cu collegaminde a le file scuasciate',
 'categoryviewer-pagedlinks' => '($1) ($2)',
 
-'linkprefix' => '/^(.*?)([a-zA-Z\\x80-\\xff]+)$/sD',
-
 'about' => 'Sus a',
 'article' => 'Pàgene de le condenute',
 'newwindow' => "(iapre jndr'à 'na fenestra nova)",
@@ -2615,12 +2613,9 @@ Pò essere ca ha state già sbloccate.",
 Jidde ha state bloccate cumme parte de l'indervalle $2, ca pò essere sbloccate.",
 'ip_range_invalid' => "L'indervalle de l'IP non g'è valide.",
 'ip_range_toolarge' => 'Le indervalle de le blocche cchiù larie de /$1 non ge sonde permesse.',
-'blockme' => 'Bloccheme',
 'proxyblocker' => 'Bloccaore de proxy',
-'proxyblocker-disabled' => "'A funzione ha state disabbilitete.",
 'proxyblockreason' => "L'indirizze IP tue ha state bloccate purcè jè 'nu proxy apirte.
 Pe piacere condatte 'u provider de Indernette tue o 'u supporte tecniche e 'mbormescele de stu serie probbleme de securezze.",
-'proxyblocksuccess' => 'Spicciete.',
 'sorbs' => 'DNSBL',
 'sorbsreason' => "L'indirizze IP tue jè elegate cumme a 'nu proxy apirte jndr'à DNSBL ausate da {{SITENAME}}.",
 'sorbs_create_account_reason' => "L'indirizze IP tue jè elegate cumme a 'nu proxy apirte jndr'à DNSBL ausate da {{SITENAME}}.
@@ -2994,6 +2989,8 @@ Stu fatte ha state causate da 'nu collegamende a 'nu site esterne ca appartene a
 'spam_reverting' => "Turnanne a l'urtema revisione no ge condiene collegaminde a $1",
 'spam_blanking' => 'Tutte le revisiune condènene collegaminde a $1, vacande',
 'spam_deleting' => 'Tutte le revisiune condènene collegaminde a $1, stoche a scangelle',
+'simpleantispam-label' => "Verifiche andi-spam.
+'''NO''' anghiè quiste!",
 
 # Info page
 'pageinfo-title' => '\'Mbormaziune pe "$1"',
index 0dc8417..919beac 100644 (file)
@@ -887,7 +887,7 @@ $2',
 'logout' => 'Завершение сеанса',
 'userlogout' => 'Завершение сеанса',
 'notloggedin' => 'Вы не представились системе',
-'userlogin-noaccount' => 'Нет учетной записи?',
+'userlogin-noaccount' => 'Нет учётной записи?',
 'userlogin-joinproject' => 'Присоединиться к проекту',
 'nologin' => 'Нет учётной записи? $1.',
 'nologinlink' => 'Создать учётную запись',
@@ -962,7 +962,7 @@ $2',
 Пожалуйста, представьтесь системе заново после получения пароля.',
 'blocked-mailpassword' => 'Редактирование с вашего IP-адреса запрещено, поэтому заблокирована и функция восстановления пароля.',
 'eauthentsent' => 'На указанный адрес электронной почты отправлено письмо. 
¡ледуйте изложенным там инструкциям для подтверждения того, что этот адрес действительно принадлежит вам.',
§Ñ\82об Ð¿Ð¾Ð»Ñ\83Ñ\87аÑ\82Ñ\8c Ð¿Ð¸Ñ\81Ñ\8cма Ð² Ð´Ð°Ð»Ñ\8cнейÑ\88ем, Ñ\81ледуйте изложенным там инструкциям для подтверждения того, что этот адрес действительно принадлежит вам.',
 'throttled-mailpassword' => 'Функция напоминания пароля уже использовалась в течение {{PLURAL:$1|последнего часа|последних $1 часов}}.
 Для предотвращения злоупотреблений, разрешено запрашивать не более одного напоминания за $1 {{PLURAL:$1|час|часа|часов}}.',
 'mailerror' => 'Ошибка при отправке почты: $1',
@@ -1415,15 +1415,15 @@ $3 {{GENDER:$3|указал|указала}} следующую причину:
 * Неуместная личная информация
 *: ''домашний адрес, номера телефонов, номер паспорта и т. д.''",
 'revdelete-legend' => 'Установить ограничения:',
-'revdelete-hide-text' => 'СкÑ\80Ñ\8bÑ\82Ñ\8c Ñ\82екÑ\81Ñ\82 Ñ\8dÑ\82ой Ð²ÐµÑ\80Ñ\81ии Ñ\81Ñ\82Ñ\80аниÑ\86Ñ\8b',
+'revdelete-hide-text' => 'ТекÑ\81Ñ\82 Ð¿Ñ\80авки',
 'revdelete-hide-image' => 'Скрыть содержимое файла',
 'revdelete-hide-name' => 'Скрыть действие и его объект',
-'revdelete-hide-comment' => 'СкÑ\80Ñ\8bÑ\82Ñ\8c Ð¾писание изменений',
-'revdelete-hide-user' => 'СкÑ\80Ñ\8bÑ\82Ñ\8c Ð¸Ð¼Ñ\8f Ð°Ð²Ñ\82оÑ\80а',
+'revdelete-hide-comment' => 'Ð\9eписание изменений',
+'revdelete-hide-user' => 'Ð\98мÑ\8f Ñ\83Ñ\87аÑ\81Ñ\82ника/IP-адÑ\80еÑ\81',
 'revdelete-hide-restricted' => 'Скрыть данные также и от администраторов',
 'revdelete-radio-same' => '(не изменять)',
-'revdelete-radio-set' => 'Ð\94а',
-'revdelete-radio-unset' => 'Ð\9dеÑ\82',
+'revdelete-radio-set' => 'Ð\92идимаÑ\8f',
+'revdelete-radio-unset' => 'СкÑ\80Ñ\8bÑ\82аÑ\8f',
 'revdelete-suppress' => 'Скрывать данные также и от администраторов',
 'revdelete-unsuppress' => 'Снять ограничения с восстановленных версий',
 'revdelete-log' => 'Причина:',
@@ -1866,7 +1866,7 @@ $1",
 
 # Recent changes
 'nchanges' => '$1 {{PLURAL:$1|изменение|изменения|изменений}}',
-'enhancedrc-since-last-visit' => '$1 с последнего посещения',
+'enhancedrc-since-last-visit' => '$1 {{PLURAL:$1|с последнего посещения}}',
 'enhancedrc-history' => 'история',
 'recentchanges' => 'Свежие правки',
 'recentchanges-legend' => 'Настройки свежих правок',
@@ -2967,11 +2967,8 @@ $1',
 'ipb_blocked_as_range' => 'Ошибка: IP-адрес $1 был заблокирован не напрямую и не может быть разблокирован. Однако, он принадлежит к заблокированному диапазону $2, который можно разблокировать.',
 'ip_range_invalid' => 'Недопустимый диапазон IP-адресов.',
 'ip_range_toolarge' => 'Блокировки диапазонов свыше /$1 запрещены.',
-'blockme' => 'Заблокируй меня',
 'proxyblocker' => 'Блокировка прокси',
-'proxyblocker-disabled' => 'Функция отключена.',
 'proxyblockreason' => 'Ваш IP-адрес заблокирован потому, что это открытый прокси-сервер. Пожалуйста, свяжитесь со своиим интернет-провайдером или службой поддержки, и сообщите им об этой серьёзной проблеме безопасности.',
-'proxyblocksuccess' => 'Выполнено.',
 'sorbsreason' => 'Ваш IP-адрес числится как открытый прокси в DNSBL.',
 'sorbs_create_account_reason' => 'Ваш IP-адрес числится как открытый прокси в DNSBL. Вы не можете создать учётную запись.',
 'xffblockreason' => 'Был заблокирован IP-адрес, присутствующий в заголовке X-Forwarded-For и принадлежащий либо вам, либо используемому вами прокси-серверу. Первоначальная причина блокировки была следующей: $1',
@@ -3339,6 +3336,8 @@ The wiki server can't provide data in a format your client can read.",
 'spam_reverting' => 'Откат к последней версии, не содержащей ссылки на $1',
 'spam_blanking' => 'Все версии содержат ссылки на $1, очистка',
 'spam_deleting' => 'Все версии содержали ссылки на $1, производится удаление',
+'simpleantispam-label' => "Анти-спам проверка.
+'''НЕ''' заполняйте это!",
 
 # Info page
 'pageinfo-title' => 'Сведения по «$1»',
@@ -4201,7 +4200,8 @@ MediaWiki распространяется в надежде, что она бу
 # Special:Redirect
 'redirect' => 'Перенаправление с файла, участника или идентификатора версии',
 'redirect-legend' => 'Перенаправление на файл или страницу',
-'redirect-summary' => 'Эта специальная страница перенаправляет на файл (с имени файла), страницу (с идентификатора версии) или страницу участника (с числового идентификатора участника).',
+'redirect-summary' => 'Эта специальная страница перенаправляет на файл (с имени файла), страницу (с идентификатора версии) или страницу участника (с числового идентификатора участника).
+Использование: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/revision/328429]] или [[{{#Special:Redirect}}/user/101]].',
 'redirect-submit' => 'Перейти',
 'redirect-lookup' => 'Поиск:',
 'redirect-value' => 'Значение:',
index a7375db..210df3b 100644 (file)
@@ -2586,12 +2586,9 @@ $1',
 'ipb_blocked_as_range' => 'Хыба: IP-адреса $1 не є блокована прямо а так єй не є можне одблоковати. Є частёв заблокованого россягу $2, котрый може быти одблокованый.',
 'ip_range_invalid' => 'Неплатный IP россяг.',
 'ip_range_toolarge' => 'Блокованя россягів векшых як  /$1 не є дозволене.',
-'blockme' => 'Заблокуй ня',
 'proxyblocker' => 'Блокованя проксі',
-'proxyblocker-disabled' => 'Тота фунція є выпнута.',
 'proxyblockreason' => 'Ваша IP-адреса была заблокована, зато же фунґує як отвореный проксі сервер. 
 Контактуйте свого Інтернет-провайдера або технічну підпору і інформуйте їх о тім серьёзнім беспечностнім проблемі.',
-'proxyblocksuccess' => 'Готово.',
 'sorbsreason' => 'Ваша IP-адреса є веджена як отвореный проксі в DNSBL.',
 'sorbs_create_account_reason' => 'Ваша IP-адреса є веджена як одкрытый проксі в DNSBL. З той адресы собі не можете створити конто.',
 'xffblockreason' => 'IP адреса написана в голові X-Forwarded-For, ці уж ваша, або проксі сервера, што хоснуєете, была заблокована. Оріґінална прічіна того блокованя: $1',
@@ -2925,6 +2922,8 @@ $2',
 'spam_reverting' => 'Реверт на послїдню верзію необсягуючу одказы на $1',
 'spam_blanking' => 'Вшыткы ревізії обсяговали одказы на $1, выпорожнєны',
 'spam_deleting' => 'Вшыткы ревізії обсяговали одказы на $1, змазане',
+'simpleantispam-label' => "Перевірка на спам.
+'''НЕ''' заповнюйте тото!",
 
 # Info page
 'pageinfo-title' => 'Інформація про "$1"',
index 36a4d5b..1272862 100644 (file)
@@ -2639,12 +2639,9 @@ $1 इत्यस्य अवरोधस्य कारणं तु "$2" 
 $2 इति प्रकारस्य अवरोधं कर्तुं शक्यते यत् अनवरोधमिच्छति ।',
 'ip_range_invalid' => 'अमान्यः ऐपिप्रकारः',
 'ip_range_toolarge' => '/$1 तः अधिकं वृहत्प्रकारकः अवरोधः नानुमतः ।',
-'blockme' => 'माम् अवरुणद्धु ।',
 'proxyblocker' => 'प्रतिहस्तकः अवरोधकः ।',
-'proxyblocker-disabled' => 'अयं कार्यकलापः निष्क्रियः ।',
 'proxyblockreason' => 'भवतः ऐपि सङ्केतः अवरुद्धः  यतः अयं कश्चन मुक्तप्रतिहस्तकः । 
 अन्तर्जालसेवादायकं सम्पर्कयतु गभीरायाः सुरक्षासमस्यायाः विषये सूचयतु च',
-'proxyblocksuccess' => 'समापित ।',
 'sorbsreason' => 'DNSBL उपयोगः {{SITENAME}} कृतस्य भवतः ऐपिसङ्केतः मुक्तप्रतिहस्तकः इति आवलीगतः',
 'sorbs_create_account_reason' => 'DNSBL उपयुक्तः {{SITENAME}} अतः भवतः ऐपिसङ्केतः अवरुद्धः यतः अयं मुक्तप्रतिहस्तकः इति आवलीगतः । अतः भवान् योजकस्थानं निर्मातुं न शक्नोति ।',
 'cant-block-while-blocked' => 'अन्ययोजकान् अवरोद्धुं भवान् नैव शक्नोति यतः भवान् अवरुद्धः ।',
@@ -2977,6 +2974,8 @@ $2 इति प्रकारस्य अवरोधं कर्तुं 
 'spam_reverting' => '$1 इत्यनेन नानुबद्धनां प्राचीनपुनरावृत्तीनां पुनस्थापनं कुर्वन्ति ।',
 'spam_blanking' => 'सर्वाः पुनरावृत्तयः $1 इत्यस्य अनुबन्धाः पूर्णपाठाः अपनीयन्ते ।',
 'spam_deleting' => 'सर्वाः पुनरावृत्तयः $1 इत्यस्य अनुबन्धाः । पूर्णपाठाः अपनीयन्ते ।',
+'simpleantispam-label' => "अनिष्टसन्देशविरोधपरीक्षणम् ।
+अस्मिन् '''नहि''' पूर्यताम् !",
 
 # Info page
 'pageinfo-title' => '"$1" कृते सूचनाः ।',
index a525e96..6c74bcd 100644 (file)
@@ -261,7 +261,7 @@ $messages = array(
 'articlepage' => 'Ыстатыйаны көр',
 'talk' => 'Ырытыы',
 'views' => 'Көрүүлэр',
-'toolbox' => 'Үнүстүрүмүөннэр',
+'toolbox' => 'Сэп-сэбиргэл',
 'userpage' => 'Кыттааччы туһунан сирэй',
 'projectpage' => 'Бырайыак сирэйэ',
 'imagepage' => 'Билэ сирэйин көрүү',
@@ -291,7 +291,7 @@ $1',
 # All link text and link target definitions of links into project namespace that get used by other message strings, with the exception of user group pages (see grouppage).
 'aboutsite' => '{{SITENAME}} туһунан',
 'aboutpage' => 'Project:туһунан',
-'copyright' => 'Ð\9cанÑ\8b Ñ\82Ñ\83һанаÑ\80Ñ\8b $1 ÐºÓ©Ò¥Ò¯Ð»Ð»Ò¯Ò¯Ñ\80.',
+'copyright' => 'Ð\9cанÑ\8b Ñ\82Ñ\83һанаÑ\80Ñ\8b $1 Ð»Ð¸Ñ\81Ñ\81иÑ\8dнÑ\81ийÑ\8d ÐºÓ©Ò¥Ò¯Ð»Ð»Ò¯Ò¯Ñ\80 (аÑ\82Ñ\8bн Ñ\8bйÑ\8bллÑ\8bбÑ\8bÑ\82аÑ\85 Ð±Ñ\83оллаÒ\95Ñ\8bна).',
 'copyrightpage' => '{{ns:project}}:бас билиитэ',
 'currentevents' => 'Туох буола турара',
 'currentevents-url' => 'Project:Сонуннар',
@@ -409,6 +409,7 @@ $1',
 'cannotdelete-title' => '«$1» сирэйи сотор сатаммат',
 'delete-hook-aborted' => 'Көннөрүү төттөрү көннөрүллүбүт.
 Эбии туох да быһаарыллыбатах.',
+'no-null-revision' => '«$1» сирэйгэ кураанах көннөрүүнү оҥорор табыллыбата',
 'badtitle' => 'Табыллыбат аат',
 'badtitletext' => 'Ыйытыллыбыт сирэй аата сыыһа, иччитэх, эбэтэр сыыһа ыйынньыктаах тыллар ыккардыларынааҕы дуу, биикилэр ыккардыларынааҕы дуу аат.',
 'perfcached' => 'Бу кээстэн ылыллыбыт онон бүтэһик уларыйыылары аахсымыан сөп. Кээскэ {{PLURAL:$1|соҕотох суруктан|$1 суруктан}} ордук хараллыбат.',
@@ -497,6 +498,9 @@ $2',
 'userlogin-resetpassword-link' => 'Киирии тылы уларытыы',
 'helplogin-url' => 'Help:Бэлиэ-ааты киллэрии',
 'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|Бэлиэтэниигэ көмө]]',
+'userlogin-loggedin' => 'Маннык аатынан киирбиккин {{GENDER:$1|$1}}.
+Атын аатынан киирэргэ аллара көстөр форманы туһан.',
+'userlogin-createanother' => 'Атын аатынан бэлиэтэн',
 'createacct-join' => 'Аллара суруй.',
 'createacct-another-join' => 'Саҥа бэлиэ-аат туһунан аллара суруй.',
 'createacct-emailrequired' => 'Email аадырыс',
@@ -557,7 +561,7 @@ $2',
 Системаҕа саҥа киирии тылы туһанан киир.',
 'blocked-mailpassword' => 'Эн IP аадырыскыттан манна тугу эмэ уларытар бобуллубут,
 онон киирии тылы өйдөтөр кыах эмиэ суох.',
-'eauthentsent' => 'Ð\91Ñ\8bÑ\81Ñ\82аÑ\85 ÐºÑ\8dмҥÑ\8d Ñ\82Ñ\83Ñ\82Ñ\82Ñ\83ллаÑ\80 ÐºÐ¸Ð¸Ñ\80ии Ñ\82Ñ\8bл Ñ\81аҥа ÐºÑ\8bÑ\82Ñ\82ааÑ\87Ñ\87Ñ\8b Ñ\8dл. Ð¿Ð¾Ñ\87Ñ\82аÑ\82Ñ\8bгаÑ\80 ыытылынна.
+'eauthentsent' => 'Эл. Ð¿Ð¾Ñ\87Ñ\82аÒ\95аÑ\80 Ñ\81Ñ\83Ñ\80Ñ\83к ыытылынна.
 Бу аадырыс эйиэнэ буоларын бигэргэтэргэ өссө тугу гыныахтааҕыҥ туһунан сурукка кэпсэниллэр.',
 'throttled-mailpassword' => 'Киирии тылы өйдөтөр тэрил бүтэһик {{PLURAL:$1|чаас|$1 чаас}} иһигэр туттулла сылдьыбыт.
 Көмүскэнэр соруктан сылтаан киирии тылы {{PLURAL:$1|чааска|$1 чааска}} биирдэ эрэ ыйытыахха сөп.',
@@ -2543,11 +2547,8 @@ $1',
 'ipb_blocked_as_range' => 'Сыыһа: $1 IP-та чопчу бобуллубатах (не блокирован), онон аһыллар кыаҕа суох. Ол гынан баран IP бу $2 диапазон сорҕотун быһыытынан бобуллубут, ону арыйыахха (бобуутун устуохха) сөп.',
 'ip_range_invalid' => 'IP-лар диапазоннара сатаммат.',
 'ip_range_toolarge' => 'Мантан  /$1 үөһэ диапазоннары хааччахтыыр сатаммат.',
-'blockme' => 'Миигин боп (блокируйдаа)',
 'proxyblocker' => 'Прокси бобуллуута',
-'proxyblocker-disabled' => 'Бу дьайыы араарыллыбыт.',
 'proxyblockreason' => 'Эн IP-ҥ аһаҕас прокси эбит, онон бобулунна. Интернет-провайдергын эбэтэр техническэй сулууспаны кытта сибээстэһэн кутталлаах суол баарын биллэр.',
-'proxyblocksuccess' => 'Сатанна.',
 'sorbsreason' => 'Эн IP-ҥ {{SITENAME}} саайт DNSBL-гар аһаҕас прокси быһыытынан сылдьар.',
 'sorbs_create_account_reason' => 'Эн IP-ҥ {{SITENAME}} саайт DNSBL-гар аһаҕас прокси быһыытынан сылдьар. Саҥаттан бэлиэтэнэр кыаҕыҥ суох.',
 'xffblockreason' => 'X-Forwarded-For баһыгар баар IP-аадырыс бобуллубут. Бу IP Эйиэнэ эбэтэр туһанар проксиҥ гиэнэ буолуон сөп. Бобуу төрүөтэ маннык эбит: $1',
@@ -2899,6 +2900,8 @@ $2',
 'spam_reverting' => 'Манна: $1 ыйынньыга суох бүтэһик торуму сөргүтүү (төннөрүү)',
 'spam_blanking' => 'Бары торумнар манна "$1" ыйынньыктаахтар, барытын суох оҥоруу',
 'spam_deleting' => 'Бары барыллар манна "$1" сигэнэллэр эит, сотуу бара турар',
+'simpleantispam-label' => "Анти-спам бэрэбиэркэтэ.
+Маны '''толорумаҥ'''!",
 
 # Info page
 'pageinfo-title' => '"$1" туһунан',
index 99e5c2b..0b18a5c 100644 (file)
@@ -1035,8 +1035,6 @@ Noa reaḱ pasnao katha [$2 rẽt pasnao sakam] latare emena',
 'block-log-flags-nocreate' => 'Ekaunṭ benao do bondogeya',
 'block-log-flags-noemail' => 'E-mail do esetgea',
 'block-log-flags-hiddenname' => 'Beoharićaḱ ńutum do ukugea',
-'blockme' => 'Esedińmẽ',
-'proxyblocksuccess' => 'Hoena',
 
 # Move page
 'movepagebtn' => 'Sakam ocogmẽ, Sakam kulmẽ',
index 9476231..d97bb14 100644 (file)
@@ -177,8 +177,6 @@ $messages = array(
 'index-category' => 'Pàginas indicizadas',
 'noindex-category' => 'Pàginas no indicitzadas',
 
-'linkprefix' => '/^(.*?)([a-zA-Z\\x80-\\xff]+)$/sD',
-
 'about' => 'A propòsitu de',
 'article' => 'Artìculu',
 'newwindow' => '(aberit in una bentana noa)',
@@ -1208,8 +1206,6 @@ Abbàida sa [[Special:BlockList|lista de IP bloccados]] pro bìder sas bloccadur
 'blocklogentry' => 'bloccau [[$1]] pro unu tempu de $2 $3',
 'unblocklogentry' => 'at sbloccau $1',
 'block-log-flags-nocreate' => 'creatzione account bloccada',
-'blockme' => 'Blocca·mi',
-'proxyblocksuccess' => 'Fatu.',
 'sorbs' => 'DNSBL',
 
 # Developer tools
index 51ba6e0..4a257c8 100644 (file)
@@ -2145,11 +2145,8 @@ Pi maggiuri nfurmazzioni, talìa la [[Special:BlockList|lista di l'IP bluccati]]
 'ipb_cant_unblock' => 'Erruri: Mpussìbbili attruvari lu bloccu cu ID $1. Putissi aviri già statu sbluccatu.',
 'ipb_blocked_as_range' => 'Sbagghiu: Lu ndirizzu IP $1 nun è suggettu a bloccu ndividuali e non pò èssiri sbloccatu. Lu bloccu è attivu mmeci a liveddu dû ntirvallu $2, ca pò èssiri sbluccatu.',
 'ip_range_invalid' => 'Ntervallu di ndirizzi IP nun vàlidu.',
-'blockme' => 'Blocca a mia',
 'proxyblocker' => 'Blocca proxy',
-'proxyblocker-disabled' => 'Sta funzioni nun è attiva.',
 'proxyblockreason' => "Lu tò ndirizzu IP hà statu bluccatu pirchì è un open proxy. Pi favuri cuntatta lu tò furnituri d'accessu a Internet o lu supportu tècnicu e nfòrmali di stu gravi prubbrema di sicurizza.",
-'proxyblocksuccess' => 'Esiquitu.',
 'sorbsreason' => 'Lu tò ndirizzu IP è alincatu comu proxy apertu ntâ lista DNSBL.',
 'sorbs_create_account_reason' => 'Lu tò ndirizzu IP è alincatu comu open proxy ntâ DNSBL. Nun poi criari un utenti.',
 'cant-block-while-blocked' => 'Nun putiti bluccari àutri utenti ntô mentri ca vui stissi siti bluccati.',
index a0e2972..736c64f 100644 (file)
@@ -1230,7 +1230,6 @@ tae an afore-blockit IP address or uisername.',
 'block-log-flags-nocreate' => 'accoont-makkin blockit',
 'range_block_disabled' => 'The administrator abeility tae mak range blocks is disabled.',
 'proxyblockreason' => 'Yer IP address haes been blockit sith it is an open proxy. Please contact yer Internet service provider or tech support an inform them o this serious security problem.',
-'proxyblocksuccess' => 'Duin',
 'sorbsreason' => 'Yer IP address is leetit as an open proxy in the DNSBL.',
 'sorbs_create_account_reason' => 'Yer IP address is leetit as an open proxy in the DNSBL. Ye canna mak an accoont',
 
index fb2921f..2c4cb3b 100644 (file)
@@ -1513,11 +1513,8 @@ $1",
 'ipb_cant_unblock' => 'Errori: Impussìbiri acciappà lu broccu cun ID $1. Lu broccu pudia assé già isthaddu buggaddu.',
 'ipb_blocked_as_range' => "Errori: L'indirizzu IP $1 nò è broccaddu individuaimmenti e nò pó assé ibbruccaddu. Lu broccu è inveci attibu a libellu di l'intervallu  $2, chi pó assé ibbruccaddu.",
 'ip_range_invalid' => "Intervallu d'indirizzi ip nò vàriddu.",
-'blockme' => 'Broccami',
 'proxyblocker' => 'Broccu di li proxy abbérthi',
-'proxyblocker-disabled' => 'Chistha funzioni nò è attiba.',
 'proxyblockreason' => "Chisth'indirizzu IP è isthaddu broccaddu parchí risultha assé un proxy abbérthu. Pa piazeri cuntattà lu propriu frunidori di sivvìzi pa la reti pa infuimmalli di chisthu grabi probrema di sigguriddai.",
-'proxyblocksuccess' => 'Broggu eseguiddu.',
 'sorbsreason' => "Chisth'indirizzu IP è erencaddu cumenti proxy abbérthu i' la listha-niedda DNSBL utirizadda da {{SITENAME}}.",
 'sorbs_create_account_reason' => "Nò è pussìbiri crià nobi registhrazioni da chisthu indirizzu IP parchí è erencaddu cumenti proxy abbérthu i' la listha-niedda DNSBL utirizadda da {{SITENAME}}.",
 
index a58585c..3233988 100644 (file)
@@ -1214,7 +1214,6 @@ Siiddus $2 lea listu maŋimus sihkomiin.',
 'blocklink' => 'hehtte',
 'contribslink' => 'rievdadusat',
 'blocklogentry' => 'esttii geavaheaddji dahje IP-čujuhusa [[$1]], eastima bistin lea $2 $3',
-'proxyblocksuccess' => 'Gárvvis.',
 
 # Developer tools
 'lockdb' => 'Gidde diehtovuođu',
index 322ec1e..95e53d8 100644 (file)
@@ -1532,7 +1532,6 @@ onkstiau ožbluokoutam IP adresō a nauduotuojō.',
 'ipb_expiry_invalid' => 'Galiuojėma čiesos nelaistėns.',
 'ipb_already_blocked' => '„$1“ jau ožblokouts',
 'ipb-needreblock' => '$1 jau īr ožblokouts. A nuorėt pakeistė nustatīmus?',
-'proxyblocksuccess' => 'Padarīt.',
 
 # Developer tools
 'unlockdbtext' => 'Atrakėnos doumenū baze grōžėns galimībe vėsėm
index 34aeed9..5bafdd5 100644 (file)
@@ -2708,12 +2708,9 @@ Možda je već deblokirana.',
 Međutim, možda je blokirana kao dio bloka $2, koji se može deblokirati.',
 'ip_range_invalid' => 'Netačan raspon IP adresa.',
 'ip_range_toolarge' => 'Grupne blokade veće od /$1 nisu dozvoljene.',
-'blockme' => 'Blokiraj me',
 'proxyblocker' => 'Bloker proksija',
-'proxyblocker-disabled' => 'Ova funkcija je onemogućena.',
 'proxyblockreason' => 'Vaša IP adresa je blokirana jer je ona otvoreni proksi.  
 Molimo vas da kontaktirate vašeg davatelja internetskih usluga (Internet Service Provider-a) ili tehničku podršku i obavijestite ih o ovom ozbiljnom sigurnosnom problemu.',
-'proxyblocksuccess' => 'Proksi uspješno blokiran.',
 'sorbsreason' => 'Vaša IP adresa je prikazana kao otvoreni proxy u DNSBL koji koristi {{SITENAME}}.',
 'sorbs_create_account_reason' => 'Vaša IP adresa je prikazana kao otvoreni proxy u DNSBL korišten od {{SITENAME}}.
 Ne možete napraviti račun',
index adcee78..a020dd0 100644 (file)
@@ -331,8 +331,6 @@ $messages = array(
 'noindex-category' => 'සූචිගත නොකළ පිටු',
 'broken-file-category' => 'භින්න වූ ගොනු සබැඳි සහිත පිටු',
 
-'linkprefix' => '/^(.*?)([a-zA-Z\\x80-\\xff]+)$/sD',
-
 'about' => 'පිළිබඳ',
 'article' => 'පටුන',
 'newwindow' => '(නව කවුළුවක විවෘතවේ)',
@@ -2601,12 +2599,9 @@ $1 ගේ වාරණයට හේතුව මෙය වේ: "$2"',
 එනමුදු, එය, $2 පරාසයෙහි කොටසක් ලෙස වාරණයට ලක් කොට ඇති අතර, එහි වාරණය අත්හිටුවිය හැක.',
 'ip_range_invalid' => 'අනීතික අන්තර්ජාල ලිපින පරාසයකි.',
 'ip_range_toolarge' => '/$1 ට වඩා විශාල පරාස කොටස්වලට ඉඩ ලබා නොදේ.',
-'blockme' => 'මා වාරණය කරන්න',
 'proxyblocker' => 'ප්‍රතියුක්ත (ප්‍රොක්සි) වාරණකරු',
-'proxyblocker-disabled' => 'මෙම කෘත්‍යය අක්‍රීය කොට ඇත.',
 'proxyblockreason' => 'ඔබගේ අන්තර්ජාල ලිපිනය විවෘත ප්‍රතියුක්තයක් (ප්‍රොක්සි) බැවින් එය වාරණය කොට ඇත.
 ඔබගේ අන්තර්ජාල සේවා ප්‍රතිපාදකයා හෝ තාක්ෂණික අනුග්‍රාහකයා හෝ අමතා මෙම බරපතළ ආරක්ෂණ ගැටළුව ඔවුනට නිරාවරණය කරන්න.',
-'proxyblocksuccess' => 'සිදුකලා.',
 'sorbs' => 'DNSBL',
 'sorbsreason' => 'ඔබගේ අන්තර්ජාල ලිපිනය, {{SITENAME}} විසින් භාවිත වන DNSBL හි විවෘත නියුතුවක් (ප්‍රොක්සියක්) ලෙස ලැයිස්තුගත කොට ඇත.',
 'sorbs_create_account_reason' => 'ඔබගේ අන්තර්ජාල ලිපිනය, {{SITENAME}} විසින් භාවිත වන DNSBL හි විවෘත නියුතුවක් (ප්‍රොක්සියක්) ලෙස ලැයිස්තුගත කොට ඇත.
@@ -2953,6 +2948,8 @@ $1 ගේ වාරණයට හේතුව මෙය වේ: "$2"',
 'spambot_username' => 'මීඩියාවිකි ස්පෑම් ඉවත්කිරීම',
 'spam_reverting' => ' $1 හට සබැඳියන් නොමැති අවසන් අනුවාදය වෙත ප්‍රතිවර්තනය වෙමින්',
 'spam_blanking' => 'සියළු සංශෝධනයන්හි  $1 වෙතවූ සබැඳියන් අඩංගු විය, හිස්කරමින්',
+'simpleantispam-label' => "ප්‍රති-ස්පෑම පරීක්‍ෂාව.
+කරුණාකර මෙය පුරවන්න '''එපා'''!",
 
 # Info page
 'pageinfo-title' => '"$1" සඳහා තොරතුරු',
index eb8b9e4..fb9d616 100644 (file)
@@ -2760,11 +2760,8 @@ blokované IP adresy nie sú zahrnuté. Pozri zoznam
 'ipb_blocked_as_range' => 'Chyba: IP adresa $1 nie je blokovaná priamo a nie je ju teda možné odblokovať. Je však blokovaná v rámci rozsahu $2, ktorý je možné odblokovať.',
 'ip_range_invalid' => 'Neplatný IP rozsah.',
 'ip_range_toolarge' => 'Bloky rozsahov väčšie ako /$1 nie sú povolené.',
-'blockme' => 'Zablokuj ma',
 'proxyblocker' => 'Blokovač proxy',
-'proxyblocker-disabled' => 'Táto funkcia je vypnutá.',
 'proxyblockreason' => 'Vaša IP adresa bola zablokovaná, pretože je otvorená proxy. Prosím kontaktujte vášho internetového poskytovateľa alebo technickú podporu a informujte ich o tomto vážnom bezpečnostnom probléme.',
-'proxyblocksuccess' => 'Hotovo.',
 'sorbsreason' => 'Vaša IP adresa je vedená ako nezabezpečený proxy server v DNSBL.',
 'sorbs_create_account_reason' => 'Vaša IP adresa je vedená ako nezabezpečený proxy server v databáze DNSBL, ktorú používa {{SITENAME}}. Nemôžete si vytvoriť účet.',
 'cant-block-while-blocked' => 'Nemôžete blokovať iných používateľov, kým ste zablokovaný.',
@@ -3139,6 +3136,8 @@ Pravdepodobne to spôsobil odkaz na externú internetovú lokalitu, ktorá sa na
 'spam_reverting' => 'Vraciam poslednú verziu, ktorá neobsahuje odkazy na $1',
 'spam_blanking' => 'Všetky revízie obsahovali odkaz na $1, odstraňujem obsah',
 'spam_deleting' => 'Všetky revízie obsahovali odkaz na $1, odstraňuje sa',
+'simpleantispam-label' => "Antispamová kontrola.
+'''NEVYPĹŇAJTE''' nasledovné!",
 
 # Info page
 'pageinfo-title' => 'Informácie o „$1“',
index bad752f..18b6e2f 100644 (file)
@@ -405,7 +405,7 @@ $messages = array(
 'articlepage' => 'Prikaže članek',
 'talk' => 'Pogovor',
 'views' => 'Pogled',
-'toolbox' => 'Pripomočki',
+'toolbox' => 'Orodja',
 'userpage' => 'Prikaži uporabnikovo stran',
 'projectpage' => 'Prikaži projektno stran',
 'imagepage' => 'Pokaži stran z datoteko',
@@ -1072,7 +1072,7 @@ Naslednji argumenti so bili izpuščeni.",
 'converter-manual-rule-error' => 'Odkril sem napako v ročnem pravilu pretvorbe jezikov',
 
 # "Undo" feature
-'undo-success' => 'Urejanje ste razveljavili. Prosim, potrdite in nato shranite spodnje spremembe.',
+'undo-success' => 'Urejanje ste razveljavili. Prosimo, preverite prikazano primerjavo redakcij in, če ustrezajo, shranite spremembe.',
 'undo-failure' => 'Zaradi navzkrižij urejanj, ki so se vmes pojavila, tega urejanja ni moč razveljaviti.',
 'undo-norev' => 'Urejanja ni mogoče razveljaviti, ker ne obstaja ali je bilo izbrisano.',
 'undo-summary' => 'Redakcija $1 uporabnika [[Special:Contributions/$2|$2]] ([[User talk:$2|pogovor]]) razveljavljena',
@@ -2746,12 +2746,9 @@ Ali želite spremeniti nastavitve blokade?',
 Je pa blokiran kot del območja $2, ki ga lahko odblokirate.',
 'ip_range_invalid' => 'Neveljaven IP-razpon.',
 'ip_range_toolarge' => 'Območja blokade večja od /$1 niso dovoljena.',
-'blockme' => 'Blokiraj me',
 'proxyblocker' => 'Blokator posredniških strežnikov',
-'proxyblocker-disabled' => 'Funkcija je onemogočena.',
 'proxyblockreason' => 'Ker uporabljate odprti posredniški strežnik, je urejanje z vašega IP-naslova preprečeno.
 Gre za resno varnostno težavo, o kateri obvestite svojega internetnega ponudnika ali tehnično podporo.',
-'proxyblocksuccess' => 'Storjeno.',
 'sorbsreason' => 'Vaš IP-naslov je v DNSBL uvrščen med odprte posredniške strežnike.',
 'sorbs_create_account_reason' => 'Vaš IP-naslov je v DNSBL, ki ga uporablja {{GRAMMAR:tožilnik|{{SITENAME}}}}, naveden kot odprti posredniški strežnik (proxy).
 Računa žal ne morete ustvariti.',
@@ -3098,6 +3095,8 @@ Omogoča vnos pojasnila v povzetku urejanja.',
 'spam_reverting' => 'Vračanje na zadnjo redakcijo brez povezav na $1',
 'spam_blanking' => 'Vse redakcije so vsebovale povezave na $1, izpraznjujem',
 'spam_deleting' => 'Vse redakcije so vsebovale povezave na $1, brišem',
+'simpleantispam-label' => "Preverjanje proti smetju.
+'''NE''' izpolnite tega!",
 
 # Info page
 'pageinfo-title' => 'Informacije o »$1«',
@@ -3769,7 +3768,7 @@ Prosimo, potrdite, da jo resnično želite znova ustvariti.",
 'confirm-unwatch-top' => 'Odstranim stran z vašega spiska nadzorov?',
 
 # Separators for various lists, etc.
-'percent' => '$1 %',
+'percent' => '$1&#160;%',
 
 # Multipage image navigation
 'imgmultipageprev' => '← prejšnja stran',
index bb2ca41..d9f0412 100644 (file)
@@ -1694,10 +1694,7 @@ Siehe de [[Special:BlockList|Liste dar gesperrta IP-Atressa und Nutzernoama]] fi
 'ipb_cant_unblock' => 'Fahler: Sperr-ID $1 ne gefunda. De Sperre wurde bereits uffgehuba.',
 'ipb_blocked_as_range' => 'Fahler: De IP-Atresse $1 wurde ols Teel dar Bereichssperre $2 indirekt gesperrt. Anne Entsperrung vu $1 alleene ies ne meeglich.',
 'ip_range_invalid' => 'Ungiltiger IP-Atressbereich.',
-'blockme' => 'Sperre miech',
-'proxyblocker-disabled' => 'Diese Funksjonn ies deaktiviert.',
 'proxyblockreason' => 'Denne IP-Atresse wurde gesperrt, do se a offener Proxy ies. Bitte kontaktiere denn Internet-Provider oder denne Systemadministratoren und informiere se ieber dieses meegliche Sicherheetsproblem.',
-'proxyblocksuccess' => 'Fattich',
 'sorbsreason' => 'De IP-Atresse ies ei dar DNSBL vu {{SITENAME}} ols offener PROXY gelistet.',
 'sorbs_create_account_reason' => 'De IP-Atresse ies ei dar DNSBL vu {{SITENAME}} ols offener PROXY gelistet. Doas Oalega neuer Nutzer ies ne meeglich.',
 'cant-block-while-blocked' => 'Du koast kenne andern Nutzer sperra, während du selbst gesperrt best',
index 0ad5309..cfba54f 100644 (file)
@@ -1300,8 +1300,6 @@ Eeg [[Special:BlockList|Mamnuucyada]] si aad u aragto liiska mamnuucyada ee hadd
 'block-log-flags-nocreate' => 'sameynta gudagalah lamaogola',
 'block-log-flags-noemail' => 'e-mailka laga mamnuucay',
 'ipb-needreblock' => '$1 mar hore aa la mamnuucay. marabtaa in aad wax ka bedesho habka?',
-'blockme' => 'I mamnuuc',
-'proxyblocksuccess' => 'waa la sameeyay.',
 
 # Move page
 'movenologin' => 'Gudaha kuma jirtid',
index 7c2e633..b3aff4e 100644 (file)
@@ -2601,11 +2601,8 @@ Mund të jetë zhbllokuar.',
 Ajo ë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.',
-'blockme' => 'Më blloko',
 'proxyblocker' => 'Bllokuesi i ndërmjetëseve',
-'proxyblocker-disabled' => 'Ky funksion është pamundësuar.',
 'proxyblockreason' => 'IP adresa juaj është bllokuar sepse është një ndërmjetëse e hapur. Ju lutem lidhuni me kompaninë e shërbimeve të Internetit që përdorni dhe i informoni për këtë problem sigurije.',
-'proxyblocksuccess' => 'Mbaruar.',
 'sorbsreason' => 'Adresa IP e juaj është radhitur si ndërmjetëse e hapur tek lista DNSBL.',
 'sorbs_create_account_reason' => 'Adresa IP e juaj është radhitur si ndërmjetëse e hapur tek lista DNSBL që përdoret nga {{SITENAME}}. Nuk ju lejohet të hapni një llogari.',
 'cant-block-while-blocked' => 'Ju nuk mund të bllokoni përdorues të tjerë ndërkohë që jeni i bllokuar.',
@@ -2936,6 +2933,8 @@ Ju lutemi provoni përsëri.',
 'spam_reverting' => "U kthye tek versioni i fundit që s'ka lidhje tek $1",
 'spam_blanking' => 'U boshatis sepse të gjitha versionet kanë lidhje tek $1',
 'spam_deleting' => 'Të gjitha inspektimet përmbanin lidhje në $1, duke fshirë',
+'simpleantispam-label' => "Kontroll anti-spam.
+'''MOS''' e plotësoni këtë!",
 
 # Info page
 'pageinfo-title' => 'Informacion për " $1 "',
index b36c9c4..49d3355 100644 (file)
@@ -536,8 +536,6 @@ $messages = array(
 'noindex-category' => 'Непописане странице',
 'broken-file-category' => 'Странице с неисправним везама до датотека',
 
-'linkprefix' => '/^(.*?)([a-zA-Z\\x80-\\xff]+)$/sD',
-
 'about' => 'О нама',
 'article' => 'Страница са садржајем',
 'newwindow' => '(отвара се у новом прозору)',
@@ -646,7 +644,7 @@ $1',
 # All link text and link target definitions of links into project namespace that get used by other message strings, with the exception of user group pages (see grouppage).
 'aboutsite' => 'О пројекту {{SITENAME}}',
 'aboutpage' => 'Project:О нама',
-'copyright' => 'Садржај је доступан под лиценцом $1.',
+'copyright' => 'Садржај је доступан под лиценцом $1 осим ако је другачије наведено.',
 'copyrightpage' => '{{ns:project}}:Ауторска права',
 'currentevents' => 'Актуелности',
 'currentevents-url' => 'Project:Новости',
@@ -802,8 +800,7 @@ $2',
 'myprivateinfoprotected' => 'Немате дозволу за мењање ваших личних информација.',
 'mypreferencesprotected' => 'Немате дозволу за мењање ваших подешавања.',
 'ns-specialprotected' => 'Посебне странице се не могу уређивати.',
-'titleprotected' => "Овај наслов је {{GENDER:$1|заштитио корисник|заштитила корисница|заштитио корисник}} [[User:$1|$1]].
-Наведени разлог: ''$2''.",
+'titleprotected' => "Овај назив је [[User:$1|$1]] заштитио од прављења. Разлог: ''$2''.",
 'filereadonlyerror' => 'Не могу да изменим датотеку „$1“ јер је ризница „$2“ у режиму за читање.
 
 Администратор који ју је закључао понудио је следеће објашњење: „$3“.',
@@ -860,14 +857,15 @@ $2',
 'userlogin-resetpassword-link' => 'Ресетујте лозинку',
 'helplogin-url' => 'Help:Logging in',
 'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|Помоћ при пријављивању]]',
+'userlogin-createanother' => 'Отвори још један налог',
 'createacct-join' => 'Унесите своје податке испод',
 'createacct-another-join' => 'Унесите податке за нови налог испод.',
 'createacct-emailrequired' => 'Адреса е-поште',
-'createacct-emailoptional' => 'Адреса е-поште (опцијоно)',
+'createacct-emailoptional' => 'Адреса е-поште (опционо)',
 'createacct-email-ph' => 'Унесите вашу адресу е-поште',
 'createacct-another-email-ph' => 'Унесите адресу е-поште',
 'createaccountmail' => 'Користите привремену, случајно створену лозинку и пошаљите на наведену адресу електронске поште',
-'createacct-realname' => 'Право име (опцијоно)',
+'createacct-realname' => 'Право име (опционо)',
 'createaccountreason' => 'Разлог:',
 'createacct-reason' => 'Разлог',
 'createacct-reason-ph' => 'Зашто правите још један налог?',
@@ -1437,7 +1435,7 @@ $1",
 'mergehistory-reason' => 'Разлог:',
 
 # Merge log
-'mergelog' => 'Ð\94невник спајања',
+'mergelog' => 'Ð\98Ñ\81Ñ\82оÑ\80иÑ\98а спајања',
 'pagemerge-logentry' => 'страница [[$1]] је спојена у [[$2]] (све до измене $3)',
 'revertmerge' => 'растави',
 'mergelogpagetext' => 'Испод се налази списак скорашњих спајања историја страница.',
@@ -2129,6 +2127,7 @@ $1',
 'listfiles_size' => 'Величина',
 'listfiles_description' => 'Опис',
 'listfiles_count' => 'Верзије',
+'listfiles-show-all' => 'Обухвати старе верзије слика',
 'listfiles-latestversion' => 'Тренутна верзија',
 'listfiles-latestversion-yes' => 'Да',
 'listfiles-latestversion-no' => 'Не',
@@ -2182,7 +2181,7 @@ $1',
 'filerevert-legend' => 'Врати датотеку',
 'filerevert-intro' => "Враћате датотеку '''[[Media:$1|$1]]''' на [$4 издање од $2; $3].",
 'filerevert-comment' => 'Разлог:',
-'filerevert-defaultcomment' => 'Ð\92Ñ\80аÑ\9bено Ð½Ð° Ð¸Ð·Ð´Ð°Ñ\9aе Ð¾Ð´ $1; $2',
+'filerevert-defaultcomment' => 'Ð\92Ñ\80аÑ\9bено Ð½Ð° Ð²ÐµÑ\80зиÑ\98Ñ\83 Ð¾Ð´ $2, $1',
 'filerevert-submit' => 'Врати',
 'filerevert-success' => "Датотека '''[[Media:$1|$1]]''' је враћена на [$4 издање од $2; $3].",
 'filerevert-badversion' => 'Не постоји раније локално издање датотеке с наведеним временским подацима.',
@@ -2587,7 +2586,7 @@ $UNWATCHURL
 'deletepage' => 'Обриши страницу',
 'confirm' => 'Потврди',
 'excontent' => 'садржај је био: „$1“',
-'excontentauthor' => 'садржај је био: „$1“ (једину измену {{GENDER:|направио је|направила је|направио је}} [[Special:Contributions/$2|$2]])',
+'excontentauthor' => 'садржај је био: „$1“ (а једини уређивач је био „[[Special:Contributions/$2|$2]]“)',
 'exbeforeblank' => 'садржај пре брисања је био: „$1“',
 'exblank' => 'страница је била празна',
 'delete-confirm' => 'Брисање странице „$1“',
@@ -2599,7 +2598,7 @@ $UNWATCHURL
 'actionfailed' => 'Радња није успела',
 'deletedtext' => "Страница „$1“ је обрисана.
 Погледајте ''$2'' за више детаља.",
-'dellogpage' => 'Ð\94невник брисања',
+'dellogpage' => 'Ð\98Ñ\81Ñ\82оÑ\80иÑ\98а брисања',
 'dellogpagetext' => 'Испод је списак последњих брисања.',
 'deletionlog' => 'дневник брисања',
 'reverted' => 'Враћено на ранију измену',
@@ -2607,6 +2606,7 @@ $UNWATCHURL
 'deleteotherreason' => 'Други/додатни разлог:',
 'deletereasonotherlist' => 'Други разлог',
 'deletereason-dropdown' => '*Најчешћи разлози за брисање
+** Спам
 ** Захтев аутора
 ** Кршење ауторских права
 ** Вандализам',
@@ -2630,7 +2630,7 @@ $UNWATCHURL
 Последњу измену је {{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]]',
-'revertpage-nouser' => 'Враћене су измене скривеног корисника на последњу измену члана [[User:$1|$1]]',
+'revertpage-nouser' => 'Враћене су измене скривеног корисника на последњу измену члана {{GENDER:$1|[[User:$1|$1]]}}',
 'rollback-success' => 'Враћене су измене {{GENDER:$1|корисника|кориснице|корисника}} $1
 на последњу измену {{GENDER:$2|корисника|кориснице|корисника}} $2.',
 
@@ -2775,7 +2775,7 @@ $1',
 'contributions' => '{{GENDER:$1|Кориснички}} доприноси',
 'contributions-title' => 'Доприноси {{GENDER:$1|корисника|кориснице|корисника}} $1',
 'mycontris' => 'Доприноси',
-'contribsub2' => 'За $1 ($2)',
+'contribsub2' => 'За {{GENDER:$3|$1}} ($2)',
 'nocontribs' => 'Измене које одговарају овим условима нису пронађене.',
 'uctop' => '(последња)',
 'month' => 'од месеца (и раније):',
@@ -2933,12 +2933,9 @@ $1',
 Она је блокирана као део блокаде $2, која може бити деблокирана.',
 'ip_range_invalid' => 'Неисправан распод ИП адреса.',
 'ip_range_toolarge' => 'Опсежна блокирања већа од /$1 нису дозвољена.',
-'blockme' => 'Блокирај ме',
 'proxyblocker' => 'Блокер посредника',
-'proxyblocker-disabled' => 'Ова функција је онемогућена.',
 'proxyblockreason' => 'Ваша ИП адреса је блокирана јер представља отворени посредник.
 Обратите се вашем добављачу интернет услуга или техничку подршку и обавестите их о овом озбиљном безбедносном проблему.',
-'proxyblocksuccess' => 'Урађено.',
 'sorbs' => 'DNSBL',
 'sorbsreason' => 'Ваша ИП адреса је наведена као отворени посредник у DNSBL-у који користи {{SITENAME}}.',
 'sorbs_create_account_reason' => 'Ваша ИП адреса је наведена као отворени посредник у DNSBL-у који користи {{SITENAME}}.
@@ -3308,6 +3305,7 @@ $1',
 'spam_reverting' => 'Враћам на последњу измену која не садржи везе до $1',
 'spam_blanking' => 'Све измене садрже везе до $1. Чистим',
 'spam_deleting' => 'Све измене садрже везе до $1. Бришем',
+'simpleantispam-label' => "Провера спама. '''НЕ''' попуњавај ово унутра!",
 
 # Info page
 'pageinfo-title' => 'Подаци о „$1“',
@@ -4103,7 +4101,7 @@ $5
 
 # Auto-summaries
 'autosumm-blank' => 'Потпуно обрисана страница',
-'autosumm-replace' => 'Замена садржаја са „$1“',
+'autosumm-replace' => 'Замена садржаја странице са „$1“',
 'autoredircomment' => 'Преусмерење на [[$1]]',
 'autosumm-new' => 'Направљена страница са: „$1“',
 
@@ -4275,8 +4273,7 @@ $5
 'specialpages' => 'Посебне странице',
 'specialpages-note' => '----
 * обичне посебне странице
-* <span class="mw-specialpagerestricted">ограничене посебне странице</span>
-* <span class="mw-specialpagecached">привремено меморисане посебне странице</span>',
+* <span class="mw-specialpagerestricted">ограничене посебне странице</span>',
 'specialpages-group-maintenance' => 'Извештаји одржавања',
 'specialpages-group-other' => 'Остале посебне странице',
 'specialpages-group-login' => 'Пријава/регистрација',
@@ -4314,7 +4311,9 @@ $5
 'tags-tag' => 'Назив ознаке',
 'tags-display-header' => 'Изглед на списковима измена',
 'tags-description-header' => 'Опис значења',
+'tags-active-header' => 'Активна?',
 'tags-hitcount-header' => 'Означене измене',
+'tags-active-yes' => 'Да',
 'tags-edit' => 'уреди',
 'tags-hitcount' => '$1 {{PLURAL:$1|измена|измене|измена}}',
 
@@ -4359,17 +4358,17 @@ $5
 'sqlite-no-fts' => '$1 без подршке претраге целог текста',
 
 # New logging system
-'logentry-delete-delete' => '$1 је {{GENDER:|обрисао|обрисала}} $3',
+'logentry-delete-delete' => '$1 је {{GENDER:|обрисао|обрисала}} страницу $3',
 'logentry-delete-restore' => '$1 је {{GENDER:$2|вратио|вратила}} страницу $3',
 'logentry-delete-event' => '$1 је {{GENDER:$2|променио|променила}} видљивост {{PLURAL:$5|догађаја|$5 догађаја}} у дневнику на $3: $4',
 'logentry-delete-revision' => '$1 је {{GENDER:$2|променио|променила}} видљивост {{PLURAL:$5|измене|$5 измена}} на страници $3: $4',
 'logentry-delete-event-legacy' => '$1 је {{GENDER:$2|променио|променила}} видљивост догађаја у дневнику на $3',
-'logentry-delete-revision-legacy' => '$1 је {{GENDER:$2|променио|променила}} видљивост изменâ на страници $3',
+'logentry-delete-revision-legacy' => '$1 је {{GENDER:$2|променио|променила}} видљивост измена на страници $3',
 'logentry-suppress-delete' => '$1 је {{GENDER:$2|потиснуо|потиснула}} страницу $3',
-'logentry-suppress-event' => '$1 је потајно {{GENDER:$2|променио|променила}} видљивост {{PLURAL:$5|догађаја|$5 догађаја}} у дневнику на $3: $4',
-'logentry-suppress-revision' => '$1 је потајно {{GENDER:$2|променио|променила}} видљивост {{PLURAL:$5|измене|$5 измена}} на страници $3: $4',
+'logentry-suppress-event' => '$1 је тајно {{GENDER:$2|променио|променила}} видљивост {{PLURAL:$5|догађаја|$5 догађаја}} у дневнику на $3: $4',
+'logentry-suppress-revision' => '$1 је тајно {{GENDER:$2|променио|променила}} видљивост {{PLURAL:$5|измене|$5 измена}} на страници $3: $4',
 'logentry-suppress-event-legacy' => '$1 је потајно {{GENDER:$2|променио|променила}} видљивост догађаја у дневнику на $3',
-'logentry-suppress-revision-legacy' => '$1 је потајно {{GENDER:$2|променио|променила}} видљивост измена на страници $3',
+'logentry-suppress-revision-legacy' => '$1 је тајно {{GENDER:$2|променио|променила}} видљивост измена на страници $3',
 'revdelete-content-hid' => 'садржај је сакривен',
 'revdelete-summary-hid' => 'опис измене је сакривен',
 'revdelete-uname-hid' => 'корисничко име је сакривено',
index f1ea384..5f442ca 100644 (file)
@@ -349,6 +349,7 @@ $messages = array(
 'tog-noconvertlink' => 'Onemogući pretvaranje naslova veza',
 'tog-norollbackdiff' => 'Izostavi razliku nakon izvršenog vraćanja',
 'tog-useeditwarning' => 'Upozori me kada napuštam stranicu sa nesačuvanim promenama',
+'tog-prefershttps' => 'Uvek koristi sigurnu konekciju kada sam prijavljen.',
 
 'underline-always' => 'uvek podvlači',
 'underline-never' => 'nikad ne podvlači',
@@ -444,8 +445,6 @@ $messages = array(
 'noindex-category' => 'Nepopisane stranice',
 'broken-file-category' => 'Stranice s neispravnim vezama do datoteka',
 
-'linkprefix' => '/^(.*?)([a-zA-Z\\x80-\\xff]+)$/sD',
-
 'about' => 'O nama',
 'article' => 'Stranica sa sadržajem',
 'newwindow' => '(otvara u novom prozoru)',
@@ -480,7 +479,7 @@ $messages = array(
 'vector-view-edit' => 'Uredi',
 'vector-view-history' => 'Istorija',
 'vector-view-view' => 'Čitaj',
-'vector-view-viewsource' => 'Izvornik',
+'vector-view-viewsource' => 'Izvorni kod',
 'actions' => 'Radnje',
 'namespaces' => 'Imenski prostori',
 'variants' => 'Varijante',
@@ -552,7 +551,7 @@ $1',
 # All link text and link target definitions of links into project namespace that get used by other message strings, with the exception of user group pages (see grouppage).
 'aboutsite' => 'O projektu {{SITENAME}}',
 'aboutpage' => 'Project:O nama',
-'copyright' => 'Sadržaj je dostupan pod licencom $1.',
+'copyright' => 'Sadržaj je dostupan pod licencom $1 osim ako je drugačije navedeno.',
 'copyrightpage' => '{{ns:project}}:Autorska prava',
 'currentevents' => 'Aktuelnosti',
 'currentevents-url' => 'Project:Novosti',
@@ -591,7 +590,7 @@ Pogledajte stranicu za [[Special:Version|izdanje]].',
 'youhavenewmessagesmulti' => 'Imate novih poruka na $1',
 'editsection' => 'uredi',
 'editold' => 'uredi',
-'viewsourceold' => 'izvornik',
+'viewsourceold' => 'izvorni kod',
 'editlink' => 'uredi',
 'viewsourcelink' => 'Izvor',
 'editsectionhint' => 'Uredite odeljak „$1“',
@@ -684,11 +683,11 @@ Podaci koji se ovde nalaze mogu biti zastareli.',
 'wrong_wfQuery_params' => 'Neispravni parametri za wfQuery()<br />
 Funkcija: $1<br />
 Upit: $2',
-'viewsource' => 'Izvornik',
+'viewsource' => 'Izvorni kod',
 'viewsource-title' => 'Prikaz izvora stranice $1',
 'actionthrottled' => 'Radnja je usporena',
 'actionthrottledtext' => 'U cilju borbe protiv nepoželjnih poruka, ograničene su vam izmene u određenom vremenu, a upravo ste prešli to ograničenje. Pokušajte ponovo za nekoliko minuta.',
-'protectedpagetext' => 'Ova stranica je zaključana za uređivanja.',
+'protectedpagetext' => 'Ova stranica je zaključana za izmene i druge radnje.',
 'viewsourcetext' => 'Možete da pogledate i umnožite izvorni tekst ove stranice:',
 'viewyourtext' => "Možete da pogledate i umnožite izvor '''vaših izmena''' na ovoj stranici:",
 'protectedinterface' => 'Ova stranica sadrži tekst korisničkog okruženja za softver na ovom vikiju i zaštićena je radi sprečavanja zloupotrebe.
@@ -702,8 +701,7 @@ $2',
 'customcssprotected' => 'Nemate dozvolu da menjate ovu CSS stranicu jer sadrži lične postavke drugog korisnika.',
 'customjsprotected' => 'Nemate dozvolu da menjate ovu stranicu javaskripta jer sadrži lične postavke drugog korisnika.',
 'ns-specialprotected' => 'Posebne stranice se ne mogu uređivati.',
-'titleprotected' => "Ovaj naslov je {{GENDER:$1|zaštitio korisnik|zaštitila korisnica|zaštitio korisnik}} [[User:$1|$1]].
-Navedeni razlog: ''$2''.",
+'titleprotected' => "Ovaj naziv je [[User:$1|$1]] zaštitio od pravljenja. Razlog: ''$2''.",
 'filereadonlyerror' => 'Ne mogu da izmenim datoteku „$1“ jer je riznica „$2“ u režimu za čitanje.
 
 Administrator koji ju je zaključao ponudio je sledeće objašnjenje: „$3“.',
@@ -759,10 +757,10 @@ Imajte na umu da neke stranice mogu nastaviti da se prikazuju kao da ste još pr
 'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|Pomoć pri prijavljivanju]]',
 'createacct-join' => 'Unesite svoje podatke ispod.',
 'createacct-emailrequired' => 'Adresa e-pošte',
-'createacct-emailoptional' => 'Adresa e-pošte (opcijono)',
+'createacct-emailoptional' => 'Adresa e-pošte (opciono)',
 'createacct-email-ph' => 'Unesite vašu adresu e-pоšte',
 'createaccountmail' => 'Koristite privremenu, slučajno stvorenu lozinku i pošaljite na navedenu adresu elektronske pošte',
-'createacct-realname' => 'Pravo ime (opcijono)',
+'createacct-realname' => 'Pravo ime (opciono)',
 'createaccountreason' => 'Razlog:',
 'createacct-reason' => 'Razlog',
 'createacct-reason-ph' => 'Zašto pravite još jedan nalog?',
@@ -1322,7 +1320,7 @@ Korišćenje navigacionih veza će poništiti ovu kolonu.',
 'mergehistory-reason' => 'Razlog:',
 
 # Merge log
-'mergelog' => 'Dnevnik spajanja',
+'mergelog' => 'Istorija spajanja',
 'pagemerge-logentry' => 'stranica [[$1]] je spojena u [[$2]] (sve do izmene $3)',
 'revertmerge' => 'rastavi',
 'mergelogpagetext' => 'Ispod se nalazi spisak skorašnjih spajanja istorija stranica.',
@@ -1336,6 +1334,7 @@ Korišćenje navigacionih veza će poništiti ovu kolonu.',
 'compareselectedversions' => 'Uporedi izabrane izmene',
 'showhideselectedversions' => 'Prikaži/sakrij izabrane izmene',
 'editundo' => 'poništi',
+'diff-empty' => '(Nema razlike)',
 'diff-multi' => '({{PLURAL:$1|nije prikazana međuizmena|nisu prikazane $1 međuizmene|nije prikazano $1 međuizmena}} {{PLURAL:$2|jednog|$2|$2}} korisnika)',
 'diff-multi-manyusers' => '({{PLURAL:$1|Nije prikazana međuizmena|Nisu prikazane $1 međuizmene|Nije prikazano $1 međuizmena}} od više od $2 korisnika)',
 'difference-missing-revision' => 'Ne mogu da pronađem {{PLURAL:$2|jednu izmenu|$2 izmene|$2 izmena}} od ove razlike ($1).
@@ -1522,6 +1521,7 @@ Ako izaberete da ga unesete, ono će biti korišćeno za pripisivanje vašeg rad
 'prefs-displaysearchoptions' => 'Postavke prikaza',
 'prefs-displaywatchlist' => 'Postavke prikaza',
 'prefs-diffs' => 'Razlike',
+'prefs-help-prefershttps' => 'Ova podešavanja će stupiti na snagu pri sledećoj prijavi.',
 
 # User preference: email validation using jQuery
 'email-address-validity-valid' => 'E-adresa je ispravna',
@@ -2003,6 +2003,10 @@ Probajte kasnije kada bude manje opterećenje.',
 'listfiles_size' => 'Veličina',
 'listfiles_description' => 'Opis',
 'listfiles_count' => 'Verzije',
+'listfiles-show-all' => 'Obuhvati stare verzije slika',
+'listfiles-latestversion' => 'Trenutna verzija',
+'listfiles-latestversion-yes' => 'Da',
+'listfiles-latestversion-no' => 'Ne',
 
 # File description page
 'file-anchor-link' => 'Datoteka',
@@ -2053,7 +2057,7 @@ Njen opis možete da izmenite na [$2 odgovarajućoj stranici].',
 'filerevert-legend' => 'Vrati datoteku',
 'filerevert-intro' => "Vraćate datoteku '''[[Media:$1|$1]]''' na [$4 izdanje od $2; $3].",
 'filerevert-comment' => 'Razlog:',
-'filerevert-defaultcomment' => 'Vraćeno na izdanje od $1; $2',
+'filerevert-defaultcomment' => 'Vraćeno na verziju od $2, $1',
 'filerevert-submit' => 'Vrati',
 'filerevert-success' => "Datoteka '''[[Media:$1|$1]]''' je vraćena na [$4 izdanje od $2; $3].",
 'filerevert-badversion' => 'Ne postoji ranije lokalno izdanje datoteke s navedenim vremenskim podacima.',
@@ -2441,7 +2445,7 @@ Podrška i dalja pomoć:
 'deletepage' => 'Obriši stranicu',
 'confirm' => 'Potvrdi',
 'excontent' => 'sadržaj je bio: „$1“',
-'excontentauthor' => 'sadržaj je bio: „$1“ (jedinu izmenu {{GENDER:|napravio je|napravila je|napravio je}} [[Special:Contributions/$2|$2]])',
+'excontentauthor' => 'sadržaj je bio: „$1“ (a jedini uređivač je bio „[[Special:Contributions/$2|$2]]“)',
 'exbeforeblank' => 'sadržaj pre brisanja je bio: „$1“',
 'exblank' => 'stranica je bila prazna',
 'delete-confirm' => 'Brisanje stranice „$1“',
@@ -2453,7 +2457,7 @@ Potvrdite svoju nameru, da razumete posledice i da ovo radite u skladu s [[{{Med
 'actionfailed' => 'Radnja nije uspela',
 'deletedtext' => "Stranica „$1“ je obrisana.
 Pogledajte ''$2'' za više detalja.",
-'dellogpage' => 'Dnevnik brisanja',
+'dellogpage' => 'Istorija brisanja',
 'dellogpagetext' => 'Ispod je spisak poslednjih brisanja.',
 'deletionlog' => 'dnevnik brisanja',
 'reverted' => 'Vraćeno na raniju izmenu',
@@ -2461,6 +2465,7 @@ Pogledajte ''$2'' za više detalja.",
 'deleteotherreason' => 'Drugi/dodatni razlog:',
 'deletereasonotherlist' => 'Drugi razlog',
 'deletereason-dropdown' => '*Najčešći razlozi za brisanje
+** Spam
 ** Zahtev autora
 ** Kršenje autorskih prava
 ** Vandalizam',
@@ -2484,7 +2489,7 @@ Poslednji autor je ujedno i jedini.',
 Poslednju izmenu je {{GENDER:$3|napravio|napravila|napravio}} [[User:$3|$3]] ([[User talk:$3|razgovor]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).',
 'editcomment' => "Opis izmene: \"''\$1''\".",
 'revertpage' => 'Vraćene izmene [[Special:Contributions/$2|$2]] ([[User talk:$2|razgovor]]) na poslednju  izmenu korisnika [[User:$1|$1]]',
-'revertpage-nouser' => 'Vraćene su izmene skrivengo korisnika na poslednju izmenu člana [[User:$1|$1]]',
+'revertpage-nouser' => 'Vraćene su izmene skrivenog korisnika na poslednju izmenu člana {{GENDER:$1|[[User:$1|$1]]}}',
 'rollback-success' => 'Vraćene su izmene {{GENDER:$1|korisnika|korisnice|korisnika}} $1
 na poslednju izmenu {{GENDER:$2|korisnika|korisnice|korisnika}} $2.',
 
@@ -2524,9 +2529,9 @@ Ovo su trenutne postavke stranice '''$1''':",
 'protect-cascadeon' => 'Ova stranica je trenutno zaštićena jer se nalazi na {{PLURAL:$1|stranici koja ima|stranicama koje imaju}} prenosivu zaštitu.
 Možete da promenite stepen zaštite, ali to neće uticati na prenosivu zaštitu.',
 'protect-default' => 'Dozvoli svim korisnicima',
-'protect-fallback' => 'Potrebno je imati ovlašćenja „$1“',
-'protect-level-autoconfirmed' => 'Blokiraj nove i anonimne korisnike',
-'protect-level-sysop' => 'Samo administratori',
+'protect-fallback' => 'Dozvoljeno samo korisnicima sa dozvolom „$1“',
+'protect-level-autoconfirmed' => 'Dopušteno samo automatski potvrđenim korisnicima',
+'protect-level-sysop' => 'Dopušteno samo administratorima',
 'protect-summary-cascade' => 'prenosiva zaštita',
 'protect-expiring' => 'ističe $1 (UTC)',
 'protect-expiring-local' => 'ističe $1',
@@ -2629,7 +2634,7 @@ $1',
 'contributions' => '{{GENDER:$1|Korisnički}} doprinosi',
 'contributions-title' => 'Doprinosi {{GENDER:$1|korisnika|korisnice|korisnika}} $1',
 'mycontris' => 'Doprinosi',
-'contribsub2' => 'Za $1 ($2)',
+'contribsub2' => 'Za {{GENDER:$3|$1}} ($2)',
 'nocontribs' => 'Izmene koje odgovaraju ovim uslovima nisu pronađene.',
 'uctop' => '(poslednja)',
 'month' => 'od meseca (i ranije):',
@@ -2787,12 +2792,9 @@ Tekuće zabrane i blokiranja možete naći [[Special:BlockList|ovde]].',
 Ona je blokirana kao deo blokade $2, koja može biti deblokirana.',
 'ip_range_invalid' => 'Neispravan raspod IP adresa.',
 'ip_range_toolarge' => 'Opsežna blokiranja veća od /$1 nisu dozvoljena.',
-'blockme' => 'Blokiraj me',
 'proxyblocker' => 'Bloker posrednika',
-'proxyblocker-disabled' => 'Ova funkcija je onemogućena.',
 'proxyblockreason' => 'Vaša IP adresa je blokirana jer predstavlja otvoreni posrednik.
 Obratite se vašem dobavljaču internet usluga ili tehničku podršku i obavestite ih o ovom ozbiljnom bezbednosnom problemu.',
-'proxyblocksuccess' => 'Urađeno.',
 'sorbs' => 'DNSBL',
 'sorbsreason' => 'Vaša IP adresa je navedena kao otvoreni posrednik u DNSBL-u koji koristi {{SITENAME}}.',
 'sorbs_create_account_reason' => 'Vaša IP adresa je navedena kao otvoreni posrednik u DNSBL-u koji koristi {{SITENAME}}.
@@ -3161,6 +3163,7 @@ Ovo je verovatno izazvano vezom do spoljašnjeg sajta koji se nalazi na crnoj li
 'spam_reverting' => 'Vraćam na poslednju izmenu koja ne sadrži veze do $1',
 'spam_blanking' => 'Sve izmene sadrže veze do $1. Čistim',
 'spam_deleting' => 'Sve izmene sadrže veze do $1. Brišem',
+'simpleantispam-label' => "Provera spama. '''NE''' popunjavaj ovo unutra!",
 
 # Info page
 'pageinfo-title' => 'Podaci o „$1“',
@@ -3194,6 +3197,7 @@ Ovo je verovatno izazvano vezom do spoljašnjeg sajta koji se nalazi na crnoj li
 'pageinfo-magic-words' => '{{PLURAL:$1|Magična reč|Magične reči}} ($1)',
 'pageinfo-hidden-categories' => '{{PLURAL:$1|Sakrivena kategorija|Sakrivene kategorije}} ($1)',
 'pageinfo-templates' => '{{PLURAL:$1|Uključeni šablon|Uključeni šabloni}} ($1)',
+'pageinfo-transclusions' => '{{PLURAL:$1|Stranica|Stranice}} uključene u ($1)',
 'pageinfo-toolboxlink' => 'Podaci o stranici',
 'pageinfo-redirectsto' => 'Preusmerava na',
 'pageinfo-redirectsto-info' => 'podaci',
@@ -3938,7 +3942,7 @@ Potvrdite da stvarno želite da napravite stranicu.",
 
 # Auto-summaries
 'autosumm-blank' => 'Potpuno obrisana stranica',
-'autosumm-replace' => 'Zamena sadržaja sa „$1“',
+'autosumm-replace' => 'Zamena sadržaja stranice sa „$1“',
 'autoredircomment' => 'Preusmerenje na [[$1]]',
 'autosumm-new' => 'Napravljena stranica sa: „$1“',
 
@@ -4109,8 +4113,7 @@ Trebalo bi da ste primili [{{SERVER}}{{SCRIPTPATH}}/COPYING primerak GNU-ove op
 'specialpages' => 'Posebne stranice',
 'specialpages-note' => '----
 * obične posebne stranice
-* <span class="mw-specialpagerestricted">ograničene posebne stranice</span>
-* <span class="mw-specialpagecached">privremeno memorisane posebne stranice</span>',
+* <span class="mw-specialpagerestricted">ograničene posebne stranice</span>',
 'specialpages-group-maintenance' => 'Izveštaji održavanja',
 'specialpages-group-other' => 'Ostale posebne stranice',
 'specialpages-group-login' => 'Prijava/registracija',
@@ -4142,12 +4145,15 @@ Trebalo bi da ste primili [{{SERVER}}{{SCRIPTPATH}}/COPYING primerak GNU-ove op
 'tags' => 'Važeće oznake izmena',
 'tag-filter' => 'Filter za [[Special:Tags|oznake]]:',
 'tag-filter-submit' => 'Filtriraj',
+'tag-list-wrapper' => '([[Special:Tags|{{PLURAL:$1|Oznaka|Oznake}}]]: $2)',
 'tags-title' => 'Oznake',
 'tags-intro' => 'Na ovoj stranici je naveden spisak oznaka s kojima program može da označi izmene i njegovo značenje.',
 'tags-tag' => 'Naziv oznake',
 'tags-display-header' => 'Izgled na spiskovima izmena',
 'tags-description-header' => 'Opis značenja',
+'tags-active-header' => 'Aktivna?',
 'tags-hitcount-header' => 'Označene izmene',
+'tags-active-yes' => 'Da',
 'tags-edit' => 'uredi',
 'tags-hitcount' => '$1 {{PLURAL:$1|izmena|izmene|izmena}}',
 
@@ -4192,17 +4198,17 @@ Trebalo bi da ste primili [{{SERVER}}{{SCRIPTPATH}}/COPYING primerak GNU-ove op
 'sqlite-no-fts' => '$1 bez podrške pretrage celog teksta',
 
 # New logging system
-'logentry-delete-delete' => '$1 {{GENDER:|je obrisao|je obrisala|je obrisao}} $3',
-'logentry-delete-restore' => '$1 {{GENDER:|je vratio|je vratila|je vratio}} stranicu $3',
-'logentry-delete-event' => '$1 {{GENDER:$2|je promenio|je promenila|je promenio}} vidljivost {{PLURAL:$5|događaja|$5 događaja|$5 događaja}} u dnevniku na $3: $4',
-'logentry-delete-revision' => '$1 {{GENDER:|je promenio|je promenila|je promenio}} vidljivost {{PLURAL:$5|izmene|$5 izmene|$5 izmena}} na stranici $3: $4',
+'logentry-delete-delete' => '$1 je {{GENDER:$2|obrisao|obrisala}} stranicu $3',
+'logentry-delete-restore' => '$1 je {{GENDER:$2|vratio|vratila}} stranicu $3',
+'logentry-delete-event' => '$1 je {{GENDER:$2|promenio|promenila}} vidljivost {{PLURAL:$5|događaja|$5 daogađaja}} u dnevniku na $3: $4',
+'logentry-delete-revision' => '$1 je {{GENDER:$2|promenio|promenila}} vidljivost {{PLURAL:$5|izmene|$5 izmjena}} na stranici $3: $4',
 'logentry-delete-event-legacy' => '$1 je {{GENDER:$2|promenio|promenila}} vidljivost događaja u dnevniku na $3',
-'logentry-delete-revision-legacy' => '$1 {{GENDER:|je promenio|je promenila|je promenio}} vidljivost izmenâ na stranici $3',
-'logentry-suppress-delete' => '$1 {{GENDER:|je potisnuo|je potisnula|je potisnuo}} stranicu $3',
-'logentry-suppress-event' => '$1 je potajno {{GENDER:|promenio|promenila|promenio}} vidljivost {{PLURAL:$5|događaja|$5 događaja|$5 događaja}} u dnevniku na $3: $4',
-'logentry-suppress-revision' => '$1 je potajno {{GENDER:|promenio|promenila|promenio}} vidljivost {{PLURAL:$5|izmene|$5 izmene|$5 izmena}} na stranici $3: $4',
-'logentry-suppress-event-legacy' => '$1 je potajno {{GENDER:|promenio|promenila|promenio}} vidljivost događajâ u dnevniku na $3',
-'logentry-suppress-revision-legacy' => '$1 je potajno {{GENDER:|promenio|promenila}} vidljivost izmena na stranici $3',
+'logentry-delete-revision-legacy' => '$1 je {{GENDER:$2|promenio|promenila}} vidljivost izmena na stranici $3',
+'logentry-suppress-delete' => '$1 je {{GENDER:$2|potisnuo|potisnula}} stranicu $3',
+'logentry-suppress-event' => '$1 je tajno {{GENDER:$2|promenio|promenila}} vidljivost {{PLURAL:$5|događaja|$5 događaja}} u dnevniku na $3: $4',
+'logentry-suppress-revision' => '$1 je tajno {{GENDER:$2|promenio|promenila}} vidljivost {{PLURAL:$5|izmene|$5 izmena}} na stranici $3: $4',
+'logentry-suppress-event-legacy' => '$1 је tajno {{GENDER:$2|promenio|promenila}} vidljivost događaj u dnevniku na $3',
+'logentry-suppress-revision-legacy' => '$1 je tajno {{GENDER:$2|promenio|promenila}} vidljivost izmena na stranici $3',
 'revdelete-content-hid' => 'sadržaj je sakriven',
 'revdelete-summary-hid' => 'opis izmene je sakriven',
 'revdelete-uname-hid' => 'korisničko ime je sakriveno',
@@ -4215,15 +4221,15 @@ Trebalo bi da ste primili [{{SERVER}}{{SCRIPTPATH}}/COPYING primerak GNU-ove op
 'logentry-move-move-noredirect' => '$1 je {{GENDER:$2|premestio|premestila}} stranicu $3 na $4 bez ostavljanja preusmerenja',
 'logentry-move-move_redir' => '$1 je {{GENDER:$2|premestio|premestila}} stranicu $3 na $4 preko preusmerenja',
 'logentry-move-move_redir-noredirect' => '$1 je {{GENDER:|premestio|premestila}} stranicu $3 na $4 preko preusmerenja bez ostavljanja preusmerenja',
-'logentry-patrol-patrol' => '$1 {{GENDER:|je označio|je označila|je označio}} izmenu $4 stranice $3 kao patroliranu',
+'logentry-patrol-patrol' => '$1 je {{GENDER:$2|osznačio|označila}} izenu $4 stranice $3 kao patroliranu',
 'logentry-patrol-patrol-auto' => '$1 je automatski {{GENDER:$2|označio|označila}} izmenu $4 stranice $3 kao pregledanu',
 'logentry-newusers-newusers' => '$1 je {{GENDER:$2|otvorio|otvorila}} korisnički nalog',
 'logentry-newusers-create' => '$1 je {{GENDER:$2|otvorio|otvorila}} korisnički nalog',
 'logentry-newusers-create2' => '$1 je {{GENDER:$2|otvorio|otvorila}} korisnički nalog $3',
 'logentry-newusers-autocreate' => 'Korisnički nalog $1 je automatski {{GENDER:$2|otvoren}}',
 'logentry-rights-rights' => '$1 je {{GENDER:$1|promenio|promenila}} članstvo grupe za $3 iz $4 u $5',
-'logentry-rights-rights-legacy' => '$1 {{GENDER:$1|je promenio|je promenila|je promenio}} članstvo grupe za $3',
-'logentry-rights-autopromote' => '$1 je automatski {{GENDER:$1|unapređen|unapređena|unapređen}} iz $4 u $5',
+'logentry-rights-rights-legacy' => '$1 je {{GENDER:$2|promenio|promenila}} čalnstvo grupe za $3',
+'logentry-rights-autopromote' => '$1 je automatski {{GENDER:$1|unapređen|unapređena}} iz $4 u $5',
 'rightsnone' => '(ništa)',
 
 # Feedback
index 9fcdd63..a5fb117 100644 (file)
@@ -2237,11 +2237,8 @@ Sjuch ju [[Special:BlockList|Lieste fon de speerde IP-Adrässen un Benutsernoome
 'ipb_blocked_as_range' => 'Failer: Ju IP-Adresse $1 wuude as Deel fon ju Beräksspeere $2 indirekt speerd. Ne Äntspeerenge fon $1 alleene is nit muugelk.',
 'ip_range_invalid' => 'Uungultige IP-Adräsberäk.',
 'ip_range_toolarge' => 'Adräsberäkke, do der gratter sunt as /$1, sunt nit ferlööwed.',
-'blockme' => 'Speer mie',
 'proxyblocker' => 'Proxy blokker',
-'proxyblocker-disabled' => 'Disse Funktion is deaktivierd.',
 'proxyblockreason' => 'Jou IP-Adrässe wuude speerd, deer ju n eepenen Proxy is. Kontaktierje jädden Jou Provider af Jou Systemtechnik un informierje Jou jou uur dit muugelke Sicherhaidsproblem.',
-'proxyblocksuccess' => 'Kloor.',
 'sorbsreason' => 'Dien IP-Adrässe is in ju DNSBL fon {{SITENAME}} as eepene PROXY liested.',
 'sorbs_create_account_reason' => 'Dien IP-Adrässe is in ju DNSBL fon {{SITENAME}} as eepene PROXY liested. Du koast neen Benutser-Account anlääse.',
 'cant-block-while-blocked' => 'Du duurst neen uur Benutsere speere, wan du sälwen speerd bäst.',
@@ -2550,6 +2547,7 @@ Do ap dän lokoale Reekener spiekerje un deerätter hier hoochleede.',
 'spambot_username' => 'MediaWiki Spam-Süüwerenge',
 'spam_reverting' => 'Lääste Version sunner Links tou $1 wier häärstoald.',
 'spam_blanking' => 'Aal Versione äntheelden Links tou $1, skeenmoaked.',
+'simpleantispam-label' => "Spamskuts-Wröige. Hier '''niks''' iendreege!",
 
 # Info page
 'pageinfo-title' => 'Informatione tou „$1“',
index e8e7e2f..410e1ba 100644 (file)
@@ -2157,11 +2157,8 @@ Pikeun rujukan, logna dipidangkeun di handap ieu:',
 'unblock-hideuser' => 'Anjeun teu bisa muka peungpeuk ieu pamaké, kusabab landihanan keur disumputkeun.',
 'ipb_cant_unblock' => 'Éror: ID peungpeuk $1 teu kapanggih. Sigana mah geus dibuka.',
 'ip_range_invalid' => 'Angka IP teu bener.',
-'blockme' => 'Peungpeuk kuring',
 'proxyblocker' => 'Pameungpeuk proxy',
-'proxyblocker-disabled' => 'Ieu fungsi keur ditumpurkeun.',
 'proxyblockreason' => "Alamat IP anjeun dipeungpeuk sabab mangrupa proxy muka. Mangga tepungan ''Internet service provider'' atanapi ''tech support'' anjeun, béjakeun masalah serius ieu.",
-'proxyblocksuccess' => 'Réngsé.',
 'sorbsreason' => "Alamat IP anjeun kadaptar salaku ''open proxy'' dina DNSBL.",
 'sorbs_create_account_reason' => "Alamat IP anjeun kadaptar salaku ''open proxy'' dina DNSBL. Anjeun teu bisa nyieun rekening",
 'cant-block-while-blocked' => 'Lamun keur dipeungpeuk, anjeun teu bisa meungpeuk séjén kontributor.',
index 1e4181f..8497694 100644 (file)
@@ -335,16 +335,16 @@ $messages = array(
 'tog-extendwatchlist' => 'Utöka bevakningslistan till att visa alla ändringar, inte bara den senaste',
 'tog-usenewrc' => 'Gruppera ändringar efter sida i senaste ändringar och bevakningslistan',
 'tog-numberheadings' => 'Automatisk numrerade rubriker',
-'tog-showtoolbar' => 'Visa redigerings-verktygsraden',
+'tog-showtoolbar' => 'Visa redigeringsverktygsraden',
 'tog-editondblclick' => 'Redigera sidor med dubbelklick',
 'tog-editsection' => 'Aktivera redigering av avsnitt genom [redigera]-länkar',
 'tog-editsectiononrightclick' => 'Aktivera redigering av avsnitt genom högerklick på underrubriker',
-'tog-showtoc' => 'Visa innehållsförteckning (för sidor som har minst fyra rubriker)',
+'tog-showtoc' => 'Visa innehållsförteckning (för sidor med minst fyra rubriker)',
 'tog-rememberpassword' => 'Kom ihåg min inloggning på den här webbläsaren (i maximalt $1 {{PLURAL:$1|dygn|dygn}})',
-'tog-watchcreations' => 'Lägg till sidor jag skapar i min bevakningslista',
-'tog-watchdefault' => 'Lägg till sidor jag redigerar i min bevakningslista',
-'tog-watchmoves' => 'Lägg till sidor jag flyttar i min bevakningslista',
-'tog-watchdeletion' => 'Lägg till sidor jag raderar i min bevakningslista',
+'tog-watchcreations' => 'Lägg till sidor jag skapar och filer jag laddar upp till min bevakningslista',
+'tog-watchdefault' => 'Lägg till sidor och filer jag redigerar i min bevakningslista',
+'tog-watchmoves' => 'Lägg till sidor och filer jag flyttar i min bevakningslista',
+'tog-watchdeletion' => 'Lägg till sidor och filer jag raderar i min bevakningslista',
 'tog-minordefault' => 'Markera automatiskt ändringar som mindre',
 'tog-previewontop' => 'Visa förhandsgranskningen ovanför redigeringsrutan',
 'tog-previewonfirst' => 'Visa förhandsgranskning när redigering påbörjas',
@@ -544,7 +544,7 @@ $messages = array(
 'articlepage' => 'Visa innehållssida',
 'talk' => 'Diskussion',
 'views' => 'Visningar',
-'toolbox' => 'Verktygslåda',
+'toolbox' => 'Verktyg',
 'userpage' => 'Visa användarsida',
 'projectpage' => 'Visa projektsida',
 'imagepage' => 'Visa filsida',
@@ -844,8 +844,8 @@ fortsätta använda ditt gamla lösenord.',
 'noemailcreate' => 'Du måste ange en giltig e-postadress',
 'passwordsent' => 'Ett nytt lösenord har skickats till den e-postadress som användaren "$1" har registrerat. När du får meddelandet, var god logga in igen.',
 'blocked-mailpassword' => 'Din IP-adress är blockerad, därför kan den inte användas för att få ett nytt lösenord.',
-'eauthentsent' => 'Ett e-brev för bekräftelse har skickats till den e-postadress som angivits.
-Innan någon annan e-post kan skickas härifrån till kontot, måste du följa instruktionerna i e-brevet för att bekräfta att kontot verkligen är ditt.',
+'eauthentsent' => 'Ett e-postmeddelande för bekräftelse har skickats till den angivna e-postadressen.
+Innan någon annan e-post kan skickas till kontot, måste du följa instruktionerna i e-postmeddelandet för att bekräfta att kontot verkligen är ditt.',
 'throttled-mailpassword' => 'En lösenordsåterställning har redan skickats för mindre än {{PLURAL:$1|en timme|$1 timmar}} sedan.
 För att förhindra missbruk skickas bara en lösenordsåterställning per {{PLURAL:$1|timme|$1-timmarsperiod}}.',
 'mailerror' => 'Fel vid skickande av e-post: $1',
@@ -1216,7 +1216,7 @@ Anledningen till blockeringen var "$2".',
 'next' => 'nästa',
 'last' => 'föregående',
 'page_first' => 'första',
-'page_last' => 'senaste',
+'page_last' => 'sista',
 'histlegend' => "Val av diff: markera i klickrutorna för att jämföra versioner och tryck enter eller knappen längst ner.<br />
 Förklaring: (nuvarande) = skillnad mot nuvarande version; (föregående) = skillnad mot föregående version; '''m''' = mindre ändring.",
 'history-fieldset-title' => 'Bläddra i historiken',
@@ -1287,15 +1287,15 @@ Andra administratörer på {{SITENAME}} kommer fortfarande att kunna läsa det d
 * Opassande personlig information
 *: ''hemadresser och telefonnummer, personnummer, etc.''",
 'revdelete-legend' => 'Ändra synlighet',
-'revdelete-hide-text' => 'Dölj versionstext',
+'revdelete-hide-text' => 'Versionstext',
 'revdelete-hide-image' => 'Dölj filinnehåll',
 'revdelete-hide-name' => 'Dölj åtgärd och sidnamn',
-'revdelete-hide-comment' => 'Dölj redigeringskommentar',
-'revdelete-hide-user' => 'Dölj skribentens användarnamn/IP-adress',
+'revdelete-hide-comment' => 'Redigeringssammanfattning',
+'revdelete-hide-user' => 'Redigerarens användarnamn/IP-adress',
 'revdelete-hide-restricted' => 'Undanhåll data från administratörer så väl som från övriga',
 'revdelete-radio-same' => '(låt vara)',
-'revdelete-radio-set' => 'Ja',
-'revdelete-radio-unset' => 'Nej',
+'revdelete-radio-set' => 'Synlig',
+'revdelete-radio-unset' => 'Dold',
 'revdelete-suppress' => 'Undanhåll data även från administratörer',
 'revdelete-unsuppress' => 'Ta bort begränsningar på återställda versioner',
 'revdelete-log' => 'Anledning:',
@@ -1547,7 +1547,7 @@ Programvaran använder detta värde för att adressera dig till andra med rätt
 'prefs-help-realname' => 'Riktigt namn behöver inte anges.
 Om du väljer att ange ditt riktiga namn, kommer det att användas för att tillskriva dig ditt arbete.',
 'prefs-help-email' => 'Att ange e-postadress är valfritt, men gör det möjligt att få ditt lösenord mejlat till dig om du glömmer det.',
-'prefs-help-email-others' => 'Du kan också välja att låta andra användare kontakta dig genom din användar-eller diskussionssida utan att avslöja din identitet.',
+'prefs-help-email-others' => 'Du kan också välja att låta andra användare kontakta dig genom din användar- eller diskussionssida utan att avslöja din identitet.',
 'prefs-help-email-required' => 'E-postadress måste anges.',
 'prefs-info' => 'Grundläggande information',
 'prefs-i18n' => 'Internationalisering',
@@ -1599,7 +1599,7 @@ Om du väljer att ange ditt riktiga namn, kommer det att användas för att till
 # Groups
 'group' => 'Grupp:',
 'group-user' => 'Användare',
-'group-autoconfirmed' => 'Bekräftade användare',
+'group-autoconfirmed' => 'Automatiskt bekräftade användare',
 'group-bot' => 'Robotar',
 'group-sysop' => 'Administratörer',
 'group-bureaucrat' => 'Byråkrater',
@@ -2844,11 +2844,8 @@ Se [[Special:BlockList|blockeringslistan]] för en översikt av gällande blocke
 'ipb_blocked_as_range' => 'Fel: IP-adressen $1 är inte direkt blockerad, och kan därför inte avblockeras. Adressen är blockerad som en del av IP-intervallet $2, som kan avblockeras.',
 'ip_range_invalid' => 'Ogiltigt IP-intervall.',
 'ip_range_toolarge' => 'Blockering av block större än /$1 är inte tillåtna.',
-'blockme' => 'Blockera mig',
 'proxyblocker' => 'Proxy-block',
-'proxyblocker-disabled' => 'Den här funktionen är avaktiverad.',
 'proxyblockreason' => 'Din IP-adress har blivit blockerad eftersom den tillhör en öppen proxy. Kontakta din internetleverantör eller din organisations eller företags tekniska support, och informera dem om denna allvarliga säkerhetsrisk.',
-'proxyblocksuccess' => 'Gjort.',
 'sorbsreason' => 'Din IP-adress är listad som öppen proxy i den DNSBL {{SITENAME}} använder.',
 'sorbs_create_account_reason' => 'Din IP-adress är listad som en öppen proxy i den DNSBL som används av {{SITENAME}}.
 Du får inte skapa ett användarkonto',
@@ -3214,6 +3211,8 @@ Detta orsakades troligen av en länk till en svartlistad webbplats.',
 'spam_reverting' => 'Återställer till den senaste versionen som inte innehåller länkar till $1',
 'spam_blanking' => 'Alla versioner innehöll en länk till $1, blankar',
 'spam_deleting' => 'Alla ändringar innehöll länkar till $1, raderar',
+'simpleantispam-label' => "Anti-spamkontroll.
+Fyll '''INTE''' i den här!",
 
 # Info page
 'pageinfo-title' => 'Information om "$1"',
@@ -3986,7 +3985,7 @@ Du bör ha fått [{{SERVER}}{{SCRIPTPATH}}/COPYING en kopia av GNU General Publi
 # Special:Redirect
 'redirect' => 'Omdirigering efter filnamn, användar-ID eller versions-ID',
 'redirect-legend' => 'Omdirigera till en fil eller sida',
-'redirect-summary' => 'Den här specialsidan omdirigerar till en fil (efter filnamn), en sida (efter versions-id) eller en användarsida (efter användar-id).',
+'redirect-summary' => 'Den här specialsidan omdirigerar till en fil (efter filnamn), en sida (efter versions-id) eller en användarsida (efter användar-id). Användning: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/revision/328429]], eller [[{{#Special:Redirect}}/user/101]].',
 'redirect-submit' => 'Kör',
 'redirect-lookup' => 'Slå upp:',
 'redirect-value' => 'Värde:',
index 499b2b0..d8ef376 100644 (file)
@@ -2294,8 +2294,6 @@ Andika sababu ya kuzuia chini (kwa mfano, kwa kutaja mifano ya kurasa zilizohari
 'block-log-flags-noemail' => 'barua pepe imezuiliwa',
 'block-log-flags-hiddenname' => 'jina la mtumiaji limefichwa',
 'ipb_already_blocked' => '"$1" tayari imeshazuiwa',
-'blockme' => 'Nizuie',
-'proxyblocksuccess' => 'Tayari.',
 
 # Developer tools
 'lockdb' => 'Funga hifadhidata',
index f915f20..1a0cca4 100644 (file)
@@ -13,6 +13,7 @@
  * @author Gaj777
  * @author Herr Kriss
  * @author Kaganer
+ * @author Krol111
  * @author Lajsikonik
  * @author Leinad
  * @author Lwh
@@ -69,15 +70,15 @@ $messages = array(
 'tog-justify' => 'Wyrůwnowej tekst we akapitach (justowańy)',
 'tog-hideminor' => 'Schow drobne pomjyńańa we ńydowno pomjyńanych',
 'tog-hidepatrolled' => 'Schow sprowdzůne sprowjyńa we ńydowno pomjyńanych',
-'tog-newpageshidepatrolled' => 'Schow sprowdzůne zajty na wykoźe nowych zajtůw',
-'tog-extendwatchlist' => 'Pokoż na mojij pozůrliśće wszyjske, a ńyjyno uostatńe sprowjyńa',
+'tog-newpageshidepatrolled' => 'Schow sprawdzůne zajty na wykoźe nowych zajtůw',
+'tog-extendwatchlist' => 'Pokoż na mojij pozůrliśće wszyjske, a ńy jyno uostatńe sprowjyńa',
 'tog-usenewrc' => 'Używej poszyrzyńo ńydowno pomjyńanych (JavaScript)',
 'tog-numberheadings' => 'Automatyczno numeracyjo titlůw',
 'tog-showtoolbar' => 'Pokoż gurt werkcojgůw (JavaScript)',
-'tog-editondblclick' => 'Edycja napoczynajům dwa klikńyńća (JavaScript)',
+'tog-editondblclick' => 'Edycyjo napoczynajům dwa klikńyńća (JavaScript)',
 'tog-editsection' => 'Kożdo tajla zajty sprowjano uosobno',
-'tog-editsectiononrightclick' => 'Klikńyńće prawym kneflym myszy na titlu tajli<br />napoczyno jego sprowjańy(JavaScript)',
-'tog-showtoc' => 'Pokoż spis treśći (na zajtach, kere majům wjyncy jak trzi tajle)',
+'tog-editsectiononrightclick' => 'Klikńyńće prawym kneflym myszy na titlu tajli<br />napoczyno jigo sprowjańy(JavaScript)',
+'tog-showtoc' => 'Pokoż spis treśći (na zajtach, kere majům wjyncyj kej trzi tajle)',
 'tog-rememberpassword' => 'Pamjyntej můj ausdruk na tym kůmputrze (nojdalij bez $1 {{PLURAL:$1|dźyń|dńůw}})',
 'tog-watchcreations' => 'Dowům pozůr na zajty, kere żech naszkryfloł',
 'tog-watchdefault' => 'Dowům pozůr na zajty, kere żech sprowjoł',
@@ -88,13 +89,13 @@ $messages = array(
 'tog-previewonfirst' => 'Obźyrej zajta przi pjyrszym sprowjańu',
 'tog-nocache' => 'Wypńij podrynczno pamjyńć',
 'tog-enotifwatchlistpages' => 'Wyślij e-brifa, kej ftoś zmjyńi zajta, na kero dowom pozůr',
-'tog-enotifusertalkpages' => 'Wyślij e-brifa, kej zajta mojij godki bydźe pomjyńono',
+'tog-enotifusertalkpages' => 'Wyślij e-brifa, kej zajta mojij godki bydźe půmjyńono',
 'tog-enotifminoredits' => 'Wyślij e-brifa tyż, kej by szło uo drobne pomjyńańa',
 'tog-enotifrevealaddr' => 'Ńy chow mojigo e-brifa we powjadomjyńach',
 'tog-shownumberswatching' => 'Pokoż, wjela sprowjorzy dowo pozůr',
 'tog-oldsig' => 'Teroźni wyglůnd Twojygo szrajbowańo',
-'tog-fancysig' => 'Szrajbńij s kodůma wiki (bez autůmatycznygo linka)',
-'tog-uselivepreview' => 'Używej dynamiczne uobźyrańy (JavaScript) (eksperymentalny)',
+'tog-fancysig' => 'Szrajbńij ze kodůma wiki (bez autůmatycznygo linka)',
+'tog-uselivepreview' => 'Używej dynamiczne uobźyrańy (JavaScript) (ekszperymentalny)',
 'tog-forceeditsummary' => 'Pedź, kejbych ńic ńy naszkryfloł we uopiśe pomjyńań',
 'tog-watchlisthideown' => 'Schow moje pomjyńańa we artiklach, na kere dowom pozůr',
 'tog-watchlisthidebots' => 'Schow pomjyńańa sprowjone bez boty we artiklach, na kere dowom pozůr',
@@ -103,9 +104,11 @@ $messages = array(
 'tog-watchlisthideanons' => 'Schow sprowjyńa anůńimowych sprowjoczy na liśće artikli, na kere dowom pozůr',
 'tog-watchlisthidepatrolled' => 'Schowej sprowdzůne sprowjyńa na pozorliśće',
 'tog-ccmeonemails' => 'Przesyłej mi kopje e-brifůw co żech je posłoł inkszym sprowjaczom',
-'tog-diffonly' => 'Å\83y pokozuj treÅ\9bÄ\87i zajtůw půnižyj porůwnańo pomjyńań',
+'tog-diffonly' => 'Å\83y pokozuj treÅ\9bÄ\87i zajtůw půniżyj porůwnańo pomjyńań',
 'tog-showhiddencats' => 'Pokoż schowane kategoryje',
 'tog-norollbackdiff' => 'Uomiń pokozywańy pomjyńań po użyću funkcyje „cofej”',
+'tog-useeditwarning' => 'Uostrzegej mje, kej uopuszczom zajta edycyji bez spamjyntańo půmjań',
+'tog-prefershttps' => 'Zowdy używej pewne połůnczyńe przi logowańu',
 
 'underline-always' => 'Dycki',
 'underline-never' => 'Ńigdy',
@@ -135,11 +138,11 @@ $messages = array(
 'sat' => 'Sob',
 'january' => 'styczyń',
 'february' => 'luty',
-'march' => 'merc',
+'march' => 'marzec',
 'april' => 'kwjećyń',
 'may_long' => 'moj',
-'june' => 'czyrwjyń',
-'july' => 'lipjyń',
+'june' => 'czyrwjyc',
+'july' => 'lipjyc',
 'august' => 'śyrpjyń',
 'september' => 'wrześyń',
 'october' => 'paźdźerńik',
@@ -147,19 +150,19 @@ $messages = array(
 'december' => 'grudźyń',
 'january-gen' => 'styczńa',
 'february-gen' => 'lutygo',
-'march-gen' => 'merca',
+'march-gen' => 'marca',
 'april-gen' => 'kwjetńa',
-'may-gen' => 'maja',
-'june-gen' => 'czyrwńa',
-'july-gen' => 'lipńa',
+'may-gen' => 'moja',
+'june-gen' => 'czyrwca',
+'july-gen' => 'lipca',
 'august-gen' => 'śyrpńa',
-'september-gen' => 'wrześńa',
+'september-gen' => 'wrzyśńa',
 'october-gen' => 'paźdźerńika',
 'november-gen' => 'listopada',
 'december-gen' => 'grudńa',
 'jan' => 'sty',
 'feb' => 'lut',
-'mar' => 'mer',
+'mar' => 'mar',
 'apr' => 'kwj',
 'may' => 'moj',
 'jun' => 'czy',
@@ -169,12 +172,21 @@ $messages = array(
 'oct' => 'paź',
 'nov' => 'lis',
 'dec' => 'gru',
+'january-date' => '$1 styczńa',
+'february-date' => '$1 lutygo',
+'april-date' => '$1 kwjytńa',
+'may-date' => '$1 moja',
+'june-date' => '$1 czyrwca',
+'august-date' => '$1 śyrpńa',
+'september-date' => '$1 wrzyśńa',
+'october-date' => '$1 paźdźyrńika',
+'december-date' => '$1 grudńa',
 
 # Categories related messages
-'pagecategories' => '{{PLURAL:$1|Kategoryjo|Kategoryje|Kategoryj}}',
+'pagecategories' => '{{PLURAL:$1|Kategoryjo|Kategoryje|Kategoryji}}',
 'category_header' => 'Zajty we katygoryji "$1"',
 'subcategories' => 'Podkatygoryje',
-'category-media-header' => 'Pliki w katygoryji "$1"',
+'category-media-header' => 'Pliki we katygoryji "$1"',
 'category-empty' => "''Terozki w tyj katygoryji sům żodne artikle a pliki''",
 'hidden-categories' => '{{PLURAL:$1|Schowano katygoryjo|Schowane katygoryje|Schowanych katygoryj}}',
 'hidden-category-category' => 'Schowane katygoryje',
@@ -182,7 +194,7 @@ $messages = array(
 'category-subcat-count-limited' => 'Ta katygoryjo mo {{PLURAL:$1|tako podkatygoryjo|$1 podkatygoryje|$1 podkatygoryji}}.',
 'category-article-count' => '{{PLURAL:$2|W tyj katygoryji je jyno jydno zajta.|W katygoryji {{PLURAL:$1|je ukozano $1 zajta|sům ukozane $1 zajty|je ukozanych $1 zajtůw}} ze cołkij wjelośći $2 zajtůw.}}',
 'category-article-count-limited' => 'W katygoryji {{PLURAL:$1|je pokozano $1 zajta|sům pokozane $1 zajty|je pokazanych $1 zajtůw}}.',
-'category-file-count' => '{{PLURAL:$2|W katygoryji snojduje śe jydyn plik.|W katygoryji {{PLURAL:$1|je pokozany $1 plik|sům pokozane $1 pliki|je pokozanych $1 plikůw}} s cołkyj liczby $2 plikůw.}}',
+'category-file-count' => '{{PLURAL:$2|W katygoryji znojduje śe jydyn plik.|W katygoryji {{PLURAL:$1|je pokozany $1 plik|sům pokozane $1 pliki|je pokozanych $1 plikůw}} ze cołkyj liczby $2 plikůw.}}',
 'category-file-count-limited' => 'W katygoryji {{PLURAL:$1|je pokozany $1 plik|sům pokozane $1 pliki|je pokozanych $1 plikůw}}.',
 'listingcontinuesabbrev' => 'ć.d.',
 'index-category' => 'Indeksowane zajty',
@@ -193,9 +205,10 @@ $messages = array(
 'article' => 'zajta',
 'newwindow' => '(uodwjyro śe we nowym uokńe)',
 'cancel' => 'Uodćepej',
-'moredotdotdot' => 'Wjyncy...',
-'mypage' => 'Moja zajta',
-'mytalk' => 'Mojo dyskusyjo',
+'moredotdotdot' => 'Wjyncyj...',
+'morenotlisted' => 'Ńy je to kůmplytno lista',
+'mypage' => 'Zajta',
+'mytalk' => 'Dyskusyjo',
 'anontalk' => 'Godka tygo IP',
 'navigation' => 'Nawigacyjo',
 'and' => '&#32;a',
@@ -206,7 +219,7 @@ $messages = array(
 'qbedit' => 'Sprowjej',
 'qbpageoptions' => 'Ta zajta',
 'qbmyoptions' => 'Moje zajty',
-'qbspecialpages' => 'Szpecyjalne zajty',
+'qbspecialpages' => 'Szpecyjolne zajty',
 'faq' => 'FAQ',
 'faqpage' => 'Project:FAQ',
 
@@ -217,7 +230,7 @@ $messages = array(
 'vector-action-protect' => 'Zawrzij',
 'vector-action-undelete' => 'Wćep',
 'vector-action-unprotect' => 'Uodymkńij',
-'vector-simplesearch-preference' => 'Włącz zaawansowane podpowiedzi wyszukiwania (tylko dla skórki Wektor)',
+'vector-simplesearch-preference' => 'Używej zaawansowane podpowjedźi sznupańo (ino lo skůrki Wektor)',
 'vector-view-create' => 'Stwůrz',
 'vector-view-edit' => 'Sprowjej',
 'vector-view-history' => 'Uobocz gyszichta',
@@ -227,6 +240,7 @@ $messages = array(
 'namespaces' => 'Raumy mjan',
 'variants' => 'Warjanty',
 
+'navigation-heading' => 'Menu nawigacyji',
 'errorpagetitle' => 'Feler',
 'returnto' => 'Nazod do zajty $1.',
 'tagline' => 'Ze {{GRAMMAR:D.lp|{{SITENAME}}}}',
@@ -238,9 +252,9 @@ $messages = array(
 'history' => 'Gyszichta zajty',
 'history_short' => 'Gyszichta',
 'updatedmarker' => 'pomjyńane uod uostatńij wizyty',
-'printableversion' => 'Wersyjo do durku',
+'printableversion' => 'Wersyjo do druku',
 'permalink' => 'Link do tyj wersyje zajty',
-'print' => 'Durkuj',
+'print' => 'Drukuj',
 'view' => 'Podglůnd',
 'edit' => 'Sprowjej',
 'create' => 'Stwůrz',
@@ -248,17 +262,18 @@ $messages = array(
 'create-this-page' => 'Stwůrz ta zajta',
 'delete' => 'Wyćep',
 'deletethispage' => 'Wyćep ta zajta',
+'undeletethispage' => 'Prziwrůć ta zajta',
 'undelete_short' => 'Wćep nazod {{PLURAL:$1|jedna wersyjo|$1 wersyje|$1 wersyji}}',
 'viewdeleted_short' => '{{PLURAL:$1|jedna wyćepano wersyjo|$1 wyćepane wersyje|$1 wyćepanych wersyjůw}}',
 'protect' => 'Zawrzij',
 'protect_change' => 'půmjyń',
-'protectthispage' => 'Zawřij ta zajta',
+'protectthispage' => 'Zawrzij ta zajta',
 'unprotect' => 'Uodymkńij',
 'unprotectthispage' => 'Uodymkńij ta zajta',
 'newpage' => 'Nowy artikel',
 'talkpage' => 'Godej uo tym artiklu',
 'talkpagelinktext' => 'dyskusyjo',
-'specialpage' => 'Špecyjalno zajta',
+'specialpage' => 'Szpecyjolno zajta',
 'personaltools' => 'Perzůnolne',
 'postcomment' => 'Skůmyntuj',
 'articlepage' => 'Zajta artikla',
@@ -267,14 +282,14 @@ $messages = array(
 'toolbox' => 'Werkcojg',
 'userpage' => 'Zajta sprowjorza',
 'projectpage' => 'Zajta projekta',
-'imagepage' => 'Zobejrz zajte pliku',
+'imagepage' => 'Uobejrz zajta pliku',
 'mediawikipage' => 'Zajta komuńikata',
-'templatepage' => 'Zajta šablůna',
-'viewhelppage' => 'Zajta pomocy',
+'templatepage' => 'Zajta mustra',
+'viewhelppage' => 'Zajta půmocy',
 'categorypage' => 'Zajta katygoryji',
 'viewtalkpage' => 'Zajta godki',
 'otherlanguages' => 'We inkszych godkach',
-'redirectedfrom' => '(Punkńyńto s $1)',
+'redirectedfrom' => '(Punkńyńto ze $1)',
 'redirectpagesub' => 'Zajta przekerowujůnco',
 'lastmodifiedat' => 'Ta zajta bůła uostatńo sprowjano $2, $1.',
 'viewcount' => 'W ta zajta filowano {{PLURAL:$1|tylko roz|$1 rozůw}}.',
@@ -282,17 +297,17 @@ $messages = array(
 'jumpto' => 'Przyńdź do:',
 'jumptonavigation' => 'nawigacyje',
 'jumptosearch' => 'sznupańo',
-'view-pool-error' => 'Felerńe, syrwyry sům przecionżone.
+'view-pool-error' => 'Felerńe, syrwyry sům przećůnżone.
 
 $1',
-'pool-timeout' => 'Zbyt długi czas oczekiwania na blokadę',
-'pool-queuefull' => 'Kolejka zadań jest pełna',
-'pool-errorunknown' => 'Feler ńyznany',
+'pool-timeout' => 'Za dugi czas uoczekiwańo na blokada',
+'pool-queuefull' => 'Kolyjność zadań je pełno',
+'pool-errorunknown' => 'Feler ńyznony',
 
 # All link text and link target definitions of links into project namespace that get used by other message strings, with the exception of user group pages (see grouppage).
 'aboutsite' => 'Uo {{GRAMMAR:MS.lp|{{SITENAME}}}}',
 'aboutpage' => 'Project:Uo serwiśe',
-'copyright' => 'Tekst udostympńany na licencyji $1.',
+'copyright' => 'Tekst udostympńany na licencyji $1, eli inakszyj ńy podano.',
 'copyrightpage' => '{{ns:project}}:Autorske prawa',
 'currentevents' => 'Aktualne przitrefjyńa',
 'currentevents-url' => 'Project:Aktualne przitrefjyńa',
@@ -302,24 +317,28 @@ $1',
 'helppage' => 'Help:Treść',
 'mainpage' => 'Przodńo zajta',
 'mainpage-description' => 'Przodńo zajta',
-'policy-url' => 'Project:Prawidua',
+'policy-url' => 'Project:Prawidła',
 'portal' => 'Portal używoczůw',
 'portal-url' => 'Project:Portal używoczůw',
 'privacy' => 'Prawidła chrůńyńo prywotnośći',
 'privacypage' => 'Project:Prawidła chrůńyńo prywotnośći',
 
 'badaccess' => 'Felerne uprawńyńo',
-'badaccess-group0' => 'Ńy moš uprawńyń coby wykůnać ta uoperacyjo.',
-'badaccess-groups' => 'Ta uoperacyjo mogům wykůnaÄ\87 ino užytkownicy s keryjś z grup {{PLURAL:$2|grupa|grupy}}:$1.',
+'badaccess-group0' => 'Ńy mosz uprawńyń coby wykůnać ta uoperacyjo.',
+'badaccess-groups' => 'Ta uoperacyjo mogům wykůnaÄ\87 ino użytkownicy ze keryjś z grup {{PLURAL:$2|grupa|grupy}}:$1.',
 
 'versionrequired' => 'Wymagano MediaWiki we wersyji $1',
-'versionrequiredtext' => 'Wymagano jest MediaWiki we wersji $1 coby skořistać s tyj zajty. Uoboč [[Special:Version]]',
+'versionrequiredtext' => 'Wymagano jest MediaWiki we wersji $1 coby skorzistać zr tyj zajty. Uobocz [[Special:Version]]',
 
 'ok' => 'OK',
 'retrievedfrom' => 'Zdrzůdło "$1"',
 'youhavenewmessages' => 'Mosz $1 ($2).',
 'newmessageslink' => 'nowe powjadůmjyńa',
 'newmessagesdifflink' => 'uostatńe pomjyńyńy',
+'youhavenewmessagesfromusers' => 'Mosz $1 uod {{PLURAL:$3|inszygo używocza|$3 używoczy}} ($2).',
+'youhavenewmessagesmanyusers' => 'Mosz $1 uod wjelu używoczy ($2).',
+'newmessageslinkplural' => '{{PLURAL:$1|jydno nowina|nowiny}}',
+'newmessagesdifflinkplural' => '{{PLURAL:$1|uostatńe sprowjyńe|uostatńe sprowjyńa}}',
 'youhavenewmessagesmulti' => 'Mosz nowe powjadůmjyńa: $1',
 'editsection' => 'Sprowjej',
 'editold' => 'sprowjej',
@@ -328,28 +347,28 @@ $1',
 'viewsourcelink' => 'zdrzůdłowy tekst',
 'editsectionhint' => 'Sprowjej tajla: $1',
 'toc' => 'Treść',
-'showtoc' => 'pokož',
+'showtoc' => 'uobejrzij',
 'hidetoc' => 'schrůń',
 'collapsible-collapse' => 'Zwjyń',
 'collapsible-expand' => 'Rozwjyń',
-'thisisdeleted' => 'Pokož/wćepej nazod $1',
-'viewdeleted' => 'Uobejřij $1',
+'thisisdeleted' => 'Pokoż/wćepej nazod $1',
+'viewdeleted' => 'Uobejrzij $1',
 'restorelink' => '{{PLURAL:$1|jedna wyćepano wersyjo|$1 wyćepane wersyje|$1 wyćepanych wersyjůw}}',
-'feedlinks' => 'Kanauy:',
-'feed-invalid' => 'Ńywuaściwy typ kanauů informacyjnygo.',
-'feed-unavailable' => 'Kanouy informacyjne ńy sům dostympne',
-'site-rss-feed' => 'Kanau RSS {{GRAMMAR:D.lp|$1}}',
+'feedlinks' => 'Kanały:',
+'feed-invalid' => 'Ńywłaściwy typ kanałů informacyjnygo.',
+'feed-unavailable' => 'Kanoły informacyjne ńy sům dostympne',
+'site-rss-feed' => 'Kan RSS {{GRAMMAR:D.lp|$1}}',
 'site-atom-feed' => 'Kanoł Atom {{GRAMMAR:D.lp|$1}}',
-'page-rss-feed' => 'Kanau RSS "$1"',
+'page-rss-feed' => 'Kan RSS "$1"',
 'page-atom-feed' => 'Kanoł Atom "$1"',
 'red-link-title' => '$1 (ńy mo zajty)',
-'sort-descending' => 'Sortuj malejąco',
-'sort-ascending' => 'Sortuj rosnąco',
+'sort-descending' => 'Sortuj pomńijszajůnco',
+'sort-ascending' => 'Sortuj rosnůnco',
 
 # Short words for each namespace, by default used in the namespace tab in monobook
 'nstab-main' => 'Zajta',
 'nstab-user' => '{{GENDER:{{BASEPAGENAME}}|Zajta używocza|Zajta używoczki}}',
-'nstab-media' => 'Medja',
+'nstab-media' => 'Pliki',
 'nstab-special' => 'Ekstra zajta',
 'nstab-project' => 'Zajta projektu',
 'nstab-image' => 'Plik',
@@ -361,21 +380,26 @@ $1',
 # Main script and global functions
 'nosuchaction' => 'Ńy mo takij uoperacyji',
 'nosuchactiontext' => 'Uoprogramowańy ńy rozpoznowo uoperacyji takij kej podano w URL.',
-'nosuchspecialpage' => 'Ńy mo takij špecyjalnyj zajty',
-'nospecialpagetext' => '<strong>Uoprogramowańy ńy rozpoznowo takij špecyjalnyj zajty.</strong>
+'nosuchspecialpage' => 'Ńy mo takij szpecyjolnyj zajty',
+'nospecialpagetext' => '<strong>Uoprogramowańy ńy rozpoznowo takij szpecyjalnyj zajty.</strong>
 
-Lista špecyjalnych zajtůw znejdźeš na [[Special:SpecialPages|{{int:specialpages}}]].',
+Lista szpecyjalnych zajtůw znojdźesz na [[Special:SpecialPages|{{int:specialpages}}]].',
 
 # General errors
 'error' => 'Feler',
 'databaseerror' => 'Feler bazy danych',
-'laggedslavemode' => 'Dej pozůr: Ta zajta može ńy mjeć nojnowšych aktualizacyjůw.',
+'databaseerror-text' => 'Pojawjůł śe feler przi wysyłańu zapytańa do bazy danych. Mogebność je, aże je to feler we uoprogramowańu.',
+'databaseerror-textcl' => 'Pojawjůł śe feler przi wysyłańu zapytańa do bazy danych.',
+'databaseerror-query' => 'Zapytańe: $1',
+'databaseerror-function' => 'Funkcyjo: $1',
+'databaseerror-error' => 'Feler: $1',
+'laggedslavemode' => 'Dej pozůr: Ta zajta może ńy mjeć nojnowszych aktualizacyjůw.',
 'readonly' => 'Baza danych je zawarto',
-'enterlockreason' => 'Naškryflej sam powůd zawarća bazy danych a za wjela (myńi-wjyncyj) ja uodymkńeš',
+'enterlockreason' => 'Naszkryflej sam powůd zawarćo bazy danych a za wjela (myńi-wjyncyj) ja uodymkńesz',
 'readonlytext' => 'Baza danych jest terozki zawarto
-- Å\84y do Å\9be wÄ\87epywaÄ\87 nowych artikli Å\84i sprowjaÄ\87 juž wćepanych. Powodym
-sům prawdopodańy čynnośći admińistracyjne. Po jejich zakůńčeńu pouno funkcjonalność bazy bydźe přywrůcono.
-Administrator, kery zablokowou baza, podou takie wyjaśńyńy:<br /> $1',
+- Å\84y do Å\9be wÄ\87epywaÄ\87 nowych artikli Å\84i sprowjaÄ\87 już wćepanych. Powodym
+sům prawdopodańy czynnośći admińistracyjne. Po jejich zakůńczyńu cołko funkcjonalność bazy bydźe prziwrůcono.
+Administrator, kery zablokowoł baza, podoł takie wyjaśńyńy:<br /> $1',
 'missing-article' => 'W databaźe ńy idzie nolyźć treść zajty „$1” $2.
 
 Uobwykle je to spůsobiůne tym, że sznupoł żeś po ńyaktualnym linku na zmjyny mjyndzy půmjyńańami, abo do wyćepanyj wersyje z gyszichty sprowjyń zajty.
@@ -383,51 +407,56 @@ Uobwykle je to spůsobiůne tym, że sznupoł żeś po ńyaktualnym linku na zmj
 Eli tak ńy je, możno śe trefił feler we softwaru MediaWiki. Kej ja, pedz uo tym [[Special:ListUsers/sysop|admińistratorowi]] a podej mu adres URL.',
 'missingarticle-rev' => '(wersyjo#: $1)',
 'missingarticle-diff' => '(dyferencyjo: $1, $2)',
-'readonly_lag' => 'Baza danych zostoua automatyčńy zawarto na čas potřebny na synchrońizacyjo zmjan mjyndzy serwerym guůwnym a serwerami postředńičůncymi.',
-'internalerror' => 'Wewnyntřny feler',
-'internalerror_info' => 'Wewnytřny feler: $1',
+'readonly_lag' => 'Baza danych zostoła automatyczńy zawarto na czas potrzebny na synchrońizacyjo zmjan mjyndzy serwerym głůwnym a serwerami postrzedńiczůncymi.',
+'internalerror' => 'Wewnyntrzny feler',
+'internalerror_info' => 'Wewnytrzny feler: $1',
 'fileappenderrorread' => 'Feler uodczytu "$1".',
 'fileappenderror' => 'Ńy idźe skopjować plika "$1" do "$2".',
 'filecopyerror' => 'Ńy idźe skopjować plika "$1" do "$2".',
 'filerenameerror' => 'Ńy idźe zmjyńić mjana plika "$1" na "$2".',
 'filedeleteerror' => 'Ńy idźe wyćepać plika "$1".',
-'directorycreateerror' => 'Ńy idźe utwořić katalogu "$1".',
+'directorycreateerror' => 'Ńy idźe utworzić katalogu "$1".',
 'filenotfound' => 'Ńy idźe znejść plika "$1".',
 'fileexistserror' => 'Ńy idźe sprowjać we pliku "$1": plik istńeje',
 'unexpected' => 'Ńyspodźewano wartość: "$1"="$2".',
-'formerror' => 'Feler: ńy idźe wysuać formulařa',
+'formerror' => 'Feler: ńy idźe wysłać formulazra',
 'badarticleerror' => 'Tyj uoperacyje ńy idźe zrobić lo tyj zajty.',
 'cannotdelete' => 'Ńy idźe wyćepać podanyj zajty abo grafiki $1.',
 'cannotdelete-title' => 'Ńy idźie wyćepać zajty "$1".',
-'badtitle' => 'Felerno tytůua',
-'badtitletext' => 'Podano felerny titel zajty. Prawdopodańy sům w ńim znoki, kerych ńy wolno užywać we titlach abo je pusty.',
-'perfcached' => 'To co sam je naszkryflane, to ino kopja s pamjyńći podryncznyj a może ńy być aktualne. Nojwjyncyj {{PLURAL:$1|jydyn wynik je|$1 wyniki sům}} w tyj pamjyńći.',
+'delete-hook-aborted' => 'Wyćepywańe sztopńynte bez hak. Przyczyna ńyuokreślůno.',
+'badtitle' => 'Felerny titel',
+'badtitletext' => 'Podano felerny titel zajty. Prawdopodańy sům w ńim znoki, kerych ńy wolno używać we titlach abo je pusty.',
+'perfcached' => 'To co sam je naszkryflane, to ino kopja ze pamjyńći podryncznyj a może ńy być aktualne. Nojwjyncyj {{PLURAL:$1|jydyn wynik je|$1 wyniki sům}} we tyj pamjyńći.',
 'perfcachedts' => 'To co sam je naszkryflane, to ino kopja s pamjyńći podryncznyj a bůło uaktualńůne $1. Nojwjyncyj {{PLURAL:$4|jeden wynik je|$4 wyniki sům}} dostympne.',
-'querypage-no-updates' => 'UaktualÅ\84\84o lo tyj zajty sům terozki zawarte. Dane, kere sam sům, Å\84y zostouy uodÅ\9bwjyžůne.',
-'wrong_wfQuery_params' => 'Felerne parametry překozane do wfQuery()<br />
+'querypage-no-updates' => 'UaktualÅ\84\84o lo tyj zajty sům terozki zawarte. Dane, kere sam sům, Å\84y zostouy uodÅ\9bwjyżůne.',
+'wrong_wfQuery_params' => 'Felerne parametry przekozane do wfQuery()<br />
 Funkcyjo: $1<br />
 Zapytańy: $2',
 'viewsource' => 'Zdrzůdłowy tekst',
 'viewsource-title' => 'Uobocz zdrzůdło lo $1',
-'actionthrottled' => 'Akcyjo wstřimano',
-'actionthrottledtext' => 'Mechańizm uobrůny před spamym uograńičo ličba wykonań tyj čynnośći we jednostce času. Průbowoužeś go uocygańić. Proša, sprůbuj na nowo za pora minut.',
+'actionthrottled' => 'Akcyjo wstrzimano',
+'actionthrottledtext' => 'Mechańizm uobrůny przed spamym uograńiczo liczba wykonań tyj czynnośći we jednostce czasu. Průbowołżeś go uocygańić. Prosza, sprůbuj na nowo za pora minut.',
 'protectedpagetext' => 'Ta zajta je zawarto przed sprowjańym.',
 'viewsourcetext' => 'We tekst zdrzůduowy tyj zajty możno dali filować, idźe go tyż kopjyrować.',
 'viewyourtext' => 'We tekst zdrzůduowy tyj zajty możno dali filować, idźe go tyż kopjować.',
-'protectedinterface' => 'Na tyj zajće znojduje śe tekst interfejsu uoprogramowańo, bestož uůna je zawarto uod sprowjańo.',
-'editinginterface' => "''''Dej pozůr:''' Sprowjosz zajta, na keryj je tekst interfejsu uoprogramowańo. Pomjyńyńa na tyj zajće zmjyńům wyglůnd interfejsu lo inkšych užytkowńikůw.",
-'cascadeprotected' => 'Ta zajta je zawarto uod sprowjańo, po takymu, co uůna je zauončono na {{PLURAL:$1|nastympujůncyj zajće, kero zostaua zawarto|nastympujůncych zajtach, kere zostauy zawarte}} ze zauončonům opcyjům dźedźičyńo:
+'protectedinterface' => 'Na tyj zajće znojduje śe tekst interfejsu uoprogramowańo, bestůż uůna je zawarto uod sprowjańo. Coby doćepnůńć abo sprowjić tůmaczyńa wszyskich serwerůw, użyj [//translatewiki.net/ translatewiki.net], průjyktu lokalizacyji MediaWiki.',
+'editinginterface' => "''''Dej pozůr:''' Sprowjosz zajta, na keryj je tekst interfejsu uoprogramowańo. Pomjyńyńa na tyj zajće zmjyńům wyglůnd interfejsu lo inkszych użytkowńikůw. Coby doćepnůńć abo sprowjić tůmaczyńa, użyj [//translatewiki.net/wiki/Main_Page?setlang=szl translatewiki.net].",
+'cascadeprotected' => 'Ta zajta je zawarto uod sprowjańo, po takymu, co uůna je załůnczůno na {{PLURAL:$1|nastympujůncyj zajće, kero zostoła zawarto|nastympujůncych zajtach, kere zostoły zawarte}} ze załůnczůnům uopcyjům dźedźiczyńo:
 $2',
-'namespaceprotected' => "Ńy moš uprowńyń, coby sprowjać zajty we přestřeńi mjan '''$1'''.",
+'namespaceprotected' => "Ńy mosz uprowńyń, coby sprowjać zajty we raumje mjan '''$1'''.",
 'customcssprotected' => 'Ńy mosz uprawńyń do sprowjańo tyj zajty, bo na ńij sům uosobiste sztalowańo inkszego użytkowńika.',
 'customjsprotected' => 'Ńy mosz uprawńyń do sprowjańo tyj zajty, bo na ńij sům uosobiste sztalowańo inkszego użytkowńika.',
-'ns-specialprotected' => 'Ńy idźe sprowjać zajtůw we přestřyni mjan {{ns:special}}.',
+'mycustomcssprotected' => 'Ńy mosz uprawńyń do sprowjańo tyj zajty CSS.',
+'mycustomjsprotected' => 'Ńy mosz uprawńyń do sprowjańo tyj zajty JavaScript.',
+'ns-specialprotected' => 'Ńy idźe sprowjać zajtůw we przestrzyni mjan {{ns:special}}.',
 'titleprotected' => "Wćepańy sam zajty uo takim mjańe zawar [[User:$1|$1]].
 Powůd zawarćo: ''$2''.",
 'filereadonlyerror' => 'Ńy idźe pomjyńać plika "$1" abo repozytorjum "$2" terozki je zawarte.
 
 Administrator kery zawarł wćepał kůmyntorz: "$3".',
 'invalidtitle-knownnamespace' => 'Felerne mjano "$3" w przestrzeńy "$2".',
+'exception-nologin' => 'Ńy jest żeś zalogůwany',
+'exception-nologin-text' => 'Ta zajta abo akcyja wymogo byćo zalogůwanym na tyj wiki.',
 
 # Virus scanner
 'virus-badscanner' => "Felerno konfiguracyjo – ńyznany skaner antywirusowy ''$1''",
@@ -435,104 +464,143 @@ Administrator kery zawarł wćepał kůmyntorz: "$3".',
 'virus-unknownscanner' => 'ńyznajůmy průgram antywirusowy',
 
 # Login and logout pages
-'logouttext' => "'''Terozki ježeś wylůgowany'''.
+'logouttext' => "'''Terozki jeżeś wylůgowany'''.
 
-Možeš dali sam sprowjać zajty we {{SITENAME}} kej ńyzalůgowany užytkowńik, abo <span class='plainlinks'>[$1 zalůgować śe nazod]</span> kej tyn som abo inkšy užytkowńik.
-Dej pozůr, co na ńykerych zajtach přeglůndarka može dali pokozywać co ježeś zalůgowany, a bydźe tak aže uodśwjyžyš jeij cache.",
+Dej pozůr, co na ńykerych zajtach przeglůndarka może dali pokozywać co jeżeś zalůgowany, a bydźe tak aże uodśwjyżysz jeij cache.",
+'welcomeuser' => 'Witej, $1',
+'welcomecreation-msg' => 'Uotwarli my sam lo Ćebje kůnto.
+Pamjyntej coby posztalować [[Special:Preferences|preferencyji]]',
 'yourname' => 'Mjano użytkowńika:',
+'userlogin-yourname' => 'Mjano używocza',
+'userlogin-yourname-ph' => 'Wszkryflej swoje mjano użytkowńika',
+'createacct-another-username-ph' => 'Wszkryflej mjano użytkowńika',
 'yourpassword' => 'Hasło:',
+'userlogin-yourpassword-ph' => 'Wszkryflej swoje hasło',
+'createacct-yourpassword-ph' => 'Wszkryflej hasło',
 'yourpasswordagain' => 'Naszkryflej ausdruk zaś',
+'createacct-yourpasswordagain' => 'Potwjyrdź hasło',
+'createacct-yourpasswordagain-ph' => 'Wszkryflej hasło jeszcze roz',
 'remembermypassword' => 'Pamjyntej můj ausdruk na tym kůmputrze (nojdalij bez $1 {{PLURAL:$1|dźyń|dńůw}})',
+'userlogin-remembermypassword' => 'Ńy wylogůwywuj mje',
+'userlogin-signwithsecure' => 'Użyj bezpjecznygo połůnczyńa',
 'yourdomainname' => 'Twoja domyna',
-'externaldberror' => 'Je jaki feler we zewnyntřnyj baźe autentyfikacyjnyj, abo ńy moš uprawńyń potřebnych do aktualizacyji zewnyntřnego kůnta.',
+'password-change-forbidden' => 'Ńy można půmjyńać haseł na tyj wiki.',
+'externaldberror' => 'Je jaki feler we zewnyntrznyj baźe autentyfikacyjnyj, abo ńy mosz uprawńyń potrzebnych do aktualizacyji zewnyntrznego kůnta.',
 'login' => 'Zaloguj śe',
-'nav-login-createaccount' => 'Logowańy / tworzińy kůnta',
-'loginprompt' => 'Muśisz mjeć zołůnczůne cookies coby můc śe sam zalůgować.',
-'userlogin' => 'Lůgowańy / Tworzyńy kůnta',
-'userloginnocreate' => 'Zalůguj śe',
+'nav-login-createaccount' => 'Logowańy / Tworzyńy kůnta',
+'loginprompt' => 'Muśisz mjeć załůnczůne cookies coby můc śe sam zalogować.',
+'userlogin' => 'Logowańy / Tworzyńy kůnta',
+'userloginnocreate' => 'Zaloguj śe',
 'logout' => 'Wyloguj',
 'userlogout' => 'Uodloguj śe',
-'notloggedin' => 'Ńy ježeś zalůgowany',
+'notloggedin' => 'Ńy jeżeś zalogowany',
+'userlogin-noaccount' => 'Ńy mosz kůnta?',
+'userlogin-joinproject' => 'Doćep śe do {{SITENAME}}',
 'nologin' => "Ńy mosz kůnta? '''$1'''.",
 'nologinlink' => 'Twůrz kůnto',
 'createaccount' => 'Twůrz nowe kůnto',
 'gotaccount' => "Mosz już kůnto? '''$1'''.",
 'gotaccountlink' => 'Naloguj śe',
 'userlogin-resetlink' => 'Zapomńoł żeś dane lo nalogowańo?',
-'createaccountmail' => 'e-brifym',
+'userlogin-resetpassword-link' => 'Ńy pamjyntosz hasła?',
+'helplogin-url' => 'Help:Logowańy',
+'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|Hilfa ze logowańym]]',
+'userlogin-loggedin' => 'Zalogowano kej {{GENDER:$1|$1}}. Użyj formulara půńiżyj, coby zalogować śe kej inkszy używocz.',
+'userlogin-createanother' => 'Twůrz inksze kůnto',
+'createacct-join' => 'Wszkryflej půńiżyj swoje dane.',
+'createacct-another-join' => 'Wszkryflej půńiżyj szczegůły nowygo kůnta.',
+'createacct-emailrequired' => 'E-brif',
+'createacct-emailoptional' => 'E-brif (uopcjůnalne)',
+'createacct-email-ph' => 'Wszkryflej swůj adres do e-brifa',
+'createacct-another-email-ph' => 'Nastow e-brif',
+'createaccountmail' => 'Użyj chwilowygo hasła losowo genyrowanygo a wyślij je na wrychtowany adres e-brifa.',
+'createacct-realname' => 'Prawdźiwe imje a nazwisko (uopcjůnalńe)',
 'createaccountreason' => 'Kůmyntorz:',
-'badretype' => 'Hasua kere žeś naškryflou ńy zgodzajům śe jydne s drugim.',
+'createacct-reason' => 'Powůd:',
+'createacct-reason-ph' => 'Pojakymu tworzisz nowe kůnta',
+'createacct-captcha' => 'Zicherkontrola',
+'createacct-imgcaptcha-ph' => 'Wszkryflej tekst, kery widoć powyżyj',
+'createacct-submit' => 'Twůrz kůnto',
+'createacct-another-submit' => 'Twůrz inksze kůnto',
+'createacct-benefit-heading' => '{{grammar:B.lp|{{SITENAME}}}} tworzům perzůny take kej Ty.',
+'createacct-benefit-body1' => '{{PLURAL:$1|edycyjo|edycyje|edycyji}}',
+'createacct-benefit-body2' => '{{PLURAL:$1|zajta|zajty|zajt}}',
+'createacct-benefit-body3' => '{{PLURAL:$1|używocz|używoczůw}} we uostatńim czaśe',
+'badretype' => 'Hasła kere żeś naszkryfloł ńy zgodzajům śe jydne ze drugim.',
 'userexists' => 'Mjano użytkowńika, kere żeś wybroł, je zajynte. Wybjer, prosza, inksze mjano.',
-'loginerror' => 'Feler při logůwańu',
+'loginerror' => 'Feler przi logowańu',
+'createacct-error' => 'Feler tworzyńo kůnta',
 'createaccounterror' => 'Ńy możno stworzić konta $1',
 'nocookiesnew' => 'Kůnto użytkowńika zostoło utworzůne, nale ńy jeżeś zalůgowany. {{SITENAME}} używo ćosteczek do logůwańo. Mosz wyłůnczone ćosteczka. Coby śe zalůgować, uodymknij ćosteczka a podej mjano a hasło swojigo kůnta.',
-'nocookieslogin' => '{{SITENAME}} užywo Ä\87osteÄ\8dek do lůgowaÅ\84o užytkowÅ\84ikůw. MoÅ¡ zablokowano jejich uobsuůga. Sprůbuj zaÅ\9b jak zauůnÄ\8dyÅ¡ uobsuůga Ä\87osteÄ\8dek.',
-'nocookiesfornew' => 'Konto sprowjorza ńy uostoło stworzone. Sprawdź, cze mosz uodymkńynto obsługe cookies.',
-'noname' => 'To ńy je půprowne mjano użytkowńika.',
-'loginsuccesstitle' => 'Lůgowańy udane',
-'loginsuccess' => "'''Terozki ježeÅ\9b zalůgowany do {{SITENAME}} jako \"\$1\".'''",
+'nocookieslogin' => '{{SITENAME}} używo Ä\87osteczek do logowaÅ\84o użytkowÅ\84ikůw. Mosz zablokowano jejich uobsÅ\82ůga. Sprůbuj zaÅ\9b kej zaÅ\82ůnczysz uobsÅ\82ůga Ä\87osteczek.',
+'nocookiesfornew' => 'Konto sprowjorza ńy uostoło stworzone. Sprawdź, co mosz uodymkńynto uobsługa cookies.',
+'noname' => 'To ńy je poprowne mjano użytkowńika.',
+'loginsuccesstitle' => 'Logowańy udane',
+'loginsuccess' => "'''Terozki jeżeÅ\9b zalogowany do {{SITENAME}} kej \"\$1\".'''",
 'nosuchuser' => 'Ńy ma sam użytkowńika uo mjańe "$1".
 Sprowdź szrajbůng, abo [[Special:UserLogin/signup|utwůrz nowe kůnto]].',
-'nosuchusershort' => 'Å\83y mo sam užytkowńika uo mjańe "$1".',
+'nosuchusershort' => 'Å\83y mo sam użytkowńika uo mjańe "$1".',
 'nouserspecified' => 'Podej mjano użytkowńika.',
-'login-userblocked' => 'Tyn sprowjorz ma zawrzite sprowjyńa. Ńy możno sie zalgować.',
+'login-userblocked' => 'Tyn sprowjorz ma zawrzite sprowjyńa. Ńy możno sie zalogować.',
 'wrongpassword' => 'Hasło kere żeś naszkryfloł je felerne. Poprůbůj naszkryflać je jeszcze roz.',
-'wrongpasswordempty' => 'Hasuo kere žeś podou je puste. Naškryflej je ješče roz.',
+'wrongpasswordempty' => 'Hasło kere żeś podou je uostawjůne blank. Naszkryflej je jeszcze roz.',
 'passwordtooshort' => 'Hasło kere żeś podoł je felerne abo za krůtke.
-Hasło muśi mjeć przinojmńij {{PLURAL:$1|1 buchsztaba|$1 buchsztabůw}} a być inksze od mjana użytkowńika.',
-'password-name-match' => 'Hasło musi być inne niż nazwa użytkownika.',
-'password-login-forbidden' => 'Ńy wolno mjyć takij nazwy a hasua.',
+Hasło muśi mjeć przinojmńij {{PLURAL:$1|1 buchsztaba|$1 buchsztabůw}} a być inksze uod mjana użytkowńika.',
+'password-name-match' => 'Hasło mo być inksze atoli mjano używocza.',
+'password-login-forbidden' => 'Mogebność wyboru tygo mjana używocza abo hasła je zawarte.',
 'mailmypassword' => 'Wyślij mi nowe hasło bez e-brif',
-'passwordremindertitle' => 'Nowe tymčasowe hasuo dla {{SITENAME}}',
-'passwordremindertext' => 'Ftůś (cheba Ty, s IP $1)
+'passwordremindertitle' => 'Nowe tymczasowe hasło lo {{SITENAME}}',
+'passwordremindertext' => 'Ftoś (cheba Ty, ze IP $1)
 pado, aże chce nowe hasło do {{SITENAME}} ($4).
 Lo użytkowńika "$2" wygenyrowano nowe hasło a je ńim "$3".
 Jak chćołżeś gynał to zrobjyć, to zalůgůj śe terozki a podej swoje hasło.
 Hasło wygaśnie za {{PLURAL:$5|1 dzień|$5 dni}}.
 
-Jak ktůś inkszy chćoł nowe hasło abo jak Ci śe przipůmńouo stare a ńy chcesz nowygo, to zignoruj to a używej starygo hasła.',
+Jak ftoś inkszy chćoł nowe hasło abo jak Ci śe przipůmńouo stare a ńy chcesz nowygo, to zignoruj to a używej starygo hasła.',
 'noemail' => 'Ńy mo u nos adresu e-brifa do "$1".',
-'noemailcreate' => 'Podaj dobry e-mail ausdruk',
-'passwordsent' => 'Nowe hasuo pošuo na e-brifa uod užytkowńika "$1".
-Zalůguj śe zaś jak dostańyš tygo brifa.',
-'blocked-mailpassword' => 'Twůj adres IP zostou zawarty a ńy možeš užywać funkcyje odzyskiwańo hasua skuli možliwośći jeji nadužywańo.',
+'noemailcreate' => 'Podej dobry e-mail ausdruk',
+'passwordsent' => 'Nowe hasło polozło na e-brifa uod użytkowńika "$1".
+Zaloguj śe zaś jak dostańysz tygo brifa.',
+'blocked-mailpassword' => 'Twůj adres IP zostoł zawarty a ńy moższ używać funkcyje uodzyskiwańo hasła skuli mogebnośći jeji nadużywańo.',
 'eauthentsent' => 'Potwjerdzeńy zostoło posłane na e-brifa.
-Jak bydźesz chćoł, coby wysyłouo Ći e-brify, pjyrwyj go przeczytej. Bydźesz tam mjoł instrukcyjo co mosz zrobić, coby pokozać, aże tyn ausdruk je Twůj.',
-'throttled-mailpassword' => 'Připůmńyńy hasua bůuo juž wysuane bez {{PLURAL:$1|uostatńo godźina|uostatńe $1 godźin}}.
-Coby powstřimać nadužyća, možliwość wysyuańa připůmńeń naštalowano na jydne bez {{PLURAL:$1|godźina|$1 godźiny}}.',
-'mailerror' => 'Při wysyuańu e-brifa zdořiu śe feler: $1',
-'acct_creation_throttle_hit' => 'Przikro nom, założył(a)żeś już {{PLURAL:$1|1 kůnto|$1 kůnta}}. Ńy możesz założyć kolejnygo.',
-'emailauthenticated' => 'Twůj adres e-brifa zostou uwjeřitelńůny $2 uo $3.',
-'emailnotauthenticated' => 'Twůj adres e-brifa ńy je uwjeřitelńůny. Půnižše funkcyje počty ńy bydům dźauać.',
-'noemailprefs' => 'Muśiš podać adres e-brifa, coby te funkcyje dźouauy.',
-'emailconfirmlink' => 'Potwjerdź swůj adres e-brifa',
-'invalidemailaddress' => 'E-brif Å\84y bydźe zaakceptůwany skiž tygo co jego format Å\84y speuÅ\84o formalnych wymagaÅ\84. ProÅ¡a naÅ¡kryflaÄ\87 poprowny adres e-brifa abo wyÄ\8dyśćić pole.',
+Jak bydźesz chćoł, coby wysyłoło Ći e-brify, pjyrwyj go przeczytej. Bydźesz tam mjoł instrukcyjo co mosz zrobić, coby pokozać, aże tyn ausdruk je Twůj.',
+'throttled-mailpassword' => 'Przipůmńyńy hasła bůło już wysłane bez {{PLURAL:$1|uostatńo godźina|uostatńe $1 godźin}}.
+Coby powstrzimać nadużyća, mogebność wysyłańo przipůmńyń nasztalowano na jydne bez {{PLURAL:$1|godźina|$1 godźiny}}.',
+'mailerror' => 'Przi wysyłańu e-brifa zdorził śe feler: $1',
+'acct_creation_throttle_hit' => 'Przikro nom, założůł(a)żeś już {{PLURAL:$1|1 kůnto|$1 kůnta}}. Ńy możesz założyć kolejnygo.',
+'emailauthenticated' => 'Twůj adres e-brifa zostoł uwjerzitelńůny $2 uo $3.',
+'emailnotauthenticated' => 'Twůj adres e-brifa ńy je uwjerzitelńůny. Půniższe funkcyje poczty ńy bydům dźołać.',
+'noemailprefs' => 'Muśisz podać adres e-brifa, coby te funkcyje dźołały.',
+'emailconfirmlink' => 'Potwjyrdź swůj adres e-brifa',
+'invalidemailaddress' => 'E-brif Å\84y bydźe zaakceptůwany skiż tygo co jigo format Å\84y speÅ\82Å\84o formalnych wymagaÅ\84. Prosza naszkryflaÄ\87 poprowny adres e-brifa abo wyczyśćić pole.',
 'cannotchangeemail' => 'Ńy możno pomjyńyc ausdruku e-mail.',
 'emaildisabled' => 'Ta zajta ńy je mogebna posyłać e-brify.',
-'accountcreated' => 'Utwůřůno kůnto',
-'accountcreatedtext' => 'Kůnto lo $1 zostouo utwůřůne.',
-'createaccount-title' => 'Stwořyńy kůnta na {{GRAMMAR:MS.lp|{{SITENAME}}}}',
-'createaccount-text' => 'Ktoś utworził na {{GRAMMAR:MS.lp|{{SITENAME}}}} ($4) dla Twojego adresa e-brif kůnto "$2". Aktualne hasło to "$3". Powińeżeś śe terozki zalogůwać a je zmjyńić.',
+'accountcreated' => 'Utwůrzůno kůnto',
+'accountcreatedtext' => 'Kůnto lo [[{{ns:User}}:$1|$1]] ([[{{ns:User talk}}:$1|dyskusyjo]]) je utwůrzůne.',
+'createaccount-title' => 'Stworzyńy kůnta na {{GRAMMAR:MS.lp|{{SITENAME}}}}',
+'createaccount-text' => 'Ftoś utworził na {{GRAMMAR:MS.lp|{{SITENAME}}}} ($4) lo Twojigo adresa e-brif kůnto "$2". Aktuelne hasło to "$3". Powińeżeś śe terozki zalogůwać a je zmjyńić.',
 'usernamehasherror' => 'Nazwa sprowjorza ńy może mjyć buchsztaby "#".',
-'login-throttled' => '!Wykonołżeś za wjela průb zalůgowańo śe na te kůnto. Poczekej chwila ńym zaś sprůbujesz.',
+'login-throttled' => 'Wykonołżeś za wjela průb zalůgowańo śe na te kůnto. Uodczekej $1 ńym zaś sprůbujesz.',
 'login-abort-generic' => 'Felerne logowańe',
 'loginlanguagelabel' => 'Godka: $1',
-'suspicious-userlogout' => 'Żądanie wylogowania zostało odrzucone ponieważ wygląda na to, że zostało wysłane przez uszkodzoną przeglądarkę lub buforujący serwer proxy.',
+'suspicious-userlogout' => 'Polecyńe wylogowańo uostoło uodćepńynte skiż tygo co wyglůnda, aże uostoło posłane bez uszkodzůna przeglůndarka abo buforujůncy serwer proxy.',
+'createacct-another-realname-tip' => 'Wszkryflańy twojigo mjana a nazwiska ńy je końyczne.
+Kej bydźesz chćoł je podoć, bydům użyte, coby dokůmyntowoć Twoje autorstwo.',
 
 # Email sending
 'php-mail-error-unknown' => 'Ńyznany feler we funkcyji mail()',
-'user-mail-no-addy' => 'Próba wysłania e‐maila bez adresu odbiorcy',
+'user-mail-no-addy' => 'Průba posłańo e‐brifa bez adresu uodbjorcy',
 
 # Change password dialog
 'resetpass' => 'Pomjyńaj hasło',
-'resetpass_announce' => 'Zalůgowoužeś śe s tymčasowym kodym uotřimanym bez e-brif. Coby zakůńčyć proces logůwańo muśiš naštalować nowe hasuo:',
+'resetpass_announce' => 'Zalůgowołżeś śe ze tymczasowym kodym uotrzimanym bez e-brif. Coby zakůńczyć proces logůwańo muśisz nasztalować nowe hasło:',
 'resetpass_header' => 'Zmjyń hasło lů swojygo kůnta',
 'oldpassword' => 'Stare hasło',
 'newpassword' => 'Nowe hasło',
 'retypenew' => 'Naszkryflej jeszcze roz nowe hasło:',
-'resetpass_submit' => 'Naštaluj hasuo a zalůguj',
-'changepassword-success' => 'Twoje hasuo zostouo půmyślńy pomjyńone! Trwo logůwańe...',
-'resetpass_forbidden' => 'Ńy idźe sam půmjyńyć hasuůw.',
+'resetpass_submit' => 'Nasztaluj hasło a zaloguj',
+'changepassword-success' => 'Twoje hasło zostoło půmyślńy půmjyńone!',
+'resetpass_forbidden' => 'Ńy idźe sam půmjyńyć hasłůw.',
 'resetpass-no-info' => 'Muśysz być zalogowany, coby uzyskać bezpostrzedńi dostymp do tyj zajty.',
 'resetpass-submit-loggedin' => 'Zmjyń hasło',
 'resetpass-submit-cancel' => 'Uodćepej',
@@ -550,31 +618,31 @@ Możliwe co właśńy zmjyńiłżeś swoje hasło abo poprosiłżeś uo nowe tym
 'passwordreset-capture-help' => 'Eli zaznaczysz to pole, łoboczysz wjadomość e-mail z hasłem.',
 'passwordreset-email' => 'E-brif:',
 'passwordreset-emailtitle' => 'Kůnto na {{GRAMMAR:MS.lp|{{SITENAME}}}}',
-'passwordreset-emailtext-ip' => 'Ftůś (cheba Ty, s IP $1)
+'passwordreset-emailtext-ip' => 'Ftoś (cheba Ty, s IP $1)
 pado, aże chce informacyji lo konta do {{GRAMMAR:MS.lp{{SITENAME}}}} ($4).
-Z tem ausdrukiem sum powjonzyne konta:
+Ze tym ausdrukym sům powjůnzane kůnta:
 $2
 
-{{PLURAL:$3|Tymczasowygo hasła|Tymczasowych haseł}} możno użyć w ciągu {{PLURAL:$5|jednego dnia|$5 dni}}.
+{{PLURAL:$3|Tymczasowygo hasła|Tymczasowych hasył}} możno użyć we {{PLURAL:$5|jedyn dźyń|$5 dńi}}.
 
-Jak chćołżeś gynał to zrobjyć, to zalůgůj śe terozki a podej swoje hasło.
+Jak chćołżeś gynał to zrobjyć, to zaloguj śe terozki a podej swoje hasło.
 
-Jak ktůś inkszy chćoł nowe hasło abo jak Ci śe przipůmńouo stare a ńy chcesz nowygo, to zignoruj to a używej starygo hasła.',
-'passwordreset-emailelement' => 'Nazwa sprowjorza: $1
+Jak ftoś inkszy chćoł nowe hasło abo jak Ci śe przipůmńoło stare a ńy chcysz nowygo, to zignoruj to a używej starygo hasła.',
+'passwordreset-emailelement' => 'Mjano sprowjorza: $1
 Tymczasowe hasło: $2',
 'passwordreset-emailsent' => 'E-brif posłany.',
 'passwordreset-emailsent-capture' => 'E-brif posłony, kerego widać niżej.',
-'passwordreset-emailerror-capture' => 'Ńy udało sie wysłać wjadomości lo sprowjorza: $1',
+'passwordreset-emailerror-capture' => 'Ńy udoło śe posłać wjadomości lo {{GENDER:$2|używocza|używoczki}}: $1',
 
 # Special:ChangeEmail
 'changeemail' => 'Pomjyno ausdruka e-mail',
 'changeemail-header' => 'Pomjyno ausduku e-mail',
 'changeemail-text' => 'Wypełnij formularz, podej nowy ausdruk a hasło.',
-'changeemail-no-info' => 'Muśysz być zalogowany, coby uzyskać bezpostrzedńi dostymp do tyj zajty.',
+'changeemail-no-info' => 'Muśisz być zalogowany, coby uzyskać bezpostrzedńi dostymp do tyj zajty.',
 'changeemail-oldemail' => 'Uobecny ausdruk:',
 'changeemail-newemail' => 'Nowy adresu e-brif',
 'changeemail-none' => 'podstawowo',
-'changeemail-submit' => 'Zapisz nowy',
+'changeemail-submit' => 'Spamjyntej nowy',
 'changeemail-cancel' => 'Uodćepej',
 
 # Edit page toolbar
@@ -592,37 +660,37 @@ Tymczasowe hasło: $2',
 'nowiki_tip' => 'Zignoruj formatowańy wiki',
 'image_tip' => 'Plik uosadzůny we zajće',
 'media_tip' => 'Link do plika',
-'sig_tip' => 'Twůj podpis z datumym i czasym',
+'sig_tip' => 'Twojo szrajbka ze datum a czasym',
 'hr_tip' => 'Poźůmo lińijo (używej mjyrńy)',
 
 # Edit pages
 'summary' => 'Popis půmjyńań:',
-'subject' => 'Tymat/naguůwek:',
+'subject' => 'Tyjma/iberszryft:',
 'minoredit' => 'To je ńywjelge sprowjyńy',
 'watchthis' => 'Dej pozůr',
 'savearticle' => 'Spamjyntej',
 'preview' => 'Uobźyrańy',
 'showpreview' => 'Uobźyrej',
-'showlivepreview' => 'Dynamičny podglůnd',
+'showlivepreview' => 'Dynamiczny podglůnd',
 'showdiff' => 'Pozdrzyj na půmjyńańy',
-'anoneditwarning' => 'Ńy jeżeś nalogowany. We gyszichće sprowjyń tyj zajty bydźe naszkryflůny twůj adres IP.',
-'anonpreviewwarning' => 'Ńy jeżeś zalogowany. Twój adres IP łostonie zapisany, eli ty bydzies sprowjać zajte.',
-'missingsummary' => "'''Připomńyńy:''' Ńy wprowadźiužeś uopisu pomjyńań. Kej go ńy chceš wprowadzać, naćiś knefel Škryflej ješče roz.",
-'missingcommenttext' => 'Wćepej kůmyntoř půńižyj.',
-'missingcommentheader' => "'''Dej pozůr:''' Treść nagłůwka je pusto - uzupeuńij go! Jeli tego ńy zrobisz, Twůj kůmyntorz bydźe naszkryflany bez naguůwka.",
+'anoneditwarning' => 'Ńy jeżeś terozki zalogowany. We gyszichće sprowjyń tyj zajty bydźe naszkryflůny twůj ausdruk IP.',
+'anonpreviewwarning' => 'Ńy jeżeś zalogowany. Twój IP ausdruk uostańy spamjyntany, eli ty bydźesz sprowjać zajte.',
+'missingsummary' => "'''Pozůr:''' Ńy wprowadźůł żeś uopisu pomjyńań. Kej go ńy chcesz wprowadzać, naćiś knefel Spamjyntej jeszcze roz.",
+'missingcommenttext' => 'Wćepej kůmyntorz půńiżyj.',
+'missingcommentheader' => "'''Dej pozůr:''' Treść nagłůwka je blank - uzupełńij go! Jeli tego ńy zrobisz, Twůj kůmyntorz bydźe naszkryflony bez nagłůwka.",
 'summary-preview' => 'Podglůnd uopisu:',
-'subject-preview' => 'Podglůnd tematu/naguůwka:',
-'blockedtitle' => 'Užytkowńik je zawarty uod sprowjyń',
-'blockedtext' => '\'\'\'Twoje kůnto abo adres IP sům zawarte.\'\'\'
+'subject-preview' => 'Podglůnd tyjmy/nagłůwka:',
+'blockedtitle' => 'Użytkowńik je zawarty uod sprowjyń',
+'blockedtext' => '\'\'\'Twoje kůnto abo IP ausdruk sům zawarte.\'\'\'
 
-Uo zawarću zdecydowou $1. Pado, aže skuli: \'\'$2\'\'.
+Uo zawarću zdecydowoł $1. Pado, aże skuli: \'\'$2\'\'.
 
 * Zawarte uod: $8
 * Uodymkńe śe: $6
-* Bez cůž: $7
+* Zawarće skiż: $7
 
-Coby wyjaśńić sprawa zawarćo, naškryflej do $1 abo inkšygo [[{{MediaWiki:Grouppage-sysop}}|admińistratora]].
\83y možeÅ¡ posuaÄ\87 e-brifa bez "poÅ\9blij e-brifa tymu užytkowÅ\84ikowi", jak Å¾eÅ\9b Å\84y podou dobrygo adresa e-brifa we [[Special:Preferences|preferencyjach kůnta]], abo jak e-brify moÅ¡ tyž zawarte. Terozki moÅ¡ adres IP $3 a nůmer zawarÄ\87a to #$5. ProÅ¡ymy podaÄ\87 jedyn abo uobadwa jak chceÅ¡ pouosprawjać uo zawarću.',
+Coby wyjaśńić sprawa zawarćo, naszkryflej do $1 abo inkszygo [[{{MediaWiki:Grouppage-sysop}}|admińistratora]].
\83y możesz posÅ\82\87 e-brifa bez "poÅ\9blij e-brifa tymu użytkowÅ\84ikowi", jak Å¼eÅ\9b Å\84y podoÅ\82 dobrygo ausdruku e-brifa we [[Special:Preferences|preferencyjach kůnta]], abo jak e-brify mosz tyż zawarte. Terozki mosz ausdruk IP $3 a nůmera zawarÄ\87o to #$5. Proszymy podaÄ\87 jedyn abo uoba jak chcysz poÅ\82osprawjać uo zawarću.',
 'autoblockedtext' => 'Tyn adres IP zostou zawarty automatyčńy, gdyž kořisto s ńygo inkšy užytkowńik, zawarty uod sprowjyń bez administratora $1.
 Powůd zawarćo:
 
@@ -632,12 +700,8 @@ Powůd zawarćo:
 * Zawarće wygaso: $6
 * Zawarće je skiž: $7
 
-Možyš skůntaktować śe s $1 abo jednym s pozostauych [[{{MediaWiki:Grouppage-sysop}}|admińistratorůw]] kejbyś chćou uzyskać informacyje uo zawarću.
-
-Pozůr: Kejžeś we [[Special:Preferences|preferencyjach]] ńy naštalowou prowiduowygo adresa e-brifa, abo e-brify moš tyž zawarte, ńy možeš skožystać s uopcyje "Poślij e-brifa tymu užytkowńikowi".
-
-Twůj adres IP je terozki $3. Idyntyfikator Twojij blokady to $5. Zanotuj śe go a podej admińistratorowi.',
-'blockednoreason' => 'ńy podano skuli čego',
+* Zawarte uod: $8 * Uodymkńe śe: $6 * Zawarće skiż: $7 Coby wyjaśńić sprawa zawarćo, naszkryflej do $1 abo inkszygo [[{{MediaWiki:Grouppage-sysop}}|admińistratora]]. Ńy możesz posłać e-brifa bez "poślij e-brifa tymu użytkowńikowi", jak żeś ńy podoł dobrygo ausdruku e-brifa we [[Special:Preferences|preferencyjach kůnta]], abo jak e-brify mosz tyż zawarte. Terozki mosz ausdruk IP $3 a nůmera zawarćo to #$5. Proszymy podać jedyn abo uoba jak chcysz połosprawjać uo zawarću.',
+'blockednoreason' => 'ńy podano skuli czygo',
 'whitelistedittext' => 'Muśiš $1 coby můc sprowjać artikle.',
 'confirmedittext' => 'Muśiš podać a potwjerdźić swůj e-brif, coby můc sam sprowjać.
 Možeš to zrobić we [[Special:Preferences|swojich štalowańach]].',
@@ -695,24 +759,24 @@ Twoje pomjyńańo sům we polu edycyji půnižyj.
 By wćepać swoje pomjyńańo muśiš pomjyńać tekst w polu na wjyrchu.
 '''Tylko''' tekst z pola na wjyrchu bydźe naškryflany we baźe jak wciśńeš \"{{int:savearticle}}\".",
 'yourtext' => 'Twůj tekst',
-'storedversion' => 'Naškryflano wersyjo',
+'storedversion' => 'Naszkryflano wersyjo',
 'nonunicodebrowser' => "'''Pozůr! Twoja přeglůndorka ńy umje poprowńy rozpoznować kodowańo UTF-8 (Unicode). Bestož wšyjske znoki, kerych Twoja přeglůndorka ńy umje rozpoznować, zamjeńůno na jejich kody heksadecymalne.'''",
 'editingold' => "'''Dej pozůr: Sprowjoš inkšo wersyjo zajty kej bježůnco. Jeli jům naškryfloš, wšyjske půźńyjše pomjyńańa bydům wyćepane.'''",
 'yourdiff' => 'Růžńice',
-'copyrightwarning' => "Pamjyntej uo tym, aže couki wkuod do {{SITENAME}} udostympÅ\84ůmy wedle zasad $2 (dokuadÅ\84ij w $1). Jak Å\84y chceÅ¡, coby koždy můg go zmjyÅ\84\87 i dali rozpowÅ¡ychÅ\84\87, Å\84y wÄ\87epuj go sam. Å kryflajůnc sam tukej poÅ\9bwjadÄ\8doÅ¡ tyž, co te pisaÅ\84y je twoje wuasne, abo Å¾eÅ\9b go wźůn(a) s materjouůw kere sům na ''public domain'', abo kůmpatybilne.<br />
-'''PROŠA ŃY WĆEPYWAĆ SAM MATYRJOUŮW KERE SŮM CHRŮŃONE PRAWYM AUTORSKIM BEZ DOZWOLEŃO WUAŚĆIĆELA!'''",
-'copyrightwarning2' => "Pamjyntej uo tym, aže couki wkuod do {{GRAMMAR:MS.lp|{{SITENAME}}}} može byÄ\87 sprowjany, pomjyÅ\84any abo wyÄ\87epany bez inkÅ¡ych užytkownikůw. Jak Å\84y chceÅ¡, coby koždy můg go zmjyÅ\84\87 i dali rozpowÅ¡ychÅ\84\87 bez uograniÄ\8dyń, ńy wćepuj go sam.<br />
-Škryflajůnc sam tukej pośwjadčoš tyž, co te pisańy je twoje wuasne, abo žeś go wźůn(a) s matyrjouůw kere sům na public domain, abo kůmpatybilne (kuknij tyž: $1).
-'''PROŠA ŃY WĆEPYWAĆ SAM MATYRJOUŮW KERE SŮM CHRŮŃONE PRAWYM AUTORSKIM BEZ DOZWOLEŃO WUAŚĆIĆELA!'''",
+'copyrightwarning' => "Pamjyntej uo tym, aże coÅ\82ki wkÅ\82od do {{SITENAME}} udostympÅ\84ůmy wedle zasad $2 (dokÅ\82adÅ\84ij we $1). Jak Å\84y chcesz, coby kożdy můg go půmjyÅ\84\87 a dalij rozpowszychÅ\84\87, Å\84y wÄ\87epuj uůnygo sam. Szkryflajůnc sam tukej poÅ\9bwjadczosz tyż, co te pisaÅ\84y je twoje wÅ\82asne, abo Å¼eÅ\9b go wźůn(a) ze materjoÅ\82ůw kere sům na ''public domain'', abo kůmpatybilne.<br />
+'''PROSZA ŃY WĆEPYWAĆ SAM MATYRJOŁŮW KERE SŮM CHRŮŃONE AUTORSKIM PRAWYM BEZ DOZWOLEŃO WŁAŚĆIĆELA!'''",
+'copyrightwarning2' => "Pamjyntej uo tym, aże coÅ\82ki wkÅ\82od do {{GRAMMAR:MS.lp|{{SITENAME}}}} może byÄ\87 sprowjany, pomjyÅ\84any abo wyÄ\87epany bez inkszych użytkownikůw. Jak Å\84y chcysz, coby kożdy můg uůnygo zmjyÅ\84\87 a dalij rozpowszychÅ\84\87 bez uograniczyń, ńy wćepuj go sam.<br />
+Szkryflajůnc sam tukej pośwjadczosz tyż, co te pisańy je twoje własne, abo żeś go wźůn(a) ze matyrjołůw kere sům na public domain, abo kůmpatybilne (kuknij tyż: $1).
+'''PROSZA ŃY WĆEPYWAĆ SAM MATYRJOŁŮW KERE SŮM CHRŮŃONE PRAWYM AUTORSKIM BEZ DOZWOLEŃO WŁAŚĆIĆELA!'''",
 'longpageerror' => "''Feler: Tekst kery żeś sam wćepywoł mo {{PLURAL:$1|jedyn kilobajt|$1 kilobajtůw}}. Maksymalno dugość tekstu ńy może być srogszo kej {{PLURAL:$2|jedyn kilobajt|$2 kilobajtůw}}. Twůj tekst ńy bydźe sam naszkryflany.'''",
 'readonlywarning' => "'''Dej pozůr: Baza danych zostoua filowo zawarto skuli potřeb admińistracyjnych. Bestůž ńy do śe terozki naškryflać Twojich pomjyńań. Radzymy přećepać nowy tekst kajś do plika tekstowego (wytnij/wklej) a wćepać sam zaś po uodymkńyńću bazy.'''
 
 Admińistrator kery zawar baza dou take wyjaśńyńe: $1",
-'protectedpagewarning' => "'''Dej pozůr: Sprowjańe tyj zajty zostoło zawarte. Mogům jům sprowjać ino użytkowńicy s uprawńyńami admińistratora.'''
+'protectedpagewarning' => "'''Dej pozůr: Sprowjańe tyj zajty zostoło zawarte. Mogům jům sprowjać ino użytkowńicy ze uprawńyńami admińistratora.'''
 Uostatńy wpis w rejerze je poniżej.",
 'semiprotectedpagewarning' => "'''Pozůr:''' Ta zajta zostoła zawarto a ino zaregiszterowani użytkownicy mogům jům sprowjać.
 Uostotńy wpis w rejerze je ńyżej.",
-'cascadeprotectedwarning' => "'''Dej pozůr:''' Ta zajta zostoua zawarto a ino užytkowńicy s uprawńyńami admińistratora mogům jům sprowjać. Zajta ta je podpjynto pod {{PLURAL:$1|nastympujůnco zajta, kero zostoua zawarto|nastympujůncych zajtach, kere zostouy zawarte}} ze zauůnčonům opcjům dźedźičyńo:",
+'cascadeprotectedwarning' => "'''Dej pozůr:''' Ta zajta zostoła zawarto a ino użytkowńicy ze uprawńyńami admińistratora mogům jům sprowjać. Zajta ta je podpjynto pod {{PLURAL:$1|nastympujůnco zajta, kero zostoła zawarto|nastympujůncych zajtach, kere zostouy zawarte}} ze załůnczonům uopcjům dźedźiczyńo:",
 'titleprotectedwarning' => "'''Dej pozůr: Zajta uo tym titlu zostoła zawarto a ino [[Special:ListGroupRights|ńykerzi użytkowńicy]] mogům jům wćepać.'''
 Uostatńy wpis z rejera je ńyżej.",
 'templatesused' => '{{PLURAL:$1|Szablon|Szablůny}} użyte na tyj zajće:',
@@ -738,7 +802,7 @@ Rejer wyćepań tyj zajty je podany půńiżej, cobyś mioł wygoda:",
 'edit-hook-aborted' => 'Sprowjyńy štopńynte skiž hoka.
 Ńy je wjadůme pů jakymu.',
 'edit-gone-missing' => 'Ńy idźe zaktualizować zajty.
-Zdowo śe, co zostoua wyćepano.',
+Zdowo śe, co zostoła wyćepano.',
 'edit-conflict' => 'Kůnflikt sprowjyń.',
 'edit-no-change' => 'Twoje sprowjyńe uostouo zignorowane pů takymu, co ńic žeś we tekśće ńy zmjyńiu.',
 'edit-already-exists' => 'Ńy idźe utwořić nowyj zajty.
@@ -755,7 +819,7 @@ Powinno być myńi jak $2 {{PLURAL:$2|wywouańy|wywouańo|wywouań}}, a terozki
 'post-expand-template-inclusion-category' => 'Zajty, na kerych dokuplowane mustry sům moc wjelge',
 'post-expand-template-argument-warning' => 'Dej pozůr: Ta zajta zawjyro přinojmyńi jedyn argument we šablůńe kery powoduje co je ůun za wjelgi. Te argumynty bydům pomińynte.',
 'post-expand-template-argument-category' => 'Zajty na kerych sům šablůny s pomińyntymi argumyntůma.',
-'parser-template-loop-warning' => 'Wykryto szablůn zapyntlyńo: [[$1]]',
+'parser-template-loop-warning' => 'Wykryto muster zapyntlyńo: [[$1]]',
 'parser-template-recursion-depth-warning' => 'Przekroczůno limit głymbokośći rekurencyji szablona ($1)',
 
 # "Undo" feature
@@ -787,12 +851,12 @@ Skuli: ''$2''",
 'page_last' => 'kůńec',
 'histlegend' => 'Wybůr růżńic do porůwnańo: postow kropki we boksach a naćiś enter abo knefel na dole.<br />
 Legynda: (akt.) - růżńice s wersyjům bjeżůncům, (poprz.) - růżńice s wersyjům poprzedzajůncům, d - drobne zmjany',
-'history-fieldset-title' => 'Přeglůndej historyjo',
+'history-fieldset-title' => 'Przeglůndej gyszichta',
 'history-show-deleted' => 'Jyno wyćepane',
 'histfirst' => 'uod počůnku',
 'histlast' => 'uod uostatka',
 'historysize' => '({{PLURAL:$1|1 bajt|$1 bajty|$1 bajtůw}})',
-'historyempty' => '(pusto)',
+'historyempty' => '(blank)',
 
 # Revision feed
 'history-feed-title' => 'Gyszichta wersyjůw',
@@ -835,7 +899,7 @@ Inkśi admińistratorzi {{GRAMMAR:D.lp|{{SITENAME}}}} dali bydům mjeć dostymp
 'revdelete-radio-unset' => 'Ńy',
 'revdelete-suppress' => 'Schrůń informacyje zarůwno před admińistratorůma jak i před inkšymi',
 'revdelete-unsuppress' => 'Usůń uograńičyńo lo wćepanej nazod historyje pomjyńań',
-'revdelete-log' => 'Čymu:',
+'revdelete-log' => 'Czymu:',
 'revdelete-submit' => 'Zaakceptuj do wybrany{{PLURAL:$1|j wersyji|ch wersyji}}',
 'revdelete-success' => 'Půmyślńy zmjyńůno widoczność wersyji.',
 'revdelete-failure' => 'Feler przi zmjyńůńu widoczności wersyji.
@@ -904,6 +968,7 @@ $1',
 'editundo' => 'uodćepej',
 'diff-multi' => '(Ńy pokozano {{PLURAL:$1|jydnyj wersyji postrzedńij|$1 wersyji postrzedńich}}, sprowjanej bez {{PLURAL:$2|jydnygo sprowjorza|$2 sprowjorzow}} .)',
 'diff-multi-manyusers' => '(Ńy pokozano {{PLURAL:$1|jydnyj wersyji postrzedńij|$1 wersyji postrzedńich}}, sprowjanej bez {{PLURAL:$2|jydnygo sprowjorza|$2 sprowjorzow}} .)',
+'difference-missing-revision' => '{{PLURAL:$2|Wersyjo|$2 wersyje|$2 wersyji}} #$1 zajty "{{PAGENAME}}" ńy {{PLURAL:$2|uostoła znaleźůno|uostoły znaleźůne|uostoło znaleźůnych}}. Zauobycz je to skiż starygo linky do wyćępanyj zajty. Powůd wyćepańa nojdźesz we [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} rejerze].',
 
 # Search results
 'searchresults' => 'Wyńiki sznupańo',
@@ -946,7 +1011,7 @@ $1',
 'search-interwiki-default' => '$1 wyńiki:',
 'search-interwiki-more' => '(wjyncyj)',
 'search-relatedarticle' => 'Podane',
-'mwsuggest-disable' => 'Wyuůnč sůgestyje AJAX',
+'mwsuggest-disable' => 'Wyłůncz sůgestyje AJAX',
 'searcheverything-enable' => 'Sznupej we wszech mjan',
 'searchrelated' => 'podane',
 'searchall' => 'wszyjske',
@@ -956,19 +1021,20 @@ $1',
 'nonefound' => "'''Dej pozůr''': Důmyślńy přešukiwane sům ino ńykere přestřyńy mjan. Poprůbuj popředźić wyšukiwano fraza předrostkym ''all:'', co spowoduje přešukańy coukij zawartośći {{GRAMMAR:D.lp|{{SITENAME}}}} (wůunčńy ze zajtami godki, šablůnůma atp.), abo poprůbuj užyć kej předrostka wybranyj, jydnyj přestřyńi mjan.",
 'search-nonefound' => 'Ńy mo wynikůw, kere uodpadajům kryterjům zapytańo.',
 'powersearch' => 'Sznupańy zaawansowane',
-'powersearch-legend' => 'Šnupańy zaawansowane',
+'powersearch-legend' => 'Sznupańy zaawansowane',
 'powersearch-ns' => 'Sznupej we przestrzyńach mjan:',
 'powersearch-redir' => 'Pokož překerowańa',
-'powersearch-field' => 'Šnupej',
+'powersearch-field' => 'Sznupej',
 'powersearch-togglelabel' => 'Zaznocz:',
 'powersearch-toggleall' => 'Wszyjsko',
 'powersearch-togglenone' => 'żodno',
 'search-external' => 'Šnupańy zewnyntřne',
 'searchdisabled' => 'Šnupańy we {{GRAMMAR:MS.lp|{{SITENAME}}}} zostouo zawarte. Zańim go zouůnčům, možeš sprůbować šnupańo bez Google. Ino zauwaž, co informacyje uo treśći {{GRAMMAR:MS.lp|{{SITENAME}}}} můgům być we Google ńyakuratne.',
+'search-error' => 'Wystůmpjůł feler przi sznupańu: $1',
 
 # Preferences page
 'preferences' => 'Preferyncyje',
-'mypreferences' => 'Moje preferyncyje',
+'mypreferences' => 'Preferyncyje',
 'prefs-edits' => 'Liczba sprowjyń:',
 'prefsnologin' => 'Ńy ježeś zalůgowany',
 'prefsnologintext' => 'Muśiš śe <span class="plainlinks">[{{fullurl:{{#Special:UserLogin}}|returnto=$1}} zalůgować]</span> coby štalować swoje preferyncyje.',
@@ -1027,14 +1093,14 @@ $1',
 'timezoneregion-indian' => 'Ocean Indyjski',
 'timezoneregion-pacific' => 'Uocean Spokojny',
 'allowemail' => 'Inkśi užytkowńicy můgům přesyuać mje e-brify',
-'prefs-searchoptions' => 'Uopcyje šnupańo',
-'prefs-namespaces' => 'Přystřyńe mjan',
+'prefs-searchoptions' => 'Sznupańe',
+'prefs-namespaces' => 'Raumy mjan',
 'defaultns' => 'Důmyślńy sznupej we nastympujůncych przystrzyńach mjan:',
 'default' => 'důmyślńy',
 'prefs-files' => 'Pliki',
 'youremail' => 'E-brif:',
-'username' => 'Mjano użytkowńika:',
-'uid' => 'ID užytkowÅ\84ika:',
+'username' => '{{GENDER:$1|Mjano używocza}}:',
+'uid' => 'ID używocza:',
 'prefs-memberingroups' => 'Należy do {{PLURAL:$1|grupy|grup:}}',
 'prefs-registration' => 'Czas twůrzyńa kůnta:',
 'yourrealname' => 'Prawdźiwe mjano',
@@ -1042,8 +1108,8 @@ $1',
 'yournick' => 'Twoja šrajba:',
 'badsig' => 'Felerno šrajba, sprowdź značńiki HTML.',
 'badsiglength' => 'Twůj szrajbůng je za dugi. Maksymalno jego dugość to $1 {{PLURAL:$1|buchsztaby|buchsztabůw}}',
-'yourgender' => 'Płeć',
-'gender-unknown' => 'ńyznana',
+'yourgender' => 'Płeć:',
+'gender-unknown' => 'ńyznano',
 'gender-male' => 'chop',
 'gender-female' => 'baba',
 'email' => 'E-brif',
@@ -1066,7 +1132,7 @@ $1',
 *Zaznačůne pole uoznačo přinoležność užytkowńika do danej grupy.
 *Ńy zaznačůne pole uoznačo, aže užytkowńik ńy noležy do danej grupy.
 * Gwjozdka * infomuje, co ńy možeš wyćepać s grupy po dodańu do ńij abo dodać po wyćepańu s grupy.',
-'userrights-reason' => 'Čymu:',
+'userrights-reason' => 'Czymu:',
 'userrights-no-interwiki' => 'Ńy moš dostympu do sprowjańo uprawńyń.',
 'userrights-nodatabase' => 'Baza danych $1 ńy istńije abo ńy je lokalno.',
 'userrights-nologin' => 'Muśiš [[Special:UserLogin|zalůgować śe]] na kůnto admińistratora, coby nadować uprawńyńo užytkowńikům.',
@@ -1081,8 +1147,8 @@ $1',
 'group-bot' => 'Boty',
 'group-sysop' => 'Admińi',
 'group-bureaucrat' => 'Bjurokraty',
-'group-suppress' => 'Rewizoře',
-'group-all' => '(wšyjscy)',
+'group-suppress' => 'Rewizorze',
+'group-all' => '(wszyjscy)',
 
 'group-user-member' => '{{GENDER:$1|używacz}}',
 'group-autoconfirmed-member' => 'Autůmatyczńy zatwjerdzůny używacz',
@@ -1091,21 +1157,21 @@ $1',
 'group-bureaucrat-member' => '{{GENDER:$1|bjurokrata}}',
 'group-suppress-member' => '{{GENDER:$1|rewizůr}}',
 
-'grouppage-user' => '{{ns:project}}:Sprowjorze',
+'grouppage-user' => '{{ns:project}}:Używacze',
 'grouppage-autoconfirmed' => '{{ns:project}}:Autůmatyczńy zatwjerdzyńi używacze',
 'grouppage-bot' => '{{ns:project}}:Boty',
 'grouppage-sysop' => '{{ns:project}}:Admińistratory',
 'grouppage-bureaucrat' => '{{ns:project}}:Bjurokraty',
-'grouppage-suppress' => '{{ns:project}}:Rewizoře',
+'grouppage-suppress' => '{{ns:project}}:Rewizorze',
 
 # Rights
-'right-read' => 'Čytej zajty',
+'right-read' => 'Czytej zajty',
 'right-edit' => 'Sprowjej zajty',
-'right-createpage' => 'Utwořůne zajty (kere ńy sům zajtůma godki)',
-'right-createtalk' => 'Utwořůne zajty godki',
+'right-createpage' => 'Utworzůne zajty (kere ńy sům zajtůma godki)',
+'right-createtalk' => 'Utworzůne zajty godki',
 'right-createaccount' => 'Utwořůne nowe kůnta užytkowńikůw',
-'right-minoredit' => 'Uoznoč půmjyńańo kej drobne',
-'right-move' => 'Přećepane zajty',
+'right-minoredit' => 'Uoznocz půmjyńańo kej drobne',
+'right-move' => 'Przećepane zajty',
 'right-move-subpages' => 'Przećep zajty wroz s jejich podzajtůma',
 'right-move-rootuserpages' => 'Překludzańy zajtůw uod užytkowńikůw',
 'right-movefile' => 'Przećepańe plikůw',
@@ -1321,7 +1387,7 @@ Idź nazod i wćepej tyn plik pod inkšym mjanym. [[File:$1|thumb|center|$1]]',
 'filename-bad-prefix' => "Mjano plika, kery wćepuješ, začyno śe uod '''\"\$1\"''' &ndash; je to mjano nojčynśćy připisywane autůmatyčńy bez cyfrowe fotoaparaty, uůno ńy dowo žodnych informacyji uo zawartośći plika. Prošymy cobyś nadou plikowi inkše, lepij zrozůmjaue mjano.",
 'upload-success-subj' => 'Wćepańe plika udouo śe',
 
-'upload-proto-error' => 'Ńyprowiduowy protokůu',
+'upload-proto-error' => 'Ńyprowidłowy protokůł',
 'upload-proto-error-text' => 'Zdalne přesůuańy plikůw wymago podańo adresu URL kery začyno śe na <code>http://</code> abo <code>ftp://</code>.',
 'upload-file-error' => 'Wewnyntřny feler',
 'upload-file-error-text' => 'Wystůmpiu wewnyntřny feler kej průbowano naškryflać tymčasowy plik na serweře. Skůntaktuj śe s [[Special:ListUsers/sysop|admińistratorym systemu]].',
@@ -1482,31 +1548,31 @@ Niżyj sům informacyje ze [$2 zajty popisu] tygo pliku.',
 'lonelypagestext' => 'Do zajtůw půńiżyj ńy adresuje żodno inkszo zajta we {{SITENAME}}.',
 'uncategorizedpages' => 'Zajty bez kategoryje',
 'uncategorizedcategories' => 'Kategoryje bez kategoriůw',
-'uncategorizedimages' => 'Pliki bez kategoriůw',
-'uncategorizedtemplates' => 'Šablôny bez kategorii',
-'unusedcategories' => 'Å\83yužywane kategoryje',
-'unusedimages' => 'Å\83yužywane pliki',
-'popularpages' => 'Zajty we kere nojčynśćej sam filujům',
-'wantedcategories' => 'Potřebne katygoryje',
-'wantedpages' => 'Nojpotřebńijše zajty',
+'uncategorizedimages' => 'Pliki bez kategoryjůw',
+'uncategorizedtemplates' => 'Mustry bez kategorii',
+'unusedcategories' => 'Å\83yużywane kategoryje',
+'unusedimages' => 'Å\83yużywane pliki',
+'popularpages' => 'Zajty we kere nojczynśćij sam filujům',
+'wantedcategories' => 'Potrzebne katygoryje',
+'wantedpages' => 'Nojpotrzebńijsze zajty',
 'wantedfiles' => 'Potrzebne pliki',
 'wantedtemplates' => 'Potrzebne szablůny',
-'mostlinked' => 'Nojčyńśćej adrésowane',
-'mostlinkedcategories' => 'Kategoryje we kerych je nojwjyncyi artikli',
-'mostlinkedtemplates' => 'Nojčyńśćej adrésowane šablôny',
-'mostcategories' => 'Zajty kere majům nojwiyncyi kategoriůw',
-'mostimages' => 'Nojčyńśćij adresowane pliki',
-'mostrevisions' => 'Nojčyńśćej sprowjane artikle',
+'mostlinked' => 'Nojczyńśćij adresowane',
+'mostlinkedcategories' => 'Kategoryje we kerych je nojwjyncyj artikli',
+'mostlinkedtemplates' => 'Nojczyńśćij adresowane mustry',
+'mostcategories' => 'Zajty kere majům nojwiyncyj kategoryjůw',
+'mostimages' => 'Nojczyńśćij adresowane pliki',
+'mostrevisions' => 'Nojczyńśćij sprowjane artikle',
 'prefixindex' => 'Wszyskie zajty wedle prefiksa',
-'shortpages' => 'Nojkrůtše zajty',
+'shortpages' => 'Nojkrůtsze zajty',
 'longpages' => 'Duge artikle',
 'deadendpages' => 'Artikle bez linkůw',
-'deadendpagestext' => 'Zajty wymjyÅ\84ůne půÅ\84¾ej Å\84y majům uodnoÅ\9bÅ\84ikůw do Å¾odnych inkÅ¡ych zajtůw kere sům na tej wiki.',
+'deadendpagestext' => 'Zajty wymjyÅ\84ůne půÅ\84¼yj Å\84y majům uodnoÅ\9bÅ\84ikůw do Å¼odnych inkszych zajtůw kere sům na tyj wiki.',
 'protectedpages' => 'Zawarte zajty',
-'protectedpages-indef' => 'Yno zabezpječyńo ńyokreślůne',
+'protectedpages-indef' => 'Ino zabezpjeczyńo ńyuokreślůne',
 'protectedpages-cascade' => 'Yno zajty zabezpjeczůne rekursywńy',
-'protectedpagestext' => 'Zajty wymjyÅ\84ůne půÅ\84¾ej sům zawarte uod prećepywańo i sprowjańo.',
-'protectedpagesempty' => 'Žodno zajta Å\84y je terozki zawarto s podanymi parametrami.',
+'protectedpagestext' => 'Zajty wymjyÅ\84ůne půÅ\84¼yj sům zawarte uod przećepywańo i sprowjańo.',
+'protectedpagesempty' => 'Å»odno zajta Å\84y je terozki zawarto ze podanymi parametrami.',
 'protectedtitles' => 'Zawarte mjana artikli',
 'protectedtitlestext' => 'Ůtwořyńy artikli uo nastympujůncych mjanach je zawarte',
 'protectedtitlesempty' => 'Do tych štalowań utwořyńy artikla uo dowolnym mjańy ńy je zawarte',
@@ -1533,7 +1599,7 @@ Niżyj sům informacyje ze [$2 zajty popisu] tygo pliku.',
 'booksources' => 'Kśůnžki',
 'booksources-search-legend' => 'Šnupej za zdřůduůma kśiůnžkowymi',
 'booksources-go' => 'Pokož',
-'booksources-text' => 'PůÅ\84¾ej znojdowo Å\9be lista uodnoÅ\9bÅ\84ikůw do inkÅ¡ych witryn, kere poÅ\9bredÅ\84\8dům we spÅ\99edažy nowych i užywanych kÅ\9b\85žek, a tyž můgům mjeÄ\87 dalÅ¡e informacyje uo poÅ¡ukiwany bez Ä\87ebje kÅ\9bůnžce',
+'booksources-text' => 'PůÅ\84¼yj je lista uodnoÅ\9bÅ\84ikůw do inkszych witryn, kere poÅ\9bredÅ\84iczům we sprzedaży nowych a używanych buchůw, a tyż můgům mjeÄ\87 dolsze informacyje uo poszukiwanym bez Ä\87ebje buchu.',
 'booksources-invalid-isbn' => 'Podany numer ISBN zostoł rozpoznany kej felerny. Sprowdź aże podany numer je zgodny s numerym kery je we zdrzůdle.',
 
 # Special:Log
@@ -1589,7 +1655,7 @@ Uobsůgiwane protokoły: <code>$1</code>',
 
 # Special:ListUsers
 'listusersfrom' => 'Pokaž užytkowńikůw začynojůnc uod:',
-'listusers-submit' => 'Pokož',
+'listusers-submit' => 'Uobejrzij',
 'listusers-noresult' => 'Ńy znejdźůno žodnygo užytkowńika.',
 
 # Special:ActiveUsers
@@ -1749,21 +1815,21 @@ Kto inkszy zdůnżůł już to zrobić abo wprowadźił własne poprowki do tre
 Autorym ostatńygo pomjyńyńo je terozki [[User:$3|$3]] ([[User talk:$3|godka]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).',
 'editcomment' => "Sprowjyńe uopisano: „''$1''”.",
 'revertpage' => 'Wycofano sprowjyńe użytkowńika [[Special:Contributions/$2|$2]] ([[User talk:$2|godka]]). Autor prziwrůcůnej wersyji to [[User:$1|$1]].',
-'rollback-success' => 'Wycofano sprowjyÅ\84a užytkowńika $1.
-Přiwrůcůno uostatńo wersyja autorstwa  $2.',
+'rollback-success' => 'Wycofano sprowjyÅ\84a użytkowńika $1.
+Prziwrůcůno uostatńo wersyja autorstwa  $2.',
 
 # Edit tokens
 'sessionfailure' => 'Feler weryfikacyji zalůgowańo.
-Polecyńy zostouo anulowane, aby ůńiknůńć přechwycyńo sesyji.
+Polecyńy zostoło anulowane, coby ůńiknůńć przechwycyńo sesyji.
 
-Naćiś „cofej”, přeuaduj zajta, a potym zaś wydej polecyńy',
+Naćiś knefel „cofej”, przeładuj zajta, a potym zaś wydej polecyńy',
 
 # Protect
 'protectlogpage' => 'Zawarte',
 'protectlogtext' => 'Půńižej znojdowo śe lista zawarć i uodymkńjyńć pojydynčych zajtůw.
 Coby přejřeć lista uobecńy zawartych zajtůw, přeńdź na zajta wykazu [[Special:ProtectedPages|zawartych zajtůw]].',
 'protectedarticle' => 'zawar [[$1]]',
-'modifiedarticleprotection' => 'pomjyńiu poziům zawarćo [[$1]]',
+'modifiedarticleprotection' => 'pomjyńiu poźům zawarćo [[$1]]',
 'unprotectedarticle' => 'uodymknyu [[$1]]',
 'movedarticleprotection' => 'przekludzůno sztalowańa zabezpjeczyńo s „[[$2]]” ku „[[$1]]”',
 'protect-title' => 'Pomjyńeńe poźomu zawarćo „$1”',
@@ -1831,14 +1897,14 @@ Naćiśńyńće '''Wyczyść''' usůńy wszyjstke zaznaczyńo a wyczyśći pole
 Jak uod czasu wyćepańo ktoś utworzůł nowo zajta uo idyntycznym mjańy, uodtwarzane wersyje znojdům śe w jeij historyji, a uobecno wersyjo pozostańy bez půmjyńań.',
 'undeleterevdel' => 'Wćepańy nazod zajty ńy bydźe přeprowadzůne kej by můguo spowodować tajlowe wyćepańy aktualnej wersyji.
 W takej sytuacyji noležy uodznačyć abo přiwrůćić widočność nojnowšym wyćepanym wersjům.',
-'undeletehistorynoadmin' => 'Ta zajta zostoua wyćepano.
-Powůd wyÄ\87epaÅ\84o je podany w podsůmowaÅ\84u půÅ\84¾ej, razym s danymi užytkowÅ\84ika, kery sprawjou zajta pÅ\99ed jei wyćepańym.
-Sama treść wyćepanych wersyji je dostympna ino do admińistratorůw',
+'undeletehistorynoadmin' => 'Ta zajta zostoła wyćepano.
+Powůd wyÄ\87epaÅ\84o je podany w podsůmowaÅ\84u půÅ\84¼yj, razym ze danymi użytkowÅ\84ika, kery sprowjoÅ\82 zajta przed jei wyćepańym.
+Samo treść wyćepanych wersyji je dostympna ino do admińistratorůw',
 'undelete-revision' => 'Wyćepńynto wersyjo $1 (s $5 $4) keryj autorym je $3:',
 'undeleterevision-missing' => 'Felerno abo brakujůnco wersyjo.
-MožeÅ¡ mjeÄ\87 felerny link abo wersyjo můgua zostaÄ\87 wÄ\87epano nazod, abo wyÄ\87epano s archiwům.',
+Możesz mjeÄ\87 felerny link abo wersyjo můgÅ\82a zostaÄ\87 wÄ\87epano nazod, abo wyÄ\87epano ze archiwům.',
 'undelete-nodiff' => 'Ńy znejdźono popřednich wersyji.',
-'undeletebtn' => 'Uodtwůř',
+'undeletebtn' => 'Uodtwůrz',
 'undeletelink' => 'ukoż abo uodtwůrz',
 'undeleteviewlink' => 'ukoż',
 'undeletereset' => 'Wyčyść',
@@ -1920,9 +1986,9 @@ $1',
 # Block/unblock
 'blockip' => 'Zawrzij sprowjorza',
 'blockip-legend' => 'Zawrzij sprowjorza',
-'blockiptext' => 'Tyn formulař suužy do zawjerańo sprowjyń spod uokreślůnygo adresu IP abo kůnkretnymu užytkowńikowi.
-ZawjeraÄ\87 noležy jydyÅ\84y po to, by zapobjec wandalizmům, zgodÅ\84y s [[{{MediaWiki:Policy-url}}|pÅ\99ijyntymi zasadami]].
-Podej powůd (np. umješčajůnc mjana zajtůw, na kerych dopuščůno śe wandalizmu).',
+'blockiptext' => 'Tyn formularz służy do zawjerańo sprowjyń spod uokreślůnygo adresu IP abo kůnkretnymu użytkowńikowi.
+ZawjeraÄ\87 noleży jydyÅ\84y po to, by zapobjec wandalizmům, zgodÅ\84y ze [[{{MediaWiki:Policy-url}}|przijyntymi reglůma]].
+Podej powůd (np. umjeszczajůnc mjana zajtůw, na kerych dopuszczůno śe wandalizmu).',
 'ipadressorusername' => 'Adres IP abo mjano użytkowńika',
 'ipbexpiry' => 'Wygaso:',
 'ipbreason' => 'Čymu:',
@@ -1936,12 +2002,12 @@ Podej powůd (np. umješčajůnc mjana zajtůw, na kerych dopuščůno śe wanda
 ** Ůsuwańy treśći zajtůw
 ** Wprowadzańy fołszywych informacyji
 ** Wulgaryzmy
-** Wypisywańy guůpot na zajtach',
+** Wypisywańy gůpot na zajtach',
 'ipbcreateaccount' => 'Ńy dozwůl utwožyć kůnta',
-'ipbemailban' => 'Zawřij možliwość wysůuańo e-brifůw',
+'ipbemailban' => 'Zawrzij mogebność wysůłańo e-brifůw',
 'ipbenableautoblock' => 'Zawřij uostatńi adres IP tygo užytkowńika i autůmatyčńy wšyjstke kolejne, s kerych bydźe průbowou sprowjać zajty',
 'ipbsubmit' => 'Zawřij uod sprowjyń tygo užytkowńika',
-'ipbother' => 'Ikšy čas',
+'ipbother' => 'Ikszy czas',
 'ipboptions' => '2 godźiny:2 hours,1 dźyń:1 day,3 dńi:3 days,1 tydźyń:1 week,2 tydńe:2 weeks,1 mjeśůnc:1 month,3 mjeśůnce:3 months,6 mjeśůncůw:6 months,1 rok:1 year,nawdy:infinite',
 'ipbotheroption' => 'inkšy',
 'ipbotherreason' => 'Inkšy powůd:',
@@ -1978,7 +2044,7 @@ Přyńdź do [[Special:BlockList|listy zawartych adresůw IP]] coby přejřeć z
 'unblocklink' => 'uodymknij',
 'change-blocklink' => 'půmjyń zawarće uod sprowjyń',
 'contribslink' => 'ajnzace',
-'autoblocker' => 'Zawarto Ci sprowjyńo autůmatyčńy, bez tůž co užywaš tygo samygo adresu IP, co užytkowńik „[[User:$1|$1]]”.
+'autoblocker' => 'Zawarto Ci sprowjyńo autůmatyczńy, bez tůż co używosz tygo samygo adresu IP, co używocz „[[User:$1|$1]]”.
 Powůd zawarća $1 to: „$2”',
 'blocklogpage' => 'Gyszichta zawjyrańo',
 'blocklogentry' => 'zawarto [[$1]], bydźe uodymkńynty: $2 $3',
@@ -1988,8 +2054,8 @@ Na li'śće ńy mo adresůw IP, kere zawarto w sposůb autůmatyčny.
 Coby přejřeć lista uobecńy aktywnych zawarć, přyńdź na zajta [[Special:BlockList|zawartych adresůw i užytkowńikůw]].",
 'unblocklogentry' => 'uodymknyu $1',
 'block-log-flags-anononly' => 'ino anůnimowi',
-'block-log-flags-nocreate' => 'tworzińy kůnta je zawrzite',
-'block-log-flags-noautoblock' => 'autůmatyčne zawjerańy uod sprawjyń wůuůnčůne',
+'block-log-flags-nocreate' => 'tworzińy kůnta je zawarte',
+'block-log-flags-noautoblock' => 'autůmatyczne zawjerańy uod sprawjyń wyłůnczůne',
 'block-log-flags-noemail' => 'e-brif zawarty',
 'block-log-flags-nousertalk' => 'ńy może sprowjać włosnyj zajty godki',
 'block-log-flags-angry-autoblock' => 'rozszerzůne automatyczne zawjyrańe załůnczůne',
@@ -2002,12 +2068,9 @@ Coby přejřeć lista uobecńy aktywnych zawarć, přyńdź na zajta [[Special:B
 'ipb_blocked_as_range' => 'Feler: Adres IP $1 ńy zostou zawarty bezpośredńo i ńy može zostać uodymkńjynty.
 Noležy uůn do zawartygo zakresu adresůw $2. Uodymknůńć možno ino couki zakres.',
 'ip_range_invalid' => 'Ńypoprowny zakres adresów IP.',
-'blockme' => 'Zawryj mi sprowjyńa',
 'proxyblocker' => 'Zawjyrańe proxy',
-'proxyblocker-disabled' => 'Ta fůnkcyjo je wůuůnčůna.',
 'proxyblockreason' => 'Twůj adres IP zostou zawarty, bo je to adres uotwartygo proxy.
 Sprawa noležy wyjaśńić s dostawcům Internetu abo půmocům techńičnům informujůnc uo tym powažnym problymje s bezpječyństwym.',
-'proxyblocksuccess' => 'Wykůnane.',
 'sorbsreason' => 'Twůj adres IP znojdowo śe na liśće serwerůw open proxy w DNSBL, užywanej bez {{GRAMMAR:B.lp|{{SITENAME}}}}.',
 'sorbs_create_account_reason' => 'Twůj adres IP znojdowo śe na liśće serwerůw open proxy w DNSBL, užywanej bez {{GRAMMAR:B.lp|{{SITENAME}}}}.
 Ńy možeš utwořić kůnta',
@@ -2035,7 +2098,7 @@ Zawjerańy i uodmykańy bazy danych wymogo coby plik můgu być naškreflany bez
 
 # Move page
 'move-page' => 'Przećep $1',
-'move-page-legend' => 'Přećiś artikel',
+'move-page-legend' => 'Przećiś artikel',
 'movepagetext' => "Przi půmocy formularza půńiżej możesz půmjyńyć mjano zajty i przećepnůńć jej gyszichta. Pod downym mjanym uostańe śa zajta przekerowujůnca. Zajty adresowane na stare mjano uostanům jak bůły.
 
 Jak śe na to decydujesz, sprowdź, eli ńy je to [[Special:DoubleRedirects|podwůjne]] abo [[Special:BrokenRedirects|złomane przekerowańy]].
@@ -2052,23 +2115,23 @@ To może być drastyczno abo ńyprzewidywalno zmjano, jak przećepńysz jako pop
 *ńy přećepuješ zajty do inkšy přestřeńy mjan
 *ńy ma sam zajty godki o takiym mjańe
 W takiych razach tekst godki třa přećepać, a jak třeba to i pouůnčyć z tym co juž sam jest, rynčńe. Abo možeš sie namyślić i nie přećepywać wcale ("checkbox" půnižyi).',
-'movearticle' => 'Přećiś artikel:',
-'movenologin' => 'Ńy jestžeś zalůgowany',
+'movearticle' => 'Przećiś artikel:',
+'movenologin' => 'Ńy jeżeś zalůgowany',
 'movenologintext' => 'Muśyš być zarejerowanym i [[Special:UserLogin|zalůgowanym]] užytkowńikym coby můc přećepnůńć zajta.',
 'movenotallowed' => 'Ńy moš uprownień do přećepywańo zajtůw.',
 'cant-move-user-page' => 'Ńy mosz uprowńyń do przekludzańo zajtůw użytkowńikůw (wyjůntkym sům jejich podstrony).',
 'cant-move-to-user-page' => 'Ńy mosz uprowńyń coby przekludźić zajta na plac kaj je zajta użytkowńika (wyjůntkym sům podzajty użytkowńika).',
 'newtitle' => 'Nowy titel:',
 'move-watch' => 'Dej pozůr',
-'movepagebtn' => 'Přećiś artikel',
-'pagemovedsub' => 'Přećiśńjyńće gotowe',
-'movepage-moved' => '\'\'\'"$1" přećiśńjynto ku "$2"\'\'\'',
-'articleexists' => 'Artikel s takym mjanym juž je, abo mjano je zue.
-Wybjer inkše mjano.',
-'cantmove-titleprotected' => 'Å\83y možeÅ¡ pÅ\99\87epnůÅ\84Ä\87 zajty, bez tůž co jei nowe mjano je Å\84ydozwolůne s kuli zabezpjeÄ\8d\84o pÅ\99ed utwoÅ\99yńym',
-'talkexists' => 'Zajta artikla zostaua přećepano, ale zajta godki ńy - zajta godki uo nowym mjańe juž sam jest. Poůunč, proša, teksty oubydwůch godek rynčńe.',
-'movedto' => 'přećiśńjynto ku',
-'movetalk' => 'Přećiś godke, jak možno.',
+'movepagebtn' => 'Przećiś artikel',
+'pagemovedsub' => 'Przećiśńyńće je fertig',
+'movepage-moved' => '\'\'\'"$1" przećiśńjynto ku "$2"\'\'\'',
+'articleexists' => 'Artikel ze takym mjanym już je, abo mjano je złe.
+Wybjer inksze mjano.',
+'cantmove-titleprotected' => 'Å\83y możesz przeÄ\87epnůÅ\84Ä\87 zajty, beztuż co jeij nowe mjano je Å\84ydozwolůne skuli zabezpjeczyÅ\84o przed utworzyńym',
+'talkexists' => 'Zajta artikla zostoła przećepano, nale zajta godki ńy - zajta godki uo nowym mjańe już sam jest. Połuncz, prosza, teksty uobydwůch godek rynczńe.',
+'movedto' => 'przećiśńjynto ku',
+'movetalk' => 'Przećiś godke, jak możno.',
 'move-subpages' => 'Přećepńij podzajty',
 'move-talk-subpages' => 'Jeli je to możliwe przekludź wszyjstke zajty godki podzajtůw',
 'movepage-page-exists' => 'Zajta $1 już istńeje a ńy idźe jeij autůmatyczńy nadszkryflać.',
@@ -2077,12 +2140,12 @@ Wybjer inkše mjano.',
 'movepage-max-pages' => 'Przekludzůnych uostało $1 {{PLURAL:$1|zajta|zajty|zajtůw}}. Wjynkszyj liczby ńy idźe przekludźić automatyczńy.',
 'movelogpage' => 'Przećepńynte',
 'movelogpagetext' => 'Uoto lista zajtůw, kere uostatńo zostouy přećepane.',
-'movereason' => 'Čymu:',
+'movereason' => 'Czymu:',
 'revertmove' => 'cofej',
 'delete_and_move' => 'Wyćep i przećep',
-'delete_and_move_text' => '== Přećepańy wymaga wyćepańo inkšyj zajty ==
-Zajta docelowo â\80\9e[[:$1]]â\80\9d juž sam jest.
-Čy chceš jům wyćepać, coby zrobić plac do přećepywanej zajty?',
+'delete_and_move_text' => '== Przećepańy wymogo wyćepańo inkszyj zajty ==
+Zajta docelowo â\80\9e[[:$1]]â\80\9d już sam jest.
+Czy chcysz jům wyćepać, coby zrobić plac do przećepywanej zajty?',
 'delete_and_move_confirm' => 'Toć, wyćep zajta',
 'delete_and_move_reason' => 'Wyćepano coby zrobić plac do přećepywanyj zajty',
 'selfmove' => 'Mjana zajtůw zdřůdowyj i docelowyj sům take same.
@@ -2288,12 +2351,12 @@ Nojprawdopodobńij zostoło to spowodowane bez link do zewnyntrznyj zajty intern
 
 $1',
 'filedelete-missing' => 'Plika „$1” ńy idźe wyćepać, bo ńy istńije.',
-'filedelete-old-unregistered' => 'Žůndanyj wersyji plika „$1” ńy ma w baźe danych.',
+'filedelete-old-unregistered' => 'Å»ůndanyj wersyji plika „$1” ńy ma w baźe danych.',
 'filedelete-current-unregistered' => 'Plika „$1” ńy ma w baźe danych.',
-'filedelete-archive-read-only' => 'Serwer WWW Å\84y može naÅ¡kryflaÄ\87 w katalůgu s archiwůma „$1”.',
+'filedelete-archive-read-only' => 'Serwer WWW Å\84y może naszkryflaÄ\87 we katalogu ze archiwůma „$1”.',
 
 # Browsing diffs
-'previousdiff' => '← Popředńy sprowjyńy',
+'previousdiff' => '← Poprzedńy sprowjyńy',
 'nextdiff' => 'Nostympne sprowjyńy →',
 
 # Media information
@@ -2310,7 +2373,7 @@ $1',
 # Special:NewFiles
 'newimages' => 'Galerjo nowych uobrozkůw',
 'imagelisttext' => "Půnižyj na {{PLURAL:$1||posortowanyj $2}} liśće {{PLURAL:$1|znojdowo|znojdujům|znojdowo}} śe '''$1''' {{PLURAL:$1|plik|pliki|plikůw}}.",
-'newimages-summary' => 'Na tyi ekstra zajće prezyntowane sům uostatńo wćepńynte pliki.',
+'newimages-summary' => 'Na tyj ekstra zajće prezyntowane sům uostatńo wćepńynte pliki.',
 'newimages-legend' => 'Filtruj',
 'newimages-label' => 'Mjano plika (abo jygo tajla):',
 'showhidebots' => '($1 boty)',
@@ -2442,7 +2505,7 @@ Eli plik był modyfikowany, dane mogům w tajli ńy być we zgodźe ze parametr
 'exif-gpsmeasuremode' => 'Tryb půmjaru',
 'exif-gpsdop' => 'Precyzjo půmjaru',
 'exif-gpsspeedref' => 'Jydnostka gibkości',
-'exif-gpsspeed' => 'Gibkość poźomo',
+'exif-gpsspeed' => 'Gibkość poźůmo',
 'exif-gpstrackref' => 'Poprawka půmjyndzy kerůnkym i celym',
 'exif-gpstrack' => 'Kerunek ruchu',
 'exif-gpsimgdirectionref' => 'Poprawka do kerůnku zdjyńćo',
@@ -2562,7 +2625,7 @@ Eli plik był modyfikowany, dane mogům w tajli ńy być we zgodźe ze parametr
 'exif-gaincontrol-1' => 'ńiske wzmocńyńe',
 'exif-gaincontrol-2' => 'wysoke wzmocńyńe',
 'exif-gaincontrol-3' => 'ńiske uosuabjyńy',
-'exif-gaincontrol-4' => 'wysoke uosuabjyńy',
+'exif-gaincontrol-4' => 'wysoke uosłabjyńy',
 
 'exif-contrast-0' => 'normalny',
 'exif-contrast-1' => 'Lichy',
@@ -2737,9 +2800,9 @@ Možeš tyž [[Special:EditWatchlist|užyć standardowygo edytora]].',
 'version' => 'Wersjo',
 'version-extensions' => 'Zainstalowane rozšeřyńa',
 'version-specialpages' => 'Szpecjalne zajty',
-'version-parserhooks' => 'Haki analizatora skuadńi (ang. parser hooks)',
+'version-parserhooks' => 'Haki analizatora składńi (yng. parser hooks)',
 'version-variables' => 'Zmjynne',
-'version-other' => 'Inkše',
+'version-other' => 'Inksze',
 'version-mediahandlers' => 'Wtyčki uobsůgi medjůw',
 'version-hooks' => 'Haki (ang. hooks)',
 'version-parser-extensiontags' => 'Značńiki rozšeřyń do analizatora skuadńi',
index e112f26..cecd1de 100644 (file)
@@ -94,7 +94,7 @@ $messages = array(
 'tog-extendwatchlist' => 'அனைத்து பொருத்தமான மாற்றங்களைக் காட்டுமாறு கவனிப்புப் பட்டியலை விரிவாக்கு',
 'tog-usenewrc' => 'அண்மைய மாற்றங்கள் மற்றும் கவனிப்புப் பட்டியல் பக்கத்தில் மாற்றங்களை பக்கத்தை பொறுத்து குழுவாக்கு',
 'tog-numberheadings' => 'தலைப்புகளுக்கு தானியங்கி இலக்கமிடு',
-'tog-showtoolbar' => 'கருவிப் பட்டையைக் காட்டு',
+'tog-showtoolbar' => 'கருவிப்பட்டையைக் காட்டு',
 'tog-editondblclick' => 'இரட்டைச் சொடுக்கில் பக்கங்களைத் தொகு',
 'tog-editsection' => '(தொகு) இணைப்புகளின் வழியாக பிரிவுத் தொகுத்தலை செயலாக்கவும்',
 'tog-editsectiononrightclick' => 'பிரிவுத் தலைப்பின் மீது வலச் சொடுக்குவதன் மூலம் பகுதித்  தொகுப்பை செயலாக்கவும்',
@@ -115,7 +115,7 @@ $messages = array(
 'tog-shownumberswatching' => 'கவனிக்கும் பயனர்களின் எண்ணிக்கையைக் காட்டவும்',
 'tog-oldsig' => 'நடப்பு கையொப்பம்:',
 'tog-fancysig' => 'வெற்றுக் கையொப்பம் (தானியங்கி இணைப்பின்றி)',
-'tog-uselivepreview' => 'நேரடி முன்தோற்றத்தைப் பயன்படுத்து (சோதனையிலுள்ளது)',
+'tog-uselivepreview' => 'நேரடி முன்தோற்றத்தைப் பயன்படுத்துங்கள் (சோதனையிலுள்ளது)',
 'tog-forceeditsummary' => 'தொகுப்புச் சுருக்கம் வெற்றாக இருக்கும் போது எனக்கு நினைவூட்டு',
 'tog-watchlisthideown' => 'எனது தொகுப்புக்களைக் கவனிப்புப் பட்டியலிலிருந்து மறை',
 'tog-watchlisthidebots' => 'தானியங்கித் தொகுப்புக்களைக் கவனிப்புப் பட்டியலிலிருந்து மறை',
@@ -229,7 +229,7 @@ $messages = array(
 'newwindow' => '(புதிய சாளரத்துள் திறக்கும்)',
 'cancel' => 'சேமிக்காமல் திரும்பு',
 'moredotdotdot' => 'மேலும்...',
-'morenotlisted' => 'இந்த பட்டியல்கல் முழுமையாக்கவில்லை.',
+'morenotlisted' => 'இந்தப் பட்டியல் முழுமையானதல்ல.',
 'mypage' => 'பக்கம்',
 'mytalk' => 'பேச்சு',
 'anontalk' => 'இந்த ஐ.பி. முகவரிக்கான பேச்சு',
@@ -304,7 +304,7 @@ $messages = array(
 'articlepage' => 'உள்ளடக்கப் பக்கத்தைப் பார்',
 'talk' => 'உரையாடல்',
 'views' => 'பார்வைகள்',
-'toolbox' => 'à®\95à®°à¯\81விபà¯\8d à®ªà¯\86à®\9fà¯\8dà®\9fி',
+'toolbox' => 'à®\95à®°à¯\81விà®\95ளà¯\8d',
 'userpage' => 'பயனர் பக்கத்தைப் பார்',
 'projectpage' => 'திட்டப் பக்கத்தைப் பார்',
 'imagepage' => 'கோப்புப் பக்கத்தை நோக்க',
@@ -334,7 +334,7 @@ $1',
 # All link text and link target definitions of links into project namespace that get used by other message strings, with the exception of user group pages (see grouppage).
 'aboutsite' => '{{SITENAME}} பற்றி',
 'aboutpage' => 'Project:விவரம்',
-'copyright' => 'உள்ளடக்கங்கள் $1 இன் கீழ் கிடைக்கின்றன.',
+'copyright' => 'à®\95à¯\81றிà®\95à¯\8dà®\95பà¯\8dபà®\9fபà¯\8dபà®\9fாவிà®\9fà¯\8dà®\9fாலà¯\8d à®\89ளà¯\8dளà®\9fà®\95à¯\8dà®\95à®\99à¯\8dà®\95ளà¯\8d $1 à®\87னà¯\8d à®\95à¯\80à®´à¯\8d à®\95ிà®\9fà¯\88à®\95à¯\8dà®\95ினà¯\8dறன.',
 'copyrightpage' => '{{ns:project}}:பதிப்புரிமை',
 'currentevents' => 'தற்போதைய நிகழ்வுகள்',
 'currentevents-url' => 'Project:நடப்பு நிகழ்வுகள்',
@@ -460,7 +460,7 @@ $1',
 'viewsource-title' => '$1க்கான மூலத்தைப்  பார்',
 'actionthrottled' => 'செயற்பாடு கட்டுப்படுத்தப்பட்டது',
 'actionthrottledtext' => 'எரிதக் காப்பு நடவடிக்கையாகப் பயனொருவர் குறித்த சிறு கால இடைவெளியில் இச்செயற்பாட்டை அதிகளவில் செய்வது தடுக்கப்பட்டுள்ளது. நீர் அவ்வெல்லையைத் தாண்டிவிட்டீர். அருள் கூர்ந்து சில நிமிடங்களில் முயலவும்.',
-'protectedpagetext' => 'இப்பக்கம் தொகுக்கப்படுவதை தவிர்ப்பதற்காக பூட்டப்பட்டுள்ளது.',
+'protectedpagetext' => 'இப்பக்கம் தொகுக்கப்படுவதையோ அல்லது பிற செயல்களைத் தவிர்ப்பதற்காகவோ பூட்டப்பட்டுள்ளது.',
 'viewsourcetext' => 'நீங்கள் இந்தப் பக்கத்தின் மூலத்தைப் பார்க்கவும் அதனை நகலெடுக்கவும் முடியும்:',
 'viewyourtext' => "நீங்கள் இந்த பக்கத்திற்கான ''' உங்கள் திருத்தங்களுக்கான ''' மூலத்தைக் காணவும்  நகலெடுக்கவும் முடியும்.",
 'protectedinterface' => 'இப்பக்கம் இம்மென் பொருளுக்கான பயனர் இடைமுக உரைகளை வழங்குகிறது, விசம தொகுப்புக்களை தவிர்ப்பதற்க்காக இப்பக்கம் பூட்டப்பட்டுள்ளது.',
@@ -529,6 +529,7 @@ $1',
 'userlogin-resetpassword-link' => 'உங்கள் கடவுச்சொல் மறந்துவிட்டதா?',
 'helplogin-url' => 'Help:புகுபதிகை',
 'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|புகுபதிவதற்கான உதவி]]',
+'userlogin-createanother' => 'மற்றொரு கணக்கு ஒன்றை உருவாக்கவும்',
 'createacct-join' => 'உங்களின் தகவலை கீழிடவும்',
 'createacct-another-join' => 'கீழே புதிய கணக்கிற்கான தகவல்களை உள்ளிடவும்.',
 'createacct-emailrequired' => 'மின்னஞ்சல் முகவரி',
@@ -585,7 +586,8 @@ $1',
 'noemailcreate' => 'ஒரு செல்லத்தக்க மின்னஞ்சல் முகவரியை நீங்கள் தரவேண்டும்.',
 'passwordsent' => '"$1" பயனருக்கான மின்னஞ்சல் முகவரிக்கு ஒரு புதிய கடவுச்சொல் அனுப்பப்பட்டுள்ளது. பெற்றுக்கொண்டதும் தயவுசெய்து மீண்டும் புகுபதிகை செய்யவும்.',
 'blocked-mailpassword' => 'உங்கள் ஐ.பி. முகவரி தடுக்கப்பட்டுள்ளது, விசம செயற்பாடுகளைத் தவிர்க்க கடவுச்சொல் மீட்புச் செயலியை நீங்கள் பயன்படுத்து அனுமதிக்கப்படவில்லை.',
-'eauthentsent' => 'உறுதிப்படுத்தல் மின்னஞ்சலொன்று நீங்கள் கொடுத்த மின்னஞ்சல் முகவரிக்கு அனுப்பப் பட்டுள்ளது. மேலதிகமாக எந்த மின்னஞ்சலும் இந்த முகவரிக்கு அனுப்பப்படு முன்னர், மின்னலில் கொடுக்கப்பட்டுள்ள அறிவுறுத்தல்களின் படி, இம்மின்னஞ்சல் முகவரி உங்களுடையது என்பதை உறுதிப்படுத்தவும்.',
+'eauthentsent' => 'உறுதிப்படுத்தல் மின்னஞ்சலொன்று நீங்கள் கொடுத்த மின்னஞ்சல் முகவரிக்கு அனுப்பப் பட்டுள்ளது.
+மேலும் மின்னஞ்சல்கள் இந்த முகவரிக்கு அனுப்பப்படும் முன்னர், மின்னஞ்சலில் கொடுக்கப்பட்டுள்ள வழிமுறைகளை பின்பற்றி, இம்மின்னஞ்சல் முகவரி உங்களுடையது தான் என்பதை உறுதிப்படுத்தவும்.',
 'throttled-mailpassword' => 'கடந்த {{PLURAL:$1|மணிநேரத்துக்குள்|$1 மணிநேரங்களுக்குள்}} ஒரு கடவுச்சொல் நினைவூட்டல் மின்னஞ்சல் ஏற்கனவே அனுப்பப்பட்டுவிட்டது. விசமப் பயன்பாடுகளைத் தவிர்ப்பதற்காக {{PLURAL:$1|மணிநேரத்திற்கு|$1 மணிநேரங்களுக்கு}} ஒரு கடவுச்சொல் நினைவூட்டல் மின்னஞ்சல் மட்டுமே அனுப்பப்படும்.',
 'mailerror' => 'மின்னஞ்சல் அனுப்புவதில் தவறு: $1',
 'acct_creation_throttle_hit' => 'தங்களது IP முகவரியை பயன்படுத்தி இந்த விக்கியில் நேற்று {{PLURAL:$1|1 கணக்கு |$1 கணக்குகள்}} உருவாக்கப்பட்டுள்ளது.  தற்போது இதுவே மிக அதிகமாக அனுமதிக்கப்பட்ட அளவாகும்.
@@ -638,8 +640,11 @@ $1',
 
 # Special:PasswordReset
 'passwordreset' => 'கடவுச்சொல்லை மீட்டமை',
+'passwordreset-text-one' => 'உங்கள் கடவுச்சொல்லை மீட்டமைக்க இந்த படிவத்தை நிறைவு செய்க.',
+'passwordreset-text-many' => '{{PLURAL:$1|உங்கள் கடவுச்சொல்லை மீட்டமைக்க புலங்கள் ஒன்றினை நிரப்பவும்.}}',
 'passwordreset-legend' => 'கடவுச்சொல்லை மீட்டமை',
 'passwordreset-disabled' => 'கடவுச்சொல் மீட்டமைப்பு இந்த விக்கியில் செயலிழக்க செய்யப்பட்டுள்ளது.',
+'passwordreset-emaildisabled' => 'மின்னஞ்சல் வசதி இந்த விக்கியில் முடக்கப்பட்டுள்ளது.',
 'passwordreset-username' => 'பயனர் பெயர்:',
 'passwordreset-domain' => 'இணையதள முகவரி:',
 'passwordreset-capture' => 'விளைவு மின்னஞ்சலை காண்',
@@ -751,9 +756,7 @@ $1 எனும் பயனரையோ வேறு [[{{MediaWiki:Grouppage-sy
 'loginreqlink' => 'புகுபதிகை',
 'loginreqpagetext' => 'ஏனைய பக்கங்களைப் பார்க்க  நீங்கள் $1 செய்ய வேண்டும்.',
 'accmailtitle' => 'கடவுச்சொல் அனுப்பப்பட்டுள்ளது.',
-'accmailtext' => "தான்தோன்றித்தனமான ஒரு கடவுச்சொல்லை [[User talk:$1|பயனர் பேச்சு:$1|$1]] $2-க்கு அனுப்பி வைக்கப்பட்டுள்ளது.
-
-இந்த புதிய கணக்கிற்கான கடவுச்சொல்லை புகுபதிகை செய்தவுடன் மாற்றிக்கொள்ளவும் ''[[Special:ChangePassword|கடவுச்சொல்லை மாற்று]]''.",
+'accmailtext' => "தானியக்கமாக [[User talk:$1|$1]]-க்கு ஒரு கடவுச்சொலை உறுவாக்கி $2-க்கு அனுப்பி வைக்கப்பட்டுள்ளது. இதனை புகுபதிகை செய்தவுடன் ''[[Special:ChangePassword|கடவுச்சொல்லை மாற்று]]'' பக்கத்தில் மாற்றிக்கொள்ளளாம்.",
 'newarticle' => '(புதிது)',
 'newarticletext' => 'ஒரு இணைப்பினூடாக நீங்கள் வந்துள்ள இப்பக்கம் இன்னும் உருவாக்கப்படவில்லை. பக்கத்தை உருவாக்குவதற்குக் கீழேயுள்ள கட்டத்துள் தட்டச்சிடத் தொடங்குங்கள். (மேலதிக விபரங்களுக்கு [[{{MediaWiki:Helppage}}|உதவிப் பக்கத்தைப்]] பார்க்கவும்). நீங்கள் தவறுதலாக இங்கே வந்திருந்தால், உங்கள் உலாவியின் பின் செல்வதற்கான பொத்தானைச் சொடுக்கவும்.',
 'anontalkpagetext' => "----''இது இன்னும் கணக்கொன்று ஏற்படுத்தாத அல்லது வழமையாக பயனர் கணக்கை பயன்படுத்தாத பயனர்களுக்குரிய கலந்துரையாடல் பக்கமாகும். அதனால் நாங்கள் இவரை அடையாளம் காண்பதற்கு எண்சார்ந்த ஐபி முகவரியைப் பயன்படுத்த வேண்டியதாய் இருக்கின்றது. இவ்வாறான ஐபி முகவரிகள் பல பயனர்களினால் பகிர்ந்துகொள்ளப்படலாம்.
@@ -767,12 +770,11 @@ $1 எனும் பயனரையோ வேறு [[{{MediaWiki:Grouppage-sy
 'userpage-userdoesnotexist' => '"<nowiki>$1</nowiki>" என்றக் கணக்கு இன்னமும் பதிவுச் செய்யப்படவில்லை. இதை உருவாக்க/தொகுக்க வேண்டுமா என்பதை உறுதிப்படுத்தவும்.',
 'userpage-userdoesnotexist-view' => 'பயனர் கணக்கு "$1" பதியப்படவில்லை',
 'blocked-notice-logextract' => 'இந்தப் பயனர் தற்சமயம் தடை செய்யப்பட்டுள்ளார். இவரது தடை பதிகையின் அண்மைய மாற்றம் கீழே தரப்பட்டுள்ளது:',
-'clearyourcache' => "'''à®\95வனிà®\95à¯\8dà®\95''' - சேமித்த பின்னர், நீங்கள் செய்த மாற்றங்களைக் காண்பதற்கு உங்கள் உலவியின் இடைமாற்று அகற்றப்பட வேண்டும்.'''
+'clearyourcache' => "'''à®\95à¯\81றிபà¯\8dபà¯\81''' - சேமித்த பின்னர், நீங்கள் செய்த மாற்றங்களைக் காண்பதற்கு உங்கள் உலவியின் இடைமாற்று அகற்றப்பட வேண்டும்.'''
 *'''மொஸில்லா பயர்பாக்ஸ் / சபாரி:''' ''Shift+Reload'', அல்லது ''Ctrl-F5'' அல்லது ''Ctrl-R'' (''⌘-R'' Mac ல்)
 *'''கூகிள் குரோம்''' ''Ctrl-Shift-R'' அழுத்தவும். (''⌘-Shift-R''  Mac ல்) ;
-*'''கொன்குவெரர்: ''' ''Reload'' அல்லது ''F5'' கிளிக் செய்யவும்;
-*'''ஒபேரா:''' ''Tools → Preferences'' இல் இடைமாற்றை அகற்றவும்;
-*'''இண்டர்நெட் எக்ஸ்ப்ளோரர்:''' ''Ctrl-Refresh'' அல்லது ''Ctrl-F5'' ஐ அழுத்தவும்.",
+*'''இண்டர்நெட் எக்ஸ்ப்ளோரர்:''' ''Ctrl-Refresh'' அல்லது ''Ctrl-F5'' ஐ அழுத்தவும்.
+*'''ஒபேரா:''' ''Tools → Preferences'' இல் இடைமாற்றை அகற்றவும்;",
 'usercssyoucanpreview' => "'''சிறு உதவி:''' தங்களது சி.எஸ்.எஸ்(CSS)-ஐ சேமிப்பதற்கு முன்பாக \"{{int:showpreview}}\" பொத்தானைப் பயனபடுத்தி சோதனை செய்யவும்",
 'userjsyoucanpreview' => "'''சிறு உதவி:''' தங்களது ஜாவா-வரிவடிவத்தை (JavaScript-ஐ) சேமிப்பதற்கு முன்பாக \"{{int:showpreview}}\" பொத்தானைப் பயனபடுத்தி சோதனை செய்யவும்",
 'usercsspreview' => "'''உங்களது பயனர் சி.எஸ்.எஸ். இன் முன் தோற்றத்தை மட்டுமே காண்கிறீர்கள் என்பதை நினைவில் கொள்ளவும்.'''
@@ -925,7 +927,7 @@ $1 எனும் பயனரையோ வேறு [[{{MediaWiki:Grouppage-sy
 'history-fieldset-title' => 'வரலாற்றில் தேடவும்',
 'history-show-deleted' => 'நீக்கப்பட்டவை மட்டும்',
 'histfirst' => 'மிகப் பழைய',
-'histlast' => 'மிக புதிய',
+'histlast' => 'மிகப் புதிய',
 'historysize' => '({{PLURAL:$1|1 பைட்டு|$1 பைட்டுகள்}})',
 'historyempty' => '(வெற்று)',
 
@@ -1178,7 +1180,7 @@ $1",
 'prefs-rendering' => 'தோற்றம்',
 'saveprefs' => 'சேமி',
 'resetprefs' => 'சேமிக்காத மாற்றங்கள் நீக்குக',
-'restoreprefs' => 'எல்லோருக்கும் பொதுவான வடிவமைப்பைத் திரும்பக்கொண்டுவரவும்.',
+'restoreprefs' => 'எல்லோருக்கும் பொதுவான வடிவமைப்பைத் திரும்பக்கொண்டுவரவும் (எல்லா பிறிவுகளிலும்).',
 'prefs-editing' => 'தொகுத்தல்',
 'rows' => 'நிரைகள் (கிடை வரிசைகள்):',
 'columns' => 'நிரல்கள்',
@@ -1236,7 +1238,7 @@ $1",
 
 அது $1 {{PLURAL:$1|எழுத்து|எழுத்துக்களுக்கு}} மேல் இருக்கக்கூடாது.',
 'yourgender' => 'பால்:',
-'gender-unknown' => 'à®\95à¯\81றிபà¯\8dபிà®\9fபà¯\8dபà®\9fவில்லை',
+'gender-unknown' => 'நானà¯\8d à®\95à¯\81றிபà¯\8dபிà®\9f à®µà®¿à®°à¯\81à®®à¯\8dபவில்லை',
 'gender-male' => 'ஆண்',
 'gender-female' => 'பெண்',
 'prefs-help-gender' => 'விருப்பத் தேர்வுதான்: ஒருவரைக் குறிப்பிடும்பொழுது, அவருடைய பால் சரியானதாக இருக்க மென்கலம் பயன்படுத்தும் தகவல். இத்தகவல் பொதுவில் கிடைக்கும்படி இனி இருக்கும்.',
@@ -1250,7 +1252,7 @@ $1",
 'prefs-signature' => 'கையெழுத்து',
 'prefs-dateformat' => 'தேதியின் வடிவமைப்பு',
 'prefs-timeoffset' => 'நேர வித்தியாசம்',
-'prefs-advancedediting' => 'à®®à¯\87à®®à¯\8dபà®\9fà¯\8dà®\9f விருப்பத்தேர்வுகள்',
+'prefs-advancedediting' => 'பà¯\8aதà¯\81 விருப்பத்தேர்வுகள்',
 'prefs-editor' => 'தொகுப்பாளர்',
 'prefs-preview' => 'முன்தோற்றம்',
 'prefs-advancedrc' => 'மேம்பட்ட விருப்பத்தேர்வுகள்',
@@ -1261,6 +1263,7 @@ $1",
 'prefs-displaysearchoptions' => 'விருப்பத்தேர்வுகளைக் காட்டு',
 'prefs-displaywatchlist' => 'விருப்பத்தேர்வுகளைக் காட்டு',
 'prefs-diffs' => 'வித்தியாசங்கள்',
+'prefs-help-prefershttps' => 'இந்த விருப்பத்தேர்வு உங்களின் அடுத்த புகுபதிகையிலிருந்து செயல்பாட்டுக்கு வரும்.',
 
 # User preference: email validation using jQuery
 'email-address-validity-valid' => 'மின்னஞ்சல் முகவரி முறையானதாகத் தோன்றுகிறது',
@@ -1286,9 +1289,11 @@ $1",
 'userrights-no-interwiki' => 'ஏனைய விக்கிகளில் பயனர் உரிமைகளை மாற்றும் அனுமதி உங்களுக்குக் கிடையாது.',
 'userrights-nodatabase' => '$1 தரவுத்தளம் கிடையாது அல்லது உள்ளக விக்கியில் கிடையாது.',
 'userrights-nologin' => 'பயனர் உரிமைகளை வழங்குவதற்கு நீங்கள் நிர்வாகி கணக்கில் [[Special:UserLogin|புகுபதிகை]] செய்ய வேண்டும்.',
-'userrights-notallowed' => 'பயனரà¯\8d à®\89ரிமà¯\88à®\95ளà¯\88 à®®à®¾à®±à¯\8dà®±à¯\81à®®à¯\8d à®\85னà¯\81மதி à®\89à®\99à¯\8dà®\95ளà¯\8d à®ªà®¯à®©à®°à¯\8d à®\95ணà®\95à¯\8dà®\95à¯\81à®\95à¯\8d கிடையாது.',
+'userrights-notallowed' => 'பயனரà¯\8d à®\89ரிமà¯\88à®\95ளà¯\88 à®®à®¾à®±à¯\8dà®±à¯\81à®®à¯\8d à®\85னà¯\81மதி à®\89à®\99à¯\8dà®\95ளà¯\81à®\95à¯\8dà®\95à¯\81 கிடையாது.',
 'userrights-changeable-col' => 'நீங்கள் மாற்றக்கூடிய குழுக்கள்',
 'userrights-unchangeable-col' => 'நீங்கள் மாற்ற முடியாத குழுக்கள்',
+'userrights-conflict' => 'பயனர் உரிமைகளின் மாற்றங்களில் முரண்பாடு உள்ளது! மறு ஆய்வு செய்து, உங்கள் மாற்றங்களை உறுதி செய்க.',
+'userrights-removed-self' => 'நீங்கள் உங்களது சொந்த உரிமைகளை வெற்றிகரமாக நீக்கியுள்ளீர்கள். இதனால் நீங்கள் இனி இந்தப்பக்கத்தினை பார்க்க இயலாது.',
 
 # Groups
 'group' => 'குழு:',
@@ -1332,7 +1337,7 @@ $1",
 'right-reupload-shared' => 'பகிர்விலுள்ள ஊடகக் கிடங்கியில் உள்ள கோப்புகளை உள்ளகப் பயன்பாட்டுக்காக மேலுரிமையுடன் பதிவேற்று',
 'right-upload_by_url' => 'யூ.ஆர்.எல். ஒன்றிலிருந்து கோப்பை பதிவேற்றல்',
 'right-purge' => 'உறுதிப்படுத்தல் பக்கமெதுவுமின்றி பக்க இடைமாற்றை நீக்கல்',
-'right-autoconfirmed' => 'பà®\95à¯\81தியாà®\95 à®\95ாà®\95à¯\8dà®\95பà¯\8dபà®\9fà¯\8dà®\9f à®ªà®\95à¯\8dà®\95à®\99à¯\8dà®\95ளà¯\88 à®¤à¯\8aà®\95à¯\81தà¯\8dதல்',
+'right-autoconfirmed' => 'à®\87ணà¯\88ய à®¨à¯\86றிமà¯\81à®±à¯\88 à®®à¯\81à®\95வரியினà¯\8d (IP) à®\85à®\9fிபà¯\8dபà®\9fà¯\88யிலà¯\8d à®\89ளà¯\8dள à®µà®¿à®\95ித à®µà®°à®®à¯\8dபà¯\81à®\95ளினà¯\8d à®\95à®\9fà¯\8dà®\9fà¯\81பà¯\8dபாà®\9fà¯\8dà®\9fà¯\81à®\95à¯\8dà®\95à¯\81 à®\89à®\9fà¯\8dபà®\9fாதà¯\80à®°à¯\8dà®\95ள்',
 'right-bot' => 'தானாக செய்யப்பட்டசெயலாக கருதப்படும்',
 'right-nominornewtalk' => 'உரையாடல் பக்கங்களில் செய்யும் சிறு தொகுப்புகளை அறிவிக்கப் புதிய செய்தி ஏதும் அனுப்பவேண்டாம்.',
 'right-apihighlimits' => 'உயர் தரம் கொண்ட  API கேள்விளை பயன்படுத்தவும்',
@@ -1410,8 +1415,8 @@ $1",
 'action-block' => 'இப்பயனரை மேலும் தொகுக்க அனுமதிக்க வேண்டாம்',
 'action-protect' => 'இந்த பக்கத்திற்கான பாதுகாப்பு நிலைகளை மாற்றவும்',
 'action-rollback' => 'ஒரு குறிப்பிட்ட பக்கத்தை திருத்திய பயனரின் திருத்தங்களை பழைய நிலைமைக்கு மாற்றவும்.',
-'action-import' => 'மறà¯\8dà®±à¯\8aà®°à¯\81 à®µà®¿à®\95à¯\8dà®\95ியிலà¯\8d à®\87à®°à¯\81நà¯\8dதà¯\81 à®\87பà¯\8dபà®\95à¯\8dà®\95தà¯\8dதை இறக்குமதி செய்யவும்',
-'action-importupload' => 'à®\95à¯\8bபà¯\8dபà¯\81 à®ªà®¤à®¿à®µà¯\87à®±à¯\8dறதà¯\8dதிலிரà¯\81நà¯\8dதà¯\81 à®\87பà¯\8dபà®\95à¯\8dà®\95தà¯\8dதை இறக்கவும்',
+'action-import' => 'மறà¯\8dà®±à¯\8aà®°à¯\81 à®µà®¿à®\95à¯\8dà®\95ியிலà¯\8d à®\87à®°à¯\81நà¯\8dதà¯\81 à®ªà®\95à¯\8dà®\95à®\99à¯\8dà®\95ளை இறக்குமதி செய்யவும்',
+'action-importupload' => 'à®\95à¯\8bபà¯\8dபà¯\81 à®ªà®¤à®¿à®µà¯\87à®±à¯\8dறதà¯\8dதிலிரà¯\81நà¯\8dதà¯\81 à®ªà®\95à¯\8dà®\95à®\99à¯\8dà®\95ளை இறக்கவும்',
 'action-patrol' => 'மற்றவர்களின் தொகுப்புகளைப் பார்வையிட்டதாகக் குறிக்கவும்',
 'action-autopatrol' => 'உங்களது திருத்தத்தைப் பார்வையிட்டதாகக் குறிப்பிட்டுக் கொள்க.',
 'action-unwatchedpages' => 'கவனிக்கப்படாத பக்கங்களின் பட்டியலைப் பார்க்க',
@@ -1420,6 +1425,8 @@ $1",
 'action-userrights-interwiki' => 'ஏனைய விக்கித் தளங்களின் பயனர் உரிமைகளைத் தொகு',
 'action-siteadmin' => 'தரவுதளத்தை பூட்டு அல்லது  பூட்டாதே',
 'action-sendemail' => 'மின்னஞ்சல்கள் அனுப்பு',
+'action-editmywatchlist' => 'உங்கள் கவனிப்பு பட்டியலை தொகு',
+'action-viewmywatchlist' => 'உங்கள் கவனிப்பு பட்டியலை பார்',
 
 # Recent changes
 'nchanges' => '{{PLURAL:$1|ஓர் மாற்றம்|$1 மாற்றங்கள்}}',
@@ -1453,7 +1460,7 @@ $1",
 'rc_categories_any' => 'ஏதாவது',
 'rc-change-size-new' => '$1 {{PLURAL:$1|பைட்டு|பைட்டுகள்}} -மாற்றத்திற்குப் பிறகு',
 'newsectionsummary' => '/* $1 */ புதிய பகுதி',
-'rc-enhanced-expand' => 'விவரà®\99à¯\8dà®\95ளà¯\88à®\95à¯\8d à®\95ாà®\9fà¯\8dà®\9fà¯\81 (à®\9aாவாநிரலà¯\8d à®¤à¯\87வà¯\88)',
+'rc-enhanced-expand' => 'விவரதà¯\8dதà¯\88 à®\95ாà®\9fà¯\8dà®\9fà¯\81',
 'rc-enhanced-hide' => 'விவரங்களை மறை',
 'rc-old-title' => 'முதலில் "$1" என உருவாக்கப்பட்டது',
 
@@ -1472,7 +1479,7 @@ $1",
 'reuploaddesc' => 'பதிவேற்றத்தை நிறுத்திவிட்டு பதிவேற்றும் படிவத்துக்கு மீளச் செல்க',
 'upload-tryagain' => 'மாற்றம் செய்யப்பட்ட கோப்புத் தகவலைச் சமர்ப்பிக்கவும்',
 'uploadnologin' => 'புகுபதிகை செய்யப்படவில்லை',
-'uploadnologintext' => 'கோப்புகளைப் பதிவேற்றம் செய்வதற்கு நீங்கள் [[Special:UserLogin|புகுபதிகை]] செய்திருக்க வேண்டும்.',
+'uploadnologintext' => 'கோப்புகளைப் பதிவேற்ற நீங்கள் $1 செய்திருக்க வேண்டும்.',
 'upload_directory_missing' => 'தரவேற்றப்படும் அடைவு (டைரெக்டரி) ( $1 ) ஐ காணவில்லை, அதோடு வலைத்தளவழங்கி வழியாக உருவாக்கவும் இயலவில்லை.',
 'upload_directory_read_only' => 'பதிவேற்ற அடைவு ($1) வழங்கனால் எழுதப்படமுடியாது.',
 'uploaderror' => 'பதிவேற்றத் தவறு',
@@ -1646,9 +1653,9 @@ $1',
 # img_auth script messages
 'img-auth-accessdenied' => 'அனுமதி மறுக்கப்பட்டது',
 'img-auth-nopathinfo' => 'PATH_INFO காணவில்லை.
-உங்கள் வழங்கி  இந்தத் தகவலை அனுப்புமாறு அமைக்கப்படவில்லை
-இது  சிச்சிஐ (CGI)- அடிப்படையிலானதாக இருக்கலாம் மற்றும் img_authக்கும் ஆதரவு கிடையாது.
-பார்க்கவும் : https://www.mediawiki.org/wiki/Manual:Image_Authorization See image authorization.',
+உங்கள் வழங்கி இந்தத் தகவலை அனுப்ப அமைக்கப்படவில்லை
+இது சிஜிஐ (CGI)- அடிப்படையிலானதாகவோ img_auth-ஐ ஆதரக்காததாகவோ இருக்கலாம் .
+பார்க்கவும் https://www.mediawiki.org/wiki/Manual:Image_Authorization.',
 'img-auth-notindir' => 'கோரிய பாதை இந்த குறிப்பிட்ட தகவலேற்று கோப்புறையில் இல்லை.',
 'img-auth-badtitle' => 'செல்லத்தக்க தலைப்பு "$1" ஐ கட்ட இயலவில்லை.',
 'img-auth-nologinnWL' => 'நீங்கள் புகுபதிகை செய்யவில்லை, மேலும் "$1"  அனுமதிக்கப்பெற்ற பட்டியலில் இல்லை',
@@ -1684,8 +1691,7 @@ $1',
 'upload_source_file' => ' (உங்கள் கணணியில் உள்ள கோப்பு)',
 
 # Special:ListFiles
-'listfiles-summary' => 'இந்த சிறப்புப் பக்கம் அனைத்து தரவேற்றப்பட்ட கோப்புகளையும் காண்பிக்கும்.
-பயனர் பெயர் மூலம் வடிகட்டும் போது, அந்த பயனர் தரவேற்றிய கோப்பின் மிக சமீபத்திய பதிப்பு மட்டும் காண்பிக்கப்பட்டுள்ளது.',
+'listfiles-summary' => 'இச்சிறப்புப் பக்கம் பதிவேற்றப்பட்ட கோப்புகளைப் பட்டியலிடுகிறது.',
 'listfiles_search_for' => 'பின்வரும் பெயருள்ள ஊடகக் கோப்பைத் தேடு:',
 'imgfile' => 'கோப்பு',
 'listfiles' => 'படிமங்களின் பட்டியல்',
@@ -1976,8 +1982,9 @@ $1',
 
 # Special:ListGroupRights
 'listgrouprights' => 'பயனர் குழு உரிமைகள்',
-'listgrouprights-key' => '<span class="listgrouprights-granted">உரிமை வழங்கப்பட்டது</span>
- * <span class="listgrouprights-revoked">உரிமை பறிக்கபட்டது</span>',
+'listgrouprights-key' => 'குறியீட்டு விளக்கம்:
+* <span class="listgrouprights-granted">உரிமை வழங்கப்பட்டது</span>
+* <span class="listgrouprights-revoked">உரிமை பறிக்கபட்டது</span>',
 'listgrouprights-group' => 'குழு',
 'listgrouprights-rights' => 'உரிமைகள்',
 'listgrouprights-helppage' => 'Help:குழு உரிமைகள்',
@@ -2049,8 +2056,8 @@ $1',
 'notanarticle' => 'ஒரு கட்டுரைப் பக்கமல்ல',
 'notvisiblerev' => 'திருத்தம் நீக்கப்பட்டுள்ளது',
 'watchlist-details' => 'பேச்சுப் பக்கங்களைத் தவிர்த்து, {{PLURAL:$1|$1 பக்கம் கவனிக்கப்பட்டது.|$1 பக்கங்கள் கவனிக்கப்பட்டன.}}',
-'wlheader-enotif' => 'மினà¯\8dனà®\9eà¯\8dà®\9aலà¯\8d à®\85றிவிதà¯\8dதலà¯\8dகள் செயல்படுத்தப்பட்டுள்ளன.',
-'wlheader-showupdated' => "à®\89மதà¯\81 à®\95à®\9fà¯\88à®\9aி à®µà®°à¯\81à®\95à¯\88à®\95à¯\8dà®\95à¯\81பà¯\8d à®ªà®¿à®©à¯\8dனரà¯\8d à®®à®¾à®±à¯\8dà®±à®\99à¯\8dà®\95ளà¯\8d à®\9aà¯\86யà¯\8dயபà¯\8dபà®\9fà¯\8dà®\9f à®ªà®\95à¯\8dà®\95à®\99à¯\8dà®\95ளà¯\8d '''தà®\9fிதà¯\8dத à®\8eà®´à¯\81தà¯\8dதà¯\81à®\95à¯\8dà®\95ளால்''' காட்டப்பட்டுள்ளன",
+'wlheader-enotif' => 'மினà¯\8dனà®\9eà¯\8dà®\9aலà¯\8d à®\85றிவிபà¯\8dபà¯\81கள் செயல்படுத்தப்பட்டுள்ளன.',
+'wlheader-showupdated' => "à®\89à®\99à¯\8dà®\95ளà¯\8d à®\95à®\9fà¯\88à®\9aி à®µà®°à¯\81à®\95à¯\88à®\95à¯\8dà®\95à¯\81பà¯\8d à®ªà®¿à®©à¯\8dனரà¯\8d à®®à®¾à®±à¯\8dà®±à®\99à¯\8dà®\95ளà¯\8d à®\9aà¯\86யà¯\8dயபà¯\8dபà®\9fà¯\8dà®\9f à®ªà®\95à¯\8dà®\95à®\99à¯\8dà®\95ளà¯\8d '''தà®\9fிதà¯\8dத à®\8eà®´à¯\81தà¯\8dதà¯\81à®\95à¯\8dà®\95ளில்''' காட்டப்பட்டுள்ளன",
 'watchmethod-recent' => 'கவனிக்கப்படுகின்ற பக்கங்களுக்காக, அண்மைய தொகுப்புகள் தேடிப் பார்க்கப்படுகிறன',
 'watchmethod-list' => 'அண்மைய தொகுப்புகளுக்காக, கவனிக்கப்படுகின்ற பக்கங்கள் தேடிப் பார்க்கப்படுகிறன',
 'watchlistcontains' => 'உங்கள் கவனிப்புப் பட்டியல் {{PLURAL:$1|ஒரு பக்கத்தைக்|$1 பக்கங்களைக்}} கொண்டுள்ளது.',
@@ -2119,10 +2126,12 @@ $NEWPAGE
 'deletecomment' => 'காரணம்:',
 'deleteotherreason' => 'வேறு மேலதிக காரணம்:',
 'deletereasonotherlist' => 'வேறு காரணம்',
-'deletereason-dropdown' => '*பொதுவான நீக்கல் காரணங்கள்
-** à®\95ாபà¯\8dபà¯\81ரிமà¯\88 à®®à¯\80றபà¯\8dபà®\9fà¯\8dà®\9fà®®à¯\88
+'deletereason-dropdown' => '* பொதுவான நீக்கல் காரணங்கள்
+** à®\8eரிதமà¯\8d/வà¯\80ணà¯\8dà®\9aà¯\86யà¯\8dதிà®\95ளà¯\8d
 ** விசமத் தொகுப்பு
-** ஆசிரியர் வேண்டுகோள்',
+** காப்புரிமை மீறப்பட்டமை
+** ஆசிரியர் வேண்டுகோள்
+** உடைந்த வழிமாற்று',
 'delete-edit-reasonlist' => 'நீக்கல் காரணங்களைத் தொகு',
 'delete-toobig' => 'இப்பக்கம் அதிகமான திருத்தங்களை கொண்டுள்ளது, குறிப்பாக $1 {{PLURAL:$1|திருத்தத்திற்கு|திருத்தங்களிற்கு}} மேல்.
 {{SITENAME}} தளத்தின் தரவுகள் தற்செயலாக அழிந்துப்போவதை தடுப்பதற்க்காக இவ்வாறான பக்கங்கள் நீக்கப்படுவது முடக்கப்பட்டுள்ளது.',
@@ -2143,7 +2152,7 @@ $NEWPAGE
 இப்பக்கத்தை கடைசியாகத் தொகுத்தவர் [[User:$3|$3]] ([[User talk:$3|Talk]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).',
 'editcomment' => "தொகுப்பிற்கான சிறுகுறிப்புக்கள் இவை:\"''\$1''\".",
 'revertpage' => '[[Special:Contributions/$2|$2]] ([[User talk:$2|பேச்சு]]) செய்தத் தொகுப்புகள் நீக்கப்பட்டு [[User:$1|$1]] இன் பதிப்புக்கு முன்நிலையாக்கப்பட்டது',
-'revertpage-nouser' => '(பயனர் பெயர் நீக்கப்பட்டது) செய்த தொகுப்புகளை இல்லாது செய்து, [[User:$1|$1]] கடைசியாகச் செய்த திருத்தத்துக்கு மாற்றப்பட்டது',
+'revertpage-nouser' => 'மறைக்கப்பட்ட பயனர் செய்த தொகுப்புகள் இல்லாது செய்யப்பட்டு, {{GENDER:$1|[[User:$1|$1]]}} கடைசியாகச் செய்த திருத்தத்துக்கு முன்நிலையாக்கப்பட்டது',
 'rollback-success' => '$1 செய்தத் தொகுப்புகள் நீக்கப்பட்டு $2 இன் பதிப்புக்கு முன்நிலையாக்கப்பட்டது.',
 
 # Edit tokens
@@ -2177,8 +2186,8 @@ $NEWPAGE
 '''$1''' பக்கத்துக்கான நடப்பு அமைப்புகள் பின்வருமாறு:",
 'protect-cascadeon' => 'இந்தப்பக்கம் படிநிலை காப்புகுட்படுத்தப்பட்ட {{PLURAL:$1|பக்கத்திற்கு|பக்கங்களிற்கு}} இணைக்கப்பட்டுள்ளமையால் காப்புச் செய்யப்பட்டுள்ளது. இந்த பக்கத்தின் காப்பு நிலையை நீங்கள் மாற்றம் செய்யலாம் எனினும் இது படிநிலை காப்பினை மாற்றம் செய்யாது.',
 'protect-default' => 'அனைத்துப் பயனரையும் உள்ளிடு',
-'protect-fallback' => '"$1" à®\85னà¯\81மதி à®¤à¯\87வà¯\88',
-'protect-level-autoconfirmed' => 'பà¯\81திய, à®ªà®¤à®¿à®µà¯\81 à®\9aà¯\86யà¯\8dயாத à®ªà®¯à®©à®°à¯\8dà®\95ளà¯\88தà¯\8d à®¤à®\9fà¯\88 à®\9aà¯\86யà¯\8d',
+'protect-fallback' => '"$1" à®\85னà¯\81மதி à®ªà¯\86à®±à¯\8dà®± à®ªà®¯à®©à®°à¯\8dà®\95ளà¯\81à®\95à¯\8dà®\95à¯\81 à®®à®\9fà¯\8dà®\9fà¯\81à®®à¯\8d à®\87à®\9aà¯\88வளி',
+'protect-level-autoconfirmed' => 'தானாà®\95 à®\89à®±à¯\81தியளிà®\95à¯\8dà®\95பà¯\8dபà®\9fà¯\8dà®\9f à®ªà®¯à®©à®°à¯\8dà®\95ளà¯\88 à®®à®\9fà¯\8dà®\9fà¯\81à®®à¯\8d à®\85னà¯\81மதி',
 'protect-level-sysop' => 'நிருவாகிகளை மட்டும் அனுமதிக்கவும்',
 'protect-summary-cascade' => 'படிநிலை',
 'protect-expiring' => '$1 (UTC) மணிக்கு காலாவதியாகிறது',
@@ -2244,7 +2253,8 @@ $NEWPAGE
 'undeletedrevisions' => '{{PLURAL:$1|1 திருத்தம் மீட்கப்பட்டது|$1 திருத்தங்கள் மீட்கப்பட்டன}}',
 'undeletedrevisions-files' => '{{PLURAL:$1|1 திருத்தம்|$1 திருத்தங்கள்}} மற்றும் {{PLURAL:$2|1 கோப்பு|$2 கோப்புகள்}} மீட்கப்பட்டன.',
 'undeletedfiles' => '{{PLURAL:$1|ஒரு கோப்பு மீட்டெடுக்கப்பட்டது|$1 கோப்புகள் மீட்டெடுக்கப்பட்டன}}',
-'cannotundelete' => 'மீள்வித்தல் தோல்வி: $1',
+'cannotundelete' => 'மீள்வித்தல் தோல்வி:
+$1',
 'undeletedpage' => "'''$1 மீட்கப்பட்டது'''
 
 அண்மைய நீக்கல்களுக்கும் மீட்புக்களுக்கும் [[Special:Log/delete|நீக்கல் பதிவைப்]] பார்க்கவும்.",
@@ -2274,7 +2284,7 @@ $1',
 'blanknamespace' => '(முதன்மை)',
 
 # Contributions
-'contributions' => 'பயனர் பங்களிப்புக்கள்',
+'contributions' => '{{GENDER:$1|பயனர்}} பங்களிப்புக்கள்',
 'contributions-title' => '$1 இற்கான பயனர் பங்களிப்புகள்',
 'mycontris' => 'பங்களிப்புக்கள்',
 'contribsub2' => '$1 பயனரின் ($2)',
@@ -2358,7 +2368,7 @@ $1',
 'badipaddress' => 'செல்லுபடியற்ற ஐ.பி. முகவரி',
 'blockipsuccesssub' => 'தடுப்பு வெற்றி',
 'blockipsuccesstext' => '[[Special:Contributions/$1|$1]] தடுக்கப்பட்டுள்ளார்.<br />
-தà®\9fà¯\81பà¯\8dபà¯\88 à®®à¯\80ளாயà¯\8dவà¯\81 à®\9aà¯\86யà¯\8dய [[Special:BlockList|தà®\9fà¯\81à®\95à¯\8dà®\95பà¯\8dபà®\9fà¯\8dà®\9f à®\90.பி. à®®à¯\81à®\95வரிà®\95ளினà¯\8d à®ªà®\9fà¯\8dà®\9fியலà¯\88பà¯\8d]] à®ªà®¾à®°்.',
+தà®\9fà¯\81பà¯\8dபà¯\88 à®®à®±à¯\81à®\86யà¯\8dவà¯\81 à®\9aà¯\86யà¯\8dய [[Special:BlockList|தà®\9fà¯\81பà¯\8dபà¯\81 à®ªà®\9fà¯\8dà®\9fியலà¯\88பà¯\8d]] à®ªà®¾à®°à¯\8dà®\95வà¯\81à®®்.',
 'ipb-blockingself' => 'நீங்கள் உங்களையே தடுக்க முயல்கிறீர்கள்! உறுதியாக இதை செய்ய விரும்புகிறீர்களா?',
 'ipb-edit-dropdown' => 'தடை காரணங்கள் தொகு',
 'ipb-unblock-addr' => '$1 இன் தடையை நீக்கு',
@@ -2429,12 +2439,9 @@ $1',
 'ipb_blocked_as_range' => 'தவறு:இந்த ஐ.பி. $1 நேரடியாக தடைச் செய்யப்படவில்லை எனவே தடையை நீக்க முடியாது. இது $2 என்ற ஐ.பி. வீச்சு தடைச் செய்யப்பட்டதால் தடைச் செய்யப்பட்டுள்ளது இவ்வீச்சிற்கான தடையை நீக்க முடியும்.',
 'ip_range_invalid' => 'செல்லுபடியற்ற ஐ.பி. வீச்சு',
 'ip_range_toolarge' => '/$1 க்கு பெரிய வரம்பு தடுப்புகள் அனுமதிக்கப்படவில்லை.',
-'blockme' => 'என்னை தடைச் செய்',
 'proxyblocker' => 'மறைவணுக்கம் (புரொக்சி) தடுப்பி',
-'proxyblocker-disabled' => 'இந்தச் செயல் செயலிழக்கச் முடக்கப்பட்டுள்ளது.',
 'proxyblockreason' => 'உங்கள் IP முகவரி தடை செய்யப்பட்டுள்ளது ஏனெனில் இது ஒரு திறந்த பதிலி(proxy).
 தயவுசெய்து உங்கள் இணைய சேவை வழங்குபவரையோ அல்லது உங்கள் நிறுவனத்தின் தொழில்நுட்ப ஆதரவையோ தொடர்பு கொள்ளவும் மேலும் அவர்களிடம் இந்த கடுமையான பாதுகாப்பு பிரச்சினை பற்றி தெரிவியுங்கள்.',
-'proxyblocksuccess' => 'வெற்றி.',
 'sorbsreason' => 'உங்கள் IP முகவரி ஒரு திறந்த பதிலியாக  DNSBL  பயன்படுத்தப்படுவதாக  {{SITENAME}} ல் பட்டியலிடப்பட்டுள்ளது.',
 'sorbs_create_account_reason' => 'உங்கள் IP முகவரி ஒரு திறந்த பதிலியாக  DNSBL  பயன்படுத்தப்படுவதாக  {{SITENAME}} ல் பட்டியலிடப்பட்டுள்ளது.
 உங்களால் கணக்கை உருவாக்க இயலாது.',
@@ -2763,13 +2770,13 @@ $1',
 'pageinfo-length' => 'பக்க நீளம் (எண்ணுண்மிகளில்)',
 'pageinfo-article-id' => 'பக்க அடையாள இலக்கம்',
 'pageinfo-language' => 'பக்க உள்ளடக்க மொழி',
-'pageinfo-robot-policy' => 'தà¯\87à®\9fà®±à¯\8dபà¯\8aறி à®¨à®¿à®²à¯\88à®®à¯\88',
-'pageinfo-robot-index' => 'வà®\95à¯\88பà¯\8dபà®\9fà®\95à¯\8dà®\95à¯\82à®\9fியது',
-'pageinfo-robot-noindex' => 'வà®\95à¯\88பà¯\8dபà®\9fாததà¯\81.',
+'pageinfo-robot-policy' => 'தானியà®\99à¯\8dà®\95ி à®®à¯\82லமà¯\8d à®\85à®\9fà¯\8dà®\9fவணà¯\88பà¯\8dபà®\9fà¯\81தà¯\8dதலà¯\8d',
+'pageinfo-robot-index' => 'à®\85னà¯\81மதிà®\95à¯\8dà®\95பà¯\8dபà®\9fà¯\81à®\95ிறது',
+'pageinfo-robot-noindex' => 'à®\85னà¯\81மதிà®\95à¯\8dà®\95பà¯\8dபà®\9fாததà¯\81',
 'pageinfo-views' => 'காட்சிகள் எண்ணிக்கை',
 'pageinfo-watchers' => 'பக்கப் பார்வையாளர்கள் எண்ணிக்கை',
 'pageinfo-few-watchers' => 'விட குறைவானது $1 {{PLURAL:$1|watcher|watchers}}',
-'pageinfo-redirects-name' => 'à®\87நà¯\8dதபà¯\8d à®ªà®\95à¯\8dà®\95தà¯\8dதிறà¯\8dà®\95ான à®µà®´à®¿à®®à®¾à®±à¯\8dà®±à¯\81à®\95ளà¯\8d',
+'pageinfo-redirects-name' => 'à®\87நà¯\8dதபà¯\8d à®ªà®\95à¯\8dà®\95தà¯\8dதிறà¯\8dà®\95ான à®µà®´à®¿à®®à®¾à®±à¯\8dà®±à¯\81à®\95ளினà¯\8d à®\8eணà¯\8dணிà®\95à¯\8dà®\95à¯\88',
 'pageinfo-subpages-name' => 'இந்தப் பக்கத்தின் துணைப் பக்கங்கள்',
 'pageinfo-subpages-value' => '$1 ($2 {{PLURAL:$2|வழிமாற்று|வழிமாற்றுகள்}}; $3 {{PLURAL:$3|வழிமாற்றில்லாதது|வழிமாற்றில்லாதவை}})',
 'pageinfo-firstuser' => 'பக்க உருவாக்குநர்',
@@ -3088,7 +3095,7 @@ $1',
 'exif-compression-1' => 'சுருக்கப்படாத',
 
 'exif-copyrighted-true' => 'பதிப்புரிமைப்பட்டது',
-'exif-copyrighted-false' => 'பà¯\8aதà¯\81 à®\95ளமà¯\8d',
+'exif-copyrighted-false' => 'பதிபà¯\8dபà¯\81ரிமà¯\88 à®¨à®¿à®²à¯\88யà¯\88 à®¤à®¿à®°à®¿à®µà¯\81à®\9aà¯\86யà¯\8dயபà¯\8dபà®\9fவிலà¯\8dலà¯\88',
 
 'exif-unknowndate' => 'நாள் தெரியாது',
 
@@ -3503,7 +3510,7 @@ $5
  * <span class="mw-specialpagerestricted">வரையறுத்த சிறப்புப் பக்கங்கள்.</span>',
 'specialpages-group-maintenance' => 'பராமரிப்பு அறிக்கைகள்',
 'specialpages-group-other' => 'ஏனைய சிறப்புப் பக்கங்கள்',
-'specialpages-group-login' => 'புகுபதிகை / கணக்கு தொடக்கம்',
+'specialpages-group-login' => 'புகுபதிகை/பயனர் கணக்கு தொடக்கம்',
 'specialpages-group-changes' => 'அண்மைய மாற்றங்களும் பதிகைகளும்',
 'specialpages-group-media' => 'ஊடக அறிக்கைகளும் பதிவேற்றங்களும்',
 'specialpages-group-users' => 'பயனர்களும் உரிமைகளும்',
@@ -3584,7 +3591,7 @@ $5
 'sqlite-no-fts' => '$1 முழு-உரை தேடல் ஆதரவு இல்லாமல்',
 
 # New logging system
-'logentry-delete-delete' => '$3 பக்கத்தை $1 நீக்கினார்',
+'logentry-delete-delete' => '$3 பக்கத்தை $1 {{GENDER:$2|நீக்கினார்}}',
 'logentry-delete-restore' => '$1 பயனரால் $3 பக்கம் மீட்டமைக்கப்பட்டது',
 'logentry-delete-event' => '$1 மாற்றிய காட்சித்தன்மை  {{PLURAL:$5| ஒரு நிகழ்வு குறிப்பேடு| $5  நிகழ்வுகள் குறிப்பேடு}} இதில்   $3 :$4',
 'logentry-delete-revision' => '$1 மாற்றப்பட்ட  காட்சித்தன்மைக்கு  {{PLURAL:$5| ஒருபரிசீலனை| $5  பரிசீலனைகளுக்கும்}} இந்த பக்கம்  $3 :$4',
index 2f40eb2..d51307f 100644 (file)
@@ -358,7 +358,7 @@ $messages = array(
 'articlepage' => 'విషయపు పేజీని చూడండి',
 'talk' => 'చర్చ',
 'views' => 'చూపులు',
-'toolbox' => 'పనిముట్ల పెట్టె',
+'toolbox' => 'పనిముట్ల',
 'userpage' => 'వాడుకరి పేజీని చూడండి',
 'projectpage' => 'ప్రాజెక్టు పేజీని చూడు',
 'imagepage' => 'ఫైలు పేజీని చూడండి',
@@ -367,7 +367,7 @@ $messages = array(
 'viewhelppage' => 'సహాయం పేజీని చూడు',
 'categorypage' => 'వర్గం పేజీని చూడు',
 'viewtalkpage' => 'చర్చను చూడు',
-'otherlanguages' => 'à°\87తర à°­à°¾à°·à°²à°²à±\8a',
+'otherlanguages' => 'à°\87తర à°­à°¾à°·à°²à°²à±\8b',
 'redirectedfrom' => '($1 నుండి మళ్ళించబడింది)',
 'redirectpagesub' => 'దారిమార్పు పుట',
 'lastmodifiedat' => 'ఈ పేజీకి $2, $1న చివరి మార్పు జరిగినది.',
@@ -388,7 +388,7 @@ $1',
 # All link text and link target definitions of links into project namespace that get used by other message strings, with the exception of user group pages (see grouppage).
 'aboutsite' => '{{SITENAME}} గురించి',
 'aboutpage' => 'Project:గురించి',
-'copyright' => 'విషయ సంగ్రహం $1  కి లోబడి లభ్యం.',
+'copyright' => 'విషయం $1 కి లోబడి లభ్యం, వేరుగా పేర్కొంటే తప్ప.',
 'copyrightpage' => '{{ns:project}}:ప్రచురణ హక్కులు',
 'currentevents' => 'ఇప్పటి ముచ్చట్లు',
 'currentevents-url' => 'Project:ఇప్పటి ముచ్చట్లు',
@@ -890,7 +890,7 @@ $2
 'nocreate-loggedin' => 'కొత్త పేజీలను సృష్టించేందుకు మీకు అనుమతి లేదు.',
 'sectioneditnotsupported-title' => 'విభాగపు దిద్దిబాట్లకి తొడ్పాటు లేదు',
 'sectioneditnotsupported-text' => 'ఈ పేజీలో విభాగాల దిద్దుబాటుకి తోడ్పాటు లేదు.',
-'permissionserrors' => 'à°\85à°¨à±\81మతà±\81à°² à°¤à°ªà±\8dపిదాలà±\81',
+'permissionserrors' => 'à°\85à°¨à±\81మతి à°²à±\8bà°ªà°\82',
 'permissionserrorstext' => 'కింద పేర్కొన్న {{PLURAL:$1|కారణం|కారణాల}} మూలంగా, ఆ పని చెయ్యడానికి మీకు అనుమతిలేదు:',
 'permissionserrorstext-withaction' => 'ఈ క్రింది {{PLURAL:$1|కారణం|కారణాల}} వల్ల, మీకు $2 అనుమతి లేదు:',
 'recreate-moveddeleted-warn' => "'''హెచ్చరిక: ఇంతకు మునుపు ఒకసారి తొలగించిన పేజీని మళ్లీ సృష్టిద్దామని మీరు ప్రయత్నిస్తున్నారు.'''
@@ -1031,7 +1031,7 @@ $3 చెప్పిన కారణం: ''$2''",
 * అనుచితమైన వ్యక్తిగత సమాచారం
 * "ఇంటి చిరునామాలు, టెలిఫోను నంబర్లు, సోషల్ సెక్యూరిటీ నంబర్లు, వగైరాలు"',
 'revdelete-legend' => 'సందర్శక నిబంధనలు అమర్చు',
-'revdelete-hide-text' => 'à°\95à±\82à°°à±\8dà°ªà±\81 à°ªà°¾à° à±\8dయానà±\8dని à°¦à°¾à°\9aà±\81',
+'revdelete-hide-text' => 'à°\95à±\82à°°à±\8dà°ªà±\81 à°ªà°¾à° à±\8dà°¯à°\82',
 'revdelete-hide-image' => 'ఫైలులోని విషయాన్ని దాచు',
 'revdelete-hide-name' => 'చర్యను, లక్ష్యాన్నీ దాచు',
 'revdelete-hide-comment' => 'దిద్దుబాటు వ్యాఖ్యను దాచు',
@@ -1326,7 +1326,7 @@ $1",
 'group-bot' => 'బాట్‌లు',
 'group-sysop' => 'నిర్వాహకులు',
 'group-bureaucrat' => 'అధికారులు',
-'group-suppress' => 'పరాà°\95à±\81à°²à±\81',
+'group-suppress' => 'మారà±\8dà°ªà±\81లనà±\81 à°ªà±\82à°°à±\8dతిà°\97à°¾ à°\95నిపిà°\82à°\9aà°\95à±\81à°\82à°¡à°¾à°\9aà±\87à°¯à±\81à°\9f',
 'group-all' => '(అందరూ)',
 
 'group-user-member' => '{{GENDER:$1|వాడుకరి}}',
@@ -1334,14 +1334,14 @@ $1",
 'group-bot-member' => '{{GENDER:$1|బాట్}}',
 'group-sysop-member' => '{{GENDER:$1|నిర్వాహకుడు|నిర్వాహకురాలు}}',
 'group-bureaucrat-member' => '{{GENDER:$1|అధికారి|అధికారిణి}}',
-'group-suppress-member' => 'పరాకు',
+'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}}:పరాà°\95à±\81',
+'grouppage-suppress' => '{{ns:project}}:మారà±\8dà°ªà±\81లనà±\81 à°ªà±\82à°°à±\8dతిà°\97à°¾ à°\95నిపిà°\82à°\9aà°\95à±\81à°\82à°¡à°¾à°\9aà±\87à°¯à±\81à°\9f',
 
 # Rights
 'right-read' => 'పేజీలు చదవడం',
@@ -1481,7 +1481,7 @@ $1",
 'rc_categories_any' => 'ఏదయినా',
 'rc-change-size-new' => 'మార్పు తర్వాత $1 {{PLURAL:$1|బైటు|బైట్లు}}',
 'newsectionsummary' => '/* $1 */ కొత్త విభాగం',
-'rc-enhanced-expand' => 'వివరాలని à°\9aà±\82పిà°\82à°\9aà±\81 (à°\9cావాసà±\8dà°\95à±\8dà°°à°¿à°ªà±\8dà°\9fà±\8d à°\85వసరà°\82)',
+'rc-enhanced-expand' => 'వివరాలనà±\81 à°\9aà±\82పిà°\82à°\9aà±\81',
 'rc-enhanced-hide' => 'వివరాలను దాచు',
 'rc-old-title' => 'మొదట "$1"గా సృష్టించారు',
 
@@ -1902,7 +1902,7 @@ https://www.mediawiki.org/wiki/Manual:Image_Authorization చూడండి.',
 'nopagetext' => 'మీరు అడిగిన పేజీ లేదు',
 'pager-newer-n' => '{{PLURAL:$1|1 కొత్తది|$1 కొత్తవి}}',
 'pager-older-n' => '{{PLURAL:$1|1 పాతది|$1 పాతవి}}',
-'suppress' => 'పరాà°\95à±\81',
+'suppress' => 'మారà±\8dà°ªà±\81లనà±\81 à°ªà±\82à°°à±\8dతిà°\97à°¾ à°\95నిపిà°\82à°\9aà°\95à±\81à°\82à°¡à°¾à°\9aà±\87à°¯à±\81à°\9f',
 'querypage-disabled' => 'పనితీరు కారణాల వలన, ఈ ప్రత్యేకపేజీని అశక్తం చేసాం.',
 
 # Book sources
@@ -2279,9 +2279,9 @@ $1',
 'contributions' => '{{GENDER:$1|వాడుకరి}} రచనలు',
 'contributions-title' => '$1 యొక్క మార్పులు-చేర్పులు',
 'mycontris' => 'మార్పులు చేర్పులు',
-'contribsub2' => '$1 ($2) కొరకు',
+'contribsub2' => '{{GENDER:$3|$1}} ($2) కొరకు',
 'nocontribs' => 'ఈ విధమైన మార్పులేమీ దొరకలేదు.',
-'uctop' => '(à°ªà±\88ది)',
+'uctop' => '(à°ªà±\8dà°°à°¸à±\8dà°¤à±\81à°¤)',
 'month' => 'ఈ నెల నుండి (అంతకు ముందువి):',
 'year' => 'ఈ సంవత్సరం నుండి (అంతకు ముందువి):',
 
@@ -2433,11 +2433,8 @@ $1',
 'ipb_blocked_as_range' => 'లోపం: ఐపీ $1 ను నేరుగా నిరోధించలేదు, అంచేత నిరోధాన్ని రద్దుపరచలేము.  అయితే, అది $2 శ్రేణిలో భాగంగా నిరోధానికి గురైంది, ఈ శ్రేణిపై ఉన్న నిరోధాన్ని రద్దుపరచవచ్చు.',
 'ip_range_invalid' => 'సరైన ఐపీ శ్రేణి కాదు.',
 'ip_range_toolarge' => '/$1  కంటే పెద్దవైన సామూహిక నిరోధాలు అనుమతించబడవు.',
-'blockme' => 'నన్ను నిరోధించు',
 'proxyblocker' => 'ప్రాక్సీ నిరోధకం',
-'proxyblocker-disabled' => 'ఈ ఫంక్షన్ను అశక్తం చేసాం.',
 'proxyblockreason' => 'మీ ఐపీ అడ్రసు ఒక ఓపెన్ ప్రాక్సీ కాబట్టి దాన్ని నిరోధించాం. మీ ఇంటర్నెట్ సేవాదారుని గానీ, సాంకేతిక సహాయకుని గానీ సంప్రదించి తీవ్రమైన ఈ భద్రతా వైఫల్యాన్ని గురించి తెలపండి.',
-'proxyblocksuccess' => 'పూర్తయింది.',
 'sorbsreason' => '{{SITENAME}} వాడే DNSBLలో మీ ఐపీ అడ్రసు ఒక ఓపెన్ ప్రాక్సీగా నమోదై ఉంది.',
 'sorbs_create_account_reason' => 'మీ ఐపీ అడ్రసు DNSBL లో ఓపెను ప్రాక్సీగా నమోదయి ఉంది. మీరు ఎకౌంటును సృష్టించజాలరు.',
 'cant-block-while-blocked' => 'నిరోధంలో ఉన్న మీరు ఇతర వాడుకరులపై నిరోధం అమలుచేయలేరు.',
index 46dcd5c..f7ea5cd 100644 (file)
@@ -1720,11 +1720,8 @@ $1',
 'ipb_cant_unblock' => 'Хато: Нишонаи баста шудани $1 ёфт нашуд. Мумкин аст пештар боз шуда бошад.',
 'ipb_blocked_as_range' => 'Хато: Нишонаи IP-и $1 ба шакли мустақим баста нашудааст ва наметавонад боз шавад. Ин нишона ҳамроҳи $2, баста шуда қобили боз шудан аст.',
 'ip_range_invalid' => 'Сафи IP номӯътабар аст.',
-'blockme' => 'Дастрасии манро қать кун',
 'proxyblocker' => 'Проксибанд',
-'proxyblocker-disabled' => 'Ин амал ғайрифаъол шудааст.',
 'proxyblockreason' => 'Аз сабаби пешкор боз (open proxy) буданаш, нишонаи IP-и шумо баста шудааст. Лутфан бо таъминкунандаи хизматҳои Интернетии худ ё пуштибони техникӣ тамос бигиред ва онҳоро бо ин мушкилии амниятии муҳим огоҳ кунед.',
-'proxyblocksuccess' => 'Анҷом шуд.',
 'sorbsreason' => 'Нишонаи IP-и шумо ҳамчун як проксии кушода дар DNSBL феҳрист шудааст, ки аз тарафи {{SITENAME}} истифода мешавад.',
 'sorbs_create_account_reason' => 'Нишонаи IP-и шумо ҳамчун проксии кушода дар DNSBL, ки аз тарафи {{SITENAME}} истифода мешавад, феҳрист шудааст. Шумо наметавонед ҳисоби корабариеро эҷод кунед',
 
index 07f31cc..40debf6 100644 (file)
@@ -1507,11 +1507,8 @@ Baroi fehristi mahrumijatho va basta şudanhoi amalijoti kununī ba [[Special:Bl
 'ipb_cant_unblock' => 'Xato: Nişonai basta şudani $1 joft naşud. Mumkin ast peştar boz şuda boşad.',
 'ipb_blocked_as_range' => 'Xato: Nişonai IP-i $1 ba şakli mustaqim basta naşudaast va nametavonad boz şavad. In nişona hamrohi $2, basta şuda qobili boz şudan ast.',
 'ip_range_invalid' => "Safi IP nomū'tabar ast.",
-'blockme' => 'Dastrasiji manro qatь kun',
 'proxyblocker' => 'Proksiband',
-'proxyblocker-disabled' => "In amal ƣajrifa'ol şudaast.",
 'proxyblockreason' => "Az sababi peşkor boz (open proxy) budanaş, nişonai IP-i şumo basta şudaast. Lutfan bo ta'minkunandai xizmathoi Internetiji xud jo puştiboni texnikī tamos bigired va onhoro bo in muşkiliji amnijatiji muhim ogoh kuned.",
-'proxyblocksuccess' => 'Ançom şud.',
 'sorbsreason' => 'Nişonai IP-i şumo hamcun jak proksiji kuşoda dar DNSBL fehrist şudaast, ki az tarafi {{SITENAME}} istifoda meşavad.',
 'sorbs_create_account_reason' => 'Nişonai IP-i şumo hamcun proksiji kuşoda dar DNSBL, ki az tarafi {{SITENAME}} istifoda meşavad, fehrist şudaast. Şumo nametavoned hisobi korabariero eçod kuned',
 
index c955c06..a0d9ec1 100644 (file)
@@ -647,6 +647,8 @@ $1',
 'userlogin-resetpassword-link' => 'ตั้งรหัสผ่านใหม่',
 'helplogin-url' => 'Help:การล็อกอิน',
 'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|คำอธิบายเรื่องการล็อกอิน]]',
+'userlogin-loggedin' => 'คุณล็อกอินในชื่อ {{GENDER:$1|$1}} แล้ว
+ใช้แบบด้านล่างเพื่อล็อกอินเป็นอีกผู้ใช้หนึ่ง',
 'userlogin-createanother' => 'สร้างอีกบัญชี',
 'createacct-join' => 'กรอกสารสนเทศของคุณด้านล่าง',
 'createacct-another-join' => 'กรอกข้อมูลของบัญชีใหม่ด้านล่าง',
@@ -1105,7 +1107,7 @@ $2
 คุณสามารถดูผลต่างนี้ได้ รายละเอียดพบได้ใน[{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} ปูมการลบ]",
 'rev-suppressed-diff-view' => "รุ่นหนึ่งของผลต่างนี้'''ถูกยับยั้ง'''
 คุณสามารถดูผลต่างนี้ได้ รายละเอียดพบได้ใน[{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} ปูมการยับยั้ง]",
-'rev-delundel' => 'à¹\81สà¸\94à¸\87/à¸\8bà¹\88อà¸\99',
+'rev-delundel' => 'à¹\80à¸\9bลีà¹\88ยà¸\99à¸\97ัศà¸\99วิสัย',
 'rev-showdeleted' => 'แสดง',
 'revisiondelete' => 'ลบ/กู้คืนรุ่น',
 'revdelete-nooldid-title' => 'ไม่มีรุ่นที่ต้องการ',
@@ -1429,6 +1431,8 @@ $1",
 'userrights-notallowed' => 'บัญชีของคุณไม่ได้รับอนุญาตให้เพิ่มหรือลดสิทธิผู้ใช้',
 'userrights-changeable-col' => 'กลุ่มที่คุณสามารถเปลี่ยนได้',
 'userrights-unchangeable-col' => 'กลุ่มที่คุณไม่สามารถเปลี่ยนได้',
+'userrights-conflict' => 'พบการเปลี่ยนแปลงสิทธิผู้ใช้ขัดกัน! โปรดทบทวนและยืนยันการเปลี่ยนแปลงของคุณ',
+'userrights-removed-self' => 'คุณเพิกถอนสิทธิของคุณสำเร็จแล้ว ฉะนั้น คุณจึงไม่สามารถเข้าถึงหน้านี้ได้อีกต่อไป',
 
 # Groups
 'group' => 'กลุ่ม:',
@@ -2107,6 +2111,8 @@ $1',
 'allpages-hide-redirects' => 'ซ่อนการเปลี่ยนทาง',
 
 # SpecialCachedPage
+'cachedspecial-viewing-cached-ttl' => 'คุณกำลังดูรุ่นที่เก็บหน่วยความจำแคชของหน้านี้ ซึ่งอาจมีอายุ $1',
+'cachedspecial-viewing-cached-ts' => 'คุณกำลังดูรุ่นที่เก็บหน่วยความจำแคชของหน้านี้ ซึ่งอาจไม่เป็นจริงทั้งหมด',
 'cachedspecial-refresh-now' => 'ดูล่าสุด',
 
 # Special:Categories
@@ -2130,7 +2136,7 @@ $1',
 'linksearch-ok' => 'ค้นหา',
 'linksearch-text' => 'สามารถใช้ตัวแทนเช่น "*.wikipedia.org" ได้
 ต้องการโดเมนระดับบนสุดเป็นอย่างน้อย เช่น "*.org"<br />
-{PLURAL:$2|โพรโทคอล}}ที่รองรับ: <code>$1</code> (ค่าโดยปริยายเป็น http:// หากไม่ระบุโพรโทคอล)',
+{{PLURAL:$2|โพรโทคอล}}ที่รองรับ: <code>$1</code> (ค่าโดยปริยายเป็น http:// หากไม่ระบุโพรโทคอล)',
 'linksearch-line' => '$1 ถูกลิงก์จาก $2',
 'linksearch-error' => 'อักขระตัวแทนอยู่ได้เฉพาะหน้าชื่อโฮสต์เท่านั้น',
 
@@ -2309,9 +2315,11 @@ $UNWATCHURL
 'deleteotherreason' => 'เหตุผลอื่น/เพิ่มเติม:',
 'deletereasonotherlist' => 'เหตุผลอื่น',
 'deletereason-dropdown' => '* เหตุผลการลบทั่วไป
-** รับแจ้งจากผู้เขียน
+** สแปม
+** ก่อกวน
 ** ละเมิดลิขสิทธิ์
-** ก่อกวน',
+** ผู้เขียนร้องขอ
+** การเปลี่ยนทางเสีย',
 'delete-edit-reasonlist' => 'แก้ไขเหตุผลการลบ',
 'delete-toobig' => 'หน้านี้มีประวัติการแก้ไขนาดใหญ่ คือ กว่า $1 รุ่น การลบหน้าเช่นนี้ถูกจำกัดเพื่อป้องกันการรบกวน{{SITENAME}}โดยบังเอิญ',
 'delete-warning-toobig' => 'หน้านี้มีประวัติการแก้ไขขนาดใหญ่ กว่า $1 รุ่น การลบหน้านี้อาจรบกวนการทำงานของฐานข้อมูลของ {{SITENAME}} โปรดดำเนินการด้วยความระมัดระวัง',
@@ -2329,7 +2337,7 @@ $UNWATCHURL
 ผู้แก้ไขล่าสุดของหน้านี้คือ [[User:$3|$3]] ([[User talk:$3|พูดคุย]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]])',
 'editcomment' => "คำอธิบายอย่างย่อคือ: \"''\$1''\"",
 'revertpage' => 'ย้อนการแก้ไขของ [[Special:Contributions/$2|$2]] ([[User talk:$2|Talk]]) ไปยังรุ่นของ [[User:$1|$1]]',
-'revertpage-nouser' => 'ยà¹\89อà¸\99à¸\81ารà¹\81à¸\81à¹\89à¹\84à¸\82à¹\82à¸\94ยà¸\9cูà¹\89à¹\83à¸\8aà¹\89à¹\84มà¹\88ระà¸\9aุà¸\8aืà¹\88อà¹\84à¸\9bยัà¸\87รุà¹\88à¸\99ลà¹\88าสุà¸\94à¹\82à¸\94ย [[User:$1|$1]]',
+'revertpage-nouser' => 'ยà¹\89อà¸\99à¸\81ารà¹\81à¸\81à¹\89à¹\84à¸\82à¹\82à¸\94ยà¸\9cูà¹\89à¹\83à¸\8aà¹\89à¹\84มà¹\88ระà¸\9aุà¸\8aืà¹\88อà¹\84à¸\9bยัà¸\87รุà¹\88à¸\99สุà¸\94à¸\97à¹\89ายà¹\82à¸\94ย {{GENDER:$1|[[User:$1|$1]]}}',
 'rollback-success' => 'ย้อนรุ่นที่แก้ไขโดย $1 ไปยังรุ่นล่าสุดโดย $2',
 
 # Edit tokens
@@ -2471,7 +2479,7 @@ $1',
 'contributions' => 'เรื่องที่เขียนโดย{{GENDER:$1|ผู้ใช้}}นี้',
 'contributions-title' => 'เรื่องที่เขียนโดย $1',
 'mycontris' => 'เรื่องที่เขียน',
-'contribsub2' => 'สำหรับ $1 ($2)',
+'contribsub2' => 'สำหรับ {{GENDER:$3|$1}} ($2)',
 'nocontribs' => 'ไม่พบการเปลี่ยนแปลงตรงกับเงื่อนไขเหล่านี้',
 'uctop' => '(ปัจจุบัน)',
 'month' => 'จากเดือน (และก่อนหน้า):',
@@ -2629,11 +2637,8 @@ $1',
 อย่างไรก็ตาม ไอพีนี้ถูกระงับในฐานะที่เป็นส่วนหนึ่งของเลขที่อยู่ไอพีในพิสัย $2 ซึ่งสามารถปลดบล็อกได้',
 'ip_range_invalid' => 'พิสัยไอพีไม่ถูกต้อง',
 'ip_range_toolarge' => 'พิสัยบล็อกที่มีขนาดใหญ่กว่า /$1 จะไม่ได้รับอนุญาต',
-'blockme' => 'บล็อกฉัน',
 'proxyblocker' => 'บล็อกพร็อกซี',
-'proxyblocker-disabled' => 'ฟังก์ชันนี้ถูกปิดใช้งาน',
 'proxyblockreason' => 'เลขที่อยู่ไอพีของคุณถูกบล็อกเนื่องจากเป็นพร็อกซีเปิด กรุณาติดต่อผู้ให้บริการอินเทอร์เน็ตหรือฝ่ายสนับสนุนเทคนิคขององค์การคุณ และแจ้งให้พวกเขาทราบถึงปัญหาความปลอดภัยร้ายแรงนี้',
-'proxyblocksuccess' => 'สำเร็จ',
 'sorbsreason' => 'เลขที่อยู่ไอพีของคุณอยู่ในพร็อกซีเปิดในส่วน DNSBL ที่ {{SITENAME}} ใช้',
 'sorbs_create_account_reason' => 'เลขที่อยู่ไอพีของคุณอยู่ในพร็อกซีเปิดในส่วน DNSBL ที่ {{SITENAME}} ใช้ 
 คุณไม่สามารถสร้างบัญชีได้',
@@ -2659,6 +2664,7 @@ $1',
 'unlockdbsuccesstext' => 'ปลดล็อกฐานข้อมูลเรียบร้อย',
 'lockfilenotwritable' => 'ไม่สามารถล็อกฐานข้อมูลได้ เนื่องจากการเขียนลงฐานข้อมูล การล็อกและการปลดล็อกจำเป็นต้องทำที่เว็บเซิร์ฟเวอร์',
 'databasenotlocked' => 'ฐานข้อมูลไม่ได้ล็อก',
+'lockedbyandtime' => '(โดย {{GENDER:$1|$1}} เมื่อวันที่ $2 เวลา $3)',
 
 # Move page
 'move-page' => 'ย้าย $1',
@@ -2898,7 +2904,7 @@ $1',
 'tooltip-t-recentchangeslinked' => 'รายการปรับปรุงล่าสุดในหน้าที่ลิงก์จากหน้านี้',
 'tooltip-feed-rss' => 'ฟีดชนิดอาร์เอสเอส (RSS) ของหน้านี้',
 'tooltip-feed-atom' => 'ฟีดอะตอม (Atom) ของหน้านี้',
-'tooltip-t-contributions' => 'รายà¸\81ารà¹\80รืà¹\88อà¸\87à¸\97ีà¹\88à¹\80à¸\82ียà¸\99à¹\82à¸\94ยà¸\9cูà¹\89à¹\83à¸\8aà¹\89à¸\84à¸\99à¸\99ีà¹\89',
+'tooltip-t-contributions' => 'รายการเรื่องที่เขียนโดยผู้ใช้นี้',
 'tooltip-t-emailuser' => 'ส่งอีเมลถึงผู้ใช้นี้',
 'tooltip-t-upload' => 'อัปโหลดไฟล์',
 'tooltip-t-specialpages' => 'รายการหน้าพิเศษทั้งหมด',
@@ -3057,8 +3063,8 @@ $1',
 'filedelete-archive-read-only' => 'ไดเรกทอรีกรุชื่อ "$1" ไม่สามารถเขียนลงได้โดยเว็บเซิร์ฟเวอร์',
 
 # Browsing diffs
-'previousdiff' => 'â\86\90 à¹\81à¸\95à¸\81à¸\95à¹\88าà¸\87ก่อนหน้า',
-'nextdiff' => 'à¹\81à¸\95à¸\81à¸\95à¹\88าà¸\87ถัดไป →',
+'previousdiff' => 'â\86\90 à¸\81ารà¹\81à¸\81à¹\89à¹\84à¸\82ก่อนหน้า',
+'nextdiff' => 'à¸\81ารà¹\81à¸\81à¹\89à¹\84à¸\82ถัดไป →',
 
 # Media information
 'mediawarning' => "'''คำเตือน''': ไฟล์รูปแบบนี้อาจมีโค้ดที่ไม่พึงประสงค์
index 4a29ea1..7c7b5e1 100644 (file)
@@ -778,8 +778,8 @@ $3 tarapyndan görkezilen sebäp: ''$2''",
 '''({{int:last}})''' = öňündäki wersiýadan tapawudy, '''{{int:minoreditletter}}''' = ujypsyzja özgerdiş.",
 'history-fieldset-title' => 'Geçmişe göz aýla',
 'history-show-deleted' => 'Diňe öçürilenler',
-'histfirst' => 'Iň irki',
-'histlast' => 'Ýaňy-ýakyndaky',
+'histfirst' => 'iň köne',
+'histlast' => 'iň täze',
 'historysize' => '({{PLURAL:$1|1 baýt|$1 baýt}})',
 'historyempty' => '(boş)',
 
@@ -1282,7 +1282,7 @@ $1 {{PLURAL:$1|simwoldan|simwoldan}} köp bolmaly däl.',
 'rc_categories' => 'Kategoriýalar bilen çäklendir ("|" bilen aýyr)',
 'rc_categories_any' => 'Islendik',
 'newsectionsummary' => '/* $1 */ täze bölüm',
-'rc-enhanced-expand' => 'Jikme-jiklikleri görkez (JavaScript gerekli)',
+'rc-enhanced-expand' => 'Jikme-jikligi görkez',
 'rc-enhanced-hide' => 'Jikme-jiklikleri gizle',
 
 # Recent changes linked
@@ -2055,9 +2055,9 @@ $1',
 'contributions' => '{{GENDER:$1|Ulanyjy}} goşantlary',
 'contributions-title' => '$1 üçin ulanyjy goşantlary',
 'mycontris' => 'Goşantlar',
-'contribsub2' => '$1 ($2)',
+'contribsub2' => '{{GENDER:$3|$1}} üçin ($2)',
 'nocontribs' => 'Bu kriteriýlere gabat gelýän üýtgeşme ýok.',
-'uctop' => '(iň soňky)',
+'uctop' => '(häzirki)',
 'month' => 'Aý:',
 'year' => 'Ýyl:',
 
@@ -2192,12 +2192,9 @@ Blokirlemesi eýýäm aýyrylan bolmagy mümkin.',
 Emma, bu adres $2 diapazonynyň bir bölegi hökmünde blokirlenipdir, diapazon blokirlemesini aýryp bilersiňiz.',
 'ip_range_invalid' => 'Nädogry IP diapazony.',
 'ip_range_toolarge' => '/$1 blokdan uly aralyk blokirlemelere rugsat berilmeýär',
-'blockme' => 'Meni blokirle',
 'proxyblocker' => 'Proksi blokirleýji',
-'proxyblocker-disabled' => 'Bu funksiýa ýapyk.',
 'proxyblockreason' => 'IP adresiňiz açyk proksidigi sebäpli blokirlenipdir.
 Internet üpjün edijiňiz ýa-da goldaw gullugy bilen habarlaşyp, olary bu çynlakaý howpsuzlyk problemasy barada habardar ediň.',
-'proxyblocksuccess' => 'Ýerine ýetirildi.',
 'sorbsreason' => 'IP adresiňiz {{SITENAME}} tarapyndan ulanylýan DNSBL-de açyk proksi hökmünde sanawa goşulypdyr.',
 'sorbs_create_account_reason' => 'IP adresiňiz {{SITENAME}} tarapyndan ulanylýan DNSBL-de açyk proksi hökmünde sanawa goşulypdyr.
 Hasap açyp bilmeýärsiňiz.',
@@ -2491,6 +2488,8 @@ Mazmun üçin bir sebäp goşmaga rugsat berýär',
 'spambot_username' => 'MediaWiki spam arassalaýyş',
 'spam_reverting' => '$1 sahypasyna çykgytlary bolmadyk iň soňky wersiýasyna yzyna getirilýär',
 'spam_blanking' => 'Ähli wersiýalarda $1 sahypasyna çykgytlar bar, boşadylýar',
+'simpleantispam-label' => "Anti-spam barlagy.
+Muny '''DOLDURMAŇ'''!",
 
 # Patrolling
 'markaspatrolleddiff' => 'Patrullyk edilen diýip belle',
index 8a0f804..028870d 100644 (file)
@@ -285,8 +285,6 @@ $messages = array(
 'broken-file-category' => 'Mga pahina na may sirang mga kawing ng talaksan',
 'categoryviewer-pagedlinks' => '($1) ($2)',
 
-'linkprefix' => '/^(.*?)([a-zA-Z\\x80-\\xff]+)$/sD',
-
 'about' => 'Patungkol',
 'article' => 'Pahina ng nilalaman',
 'newwindow' => '(magbubukas sa bagong bintana)',
@@ -2591,11 +2589,8 @@ Tingnan ang [[Special:BlockList|talaan ng pagharang]] para sa lista ng kasalukuy
 'ipb_blocked_as_range' => 'Mali: Hindi diretsong nakaharang ang IP na $1 at hindi maaaring tanggalin sa pagkakaharang. Bagaman, bahagi ito sa sakop na $2, na maaaring tanggalin sa pagkaharang.',
 'ip_range_invalid' => 'Hindi tamang sakop ng IP.',
 'ip_range_toolarge' => 'Hindi pinapayagan ang mga saklaw ng pagharang na mas malaki kaysa /$1.',
-'blockme' => 'Harangin ako',
 'proxyblocker' => 'Pangharang ng proxy',
-'proxyblocker-disabled' => 'Nakapatay ang pagharang sa proxy.',
 'proxyblockreason' => 'Hinarang ang IP address mo dahil bukas na proxy ito. Makipag-ugnayan sa iyong tagabigay ng serbisyong Internet o suportang teknikal at ipaalam sa kanila itong seryesong suliranin sa seguridad.',
-'proxyblocksuccess' => 'Tapos na.',
 'sorbs' => 'DNSBL',
 'sorbsreason' => 'Nakalista ang IP address mo bilang isang bukas na proxy sa DNSBL na ginagamit ng sayt na ito.',
 'sorbs_create_account_reason' => 'Nakalista ang IP address mo bilang isang bukas na proxy sa DNSBL na ginagamit ng sayt na ito. Hindi ka makakalikha ng akawnt',
@@ -2956,6 +2951,8 @@ Maaaring dahil ito sa isang kawing sa isang nakatalang hinarang dahil di-kinaisn
 'spam_reverting' => "Ibinabalik sa huling bersyon na 'di-naglalaman ng mga kawing sa $1",
 'spam_blanking' => 'Lahat ng mga pagbabago ay naglalaman ng mga kawing sa $1, pagpapatlang',
 'spam_deleting' => 'Lahat ng mga pagbabago ay naglalaman ng mga kawing sa $1, binubura',
+'simpleantispam-label' => "Pagsusuring panlaban sa \"manlulusob\" (''spam'').
+'''HUWAG''' itong lagyan ng laman!",
 
 # Info page
 'pageinfo-title' => 'Kabatiran para sa "$1"',
index 5deddb1..1329371 100644 (file)
@@ -890,7 +890,6 @@ Vakai ki he [[Special:Log/delete|hokohoko tāmateʻi]] ki he lekooti ʻo e ngaah
 'ipb_already_blocked' => 'Kuo ʻosi taʻofi ʻa e "$1"',
 'ipb_cant_unblock' => 'Hala: naʻe ʻikai ʻilo ko e taʻofi fika $1. Mahalo pē kuo ʻosi ʻene taʻetaʻofi.',
 'ip_range_invalid' => 'ʻOku taʻeʻaonga ʻa e fakavā IP',
-'proxyblocksuccess' => 'Kuo fai.',
 
 # Developer tools
 'lockdb' => 'Lokaʻi ʻa e tānekingaʻilo',
index d9accb7..c0c2bef 100644 (file)
@@ -36,6 +36,7 @@
  * @author LuCKY
  * @author Mach
  * @author Manco Capac
+ * @author Meelo
  * @author Metal Militia
  * @author Mirzali
  * @author Mskyrider
@@ -1550,8 +1551,8 @@ Aramanızın başına '''all:''' önekini ekleyerek tüm içeriği aramayı (tar
 $1 {{PLURAL:$1|karakterin|karakterin}} altında olmalı.',
 'yourgender' => 'Nasıl açıklamayı tercih edersiniz?',
 'gender-unknown' => 'Söylemek istemiyorsanız',
-'gender-male' => 'Wiki düzenlemelerinde erkek olarak',
-'gender-female' => 'Wiki düzenlemelerinde kadın olarak',
+'gender-male' => 'Viki sayfalarını erkek olarak düzenliyorum',
+'gender-female' => 'Viki sayfalarını kadın olarak düzenliyorum',
 'prefs-help-gender' => 'Bu tercih ayarı isteğe bağlıdır.
 Yazılımda söz değerlerinin başlarında bulunan cinsiyete uygun gramerler için kullanılır.
 Bu bilgiler herkes tarafından görülebilir.',
@@ -2052,12 +2053,14 @@ Sıradaki liste sadece bu dosyaya bağlantı veren {{PLURAL:$1|ilk dosyayı|ilk
 'morelinkstoimage' => 'Bu dosyaya [[Special:WhatLinksHere/$1|daha fazla bağlantıları]] gör.',
 'linkstoimage-redirect' => '$1 (dosya yönlendirme) $2',
 'duplicatesoffile' => 'Şu {{PLURAL:$1|dosya|$1 dosya}}, bu dosyanın kopyası ([[Special:FileDuplicateSearch/$2|daha fazla ayrıntı]]):',
-'sharedupload' => 'Bu dosya $1 deposundan ve diğer projelerde kullanılıyor olabilir.',
+'sharedupload' => 'Bu dosya $1 projesinden olup, diğer projelerde kullanılıyor olabilir.',
 'sharedupload-desc-there' => 'Bu dosya $1 deposundan ve diğer projeler tarafından kullanılıyor olabilir. Daha fazla bilgi için lütfen [$2 dosya açıklama sayfasına] bakın.',
 'sharedupload-desc-here' => 'Bu dosya $1 deposundan ve diğer projeler tarafından kullanılıyor olabilir.
 Aşağıda [$2 dosya açıklama sayfasındaki] açıklama gösteriliyor.',
-'sharedupload-desc-create' => 'Bu dosya, $1 ve diğer projeler tarafından kullanılıyor olabilir. 
-Dosya açıklamasını düzenlemek isterseniz, [$2 dosya açıklama sayfası] bulunmaktadır.',
+'sharedupload-desc-edit' => 'Bu dosya $1 projesinden olup, diğer projelerde kullanılıyor olabilir.
+Dosyanın açıklama sayfasında değişiklik yapmak için ilgili sayfaya [$2 buradan] gidebilirsiniz.',
+'sharedupload-desc-create' => 'Bu dosya $1 projesinden olup, diğer projelerde kullanılıyor olabilir.
+Dosyanın açıklama sayfasında değişiklik yapmak için ilgili sayfaya [$2 buradan] gidebilirsiniz.',
 'filepage-nofile' => 'Bu isimde bir dosya yok.',
 'filepage-nofile-link' => 'Bu isimde bir dosya yok, ama siz [$1 yükleyebilirsiniz].',
 'uploadnewversion-linktext' => 'Dosyanın yenisini yükleyin',
@@ -2636,7 +2639,7 @@ $1',
 'contributions' => '{{GENDER:$1|Kullanıcı}} katkıları',
 'contributions-title' => '$1 için kullanıcı katkıları',
 'mycontris' => 'Katkılar',
-'contribsub2' => '$1 ($2)',
+'contribsub2' => '{{GENDER:$3|$1}} ($2) tarafından',
 'nocontribs' => 'Bu kriterlere uyan değişiklik bulunamadı',
 'uctop' => '(son)',
 'month' => 'Ay:',
@@ -2789,12 +2792,9 @@ Engelleme kaldırılmış olabilir.',
 Ancak, bu adres $2 aralığının parçası olarak engellenmiş, aralık engellemesini kaldırabilirsiniz.',
 'ip_range_invalid' => 'Geçersiz IP aralığı.',
 'ip_range_toolarge' => '/$1 bloktan daha büyük aralık bloklarına izin verilmez.',
-'blockme' => 'Beni engelle',
 'proxyblocker' => 'Proxy engelleyici',
-'proxyblocker-disabled' => 'Bu işlev devre dışı bırakıldı.',
 'proxyblockreason' => 'IP adresiniz açık bir proxy olduğu için engellendi.
 Lütfen İnternet sevis sağlayınız ile ya da teknik destek ile irtibat kurun ve bu ciddi güvenlik probleminden haberdar edin.',
-'proxyblocksuccess' => 'Tamamlanmıştır.',
 'sorbsreason' => "IP adresiniz, {{SITENAME}} sitesi tarafından kullanılan DNSBL'de açık proxy olarak listelenmiş.",
 'sorbs_create_account_reason' => "IP adresiniz {{SITENAME}} sitesi tarafından kullanılan DNSBL'de açık proxy olarak listelenmiş.
 Hesap oluşturamazsınız",
@@ -3118,6 +3118,8 @@ Geçici dosya kayıp.',
 'spambot_username' => 'Medyaviki spam temizleme',
 'spam_reverting' => '$1 ile bağlantı içermeyen son sürüme geri dönülüyor',
 'spam_blanking' => 'Tüm revizyonlar $1 sayfasına bağlantı içeriyor, boşaltılıyor',
+'simpleantispam-label' => "Anti-spam denetimi.
+Bunu '''doldurmayın'''!",
 
 # Info page
 'pageinfo-title' => 'Bilgi için "$1"',
index bb8c4f5..f6b9404 100644 (file)
@@ -348,8 +348,6 @@ $messages = array(
 'noindex-category' => 'Индексланмаган битләр',
 'broken-file-category' => 'Файлларга эшләми торган сылтамалар булган битләр',
 
-'linkprefix' => '/^(.*?)([a-zA-Z\\x80-\\xff]+)$/sD',
-
 'about' => 'Тасвирлама',
 'article' => 'Мәкалә',
 'newwindow' => '(яңа тәрәзәдә ачыла)',
@@ -1992,7 +1990,6 @@ $1',
 'block-log-flags-noemail' => 'хат җибәрү тыелган',
 'block-log-flags-hiddenname' => 'кулланучының исеме яшерелгән',
 'proxyblocker' => 'Прокси тыю',
-'proxyblocksuccess' => 'Эшләнде',
 'sorbsreason' => 'Сезнең IP адресыгыз DNSBLда ачык прокси дип санала.',
 
 # Developer tools
index 4fbc4eb..8f96b5b 100644 (file)
@@ -216,8 +216,6 @@ $messages = array(
 'index-category' => 'İndekslanğan bitlär',
 'noindex-category' => 'İndekslanmağan bitlär',
 
-'linkprefix' => '/^(.*?)([a-zA-Z\\x80-\\xff]+)$/sD',
-
 'about' => 'Taswirlama',
 'article' => 'Mäqälä',
 'newwindow' => '(yaña täräzädä açıla)',
@@ -1687,7 +1685,6 @@ $1',
 'ipb_expiry_invalid' => 'İskärü waqıtı xatalı.',
 'ip_range_invalid' => 'Xatalı IP arası.',
 'proxyblocker' => 'Proksi tıyu',
-'proxyblocksuccess' => 'Eşlände',
 'sorbsreason' => 'Sezneñ IP adresığız DNSBLda açıq proksi dip sanala.',
 
 # Developer tools
index 7c16f4a..9900e24 100644 (file)
@@ -44,6 +44,7 @@ $namespaceAliases = array(
 $namespaceGenderAliases = array();
 
 $linkTrail = '/^([a-zа-яёӝӟӥӧӵ“»]+)(.*)$/sDu';
+$linkPrefixCharset = '„«';
 $fallback8bitEncoding = 'windows-1251';
 $separatorTransformTable = array( ',' => "\xc2\xa0", '.' => ',' );
 
@@ -151,8 +152,6 @@ $messages = array(
 'index-category' => 'Индексировать кароно бамъёс',
 'noindex-category' => 'Индексировать каронтэм бамъёс',
 
-'linkprefix' => '/^(.*?)(„|«)$/sDu',
-
 'about' => 'Та сярысь',
 'article' => 'Статья',
 'mypage' => 'Ас бам',
index f30bce4..aad3464 100644 (file)
@@ -173,8 +173,6 @@ $messages = array(
 'broken-file-category' => 'ھۆججەت ئۇلىنىشى بۇزۇلغان بەتلەر',
 'categoryviewer-pagedlinks' => '($1) ($2)',
 
-'linkprefix' => '/^(.*?)([a-zA-Z\\x80-\\xff]+)$/sD',
-
 'about' => 'ھەققىدە',
 'article' => 'مۇندەرىجە',
 'newwindow' => '(يېڭى كۆزنەكتە ئاچ)',
@@ -2518,12 +2516,9 @@ $1',
 ئەمما ئۇ $2 نىڭ چەكلەش دائىرىسى ئىچىدە، بۇ دائىرىنى چەكلەشتىن بىكار قىلغىلى بولىدۇ.',
 'ip_range_invalid' => 'IP دائىرىسى ئىناۋەتسىز.',
 'ip_range_toolarge' => '/$1 دىن چوڭ بولغان چەكلەش دائىرىسىگە يول قويۇلمايدۇ.',
-'blockme' => 'مېنى چەكلە',
 'proxyblocker' => 'ۋاكالەتچىنى چەكلىگۈچى',
-'proxyblocker-disabled' => 'بۇ ئىقتىدار چەكلەنگەن.',
 'proxyblockreason' => 'IP ئادرېسىڭىز ئوچۇق ۋاكالەتچى، ئۇ ئاللىبۇرۇن چەكلەنگەن.
 ئىنتېرنېت مۇلازىمىتى تەمىنلىگۈچى سودىگەر ياكى تېخنىكىلىق قوللىغۇچى بىلەن ئالاقىلىشىڭ ھەمدە ئۇلارغا بۇ ئېغىر بىخەتەرلىك مەسىلىسىنى ئۇقتۇرۇڭ.',
-'proxyblocksuccess' => 'تامام',
 'sorbs' => 'DNSBL',
 'sorbsreason' => 'IP ئادرېسىڭىز {{SITENAME}} دا DNSBL تەرىپىدىن ئوچۇق ۋاكالەتچى تىزىملىكىگە قوشۇلغان.',
 'sorbs_create_account_reason' => 'IP ئادرېسىڭىز {{SITENAME}} دا DNSBL تەرىپىدىن ئوچۇق ۋاكالەتچى تىزىملىكىگە قوشۇلغان.
index 5fd2c79..7a7c4de 100644 (file)
@@ -361,6 +361,7 @@ $magicWords = array(
 );
 
 $linkTrail = '/^([a-zабвгґдеєжзиіїйклмнопрстуфхцчшщьєюяёъы“»]+)(.*)$/sDu';
+$linkPrefixCharset = '„«';
 
 $messages = array(
 # User preference toggles
@@ -504,8 +505,6 @@ $messages = array(
 'broken-file-category' => 'Сторінки, що посилаються на неіснуючі файли',
 'categoryviewer-pagedlinks' => '($1) ($2)',
 
-'linkprefix' => '/^(.*?)(„|«)$/sD',
-
 'about' => 'Про',
 'article' => 'Стаття',
 'newwindow' => '(відкривається в новому вікні)',
@@ -894,8 +893,8 @@ $1',
 'passwordsent' => 'Новий пароль був надісланий на адресу електронної пошти, зазначену для "$1".
 Будь ласка, ввійдіть до системи після отримання пароля.',
 'blocked-mailpassword' => 'Редагування з вашої IP-адреси заборонено, заблокована також функція відновлення пароля.',
-'eauthentsent' => 'Ð\9dа Ð·Ð°Ð·Ð½Ð°Ñ\87енÑ\83 Ð°Ð´Ñ\80еÑ\81Ñ\83 ÐµÐ»ÐµÐºÑ\82Ñ\80онноÑ\97 Ð¿Ð¾Ñ\88Ñ\82и Ð½Ð°Ð´Ñ\96Ñ\81ланий Ð»Ð¸Ñ\81Ñ\82 Ñ\96з Ð·Ð°Ð¿Ð¸Ñ\82ом Ð½Ð° Ð¿Ñ\96дÑ\82веÑ\80дженнÑ\8f Ð·Ð¼Ñ\96ни Ð°Ð´Ñ\80еÑ\81и.
£ Ð»Ð¸Ñ\81Ñ\82Ñ\96 Ñ\82акож Ð¾Ð¿Ð¸Ñ\81анÑ\96 Ð´Ñ\96Ñ\97, Ñ\8fкÑ\96 Ð¿Ð¾Ñ\82Ñ\80Ñ\96бно Ð²Ð¸ÐºÐ¾Ð½Ð°Ñ\82и Ð´Ð»Ñ\8f Ð¿Ñ\96дÑ\82веÑ\80дженнÑ\8f Ñ\82ого, Ñ\89о Ñ\86Ñ\8f Ð°Ð´Ñ\80еÑ\81а ÐµÐ»ÐµÐºÑ\82Ñ\80онноÑ\97 Ð¿Ð¾Ñ\88Ñ\82и Ñ\81пÑ\80авдÑ\96 належить вам.',
+'eauthentsent' => 'Ð\9dа Ð²ÐºÐ°Ð·Ð°Ð½Ñ\83 Ð°Ð´Ñ\80еÑ\81Ñ\83 ÐµÐ»ÐµÐºÑ\82Ñ\80онноÑ\97 Ð¿Ð¾Ñ\88Ñ\82и Ð²Ñ\96дпÑ\80авлено Ð»Ð¸Ñ\81Ñ\82.
©Ð¾Ð± Ð¾Ñ\82Ñ\80имÑ\83ваÑ\82и Ð»Ð¸Ñ\81Ñ\82и Ð½Ð°Ð´Ð°Ð»Ñ\96, Ð´Ð¾Ñ\82Ñ\80имÑ\83йÑ\82еÑ\81Ñ\8c Ð²Ð¸ÐºÐ»Ð°Ð´ÐµÐ½Ð¸Ñ\85 Ñ\82ам Ñ\96нÑ\81Ñ\82Ñ\80Ñ\83кÑ\86Ñ\96й Ð´Ð»Ñ\8f Ð¿Ñ\96дÑ\82веÑ\80дженнÑ\8f Ñ\82ого, Ñ\89о Ñ\86Ñ\8f Ð°Ð´Ñ\80еÑ\81а належить вам.',
 'throttled-mailpassword' => 'Інструкція по відновленню паролю вже була вислана електронною поштою протягом {{PLURAL:$1|останньої години|останніх $1 годин}}.
 Для попередження зловживань дозволено надсилати тільки одну інструкцію за {{PLURAL:$1|годину|$1 години|$1 годин}}.',
 'mailerror' => 'Помилка при відправці пошти: $1',
@@ -1344,15 +1343,15 @@ $3 зазначив таку причину: ''$2''",
 * Непотрібна особиста інформація
 *: ''домашні адреси, номери телефонів, номер паспорта тощо.''",
 'revdelete-legend' => 'Установити обмеження',
-'revdelete-hide-text' => 'Ð\9fÑ\80иÑ\85ований Ñ\82екÑ\81Ñ\82 Ñ\86Ñ\96Ñ\94Ñ\97 Ð²ÐµÑ\80Ñ\81Ñ\96Ñ\97 Ñ\81Ñ\82оÑ\80Ñ\96нки',
+'revdelete-hide-text' => 'ТекÑ\81Ñ\82 Ð²Ð¸Ð¿Ñ\80авленÑ\8c',
 'revdelete-hide-image' => 'Приховати вміст файлу',
 'revdelete-hide-name' => "Приховати дію та її об'єкт",
-'revdelete-hide-comment' => 'Ð\9fÑ\80иÑ\85оваÑ\82и ÐºÐ¾Ð¼ÐµÐ½Ñ\82аÑ\80',
-'revdelete-hide-user' => "Ð\9fÑ\80иÑ\85оваÑ\82и Ñ\96м'Ñ\8f Ð°Ð²Ñ\82оÑ\80а",
+'revdelete-hide-comment' => 'Ð\9fÑ\96дÑ\81Ñ\83мок Ð·Ð¼Ñ\96н',
+'revdelete-hide-user' => "Ð\86м'Ñ\8f Ð°Ð²Ñ\82оÑ\80а/IP Ð°Ð´Ñ\80еÑ\81а",
 'revdelete-hide-restricted' => 'Приховати дані також і від адміністраторів',
 'revdelete-radio-same' => '(не змінювати)',
-'revdelete-radio-set' => 'Так',
-'revdelete-radio-unset' => 'Ð\9dÑ\96',
+'revdelete-radio-set' => 'Ð\92идимий',
+'revdelete-radio-unset' => 'Ð\9fÑ\80иÑ\85ований',
 'revdelete-suppress' => 'Приховувати дані також і від адміністраторів',
 'revdelete-unsuppress' => 'Зняти обмеження з відновлених версій',
 'revdelete-log' => 'Причина:',
@@ -1595,9 +1594,9 @@ $1",
 'badsiglength' => 'Ваш підпис дуже довгий.
 Повинно бути не більше $1 {{PLURAL:$1|символу|символів|символів}}.',
 'yourgender' => 'Як ви волієте бути описаним?',
-'gender-unknown' => 'Я Ð½Ðµ Ð²Ð¾Ð»Ñ\96Ñ\8e Ð´ÐµÑ\82алÑ\96зÑ\83ваÑ\82и',
-'gender-male' => 'Ð\92Ñ\96н Ñ\80едагÑ\83Ñ\94 Ð²Ñ\96кÑ\96\81Ñ\82оÑ\80Ñ\96нки',
-'gender-female' => 'Ð\92она Ñ\80едагÑ\83Ñ\94 Ð²Ñ\96кÑ\96\81Ñ\82оÑ\80Ñ\96нки',
+'gender-unknown' => 'Ð\9dе Ð²Ð¸Ð·Ð½Ð°Ñ\87ена',
+'gender-male' => 'ЧоловÑ\96Ñ\87а',
+'gender-female' => 'Ð\96Ñ\96ноÑ\87а',
 'prefs-help-gender' => "Задання цього параметру - необов'язкове. Застосовується рушієм у тих звертаннях до користувача, які залежать від статі.
 Ця інформація загальнодоступна.",
 'email' => 'Електронна пошта',
@@ -2913,12 +2912,9 @@ $1',
 'ipb_blocked_as_range' => 'Помилка: IP-адреса $1 була заблокована не напряму і не може бути розблокована. Однак, вона належить до заблокованого діапазону $2, який можна розблокувати.',
 'ip_range_invalid' => 'Неприпустимий діапазон IP-адрес.',
 'ip_range_toolarge' => 'Блокування діапазонів, більших за /$1, не дозволені.',
-'blockme' => 'Заблокуй мене',
 'proxyblocker' => 'Блокування проксі',
-'proxyblocker-disabled' => 'Функція відключена.',
 'proxyblockreason' => "Ваша IP-адреса заблокована, тому що це — відкритий проксі.
 Будь ласка, зв'яжіться з вашим Інтернет-провайдером чи службою підтримки й повідомте їм про цю серйозну проблему безпеки.",
-'proxyblocksuccess' => 'Виконано.',
 'sorbs' => 'DNSBL',
 'sorbsreason' => 'Ваша IP-адреса числиться як відкритий проксі в DNSBL.',
 'sorbs_create_account_reason' => 'Ваша IP-адреса числиться як відкритий проксі в DNSBL. Ви не можете створити обліковий запис.',
@@ -3281,6 +3277,8 @@ The wiki server can't provide data in a format your client can read.",
 'spam_reverting' => 'Відкинути до останньої версії, що не містить посилання на $1',
 'spam_blanking' => 'Всі версії містять посилання на $1, очистка',
 'spam_deleting' => 'Все версії, що містили посилання на $1, вилучаються',
+'simpleantispam-label' => "Перевірка на спам.
+'''НЕ''' заповнюйте це!",
 
 # Info page
 'pageinfo-title' => 'Інформація про " $1 "',
@@ -4221,7 +4219,7 @@ MediaWiki поширюється в надії, що вона буде кори
 # Special:Redirect
 'redirect' => 'Перенаправлення за файлом, користувачем або ID версії',
 'redirect-legend' => 'Перенаправити на файл чи сторінку',
-'redirect-summary' => 'Ця спеціальна сторінка перенаправляє на файл (за поданою назвою файлу), сторінку (за поданим ID версії) або сторінку користувача (за поданим числовим ID користувача).',
+'redirect-summary' => 'Ця спеціальна сторінка перенаправляє на файл (за поданою назвою файлу), сторінку (за поданим ID версії) або сторінку користувача (за поданим числовим ID користувача). Використання: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/revision/328429]] або [[{{#Special:Redirect}}/user/101]].',
 'redirect-submit' => 'Перейти',
 'redirect-lookup' => 'Шукати:',
 'redirect-value' => 'Значення:',
index c33570f..cc5d0f7 100644 (file)
@@ -1427,7 +1427,6 @@ Also see [[Special:WantedCategories|wanted categories]].',
 'contribslink' => 'شـراکـت',
 'blocklogpage' => 'نوشتۂ پابندی',
 'block-log-flags-nocreate' => 'کھاتے کی تخلیق غیرفعال',
-'proxyblocksuccess' => 'کردیا.',
 
 # Move page
 'move-page' => 'منتقلی',
index d89d929..b154b14 100644 (file)
@@ -107,6 +107,7 @@ $magicWords = array(
 );
 
 $linkTrail = '/^([a-zʻʼ“»]+)(.*)$/sDu';
+$linkPrefixCharset = 'a-zA-Z\\x80-\\xffʻʼ«„';
 
 $messages = array(
 # User preference toggles
@@ -236,8 +237,6 @@ $messages = array(
 'noindex-category' => 'Indekslanmaydigan sahifalar',
 'broken-file-category' => 'Ishlamaydigan fayl havolalari bor sahifalar',
 
-'linkprefix' => '/^(.*?)([a-zA-Z\\x80-\\xffʻʼ«„]+)$/sDu',
-
 'about' => 'Haqida',
 'article' => 'Sahifa',
 'newwindow' => '(yangi oynada ochiladi)',
@@ -1463,7 +1462,6 @@ Yaqinda sodir etilgan yoʻqotishlar uchun $2ni koʻring.',
 'blocklogentry' => '$2 muddatga [[$1]]ni chetlashtirdi $3',
 'block-log-flags-nocreate' => 'hisob ochish toʻxtatilgan',
 'block-log-flags-nousertalk' => "o'zining munozara sahifasini tahrirlay olmaydi",
-'proxyblocksuccess' => 'Bajarildi.',
 
 # Move page
 'move-page' => '$1 — qayta nomlash',
index da6ccc3..535d622 100644 (file)
@@ -2567,11 +2567,8 @@ Qua soto ghe xe el registro de le sopression:',
 'ipb_blocked_as_range' => "Eror: L'indirizo IP $1 no'l xe sogeto a bloco individual e no'l pol èssar sblocà. El bloco el xe invesse ativo a livel de l'intervalo $2, che el pol èssar sblocà.",
 'ip_range_invalid' => 'Intervało de indirissi IP mìa vałido.',
 'ip_range_toolarge' => 'No se pol mia blocar intervali piassè grandi de /$1',
-'blockme' => 'Blòcheme',
 'proxyblocker' => 'Bloco dei proxy verti',
-'proxyblocker-disabled' => 'Sta funzion la xe disabilità.',
 'proxyblockreason' => 'Sto indirizo IP el xe stà blocà parché el risulta èssar un proxy verto. Se prega de contatar el proprio fornitor de acesso a Internet o el suporto tènico e dirghe de sto grave problema de sicureza.',
-'proxyblocksuccess' => 'Fatto.',
 'sorbsreason' => 'Sto indirizo IP el xe elencà come proxy verto ne la lista nera DNSBL doparà da {{SITENAME}}.',
 'sorbs_create_account_reason' => 'No se pol crear acessi novi da sto indirizo IP parché el xe elencà come proxy verto ne la lista nera DNSBL doparà da {{SITENAME}}.',
 'xffblockreason' => "Un indiriso IP presente ne l'intestasion X-Forwarded-For, tuo o del server proxy che te sì drio doparar, el xe stà blocà. La motivasion originale del bloco la xe: $1",
@@ -2905,6 +2902,8 @@ Questo xe probabilmente dovùo a la presenza de un colegamento a un sito foresto
 'spam_reverting' => "Ripristinà l'ultima version priva de colegamenti a $1",
 'spam_blanking' => 'Pàxena svodà, tute łe version le contegneva cołegamenti a $1',
 'spam_deleting' => 'Pàjina scansełà, tute łe version łe contegneva ligamenti a $1',
+'simpleantispam-label' => "Controlo anti spam.
+'''NO STA''' scrivar gnente qua de soto!",
 
 # Info page
 'pageinfo-title' => 'Informasion par "$1"',
index 03321d2..a724c1e 100644 (file)
@@ -2075,12 +2075,9 @@ Alemba om anttud blokiruindaiglehtez:',
 'ipb_cant_unblock' => 'Petuz: ei voi löuta ID $1:n blokiruindad.
 Voib olda, se om jo heittud.',
 'ip_range_invalid' => 'Vär IP-diapazon.',
-'blockme' => 'Blokiruigat mindai',
 'proxyblocker' => 'Proxy-blokator',
-'proxyblocker-disabled' => 'Nece funkcii ei ole kävutamas.',
 'proxyblockreason' => 'Teiden IP-adres om blokiruidud, sikš miše se om avoin proksi.
 Olgat hüväd, säkat pagin teiden Internet-provaideranke i kirjutagat hänele necen varuitomuden problemas.',
-'proxyblocksuccess' => 'Vaumiž.',
 'sorbsreason' => 'Teiden IP-adres om ozutadud kut avaitud proksi {{SITENAME}}-saitan DNSBL-an mustas nimikirjuteses.',
 'cant-block-while-blocked' => 'Teile ei sa blokiruida toižid kävutajid, sikš miše tö iče olet blokiruidud.',
 
index f95d7c6..2a1fca9 100644 (file)
@@ -854,8 +854,7 @@ Hãy nhập một địa chỉ có định dạng đúng hoặc bỏ trống ô
 
 Xin hãy bỏ qua thông điệp này nếu tài khoản này không phải do bạn tạo ra.',
 'usernamehasherror' => 'Tên người dùng không thể chứa dấu rào',
-'login-throttled' => 'Bạn đã thử quá nhiều mật khẩu của tài khoản này.
-Xin hãy đợi $1 rồi thử lại.',
+'login-throttled' => 'Bạn đã hết quyền thử mật khẩu tài khoản này vì bạn đã nhập sai quá nhiều. Xin hãy đợi $1 rồi hãy thử lại.',
 'login-abort-generic' => 'Thất bại khi đăng nhập',
 'loginlanguagelabel' => 'Ngôn ngữ: $1',
 'suspicious-userlogout' => 'Đã bỏ qua yêu cầu đăng xuất bạn, hình như được gửi từ trình duyệt hoặc máy proxy nhớ đệm hư.',
@@ -1031,7 +1030,7 @@ Có thể nó đã bị di chuyển hoặc xóa đi trong khi bạn đang xem tr
 'accmailtitle' => 'Đã gửi mật khẩu.',
 'accmailtext' => "Một mật khẩu được tạo ngẫu nhiên cho [[User talk:$1|$1]] đã được gửi đến $2. Có thể đổi mật khẩu tại trang ''[[Special:ChangePassword|đổi mật khẩu]]'' sau khi đã đăng nhập.",
 'newarticle' => '(Mới)',
-'newarticletext' => '<div style="margin-top: 0px;" class="emptymwmsg mediawiki_newarticletext"></div>',
+'newarticletext' => "Bạn đi đến đây từ một liên kết đến một trang chưa tồn tại. Để tạo trang, hãy bắt đầu gõ vào ô bên dưới (xem [[{{MediaWiki:Helppage}}|trang trợ giúp]] để có thêm thông tin). Nếu bạn đến đây do nhầm lẫn, chỉ cần nhấn vào nút '''Lùi''' (''Back'') trong trình duyệt của bạn.",
 'anontalkpagetext' => "----''Đây là trang thảo luận của một người dùng vô danh chưa tạo tài khoản hoặc có tài khoản nhưng không đăng nhập.
 Do đó chúng ta phải dùng một dãy số gọi là địa chỉ IP để xác định anh/chị ta.
 Một địa chỉ IP như vậy có thể có nhiều người cùng dùng chung.
@@ -2510,9 +2509,11 @@ Xin xác nhận việc bạn định làm, và hiểu rõ những hệ lụy c
 'deleteotherreason' => 'Lý do khác/bổ sung:',
 'deletereasonotherlist' => 'Lý do khác',
 'deletereason-dropdown' => '*Các lý do xóa phổ biến
-** Tác giả yêu cầu
+** Đăng tỉ thư rác
+** Phá hoại
 ** Vi phạm bản quyền
-** Phá hoại',
+** Tác giả yêu cầu
+** Chuyển hướng sai',
 'delete-edit-reasonlist' => 'Sửa lý do xóa',
 'delete-toobig' => 'Trang này có lịch sử sửa đổi lớn, đến hơn {{PLURAL:$1|lần|lần}} sửa đổi.
 Việc xóa các trang như vậy bị hạn chế để ngăn ngừa phá hoại do vô ý cho {{SITENAME}}.',
@@ -2825,11 +2826,8 @@ Xem lại những lần cấm tại [[Special:BlockList|danh sách cấm]].',
 'ipb_blocked_as_range' => 'Lỗi: Địa chỉ IP $1 không bị cấm trực tiếp và do đó không thể bỏ cấm. Tuy nhiên, nó bị cấm do là một bộ phận của dải IP $2, bạn có thể bỏ cấm dải này.',
 'ip_range_invalid' => 'Dải IP không hợp lệ.',
 'ip_range_toolarge' => 'Không được phép cấm dải IP lớn hơn /$1.',
-'blockme' => 'Cấm tôi',
 'proxyblocker' => 'Cấm proxy',
-'proxyblocker-disabled' => 'Chức năng này đã bị tắt.',
 'proxyblockreason' => 'Địa chỉ IP của bạn đã bị cấm vì là proxy mở. Xin hãy liên hệ nhà cung cấp dịch vụ Internet hoặc bộ phận hỗ trợ kỹ thuật của bạn và thông báo với họ về vấn đề an ninh nghiêm trọng này.',
-'proxyblocksuccess' => 'Xong.',
 'sorbsreason' => 'Địa chỉ IP của bạn bị liệt kê là một proxy mở trong DNSBL mà {{SITENAME}} đang sử dụng.',
 'sorbs_create_account_reason' => 'Địa chỉ chỉ IP của bạn bị liệt kê là một proxy mở trong DNSBL mà {{SITENAME}} đang sử dụng. Bạn không thể mở tài khoản.',
 'xffblockreason' => 'Đầu đề X-Forwarded-For chứa một địa chỉ IP đã bị cấm, địa chỉ này hoặc của bạn hoặc của một máy chủ proxy bạn đang sử dụng. Lý do cấm ban đầu là: $1',
@@ -3193,6 +3191,8 @@ Lưu nó vào máy tính của bạn rồi tải nó lên đây.',
 'spam_reverting' => 'Lùi lại đến phiên bản cuối không chứa liên kết đến $1',
 'spam_blanking' => 'Tất cả các phiên bản có liên kết đến $1; tẩy trống',
 'spam_deleting' => 'Tất cả các phiên bản có liên kết đến $1; xóa',
+'simpleantispam-label' => "Hệ thông đang kiểm tra chống spam.
+Xin '''ĐỪNG''' điền gì vào!",
 
 # Info page
 'pageinfo-title' => 'Thông tin về “$1”',
index 832415e..11b0ea3 100644 (file)
@@ -8,6 +8,7 @@
  * @file
  *
  * @author Altaileopard
+ * @author Bua333
  * @author Matma Rex
  * @author Midnight Gambler
  * @author Silvicola
@@ -103,7 +104,12 @@ $messages = array(
 'friday' => 'Freidooch',
 'saturday' => 'Samsdooch',
 'sun' => 'Su',
+'mon' => 'Mon',
+'tue' => 'Die',
+'wed' => 'Mid',
 'thu' => 'Du',
+'fri' => 'Fre',
+'sat' => 'Sam',
 'january' => 'Januaar',
 'february' => 'Feebruaar',
 'march' => 'Märds',
@@ -150,6 +156,7 @@ $messages = array(
 'hidden-categories' => '{{PLURAL:$1|Fârschdegde ghadegorii|Fârschdegde ghadegoriin}}',
 'category-subcat-count' => 'Di ghadegorii umfasd {{PLURAL:$2|bloos a undâr-ghadegorii|dsam $2 undâr-ghadegoriâ, wofoo {{PLURAL:$1|nôr ôône| $1}}}} undn ôôdsajchd wärn.',
 'category-article-count' => 'Di ghadegorii umfasd {{PLURAL:$2|bloos a sajdn|$2 sajdn, wofoo hiir {{PLURAL:$1|aane undn ôôdsajchd wärd|l$1 ôôdsajchd undn wärn}}}}.',
+'category-file-count' => '{{PLURAL:$2|Di Gadegorii umfasd bloos a Dadei.|Di folgende {{PLURAL:$1|Dadei is|$1 Dadein sind}} in der Gadegorii, von insgsamd $2 Dadein anzeichd.}}',
 'listingcontinuesabbrev' => '(Fôrdsedsung)',
 'noindex-category' => 'Seidn, wou net indexierd sin',
 
@@ -220,7 +227,7 @@ $messages = array(
 'articlepage' => "D'inhalds-sajdn dsajchn",
 'talk' => 'Disghusjoon',
 'views' => 'Ôôsichdn',
-'toolbox' => 'Werchdsajch-ghisdn',
+'toolbox' => 'Werchdsajch',
 'userpage' => "D'benudsârsajdn dsajchn",
 'projectpage' => "D'brojägdsajdn dsjachn",
 'imagepage' => "D'dadhaj-sajdn dsajchn",
@@ -336,7 +343,8 @@ Wen's des ned is, bisd womeeglich iwa ân feela in dr sofdwäâr gschdolbäd. In
 # Login and logout pages
 'yourname' => 'Benudsârnôômâ',
 'yourpassword' => 'Bhaswôrd:',
-'remembermypassword' => 'Af dem ghombjuudâr schdändich ôôgmäld blajm (for a maximum of $1 {{PLURAL:$1|day|days}})',
+'yourpasswordagain' => 'Bassworrd widderhulln:',
+'remembermypassword' => 'Miid den Brauser dauerhafd ogmeld bleim (maximal $1 {{PLURAL:$1|Dooch|Dooch}})',
 'login' => 'Ôômeldn',
 'nav-login-createaccount' => 'Oomeldn / Ghondoo ooleeng',
 'loginprompt' => 'Zum Omelldn mäin Guggies agdivierd sei.',
@@ -345,7 +353,10 @@ Wen's des ned is, bisd womeeglich iwa ân feela in dr sofdwäâr gschdolbäd. In
 'userlogout' => 'Abmeldn',
 'nologin' => 'Du hast ka Nutzergonto? $1',
 'nologinlink' => 'Sich als najâr Ôôgmeldâr ôômäldn',
+'createaccount' => 'Nutzergonto olegn',
+'gotaccount' => 'Du host scho a Benudtzergondo? $1',
 'gotaccountlink' => 'Omeldn',
+'userlogin-resetlink' => 'Omeldedadn vergessn?',
 'mailmypassword' => ' najs passwôrd iwâr iimejl dsuschign lasn',
 'loginlanguagelabel' => 'Sproch: $1',
 
@@ -406,7 +417,8 @@ Wen'D dich awâr hiirhäär bloos fârlaafn hasd, glig ââfach af '''Zurück'''
 'noarticletext' => 'Dii sajdn gibd\'s bis eds no ned.
 Duu ghâusch nach däm ausdrug aa [[Special:Search/{{PAGENAME}}|in alle sajdn suchng]],
 <span class="plainlinks"> [{{fullurl:{{#special:Log}}|page={{FULLPAGENAMEE}}}} in di dsugheerichng log-biichâr suchng] odâr dii sajdn [{{fullurl:{{FULLPAGENAME}}|action=edit}} ôôleeng un najschrajm]</span>.',
-'previewnote' => "'''Hiir siggsd bloos, wii's wärn dääd, dii sajdn is ôbâr no ned gschbaichärd!'''",
+'noarticletext-nopermission' => 'Däi Seidn endhäld momendan nu kan Dexd, [[Special:Search/{{PAGENAME}}|Du derfsd däi Seidn a net derschdelln]].',
+'previewnote' => "'''Des is blouss a Vuurschau.''' Däi Seidn is nu net gschbeicherd worrn.",
 'editing' => 'Beärbâdn fon $1',
 'editingsection' => 'Beärwâdn fo $1 (bloos abschnid)',
 'copyrightwarning' => "''Ghobhiir jôô ghâ web-sajdn, dii där ned ghärn, un benuds ghâ uurheewarrechdlich gschidsde wärgghe oone geneemichung fom uurheewâr!'''<br />
@@ -418,15 +430,24 @@ Hirmid sagsd, das Du den dhägsd '''selbâr gschriim''' hasd, das däär dhägsd
 'template-semiprotected' => '(ned ôôgmeldede un naje benudsär däfn hiir ned schrajm)',
 'hiddencategories' => 'Dii sajdn ghäärd dsu {{PLURAL:$1|aanâr fârschdegdn|$1 fârschdegde}} ghadegoriin:',
 'permissionserrorstext-withaction' => 'Du däfsd ned $2, des{{PLURAL:$1||}}dâsweechn:',
+'recreate-moveddeleted-warn' => "'''Achdung: Du derschdellsd a Seidn, däi wou bereids fräiers glöschd worrn is.'''
+
+Bidde dou sorgfälldig ieberbriefen, ob die erneide Seidnderschdellung die Richdlinien endschbrechn doud.
+
+Zu deiner Informadion folchd des Lösch- un Verschäibungs-Logbuch miid der Begrindung vo der vuurherichen Löschung:",
+'moveddeleted-notice' => 'Däi Seidn is glöschd worrn. Zur Informadion folchds Lösch- un Verschäibungslogbuch vo dera Seidn.',
 
 # Parser/template warnings
 'post-expand-template-inclusion-warning' => "'''Wannung''': Däi Gräiss vo eibundne Vuurloong is zu grouss, einiche Vuurloong könna net eibundn werrn.",
 'post-expand-template-inclusion-category' => 'Seidn, in dena wou däi maximale Gräiss vo eibundne Vuurloong ieberschriddn is.',
+'post-expand-template-argument-warning' => "'''Wannung:''' Däi Seidn endhäld mindesdens an Barameder in anner Vuurlooch, der wou exbandierd zu grouß is.",
+'post-expand-template-argument-category' => 'Seidn miid ignorierde Vuurloongbarameder',
 
 # History pages
 'viewpagelogs' => 'Logbicher fär dii sajdn dsajchn',
 'currentrev-asof' => 'Jedsiche wärsjoon, am $2 um $3 gmachd',
 'revisionasof' => 'Wärsjoon fom $2 um $3 Uur',
+'revision-info' => 'Version vom $1 Uhr von $2',
 'previousrevision' => '← wärsjoon dâfoor',
 'nextrevision' => 'Nägsdnajâre wärsjoon →',
 'currentrevisionlink' => 'Geechnwärdiche wärsjoon',
@@ -436,9 +457,13 @@ Hirmid sagsd, das Du den dhägsd '''selbâr gschriim''' hasd, das däär dhägsd
 * '''({{int:cur}})''' = undârschiid dsur geechnwärdichn wärsjoon, '''({{int:last}})''' = undârschiid dsur foorichn wärsjoon
 * Uurdsajd/Daadum = wärsjoon dsu dära dsajd, '''{{int:minoreditletter}}''' = glane ändärung.",
 'history-fieldset-title' => 'Suchng in där wärsjoonsfolche',
+'history-show-deleted' => 'Blouss glöschde Versiona zeing',
 'histfirst' => 'älldsde',
 'histlast' => 'neisde',
 
+# Revision feed
+'history-feed-item-nocomment' => '$1 bis $2',
+
 # Revision deletion
 'rev-delundel' => 'ôôdsajng/fârbärng',
 'revdel-restore' => 'Ändârn, was oodsajchd wäd',
@@ -449,10 +474,11 @@ Hirmid sagsd, das Du den dhägsd '''selbâr gschriim''' hasd, das däär dhägsd
 'revertmerge' => 'Dsrig fôr dii fârajnichung',
 
 # Diffs
-'history-title' => 'Wärsjoonsfolche fo „$1“',
+'history-title' => '$1: Versionsgschichd',
 'lineno' => 'Dsajln $1:',
 'compareselectedversions' => 'Ausgwäälde wärsjoona fârglajchn',
 'editundo' => 'riggängich machng',
+'diff-multi' => '({{PLURAL:$1|A dazwischaliengde Version|$1 dazwischaliengde Versiona}} von {{PLURAL:$2|am Nutzer|$2 Nutzern}} {{PLURAL:$1|wird|werrn}} ned ozeichd)',
 
 # Search results
 'searchresults' => 'Bam suchng gfundne sachng',
@@ -465,8 +491,10 @@ Hirmid sagsd, das Du den dhägsd '''selbâr gschriim''' hasd, das däär dhägsd
 'prevn' => '{{PLURAL:$1|foorichâr|fooriche $1}}',
 'nextn' => '{{PLURAL:$1|nägschdâr|nägschde $1}}',
 'prevn-title' => '{{PLURAL:$1|Vuurherichs Ergebnis|Vuurheriche $1 Ergebniss}}',
+'nextn-title' => '{{PLURAL:$1|Folngnds Ergebnis|Folngnde $1 Ergebniss}}',
 'shown-title' => 'Zeich mer $1 {{PLURAL:$1|Ergebnis|Ergebniss}} bro Seidn',
 'viewprevnext' => 'Dsajch ($1 {{int:pipe-separator}} $2) ($3)',
+'searchmenu-exists' => "'''Es gidd a Seidn, däi wou´n Nooma „[[:$1]]“ hodd.'''",
 'searchmenu-new' => "'''Derschdell dai Seidn „[[:$1]]“ in diesn Wigi.'''",
 'searchprofile-articles' => 'Inhaldsseidn',
 'searchprofile-project' => 'Hilf- un Brojegdseidn',
@@ -479,6 +507,7 @@ Hirmid sagsd, das Du den dhägsd '''selbâr gschriim''' hasd, das däär dhägsd
 'searchprofile-everything-tooltip' => 'Gsamdn Inhald durchsoung (aa Disgussionsseidn)',
 'searchprofile-advanced-tooltip' => 'Soung in weidere Namensraim',
 'search-result-size' => '$1 ({{PLURAL:$2|1 wôrd|$2 wärdâr}})',
+'search-result-category-size' => '{{PLURAL:$1|1 Seidn|$1 Seidn}} ({{PLURAL:$2|1 Untergadegorii|$2 Untergadegoriin}}, {{PLURAL:$3|1 Dadei|$3 Dadein}})',
 'search-result-score' => 'Âjschleechich: $1 %',
 'search-redirect' => '(Wajdalajdung fon „$1“ häa)',
 'search-section' => '(Abschnid $1)',
@@ -560,6 +589,8 @@ Hirmid sagsd, das Du den dhägsd '''selbâr gschriim''' hasd, das däär dhägsd
 'allowemail' => 'Iimejl-embfang fon andrâ ôôschdeln',
 'youremail' => 'E-mail:',
 'yourrealname' => 'Bürcherlicher Noma:',
+'prefs-help-email' => 'Des Ogeem vo anner E-Mail-Adressn is obdional, ermöglichd obber däi Zusendung vo an Ersatzbassworrd, wennsd dei Bassworrd vergessn hosd.',
+'prefs-help-email-others' => 'Miid andre Benutzer koost a iber däi Benutzerdisgussionsseidn Kondagd afneha, ohne dass dei Idendidäd offenleeng moussd.',
 
 # Groups
 'group-sysop' => 'Adminisdradoorn',
@@ -580,13 +611,18 @@ Hirmid sagsd, das Du den dhägsd '''selbâr gschriim''' hasd, das däär dhägsd
 'recentchanges' => 'ledsde änderunga',
 'recentchanges-legend' => 'Âjschdelunga, wii di ledsdn ändrunga dsajchd wärn solln',
 'recentchanges-feed-description' => 'Fârfolch mid dem Fiid dii ledsdn ändrungn in {{SITENAME}}.',
+'recentchanges-label-newpage' => 'Neie Seidn',
 'recentchanges-label-minor' => 'Blos a weng wôs is gändârd wôrn',
+'recentchanges-label-bot' => 'Ändrung durch an Bot',
+'recentchanges-label-unpatrolled' => 'Net-kondrollierde Ändrung',
 'rcnote' => "Des {{Plural:$1|is dii aane ändrung|sin dii '''$1''' ändrunga}}, dii in di {{Plural:$2|ledsdn 24 schdundn|ledsdn '''$2''' doochn}} gmachd wôrn {{Plural:$1|is|sin}}. Schdand is fom $4, $5 uur.",
+'rcnotefrom' => "Oozeichd werrn däi Ändrunga seid '''$2''' (max. '''$1''' Eidrääch).",
 'rclistfrom' => 'Bloos di ändrunga dsajchn sajd $1',
 'rcshowhideminor' => 'Glenâre Ändrungn $1',
 'rcshowhidebots' => 'Bods (bearbajdâr, dii ajchendlich brograme san) $1',
 'rcshowhideliu' => 'Ôôgmäldâde bearbajdâr $1',
 'rcshowhideanons' => '$1 uuôôgmäldâde bearbajdâr',
+'rcshowhidepatr' => 'Gondrollierde Ändrunga $1',
 'rcshowhidemine' => 'Ajchne bajdrääch $1',
 'rclinks' => 'Dsajch dii ledsdn $1 ändrunga fo di ledsdn $2 dooch.<br />$3',
 'diff' => 'undârschiid',
@@ -596,7 +632,7 @@ Hirmid sagsd, das Du den dhägsd '''selbâr gschriim''' hasd, das däär dhägsd
 'minoreditletter' => 'g',
 'newpageletter' => 'N',
 'boteditletter' => 'B',
-'rc-enhanced-expand' => 'Ajndslhajdn ôôdsajchn (gäd bloos mid JavaScript)',
+'rc-enhanced-expand' => 'Eindslheidn oodseichn (gäd bloos mid JavaScript)',
 'rc-enhanced-hide' => 'Glaanichghajdn ned dsajng',
 
 # Recent changes linked
@@ -614,11 +650,13 @@ Hirmid sagsd, das Du den dhägsd '''selbâr gschriim''' hasd, das däär dhägsd
 'uploadedimage' => 'had „[[$1]]“ naufglôôdn',
 
 'license' => 'Lizenz',
+'license-header' => 'Lizenz',
 
 # File description page
 'file-anchor-link' => 'Daddei',
 'filehist' => 'Wärsjoona bis eds',
 'filehist-help' => 'Glig af ân dsajdbhungd, um dii dôômôôliche fasung ôôdsuschaua',
+'filehist-revert' => 'zricksedzn',
 'filehist-current' => 'agduäl',
 'filehist-datetime' => 'Âjschdlungs-daadum un -dsajd',
 'filehist-thumb' => 'Schbigbildlâ',
@@ -652,6 +690,7 @@ S'gajd awâr aa â [[Special:WhatLinksHere/$2|lisdn mid alâ fârwajs]].",
 'ncategories' => '$1 {{PLURAL:$1|GhadegoriiGhadegoriin}}',
 'nmembers' => '{{PLURAL:$1|1 âjdrôôch|$1 âjdrääch}}',
 'prefixindex' => 'Ale sajdn mid brääfigs',
+'usercreated' => '{{GENDER:$3|Ersschdelld}} am $1 um $2 Uhr',
 'newpages' => 'Naje sajdn',
 'move' => 'Umdaafn',
 'movethispage' => 'Sajdn umdaafn',
@@ -680,6 +719,7 @@ S'gajd awâr aa â [[Special:WhatLinksHere/$2|lisdn mid alâ fârwajs]].",
 
 # Special:LinkSearch
 'linksearch' => 'Linggs nach ausârhalb',
+'linksearch-line' => '$1 is verlingt vo $2',
 
 # Special:ListGroupRights
 'listgrouprights-members' => '(Lisdn fon dâ midgliidâr)',
@@ -690,6 +730,7 @@ S'gajd awâr aa â [[Special:WhatLinksHere/$2|lisdn mid alâ fârwajs]].",
 # Watchlist
 'watchlist' => 'Beoobachdungslisdn',
 'mywatchlist' => 'Beoobachdungslisdn',
+'watchlistfor2' => 'Fär $1 ($2)',
 'addedwatchtext' => "Di sajdn „[[:$1]]“ schdäd eds mid af dajnâr [[Special:Watchlist|beoobachdungs-lisdn]] .
 
 Wen sich af der sajdn oda iirâr disghusjoons-sajdn was duud, wärd se ab eds
@@ -712,6 +753,7 @@ Wenns'd dii sajdn irchendwan amôl nimä fârfolchn wilsd, musd bloos af „{{in
 'deletepage' => 'Sajdn leschn',
 'confirmdeletetext' => "Duu bisd grôd dâbaj, â sajdn midsamd alle dsugheeriche alde wärsjoona ds'leschn. Bide beschdäädich, das De wasd, was des als bewirgd, un das De Dich dâbaj aa an d'[[{{MediaWiki:Policy-url}}|richliinjen]] fo dem wighi hiir häldsd.",
 'actioncomplete' => 'Erleedichd',
+'actionfailed' => 'Agdsion fehlgschloong',
 'deletedtext' => '„$1“ is gleschd wôrn. Im $2 findsd â lisdn mid dâ ledsdn leschunga.',
 'dellogpage' => 'Logbuch fo di leschunga',
 'deletecomment' => 'Grund:',
@@ -763,10 +805,10 @@ Wenns'd dii sajdn irchendwan amôl nimä fârfolchn wilsd, musd bloos af „{{in
 'blanknamespace' => '(Sajdn)',
 
 # Contributions
-'contributions' => 'Ajchne bajdrääch',
+'contributions' => 'Eichne Beidrääch',
 'contributions-title' => 'Bajdrääch fo „$1“',
 'mycontris' => 'Bajdreech',
-'contribsub2' => 'Fär $1 ($2)',
+'contribsub2' => 'Von {{GENDER:$3|$1}} ($2)',
 'uctop' => '(agduell)',
 'month' => 'bis moonad:',
 'year' => 'bis dsum jôôr:',
@@ -778,6 +820,7 @@ Wenns'd dii sajdn irchendwan amôl nimä fârfolchn wilsd, musd bloos af „{{in
 'sp-contributions-talk' => 'Disgussion',
 'sp-contributions-search' => 'Bajdreech suchng',
 'sp-contributions-username' => 'IP-adresn odär nôômâ fom Ôôgmeldn:',
+'sp-contributions-toponly' => 'Blouss agduelle Versiona zeing',
 'sp-contributions-submit' => 'Suchng',
 
 # What links here
@@ -785,6 +828,7 @@ Wenns'd dii sajdn irchendwan amôl nimä fârfolchn wilsd, musd bloos af „{{in
 'whatlinkshere-title' => 'Sajdn, di af „$1“ fârwajsn',
 'whatlinkshere-page' => 'Sajdn:',
 'linkshere' => "Dii afgfiirdn sajdn fârwajsn af ''„[[:$1]]“''':",
+'nolinkshere' => "Ka Seidn verlingt af '''„[[:$1]]“'''.",
 'isredirect' => 'Wajdârlajdungssajdn',
 'istemplate' => 'Foorlaachn-ajbindung',
 'isimage' => 'Daddeilink',
@@ -794,7 +838,7 @@ Wenns'd dii sajdn irchendwan amôl nimä fârfolchn wilsd, musd bloos af „{{in
 'whatlinkshere-hideredirs' => '$1 wajdârlajdungn',
 'whatlinkshere-hidetrans' => '$1 Foorlaachn-ajbindunga',
 'whatlinkshere-hidelinks' => '$1 Fârwajse',
-'whatlinkshere-hideimages' => '$1 Bild-fârwajse',
+'whatlinkshere-hideimages' => 'Daddeilings $1',
 'whatlinkshere-filters' => 'Fildhâr',
 
 # Block/unblock
@@ -850,6 +894,7 @@ Schrajb bide den '''naja'' nôômâ fo dâr sajdn undârals '''Dsiil'' nâj un '
 
 # Thumbnails
 'thumbnail-more' => 'Grässär machng',
+'thumbnail_error' => 'Fehler beim Derschdelln von Vuurschaubilld: $1',
 
 # Tooltip help for the actions
 'tooltip-pt-userpage' => 'Daj benudsâr-sajdn',
@@ -977,7 +1022,23 @@ Bloos  dsajln, dii mi´m dsajchn * ôôfanga, wärn berigsichdichd. Un dä ärsc
 'watchlisttools-edit' => 'Beobachdungslisdn dsajchn un ändârn',
 'watchlisttools-raw' => "In där beoobachdungslisdn ds'fuâs rumworschdln",
 
+# Core parser functions
+'duplicate-defaultsort' => 'Der Sordierungsschlissl „$2“ ieberschreibt den vuurher verwendten Schlissl „$1“.',
+
 # Special:SpecialPages
 'specialpages' => 'Schbedsjaal-sajdn',
 
+# External image whitelist
+'external_image_whitelist' => ' # Däi Zeiln net verändern.<pre>
+# Undnstehnd könna Fragmende vo reguläre Asdrigg (der Deil zwischa die //) eigeem werrn.
+# Däi werrn miid die URLs vo Bilder aus egsderne Gwelln verglichng.
+# A bositiver Vergleich fiehrt zur Ozeich vom Bild, sunsd werrds Bild blouss als Link ozeichd.
+# Zeiln, däi wou miid an # ofanga, werrn als Gommendar behandld.
+# Zwischa Grouß- un Klaaschreibung werrd net underschiedn.
+
+# Fragmende vo reguläre Asdrigg nach dera Zeiln eidroong. Däi Zeiln net verändern.</pre>',
+
+# Special:Tags
+'tag-filter' => '[[Special:Tags|Marrgierungs]]-Fillder:',
+
 );
index 4136315..a2868eb 100644 (file)
@@ -120,7 +120,7 @@ $messages = array(
 'tog-hidepatrolled' => 'Klänedön redakamis pezepöl in lised votükamas nulik.',
 'tog-newpageshidepatrolled' => 'Klänedön padis pezepöl in lised padas nulik',
 'tog-extendwatchlist' => 'Stäänükön galädalisedi ad jonön votükamis tefik valik, e no te nulikünos',
-'tog-usenewrc' => 'Grupön votükamis pado in votukäms nulik e galädalised (me JavaScript)',
+'tog-usenewrc' => 'Grupön votükamis pado in votukäms nulik e galädalised',
 'tog-numberheadings' => 'Givön itjäfidiko nümis dilädatiädes',
 'tog-showtoolbar' => 'Jonön redakamastumemi',
 'tog-editondblclick' => 'Dälön redakön padis pö drän telik mugaknopa',
@@ -250,6 +250,7 @@ $messages = array(
 'newwindow' => '(maifikon in fenät nulik)',
 'cancel' => 'Stöpädön',
 'moredotdotdot' => 'Plu...',
+'morenotlisted' => 'Lised at no binon lölöfik.',
 'mypage' => 'Pad',
 'mytalk' => 'Bespiks',
 'anontalk' => 'Bespiks ela IP at',
@@ -278,7 +279,7 @@ $messages = array(
 'vector-view-history' => 'Logön jenotemi',
 'vector-view-view' => 'Reidön',
 'vector-view-viewsource' => 'Logön fonäti',
-'actions' => 'Duns',
+'actions' => 'Dunots',
 'namespaces' => 'Nemaspads',
 
 'errorpagetitle' => 'Pöl',
@@ -319,7 +320,7 @@ $messages = array(
 'articlepage' => 'Jonön ninädapadi',
 'talk' => 'Bespik',
 'views' => 'Logams',
-'toolbox' => 'Stumem',
+'toolbox' => 'Stums',
 'userpage' => 'Logön gebanapadi',
 'projectpage' => 'Logön proyegapadi',
 'imagepage' => 'Jonön ragivapad',
@@ -419,6 +420,7 @@ Mögos i, das atos sinifon, das dabinon säkädil pö program fa {{SITENAME}} pa
 # General errors
 'error' => 'Pöl',
 'databaseerror' => 'Pöl in nünodem',
+'databaseerror-function' => 'Dunod: $1',
 'databaseerror-error' => 'Pöl: $1',
 'laggedslavemode' => 'Nuned: pad ba labon votükamis brefabüik',
 'readonly' => 'Vük pefärmükon',
@@ -461,7 +463,7 @@ Beg: $2',
 'viewsource-title' => 'Logön fonäti pada: "$1"',
 'actionthrottled' => 'Dun pemiedükon',
 'actionthrottledtext' => 'Ad tadunön reklamami itjäfidik (el „spam“), dunot at no padälon tu suvo dü brefüp. Ya erivol miedi gretikün. Steifülolös nogna pos minuts anik.',
-'protectedpagetext' => 'Pad at pejelon ad neletön redakami.',
+'protectedpagetext' => 'Pad at pejelon ad neletön redakami u dunotis votik.',
 'viewsourcetext' => 'Kanol logön e kopiedön fonätakoti pada at:',
 'protectedinterface' => 'Pad at jafon vödemis sitanünas, ed anu pelökofärmükon ad vitön migebis.',
 'editinginterface' => "'''Nuned:''' redakol padi, kel labükon vödemis bevüik pro programem.
@@ -469,6 +471,10 @@ Votükams pada at oflunons logoti gebanasita pro gebans votik.
 Ad läükön u votükön tradutodis pro els wiki valik, demolös gebi ela [//translatewiki.net/?setlang=vo translatewiki.net]: topükamaproyeg ela MediaWiki.",
 'cascadeprotected' => 'Pad at pejelon ta redakam, bi pakeninükon fa {{PLURAL:$1|pad|pads}} sököl, kels pejelons ma „jänajel“: $2',
 'namespaceprotected' => "No dalol redakön padis in nemaspad: '''$1'''.",
+'mycustomcssprotected' => 'No dalol redakön padi: CSS at.',
+'mycustomjsprotected' => 'No dalol redakön padi: JavaScript at.',
+'myprivateinfoprotected' => 'No dalol redakön nünis privatik ola.',
+'mypreferencesprotected' => 'No dalol votükön buükamis olik.',
 'ns-specialprotected' => 'Pads patik no kanons paredakön.',
 'titleprotected' => "Jaf tiäda at penemögükon fa geban: [[User:$1|$1]].
 Kod binon: ''$2''.",
@@ -568,7 +574,7 @@ Sekü atos, visitans ladeti-IP at geböls no dalons jafön kalis pluik ün atim.
 'invalidemailaddress' => 'Ladet leäktronik no kanon pazepön bi fomät onik jiniko no lonöfon.
 Penolös ladeti labü fomät lonöföl, u vagükolös penamaspadi.',
 'accountcreated' => 'Kal pejafon',
-'accountcreatedtext' => 'Gebanakal pro $1 pejafon.',
+'accountcreatedtext' => 'Gebanakal pro [[{{ns:User}}:$1|$1]] ([[{{ns:User talk}}:$1|bespik]]) pejafon.',
 'createaccount-title' => 'Kalijafam in {{SITENAME}}',
 'createaccount-text' => 'Ek ejafon kali pro ladet leäktronik ola in {{SITENAME}} ($4) labü nem: „$2“ e letavöd: „$3“. Kanol nunädön oli e votükön letavödi olik anu.
 
@@ -678,7 +684,7 @@ Ladet-IP olik binon $3, e nüm blokama at binon #$5. Mäniotolös nünis löpik
 'nosuchsectiontitle' => 'Diläd no petuvöl',
 'nosuchsectiontext' => 'Esteifülol ad redakön dilädi no dabinöli.',
 'loginreqtitle' => 'Nunädam Paflagon',
-'loginreqlink' => 'nunädolös obi',
+'loginreqlink' => 'nunädön oli',
 'loginreqpagetext' => 'Mutol $1 ad logön padis votik.',
 'accmailtitle' => 'Letavöd pesedon.',
 'accmailtext' => "Letavöd fädik pro [[User talk:$1|$1]] pasedon lü $2.
@@ -747,7 +753,8 @@ If no vilol, das vödems olik poredakons nenmisero, tän no pladolös onis isio.
 Garanol obes, das ol it epenol atosi, u das ekopiedol atosi se räyun notidik u se fon libik sümik (logolös eli $1 pro notets).
 
 '''NO PLADOLÖD ISIO NEN DÄL LAUTANA VÖDEMIS LABÜ KOPIEDAGITÄT!'''",
-'longpageerror' => "'''PÖL: Vödem fa ol pesedöl labon lunoti miljölätas $1, kelos pluon leigodü völad muik pedälöl miljölätas $2. No kanon padakipön.'''",
+'longpageerror' => "'''Pöl: Vödem fa ol pesedöl labon lunoti {{PLURAL:$1|miljöläta bal|miljölätas $1}}, kelos pluon leigodü völad muik pedälöl {{PLURAL:$2|miljöläta bal|miljölätas $2}}.'''
+No kanon padakipön.",
 'readonlywarning' => "'''NUNED: Vük pefärmükon kodü kodididazesüd. No kanol dakipön votükamis olik anu. Kopiedolös vödemi nulik ini program votik e dakipolös oni in nünöm olik. Poso okanol dönu steifülön ad pladön oni isio.'''
 
 Geban, kel efärmükon oni, egevon kodi at: $1",
@@ -806,6 +813,7 @@ Paramet(s) at pemoädon(s).',
 'undo-failure' => 'No eplöpos ad sädunön redakami at sekü konflits vü redakams vüik.',
 'undo-norev' => 'No eplöpos ad sädunön redakami at, bi no dabinon u pämoükon.',
 'undo-summary' => 'Äsädunon votükami $1 fa [[Special:Contributions/$2|$2]] ([[User talk:$2|Bespikapad]])',
+'undo-summary-username-hidden' => 'Sädunön revidi: $1 fa geban peklenädöl',
 
 # Account creation failure
 'cantcreateaccounttitle' => 'Kal no kanon pajafön',
@@ -1158,7 +1166,7 @@ Muton labön {{PLURAL:$1|malati|malatis}} läs $1.',
 'right-ipblock-exempt' => 'Nedemön blokamis-IP, blokamis itjäfidik e grupiblokamis',
 'right-proxyunbannable' => 'Nedemön blokamis itjäfidik pladulömas',
 'right-protect' => 'Votükön jelanivodis e redakön padis pejelöl',
-'right-editprotected' => 'Bevobön padis pejelöl (nen vatafalajel)',
+'right-editprotected' => 'Bevobön padis pejelöl äs "{{int:protect-level-sysop}}"',
 'right-editinterface' => 'Votükön gebanaloveikömi',
 'right-editusercssjs' => 'Redakön ragivis-CSS e -JS gebanas votik',
 'right-editusercss' => 'Redakön ragivis-CSS gebanas votik',
@@ -1271,7 +1279,7 @@ Pads [[Special:Watchlist|galädaliseda olik]] '''pakazetons'''.",
 'uploadbtn' => 'Löpükön ragivi',
 'reuploaddesc' => 'Nosükon lopükami e geikön lü löpükamafomet.',
 'uploadnologin' => 'No enunädon oki',
-'uploadnologintext' => 'Mutol [[Special:UserLogin|nunädön oli]] ad löpükön ragivis.',
+'uploadnologintext' => 'Mutol $1 ad löpükön ragivis.',
 'upload_directory_missing' => 'Löpükamaragiviär ($1) no dabinon e no ekanon pajafön fa dünanünöm bevüresodik.',
 'upload_directory_read_only' => 'Ragiviär lopükama ($1) no kanon papenön fa dünanünöm bevüresodik.',
 'uploaderror' => 'Pök pö löpükam',
@@ -1585,7 +1593,7 @@ Primanünods: ninädasot/donasot, a.s. <code>image/jpeg</code>.',
 'booksources-invalid-isbn' => 'El ISBN at jiniko no lonöfon; kontrololös pökis po kopiedam se rigafonät.',
 
 # Special:Log
-'specialloguserlabel' => 'Geban:',
+'specialloguserlabel' => 'Dunan:',
 'speciallogtitlelabel' => 'Lükömöp (tiäd u geban):',
 'log' => 'Jenotaliseds',
 'all-logs-page' => 'Jenotaliseds notidik valik',
@@ -1643,7 +1651,7 @@ Protoks pestütöl: <code>$1</code>',
 'listusers-blocked' => '(pebloköl)',
 
 # Special:ActiveUsers
-'activeusers-count' => '{{PLURAL:$1|redakam|redakams}} $1 ün {{PLURAL:$3|del lätik|dels lätik $3}}',
+'activeusers-count' => '{{PLURAL:$1|dunot|dunots}} $1 ün {{PLURAL:$3|del lätik|dels lätik $3}}',
 'activeusers-hidebots' => 'Klänedolöd elis bot',
 'activeusers-hidesysops' => 'Klänedolöd guvanis',
 'activeusers-noresult' => 'Geban nonik petuvon.',
@@ -1778,10 +1786,12 @@ $2 jonon moükamis nulik.',
 'deletecomment' => 'Kod:',
 'deleteotherreason' => 'Kod votik:',
 'deletereasonotherlist' => 'Kod votik',
-'deletereason-dropdown' => '* Kods kösömik moükama
-** Beg lautana
+'deletereason-dropdown' => "* Kods kösömik moükama
+** 'Spam'
+** Vandalim
 ** Kopiedagitäts
-** Vandalim',
+** Beg lautana
+** Lüodüköm dädik",
 'delete-edit-reasonlist' => 'Redakön kodis moükama',
 'delete-toobig' => 'Pad at labon redakamajenotemi lunik ({{PLURAL:$1|revid|revids}} plu $1).
 Moükam padas somik pemiedükon ad vitön däropami pö {{SITENAME}}.',
@@ -1812,7 +1822,7 @@ Välolös knopi: „Geikön“ e dönulodolös padi, de kel ekömol, e tän stei
 Logolös [[Special:ProtectedPages|lisedi padas pejelöl]], kö pajonons padijelams anu lonöföls.',
 'protectedarticle' => 'ejelon padi: „[[$1]]“',
 'modifiedarticleprotection' => 'evotükon jelanivodi pada: „[[$1]]“',
-'unprotectedarticle' => 'Pad: „[[$1]]“ pesäjelon.',
+'unprotectedarticle' => 'esäjelon padi: "[[$1]]"',
 'movedarticleprotection' => 'moved protection settings from „[[$2]]“ to „[[$1]]“',
 'protect-title' => 'lonon jelanivodi pada: „$1“',
 'prot_1movedto2' => '[[$1]] petopätükon lü [[$2]]',
@@ -1924,7 +1934,7 @@ $1',
 'contributions' => '{{GENDER:$1|Gebanakeblünots}}',
 'contributions-title' => 'Gebanakeblünots pro $1',
 'mycontris' => 'Keblünots',
-'contribsub2' => 'Tefü $1 ($2)',
+'contribsub2' => '{{GENDER:$3|Hiela|Jiela|Ela}} $1 ($2)',
 'nocontribs' => 'Votükams nonik petuvons me paramets at.',
 'uctop' => '(anuik)',
 'month' => 'De mul (e büiks):',
@@ -2053,12 +2063,9 @@ Logolös blokamis e xilis anu lonöfölis in [[Special:BlockList|lised blokamas]
 'ipb_blocked_as_range' => 'Pöl: ladet-IP $1 no peblokon stedöfiko e no kanon pasäblokön.
 Peblokon ye as dil ladetema: $2, kel kanon pasäblokön.',
 'ip_range_invalid' => 'Ladetem-IP no lonöföl.',
-'blockme' => 'Blokolöd obi',
 'proxyblocker' => 'Bloköm pladulömas',
-'proxyblocker-disabled' => 'Dun at penemogükon.',
 'proxyblockreason' => 'Ladet-IP olik peblokon bi binon pladulöm maifik.
 Kosikolös ko dünigevan bevüresodik u kaenastütans olik e nunolös ones sefasäkädi fefik at.',
-'proxyblocksuccess' => 'Peledunon.',
 'sorbsreason' => 'Ladet-IP olik palisedon as pladulöm maifik pö el DNSBL fa {{SITENAME}} pageböl.',
 'sorbs_create_account_reason' => 'Ladet-IP olik palisedon as pladulöm maifik pö el DNSBL fa {{SITENAME}} pageböl.
 No dalol jafön kali.',
index 6a082f4..3998ef4 100644 (file)
@@ -734,7 +734,6 @@ Cüľľellä $2 on spiiska viimeiziss pühcimühsiiss.',
 'blocklogentry' => 'piätteli cäüttijää vai IP-cislaa [[$1]]. Piättelemin lõpub $2 $3',
 'unblocklogentry' => 'rooci cäüttijält $1 muutuzpiäsüss',
 'block-log-flags-nocreate' => 'lukuloomin piäteltü',
-'proxyblocksuccess' => 'On tehtü.',
 
 # Move page
 'movepagetext' => "Alla õlõvall ruumõll võitta anta cüľľelle uutt nimiä; kõik cüľľee istori leeb liikutõttu uuvvõ nimee alle.
index 998d675..7ed5278 100644 (file)
@@ -1562,7 +1562,6 @@ Perämäidsi kistutuisi ja tagasitegemiisi saat kaiaq [[Special:Log/delete|kistu
 'ip_range_invalid' => 'Viganõ puutri võrgoaadrõsi kujo.',
 'proxyblocker' => 'Vaihõserveri kinniqpidämine',
 'proxyblockreason' => "Su puutri võrgoaadrõs om kinniq peet, selle et taa om avalik vaihõserver. Otsiq üles uma võrgoliini pakja vai puutrias'atundja ja kõnõlõq näile taast hädäst.",
-'proxyblocksuccess' => 'Valmis.',
 'sorbsreason' => 'Su puutri võrgoaadrõs om SORBS-i mustan nimekirän ku avalik vaihõserver.',
 'sorbs_create_account_reason' => 'Su puutri võrgoaadrõs om pant SORBS-i musta nimekirjä ku avalik vaihõserver. Sa saa-i pruukjanimme tetäq',
 
index 714c552..52b3202 100644 (file)
@@ -1724,7 +1724,6 @@ Loukîz li [[Special:BlockList|djivêye des blocaedjes]] po vey les blocaedjes 
 'ip_range_invalid' => "Fortchete d' adresses IP nén valide.",
 'proxyblocker' => 'Blocaedje di procsi',
 'proxyblockreason' => "Voste adresse IP a stî blokêye paski c' est on procsi k' est å lådje. Contactez vost ahesseu Internet ou l' siervice di sopoirt tecnike eyet lzî dire po çoula, la k' c' est on problinme di såvrité serieus.",
-'proxyblocksuccess' => 'Fwait.',
 'sorbsreason' => "Voste adresse IP si trove dins l' djivêye des procsis å lådje di DNSBL.",
 'sorbs_create_account_reason' => "Voste adresse IP si trove dins l' djivêye des procsis å lådje di DNSBL. Vos n' poloz nén ahiver on conte d' uzeu.",
 
index c9caa3c..14db086 100644 (file)
@@ -203,8 +203,6 @@ $messages = array(
 'noindex-category' => 'Mga diri nakatudlokan nga pagkli',
 'broken-file-category' => 'Mga pakli nga mayda utod nga mga sumpay hin paypay',
 
-'linkprefix' => '/^(.*?)([a-zA-Z\\x80-\\xff]+)$/sD',
-
 'about' => 'Mahitungod han',
 'article' => 'Pakli hin sulod',
 'newwindow' => '(nabuklad hin bag-o nga tamboan o bintana)',
@@ -1899,8 +1897,6 @@ Tanggala an pagpugong $1',
 'block-log-flags-nousertalk' => 'diri makakaliwat hit kalugaringon nga hiruhimangraw nga pakli',
 'block-log-flags-hiddenname' => 'nakatago an agnay-hit-gumaramit',
 'ipb_already_blocked' => '"$1" in ginpugngan na',
-'blockme' => 'Pugngi ako',
-'proxyblocksuccess' => 'Human na.',
 'ipbnounblockself' => 'Diri ka gintutugotan hin pagtanggal hit pagpugong ha kalugaringon',
 
 # Developer tools
index 295b15f..092ea9d 100644 (file)
@@ -1850,11 +1850,8 @@ Dangaa bëgg a soppi anam yi?',
 'ipb_cant_unblock' => 'Njuumte: téyeg $1 gisuwul. Xéj-na dañ kaa téyedi ba noppi.',
 'ipb_blocked_as_range' => 'Njuumte: màkkaan mi $1 téyewuñ ko moom kase, kon doo ko man téyedi. Ci mbooloom $2 la bokk, faww nga téyedi mbooloo mépp.',
 'ip_range_invalid' => 'Mbooloom IP mi baaxul.',
-'blockme' => 'Téye ma',
 'proxyblocker' => 'Téyekatu yóbbantekat',
-'proxyblocker-disabled' => 'Bii solo doxul.',
 'proxyblockreason' => 'Dañ téye sa IP ndax dadi ab yóbbantekat bu ubbeeku. Di la ñaan nga jublu ci sa ki la jox internet yegge ko jafe-jafeb kaaraange bi.',
-'proxyblocksuccess' => 'Jàll na.',
 'sorbsreason' => 'Sa màkkaanu IP dañ ko limaale niki ab yóbbantekat bu ubbeeku ci DNSBL bi {{SITENAME}} di jëfandikoo.',
 'sorbs_create_account_reason' => 'Sa màkkaanu IP dañ ko limaale niki ab yóbbantekat bu ubbeeku ci DNSBL bi {{SITENAME}} di jëfandikoo. Kon sag mbindu du mana nekk.',
 'cant-block-while-blocked' => 'Manoo di téye yeneen jëfandikukat ci diir bi ñu la téye.',
index b8c6da0..69a4b97 100644 (file)
@@ -1545,7 +1545,6 @@ $1",
 'blocklogtext' => '箇是用戶封搭解封操作個記錄。自動封個IP地址弗排。到[[Special:BlockList|IP 封表]]裏望目前生效個封表。',
 'unblocklogentry' => '$1已经拨解封',
 'block-log-flags-nocreate' => '建账号禁用哉',
-'proxyblocksuccess' => '好哉。',
 
 # Developer tools
 'lockdb' => '鎖數據庫',
index 1bdd3ed..a0edc0c 100644 (file)
@@ -990,7 +990,6 @@ $2 шидрә һарһлһна төлә хәләтн.',
 'blocklogentry' => '[[$1]] бүслсн $2 күртл, $3 учрта',
 'unblocklogentry' => '$1-г бүслсн биш болулв',
 'block-log-flags-nocreate' => 'бичгдлһиг бүтәҗ болшго',
-'blockme' => 'Намаг бүслчк',
 
 # Move page
 'movepagetext' => "Та дораһар цаасар, халхин сольлһна тууҗ көндәд, терүнә нериг сольх.
index 22e160f..4c40e02 100644 (file)
@@ -413,7 +413,7 @@ $messages = array(
 'articlepage' => 'זען אינהאַלט בלאַט',
 'talk' => 'שמועס',
 'views' => 'קוקן',
-'toolbox' => 'געצייג קאסטן',
+'toolbox' => 'געצייג',
 'userpage' => 'זען באַניצער בלאַט',
 'projectpage' => 'זען פראיעקט בלאַט',
 'imagepage' => 'זען טעקע בלאט',
@@ -726,7 +726,7 @@ $2',
 'passwordsent' => 'א ניי פאסווארט איז געשיקט געווארן צום ע-פאסט אדרעס רעגיסטרירט פאר "$1".
 ביטע ווידער אריינלאגירן נאך דעם וואס איר באקומט עס.',
 'blocked-mailpassword' => 'אייער איי פי אדרעס איז בלאקירט צו רעדאקטירן, דערוועגן זענט איר נישט ערלויבט צו באניצן מיטן פאסווארט ווידעראויפלעבונג פֿונקציע כדי צו פארמיידן סיסטעם קרומבאניץ.',
-'eauthentsent' => '×\90 ×\91×\90ש×\98×¢×\98×\99×\92×\95× ×\92 ×¢-×\91ר×\99×\95×\95 ×\90×\99×\96 ×\92עש×\99ק×\98 ×\92×¢×\95×\95×\90ר×\9f ×¦×\95 ×\93×¢×\9d ×\91×\90ש×\98×\99×\9e×\98×\9f ×¢-פ×\90ס×\98 ×\90×\93רעס. ×\90×\99×\99×\93ער ×¡×\99×\99 ×\95×\95×\90ס אנדערע ע-פאסט וועט ווערן געשיקט צו דער קאנטע, וועט איר דארפן פאלגן די אנווייזונגען אין דער מעלדונג כדי צו זיין זיכער אז די קאנטע איז טאקע אייערס.',
+'eauthentsent' => '×\90 ×\91×\90ש×\98×¢×\98×\99×\92×\95× ×\92 ×¢-×\91ר×\99×\95×\95 ×\90×\99×\96 ×\92עש×\99ק×\98 ×\92×¢×\95×\95×\90ר×\9f ×¦×\95 ×\93×¢×\9d ×\91×\90ש×\98×\99×\9e×\98×\9f ×¢-פ×\90ס×\98 ×\90×\93רעס. ×\90×\99×\99×\93ער ×¡×\99×\99 ×\95×\95×¢×\9c×\9b×¢ אנדערע ע-פאסט וועט ווערן געשיקט צו דער קאנטע, וועט איר דארפן פאלגן די אנווייזונגען אין דער מעלדונג כדי צו זיין זיכער אז די קאנטע איז טאקע אייערס.',
 'throttled-mailpassword' => "מ'האט שוין געשיקט א בליצבריוו צוריקצושטעלן דאס פאסווארט, אין {{PLURAL:$1|דער לעצטער שעה|די לעצטע $1 שעה'ן}}. כדי צו פארמײַדן שלעכט באניצן, נאר איין פאסווארט צוריקשטעלן בליצבריוו וועט געשיקט ווערן אין {{PLURAL:$1|א שעה |$1 שעה'ן}}.",
 'mailerror' => 'פֿעלער שיקנדיג פאסט: $1',
 'acct_creation_throttle_hit' => 'באַזוכער צו דער וויקי וואס באַניצן אייער IP אַדרעס האָבן שױן באַשאַפֿן {{PLURAL:$1|1 קאנטע|$1 קאנטעס}} במשך דעם לעצטן טאָג, דעם מאַקסימום וואָס מען ערלויבט אין דעם פעריאד.
@@ -1174,15 +1174,15 @@ $2
 * אויפדעקונג פון פריוואטקייט אינפארמאציע
 * ''היים אדרעסן, טעלעפאן נומערן, אדער סאשעל סעקיורעטי, א.א.וו.:'''",
 'revdelete-legend' => 'שטעלט ווייזונג באגרענעצונגען',
-'revdelete-hide-text' => '×\91×\90×\94×\90×\9c×\98 ×\90×\99× ×\94×\90×\9c×\98 ×¤×\95×\9f ×\95×\95ערס×\99×¢',
+'revdelete-hide-text' => '×\95×\95ערס×\99×¢ ×\98עקס×\98',
 'revdelete-hide-image' => 'באהאלט טעקע אינהאלט',
 'revdelete-hide-name' => 'באהאלט אקציע און ציל',
-'revdelete-hide-comment' => '×\91×\90×\94×\90×\9c×\98 ×¢× ×\93ער×\9f ×\94ער×\94',
-'revdelete-hide-user' => "×\91×\90Ö·×\94×\90Ö·×\9c×\98×\9f ×¨×¢×\93×\90ַק×\98×\90ר'ס ×\91×\90× ×\99צער-× ×\90×\9e×¢×\9f/IP-×\90Ö·×\93רעס",
+'revdelete-hide-comment' => 'רע×\93×\90ק×\98×\99ר×\95× ×\92 ×¨×¢×\96×\95×\9e×¢',
+'revdelete-hide-user' => "רעדאַקטאר'ס באניצער-נאמען/IP-אַדרעס",
 'revdelete-hide-restricted' => 'באהאלט אינפארמאציע אויך פון אדמיניסטראטורן פונקט ווי פשוטע באנוצער',
 'revdelete-radio-same' => '(נישט ענדערן)',
-'revdelete-radio-set' => '×\99×\90',
-'revdelete-radio-unset' => '× ×\99×\99ן',
+'revdelete-radio-set' => '×\96×¢×\91×\90ר',
+'revdelete-radio-unset' => 'פֿ×\90ַר×\91×\90ָר×\92ן',
 'revdelete-suppress' => 'באַהאַלטן אינפֿארמאַציע פון אַדמיניסטראַטארן ווי אויך אנדערע',
 'revdelete-unsuppress' => 'טוה אפ באגרענעצונגן אין גענדערטע רעוויזיעס',
 'revdelete-log' => 'אורזאַך:',
@@ -1481,6 +1481,8 @@ $1",
 'userrights-notallowed' => 'איר האט נישט קיין ערלויבניש צוצולייגן אדער אוועקנעמען באַניצער רעכטן.',
 'userrights-changeable-col' => 'גרופעס איר קענט ענדערן',
 'userrights-unchangeable-col' => 'גרופעס איר קענט נישט ענדערן',
+'userrights-conflict' => 'קאנפֿליקט פון באניצער־רעכטן ענדערונגען! זייט אזוי גוט רעצענזירן און באשטעטיקן אײַערע ענדערונגען.',
+'userrights-removed-self' => 'איר האט דערפאלגרייך אראפגענומען אייערע אייגענע רעכטע. אזוי קענט איר מער נישט דערגרייכן דעם בלאט.',
 
 # Groups
 'group' => 'גרופע:',
@@ -2681,11 +2683,8 @@ $1',
 דאך איז ער בלאקירט אַלס א טייל פֿון דעם אָפשטאַנד $2, וואָס מ'קען יא אויפֿבלאקירן.",
 'ip_range_invalid' => 'אומריכטיגער IP גרייך.',
 'ip_range_toolarge' => 'אָפשטאַנדן גרעסער ווי /$1 קען מען נישט בלאקירן.',
-'blockme' => 'בלאקירט מיך',
 'proxyblocker' => 'פראקסי בלאקער',
-'proxyblocker-disabled' => 'די  פֿונקציע איז אומאַקטיווירט.',
 'proxyblockreason' => 'אייער איי.פי. אדרעס איז געווארן געבלאקט צוליב דעם ווייל דאס איז א אפענער פראקסי. ביטע פארבינדט זיך מיט אייער אינטערנעט סערוויס פראוויידער אדער טעקס סאפארט צו אינפארמירן זיי איבער דעם ערענסטן זיכערהייט פראבלעם.',
-'proxyblocksuccess' => 'געטאן.',
 'cant-block-while-blocked' => 'איר קען נישט בלאקירן קיין אנדערע באניצער ווען איר זענט אליין בלאקירט.',
 'ipbblocked' => 'איר קען נישט בלאקירן אדער אויפבלאקירן אנדערע באניצער, ווייל איר זענט אליין בלאקירט.',
 'ipbnounblockself' => 'איר זענט נישט ערלויבט זיך אליין אויסבלאקירן',
index d5b9bda..1d2c0b2 100644 (file)
@@ -2530,11 +2530,8 @@ $1',
 Sùgbọ́n ó jẹ́ dídílọ́nà gẹ́gẹ́bí ìkan nínú ìgbàjá $2, èyí sì ṣe é mọ́ dí lọ́nà mọ́.',
 'ip_range_invalid' => 'Àdìmọ́ IP aláìníìbámu.',
 'ip_range_toolarge' => 'Ìgbàjá ìdínà tó tóbi ju /$1 kò jẹ́ gbígbà ní àyè.',
-'blockme' => 'Dínà mi',
 'proxyblocker' => 'Olùdínà ẹ̀rọ-ìwọ̀fà ẹlòmíràn',
-'proxyblocker-disabled' => 'Ìmúṣe yìí jẹ́ dídálẹ́kun.',
 'proxyblockreason' => 'Àdírẹ́ẹ̀sì IP yín ti jẹ́ dídílọ́nà nítorípé ó jẹ́ ẹ̀rọ alàìlórúkọ ẹlòmíràn ìgboro. Ẹ sọ ìsòro yìí fún olùpèsè ìwọ̀fà Internet yín tàbí aṣeàtìlẹyìn ẹ̀rọ-ìpèsè ibiiṣẹ́ yín.',
-'proxyblocksuccess' => 'Ṣetán',
 'sorbsreason' => 'Àdírẹ́ẹ̀sì IP yín jẹ́ títòjọ bíi ẹ̀rọ-ìwọ̀fà ẹlòmíràn àsíílẹ̀ nínú DNSBL tí {{SITENAME}} lò.',
 'sorbs_create_account_reason' => 'Àdírẹ́ẹ̀sì IP yín jẹ́ títòjọ bíi ẹ̀rọ-ìwọ̀fà ẹlòmíràn àsíílẹ̀ nínú DNSBL tí {{SITENAME}} lò.
 Ẹ kò le dá àpamọ́.',
index 284ecc6..d048e58 100644 (file)
@@ -663,7 +663,7 @@ $1',
 'passwordsent' => '新嘅密碼已經寄咗畀呢位用戶 "$1" 嘅電郵地址。收到之後請重新登入。',
 'blocked-mailpassword' => '你嘅IP地址被鎖住,唔可以用密碼復原功能以防止濫用。',
 'eauthentsent' => '確認電郵已經傳送到指定嘅電郵地址。喺其它嘅郵件傳送到呢個戶口之前,你需要按電郵嘅指示,嚟確認呢個戶口真係屬於你嘅。',
-'throttled-mailpassword' => '一個密碼提醒已經響$1個鐘頭之前發送咗。為咗防止濫用,響$1個鐘頭之內只可以發送一個密碼提醒。',
+'throttled-mailpassword' => '一個密碼提醒已經響$1{{PLURAL:$1|個鐘頭}}之前發送咗。為咗防止濫用,響$1{{PLURAL:$1|個鐘頭}}之內只可以發送一個密碼提醒。',
 'mailerror' => '傳送電郵錯誤: $1',
 'acct_creation_throttle_hit' => '利用你呢個IP地址嘅訪客響上一日已經開咗 $1 個戶口,係響呢段時間嘅上限。
 結果,利用呢個IP地址嘅訪客唔可以響呢段時間再開多個戶口。',
@@ -2328,11 +2328,8 @@ $1',
 'ipb_blocked_as_range' => '錯誤:個IP $1 無直接封鎖,唔可以解封。但係佢係響 $2 嘅封鎖範圍之內,嗰段範圍係可以解封嘅。',
 'ip_range_invalid' => '無效嘅IP範圍',
 'ip_range_toolarge' => '大過 /$1 嘅封鎖範圍係唔容許嘅。',
-'blockme' => '封鎖我',
 'proxyblocker' => 'Proxy 封鎖器',
-'proxyblocker-disabled' => '呢個功能已經停用。',
 'proxyblockreason' => '你嘅IP係一個公開(指任何人都可以用,無須身份認證?)嘅代理地址,因此被封鎖。請聯絡你嘅Internet服務提供商或技術支援,向佢哋報告呢個嚴重嘅安全問題。',
-'proxyblocksuccess' => '完成。',
 'sorbsreason' => '你嘅IP地址已經畀響{{SITENAME}}度用嘅DNSBL列咗做公開代理。',
 'sorbs_create_account_reason' => '你嘅IP地址已經畀響{{SITENAME}}度用嘅DNSBL列咗做公開代理。你唔可以開新戶口。',
 'cant-block-while-blocked' => '當你被封鎖嗰陣唔可以封鎖其他用戶。',
@@ -2648,6 +2645,8 @@ $1',
 'spambot_username' => 'MediaWiki垃圾清除',
 'spam_reverting' => '恢復返去最後一個唔包含指去$1嘅連結嘅嗰個修訂。',
 'spam_blanking' => '全部版本都含有指去$1嘅連結,留空',
+'simpleantispam-label' => "反垃圾檢查。
+'''唔好'''加入呢個!",
 
 # Skin names
 'skinname-cologneblue' => '科隆藍',
@@ -2715,6 +2714,12 @@ $1',
 'bydate' => '以時間',
 'sp-newimages-showfrom' => '顯示由$1 $2嘅新檔',
 
+# Video information, used by Language::formatTimePeriod() to format lengths in the above messages
+'hours' => '$1{{PLURAL:$1|個鐘}}',
+
+# Human-readable timestamps
+'hours-ago' => '$1{{PLURAL:$1|個鐘}}之前',
+
 # Bad image list
 'bad_image_list' => '請根據下面嘅格式去寫:
 
@@ -3019,7 +3024,7 @@ Variants for Chinese language
 'exif-gpsmeasuremode-3' => '三維量度',
 
 # Pseudotags used for GPSSpeedRef
-'exif-gpsspeed-k' => 'å\8d\83ç±³/小時',
+'exif-gpsspeed-k' => 'å\85¬é\87\8c/小時',
 'exif-gpsspeed-m' => '英里/小時',
 'exif-gpsspeed-n' => '浬/小時',
 
@@ -3310,4 +3315,7 @@ MediaWiki是基於使用目的而加以發佈,但係就唔會負上任何嘅
 'searchsuggest-search' => '搵嘢',
 'searchsuggest-containing' => '名單傳送緊...',
 
+# Durations
+'duration-hours' => '$1{{PLURAL:$1|個鐘}}',
+
 );
index 6682da4..4936f8e 100644 (file)
@@ -26,6 +26,7 @@
  * @author Fengchao
  * @author Franklsf95
  * @author Gaoxuewei
+ * @author GeneralNFS
  * @author Gzdavidwong
  * @author Happy
  * @author Hercule
@@ -397,7 +398,7 @@ $messages = array(
 'tog-watchlisthideliu' => '隐藏监视列表中的登录用户的编辑',
 'tog-watchlisthideanons' => '隐藏监视列表中的匿名用户的编辑',
 'tog-watchlisthidepatrolled' => '隐藏监视列表中的已巡查编辑',
-'tog-ccmeonemails' => '给我发送我发送给其他用户的电子邮件的副本',
+'tog-ccmeonemails' => '把我给其他用户发送的电子邮件的副本发送给我',
 'tog-diffonly' => '不在差异下面显示页面内容',
 'tog-showhiddencats' => '显示隐藏分类',
 'tog-noconvertlink' => '停用链接文字转换',
@@ -578,7 +579,7 @@ $messages = array(
 'articlepage' => '查看内容页面',
 'talk' => '讨论',
 'views' => '查看',
-'toolbox' => '工具',
+'toolbox' => '工具',
 'userpage' => '查看用户页面',
 'projectpage' => '查看项目页面',
 'imagepage' => '查看文件页面',
@@ -727,10 +728,9 @@ $1',
 'no-null-revision' => '无法创建对"$1"页面新的空白修订',
 'badtitle' => '错误标题',
 'badtitletext' => '所请求页面的标题是无效的、不存在,跨语言或跨wiki链接的标题错误。它可能包含一个或更多的不能用于标题的字符。',
-'perfcached' => '下列数据已缓存,但可能已过时。最高{{PLURAL:$1|一个结果|$1个结果}}在缓存中可用。',
-'perfcachedts' => '下列数据已缓存,最后更新于$1。缓存中最多可有{{PLURAL:$4|1个结果|$4个结果}}。',
-'querypage-no-updates' => '当前禁止对此页面进行更新。
-此处的数据将不能被立即刷新。',
+'perfcached' => '以下是缓存的数据,可能不是最新的数据。缓存中最多有{{PLURAL:$1|$1条结果}}。',
+'perfcachedts' => '以下是缓存的数据,最后更新于$1。缓存中最多有{{PLURAL:$4|$4条结果}}。',
+'querypage-no-updates' => '该页面的更新目前停用。这里的数据不会马上刷新。',
 'wrong_wfQuery_params' => '错误的参数被传递到 wfQuery()<br />
 函数:$1<br />
 查询:$2',
@@ -929,7 +929,7 @@ $2',
 'passwordreset-capture-help' => '如果您选中此框,电子邮件(包括临时密码)将显示,并发送给用户。',
 'passwordreset-email' => '电子邮件地址:',
 'passwordreset-emailtitle' => '在 {{SITENAME}} 的帐户详细信息',
-'passwordreset-emailtext-ip' => '有人通过IP地址$1(可能是您请求重设{{SITENAME}}($4)上相关账户的密码。{{PLURAL:$3|以下账户|此账户}}与该电子邮件地址关联:
+'passwordreset-emailtext-ip' => '有人(可能是您,来自IP地址$1)请求重设{{SITENAME}}($4)上相关账户的密码。{{PLURAL:$3|以下账户|此账户}}与该电子邮件地址关联:
 
 $2
 
@@ -1176,7 +1176,7 @@ $2
 # "Undo" feature
 'undo-success' => '该编辑可以被撤销。请检查下面的对比以核实你想要撤销的内容,然后保存下面的更改以完成撤销。',
 'undo-failure' => '因存在冲突的中间编辑,本编辑不能撤销。',
-'undo-norev' => '由于其修订版本不存在或已删除,此编辑不能撤销。',
+'undo-norev' => '该编辑无法撤消,因为它不存在或已被删除。',
 'undo-summary' => '撤销[[Special:Contributions/$2|$2]]([[User talk:$2|讨论]])的版本$1',
 'undo-summary-username-hidden' => '取消由一匿名用户所作的修订$1',
 
@@ -1400,7 +1400,7 @@ $1",
 'powersearch' => '高级搜索',
 'powersearch-legend' => '高级搜索',
 'powersearch-ns' => '在以下的名字空间中搜索:',
-'powersearch-redir' => '列出重定向',
+'powersearch-redir' => '列出重定向',
 'powersearch-field' => '搜索',
 'powersearch-togglelabel' => '选择:',
 'powersearch-toggleall' => '全选',
@@ -2085,7 +2085,7 @@ $1',
 
 # Random redirect
 'randomredirect' => '随机重定向',
-'randomredirect-nopages' => '在 "$1" 名字空间中没有重定向页面。',
+'randomredirect-nopages' => '“$1”名字空间中没有重定向。',
 
 # Statistics
 'statistics' => '统计',
@@ -2128,7 +2128,7 @@ $1',
 'brokenredirects-delete' => '删除',
 
 'withoutinterwiki' => '无语言链接页面',
-'withoutinterwiki-summary' => '以下的页面是未有语言链接到其它语言版本。',
+'withoutinterwiki-summary' => '以下页面没有链接至其它语言版本。',
 'withoutinterwiki-legend' => '前缀',
 'withoutinterwiki-submit' => '显示',
 
@@ -2146,7 +2146,7 @@ $1',
 'ntransclusions' => '用于$1个页面中',
 'specialpage-empty' => '无该报告的结果。',
 'lonelypages' => '孤立页面',
-'lonelypagestext' => '以下页面尚未被{{SITENAME}}中的其它页面链接或被之包含。',
+'lonelypagestext' => '以下页面没有被{{SITENAME}}的其它页面链接或包含。',
 'uncategorizedpages' => '未归类页面',
 'uncategorizedcategories' => '未归类分类',
 'uncategorizedimages' => '未归类文件',
@@ -2174,14 +2174,14 @@ $1',
 'shortpages' => '短页面',
 'longpages' => '长页面',
 'deadendpages' => '断链页面',
-'deadendpagestext' => '以下页面没有链接到{{SITENAME}}中的其它页面。',
+'deadendpagestext' => '以下页面没有链接至{{SITENAME}}的其它页面。',
 'protectedpages' => '受保护页面',
 'protectedpages-indef' => '仅无限期保护',
 'protectedpages-cascade' => '仅连锁保护',
 'protectedpagestext' => '以下页面受到保护,不能移移或编辑',
 'protectedpagesempty' => '在这些参数下没有页面正在保护。',
 'protectedtitles' => '受保护标题',
-'protectedtitlestext' => '以下的页面已经被保护以防止创建',
+'protectedtitlestext' => '以下标题受到保护,不能创建',
 'protectedtitlesempty' => '在这些参数之下并无标题正在保护。',
 'listusers' => '用户列表',
 'listusers-editsonly' => '只显示有编辑的用户',
@@ -2196,7 +2196,7 @@ $1',
 'movethispage' => '移动本页',
 'unusedimagestext' => '下列文件已存在,但并未插入任何页面。
 请注意其它网站可能会直接通过URL链接此文件,因此下面列出的文件依然有可能被使用。',
-'unusedcategoriestext' => '虽然没有被其它页面或者分类所采用,但列表中的分类页依然存在。',
+'unusedcategoriestext' => '以下分类页面实际存在,即使没有其它页面或分类利用它们。',
 'notargettitle' => '无目标',
 'notargettext' => '您还没有指定一个目标页面或用户以进行此项操作。',
 'nopagetitle' => '无目标页面',
@@ -2465,7 +2465,7 @@ $UNWATCHURL
 'editcomment' => '编辑摘要:"<i>$1</i>"。',
 'revertpage' => '已恢复[[Special:Contributions/$2|$2]]([[User talk:$2|讨论]])的编辑至[[User:$1|$1]]的最后一个修订版本',
 'revertpage-nouser' => '恢复由隐藏用户的编辑到{{GENDER:$1|[[User:$1|$1]]}}的最后一个修订版本',
-'rollback-success' => '已恢复$1的编辑,更改回$2的最后修订版本。',
+'rollback-success' => '已恢复$1的编辑,更改回$2的最后版本。',
 
 # Edit tokens
 'sessionfailure-title' => '会话无效',
@@ -2752,11 +2752,8 @@ $1被封禁的理由是:“$2”',
 'ipb_blocked_as_range' => '错误:IP地址$1未被直接封禁,故无法解除封禁。然而,它位于IP地址段$2的封禁范围内,后者可被解除封禁。',
 'ip_range_invalid' => '无效的IP地址段。',
 'ip_range_toolarge' => '不允许大于/$1的段封禁。',
-'blockme' => '封禁我',
 'proxyblocker' => '代理封禁器',
-'proxyblocker-disabled' => '此功能已禁用。',
 'proxyblockreason' => '您的IP地址为已被封禁的公开代理。请联系您的互联网服务提供商或技术支持者,并告知他们此严重的安全问题。',
-'proxyblocksuccess' => '完成。',
 'sorbsreason' => '在{{SITENAME}}使用的DNSBL中,您的IP地址被列为公开代理。',
 'sorbs_create_account_reason' => '在{{SITENAME}}使用的DNSBL中,您的IP地址被列为公开代理,因此您不能创建新账户。',
 'xffblockreason' => '您或您正在使用的代理服务器呈现在X-Forwarded-For数据包头的一个IP地址已被封禁。封禁原因为:$1',
@@ -3054,7 +3051,7 @@ $2',
 'tooltip-save' => '保存你的更改',
 'tooltip-preview' => '预览您的更改,请在保存前使用此功能!',
 'tooltip-diff' => '显示您对该文字所做的更改',
-'tooltip-compareselectedversions' => '查看此页面两个选定的修订版本间的差异。',
+'tooltip-compareselectedversions' => '查看该页面两个选定的版本之间的差异。',
 'tooltip-watch' => '添加本页面至你的监视列表',
 'tooltip-watchlistedit-normal-submit' => '删除标题',
 'tooltip-watchlistedit-raw-submit' => '更新监视列表',
@@ -3064,7 +3061,7 @@ $2',
 'tooltip-undo' => '“撤销”可以恢复该编辑并在预览模式下打开编辑表单。它允许在摘要中加入原因。',
 'tooltip-preferences-save' => '保存系统设置',
 'tooltip-summary' => '请输入简短的摘要',
-'tooltip-iwiki' => '$1——$2',
+'tooltip-iwiki' => '$1 – $2',
 
 # Stylesheets
 'common.css' => '/* 此处的 CSS 将应用于所有的皮肤 */',
@@ -3114,6 +3111,8 @@ $2',
 'spam_reverting' => '恢复到不包含链接的最近修订版本$1',
 'spam_blanking' => '消隐所有包含链接至$1的修订',
 'spam_deleting' => '正在删除所有包含至$1的版本',
+'simpleantispam-label' => "反垃圾检查。
+'''不要'''加入这个!",
 
 # Info page
 'pageinfo-title' => '“$1”的信息',
@@ -3787,7 +3786,7 @@ $5
 # action=purge
 'confirm_purge_button' => '确定',
 'confirm-purge-top' => '要清除此页面的缓存吗?',
-'confirm-purge-bottom' => '清理一页将会清除快取以及强迫显示最现时之修订版本。',
+'confirm-purge-bottom' => '清除页面数据会清除缓存并强制显示最近的版本。',
 
 # action=watch/unwatch
 'confirm-watch-button' => '确定',
@@ -3907,12 +3906,12 @@ MediaWiki发表时预期有用,但对此'''无任何保证''',亦无隐含
 # Special:Redirect
 'redirect' => '重定向(按文件、用户或版本ID)',
 'redirect-legend' => '重定向至文件或页面',
-'redirect-summary' => '本特殊页面会重定向到一个文件(给予文件名),一个页面(给予修订版本ID),或一个用户页面(给予用户数字ID)。',
+'redirect-summary' => '本特殊页面可以跳转至一个文件(提供文件名)、页面(提供版本ID)或用户页面(提供数字用户ID)。用法:[[{{#Special:Redirect}}/file/Example.jpg]]、[[{{#Special:Redirect}}/revision/328429]]或[[{{#Special:Redirect}}/user/101]]。',
 'redirect-submit' => '提交',
 'redirect-lookup' => '基于:',
 'redirect-value' => '值:',
 'redirect-user' => '用户ID',
-'redirect-revision' => '页面修订',
+'redirect-revision' => '页面版本ID',
 'redirect-file' => '文件名',
 'redirect-not-exists' => '没找到相应值',
 
@@ -4141,6 +4140,6 @@ MediaWiki发表时预期有用,但对此'''无任何保证''',亦无隐含
 'limitreport-templateargumentsize' => '模板参数大小',
 'limitreport-templateargumentsize-value' => '$1/$2 字节',
 'limitreport-expansiondepth' => '最高扩展深度',
-'limitreport-expensivefunctioncount' => '昂贵的函数分析技术器',
+'limitreport-expensivefunctioncount' => '高级函数分析器',
 
 );
index 3bc7673..4da841d 100644 (file)
@@ -483,7 +483,7 @@ $messages = array(
 'articlepage' => '檢視內容頁面',
 'talk' => '討論',
 'views' => '檢視',
-'toolbox' => '工具',
+'toolbox' => '工具',
 'userpage' => '檢視用戶頁面',
 'projectpage' => '檢視計劃頁面',
 'imagepage' => '檢視檔案頁面',
@@ -2405,10 +2405,12 @@ $UNWATCHURL
 'deletecomment' => '理由:',
 'deleteotherreason' => '其它/附加的理由:',
 'deletereasonotherlist' => '其它理由',
-'deletereason-dropdown' => '*常用刪除理由
-** 作者請求
+'deletereason-dropdown' => '* 常見刪除理由
+** 濫發電郵
+** 破壞
 ** 侵犯版權
-** 破壞',
+** 作者請求
+** 損壞重定向頁',
 'delete-edit-reasonlist' => '編輯刪除理由',
 'delete-toobig' => '這個頁面有一個十分大量的編輯歷史,超過$1次修訂。刪除此類頁面的動作已經被限制,以防止在{{SITENAME}}上的意外擾亂。',
 'delete-warning-toobig' => '這個頁面有一個十分大量的編輯歷史,超過$1次修訂。刪除它可能會擾亂{{SITENAME}}的資料庫操作;在繼續此動作前請小心。',
@@ -2719,11 +2721,8 @@ $1被封禁的理由是“$2”',
 'ipb_blocked_as_range' => '錯誤: 該IP $1 無直接查封,不可以解除封禁。但是它是在 $2 的查封範圍之內,該段範圍是可以解除封禁的。',
 'ip_range_invalid' => '無效的IP範圍。',
 'ip_range_toolarge' => '大於 /$1 的封鎖範圍是不容許的。',
-'blockme' => '查封我',
 'proxyblocker' => '代理封鎖器',
-'proxyblocker-disabled' => '這個功能已經停用。',
 'proxyblockreason' => '您的IP位址是一個開放的代理,它已經被封鎖。請聯繫您的網際網路服務提供商或技術支援者並告知告知他們該嚴重的安全問題。',
-'proxyblocksuccess' => '完成。',
 'sorbsreason' => '您的IP位址在{{SITENAME}}中被 DNSBL列為屬於開放代理服務器。',
 'sorbs_create_account_reason' => '由於您的IP位址在{{SITENAME}}中被 DNSBL列為屬於開放代理服務器,所以您無法建立賬號。',
 'xffblockreason' => '您或您使用的代理伺服器X-Forwarded-For字段所包含的一個IP地址已被封禁。原始封禁理由:$1',
@@ -3040,6 +3039,7 @@ $2',
 'tooltip-undo' => '「復原」可以在編輯模式上開啟編輯表格以便恢復。它容許在摘要中加入原因。',
 'tooltip-preferences-save' => '儲存使用偏好',
 'tooltip-summary' => '輸入一個簡短的摘要',
+'tooltip-iwiki' => '$1 – $2',
 
 # Stylesheets
 'common.css' => '/* 此處的 CSS 將應用於所有的面板 */',
@@ -3085,6 +3085,8 @@ $2',
 'spam_reverting' => '恢復到不包含連結至$1的最近修訂版本',
 'spam_blanking' => '所有包含連結至$1的修訂,清空',
 'spam_deleting' => '所有包含連結至$1的修訂,刪除中',
+'simpleantispam-label' => "反垃圾檢查。
+'''不要'''加入這個!",
 
 # Info page
 'pageinfo-title' => '「$1」的信息',
@@ -3939,6 +3941,8 @@ MediaWiki是基於使用目的而加以發佈,然而不負任何擔保責任
 'tags-description-header' => '解釋完整描述',
 'tags-active-header' => '存檔?',
 'tags-hitcount-header' => '已加上標籤的更改',
+'tags-active-yes' => '是',
+'tags-active-no' => '否',
 'tags-edit' => '編輯',
 'tags-hitcount' => '$1次更改',
 
diff --git a/maintenance/archives/patch-rc_source.sql b/maintenance/archives/patch-rc_source.sql
new file mode 100644 (file)
index 0000000..7dedd74
--- /dev/null
@@ -0,0 +1,16 @@
+-- first step of migrating recentchanges rc_type to rc_source
+ALTER TABLE /*$wgDBprefix*/recentchanges
+  ADD rc_source varbinary(16) NOT NULL default '';
+
+-- Populate rc_source field with the data from rc_type
+-- Large wiki's might prefer the PopulateRecentChangeSource maintenance
+-- script to batch updates into groups rather than all at once.
+UPDATE /*$wgDBprefix*/recentchanges
+  SET rc_source = CASE
+    WHEN rc_type = 0 THEN 'mw.edit'
+    WHEN rc_type = 1 THEN 'mw.new'
+    WHEN rc_type = 3 THEN 'mw.log'
+    WHEN rc_type = 5 THEN 'mw.external'
+    ELSE ''
+  END
+WHERE rc_source = '';
index c2ba555..ba7c3cf 100644 (file)
@@ -81,10 +81,9 @@ class UploadStashCleanup extends Maintenance {
                                try {
                                        $stash->getFile( $key, true );
                                        $stash->removeFileNoAuth( $key );
-                               } catch ( UploadStashBadPathException $ex ) {
-                                       $this->output( "Failed removing stashed upload with key: $key\n" );
-                               } catch ( UploadStashZeroLengthFileException $ex ) {
-                                       $this->output( "Failed removing stashed upload with key: $key\n" );
+                               } catch ( UploadStashException $ex ) {
+                                       $type = get_class( $ex );
+                                       $this->output( "Failed removing stashed upload with key: $key ($type)\n" );
                                }
                                if ( $i % 100 == 0 ) {
                                        $this->output( "$i\n" );
index abedc61..e03763f 100644 (file)
@@ -70,7 +70,7 @@ while ( ( $line = Maintenance::readconsole() ) !== false ) {
                readline_write_history( $historyFile );
        }
        $val = eval( $line . ";" );
-       if ( wfIsHipHop() || is_null( $val ) ) {
+       if ( wfIsHHVM() || is_null( $val ) ) {
                echo "\n";
        } elseif ( is_string( $val ) || is_numeric( $val ) ) {
                echo "$val\n";
index 6b7f38a..548bb2f 100644 (file)
@@ -118,7 +118,6 @@ Wiki configuration for testing:
   // Enable weird and wonderful options:
                                                          // Increase default error reporting level.
   error_reporting (E_ALL);    // At a later date could be increased to E_ALL | E_STRICT
-  $wgBlockOpenProxies = true; // Some block pages require this to be true in order to test.
   $wgEnableUploads = true;    // enable uploads.
   $wgDBerrorLog = "/root/mediawiki-db-error-log.txt";  // log DB errors, replace with suitable path.
   $wgShowSQLErrors = true;    // Show SQL errors (instead of saying the query was hidden).
@@ -1483,24 +1482,6 @@ class watchlistTest extends pageTest {
        }
 }
 
-
-/**
- ** a page test for "Special:Blockme"
- */
-class specialBlockmeTest extends pageTest {
-       function __construct() {
-               $this->pagePath = "index.php?title=Special:Blockme";
-
-               $this->params = array ();
-
-               // sometimes we specify "ip", and sometimes we don't.
-               if ( wikiFuzz::randnum( 1 ) == 0 ) {
-                       $this->params["ip"] = wikiFuzz::chooseInput( array( "10.12.41.213", wikiFuzz::randnum( 8134, -10 ), wikiFuzz::makeFuzz( 2 ) ) );
-               }
-       }
-}
-
-
 /**
  ** a page test for "Special:Movepage"
  */
@@ -2161,7 +2142,7 @@ class GeSHi_Test extends pageTest {
 /**
  ** selects a page test to run.
  * @param $count
- * @return \api|\confirmEmail|\contributionsTest|\editPageTest|\imagelistTest|\imagepageTest|\ipblocklistTest|\listusersTest|\mimeSearchTest|\newImagesTest|\pageDeletion|\pageHistoryTest|\pageProtectionForm|\prefixindexTest|\profileInfo|\recentchangesTest|\redirectTest|\searchTest|\specialAllmessagesTest|\specialAllpagesTest|\specialBlockip|\specialBlockmeTest|\specialBooksourcesTest|\specialCategoryTree|\specialChemicalsourcesTest|\specialCitePageTest|\specialExportTest|\specialFilepathPageTest|\specialImportPageTest|\specialLinksearch|\specialLockdbPageTest|\specialLogTest|\specialMovePage|\specialNewpagesPageTest|\specialRenameuserPageTest|\specialRevisionDeletePageTest|\specialUndeletePageTest|\specialUnlockdbPageTest|\specialUserrights|\successfulUserLoginTest|\thumbTest|\userLoginTest|\viewPageTest|\watchlistTest
+ * @return \api|\confirmEmail|\contributionsTest|\editPageTest|\imagelistTest|\imagepageTest|\ipblocklistTest|\listusersTest|\mimeSearchTest|\newImagesTest|\pageDeletion|\pageHistoryTest|\pageProtectionForm|\prefixindexTest|\profileInfo|\recentchangesTest|\redirectTest|\searchTest|\specialAllmessagesTest|\specialAllpagesTest|\specialBlockip|\specialBooksourcesTest|\specialCategoryTree|\specialChemicalsourcesTest|\specialCitePageTest|\specialExportTest|\specialFilepathPageTest|\specialImportPageTest|\specialLinksearch|\specialLockdbPageTest|\specialLogTest|\specialMovePage|\specialNewpagesPageTest|\specialRenameuserPageTest|\specialRevisionDeletePageTest|\specialUndeletePageTest|\specialUnlockdbPageTest|\specialUserrights|\successfulUserLoginTest|\thumbTest|\userLoginTest|\viewPageTest|\watchlistTest
  */
 function selectPageTest( $count ) {
 
@@ -2197,7 +2178,6 @@ function selectPageTest( $count ) {
                case 20: return new redirectTest();
                case 21: return new confirmEmail();
                case 22: return new watchlistTest();
-               case 23: return new specialBlockmeTest();
                case 24: return new specialUndeletePageTest();
                case 25: return new specialMovePage();
                case 26: return new specialUnlockdbPageTest();
index e98e9c0..c595980 100644 (file)
@@ -9,6 +9,7 @@
                                        "mw.Map",
                                        "mw.Message",
                                        "mw.loader",
+                                       "mw.loader.store",
                                        "mw.log",
                                        "mw.html",
                                        "mw.html.Cdata",
@@ -21,6 +22,7 @@
                                "classes": [
                                        "mw.Title",
                                        "mw.inspect",
+                                       "mw.inspect.reports",
                                        "mw.notification",
                                        "mw.user",
                                        "mw.util",
index 8676d74..267666d 100644 (file)
@@ -266,7 +266,6 @@ $wgIgnoredMessages = array(
 
 /** Optional messages, which may be translated only if changed in the target language. */
 $wgOptionalMessages = array(
-       'linkprefix',
        'feed-atom',
        'feed-rss',
        'unit-pixel',
index ca3a4f5..1ac9500 100644 (file)
@@ -169,9 +169,6 @@ $wgMessageStructure = array(
                'broken-file-category',
                'categoryviewer-pagedlinks',
        ),
-       'mainpage' => array(
-               'linkprefix',
-       ),
        'miscellaneous1' => array(
                'about',
                'article',
@@ -2378,11 +2375,8 @@ $wgMessageStructure = array(
                'ipb_blocked_as_range',
                'ip_range_invalid',
                'ip_range_toolarge',
-               'blockme',
                'proxyblocker',
-               'proxyblocker-disabled',
                'proxyblockreason',
-               'proxyblocksuccess',
                'sorbs',
                'sorbsreason',
                'sorbs_create_account_reason',
@@ -2779,6 +2773,7 @@ $wgMessageStructure = array(
                'spam_reverting',
                'spam_blanking',
                'spam_deleting',
+               'simpleantispam-label',
        ),
        'info' => array(
                'pageinfo-header',
index bc10bc2..622712e 100755 (executable)
@@ -6,7 +6,7 @@ then
        JSDUCK_MWVERSION="$2"
 elif [[ "$*" != "" ]]
 then
-       echo "Usage $0: [--version <mediawiki version>]"
+       echo "Usage: $0 [--version <mediawiki version>]"
        echo
        exit 1
 fi
index acfabc3..607d7b9 100644 (file)
@@ -2,7 +2,7 @@
 define mw_prefix='{$wgDBprefix}';
 
 
-CREATE SEQUENCE user_user_id_seq MINVALUE 0 START WITH 0;
+CREATE SEQUENCE user_user_id_seq;
 CREATE TABLE &mw_prefix.mwuser ( -- replace reserved word 'user'
   user_id                   NUMBER  NOT NULL,
   user_name                 VARCHAR2(255)     NOT NULL,
@@ -27,7 +27,7 @@ CREATE INDEX &mw_prefix.mwuser_i02 ON &mw_prefix.mwuser (user_email, user_name);
 
 -- Create a dummy user to satisfy fk contraints especially with revisions
 INSERT INTO &mw_prefix.mwuser
-  VALUES (user_user_id_seq.nextval,'Anonymous',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL, '', current_timestamp, current_timestamp, 0);
+  VALUES (0,'Anonymous',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL, '', current_timestamp, current_timestamp, 0);
 
 CREATE TABLE &mw_prefix.user_groups (
   ug_user   NUMBER      DEFAULT 0 NOT NULL,
diff --git a/maintenance/populateRecentChangesSource.php b/maintenance/populateRecentChangesSource.php
new file mode 100644 (file)
index 0000000..0e8e501
--- /dev/null
@@ -0,0 +1,105 @@
+<?php
+/**
+ * Upgrade script to populate the rc_source field
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup Maintenance
+ */
+
+require_once dirname( __FILE__ ) . '/Maintenance.php';
+
+/**
+ * Maintenance script to populate the rc_source field.
+ *
+ * @ingroup Maintenance
+ * @since 1.22
+ */
+class PopulateRecentChangesSource extends LoggedUpdateMaintenance {
+       public function __construct() {
+               parent::__construct();
+               $this->mDescription = "Populates rc_source field of the recentchanges table with the data in rc_type.";
+               $this->setBatchSize( 100 );
+       }
+
+       protected function doDBUpdates() {
+               $dbw = $this->getDB( DB_MASTER );
+               if ( !$dbw->fieldExists( 'recentchanges', 'rc_source' ) ) {
+                       $this->error( 'rc_source field in recentchanges table does not exist.' );
+               }
+
+               $start = $dbw->selectField( 'recentchanges', 'MIN(rc_id)', false, __METHOD__ );
+               if ( !$start ) {
+                       $this->output( "Nothing to do.\n" );
+                       return true;
+               }
+               $end = $dbw->selectField( 'recentchanges', 'MAX(rc_id)', false, __METHOD__ );
+               $end += $this->mBatchSize - 1;
+               $blockStart = $start;
+               $blockEnd = $start + $this->mBatchSize - 1;
+
+               $updatedValues = $this->buildUpdateCondition( $dbw );
+
+               while ( $blockEnd <= $end ) {
+                       $cond = "rc_id BETWEEN $blockStart AND $blockEnd";
+
+                       $dbw->update(
+                               'recentchanges',
+                               array( $updatedValues ),
+                               array(
+                                       "rc_source = ''",
+                                       "rc_id BETWEEN $blockStart AND $blockEnd"
+                               ),
+                               __METHOD__
+                       );
+
+                       $this->output( "." );
+                       wfWaitForSlaves();
+
+                       $blockStart += $this->mBatchSize;
+                       $blockEnd += $this->mBatchSize;
+               }
+
+               $this->output( "\nDone.\n" );
+       }
+
+       protected function getUpdateKey() {
+               return __CLASS__;
+       }
+
+       protected function buildUpdateCondition( DatabaseBase $dbw ) {
+               $rcNew = $dbw->addQuotes( RC_NEW );
+               $rcSrcNew = $dbw->addQuotes( RecentChange::SRC_NEW );
+               $rcEdit = $dbw->addQuotes( RC_EDIT );
+               $rcSrcEdit = $dbw->addQuotes( RecentChange::SRC_EDIT );
+               $rcLog = $dbw->addQuotes( RC_LOG );
+               $rcSrcLog = $dbw->addQuotes( RecentChange::SRC_LOG );
+               $rcExternal = $dbw->addQuotes( RC_EXTERNAL );
+               $rcSrcExternal = $dbw->addQuotes( RecentChange::SRC_EXTERNAL );
+
+               return "rc_source = CASE
+                                       WHEN rc_type = $rcNew THEN $rcSrcNew
+                                       WHEN rc_type = $rcEdit THEN $rcSrcEdit
+                                       WHEN rc_type = $rcLog THEN $rcSrcLog
+                                       WHEN rc_type = $rcExternal THEN $rcSrcExternal
+                                       ELSE ''
+                               END";
+       }
+}
+
+$maintClass = "PopulateRecentChangesSource";
+require_once RUN_MAINTENANCE_IF_MAIN;
index 3c69125..042790f 100644 (file)
@@ -1,6 +1,7 @@
 <?php
 /**
- * Populates the rev_len field for old revisions created before MW 1.10.
+ * Populates the rev_len and ar_len fields for old revisions created
+ * before MW 1.10.
  *
  * 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
 require_once __DIR__ . '/Maintenance.php';
 
 /**
- * Maintenance script that populates the rev_len field for old revisions
- * created before MW 1.10.
+ * Maintenance script that populates the rev_len and ar_len fields
+ * for old revisions created before MW 1.10.
  *
  * @ingroup Maintenance
  */
 class PopulateRevisionLength extends LoggedUpdateMaintenance {
        public function __construct() {
                parent::__construct();
-               $this->mDescription = "Populates the rev_len field";
+               $this->mDescription = "Populates the rev_len and ar_len fields";
                $this->setBatchSize( 200 );
        }
 
        protected function getUpdateKey() {
-               return 'populate rev_len';
-       }
-
-       protected function updateSkippedMessage() {
-               return 'rev_len column of revision table already populated.';
+               return 'populate rev_len and ar_len';
        }
 
        public function doDBUpdates() {
                $db = $this->getDB( DB_MASTER );
                if ( !$db->tableExists( 'revision' ) ) {
                        $this->error( "revision table does not exist", true );
+               } elseif ( !$db->tableExists( 'archive' ) ) {
+                       $this->error( "archive table does not exist", true );
                } elseif ( !$db->fieldExists( 'revision', 'rev_len', __METHOD__ ) ) {
                        $this->output( "rev_len column does not exist\n\n", true );
                        return false;
                }
 
                $this->output( "Populating rev_len column\n" );
+               $rev = $this->doLenUpdates( 'revision', 'rev_id', 'rev', Revision::selectFields() );
+
+               $this->output( "Populating ar_len column\n" );
+               $ar = $this->doLenUpdates( 'archive', 'ar_id', 'ar', Revision::selectArchiveFields() );
+
+               $this->output( "rev_len and ar_len population complete [$rev revision rows, $ar archive rows].\n" );
+               return true;
+       }
 
-               $start = $db->selectField( 'revision', 'MIN(rev_id)', false, __METHOD__ );
-               $end = $db->selectField( 'revision', 'MAX(rev_id)', false, __METHOD__ );
+       /**
+        * @param string $table
+        * @param string $idCol
+        * @param string $prefix
+        * @param array $fields
+        * @return int
+        */
+       protected function doLenUpdates( $table, $idCol, $prefix, $fields ) {
+               $db = $this->getDB( DB_MASTER );
+               $start = $db->selectField( $table, "MIN($idCol)", false, __METHOD__ );
+               $end = $db->selectField( $table, "MAX($idCol)", false, __METHOD__ );
                if ( !$start || !$end ) {
-                       $this->output( "...revision table seems to be empty.\n" );
-                       return true;
+                       $this->output( "...$table table seems to be empty.\n" );
+                       return 0;
                }
 
                # Do remaining chunks
                $blockStart = intval( $start );
                $blockEnd = intval( $start ) + $this->mBatchSize - 1;
                $count = 0;
-               $missing = 0;
-               $fields = Revision::selectFields();
+
                while ( $blockStart <= $end ) {
-                       $this->output( "...doing rev_id from $blockStart to $blockEnd\n" );
+                       $this->output( "...doing $idCol from $blockStart to $blockEnd\n" );
                        $res = $db->select(
-                               'revision',
+                               $table,
                                $fields,
                                array(
-                                       "rev_id >= $blockStart",
-                                       "rev_id <= $blockEnd",
-                                       "rev_len IS NULL"
+                                       "$idCol >= $blockStart",
+                                       "$idCol <= $blockEnd",
+                                       "{$prefix}_len IS NULL"
                                ),
                                __METHOD__
                        );
+
+                       $db->begin( __METHOD__ );
                        # Go through and update rev_len from these rows.
                        foreach ( $res as $row ) {
-                               $rev = new Revision( $row );
-                               $content = $rev->getContent();
-                               if ( !$content ) {
-                                       # This should not happen, but sometimes does (bug 20757)
-                                       $this->output( "Content of revision {$row->rev_id} unavailable!\n" );
-                                       $missing++;
-                               }
-                               else {
-                                       # Update the row...
-                                       $db->update( 'revision',
-                                                        array( 'rev_len' => $content->getSize() ),
-                                                        array( 'rev_id' => $row->rev_id ),
-                                                        __METHOD__ );
+                               if ( $this->upgradeRow( $row, $table, $idCol, $prefix ) ) {
                                        $count++;
                                }
                        }
+                       $db->commit( __METHOD__ );
+
                        $blockStart += $this->mBatchSize;
                        $blockEnd += $this->mBatchSize;
                        wfWaitForSlaves();
                }
 
-               $this->output( "rev_len population complete ... {$count} rows changed ({$missing} missing)\n" );
+               return $count;
+       }
+
+       /**
+        * @param $row
+        * @param string $table
+        * @param string $idCol
+        * @param string $prefix
+        * @return bool
+        */
+       protected function upgradeRow( $row, $table, $idCol, $prefix ) {
+               $db = $this->getDB( DB_MASTER );
+
+               $rev = ( $table === 'archive' )
+                       ? Revision::newFromArchiveRow( $row )
+                       : new Revision( $row );
+
+               $content = $rev->getContent();
+               if ( !$content ) {
+                       # This should not happen, but sometimes does (bug 20757)
+                       $id = $row->$idCol;
+                       $this->output( "Content of $table $id unavailable!\n" );
+                       return false;
+               }
+
+               # Update the row...
+               $db->update( $table,
+                       array( "{$prefix}_len" => $content->getSize() ),
+                       array( $idCol => $row->$idCol ),
+                       __METHOD__
+               );
+
                return true;
        }
 }
index bc2428e..d0d1e92 100644 (file)
@@ -411,6 +411,7 @@ CREATE TABLE recentchanges (
   rc_this_oldid      INTEGER      NOT NULL,
   rc_last_oldid      INTEGER      NOT NULL,
   rc_type            SMALLINT     NOT NULL  DEFAULT 0,
+  rc_source          TEXT         NOT NULL,
   rc_patrolled       SMALLINT     NOT NULL  DEFAULT 0,
   rc_ip              CIDR,
   rc_old_len         INTEGER,
diff --git a/maintenance/proxyCheck.php b/maintenance/proxyCheck.php
deleted file mode 100644 (file)
index b52f20f..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-<?php
-/**
- * Command line script to check for an open proxy at a specified location.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- * @ingroup Maintenance
- */
-
-if ( PHP_SAPI != 'cli' ) {
-       die( 1 );
-}
-
-/**
- *
- */
-$output = '';
-
-/**
- * Exit if there are not enough parameters, or if it's not command line mode
- */
-if ( ( isset( $_REQUEST ) && array_key_exists( 'argv', $_REQUEST ) ) || count( $argv ) < 4 ) {
-       $output .= "Incorrect parameters\n";
-} else {
-       /**
-        * Get parameters
-        */
-       $ip = $argv[1];
-       $port = $argv[2];
-       $url = $argv[3];
-       $host = trim( `hostname` );
-       $output = "Connecting to $ip:$port, target $url, this hostname $host\n";
-
-       # Open socket
-       $sock = @fsockopen( $ip, $port, $errno, $errstr, 5 );
-       if ( $errno == 0 ) {
-               $output .= "Connected\n";
-               # Send payload
-               $request = "GET $url HTTP/1.0\r\n";
-#              $request .= "Proxy-Connection: Keep-Alive\r\n";
-#              $request .= "Pragma: no-cache\r\n";
-#              $request .= "Host: ".$url."\r\n";
-#              $request .= "User-Agent: MediaWiki open proxy check\r\n";
-               $request .= "\r\n";
-               @fputs( $sock, $request );
-               $response = fgets( $sock, 65536 );
-               $output .= $response;
-               @fclose( $sock );
-       } else {
-               $output .= "No connection\n";
-       }
-}
-
-$output = escapeshellarg( $output );
-
-#`echo $output >> /home/tstarling/open/proxy.log`;
index 1834825..315176c 100644 (file)
@@ -83,6 +83,7 @@ class RebuildRecentchanges extends Maintenance {
                                'rc_this_oldid' => 'rev_id',
                                'rc_last_oldid' => 0, // is this ok?
                                'rc_type'       => $dbw->conditional( 'page_is_new != 0', RC_NEW, RC_EDIT ),
+                               'rc_source'     => $dbw->conditional( 'page_is_new != 0', $dbw->addQuotes( RecentChange::SRC_NEW ), $dbw->addQuotes( RecentChange::SRC_EDIT ) ),
                                'rc_deleted'    => 'rev_deleted'
                        ), array(
                                'rev_timestamp > ' . $dbw->addQuotes( $dbw->timestamp( $cutoff ) ),
index de92ef5..af01a30 100644 (file)
@@ -1062,6 +1062,10 @@ CREATE TABLE /*_*/recentchanges (
   -- The type of change entry (RC_EDIT,RC_NEW,RC_LOG,RC_EXTERNAL)
   rc_type tinyint unsigned NOT NULL default 0,
 
+  -- The source of the change entry (replaces rc_type)
+  -- default of '' is temporary, needed for initial migration
+  rc_source varchar(16) binary not null default '',
+
   -- If the Recent Changes Patrol option is enabled,
   -- users may mark edits as having been reviewed to
   -- remove a warning flag on the RC list.
@@ -1109,8 +1113,9 @@ CREATE TABLE /*_*/watchlist (
   wl_namespace int NOT NULL default 0,
   wl_title varchar(255) binary NOT NULL default '',
 
-  -- Timestamp when user was last sent a notification e-mail;
-  -- cleared when the user visits the page.
+  -- Timestamp used to send notification e-mails and show "updated since last visit" markers on
+  -- history and recent changes / watchlist. Set to NULL when the user visits the latest revision
+  -- of the page, which means that they should be sent an e-mail on the next change.
   wl_notificationtimestamp varbinary(14)
 
 ) /*$wgDBTableOptions*/;
index 378217f..5c93964 100644 (file)
@@ -132,6 +132,8 @@ class UpdateMediaWiki extends Maintenance {
                        wfCountDown( 5 );
                }
 
+               $time1 = new MWTimestamp();
+
                $shared = $this->hasOption( 'doshared' );
 
                $updates = array( 'core', 'extensions' );
@@ -164,8 +166,10 @@ class UpdateMediaWiki extends Maintenance {
                if ( !$this->hasOption( 'nopurge' ) ) {
                        $updater->purgeCache();
                }
+               $time2 = new MWTimestamp();
 
                $this->output( "\nDone.\n" );
+               $this->output( "\nThe job took ". $time2->diff( $time1 )->format( "%i:%S" ). "\n" );
        }
 
        function afterFinalSetup() {
index c033647..463dec8 100644 (file)
@@ -87,7 +87,7 @@ return array(
                'localBasePath' => $GLOBALS['wgStyleDirectory'],
        ),
        'skins.vector' => array(
-               // Keep in sync with WebInstallerOutput::getCSS()
+               // Used in the web installer. Test it after modifying this definition!
                'styles' => array(
                        'common/commonElements.css' => array( 'media' => 'screen' ),
                        'common/commonContent.css' => array( 'media' => 'screen' ),
@@ -103,7 +103,7 @@ return array(
                        'common/commonElements.css' => array( 'media' => 'screen' ),
                        'common/commonContent.css' => array( 'media' => 'screen' ),
                        'common/commonInterface.css' => array( 'media' => 'screen' ),
-                       'vector/styles-beta.less' => array( 'media' => 'screen' ),
+                       'vector/styles-beta.less',
                ),
                'remoteBasePath' => $GLOBALS['wgStylePath'],
                'localBasePath' => $GLOBALS['wgStyleDirectory'],
@@ -655,7 +655,10 @@ return array(
        ),
        'mediawiki.inspect' => array(
                'scripts' => 'resources/mediawiki/mediawiki.inspect.js',
-               'dependencies' => 'jquery.byteLength',
+               'dependencies' => array(
+                       'jquery.byteLength',
+                       'jquery.json',
+               ),
                'targets' => array( 'desktop', 'mobile' ),
        ),
        'mediawiki.feedback' => array(
@@ -1008,6 +1011,9 @@ return array(
                'scripts' => 'resources/mediawiki.special/mediawiki.special.preferences.js',
                'styles' => 'resources/mediawiki.special/mediawiki.special.preferences.css',
                'position' => 'top',
+               'skinStyles' => array(
+                       'vector' => 'skins/vector/special.preferences.less',
+               ),
        ),
        'mediawiki.special.recentchanges' => array(
                'scripts' => 'resources/mediawiki.special/mediawiki.special.recentchanges.js',
@@ -1106,14 +1112,9 @@ return array(
                'localBasePath' => $GLOBALS['wgStyleDirectory'],
        ),
        'mediawiki.legacy.config' => array(
+               // Used in the web installer. Test it after modifying this definition!
                'scripts' => 'common/config.js',
-               'styles' => array( 'common/config.css', 'common/config-cc.css' ),
-               'remoteBasePath' => $GLOBALS['wgStylePath'],
-               'localBasePath' => $GLOBALS['wgStyleDirectory'],
-               'dependencies' => 'mediawiki.legacy.wikibits',
-       ),
-       'mediawiki.legacy.IEFixes' => array(
-               'scripts' => 'common/IEFixes.js',
+               'styles' => array( 'common/config.css' ),
                'remoteBasePath' => $GLOBALS['wgStylePath'],
                'localBasePath' => $GLOBALS['wgStyleDirectory'],
                'dependencies' => 'mediawiki.legacy.wikibits',
@@ -1129,6 +1130,7 @@ return array(
                'position' => 'top',
        ),
        'mediawiki.legacy.shared' => array(
+               // Used in the web installer. Test it after modifying this definition!
                'styles' => array( 'common/shared.css' => array( 'media' => 'screen' ) ),
                'remoteBasePath' => $GLOBALS['wgStylePath'],
                'localBasePath' => $GLOBALS['wgStyleDirectory'],
index bdc6675..b3d7bb3 100644 (file)
                        var name = mw.language.months.names[i].toLowerCase();
                        ts.monthNames[name] = i + 1;
                        regex.push( $.escapeRE( name ) );
-                       name = mw.language.months.genitive[i].toLowerCase().replace( '.', '' );
+                       name = mw.language.months.genitive[i].toLowerCase();
                        ts.monthNames[name] = i + 1;
                        regex.push( $.escapeRE( name ) );
-                       name = mw.language.months.abbrev[i].toLowerCase();
+                       name = mw.language.months.abbrev[i].toLowerCase().replace( '.', '' );
                        ts.monthNames[name] = i + 1;
                        regex.push( $.escapeRE( name ) );
                }
index f9a14b0..cdc6767 100644 (file)
                                                        tokenCache[tokenType] = params.token = undefined;
                                                        return api.post( params );
                                                }
+                                               // Pass the promise forward, so the caller gets error codes
+                                               return this;
                                        }
                                );
                        } else {
index 9ffe11c..768a9c6 100644 (file)
@@ -33,28 +33,9 @@ section.mw-form-header {
        font-size: 0.9em;
        margin: 0 0 1em 0;
        padding: 0.5em;
-       border: 1px solid;
        word-wrap: break-word;
 }
 
-.mw-ui-vform .errorbox {
-       color: #cc0000;
-       border-color: #fac5c5;
-       background-color: #fae3e3;
-}
-
-.mw-ui-vform .warningbox {
-       color: #705000;
-       border-color: #fde29b;
-       background-color: #fdf1d1;
-}
-
-.mw-ui-vform .successbox {
-       color: #009000;
-       border-color: #b7fdb5;
-       background-color: #e1fddf;
-}
-
 /*
  * Override the right margin of the form to give space in case a benefits
  * column appears to the side.
index b236019..4a64566 100644 (file)
 
                        // thumb.php-generated thumbnails
                        thumbPhpRegex = /thumb\.php/,
-
                        regexes = [
                                // Thumbnails
-                               /\/[a-f0-9]\/[a-f0-9]{2}\/([^\s\/]+)\/[0-9]+px-\1[^\s\/]*$/,
+                               /\/[a-f0-9]\/[a-f0-9]{2}\/([^\s\/]+)\/[^\s\/]+-\1[^\s\/]*$/,
 
                                // Thumbnails in non-hashed upload directories
-                               /\/([^\s\/]+)\/[0-9]+px-\1[^\s\/]*$/,
+                               /\/([^\s\/]+)\/[^\s\/]+-\1[^\s\/]*$/,
 
                                // Full size images
                                /\/[a-f0-9]\/[a-f0-9]{2}\/([^\s\/]+)$/,
index 5cecc16..d93254b 100644 (file)
@@ -4,14 +4,52 @@
  * @author Ori Livneh
  * @since 1.22
  */
+/*jshint devel:true */
 ( function ( mw, $ ) {
 
+       function sortByProperty( array, prop, descending ) {
+               var order = descending ? -1 : 1;
+               return array.sort( function ( a, b ) {
+                       return a[prop] > b[prop] ? order : a[prop] < b[prop] ? -order : 0;
+               } );
+       }
+
+       function humanSize( bytes ) {
+               if ( !$.isNumeric( bytes ) || bytes === 0 ) { return bytes; }
+               var i = 0, units = [ '', ' kB', ' MB', ' GB', ' TB', ' PB' ];
+               for ( ; bytes >= 1024; bytes /= 1024 ) { i++; }
+               return bytes.toFixed( 1 ) + units[i];
+       }
+
        /**
         * @class mw.inspect
         * @singleton
         */
        var inspect = {
 
+               /**
+                * Return a map of all dependency relationships between loaded modules.
+                *
+                * @return {Object} Maps module names to objects. Each sub-object has
+                *  two properties, 'requires' and 'requiredBy'.
+                */
+               getDependencyGraph: function () {
+                       var modules = inspect.getLoadedModules(), graph = {};
+
+                       $.each( modules, function ( moduleIndex, moduleName ) {
+                               var dependencies = mw.loader.moduleRegistry[moduleName].dependencies || [];
+
+                               graph[moduleName] = graph[moduleName] || { requiredBy: [] };
+                               graph[moduleName].requires = dependencies;
+
+                               $.each( dependencies, function ( depIndex, depName ) {
+                                       graph[depName] = graph[depName] || { requiredBy: [] };
+                                       graph[depName].requiredBy.push( moduleName );
+                               } );
+                       } );
+                       return graph;
+               },
+
                /**
                 * Calculate the byte size of a ResourceLoader module.
                 *
                        return payload;
                },
 
+               /**
+                * Given CSS source, count both the total number of selectors it
+                * contains and the number which match some element in the current
+                * document.
+                *
+                * @param {string} css CSS source
+                * @return Selector counts
+                * @return {number} return.selectors Total number of selectors
+                * @return {number} return.matched Number of matched selectors
+                */
+               auditSelectors: function ( css ) {
+                       var selectors = { total: 0, matched: 0 },
+                               style = document.createElement( 'style' ),
+                               sheet, rules;
+
+                       style.textContent = css;
+                       document.body.appendChild( style );
+                       // Standards-compliant browsers use .sheet.cssRules, IE8 uses .styleSheet.rules…
+                       sheet = style.sheet || style.styleSheet;
+                       rules = sheet.cssRules || sheet.rules;
+                       $.each( rules, function ( index, rule ) {
+                               selectors.total++;
+                               if ( document.querySelector( rule.selectorText ) !== null ) {
+                                       selectors.matched++;
+                               }
+                       } );
+                       document.body.removeChild( style );
+                       return selectors;
+               },
+
                /**
                 * Get a list of all loaded ResourceLoader modules.
                 *
                },
 
                /**
-                * Print a breakdown of all loaded modules and their size in kilobytes
-                * to the debug console. Modules are ordered from largest to smallest.
+                * Print tabular data to the console, using console.table, console.log,
+                * or mw.log (in declining order of preference).
+                *
+                * @param {Array} data Tabular data represented as an array of objects
+                *  with common properties.
+                */
+               dumpTable: function ( data ) {
+                       try {
+                               // Bartosz made me put this here.
+                               if ( window.opera ) { throw window.opera; }
+                               // Use Function.prototype#call to force an exception on Firefox,
+                               // which doesn't define console#table but doesn't complain if you
+                               // try to invoke it.
+                               console.table.call( console, data );
+                               return;
+                       } catch (e) {}
+                       try {
+                               console.log( $.toJSON( data, null, 2 ) );
+                               return;
+                       } catch (e) {}
+                       mw.log( data );
+               },
+
+               /**
+                * Generate and print one more reports. When invoked with no arguments,
+                * print all reports.
+                *
+                * @param {string...} [reports] Report names to run, or unset to print
+                *  all available reports.
                 */
-               inspectModules: function () {
-                       var console = window.console;
+               runReports: function () {
+                       var reports = arguments.length > 0 ?
+                               Array.prototype.slice.call( arguments ) :
+                               $.map( inspect.reports, function ( v, k ) { return k; } );
 
-                       $( function () {
+                       $.each( reports, function ( index, name ) {
+                               inspect.dumpTable( inspect.reports[name]() );
+                       } );
+               },
+
+               /**
+                * @class mw.inspect.reports
+                * @singleton
+                */
+               reports: {
+                       /**
+                        * Generate a breakdown of all loaded modules and their size in
+                        * kilobytes. Modules are ordered from largest to smallest.
+                        */
+                       size: function () {
                                // Map each module to a descriptor object.
                                var modules = $.map( inspect.getLoadedModules(), function ( module ) {
                                        return {
                                } );
 
                                // Sort module descriptors by size, largest first.
-                               modules.sort( function ( a, b ) {
-                                       return b.size - a.size;
-                               } );
+                               sortByProperty( modules, 'size', true );
 
                                // Convert size to human-readable string.
                                $.each( modules, function ( i, module ) {
-                                       module.size = module.size > 1024 ?
-                                               ( module.size / 1024 ).toFixed( 2 ) + ' KB' :
-                                               ( module.size !== null ? module.size + ' B' : null );
+                                       module.size = humanSize( module.size );
                                } );
 
-                               if ( console ) {
-                                       if ( console.table ) {
-                                               console.table( modules );
-                                       } else {
-                                               $.each( modules, function ( i, module ) {
-                                                       console.log( [ module.name, module.size ].join( '\t' ) );
-                                               } );
-                                       }
+                               return modules;
+                       },
+
+                       /**
+                        * For each module with styles, count the number of selectors, and
+                        * count how many match against some element currently in the DOM.
+                        */
+                       css: function () {
+                               var modules = [];
+
+                               $.each( inspect.getLoadedModules(), function ( index, name ) {
+                                       var css, stats, module = mw.loader.moduleRegistry[name];
+
+                                       try {
+                                               css = module.style.css.join();
+                                       } catch (e) { return; } // skip
+
+                                       stats = inspect.auditSelectors( css );
+                                       modules.push( {
+                                               module: name,
+                                               allSelectors: stats.total,
+                                               matchedSelectors: stats.matched,
+                                               percentMatched: stats.total !== 0 ?
+                                                       ( stats.matched / stats.total * 100 ).toFixed( 2 )  + '%' : null
+                                       } );
+                               } );
+                               sortByProperty( modules, 'allSelectors', true );
+                               return modules;
+                       },
+
+                       /**
+                        * Report stats on mw.loader.store: the number of localStorage
+                        * cache hits and misses, the number of items purged from the
+                        * cache, and the total size of the module blob in localStorage.
+                        */
+                       store: function () {
+                               var raw, stats = { enabled: mw.loader.store.enabled };
+                               if ( stats.enabled ) {
+                                       $.extend( stats, mw.loader.store.stats );
+                                       try {
+                                               raw = localStorage.getItem( mw.loader.store.getStoreKey() );
+                                               stats.totalSize = humanSize( $.byteLength( raw ) );
+                                       } catch (e) {}
                                }
-                       } );
+                               return [stats];
+                       }
                }
        };
 
        if ( mw.config.get( 'debug' ) ) {
-               inspect.getModuleSize = function () { return null; };
-               mw.log( 'mw.inspect: Module sizes are not available in debug mode.' );
+               mw.log( 'mw.inspect: reports are not available in debug mode.' );
        }
 
        mw.inspect = inspect;
index 4138ac8..9267a49 100644 (file)
@@ -1,5 +1,9 @@
-/*
- * Core MediaWiki JavaScript Library
+/**
+ * Base library for MediaWiki.
+ *
+ * @class mw
+ * @alternateClassName mediaWiki
+ * @singleton
  */
 
 var mw = ( function ( $, undefined ) {
@@ -10,6 +14,31 @@ var mw = ( function ( $, undefined ) {
        var hasOwn = Object.prototype.hasOwnProperty,
                slice = Array.prototype.slice;
 
+       /**
+        * Log a message to window.console, if possible. Useful to force logging of some
+        * errors that are otherwise hard to detect (I.e., this logs also in production mode).
+        * Gets console references in each invocation, so that delayed debugging tools work
+        * fine. No need for optimization here, which would only result in losing logs.
+        *
+        * @private
+        * @method log_
+        * @param {string} msg text for the log entry.
+        * @param {Error} [e]
+        */
+       function log( msg, e ) {
+               var console = window.console;
+               if ( console && console.log ) {
+                       console.log( msg );
+                       // If we have an exception object, log it through .error() to trigger
+                       // proper stacktraces in browsers that support it. There are no (known)
+                       // browsers that don't support .error(), that do support .log() and
+                       // have useful exception handling through .log().
+                       if ( e && console.error ) {
+                               console.error( String( e ), e );
+                       }
+               }
+       }
+
        /* Object constructors */
 
        /**
@@ -197,7 +226,7 @@ var mw = ( function ( $, undefined ) {
                },
 
                /**
-                * Converts message object to it's string form based on the state of format.
+                * Converts message object to its string form based on the state of format.
                 *
                 * @return {string} Message as a string in the current form or `<key>` if key does not exist.
                 */
@@ -291,11 +320,7 @@ var mw = ( function ( $, undefined ) {
        };
 
        /**
-        * Base library for MediaWiki.
-        *
         * @class mw
-        * @alternateClassName mediaWiki
-        * @singleton
         */
        return {
                /* Public Members */
@@ -592,7 +617,7 @@ var mw = ( function ( $, undefined ) {
                                                        try {
                                                                styleEl.styleSheet.cssText += cssText; // IE
                                                        } catch ( e ) {
-                                                               log( 'addEmbeddedCSS fail\ne.message: ' + e.message, e );
+                                                               log( 'addEmbeddedCSS fail', e );
                                                        }
                                                } else {
                                                        styleEl.appendChild( document.createTextNode( String( cssText ) ) );
@@ -770,30 +795,6 @@ var mw = ( function ( $, undefined ) {
                                return filter( 'ready', dependencies ).length === dependencies.length;
                        }
 
-                       /**
-                        * Log a message to window.console, if possible. Useful to force logging of some
-                        * errors that are otherwise hard to detect (I.e., this logs also in production mode).
-                        * Gets console references in each invocation, so that delayed debugging tools work
-                        * fine. No need for optimization here, which would only result in losing logs.
-                        *
-                        * @private
-                        * @param {string} msg text for the log entry.
-                        * @param {Error} [e]
-                        */
-                       function log( msg, e ) {
-                               var console = window.console;
-                               if ( console && console.log ) {
-                                       console.log( msg );
-                                       // If we have an exception object, log it through .error() to trigger
-                                       // proper stacktraces in browsers that support it. There are no (known)
-                                       // browsers that don't support .error(), that do support .log() and
-                                       // have useful exception handling through .log().
-                                       if ( e && console.error ) {
-                                               console.error( e );
-                                       }
-                               }
-                       }
-
                        /**
                         * A module has entered state 'ready', 'error', or 'missing'. Automatically update pending jobs
                         * and modules that depend upon this module. if the given module failed, propagate the 'error'
@@ -834,29 +835,26 @@ var mw = ( function ( $, undefined ) {
                                                j -= 1;
                                                try {
                                                        if ( hasErrors ) {
-                                                               throw new Error( 'Module ' + module + ' failed.');
+                                                               if ( $.isFunction( job.error ) ) {
+                                                                       job.error( new Error( 'Module ' + module + ' has failed dependencies' ), [module] );
+                                                               }
                                                        } else {
                                                                if ( $.isFunction( job.ready ) ) {
                                                                        job.ready();
                                                                }
                                                        }
                                                } catch ( e ) {
-                                                       if ( $.isFunction( job.error ) ) {
-                                                               try {
-                                                                       job.error( e, [module] );
-                                                               } catch ( ex ) {
-                                                                       // A user-defined operation raised an exception. Swallow to protect
-                                                                       // our state machine!
-                                                                       log( 'Exception thrown by job.error()', ex );
-                                                               }
-                                                       }
+                                                       // A user-defined callback raised an exception.
+                                                       // Swallow it to protect our state machine!
+                                                       log( 'Exception thrown by job.error', e );
                                                }
                                        }
                                }
 
                                if ( registry[module].state === 'ready' ) {
-                                       // The current module became 'ready'. Recursively execute all dependent modules that are loaded
-                                       // and now have all dependencies satisfied.
+                                       // The current module became 'ready'. Set it in the module store, and recursively execute all
+                                       // dependent modules that are loaded and now have all dependencies satisfied.
+                                       mw.loader.store.set( module, registry[module] );
                                        for ( m in registry ) {
                                                if ( registry[m].state === 'loaded' && allReady( registry[m].dependencies ) ) {
                                                        execute( m );
@@ -1009,7 +1007,7 @@ var mw = ( function ( $, undefined ) {
                                        } catch ( e ) {
                                                // This needs to NOT use mw.log because these errors are common in production mode
                                                // and not in debug mode, such as when a symbol that should be global isn't exported
-                                               log( 'Exception thrown by ' + module + ': ' + e.message, e );
+                                               log( 'Exception thrown by ' + module, e );
                                                registry[module].state = 'error';
                                                handlePending( module );
                                        }
@@ -1210,7 +1208,7 @@ var mw = ( function ( $, undefined ) {
                                addScript( sourceLoadScript + '?' + $.param( request ) + '&*', null, async );
                        }
 
-                       /* Public Methods */
+                       /* Public Members */
                        return {
                                /**
                                 * The module registry is exposed as an aid for debugging and inspecting page
@@ -1233,7 +1231,7 @@ var mw = ( function ( $, undefined ) {
                                 */
                                work: function () {
                                        var     reqBase, splits, maxQueryLength, q, b, bSource, bGroup, bSourceGroup,
-                                               source, group, g, i, modules, maxVersion, sourceLoadScript,
+                                               source, concatSource, group, g, i, modules, maxVersion, sourceLoadScript,
                                                currReqBase, currReqBaseLength, moduleMap, l,
                                                lastDotIndex, prefix, suffix, bytesAdded, async;
 
@@ -1259,6 +1257,21 @@ var mw = ( function ( $, undefined ) {
                                                        }
                                                }
                                        }
+
+                                       mw.loader.store.init();
+                                       if ( mw.loader.store.enabled ) {
+                                               concatSource = [];
+                                               batch = $.grep( batch, function ( module ) {
+                                                       var source = mw.loader.store.get( module );
+                                                       if ( source ) {
+                                                               concatSource.push( source );
+                                                               return false;
+                                                       }
+                                                       return true;
+                                               } );
+                                               $.globalEval( concatSource.join( ';' ) );
+                                       }
+
                                        // Early exit if there's nothing to load...
                                        if ( !batch.length ) {
                                                return;
@@ -1453,16 +1466,19 @@ var mw = ( function ( $, undefined ) {
                                 * @param {Function|Array} script Function with module code or Array of URLs to
                                 *  be used as the src attribute of a new `<script>` tag.
                                 * @param {Object} style Should follow one of the following patterns:
+                                *
                                 *     { "css": [css, ..] }
                                 *     { "url": { <media>: [url, ..] } }
+                                *
                                 * And for backwards compatibility (needs to be supported forever due to caching):
+                                *
                                 *     { <media>: css }
                                 *     { <media>: [url, ..] }
                                 *
                                 * The reason css strings are not concatenated anymore is bug 31676. We now check
                                 * whether it's safe to extend the stylesheet (see #canExpandStylesheetWith).
                                 *
-                                * @param {Object} msgs List of key/value pairs to be added to {@link mw#messages}.
+                                * @param {Object} msgs List of key/value pairs to be added to mw#messages.
                                 */
                                implement: function ( module, script, style, msgs ) {
                                        // Validate input
@@ -1702,15 +1718,249 @@ var mw = ( function ( $, undefined ) {
                                },
 
                                /**
-                                * @inheritdoc mw.inspect#inspectModules
+                                * @inheritdoc mw.inspect#runReports
                                 * @method
                                 */
                                inspect: function () {
+                                       var args = slice.call( arguments );
                                        mw.loader.using( 'mediawiki.inspect', function () {
-                                               mw.inspect.inspectModules();
+                                               mw.inspect.runReports.apply( mw.inspect, args );
                                        } );
-                               }
+                               },
+
+                               /**
+                                * On browsers that implement the localStorage API, the module store serves as a
+                                * smart complement to the browser cache. Unlike the browser cache, the module store
+                                * can slice a concatenated response from ResourceLoader into its constituent
+                                * modules and cache each of them separately, using each module's versioning scheme
+                                * to determine when the cache should be invalidated.
+                                *
+                                * @singleton
+                                * @class mw.loader.store
+                                */
+                               store: {
+                                       // Whether the store is in use on this page.
+                                       enabled: null,
+
+                                       // The contents of the store, mapping '[module name]@[version]' keys
+                                       // to module implementations.
+                                       items: {},
+
+                                       // Cache hit stats
+                                       stats: { hits: 0, misses: 0, expired: 0 },
+
+                                       /**
+                                        * Construct a JSON-serializable object representing the content of the store.
+                                        * @return {Object} Module store contents.
+                                        */
+                                       toJSON: function () {
+                                               return { items: mw.loader.store.items, vary: mw.loader.store.getVary() };
+                                       },
+
+                                       /**
+                                        * Get the localStorage key for the entire module store. The key references
+                                        * $wgDBname to prevent clashes between wikis which share a common host.
+                                        *
+                                        * @return {string} localStorage item key
+                                        */
+                                       getStoreKey: function () {
+                                               return 'MediaWikiModuleStore:' + mw.config.get( 'wgDBname' );
+                                       },
+
+                                       /**
+                                        * Get a string key on which to vary the module cache.
+                                        * @return {string} String of concatenated vary conditions.
+                                        */
+                                       getVary: function () {
+                                               return [
+                                                       mw.config.get( 'skin' ),
+                                                       mw.config.get( 'wgResourceLoaderStorageVersion' ),
+                                                       mw.config.get( 'wgUserLanguage' )
+                                               ].join(':');
+                                       },
+
+                                       /**
+                                        * Get a string key for a specific module. The key format is '[name]@[version]'.
+                                        *
+                                        * @param {string} module Module name
+                                        * @return {string|null} Module key or null if module does not exist
+                                        */
+                                       getModuleKey: function ( module ) {
+                                               return typeof registry[module] === 'object' ?
+                                                       ( module + '@' + registry[module].version ) : null;
+                                       },
+
+                                       /**
+                                        * Initialize the store by retrieving it from localStorage and (if successfully
+                                        * retrieved) decoding the stored JSON value to a plain object.
+                                        *
+                                        * The try / catch block is used for JSON & localStorage feature detection.
+                                        * See the in-line documentation for Modernizr's localStorage feature detection
+                                        * code for a full account of why we need a try / catch: <http://git.io/4NEwKg>.
+                                        */
+                                       init: function () {
+                                               var raw, data, optedIn;
+
+                                               if ( mw.loader.store.enabled !== null ) {
+                                                       // #init already ran.
+                                                       return;
+                                               }
 
+                                               // Temporarily allow users to opt-in during mw.loader.store test phase by
+                                               // manually setting a cookie (bug 56397).
+                                               optedIn = /ResourceLoaderStorageEnabled=1/.test( document.cookie );
+
+                                               if ( !( mw.config.get( 'wgResourceLoaderStorageEnabled' ) || optedIn ) || mw.config.get( 'debug' ) ) {
+                                                       // Disabled by configuration, or because debug mode is set.
+                                                       mw.loader.store.enabled = false;
+                                                       return;
+                                               }
+
+                                               try {
+                                                       raw = localStorage.getItem( mw.loader.store.getStoreKey() );
+                                                       // If we get here, localStorage is available; mark enabled.
+                                                       mw.loader.store.enabled = true;
+                                                       data = JSON.parse( raw );
+                                                       if ( data && typeof data.items === 'object' && data.vary === mw.loader.store.getVary() ) {
+                                                               mw.loader.store.items = data.items;
+                                                               return;
+                                                       }
+                                               } catch (e) {}
+
+                                               if ( raw === undefined ) {
+                                                       mw.loader.store.enabled = false;  // localStorage failed; disable store.
+                                               } else {
+                                                       mw.loader.store.update();
+                                               }
+                                       },
+
+                                       /**
+                                        * Retrieve a module from the store and update cache hit stats.
+                                        *
+                                        * @param {string} module Module name
+                                        * @return {string|boolean} Module implementation or false if unavailable
+                                        */
+                                       get: function ( module ) {
+                                               var key;
+
+                                               if ( mw.loader.store.enabled !== true ) {
+                                                       return false;
+                                               }
+
+                                               key = mw.loader.store.getModuleKey( module );
+                                               if ( key in mw.loader.store.items ) {
+                                                       mw.loader.store.stats.hits++;
+                                                       return mw.loader.store.items[key];
+                                               }
+                                               mw.loader.store.stats.misses++;
+                                               return false;
+                                       },
+
+                                       /**
+                                        * Stringify a module and queue it for storage.
+                                        *
+                                        * @param {string} module Module name
+                                        * @param {Object} descriptor The module's descriptor as set in the registry
+                                        */
+                                       set: function ( module, descriptor ) {
+                                               var args, key;
+
+                                               if ( mw.loader.store.enabled !== true ) {
+                                                       return false;
+                                               }
+
+                                               key = mw.loader.store.getModuleKey( module );
+
+                                               if ( key in mw.loader.store.items ) {
+                                                       // Already set; decline to store.
+                                                       return false;
+                                               }
+
+                                               if ( descriptor.state !== 'ready' ) {
+                                                       // Module failed to load; decline to store.
+                                                       return false;
+                                               }
+
+                                               if ( !descriptor.version || $.inArray( descriptor.group, [ 'private', 'user', 'site' ] ) !== -1 ) {
+                                                       // Unversioned, private, or site-/user-specific; decline to store.
+                                                       return false;
+                                               }
+
+                                               if ( $.inArray( undefined, [ descriptor.script, descriptor.style, descriptor.messages ] ) !== -1 ) {
+                                                       // Partial descriptor; decline to store.
+                                                       return false;
+                                               }
+
+                                               try {
+                                                       args = [
+                                                               JSON.stringify( module ),
+                                                               typeof descriptor.script === 'function' ?
+                                                                       String( descriptor.script ) : JSON.stringify( descriptor.script ),
+                                                               JSON.stringify( descriptor.style ),
+                                                               JSON.stringify( descriptor.messages )
+                                                       ];
+                                               } catch (e) {
+                                                       return;
+                                               }
+                                               mw.loader.store.items[key] = 'mw.loader.implement(' + args.join(',') + ');';
+                                               mw.loader.store.update();
+                                       },
+
+                                       /**
+                                        * Iterate through the module store, removing any item that does not correspond
+                                        * (in name and version) to an item in the module registry.
+                                        */
+                                       prune: function () {
+                                               var key, module;
+
+                                               if ( mw.loader.store.enabled !== true ) {
+                                                       return false;
+                                               }
+
+                                               for ( key in mw.loader.store.items ) {
+                                                       module = key.substring( 0, key.indexOf( '@' ) );
+                                                       if ( mw.loader.store.getModuleKey( module ) !== key ) {
+                                                               mw.loader.store.stats.expired++;
+                                                               delete mw.loader.store.items[key];
+                                                       }
+                                               }
+                                       },
+
+                                       /**
+                                        * Sync modules to localStorage.
+                                        *
+                                        * This function debounces localStorage updates. When called multiple times in
+                                        * quick succession, the calls are coalesced into a single update operation.
+                                        * This allows us to call #update without having to consider the module load
+                                        * queue; the call to localStorage.setItem will be naturally deferred until the
+                                        * page is quiescent.
+                                        *
+                                        * Because localStorage is shared by all pages with the same origin, if multiple
+                                        * pages are loaded with different module sets, the possibility exists that
+                                        * modules saved by one page will be clobbered by another. But the impact would
+                                        * be minor and the problem would be corrected by subsequent page views.
+                                        */
+                                       update: ( function () {
+                                               var timer;
+
+                                               function flush() {
+                                                       var data;
+                                                       if ( mw.loader.store.enabled !== true ) {
+                                                               return false;
+                                                       }
+                                                       mw.loader.store.prune();
+                                                       try {
+                                                               data = JSON.stringify( mw.loader.store );
+                                                               localStorage.setItem( mw.loader.store.getStoreKey(), data );
+                                                       } catch (e) {}
+                                               }
+
+                                               return function () {
+                                                       clearTimeout( timer );
+                                                       timer = setTimeout( flush, 2000 );
+                                               };
+                                       }() )
+                               }
                        };
                }() ),
 
index 3e375fb..78febd2 100644 (file)
@@ -4,7 +4,7 @@
  */
 ( function ( mw, $ ) {
        var user,
-               callbacks = {},
+               deferreds = {},
                // Extend the skeleton mw.user from mediawiki.js
                // This is kind of ugly but we're stuck with this for b/c reasons
                options = mw.user.options || new mw.Map(),
         *
         * @private
         * @param {string} info One of 'groups' or 'rights'
-        * @param {Function} callback
+        * @param {Function} [callback]
+        * @return {jQuery.Promise}
         */
        function getUserInfo( info, callback ) {
                var api;
-               if ( callbacks[info] ) {
-                       callbacks[info].add( callback );
-                       return;
+               if ( !deferreds[info] ) {
+
+                       deferreds.rights = $.Deferred();
+                       deferreds.groups = $.Deferred();
+
+                       api = new mw.Api();
+                       api.get( {
+                               action: 'query',
+                               meta: 'userinfo',
+                               uiprop: 'rights|groups'
+                       } ).always( function ( data ) {
+                               var rights, groups;
+                               if ( data.query && data.query.userinfo ) {
+                                       rights = data.query.userinfo.rights;
+                                       groups = data.query.userinfo.groups;
+                               }
+                               deferreds.rights.resolve( rights || [] );
+                               deferreds.groups.resolve( groups || [] );
+                       } );
+
                }
-               callbacks.rights = $.Callbacks('once memory');
-               callbacks.groups = $.Callbacks('once memory');
-               callbacks[info].add( callback );
-               api = new mw.Api();
-               api.get( {
-                       action: 'query',
-                       meta: 'userinfo',
-                       uiprop: 'rights|groups'
-               } ).always( function ( data ) {
-                       var rights, groups;
-                       if ( data.query && data.query.userinfo ) {
-                               rights = data.query.userinfo.rights;
-                               groups = data.query.userinfo.groups;
-                       }
-                       callbacks.rights.fire( rights || [] );
-                       callbacks.groups.fire( groups || [] );
-               } );
+
+               return deferreds[info].done( callback ).promise();
        }
 
        mw.user = user = {
                /**
                 * Get the current user's groups
                 *
-                * @param {Function} callback
+                * @param {Function} [callback]
+                * @return {jQuery.Promise}
                 */
                getGroups: function ( callback ) {
-                       getUserInfo( 'groups', callback );
+                       return getUserInfo( 'groups', callback );
                },
 
                /**
                 * Get the current user's rights
                 *
-                * @param {Function} callback
+                * @param {Function} [callback]
+                * @return {jQuery.Promise}
                 */
                getRights: function ( callback ) {
-                       getUserInfo( 'rights', callback );
+                       return getUserInfo( 'rights', callback );
                }
        };
 
index 9c3a8b3..4334a9f 100644 (file)
@@ -13,7 +13,7 @@
                 * (don't call before document ready)
                 */
                init: function () {
-                       var profile, $tocTitle, $tocToggleLink, hideTocCookie;
+                       var profile;
 
                        /* Set tooltipAccessKeyPrefix */
                        profile = $.client.profile();
                        } )();
 
                        // Table of contents toggle
-                       $tocTitle = $( '#toctitle' );
-                       $tocToggleLink = $( '#togglelink' );
-                       // Only add it if there is a TOC and there is no toggle added already
-                       if ( $( '#toc' ).length && $tocTitle.length && !$tocToggleLink.length ) {
-                               hideTocCookie = $.cookie( 'mw_hidetoc' );
+                       mw.hook( 'wikipage.content' ).add( function () {
+                               var $tocTitle, $tocToggleLink, hideTocCookie;
+                               $tocTitle = $( '#toctitle' );
+                               $tocToggleLink = $( '#togglelink' );
+                               // Only add it if there is a TOC and there is no toggle added already
+                               if ( $( '#toc' ).length && $tocTitle.length && !$tocToggleLink.length ) {
+                                       hideTocCookie = $.cookie( 'mw_hidetoc' );
                                        $tocToggleLink = $( '<a href="#" class="internal" id="togglelink"></a>' )
                                                .text( mw.msg( 'hidetoc' ) )
                                                .click( function ( e ) {
                                                        e.preventDefault();
                                                        util.toggleToc( $(this) );
                                                } );
-                               $tocTitle.append(
-                                       $tocToggleLink
-                                               .wrap( '<span class="toctoggle"></span>' )
-                                               .parent()
-                                                       .prepend( '&nbsp;[' )
-                                                       .append( ']&nbsp;' )
-                               );
-
-                               if ( hideTocCookie === '1' ) {
-                                       util.toggleToc( $tocToggleLink );
+                                       $tocTitle.append(
+                                               $tocToggleLink
+                                                       .wrap( '<span class="toctoggle"></span>' )
+                                                       .parent()
+                                                               .prepend( '&nbsp;[' )
+                                                               .append( ']&nbsp;' )
+                                       );
+
+                                       if ( hideTocCookie === '1' ) {
+                                               util.toggleToc( $tocToggleLink );
+                                       }
                                }
-                       }
+                       } );
                },
 
                /* Main body */
index 09f0910..e35fcd1 100644 (file)
@@ -1,9 +1,12 @@
-// IE fixes javascript
+// IE fixes javascript loaded by wikibits.js for IE <= 6.0
 ( function ( mw, $ ) {
 
 var doneIEAlphaFix, doneIETransform, expandedURLs, fixalpha, isMSIE55,
-       relativeforfloats, setrelative;
+       relativeforfloats, setrelative, hasClass;
 
+// Also returns true for IE6, 7, 8, 9 and 10. createPopup is removed in IE11.
+// Good thing this is only loaded for IE <= 6 by wikibits.
+// Might as well set it to true.
 isMSIE55 = window.isMSIE55 = ( window.showModalDialog && window.clipboardData && window.createPopup );
 doneIETransform = window.doneIETransform = undefined;
 doneIEAlphaFix = window.doneIEAlphaFix = undefined;
@@ -99,8 +102,8 @@ setrelative = window.setrelative = function ( nodes ) {
 };
 
 // Expand links for printing
-String.prototype.hasClass = function ( classWanted ) {
-       var i = 0, classArr = this.split(/\s/);
+hasClass = function ( classText, classWanted ) {
+       var i = 0, classArr = classText.split(/\s/);
        for ( i = 0; i < classArr.length; i++ ) {
                if ( classArr[i].toLowerCase() === classWanted.toLowerCase() ) {
                        return true;
@@ -121,7 +124,7 @@ window.onbeforeprint = function () {
                allLinks = contentEl.getElementsByTagName( 'a' );
 
                for ( i = 0; i < allLinks.length; i++ ) {
-                       if ( allLinks[i].className.hasClass( 'external' ) && !allLinks[i].className.hasClass( 'free' ) ) {
+                       if ( hasClass( allLinks[i].className, 'external' ) && !hasClass( allLinks[i].className, 'free' ) ) {
                                expandedLink = document.createElement( 'span' );
                                expandedText = document.createTextNode( ' (' + allLinks[i].href + ')' );
                                expandedLink.appendChild( expandedText );
index 76ec4af..742f839 100644 (file)
@@ -157,15 +157,12 @@ dd {
        margin-bottom: .1em;
 }
 
+/* IE 6 and 7 lack support for quotes aroud the <q> element ('::before' and '::after'
+   pseudoelements, 'quotes' property). Let's italicize it instead (using the star hack). */
 q {
-       font-family: Times, "Times New Roman", serif;
-       font-style: italic;
-}
-/* Disabled for now
-blockquote {
-       font-family: Times, "Times New Roman", serif;
-       font-style: italic;
-}*/
+       *font-style: italic;
+}
+
 pre, code, tt, kbd, samp, .mw-code {
        /*
         * Some browsers will render the monospace text too small, namely Firefox, Chrome and Safari.
index 7cc58e3..ac7265a 100644 (file)
@@ -535,52 +535,57 @@ table.collapsed tr.collapsable {
 }
 
 /* success and error messages */
+.error,
+.warning,
 .success {
-       color: green;
        font-size: larger;
 }
+.error {
+       color: #cc0000;
+}
 .warning {
-       color: #FFA500; /* orange */
-       font-size: larger;
+       color: #705000;
 }
-.error {
-       color: red;
-       font-size: larger;
+.success {
+       color: #009000;
 }
+
 .errorbox,
 .warningbox,
 .successbox {
-       font-size: larger;
-       border: 2px solid;
+       border: 1px solid;
        padding: .5em 1em;
-       margin-bottom: 2em;
-       color: #000;
+       margin-bottom: 1em;
        display: -moz-inline-block;
        display: inline-block;
        zoom: 1;
        *display: inline;
 }
-.errorbox {
-       border-color: red;
-       background-color: #fff2f2;
-}
-.warningbox {
-       border-color: #FF8C00; /* darkorange */
-       background-color: #FFFFC0;
-}
-.successbox {
-       border-color: green;
-       background-color: #dfd;
-}
 .errorbox h2,
 .warningbox h2,
 .successbox h2 {
        font-size: 1em;
+       color: inherit;
        font-weight: bold;
        display: inline;
        margin: 0 .5em 0 0;
        border: none;
 }
+.errorbox {
+       color: #cc0000;
+       border-color: #fac5c5;
+       background-color: #fae3e3;
+}
+.warningbox {
+       color: #705000;
+       border-color: #fde29b;
+       background-color: #fdf1d1;
+}
+.successbox {
+       color: #009000;
+       border-color: #b7fdb5;
+       background-color: #e1fddf;
+}
 
 /* general info/warning box for SP */
 .mw-infobox {
index 7dc4a95..65db555 100644 (file)
 /**
  * MediaWiki legacy wikibits
  */
-/*jshint quotmark:false, onevar:false */
 ( function ( mw, $ ) {
-       var isIE6, isGecko,
+       var msg,
+               win = window,
                ua = navigator.userAgent.toLowerCase(),
-               uaMsg = 'Use feature detection or module jquery.client instead.';
-
-/**
- * User-agent sniffing.
- * To be removed in MediaWiki 1.23.
- *
- * @deprecated since 1.17 Use jquery.client instead.
- */
-mw.log.deprecate( window, 'clientPC', ua, uaMsg );
-$.each([
-               'is_gecko',
-               'is_chrome_mac',
-               'is_chrome',
-               'webkit_version',
-               'is_safari_win',
-               'is_safari',
-               'webkit_match',
-               'is_ff2',
-               'ff2_bugs',
-               'is_ff2_win',
-               'is_ff2_x11',
-               'opera95_bugs',
-               'opera7_bugs',
-               'opera6_bugs',
-               'is_opera_95',
-               'is_opera_preseven',
-               'is_opera',
-               'ie6_bugs'
-       ],
-       function ( i, key ) {
-               mw.log.deprecate( window, key, false, uaMsg );
-       }
-);
-if ( /msie ([0-9]{1,}[\.0-9]{0,})/.exec( ua ) && parseFloat( RegExp.$1 ) <= 6.0 ) {
-       isIE6 = true;
-}
-isGecko = /gecko/.test( ua ) && !/khtml|spoofer|netscape\/7\.0/.test( ua );
-
-// add any onload functions in this hook (please don't hard-code any events in the xhtml source)
-window.doneOnloadHook = undefined;
-
-if ( !window.onloadFuncts ) {
-       window.onloadFuncts = [];
-}
-
-window.addOnloadHook = function( hookFunct ) {
-       // Allows add-on scripts to add onload functions
-       if( !window.doneOnloadHook ) {
-               window.onloadFuncts[window.onloadFuncts.length] = hookFunct;
-       } else {
-               hookFunct(); // bug in MSIE script loading
-       }
-};
-
-window.importScript = function( page ) {
-       var uri = mw.config.get( 'wgScript' ) + '?title=' +
-               mw.util.wikiUrlencode( page ) +
-               '&action=raw&ctype=text/javascript';
-       return window.importScriptURI( uri );
-};
-
-window.loadedScripts = {}; // included-scripts tracker
-window.importScriptURI = function( url ) {
-       if ( window.loadedScripts[url] ) {
-               return null;
-       }
-       window.loadedScripts[url] = true;
-       var s = document.createElement( 'script' );
-       s.setAttribute( 'src', url );
-       s.setAttribute( 'type', 'text/javascript' );
-       document.getElementsByTagName('head')[0].appendChild( s );
-       return s;
-};
-
-window.importStylesheet = function( page ) {
-       return window.importStylesheetURI( mw.config.get( 'wgScript' ) + '?action=raw&ctype=text/css&title=' + mw.util.wikiUrlencode( page ) );
-};
-
-window.importStylesheetURI = function( url, media ) {
-       var l = document.createElement( 'link' );
-       l.rel = 'stylesheet';
-       l.href = url;
-       if ( media ) {
-               l.media = media;
-       }
-       document.getElementsByTagName('head')[0].appendChild( l );
-       return l;
-};
-
-window.appendCSS = function( text ) {
-       var s = document.createElement( 'style' );
-       s.type = 'text/css';
-       s.rel = 'stylesheet';
-       if ( s.styleSheet ) {
-               s.styleSheet.cssText = text; // IE
-       } else {
-               s.appendChild( document.createTextNode( text + '' ) ); // Safari sometimes borks on null
-       }
-       document.getElementsByTagName('head')[0].appendChild( s );
-       return s;
-};
+               isIE6 = ( /msie ([0-9]{1,}[\.0-9]{0,})/.exec( ua ) && parseFloat( RegExp.$1 ) <= 6.0 ),
+               isGecko = /gecko/.test( ua ) && !/khtml|spoofer|netscape\/7\.0/.test( ua ),
+               onloadFuncts = [];
 
 if ( mw.config.get( 'wgBreakFrames' ) ) {
        // Note: In IE < 9 strict comparison to window is non-standard (the standard didn't exist yet)
        // it works only comparing to window.self or window.window (http://stackoverflow.com/q/4850978/319266)
-       if ( window.top !== window.self ) {
+       if ( win.top !== win.self ) {
                // Un-trap us from framesets
-               window.top.location = window.location;
+               win.top.location = win.location;
        }
 }
 
-window.changeText = function( el, newText ) {
-       // Safari work around
-       if ( el.innerText ) {
-               el.innerText = newText;
-       } else if ( el.firstChild && el.firstChild.nodeValue ) {
-               el.firstChild.nodeValue = newText;
-       }
-};
-
-window.killEvt = function( evt ) {
-       evt = evt || window.event || window.Event; // W3C, IE, Netscape
-       if ( typeof evt.preventDefault !== 'undefined' ) {
-               evt.preventDefault(); // Don't follow the link
-               evt.stopPropagation();
-       } else {
-               evt.cancelBubble = true; // IE
-       }
-       return false; // Don't follow the link (IE)
-};
-
-window.mwEditButtons = [];
-mw.log.deprecate( window, 'mwCustomEditButtons', [], 'Use mw.toolbar.addButton instead.' );
-
-window.escapeQuotes = function( text ) {
-       var re = new RegExp( "'", "g" );
-       text = text.replace( re, "\\'" );
-       re = new RegExp( "\\n", "g" );
-       text = text.replace( re, "\\n" );
-       return window.escapeQuotesHTML( text );
-};
-
-window.escapeQuotesHTML = function( text ) {
-       var re = new RegExp( '&', "g" );
-       text = text.replace( re, "&amp;" );
-       re = new RegExp( '"', "g" );
-       text = text.replace( re, "&quot;" );
-       re = new RegExp( '<', "g" );
-       text = text.replace( re, "&lt;" );
-       re = new RegExp( '>', "g" );
-       text = text.replace( re, "&gt;" );
-       return text;
-};
-
-/**
- * Accesskey prefix utilities.
- * To be removed in MediaWiki 1.23.
- *
- * @deprecated since 1.17 Use mediawiki.util instead.
- */
-mw.log.deprecate( window, 'tooltipAccessKeyPrefix', 'alt-', 'Use mediawiki.util instead.' );
-mw.log.deprecate( window, 'tooltipAccessKeyRegexp', /\[(alt-)?(.)\]$/, 'Use mediawiki.util instead.' );
-mw.log.deprecate( window, 'updateTooltipAccessKeys', mw.util.updateTooltipAccessKeys, 'Use mediawiki.util instead.' );
-
-/**
- * Add a link to one of the portlet menus on the page, including:
- *
- * p-cactions: Content actions (shown as tabs above the main content in Monobook)
- * p-personal: Personal tools (shown at the top right of the page in Monobook)
- * p-navigation: Navigation
- * p-tb: Toolbox
- *
- * This function exists for the convenience of custom JS authors.  All
- * but the first three parameters are optional, though providing at
- * least an id and a tooltip is recommended.
- *
- * By default the new link will be added to the end of the list.  To
- * add the link before a given existing item, pass the DOM node of
- * that item (easily obtained with document.getElementById()) as the
- * nextnode parameter; to add the link _after_ an existing item, pass
- * the node's nextSibling instead.
- *
- * @param portlet String id of the target portlet ("p-cactions", "p-personal", "p-navigation" or "p-tb")
- * @param href String link URL
- * @param text String link text (will be automatically lowercased by CSS for p-cactions in Monobook)
- * @param id String id of the new item, should be unique and preferably have the appropriate prefix ("ca-", "pt-", "n-" or "t-")
- * @param tooltip String text to show when hovering over the link, without accesskey suffix
- * @param accesskey String accesskey to activate this link (one character, try to avoid conflicts)
- * @param nextnode Node the DOM node before which the new item should be added, should be another item in the same list
- *
- * @return Node -- the DOM node of the new item (an LI element) or null
- */
-window.addPortletLink = function( portlet, href, text, id, tooltip, accesskey, nextnode ) {
-       var root = document.getElementById( portlet );
-       if ( !root ) {
-               return null;
-       }
-       var uls = root.getElementsByTagName( 'ul' );
-       var node;
-       if ( uls.length > 0 ) {
-               node = uls[0];
-       } else {
-               node = document.createElement( 'ul' );
-               var lastElementChild = null;
-               for ( var i = 0; i < root.childNodes.length; ++i ) { /* get root.lastElementChild */
-                       if ( root.childNodes[i].nodeType === 1 ) {
-                               lastElementChild = root.childNodes[i];
-                       }
-               }
-               if ( lastElementChild && lastElementChild.nodeName.match( /div/i ) ) {
-                       /* Insert into the menu divs */
-                       lastElementChild.appendChild( node );
-               } else {
-                       root.appendChild( node );
-               }
-       }
-       if ( !node ) {
-               return null;
-       }
-
-       // unhide portlet if it was hidden before
-       root.className = root.className.replace( /(^| )emptyPortlet( |$)/, "$2" );
-
-       var link = document.createElement( 'a' );
-       link.appendChild( document.createTextNode( text ) );
-       link.href = href;
-
-       // Wrap in a span - make it work with vector tabs and has no effect on any other portlets
-       var span = document.createElement( 'span' );
-       span.appendChild( link );
-
-       var item = document.createElement( 'li' );
-       item.appendChild( span );
-       if ( id ) {
-               item.id = id;
-       }
-
-       if ( accesskey ) {
-               link.setAttribute( 'accesskey', accesskey );
-               tooltip += ' [' + accesskey + ']';
-       }
-       if ( tooltip ) {
-               link.setAttribute( 'title', tooltip );
-       }
-       if ( accesskey && tooltip ) {
-               mw.util.updateTooltipAccessKeys( [link] );
-       }
-
-       if ( nextnode && nextnode.parentNode === node ) {
-               node.insertBefore( item, nextnode );
-       } else {
-               node.appendChild( item );  // IE compatibility (?)
-       }
-
-       return item;
-};
-
-window.getInnerText = function( el ) {
-       if ( typeof el === 'string' ) {
-               return el;
-       }
-       if ( typeof el === 'undefined' ) {
-               return el;
-       }
-       // Custom sort value through 'data-sort-value' attribute
-       // (no need to prepend hidden text to change sort value)
-       if ( el.nodeType && el.getAttribute( 'data-sort-value' ) !== null ) {
-               // Make sure it's a valid DOM element (.nodeType) and that the attribute is set (!null)
-               return el.getAttribute( 'data-sort-value' );
-       }
-       if ( el.textContent ) {
-               return el.textContent; // not needed but it is faster
-       }
-       if ( el.innerText ) {
-               return el.innerText; // IE doesn't have textContent
-       }
-       var str = '';
-
-       var cs = el.childNodes;
-       var l = cs.length;
-       for ( var i = 0; i < l; i++ ) {
-               switch ( cs[i].nodeType ) {
-                       case 1: // ELEMENT_NODE
-                               str += window.getInnerText( cs[i] );
-                               break;
-                       case 3: // TEXT_NODE
-                               str += cs[i].nodeValue;
-                               break;
-               }
-       }
-       return str;
-};
-
-/**
- * Toggle checkboxes with shift selection.
- * To be removed in MediaWiki 1.23.
- *
- * @deprecated since 1.17 Use jquery.checkboxShiftClick instead.
- */
-$.each({
-       checkboxes: [],
-       lastCheckbox: null,
-       setupCheckboxShiftClick: $.noop,
-       addCheckboxClickHandlers: $.noop,
-       checkboxClickHandler: $.noop
-}, function ( key, val ) {
-       mw.log.deprecate( window, key, val, 'Use jquery.checkboxShiftClick instead.' );
-} );
-
-/*
-       Written by Jonathan Snook, http://www.snook.ca/jonathan
-       Add-ons by Robert Nyman, http://www.robertnyman.com
-       Author says "The credit comment is all it takes, no license. Go crazy with it!:-)"
-       From http://www.robertnyman.com/2005/11/07/the-ultimate-getelementsbyclassname/
-*/
-window.getElementsByClassName = function( oElm, strTagName, oClassNames ) {
-       var arrReturnElements = [];
-       if ( typeof oElm.getElementsByClassName === 'function' ) {
-               /* Use a native implementation where possible FF3, Saf3.2, Opera 9.5 */
-               var arrNativeReturn = oElm.getElementsByClassName( oClassNames );
-               if ( strTagName === '*' ) {
-                       return arrNativeReturn;
-               }
-               for ( var h = 0; h < arrNativeReturn.length; h++ ) {
-                       if( arrNativeReturn[h].tagName.toLowerCase() === strTagName.toLowerCase() ) {
-                               arrReturnElements[arrReturnElements.length] = arrNativeReturn[h];
-                       }
-               }
-               return arrReturnElements;
-       }
-       var arrElements = ( strTagName === '*' && oElm.all ) ? oElm.all : oElm.getElementsByTagName( strTagName );
-       var arrRegExpClassNames = [];
-       if( typeof oClassNames === 'object' ) {
-               for( var i = 0; i < oClassNames.length; i++ ) {
-                       arrRegExpClassNames[arrRegExpClassNames.length] =
-                               new RegExp("(^|\\s)" + oClassNames[i].replace(/\-/g, "\\-") + "(\\s|$)");
-               }
-       } else {
-               arrRegExpClassNames[arrRegExpClassNames.length] =
-                       new RegExp("(^|\\s)" + oClassNames.replace(/\-/g, "\\-") + "(\\s|$)");
-       }
-       var oElement;
-       var bMatchesAll;
-       for( var j = 0; j < arrElements.length; j++ ) {
-               oElement = arrElements[j];
-               bMatchesAll = true;
-               for( var k = 0; k < arrRegExpClassNames.length; k++ ) {
-                       if( !arrRegExpClassNames[k].test( oElement.className ) ) {
-                               bMatchesAll = false;
-                               break;
-                       }
-               }
-               if( bMatchesAll ) {
-                       arrReturnElements[arrReturnElements.length] = oElement;
-               }
-       }
-       return ( arrReturnElements );
-};
-
-window.redirectToFragment = function( fragment ) {
+win.redirectToFragment = function ( fragment ) {
        var webKitVersion,
-               match = navigator.userAgent.match(/AppleWebKit\/(\d+)/);
+               match = navigator.userAgent.match( /AppleWebKit\/(\d+)/ );
        if ( match ) {
                webKitVersion = parseInt( match[1], 10 );
                if ( webKitVersion < 420 ) {
@@ -375,8 +29,8 @@ window.redirectToFragment = function( fragment ) {
                        return;
                }
        }
-       if ( !window.location.hash ) {
-               window.location.hash = fragment;
+       if ( !win.location.hash ) {
+               win.location.hash = fragment;
 
                // Mozilla needs to wait until after load, otherwise the window doesn't
                // scroll.  See <https://bugzilla.mozilla.org/show_bug.cgi?id=516293>.
@@ -386,8 +40,8 @@ window.redirectToFragment = function( fragment ) {
                // well.
                if ( isGecko ) {
                        $( function () {
-                               if ( window.location.hash === fragment ) {
-                                       window.location.hash = fragment;
+                               if ( win.location.hash === fragment ) {
+                                       win.location.hash = fragment;
                                }
                        } );
                }
@@ -395,107 +49,200 @@ window.redirectToFragment = function( fragment ) {
 };
 
 /**
- * Add a cute little box at the top of the screen to inform the user of
- * something, replacing any preexisting message.
+ * User-agent sniffing.
+ * To be removed in MediaWiki 1.23.
  *
- * @deprecated since 1.17 Use the 'mediawiki.notify' module instead.
- * @param {string|HTMLElement} message To be put inside the message box.
+ * @deprecated since 1.17 Use jquery.client instead
  */
-mw.log.deprecate( window, 'jsMsg', mw.util.jsMessage, 'Use mediawiki.notify instead.' );
+
+msg = 'Use feature detection or module jquery.client instead';
+
+mw.log.deprecate( win, 'clientPC', ua, msg );
+
+// Ignored dummy values
+mw.log.deprecate( win, 'is_gecko', false, msg );
+mw.log.deprecate( win, 'is_chrome_mac', false, msg );
+mw.log.deprecate( win, 'is_chrome', false, msg );
+mw.log.deprecate( win, 'webkit_version', false, msg );
+mw.log.deprecate( win, 'is_safari_win', false, msg );
+mw.log.deprecate( win, 'is_safari', false, msg );
+mw.log.deprecate( win, 'webkit_match', false, msg );
+mw.log.deprecate( win, 'is_ff2', false, msg );
+mw.log.deprecate( win, 'ff2_bugs', false, msg );
+mw.log.deprecate( win, 'is_ff2_win', false, msg );
+mw.log.deprecate( win, 'is_ff2_x11', false, msg );
+mw.log.deprecate( win, 'opera95_bugs', false, msg );
+mw.log.deprecate( win, 'opera7_bugs', false, msg );
+mw.log.deprecate( win, 'opera6_bugs', false, msg );
+mw.log.deprecate( win, 'is_opera_95', false, msg );
+mw.log.deprecate( win, 'is_opera_preseven', false, msg );
+mw.log.deprecate( win, 'is_opera', false, msg );
+mw.log.deprecate( win, 'ie6_bugs', false, msg );
 
 /**
- * Inject a cute little progress spinner after the specified element
+ * DOM utilities for handling of events, text nodes and selecting elements
+ *
+ * To be removed in MediaWiki 1.23.
  *
- * @param element Element to inject after
- * @param id Identifier string (for use with removeSpinner(), below)
+ * @deprecated since 1.17 Use jQuery instead
  */
-window.injectSpinner = function( element, id ) {
-       var spinner = document.createElement( 'img' );
-       spinner.id = 'mw-spinner-' + id;
-       spinner.src = mw.config.get( 'stylepath' ) + '/common/images/spinner.gif';
-       spinner.alt = spinner.title = '...';
-       if( element.nextSibling ) {
-               element.parentNode.insertBefore( spinner, element.nextSibling );
+msg = 'Use jQuery instead';
+
+// Ignored dummy values
+mw.log.deprecate( win, 'doneOnloadHook', undefined );
+mw.log.deprecate( win, 'onloadFuncts', [] );
+mw.log.deprecate( win, 'runOnloadHook', $.noop );
+mw.log.deprecate( win, 'changeText', $.noop );
+mw.log.deprecate( win, 'killEvt', $.noop );
+mw.log.deprecate( win, 'addHandler', $.noop );
+mw.log.deprecate( win, 'hookEvent', $.noop );
+mw.log.deprecate( win, 'addClickHandler', $.noop );
+mw.log.deprecate( win, 'removeHandler', $.noop );
+mw.log.deprecate( win, 'getElementsByClassName', function () { return []; } );
+mw.log.deprecate( win, 'getInnerText', function () { return ''; } );
+
+// Run a function after the window onload event is fired
+mw.log.deprecate( win, 'addOnloadHook', function ( hookFunct ) {
+       if ( onloadFuncts ) {
+               onloadFuncts.push(hookFunct);
        } else {
-               element.parentNode.appendChild( spinner );
+               // If func queue is gone the event has happened already,
+               // run immediately instead of queueing.
+               hookFunct();
        }
-};
+} );
 
-/**
- * Remove a progress spinner added with injectSpinner()
- *
- * @param id Identifier string
- */
-window.removeSpinner = function( id ) {
-       var spinner = document.getElementById( 'mw-spinner-' + id );
-       if( spinner ) {
-               spinner.parentNode.removeChild( spinner );
-       }
-};
+$( win ).on( 'load', function () {
+       var i, functs;
 
-window.runOnloadHook = function() {
-       // don't run anything below this for non-dom browsers
-       if ( window.doneOnloadHook || !( document.getElementById && document.getElementsByTagName ) ) {
+       // Don't run twice
+       if ( !onloadFuncts ) {
                return;
        }
 
-       // set this before running any hooks, since any errors below
-       // might cause the function to terminate prematurely
-       window.doneOnloadHook = true;
+       // Deference and clear onloadFuncts before running any
+       // hooks to make sure we don't miss any addOnloadHook
+       // calls.
+       functs = onloadFuncts.slice();
+       onloadFuncts = undefined;
 
-       // Run any added-on functions
-       for ( var i = 0; i < window.onloadFuncts.length; i++ ) {
-               window.onloadFuncts[i]();
+       // Execute the queued functions
+       for ( i = 0; i < functs.length; i++ ) {
+               functs[i]();
        }
-};
+} );
 
 /**
- * Add an event handler to an element
+ * Toggle checkboxes with shift selection
  *
- * @param element Element to add handler to
- * @param attach String Event to attach to
- * @param handler callable Event handler callback
+ * To be removed in MediaWiki 1.23.
+ *
+ * @deprecated since 1.17 Use jquery.checkboxShiftClick instead
  */
-window.addHandler = function( element, attach, handler ) {
-       if( element.addEventListener ) {
-               element.addEventListener( attach, handler, false );
-       } else if( element.attachEvent ) {
-               element.attachEvent( 'on' + attach, handler );
-       }
-};
+msg = 'Use jquery.checkboxShiftClick instead';
+mw.log.deprecate( win, 'checkboxes', [], msg );
+mw.log.deprecate( win, 'lastCheckbox', null, msg );
+mw.log.deprecate( win, 'setupCheckboxShiftClick', $.noop, msg );
+mw.log.deprecate( win, 'addCheckboxClickHandlers', $.noop, msg );
+mw.log.deprecate( win, 'checkboxClickHandler', $.noop, msg );
 
-window.hookEvent = function( hookName, hookFunct ) {
-       window.addHandler( window, hookName, hookFunct );
-};
+/**
+ * Add a button to the default editor toolbar
+ *
+ * To be removed in MediaWiki 1.23.
+ *
+ * @deprecated since 1.17 Use mw.toolbar instead
+ */
+mw.log.deprecate( win, 'mwEditButtons', [], 'Use mw.toolbar instead' );
+mw.log.deprecate( win, 'mwCustomEditButtons', [], 'Use mw.toolbar instead' );
 
 /**
- * Add a click event handler to an element
+ * Spinner creation, injection and removal
+ *
+ * To be removed in MediaWiki 1.23.
  *
- * @param element Element to add handler to
- * @param handler callable Event handler callback
+ * @deprecated since 1.18 Use jquery.spinner instead
  */
-window.addClickHandler = function( element, handler ) {
-       window.addHandler( element, 'click', handler );
-};
+mw.log.deprecate( win, 'injectSpinner', $.noop, 'Use jquery.spinner instead' );
+mw.log.deprecate( win, 'removeSpinner', $.noop, 'Use jquery.spinner instead' );
 
 /**
- * Removes an event handler from an element
+ * Escape utilities
+ *
+ * To be removed in MediaWiki 1.23.
  *
- * @param element Element to remove handler from
- * @param remove String Event to remove
- * @param handler callable Event handler callback to remove
+ * @deprecated since 1.18 Use mw.html instead
  */
-window.removeHandler = function( element, remove, handler ) {
-       if( window.removeEventListener ) {
-               element.removeEventListener( remove, handler, false );
-       } else if( window.detachEvent ) {
-               element.detachEvent( 'on' + remove, handler );
+mw.log.deprecate( win, 'escapeQuotes', $.noop,'Use mw.html instead' );
+mw.log.deprecate( win, 'escapeQuotesHTML', $.noop,'Use mw.html instead' );
+
+/**
+ * Display a message to the user
+ *
+ * To be removed in MediaWiki 1.23.
+ *
+ * @deprecated since 1.17 Use mediawiki.notify instead
+ * @param {string|HTMLElement} message To be put inside the message box
+ */
+mw.log.deprecate( win, 'jsMsg', mw.util.jsMessage, 'Use mediawiki.notify instead' );
+
+/**
+ * Misc. utilities
+ *
+ * To be removed in MediaWiki 1.23.
+ *
+ * @deprecated since 1.17 Use mediawiki.util instead
+ */
+msg = 'Use mediawiki.util instead';
+mw.log.deprecate( win, 'tooltipAccessKeyPrefix', 'alt-', msg );
+mw.log.deprecate( win, 'tooltipAccessKeyRegexp', /\[(alt-)?(.)\]$/, msg );
+mw.log.deprecate( win, 'updateTooltipAccessKeys', mw.util.updateTooltipAccessKeys, msg );
+mw.log.deprecate( win, 'addPortletLink', mw.util.addPortletLink, msg );
+mw.log.deprecate( win, 'appendCSS', mw.util.addCSS );
+
+/**
+ * Wikipage import methods
+ */
+
+// included-scripts tracker
+win.loadedScripts = {};
+
+win.importScript = function ( page ) {
+       var uri = mw.config.get( 'wgScript' ) + '?title=' +
+               mw.util.wikiUrlencode( page ) +
+               '&action=raw&ctype=text/javascript';
+       return win.importScriptURI( uri );
+};
+
+win.importScriptURI = function ( url ) {
+       if ( win.loadedScripts[url] ) {
+               return null;
        }
+       win.loadedScripts[url] = true;
+       var s = document.createElement( 'script' );
+       s.setAttribute( 'src', url );
+       s.setAttribute( 'type', 'text/javascript' );
+       document.getElementsByTagName( 'head' )[0].appendChild( s );
+       return s;
+};
+
+win.importStylesheet = function( page ) {
+       return win.importStylesheetURI( mw.config.get( 'wgScript' ) + '?action=raw&ctype=text/css&title=' + mw.util.wikiUrlencode( page ) );
+};
+
+win.importStylesheetURI = function( url, media ) {
+       var l = document.createElement( 'link' );
+       l.rel = 'stylesheet';
+       l.href = url;
+       if ( media ) {
+               l.media = media;
+       }
+       document.getElementsByTagName('head')[0].appendChild( l );
+       return l;
 };
-window.hookEvent( 'load', window.runOnloadHook );
 
 if ( isIE6 ) {
-       window.importScriptURI( mw.config.get( 'stylepath' ) + '/common/IEFixes.js' );
+       win.importScriptURI( mw.config.get( 'stylepath' ) + '/common/IEFixes.js' );
 }
 
 }( mediaWiki, jQuery ) );
index 3462795..08e662d 100644 (file)
@@ -1,10 +1,10 @@
 @html-font-size: 90%;
 
 @body-font-size: inherit;
-@body-font-color: inherit;
 
 // Page content
 @content-font-family: "Helvetica Neue", "Helvetica", "Nimbus Sans L", "Arial", "Liberation Sans", sans-serif;
+@content-font-color: #252525;
 @content-font-size: 0.9em;
 @content-line-height: inherit;
 @content-padding: 1em;
index 67313c9..45258e5 100644 (file)
@@ -7,17 +7,28 @@
 
        // Use the same function for all navigation headings - don't repeat
        function toggle( $element ) {
+               var isCollapsed = $element.parent().is( '.collapsed' );
+
                $.cookie(
                        'vector-nav-' + $element.parent().attr( 'id' ),
-                       $element.parent().is( '.collapsed' ),
+                       isCollapsed,
                        { 'expires': 30, 'path': '/' }
                );
+
                $element
                        .parent()
                        .toggleClass( 'expanded' )
                        .toggleClass( 'collapsed' )
                        .find( '.body' )
                        .slideToggle( 'fast' );
+               isCollapsed = !isCollapsed;
+
+               $element
+                       .find( '> a' )
+                       .attr( {
+                               'aria-pressed': isCollapsed ? 'false' : 'true',
+                               'aria-expanded': isCollapsed ? 'false' : 'true'
+                       } );
        }
 
        /* Browser Support */
                        .each( function ( i ) {
                                var id = $(this).attr( 'id' ),
                                        state = $.cookie( 'vector-nav-' + id );
+                               $(this).find( 'ul:first' ).attr( 'id', id + '-list' );
                                // Add anchor tag to heading for better accessibility
-                               $( this ).find( 'h3' ).wrapInner( $( '<a href="#"></a>' ).click( false ) );
+                               $( this ).find( 'h3' ).wrapInner(
+                                       $( '<a>' )
+                                               .attr( {
+                                                       href: '#',
+                                                       'aria-haspopup': 'true',
+                                                       'aria-controls': id + '-list',
+                                                       role: 'button'
+                                               } )
+                                               .click( false )
+                               );
                                // In the case that we are not showing the new version, let's show the languages by default
                                if (
                                        state === 'true' ||
                                                .find( '.body' )
                                                .hide() // bug 34450
                                                .show();
+                                       $(this).find( 'h3 > a' )
+                                               .attr( {
+                                                       'aria-pressed': 'true',
+                                                       'aria-expanded': 'true'
+                                               } );
                                } else {
                                        $(this)
                                                .addClass( 'collapsed' )
                                                .removeClass( 'expanded' );
+                                       $(this).find( 'h3 > a' )
+                                               .attr( {
+                                                       'aria-pressed': 'false',
+                                                       'aria-expanded': 'false'
+                                               } );
                                }
                                // Re-save cookie
                                if ( state !== null ) {
diff --git a/skins/vector/images/preferences-break.png b/skins/vector/images/preferences-break.png
deleted file mode 100644 (file)
index b529308..0000000
Binary files a/skins/vector/images/preferences-break.png and /dev/null differ
diff --git a/skins/vector/images/preferences-fade.png b/skins/vector/images/preferences-fade.png
deleted file mode 100644 (file)
index 638084d..0000000
Binary files a/skins/vector/images/preferences-fade.png and /dev/null differ
diff --git a/skins/vector/images/preferences/break.png b/skins/vector/images/preferences/break.png
new file mode 100644 (file)
index 0000000..b529308
Binary files /dev/null and b/skins/vector/images/preferences/break.png differ
diff --git a/skins/vector/images/preferences/fade.png b/skins/vector/images/preferences/fade.png
new file mode 100644 (file)
index 0000000..638084d
Binary files /dev/null and b/skins/vector/images/preferences/fade.png differ
index f5cf5e5..f8be097 100644 (file)
@@ -37,7 +37,7 @@ div#content {
        /* Merge the border with tabs' one (in their background image) */
        margin-top: -1px;
        background-color: white;
-       color: @body-font-color;
+       color: @content-font-color;
        direction: ltr;
 }
 /* Hide, but keep accessible for screen-readers */
@@ -378,6 +378,9 @@ div#simpleSearch {
 div#simpleSearch input:focus {
        outline: none;
 }
+div#simpleSearch input {
+       color: black;
+}
 div#simpleSearch input.placeholder {
        color: #999;
 }
@@ -407,7 +410,6 @@ div#simpleSearch input#searchInput {
         * this from ever being shown anyways.
        */
        font-size: 13px;
-       color: black;
        background-color: transparent;
        direction: ltr;
 }
@@ -547,104 +549,6 @@ div#footer #footer-places li {
        text-decoration: none;
 }
 
-/*
- *
- * The following code is highly modified from monobook. It would be nice if the
- * preftoc id was more human readable like preferences-toc for instance,
- * howerver this would require backporting the other skins.
- */
-
-/* Preferences */
-#preftoc {
-       /* Tabs */
-       width: 100%;
-       float: left;
-       clear: both;
-       margin: 0 !important;
-       padding: 0 !important;
-       .background-image('images/preferences-break.png');
-       background-position: bottom left;
-       background-repeat: no-repeat;
-}
-       #preftoc li {
-               /* Tab */
-               float: left;
-               margin: 0;
-               padding: 0;
-               padding-right: 1px;
-               height: 2.25em;
-               white-space: nowrap;
-               list-style-type: none;
-               list-style-image: none;
-               .background-image('images/preferences-break.png');
-               background-position: bottom right;
-               background-repeat: no-repeat;
-       }
-       /* Sadly, IE6 won't understand this */
-       #preftoc li:first-child {
-               margin-left: 1px;
-       }
-       #preftoc a,
-       #preftoc a:active {
-               display: inline-block;
-               position: relative;
-               color: @menu-link-color;
-               padding: 0.5em;
-               text-decoration: none;
-               background-image: none;
-               font-size: 0.9em;
-       }
-       #preftoc a:hover,
-       #preftoc a:focus {
-               text-decoration: underline;
-       }
-       #preftoc li.selected a {
-               .background-image('images/preferences-fade.png');
-               background-position: bottom;
-               background-repeat: repeat-x;
-               color: #333;
-               text-decoration: none;
-       }
-#preferences {
-       float: left;
-       width: 100%;
-       margin: 0;
-       margin-top: -2px;
-       clear: both;
-       border: solid 1px #ccc;
-       background-color: #fafafa;
-}
-#preferences fieldset {
-       border: none;
-       border-top: solid 1px #ccc;
-}
-#preferences fieldset.prefsection {
-       border: none;
-       padding: 0;
-       margin: 1em;
-}
-#preferences legend {
-       color: #666;
-}
-#preferences fieldset.prefsection legend.mainLegend {
-       display: none;
-}
-#preferences td {
-       padding-left: 0.5em;
-       padding-right: 0.5em;
-}
-.htmlform-tip {
-       font-size: x-small;
-       padding: .2em 2em;
-       color: #666;
-}
-#preferences div.mw-prefs-buttons {
-       padding: 1em;
-}
-#preferences div.mw-prefs-buttons input {
-       margin-right: 0.25em;
-}
-
 ul {
        list-style-type: disc;
        .list-style-image('images/bullet-icon.png');
diff --git a/skins/vector/special.preferences.less b/skins/vector/special.preferences.less
new file mode 100644 (file)
index 0000000..a9b1006
--- /dev/null
@@ -0,0 +1,114 @@
+@import "mediawiki.mixins";
+@import "variables";
+
+/**
+ * The following code is highly modified from monobook. It would be nice if the
+ * preftoc id was more human readable like preferences-toc for instance,
+ * howerver this would require backporting the other skins.
+ */
+
+#preftoc {
+       /* Tabs */
+       width: 100%;
+       float: left;
+       clear: both;
+       margin: 0 !important;
+       padding: 0 !important;
+       .background-image('images/preferences/break.png');
+       background-position: bottom left;
+       background-repeat: no-repeat;
+
+       li {
+               /* Tab */
+               float: left;
+               margin: 0;
+               padding: 0;
+               padding-right: 1px;
+               height: 2.25em;
+               white-space: nowrap;
+               list-style-type: none;
+               list-style-image: none;
+               .background-image('images/preferences/break.png');
+               background-position: bottom right;
+               background-repeat: no-repeat;
+
+               /* Sadly, IE6 won't understand this */
+               &:first-child {
+                       margin-left: 1px;
+               }
+
+               &.selected {
+                       a {
+                               .background-image('images/preferences/fade.png');
+                               background-position: bottom;
+                               background-repeat: repeat-x;
+                               color: #333;
+                               text-decoration: none;
+                       }
+               }
+       }
+
+       a,
+       a:active {
+               display: inline-block;
+               position: relative;
+               color: @menu-link-color;
+               padding: 0.5em;
+               text-decoration: none;
+               background-image: none;
+               font-size: 0.9em;
+       }
+
+       a:hover,
+       a:focus {
+               text-decoration: underline;
+       }
+}
+
+#preferences {
+       float: left;
+       width: 100%;
+       margin: 0;
+       margin-top: -2px;
+       clear: both;
+       border: solid 1px #ccc;
+       background-color: #fafafa;
+
+       fieldset {
+               border: none;
+               border-top: solid 1px #ccc;
+
+               &.prefsection {
+                       border: none;
+                       padding: 0;
+                       margin: 1em;
+
+                       legend.mainLegend {
+                               display: none;
+                       }
+               }
+       }
+
+       legend {
+               color: #666;
+       }
+
+       td {
+               padding-left: 0.5em;
+               padding-right: 0.5em;
+       }
+
+       div.mw-prefs-buttons {
+               padding: 1em;
+
+               input {
+                       margin-right: 0.25em;
+               }
+       }
+}
+
+.htmlform-tip {
+       font-size: x-small;
+       padding: .2em 2em;
+       color: #666;
+}
index 62e3d55..a76b639 100644 (file)
@@ -1,9 +1,13 @@
 @import "variables.less";
 @import "beta/variables.less";
-@import "screen.less";
-@import "beta/screen.less";
-@import "externalLinks.less";
-@import "collapsibleNav.less";
+
+@media screen {
+       @import "screen.less";
+       @import "beta/screen.less";
+       @import "externalLinks.less";
+       @import "collapsibleNav.less";
+}
+
 @media screen and (min-width: 982px) {
        @import "screen-hd.less";
 }
index a5db63c..bd45851 100644 (file)
@@ -1,7 +1,10 @@
 @import "variables.less";
-@import "screen.less";
-@import "externalLinks.less";
-@import "collapsibleNav.less";
+
+@media screen {
+       @import "screen.less";
+       @import "externalLinks.less";
+       @import "collapsibleNav.less";
+}
 
 @media screen and (min-width: 982px) {
        @import "screen-hd.less";
index fac7f82..691e0fd 100644 (file)
@@ -1,10 +1,10 @@
 @html-font-size: 1em;
 
 @body-font-size: 1em;
-@body-font-color: #252525;
 
 // Page content
 @content-font-family: sans-serif;
+@content-font-color: black;
 @content-font-size: 0.8em;
 @content-line-height: 1.5em;
 @content-padding: 1.5em 1.5em 1.5em 1.75em;
index 0939ebe..dec8e22 100644 (file)
@@ -40,7 +40,6 @@ $wgAutoloadClasses += array(
        'MediaWikiPHPUnitCommand' => "$testDir/phpunit/MediaWikiPHPUnitCommand.php",
        'MediaWikiPHPUnitTestListener' => "$testDir/phpunit/MediaWikiPHPUnitTestListener.php",
        'MediaWikiLangTestCase' => "$testDir/phpunit/MediaWikiLangTestCase.php",
-       'MediaWikiProvide' => "$testDir/phpunit/includes/Providers.php",
        'TestUser' => "$testDir/phpunit/includes/TestUser.php",
 
        # tests/phpunit/includes
@@ -56,10 +55,10 @@ $wgAutoloadClasses += array(
        # tests/phpunit/includes/api
        'ApiFormatTestBase' => "$testDir/phpunit/includes/api/format/ApiFormatTestBase.php",
        'ApiTestCase' => "$testDir/phpunit/includes/api/ApiTestCase.php",
-       'ApiTestContext' => "$testDir/phpunit/includes/api/ApiTestCase.php",
-       'MockApi' => "$testDir/phpunit/includes/api/ApiTestCase.php",
+       'ApiTestContext' => "$testDir/phpunit/includes/api/ApiTestContext.php",
+       'MockApi' => "$testDir/phpunit/includes/api/MockApi.php",
+       'UserWrapper' => "$testDir/phpunit/includes/api/UserWrapper.php",
        'RandomImageGenerator' => "$testDir/phpunit/includes/api/RandomImageGenerator.php",
-       'UserWrapper' => "$testDir/phpunit/includes/api/ApiTestCase.php",
 
        # tests/phpunit/includes/content
        'DummyContentHandlerForTesting' => "$testDir/phpunit/includes/content/ContentHandlerTest.php",
@@ -79,6 +78,9 @@ $wgAutoloadClasses += array(
        # tests/phpunit/includes/libs
        'GenericArrayObjectTest' => "$testDir/phpunit/includes/libs/GenericArrayObjectTest.php",
 
+       # tests/phpunit/media
+       'FakeDimensionFile' => "$testDir/phpunit/includes/media/FakeDimensionFile.php",
+
        # tests/phpunit/includes/site
        'SiteTest' => "$testDir/phpunit/includes/site/SiteTest.php",
        'TestSites' => "$testDir/phpunit/includes/site/TestSites.php",
index 3f8d7f9..58ea1ed 100644 (file)
  */
 class ParserTest {
        /**
-        * boolean $color whereas output should be colorized
+        * @var bool $color whereas output should be colorized
         */
        private $color;
 
        /**
-        * boolean $showOutput Show test output
+        * @var bool $showOutput Show test output
         */
        private $showOutput;
 
        /**
-        * boolean $useTemporaryTables Use temporary tables for the temporary database
+        * @var bool $useTemporaryTables Use temporary tables for the temporary database
         */
        private $useTemporaryTables = true;
 
        /**
-        * boolean $databaseSetupDone True if the database has been set up
+        * @var bool $databaseSetupDone True if the database has been set up
         */
        private $databaseSetupDone = false;
 
@@ -65,7 +65,7 @@ class ParserTest {
        private $dbClone;
 
        /**
-        * string $oldTablePrefix Original table prefix
+        * @var string $oldTablePrefix Original table prefix
         */
        private $oldTablePrefix;
 
@@ -494,6 +494,9 @@ class ParserTest {
 
        /**
         * Get a Parser object
+        *
+        * @param string $preprocessor
+        * @return Parser
         */
        function getParser( $preprocessor = null ) {
                global $wgParserConf;
@@ -566,6 +569,7 @@ class ParserTest {
                        $out = $parser->getPreloadText( $input, $title, $options );
                } else {
                        $output = $parser->parse( $input, $title, $options, true, true, 1337 );
+                       $output->setTOCEnabled( !isset( $opts['notoc'] ) );
                        $out = $output->getText();
 
                        if ( isset( $opts['showtitle'] ) ) {
@@ -618,7 +622,7 @@ class ParserTest {
        /**
         * Use a regex to find out the value of an option
         * @param $key String: name of option val to retrieve
-        * @param $opts Options array to look in
+        * @param $opts array: Options array to look in
         * @param $default Mixed: default value returned if not found
         */
        private static function getOptionValue( $key, $opts, $default ) {
index 3266b16..13312bb 100644 (file)
@@ -26,6 +26,7 @@
 # showtitle     make the first line the title
 # comment       run through Linker::formatComment() instead of main parser
 # local         format section links in edit comment text as local links
+# notoc         disable table of contents
 #
 # You can also set the following parser properties via test options:
 #  wgEnableUploads, wgAllowExternalImages, wgMaxTocLevel,
@@ -403,9 +404,12 @@ Simple list
 * Item 1
 * Item 2
 !! result
-<ul><li> Item 1
-</li><li> Item 2
-</li></ul>
+<ul>
+<li> Item 1
+</li>
+<li> Item 2
+</li>
+</ul>
 
 !! end
 
@@ -428,22 +432,38 @@ Italics and bold
 * plain l'''italic''plain
 * plain l''''bold''' plain
 !! result
-<ul><li> plain
-</li><li> plain<i>italic</i>plain
-</li><li> plain<i>italic</i>plain<i>italic</i>plain
-</li><li> plain<b>bold</b>plain
-</li><li> plain<b>bold</b>plain<b>bold</b>plain
-</li><li> plain<i>italic</i>plain<b>bold</b>plain
-</li><li> plain<b>bold</b>plain<i>italic</i>plain
-</li><li> plain<i>italic<b>bold-italic</b>italic</i>plain
-</li><li> plain<b>bold<i>bold-italic</i>bold</b>plain
-</li><li> plain<i><b>bold-italic</b>italic</i>plain
-</li><li> plain<b><i>bold-italic</i>bold</b>plain
-</li><li> plain<i>italic<b>bold-italic</b></i>plain
-</li><li> plain<b>bold<i>bold-italic</i></b>plain
-</li><li> plain l'<i>italic</i>plain
-</li><li> plain l'<b>bold</b> plain
-</li></ul>
+<ul>
+<li> plain
+</li>
+<li> plain<i>italic</i>plain
+</li>
+<li> plain<i>italic</i>plain<i>italic</i>plain
+</li>
+<li> plain<b>bold</b>plain
+</li>
+<li> plain<b>bold</b>plain<b>bold</b>plain
+</li>
+<li> plain<i>italic</i>plain<b>bold</b>plain
+</li>
+<li> plain<b>bold</b>plain<i>italic</i>plain
+</li>
+<li> plain<i>italic<b>bold-italic</b>italic</i>plain
+</li>
+<li> plain<b>bold<i>bold-italic</i>bold</b>plain
+</li>
+<li> plain<i><b>bold-italic</b>italic</i>plain
+</li>
+<li> plain<b><i>bold-italic</i>bold</b>plain
+</li>
+<li> plain<i>italic<b>bold-italic</b></i>plain
+</li>
+<li> plain<b>bold<i>bold-italic</i></b>plain
+</li>
+<li> plain l'<i>italic</i>plain
+</li>
+<li> plain l'<b>bold</b> plain
+</li>
+</ul>
 
 !! end
 
@@ -985,15 +1005,24 @@ nowiki 3
 *There is not nowiki.
 *There is <nowiki>nowiki</nowiki>.
 !! result
-<dl><dd>There is not nowiki.
-</dd><dd>There is nowiki.
-</dd></dl>
-<ol><li>There is not nowiki.
-</li><li>There is nowiki.
-</li></ol>
-<ul><li>There is not nowiki.
-</li><li>There is nowiki.
-</li></ul>
+<dl>
+<dd>There is not nowiki.
+</dd>
+<dd>There is nowiki.
+</dd>
+</dl>
+<ol>
+<li>There is not nowiki.
+</li>
+<li>There is nowiki.
+</li>
+</ol>
+<ul>
+<li>There is not nowiki.
+</li>
+<li>There is nowiki.
+</li>
+</ul>
 
 !! end
 
@@ -1390,11 +1419,21 @@ Bug 52763: Preformatted in <blockquote>
 !! input
 <blockquote>
  Blah
+{|
+|
+ indented cell (no pre-wrapping!)
+|}
 </blockquote>
 !! result
 <blockquote>
 <p> Blah
 </p>
+<table>
+<tr>
+<td>
+<p> indented cell (no pre-wrapping!)
+</p>
+</td></tr></table>
 </blockquote>
 
 !! end
@@ -1747,8 +1786,10 @@ Templates: Strip leading and trailing whitespace from named-param values
 </p><p>b
 </p><p>c
 </p>
-<ul><li> d
-</li></ul>
+<ul>
+<li> d
+</li>
+</ul>
 
 !! end
 
@@ -1789,8 +1830,10 @@ Templates: Don't strip whitespace from positional-param values
 </pre>
 <p><br />
 </p>
-<ul><li> f
-</li></ul>
+<ul>
+<li> f
+</li>
+</ul>
 <p><br />
 </p>
 <pre>g
@@ -1860,7 +1903,7 @@ data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},
 !! end
 
 !! test
-Templates: Dont escape already nowiki-escaped text in template parameters
+Templates: Don't escape already nowiki-escaped text in template parameters
 !! options
 parsoid=html2wt,wt2wt
 !! input
@@ -2139,9 +2182,9 @@ parsoid=wt2html,wt2wt
  [[Category:foo]] <!-- No pre-wrapping -->
 {{echo| [[Category:foo]]}} <!-- No pre-wrapping -->
 !! result
- <link rel="mw:WikiLink/Category" href="./Category:Foo"> <!-- No pre-wrapping -->
+ <link rel="mw:PageProp/Category" href="./Category:Foo"> <!-- No pre-wrapping -->
 <span about="#mwt1" typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":" [[Category:foo]]"}},"i":0}}]}'> </span>
-<link rel="mw:WikiLink/Category" href="./Category:Foo" about="#mwt1"> <!-- No pre-wrapping -->
+<link rel="mw:PageProp/Category" href="./Category:Foo" about="#mwt1"> <!-- No pre-wrapping -->
 !! end
 
 !! test
@@ -2153,9 +2196,9 @@ parsoid=wt2html,wt2wt
  [[Category:foo]] {{echo|b}}
 !! result
 <pre>
-<link rel="mw:WikiLink/Category" href="./Category:Foo"> a
+<link rel="mw:PageProp/Category" href="./Category:Foo"> a
 
-<link rel="mw:WikiLink/Category" href="./Category:Foo"> <span about="#mwt1" typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"b"}},"i":0}}]}'>b</span></pre>
+<link rel="mw:PageProp/Category" href="./Category:Foo"> <span about="#mwt1" typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"b"}},"i":0}}]}'>b</span></pre>
 !! end
 
 ###
@@ -2240,8 +2283,11 @@ Simple definition
 !! input
 ; name : Definition
 !! result
-<dl><dt> name&#160;</dt><dd> Definition
-</dd></dl>
+<dl>
+<dt> name&#160;</dt>
+<dd> Definition
+</dd>
+</dl>
 
 !! end
 
@@ -2250,8 +2296,10 @@ Definition list for indentation only
 !! input
 : Indented text
 !! result
-<dl><dd> Indented text
-</dd></dl>
+<dl>
+<dd> Indented text
+</dd>
+</dl>
 
 !! end
 
@@ -2260,8 +2308,11 @@ Definition list with no space
 !! input
 ;name:Definition
 !! result
-<dl><dt>name</dt><dd>Definition
-</dd></dl>
+<dl>
+<dt>name</dt>
+<dd>Definition
+</dd>
+</dl>
 
 !!end
 
@@ -2270,8 +2321,11 @@ Definition list with URL link
 !! input
 ; http://example.com/ : definition
 !! result
-<dl><dt> <a rel="nofollow" class="external free" href="http://example.com/">http://example.com/</a>&#160;</dt><dd> definition
-</dd></dl>
+<dl>
+<dt> <a rel="nofollow" class="external free" href="http://example.com/">http://example.com/</a>&#160;</dt>
+<dd> definition
+</dd>
+</dl>
 
 !! end
 
@@ -2280,8 +2334,11 @@ Definition list with bracketed URL link
 !! input
 ;[http://www.example.com/ Example]:Something about it
 !! result
-<dl><dt><a rel="nofollow" class="external text" href="http://www.example.com/">Example</a></dt><dd>Something about it
-</dd></dl>
+<dl>
+<dt><a rel="nofollow" class="external text" href="http://www.example.com/">Example</a></dt>
+<dd>Something about it
+</dd>
+</dl>
 
 !! end
 
@@ -2290,8 +2347,11 @@ Definition list with wikilink containing colon
 !! input
 ; [[Help:FAQ]]: The least-read page on Wikipedia
 !! result
-<dl><dt> <a href="/index.php?title=Help:FAQ&amp;action=edit&amp;redlink=1" class="new" title="Help:FAQ (page does not exist)">Help:FAQ</a></dt><dd> The least-read page on Wikipedia
-</dd></dl>
+<dl>
+<dt> <a href="/index.php?title=Help:FAQ&amp;action=edit&amp;redlink=1" class="new" title="Help:FAQ (page does not exist)">Help:FAQ</a></dt>
+<dd> The least-read page on Wikipedia
+</dd>
+</dl>
 
 !! end
 
@@ -2301,8 +2361,11 @@ Definition list with news link containing colon
 !! input
 ;  news:alt.wikipedia.rox: This isn't even a real newsgroup!
 !! result
-<dl><dt>  <a rel="nofollow" class="external free" href="news:alt.wikipedia.rox">news:alt.wikipedia.rox</a></dt><dd> This isn't even a real newsgroup!
-</dd></dl>
+<dl>
+<dt>  <a rel="nofollow" class="external free" href="news:alt.wikipedia.rox">news:alt.wikipedia.rox</a></dt>
+<dd> This isn't even a real newsgroup!
+</dd>
+</dl>
 
 !! end
 
@@ -2311,8 +2374,10 @@ Malformed definition list with colon
 !! input
 ;  news:alt.wikipedia.rox -- don't crash or enter an infinite loop
 !! result
-<dl><dt>  <a rel="nofollow" class="external free" href="news:alt.wikipedia.rox">news:alt.wikipedia.rox</a> -- don't crash or enter an infinite loop
-</dt></dl>
+<dl>
+<dt>  <a rel="nofollow" class="external free" href="news:alt.wikipedia.rox">news:alt.wikipedia.rox</a> -- don't crash or enter an infinite loop
+</dt>
+</dl>
 
 !! end
 
@@ -2321,8 +2386,11 @@ Definition lists: colon in external link text
 !! input
 ; [http://www.wikipedia2.org/ Wikipedia : The Next Generation]: OK, I made that up
 !! result
-<dl><dt> <a rel="nofollow" class="external text" href="http://www.wikipedia2.org/">Wikipedia&#160;: The Next Generation</a></dt><dd> OK, I made that up
-</dd></dl>
+<dl>
+<dt> <a rel="nofollow" class="external text" href="http://www.wikipedia2.org/">Wikipedia&#160;: The Next Generation</a></dt>
+<dd> OK, I made that up
+</dd>
+</dl>
 
 !! end
 
@@ -2331,8 +2399,10 @@ Definition lists: colon in HTML attribute
 !! input
 ;<b style="display: inline">bold</b>
 !! result
-<dl><dt><b style="display: inline">bold</b>
-</dt></dl>
+<dl>
+<dt><b style="display: inline">bold</b>
+</dt>
+</dl>
 
 !! end
 
@@ -2341,8 +2411,11 @@ Definition lists: self-closed tag
 !! input
 ;one<br/>two : two-line fun
 !! result
-<dl><dt>one<br />two&#160;</dt><dd> two-line fun
-</dd></dl>
+<dl>
+<dt>one<br />two&#160;</dt>
+<dd> two-line fun
+</dd>
+</dl>
 
 !! end
 
@@ -2371,16 +2444,19 @@ Definition and unordered list using wiki syntax nested in unordered list using h
 <ul><li>
 ; term : description
 * unordered
-</li>
-</ul>
+</li></ul>
 !! result
 <ul><li>
-<dl><dt> term&#160;</dt><dd> description
-</dd></dl>
-<ul><li> unordered
-</li></ul>
+<dl>
+<dt> term&#160;</dt>
+<dd> description
+</dd>
+</dl>
+<ul>
+<li> unordered
 </li>
 </ul>
+</li></ul>
 
 !! end
 
@@ -2391,8 +2467,11 @@ Definition list with empty definition and following paragraph
 ; term:
 Paragraph text
 !! result
-<dl><dt> term</dt><dd>
-</dd></dl>
+<dl>
+<dt> term</dt>
+<dd>
+</dd>
+</dl>
 <p>Paragraph text
 </p>
 !! end
@@ -2421,10 +2500,14 @@ Definition Lists: No nesting: Multiple dd's
 :a
 :b
 !! result
-<dl><dt>x
-</dt><dd>a
-</dd><dd>b
-</dd></dl>
+<dl>
+<dt>x
+</dt>
+<dd>a
+</dd>
+<dd>b
+</dd>
+</dl>
 
 !! end
 
@@ -2435,12 +2518,18 @@ Definition Lists: Indentation: Regular
 ::i2
 :::i3
 !! result
-<dl><dd>i1
-<dl><dd>i2
-<dl><dd>i3
-</dd></dl>
-</dd></dl>
-</dd></dl>
+<dl>
+<dd>i1
+<dl>
+<dd>i2
+<dl>
+<dd>i3
+</dd>
+</dl>
+</dd>
+</dl>
+</dd>
+</dl>
 
 !! end
 
@@ -2450,11 +2539,17 @@ Definition Lists: Indentation: Missing 1st level
 ::i2
 :::i3
 !! result
-<dl><dd><dl><dd>i2
-<dl><dd>i3
-</dd></dl>
-</dd></dl>
-</dd></dl>
+<dl>
+<dd><dl>
+<dd>i2
+<dl>
+<dd>i3
+</dd>
+</dl>
+</dd>
+</dl>
+</dd>
+</dl>
 
 !! end
 
@@ -2463,10 +2558,16 @@ Definition Lists: Indentation: Multi-level indent
 !! input
 :::i3
 !! result
-<dl><dd><dl><dd><dl><dd>i3
-</dd></dl>
-</dd></dl>
-</dd></dl>
+<dl>
+<dd><dl>
+<dd><dl>
+<dd>i3
+</dd>
+</dl>
+</dd>
+</dl>
+</dd>
+</dl>
 
 !! end
 
@@ -2501,7 +2602,9 @@ parsoid
 |a
 |} 
 !! result
-<dl><dd> <table><tr><td>a</td></tr></table> </dd></dl>
+<dl>
+<dd> <table><tr><td>a</td></tr></table> </dd>
+</dl>
 !! end
 ## The PHP parser treats : items (dd) without a corresponding ; item (dt)
 ## as an empty dt item.  It also ignores all but the last ";" when followed
@@ -2554,13 +2657,17 @@ Table / list interaction: indented table with lists in table contents
 
 <tr>
 <td> a
-<ul><li> b
-</li></ul>
+<ul>
+<li> b
+</li>
+</ul>
 </td></tr>
 <tr>
 <td> c
-<ul><li> d
-</li></ul>
+<ul>
+<li> d
+</li>
+</ul>
 </td></tr></table></dd></dl>
 
 !! end
@@ -2583,18 +2690,27 @@ Table / list interaction: lists nested in tables nested in indented lists
 <dl><dd><table>
 <tr>
 <td>
-<dl><dd>a
-</dd><dd>b
-</dd></dl>
+<dl>
+<dd>a
+</dd>
+<dd>b
+</dd>
+</dl>
 </td>
 <td>
-<ul><li>c
-</li><li>d
-</li></ul>
+<ul>
+<li>c
+</li>
+<li>d
+</li>
+</ul>
 </td></tr></table></dd></dl>
-<ul><li>e
-</li><li>f
-</li></ul>
+<ul>
+<li>e
+</li>
+<li>f
+</li>
+</ul>
 
 !!end
 
@@ -2682,11 +2798,18 @@ Definition Lists: Nesting: Test 4
 ::;t3
 :::d3
 !! result
-<dl><dd><dl><dd><dl><dt>t3
-</dt><dd>d3
-</dd></dl>
-</dd></dl>
-</dd></dl>
+<dl>
+<dd><dl>
+<dd><dl>
+<dt>t3
+</dt>
+<dd>d3
+</dd>
+</dl>
+</dd>
+</dl>
+</dd>
+</dl>
 
 !! end
 
@@ -2703,13 +2826,22 @@ php
 ::* bar
 :; baz
 !! result
-<dl><dd><dl><dt><ul><li> foo
-</li><li> bar
-</li></ul>
-</dt></dl>
-<dl><dt> baz
-</dt></dl>
-</dd></dl>
+<dl>
+<dd><dl>
+<dt><ul>
+<li> foo
+</li>
+<li> bar
+</li>
+</ul>
+</dt>
+</dl>
+<dl>
+<dt> baz
+</dt>
+</dl>
+</dd>
+</dl>
 
 !! end
 !! test
@@ -2721,9 +2853,19 @@ parsoid
 ::* bar
 :; baz
 !! result
-<dl><dd><dl><dt><ul><li> foo
-</li></ul></dt><dd><ul><li> bar
-</li></ul></dd><dt> baz</dt></dl></dd></dl>
+<dl>
+<dd><dl>
+<dt><ul>
+<li> foo
+</li>
+</ul></dt>
+<dd><ul>
+<li> bar
+</li>
+</ul></dd>
+<dt> baz</dt>
+</dl></dd>
+</dl>
 !! end
 
 !! test
@@ -2732,10 +2874,15 @@ Definition Lists: Mixed Lists: Test 2
 *: d1
 *: d2
 !! result
-<ul><li><dl><dd> d1
-</dd><dd> d2
-</dd></dl>
-</li></ul>
+<ul>
+<li><dl>
+<dd> d1
+</dd>
+<dd> d2
+</dd>
+</dl>
+</li>
+</ul>
 
 !! end
 
@@ -2746,12 +2893,21 @@ Definition Lists: Mixed Lists: Test 3
 *::: d1
 *::: d2
 !! result
-<ul><li><dl><dd><dl><dd><dl><dd> d1
-</dd><dd> d2
-</dd></dl>
-</dd></dl>
-</dd></dl>
-</li></ul>
+<ul>
+<li><dl>
+<dd><dl>
+<dd><dl>
+<dd> d1
+</dd>
+<dd> d2
+</dd>
+</dl>
+</dd>
+</dl>
+</dd>
+</dl>
+</li>
+</ul>
 
 !! end
 
@@ -2762,10 +2918,17 @@ Definition Lists: Mixed Lists: Test 4
 *;d1 :d2
 *;d3 :d4
 !! result
-<ul><li><dl><dt>d1&#160;</dt><dd>d2
-</dd><dt>d3&#160;</dt><dd>d4
-</dd></dl>
-</li></ul>
+<ul>
+<li><dl>
+<dt>d1&#160;</dt>
+<dd>d2
+</dd>
+<dt>d3&#160;</dt>
+<dd>d4
+</dd>
+</dl>
+</li>
+</ul>
 
 !! end
 
@@ -2776,11 +2939,17 @@ Definition Lists: Mixed Lists: Test 5
 *:d1
 *:: d2
 !! result
-<ul><li><dl><dd>d1
-<dl><dd> d2
-</dd></dl>
-</dd></dl>
-</li></ul>
+<ul>
+<li><dl>
+<dd>d1
+<dl>
+<dd> d2
+</dd>
+</dl>
+</dd>
+</dl>
+</li>
+</ul>
 
 !! end
 
@@ -2791,13 +2960,23 @@ Definition Lists: Mixed Lists: Test 6
 #*:d1
 #*::: d3
 !! result
-<ol><li><ul><li><dl><dd>d1
-<dl><dd><dl><dd> d3
-</dd></dl>
-</dd></dl>
-</dd></dl>
-</li></ul>
-</li></ol>
+<ol>
+<li><ul>
+<li><dl>
+<dd>d1
+<dl>
+<dd><dl>
+<dd> d3
+</dd>
+</dl>
+</dd>
+</dl>
+</dd>
+</dl>
+</li>
+</ul>
+</li>
+</ol>
 
 !! end
 
@@ -2808,10 +2987,15 @@ Definition Lists: Mixed Lists: Test 7
 :* d1
 :* d2
 !! result
-<dl><dd><ul><li> d1
-</li><li> d2
-</li></ul>
-</dd></dl>
+<dl>
+<dd><ul>
+<li> d1
+</li>
+<li> d2
+</li>
+</ul>
+</dd>
+</dl>
 
 !! end
 
@@ -2822,12 +3006,20 @@ Definition Lists: Mixed Lists: Test 8
 :* d1
 ::* d2
 !! result
-<dl><dd><ul><li> d1
-</li></ul>
-<dl><dd><ul><li> d2
-</li></ul>
-</dd></dl>
-</dd></dl>
+<dl>
+<dd><ul>
+<li> d1
+</li>
+</ul>
+<dl>
+<dd><ul>
+<li> d2
+</li>
+</ul>
+</dd>
+</dl>
+</dd>
+</dl>
 
 !! end
 
@@ -2837,9 +3029,14 @@ Definition Lists: Mixed Lists: Test 9
 !! input
 *;foo :bar
 !! result
-<ul><li><dl><dt>foo&#160;</dt><dd>bar
-</dd></dl>
-</li></ul>
+<ul>
+<li><dl>
+<dt>foo&#160;</dt>
+<dd>bar
+</dd>
+</dl>
+</li>
+</ul>
 
 !! end
 
@@ -2849,10 +3046,17 @@ Definition Lists: Mixed Lists: Test 10
 !! input
 *#;foo :bar
 !! result
-<ul><li><ol><li><dl><dt>foo&#160;</dt><dd>bar
-</dd></dl>
-</li></ol>
-</li></ul>
+<ul>
+<li><ol>
+<li><dl>
+<dt>foo&#160;</dt>
+<dd>bar
+</dd>
+</dl>
+</li>
+</ol>
+</li>
+</ul>
 
 !! end
 
@@ -2869,17 +3073,37 @@ php
 *#*#;*;;foo :bar
 *#*#;boo :baz
 !! result
-<ul><li><ol><li><ul><li><ol><li><dl><dt>foo&#160;</dt><dd><ul><li><dl><dt><dl><dt>bar
-</dt></dl>
-</dd></dl>
-</li></ul>
-</dd></dl>
-<dl><dt>boo&#160;</dt><dd>baz
-</dd></dl>
-</li></ol>
-</li></ul>
-</li></ol>
-</li></ul>
+<ul>
+<li><ol>
+<li><ul>
+<li><ol>
+<li><dl>
+<dt>foo&#160;</dt>
+<dd><ul>
+<li><dl>
+<dt><dl>
+<dt>bar
+</dt>
+</dl>
+</dd>
+</dl>
+</li>
+</ul>
+</dd>
+</dl>
+<dl>
+<dt>boo&#160;</dt>
+<dd>baz
+</dd>
+</dl>
+</li>
+</ol>
+</li>
+</ul>
+</li>
+</ol>
+</li>
+</ul>
 
 !! end
 !! test
@@ -2906,9 +3130,17 @@ parsoid
 <dt>
 <dl>
 <dt>foo<span typeof="mw:Placeholder" data-parsoid='{"src":" "}'>&nbsp;</span></dt>
-<dd data-parsoid='{"stx":"row"}'>bar</dd></dl></dt></dl></li></ul></dt>
+<dd data-parsoid='{"stx":"row"}'>bar</dd>
+</dl></dt>
+</dl></li>
+</ul></dt>
 <dt>boo<span typeof="mw:Placeholder" data-parsoid='{"src":" "}'>&nbsp;</span></dt>
-<dd data-parsoid='{"stx":"row"}'>baz</dd></dl></li></ol></li></ul></li></ol></li></ul>
+<dd data-parsoid='{"stx":"row"}'>baz</dd>
+</dl></li>
+</ol></li>
+</ul></li>
+</ol></li>
+</ul>
 !! end
 
 
@@ -2919,15 +3151,32 @@ php
 !! input
 *#;*::;; foo : bar (who uses this?)
 !! result
-<ul><li><ol><li><dl><dt> foo&#160;</dt><dd><ul><li><dl><dd><dl><dd><dl><dt><dl><dt> bar (who uses this?)
-</dt></dl>
-</dd></dl>
-</dd></dl>
-</dd></dl>
-</li></ul>
-</dd></dl>
-</li></ol>
-</li></ul>
+<ul>
+<li><ol>
+<li><dl>
+<dt> foo&#160;</dt>
+<dd><ul>
+<li><dl>
+<dd><dl>
+<dd><dl>
+<dt><dl>
+<dt> bar (who uses this?)
+</dt>
+</dl>
+</dd>
+</dl>
+</dd>
+</dl>
+</dd>
+</dl>
+</li>
+</ul>
+</dd>
+</dl>
+</li>
+</ol>
+</li>
+</ul>
 
 !! end
 !! test
@@ -2953,7 +3202,15 @@ parsoid
 <dt>
 <dl>
 <dt> foo<span typeof="mw:Placeholder" data-parsoid='{"src":" "}'>&nbsp;</span></dt>
-<dd data-parsoid='{"stx":"row"}'> bar (who uses this?)</dd></dl></dt></dl></dd></dl></dd></dl></li></ul></dt></dl></li></ol></li></ul>
+<dd data-parsoid='{"stx":"row"}'> bar (who uses this?)</dd>
+</dl></dt>
+</dl></dd>
+</dl></dd>
+</dl></li>
+</ul></dt>
+</dl></li>
+</ol></li>
+</ul>
 !! end
 
 ###
@@ -3393,6 +3650,8 @@ External links: link text with spaces
 
 !! test
 External links: wiki links within external link (Bug 3695)
+!! options
+php
 !! input
 [http://example.com [[wikilink]] embedded in ext link]
 !! result
@@ -3400,6 +3659,16 @@ External links: wiki links within external link (Bug 3695)
 </p>
 !! end
 
+!! test
+Parsoid: External links: wiki links within external link (Bug 3695)
+!! options
+parsoid
+!! input
+[http://example.com [[wikilink]] embedded in ext link]
+!! result
+<p><a rel="mw:ExtLink" href="http://example.com"></a><a rel="mw:WikiLink" href="./Wikilink">wikilink</a><span> embedded in ext link</span></p>
+!! end
+
 !! test
 BUG 787: Links with one slash after the url protocol are invalid
 !! input
@@ -3544,6 +3813,8 @@ External link containing double-single-quotes in text embedded in italics (bug 4
 
 !! test
 External link containing double-single-quotes with no space separating the url from text in italics
+!! options
+php
 !! input
 [http://www.musee-picasso.fr/pages/page_id18528_u1l2.htm''La muerte de Casagemas'' (1901) en el sitio de [[Museo Picasso (París)|Museo Picasso]].]
 !! result
@@ -3551,6 +3822,16 @@ External link containing double-single-quotes with no space separating the url f
 </p>
 !! end
 
+!! test
+Parsoid:External link containing double-single-quotes with no space separating the url from text in italics
+!! options
+parsoid
+!! input
+[http://www.musee-picasso.fr/pages/page_id18528_u1l2.htm''La muerte de Casagemas'' (1901) en el sitio de [[Museo Picasso (París)|Museo Picasso]].]
+!! result
+<p><a rel="mw:ExtLink" href="http://www.musee-picasso.fr/pages/page_id18528_u1l2.htm"><i>La muerte de Casagemas</i> (1901) en el sitio de </a><a rel="mw:WikiLink" href="./Museo_Picasso_(París)">Museo Picasso</a><span>.</span></p>
+!! end
+
 !! test
 External link with comments in link text
 !! input
@@ -4427,7 +4708,9 @@ parsoid=wt2html,wt2wt
 !! result
 <table>
 <tr>
-<td><ul><li>a</li></ul></td>
+<td><ul>
+<li>a</li>
+</ul></td>
 </tr>
 </table>
 !! end
@@ -4650,6 +4933,19 @@ Namespace takes precedence over interwiki link (bug 51680)
 </p>
 !! end
 
+# The previous test doesn't work correctly in html2*, due to not recognizing the
+# link as an internal one. This one checks for the correct behavior.
+!! test
+Link to namespace preferred over interwiki with correct rel attribute
+!! options
+parsoid=html2wt,html2html
+!! input
+[[MemoryAlpha:AlphaTest]]
+!! result
+<p><a rel="mw:WikiLink" href="./MemoryAlpha:AlphaTest">MemoryAlpha:AlphaTest</a>
+</p>
+!! end
+
 !! test
 Piped link to namespace
 !! input
@@ -5083,6 +5379,16 @@ Parsoid-centric test: Whitespace in ext- and wiki-links should be preserved
 </p>
 !! end
 
+!! test
+Parsoid: Scoped parsing should handle mixed transclusions and plain text
+!! options
+parsoid
+!! input
+[[Foo|{{echo|a}} b {{echo|c}}]]
+!! result
+<p data-parsoid='{"dsr":[0,20,0,0]}'><a rel="mw:WikiLink" href="Foo"><span about="#mwt2" typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"a"}},"i":0}}]}'>a</span> b <span about="#mwt3" typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"c"}},"i":0}}]}'>c</span></a></p>
+!! end
+
 ###
 ### Interwiki links (see maintenance/interwiki.sql)
 ###
@@ -5111,9 +5417,12 @@ Interwiki link encoding conversion (bug 1636)
 *[[Wikipedia:ro:Olteni&#0355;a]]
 *[[Wikipedia:ro:Olteni&#355;a]]
 !! result
-<ul><li><a href="http://en.wikipedia.org/wiki/ro:Olteni%C5%A3a" class="extiw" title="wikipedia:ro:Olteniţa">Wikipedia:ro:Olteni&#355;a</a>
-</li><li><a href="http://en.wikipedia.org/wiki/ro:Olteni%C5%A3a" class="extiw" title="wikipedia:ro:Olteniţa">Wikipedia:ro:Olteni&#355;a</a>
-</li></ul>
+<ul>
+<li><a href="http://en.wikipedia.org/wiki/ro:Olteni%C5%A3a" class="extiw" title="wikipedia:ro:Olteniţa">Wikipedia:ro:Olteni&#355;a</a>
+</li>
+<li><a href="http://en.wikipedia.org/wiki/ro:Olteni%C5%A3a" class="extiw" title="wikipedia:ro:Olteniţa">Wikipedia:ro:Olteni&#355;a</a>
+</li>
+</ul>
 
 !! end
 
@@ -5126,6 +5435,22 @@ Interwiki link with fragment (bug 2130)
 </p>
 !! end
 
+# Ideally the wikipedia: prefix here should be proto-relative too
+!! test
+Different interwiki prefixes mapping to the same URL
+!! options
+parsoid
+!! input
+[[wikipedia:Foo]]
+
+[[:en:Foo]]
+!! result
+<p data-parsoid='{"dsr":[0,17,0,0]}'><a rel="mw:ExtLink" href="http://en.wikipedia.org/wiki/Foo" data-parsoid='{"stx":"simple","a":{"href":"http://en.wikipedia.org/wiki/Foo"},"sa":{"href":"wikipedia:Foo"},"isIW":true,"dsr":[0,17,null,1]}'>wikipedia:Foo</a></p>
+
+
+<p data-parsoid='{"dsr":[19,30,0,0]}'><a rel="mw:ExtLink" href="//en.wikipedia.org/wiki/Foo" data-parsoid='{"stx":"simple","a":{"href":"//en.wikipedia.org/wiki/Foo"},"sa":{"href":":en:Foo"},"isIW":true,"dsr":[19,30,null,1]}'>en:Foo</a></p>
+!! end
+
 
 ###
 ### Interlanguage links
@@ -5225,7 +5550,7 @@ parsoid
 [[ko:]]
 !! result
 <p>
-<link rel="mw:WikiLink/Language" href="http://ko.wikipedia.org/wiki/"></p>
+<link rel="mw:PageProp/Language" href="http://ko.wikipedia.org/wiki/"></p>
 !! end
 
 !! test
@@ -5235,7 +5560,7 @@ parsoid
 !! input
 [[:ko:]]
 !! result
-<p><a rel="mw:WikiLink/Interwiki" href="http://ko.wikipedia.org/wiki/">ko:</a></p>
+<p><a rel="mw:ExtLink" href="//ko.wikipedia.org/wiki/">ko:</a></p>
 !! end
 
 ###
@@ -5298,7 +5623,7 @@ parsoid=wt2html
 !! input
 #REDIRECT [[Category:Foo]]
 !! result
-<link rel="mw:PageProp/redirect" href="./Category:Foo"><link rel="mw:WikiLink/Category" href="./Category:Foo">
+<link rel="mw:PageProp/redirect" href="./Category:Foo"><link rel="mw:PageProp/Category" href="./Category:Foo">
 !! end
 
 !! test
@@ -5308,7 +5633,7 @@ parsoid=wt2html
 !! input
 #REDIRECT [[Category%3AFoo]]
 !! result
-<link rel="mw:PageProp/redirect" href="./Category:Foo"><link rel="mw:WikiLink/Category" href="./Category:Foo">
+<link rel="mw:PageProp/redirect" href="./Category:Foo"><link rel="mw:PageProp/Category" href="./Category:Foo">
 !! end
 
 !! test
@@ -5541,10 +5866,14 @@ Common list
 * item 2
 *item 3
 !! result
-<ul><li>Common list
-</li><li> item 2
-</li><li>item 3
-</li></ul>
+<ul>
+<li>Common list
+</li>
+<li> item 2
+</li>
+<li>item 3
+</li>
+</ul>
 
 !! end
 
@@ -5555,10 +5884,14 @@ Numbered list
 #item 2
 # item 3
 !! result
-<ol><li>Numbered list
-</li><li>item 2
-</li><li> item 3
-</li></ol>
+<ol>
+<li>Numbered list
+</li>
+<li>item 2
+</li>
+<li> item 3
+</li>
+</ol>
 
 !! end
 
@@ -5581,35 +5914,67 @@ Mixed list
 *** Level 3
 #** Level 3, but ordered
 !! result
-<ul><li>Mixed list
-<ol><li> with numbers
-</li></ol>
-<ul><li> and bullets
-</li></ul>
-<ol><li> and numbers
-</li></ol>
-</li><li>bullets again
-<ul><li>bullet level 2
-<ul><li>bullet level 3
-<ol><li>Number on level 4
-</li></ol>
-</li></ul>
-</li><li>bullet level 2
-<ol><li>Number on level 3
-</li><li>Number on level 3
-</li></ol>
-</li></ul>
-<ol><li>number level 2
-</li></ol>
-</li><li>Level 1
-<ul><li><ul><li> Level 3
-</li></ul>
-</li></ul>
-</li></ul>
-<ol><li><ul><li><ul><li> Level 3, but ordered
-</li></ul>
-</li></ul>
-</li></ol>
+<ul>
+<li>Mixed list
+<ol>
+<li> with numbers
+</li>
+</ol>
+<ul>
+<li> and bullets
+</li>
+</ul>
+<ol>
+<li> and numbers
+</li>
+</ol>
+</li>
+<li>bullets again
+<ul>
+<li>bullet level 2
+<ul>
+<li>bullet level 3
+<ol>
+<li>Number on level 4
+</li>
+</ol>
+</li>
+</ul>
+</li>
+<li>bullet level 2
+<ol>
+<li>Number on level 3
+</li>
+<li>Number on level 3
+</li>
+</ol>
+</li>
+</ul>
+<ol>
+<li>number level 2
+</li>
+</ol>
+</li>
+<li>Level 1
+<ul>
+<li><ul>
+<li> Level 3
+</li>
+</ul>
+</li>
+</ul>
+</li>
+</ul>
+<ol>
+<li><ul>
+<li><ul>
+<li> Level 3, but ordered
+</li>
+</ul>
+</li>
+</ul>
+</li>
+</ol>
 
 !! end
 
@@ -5619,10 +5984,14 @@ Nested lists 1
 *foo
 **bar
 !! result
-<ul><li>foo
-<ul><li>bar
-</li></ul>
-</li></ul>
+<ul>
+<li>foo
+<ul>
+<li>bar
+</li>
+</ul>
+</li>
+</ul>
 
 !! end
 
@@ -5632,10 +6001,15 @@ Nested lists 2
 **foo
 *bar
 !! result
-<ul><li><ul><li>foo
-</li></ul>
-</li><li>bar
-</li></ul>
+<ul>
+<li><ul>
+<li>foo
+</li>
+</ul>
+</li>
+<li>bar
+</li>
+</ul>
 
 !! end
 
@@ -5645,10 +6019,14 @@ Nested lists 3 (first element empty)
 *
 **bar
 !! result
-<ul><li>
-<ul><li>bar
-</li></ul>
-</li></ul>
+<ul>
+<li>
+<ul>
+<li>bar
+</li>
+</ul>
+</li>
+</ul>
 
 !! end
 
@@ -5658,10 +6036,15 @@ Nested lists 4 (first element empty)
 **
 *bar
 !! result
-<ul><li><ul><li>
-</li></ul>
-</li><li>bar
-</li></ul>
+<ul>
+<li><ul>
+<li>
+</li>
+</ul>
+</li>
+<li>bar
+</li>
+</ul>
 
 !! end
 
@@ -5671,10 +6054,15 @@ Nested lists 5 (both elements empty)
 **
 *
 !! result
-<ul><li><ul><li>
-</li></ul>
-</li><li>
-</li></ul>
+<ul>
+<li><ul>
+<li>
+</li>
+</ul>
+</li>
+<li>
+</li>
+</ul>
 
 !! end
 
@@ -5684,10 +6072,14 @@ Nested lists 6 (both elements empty)
 *
 **
 !! result
-<ul><li>
-<ul><li>
-</li></ul>
-</li></ul>
+<ul>
+<li>
+<ul>
+<li>
+</li>
+</ul>
+</li>
+</ul>
 
 !! end
 
@@ -5696,10 +6088,16 @@ Nested lists 7 (skip initial nesting levels)
 !! input
 *** foo
 !! result
-<ul><li><ul><li><ul><li> foo
-</li></ul>
-</li></ul>
-</li></ul>
+<ul>
+<li><ul>
+<li><ul>
+<li> foo
+</li>
+</ul>
+</li>
+</ul>
+</li>
+</ul>
 
 !! end
 
@@ -5711,13 +6109,21 @@ Nested lists 8 (multiple nesting transitions)
 ** baz
 * boo
 !! result
-<ul><li> foo
-<ul><li><ul><li> bar
-</li></ul>
-</li><li> baz
-</li></ul>
-</li><li> boo
-</li></ul>
+<ul>
+<li> foo
+<ul>
+<li><ul>
+<li> bar
+</li>
+</ul>
+</li>
+<li> baz
+</li>
+</ul>
+</li>
+<li> boo
+</li>
+</ul>
 
 !! end
 
@@ -5728,10 +6134,14 @@ Nested lists 8 (multiple nesting transitions)
 *<!--cmt-->bar
 <!--cmt-->*baz
 !! result
-<ul><li>foo
-</li><li>bar
-</li><li>baz
-</li></ul>
+<ul>
+<li>foo
+</li>
+<li>bar
+</li>
+<li>baz
+</li>
+</ul>
 
 !! end
 
@@ -5741,9 +6151,12 @@ Nested lists 8 (multiple nesting transitions)
 *foo {{echo|bar
 }}*baz
 !! result
-<ul><li>foo bar
-</li><li>baz
-</li></ul>
+<ul>
+<li>foo bar
+</li>
+<li>baz
+</li>
+</ul>
 
 !! end
 
@@ -5754,10 +6167,14 @@ List items are not parsed correctly following a <pre> block (bug 785)
 * <pre>bar</pre>
 * zar
 !! result
-<ul><li> <pre>foo</pre>
-</li><li> <pre>bar</pre>
-</li><li> zar
-</li></ul>
+<ul>
+<li> <pre>foo</pre>
+</li>
+<li> <pre>bar</pre>
+</li>
+<li> zar
+</li>
+</ul>
 
 !! end
 
@@ -5776,18 +6193,30 @@ List items from template
 * notSOL{{inner list}}
 * item 2
 !! result
-<ul><li> item 1
-</li><li> item 2
-</li></ul>
-<ul><li> item 0
-</li><li> item 1
-</li><li> item 2
-</li></ul>
-<ul><li> item 0
-</li><li> notSOL
-</li><li> item 1
-</li><li> item 2
-</li></ul>
+<ul>
+<li> item 1
+</li>
+<li> item 2
+</li>
+</ul>
+<ul>
+<li> item 0
+</li>
+<li> item 1
+</li>
+<li> item 2
+</li>
+</ul>
+<ul>
+<li> item 0
+</li>
+<li> notSOL
+</li>
+<li> item 1
+</li>
+<li> item 2
+</li>
+</ul>
 
 !! end
 
@@ -5800,14 +6229,22 @@ List interrupted by empty line or heading
 == A heading ==
 * Another list item
 !! result
-<ul><li> foo
-</li></ul>
-<ul><li><ul><li> bar
-</li></ul>
-</li></ul>
+<ul>
+<li> foo
+</li>
+</ul>
+<ul>
+<li><ul>
+<li> bar
+</li>
+</ul>
+</li>
+</ul>
 <h2><span class="mw-headline" id="A_heading">A heading</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&amp;action=edit&amp;section=1" title="Edit section: A heading">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
-<ul><li> Another list item
-</li></ul>
+<ul>
+<li> Another list item
+</li>
+</ul>
 
 !!end
 
@@ -5837,11 +6274,16 @@ Single-comment whitespace lines dont break lists, and neither do multi-comment w
  <!--foo--> <!----> <!--This line NOT split the list either--> 
 *d
 !!result
-<ul><li>a
-</li><li>b
-</li><li>c
-</li><li>d
-</li></ul>
+<ul>
+<li>a
+</li>
+<li>b
+</li>
+<li>c
+</li>
+<li>d
+</li>
+</ul>
 
 !!end
 
@@ -5857,11 +6299,16 @@ Replacing whitespace with tabs still doesn't break the list (gerrit 78327)
         either-->       
 *d
 !!result
-<ul><li>a
-</li><li>b
-</li><li>c
-</li><li>d
-</li></ul>
+<ul>
+<li>a
+</li>
+<li>b
+</li>
+<li>c
+</li>
+<li>d
+</li>
+</ul>
 
 !!end
 
@@ -5881,13 +6328,17 @@ parsoid=wt2html,wt2wt
 </li>
 </ul>
 !!result
-<ul><li> foo</li>
+<ul>
+<li> foo</li>
 <li>li-hack</li>
 <li about="#mwt1" typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"<li>templated li-hack"}}}}]}'>templated li-hack</li>
-<li> <!--foo--> </li><li> li-hack with preceding comments</li></ul>
+<li> <!--foo--> </li>
+<li> li-hack with preceding comments</li>
+</ul>
 
 <ul>
-<li></li><li>not a li-hack
+<li></li>
+<li>not a li-hack
 </li>
 </ul>
 !!end
@@ -5904,7 +6355,19 @@ parsoid
 : foo
 :: bar
 !! result
-<ol><li> foo<ol><li> bar</li></ol></li></ol><ul><li> foo<ul><li> bar</li></ul></li></ul><dl><dd> foo<dl><dd> bar</dd></dl></dd></dl>
+<ol>
+<li> foo<ol>
+<li> bar</li>
+</ol></li>
+</ol><ul>
+<li> foo<ul>
+<li> bar</li>
+</ul></li>
+</ul><dl>
+<dd> foo<dl>
+<dd> bar</dd>
+</dl></dd>
+</dl>
 !! end
 
 !! test
@@ -5935,10 +6398,14 @@ parsoid
 *b</div>
 !! result
 <div>
-<ul><li>a
-</li></ul></div><div>
-<ul><li>b
-</li></ul></div>
+<ul>
+<li>a
+</li>
+</ul></div><div>
+<ul>
+<li>b
+</li>
+</ul></div>
 !! end
 
 !! test
@@ -5953,9 +6420,12 @@ parsoid
 !! result
 <p><span></span>
 </p>
-<ul><li>a<span></span>
-</li><li>b
-</li></ul>
+<ul>
+<li>a<span></span>
+</li>
+<li>b
+</li>
+</ul>
 !! end
 
 !! test
@@ -5967,9 +6437,12 @@ parsoid
 # <s> a
 # b </s>
 !! result
-<ol><li> <s> a </s>
-</li><li> <s> b </s>
-</li></ol>
+<ol>
+<li> <s> a </s>
+</li>
+<li> <s> b </s>
+</li>
+</ol>
 !! end
 
 !!test
@@ -6161,21 +6634,36 @@ Magic Words LOCAL (UTC)
 * {{LOCALDOW}}
 * {{LOCALTIMESTAMP}}
 !! result
-<ul><li> 01
-</li><li> 1
-</li><li> January
-</li><li> January
-</li><li> Jan
-</li><li> 1
-</li><li> 01
-</li><li> Thursday
-</li><li> 1970
-</li><li> 00:02
-</li><li> 00
-</li><li> 1
-</li><li> 4
-</li><li> 19700101000203
-</li></ul>
+<ul>
+<li> 01
+</li>
+<li> 1
+</li>
+<li> January
+</li>
+<li> January
+</li>
+<li> Jan
+</li>
+<li> 1
+</li>
+<li> 01
+</li>
+<li> Thursday
+</li>
+<li> 1970
+</li>
+<li> 00:02
+</li>
+<li> 00
+</li>
+<li> 1
+</li>
+<li> 4
+</li>
+<li> 19700101000203
+</li>
+</ul>
 
 !! end
 
@@ -7863,9 +8351,9 @@ parsoid=wt2html,wt2wt
 <!--Two categories (Bug 50330)-->
 <table>[[Category:bar1]][[Category:bar2]]<tr><td>foo</td></tr></table>
 !!result
-<link rel="mw:WikiLink/Category" href="./Category:Foo1"><table><tbody><tr><td>foo</td></tr></tbody></table>
+<link rel="mw:PageProp/Category" href="./Category:Foo1"><table><tbody><tr><td>foo</td></tr></tbody></table>
 <!--Two categories (Bug 50330)-->
-<link rel="mw:WikiLink/Category" href="./Category:Bar1"><link rel="mw:WikiLink/Category" href="./Category:Bar2"><table><tbody><tr><td>foo</td></tr></tbody></table>
+<link rel="mw:PageProp/Category" href="./Category:Bar1"><link rel="mw:PageProp/Category" href="./Category:Bar2"><table><tbody><tr><td>foo</td></tr></tbody></table>
 !!end
 
 !!test
@@ -7975,9 +8463,12 @@ unused}}}}
 *{{echo|b {{nonexistent|
 unused}}}}
 !!result
-<ul><li>a <a href="/index.php?title=Template:Nonexistent&amp;action=edit&amp;redlink=1" class="new" title="Template:Nonexistent (page does not exist)">Template:Nonexistent</a>
-</li><li>b <a href="/index.php?title=Template:Nonexistent&amp;action=edit&amp;redlink=1" class="new" title="Template:Nonexistent (page does not exist)">Template:Nonexistent</a>
-</li></ul>
+<ul>
+<li>a <a href="/index.php?title=Template:Nonexistent&amp;action=edit&amp;redlink=1" class="new" title="Template:Nonexistent (page does not exist)">Template:Nonexistent</a>
+</li>
+<li>b <a href="/index.php?title=Template:Nonexistent&amp;action=edit&amp;redlink=1" class="new" title="Template:Nonexistent (page does not exist)">Template:Nonexistent</a>
+</li>
+</ul>
 
 !!end
 
@@ -9716,6 +10207,30 @@ parsoid
 #</span>
 #</p>
 
+!! test
+Caption with a template in it
+!! options
+parsoid
+!! input
+[[Image:Foobar.jpg|thumb|200px|This caption has a {{echo|transclusion}} in it.]]
+!! result
+<figure typeof="mw:Image/Thumb"><a href="./File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/200px-Foobar.jpg" height="23" width="200"></a><figcaption>This caption has a <span about="#mwt1" typeof="mw:Transclusion" data-mw="{&quot;parts&quot;:[{&quot;template&quot;:{&quot;target&quot;:{&quot;wt&quot;:&quot;echo&quot;,&quot;href&quot;:&quot;./Template:Echo&quot;},&quot;params&quot;:{&quot;1&quot;:{&quot;wt&quot;:&quot;transclusion&quot;}},&quot;i&quot;:0}}]}">transclusion</span> in it.</figcaption></figure>
+!! end
+
+!! test
+Caption with unbalanced tags in it
+!! options
+parsoid
+!! input
+foo
+[[Image:Foobar.jpg|thumb|200px|This caption has a <center>unbalanced tag in it.]]
+bar
+!! result
+<p>foo</p>
+<figure typeof="mw:Image/Thumb"><a href="File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" height="23" width="200"></a><figcaption>This caption has a <center>unbalanced tag in it.</center></figcaption></figure>
+<p>bar</p>
+!! end
+
 
 ###
 ### Subpages
@@ -9986,7 +10501,7 @@ language=is
 !! input
 x[[Category:Foo]]y
 !! result
-<p>x<link rel="mw:WikiLink/Category" href="Category:Foo">y</p>
+<p>x<link rel="mw:PageProp/Category" href="Category:Foo">y</p>
 !! end
 
 !! test
@@ -10012,8 +10527,8 @@ parsoid
 [[Category:Foo]]
 [[Category:Foo|Bar]]
 !! result
-<link rel="mw:WikiLink/Category" href="Category:Foo">
-<link rel="mw:WikiLink/Category" href="Category:Foo#Bar">
+<link rel="mw:PageProp/Category" href="Category:Foo">
+<link rel="mw:PageProp/Category" href="Category:Foo#Bar">
 !! end
 
 ###
@@ -10103,6 +10618,7 @@ Some text
 </li>
 </ul>
 </div>
+
 <h2><span class="mw-headline" id="Headline_1">Headline 1</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&amp;action=edit&amp;section=1" title="Edit section: Headline 1">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
 <h3><span class="mw-headline" id="Subheadline_1">Subheadline 1</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&amp;action=edit&amp;section=2" title="Edit section: Subheadline 1">edit</a><span class="mw-editsection-bracket">]</span></span></h3>
 <h5><span class="mw-headline" id="Skipping_a_level">Skipping a level</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&amp;action=edit&amp;section=3" title="Edit section: Skipping a level">edit</a><span class="mw-editsection-bracket">]</span></span></h5>
@@ -10158,6 +10674,7 @@ Handling of sections up to level 6 and beyond
 </li>
 </ul>
 </div>
+
 <h1><span class="mw-headline" id="Level_1_Heading">Level 1 Heading</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&amp;action=edit&amp;section=1" title="Edit section: Level 1 Heading">edit</a><span class="mw-editsection-bracket">]</span></span></h1>
 <h2><span class="mw-headline" id="Level_2_Heading">Level 2 Heading</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&amp;action=edit&amp;section=2" title="Edit section: Level 2 Heading">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
 <h3><span class="mw-headline" id="Level_3_Heading">Level 3 Heading</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&amp;action=edit&amp;section=3" title="Edit section: Level 3 Heading">edit</a><span class="mw-editsection-bracket">]</span></span></h3>
@@ -10200,6 +10717,7 @@ TOC regression (bug 9764)
 </li>
 </ul>
 </div>
+
 <h2><span class="mw-headline" id="title_1">title 1</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&amp;action=edit&amp;section=1" title="Edit section: title 1">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
 <h3><span class="mw-headline" id="title_1.1">title 1.1</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&amp;action=edit&amp;section=2" title="Edit section: title 1.1">edit</a><span class="mw-editsection-bracket">]</span></span></h3>
 <h4><span class="mw-headline" id="title_1.1.1">title 1.1.1</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&amp;action=edit&amp;section=3" title="Edit section: title 1.1.1">edit</a><span class="mw-editsection-bracket">]</span></span></h4>
@@ -10236,6 +10754,7 @@ wgMaxTocLevel=3
 </li>
 </ul>
 </div>
+
 <h2><span class="mw-headline" id="title_1">title 1</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&amp;action=edit&amp;section=1" title="Edit section: title 1">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
 <h3><span class="mw-headline" id="title_1.1">title 1.1</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&amp;action=edit&amp;section=2" title="Edit section: title 1.1">edit</a><span class="mw-editsection-bracket">]</span></span></h3>
 <h4><span class="mw-headline" id="title_1.1.1">title 1.1.1</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&amp;action=edit&amp;section=3" title="Edit section: title 1.1.1">edit</a><span class="mw-editsection-bracket">]</span></span></h4>
@@ -10266,6 +10785,7 @@ wgMaxTocLevel=3
 <li class="toclevel-1 tocsection-5"><a href="#Section_2"><span class="tocnumber">2</span> <span class="toctext">Section 2</span></a></li>
 </ul>
 </div>
+
 <h2><span class="mw-headline" id="Section_1">Section 1</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&amp;action=edit&amp;section=1" title="Edit section: Section 1">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
 <h3><span class="mw-headline" id="Section_1.1">Section 1.1</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&amp;action=edit&amp;section=2" title="Edit section: Section 1.1">edit</a><span class="mw-editsection-bracket">]</span></span></h3>
 <h4><span class="mw-headline" id="Section_1.1.1">Section 1.1.1</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&amp;action=edit&amp;section=3" title="Edit section: Section 1.1.1">edit</a><span class="mw-editsection-bracket">]</span></span></h4>
@@ -10358,6 +10878,7 @@ __TOC__
 <li class="toclevel-1 tocsection-3"><a href="#title_2"><span class="tocnumber">2</span> <span class="toctext">title 2</span></a></li>
 </ul>
 </div>
+
 <h2><span class="mw-headline" id="title_1">title 1</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&amp;action=edit&amp;section=1" title="Edit section: title 1">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
 <h3><span class="mw-headline" id="title_1.1">title 1.1</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&amp;action=edit&amp;section=2" title="Edit section: title 1.1">edit</a><span class="mw-editsection-bracket">]</span></span></h3>
 <h2><span class="mw-headline" id="title_2">title 2</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&amp;action=edit&amp;section=3" title="Edit section: title 2">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
@@ -10421,6 +10942,7 @@ section 5
 <li class="toclevel-1 tocsection-5"><a href="#text_.22_text"><span class="tocnumber">5</span> <span class="toctext">text " text</span></a></li>
 </ul>
 </div>
+
 <h2><span class="mw-headline" id="text_.3E_text">text &gt; text</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&amp;action=edit&amp;section=1" title="Edit section: text > text">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
 <p>section 1
 </p>
@@ -10455,6 +10977,7 @@ Headers with excess '=' characters
 <li class="toclevel-1 tocsection-4"><a href="#.3Ditalic_heading"><span class="tocnumber">4</span> <span class="toctext">=<i>italic</i> heading</span></a></li>
 </ul>
 </div>
+
 <h1><span class="mw-headline" id="foo.3D">foo=</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&amp;action=edit&amp;section=1" title="Edit section: foo=">edit</a><span class="mw-editsection-bracket">]</span></span></h1>
 <h1><span class="mw-headline" id=".3Dfoo">=foo</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&amp;action=edit&amp;section=2" title="Edit section: =foo">edit</a><span class="mw-editsection-bracket">]</span></span></h1>
 <h1><span class="mw-headline" id="italic_heading.3D"><i>italic</i> heading=</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&amp;action=edit&amp;section=3" title="Edit section: italic heading=">edit</a><span class="mw-editsection-bracket">]</span></span></h1>
@@ -10492,6 +11015,7 @@ __NOEDITSECTION__
 </li>
 </ul>
 </div>
+
 <h1><span class="mw-headline" id="Header_1">Header 1</span></h1>
 <h2><span class="mw-headline" id="Header_1.1">Header 1.1</span></h2>
 <h2><span class="mw-headline" id="Header_1.2">Header 1.2</span></h2>
@@ -11665,8 +12189,10 @@ disabled
 !! result
 <ul>
 <li>One
-</li><li>Two
-</li></ul>
+</li>
+<li>Two
+</li>
+</ul>
 
 !! end
 
@@ -11697,8 +12223,10 @@ disabled
 !! result
 <ol>
 <li>One
-</li><li>Two
-</li></ol>
+</li>
+<li>Two
+</li>
+</ol>
 
 !! end
 
@@ -11743,12 +12271,16 @@ disabled
 !! result
 <ul>
 <li>One
-</li><li>Two:
+</li>
+<li>Two:
 <ul>
 <li>Sub-one
-</li><li>Sub-two
-</li></ul>
-</li></ul>
+</li>
+<li>Sub-two
+</li>
+</ul>
+</li>
+</ul>
 
 !! end
 
@@ -11793,21 +12325,27 @@ disabled
 !! result
 <ol>
 <li>One
-</li><li>Two:
+</li>
+<li>Two:
 <ol>
 <li>Sub-one
-</li><li>Sub-two
-</li></ol>
-</li></ol>
+</li>
+<li>Sub-two
+</li>
+</ol>
+</li>
+</ol>
 
 !! end
 
 !! test
 HTML ordered list item with parameters oddity
 !! input
-<ol><li id="fragment">One</li></ol>
+<ol><li id="fragment">One</li>
+</ol>
 !! result
-<ol><li id="fragment">One</li></ol>
+<ol><li id="fragment">One</li>
+</ol>
 
 !! end
 
@@ -11866,6 +12404,7 @@ http://<div id="toc" class="toc"><div id="toctitle"><h2>Contents</h2></div>
 </ul>
 </div>
 
+
 !! end
 
 !! test
@@ -13031,9 +13570,13 @@ Handling of &#x0A; in URLs
 !! input
 **irc://&#x0A;a
 !! result
-<ul><li><ul><li><a rel="nofollow" class="external free" href="irc://%0Aa">irc://%0Aa</a>
-</li></ul>
-</li></ul>
+<ul>
+<li><ul>
+<li><a rel="nofollow" class="external free" href="irc://%0Aa">irc://%0Aa</a>
+</li>
+</ul>
+</li>
+</ul>
 
 !!end
 
@@ -13093,29 +13636,52 @@ title=[[Parser test]]
 * {{SUBJECTSPACEE}}
 * {{Dynamic|{{NUMBEROFUSERS}}|{{NUMBEROFPAGES}}|{{CURRENTVERSION}}|{{CONTENTLANGUAGE}}|{{DIRECTIONMARK}}|{{CURRENTTIMESTAMP}}|{{NUMBEROFARTICLES}}}}
 !! result
-<ul><li> Parser test
-</li><li> Parser_test
-</li><li> Parser test
-</li><li> Parser_test
-</li><li> Parser test
-</li><li> Parser_test
-</li><li> Parser test
-</li><li> Parser_test
-</li><li> Parser test
-</li><li> Parser_test
-</li><li> Talk:Parser test
-</li><li> Talk:Parser_test
-</li><li> Parser test
-</li><li> Parser_test
-</li><li> 
-</li><li> 
-</li><li> 0
-</li><li> Talk
-</li><li> Talk
-</li><li> 
-</li><li> 
-</li><li> <a href="/index.php?title=Template:Dynamic&amp;action=edit&amp;redlink=1" class="new" title="Template:Dynamic (page does not exist)">Template:Dynamic</a>
-</li></ul>
+<ul>
+<li> Parser test
+</li>
+<li> Parser_test
+</li>
+<li> Parser test
+</li>
+<li> Parser_test
+</li>
+<li> Parser test
+</li>
+<li> Parser_test
+</li>
+<li> Parser test
+</li>
+<li> Parser_test
+</li>
+<li> Parser test
+</li>
+<li> Parser_test
+</li>
+<li> Talk:Parser test
+</li>
+<li> Talk:Parser_test
+</li>
+<li> Parser test
+</li>
+<li> Parser_test
+</li>
+<li> 
+</li>
+<li> 
+</li>
+<li> 0
+</li>
+<li> Talk
+</li>
+<li> Talk
+</li>
+<li> 
+</li>
+<li> 
+</li>
+<li> <a href="/index.php?title=Template:Dynamic&amp;action=edit&amp;redlink=1" class="new" title="Template:Dynamic (page does not exist)">Template:Dynamic</a>
+</li>
+</ul>
 
 !! end
 ### Note: Above tests excludes the "{{NUMBEROFADMINS}}" magic word because it generates a MySQL error when included.
@@ -13503,13 +14069,25 @@ disabled
 !! input
 :;;;::
 !! result
-<dl><dd><dl><dt><dl><dt><dl><dt><dl><dd><dl><dd>
-</dd></dl>
-</dd></dl>
-</dt></dl>
-</dt></dl>
-</dt></dl>
-</dd></dl>
+<dl>
+<dd><dl>
+<dt><dl>
+<dt><dl>
+<dt><dl>
+<dd><dl>
+<dd>
+</dd>
+</dl>
+</dd>
+</dl>
+</dt>
+</dl>
+</dt>
+</dl>
+</dt>
+</dl>
+</dd>
+</dl>
 
 !!end
 
@@ -13651,10 +14229,17 @@ Definition list code coverage
 ; title : def
 ;title: def
 !! result
-<dl><dt> title  &#160;</dt><dd> def
-</dd><dt> title&#160;</dt><dd> def
-</dd><dt>title</dt><dd> def
-</dd></dl>
+<dl>
+<dt> title  &#160;</dt>
+<dd> def
+</dd>
+<dt> title&#160;</dt>
+<dd> def
+</dd>
+<dt>title</dt>
+<dd> def
+</dd>
+</dl>
 
 !! end
 
@@ -13749,6 +14334,7 @@ Out-of-order TOC heading levels
 </li>
 </ul>
 </div>
+
 <h2><span class="mw-headline" id="2">2</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&amp;action=edit&amp;section=1" title="Edit section: 2">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
 <h6><span class="mw-headline" id="6">6</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&amp;action=edit&amp;section=2" title="Edit section: 6">edit</a><span class="mw-editsection-bracket">]</span></span></h6>
 <h3><span class="mw-headline" id="3">3</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&amp;action=edit&amp;section=3" title="Edit section: 3">edit</a><span class="mw-editsection-bracket">]</span></span></h3>
@@ -13961,6 +14547,17 @@ title=[[Duna]] language=sr
 </p>
 !! end
 
+!! test
+Link to a section of a variant of this title shouldn't be parsed as self-link
+!! options
+title=[[Duna]] language=sr
+!! input
+[[Dуна]] is a self-link while [[Dunа#Foo]] and [[Dуна#Foo]] are not self-links.
+!! result
+<p><strong class="selflink">Dуна</strong> is a self-link while <a href="/wiki/%D0%94%D1%83%D0%BD%D0%B0" title="Дуна">Dunа#Foo</a> and <a href="/wiki/%D0%94%D1%83%D0%BD%D0%B0" title="Дуна">Dуна#Foo</a> are not self-links.
+</p>
+!! end
+
 !! test
 Link to pages in language variants
 !! options
@@ -14330,9 +14927,12 @@ Bug 529: Uncovered bullet
 !! input
 * Foo {{bullet}}
 !! result
-<ul><li> Foo 
-</li><li> Bar
-</li></ul>
+<ul>
+<li> Foo 
+</li>
+<li> Bar
+</li>
+</ul>
 
 !! end
 
@@ -14347,15 +14947,30 @@ Bug 529: Uncovered bullet leaving empty list, normally removed by tidy
 !! input
 ******* Foo {{bullet}}
 !! result
-<ul><li><ul><li><ul><li><ul><li><ul><li><ul><li><ul><li> Foo 
-</li></ul>
-</li></ul>
-</li></ul>
-</li></ul>
-</li></ul>
-</li></ul>
-</li><li> Bar
-</li></ul>
+<ul>
+<li><ul>
+<li><ul>
+<li><ul>
+<li><ul>
+<li><ul>
+<li><ul>
+<li> Foo 
+</li>
+</ul>
+</li>
+</ul>
+</li>
+</ul>
+</li>
+</ul>
+</li>
+</ul>
+</li>
+</ul>
+</li>
+<li> Bar
+</li>
+</ul>
 
 !! end
 
@@ -14387,9 +15002,12 @@ Bug 529: Uncovered bullet in parser function result
 !! input
 * Foo {{lc:{{bullet}} }}
 !! result
-<ul><li> Foo 
-</li><li> bar
-</li></ul>
+<ul>
+<li> Foo 
+</li>
+<li> bar
+</li>
+</ul>
 
 !! end
 
@@ -15315,6 +15933,7 @@ __TOC__
 <li class="toclevel-1 tocsection-1"><a href="#Lost_episodes"><span class="tocnumber">1</span> <span class="toctext"><i>Lost</i> episodes</span></a></li>
 </ul>
 </div>
+
 <h2><span class="mw-headline" id="Lost_episodes"><i>Lost</i> episodes</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Main_Page&amp;action=edit&amp;section=1" title="Edit section: Lost episodes">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
 
 !! end
@@ -15332,6 +15951,7 @@ __TOC__
 <li class="toclevel-1 tocsection-1"><a href="#should_be_bold_then_normal_text"><span class="tocnumber">1</span> <span class="toctext"><b>should be bold</b> then normal text</span></a></li>
 </ul>
 </div>
+
 <h2><span class="mw-headline" id="should_be_bold_then_normal_text"><b>should be bold</b> then normal text</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Main_Page&amp;action=edit&amp;section=1" title="Edit section: should be bold then normal text">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
 
 !! end
@@ -15349,6 +15969,7 @@ __TOC__
 <li class="toclevel-1 tocsection-1"><a href="#Image"><span class="tocnumber">1</span> <span class="toctext">Image</span></a></li>
 </ul>
 </div>
+
 <h2><span class="mw-headline" id="Image">Image <a href="/wiki/File:Foobar.jpg" class="image"><img alt="Foobar.jpg" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a></span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Main_Page&amp;action=edit&amp;section=1" title="Edit section: Image">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
 
 !! end
@@ -15366,6 +15987,7 @@ __TOC__
 <li class="toclevel-1 tocsection-1"><a href="#Quote"><span class="tocnumber">1</span> <span class="toctext">Quote</span></a></li>
 </ul>
 </div>
+
 <h2><span class="mw-headline" id="Quote"><blockquote>Quote</blockquote></span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Main_Page&amp;action=edit&amp;section=1" title="Edit section: Quote">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
 
 !! end
@@ -15385,6 +16007,7 @@ QED
 <li class="toclevel-1 tocsection-1"><a href="#Proof:_2_.3C_3"><span class="tocnumber">1</span> <span class="toctext">Proof: 2 &lt; 3</span></a></li>
 </ul>
 </div>
+
 <h2><span class="mw-headline" id="Proof:_2_.3C_3">Proof: 2 &lt; 3</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Main_Page&amp;action=edit&amp;section=1" title="Edit section: Proof: 2 &lt; 3">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
 <p><small>Hanc marginis exiguitas non caperet.</small>
 QED
@@ -15405,6 +16028,7 @@ __TOC__
 <li class="toclevel-1 tocsection-2"><a href="#Foo_Bar_2"><span class="tocnumber">2</span> <span class="toctext"><i>Foo</i> Bar</span></a></li>
 </ul>
 </div>
+
 <h2><span class="mw-headline" id="Foo_Bar"><i>Foo</i> <b>Bar</b></span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&amp;action=edit&amp;section=1" title="Edit section: Foo Bar">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
 <h2><span class="mw-headline" id="Foo_Bar_2"><i>Foo</i> <blockquote>Bar</blockquote></span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&amp;action=edit&amp;section=2" title="Edit section: Foo Bar">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
 
@@ -15424,6 +16048,7 @@ __TOC__
 <li class="toclevel-1 tocsection-2"><a href="#b.22.3EEvilbye"><span class="tocnumber">2</span> <span class="toctext"><sup> b"&gt;Evilbye</sup></span></a></li>
 </ul>
 </div>
+
 <h2><span class="mw-headline" id="Hello"><sup class="in-h2">Hello</sup></span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&amp;action=edit&amp;section=1" title="Edit section: Hello">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
 <h2><span class="mw-headline" id="b.22.3EEvilbye"><sup> b"&gt;Evilbye</sup></span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&amp;action=edit&amp;section=2" title="Edit section: b&quot;>Evilbye">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
 
@@ -15452,6 +16077,7 @@ __TOC__
 <li class="toclevel-1 tocsection-5"><a href="#Attributes_after_dir_on_these_span_tags_must_be_deleted_from_the_TOC"><span class="tocnumber">5</span> <span class="toctext"><span dir="ltr">Attributes after dir on these span tags must be deleted from the TOC</span></span></a></li>
 </ul>
 </div>
+
 <h2><span class="mw-headline" id="C.2B.2B"><span dir="ltr">C++</span></span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&amp;action=edit&amp;section=1" title="Edit section: C++">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
 <h2><span class="mw-headline" id=".D7.96.D7.91.D7.A0.D7.92.21"><span dir="rtl">זבנג!</span></span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&amp;action=edit&amp;section=2" title="Edit section: זבנג!">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
 <h2><span class="mw-headline" id="The_attributes_on_these_span_tags_must_be_deleted_from_the_TOC"><span style="font-style: italic">The attributes on these span tags must be deleted from the TOC</span></span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&amp;action=edit&amp;section=3" title="Edit section: The attributes on these span tags must be deleted from the TOC">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
@@ -15741,6 +16367,29 @@ HttP://MediaWiki.Org/
 </p>
 !! end
 
+!!test
+Disable TOC
+!! options
+notoc
+!! input
+Lead
+== Section 1 ==
+== Section 2 ==
+== Section 3 ==
+== Section 4 ==
+== Section 5 ==
+!! result
+<p>Lead
+</p>
+
+<h2><span class="mw-headline" id="Section_1">Section 1</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&amp;action=edit&amp;section=1" title="Edit section: Section 1">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
+<h2><span class="mw-headline" id="Section_2">Section 2</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&amp;action=edit&amp;section=2" title="Edit section: Section 2">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
+<h2><span class="mw-headline" id="Section_3">Section 3</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&amp;action=edit&amp;section=3" title="Edit section: Section 3">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
+<h2><span class="mw-headline" id="Section_4">Section 4</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&amp;action=edit&amp;section=4" title="Edit section: Section 4">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
+<h2><span class="mw-headline" id="Section_5">Section 5</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&amp;action=edit&amp;section=5" title="Edit section: Section 5">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
+
+!! end
+
 
 ###
 ### Parsoids-specific tests
@@ -15756,11 +16405,14 @@ parsoid=wt2html,wt2wt
 {{echo|:a}}
 !!result
 <span about="#mwt1" typeof="mw:Transclusion">
-</span><ul about="#mwt1"><li>a</li></ul>
+</span><ul about="#mwt1"><li>a</li>
+</ul>
 <span about="#mwt2" typeof="mw:Transclusion">
-</span><ol about="#mwt2"><li>a</li></ol>
+</span><ol about="#mwt2"><li>a</li>
+</ol>
 <span about="#mwt3" typeof="mw:Transclusion">
-</span><dl about="#mwt3"><dd>a</dd></dl>
+</span><dl about="#mwt3"><dd>a</dd>
+</dl>
 !!end
 
 #### ----------------------------------------------------------------
@@ -15831,11 +16483,12 @@ A <ref>
 
 <references />
 !!result
-<p>A <span about="#mwt1" class="reference" data-mw='{"name":"ref","body":{"html":"This is a <b data-parsoid=\"{&amp;quot;dsr&amp;quot;:[19,40,3,3]}\"><a rel=\"mw:WikiLink\" href=\"./Bolded_link\" data-parsoid=\"{&amp;quot;stx&amp;quot;:&amp;quot;simple&amp;quot;,&amp;quot;a&amp;quot;:{&amp;quot;href&amp;quot;:&amp;quot;./Bolded_link&amp;quot;},&amp;quot;sa&amp;quot;:{&amp;quot;href&amp;quot;:&amp;quot;bolded link&amp;quot;},&amp;quot;dsr&amp;quot;:[22,37,2,2]}\">bolded link</a></b> and this is a <span about=\"#mwt5\" typeof=\"mw:Transclusion\" data-mw=\"{&amp;quot;parts&amp;quot;:[{&amp;quot;template&amp;quot;:{&amp;quot;target&amp;quot;:{&amp;quot;wt&amp;quot;:&amp;quot;echo&amp;quot;,&amp;quot;href&amp;quot;:&amp;quot;./Template:Echo&amp;quot;},&amp;quot;params&amp;quot;:{&amp;quot;1&amp;quot;:{&amp;quot;wt&amp;quot;:&amp;quot;transclusion&amp;quot;}},&amp;quot;i&amp;quot;:0}}]}\" data-parsoid=\"{&amp;quot;dsr&amp;quot;:[55,76,null,null],&amp;quot;pi&amp;quot;:[[{&amp;quot;k&amp;quot;:&amp;quot;1&amp;quot;,&amp;quot;spc&amp;quot;:[&amp;quot;&amp;quot;,&amp;quot;&amp;quot;,&amp;quot;&amp;quot;,&amp;quot;&amp;quot;]}]]}\">transclusion</span>\n"},"attrs":{}}' id="cite_ref-1-0" rel="dc:references" typeof="mw:Extension/ref"><a href="#cite_note-1">[1]</a></span></p>
+<p>A <span about="#mwt2" class="reference" data-mw='{"name":"ref","body":{"html":"This is a &lt;b data-parsoid=&#39;{\"dsr\":[19,40,3,3]}&#39;>&lt;a rel=\"mw:WikiLink\" href=\"./Bolded_link\" data-parsoid=&#39;{\"stx\":\"simple\",\"a\":{\"href\":\"./Bolded_link\"},\"sa\":{\"href\":\"bolded link\"},\"dsr\":[22,37,2,2]}&#39;>bolded link&lt;/a>&lt;/b> and this is a &lt;span about=\"#mwt5\" typeof=\"mw:Transclusion\" data-mw=&#39;{\"parts\":[{\"template\":{\"target\":{\"wt\":\"echo\",\"href\":\"./Template:Echo\"},\"params\":{\"1\":{\"wt\":\"transclusion\"}},\"i\":0}}]}&#39; data-parsoid=&#39;{\"dsr\":[55,76,null,null],\"pi\":[[{\"k\":\"1\",\"spc\":[\"\",\"\",\"\",\"\"]}]]}&#39;>transclusion&lt;/span>\n"},"attrs":{}}' id="cite_ref-1-0" rel="dc:references" typeof="mw:Extension/ref"><a href="#cite_note-1">[1]</a></span></p>
 
-<ol class="references" typeof="mw:Extension/references" about="#mwt2" data-mw='{"name":"references","attrs":{}}'>
-<li about="#cite_note-1" id="cite_note-1"><span rel="mw:referencedBy"><a href="#cite_ref-1-0">↑</a></span> This is a <b><a rel="mw:WikiLink" href="./Bolded_link">bolded link</a></b> and this is a <span about="#mwt3" typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"transclusion"}},"i":0}}]}'>transclusion</span>
-</li></ol>
+<ol class="references" typeof="mw:Extension/references" about="#mwt4" data-mw='{"name":"references","attrs":{}}'>
+<li about="#cite_note-1" id="cite_note-1"><span rel="mw:referencedBy"><a href="#cite_ref-1-0">↑</a></span> This is a <b><a rel="mw:WikiLink" href="./Bolded_link">bolded link</a></b> and this is a <span about="#mwt5" typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"transclusion"}},"i":0}}]}'>transclusion</span>
+</li>
+</ol>
 !!end
 
 !!test
@@ -15857,7 +16510,8 @@ A <ref>
 <li about="#cite_note-1" id="cite_note-1"><span rel="mw:referencedBy"><a href="#cite_ref-1-0">↑</a></span> foo
  bar
  baz
-</li></ol>
+</li>
+</ol>
 !!end
 
 !!test
@@ -15893,7 +16547,8 @@ baz
 
 
 booz
-</li></ol>
+</li>
+</ol>
 !!end
 
 !!test
@@ -15905,10 +16560,10 @@ A <ref> foo {{echo|</ref> B C}}
 
 <references />
 !!result
-<p>A <span about="#mwt1" class="reference" data-mw='{"name":"ref","body":{"html":"foo <span typeof=\"mw:Nowiki\" data-parsoid=\"{&amp;quot;src&amp;quot;:&amp;quot;{{&amp;quot;,&amp;quot;dsr&amp;quot;:[12,14,0,0]}\">{{</span>echo|"},"attrs":{}}' id="cite_ref-1-0" rel="dc:references" typeof="mw:Extension/ref"><a href="#cite_note-1">[1]</a></span> B C<span typeof="mw:Nowiki">}}</span></p>
-
-<ol about="#mwt2" class="references" typeof="mw:Extension/references" data-mw='{"name":"references","attrs":{}}'>
-<li about="#cite_note-1" id="cite_note-1"><span rel="mw:referencedBy"><a href="#cite_ref-1-0">↑</a></span> foo <span typeof="mw:Nowiki">{{</span>echo|</li></ol>
+<p>A <span class="reference" data-mw="{&quot;name&quot;:&quot;ref&quot;,&quot;body&quot;:{&quot;html&quot;:&quot;foo <span typeof=\&quot;mw:Nowiki\&quot; data-parsoid='{\&quot;src\&quot;:\&quot;{{\&quot;,\&quot;dsr\&quot;:[12,14,0,0]}'>{{</span>echo|&quot;},&quot;attrs&quot;:{}}" id="cite_ref-1-0" rel="dc:references" typeof="mw:Extension/ref"><a href="#cite_note-1">[1]</a></span> B C<span typeof="mw:Nowiki">}}</span></p>
+<ol class="references" typeof="mw:Extension/references" data-mw="{&quot;name&quot;:&quot;references&quot;,&quot;attrs&quot;:{}}">
+<li id="cite_note-1"><span rel="mw:referencedBy"><a href="#cite_ref-1-0">↑</a></span> foo <span typeof="mw:Nowiki">{{</span>echo|</li>
+</ol>
 !!end
 
 !!test
@@ -15917,13 +16572,12 @@ Ref: 9. unclosed comments should not leak out of ref-body
 parsoid
 !!input
 A <ref> foo <!--</ref> B C
-
 <references />
 !!result
-<p>A <span about="#mwt1" class="reference" data-mw='{"name":"ref","body":{"html":"foo <!---->"},"attrs":{}}' id="cite_ref-1-0" rel="dc:references" typeof="mw:Extension/ref"><a href="#cite_note-1">[1]</a></span> B C</p>
-
-<ol about="#mwt2" class="references" typeof="mw:Extension/references" data-mw='{"name":"references","attrs":{}}'>
-<li about="#cite_note-1" id="cite_note-1"><span rel="mw:referencedBy"><a href="#cite_ref-1-0">↑</a></span> foo <!----></li></ol>
+<p>A <span class="reference" data-mw='{"name":"ref","body":{"html":"foo &lt;!---->"},"attrs":{}}' id="cite_ref-1-0" rel="dc:references" typeof="mw:Extension/ref"><a href="#cite_note-1">[1]</a></span> B C</p>
+<ol class="references" typeof="mw:Extension/references" data-mw='{"name":"references","attrs":{}}'>
+<li id="cite_note-1"><span rel="mw:referencedBy"><a href="#cite_ref-1-0">↑</a></span> foo </li>
+</ol>
 !!end
 
 !!test
@@ -15935,10 +16589,12 @@ A <ref> <b> foo </ref> B C
 
 <references />
 !!result
-<p>A <span about="#mwt1" class="reference" data-mw='{"name":"ref","body":{"html":"<b data-parsoid=\"{&amp;quot;stx&amp;quot;:&amp;quot;html&amp;quot;,&amp;quot;autoInsertedEnd&amp;quot;:true,&amp;quot;dsr&amp;quot;:[8,16,3,0]}\"> foo </b>"},"attrs":{}}' id="cite_ref-1-0" rel="dc:references" typeof="mw:Extension/ref"><a href="#cite_note-1">[1]</a></span> B C</p>
+<p data-parsoid='{"dsr":[0,26,0,0]}'>A <span about="#mwt2" class="reference" data-mw='{"name":"ref","body":{"html":"&lt;b data-parsoid=&#39;{\"stx\":\"html\",\"autoInsertedEnd\":true,\"dsr\":[8,16,3,0]}&#39;> foo &lt;/b>"},"attrs":{}}' id="cite_ref-1-0" rel="dc:references" typeof="mw:Extension/ref" data-parsoid='{"src":"&lt;ref> &lt;b> foo &lt;/ref>","dsr":[2,22,5,6]}'><a href="#cite_note-1">[1]</a></span> B C</p>
 
-<ol about="#mwt2" class="references" typeof="mw:Extension/references" data-mw='{"name":"references","attrs":{}}'>
-<li about="#cite_note-1" id="cite_note-1"><span rel="mw:referencedBy"><a href="#cite_ref-1-0">↑</a></span> <b> foo </b></li></ol>
+
+<ol class="references" typeof="mw:Extension/references" about="#mwt4" data-parsoid='{"src":"&lt;references />","dsr":[28,42,2,2]}' data-mw='{"name":"references","attrs":{}}'>
+<li about="#cite_note-1" id="cite_note-1" data-parsoid="{}"><span rel="mw:referencedBy"><a href="#cite_ref-1-0">↑</a></span> <b data-parsoid='{"stx":"html","autoInsertedEnd":true,"dsr":[8,16,3,0]}'> foo </b></li>
+</ol>
 !!end
 
 !!test
@@ -15949,8 +16605,8 @@ parsoid
 A <ref>foo</ref> B
 C <ref>bar</ref> D
 !!result
-<p>A <span about="#mwt1" class="reference" data-mw='{"name":"ref","body":{"html":"foo"},"attrs":{}}' id="cite_ref-1-0" rel="dc:references" typeof="mw:Extension/ref"><a href="#cite_note-1">[1]</a></span> B
-C <span about="#mwt2" class="reference" data-mw='{"name":"ref","body":{"html":"bar"},"attrs":{}}' id="cite_ref-2-0" rel="dc:references" typeof="mw:Extension/ref"><a href="#cite_note-2">[2]</a></span> D</p>
+<p data-parsoid='{"dsr":[0,37,0,0]}'>A <span about="#mwt2" class="reference" data-mw='{"name":"ref","body":{"html":"foo"},"attrs":{}}' id="cite_ref-1-0" rel="dc:references" typeof="mw:Extension/ref" data-parsoid='{"src":"&lt;ref>foo&lt;/ref>","dsr":[2,16,5,6]}'><a href="#cite_note-1">[1]</a></span> B
+C <span about="#mwt4" class="reference" data-mw='{"name":"ref","body":{"html":"bar"},"attrs":{}}' id="cite_ref-2-0" rel="dc:references" typeof="mw:Extension/ref" data-parsoid='{"src":"&lt;ref>bar&lt;/ref>","dsr":[21,35,5,6]}'><a href="#cite_note-2">[2]</a></span> D</p>
 !!end
 
 !!test
@@ -15997,10 +16653,11 @@ parsoid
 
 <references />
 !!result
-<p><span about="#mwt1" class="reference" data-mw="{&quot;name&quot;:&quot;ref&quot;,&quot;body&quot;:{&quot;html&quot;:&quot;foo &amp;lt;ref&amp;gt;bar&amp;lt;/ref&amp;gt; baz&quot;},&quot;attrs&quot;:{}}" id="cite_ref-1-0" rel="dc:references" typeof="mw:Extension/ref"><a href="#cite_note-1">[1]</a></span></p>
+<p data-parsoid='{"dsr":[0,33,0,0]}'><span about="#mwt2" class="reference" data-mw='{"name":"ref","body":{"html":"foo &amp;lt;ref>bar&amp;lt;/ref> baz"},"attrs":{}}' id="cite_ref-1-0" rel="dc:references" typeof="mw:Extension/ref" data-parsoid='{"src":"&lt;ref>foo &lt;ref>bar&lt;/ref> baz&lt;/ref>","dsr":[0,33,5,6]}'><a href="#cite_note-1">[1]</a></span></p>
 
-<ol class="references" typeof="mw:Extension/references" about="#mwt2" data-mw="{&quot;name&quot;:&quot;references&quot;,&quot;attrs&quot;:{}}">
-<li about="#cite_note-1" id="cite_note-1" data-parsoid="{}"><span rel="mw:referencedBy"><a href="#cite_ref-1-0">↑</a></span> foo &lt;ref&gt;bar&lt;/ref&gt; baz</li></ol>
+<ol class="references" typeof="mw:Extension/references" about="#mwt5" data-parsoid='{"src":"&lt;references />","dsr":[35,49,2,2]}' data-mw='{"name":"references","attrs":{}}'>
+<li about="#cite_note-1" id="cite_note-1" data-parsoid="{}"><span rel="mw:referencedBy"><a href="#cite_ref-1-0">↑</a></span> foo &lt;ref>bar&lt;/ref> baz</li>
+</ol>
 !!end
 
 !!test
@@ -16016,7 +16673,8 @@ B1 <ref name="b" /> B2 <ref name="b">bar</ref>
 <p>A1 <span about="#mwt3" class="reference" data-mw='{"name":"ref","body":{"html":"foo"},"attrs":{"name":"a"}}' id="cite_ref-a-1-0" rel="dc:references" typeof="mw:Extension/ref"><a href="#cite_note-a-1">[1]</a></span> A2 <span about="#mwt4" class="reference" data-mw='{"name":"ref","attrs":{"name":"a"}}' id="cite_ref-a-1-1" rel="dc:references" typeof="mw:Extension/ref"><a href="#cite_note-a-1">[1]</a></span>
 B1 <span about="#mwt7" class="reference" data-mw='{"name":"ref","attrs":{"name":"b"}}' id="cite_ref-b-2-0" rel="dc:references" typeof="mw:Extension/ref"><a href="#cite_note-b-2">[2]</a></span> B2 <span about="#mwt8" class="reference" data-mw='{"name":"ref","body":{"html":"bar"},"attrs":{"name":"b"}}' id="cite_ref-b-2-1" rel="dc:references" typeof="mw:Extension/ref"><a href="#cite_note-b-2">[2]</a></span></p>
 
-<ol about="#mwt10" class="references" typeof="mw:Extension/references" data-mw='{"name":"references","attrs":{}}'><li about="#cite_note-a-1" id="cite_note-a-1"><span rel="mw:referencedBy">↑ <a href="#cite_ref-a-1-0">1.0</a> <a href="#cite_ref-a-1-1">1.1</a></span> foo</li><li about="#cite_note-b-2" id="cite_note-b-2"><span rel="mw:referencedBy">↑ <a href="#cite_ref-b-2-0">2.0</a> <a href="#cite_ref-b-2-1">2.1</a></span> bar</li></ol>
+<ol about="#mwt10" class="references" typeof="mw:Extension/references" data-mw='{"name":"references","attrs":{}}'><li about="#cite_note-a-1" id="cite_note-a-1"><span rel="mw:referencedBy">↑ <a href="#cite_ref-a-1-0">1.0</a> <a href="#cite_ref-a-1-1">1.1</a></span> foo</li><li about="#cite_note-b-2" id="cite_note-b-2"><span rel="mw:referencedBy">↑ <a href="#cite_ref-b-2-0">2.0</a> <a href="#cite_ref-b-2-1">2.1</a></span> bar</li>
+</ol>
 !!end
 
 !!test
@@ -16042,7 +16700,8 @@ B <ref group="b">bar</ref>
 <p>A <span about="#mwt2" class="reference" data-mw='{"name":"ref","body":{"html":"foo"},"attrs":{"group":"a"}}' id="cite_ref-1-0" rel="dc:references" typeof="mw:Extension/ref"><a href="#cite_note-1">[a 1]</a></span>
 B <span about="#mwt4" class="reference" data-mw='{"name":"ref","body":{"html":"bar"},"attrs":{"group":"b"}}' id="cite_ref-1-0" rel="dc:references" typeof="mw:Extension/ref"><a href="#cite_note-1">[b 1]</a></span></p>
 
-<ol about="#mwt6" class="references" typeof="mw:Extension/references" data-mw='{"name":"references","attrs":{"group":"a"}}'><li about="#cite_note-1" id="cite_note-1"><span rel="mw:referencedBy"><a href="#cite_ref-1-0">↑</a></span> foo</li></ol>
+<ol about="#mwt6" class="references" typeof="mw:Extension/references" data-mw='{"name":"references","attrs":{"group":"a"}}'><li about="#cite_note-1" id="cite_note-1"><span rel="mw:referencedBy"><a href="#cite_ref-1-0">↑</a></span> foo</li>
+</ol>
 !!end
 
 !!test
@@ -16060,11 +16719,13 @@ B <ref>bar</ref>
 !!result
 <p>A <span about="#mwt2" class="reference" data-mw='{"name":"ref","body":{"html":"foo"},"attrs":{}}' id="cite_ref-1-0" rel="dc:references" typeof="mw:Extension/ref"><a href="#cite_note-1">[1]</a></span></p>
 
-<ol about="#mwt4" class="references" typeof="mw:Extension/references" data-mw='{"name":"references","attrs":{}}'><li about="#cite_note-1" id="cite_note-1"><span rel="mw:referencedBy"><a href="#cite_ref-1-0">↑</a></span> foo</li></ol>
+<ol about="#mwt4" class="references" typeof="mw:Extension/references" data-mw='{"name":"references","attrs":{}}'><li about="#cite_note-1" id="cite_note-1"><span rel="mw:referencedBy"><a href="#cite_ref-1-0">↑</a></span> foo</li>
+</ol>
 
 <p>B <span about="#mwt6" class="reference" data-mw='{"name":"ref","body":{"html":"bar"},"attrs":{}}' id="cite_ref-1-0" rel="dc:references" typeof="mw:Extension/ref"><a href="#cite_note-1">[1]</a></span></p>
 
-<ol about="#mwt8" class="references" typeof="mw:Extension/references" data-mw='{"name":"references","attrs":{}}'><li about="#cite_note-1" id="cite_note-1"><span rel="mw:referencedBy"><a href="#cite_ref-1-0">↑</a></span> bar</li></ol>
+<ol about="#mwt8" class="references" typeof="mw:Extension/references" data-mw='{"name":"references","attrs":{}}'><li about="#cite_note-1" id="cite_note-1"><span rel="mw:referencedBy"><a href="#cite_ref-1-0">↑</a></span> bar</li>
+</ol>
 !!end
 
 !!test
@@ -16084,11 +16745,13 @@ C <ref>cfoo</ref>
 <p>A <span about="#mwt2" class="reference" data-mw='{"name":"ref","body":{"html":"afoo"},"attrs":{"group":"a"}}' id="cite_ref-1-0" rel="dc:references" typeof="mw:Extension/ref"><a href="#cite_note-1">[a 1]</a></span>
 B <span about="#mwt4" class="reference" data-mw='{"name":"ref","body":{"html":"bfoo"},"attrs":{}}' id="cite_ref-1-0" rel="dc:references" typeof="mw:Extension/ref" data-parsoid='{"src":"<ref>bfoo</ref>","dsr":[30,45,5,6]}'><a href="#cite_note-1">[1]</a></span></p>
 
-<ol about="#mwt6" class="references" typeof="mw:Extension/references" data-mw='{"name":"references","attrs":{"group":"a"}}'><li about="#cite_note-1" id="cite_note-1"><span rel="mw:referencedBy"><a href="#cite_ref-1-0">↑</a></span> afoo</li></ol>
+<ol about="#mwt6" class="references" typeof="mw:Extension/references" data-mw='{"name":"references","attrs":{"group":"a"}}'><li about="#cite_note-1" id="cite_note-1"><span rel="mw:referencedBy"><a href="#cite_ref-1-0">↑</a></span> afoo</li>
+</ol>
 
 <p>C <span about="#mwt8" class="reference" data-mw='{"name":"ref","body":{"html":"cfoo"},"attrs":{}}' id="cite_ref-2-0" rel="dc:references" typeof="mw:Extension/ref"><a href="#cite_note-2">[2]</a></span></p>
 
-<ol about="#mwt10" class="references" typeof="mw:Extension/references" data-mw='{"name":"references","attrs":{}}'><li about="#cite_note-1" id="cite_note-1"><span rel="mw:referencedBy"><a href="#cite_ref-1-0">↑</a></span> bfoo</li><li about="#cite_note-2" id="cite_note-2"><span rel="mw:referencedBy"><a href="#cite_ref-2-0">↑</a></span> cfoo</li></ol>
+<ol about="#mwt10" class="references" typeof="mw:Extension/references" data-mw='{"name":"references","attrs":{}}'><li about="#cite_note-1" id="cite_note-1"><span rel="mw:referencedBy"><a href="#cite_ref-1-0">↑</a></span> bfoo</li><li about="#cite_note-2" id="cite_note-2"><span rel="mw:referencedBy"><a href="#cite_ref-2-0">↑</a></span> cfoo</li>
+</ol>
 !!end
 
 !!test
@@ -16104,10 +16767,14 @@ B <ref name="b">bar</ref>
 This should just get lost.
 </references>
 !!result
-<p>A <span about="#mwt2" class="reference" data-mw='{"name":"ref","attrs":{"name":"a"}}' id="cite_ref-a-1-0" rel="dc:references" typeof="mw:Extension/ref"><a href="#cite_note-a-1">[1]</a></span>
-B <span about="#mwt4" class="reference" data-mw='{"name":"ref","body":{"html":"bar"},"attrs":{"name":"b"}}' id="cite_ref-b-2-0" rel="dc:references" typeof="mw:Extension/ref"><a href="#cite_note-b-2">[2]</a></span></p>
+<p data-parsoid='{"dsr":[0,57,0,0]}'>A <span about="#mwt2" class="reference" data-mw='{"name":"ref","attrs":{"name":"a"}}' id="cite_ref-a-1-0" rel="dc:references" typeof="mw:Extension/ref" data-parsoid='{"src":"&lt;ref name=\"a\" />","dsr":[2,18,16,0]}'><a href="#cite_note-a-1">[1]</a></span>
+B <span about="#mwt4" class="reference" data-mw='{"name":"ref","body":{"html":"bar"},"attrs":{"name":"b"}}' id="cite_ref-b-2-0" rel="dc:references" typeof="mw:Extension/ref" data-parsoid='{"src":"&lt;ref name=\"b\">bar&lt;/ref>","dsr":[21,44,14,6]}'><a href="#cite_note-b-2">[2]</a></span></p>
+
 
-<ol class="references" typeof="mw:Extension/references" about="#mwt6" data-mw='{"name":"references","body":{"extsrc":"<ref name=\"a\">foo</ref>\nThis should just get lost.","html":"\n<span about=\"#mwt8\" class=\"reference\" data-mw=\"{&amp;quot;name&amp;quot;:&amp;quot;ref&amp;quot;,&amp;quot;body&amp;quot;:{&amp;quot;html&amp;quot;:&amp;quot;foo&amp;quot;},&amp;quot;attrs&amp;quot;:{&amp;quot;name&amp;quot;:&amp;quot;a&amp;quot;}}\" rel=\"dc:references\" typeof=\"mw:Extension/ref\"><a href=\"#cite_note-a-1\">[1]</a></span>\n"},"attrs":{}}'><li about="#cite_note-a-1" id="cite_note-a-1"><span rel="mw:referencedBy"><a href="#cite_ref-a-1-0">↑</a></span> foo</li><li about="#cite_note-b-2" id="cite_note-b-2"><span rel="mw:referencedBy"><a href="#cite_ref-b-2-0">↑</a></span> bar</li></ol>
+<ol class="references" typeof="mw:Extension/references" about="#mwt6" data-parsoid='{"src":"&lt;references>\n&lt;ref name=\"a\">foo&lt;/ref>\nThis should just get lost.\n&lt;/references>","dsr":[46,123,2,2]}' data-mw='{"name":"references","body":{"extsrc":"&lt;ref name=\"a\">foo&lt;/ref>\nThis should just get lost.","html":"\n&lt;span about=\"#mwt8\" class=\"reference\" data-mw=&#39;{\"name\":\"ref\",\"body\":{\"html\":\"foo\"},\"attrs\":{\"name\":\"a\"}}&#39; rel=\"dc:references\" typeof=\"mw:Extension/ref\">&lt;a href=\"#cite_note-a-1\">[1]&lt;/a>&lt;/span>\n"},"attrs":{}}'>
+<li about="#cite_note-a-1" id="cite_note-a-1" data-parsoid="{}"><span rel="mw:referencedBy"><a href="#cite_ref-a-1-0">↑</a></span> foo</li>
+<li about="#cite_note-b-2" id="cite_note-b-2" data-parsoid="{}"><span rel="mw:referencedBy"><a href="#cite_ref-b-2-0">↑</a></span> bar</li>
+</ol>
 !!end
 
 !!test
@@ -16134,15 +16801,18 @@ B <ref name="b" />
 <ref name="b">foo</ref>
 </references>
 !! result
-<p>A <span about="#mwt2" class="reference" data-mw='{"name":"ref","body":{"html":"foo bar for a"},"attrs":{}}' id="cite_ref-1-0" rel="dc:references" typeof="mw:Extension/ref"><a href="#cite_note-1">[1]</a></span>
-B <span about="#mwt4" class="reference" data-mw='{"name":"ref","attrs":{"name":"b"}}' id="cite_ref-b-2-0" rel="dc:references" typeof="mw:Extension/ref"><a href="#cite_note-b-2">[2]</a></span></p>
+<p data-parsoid='{"dsr":[0,45,0,0]}'>A <span about="#mwt2" class="reference" data-mw='{"name":"ref","body":{"html":"foo bar for a"},"attrs":{}}' id="cite_ref-1-0" rel="dc:references" typeof="mw:Extension/ref" data-parsoid='{"src":"&lt;ref>foo bar for a&lt;/ref>","dsr":[2,26,5,6]}'><a href="#cite_note-1">[1]</a></span>
+B <span about="#mwt4" class="reference" data-mw='{"name":"ref","attrs":{"name":"b"}}' id="cite_ref-b-2-0" rel="dc:references" typeof="mw:Extension/ref" data-parsoid='{"src":"&lt;ref name=\"b\" />","dsr":[29,45,16,0]}'><a href="#cite_note-b-2">[2]</a></span></p>
+
 
-<ol class="references" typeof="mw:Extension/references" about="#mwt6" data-mw='{"name":"references","attrs":{}}'>
-<li about="#cite_note-1" id="cite_note-1"><span rel="mw:referencedBy"><a href="#cite_ref-1-0">↑</a></span> foo bar for a</li>
-<li about="#cite_note-b-2" id="cite_note-b-2"><span rel="mw:referencedBy"><a href="#cite_ref-b-2-0">↑</a></span> </li></ol>
+<ol class="references" typeof="mw:Extension/references" about="#mwt6" data-parsoid='{"src":"&lt;references />","dsr":[47,61,2,2]}' data-mw='{"name":"references","attrs":{}}'>
+<li about="#cite_note-1" id="cite_note-1" data-parsoid="{}"><span rel="mw:referencedBy"><a href="#cite_ref-1-0">↑</a></span> foo bar for a</li>
+<li about="#cite_note-b-2" id="cite_note-b-2" data-parsoid="{}"><span rel="mw:referencedBy"><a href="#cite_ref-b-2-0">↑</a></span> </li></ol>
 
-<ol class="references" typeof="mw:Extension/references" about="#mwt8" data-mw='{"name":"references","body":{"extsrc":"<ref name=\"b\">foo</ref>","html":"\n<span about=\"#mwt10\" class=\"reference\" data-mw=\"{&amp;quot;name&amp;quot;:&amp;quot;ref&amp;quot;,&amp;quot;body&amp;quot;:{&amp;quot;html&amp;quot;:&amp;quot;foo&amp;quot;},&amp;quot;attrs&amp;quot;:{&amp;quot;name&amp;quot;:&amp;quot;b&amp;quot;}}\" rel=\"dc:references\" typeof=\"mw:Extension/ref\"><a href=\"#cite_note-b-1\">[1]</a></span>\n"},"attrs":{}}'>
-<li about="#cite_note-b-1" id="cite_note-b-1"><span rel="mw:referencedBy">↑</span> foo</li></ol>
+
+<ol class="references" typeof="mw:Extension/references" about="#mwt8" data-parsoid='{"src":"&lt;references>\n&lt;ref name=\"b\">foo&lt;/ref>\n&lt;/references>","dsr":[63,113,2,2]}' data-mw='{"name":"references","body":{"extsrc":"&lt;ref name=\"b\">foo&lt;/ref>","html":"\n&lt;span about=\"#mwt10\" class=\"reference\" data-mw=&#39;{\"name\":\"ref\",\"body\":{\"html\":\"foo\"},\"attrs\":{\"name\":\"b\"}}&#39; rel=\"dc:references\" typeof=\"mw:Extension/ref\">&lt;a href=\"#cite_note-b-1\">[1]&lt;/a>&lt;/span>\n"},"attrs":{}}'>
+<li about="#cite_note-b-1" id="cite_note-b-1" data-parsoid="{}"><span rel="mw:referencedBy">↑</span> foo</li>
+</ol>
 !! end
 
 #### ----------------------------------------------------------------
@@ -16438,22 +17108,38 @@ Lists: 1. Nested inside html
 
 #<nowiki>;foo</nowiki>
 !! result
-<ul><li>*foo
-</li></ul>
-<ul><li>#foo
-</li></ul>
-<ul><li>:foo
-</li></ul>
-<ul><li>;foo
-</li></ul>
-<ol><li>*foo
-</li></ol>
-<ol><li>#foo
-</li></ol>
-<ol><li>:foo
-</li></ol>
-<ol><li>;foo
-</li></ol>
+<ul>
+<li>*foo
+</li>
+</ul>
+<ul>
+<li>#foo
+</li>
+</ul>
+<ul>
+<li>:foo
+</li>
+</ul>
+<ul>
+<li>;foo
+</li>
+</ul>
+<ol>
+<li>*foo
+</li>
+</ol>
+<ol>
+<li>#foo
+</li>
+</ol>
+<ol>
+<li>:foo
+</li>
+</ol>
+<ol>
+<li>;foo
+</li>
+</ol>
 
 !!end
 
@@ -16469,15 +17155,24 @@ Lists: 2. Inside definition lists
 
 :<nowiki>:foo</nowiki>
 !! result
-<dl><dt>;foo
-</dt></dl>
-<dl><dt>:foo
-</dt></dl>
-<dl><dt>:foo
-</dt><dd>bar
-</dd></dl>
-<dl><dd>:foo
-</dd></dl>
+<dl>
+<dt>;foo
+</dt>
+</dl>
+<dl>
+<dt>:foo
+</dt>
+</dl>
+<dl>
+<dt>:foo
+</dt>
+<dd>bar
+</dd>
+</dl>
+<dl>
+<dd>:foo
+</dd>
+</dl>
 
 !!end
 
@@ -16488,10 +17183,14 @@ Lists: 3. Only bullets at start of text should be escaped
 
 *<nowiki>*foo</nowiki>''it''*bar
 !! result
-<ul><li>*foo*bar
-</li></ul>
-<ul><li>*foo<i>it</i>*bar
-</li></ul>
+<ul>
+<li>*foo*bar
+</li>
+</ul>
+<ul>
+<li>*foo<i>it</i>*bar
+</li>
+</ul>
 
 !!end
 
@@ -16506,12 +17205,18 @@ parsoid
 
 *[[Foo]]: bar
 !! result
-<ul><li>foo*bar
-</li></ul>
-<ul><li><i>foo</i>*bar
-</li></ul>
-<ul><li><a rel="mw:WikiLink" href="Foo">Foo</a>: bar
-</li></ul>
+<ul>
+<li>foo*bar
+</li>
+</ul>
+<ul>
+<li><i>foo</i>*bar
+</li>
+</ul>
+<ul>
+<li><a rel="mw:WikiLink" href="Foo">Foo</a>: bar
+</li>
+</ul>
 !!end
 
 !! test
@@ -16529,18 +17234,30 @@ Lists: 5. No unnecessary escapes
 
 * <s></s>: a
 !! result
-<ul><li> bar <span>[[foo]]</span>
-</li></ul>
-<ul><li>=bar <span>[[foo]]</span>
-</li></ul>
-<ul><li>[[bar <span>[[foo]]</span>
-</li></ul>
-<ul><li>]]bar <span>[[foo]]</span>
-</li></ul>
-<ul><li>=bar <span>foo]]</span>=
-</li></ul>
-<ul><li> <s></s>: a
-</li></ul>
+<ul>
+<li> bar <span>[[foo]]</span>
+</li>
+</ul>
+<ul>
+<li>=bar <span>[[foo]]</span>
+</li>
+</ul>
+<ul>
+<li>[[bar <span>[[foo]]</span>
+</li>
+</ul>
+<ul>
+<li>]]bar <span>[[foo]]</span>
+</li>
+</ul>
+<ul>
+<li>=bar <span>foo]]</span>=
+</li>
+</ul>
+<ul>
+<li> <s></s>: a
+</li>
+</ul>
 
 !!end
 
@@ -17738,6 +18455,63 @@ parsoid=html2wt
 </ul>
 !! end
 
+!! test
+Don't strip leading whitespace when handling indent-pre suppressing tags
+!! options
+parsoid=html2wt
+!! input
+{|
+  | indented row
+|}
+<blockquote>
+ '''This is very bold of you!'''
+
+{|
+|
+ indented cell (no pre-wrapping!)
+|}
+</blockquote>
+foo
+ <div>bar</div>
+!! result
+<table>
+  <tr><td> indented row</td></tr>
+</table>
+<blockquote><p>
+ <b>This is very bold of you!</b>
+</p>
+<table><tr><td>
+ indented cell (no pre-wrapping!)
+</td></tr></table>
+</blockquote>
+<p>foo</p>
+ <div>bar</div>
+!! end
+
+!! test
+Strip leading whitespace when handling indent-pre inducing tags
+!! options
+parsoid=html2wt
+!! input
+foo
+<span>bar</span>
+
+<span>foo2
+</span>bar2
+
+<div>foo</div>
+<span>bar</span>
+!! result
+<p>foo</p>
+ <span>bar</span>
+
+<span>foo2
+ </span>bar2
+
+<div>foo</div>
+ <span>bar</span>
+!! end
+
 # Wacky -- the leading newline in input is required because
 # that is what the serializer emits. To be fixed. Not fixing
 # the test because this test is required to test serialization of
@@ -17885,6 +18659,16 @@ parsoid=html2wt
 <p><i>A</i><b data-parsoid='{}'><i data-parsoid='{}'>B</i></b></p>
 !! end
 
+!!test
+5. Bug 54262: New entities
+!! options
+parsoid=html2wt
+!! input
+foo
+!! result
+<span typeof="mw:Entity">foo</span>
+!! end
+
 # -----------------------------------------------------------------
 # End of section for Parsoid-only html2wt tests for serialization
 # of new content
index 349c510..8c849bc 100644 (file)
@@ -777,7 +777,7 @@ abstract class MediaWikiTestCase extends PHPUnit_Framework_TestCase {
        }
 
        /**
-        * Returns true iff the given namespace defaults to Wikitext
+        * Returns true if the given namespace defaults to Wikitext
         * according to $wgNamespaceContentModels
         *
         * @param int $ns The namespace ID to check
diff --git a/tests/phpunit/data/autoloader/TestAutoloadedCamlClass.php b/tests/phpunit/data/autoloader/TestAutoloadedCamlClass.php
new file mode 100644 (file)
index 0000000..6dfce7a
--- /dev/null
@@ -0,0 +1,4 @@
+<?php
+
+class TestAutoloadedCamlClass {
+}
diff --git a/tests/phpunit/data/autoloader/TestAutoloadedClass.php b/tests/phpunit/data/autoloader/TestAutoloadedClass.php
new file mode 100644 (file)
index 0000000..9ceedf6
--- /dev/null
@@ -0,0 +1,4 @@
+<?php
+
+class TestAutoloadedClass {
+}
diff --git a/tests/phpunit/data/autoloader/TestAutoloadedLocalClass.php b/tests/phpunit/data/autoloader/TestAutoloadedLocalClass.php
new file mode 100644 (file)
index 0000000..1b397cd
--- /dev/null
@@ -0,0 +1,4 @@
+<?php
+
+class TestAutoloadedLocalClass {
+}
diff --git a/tests/phpunit/data/autoloader/TestAutoloadedSerializedClass.php b/tests/phpunit/data/autoloader/TestAutoloadedSerializedClass.php
new file mode 100644 (file)
index 0000000..80b9d58
--- /dev/null
@@ -0,0 +1,4 @@
+<?php
+
+class TestAutoloadedSerializedClass {
+}
index fe3bc68..4b489a9 100644 (file)
@@ -36,3 +36,8 @@ http://commons.wikimedia.org/wiki/File:Animated_PNG_example_bouncing_beach_ball.
 Public Domain
 Holger Will
 
+Tux.svg
+https://commons.wikimedia.org/wiki/File:Tux.svg
+Larry Ewing, Simon Budig, Anja Gerwinski
+"The copyright holder of this file allows anyone to use it for any purpose, provided that the copyright holder is properly attributed. Redistribution, derivative work, commercial use, and all other use is permitted."
+
diff --git a/tests/phpunit/data/media/Tux.svg b/tests/phpunit/data/media/Tux.svg
new file mode 100644 (file)
index 0000000..3956107
--- /dev/null
@@ -0,0 +1,902 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>\r
+<svg xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="100%" width="100%" version="1.1" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" viewBox="0 0 349.46883 405.12272">\r
+ <title>Tux</title>\r
+ <desc>For more information see: http://commons.wikimedia.org/wiki/Image:Tux.svg</desc>\r
+ <radialGradient id="ag" gradientUnits="userSpaceOnUse" cy="-551.04" cx="274.822" gradientTransform="matrix(.5671 0 0 -.2835 81.263 201.645)" r="165.384">\r
+  <stop stop-opacity=".502" offset="0"/>\r
+  <stop stop-opacity="0" offset="1"/>\r
+ </radialGradient>\r
+ <path fill="url(#ag)" d="m330.892 357.885c0 25.898-41.989 46.893-93.785 46.893-51.795 0-93.784-20.994-93.784-46.893s41.989-46.893 93.784-46.893c51.795 0.001 93.785 20.995 93.785 46.893z"/>\r
+ <radialGradient id="ak" gradientUnits="userSpaceOnUse" cy="-551.042" cx="268.794" gradientTransform="matrix(.5823 0 0 -.2835 -61.6052 201.14)" r="165.383">\r
+  <stop stop-opacity=".502" offset="0"/>\r
+  <stop stop-opacity="0" offset="1"/>\r
+ </radialGradient>\r
+ <path fill="url(#ak)" d="m191.223 357.381c0 25.897-43.117 46.892-96.306 46.892-53.188 0-96.305-20.994-96.305-46.892s43.117-46.893 96.305-46.893c53.188 0.001 96.306 20.995 96.306 46.893z"/>\r
+ <g transform="translate(8.99996 9.00046)">\r
+  <path d="m292.327 256.606c-4.752 19.584-28.872 60.48-41.688 78.48-12.815 18.072-11.231 34.344-34.92 28.008-23.616-6.336-30.24-5.184-54.647-3.744-24.265 1.439-19.009-0.721-34.2 6.12-15.12 6.84-65.88-82.944-69.984-99.647-4.031-16.705-5.976-14.689 4.536-32.761 10.513-18.071 12.024-35.928 25.92-57.816 13.896-21.96 29.952-33.12 28.8-49.896-4.535-62.28-8.136-93.384 19.513-107.784 26.352-13.68 48.384-5.544 57.096-0.864 3.744 2.016 11.376 5.904 17.064 12.744 5.688 6.696 10.8 16.848 13.68 29.664 5.904 25.704-2.448 17.208 4.248 46.656 6.624 29.375 20.088 43.775 36.504 67.031 16.414 23.257 33.55 61.633 28.078 83.809z"/>\r
+  <g transform="translate(-12.4048,10.0005)">\r
+   <path fill="#666" d="m148.47 94.049c4.319-1.728 3.592-1.958 6.472-8.222 2.304-4.824 4.328-6.898 4.256-14.242 0-7.2-2.232-9.648-5.616-14.328-3.24-4.464-8.424-4.68-11.664-4.104-1.872 0.288-4.319 2.664-5.976 6.192-1.08 2.376-1.944 5.4-2.017 8.568-0.216 8.496 0.505 11.736 2.448 17.496 2.305 6.769 7.921 10.297 12.097 8.64z"/>\r
+   <path fill="#6d6d6d" d="m148.47 94.023c4.293-1.717 3.563-1.954 6.425-8.178 2.289-4.793 4.312-6.861 4.271-14.164 0.027-7.152-2.162-9.702-5.488-14.201-3.296-4.345-8.376-4.509-11.593-3.953-1.916 0.283-4.354 2.569-6.038 5.968-1.159 2.31-2.016 5.353-2.087 8.535-0.212 8.438 0.547 11.691 2.46 17.417 2.268 6.731 7.901 10.221 12.05 8.576z"/>\r
+   <path fill="#757575" d="m148.471 93.996c4.264-1.706 3.533-1.95 6.377-8.133 2.273-4.762 4.296-6.823 4.288-14.085 0.053-7.105-2.093-9.756-5.363-14.075-3.35-4.225-8.327-4.338-11.52-3.801-1.961 0.278-4.389 2.474-6.099 5.744-1.242 2.245-2.089 5.305-2.16 8.501-0.207 8.38 0.591 11.647 2.473 17.34 2.231 6.691 7.881 10.144 12.004 8.509z"/>\r
+   <path fill="#7c7c7c" d="m148.471 93.969c4.235-1.694 3.506-1.946 6.329-8.089 2.26-4.731 4.28-6.786 4.304-14.006 0.081-7.058-2.021-9.811-5.236-13.948-3.403-4.105-8.278-4.167-11.446-3.649-2.006 0.273-4.424 2.379-6.16 5.519-1.322 2.179-2.161 5.257-2.232 8.468-0.202 8.323 0.636 11.603 2.486 17.261 2.191 6.654 7.859 10.068 11.955 8.444z"/>\r
+   <path fill="#848484" d="m148.471 93.943c4.209-1.684 3.477-1.942 6.282-8.045 2.245-4.7 4.266-6.749 4.319-13.928 0.107-7.01-1.95-9.864-5.109-13.821-3.458-3.985-8.23-3.996-11.375-3.498-2.049 0.268-4.458 2.284-6.222 5.295-1.403 2.114-2.233 5.21-2.303 8.435-0.198 8.265 0.679 11.559 2.498 17.183 2.156 6.615 7.842 9.992 11.91 8.379z"/>\r
+   <path fill="#8c8c8c" d="m148.471 93.916c4.181-1.672 3.448-1.938 6.235-8 2.23-4.668 4.249-6.711 4.335-13.85 0.134-6.962-1.88-9.918-4.982-13.695-3.513-3.865-8.183-3.825-11.303-3.347-2.094 0.263-4.492 2.189-6.283 5.07-1.484 2.049-2.306 5.163-2.375 8.401-0.193 8.207 0.723 11.515 2.511 17.105 2.118 6.58 7.821 9.919 11.862 8.316z"/>\r
+   <path fill="#939393" d="m148.472 93.889c4.152-1.661 3.419-1.934 6.188-7.956 2.215-4.638 4.233-6.674 4.35-13.771 0.161-6.915-1.809-9.972-4.854-13.568-3.567-3.746-8.134-3.654-11.23-3.195-2.138 0.259-4.527 2.094-6.345 4.847-1.564 1.983-2.378 5.115-2.447 8.368-0.188 8.149 0.767 11.47 2.523 17.026 2.079 6.54 7.8 9.841 11.815 8.249z"/>\r
+   <path fill="#9b9b9b" d="m148.472 93.863c4.125-1.65 3.391-1.93 6.141-7.912 2.2-4.607 4.217-6.637 4.366-13.693 0.188-6.868-1.739-10.026-4.729-13.441-3.621-3.626-8.085-3.484-11.157-3.044-2.183 0.253-4.562 1.999-6.406 4.622-1.646 1.918-2.45 5.068-2.52 8.335-0.185 8.091 0.811 11.426 2.535 16.948 2.044 6.502 7.782 9.766 11.77 8.185z"/>\r
+   <path fill="#a3a3a3" d="m148.472 93.836c4.097-1.639 3.361-1.926 6.094-7.867 2.185-4.576 4.201-6.599 4.382-13.614 0.214-6.82-1.669-10.081-4.603-13.315-3.676-3.506-8.036-3.313-11.084-2.893-2.229 0.249-4.598 1.904-6.47 4.398-1.726 1.852-2.521 5.021-2.591 8.301-0.18 8.034 0.854 11.382 2.548 16.87 2.008 6.465 7.763 9.691 11.724 8.12z"/>\r
+   <path fill="#aaa" d="m148.472 93.809c4.069-1.628 3.334-1.922 6.047-7.823 2.17-4.544 4.185-6.562 4.396-13.536 0.242-6.772-1.597-10.134-4.475-13.188-3.73-3.387-7.989-3.142-11.013-2.741-2.271 0.243-4.632 1.809-6.53 4.173-1.808 1.787-2.594 4.974-2.662 8.268-0.176 7.976 0.897 11.337 2.56 16.792 1.97 6.427 7.743 9.615 11.677 8.055z"/>\r
+   <path fill="#b2b2b2" d="m148.473 93.782c4.041-1.617 3.304-1.918 5.999-7.778 2.154-4.514 4.169-6.524 4.412-13.458 0.269-6.725-1.526-10.188-4.349-13.062-3.784-3.267-7.939-2.971-10.939-2.589-2.316 0.238-4.666 1.714-6.592 3.949-1.888 1.721-2.667 4.926-2.734 8.234-0.171 7.918 0.941 11.293 2.572 16.713 1.933 6.391 7.723 9.541 11.631 7.991z"/>\r
+   <path fill="#bababa" d="m148.473 93.756c4.014-1.606 3.275-1.914 5.951-7.734 2.141-4.482 4.153-6.487 4.43-13.379 0.295-6.678-1.457-10.243-4.223-12.935-3.839-3.147-7.892-2.8-10.867-2.438-2.36 0.233-4.701 1.619-6.653 3.725-1.969 1.656-2.739 4.879-2.806 8.201-0.167 7.86 0.984 11.249 2.585 16.636 1.895 6.35 7.702 9.462 11.583 7.924z"/>\r
+   <path fill="#c1c1c1" d="m148.473 93.729c3.985-1.595 3.247-1.91 5.904-7.69 2.125-4.451 4.138-6.45 4.445-13.3 0.321-6.63-1.387-10.297-4.096-12.808-3.894-3.028-7.844-2.629-10.795-2.287-2.405 0.229-4.735 1.524-6.716 3.5-2.049 1.59-2.811 4.831-2.878 8.167-0.161 7.802 1.029 11.205 2.599 16.557 1.859 6.314 7.683 9.389 11.537 7.861z"/>\r
+   <path fill="#c9c9c9" d="m148.473 93.702c3.958-1.583 3.219-1.906 5.857-7.646 2.11-4.42 4.121-6.412 4.46-13.222 0.35-6.583-1.315-10.351-3.969-12.682-3.947-2.908-7.794-2.458-10.722-2.135-2.45 0.224-4.771 1.429-6.777 3.276-2.13 1.525-2.883 4.784-2.95 8.135-0.157 7.745 1.073 11.16 2.611 16.479 1.821 6.276 7.663 9.313 11.49 7.795z"/>\r
+   <path fill="#d1d1d1" d="m148.474 93.676c3.93-1.573 3.188-1.902 5.809-7.601 2.097-4.389 4.107-6.375 4.477-13.144 0.375-6.535-1.245-10.404-3.842-12.555-4.002-2.788-7.747-2.287-10.65-1.984-2.493 0.219-4.805 1.334-6.837 3.052-2.213 1.459-2.957 4.736-3.022 8.101-0.153 7.687 1.116 11.116 2.623 16.401 1.782 6.237 7.642 9.237 11.442 7.73z"/>\r
+   <path fill="#d8d8d8" d="m148.474 93.649c3.901-1.562 3.16-1.898 5.762-7.557 2.082-4.358 4.091-6.338 4.493-13.065 0.401-6.487-1.176-10.458-3.716-12.428-4.057-2.668-7.698-2.116-10.578-1.832-2.538 0.214-4.839 1.239-6.899 2.827-2.292 1.394-3.029 4.689-3.094 8.068-0.148 7.629 1.16 11.072 2.636 16.322 1.746 6.2 7.623 9.161 11.396 7.665z"/>\r
+   <path fill="#e0e0e0" d="m148.474 93.622c3.875-1.55 3.132-1.894 5.715-7.512 2.066-4.327 4.075-6.3 4.508-12.987 0.429-6.44-1.104-10.513-3.588-12.302-4.111-2.549-7.65-1.945-10.506-1.681-2.582 0.209-4.874 1.144-6.961 2.604-2.373 1.328-3.102 4.642-3.165 8.034-0.145 7.571 1.204 11.027 2.647 16.244 1.709 6.162 7.604 9.086 11.35 7.6z"/>\r
+   <path fill="#e8e8e8" d="m148.474 93.596c3.847-1.54 3.104-1.89 5.668-7.468 2.052-4.296 4.059-6.263 4.523-12.908 0.456-6.393-1.034-10.567-3.462-12.175-4.165-2.429-7.601-1.774-10.433-1.529-2.627 0.204-4.908 1.049-7.023 2.379-2.453 1.263-3.173 4.594-3.236 8.001-0.141 7.514 1.247 10.983 2.659 16.166 1.673 6.123 7.585 9.008 11.304 7.534z"/>\r
+   <path fill="#efefef" d="m148.475 93.569c3.817-1.528 3.073-1.886 5.62-7.424 2.036-4.265 4.043-6.226 4.539-12.83 0.482-6.345-0.964-10.621-3.336-12.048-4.219-2.31-7.552-1.604-10.359-1.378-2.672 0.199-4.943 0.954-7.084 2.155-2.535 1.197-3.246 4.546-3.311 7.967-0.135 7.456 1.292 10.939 2.673 16.087 1.636 6.087 7.565 8.935 11.258 7.471z"/>\r
+   <path fill="#f7f7f7" d="m148.475 93.542c3.791-1.517 3.046-1.882 5.572-7.379 2.022-4.234 4.027-6.188 4.556-12.751 0.51-6.297-0.894-10.675-3.208-11.921-4.274-2.19-7.505-1.433-10.289-1.227-2.715 0.194-4.978 0.859-7.146 1.93-2.614 1.132-3.317 4.5-3.381 7.935-0.131 7.398 1.335 10.895 2.686 16.009 1.597 6.047 7.544 8.858 11.21 7.404z"/>\r
+   <path fill="#fff" d="m148.475 93.516c3.763-1.506 3.017-1.878 5.525-7.335 2.007-4.203 4.012-6.151 4.571-12.673 0.536-6.25-0.823-10.729-3.082-11.795-4.328-2.07-7.456-1.262-10.216-1.075-2.76 0.189-5.012 0.764-7.207 1.706-2.696 1.066-3.39 4.452-3.453 7.901-0.126 7.34 1.379 10.85 2.698 15.931 1.561 6.01 7.525 8.782 11.164 7.34z"/>\r
+  </g>\r
+  <path d="m132.033 74.7465c2.16 0 4.896 1.44 6.191 3.384 1.368 1.944 2.376 4.68 2.376 7.776 0 4.608-0.504 9.72-3.239 11.304-0.864 0.504-2.736 0.936-3.816 0.936-2.448 0-2.664-1.584-4.968-3.96-0.792-0.864-3.168-5.04-3.168-8.496 0-2.16-0.504-5.256 1.368-7.992 1.296-2.016 2.952-2.952 5.256-2.952z"/>\r
+  <g transform="translate(-12.4048,10.0005)">\r
+   <path d="m143.862 68.608c0.844-1.305 4.222-0.69 5.45 1.996 1.229 2.687 0.998 8.522 0.153 8.829-2.226 0.691-1.535-2.534-3.454-5.451-1.919-2.762-2.994-4.067-2.149-5.374z"/>\r
+   <path fill="#070707" d="m143.916 68.664c0.833-1.289 4.169-0.681 5.381 1.971 1.215 2.653 0.985 8.414 0.152 8.717-2.198 0.682-1.516-2.502-3.411-5.382-1.895-2.728-2.956-4.017-2.122-5.306z"/>\r
+   <path fill="#0f0f0f" d="m143.97 68.719c0.822-1.272 4.114-0.673 5.312 1.945 1.198 2.619 0.973 8.306 0.15 8.605-2.169 0.673-1.497-2.47-3.367-5.313-1.871-2.692-2.918-3.964-2.095-5.237z"/>\r
+   <path fill="#161616" d="m144.024 68.774c0.812-1.255 4.062-0.664 5.243 1.92 1.182 2.585 0.96 8.198 0.147 8.493-2.141 0.665-1.477-2.438-3.323-5.244-1.846-2.657-2.88-3.913-2.067-5.169z"/>\r
+   <path fill="#1e1e1e" d="m144.078 68.829c0.801-1.239 4.008-0.655 5.174 1.895 1.167 2.551 0.947 8.09 0.146 8.381-2.113 0.656-1.458-2.405-3.28-5.174-1.821-2.623-2.842-3.863-2.04-5.102z"/>\r
+   <path fill="#262626" d="m144.132 68.884c0.791-1.222 3.955-0.646 5.105 1.87 1.151 2.517 0.935 7.982 0.144 8.27-2.085 0.647-1.438-2.374-3.235-5.105-1.798-2.589-2.805-3.812-2.014-5.035z"/>\r
+   <path fill="#2d2d2d" d="m144.186 68.939c0.779-1.206 3.9-0.638 5.036 1.844 1.135 2.483 0.922 7.874 0.142 8.158-2.057 0.639-1.419-2.341-3.192-5.037-1.773-2.552-2.766-3.758-1.986-4.965z"/>\r
+   <path fill="#353535" d="m144.24 68.994c0.769-1.189 3.848-0.629 4.967 1.819 1.12 2.449 0.909 7.766 0.141 8.046-2.028 0.629-1.399-2.31-3.148-4.967-1.75-2.518-2.73-3.708-1.96-4.898z"/>\r
+   <path fill="#3d3d3d" d="m144.294 69.049c0.76-1.172 3.794-0.621 4.898 1.793 1.104 2.415 0.896 7.658 0.138 7.934-2 0.621-1.38-2.277-3.104-4.898-1.725-2.482-2.691-3.655-1.932-4.829z"/>\r
+   <path fill="#444" d="m144.348 69.104c0.748-1.156 3.74-0.612 4.829 1.768 1.088 2.38 0.884 7.55 0.136 7.822-1.973 0.612-1.36-2.245-3.062-4.829-1.699-2.448-2.651-3.604-1.903-4.761z"/>\r
+   <path fill="#4c4c4c" d="m144.402 69.16c0.737-1.14 3.687-0.603 4.76 1.743 1.073 2.347 0.871 7.442 0.134 7.71-1.943 0.604-1.341-2.213-3.017-4.76-1.676-2.414-2.614-3.554-1.877-4.693z"/>\r
+   <path fill="#545454" d="m144.456 69.215c0.727-1.123 3.634-0.595 4.691 1.717 1.057 2.313 0.857 7.334 0.132 7.598-1.916 0.595-1.321-2.181-2.973-4.691-1.652-2.378-2.577-3.501-1.85-4.624z"/>\r
+   <path fill="#5b5b5b" d="m144.51 69.27c0.717-1.106 3.58-0.585 4.622 1.692 1.041 2.278 0.847 7.226 0.131 7.486-1.888 0.586-1.303-2.149-2.93-4.622-1.628-2.343-2.539-3.45-1.823-4.556z"/>\r
+   <path fill="#636363" d="m144.564 69.325c0.705-1.09 3.526-0.577 4.553 1.667 1.026 2.245 0.833 7.118 0.128 7.375-1.858 0.577-1.282-2.117-2.885-4.553-1.604-2.309-2.501-3.399-1.796-4.489z"/>\r
+   <path fill="#6b6b6b" d="m144.618 69.38c0.694-1.073 3.473-0.568 4.483 1.642 1.011 2.21 0.82 7.01 0.127 7.263-1.831 0.568-1.264-2.084-2.842-4.484-1.578-2.274-2.462-3.347-1.768-4.421z"/>\r
+   <path fill="#727272" d="m144.672 69.435c0.685-1.057 3.42-0.56 4.414 1.617 0.995 2.176 0.81 6.902 0.125 7.15-1.803 0.56-1.243-2.053-2.798-4.415-1.554-2.238-2.425-3.295-1.741-4.352z"/>\r
+   <path fill="#7a7a7a" d="m144.726 69.49c0.673-1.041 3.365-0.551 4.345 1.591 0.979 2.143 0.796 6.794 0.123 7.039-1.775 0.551-1.224-2.021-2.754-4.346-1.53-2.203-2.387-3.244-1.714-4.284z"/>\r
+   <path fill="#828282" d="m144.78 69.545c0.662-1.023 3.313-0.542 4.276 1.566 0.964 2.108 0.782 6.686 0.121 6.926-1.746 0.542-1.204-1.988-2.711-4.276-1.505-2.167-2.348-3.192-1.686-4.216z"/>\r
+   <path fill="#898989" d="m144.834 69.6c0.652-1.007 3.259-0.533 4.207 1.541s0.771 6.578 0.119 6.815c-1.718 0.534-1.185-1.956-2.666-4.207-1.482-2.134-2.311-3.142-1.66-4.149z"/>\r
+   <path fill="#919191" d="m144.888 69.655c0.641-0.99 3.206-0.524 4.138 1.516 0.933 2.04 0.758 6.47 0.117 6.703-1.69 0.525-1.165-1.924-2.623-4.138-1.457-2.098-2.273-3.09-1.632-4.081z"/>\r
+   <path fill="#999" d="m144.942 69.71c0.63-0.974 3.152-0.516 4.069 1.49s0.744 6.362 0.114 6.591c-1.662 0.516-1.146-1.892-2.579-4.069-1.432-2.062-2.234-3.037-1.604-4.012z"/>\r
+  </g>\r
+  <g transform="translate(-12.4048,10.0005)">\r
+   <path fill="#666" d="m193.11 94.985c10.8-1.152 14.616-5.328 16.56-12.6 1.729-6.48 1.801-13.68-3.023-22.104-4.536-8.063-7.128-9.36-13.681-9.864-10.079-0.864-14.832 6.192-17.063 11.232-2.376 5.472-1.872 4.68-1.729 11.592 0.145 7.272 4.245 9.299 6.766 13.835 2.519 4.465 10.946 7.982 12.17 7.909z"/>\r
+   <path fill="#6d6d6d" d="m193.115 94.944c10.759-1.131 14.618-5.354 16.515-12.569 1.701-6.525 1.785-13.686-3.002-21.912-4.434-7.797-7.038-9.081-13.512-9.581-10.049-0.861-14.941 5.873-17.181 10.874-2.304 5.28-1.878 4.718-1.726 11.539 0.16 7.268 4.268 9.223 6.784 13.76 2.521 4.475 10.898 7.962 12.122 7.889z"/>\r
+   <path fill="#757575" d="m193.12 94.902c10.718-1.11 14.62-5.379 16.469-12.538 1.676-6.57 1.771-13.692-2.979-21.721-4.331-7.53-6.947-8.801-13.344-9.297-10.018-0.858-15.05 5.553-17.298 10.516-2.229 5.087-1.885 4.757-1.722 11.487 0.176 7.264 4.289 9.146 6.803 13.686 2.52 4.485 10.848 7.942 12.071 7.867z"/>\r
+   <path fill="#7c7c7c" d="m193.126 94.861c10.675-1.09 14.621-5.405 16.423-12.507 1.648-6.616 1.756-13.698-2.958-21.529-4.229-7.263-6.856-8.522-13.176-9.014-9.985-0.854-15.158 5.234-17.414 10.158-2.156 4.895-1.891 4.795-1.719 11.434 0.193 7.26 4.31 9.07 6.822 13.611 2.52 4.495 10.798 7.922 12.022 7.847z"/>\r
+   <path fill="#848484" d="m193.131 94.82c10.635-1.069 14.623-5.431 16.377-12.476 1.622-6.661 1.741-13.704-2.936-21.337-4.126-6.996-6.767-8.242-13.008-8.73-9.955-0.852-15.267 4.915-17.53 9.8-2.084 4.703-1.896 4.833-1.716 11.38 0.209 7.256 4.332 8.995 6.841 13.537 2.52 4.505 10.748 7.902 11.972 7.826z"/>\r
+   <path fill="#8c8c8c" d="m193.136 94.778c10.593-1.048 14.625-5.457 16.331-12.445 1.596-6.706 1.726-13.709-2.913-21.145-4.025-6.729-6.678-7.963-12.841-8.447-9.924-0.848-15.375 4.595-17.647 9.441-2.01 4.51-1.903 4.872-1.712 11.328 0.225 7.251 4.354 8.918 6.858 13.462 2.521 4.517 10.7 7.883 11.924 7.806z"/>\r
+   <path fill="#939393" d="m193.141 94.737c10.552-1.027 14.627-5.482 16.286-12.414 1.568-6.751 1.711-13.715-2.893-20.954-3.922-6.462-6.586-7.683-12.672-8.163-9.892-0.845-15.483 4.276-17.764 9.083-1.938 4.318-1.909 4.91-1.709 11.275 0.24 7.247 4.375 8.842 6.878 13.387 2.521 4.528 10.651 7.863 11.874 7.786z"/>\r
+   <path fill="#9b9b9b" d="m193.146 94.695c10.51-1.007 14.63-5.508 16.241-12.382 1.542-6.796 1.694-13.721-2.87-20.762-3.82-6.195-6.496-7.404-12.504-7.879-9.861-0.842-15.592 3.956-17.882 8.725-1.863 4.126-1.915 4.949-1.706 11.223 0.258 7.243 4.397 8.766 6.897 13.313 2.521 4.535 10.601 7.841 11.824 7.762z"/>\r
+   <path fill="#a3a3a3" d="m193.151 94.654c10.469-0.986 14.632-5.534 16.196-12.351 1.515-6.842 1.68-13.727-2.85-20.57-3.717-5.928-6.405-7.125-12.335-7.596-9.83-0.839-15.7 3.637-17.998 8.367-1.791 3.933-1.922 4.987-1.703 11.169 0.273 7.239 4.419 8.689 6.916 13.238 2.521 4.547 10.551 7.822 11.774 7.743z"/>\r
+   <path fill="#aaa" d="m193.157 94.612c10.427-0.965 14.633-5.56 16.149-12.32 1.488-6.887 1.666-13.733-2.826-20.379-3.615-5.661-6.316-6.845-12.168-7.313-9.799-0.835-15.809 3.317-18.114 8.009-1.718 3.741-1.928 5.025-1.7 11.117 0.29 7.235 4.44 8.613 6.936 13.163 2.519 4.558 10.499 7.804 11.723 7.723z"/>\r
+   <path fill="#b2b2b2" d="m193.162 94.571c10.386-0.944 14.635-5.585 16.104-12.289 1.462-6.932 1.649-13.739-2.806-20.188-3.512-5.394-6.225-6.565-11.999-7.029-9.768-0.833-15.917 2.998-18.23 7.651-1.646 3.549-1.935 5.064-1.697 11.064 0.306 7.231 4.462 8.537 6.954 13.088 2.52 4.569 10.451 7.784 11.674 7.703z"/>\r
+   <path fill="#bababa" d="m193.167 94.529c10.345-0.923 14.638-5.611 16.059-12.258 1.436-6.977 1.636-13.744-2.782-19.995-3.41-5.127-6.135-6.286-11.832-6.746-9.736-0.829-16.025 2.679-18.347 7.293-1.572 3.356-1.941 5.103-1.694 11.011 0.322 7.227 4.484 8.461 6.973 13.014 2.519 4.579 10.4 7.764 11.623 7.681z"/>\r
+   <path fill="#c1c1c1" d="m193.172 94.488c10.304-0.903 14.64-5.637 16.014-12.227 1.409-7.022 1.62-13.75-2.762-19.804-3.308-4.86-6.044-6.006-11.662-6.462-9.705-0.826-16.135 2.359-18.466 6.935-1.498 3.164-1.945 5.141-1.689 10.958 0.338 7.223 4.506 8.385 6.991 12.939 2.519 4.59 10.351 7.744 11.574 7.661z"/>\r
+   <path fill="#c9c9c9" d="m193.177 94.447c10.262-0.882 14.641-5.663 15.967-12.196 1.383-7.068 1.605-13.756-2.738-19.612-3.206-4.593-5.954-5.727-11.496-6.179-9.673-0.823-16.242 2.04-18.581 6.577-1.425 2.972-1.952 5.179-1.687 10.906 0.354 7.219 4.526 8.308 7.01 12.865 2.52 4.598 10.302 7.723 11.525 7.639z"/>\r
+   <path fill="#d1d1d1" d="m193.182 94.405c10.221-0.861 14.643-5.688 15.922-12.165 1.355-7.113 1.591-13.762-2.717-19.42-3.104-4.326-5.864-5.448-11.327-5.895-9.644-0.82-16.352 1.721-18.698 6.219-1.353 2.779-1.959 5.217-1.684 10.853 0.369 7.214 4.549 8.232 7.028 12.79 2.521 4.609 10.254 7.703 11.476 7.618z"/>\r
+   <path fill="#d8d8d8" d="m193.187 94.364c10.179-0.841 14.645-5.714 15.876-12.133 1.33-7.158 1.576-13.768-2.694-19.229-3.001-4.059-5.773-5.168-11.16-5.612-9.61-0.817-16.459 1.401-18.813 5.861-1.279 2.586-1.965 5.256-1.682 10.8 0.387 7.21 4.571 8.156 7.049 12.715 2.519 4.619 10.202 7.684 11.424 7.598z"/>\r
+   <path fill="#e0e0e0" d="m193.193 94.322c10.137-0.82 14.646-5.74 15.83-12.103 1.303-7.203 1.561-13.773-2.673-19.037-2.898-3.792-5.684-4.889-10.991-5.328-9.58-0.813-16.568 1.082-18.931 5.502-1.206 2.395-1.972 5.294-1.679 10.747 0.403 7.207 4.592 8.08 7.067 12.641 2.521 4.631 10.154 7.666 11.377 7.578z"/>\r
+   <path fill="#e8e8e8" d="m193.198 94.281c10.096-0.799 14.648-5.766 15.785-12.071 1.275-7.249 1.545-13.779-2.651-18.845-2.796-3.525-5.593-4.609-10.823-5.044-9.549-0.81-16.677 0.762-19.048 5.145-1.133 2.202-1.978 5.333-1.675 10.694 0.419 7.202 4.614 8.003 7.086 12.566 2.52 4.638 10.103 7.643 11.326 7.555z"/>\r
+   <path fill="#efefef" d="m193.203 94.239c10.055-0.778 14.65-5.792 15.739-12.04 1.25-7.293 1.531-13.785-2.629-18.653-2.694-3.258-5.502-4.33-10.655-4.761-9.517-0.807-16.785 0.443-19.165 4.786-1.059 2.01-1.983 5.372-1.671 10.642 0.435 7.198 4.636 7.928 7.104 12.492 2.52 4.649 10.055 7.624 11.277 7.534z"/>\r
+   <path fill="#f7f7f7" d="m193.208 94.198c10.014-0.757 14.652-5.817 15.694-12.009 1.223-7.339 1.516-13.792-2.607-18.462-2.592-2.991-5.413-4.05-10.486-4.478-9.487-0.804-16.895 0.124-19.282 4.428-0.986 1.817-1.989 5.41-1.668 10.589 0.451 7.194 4.657 7.851 7.123 12.417 2.519 4.661 10.004 7.605 11.226 7.515z"/>\r
+   <path fill="#fff" d="m193.213 94.156c9.973-0.737 14.654-5.843 15.648-11.978 1.197-7.384 1.501-13.797-2.585-18.27-2.489-2.724-5.322-3.771-10.319-4.194-9.455-0.801-17.002-0.196-19.397 4.07-0.913 1.625-1.996 5.448-1.665 10.536 0.467 7.19 4.679 7.775 7.142 12.342 2.519 4.671 9.954 7.586 11.176 7.494z"/>\r
+  </g>\r
+  <path d="m179.841 74.4585c5.4 0 8.568 4.824 9.648 11.016 0.432 2.808-0.216 6.048-1.944 8.28-1.944 2.592-5.4 4.176-8.208 4.176-2.664 0-5.688 0.432-7.271-1.728-1.584-2.232-1.944-7.2-1.944-10.728 0-3.96 1.152-6.768 3.168-9 1.511-1.657 4.247-2.016 6.551-2.016z"/>\r
+  <g transform="translate(-12.4048,10.0005)">\r
+   <path d="m192.591 66.68c0.98-0.653 2.612 0 4.489 2.122 2.039 2.285 2.938 4.08 0.489 5.385-1.877 0.98-2.448-1.958-3.59-3.182-1.795-1.959-3.346-3.02-1.388-4.325z"/>\r
+   <path fill="#070707" d="m192.631 66.738c0.96-0.649 2.573 0 4.423 2.09 2.009 2.251 2.864 4.02 0.481 5.305-1.837 0.977-2.403-1.929-3.525-3.135-1.768-1.925-3.296-2.965-1.379-4.26z"/>\r
+   <path fill="#0f0f0f" d="m192.671 66.797c0.939-0.645 2.534 0 4.356 2.059 1.978 2.217 2.792 3.958 0.474 5.225-1.798 0.974-2.357-1.9-3.46-3.087-1.742-1.895-3.247-2.913-1.37-4.197z"/>\r
+   <path fill="#161616" d="m192.711 66.855c0.919-0.641 2.495 0 4.289 2.027 1.948 2.184 2.721 3.898 0.467 5.146-1.759 0.971-2.313-1.871-3.396-3.041-1.715-1.861-3.197-2.858-1.36-4.132z"/>\r
+   <path fill="#1e1e1e" d="m192.751 66.914c0.899-0.637 2.457 0 4.223 1.996 1.918 2.149 2.647 3.838 0.46 5.065-1.72 0.968-2.269-1.842-3.331-2.993-1.689-1.83-3.148-2.805-1.352-4.068z"/>\r
+   <path fill="#262626" d="m192.791 66.973c0.878-0.633 2.418 0 4.155 1.964 1.888 2.116 2.576 3.777 0.453 4.986-1.68 0.965-2.224-1.813-3.267-2.946-1.661-1.798-3.097-2.752-1.341-4.004z"/>\r
+   <path fill="#2d2d2d" d="m192.831 67.031c0.858-0.629 2.379 0 4.089 1.933 1.857 2.082 2.503 3.717 0.445 4.906-1.641 0.961-2.178-1.784-3.201-2.898-1.636-1.767-3.048-2.7-1.333-3.941z"/>\r
+   <path fill="#353535" d="m192.87 67.09c0.838-0.625 2.341 0 4.023 1.902 1.827 2.047 2.431 3.656 0.438 4.826-1.601 0.958-2.133-1.755-3.137-2.852-1.608-1.735-2.998-2.646-1.324-3.876z"/>\r
+   <path fill="#3d3d3d" d="m192.91 67.148c0.818-0.621 2.302 0 3.956 1.87 1.797 2.014 2.359 3.596 0.431 4.746-1.562 0.956-2.088-1.726-3.071-2.804-1.583-1.702-2.95-2.592-1.316-3.812z"/>\r
+   <path fill="#444" d="m192.95 67.207c0.798-0.617 2.263 0 3.889 1.839 1.768 1.98 2.287 3.535 0.425 4.666-1.523 0.952-2.043-1.697-3.008-2.757-1.556-1.671-2.899-2.539-1.306-3.748z"/>\r
+   <path fill="#4c4c4c" d="m192.99 67.266c0.777-0.614 2.224 0 3.823 1.807 1.735 1.946 2.214 3.474 0.416 4.586-1.483 0.949-1.998-1.667-2.942-2.709-1.529-1.639-2.85-2.486-1.297-3.684z"/>\r
+   <path fill="#545454" d="m193.03 67.325c0.757-0.61 2.185 0 3.756 1.775 1.706 1.912 2.143 3.414 0.409 4.506-1.444 0.946-1.953-1.639-2.878-2.663-1.502-1.606-2.799-2.431-1.287-3.618z"/>\r
+   <path fill="#5b5b5b" d="m193.07 67.383c0.736-0.605 2.146 0 3.688 1.744 1.677 1.878 2.07 3.353 0.402 4.426-1.405 0.943-1.908-1.609-2.813-2.615-1.475-1.575-2.749-2.378-1.277-3.555z"/>\r
+   <path fill="#636363" d="m193.11 67.442c0.716-0.602 2.106 0 3.622 1.712 1.646 1.844 1.998 3.293 0.395 4.347-1.364 0.94-1.862-1.581-2.748-2.568-1.449-1.543-2.701-2.326-1.269-3.491z"/>\r
+   <path fill="#6b6b6b" d="m193.15 67.5c0.696-0.598 2.069 0 3.556 1.681 1.615 1.811 1.925 3.232 0.387 4.267-1.325 0.937-1.818-1.552-2.683-2.521-1.423-1.511-2.651-2.272-1.26-3.427z"/>\r
+   <path fill="#727272" d="m193.19 67.559c0.675-0.594 2.03 0 3.489 1.649 1.585 1.777 1.853 3.172 0.38 4.187-1.287 0.935-1.774-1.522-2.619-2.473-1.396-1.48-2.601-2.219-1.25-3.363z"/>\r
+   <path fill="#7a7a7a" d="m193.23 67.618c0.654-0.59 1.991 0 3.422 1.618 1.555 1.743 1.781 3.111 0.373 4.107-1.247 0.931-1.729-1.494-2.554-2.426-1.369-1.448-2.551-2.166-1.241-3.299z"/>\r
+   <path fill="#828282" d="m193.269 67.677c0.635-0.586 1.953 0 3.355 1.586 1.525 1.708 1.709 3.05 0.366 4.026-1.208 0.928-1.684-1.464-2.489-2.378-1.342-1.416-2.501-2.112-1.232-3.234z"/>\r
+   <path fill="#898989" d="m193.309 67.735c0.614-0.582 1.914 0 3.29 1.555 1.493 1.675 1.636 2.99 0.357 3.947-1.169 0.925-1.639-1.435-2.424-2.332-1.316-1.384-2.452-2.058-1.223-3.17z"/>\r
+   <path fill="#919191" d="m193.349 67.794c0.595-0.578 1.875 0 3.223 1.523 1.464 1.641 1.564 2.93 0.351 3.867-1.129 0.922-1.594-1.406-2.359-2.284-1.29-1.352-2.403-2.005-1.215-3.106z"/>\r
+   <path fill="#999" d="m193.389 67.853c0.573-0.574 1.836 0 3.155 1.492 1.435 1.607 1.492 2.869 0.345 3.787-1.091 0.919-1.55-1.377-2.295-2.237-1.263-1.32-2.353-1.953-1.205-3.042z"/>\r
+  </g>\r
+  <g transform="translate(-12.4048,10.0005)">\r
+   <path d="m165.498 69.906c1.693-0.654 3.012-0.69 5.63 1.036 3.166 2.088 1.705 5.245-0.779 4.601-2.146-0.556-2.417-0.681-4.391-1.086-3.101-0.648-3.641-3.322-0.46-4.551z"/>\r
+   <path fill="#050505" d="m165.564 70.033c1.658-0.629 2.973-0.656 5.555 1.026 3.066 2.009 1.654 5.012-0.805 4.38-2.131-0.547-2.345-0.656-4.284-1.052-3.055-0.634-3.587-3.173-0.466-4.354z"/>\r
+   <path fill="#0a0a0a" d="m165.63 70.16c1.623-0.604 2.935-0.622 5.481 1.015 2.965 1.93 1.602 4.779-0.83 4.159-2.119-0.539-2.274-0.63-4.179-1.018-3.009-0.618-3.533-3.022-0.472-4.156z"/>\r
+   <path fill="#0f0f0f" d="m165.696 70.287c1.587-0.579 2.895-0.587 5.406 1.005 2.864 1.851 1.551 4.546-0.855 3.938-2.105-0.53-2.203-0.605-4.073-0.983-2.963-0.604-3.48-2.873-0.478-3.96z"/>\r
+   <path fill="#141414" d="m165.761 70.413c1.553-0.553 2.856-0.553 5.331 0.995 2.766 1.772 1.5 4.313-0.88 3.717-2.092-0.521-2.131-0.58-3.967-0.949-2.916-0.588-3.425-2.723-0.484-3.763z"/>\r
+   <path fill="#191919" d="m165.827 70.54c1.519-0.528 2.818-0.519 5.258 0.984 2.664 1.693 1.448 4.079-0.905 3.497-2.079-0.513-2.06-0.554-3.861-0.915-2.873-0.573-3.373-2.573-0.492-3.566z"/>\r
+   <path fill="#1e1e1e" d="m165.893 70.667c1.482-0.503 2.778-0.484 5.183 0.974 2.564 1.614 1.397 3.846-0.93 3.276-2.067-0.504-1.989-0.529-3.756-0.88-2.826-0.559-3.319-2.425-0.497-3.37z"/>\r
+   <path fill="#232323" d="m165.959 70.793c1.447-0.478 2.74-0.45 5.108 0.964 2.464 1.535 1.345 3.613-0.955 3.055-2.053-0.496-1.917-0.503-3.651-0.846-2.779-0.543-3.264-2.274-0.502-3.173z"/>\r
+   <path fill="#282828" d="m166.025 70.92c1.412-0.453 2.701-0.416 5.034 0.954 2.362 1.456 1.293 3.38-0.981 2.834-2.04-0.487-1.845-0.478-3.545-0.812-2.733-0.528-3.21-2.125-0.508-2.976z"/>\r
+   <path fill="#2d2d2d" d="m166.09 71.047c1.378-0.428 2.663-0.382 4.96 0.943 2.264 1.377 1.242 3.146-1.006 2.613-2.026-0.478-1.773-0.453-3.438-0.777-2.688-0.513-3.158-1.974-0.516-2.779z"/>\r
+   <path fill="#333" d="m166.156 71.173c1.343-0.402 2.624-0.347 4.885 0.933 2.163 1.298 1.191 2.914-1.029 2.392-2.015-0.47-1.703-0.428-3.334-0.743-2.642-0.498-3.104-1.824-0.522-2.582z"/>\r
+   <path fill="#383838" d="m166.222 71.3c1.307-0.377 2.585-0.313 4.81 0.922 2.063 1.219 1.14 2.681-1.055 2.171-2.001-0.461-1.631-0.402-3.229-0.708-2.594-0.483-3.048-1.674-0.526-2.385z"/>\r
+   <path fill="#3d3d3d" d="m166.288 71.427c1.272-0.352 2.546-0.279 4.736 0.913 1.962 1.14 1.088 2.447-1.081 1.95-1.988-0.452-1.56-0.377-3.122-0.674-2.55-0.469-2.995-1.526-0.533-2.189z"/>\r
+   <path fill="#424242" d="m166.354 71.554c1.236-0.327 2.507-0.245 4.661 0.902 1.861 1.061 1.037 2.214-1.106 1.729-1.974-0.444-1.488-0.352-3.016-0.64-2.504-0.453-2.942-1.375-0.539-1.991z"/>\r
+   <path fill="#474747" d="m166.419 71.68c1.203-0.302 2.469-0.21 4.587 0.892 1.762 0.982 0.986 1.98-1.13 1.508-1.962-0.435-1.417-0.326-2.911-0.606-2.458-0.437-2.888-1.224-0.546-1.794z"/>\r
+   <path fill="#4c4c4c" d="m166.485 71.807c1.167-0.276 2.429-0.176 4.513 0.882 1.66 0.903 0.935 1.748-1.156 1.288-1.948-0.426-1.345-0.301-2.805-0.572-2.412-0.423-2.834-1.076-0.552-1.598z"/>\r
+   <path fill="#515151" d="m166.551 71.934c1.133-0.251 2.391-0.142 4.438 0.871 1.56 0.824 0.883 1.515-1.181 1.067-1.936-0.417-1.274-0.275-2.699-0.537-2.366-0.408-2.781-0.926-0.558-1.401z"/>\r
+   <path fill="#565656" d="m166.617 72.061c1.097-0.227 2.351-0.108 4.363 0.861 1.46 0.745 0.831 1.281-1.206 0.846-1.922-0.409-1.202-0.25-2.594-0.503-2.319-0.393-2.726-0.777-0.563-1.204z"/>\r
+   <path fill="#5b5b5b" d="m166.683 72.187c1.062-0.201 2.312-0.073 4.289 0.851 1.358 0.666 0.778 1.048-1.231 0.625-1.91-0.4-1.131-0.225-2.489-0.469-2.274-0.377-2.672-0.626-0.569-1.007z"/>\r
+   <path fill="#606060" d="m166.748 72.314c1.027-0.176 2.274-0.04 4.215 0.84 1.26 0.587 0.729 0.815-1.256 0.404-1.896-0.392-1.06-0.2-2.383-0.435-2.228-0.361-2.619-0.475-0.576-0.809z"/>\r
+   <path fill="#666" d="m166.814 72.44c0.992-0.151 2.234-0.005 4.14 0.83 1.159 0.508 0.677 0.582-1.281 0.183-1.883-0.383-0.987-0.174-2.276-0.4-2.183-0.346-2.566-0.325-0.583-0.613z"/>\r
+  </g>\r
+  <g transform="translate(-12.4048,10.0005)">\r
+   <path fill="#666" d="m159.99 128.249c-9.36 0.36-24.192-25.848-24.552-14.976-0.288 9.216 0.216 9.072 0.216 18 0 5.976-2.736 6.408-8.64 15.408-3.024 4.752-5.4 9.864-7.272 15.048-1.152 3.096-2.232 6.336-3.096 9.504-0.36 1.584-1.008 3.24-1.368 4.824-2.952 10.872-13.464 24.264-15.912 35.136-2.448 10.8-5.328 17.712-4.968 32.185 0.36 14.472 0.504 10.295 4.896 13.896 4.32 3.601 8.784 6.983 15.624 13.032 7.2 6.264 22.177 17.208 24.192 20.592 2.16 3.456 2.088 11.232 0.792 13.752-1.296 2.448-12.6 3.816-12.528 3.816-0.071 0 9.864 13.68 11.809 15.623 1.872 1.873 9.936 10.873 42.768 4.752 18.504-3.455 32.832-13.823 43.2-23.832 13.392-13.031 6.624-16.775 8.352-23.327 2.521-9.433 10.729-12.96 12.601-23.616 0.216-1.512 0.72-2.664 2.088-4.896 2.088-3.168 1.584-9.432 1.584-15.191 0-14.977-1.729-30.24-5.185-41.472-3.168-10.512-8.208-17.856-12.527-27.36-8.641-18.936-8.208-27.432-15.912-39.528-8.784-13.968-4.464-23.256-16.128-22.68-14.546 0.79-26.282 20.734-40.034 21.31z"/>\r
+   <path fill="#6d6d6d" d="m159.973 129.334c-9.281 0.353-23.746-25.511-24.242-15.179-0.316 8.755 0.1 8.678 0.03 17.247-0.15 5.87-2.953 6.637-8.727 15.481-3.013 4.763-5.273 9.812-6.993 14.877-0.968 3.253-1.56 6.422-2.43 9.526-0.415 1.642-1.497 3.187-2.185 5.042-3.254 10.78-13.545 24.182-15.961 34.877-2.466 10.81-5.37 17.694-4.961 32.141 0.366 14 0.395 10.177 4.773 13.816 4.283 3.616 8.839 7.069 15.662 13.103 7.183 6.248 22.237 17.216 24.243 20.588 2.149 3.444 2.131 11.317 0.844 13.823-1.284 2.439-12.579 3.875-12.508 3.875-0.071 0 9.815 13.566 11.757 15.508 1.87 1.87 9.902 10.809 42.678 4.704 18.524-3.455 33.124-13.753 43.078-23.856 12.789-12.762 6.107-16.773 7.826-23.291 2.513-9.416 11.277-12.961 13.143-23.602 0.216-1.508 0.754-2.654 2.113-4.876 2.096-3.202 1.561-9.447 1.582-15.185 0.067-15.027-1.705-30.234-5.159-41.434-3.171-10.483-8.204-17.817-12.515-27.305-8.624-18.906-8.221-27.415-15.933-39.474-8.586-13.613-4.601-22.583-16.011-21.99-14.374 0.826-26.375 21.016-40.104 21.584z"/>\r
+   <path fill="#757575" d="m159.955 130.419c-9.201 0.346-23.299-25.175-23.931-15.383-0.344 8.295-0.017 8.284-0.156 16.494-0.301 5.764-3.17 6.867-8.812 15.555-3.002 4.774-5.148 9.76-6.714 14.706-0.784 3.41-0.889 6.508-1.764 9.548-0.471 1.699-1.986 3.133-3.003 5.259-3.554 10.688-13.624 24.1-16.009 34.619-2.483 10.82-5.411 17.678-4.954 32.097 0.373 13.528 0.285 10.058 4.651 13.739 4.244 3.632 8.893 7.154 15.699 13.171 7.167 6.233 22.299 17.224 24.294 20.585 2.142 3.432 2.175 11.404 0.896 13.896-1.271 2.428-12.558 3.932-12.486 3.932-0.071 0 9.768 13.453 11.705 15.392 1.867 1.867 9.867 10.744 42.588 4.655 18.545-3.453 33.415-13.682 42.956-23.879 12.187-12.492 5.591-16.771 7.3-23.258 2.507-9.398 11.826-12.959 13.687-23.586 0.215-1.5 0.788-2.643 2.138-4.854 2.104-3.235 1.538-9.462 1.58-15.178 0.133-15.076-1.681-30.228-5.135-41.394-3.173-10.455-8.199-17.779-12.501-27.25-8.609-18.877-8.234-27.399-15.952-39.42-8.389-13.258-4.739-21.911-15.895-21.301-14.21 0.859-26.474 21.295-40.182 21.855z"/>\r
+   <path fill="#7c7c7c" d="m159.938 131.504c-9.122 0.338-22.854-24.838-23.622-15.586-0.37 7.833-0.131 7.89-0.341 15.741-0.452 5.657-3.388 7.096-8.899 15.628-2.99 4.785-5.021 9.708-6.433 14.535-0.602 3.566-0.218 6.594-1.099 9.57-0.526 1.756-2.475 3.08-3.82 5.477-3.854 10.596-13.703 24.016-16.057 34.361-2.501 10.829-5.453 17.66-4.948 32.052 0.38 13.059 0.177 9.939 4.529 13.66 4.208 3.648 8.948 7.239 15.739 13.242 7.149 6.217 22.358 17.232 24.345 20.581 2.13 3.42 2.216 11.489 0.946 13.968-1.259 2.417-12.538 3.99-12.466 3.99-0.072 0 9.718 13.34 11.653 15.275 1.865 1.864 9.833 10.681 42.498 4.607 18.565-3.453 33.706-13.609 42.834-23.902 11.583-12.223 5.074-16.771 6.774-23.223 2.499-9.382 12.375-12.959 14.229-23.57 0.215-1.496 0.821-2.633 2.162-4.834 2.111-3.271 1.516-9.478 1.578-15.173 0.199-15.125-1.657-30.221-5.109-41.354-3.177-10.427-8.196-17.741-12.488-27.195-8.594-18.848-8.247-27.383-15.972-39.366-8.192-12.903-4.877-21.239-15.779-20.612-14.041 0.894-26.569 21.576-40.254 22.128z"/>\r
+   <path fill="#848484" d="m159.921 132.589c-9.043 0.331-22.406-24.502-23.312-15.79-0.398 7.373-0.247 7.496-0.527 14.988-0.602 5.551-3.604 7.326-8.984 15.702-2.98 4.796-4.896 9.656-6.154 14.364-0.417 3.723 0.455 6.679-0.432 9.592-0.582 1.813-2.964 3.026-4.639 5.694-4.153 10.504-13.782 23.936-16.104 34.104-2.519 10.838-5.495 17.643-4.941 32.008 0.387 12.586 0.067 9.819 4.407 13.582 4.171 3.664 9.002 7.324 15.777 13.311 7.132 6.201 22.419 17.24 24.396 20.576 2.12 3.41 2.259 11.578 0.998 14.041-1.247 2.408-12.517 4.049-12.446 4.049-0.07 0 9.67 13.227 11.604 15.16 1.861 1.861 9.798 10.615 42.409 4.558 18.584-3.45 33.996-13.538 42.711-23.926 10.979-11.952 4.557-16.769 6.248-23.187 2.491-9.367 12.924-12.959 14.771-23.557 0.215-1.49 0.856-2.622 2.188-4.813 2.118-3.305 1.491-9.494 1.575-15.166 0.267-15.174-1.635-30.215-5.086-41.314-3.179-10.399-8.19-17.703-12.473-27.141-8.579-18.818-8.262-27.366-15.994-39.312-7.993-12.547-5.013-20.565-15.661-19.922-13.876 0.927-26.669 21.855-40.331 22.399z"/>\r
+   <path fill="#8c8c8c" d="m159.903 133.674c-8.963 0.323-21.961-24.165-23.001-15.994-0.426 6.912-0.363 7.102-0.713 14.236-0.753 5.445-3.821 7.554-9.071 15.775-2.969 4.807-4.768 9.604-5.875 14.192-0.232 3.881 1.128 6.766 0.234 9.615-0.638 1.87-3.452 2.972-5.455 5.911-4.455 10.413-13.862 23.853-16.153 33.845-2.537 10.849-5.537 17.625-4.935 31.963 0.393 12.115-0.042 9.701 4.285 13.505 4.133 3.68 9.057 7.409 15.814 13.38 7.116 6.188 22.48 17.248 24.447 20.574 2.109 3.398 2.301 11.662 1.049 14.113-1.235 2.396-12.496 4.104-12.425 4.104-0.071 0 9.622 13.114 11.552 15.045 1.86 1.858 9.763 10.552 42.319 4.509 18.604-3.449 34.288-13.467 42.589-23.949 10.377-11.682 4.04-16.766 5.721-23.15 2.486-9.35 13.474-12.959 15.316-23.542 0.214-1.483 0.89-2.611 2.213-4.793 2.126-3.339 1.468-9.507 1.573-15.158 0.333-15.224-1.611-30.208-5.062-41.276-3.181-10.37-8.186-17.664-12.459-27.085-8.563-18.789-8.275-27.35-16.014-39.258-7.796-12.192-5.151-19.893-15.545-19.233-13.707 0.961-26.763 22.134-40.404 22.671z"/>\r
+   <path fill="#939393" d="m159.886 134.759c-8.885 0.316-21.516-23.829-22.691-16.197-0.454 6.451-0.479 6.708-0.899 13.482-0.903 5.339-4.038 7.784-9.157 15.849-2.957 4.818-4.642 9.552-5.595 14.021-0.05 4.037 1.799 6.852 0.9 9.637-0.693 1.928-3.941 2.919-6.273 6.129-4.756 10.32-13.941 23.77-16.201 33.587-2.555 10.858-5.579 17.608-4.928 31.92 0.399 11.644-0.151 9.581 4.162 13.424 4.096 3.697 9.111 7.494 15.854 13.451 7.099 6.17 22.541 17.256 24.498 20.569 2.1 3.387 2.344 11.75 1.101 14.186-1.223 2.387-12.476 4.163-12.404 4.163-0.071 0 9.573 13.001 11.5 14.929 1.857 1.856 9.729 10.488 42.229 4.461 18.625-3.449 34.579-13.396 42.467-23.973 9.774-11.412 3.523-16.764 5.195-23.115 2.479-9.334 14.022-12.959 15.858-23.527 0.214-1.479 0.924-2.601 2.238-4.772 2.134-3.373 1.445-9.522 1.571-15.151 0.399-15.273-1.587-30.201-5.036-41.237-3.185-10.342-8.184-17.625-12.446-27.03-8.548-18.76-8.288-27.333-16.034-39.204-7.598-11.837-5.289-19.221-15.428-18.544-13.543 0.994-26.863 22.413-40.481 22.942z"/>\r
+   <path fill="#9b9b9b" d="m159.868 135.844c-8.805 0.308-21.068-23.492-22.381-16.401-0.481 5.991-0.594 6.314-1.085 12.73-1.053 5.232-4.253 8.013-9.243 15.922-2.946 4.829-4.515 9.5-5.314 13.85 0.133 4.194 2.471 6.937 1.565 9.658-0.749 1.986-4.43 2.866-7.091 6.347-5.056 10.229-14.021 23.689-16.249 33.329-2.572 10.868-5.621 17.591-4.921 31.876 0.405 11.172-0.261 9.463 4.04 13.346 4.058 3.713 9.166 7.58 15.892 13.521 7.082 6.155 22.601 17.265 24.548 20.567 2.092 3.373 2.388 11.834 1.152 14.256-1.21 2.377-12.454 4.222-12.383 4.222-0.071 0 9.523 12.888 11.45 14.813 1.854 1.854 9.692 10.424 42.138 4.412 18.645-3.447 34.871-13.324 42.345-23.996 9.171-11.143 3.007-16.762 4.669-23.08 2.472-9.317 14.572-12.959 16.401-23.514 0.214-1.473 0.958-2.588 2.265-4.75 2.142-3.408 1.421-9.539 1.568-15.145 0.466-15.324-1.564-30.196-5.012-41.198-3.187-10.313-8.179-17.587-12.433-26.976-8.533-18.73-8.301-27.316-16.054-39.149-7.401-11.482-5.426-18.548-15.313-17.855-13.373 1.029-26.958 22.694-40.554 23.215z"/>\r
+   <path fill="#a3a3a3" d="m159.851 136.929c-8.727 0.301-20.622-23.156-22.071-16.604-0.509 5.529-0.71 5.919-1.271 11.976-1.203 5.126-4.47 8.243-9.328 15.996-2.936 4.84-4.39 9.448-5.036 13.679 0.316 4.351 3.143 7.023 2.231 9.68-0.804 2.043-4.919 2.812-7.908 6.563-5.356 10.137-14.101 23.607-16.298 33.072-2.589 10.877-5.661 17.574-4.913 31.832 0.412 10.699-0.37 9.342 3.918 13.268 4.021 3.729 9.221 7.664 15.93 13.59 7.064 6.139 22.661 17.271 24.599 20.563 2.081 3.363 2.43 11.922 1.204 14.33-1.198 2.365-12.434 4.278-12.363 4.278-0.07 0 9.477 12.774 11.399 14.697 1.851 1.851 9.659 10.36 42.048 4.364 18.666-3.447 35.162-13.254 42.223-24.021 8.568-10.873 2.49-16.761 4.144-23.045 2.464-9.301 15.121-12.958 16.943-23.498 0.215-1.467 0.992-2.579 2.29-4.729 2.148-3.441 1.398-9.553 1.566-15.139 0.532-15.373-1.541-30.189-4.987-41.158-3.188-10.285-8.174-17.549-12.419-26.921-8.518-18.701-8.313-27.3-16.073-39.096-7.204-11.126-5.564-17.875-15.196-17.165-13.21 1.064-27.058 22.975-40.632 23.488z"/>\r
+   <path fill="#aaa" d="m159.834 138.014c-8.646 0.293-20.176-22.819-21.761-16.808-0.536 5.069-0.826 5.526-1.457 11.224-1.354 5.02-4.687 8.472-9.416 16.069-2.924 4.851-4.262 9.396-4.756 13.508 0.501 4.507 3.814 7.109 2.897 9.702-0.858 2.1-5.406 2.759-8.725 6.782-5.657 10.045-14.181 23.524-16.347 32.812-2.606 10.888-5.703 17.557-4.906 31.787 0.418 10.229-0.479 9.225 3.795 13.189 3.984 3.745 9.275 7.749 15.968 13.66 7.048 6.124 22.723 17.279 24.651 20.559 2.07 3.352 2.472 12.008 1.255 14.402-1.186 2.355-12.414 4.337-12.343 4.337-0.071 0 9.428 12.66 11.348 14.581 1.85 1.848 9.624 10.297 41.958 4.314 18.687-3.444 35.453-13.18 42.102-24.043 7.965-10.602 1.973-16.758 3.616-23.01 2.457-9.283 15.67-12.957 17.487-23.482 0.214-1.461 1.026-2.568 2.315-4.709 2.155-3.477 1.375-9.568 1.563-15.131 0.6-15.424-1.518-30.184-4.963-41.119-3.192-10.257-8.17-17.511-12.405-26.866-8.502-18.672-8.328-27.284-16.095-39.042-7.005-10.771-5.701-17.203-15.078-16.476-13.04 1.098-27.152 23.255-40.703 23.76z"/>\r
+   <path fill="#b2b2b2" d="m159.816 139.099c-8.567 0.286-19.729-22.483-21.45-17.012-0.563 4.608-0.942 5.132-1.643 10.471-1.506 4.914-4.904 8.701-9.502 16.143-2.913 4.862-4.137 9.344-4.477 13.336 0.685 4.665 4.486 7.195 3.564 9.725-0.915 2.157-5.897 2.705-9.543 6.999-5.958 9.953-14.262 23.443-16.396 32.554-2.624 10.898-5.745 17.54-4.9 31.744 0.426 9.757-0.588 9.105 3.674 13.111 3.945 3.761 9.33 7.834 16.006 13.729 7.032 6.109 22.783 17.288 24.702 20.557 2.06 3.338 2.515 12.094 1.306 14.473-1.173 2.346-12.392 4.395-12.321 4.395-0.07 0 9.379 12.549 11.296 14.465 1.847 1.848 9.591 10.234 41.868 4.268 18.706-3.444 35.745-13.11 41.979-24.066 7.361-10.332 1.456-16.757 3.091-22.974 2.45-9.269 16.219-12.958 18.03-23.47 0.213-1.455 1.06-2.557 2.34-4.688 2.164-3.509 1.352-9.583 1.562-15.124 0.665-15.473-1.494-30.177-4.938-41.08-3.195-10.228-8.166-17.472-12.393-26.811-8.486-18.642-8.341-27.267-16.114-38.987-6.809-10.416-5.838-16.531-14.962-15.787-12.873 1.129-27.25 23.531-40.779 24.029z"/>\r
+   <path fill="#bababa" d="m159.799 140.184c-8.487 0.279-19.282-22.146-21.141-17.215-0.591 4.147-1.057 4.737-1.828 9.717-1.656 4.808-5.121 8.931-9.588 16.217-2.902 4.873-4.01 9.292-4.197 13.165 0.868 4.822 5.158 7.281 4.23 9.747-0.971 2.215-6.385 2.651-10.361 7.216-6.258 9.861-14.339 23.36-16.442 32.297-2.643 10.906-5.787 17.521-4.894 31.699 0.432 9.285-0.697 8.986 3.552 13.032 3.908 3.776 9.384 7.919 16.043 13.799 7.016 6.093 22.845 17.296 24.753 20.552 2.051 3.328 2.559 12.18 1.358 14.547-1.161 2.334-12.372 4.451-12.301 4.451-0.071 0 9.33 12.436 11.245 14.35 1.844 1.844 9.555 10.17 41.777 4.219 18.727-3.443 36.036-13.039 41.857-24.09 6.759-10.063 0.939-16.756 2.565-22.939 2.442-9.25 16.768-12.957 18.572-23.453 0.213-1.451 1.095-2.547 2.365-4.668 2.171-3.543 1.329-9.599 1.56-15.117 0.732-15.522-1.471-30.172-4.913-41.042-3.197-10.2-8.161-17.433-12.379-26.756-8.471-18.612-8.354-27.25-16.135-38.933-6.609-10.061-5.976-15.858-14.845-15.098-12.706 1.165-27.347 23.813-40.853 24.303z"/>\r
+   <path fill="#c1c1c1" d="m159.781 141.269c-8.408 0.271-18.837-21.81-20.83-17.419-0.619 3.687-1.173 4.344-2.014 8.965-1.808 4.701-5.338 9.16-9.674 16.29-2.892 4.884-3.885 9.24-3.918 12.994 1.052 4.978 5.829 7.367 4.896 9.769-1.026 2.272-6.874 2.598-11.178 7.434-6.56 9.769-14.419 23.277-16.491 32.039-2.66 10.916-5.829 17.504-4.887 31.656 0.438 8.813-0.807 8.867 3.43 12.953 3.87 3.793 9.438 8.004 16.082 13.868 6.997 6.077 22.904 17.304 24.803 20.55 2.041 3.314 2.601 12.266 1.409 14.617-1.149 2.324-12.351 4.51-12.28 4.51-0.07 0 9.282 12.321 11.194 14.233 1.841 1.842 9.521 10.106 41.688 4.17 18.746-3.44 36.326-12.967 41.734-24.112 6.156-9.793 0.423-16.754 2.038-22.904 2.438-9.235 17.318-12.957 19.117-23.438 0.212-1.444 1.128-2.536 2.39-4.647 2.18-3.578 1.306-9.613 1.558-15.11 0.799-15.571-1.447-30.165-4.889-41.002-3.2-10.172-8.156-17.395-12.364-26.701-8.456-18.583-8.367-27.234-16.155-38.88-6.413-9.705-6.114-15.185-14.729-14.408-12.541 1.197-27.445 24.091-40.93 24.573z"/>\r
+   <path fill="#c9c9c9" d="m159.764 142.354c-8.329 0.264-18.392-21.473-20.521-17.622-0.646 3.225-1.289 3.949-2.2 8.211-1.957 4.596-5.555 9.39-9.761 16.364-2.879 4.895-3.757 9.188-3.638 12.823 1.235 5.135 6.502 7.453 5.562 9.791-1.081 2.329-7.362 2.544-11.995 7.651-6.859 9.677-14.499 23.195-16.54 31.78-2.677 10.927-5.87 17.488-4.879 31.611 0.444 8.344-0.916 8.748 3.307 12.875 3.834 3.81 9.492 8.09 16.121 13.939 6.98 6.061 22.965 17.311 24.854 20.545 2.031 3.303 2.643 12.352 1.461 14.69-1.137 2.313-12.33 4.567-12.26 4.567-0.07 0 9.233 12.209 11.143 14.117 1.839 1.84 9.486 10.043 41.599 4.122 18.767-3.44 36.618-12.896 41.612-24.137 5.554-9.522-0.094-16.751 1.513-22.868 2.43-9.219 17.866-12.957 19.659-23.424 0.213-1.439 1.162-2.525 2.415-4.627 2.188-3.612 1.282-9.629 1.556-15.104 0.865-15.621-1.424-30.158-4.864-40.962-3.202-10.144-8.153-17.357-12.351-26.646-8.441-18.554-8.381-27.218-16.176-38.826-6.216-9.35-6.251-14.513-14.612-13.719-12.374 1.235-27.543 24.375-41.005 24.849z"/>\r
+   <path fill="#d1d1d1" d="m159.747 143.439c-8.25 0.256-17.944-21.137-20.21-17.826-0.675 2.765-1.406 3.555-2.386 7.459-2.108 4.489-5.772 9.619-9.847 16.437-2.869 4.906-3.631 9.136-3.358 12.652 1.419 5.292 7.174 7.538 6.228 9.812-1.137 2.387-7.852 2.491-12.813 7.869-7.161 9.586-14.579 23.114-16.588 31.522-2.695 10.938-5.912 17.471-4.873 31.568 0.451 7.871-1.025 8.629 3.185 12.797 3.796 3.824 9.547 8.174 16.158 14.008 6.964 6.047 23.026 17.32 24.905 20.541 2.021 3.292 2.686 12.439 1.513 14.764-1.125 2.303-12.31 4.625-12.239 4.625-0.07 0 9.186 12.094 11.092 14.002 1.836 1.836 9.45 9.978 41.509 4.072 18.786-3.439 36.909-12.824 41.49-24.16 4.948-9.252-0.611-16.748 0.985-22.832 2.423-9.203 18.415-12.957 20.203-23.41 0.212-1.434 1.196-2.514 2.44-4.605 2.193-3.646 1.259-9.645 1.553-15.098 0.932-15.67-1.4-30.151-4.84-40.922-3.205-10.115-8.148-17.319-12.336-26.592-8.427-18.524-8.396-27.201-16.197-38.771-6.017-8.995-6.388-13.84-14.495-13.03-12.207 1.266-27.64 24.652-41.079 25.118z"/>\r
+   <path fill="#d8d8d8" d="m159.729 144.524c-8.17 0.249-17.498-20.8-19.9-18.03-0.702 2.304-1.521 3.162-2.571 6.706-2.259 4.383-5.988 9.848-9.933 16.511-2.858 4.917-3.504 9.084-3.079 12.48 1.604 5.449 7.846 7.625 6.895 9.835-1.193 2.444-8.342 2.438-13.631 8.087-7.461 9.493-14.658 23.031-16.637 31.262-2.712 10.947-5.953 17.455-4.865 31.524 0.458 7.399-1.135 8.511 3.063 12.718 3.758 3.842 9.601 8.26 16.196 14.078 6.946 6.031 23.087 17.328 24.956 20.538 2.011 3.28 2.729 12.524 1.563 14.835-1.112 2.293-12.289 4.684-12.218 4.684-0.071 0 9.136 11.981 11.04 13.886 1.834 1.834 9.417 9.913 41.419 4.024 18.807-3.438 37.2-12.752 41.368-24.184 4.346-8.982-1.128-16.747 0.46-22.798 2.416-9.187 18.964-12.956 20.746-23.394 0.211-1.429 1.229-2.504 2.465-4.586 2.202-3.681 1.236-9.658 1.551-15.091 0.998-15.72-1.377-30.146-4.814-40.884-3.208-10.086-8.145-17.28-12.323-26.536-8.411-18.495-8.408-27.185-16.217-38.717-5.82-8.64-6.526-13.168-14.38-12.341-12.04 1.303-27.736 24.934-41.154 25.393z"/>\r
+   <path fill="#e0e0e0" d="m159.712 145.609c-8.091 0.241-17.052-20.464-19.59-18.233-0.729 1.843-1.637 2.767-2.757 5.953-2.409 4.276-6.206 10.077-10.02 16.584-2.847 4.928-3.378 9.032-2.8 12.309 1.787 5.606 8.519 7.711 7.561 9.857-1.248 2.502-8.829 2.384-14.448 8.304-7.761 9.402-14.738 22.95-16.684 31.006-2.731 10.955-5.996 17.436-4.859 31.48 0.464 6.928-1.244 8.389 2.939 12.639 3.722 3.857 9.656 8.344 16.234 14.148 6.932 6.014 23.148 17.336 25.008 20.533 2 3.268 2.771 12.611 1.615 14.907-1.1 2.282-12.268 4.741-12.198 4.741-0.069 0 9.089 11.867 10.989 13.77 1.831 1.831 9.382 9.85 41.329 3.977 18.827-3.438 37.492-12.683 41.246-24.207 3.743-8.715-1.646-16.746-0.066-22.762 2.409-9.171 19.514-12.957 21.289-23.381 0.211-1.422 1.265-2.494 2.49-4.564 2.21-3.715 1.213-9.674 1.549-15.084 1.065-15.77-1.354-30.139-4.791-40.844-3.21-10.058-8.14-17.241-12.309-26.481-8.396-18.466-8.421-27.168-16.237-38.664-5.622-8.284-6.663-12.495-14.262-11.651-11.872 1.335-27.833 25.212-41.228 25.663z"/>\r
+   <path fill="#e8e8e8" d="m159.694 146.694c-8.012 0.234-16.605-20.127-19.279-18.437-0.757 1.383-1.753 2.373-2.943 5.2-2.56 4.171-6.423 10.307-10.105 16.658-2.835 4.939-3.251 8.979-2.52 12.138 1.97 5.763 9.189 7.796 8.226 9.879-1.303 2.559-9.318 2.33-15.265 8.521-8.063 9.31-14.818 22.867-16.733 30.748-2.748 10.967-6.037 17.419-4.853 31.436 0.472 6.457-1.353 8.271 2.818 12.562 3.685 3.873 9.711 8.429 16.273 14.218 6.913 6 23.207 17.344 25.058 20.529 1.991 3.257 2.814 12.697 1.666 14.98-1.087 2.271-12.247 4.799-12.177 4.799-0.07 0 9.04 11.755 10.938 13.654 1.829 1.828 9.349 9.785 41.239 3.926 18.847-3.435 37.783-12.609 41.124-24.229 3.14-8.444-2.161-16.743-0.592-22.728 2.401-9.152 20.062-12.955 21.831-23.364 0.211-1.417 1.298-2.483 2.516-4.544 2.217-3.748 1.19-9.689 1.547-15.076 1.132-15.82-1.331-30.133-4.766-40.806-3.213-10.03-8.136-17.203-12.296-26.427-8.38-18.436-8.435-27.151-16.257-38.609-5.425-7.929-6.802-11.822-14.146-10.962-11.706 1.368-27.931 25.491-41.304 25.934z"/>\r
+   <path fill="#efefef" d="m159.677 147.779c-7.934 0.226-16.16-19.791-18.97-18.64-0.785 0.921-1.869 1.979-3.13 4.447-2.71 4.064-6.639 10.536-10.19 16.731-2.824 4.95-3.125 8.928-2.24 11.967 2.152 5.919 9.86 7.882 8.892 9.901-1.358 2.616-9.808 2.277-16.083 8.739-8.363 9.218-14.896 22.784-16.781 30.489-2.766 10.977-6.079 17.402-4.846 31.393 0.478 5.984-1.462 8.152 2.696 12.482 3.646 3.889 9.765 8.514 16.311 14.287 6.896 5.983 23.269 17.352 25.109 20.526 1.98 3.245 2.855 12.782 1.718 15.052-1.076 2.262-12.227 4.857-12.156 4.857-0.07 0 8.991 11.641 10.887 13.537 1.826 1.826 9.313 9.723 41.148 3.879 18.868-3.434 38.074-12.538 41.002-24.254 2.537-8.174-2.678-16.741-1.119-22.69 2.396-9.138 20.612-12.957 22.375-23.351 0.212-1.412 1.332-2.473 2.541-4.523 2.226-3.783 1.166-9.704 1.545-15.07 1.197-15.869-1.307-30.125-4.741-40.766-3.215-10.002-8.131-17.165-12.282-26.372-8.365-18.407-8.447-27.135-16.277-38.555-5.228-7.574-6.938-11.15-14.029-10.272-11.541 1.402-28.029 25.771-41.38 26.206z"/>\r
+   <path fill="#f7f7f7" d="m159.66 148.864c-7.854 0.219-15.714-19.454-18.66-18.844-0.812 0.461-1.983 1.585-3.314 3.694-2.86 3.958-6.856 10.766-10.278 16.805-2.813 4.961-2.998 8.876-1.96 11.796 2.337 6.076 10.533 7.968 9.558 9.923-1.415 2.673-10.296 2.223-16.899 8.956-8.664 9.126-14.978 22.702-16.83 30.23-2.783 10.986-6.121 17.386-4.839 31.349 0.484 5.515-1.571 8.033 2.573 12.403 3.609 3.906 9.82 8.6 16.35 14.357 6.88 5.969 23.329 17.36 25.16 20.523 1.971 3.232 2.898 12.869 1.77 15.124-1.063 2.252-12.206 4.915-12.136 4.915-0.07 0 8.942 11.527 10.835 13.422 1.824 1.822 9.279 9.658 41.059 3.83 18.889-3.434 38.366-12.467 40.881-24.278 1.934-7.903-3.195-16.739-1.646-22.655 2.388-9.121 21.161-12.955 22.918-23.336 0.211-1.404 1.366-2.461 2.566-4.502 2.232-3.816 1.143-9.719 1.542-15.063 1.265-15.92-1.283-30.12-4.717-40.727-3.219-9.974-8.128-17.127-12.269-26.317-8.349-18.378-8.461-27.119-16.298-38.501-5.029-7.219-7.075-10.478-13.912-9.584-11.373 1.438-28.126 26.053-41.454 26.48z"/>\r
+   <path fill="#fff" d="m159.642 149.949c-7.774 0.211-15.268-19.118-18.35-19.048-0.84 0-2.1 1.191-3.501 2.941-3.011 3.852-7.072 10.995-10.363 16.878-2.803 4.972-2.872 8.824-1.682 11.625 2.521 6.233 11.205 8.054 10.225 9.945-1.471 2.731-10.785 2.17-17.719 9.174-8.964 9.034-15.056 22.621-16.877 29.973-2.801 10.995-6.163 17.368-4.832 31.304 0.49 5.043-1.681 7.914 2.451 12.325 3.571 3.923 9.874 8.685 16.387 14.427 6.863 5.953 23.391 17.368 25.211 20.521 1.962 3.221 2.942 12.954 1.821 15.196-1.051 2.24-12.185 4.972-12.115 4.972-0.069 0 8.895 11.416 10.784 13.307 1.821 1.82 9.244 9.595 40.97 3.781 18.907-3.431 38.656-12.396 40.758-24.302 1.331-7.633-3.712-16.736-2.171-22.619 2.381-9.104 21.71-12.956 23.461-23.321 0.21-1.399 1.399-2.45 2.591-4.481 2.24-3.852 1.12-9.734 1.54-15.057 1.331-15.968-1.26-30.113-4.692-40.688-3.221-9.945-8.123-17.088-12.255-26.262-8.334-18.348-8.474-27.102-16.318-38.447-4.832-6.863-7.213-9.805-13.796-8.894-11.205 1.469-28.222 26.33-41.528 26.75z"/>\r
+  </g>\r
+  <path fill="#995900" d="m152.553 88.8575c5.256-0.648 12.456 0.648 15.769 3.096 3.096 2.304 5.256 3.528 8.063 4.464 9.433 3.096 21.816 4.536 21.24 13.032-0.648 10.151-3.6 14.688-12.024 17.351-6.768 2.088-18.863 13.824-28.224 13.824-4.176 0-10.008 0.216-13.392-1.008-3.24-1.152-7.776-6.624-13.104-11.016-5.328-4.32-10.296-8.928-10.439-14.976-0.217-6.407 3.96-8.496 9.863-13.607 3.097-2.736 8.712-7.272 12.601-9.288 3.599-1.799 5.903-1.439 9.647-1.872z"/>\r
+  <g transform="translate(-12.4048,10.0005)">\r
+   <path fill="#9e5f00" d="m165.068 78.951c5.225-0.644 12.384 0.645 15.677 3.079 3.078 2.29 5.227 3.51 8.018 4.438 9.375 3.078 21.729 4.529 21.159 12.973-0.641 10.09-3.669 14.581-12.041 17.223-6.723 2.073-18.768 13.589-28.07 13.64-4.21 0.032-9.926 0.234-13.287-0.977-3.215-1.142-7.737-6.608-13.031-10.969-5.291-4.292-10.26-8.774-10.317-14.765-0.153-6.252 3.912-8.411 9.773-13.488 3.071-2.71 8.594-7.303 12.463-9.333 3.563-1.803 5.933-1.392 9.656-1.821z"/>\r
+   <path fill="#a36400" d="m165.177 79.044c5.195-0.641 12.313 0.64 15.587 3.06 3.06 2.278 5.194 3.494 7.971 4.413 9.317 3.06 21.641 4.522 21.078 12.914-0.634 10.027-3.737 14.474-12.058 17.094-6.678 2.057-18.673 13.352-27.919 13.454-4.241 0.064-9.842 0.252-13.18-0.945-3.19-1.133-7.7-6.592-12.96-10.921-5.254-4.264-10.222-8.622-10.192-14.555-0.093-6.099 3.863-8.329 9.681-13.369 3.048-2.685 8.478-7.335 12.328-9.379 3.526-1.804 5.963-1.338 9.664-1.766z"/>\r
+   <path fill="#a86a00" d="m165.287 79.138c5.165-0.637 12.241 0.637 15.496 3.043 3.042 2.264 5.165 3.476 7.924 4.387 9.26 3.042 21.556 4.515 20.998 12.854-0.627 9.968-3.805 14.368-12.074 16.967-6.633 2.042-18.576 13.117-27.766 13.27-4.276 0.096-9.759 0.27-13.075-0.914-3.165-1.123-7.661-6.577-12.887-10.874-5.217-4.236-10.187-8.468-10.069-14.345-0.03-5.943 3.815-8.244 9.589-13.249 3.023-2.66 8.36-7.366 12.191-9.424 3.49-1.808 5.993-1.291 9.673-1.715z"/>\r
+   <path fill="#ad7000" d="m165.396 79.23c5.135-0.633 12.17 0.633 15.404 3.025 3.025 2.251 5.137 3.46 7.88 4.361 9.201 3.025 21.467 4.508 20.917 12.796-0.62 9.905-3.874 14.26-12.093 16.837-6.586 2.027-18.479 12.882-27.611 13.086-4.311 0.127-9.677 0.287-12.971-0.883-3.14-1.113-7.622-6.561-12.814-10.826-5.18-4.208-10.148-8.315-9.945-14.135 0.031-5.789 3.768-8.16 9.497-13.129 2.999-2.635 8.244-7.398 12.055-9.47 3.454-1.808 6.023-1.24 9.681-1.662z"/>\r
+   <path fill="#b27600" d="m165.506 79.325c5.105-0.63 12.099 0.629 15.314 3.007 3.007 2.237 5.104 3.442 7.832 4.335 9.145 3.007 21.38 4.501 20.837 12.737-0.614 9.844-3.943 14.154-12.109 16.709-6.541 2.011-18.385 12.645-27.46 12.9-4.342 0.16-9.592 0.306-12.862-0.851-3.115-1.103-7.584-6.545-12.743-10.779-5.144-4.18-10.112-8.162-9.821-13.924 0.092-5.634 3.718-8.076 9.405-13.009 2.975-2.609 8.126-7.429 11.919-9.514 3.416-1.812 6.052-1.192 9.688-1.611z"/>\r
+   <path fill="#b77b00" d="m165.615 79.417c5.075-0.626 12.026 0.626 15.224 2.989 2.989 2.225 5.075 3.425 7.786 4.31 9.087 2.989 21.292 4.494 20.756 12.678-0.606 9.781-4.012 14.046-12.126 16.581-6.496 1.997-18.289 12.41-27.307 12.716-4.376 0.191-9.51 0.323-12.758-0.82-3.09-1.094-7.546-6.53-12.671-10.732-5.106-4.152-10.074-8.008-9.697-13.713 0.155-5.479 3.67-7.992 9.313-12.889 2.951-2.585 8.011-7.461 11.783-9.56 3.38-1.814 6.083-1.143 9.697-1.56z"/>\r
+   <path fill="#bc8100" d="m165.725 79.511c5.044-0.622 11.954 0.622 15.133 2.972 2.971 2.211 5.045 3.408 7.739 4.284 9.029 2.971 21.205 4.487 20.675 12.619-0.6 9.719-4.079 13.939-12.143 16.451-6.45 1.982-18.192 12.175-27.153 12.532-4.41 0.223-9.428 0.341-12.653-0.789-3.065-1.084-7.507-6.514-12.598-10.684-5.069-4.124-10.038-7.855-9.574-13.504 0.217-5.324 3.622-7.908 9.222-12.77 2.926-2.559 7.894-7.492 11.646-9.605 3.344-1.816 6.113-1.092 9.706-1.506z"/>\r
+   <path fill="#c18700" d="m165.834 79.604c5.015-0.618 11.883 0.619 15.043 2.954 2.953 2.198 5.015 3.391 7.693 4.259 8.972 2.953 21.118 4.48 20.594 12.559-0.593 9.66-4.147 13.833-12.159 16.324-6.405 1.967-18.098 11.94-27.002 12.347-4.441 0.255-9.343 0.359-12.546-0.757-3.04-1.074-7.469-6.498-12.526-10.637-5.032-4.096-10-7.701-9.45-13.293 0.278-5.169 3.574-7.823 9.13-12.649 2.903-2.534 7.776-7.524 11.511-9.651 3.306-1.821 6.141-1.044 9.712-1.456z"/>\r
+   <path fill="#c68d00" d="m165.944 79.697c4.984-0.615 11.811 0.614 14.952 2.936 2.935 2.184 4.983 3.374 7.646 4.233 8.915 2.935 21.031 4.473 20.515 12.5-0.586 9.597-4.218 13.726-12.177 16.195-6.36 1.951-18.002 11.703-26.849 12.162-4.476 0.287-9.261 0.377-12.441-0.726-3.015-1.064-7.431-6.482-12.453-10.589-4.995-4.068-9.965-7.549-9.326-13.083 0.34-5.015 3.524-7.741 9.038-12.531 2.878-2.508 7.658-7.555 11.374-9.696 3.269-1.82 6.171-0.992 9.721-1.401z"/>\r
+   <path fill="#cc9200" d="m166.054 79.791c4.952-0.61 11.738 0.611 14.86 2.918 2.918 2.172 4.954 3.357 7.601 4.207 8.857 2.918 20.942 4.466 20.432 12.442-0.578 9.536-4.285 13.62-12.192 16.066-6.314 1.936-17.906 11.468-26.696 11.978-4.509 0.319-9.178 0.394-12.335-0.696-2.989-1.054-7.393-6.466-12.382-10.541-4.959-4.04-9.928-7.395-9.202-12.873 0.401-4.859 3.477-7.655 8.945-12.411 2.854-2.482 7.542-7.586 11.239-9.741 3.233-1.824 6.201-0.943 9.73-1.349z"/>\r
+   <path fill="#d19800" d="m166.163 79.883c4.923-0.606 11.668 0.608 14.771 2.901 2.9 2.158 4.924 3.339 7.554 4.181 8.801 2.9 20.855 4.459 20.352 12.383-0.571 9.474-4.353 13.512-12.21 15.938-6.269 1.921-17.81 11.233-26.543 11.793-4.542 0.351-9.094 0.413-12.229-0.664-2.965-1.044-7.354-6.45-12.311-10.494-4.921-4.012-9.89-7.241-9.079-12.662 0.465-4.705 3.431-7.571 8.855-12.29 2.83-2.458 7.425-7.618 11.102-9.787 3.197-1.827 6.231-0.893 9.738-1.299z"/>\r
+   <path fill="#d69e00" d="m166.273 79.978c4.893-0.603 11.596 0.603 14.679 2.882 2.883 2.145 4.895 3.323 7.507 4.156 8.744 2.882 20.77 4.452 20.272 12.324-0.565 9.412-4.422 13.406-12.228 15.81-6.224 1.905-17.714 10.996-26.39 11.608-4.576 0.383-9.012 0.431-12.124-0.633-2.94-1.034-7.316-6.434-12.237-10.446-4.884-3.984-9.854-7.089-8.955-12.452 0.525-4.551 3.382-7.489 8.764-12.171 2.805-2.432 7.307-7.649 10.965-9.832 3.16-1.829 6.261-0.845 9.747-1.246z"/>\r
+   <path fill="#dba300" d="m166.382 80.07c4.863-0.599 11.525 0.6 14.59 2.865 2.864 2.131 4.862 3.305 7.461 4.13 8.686 2.864 20.682 4.445 20.19 12.264-0.559 9.352-4.491 13.299-12.244 15.681-6.179 1.89-17.619 10.761-26.237 11.423-4.608 0.415-8.929 0.449-12.018-0.601-2.915-1.024-7.277-6.418-12.166-10.399-4.847-3.956-9.815-6.935-8.831-12.241 0.587-4.396 3.333-7.404 8.671-12.051 2.782-2.407 7.191-7.681 10.83-9.878 3.123-1.829 6.29-0.793 9.754-1.193z"/>\r
+   <path fill="#e0a900" d="m166.492 80.164c4.832-0.595 11.453 0.596 14.498 2.847 2.847 2.118 4.833 3.289 7.414 4.104 8.629 2.846 20.595 4.438 20.111 12.205-0.553 9.29-4.56 13.193-12.262 15.553-6.134 1.875-17.522 10.526-26.085 11.239-4.642 0.447-8.845 0.467-11.912-0.57-2.89-1.015-7.238-6.402-12.093-10.351-4.81-3.928-9.78-6.782-8.708-12.032 0.649-4.241 3.285-7.32 8.58-11.932 2.757-2.381 7.073-7.712 10.693-9.923 3.088-1.831 6.321-0.743 9.764-1.14z"/>\r
+   <path fill="#e5af00" d="m166.601 80.257c4.803-0.592 11.382 0.592 14.407 2.829 2.829 2.105 4.804 3.271 7.368 4.079 8.571 2.828 20.507 4.431 20.029 12.146-0.544 9.228-4.627 13.085-12.277 15.423-6.089 1.861-17.427 10.29-25.932 11.055-4.676 0.478-8.763 0.484-11.807-0.539-2.865-1.005-7.2-6.387-12.021-10.304-4.772-3.9-9.742-6.629-8.583-11.821 0.711-4.085 3.236-7.236 8.487-11.812 2.732-2.357 6.957-7.744 10.557-9.968 3.052-1.834 6.351-0.694 9.772-1.088z"/>\r
+   <path fill="#eab500" d="m166.711 80.351c4.772-0.588 11.31 0.589 14.317 2.811 2.811 2.092 4.771 3.254 7.321 4.054 8.514 2.81 20.42 4.424 19.948 12.087-0.538 9.165-4.695 12.979-12.294 15.295-6.044 1.845-17.332 10.054-25.779 10.869-4.708 0.511-8.68 0.503-11.7-0.507-2.84-0.995-7.163-6.371-11.949-10.257-4.736-3.872-9.706-6.475-8.46-11.61 0.773-3.931 3.188-7.152 8.396-11.692 2.709-2.331 6.839-7.775 10.421-10.013 3.013-1.839 6.38-0.645 9.779-1.037z"/>\r
+   <path fill="#efba00" d="m166.82 80.443c4.742-0.584 11.238 0.585 14.226 2.794 2.794 2.078 4.743 3.237 7.276 4.027 8.456 2.793 20.332 4.417 19.868 12.029-0.531 9.104-4.766 12.872-12.313 15.167-5.997 1.83-17.234 9.819-25.626 10.685-4.742 0.542-8.596 0.52-11.595-0.476-2.815-0.985-7.124-6.355-11.877-10.209-4.699-3.844-9.668-6.322-8.336-11.4 0.835-3.778 3.14-7.068 8.304-11.573 2.686-2.306 6.724-7.807 10.285-10.059 2.978-1.84 6.411-0.595 9.788-0.985z"/>\r
+   <path fill="#f4c000" d="m166.93 80.537c4.711-0.58 11.166 0.582 14.135 2.776 2.775 2.066 4.713 3.22 7.229 4.002 8.399 2.775 20.246 4.41 19.787 11.969-0.522 9.043-4.832 12.765-12.328 15.039-5.952 1.815-17.139 9.584-25.473 10.501-4.776 0.574-8.513 0.538-11.49-0.445-2.79-0.976-7.085-6.34-11.804-10.162-4.662-3.816-9.632-6.168-8.213-11.189 0.896-3.623 3.092-6.984 8.213-11.454 2.66-2.281 6.604-7.838 10.147-10.104 2.942-1.844 6.441-0.547 9.797-0.933z"/>\r
+   <path fill="#f9c600" d="m167.039 80.63c4.683-0.577 11.095 0.577 14.045 2.758 2.758 2.052 4.683 3.203 7.184 3.976 8.341 2.757 20.157 4.403 19.706 11.91-0.518 8.981-4.901 12.659-12.346 14.911-5.906 1.799-17.044 9.347-25.32 10.315-4.809 0.606-8.431 0.556-11.384-0.413-2.765-0.966-7.048-6.324-11.732-10.114-4.625-3.788-9.594-6.016-8.088-10.98 0.958-3.467 3.043-6.9 8.12-11.333 2.637-2.256 6.488-7.87 10.013-10.15 2.902-1.844 6.468-0.495 9.802-0.88z"/>\r
+  </g>\r
+  <path fill="#fc0" d="m154.744 90.7245c4.65-0.573 11.022 0.574 13.954 2.74 2.739 2.039 4.651 3.186 7.136 3.951 8.284 2.739 20.071 4.396 19.626 11.851-0.51 8.919-4.97 12.551-12.362 14.781-5.861 1.784-16.947 9.112-25.168 10.131-4.842 0.638-8.347 0.574-11.277-0.382-2.74-0.956-7.01-6.308-11.66-10.067-4.588-3.76-9.559-5.862-7.965-10.769 1.02-3.313 2.995-6.816 8.028-11.213 2.612-2.23 6.371-7.901 9.876-10.195 2.867-1.847 6.499-0.447 9.812-0.828z"/>\r
+  <g transform="translate(-12.4048,10.0005)">\r
+   <path fill="#fc0" d="m167.982 83.609c1.008 2.088 3.6 2.376 5.328 3.312 1.655 0.936 2.592 1.152 3.239 0.792 1.44-0.792 0.36-3.384-1.079-4.32-1.368-0.935-8.064-1.151-7.488 0.216z"/>\r
+   <path fill="#f9c600" d="m168.125 83.631c0.982 2.035 3.508 2.316 5.193 3.229 1.614 0.912 2.526 1.123 3.158 0.771 1.402-0.771 0.35-3.298-1.054-4.21-1.332-0.913-7.859-1.123-7.297 0.21z"/>\r
+   <path fill="#f4c000" d="m168.267 83.653c0.957 1.982 3.418 2.255 5.058 3.144 1.572 0.889 2.461 1.094 3.076 0.752 1.367-0.752 0.342-3.213-1.025-4.101-1.299-0.889-7.656-1.094-7.109 0.205z"/>\r
+   <path fill="#efba00" d="m168.409 83.674c0.932 1.929 3.327 2.195 4.924 3.06 1.53 0.865 2.395 1.064 2.993 0.732 1.331-0.732 0.333-3.127-0.998-3.992-1.264-0.864-7.451-1.064-6.919 0.2z"/>\r
+   <path fill="#eab500" d="m168.552 83.696c0.905 1.876 3.234 2.135 4.787 2.977 1.488 0.841 2.329 1.035 2.912 0.711 1.294-0.711 0.323-3.041-0.971-3.882-1.228-0.841-7.246-1.036-6.728 0.194z"/>\r
+   <path fill="#e5af00" d="m168.694 83.718c0.881 1.823 3.144 2.075 4.653 2.892 1.446 0.818 2.264 1.006 2.83 0.692 1.257-0.692 0.313-2.956-0.943-3.773-1.195-0.818-7.043-1.007-6.54 0.189z"/>\r
+   <path fill="#e0a900" d="m168.837 83.739c0.855 1.771 3.053 2.015 4.519 2.809 1.403 0.793 2.198 0.977 2.747 0.671 1.221-0.671 0.306-2.87-0.916-3.664-1.161-0.793-6.839-0.976-6.35 0.184z"/>\r
+   <path fill="#dba300" d="m168.979 83.761c0.829 1.718 2.962 1.955 4.383 2.725 1.363 0.77 2.132 0.948 2.666 0.651 1.184-0.651 0.296-2.784-0.889-3.554-1.125-0.77-6.634-0.948-6.16 0.178z"/>\r
+   <path fill="#d69e00" d="m169.121 83.782c0.804 1.665 2.871 1.895 4.249 2.641 1.32 0.747 2.066 0.918 2.583 0.631 1.148-0.631 0.287-2.698-0.861-3.444-1.091-0.746-6.43-0.919-5.971 0.172z"/>\r
+   <path fill="#d19800" d="m169.264 83.804c0.777 1.612 2.778 1.834 4.112 2.557 1.279 0.723 2.001 0.889 2.501 0.611 1.112-0.611 0.278-2.612-0.834-3.335-1.055-0.722-6.224-0.889-5.779 0.167z"/>\r
+   <path fill="#cc9200" d="m169.406 83.826c0.753 1.559 2.688 1.774 3.979 2.473 1.236 0.699 1.936 0.86 2.42 0.591 1.074-0.591 0.269-2.527-0.808-3.226-1.021-0.699-6.021-0.86-5.591 0.162z"/>\r
+   <path fill="#c68c00" d="m169.549 83.847c0.728 1.506 2.597 1.714 3.844 2.389 1.194 0.675 1.869 0.831 2.337 0.571 1.039-0.571 0.26-2.441-0.779-3.116-0.988-0.675-5.818-0.831-5.402 0.156z"/>\r
+   <path fill="#c18700" d="m169.691 83.869c0.702 1.453 2.506 1.654 3.709 2.305 1.152 0.652 1.803 0.802 2.254 0.551 1.002-0.551 0.251-2.355-0.751-3.006-0.953-0.652-5.613-0.802-5.212 0.15z"/>\r
+   <path fill="#bc8100" d="m169.833 83.89c0.677 1.4 2.415 1.594 3.574 2.221 1.111 0.628 1.738 0.772 2.173 0.531 0.965-0.531 0.241-2.27-0.725-2.897-0.917-0.627-5.408-0.772-5.022 0.145z"/>\r
+   <path fill="#b77b00" d="m169.976 83.912c0.65 1.347 2.322 1.533 3.438 2.137 1.069 0.604 1.673 0.743 2.091 0.511 0.93-0.511 0.233-2.184-0.696-2.788-0.884-0.603-5.205-0.743-4.833 0.14z"/>\r
+   <path fill="#b27500" d="m170.118 83.934c0.626 1.294 2.232 1.473 3.304 2.053 1.027 0.581 1.606 0.714 2.009 0.491 0.893-0.491 0.224-2.098-0.669-2.678-0.85-0.58-5.001-0.715-4.644 0.134z"/>\r
+   <path fill="#ad7000" d="m170.261 83.955c0.6 1.242 2.14 1.413 3.168 1.97 0.984 0.557 1.541 0.685 1.927 0.47 0.855-0.47 0.214-2.012-0.644-2.569-0.812-0.555-4.794-0.684-4.451 0.129z"/>\r
+   <path fill="#a86a00" d="m170.403 83.977c0.574 1.189 2.05 1.353 3.034 1.886 0.942 0.533 1.475 0.656 1.844 0.45 0.82-0.45 0.205-1.926-0.615-2.459-0.779-0.533-4.591-0.656-4.263 0.123z"/>\r
+   <path fill="#a36400" d="m170.545 83.998c0.55 1.136 1.959 1.292 2.899 1.802 0.901 0.509 1.41 0.626 1.762 0.43 0.783-0.43 0.197-1.841-0.587-2.35-0.745-0.508-4.387-0.626-4.074 0.118z"/>\r
+   <path fill="#9e5e00" d="m170.688 84.02c0.522 1.083 1.867 1.232 2.764 1.718 0.859 0.486 1.343 0.597 1.68 0.41 0.746-0.41 0.188-1.755-0.561-2.241-0.709-0.484-4.182-0.597-3.883 0.113z"/>\r
+   <path fill="#995900" d="m170.83 84.042c0.498 1.03 1.776 1.172 2.629 1.634 0.817 0.462 1.278 0.568 1.599 0.39 0.71-0.39 0.178-1.669-0.533-2.131-0.676-0.461-3.979-0.568-3.695 0.107z"/>\r
+  </g>\r
+  <g transform="translate(-12.4048,10.0005)">\r
+   <path fill="#fc0" d="m152.875 86.29c-0.325 0.813 1.952 2.359 3.091 1.301 1.222-1.057 2.686-2.033 3.175-2.359 2.195-1.465 1.383-2.522-2.278-1.871-3.663 0.651-3.663 2.115-3.988 2.929z"/>\r
+   <path fill="#f9c600" d="m152.934 86.279c-0.318 0.794 1.906 2.304 3.019 1.271 1.193-1.033 2.623-1.986 3.102-2.305 2.145-1.431 1.351-2.463-2.226-1.828-3.578 0.637-3.578 2.067-3.895 2.862z"/>\r
+   <path fill="#f4c000" d="m152.993 86.269c-0.31 0.775 1.861 2.25 2.948 1.241 1.164-1.008 2.56-1.939 3.026-2.25 2.095-1.397 1.319-2.405-2.173-1.784-3.491 0.62-3.491 2.017-3.801 2.793z"/>\r
+   <path fill="#efba00" d="m153.051 86.258c-0.302 0.757 1.817 2.195 2.878 1.211 1.136-0.984 2.497-1.892 2.952-2.195 2.044-1.363 1.287-2.347-2.118-1.741-3.409 0.606-3.409 1.968-3.712 2.725z"/>\r
+   <path fill="#eab500" d="m153.11 86.248c-0.295 0.738 1.771 2.141 2.805 1.181 1.108-0.959 2.437-1.845 2.88-2.141 1.993-1.329 1.255-2.289-2.066-1.698-3.324 0.591-3.324 1.92-3.619 2.658z"/>\r
+   <path fill="#e5af00" d="m153.169 86.238c-0.287 0.719 1.727 2.086 2.733 1.151 1.08-0.935 2.374-1.798 2.807-2.086 1.942-1.296 1.224-2.23-2.015-1.655s-3.238 1.87-3.525 2.59z"/>\r
+   <path fill="#e0a900" d="m153.228 86.228c-0.28 0.7 1.681 2.032 2.661 1.121 1.052-0.91 2.312-1.751 2.732-2.031 1.893-1.262 1.191-2.172-1.961-1.611-3.152 0.559-3.152 1.82-3.432 2.521z"/>\r
+   <path fill="#dba300" d="m153.286 86.217c-0.271 0.681 1.636 1.977 2.591 1.09 1.023-0.886 2.25-1.704 2.659-1.977 1.84-1.228 1.159-2.114-1.909-1.568-3.068 0.547-3.068 1.773-3.341 2.455z"/>\r
+   <path fill="#d69e00" d="m153.345 86.207c-0.265 0.662 1.591 1.922 2.519 1.061 0.995-0.862 2.188-1.657 2.586-1.922 1.789-1.194 1.127-2.055-1.855-1.525-2.985 0.53-2.985 1.723-3.25 2.386z"/>\r
+   <path fill="#d19800" d="m153.404 86.197c-0.257 0.643 1.546 1.868 2.447 1.03 0.967-0.837 2.126-1.61 2.512-1.868 1.739-1.16 1.095-1.997-1.803-1.481-2.899 0.516-2.899 1.674-3.156 2.319z"/>\r
+   <path fill="#cc9200" d="m153.463 86.187c-0.25 0.625 1.5 1.813 2.375 1 0.939-0.813 2.064-1.563 2.439-1.813 1.688-1.126 1.063-1.938-1.75-1.438-2.814 0.5-2.814 1.625-3.064 2.251z"/>\r
+   <path fill="#c68c00" d="m153.521 86.176c-0.242 0.605 1.456 1.758 2.304 0.97 0.911-0.788 2.002-1.516 2.366-1.758 1.637-1.092 1.031-1.88-1.698-1.395-2.729 0.486-2.729 1.576-2.972 2.183z"/>\r
+   <path fill="#c18700" d="m153.58 86.166c-0.233 0.587 1.41 1.704 2.233 0.939 0.882-0.763 1.938-1.469 2.292-1.704 1.586-1.058 0.999-1.822-1.646-1.352-2.644 0.472-2.644 1.529-2.879 2.117z"/>\r
+   <path fill="#bc8100" d="m153.639 86.156c-0.228 0.568 1.364 1.649 2.16 0.91 0.854-0.739 1.878-1.422 2.219-1.649 1.536-1.024 0.967-1.764-1.593-1.308s-2.559 1.477-2.786 2.047z"/>\r
+   <path fill="#b77b00" d="m153.698 86.146c-0.22 0.549 1.32 1.594 2.089 0.879 0.825-0.715 1.815-1.375 2.146-1.595 1.484-0.99 0.935-1.705-1.54-1.265s-2.475 1.43-2.695 1.981z"/>\r
+   <path fill="#b27500" d="m153.756 86.135c-0.211 0.53 1.275 1.54 2.019 0.85 0.797-0.69 1.753-1.328 2.072-1.54 1.434-0.957 0.902-1.646-1.487-1.221s-2.391 1.38-2.604 1.911z"/>\r
+   <path fill="#ad7000" d="m153.815 86.125c-0.204 0.512 1.229 1.486 1.946 0.82 0.769-0.666 1.69-1.281 1.997-1.486 1.385-0.922 0.871-1.588-1.434-1.178s-2.304 1.331-2.509 1.844z"/>\r
+   <path fill="#a86a00" d="m153.874 86.114c-0.196 0.493 1.185 1.431 1.875 0.79 0.74-0.642 1.628-1.234 1.924-1.431 1.332-0.889 0.84-1.53-1.381-1.135s-2.221 1.283-2.418 1.776z"/>\r
+   <path fill="#a36400" d="m153.933 86.104c-0.189 0.474 1.139 1.376 1.803 0.759 0.712-0.617 1.566-1.187 1.851-1.376 1.281-0.855 0.808-1.472-1.329-1.092-2.135 0.38-2.135 1.234-2.325 1.709z"/>\r
+   <path fill="#9e5e00" d="m153.991 86.094c-0.181 0.455 1.095 1.322 1.732 0.729 0.684-0.592 1.504-1.14 1.776-1.321 1.231-0.821 0.775-1.414-1.274-1.048-2.051 0.364-2.051 1.184-2.234 1.64z"/>\r
+   <path fill="#995900" d="m154.05 86.083c-0.174 0.436 1.05 1.267 1.66 0.699 0.656-0.568 1.442-1.093 1.704-1.267 1.181-0.787 0.743-1.355-1.223-1.005s-1.966 1.136-2.141 1.573z"/>\r
+  </g>\r
+  <g transform="translate(-12.4048,10.0005)">\r
+   <path fill="#fc0" d="m156.951 107.887c-0.229 2.858 6.343-4.286 6.743-4.915 0.856-1.543 3.715-5.886 4.172-7.715 0.857-3.2 2.401-5.543 1.429-8.915-0.343-1.086-2.742-1.372-3.829-0.687-3.086 1.829-2.629 4.058-2.972 6.115-1.143 5.831-5.143 11.717-5.543 16.117z"/>\r
+   <path fill="#ffcc02" d="m157.22 107.441c-0.22 2.787 6.178-4.188 6.566-4.802 0.833-1.506 3.614-5.745 4.056-7.529 0.831-3.122 2.333-5.408 1.382-8.695-0.337-1.058-2.678-1.333-3.735-0.663-3.006 1.788-2.557 3.96-2.889 5.967-1.105 5.685-4.997 11.431-5.38 15.722z"/>\r
+   <path fill="#ffcc05" d="m157.488 106.995c-0.209 2.715 6.014-4.091 6.392-4.69 0.811-1.469 3.513-5.603 3.941-7.342 0.804-3.043 2.264-5.273 1.331-8.474-0.329-1.031-2.61-1.295-3.64-0.64-2.927 1.747-2.486 3.863-2.806 5.818-1.068 5.541-4.851 11.146-5.218 15.328z"/>\r
+   <path fill="#ffcc07" d="m157.757 106.548c-0.198 2.645 5.85-3.993 6.217-4.577 0.785-1.431 3.409-5.461 3.824-7.156 0.779-2.964 2.196-5.138 1.282-8.253-0.322-1.003-2.543-1.257-3.545-0.618-2.847 1.706-2.414 3.766-2.722 5.67-1.031 5.398-4.706 10.862-5.056 14.934z"/>\r
+   <path fill="#ffcd0a" d="m158.026 106.102c-0.189 2.573 5.684-3.896 6.04-4.465 0.762-1.394 3.309-5.32 3.709-6.969 0.753-2.886 2.129-5.004 1.233-8.033-0.315-0.976-2.478-1.219-3.45-0.595-2.768 1.665-2.343 3.668-2.64 5.522-0.993 5.254-4.558 10.577-4.892 14.54z"/>\r
+   <path fill="#ffcd0c" d="m158.294 105.655c-0.179 2.503 5.52-3.798 5.865-4.351 0.738-1.357 3.207-5.179 3.594-6.783 0.727-2.807 2.061-4.869 1.185-7.813-0.309-0.948-2.411-1.18-3.356-0.572-2.687 1.623-2.271 3.571-2.556 5.374-0.958 5.11-4.414 10.291-4.732 14.145z"/>\r
+   <path fill="#ffcd0f" d="m158.563 105.209c-0.169 2.431 5.354-3.701 5.688-4.239 0.715-1.319 3.106-5.037 3.479-6.596 0.7-2.728 1.992-4.734 1.135-7.592-0.301-0.92-2.344-1.142-3.261-0.549-2.608 1.583-2.199 3.474-2.473 5.226-0.919 4.965-4.267 10.005-4.568 13.75z"/>\r
+   <path fill="#ffcd11" d="m158.831 104.762c-0.159 2.361 5.19-3.602 5.515-4.126 0.69-1.282 3.004-4.896 3.361-6.409 0.674-2.649 1.924-4.599 1.087-7.372-0.295-0.893-2.277-1.104-3.167-0.526-2.527 1.541-2.128 3.376-2.389 5.077-0.883 4.822-4.122 9.721-4.407 13.356z"/>\r
+   <path fill="#ffce14" d="m159.1 104.316c-0.149 2.289 5.024-3.505 5.338-4.014 0.667-1.244 2.901-4.754 3.247-6.223 0.646-2.571 1.854-4.464 1.037-7.151-0.287-0.865-2.211-1.065-3.072-0.504-2.448 1.5-2.056 3.279-2.306 4.929-0.845 4.679-3.976 9.437-4.244 12.963z"/>\r
+   <path fill="#ffce16" d="m159.369 103.869c-0.139 2.219 4.86-3.407 5.162-3.9 0.643-1.208 2.801-4.613 3.131-6.037 0.622-2.492 1.787-4.329 0.988-6.93-0.28-0.838-2.146-1.027-2.978-0.481-2.368 1.459-1.983 3.182-2.223 4.781-0.807 4.533-3.829 9.151-4.08 12.567z"/>\r
+   <path fill="#ffce19" d="m159.637 103.423c-0.13 2.147 4.695-3.31 4.986-3.788 0.62-1.17 2.699-4.471 3.016-5.85 0.596-2.414 1.719-4.195 0.939-6.71-0.273-0.81-2.079-0.989-2.883-0.458-2.289 1.418-1.913 3.084-2.139 4.632-0.77 4.391-3.684 8.866-3.919 12.174z"/>\r
+   <path fill="#ffce1c" d="m159.906 102.977c-0.119 2.076 4.531-3.213 4.811-3.676 0.597-1.133 2.599-4.33 2.899-5.664 0.57-2.335 1.651-4.06 0.891-6.49-0.267-0.782-2.012-0.95-2.787-0.435-2.21 1.377-1.842 2.987-2.057 4.484-0.734 4.247-3.539 8.582-3.757 11.781z"/>\r
+   <path fill="#ffcf1e" d="m160.174 102.53c-0.108 2.005 4.366-3.115 4.637-3.563 0.571-1.096 2.496-4.189 2.784-5.478 0.543-2.256 1.581-3.925 0.841-6.269-0.26-0.754-1.945-0.912-2.693-0.412-2.129 1.336-1.77 2.889-1.973 4.336-0.697 4.103-3.394 8.297-3.596 11.386z"/>\r
+   <path fill="#ffcf21" d="m160.443 102.084c-0.099 1.934 4.201-3.018 4.46-3.45 0.548-1.059 2.394-4.047 2.668-5.291 0.517-2.178 1.514-3.79 0.793-6.049-0.253-0.727-1.879-0.874-2.599-0.39-2.051 1.295-1.698 2.792-1.891 4.188-0.658 3.959-3.246 8.012-3.431 10.992z"/>\r
+   <path fill="#ffcf23" d="m160.712 101.637c-0.089 1.863 4.036-2.919 4.283-3.337 0.526-1.021 2.294-3.905 2.553-5.104 0.491-2.099 1.447-3.655 0.744-5.828-0.246-0.699-1.813-0.835-2.505-0.367-1.969 1.253-1.625 2.694-1.805 4.04-0.623 3.814-3.101 7.726-3.27 10.596z"/>\r
+   <path fill="#ffcf26" d="m160.98 101.191c-0.079 1.792 3.872-2.822 4.107-3.225 0.502-0.984 2.192-3.764 2.438-4.918 0.464-2.02 1.378-3.52 0.694-5.607-0.238-0.672-1.746-0.797-2.41-0.344-1.89 1.212-1.555 2.597-1.723 3.891-0.583 3.671-2.953 7.442-3.106 10.203z"/>\r
+   <path fill="#ffd028" d="m161.249 100.744c-0.068 1.721 3.707-2.724 3.933-3.112 0.478-0.947 2.091-3.623 2.321-4.731 0.439-1.942 1.311-3.386 0.646-5.387-0.232-0.645-1.68-0.758-2.316-0.321-1.81 1.171-1.481 2.5-1.639 3.743-0.548 3.527-2.809 7.156-2.945 9.808z"/>\r
+   <path fill="#ffd02b" d="m161.517 100.298c-0.06 1.65 3.543-2.627 3.757-2.999 0.454-0.91 1.989-3.481 2.206-4.545 0.413-1.863 1.242-3.25 0.597-5.167-0.225-0.617-1.613-0.72-2.221-0.298-1.73 1.13-1.411 2.402-1.557 3.595-0.509 3.383-2.662 6.871-2.782 9.414z"/>\r
+   <path fill="#ffd02d" d="m161.786 99.852c-0.049 1.579 3.377-2.529 3.581-2.887 0.431-0.872 1.887-3.34 2.091-4.359 0.387-1.784 1.173-3.116 0.547-4.946-0.217-0.589-1.546-0.682-2.126-0.275-1.649 1.089-1.339 2.305-1.472 3.446-0.474 3.24-2.518 6.587-2.621 9.021z"/>\r
+   <path fill="#ffd030" d="m162.055 99.405c-0.039 1.508 3.212-2.432 3.404-2.773 0.407-0.835 1.786-3.199 1.976-4.172 0.359-1.706 1.104-2.981 0.499-4.726-0.211-0.562-1.481-0.644-2.032-0.253-1.571 1.048-1.268 2.208-1.389 3.298-0.436 3.096-2.372 6.302-2.458 8.626z"/>\r
+   <path fill="#ffd133" d="m162.323 98.958c-0.029 1.437 3.048-2.334 3.23-2.661 0.383-0.798 1.684-3.057 1.858-3.986 0.334-1.627 1.037-2.846 0.45-4.505-0.204-0.534-1.414-0.605-1.938-0.23-1.49 1.007-1.195 2.11-1.306 3.15-0.397 2.953-2.224 6.018-2.294 8.232z"/>\r
+  </g>\r
+  <g transform="translate(-12.4048,10.0005)">\r
+   <path fill="#fc0" d="m179.646 95.994c-3.168 3.456-5.4 6.767-7.2 9-1.872 2.304-6.48 5.04-4.176 7.704 1.943 2.376 9.936-1.944 16.128-6.552 6.12-4.608 15.696-8.711 11.016-13.967-2.448-2.664-8.208-2.088-10.439-0.648-1.729 1.078-2.737 1.655-5.329 4.463z"/>\r
+   <path fill="#ffcc02" d="m179.782 96.147c-3.118 3.378-5.313 6.628-7.086 8.809-1.841 2.249-6.375 4.945-4.13 7.534 1.893 2.31 9.724-1.943 15.795-6.469 6.001-4.525 15.38-8.574 10.82-13.682-2.387-2.588-8.022-1.998-10.209-0.584-1.692 1.062-2.665 1.67-5.19 4.392z"/>\r
+   <path fill="#ffcc05" d="m179.919 96.3c-3.068 3.3-5.227 6.488-6.973 8.619-1.81 2.193-6.271 4.85-4.085 7.364 1.843 2.243 9.513-1.943 15.463-6.386 5.882-4.442 15.064-8.437 10.623-13.396-2.323-2.513-7.835-1.907-9.978-0.52-1.655 1.044-2.593 1.684-5.05 4.319z"/>\r
+   <path fill="#ffcc07" d="m180.055 96.454c-3.02 3.222-5.14 6.347-6.859 8.428-1.78 2.138-6.166 4.754-4.04 7.194 1.793 2.177 9.302-1.942 15.131-6.303 5.762-4.359 14.748-8.299 10.427-13.11-2.261-2.437-7.648-1.817-9.747-0.456-1.619 1.025-2.522 1.698-4.912 4.247z"/>\r
+   <path fill="#ffcd0a" d="m180.191 96.607c-2.97 3.143-5.052 6.207-6.745 8.237-1.749 2.082-6.063 4.659-3.994 7.023 1.743 2.111 9.09-1.941 14.798-6.219 5.644-4.276 14.433-8.162 10.231-12.824-2.199-2.361-7.463-1.727-9.518-0.392-1.581 1.008-2.45 1.713-4.772 4.175z"/>\r
+   <path fill="#ffcd0c" d="m180.327 96.761c-2.92 3.065-4.965 6.066-6.631 8.047-1.718 2.027-5.957 4.564-3.949 6.853 1.693 2.044 8.878-1.94 14.466-6.136 5.524-4.194 14.116-8.024 10.034-12.538-2.137-2.286-7.275-1.636-9.285-0.328-1.546 0.988-2.38 1.726-4.635 4.102z"/>\r
+   <path fill="#ffcd0f" d="m180.464 96.914c-2.871 2.987-4.879 5.926-6.518 7.857-1.688 1.971-5.854 4.468-3.903 6.683 1.643 1.978 8.666-1.94 14.133-6.053 5.404-4.111 13.801-7.887 9.839-12.251-2.075-2.21-7.091-1.546-9.056-0.264-1.509 0.969-2.308 1.738-4.495 4.028z"/>\r
+   <path fill="#ffcd11" d="m180.6 97.067c-2.821 2.909-4.792 5.786-6.404 7.667-1.657 1.916-5.748 4.373-3.858 6.512 1.593 1.912 8.455-1.938 13.802-5.969 5.284-4.028 13.484-7.75 9.641-11.966-2.012-2.134-6.902-1.456-8.823-0.199-1.474 0.951-2.238 1.752-4.358 3.955z"/>\r
+   <path fill="#ffce14" d="m180.736 97.221c-2.771 2.83-4.705 5.645-6.29 7.476-1.626 1.86-5.644 4.278-3.813 6.342 1.542 1.845 8.244-1.938 13.47-5.886 5.166-3.945 13.169-7.612 9.444-11.68-1.949-2.059-6.716-1.365-8.592-0.135-1.437 0.933-2.166 1.766-4.219 3.883z"/>\r
+   <path fill="#ffce16" d="m180.872 97.375c-2.722 2.752-4.617 5.504-6.176 7.286-1.595 1.805-5.539 4.182-3.767 6.172 1.49 1.779 8.031-1.937 13.136-5.803 5.046-3.862 12.853-7.475 9.249-11.394-1.889-1.983-6.53-1.274-8.362-0.071-1.4 0.914-2.095 1.779-4.08 3.81z"/>\r
+   <path fill="#ffce19" d="m181.009 97.528c-2.673 2.674-4.53 5.364-6.063 7.095-1.564 1.749-5.435 4.087-3.722 6.001 1.44 1.713 7.82-1.936 12.804-5.719 4.927-3.78 12.537-7.338 9.052-11.108-1.825-1.907-6.343-1.185-8.13-0.007-1.364 0.896-2.024 1.793-3.941 3.738z"/>\r
+   <path fill="#ffce1c" d="m181.145 97.682c-2.623 2.595-4.444 5.225-5.949 6.904-1.534 1.693-5.33 3.992-3.676 5.831 1.39 1.646 7.608-1.935 12.471-5.636 4.808-3.697 12.221-7.2 8.856-10.822-1.764-1.832-6.157-1.094-7.9 0.057-1.327 0.878-1.952 1.807-3.802 3.666z"/>\r
+   <path fill="#ffcf1e" d="m181.281 97.835c-2.573 2.517-4.357 5.084-5.835 6.714-1.503 1.638-5.226 3.896-3.631 5.661 1.34 1.58 7.396-1.935 12.139-5.553 4.689-3.614 11.905-7.063 8.659-10.536-1.701-1.756-5.97-1.004-7.668 0.121-1.291 0.86-1.881 1.821-3.664 3.593z"/>\r
+   <path fill="#ffcf21" d="m181.417 97.988c-2.522 2.439-4.27 4.944-5.721 6.524-1.472 1.582-5.121 3.801-3.586 5.491 1.29 1.513 7.186-1.934 11.807-5.47 4.569-3.531 11.589-6.926 8.463-10.25-1.639-1.68-5.783-0.914-7.438 0.186-1.254 0.84-1.809 1.834-3.525 3.519z"/>\r
+   <path fill="#ffcf23" d="m181.554 98.142c-2.476 2.361-4.185 4.803-5.608 6.333-1.441 1.527-5.017 3.706-3.54 5.32 1.24 1.447 6.974-1.933 11.474-5.386 4.45-3.448 11.273-6.788 8.268-9.964-1.577-1.605-5.599-0.823-7.207 0.25-1.22 0.822-1.74 1.848-3.387 3.447z"/>\r
+   <path fill="#ffcf26" d="m181.69 98.295c-2.425 2.283-4.098 4.663-5.494 6.143-1.411 1.471-4.912 3.61-3.495 5.15 1.19 1.381 6.763-1.932 11.142-5.303 4.331-3.366 10.957-6.65 8.07-9.679-1.514-1.529-5.411-0.732-6.976 0.313-1.182 0.805-1.667 1.863-3.247 3.376z"/>\r
+   <path fill="#ffd028" d="m181.826 98.449c-2.375 2.204-4.009 4.522-5.38 5.952-1.38 1.416-4.808 3.515-3.449 4.98 1.14 1.314 6.551-1.932 10.81-5.22 4.211-3.283 10.641-6.513 7.874-9.393-1.452-1.454-5.226-0.642-6.745 0.378-1.147 0.786-1.597 1.876-3.11 3.303z"/>\r
+   <path fill="#ffd02b" d="m181.962 98.602c-2.324 2.127-3.922 4.382-5.266 5.762-1.349 1.36-4.703 3.42-3.404 4.809 1.089 1.248 6.34-1.93 10.478-5.136 4.092-3.2 10.325-6.376 7.677-9.106-1.389-1.378-5.038-0.552-6.513 0.441-1.111 0.768-1.526 1.89-2.972 3.23z"/>\r
+   <path fill="#ffd02d" d="m182.099 98.756c-2.276 2.048-3.836 4.241-5.153 5.571-1.318 1.305-4.599 3.324-3.359 4.639 1.039 1.182 6.128-1.93 10.146-5.053 3.973-3.117 10.009-6.238 7.48-8.82-1.328-1.303-4.852-0.462-6.282 0.506-1.074 0.748-1.454 1.903-2.832 3.157z"/>\r
+   <path fill="#ffd030" d="m182.235 98.909c-2.228 1.97-3.749 4.101-5.039 5.381-1.288 1.249-4.494 3.229-3.313 4.469 0.988 1.115 5.916-1.929 9.813-4.97 3.853-3.034 9.693-6.101 7.285-8.535-1.267-1.227-4.666-0.371-6.052 0.57-1.038 0.731-1.384 1.918-2.694 3.085z"/>\r
+   <path fill="#ffd133" d="m182.371 99.063c-2.177 1.892-3.662 3.96-4.925 5.19-1.257 1.193-4.39 3.133-3.268 4.298 0.938 1.049 5.704-1.928 9.479-4.886 3.734-2.952 9.377-5.963 7.088-8.249-1.203-1.151-4.479-0.281-5.821 0.634-0.999 0.713-1.31 1.931-2.553 3.013z"/>\r
+  </g>\r
+  <g transform="translate(-12.4048,10.0005)">\r
+   <path fill="#fff" d="m186.414 168.569c0.864-2.808 28.872-9.432 33.48-7.272 4.536 2.16 26.279 33.768 22.392 35.496-3.888 1.657-12.24-10.512-24.408-16.128s-32.328-9.216-31.464-12.096z"/>\r
+   <path fill="#f9f9f9" d="m187.239 168.626c0.848-2.761 28.145-9.076 32.69-6.997 4.476 2.079 25.768 32.897 21.943 34.591-3.824 1.625-11.965-10.346-23.94-15.874-11.976-5.527-31.541-8.89-30.693-11.72z"/>\r
+   <path fill="#f4f4f4" d="m188.063 168.683c0.832-2.714 27.418-8.72 31.899-6.722 4.417 1.998 25.259 32.026 21.497 33.685-3.76 1.595-11.689-10.18-23.474-15.619-11.782-5.438-30.754-8.565-29.922-11.344z"/>\r
+   <path fill="#efefef" d="m188.888 168.74c0.814-2.668 26.69-8.364 31.109-6.447 4.357 1.917 24.746 31.155 21.049 32.779-3.695 1.563-11.416-10.014-23.007-15.364-11.59-5.349-29.967-8.239-29.151-10.968z"/>\r
+   <path fill="#eaeaea" d="m189.712 168.797c0.801-2.621 25.964-8.009 30.32-6.173 4.299 1.837 24.235 30.285 20.603 31.874-3.633 1.532-11.142-9.847-22.54-15.109-11.4-5.261-29.182-7.914-28.383-10.592z"/>\r
+   <path fill="#e5e5e5" d="m190.537 168.853c0.783-2.573 25.236-7.652 29.53-5.897 4.239 1.756 23.723 29.414 20.155 30.968-3.569 1.501-10.867-9.681-22.074-14.854-11.206-5.172-28.395-7.589-27.611-10.217z"/>\r
+   <path fill="#e0e0e0" d="m191.361 168.91c0.768-2.527 24.51-7.296 28.74-5.622 4.18 1.675 23.212 28.543 19.708 30.063-3.505 1.469-10.593-9.516-21.607-14.6-11.014-5.083-27.608-7.263-26.841-9.841z"/>\r
+   <path fill="#dbdbdb" d="m192.186 168.967c0.751-2.48 23.781-6.941 27.95-5.347 4.119 1.593 22.7 27.671 19.26 29.157-3.441 1.438-10.318-9.349-21.141-14.345-10.821-4.994-26.821-6.938-26.069-9.465z"/>\r
+   <path fill="#d6d6d6" d="m193.01 169.024c0.735-2.433 23.057-6.585 27.16-5.073 4.062 1.513 22.19 26.801 18.813 28.252-3.377 1.407-10.043-9.183-20.673-14.09-10.629-4.906-26.035-6.612-25.3-9.089z"/>\r
+   <path fill="#d1d1d1" d="m193.835 169.081c0.72-2.387 22.328-6.229 26.37-4.798 4.001 1.432 21.678 25.93 18.365 27.346-3.313 1.376-9.768-9.017-20.206-13.835-10.437-4.817-25.248-6.287-24.529-8.713z"/>\r
+   <path fill="#ccc" d="m194.659 169.137c0.703-2.339 21.603-5.873 25.58-4.521 3.942 1.351 21.167 25.059 17.918 26.44-3.249 1.345-9.493-8.851-19.739-13.58-10.245-4.729-24.462-5.963-23.759-8.339z"/>\r
+   <path fill="#c6c6c6" d="m195.484 169.194c0.687-2.292 20.874-5.517 24.79-4.247 3.882 1.27 20.655 24.188 17.47 25.535-3.185 1.314-9.219-8.685-19.271-13.326-10.054-4.639-23.676-5.636-22.989-7.962z"/>\r
+   <path fill="#c1c1c1" d="m196.308 169.251c0.671-2.246 20.147-5.161 24-3.973 3.822 1.19 20.145 23.318 17.022 24.63-3.121 1.283-8.943-8.519-18.805-13.071-9.859-4.551-22.888-5.311-22.217-7.586z"/>\r
+   <path fill="#bcbcbc" d="m197.133 169.308c0.654-2.199 19.421-4.805 23.21-3.698 3.764 1.109 19.634 22.447 16.575 23.724-3.057 1.252-8.669-8.353-18.338-12.816-9.668-4.462-22.102-4.985-21.447-7.21z"/>\r
+   <path fill="#b7b7b7" d="m197.957 169.365c0.64-2.152 18.693-4.45 22.42-3.423 3.705 1.027 19.122 21.575 16.129 22.818-2.993 1.221-8.395-8.186-17.872-12.561-9.476-4.373-21.315-4.66-20.677-6.834z"/>\r
+   <path fill="#b2b2b2" d="m198.782 169.421c0.622-2.105 17.966-4.093 21.63-3.147 3.646 0.946 18.61 20.704 15.681 21.912-2.93 1.19-8.12-8.02-17.404-12.306-9.284-4.284-20.53-4.335-19.907-6.459z"/>\r
+   <path fill="#adadad" d="m199.606 169.478c0.606-2.058 17.239-3.737 20.84-2.873 3.586 0.866 18.099 19.834 15.234 21.008-2.866 1.158-7.847-7.855-16.938-12.052-9.091-4.196-19.742-4.009-19.136-6.083z"/>\r
+   <path fill="#a8a8a8" d="m200.431 169.535c0.59-2.011 16.512-3.382 20.05-2.598 3.525 0.785 17.588 18.963 14.786 20.102-2.803 1.127-7.571-7.688-16.472-11.797-8.898-4.107-18.955-3.684-18.364-5.707z"/>\r
+   <path fill="#a3a3a3" d="m201.255 169.592c0.574-1.965 15.785-3.026 19.261-2.323 3.467 0.704 17.076 18.092 14.339 19.196-2.738 1.096-7.296-7.522-16.004-11.542-8.707-4.018-18.17-3.358-17.596-5.331z"/>\r
+   <path fill="#9e9e9e" d="m202.08 169.649c0.559-1.918 15.059-2.67 18.47-2.048 3.407 0.623 16.565 17.221 13.892 18.29-2.674 1.065-7.022-7.356-15.537-11.287-8.515-3.929-17.383-3.033-16.825-4.955z"/>\r
+   <path fill="#999" d="m202.904 169.705c0.542-1.871 14.331-2.314 17.68-1.773 3.349 0.542 16.055 16.35 13.444 17.385-2.61 1.034-6.747-7.19-15.07-11.032-8.322-3.841-16.596-2.708-16.054-4.58z"/>\r
+  </g>\r
+  <g transform="translate(-12.4048,10.0005)">\r
+   <path fill="#fff" d="m151.134 211.625c2.881 0.144 0.145 16.271 0.145 32.903s2.231 22.464 0.144 24.552-5.688-5.399-5.688-22.031c-0.001-16.632 2.519-35.568 5.399-35.424z"/>\r
+   <path fill="#f9f9f9" d="m151.105 212.016c2.783 0.162 0.109 16.052 0.097 32.419-0.012 16.366 2.188 22.208 0.164 24.237-2.02 2.029-5.561-5.383-5.546-21.752 0.012-16.367 2.502-35.065 5.285-34.904z"/>\r
+   <path fill="#f4f4f4" d="m151.075 212.407c2.687 0.18 0.076 15.832 0.051 31.934-0.023 16.102 2.143 21.951 0.185 23.924-1.953 1.968-5.435-5.367-5.405-21.473 0.024-16.103 2.484-34.564 5.169-34.385z"/>\r
+   <path fill="#efefef" d="m151.046 212.797c2.588 0.197 0.041 15.613 0.004 31.449-0.036 15.836 2.098 21.694 0.204 23.609-1.886 1.907-5.308-5.352-5.263-21.195 0.037-15.835 2.467-34.058 5.055-33.863z"/>\r
+   <path fill="#eaeaea" d="m151.017 213.189c2.49 0.214 0.007 15.392-0.043 30.962-0.05 15.571 2.052 21.439 0.224 23.297-1.818 1.848-5.181-5.334-5.122-20.916 0.049-15.571 2.45-33.557 4.941-33.343z"/>\r
+   <path fill="#e5e5e5" d="m150.987 213.581c2.394 0.23-0.027 15.17-0.089 30.477s2.007 21.182 0.244 22.982c-1.751 1.787-5.055-5.32-4.98-20.638 0.061-15.305 2.431-33.053 4.825-32.821z"/>\r
+   <path fill="#e0e0e0" d="m150.958 213.971c2.297 0.248-0.062 14.951-0.136 29.99-0.074 15.041 1.962 20.927 0.264 22.668-1.683 1.728-4.928-5.301-4.839-20.356 0.074-15.04 2.414-32.551 4.711-32.302z"/>\r
+   <path fill="#dbdbdb" d="m150.928 214.362c2.199 0.266-0.096 14.73-0.182 29.506-0.087 14.775 1.915 20.67 0.282 22.354-1.615 1.667-4.8-5.286-4.696-20.078 0.087-14.776 2.397-32.048 4.596-31.782z"/>\r
+   <path fill="#d6d6d6" d="m150.899 214.752c2.102 0.283-0.13 14.511-0.229 29.021-0.099 14.511 1.87 20.413 0.303 22.04-1.549 1.607-4.674-5.27-4.556-19.799 0.1-14.51 2.38-31.545 4.482-31.262z"/>\r
+   <path fill="#d1d1d1" d="m150.87 215.144c2.005 0.301-0.165 14.29-0.274 28.535-0.112 14.245 1.824 20.155 0.321 21.725-1.479 1.548-4.547-5.252-4.413-19.519 0.11-14.245 2.361-31.043 4.366-30.741z"/>\r
+   <path fill="#ccc" d="m150.84 215.536c1.908 0.317-0.197 14.069-0.32 28.049-0.124 13.979 1.779 19.899 0.342 21.412-1.413 1.486-4.42-5.238-4.272-19.242 0.122-13.979 2.343-30.54 4.25-30.219z"/>\r
+   <path fill="#c6c6c6" d="m150.811 215.926c1.811 0.334-0.233 13.85-0.368 27.564-0.136 13.713 1.735 19.643 0.362 21.096-1.346 1.428-4.293-5.219-4.131-18.961 0.136-13.712 2.327-30.035 4.137-29.699z"/>\r
+   <path fill="#c1c1c1" d="m150.781 216.317c1.714 0.354-0.267 13.629-0.414 27.078s1.69 19.387 0.382 20.783c-1.277 1.367-4.166-5.203-3.989-18.682 0.148-13.449 2.308-29.533 4.021-29.179z"/>\r
+   <path fill="#bcbcbc" d="m150.752 216.708c1.616 0.371-0.301 13.41-0.461 26.594-0.161 13.184 1.646 19.13 0.402 20.469-1.211 1.307-4.04-5.188-3.847-18.402 0.16-13.185 2.29-29.033 3.906-28.661z"/>\r
+   <path fill="#b7b7b7" d="m150.723 217.099c1.519 0.387-0.336 13.188-0.509 26.106-0.173 12.92 1.601 18.875 0.423 20.156-1.144 1.246-3.913-5.171-3.706-18.123 0.172-12.919 2.273-28.529 3.792-28.139z"/>\r
+   <path fill="#b2b2b2" d="m150.693 217.491c1.422 0.404-0.37 12.969-0.554 25.621-0.186 12.653 1.555 18.617 0.441 19.842-1.076 1.187-3.786-5.156-3.563-17.846 0.184-12.652 2.255-28.024 3.676-27.617z"/>\r
+   <path fill="#adadad" d="m150.664 217.881c1.325 0.422-0.404 12.748-0.601 25.136-0.198 12.388 1.51 18.36 0.462 19.528-1.008 1.125-3.66-5.139-3.423-17.566 0.197-12.389 2.238-27.521 3.562-27.098z"/>\r
+   <path fill="#a8a8a8" d="m150.634 218.272c1.229 0.439-0.438 12.527-0.646 24.65-0.21 12.123 1.464 18.104 0.48 19.213-0.939 1.066-3.531-5.121-3.279-17.285 0.208-12.123 2.219-27.019 3.445-26.578z"/>\r
+   <path fill="#a3a3a3" d="m150.605 218.663c1.13 0.457-0.474 12.309-0.694 24.166-0.222 11.857 1.419 17.848 0.501 18.899-0.873 1.006-3.405-5.106-3.139-17.009 0.222-11.855 2.202-26.515 3.332-26.056z"/>\r
+   <path fill="#9e9e9e" d="m150.576 219.054c1.033 0.474-0.507 12.088-0.741 23.68-0.234 11.593 1.374 17.591 0.521 18.585-0.806 0.946-3.279-5.089-2.997-16.729 0.233-11.591 2.184-26.011 3.217-25.536z"/>\r
+   <path fill="#999" d="m150.546 219.444c0.937 0.492-0.541 11.868-0.787 23.195-0.246 11.326 1.329 17.335 0.541 18.271-0.737 0.885-3.151-5.074-2.855-16.449 0.245-11.328 2.166-25.509 3.101-25.017z"/>\r
+  </g>\r
+  <g transform="translate(-12.4048,10.0005)">\r
+   <path fill="#fff" d="m157.434 167.161c1.735 0.192 12.437-2.218 12.822-1.254 0.386 0.772-6.651 2.893-8.966 5.303-0.771 0.771-2.796 2.603-4.049 2.41-0.964-0.096-1.543-2.121-2.989-3.664-3.471-3.47-5.688-3.181-5.013-4.531 0.579-1.06 5.207 1.446 8.195 1.736z"/>\r
+   <path fill="#fbfbfb" d="m157.479 167.201c1.7 0.188 12.176-2.171 12.554-1.227 0.377 0.755-6.512 2.832-8.778 5.191-0.755 0.755-2.736 2.549-3.964 2.36-0.942-0.094-1.51-2.077-2.926-3.587-3.398-3.397-5.568-3.115-4.907-4.436 0.565-1.038 5.096 1.415 8.021 1.699z"/>\r
+   <path fill="#f8f8f8" d="m157.525 167.241c1.663 0.184 11.914-2.124 12.283-1.201 0.369 0.739-6.372 2.771-8.589 5.08-0.738 0.739-2.679 2.494-3.879 2.309-0.924-0.092-1.479-2.032-2.863-3.51-3.325-3.324-5.449-3.048-4.803-4.341 0.555-1.015 4.988 1.385 7.851 1.663z"/>\r
+   <path fill="#f5f5f5" d="m157.57 167.281c1.626 0.18 11.652-2.078 12.014-1.175 0.361 0.723-6.231 2.711-8.4 4.969-0.723 0.722-2.619 2.439-3.793 2.258-0.903-0.09-1.446-1.987-2.802-3.433-3.252-3.251-5.329-2.981-4.695-4.245 0.54-0.993 4.876 1.354 7.676 1.626z"/>\r
+   <path fill="#f2f2f2" d="m157.615 167.321c1.59 0.176 11.391-2.031 11.745-1.148 0.352 0.706-6.093 2.649-8.212 4.856-0.707 0.707-2.562 2.385-3.709 2.208-0.883-0.088-1.413-1.943-2.738-3.356-3.179-3.178-5.209-2.914-4.591-4.15 0.529-0.971 4.768 1.324 7.505 1.59z"/>\r
+   <path fill="#efefef" d="m157.66 167.361c1.554 0.172 11.13-1.985 11.475-1.122 0.346 0.69-5.952 2.589-8.022 4.745-0.69 0.69-2.503 2.33-3.624 2.157-0.863-0.086-1.381-1.898-2.675-3.279-3.106-3.105-5.09-2.847-4.486-4.055 0.517-0.948 4.658 1.294 7.332 1.554z"/>\r
+   <path fill="#ebebeb" d="m157.705 167.401c1.518 0.168 10.868-1.938 11.206-1.096 0.336 0.674-5.813 2.528-7.835 4.634-0.674 0.674-2.444 2.275-3.539 2.106-0.842-0.084-1.348-1.853-2.612-3.202-3.032-3.032-4.97-2.78-4.38-3.959 0.505-0.926 4.549 1.263 7.16 1.517z"/>\r
+   <path fill="#e8e8e8" d="m157.751 167.441c1.48 0.164 10.606-1.892 10.936-1.069 0.329 0.657-5.673 2.467-7.646 4.522-0.658 0.657-2.385 2.22-3.453 2.055-0.822-0.082-1.315-1.809-2.549-3.124-2.96-2.96-4.851-2.714-4.275-3.865 0.491-0.904 4.438 1.233 6.987 1.481z"/>\r
+   <path fill="#e5e5e5" d="m157.796 167.481c1.444 0.16 10.346-1.845 10.666-1.043 0.32 0.641-5.532 2.406-7.458 4.41-0.641 0.642-2.325 2.166-3.367 2.005-0.803-0.08-1.284-1.764-2.486-3.047-2.887-2.887-4.732-2.647-4.17-3.769 0.48-0.882 4.329 1.202 6.815 1.444z"/>\r
+   <path fill="#e2e2e2" d="m157.841 167.521c1.407 0.156 10.083-1.799 10.397-1.017 0.312 0.625-5.394 2.346-7.271 4.299-0.625 0.625-2.267 2.111-3.282 1.954-0.782-0.078-1.251-1.719-2.423-2.97-2.814-2.814-4.612-2.58-4.065-3.674 0.469-0.859 4.221 1.172 6.644 1.408z"/>\r
+   <path fill="#dfdfdf" d="m157.886 167.56c1.37 0.152 9.821-1.751 10.127-0.99 0.304 0.609-5.254 2.285-7.081 4.188-0.609 0.609-2.208 2.056-3.198 1.903-0.761-0.076-1.218-1.675-2.36-2.893-2.741-2.741-4.492-2.513-3.959-3.579 0.456-0.837 4.111 1.142 6.471 1.371z"/>\r
+   <path fill="#dbdbdb" d="m157.931 167.6c1.335 0.148 9.561-1.704 9.857-0.963 0.296 0.592-5.114 2.223-6.893 4.076-0.593 0.593-2.149 2.001-3.113 1.853-0.741-0.074-1.186-1.631-2.297-2.817-2.668-2.667-4.373-2.446-3.854-3.483 0.445-0.815 4.003 1.111 6.3 1.334z"/>\r
+   <path fill="#d8d8d8" d="m157.977 167.64c1.298 0.144 9.299-1.658 9.587-0.937 0.288 0.576-4.974 2.163-6.704 3.964-0.576 0.577-2.091 1.947-3.027 1.803-0.721-0.072-1.153-1.586-2.234-2.74-2.596-2.594-4.253-2.379-3.748-3.388 0.431-0.792 3.891 1.081 6.126 1.298z"/>\r
+   <path fill="#d5d5d5" d="m158.022 167.68c1.261 0.14 9.037-1.611 9.317-0.911 0.28 0.56-4.834 2.102-6.516 3.853-0.56 0.561-2.032 1.892-2.942 1.752-0.7-0.07-1.12-1.541-2.172-2.663-2.521-2.521-4.133-2.312-3.643-3.292 0.421-0.77 3.784 1.05 5.956 1.261z"/>\r
+   <path fill="#d2d2d2" d="m158.067 167.72c1.225 0.136 8.775-1.564 9.049-0.884 0.271 0.543-4.695 2.041-6.327 3.741-0.545 0.544-1.974 1.837-2.857 1.701-0.682-0.068-1.09-1.497-2.109-2.585-2.449-2.449-4.014-2.246-3.538-3.198 0.407-0.748 3.673 1.02 5.782 1.225z"/>\r
+   <path fill="#cfcfcf" d="m158.112 167.76c1.188 0.132 8.515-1.518 8.779-0.858 0.264 0.527-4.555 1.98-6.139 3.63-0.527 0.528-1.915 1.782-2.772 1.65-0.66-0.066-1.057-1.452-2.046-2.508-2.376-2.376-3.895-2.179-3.433-3.103 0.397-0.725 3.565 0.99 5.611 1.189z"/>\r
+   <path fill="#ccc" d="m158.157 167.8c1.152 0.128 8.253-1.472 8.51-0.832 0.255 0.511-4.415 1.92-5.95 3.518-0.512 0.512-1.855 1.728-2.688 1.6-0.64-0.064-1.023-1.407-1.983-2.431-2.303-2.303-3.773-2.112-3.326-3.007 0.383-0.703 3.454 0.959 5.437 1.152z"/>\r
+   <path fill="#c8c8c8" d="m158.203 167.84c1.115 0.124 7.991-1.425 8.239-0.805 0.248 0.494-4.274 1.858-5.761 3.406-0.496 0.496-1.798 1.673-2.603 1.549-0.62-0.063-0.992-1.363-1.921-2.354-2.229-2.229-3.655-2.045-3.221-2.912 0.372-0.681 3.346 0.929 5.267 1.116z"/>\r
+   <path fill="#c5c5c5" d="m158.248 167.88c1.079 0.12 7.73-1.379 7.97-0.779 0.239 0.478-4.135 1.798-5.572 3.295-0.479 0.479-1.739 1.618-2.518 1.498-0.6-0.06-0.959-1.318-1.857-2.277-2.157-2.157-3.535-1.978-3.116-2.816 0.359-0.659 3.235 0.898 5.093 1.079z"/>\r
+   <path fill="#c2c2c2" d="m158.293 167.92c1.042 0.116 7.469-1.332 7.701-0.753 0.231 0.462-3.995 1.737-5.385 3.184-0.463 0.463-1.68 1.563-2.432 1.447-0.579-0.058-0.927-1.273-1.796-2.2-2.084-2.084-3.415-1.911-3.01-2.721 0.348-0.636 3.127 0.868 4.922 1.043z"/>\r
+   <path fill="#bfbfbf" d="m158.338 167.959c1.007 0.112 7.207-1.285 7.432-0.726 0.223 0.446-3.855 1.676-5.196 3.072-0.447 0.447-1.621 1.509-2.347 1.397-0.56-0.056-0.895-1.229-1.732-2.123-2.011-2.011-3.296-1.844-2.905-2.626 0.334-0.614 3.016 0.838 4.748 1.006z"/>\r
+  </g>\r
+  <g transform="translate(-12.4048,10.0005)">\r
+   <path d="m194.253 11.922c-1.222 2.631-3.812 23.214-0.248 20.892 3.594-2.341 13.57-5.312 19.886-7.013 7.003-1.886-17.188-19.463-19.638-13.879z"/>\r
+   <path fill="#060606" d="m194.485 12.307c-1.21 2.594-3.704 22.255-0.234 20.007 3.491-2.262 13.077-5.1 19.039-6.782 6.59-1.905-16.436-18.609-18.805-13.225z"/>\r
+   <path fill="#0c0c0c" d="m194.717 12.691c-1.198 2.557-3.595 21.296-0.221 19.124 3.391-2.184 12.586-4.888 18.194-6.551 6.177-1.924-15.684-17.757-17.973-12.573z"/>\r
+   <path fill="#131313" d="m194.949 13.076c-1.187 2.52-3.487 20.337-0.207 18.239 3.288-2.105 12.093-4.676 17.348-6.321 5.763-1.941-14.933-16.903-17.141-11.918z"/>\r
+   <path fill="#191919" d="m195.181 13.46c-1.177 2.483-3.379 19.378-0.193 17.355 3.187-2.027 11.6-4.464 16.502-6.09 5.349-1.959-14.182-16.05-16.309-11.265z"/>\r
+   <path fill="#1f1f1f" d="m195.413 13.845c-1.164 2.446-3.27 18.419-0.18 16.471 3.086-1.949 11.107-4.252 15.657-5.859 4.935-1.978-13.43-15.198-15.477-10.612z"/>\r
+   <path fill="#262626" d="m195.645 14.229c-1.153 2.409-3.162 17.46-0.166 15.586 2.983-1.87 10.616-4.04 14.811-5.628 4.521-1.995-12.679-14.344-14.645-9.958z"/>\r
+   <path fill="#2c2c2c" d="m195.878 14.614c-1.142 2.372-3.055 16.501-0.152 14.702 2.882-1.792 10.123-3.828 13.965-5.398 4.107-2.013-11.929-13.49-13.813-9.304z"/>\r
+   <path fill="#333" d="m196.11 14.999c-1.131 2.335-2.946 15.542-0.14 13.817 2.78-1.713 9.631-3.616 13.119-5.167 3.695-2.031-11.175-12.637-12.979-8.65z"/>\r
+   <path fill="#393939" d="m196.342 15.383c-1.118 2.299-2.838 14.583-0.126 12.934 2.68-1.636 9.139-3.404 12.274-4.937 3.28-2.049-10.425-11.784-12.148-7.997z"/>\r
+   <path fill="#3f3f3f" d="m196.574 15.768c-1.108 2.261-2.729 13.624-0.112 12.049 2.577-1.557 8.646-3.192 11.429-4.706 2.865-2.068-9.675-10.931-11.317-7.343z"/>\r
+   <path fill="#464646" d="m196.806 16.152c-1.097 2.225-2.622 12.665-0.1 11.165 2.477-1.479 8.154-2.98 10.583-4.475 2.453-2.086-8.922-10.078-10.483-6.69z"/>\r
+   <path fill="#4c4c4c" d="m197.038 16.537c-1.085 2.188-2.513 11.706-0.085 10.28 2.374-1.4 7.661-2.768 9.737-4.244 2.039-2.104-8.171-9.225-9.652-6.036z"/>\r
+   <path fill="#525252" d="m197.27 16.921c-1.073 2.151-2.405 10.747-0.071 9.396 2.272-1.322 7.168-2.556 8.891-4.013 1.625-2.122-7.42-8.371-8.82-5.383z"/>\r
+   <path fill="#595959" d="m197.502 17.306c-1.062 2.113-2.297 9.788-0.058 8.512 2.172-1.244 6.677-2.344 8.046-3.783 1.211-2.14-6.669-7.518-7.988-4.729z"/>\r
+   <path fill="#5f5f5f" d="m197.734 17.69c-1.05 2.077-2.188 8.829-0.044 7.627 2.069-1.165 6.184-2.132 7.2-3.552 0.797-2.157-5.917-6.664-7.156-4.075z"/>\r
+   <path fill="#666" d="m197.966 18.075c-1.038 2.04-2.079 7.87-0.029 6.743 1.968-1.087 5.69-1.92 6.354-3.321 0.382-2.176-5.167-5.812-6.325-3.422z"/>\r
+   <path fill="#6c6c6c" d="m198.198 18.459c-1.027 2.003-1.972 6.911-0.017 5.859 1.866-1.008 5.198-1.708 5.509-3.09-0.03-2.194-4.415-4.959-5.492-2.769z"/>\r
+   <path fill="#727272" d="m198.43 18.844c-1.017 1.966-1.863 5.952-0.003 4.975 1.765-0.93 4.706-1.496 4.662-2.86-0.443-2.212-3.662-4.106-4.659-2.115z"/>\r
+   <path fill="#797979" d="m198.662 19.228c-1.004 1.929-1.755 4.993 0.011 4.09 1.663-0.852 4.215-1.284 3.817-2.629-0.858-2.23-2.912-3.251-3.828-1.461z"/>\r
+   <path fill="#7f7f7f" d="m198.894 19.612c-0.993 1.892-1.647 4.034 0.023 3.206 1.563-0.773 3.723-1.072 2.973-2.398-1.272-2.248-2.161-2.399-2.996-0.808z"/>\r
+  </g>\r
+  <g transform="translate(-12.4048,10.0005)">\r
+   <path d="m143.502 46.386c-0.72 2.16 8.712 5.112 10.801 6.984 2.808 2.52 3.023 7.488 6.336 5.472 2.159-1.296 0.504-4.176-3.456-8.568-5.833-6.481-13.033-5.689-13.681-3.888z"/>\r
+   <path fill="#050505" d="m143.991 46.582c-0.716 2.073 8.275 4.9 10.336 6.741 2.745 2.457 2.961 7.249 6.146 5.313 2.094-1.254 0.449-4.072-3.343-8.28-5.574-6.203-12.491-5.505-13.139-3.774z"/>\r
+   <path fill="#0a0a0a" d="m144.479 46.779c-0.71 1.987 7.839 4.688 9.873 6.498 2.682 2.394 2.897 7.009 5.956 5.154 2.028-1.212 0.395-3.968-3.228-7.993-5.319-5.926-11.953-5.321-12.601-3.659z"/>\r
+   <path fill="#0f0f0f" d="m144.967 46.976c-0.704 1.9 7.403 4.476 9.41 6.254 2.62 2.33 2.835 6.77 5.766 4.995 1.964-1.171 0.342-3.864-3.112-7.706-5.064-5.647-11.415-5.137-12.064-3.543z"/>\r
+   <path fill="#141414" d="m145.456 47.172c-0.701 1.813 6.966 4.263 8.946 6.011 2.557 2.266 2.772 6.53 5.575 4.835 1.897-1.129 0.287-3.76-2.998-7.418-4.807-5.369-10.874-4.952-11.523-3.428z"/>\r
+   <path fill="#191919" d="m145.944 47.369c-0.696 1.726 6.53 4.051 8.483 5.768 2.493 2.203 2.71 6.291 5.385 4.676 1.833-1.087 0.231-3.656-2.884-7.13-4.551-5.093-10.335-4.769-10.984-3.314z"/>\r
+   <path fill="#1e1e1e" d="m146.433 47.565c-0.692 1.64 6.093 3.839 8.019 5.525 2.431 2.14 2.647 6.052 5.194 4.517 1.768-1.046 0.179-3.552-2.77-6.843-4.293-4.814-9.794-4.585-10.443-3.199z"/>\r
+   <path fill="#232323" d="m146.921 47.762c-0.686 1.553 5.657 3.627 7.558 5.282 2.367 2.076 2.583 5.813 5.003 4.357 1.702-1.003 0.124-3.448-2.654-6.555-4.04-4.537-9.257-4.401-9.907-3.084z"/>\r
+   <path fill="#282828" d="m147.409 47.959c-0.681 1.466 5.221 3.415 7.094 5.039 2.305 2.013 2.521 5.573 4.813 4.198 1.637-0.962 0.07-3.344-2.54-6.268-3.782-4.26-8.717-4.218-9.367-2.969z"/>\r
+   <path fill="#2d2d2d" d="m147.898 48.156c-0.677 1.379 4.784 3.203 6.63 4.795 2.242 1.949 2.457 5.333 4.622 4.039 1.572-0.92 0.016-3.24-2.425-5.98-3.526-3.983-8.177-4.034-8.827-2.854z"/>\r
+   <path fill="#333" d="m148.386 48.353c-0.673 1.292 4.348 2.99 6.167 4.552 2.179 1.886 2.394 5.095 4.432 3.88 1.506-0.878-0.038-3.136-2.312-5.693-3.268-3.705-7.636-3.85-8.287-2.739z"/>\r
+   <path fill="#383838" d="m148.875 48.549c-0.668 1.206 3.911 2.778 5.703 4.309 2.116 1.823 2.331 4.855 4.242 3.721 1.439-0.836-0.093-3.032-2.197-5.405-3.013-3.428-7.098-3.667-7.748-2.625z"/>\r
+   <path fill="#3d3d3d" d="m149.363 48.746c-0.662 1.119 3.475 2.566 5.24 4.065 2.053 1.759 2.268 4.616 4.052 3.562 1.375-0.795-0.147-2.928-2.082-5.118-2.757-3.15-6.559-3.483-7.21-2.509z"/>\r
+   <path fill="#424242" d="m149.851 48.942c-0.657 1.032 3.039 2.354 4.776 3.823 1.99 1.696 2.205 4.376 3.861 3.402 1.31-0.753-0.201-2.824-1.967-4.831-2.5-2.871-6.018-3.298-6.67-2.394z"/>\r
+   <path fill="#474747" d="m150.34 49.139c-0.652 0.946 2.603 2.142 4.313 3.58 1.927 1.632 2.143 4.137 3.671 3.243 1.244-0.712-0.256-2.72-1.853-4.543-2.245-2.595-5.48-3.115-6.131-2.28z"/>\r
+   <path fill="#4c4c4c" d="m150.828 49.336c-0.647 0.859 2.166 1.93 3.851 3.336 1.863 1.569 2.079 3.898 3.48 3.084 1.179-0.67-0.31-2.616-1.739-4.255-1.988-2.317-4.94-2.932-5.592-2.165z"/>\r
+   <path fill="#515151" d="m151.317 49.533c-0.645 0.772 1.729 1.718 3.386 3.093 1.802 1.505 2.018 3.658 3.29 2.925 1.114-0.628-0.364-2.512-1.624-3.968-1.732-2.04-4.4-2.748-5.052-2.05z"/>\r
+   <path fill="#565656" d="m151.805 49.729c-0.639 0.685 1.293 1.505 2.923 2.85 1.738 1.442 1.954 3.419 3.1 2.766 1.048-0.586-0.418-2.408-1.509-3.681-1.476-1.762-3.862-2.563-4.514-1.935z"/>\r
+   <path fill="#5b5b5b" d="m152.293 49.926c-0.633 0.598 0.857 1.293 2.46 2.606 1.677 1.379 1.892 3.18 2.91 2.606 0.983-0.544-0.473-2.304-1.395-3.393-1.22-1.483-3.322-2.379-3.975-1.819z"/>\r
+   <path fill="#606060" d="m152.782 50.123c-0.629 0.512 0.42 1.081 1.996 2.363 1.613 1.315 1.828 2.94 2.719 2.447 0.918-0.502-0.525-2.2-1.28-3.105-0.963-1.207-2.782-2.196-3.435-1.705z"/>\r
+   <path fill="#666" d="m153.27 50.319c-0.624 0.425-0.017 0.869 1.533 2.12 1.55 1.252 1.765 2.701 2.528 2.288 0.853-0.461-0.581-2.096-1.166-2.818-0.706-0.929-2.242-2.012-2.895-1.59z"/>\r
+  </g>\r
+  <g transform="translate(-12.4048,10.0005)">\r
+   <path d="m193.47 45.594c-0.072 1.08 2.951 1.728 4.896 2.448 1.944 0.648 5.76 3.24 7.56 5.256 1.801 1.944 5.688 7.704 6.553 6.192 0.863-1.368-2.017-5.328-2.809-6.984s-3.239-5.256-7.128-6.48c-3.384-1.008-9-1.297-9.072-0.432z"/>\r
+   <path fill="#060606" d="m193.779 45.67c-0.071 1.05 2.869 1.685 4.758 2.387 1.891 0.633 5.598 3.161 7.345 5.122 1.747 1.893 5.535 7.493 6.376 6.027 0.84-1.328-1.936-5.173-2.738-6.795-0.8-1.622-3.175-5.09-6.952-6.301-3.282-0.995-8.718-1.28-8.789-0.44z"/>\r
+   <path fill="#0c0c0c" d="m194.088 45.747c-0.07 1.021 2.785 1.641 4.62 2.327 1.836 0.618 5.436 3.081 7.131 4.988 1.692 1.842 5.382 7.282 6.198 5.862 0.814-1.288-1.855-5.019-2.67-6.607-0.808-1.587-3.11-4.925-6.776-6.122-3.178-0.983-8.433-1.264-8.503-0.448z"/>\r
+   <path fill="#131313" d="m194.397 45.824c-0.071 0.991 2.702 1.598 4.481 2.267 1.782 0.603 5.272 3.001 6.916 4.854 1.64 1.791 5.229 7.071 6.022 5.697 0.788-1.248-1.776-4.865-2.603-6.418-0.815-1.554-3.044-4.759-6.599-5.942-3.073-0.973-8.148-1.251-8.217-0.458z"/>\r
+   <path fill="#191919" d="m194.706 45.9c-0.069 0.961 2.618 1.555 4.345 2.206 1.728 0.588 5.109 2.922 6.7 4.721 1.586 1.74 5.075 6.86 5.846 5.531 0.764-1.207-1.696-4.711-2.532-6.23-0.824-1.519-2.979-4.593-6.424-5.763-2.972-0.959-7.866-1.234-7.935-0.465z"/>\r
+   <path fill="#1f1f1f" d="m195.015 45.977c-0.07 0.931 2.534 1.511 4.207 2.146 1.672 0.573 4.945 2.843 6.485 4.586 1.531 1.689 4.921 6.649 5.668 5.367 0.738-1.167-1.616-4.557-2.464-6.042-0.832-1.485-2.914-4.428-6.247-5.583-2.868-0.948-7.581-1.219-7.649-0.474z"/>\r
+   <path fill="#262626" d="m195.324 46.054c-0.069 0.901 2.451 1.468 4.069 2.085 1.618 0.557 4.784 2.763 6.271 4.453 1.479 1.638 4.769 6.438 5.491 5.201 0.714-1.127-1.536-4.402-2.396-5.854-0.839-1.451-2.848-4.263-6.07-5.404-2.765-0.934-7.298-1.202-7.365-0.481z"/>\r
+   <path fill="#2c2c2c" d="m195.632 46.13c-0.067 0.872 2.369 1.424 3.933 2.025 1.563 0.542 4.621 2.684 6.056 4.318 1.424 1.587 4.615 6.228 5.315 5.036 0.688-1.086-1.456-4.248-2.326-5.665-0.848-1.416-2.783-4.097-5.896-5.224-2.662-0.923-7.015-1.187-7.082-0.49z"/>\r
+   <path fill="#333" d="m195.941 46.207c-0.068 0.842 2.285 1.381 3.794 1.964 1.51 0.527 4.458 2.605 5.842 4.185 1.37 1.536 4.461 6.016 5.138 4.871 0.662-1.046-1.377-4.093-2.258-5.476-0.855-1.382-2.718-3.932-5.718-5.045-2.56-0.911-6.732-1.172-6.798-0.499z"/>\r
+   <path fill="#393939" d="m196.25 46.284c-0.066 0.813 2.202 1.338 3.656 1.904 1.456 0.512 4.296 2.525 5.627 4.051 1.317 1.485 4.308 5.805 4.961 4.706 0.638-1.006-1.296-3.939-2.188-5.288-0.863-1.348-2.652-3.766-5.542-4.866-2.457-0.899-6.449-1.157-6.514-0.507z"/>\r
+   <path fill="#3f3f3f" d="m196.559 46.36c-0.067 0.783 2.118 1.295 3.518 1.844 1.402 0.497 4.133 2.446 5.412 3.917 1.263 1.434 4.155 5.594 4.785 4.541 0.612-0.966-1.217-3.785-2.12-5.1-0.872-1.313-2.587-3.6-5.366-4.687-2.353-0.886-6.165-1.141-6.229-0.515z"/>\r
+   <path fill="#464646" d="m196.868 46.437c-0.065 0.753 2.035 1.251 3.38 1.783 1.349 0.482 3.972 2.367 5.197 3.783 1.21 1.383 4.002 5.383 4.608 4.375 0.588-0.926-1.137-3.63-2.052-4.911-0.879-1.279-2.521-3.435-5.189-4.507-2.25-0.874-5.881-1.125-5.944-0.523z"/>\r
+   <path fill="#4c4c4c" d="m197.177 46.514c-0.066 0.723 1.95 1.208 3.241 1.723 1.293 0.467 3.809 2.287 4.983 3.649 1.155 1.332 3.848 5.172 4.431 4.21 0.563-0.885-1.057-3.476-1.982-4.723-0.888-1.245-2.456-3.269-5.014-4.328-2.146-0.862-5.597-1.109-5.659-0.531z"/>\r
+   <path fill="#525252" d="m197.486 46.591c-0.066 0.693 1.868 1.164 3.104 1.662 1.239 0.452 3.646 2.208 4.769 3.515 1.102 1.281 3.695 4.961 4.254 4.045 0.537-0.845-0.976-3.321-1.913-4.534-0.896-1.21-2.391-3.103-4.838-4.148-2.044-0.851-5.315-1.095-5.376-0.54z"/>\r
+   <path fill="#595959" d="m197.795 46.667c-0.064 0.664 1.784 1.121 2.968 1.602 1.184 0.437 3.481 2.128 4.552 3.381 1.049 1.23 3.542 4.75 4.078 3.88 0.512-0.805-0.897-3.167-1.846-4.346-0.902-1.176-2.325-2.938-4.66-3.969-1.942-0.838-5.031-1.078-5.092-0.548z"/>\r
+   <path fill="#5f5f5f" d="m198.104 46.744c-0.065 0.634 1.701 1.078 2.829 1.541 1.13 0.421 3.318 2.049 4.338 3.248 0.994 1.179 3.388 4.539 3.899 3.715 0.487-0.765-0.815-3.013-1.775-4.157-0.911-1.142-2.261-2.772-4.485-3.79-1.837-0.826-4.746-1.064-4.806-0.557z"/>\r
+   <path fill="#666" d="m198.413 46.821c-0.063 0.604 1.617 1.034 2.691 1.481 1.076 0.406 3.157 1.969 4.123 3.113 0.94 1.128 3.234 4.328 3.724 3.55 0.462-0.725-0.737-2.858-1.707-3.969-0.919-1.108-2.195-2.606-4.309-3.61-1.734-0.814-4.463-1.049-4.522-0.565z"/>\r
+   <path fill="#6c6c6c" d="m198.721 46.897c-0.063 0.574 1.534 0.991 2.554 1.42 1.021 0.391 2.994 1.89 3.908 2.979 0.887 1.077 3.082 4.117 3.548 3.384 0.436-0.685-0.657-2.704-1.64-3.78-0.927-1.074-2.13-2.44-4.132-3.431-1.631-0.8-4.179-1.031-4.238-0.572z"/>\r
+   <path fill="#727272" d="m199.03 46.974c-0.063 0.544 1.451 0.948 2.416 1.36 0.967 0.376 2.831 1.811 3.694 2.846 0.833 1.026 2.928 3.906 3.369 3.219 0.411-0.644-0.576-2.549-1.569-3.592-0.936-1.04-2.064-2.275-3.956-3.251-1.528-0.79-3.896-1.017-3.954-0.582z"/>\r
+   <path fill="#797979" d="m199.339 47.051c-0.062 0.515 1.368 0.904 2.278 1.299 0.913 0.361 2.669 1.731 3.479 2.712 0.779 0.975 2.774 3.695 3.193 3.054 0.386-0.604-0.497-2.396-1.501-3.403-0.942-1.005-1.999-2.11-3.78-3.072-1.424-0.778-3.612-1.002-3.669-0.59z"/>\r
+   <path fill="#7f7f7f" d="m199.648 47.127c-0.063 0.485 1.284 0.861 2.14 1.239 0.859 0.346 2.506 1.652 3.265 2.578 0.726 0.924 2.621 3.484 3.017 2.889 0.361-0.564-0.417-2.241-1.432-3.215-0.951-0.971-1.935-1.944-3.604-2.893-1.323-0.765-3.33-0.986-3.386-0.598z"/>\r
+  </g>\r
+  <g transform="translate(-12.4048,10.0005)">\r
+   <path fill="#995900" d="m303.631 262.529c4.464 6.479-0.145 14.904 3.096 20.089 5.328 8.496 16.056 17.063 20.16 19.439 2.952 1.8 7.128 3.527 6.983 8.783-0.216 5.977-3.168 7.561-4.823 9.217-3.313 3.313-20.017 11.592-31.104 19.152-13.968 9.576-18.792 13.824-23.328 18.359-7.056 7.057-13.752 9.432-24.48 9.432s-15.552-2.231-18.863-5.184c-3.313-2.88-6.984-10.225-6.624-21.168 0.288-10.872 3.744-20.809 5.399-37.729 0.721-7.271 0.648-16.271 0.648-24.264 0-10.08 0.144-18.648 2.304-19.943 3.889-2.448 4.752-2.592 9.36-2.592 4.607 0 6.696 0.287 8.208 1.799 1.439 1.44 0.864 4.752 0.359 9.433-0.432 4.681 1.801 6.192 4.032 8.136 2.232 1.872 4.248 4.248 11.305 4.824 7.056 0.504 9.647-0.648 12.96-2.736 3.312-2.088 7.991-5.832 9.72-7.992 1.656-2.088 5.76-9.287 6.552-9.287 0.719 0 5.472-1.656 8.136 2.232z"/>\r
+   <path fill="#9e5e00" d="m303.631 262.529c4.464 6.479-0.145 14.904 3.096 20.089 5.328 8.496 16.056 17.063 20.16 19.439 2.952 1.8 7.128 3.527 6.983 8.783-0.216 5.977-3.168 7.561-4.823 9.217-3.313 3.313-20.12 11.556-31.26 19.008-13.885 9.371-18.903 13.54-23.521 17.902-6.912 6.74-13.414 9.084-23.915 9.019-10.411-0.047-15.116-2.181-18.414-5.118-3.297-2.867-6.931-9.966-6.613-20.578 0.205-10.851 3.701-20.683 5.256-37.279 0.666-7.379 0.407-16.303 0.335-24.375-0.076-10.068-0.072-18.627 2.084-19.922 3.889-2.444 4.752-2.592 9.36-2.592 4.607 0 6.7 0.291 8.208 1.799 1.491 1.492 0.767 4.887 0.205 9.408-0.63 4.658 1.458 6.486 3.795 8.607 2.34 2.059 4.489 4.471 11.534 5.021 7.232 0.482 10.015-0.832 13.362-3.106 3.303-2.207 7.773-5.903 9.513-8.168 1.641-2.132 5.727-9.386 6.519-9.386 0.719 0 5.472-1.656 8.136 2.232z"/>\r
+   <path fill="#a36400" d="m303.631 262.529c4.464 6.479-0.145 14.904 3.096 20.089 5.328 8.496 16.056 17.063 20.16 19.439 2.952 1.8 7.128 3.527 6.983 8.783-0.216 5.977-3.168 7.561-4.823 9.217-3.313 3.313-20.226 11.52-31.414 18.863-13.803 9.166-19.016 13.256-23.717 17.447-6.768 6.422-13.075 8.733-23.35 8.604-10.094-0.094-14.682-2.131-17.964-5.055-3.283-2.852-6.876-9.705-6.603-19.987 0.122-10.828 3.657-20.556 5.112-36.828 0.612-7.487 0.165-16.336 0.021-24.487-0.15-10.058-0.287-18.605 1.865-19.9 3.889-2.44 4.752-2.592 9.36-2.592 4.607 0 6.703 0.295 8.208 1.799 1.541 1.541 0.67 5.02 0.051 9.383-0.828 4.637 1.116 6.781 3.556 9.078 2.448 2.248 4.731 4.695 11.766 5.221 7.409 0.461 10.383-1.016 13.767-3.477 3.29-2.326 7.552-5.977 9.302-8.346 1.627-2.175 5.695-9.482 6.487-9.482 0.72-0.001 5.473-1.657 8.137 2.231z"/>\r
+   <path fill="#a86a00" d="m303.631 262.529c4.464 6.479-0.145 14.904 3.096 20.089 5.328 8.496 16.056 17.063 20.16 19.439 2.952 1.8 7.128 3.527 6.983 8.783-0.216 5.977-3.168 7.561-4.823 9.217-3.313 3.313-20.329 11.484-31.568 18.72-13.721 8.961-19.127 12.972-23.911 16.989-6.624 6.105-12.737 8.384-22.785 8.189-9.777-0.141-14.245-2.08-17.514-4.99-3.27-2.836-6.822-9.445-6.591-19.396 0.038-10.807 3.613-20.43 4.968-36.378 0.558-7.597-0.076-16.368-0.292-24.599-0.228-10.047-0.504-18.584 1.645-19.879 3.889-2.438 4.752-2.592 9.36-2.592 4.607 0 6.707 0.299 8.208 1.799 1.591 1.593 0.573 5.152-0.104 9.357-1.025 4.615 0.774 7.077 3.319 9.551 2.556 2.434 4.972 4.918 11.995 5.418 7.585 0.439 10.75-1.199 14.17-3.849 3.279-2.444 7.333-6.048 9.093-8.521 1.613-2.219 5.663-9.58 6.455-9.58 0.719 0.001 5.472-1.655 8.136 2.233z"/>\r
+   <path fill="#ad7000" d="m303.631 262.529c4.464 6.479-0.145 14.904 3.096 20.089 5.328 8.496 16.056 17.063 20.16 19.439 2.952 1.8 7.128 3.527 6.983 8.783-0.216 5.977-3.168 7.561-4.823 9.217-3.313 3.313-20.434 11.447-31.724 18.576-13.637 8.756-19.238 12.687-24.106 16.531-6.479 5.789-12.397 8.035-22.219 7.776-9.461-0.188-13.809-2.03-17.063-4.925-3.254-2.823-6.769-9.188-6.581-18.807-0.043-10.785 3.571-20.305 4.824-35.928 0.504-7.705-0.316-16.402-0.604-24.711-0.303-10.037-0.72-18.563 1.425-19.857 3.889-2.434 4.752-2.592 9.36-2.592 4.607 0 6.711 0.303 8.208 1.799 1.642 1.643 0.475 5.285-0.259 9.332-1.225 4.594 0.432 7.373 3.082 10.022 2.664 2.621 5.212 5.142 12.225 5.616 7.762 0.418 11.117-1.383 14.573-4.219 3.269-2.563 7.113-6.121 8.885-8.698 1.598-2.261 5.63-9.677 6.422-9.677 0.719 0.002 5.472-1.654 8.136 2.234z"/>\r
+   <path fill="#b27500" d="m303.631 262.529c4.464 6.479-0.145 14.904 3.096 20.089 5.328 8.496 16.056 17.063 20.16 19.439 2.952 1.8 7.128 3.527 6.983 8.783-0.216 5.977-3.168 7.561-4.823 9.217-3.313 3.313-20.538 11.412-31.879 18.432-13.554 8.551-19.35 12.402-24.3 16.074-6.336 5.473-12.06 7.686-21.654 7.361-9.144-0.233-13.374-1.979-16.613-4.859-3.24-2.809-6.714-8.928-6.57-18.216-0.126-10.765 3.528-20.179 4.68-35.478 0.45-7.813-0.558-16.435-0.918-24.822-0.378-10.026-0.936-18.541 1.206-19.836 3.889-2.43 4.752-2.592 9.36-2.592 4.607 0 6.714 0.305 8.208 1.799 1.691 1.693 0.378 5.418-0.414 9.307-1.422 4.572 0.09 7.668 2.844 10.494 2.772 2.808 5.454 5.363 12.456 5.814 7.938 0.396 11.484-1.566 14.977-4.591 3.258-2.682 6.894-6.192 8.676-8.874 1.584-2.304 5.598-9.773 6.39-9.773 0.718 0 5.471-1.656 8.135 2.232z"/>\r
+   <path fill="#b77b00" d="m303.631 262.529c4.464 6.479-0.145 14.904 3.096 20.089 5.328 8.496 16.056 17.063 20.16 19.439 2.952 1.8 7.128 3.527 6.983 8.783-0.216 5.977-3.168 7.561-4.823 9.217-3.313 3.313-20.643 11.376-32.033 18.288-13.472 8.345-19.461 12.118-24.494 15.616-6.192 5.156-11.723 7.338-21.089 6.949-8.827-0.281-12.938-1.93-16.164-4.795-3.226-2.795-6.66-8.67-6.56-17.627-0.209-10.742 3.485-20.052 4.536-35.027 0.396-7.92-0.799-16.467-1.23-24.934-0.454-10.015-1.152-18.52 0.985-19.814 3.889-2.426 4.752-2.592 9.36-2.592 4.607 0 6.718 0.31 8.208 1.799 1.743 1.744 0.281 5.553-0.569 9.281-1.62 4.551-0.252 7.963 2.607 10.967 2.88 2.994 5.694 5.586 12.686 6.012 8.115 0.373 11.852-1.75 15.379-4.961 3.248-2.801 6.676-6.264 8.469-9.051 1.568-2.348 5.564-9.871 6.356-9.871 0.72 0 5.473-1.656 8.137 2.232z"/>\r
+   <path fill="#bc8100" d="m303.631 262.529c4.464 6.479-0.145 14.904 3.096 20.089 5.328 8.496 16.056 17.063 20.16 19.439 2.952 1.8 7.128 3.527 6.983 8.783-0.216 5.977-3.168 7.561-4.823 9.217-3.313 3.313-20.747 11.34-32.188 18.145-13.389 8.139-19.574 11.834-24.689 15.159-6.048 4.839-11.383 6.988-20.523 6.534-8.51-0.328-12.503-1.879-15.714-4.73-3.211-2.779-6.606-8.41-6.549-17.035-0.292-10.721 3.441-19.926 4.393-34.578 0.342-8.028-1.041-16.498-1.545-25.045-0.529-10.004-1.368-18.498 0.767-19.793 3.889-2.422 4.752-2.592 9.36-2.592 4.607 0 6.721 0.313 8.208 1.799 1.793 1.793 0.184 5.686-0.723 9.256-1.818 4.529-0.595 8.259 2.367 11.438 2.988 3.184 5.938 5.811 12.917 6.211 8.291 0.352 12.22-1.934 15.783-5.332 3.236-2.92 6.454-6.336 8.258-9.227 1.556-2.391 5.533-9.969 6.325-9.969 0.72-0.001 5.473-1.657 8.137 2.231z"/>\r
+   <path fill="#c18700" d="m303.631 262.529c4.464 6.479-0.145 14.904 3.096 20.089 5.328 8.496 16.056 17.063 20.16 19.439 2.952 1.8 7.128 3.527 6.983 8.783-0.216 5.977-3.168 7.561-4.823 9.217-3.313 3.313-20.852 11.304-32.343 18-13.306 7.936-19.685 11.549-24.883 14.703-5.904 4.521-11.045 6.638-19.959 6.119-8.193-0.375-12.067-1.828-15.264-4.666-3.197-2.764-6.553-8.149-6.537-16.444-0.375-10.699 3.397-19.8 4.248-34.128 0.288-8.137-1.282-16.531-1.858-25.156-0.604-9.994-1.584-18.477 0.547-19.771 3.889-2.42 4.752-2.592 9.36-2.592 4.607 0 6.725 0.316 8.208 1.799 1.843 1.845 0.087 5.818-0.878 9.231-2.017 4.507-0.937 8.554 2.131 11.909 3.096 3.369 6.178 6.033 13.146 6.408 8.468 0.33 12.587-2.117 16.187-5.703 3.225-3.038 6.235-6.408 8.049-9.402 1.541-2.436 5.501-10.066 6.293-10.066 0.72-0.001 5.473-1.657 8.137 2.231z"/>\r
+   <path fill="#c68c00" d="m303.631 262.529c4.464 6.479-0.145 14.904 3.096 20.089 5.328 8.496 16.056 17.063 20.16 19.439 2.952 1.8 7.128 3.527 6.983 8.783-0.216 5.977-3.168 7.561-4.823 9.217-3.313 3.313-20.955 11.268-32.498 17.855-13.223 7.73-19.796 11.266-25.077 14.246-5.761 4.204-10.706 6.289-19.394 5.707-7.877-0.423-11.631-1.779-14.813-4.602-3.183-2.751-6.498-7.891-6.527-15.855-0.457-10.677 3.355-19.674 4.104-33.678 0.234-8.244-1.521-16.563-2.17-25.268-0.681-9.983-1.8-18.455 0.327-19.75 3.889-2.416 4.752-2.592 9.36-2.592 4.607 0 6.729 0.32 8.208 1.799 1.894 1.895-0.011 5.951-1.033 9.207-2.214 4.484-1.278 8.848 1.895 12.379 3.203 3.558 6.418 6.258 13.377 6.607 8.644 0.309 12.952-2.301 16.589-6.074 3.215-3.156 6.016-6.479 7.841-9.58 1.526-2.477 5.468-10.162 6.26-10.162 0.718 0.001 5.471-1.655 8.135 2.233z"/>\r
+   <path fill="#cc9200" d="m303.631 262.529c4.464 6.479-0.145 14.904 3.096 20.089 5.328 8.496 16.056 17.063 20.16 19.439 2.952 1.8 7.128 3.527 6.983 8.783-0.216 5.977-3.168 7.561-4.823 9.217-3.313 3.313-21.061 11.232-32.652 17.712-13.141 7.524-19.908 10.979-25.272 13.788-5.616 3.888-10.368 5.939-18.828 5.292-7.56-0.468-11.195-1.728-14.363-4.536-3.168-2.736-6.444-7.632-6.517-15.264-0.54-10.656 3.313-19.549 3.96-33.229 0.181-8.352-1.764-16.596-2.483-25.38-0.757-9.972-2.017-18.433 0.107-19.728 3.889-2.412 4.752-2.592 9.36-2.592 4.607 0 6.731 0.323 8.208 1.799 1.944 1.945-0.108 6.084-1.188 9.181-2.412 4.464-1.62 9.144 1.656 12.853 3.313 3.744 6.66 6.479 13.608 6.803 8.819 0.289 13.319-2.483 16.991-6.443 3.204-3.275 5.797-6.553 7.633-9.756 1.512-2.52 5.436-10.26 6.228-10.26 0.719 0 5.472-1.656 8.136 2.232z"/>\r
+   <path fill="#d19800" d="m303.631 262.529c4.464 6.479-0.145 14.904 3.096 20.089 5.328 8.496 16.056 17.063 20.16 19.439 2.952 1.8 7.128 3.527 6.983 8.783-0.216 5.977-3.168 7.561-4.823 9.217-3.313 3.313-21.164 11.195-32.808 17.568-13.057 7.318-20.019 10.695-25.466 13.33-5.473 3.571-10.03 5.592-18.263 4.879-7.243-0.516-10.761-1.678-13.914-4.472-3.153-2.722-6.391-7.372-6.506-14.674-0.623-10.634 3.27-19.422 3.816-32.778 0.126-8.459-2.005-16.627-2.797-25.49-0.832-9.961-2.232-18.412-0.112-19.707 3.889-2.408 4.752-2.592 9.36-2.592 4.607 0 6.736 0.328 8.208 1.799 1.995 1.996-0.205 6.219-1.343 9.156-2.61 4.442-1.962 9.438 1.419 13.323 3.42 3.931 6.9 6.703 13.838 7.002 8.997 0.267 13.687-2.668 17.395-6.815 3.194-3.395 5.577-6.623 7.424-9.932 1.497-2.564 5.403-10.357 6.195-10.357 0.721 0 5.474-1.656 8.138 2.232z"/>\r
+   <path fill="#d69e00" d="m303.631 262.529c4.464 6.479-0.145 14.904 3.096 20.089 5.328 8.496 16.056 17.063 20.16 19.439 2.952 1.8 7.128 3.527 6.983 8.783-0.216 5.977-3.168 7.561-4.823 9.217-3.313 3.313-21.27 11.16-32.962 17.424-12.975 7.114-20.132 10.412-25.661 12.874-5.327 3.254-9.69 5.242-17.697 4.464-6.927-0.563-10.325-1.627-13.464-4.406-3.14-2.707-6.337-7.113-6.494-14.084-0.706-10.611 3.225-19.295 3.672-32.328 0.072-8.567-2.247-16.66-3.111-25.603-0.907-9.95-2.448-18.39-0.331-19.685 3.889-2.404 4.752-2.592 9.36-2.592 4.607 0 6.739 0.332 8.208 1.799 2.045 2.045-0.302 6.352-1.497 9.131-2.808 4.421-2.304 9.734 1.18 13.795 3.528 4.119 7.144 6.927 14.069 7.199 9.173 0.246 14.055-2.851 17.799-7.185 3.182-3.514 5.356-6.696 7.214-10.108 1.483-2.607 5.371-10.455 6.163-10.455 0.719 0 5.472-1.656 8.136 2.232z"/>\r
+   <path fill="#dba300" d="m303.631 262.529c4.464 6.479-0.145 14.904 3.096 20.089 5.328 8.496 16.056 17.063 20.16 19.439 2.952 1.8 7.128 3.527 6.983 8.783-0.216 5.977-3.168 7.561-4.823 9.217-3.313 3.313-21.373 11.124-33.116 17.279-12.893 6.91-20.243 10.127-25.855 12.418-5.184 2.937-9.353 4.892-17.133 4.05-6.609-0.609-9.889-1.577-13.014-4.343-3.125-2.692-6.282-6.854-6.483-13.492-0.789-10.592 3.182-19.17 3.528-31.879 0.018-8.676-2.488-16.692-3.425-25.713-0.982-9.94-2.664-18.369-0.551-19.664 3.889-2.401 4.752-2.592 9.36-2.592 4.607 0 6.743 0.334 8.208 1.799 2.095 2.097-0.399 6.484-1.652 9.105-3.006 4.398-2.646 10.029 0.943 14.268 3.636 4.305 7.384 7.148 14.299 7.397 9.349 0.224 14.422-3.034 18.202-7.558 3.171-3.631 5.137-6.768 7.005-10.284 1.469-2.649 5.339-10.552 6.131-10.552 0.72 0.001 5.473-1.655 8.137 2.233z"/>\r
+   <path fill="#e0a900" d="m303.631 262.529c4.464 6.479-0.145 14.904 3.096 20.089 5.328 8.496 16.056 17.063 20.16 19.439 2.952 1.8 7.128 3.527 6.983 8.783-0.216 5.977-3.168 7.561-4.823 9.217-3.313 3.313-21.478 11.088-33.271 17.136-12.81 6.704-20.354 9.843-26.05 11.96-5.04 2.62-9.015 4.543-16.567 3.637-6.293-0.656-9.453-1.527-12.563-4.277-3.11-2.68-6.229-6.596-6.474-12.903-0.871-10.569 3.141-19.044 3.384-31.428-0.035-8.784-2.728-16.726-3.735-25.826-1.06-9.929-2.88-18.347-0.771-19.642 3.889-2.397 4.752-2.592 9.36-2.592 4.607 0 6.747 0.338 8.208 1.799 2.146 2.146-0.497 6.617-1.808 9.08-3.203 4.377-2.987 10.324 0.706 14.738 3.744 4.493 7.624 7.373 14.529 7.596 9.526 0.203 14.789-3.217 18.605-7.926 3.161-3.752 4.918-6.841 6.797-10.463 1.454-2.693 5.306-10.648 6.098-10.648 0.719-0.001 5.472-1.657 8.136 2.231z"/>\r
+   <path fill="#e5af00" d="m303.631 262.529c4.464 6.479-0.145 14.904 3.096 20.089 5.328 8.496 16.056 17.063 20.16 19.439 2.952 1.8 7.128 3.527 6.983 8.783-0.216 5.977-3.168 7.561-4.823 9.217-3.313 3.313-21.582 11.052-33.427 16.992-12.726 6.498-20.466 9.558-26.244 11.502-4.896 2.304-8.676 4.193-16.002 3.222-5.976-0.702-9.018-1.476-12.114-4.212-3.096-2.664-6.174-6.336-6.462-12.313-0.953-10.547 3.097-18.918 3.24-30.978-0.09-8.892-2.97-16.758-4.05-25.938-1.134-9.918-3.096-18.324-0.99-19.619 3.889-2.395 4.752-2.592 9.36-2.592 4.607 0 6.75 0.342 8.208 1.799 2.196 2.197-0.594 6.75-1.962 9.055-3.402 4.355-3.33 10.619 0.468 15.21 3.852 4.681 7.866 7.597 14.76 7.794 9.702 0.18 15.156-3.402 19.008-8.298 3.15-3.87 4.698-6.912 6.589-10.638 1.439-2.736 5.273-10.746 6.065-10.746 0.72 0 5.473-1.656 8.137 2.232z"/>\r
+   <path fill="#eab500" d="m303.631 262.529c4.464 6.479-0.145 14.904 3.096 20.089 5.328 8.496 16.056 17.063 20.16 19.439 2.952 1.8 7.128 3.527 6.983 8.783-0.216 5.977-3.168 7.561-4.823 9.217-3.313 3.313-21.687 11.016-33.582 16.848-12.643 6.293-20.576 9.274-26.438 11.045-4.752 1.987-8.338 3.846-15.438 2.809-5.658-0.75-8.582-1.426-11.664-4.147-3.08-2.649-6.119-6.077-6.45-11.722-1.037-10.526 3.053-18.792 3.096-30.528-0.144-9-3.211-16.79-4.363-26.049-1.21-9.907-3.312-18.304-1.21-19.599 3.889-2.391 4.752-2.592 9.36-2.592 4.607 0 6.754 0.346 8.208 1.799 2.247 2.248-0.691 6.885-2.117 9.029-3.6 4.336-3.672 10.916 0.231 15.682 3.96 4.867 8.106 7.82 14.989 7.992 9.879 0.158 15.523-3.586 19.411-8.668 3.141-3.99 4.479-6.984 6.38-10.814 1.426-2.78 5.241-10.844 6.033-10.844 0.721-0.001 5.474-1.657 8.138 2.231z"/>\r
+   <path fill="#efba00" d="m303.631 262.529c4.464 6.479-0.145 14.904 3.096 20.089 5.328 8.496 16.056 17.063 20.16 19.439 2.952 1.8 7.128 3.527 6.983 8.783-0.216 5.977-3.168 7.561-4.823 9.217-3.313 3.313-21.791 10.98-33.735 16.703-12.562 6.089-20.69 8.99-26.633 10.589-4.608 1.67-7.999 3.496-14.872 2.394-5.343-0.796-8.147-1.375-11.215-4.082-3.066-2.636-6.065-5.818-6.439-11.132-1.12-10.504 3.009-18.666 2.952-30.077-0.198-9.109-3.453-16.822-4.677-26.162-1.285-9.896-3.528-18.281-1.43-19.576 3.889-2.387 4.752-2.592 9.36-2.592 4.607 0 6.757 0.35 8.208 1.799 2.297 2.297-0.788 7.018-2.271 9.004-3.798 4.314-4.014 11.211-0.008 16.154 4.068 5.055 8.35 8.043 15.221 8.189 10.056 0.137 15.892-3.77 19.815-9.039 3.128-4.107 4.258-7.057 6.17-10.99 1.411-2.824 5.209-10.941 6.001-10.941 0.72-0.001 5.473-1.657 8.137 2.231z"/>\r
+   <path fill="#f4c000" d="m303.631 262.529c4.464 6.479-0.145 14.904 3.096 20.089 5.328 8.496 16.056 17.063 20.16 19.439 2.952 1.8 7.128 3.527 6.983 8.783-0.216 5.977-3.168 7.561-4.823 9.217-3.313 3.313-21.896 10.943-33.891 16.561-12.478 5.883-20.801 8.704-26.827 10.131-4.464 1.353-7.661 3.146-14.307 1.979-5.025-0.843-7.711-1.325-10.765-4.019-3.053-2.621-6.012-5.558-6.429-10.541-1.203-10.482 2.966-18.539 2.809-29.627-0.253-9.217-3.694-16.855-4.99-26.272-1.361-9.886-3.744-18.261-1.649-19.556 3.889-2.383 4.752-2.592 9.36-2.592 4.607 0 6.761 0.353 8.208 1.799 2.347 2.349-0.885 7.15-2.426 8.979-3.996 4.291-4.356 11.505-0.245 16.625 4.176 5.241 8.59 8.265 15.451 8.388 10.23 0.115 16.258-3.953 20.218-9.41 3.117-4.227 4.039-7.129 5.961-11.168 1.396-2.865 5.177-11.037 5.969-11.037 0.72 0 5.473-1.656 8.137 2.232z"/>\r
+   <path fill="#f9c600" d="m303.631 262.529c4.464 6.479-0.145 14.904 3.096 20.089 5.328 8.496 16.056 17.063 20.16 19.439 2.952 1.8 7.128 3.527 6.983 8.783-0.216 5.977-3.168 7.561-4.823 9.217-3.313 3.313-21.999 10.908-34.046 16.416-12.395 5.678-20.912 8.421-27.021 9.674-4.32 1.036-7.322 2.797-13.741 1.566-4.709-0.891-7.275-1.275-10.314-3.953-3.037-2.607-5.958-5.299-6.419-9.951-1.284-10.461 2.925-18.414 2.664-29.178-0.306-9.324-3.934-16.887-5.302-26.385-1.437-9.875-3.96-18.238-1.869-19.533 3.889-2.379 4.752-2.592 9.36-2.592 4.607 0 6.765 0.356 8.208 1.799 2.397 2.398-0.983 7.283-2.581 8.955-4.194 4.269-4.698 11.799-0.482 17.096 4.284 5.429 8.83 8.488 15.682 8.586 10.407 0.094 16.625-4.137 20.621-9.781 3.106-4.345 3.819-7.199 5.753-11.344 1.382-2.909 5.144-11.135 5.936-11.135 0.718 0 5.471-1.656 8.135 2.232z"/>\r
+   <path fill="#fc0" d="m303.631 262.529c4.464 6.479-0.145 14.904 3.096 20.089 5.328 8.496 16.056 17.063 20.16 19.439 2.952 1.8 7.128 3.527 6.983 8.783-0.216 5.977-3.168 7.561-4.823 9.217-3.313 3.313-22.104 10.872-34.2 16.271-12.313 5.473-21.024 8.137-27.217 9.217-4.176 0.72-6.983 2.447-13.176 1.151-4.392-0.937-6.84-1.224-9.864-3.888-3.023-2.592-5.903-5.04-6.407-9.359-1.368-10.441 2.88-18.289 2.52-28.729-0.36-9.432-4.176-16.92-5.616-26.496-1.512-9.864-4.176-18.217-2.088-19.512 3.889-2.377 4.752-2.592 9.36-2.592 4.607 0 6.768 0.359 8.208 1.799 2.448 2.449-1.08 7.416-2.736 8.929-4.392 4.248-5.04 12.096-0.72 17.567 4.392 5.617 9.072 8.713 15.912 8.785 10.584 0.071 16.992-4.32 21.023-10.152 3.097-4.465 3.601-7.272 5.544-11.521 1.368-2.952 5.112-11.231 5.904-11.231 0.72 0.001 5.473-1.655 8.137 2.233z"/>\r
+  </g>\r
+  <g transform="translate(-12.4048,10.0005)">\r
+   <path fill="#fc0" d="m236.263 275.762c-0.709-0.258-3.932-15.209-2.191-16.239 3.351-1.997 4.253-2.319 8.377-2.319s6.057 0.322 7.346 1.61c2.126 2.127-1.159 6.702-2.448 7.991-3.738 3.673-10.375 9.215-11.084 8.957z"/>\r
+   <path fill="#ffcc02" d="m236.492 275.286c-0.77-0.326-4.102-14.719-2.368-15.742 3.344-1.992 4.278-2.211 8.322-2.211 4.124 0 5.976 0.269 7.282 1.602 2.105 2.136-1.119 6.572-2.398 7.852-3.727 3.663-10.081 8.811-10.838 8.499z"/>\r
+   <path fill="#ffcc05" d="m236.721 274.809c-0.832-0.393-4.273-14.229-2.547-15.247 3.339-1.983 4.306-2.101 8.269-2.101 4.124 0 5.896 0.213 7.217 1.592 2.087 2.146-1.076 6.443-2.346 7.714-3.718 3.653-9.788 8.409-10.593 8.042z"/>\r
+   <path fill="#ffcc07" d="m236.949 274.333c-0.892-0.461-4.443-13.74-2.722-14.75 3.331-1.979 4.33-1.992 8.213-1.992 4.124 0 5.814 0.158 7.151 1.582 2.068 2.156-1.033 6.316-2.294 7.574-3.707 3.644-9.494 8.007-10.348 7.586z"/>\r
+   <path fill="#ffcd0a" d="m237.178 273.855c-0.954-0.528-4.614-13.249-2.9-14.254 3.326-1.972 4.355-1.882 8.158-1.882 4.124 0 5.734 0.104 7.089 1.572 2.048 2.166-0.993 6.187-2.243 7.437-3.699 3.634-9.202 7.605-10.104 7.127z"/>\r
+   <path fill="#ffcd0c" d="m237.407 273.377c-1.015-0.596-4.785-12.758-3.077-13.758 3.319-1.965 4.382-1.771 8.104-1.771 4.124 0 5.653 0.049 7.023 1.563 2.029 2.176-0.951 6.057-2.19 7.299-3.69 3.623-8.91 7.201-9.86 6.667z"/>\r
+   <path fill="#ffcd0f" d="m237.636 272.901c-1.077-0.662-4.956-12.27-3.256-13.261 3.313-1.959 4.408-1.663 8.05-1.663 4.124 0 5.573-0.006 6.959 1.553 2.01 2.186-0.908 5.93-2.14 7.159-3.679 3.614-8.615 6.8-9.613 6.212z"/>\r
+   <path fill="#ffcd11" d="m237.864 272.424c-1.137-0.731-5.126-11.779-3.431-12.766 3.306-1.951 4.433-1.553 7.994-1.553 4.123 0 5.492-0.061 6.895 1.543 1.991 2.193-0.867 5.801-2.089 7.021-3.669 3.606-8.321 6.397-9.369 5.755z"/>\r
+   <path fill="#ffce14" d="m238.093 271.948c-1.197-0.799-5.297-11.289-3.607-12.27 3.299-1.946 4.459-1.443 7.938-1.443 4.124 0 5.412-0.115 6.83 1.533 1.973 2.204-0.824 5.671-2.037 6.883-3.659 3.596-8.028 5.993-9.124 5.297z"/>\r
+   <path fill="#ffce16" d="m238.322 271.471c-1.26-0.867-5.468-10.801-3.786-11.773 3.293-1.939 4.485-1.334 7.884-1.334 4.124 0 5.332-0.171 6.767 1.524 1.953 2.213-0.783 5.542-1.985 6.743-3.651 3.586-7.736 5.591-8.88 4.84z"/>\r
+   <path fill="#ffce19" d="m238.551 270.995c-1.32-0.935-5.639-10.312-3.963-11.277 3.286-1.934 4.511-1.225 7.829-1.225 4.124 0 5.252-0.226 6.702 1.514 1.934 2.224-0.741 5.414-1.934 6.605-3.64 3.576-7.442 5.187-8.634 4.383z"/>\r
+   <path fill="#ffce1c" d="m238.779 270.517c-1.382-1.002-5.809-9.821-4.14-10.781 3.279-1.926 4.535-1.114 7.774-1.114 4.124 0 5.171-0.279 6.637 1.504 1.914 2.233-0.698 5.285-1.882 6.467-3.63 3.566-7.148 4.784-8.389 3.924z"/>\r
+   <path fill="#ffcf1e" d="m239.008 270.04c-1.442-1.068-5.979-9.33-4.316-10.283 3.272-1.922 4.562-1.006 7.72-1.006 4.124 0 5.09-0.334 6.572 1.494 1.895 2.244-0.657 5.156-1.83 6.328-3.622 3.556-6.857 4.383-8.146 3.467z"/>\r
+   <path fill="#ffcf21" d="m239.237 269.563c-1.505-1.137-6.151-8.841-4.495-9.788 3.267-1.914 4.588-0.896 7.666-0.896 4.124 0 5.009-0.389 6.508 1.486 1.875 2.252-0.616 5.025-1.778 6.188-3.613 3.548-6.564 3.981-7.901 3.01z"/>\r
+   <path fill="#ffcf23" d="m239.466 269.086c-1.565-1.205-6.321-8.352-4.672-9.293 3.262-1.906 4.613-0.785 7.61-0.785 4.124 0 4.93-0.444 6.444 1.476 1.855 2.261-0.573 4.897-1.728 6.052-3.601 3.537-6.269 3.575-7.654 2.55z"/>\r
+   <path fill="#ffcf26" d="m239.694 268.61c-1.627-1.273-6.492-7.861-4.849-8.796 3.255-1.901 4.64-0.677 7.556-0.677 4.124 0 4.849-0.499 6.379 1.466 1.837 2.271-0.531 4.769-1.675 5.912-3.593 3.528-5.977 3.174-7.411 2.095z"/>\r
+   <path fill="#ffd028" d="m239.923 268.133c-1.688-1.34-6.663-7.373-5.025-8.301 3.248-1.895 4.665-0.566 7.501-0.566 4.124 0 4.768-0.555 6.314 1.456 1.817 2.28-0.489 4.64-1.624 5.774-3.583 3.519-5.684 2.771-7.166 1.637z"/>\r
+   <path fill="#ffd02b" d="m240.152 267.657c-1.749-1.408-6.834-6.883-5.203-7.805 3.241-1.889 4.69-0.457 7.446-0.457 4.124 0 4.687-0.609 6.25 1.447 1.798 2.289-0.448 4.51-1.573 5.635-3.572 3.509-5.39 2.367-6.92 1.18z"/>\r
+   <path fill="#ffd02d" d="m240.381 267.178c-1.811-1.475-7.005-6.391-5.381-7.307 3.235-1.881 4.717-0.348 7.393-0.348 4.124 0 4.606-0.664 6.185 1.438 1.779 2.299-0.405 4.381-1.521 5.496-3.564 3.501-5.098 1.965-6.676 0.721z"/>\r
+   <path fill="#ffd030" d="m240.609 266.702c-1.871-1.543-7.175-5.902-5.557-6.811 3.228-1.875 4.741-0.238 7.336-0.238 4.124 0 4.526-0.719 6.122 1.428 1.759 2.31-0.364 4.252-1.471 5.357-3.552 3.49-4.803 1.562-6.43 0.264z"/>\r
+   <path fill="#ffd133" d="m240.838 266.225c-1.933-1.611-7.346-5.413-5.734-6.314 3.222-1.869 4.768-0.129 7.281-0.129 4.124 0 4.446-0.773 6.058 1.418 1.74 2.318-0.322 4.123-1.418 5.219-3.545 3.48-4.512 1.16-6.187-0.194z"/>\r
+  </g>\r
+  <g transform="translate(-12.4048,10.0005)">\r
+   <path fill="#fc0" d="m302.769 263.374c3.742 5.461-0.062 12.76 2.638 17.117-6.809-6.258-9.938-8.834-19.324 0.367 2.577-3.742 3.129-6.257 4.725-9.814 1.104-2.455 4.354-9.57 5.029-9.57 0.613-0.001 4.724-1.35 6.932 1.9z"/>\r
+   <path fill="#ffcc02" d="m302.73 263.413c3.655 5.334 0.035 12.601 2.578 16.723-6.668-6.098-9.729-8.666-18.908 0.322 2.421-3.527 3.025-6.094 4.605-9.592 1.122-2.462 4.243-9.302 4.951-9.311 0.628-0.008 4.617-1.318 6.774 1.858z"/>\r
+   <path fill="#ffcc05" d="m302.691 263.45c3.568 5.209 0.132 12.441 2.517 16.332-6.526-5.938-9.519-8.5-18.492 0.277 2.268-3.314 2.924-5.934 4.488-9.372 1.141-2.468 4.132-9.032 4.873-9.052 0.641-0.013 4.508-1.284 6.614 1.815z"/>\r
+   <path fill="#ffcc07" d="m302.652 263.487c3.48 5.086 0.229 12.282 2.457 15.939-6.386-5.777-9.311-8.332-18.076 0.232 2.111-3.1 2.819-5.771 4.369-9.15 1.158-2.475 4.021-8.762 4.795-8.791 0.655-0.019 4.399-1.254 6.455 1.77z"/>\r
+   <path fill="#ffcd0a" d="m302.614 263.524c3.393 4.96 0.323 12.123 2.396 15.549-6.245-5.617-9.102-8.164-17.66 0.188 1.955-2.887 2.716-5.611 4.251-8.93 1.176-2.481 3.91-8.494 4.716-8.533 0.67-0.027 4.291-1.219 6.297 1.726z"/>\r
+   <path fill="#ffcd0c" d="m302.575 263.562c3.306 4.835 0.419 11.964 2.335 15.155-6.104-5.457-8.891-7.996-17.244 0.143 1.8-2.673 2.613-5.449 4.133-8.707 1.194-2.488 3.8-8.225 4.638-8.273 0.684-0.036 4.182-1.189 6.138 1.682z"/>\r
+   <path fill="#ffcd0f" d="m302.536 263.599c3.219 4.71 0.517 11.805 2.275 14.765-5.963-5.299-8.683-7.83-16.828 0.098 1.644-2.461 2.51-5.289 4.015-8.486 1.212-2.496 3.688-7.956 4.559-8.016 0.698-0.04 4.075-1.155 5.979 1.639z"/>\r
+   <path fill="#ffcd11" d="m302.497 263.637c3.131 4.585 0.612 11.645 2.216 14.371-5.822-5.137-8.474-7.661-16.413 0.053 1.489-2.245 2.406-5.125 3.896-8.264 1.229-2.504 3.576-7.688 4.479-7.756 0.714-0.046 3.968-1.123 5.822 1.596z"/>\r
+   <path fill="#ffce14" d="m302.458 263.674c3.044 4.459 0.708 11.486 2.154 13.979-5.681-4.978-8.263-7.493-15.996 0.009 1.334-2.033 2.303-4.965 3.779-8.043 1.247-2.511 3.464-7.418 4.4-7.498 0.728-0.052 3.859-1.089 5.663 1.553z"/>\r
+   <path fill="#ffce16" d="m302.42 263.711c2.956 4.336 0.804 11.328 2.094 13.588-5.54-4.817-8.055-7.326-15.58-0.036 1.178-1.819 2.199-4.804 3.659-7.822 1.267-2.517 3.354-7.149 4.323-7.237 0.741-0.061 3.75-1.058 5.504 1.507z"/>\r
+   <path fill="#ffce19" d="m302.381 263.749c2.868 4.211 0.9 11.168 2.033 13.196-5.398-4.657-7.845-7.159-15.164-0.081 1.022-1.605 2.097-4.642 3.542-7.601 1.283-2.524 3.241-6.88 4.244-6.979 0.755-0.067 3.642-1.026 5.345 1.465z"/>\r
+   <path fill="#ffce1c" d="m302.342 263.788c2.78 4.084 0.997 11.008 1.973 12.803-5.258-4.498-7.635-6.991-14.748-0.127 0.867-1.391 1.994-4.479 3.424-7.379 1.302-2.531 3.13-6.61 4.166-6.719 0.768-0.074 3.532-0.992 5.185 1.422z"/>\r
+   <path fill="#ffcf1e" d="m302.302 263.825c2.693 3.959 1.093 10.85 1.913 12.411-5.117-4.338-7.427-6.825-14.333-0.172 0.713-1.177 1.891-4.317 3.307-7.157 1.318-2.537 3.018-6.342 4.086-6.461 0.784-0.08 3.426-0.959 5.027 1.379z"/>\r
+   <path fill="#ffcf21" d="m302.263 263.862c2.606 3.834 1.188 10.689 1.853 12.02-4.976-4.178-7.217-6.657-13.916-0.217 0.556-0.963 1.786-4.156 3.188-6.936 1.337-2.545 2.906-6.072 4.008-6.202 0.797-0.086 3.318-0.927 4.867 1.335z"/>\r
+   <path fill="#ffcf23" d="m302.225 263.899c2.519 3.71 1.285 10.531 1.791 11.628-4.835-4.019-7.008-6.489-13.5-0.262 0.4-0.75 1.684-3.994 3.068-6.714 1.356-2.553 2.797-5.805 3.931-5.943 0.813-0.093 3.209-0.895 4.71 1.291z"/>\r
+   <path fill="#ffcf26" d="m302.186 263.937c2.431 3.584 1.381 10.371 1.73 11.235-4.693-3.857-6.798-6.322-13.084-0.307 0.245-0.535 1.58-3.832 2.951-6.492 1.373-2.56 2.686-5.535 3.852-5.685 0.828-0.1 3.101-0.861 4.551 1.249z"/>\r
+   <path fill="#ffd028" d="m302.147 263.974c2.344 3.46 1.477 10.213 1.671 10.845-4.553-3.699-6.589-6.156-12.668-0.354 0.089-0.321 1.477-3.67 2.832-6.271 1.392-2.565 2.574-5.267 3.772-5.425 0.842-0.104 2.994-0.828 4.393 1.205z"/>\r
+   <path fill="#ffd02b" d="m302.108 264.012c2.257 3.334 1.573 10.053 1.61 10.451-4.412-3.537-6.38-5.987-12.253-0.396-0.064-0.109 1.374-3.51 2.716-6.05 1.408-2.573 2.462-4.997 3.693-5.166 0.856-0.112 2.886-0.796 4.234 1.161z"/>\r
+   <path fill="#ffd02d" d="m302.069 264.049c2.17 3.209 1.67 9.894 1.55 10.061-4.271-3.379-6.17-5.82-11.836-0.441-0.221 0.104 1.271-3.35 2.596-5.83 1.428-2.58 2.352-4.728 3.615-4.906 0.87-0.12 2.777-0.765 4.075 1.116z"/>\r
+   <path fill="#ffd030" d="m302.03 264.086c2.082 3.084 1.767 9.736 1.49 9.668-4.131-3.219-5.961-5.652-11.42-0.486-0.377 0.318 1.167-3.188 2.478-5.607 1.445-2.586 2.239-4.459 3.536-4.647 0.884-0.127 2.669-0.732 3.916 1.072z"/>\r
+   <path fill="#ffd133" d="m301.991 264.124c1.995 2.959 1.862 9.576 1.43 9.277-3.989-3.059-5.752-5.486-11.005-0.531-0.532 0.531 1.064-3.027 2.36-5.387 1.463-2.594 2.128-4.189 3.458-4.389 0.899-0.133 2.561-0.699 3.757 1.03z"/>\r
+  </g>\r
+  <g transform="translate(-12.4048,10.0005)">\r
+   <path fill="#fc0" d="m305.862 283.481c5.977 7.848 17.064 16.271 21.024 18.576 2.88 1.656 7.056 3.6 6.983 8.783-0.144 5.904-3.168 7.561-4.823 9.217-3.313 3.313-22.177 10.943-34.2 16.271-12.24 5.4-21.097 8.209-27.217 9.217-4.104 0.647-7.056 2.375-13.176 1.151-4.32-0.864-6.912-1.296-9.864-3.888-2.951-2.52-5.976-5.184-6.407-9.359-1.225-10.369 3.672-16.921 8.424-25.921 3.888-7.2 11.735-8.64 16.632-7.991 17.568 2.375 16.416-8.641 21.24-13.465 4.464-4.463 17.208-8.064 21.384-2.591z"/>\r
+   <path fill="#ffcc02" d="m305.81 283.553c5.962 7.83 17.024 16.234 20.975 18.533 2.873 1.652 7.039 3.592 6.969 8.764-0.145 5.891-3.161 7.542-4.813 9.195-3.304 3.304-22.34 10.992-34.24 16.088-12.259 5.176-20.647 7.873-26.802 8.959-4.077 0.684-7.156 2.394-13.258 1.177-4.304-0.854-6.767-1.231-9.707-3.812-2.939-2.51-5.756-4.961-6.185-9.117-1.211-10.34 3.365-16.657 8.044-25.65 3.89-7.375 11.791-8.434 16.665-7.777 17.514 2.414 16.206-8.959 21.02-13.772 4.452-4.454 17.166-8.047 21.332-2.588z"/>\r
+   <path fill="#ffcc05" d="m305.76 283.627c5.946 7.812 16.982 16.195 20.925 18.488 2.866 1.648 7.022 3.584 6.951 8.743-0.144 5.876-3.153 7.524-4.801 9.173-3.298 3.297-22.506 11.043-34.28 15.907-12.279 4.949-20.201 7.538-26.389 8.699-4.051 0.721-7.256 2.413-13.341 1.202-4.286-0.846-6.619-1.168-9.55-3.733-2.924-2.501-5.536-4.741-5.959-8.877-1.198-10.312 3.058-16.394 7.664-25.379 3.89-7.552 11.845-8.229 16.698-7.563 17.458 2.453 15.995-9.279 20.797-14.08 4.443-4.443 17.127-8.026 21.285-2.58z"/>\r
+   <path fill="#ffcc07" d="m305.707 283.702c5.935 7.791 16.943 16.156 20.876 18.444 2.859 1.644 7.007 3.574 6.935 8.722-0.144 5.862-3.146 7.508-4.79 9.151-3.288 3.289-22.668 11.093-34.319 15.726-12.298 4.723-19.753 7.202-25.974 8.44-4.024 0.756-7.357 2.431-13.423 1.226-4.27-0.836-6.473-1.101-9.394-3.654-2.911-2.492-5.317-4.52-5.735-8.635-1.185-10.285 2.75-16.133 7.284-25.109 3.892-7.727 11.9-8.023 16.731-7.35 17.402 2.494 15.785-9.599 20.575-14.389 4.433-4.432 17.087-8.007 21.234-2.572z"/>\r
+   <path fill="#ffcd0a" d="m305.655 283.774c5.92 7.773 16.904 16.119 20.826 18.4 2.854 1.642 6.99 3.566 6.919 8.703-0.143 5.848-3.138 7.488-4.779 9.129-3.28 3.281-22.832 11.143-34.358 15.543-12.317 4.498-19.307 6.867-25.561 8.182-3.997 0.793-7.457 2.45-13.506 1.251-4.252-0.828-6.325-1.036-9.236-3.577-2.896-2.482-5.096-4.298-5.51-8.393-1.172-10.258 2.443-15.869 6.904-24.84 3.892-7.901 11.955-7.818 16.763-7.135 17.349 2.532 15.576-9.918 20.355-14.697 4.422-4.422 17.046-7.987 21.183-2.566z"/>\r
+   <path fill="#ffcd0c" d="m305.603 283.847c5.906 7.756 16.864 16.081 20.777 18.358 2.846 1.635 6.973 3.557 6.901 8.68-0.142 5.835-3.131 7.472-4.767 9.107-3.273 3.273-22.997 11.193-34.399 15.361-12.337 4.273-18.858 6.533-25.146 7.924-3.971 0.829-7.557 2.469-13.588 1.276-4.234-0.82-6.179-0.972-9.078-3.5-2.884-2.474-4.878-4.076-5.287-8.152-1.158-10.229 2.137-15.606 6.523-24.569 3.895-8.076 12.01-7.611 16.797-6.92 17.293 2.571 15.366-10.236 20.134-15.004 4.412-4.411 17.006-7.969 21.133-2.561z"/>\r
+   <path fill="#ffcd0f" d="m305.552 283.92c5.892 7.736 16.824 16.043 20.728 18.313 2.839 1.634 6.957 3.55 6.886 8.66-0.142 5.821-3.124 7.454-4.756 9.086-3.266 3.267-23.161 11.243-34.438 15.179-12.356 4.047-18.411 6.198-24.733 7.666-3.943 0.865-7.656 2.486-13.67 1.301-4.218-0.812-6.032-0.908-8.922-3.422-2.869-2.465-4.656-3.855-5.063-7.91-1.145-10.203 1.83-15.344 6.145-24.299 3.895-8.252 12.064-7.408 16.83-6.707 17.237 2.61 15.154-10.557 19.912-15.313 4.399-4.399 16.963-7.949 21.081-2.554z"/>\r
+   <path fill="#ffcd11" d="m305.501 283.993c5.877 7.719 16.782 16.004 20.678 18.271 2.833 1.628 6.939 3.54 6.869 8.639-0.143 5.807-3.116 7.436-4.745 9.064-3.257 3.258-23.324 11.293-34.479 14.996-12.375 3.822-17.963 5.863-24.319 7.408-3.917 0.9-7.757 2.504-13.752 1.324-4.2-0.802-5.886-0.842-8.765-3.344-2.856-2.454-4.438-3.633-4.838-7.669-1.132-10.173 1.521-15.081 5.764-24.029 3.896-8.426 12.119-7.2 16.863-6.491 17.183 2.649 14.945-10.875 19.689-15.621 4.391-4.389 16.926-7.93 21.035-2.548z"/>\r
+   <path fill="#ffce14" d="m305.448 284.066c5.863 7.701 16.743 15.966 20.629 18.228 2.826 1.625 6.924 3.531 6.853 8.619-0.142 5.793-3.108 7.418-4.733 9.043-3.25 3.25-23.489 11.342-34.518 14.813-12.396 3.598-17.517 5.529-23.905 7.148-3.891 0.938-7.857 2.524-13.834 1.351-4.185-0.793-5.74-0.776-8.609-3.267-2.841-2.444-4.217-3.412-4.613-7.426-1.118-10.146 1.216-14.818 5.385-23.76 3.896-8.602 12.174-6.996 16.896-6.277 17.128 2.688 14.735-11.195 19.468-15.929 4.378-4.38 16.883-7.911 20.981-2.543z"/>\r
+   <path fill="#ffce16" d="m305.396 284.139c5.85 7.682 16.703 15.928 20.579 18.184 2.82 1.62 6.907 3.523 6.837 8.598-0.141 5.779-3.101 7.4-4.722 9.021-3.242 3.242-23.653 11.393-34.559 14.631-12.414 3.372-17.068 5.194-23.491 6.891-3.862 0.975-7.957 2.543-13.917 1.375-4.167-0.783-5.592-0.713-8.451-3.188-2.827-2.437-3.997-3.19-4.389-7.187-1.105-10.117 0.908-14.555 5.004-23.487 3.898-8.776 12.229-6.79 16.928-6.063 17.074 2.728 14.525-11.515 19.248-16.236 4.37-4.371 16.845-7.894 20.933-2.539z"/>\r
+   <path fill="#ffce19" d="m305.344 284.211c5.836 7.664 16.663 15.891 20.529 18.141 2.813 1.617 6.892 3.516 6.82 8.578-0.14 5.765-3.093 7.382-4.71 8.999-3.235 3.233-23.817 11.442-34.598 14.448-12.434 3.146-16.621 4.859-23.077 6.633-3.837 1.01-8.058 2.561-13.999 1.4-4.15-0.775-5.446-0.648-8.295-3.111-2.814-2.426-3.777-2.969-4.164-6.944-1.094-10.09 0.601-14.293 4.624-23.22 3.898-8.951 12.282-6.584 16.961-5.848 17.019 2.767 14.314-11.834 19.025-16.545 4.361-4.359 16.806-7.872 20.884-2.531z"/>\r
+   <path fill="#ffce1c" d="m305.292 284.286c5.822 7.646 16.623 15.852 20.481 18.096 2.806 1.613 6.874 3.507 6.804 8.558-0.141 5.751-3.086 7.364-4.699 8.978-3.227 3.227-23.981 11.492-34.638 14.267-12.453 2.921-16.173 4.524-22.663 6.374-3.81 1.046-8.158 2.578-14.082 1.424-4.133-0.766-5.299-0.583-8.137-3.033-2.801-2.416-3.558-2.748-3.94-6.703-1.08-10.062 0.293-14.029 4.244-22.947 3.9-9.127 12.338-6.379 16.994-5.635 16.964 2.805 14.105-12.152 18.805-16.853 4.348-4.351 16.763-7.856 20.831-2.526z"/>\r
+   <path fill="#ffcf1e" d="m305.241 284.358c5.808 7.627 16.582 15.814 20.432 18.053 2.799 1.609 6.856 3.498 6.787 8.536-0.141 5.738-3.079 7.347-4.688 8.957-3.219 3.218-24.145 11.541-34.677 14.084-12.473 2.695-15.727 4.188-22.25 6.115-3.783 1.083-8.258 2.599-14.163 1.448-4.116-0.756-5.153-0.518-7.981-2.954-2.786-2.408-3.337-2.526-3.716-6.462-1.066-10.034-0.013-13.766 3.864-22.678 3.901-9.303 12.393-6.172 17.027-5.42 16.908 2.844 13.896-12.473 18.583-17.16 4.338-4.337 16.723-7.835 20.782-2.519z"/>\r
+   <path fill="#ffcf21" d="m305.189 284.431c5.793 7.608 16.542 15.776 20.382 18.009 2.792 1.605 6.84 3.49 6.771 8.516-0.14 5.725-3.071 7.33-4.677 8.936-3.211 3.211-24.309 11.591-34.717 13.902-12.491 2.47-15.278 3.854-21.836 5.856-3.756 1.119-8.357 2.616-14.246 1.474-4.099-0.748-5.006-0.453-7.823-2.877-2.772-2.398-3.118-2.306-3.492-6.22-1.053-10.007-0.319-13.505 3.484-22.407 3.903-9.479 12.448-5.969 17.062-5.207 16.853 2.883 13.684-12.791 18.36-17.469 4.328-4.328 16.683-7.819 20.732-2.513z"/>\r
+   <path fill="#ffcf23" d="m305.137 284.504c5.778 7.59 16.503 15.736 20.332 17.965 2.786 1.602 6.825 3.482 6.755 8.496-0.139 5.709-3.064 7.311-4.665 8.912-3.204 3.203-24.474 11.642-34.759 13.721-12.51 2.244-14.829 3.52-21.421 5.598-3.729 1.155-8.457 2.635-14.327 1.499-4.082-0.739-4.86-0.389-7.667-2.8-2.76-2.389-2.897-2.083-3.268-5.979-1.04-9.979-0.627-13.24 3.104-22.138 3.903-9.653 12.503-5.762 17.093-4.991 16.799 2.922 13.475-13.111 18.141-17.777 4.318-4.316 16.643-7.799 20.682-2.506z"/>\r
+   <path fill="#ffcf26" d="m305.086 284.579c5.765 7.57 16.463 15.697 20.282 17.92 2.779 1.599 6.809 3.474 6.738 8.476-0.139 5.696-3.056 7.293-4.654 8.892-3.194 3.194-24.637 11.69-34.797 13.536-12.529 2.021-14.382 3.185-21.007 5.341-3.703 1.191-8.559 2.652-14.411 1.523-4.065-0.73-4.713-0.324-7.509-2.723-2.745-2.379-2.679-1.861-3.043-5.735-1.027-9.952-0.936-12.979 2.724-21.868 3.905-9.828 12.557-5.557 17.126-4.777 16.744 2.961 13.265-13.431 17.919-18.084 4.307-4.309 16.602-7.783 20.632-2.501z"/>\r
+   <path fill="#ffd028" d="m305.033 284.651c5.752 7.553 16.423 15.66 20.234 17.878 2.771 1.593 6.791 3.464 6.722 8.454-0.139 5.682-3.049 7.275-4.643 8.869-3.188 3.188-24.801 11.74-34.838 13.355-12.548 1.793-13.935 2.85-20.593 5.082-3.676 1.228-8.658 2.67-14.493 1.547-4.048-0.721-4.565-0.258-7.353-2.644-2.731-2.37-2.457-1.64-2.818-5.495-1.014-9.923-1.242-12.716 2.345-21.597 3.905-10.004 12.611-5.351 17.158-4.563 16.689 3 13.055-13.75 17.698-18.393 4.297-4.295 16.562-7.761 20.581-2.493z"/>\r
+   <path fill="#ffd02b" d="m304.982 284.724c5.737 7.534 16.382 15.622 20.184 17.834 2.766 1.59 6.774 3.456 6.705 8.433-0.138 5.67-3.041 7.26-4.631 8.85-3.18 3.179-24.966 11.789-34.877 13.172-12.568 1.568-13.487 2.515-20.179 4.824-3.65 1.263-8.759 2.688-14.575 1.572-4.031-0.713-4.42-0.195-7.196-2.566-2.718-2.361-2.238-1.42-2.594-5.254-1.001-9.896-1.549-12.453 1.964-21.328 3.907-10.178 12.666-5.145 17.192-4.348 16.634 3.039 12.844-14.068 17.476-18.701 4.285-4.286 16.521-7.743 20.531-2.488z"/>\r
+   <path fill="#ffd02d" d="m304.93 284.797c5.723 7.516 16.342 15.584 20.135 17.789 2.758 1.588 6.758 3.449 6.688 8.414-0.138 5.654-3.034 7.24-4.619 8.826-3.173 3.172-25.13 11.84-34.918 12.99-12.587 1.344-13.039 2.18-19.766 4.564-3.622 1.301-8.856 2.709-14.657 1.599-4.014-0.704-4.272-0.13-7.039-2.489-2.702-2.352-2.018-1.197-2.369-5.012-0.987-9.868-1.855-12.189 1.584-21.057 3.908-10.354 12.722-4.94 17.226-4.135 16.578 3.078 12.634-14.389 17.254-19.009 4.275-4.273 16.481-7.721 20.481-2.48z"/>\r
+   <path fill="#ffd030" d="m304.879 284.87c5.709 7.498 16.302 15.547 20.085 17.748 2.752 1.582 6.741 3.438 6.673 8.391-0.139 5.642-3.027 7.224-4.609 8.806-3.164 3.164-25.293 11.89-34.956 12.808-12.606 1.119-12.592 1.844-19.352 4.308-3.596 1.336-8.958 2.726-14.739 1.622-3.997-0.695-4.127-0.065-6.882-2.411-2.69-2.343-1.799-0.976-2.146-4.771-0.974-9.84-2.163-11.928 1.204-20.787 3.91-10.529 12.777-4.734 17.258-3.92 16.524 3.117 12.424-14.707 17.034-19.316 4.263-4.267 16.439-7.706 20.43-2.478z"/>\r
+   <path fill="#ffd133" d="m304.826 284.943c5.695 7.479 16.263 15.509 20.036 17.703 2.745 1.579 6.726 3.431 6.656 8.372-0.137 5.627-3.02 7.205-4.597 8.783-3.157 3.156-25.458 11.939-34.997 12.625-12.626 0.893-12.145 1.51-18.938 4.049-3.569 1.373-9.058 2.745-14.822 1.646-3.979-0.686-3.979 0-6.725-2.332-2.676-2.334-1.578-0.756-1.921-4.529-0.961-9.813-2.471-11.665 0.824-20.516 3.91-10.705 12.83-4.529 17.29-3.707 16.47 3.156 12.215-15.027 16.813-19.625 4.255-4.253 16.401-7.684 20.381-2.469z"/>\r
+  </g>\r
+  <g transform="translate(-12.4048,10.0005)">\r
+   <path fill="#995900" d="m52.494 273.618c-6.479 4.68-22.896 4.248-27.072 9.719-4.104 5.473 0.145 13.393 0.072 28.08 0 6.265-1.08 11.017-1.8 14.832-1.008 4.824-1.656 8.209 0.36 11.664 3.672 6.121 9.575 7.633 43.344 14.688 18.072 3.744 35.136 13.464 46.584 14.399 11.448 0.865 13.896-2.951 20.88-9.144 6.912-6.192 9.144-4.248 8.928-17.856-0.216-13.535-8.928-17.567-18.792-33.191s-11.448-18.504-18-28.872c-6.552-10.224-19.512-28.8-26.928-29.017-5.904-0.144-9.216 3.024-12.888 6.769s-8.208 13.248-14.688 17.929z"/>\r
+   <path fill="#9e5e00" d="m52.598 273.905c-6.397 4.702-22.475 3.788-27.062 9.512-4.154 5.414 0.228 13.276 0.098 27.955-0.025 6.23-1.152 10.881-1.937 14.877-1.037 4.871-1.678 8.201 0.349 11.619 3.787 6.162 9.695 7.123 43.456 14.168 18.061 3.737 34.541 13.307 46.343 14.112 11.186 0.792 13.564-2.829 20.463-8.96 6.896-6.195 9.024-4.277 8.858-17.406-0.075-13.521-8.305-17.349-18.169-32.973s-11.448-18.504-18-28.872c-6.552-10.224-19.512-28.8-26.928-29.017-5.904-0.144-9.216 3.024-12.888 6.769s-8.153 13.479-14.583 18.216z"/>\r
+   <path fill="#a36400" d="m52.703 274.193c-6.314 4.724-22.054 3.327-27.051 9.304-4.204 5.356 0.31 13.16 0.123 27.828-0.051 6.198-1.225 10.748-2.074 14.924-1.065 4.918-1.699 8.194 0.339 11.571 3.902 6.206 9.813 6.617 43.567 13.651 18.05 3.73 33.948 13.146 46.101 13.824 10.923 0.72 13.234-2.707 20.045-8.777 6.885-6.199 8.907-4.305 8.792-16.956 0.064-13.507-7.683-17.129-17.547-32.753s-11.448-18.504-18-28.872c-6.552-10.224-19.512-28.8-26.928-29.017-5.904-0.144-9.216 3.024-12.888 6.769s-8.1 13.709-14.479 18.504z"/>\r
+   <path fill="#a86a00" d="m52.807 274.481c-6.231 4.745-21.633 2.866-27.04 9.094-4.255 5.299 0.393 13.047 0.148 27.702-0.076 6.167-1.297 10.616-2.211 14.972-1.095 4.965-1.721 8.188 0.328 11.524 4.018 6.25 9.932 6.108 43.679 13.134 18.039 3.721 33.354 12.988 45.86 13.535 10.659 0.648 12.902-2.585 19.627-8.593 6.869-6.203 8.788-4.335 8.723-16.507 0.205-13.492-7.06-16.909-16.924-32.533s-11.448-18.504-18-28.872c-6.552-10.224-19.512-28.8-26.928-29.017-5.904-0.144-9.216 3.024-12.888 6.769s-8.045 13.94-14.374 18.792z"/>\r
+   <path fill="#ad7000" d="m52.912 274.769c-6.149 4.767-21.211 2.405-27.029 8.885-4.306 5.242 0.476 12.931 0.173 27.576-0.101 6.136-1.368 10.483-2.347 15.019-1.123 5.012-1.742 8.181 0.316 11.478 4.133 6.293 10.052 5.603 43.791 12.614 18.028 3.716 32.76 12.83 45.619 13.248 10.396 0.576 12.57-2.463 19.21-8.409 6.854-6.206 8.668-4.363 8.653-16.056 0.347-13.479-6.437-16.69-16.301-32.314s-11.448-18.504-18-28.872c-6.552-10.224-19.512-28.8-26.928-29.017-5.904-0.144-9.216 3.024-12.888 6.769s-7.991 14.169-14.269 19.079z"/>\r
+   <path fill="#b27500" d="m53.016 275.057c-6.066 4.787-20.79 1.943-27.019 8.676-4.355 5.184 0.559 12.816 0.198 27.45-0.126 6.103-1.44 10.351-2.484 15.065-1.151 5.059-1.764 8.172 0.307 11.431 4.248 6.336 10.17 5.094 43.901 12.096 18.019 3.708 32.166 12.672 45.378 12.96 10.135 0.504 12.24-2.34 18.792-8.227 6.841-6.209 8.551-4.391 8.586-15.605 0.486-13.464-5.813-16.47-15.678-32.094s-11.448-18.504-18-28.872c-6.552-10.224-19.512-28.8-26.928-29.017-5.904-0.144-9.216 3.024-12.888 6.769s-7.937 14.399-14.165 19.368z"/>\r
+   <path fill="#b77b00" d="m53.121 275.344c-5.983 4.811-20.369 1.484-27.008 8.469-4.406 5.126 0.641 12.7 0.224 27.324-0.151 6.068-1.512 10.216-2.621 15.111-1.181 5.105-1.785 8.166 0.295 11.385 4.363 6.379 10.289 4.586 44.014 11.576 18.008 3.701 31.572 12.515 45.137 12.672 9.872 0.433 11.909-2.217 18.375-8.041 6.825-6.215 8.431-4.422 8.518-15.156 0.627-13.45-5.191-16.251-15.056-31.875s-11.448-18.504-18-28.872c-6.552-10.224-19.512-28.8-26.928-29.017-5.904-0.144-9.216 3.024-12.888 6.769s-7.884 14.631-14.062 19.655z"/>\r
+   <path fill="#bc8100" d="m53.225 275.633c-5.9 4.832-19.948 1.023-26.997 8.259-4.457 5.069 0.724 12.585 0.249 27.198-0.177 6.037-1.584 10.082-2.758 15.158-1.21 5.152-1.808 8.158 0.284 11.338 4.479 6.422 10.407 4.078 44.125 11.059 17.997 3.693 30.979 12.355 44.896 12.384 9.608 0.36 11.578-2.095 17.957-7.858 6.812-6.217 8.313-4.449 8.45-14.707 0.766-13.435-4.569-16.03-14.434-31.654s-11.448-18.504-18-28.872c-6.552-10.224-19.512-28.8-26.928-29.017-5.904-0.144-9.216 3.024-12.888 6.769s-7.829 14.859-13.956 19.943z"/>\r
+   <path fill="#c18700" d="m53.329 275.92c-5.817 4.854-19.526 0.563-26.985 8.051-4.507 5.011 0.807 12.47 0.273 27.072-0.201 6.004-1.655 9.949-2.894 15.205-1.239 5.199-1.829 8.151 0.273 11.291 4.594 6.465 10.526 3.57 44.236 10.541 17.985 3.686 30.384 12.196 44.655 12.096 9.345 0.287 11.245-1.973 17.539-7.676 6.797-6.221 8.193-4.479 8.381-14.256 0.906-13.42-3.946-15.812-13.811-31.436s-11.448-18.504-18-28.872c-6.552-10.224-19.512-28.8-26.928-29.017-5.904-0.144-9.216 3.024-12.888 6.769s-7.774 15.094-13.851 20.232z"/>\r
+   <path fill="#c68c00" d="m53.433 276.209c-5.734 4.875-19.104 0.101-26.975 7.84-4.558 4.955 0.89 12.355 0.299 26.947-0.227 5.973-1.728 9.816-3.031 15.252-1.267 5.246-1.851 8.145 0.263 11.244 4.709 6.508 10.646 3.063 44.349 10.022 17.975 3.679 29.79 12.038 44.413 11.808 9.083 0.217 10.915-1.851 17.122-7.492 6.782-6.224 8.074-4.506 8.313-13.806 1.048-13.405-3.323-15.592-13.188-31.216s-11.448-18.504-18-28.872c-6.552-10.224-19.512-28.8-26.928-29.017-5.904-0.144-9.216 3.024-12.888 6.769s-7.722 15.322-13.749 20.521z"/>\r
+   <path fill="#cc9200" d="m53.538 276.497c-5.651 4.896-18.684-0.359-26.964 7.633-4.607 4.896 0.972 12.24 0.324 26.82-0.252 5.939-1.8 9.684-3.168 15.299-1.296 5.293-1.872 8.137 0.252 11.196 4.824 6.552 10.764 2.556 44.46 9.505 17.964 3.672 29.196 11.879 44.172 11.52 8.82 0.144 10.584-1.729 16.704-7.309 6.768-6.228 7.956-4.535 8.244-13.355 1.188-13.393-2.7-15.372-12.564-30.996s-11.448-18.504-18-28.872c-6.552-10.224-19.512-28.8-26.928-29.017-5.904-0.144-9.216 3.024-12.888 6.769s-7.668 15.551-13.644 20.807z"/>\r
+   <path fill="#d19800" d="m53.642 276.786c-5.569 4.918-18.263-0.82-26.953 7.424-4.658 4.838 1.055 12.123 0.35 26.693-0.277 5.907-1.872 9.551-3.305 15.346-1.325 5.34-1.894 8.129 0.241 11.15 4.938 6.596 10.883 2.048 44.571 8.984 17.953 3.666 28.602 11.723 43.931 11.232 8.558 0.072 10.253-1.605 16.287-7.123 6.753-6.232 7.837-4.566 8.175-12.906 1.329-13.379-2.077-15.153-11.941-30.777s-11.448-18.504-18-28.872c-6.552-10.224-19.512-28.8-26.928-29.017-5.904-0.144-9.216 3.024-12.888 6.769s-7.614 15.783-13.54 21.097z"/>\r
+   <path fill="#d69e00" d="m53.747 277.073c-5.486 4.94-17.842-1.281-26.942 7.215-4.709 4.781 1.138 12.01 0.374 26.568-0.302 5.875-1.943 9.417-3.441 15.393-1.354 5.387-1.915 8.123 0.23 11.104 5.055 6.639 11.002 1.541 44.684 8.467 17.942 3.658 28.008 11.563 43.688 10.944 8.296 0 9.923-1.483 15.869-6.941 6.74-6.235 7.72-4.593 8.108-12.456 1.468-13.363-1.455-14.933-11.319-30.557s-11.448-18.504-18-28.872c-6.552-10.224-19.512-28.8-26.928-29.017-5.904-0.144-9.216 3.024-12.888 6.769s-7.56 16.012-13.435 21.383z"/>\r
+   <path fill="#dba300" d="m53.851 277.361c-5.403 4.962-17.421-1.741-26.932 7.007-4.759 4.723 1.221 11.893 0.399 26.441-0.327 5.843-2.016 9.283-3.578 15.439-1.383 5.434-1.937 8.115 0.22 11.057 5.169 6.682 11.12 1.033 44.795 7.949 17.932 3.649 27.414 11.404 43.448 10.656 8.031-0.072 9.59-1.361 15.451-6.758 6.725-6.238 7.6-4.623 8.039-12.006 1.609-13.35-0.832-14.714-10.696-30.338s-11.448-18.504-18-28.872c-6.552-10.224-19.512-28.8-26.928-29.017-5.904-0.144-9.216 3.024-12.888 6.769s-7.504 16.245-13.33 21.673z"/>\r
+   <path fill="#e0a900" d="m53.956 277.649c-5.321 4.982-16.999-2.203-26.921 6.797-4.81 4.666 1.303 11.779 0.425 26.316-0.353 5.811-2.088 9.15-3.715 15.486-1.411 5.48-1.959 8.108 0.208 11.01 5.285 6.725 11.239 0.525 44.907 7.431 17.92 3.644 26.819 11.246 43.207 10.368 7.769-0.145 9.259-1.239 15.034-6.574 6.71-6.242 7.479-4.65 7.97-11.557 1.75-13.334-0.209-14.493-10.073-30.117s-11.448-18.504-18-28.872c-6.552-10.224-19.512-28.8-26.928-29.017-5.904-0.144-9.216 3.024-12.888 6.769s-7.452 16.474-13.226 21.96z"/>\r
+   <path fill="#e5af00" d="m54.06 277.937c-5.238 5.004-16.578-2.664-26.91 6.588-4.86 4.608 1.386 11.664 0.45 26.19-0.378 5.777-2.16 9.018-3.853 15.533-1.439 5.526-1.979 8.101 0.198 10.963 5.4 6.768 11.358 0.018 45.018 6.912 17.91 3.635 26.227 11.088 42.967 10.08 7.506-0.217 8.928-1.117 14.615-6.391 6.696-6.246 7.362-4.68 7.902-11.105 1.89-13.32 0.414-14.274-9.45-29.898s-11.448-18.504-18-28.872c-6.552-10.224-19.512-28.8-26.928-29.017-5.904-0.144-9.216 3.024-12.888 6.769s-7.397 16.704-13.121 22.248z"/>\r
+   <path fill="#eab500" d="m54.165 278.225c-5.155 5.025-16.157-3.124-26.899 6.38-4.91 4.55 1.469 11.548 0.476 26.063-0.403 5.746-2.232 8.885-3.989 15.58-1.469 5.573-2.002 8.094 0.188 10.916 5.515 6.812 11.477-0.49 45.129 6.394 17.899 3.629 25.633 10.93 42.725 9.792 7.243-0.288 8.598-0.993 14.199-6.206 6.681-6.25 7.243-4.709 7.833-10.656 2.031-13.306 1.037-14.055-8.827-29.679s-11.448-18.504-18-28.872c-6.552-10.224-19.512-28.8-26.928-29.017-5.904-0.144-9.216 3.024-12.888 6.769s-7.346 16.935-13.019 22.536z"/>\r
+   <path fill="#efba00" d="m54.269 278.513c-5.073 5.048-15.736-3.585-26.889 6.171-4.961 4.492 1.552 11.434 0.5 25.938-0.428 5.713-2.304 8.752-4.125 15.627-1.498 5.621-2.023 8.086 0.176 10.869 5.631 6.854 11.596-0.996 45.241 5.875 17.889 3.623 25.038 10.771 42.483 9.504 6.981-0.359 8.266-0.871 13.781-6.022 6.668-6.253 7.125-4.737 7.766-10.206 2.17-13.291 1.659-13.835-8.205-29.459s-11.448-18.504-18-28.872c-6.552-10.224-19.512-28.8-26.928-29.017-5.904-0.144-9.216 3.024-12.888 6.769s-7.289 17.164-12.912 22.823z"/>\r
+   <path fill="#f4c000" d="m54.374 278.801c-4.99 5.068-15.314-4.047-26.878 5.962-5.012 4.435 1.634 11.317 0.525 25.812-0.453 5.682-2.376 8.618-4.263 15.674-1.526 5.668-2.045 8.08 0.166 10.822 5.745 6.898 11.714-1.505 45.353 5.357 17.878 3.613 24.444 10.613 42.243 9.216 6.717-0.433 7.934-0.749 13.362-5.839 6.653-6.258 7.007-4.768 7.697-9.756 2.312-13.277 2.282-13.616-7.582-29.24s-11.448-18.504-18-28.872c-6.552-10.224-19.512-28.8-26.928-29.017-5.904-0.144-9.216 3.024-12.888 6.769s-7.235 17.395-12.807 23.112z"/>\r
+   <path fill="#f9c600" d="m54.477 279.088c-4.906 5.092-14.893-4.506-26.866 5.754-5.062 4.377 1.717 11.203 0.551 25.686-0.479 5.648-2.448 8.485-4.399 15.721-1.555 5.715-2.066 8.072 0.155 10.775 5.86 6.941 11.833-2.012 45.464 4.839 17.867 3.607 23.851 10.454 42.001 8.929 6.455-0.504 7.604-0.627 12.946-5.656 6.638-6.26 6.886-4.795 7.628-9.307 2.452-13.262 2.905-13.396-6.959-29.02s-11.448-18.504-18-28.872c-6.552-10.224-19.512-28.8-26.928-29.017-5.904-0.144-9.216 3.024-12.888 6.769s-7.182 17.626-12.705 23.399z"/>\r
+   <path fill="#fc0" d="m54.582 279.377c-4.823 5.111-14.472-4.969-26.855 5.543-5.112 4.32 1.8 11.088 0.576 25.561-0.504 5.616-2.521 8.352-4.536 15.768-1.584 5.76-2.088 8.064 0.144 10.729 5.977 6.984 11.952-2.52 45.576 4.32 17.856 3.6 23.256 10.295 41.76 8.64 6.192-0.576 7.272-0.504 12.528-5.472 6.624-6.264 6.768-4.824 7.56-8.856 2.592-13.248 3.528-13.176-6.336-28.8s-11.448-18.504-18-28.872c-6.552-10.224-19.512-28.8-26.928-29.017-5.904-0.144-9.216 3.024-12.888 6.769s-7.129 17.855-12.601 23.687z"/>\r
+  </g>\r
+  <g transform="translate(-12.4048,10.0005)">\r
+   <path fill="#fc0" d="m57.701 285.002c-4.278 4.539-18.28-1.36-24.892 3.631-4.732 3.5 2.398 7.908 1.426 20.873-0.389 4.926-3.824 5.834-2.398 12.64 1.103 5.121 2.27 4.991 4.214 7.325 5.315 6.223 4.084 1.686 34.355 7.777 16.011 3.242 20.938 9.271 37.596 7.779 5.575-0.518 6.612-0.453 11.279-4.926 5.964-5.575 2.917-4.408 3.565-7.973 2.269-11.863 0.453-13.938-8.428-28.004-8.88-14.066-8.102-14.844-13.936-24.113-5.834-9.141-13.872-25.799-20.549-25.93-5.25-0.129-8.297 2.723-11.603 6.094-3.305 3.372-5.768 19.643-10.629 24.827z"/>\r
+   <path fill="#ffcc02" d="m57.995 285.094c-4.461 4.701-18.604-1.196-25.06 3.705-4.701 3.514 2.578 8.05 1.608 20.68-0.4 4.896-3.733 5.877-2.458 12.634 0.986 5.093 2.357 4.938 4.334 7.201 5.686 6.131 4.673 1.826 34.119 7.743 15.918 3.211 20.815 9.215 37.372 7.732 5.541-0.513 6.479-0.463 11.155-4.849 5.865-5.407 2.858-4.287 3.412-8.058 2.066-11.787 0.442-13.981-8.188-27.649-8.83-13.983-8.143-14.698-13.941-23.913-5.8-9.085-13.701-25.805-20.338-25.934-5.219-0.129-8.248 2.705-11.533 6.057-3.287 3.352-5.644 19.505-10.482 24.651z"/>\r
+   <path fill="#ffcc05" d="m58.289 285.185c-4.644 4.866-18.93-1.031-25.229 3.78-4.669 3.527 2.759 8.191 1.792 20.488-0.413 4.866-3.642 5.918-2.519 12.625 0.872 5.066 2.447 4.887 4.455 7.078 6.057 6.04 5.263 1.969 33.883 7.709 15.825 3.18 20.693 9.159 37.148 7.686 5.508-0.506 6.345-0.471 11.03-4.77 5.768-5.24 2.803-4.168 3.26-8.142 1.865-11.716 0.432-14.027-7.949-27.298-8.78-13.899-8.183-14.553-13.948-23.713-5.764-9.03-13.529-25.811-20.126-25.938-5.188-0.129-8.198 2.688-11.465 6.021-3.266 3.331-5.517 19.368-10.332 24.474z"/>\r
+   <path fill="#ffcc07" d="m58.584 285.276c-4.827 5.03-19.255-0.865-25.397 3.855-4.639 3.541 2.938 8.332 1.975 20.295-0.425 4.838-3.551 5.961-2.578 12.619 0.757 5.039 2.536 4.834 4.575 6.955 6.428 5.949 5.852 2.108 33.646 7.674 15.733 3.147 20.571 9.103 36.925 7.639 5.475-0.501 6.211-0.48 10.905-4.693 5.67-5.072 2.745-4.045 3.107-8.224 1.663-11.642 0.42-14.073-7.711-26.946-8.73-13.814-8.223-14.406-13.952-23.511-5.729-8.976-13.358-25.817-19.916-25.944-5.156-0.127-8.148 2.674-11.396 5.984s-5.391 19.229-10.183 24.297z"/>\r
+   <path fill="#ffcd0a" d="m58.878 285.368c-5.01 5.193-19.579-0.701-25.565 3.93-4.607 3.555 3.117 8.475 2.157 20.102-0.437 4.809-3.459 6.004-2.639 12.613 0.643 5.011 2.626 4.781 4.695 6.83 6.799 5.857 6.442 2.25 33.411 7.64 15.641 3.118 20.45 9.048 36.701 7.593 5.44-0.494 6.076-0.488 10.781-4.615 5.57-4.904 2.688-3.926 2.954-8.308 1.462-11.569 0.409-14.118-7.472-26.593-8.68-13.732-8.263-14.262-13.958-23.311-5.695-8.922-13.188-25.824-19.705-25.951-5.125-0.127-8.1 2.658-11.326 5.949-3.227 3.289-5.266 19.091-10.034 24.121z"/>\r
+   <path fill="#ffcd0c" d="m59.173 285.458c-5.193 5.358-19.905-0.535-25.734 4.008-4.577 3.566 3.297 8.613 2.34 19.908-0.449 4.778-3.368 6.045-2.698 12.605 0.526 4.982 2.715 4.729 4.815 6.707 7.17 5.766 7.031 2.391 33.175 7.604 15.549 3.088 20.328 8.994 36.477 7.547 5.408-0.488 5.943-0.498 10.657-4.537 5.472-4.737 2.63-3.805 2.802-8.393 1.261-11.494 0.398-14.162-7.232-26.24-8.63-13.647-8.304-14.117-13.964-23.109-5.66-8.867-13.017-25.829-19.494-25.956-5.094-0.126-8.05 2.642-11.257 5.912-3.21 3.27-5.142 18.955-9.887 23.944z"/>\r
+   <path fill="#ffcd0f" d="m59.467 285.547c-5.376 5.523-20.229-0.369-25.903 4.084-4.545 3.58 3.478 8.756 2.523 19.715-0.461 4.75-3.277 6.088-2.758 12.599 0.411 4.955 2.804 4.677 4.936 6.584 7.541 5.675 7.621 2.532 32.938 7.569 15.456 3.056 20.206 8.938 36.253 7.5 5.375-0.482 5.811-0.506 10.533-4.459 5.374-4.57 2.572-3.686 2.649-8.477 1.058-11.42 0.387-14.209-6.995-25.888-8.58-13.563-8.344-13.972-13.969-22.909-5.626-8.813-12.846-25.834-19.283-25.961-5.063-0.125-8 2.625-11.188 5.877-3.188 3.251-5.015 18.818-9.736 23.766z"/>\r
+   <path fill="#ffcd11" d="m59.76 285.639c-5.559 5.688-20.555-0.205-26.071 4.158-4.515 3.594 3.657 8.896 2.706 19.521-0.473 4.721-3.186 6.131-2.818 12.594 0.297 4.926 2.894 4.623 5.057 6.459 7.911 5.584 8.21 2.674 32.703 7.535 15.362 3.025 20.084 8.883 36.027 7.453 5.342-0.477 5.677-0.515 10.409-4.381 5.275-4.402 2.516-3.564 2.497-8.561 0.855-11.348 0.375-14.254-6.756-25.535-8.53-13.479-8.385-13.825-13.976-22.709-5.59-8.758-12.673-25.842-19.071-25.965-5.031-0.125-7.951 2.608-11.119 5.838-3.168 3.232-4.888 18.684-9.588 23.593z"/>\r
+   <path fill="#ffce14" d="m60.055 285.73c-5.742 5.851-20.88-0.04-26.24 4.233-4.483 3.607 3.837 9.039 2.889 19.33-0.485 4.69-3.095 6.172-2.878 12.584 0.182 4.9 2.982 4.572 5.177 6.338 8.282 5.492 8.8 2.814 32.467 7.498 15.271 2.996 19.962 8.828 35.804 7.408 5.309-0.472 5.543-0.523 10.285-4.304 5.177-4.235 2.458-3.444 2.344-8.644 0.654-11.273 0.364-14.299-6.518-25.184-8.479-13.395-8.425-13.679-13.98-22.507-5.556-8.704-12.502-25.849-18.86-25.972-5-0.123-7.901 2.593-11.05 5.803s-4.765 18.547-9.44 23.417z"/>\r
+   <path fill="#ffce16" d="m60.349 285.821c-5.925 6.016-21.205 0.125-26.408 4.309-4.453 3.621 4.017 9.18 3.07 19.137-0.496 4.662-3.002 6.215-2.938 12.579 0.066 4.872 3.072 4.518 5.298 6.213 8.653 5.401 9.389 2.956 32.23 7.464 15.178 2.964 19.84 8.771 35.58 7.361 5.275-0.465 5.409-0.532 10.159-4.225 5.079-4.068 2.401-3.324 2.192-8.729 0.452-11.2 0.354-14.346-6.279-24.831-8.429-13.312-8.464-13.534-13.985-22.306-5.521-8.65-12.331-25.854-18.649-25.978-4.969-0.123-7.853 2.577-10.98 5.767-3.129 3.19-4.637 18.409-9.29 23.239z"/>\r
+   <path fill="#ffce19" d="m60.643 285.911c-6.107 6.18-21.529 0.291-26.577 4.385-4.421 3.635 4.197 9.322 3.254 18.945-0.508 4.631-2.911 6.256-2.997 12.571-0.049 4.845 3.161 4.465 5.418 6.089 9.023 5.309 9.979 3.098 31.994 7.43 15.085 2.934 19.718 8.717 35.355 7.314 5.242-0.459 5.276-0.541 10.036-4.148 4.98-3.899 2.344-3.203 2.039-8.811 0.25-11.127 0.342-14.391-6.04-24.479-8.38-13.228-8.505-13.389-13.991-22.105-5.486-8.596-12.16-25.859-18.438-25.982-4.938-0.123-7.803 2.561-10.911 5.73-3.109 3.17-4.513 18.272-9.142 23.061z"/>\r
+   <path fill="#ffce1c" d="m60.938 286.002c-6.291 6.344-21.855 0.455-26.746 4.459-4.391 3.648 4.377 9.463 3.437 18.752-0.521 4.603-2.82 6.299-3.058 12.564-0.163 4.817 3.251 4.413 5.539 5.965 9.395 5.219 10.567 3.24 31.758 7.396 14.993 2.903 19.597 8.661 35.132 7.269 5.209-0.453 5.143-0.55 9.912-4.07 4.882-3.732 2.286-3.082 1.887-8.895 0.048-11.053 0.329-14.436-5.802-24.126-8.33-13.144-8.545-13.243-13.997-21.905-5.451-8.541-11.988-25.865-18.228-25.988-4.906-0.121-7.753 2.545-10.843 5.695-3.088 3.149-4.385 18.132-8.991 22.884z"/>\r
+   <path fill="#ffcf1e" d="m61.232 286.092c-6.473 6.51-22.18 0.621-26.914 4.535-4.359 3.662 4.557 9.604 3.619 18.559-0.532 4.574-2.729 6.342-3.117 12.559-0.278 4.789 3.34 4.36 5.659 5.842 9.766 5.127 11.157 3.381 31.521 7.359 14.9 2.872 19.475 8.607 34.908 7.223 5.176-0.447 5.008-0.559 9.787-3.992 4.784-3.565 2.229-2.963 1.735-8.979-0.155-10.98 0.317-14.482-5.564-23.773-8.279-13.061-8.585-13.098-14.002-21.705-5.416-8.485-11.817-25.871-18.017-25.992-4.875-0.121-7.704 2.527-10.773 5.658-3.068 3.128-4.26 17.995-8.842 22.706z"/>\r
+   <path fill="#ffcf21" d="m61.526 286.184c-6.655 6.673-22.505 0.785-27.082 4.611-4.328 3.674 4.736 9.744 3.802 18.365-0.544 4.543-2.638 6.383-3.178 12.551-0.394 4.761 3.43 4.308 5.78 5.718 10.136 5.036 11.746 3.522 31.285 7.325 14.808 2.841 19.353 8.551 34.685 7.176 5.142-0.441 4.875-0.567 9.663-3.914 4.685-3.398 2.171-2.842 1.582-9.063-0.357-10.906 0.307-14.527-5.325-23.422-8.23-12.976-8.625-12.951-14.008-21.503-5.382-8.432-11.646-25.878-17.806-25.999-4.844-0.119-7.654 2.512-10.704 5.622-3.049 3.109-4.134 17.859-8.694 22.533z"/>\r
+   <path fill="#ffcf23" d="m61.821 286.275c-6.839 6.837-22.83 0.95-27.251 4.687-4.298 3.688 4.916 9.886 3.984 18.172-0.557 4.515-2.546 6.426-3.237 12.545-0.509 4.732 3.519 4.255 5.9 5.594 10.507 4.945 12.336 3.663 31.05 7.29 14.715 2.812 19.23 8.496 34.46 7.129 5.108-0.435 4.74-0.575 9.538-3.835 4.587-3.23 2.113-2.723 1.43-9.146-0.559-10.834 0.296-14.572-5.086-23.069-8.18-12.892-8.666-12.806-14.014-21.302-5.347-8.377-11.476-25.885-17.595-26.004-4.813-0.119-7.605 2.496-10.635 5.584-3.029 3.088-4.008 17.722-8.544 22.355z"/>\r
+   <path fill="#ffcf26" d="m62.115 286.366c-7.021 7.002-23.154 1.115-27.42 4.762-4.266 3.701 5.096 10.027 4.168 17.979-0.568 4.486-2.455 6.469-3.297 12.538-0.624 4.706 3.607 4.203 6.021 5.472 10.878 4.852 12.925 3.803 30.813 7.254 14.622 2.78 19.108 8.441 34.236 7.084 5.075-0.43 4.606-0.584 9.413-3.759 4.489-3.063 2.058-2.601 1.277-9.229-0.761-10.76 0.284-14.618-4.848-22.717-8.129-12.809-8.706-12.66-14.019-21.102-5.313-8.322-11.304-25.891-17.384-26.01-4.781-0.118-7.556 2.48-10.566 5.55-3.008 3.068-3.881 17.584-8.394 22.178z"/>\r
+   <path fill="#ffd028" d="m62.409 286.456c-7.204 7.166-23.479 1.281-27.588 4.838-4.235 3.715 5.275 10.168 4.351 17.787-0.58 4.455-2.364 6.51-3.357 12.53-0.738 4.679 3.697 4.149 6.142 5.347 11.249 4.763 13.515 3.946 30.577 7.221 14.529 2.748 18.986 8.386 34.012 7.037 5.043-0.424 4.474-0.594 9.289-3.68 4.392-2.897 2-2.481 1.125-9.314-0.963-10.686 0.273-14.664-4.608-22.364-8.079-12.726-8.746-12.517-14.024-20.901-5.277-8.269-11.133-25.896-17.173-26.015-4.75-0.116-7.506 2.464-10.497 5.513-2.992 3.047-3.759 17.447-8.249 22.001z"/>\r
+   <path fill="#ffd02b" d="m62.704 286.547c-7.388 7.33-23.805 1.445-27.757 4.912-4.204 3.729 5.455 10.311 4.533 17.594-0.593 4.427-2.272 6.553-3.417 12.523-0.854 4.651 3.786 4.098 6.262 5.225 11.619 4.671 14.104 4.087 30.341 7.185 14.438 2.72 18.864 8.33 33.787 6.991 5.01-0.418 4.341-0.604 9.166-3.604 4.292-2.729 1.942-2.359 0.972-9.396-1.163-10.613 0.263-14.709-4.369-22.012-8.03-12.641-8.787-12.371-14.03-20.7-5.243-8.214-10.962-25.903-16.962-26.021-4.719-0.117-7.457 2.447-10.428 5.477-2.972 3.029-3.632 17.313-8.098 21.826z"/>\r
+   <path fill="#ffd02d" d="m62.998 286.638c-7.57 7.493-24.13 1.61-27.925 4.987-4.174 3.742 5.635 10.451 4.716 17.4-0.604 4.398-2.182 6.596-3.478 12.518-0.969 4.623 3.875 4.045 6.382 5.101 11.991 4.579 14.694 4.228 30.105 7.149 14.345 2.688 18.743 8.275 33.563 6.944 4.977-0.411 4.207-0.61 9.042-3.524 4.193-2.562 1.884-2.239 0.819-9.481-1.366-10.538 0.251-14.754-4.133-21.659-7.979-12.557-8.825-12.224-14.034-20.498-5.208-8.16-10.791-25.91-16.751-26.027-4.688-0.114-7.407 2.433-10.358 5.441-2.951 3.01-3.505 17.174-7.948 21.649z"/>\r
+   <path fill="#ffd030" d="m63.292 286.729c-7.753 7.658-24.454 1.775-28.094 5.063-4.142 3.756 5.815 10.594 4.899 17.209-0.616 4.367-2.09 6.638-3.537 12.51-1.084 4.596 3.964 3.992 6.502 4.977 12.362 4.488 15.283 4.369 29.869 7.115 14.252 2.656 18.621 8.22 33.34 6.898 4.943-0.406 4.073-0.621 8.917-3.447 4.095-2.395 1.828-2.119 0.667-9.565-1.568-10.465 0.239-14.8-3.894-21.307-7.929-12.474-8.866-12.078-14.04-20.298-5.173-8.105-10.619-25.916-16.54-26.031-4.656-0.115-7.357 2.415-10.289 5.404-2.932 2.988-3.38 17.036-7.8 21.472z"/>\r
+   <path fill="#ffd133" d="m63.587 286.82c-7.937 7.822-24.78 1.94-28.263 5.138-4.111 3.77 5.995 10.734 5.081 17.016-0.627 4.339-1.998 6.68-3.597 12.504-1.199 4.568 4.054 3.939 6.623 4.854 12.732 4.396 15.873 4.51 29.633 7.08 14.16 2.625 18.499 8.164 33.116 6.852 4.909-0.4 3.939-0.629 8.793-3.369 3.997-2.227 1.77-1.999 0.514-9.648-1.771-10.393 0.228-14.846-3.654-20.955-7.88-12.39-8.907-11.934-14.046-20.098-5.139-8.051-10.448-25.922-16.329-26.037-4.625-0.113-7.309 2.398-10.221 5.368s-3.254 16.897-7.65 21.295z"/>\r
+  </g>\r
+  <g transform="translate(-12.4048,10.0005)">\r
+   <path d="m88.782 218.681c-0.936 2.088-1.728 20.017 2.952 27 4.68 6.911 3.312 10.872-1.872 5.616-5.4-5.112-8.928-12.816-9-18.145 0-3.096 2.376-15.84 3.313-17.208 1.007-1.44 5.327 1.153 4.607 2.737z"/>\r
+   <path fill="#030303" d="m88.692 219.032c-0.903 2.34-1.656 19.698 3.01 26.668 4.665 6.901 3.186 10.49-1.84 5.356-5.23-4.997-8.607-12.47-8.741-17.787-0.043-3.114 2.236-15.419 3.133-16.791 0.961-1.394 5.126 0.989 4.438 2.554z"/>\r
+   <path fill="#070707" d="m88.602 219.379c-0.871 2.593-1.584 19.383 3.067 26.338 4.65 6.891 3.06 10.109-1.808 5.098-5.062-4.882-8.287-12.125-8.481-17.432-0.087-3.131 2.095-14.998 2.952-16.373 0.915-1.345 4.926 0.828 4.27 2.369z"/>\r
+   <path fill="#0b0b0b" d="m88.512 219.729c-0.839 2.844-1.513 19.066 3.124 26.006 4.638 6.881 2.935 9.729-1.774 4.84-4.893-4.768-7.967-11.779-8.223-17.076-0.129-3.149 1.955-14.576 2.772-15.955 0.868-1.299 4.724 0.665 4.101 2.185z"/>\r
+   <path fill="#0f0f0f" d="m88.422 220.079c-0.806 3.096-1.439 18.748 3.183 25.674 4.623 6.869 2.809 9.347-1.742 4.58-4.724-4.651-7.646-11.434-7.963-16.719-0.173-3.168 1.814-14.154 2.592-15.537 0.819-1.252 4.52 0.504 3.93 2.002z"/>\r
+   <path fill="#131313" d="m88.332 220.426c-0.773 3.349-1.368 18.433 3.24 25.345 4.608 6.858 2.682 8.964-1.71 4.319-4.554-4.535-7.326-11.088-7.704-16.361-0.216-3.186 1.674-13.734 2.412-15.12 0.774-1.206 4.32 0.343 3.762 1.817z"/>\r
+   <path fill="#161616" d="m88.242 220.777c-0.741 3.601-1.296 18.115 3.298 25.013 4.594 6.848 2.556 8.582-1.678 4.061-4.385-4.421-7.006-10.742-7.444-16.006-0.26-3.203 1.533-13.313 2.231-14.702 0.728-1.16 4.118 0.18 3.593 1.634z"/>\r
+   <path fill="#1a1a1a" d="m88.152 221.125c-0.709 3.853-1.224 17.799 3.355 24.682 4.579 6.837 2.43 8.201-1.646 3.802-4.216-4.306-6.686-10.397-7.186-15.649-0.303-3.221 1.394-12.892 2.052-14.285 0.682-1.112 3.918 0.018 3.425 1.45z"/>\r
+   <path fill="#1e1e1e" d="m88.062 221.475c-0.677 4.104-1.152 17.482 3.413 24.35 4.564 6.826 2.304 7.82-1.613 3.543-4.046-4.191-6.365-10.051-6.927-15.293-0.345-3.24 1.253-12.47 1.872-13.867 0.634-1.066 3.716-0.144 3.255 1.267z"/>\r
+   <path fill="#222" d="m87.972 221.825c-0.645 4.355-1.08 17.164 3.47 24.018 4.551 6.816 2.179 7.439-1.58 3.285-3.877-4.076-6.044-9.707-6.667-14.938-0.389-3.258 1.112-12.049 1.691-13.449 0.587-1.019 3.514-0.306 3.086 1.084z"/>\r
+   <path fill="#262626" d="m87.883 222.172c-0.612 4.609-1.009 16.849 3.527 23.688 4.536 6.804 2.052 7.056-1.548 3.024-3.708-3.961-5.724-9.36-6.408-14.58-0.432-3.276 0.973-11.629 1.513-13.032 0.54-0.971 3.311-0.467 2.916 0.9z"/>\r
+   <path fill="#2a2a2a" d="m87.792 222.523c-0.579 4.86-0.936 16.531 3.586 23.356 4.521 6.793 1.926 6.675-1.516 2.765-3.539-3.845-5.403-9.015-6.148-14.224-0.476-3.293 0.831-11.207 1.332-12.614 0.493-0.925 3.11-0.63 2.746 0.717z"/>\r
+   <path fill="#2d2d2d" d="m87.702 222.872c-0.547 5.112-0.864 16.215 3.644 23.025 4.507 6.783 1.8 6.293-1.483 2.506-3.369-3.73-5.083-8.669-5.89-13.867-0.519-3.312 0.691-10.785 1.152-12.197 0.446-0.878 2.908-0.792 2.577 0.533z"/>\r
+   <path fill="#313131" d="m87.612 223.221c-0.515 5.363-0.792 15.898 3.701 22.693 4.492 6.772 1.674 5.912-1.451 2.248-3.2-3.615-4.763-8.324-5.63-13.512-0.563-3.33 0.551-10.363 0.972-11.779 0.399-0.831 2.707-0.953 2.408 0.35z"/>\r
+   <path fill="#353535" d="m87.522 223.57c-0.482 5.616-0.72 15.581 3.758 22.363 4.479 6.761 1.549 5.53-1.418 1.987-3.031-3.5-4.442-7.978-5.371-13.154-0.604-3.348 0.41-9.943 0.792-11.361 0.352-0.785 2.506-1.115 2.239 0.165z"/>\r
+   <path fill="#393939" d="m87.432 223.918c-0.45 5.869-0.648 15.265 3.815 22.033 4.464 6.75 1.422 5.147-1.386 1.728-2.862-3.384-4.122-7.632-5.112-12.798-0.647-3.366 0.271-9.522 0.612-10.944 0.307-0.737 2.305-1.278 2.071-0.019z"/>\r
+   <path fill="#3d3d3d" d="m87.343 224.269c-0.418 6.12-0.576 14.946 3.873 21.7 4.45 6.74 1.296 4.767-1.354 1.469-2.692-3.27-3.802-7.286-4.853-12.441-0.691-3.383 0.129-9.101 0.432-10.527 0.26-0.691 2.103-1.44 1.902-0.201z"/>\r
+   <path fill="#414141" d="m87.252 224.618c-0.385 6.373-0.504 14.631 3.932 21.369 4.436 6.729 1.17 4.385-1.321 1.211-2.523-3.154-3.481-6.941-4.594-12.086-0.734-3.402-0.011-8.68 0.252-10.109 0.212-0.644 1.901-1.602 1.731-0.385z"/>\r
+   <path fill="#444" d="m87.162 224.967c-0.353 6.623-0.432 14.314 3.989 21.037 4.421 6.719 1.044 4.004-1.289 0.951-2.354-3.039-3.161-6.595-4.334-11.729-0.778-3.42-0.151-8.258 0.071-9.691 0.166-0.597 1.7-1.763 1.563-0.568z"/>\r
+   <path fill="#484848" d="m87.072 225.316c-0.32 6.876-0.36 13.997 4.047 20.707 4.406 6.707 0.918 3.622-1.257 0.692-2.186-2.924-2.841-6.25-4.075-11.373-0.82-3.438-0.292-7.838-0.108-9.273 0.119-0.551 1.498-1.926 1.393-0.753z"/>\r
+   <path fill="#4c4c4c" d="m86.982 225.665c-0.288 7.129-0.288 13.68 4.104 20.377 4.393 6.695 0.792 3.24-1.224 0.432s-2.52-5.904-3.816-11.016c-0.863-3.457-0.432-7.416-0.287-8.856 0.071-0.505 1.295-2.089 1.223-0.937z"/>\r
+  </g>\r
+  <g transform="translate(-12.4048,10.0005)">\r
+   <path d="m88.782 218.681c4.32-9.433 6.696-19.584 12.888-29.448 6.12-9.792 3.672-13.608-0.863-8.64-4.536 4.968-9.504 15.48-9.504 15.48s-5.832 9.216-7.128 19.872c-0.217 1.8 3.887 4.248 4.607 2.736z"/>\r
+   <path fill="#020202" d="m88.968 218.071c4.279-9.5 6.615-19.246 12.586-28.802 5.901-9.488 3.608-13.279-0.764-8.472-4.403 4.847-9.302 15.236-9.368 15.381 0 0-5.637 8.993-6.877 19.239-0.209 1.771 3.72 4.145 4.423 2.654z"/>\r
+   <path fill="#050505" d="m89.152 217.459c4.239-9.566 6.535-18.908 12.284-28.155 5.683-9.183 3.547-12.95-0.663-8.303-4.27 4.725-9.099 14.991-9.231 15.282 0 0-5.442 8.766-6.627 18.605-0.201 1.741 3.554 4.042 4.237 2.571z"/>\r
+   <path fill="#070707" d="m89.338 216.849c4.199-9.634 6.454-18.572 11.981-27.51 5.465-8.878 3.484-12.621-0.563-8.135-4.137 4.605-8.896 14.748-9.096 15.184 0 0-5.247 8.542-6.376 17.972-0.192 1.713 3.387 3.937 4.054 2.489z"/>\r
+   <path fill="#0a0a0a" d="m89.522 216.239c4.159-9.701 6.375-18.234 11.68-26.865 5.247-8.573 3.422-12.292-0.461-7.966-4.005 4.483-8.693 14.503-8.96 15.085 0 0-5.053 8.317-6.126 17.337-0.186 1.684 3.219 3.837 3.867 2.409z"/>\r
+   <path fill="#0c0c0c" d="m89.708 215.627c4.118-9.769 6.294-17.897 11.376-26.218 5.029-8.268 3.36-11.962-0.359-7.797-3.871 4.361-8.49 14.259-8.823 14.985 0 0-4.858 8.093-5.876 16.706-0.179 1.653 3.051 3.731 3.682 2.324z"/>\r
+   <path fill="#0f0f0f" d="m89.893 215.017c4.077-9.836 6.213-17.56 11.073-25.573 4.812-7.963 3.298-11.633-0.259-7.629-3.738 4.241-8.288 14.015-8.688 14.887 0 0-4.663 7.868-5.625 16.072-0.169 1.624 2.886 3.628 3.499 2.243z"/>\r
+   <path fill="#111" d="m90.078 214.407c4.037-9.904 6.133-17.224 10.772-24.928 4.593-7.658 3.234-11.304-0.159-7.46-3.605 4.119-8.085 13.771-8.551 14.788 0 0-4.47 7.645-5.375 15.439-0.162 1.594 2.718 3.524 3.313 2.161z"/>\r
+   <path fill="#141414" d="m90.263 213.795c3.997-9.971 6.052-16.885 10.47-24.281 4.374-7.353 3.172-10.975-0.059-7.291-3.472 3.998-7.882 13.526-8.415 14.689 0 0-4.273 7.418-5.124 14.805-0.154 1.565 2.551 3.421 3.128 2.078z"/>\r
+   <path fill="#161616" d="m90.449 213.184c3.956-10.037 5.972-16.548 10.167-23.635 4.156-7.048 3.11-10.645 0.043-7.123-3.34 3.877-7.68 13.283-8.279 14.591 0 0-4.08 7.194-4.874 14.172-0.147 1.535 2.383 3.317 2.943 1.995z"/>\r
+   <path fill="#191919" d="m90.634 212.573c3.916-10.105 5.892-16.209 9.865-22.989 3.938-6.743 3.047-10.316 0.144-6.954-3.207 3.755-7.477 13.038-8.143 14.491 0 0-3.886 6.969-4.624 13.54-0.139 1.506 2.217 3.213 2.758 1.912z"/>\r
+   <path fill="#1c1c1c" d="m90.819 211.963c3.875-10.174 5.811-15.874 9.563-22.344 3.721-6.438 2.984-9.987 0.244-6.785-3.073 3.634-7.274 12.793-8.007 14.392 0 0-3.69 6.745-4.373 12.905-0.131 1.477 2.049 3.112 2.573 1.832z"/>\r
+   <path fill="#1e1e1e" d="m91.004 211.352c3.836-10.24 5.73-15.536 9.262-21.698 3.501-6.133 2.922-9.658 0.344-6.617-2.94 3.513-7.071 12.55-7.87 14.293 0 0-3.496 6.521-4.123 12.272-0.124 1.447 1.881 3.009 2.387 1.75z"/>\r
+   <path fill="#212121" d="m91.189 210.741c3.795-10.307 5.649-15.198 8.958-21.052 3.284-5.828 2.859-9.328 0.445-6.448-2.808 3.392-6.868 12.305-7.734 14.195 0 0-3.301 6.295-3.872 11.639-0.115 1.418 1.715 2.904 2.203 1.666z"/>\r
+   <path fill="#232323" d="m91.375 210.129c3.754-10.373 5.569-14.86 8.655-20.405 3.066-5.523 2.797-8.999 0.547-6.279-2.675 3.27-6.666 12.061-7.599 14.097 0 0-3.106 6.07-3.622 11.004-0.107 1.388 1.548 2.801 2.019 1.583z"/>\r
+   <path fill="#262626" d="m91.559 209.519c3.714-10.44 5.489-14.523 8.354-19.76 2.847-5.218 2.735-8.67 0.647-6.111-2.542 3.149-6.463 11.817-7.462 13.997 0 0-2.912 5.848-3.372 10.373-0.099 1.357 1.381 2.697 1.833 1.501z"/>\r
+   <path fill="#282828" d="m91.745 208.909c3.674-10.509 5.408-14.187 8.051-19.115 2.629-4.913 2.672-8.341 0.748-5.942-2.409 3.028-6.261 11.573-7.326 13.898 0 0-2.717 5.621-3.121 9.738-0.092 1.33 1.213 2.595 1.648 1.421z"/>\r
+   <path fill="#2b2b2b" d="m91.929 208.297c3.634-10.575 5.328-13.848 7.75-18.468 2.41-4.608 2.609-8.011 0.848-5.773-2.275 2.906-6.058 11.328-7.189 13.799 0 0-2.522 5.397-2.871 9.106-0.084 1.299 1.046 2.491 1.462 1.336z"/>\r
+   <path fill="#2d2d2d" d="m92.115 207.687c3.593-10.643 5.247-13.512 7.447-17.823 2.191-4.303 2.547-7.682 0.949-5.605-2.144 2.786-5.855 11.085-7.055 13.701 0 0-2.327 5.172-2.62 8.473-0.076 1.269 0.881 2.386 1.279 1.254z"/>\r
+   <path fill="#303030" d="m92.301 207.077c3.552-10.71 5.167-13.175 7.145-17.178 1.974-3.998 2.484-7.353 1.05-5.436-2.011 2.664-5.652 10.84-6.918 13.602 0 0-2.133 4.947-2.37 7.839-0.07 1.24 0.712 2.283 1.093 1.173z"/>\r
+   <path fill="#333" d="m92.485 206.465c3.513-10.778 5.087-12.837 6.843-16.531s2.422-7.024 1.15-5.268c-1.877 2.543-5.449 10.596-6.781 13.502 0 0-1.938 4.724-2.12 7.207-0.061 1.211 0.545 2.18 0.908 1.09z"/>\r
+  </g>\r
+  <g transform="translate(-12.4048,10.0005)">\r
+   <path d="m273.03 225.592c2.088-5.903 1.872-20.951-3.456-30.671-1.872-3.528-3.672-7.632-4.752-7.848-1.152-0.216-3.24 2.088-3.024 2.448 0.288 0.576 10.009 14.256 7.992 32.832-0.144 1.513 2.736 4.608 3.24 3.239z"/>\r
+   <path fill="#030303" d="m272.936 224.831c2.025-5.719 1.753-20.379-3.344-29.663-1.815-3.407-3.535-7.374-4.595-7.59-1.122-0.214-3.125 2.022-2.925 2.367 0.258 0.562 9.605 13.778 7.729 31.753-0.129 1.487 2.647 4.456 3.135 3.133z"/>\r
+   <path fill="#070707" d="m272.841 224.068c1.967-5.532 1.637-19.808-3.228-28.654-1.762-3.286-3.401-7.115-4.44-7.332-1.091-0.211-3.009 1.956-2.824 2.287 0.227 0.548 9.201 13.299 7.466 30.672-0.118 1.463 2.555 4.305 3.026 3.027z"/>\r
+   <path fill="#0b0b0b" d="m272.747 223.305c1.904-5.348 1.518-19.235-3.115-27.645-1.706-3.165-3.265-6.856-4.282-7.073-1.062-0.21-2.896 1.889-2.727 2.206 0.197 0.534 8.799 12.821 7.203 29.592-0.103 1.44 2.466 4.153 2.921 2.92z"/>\r
+   <path fill="#0f0f0f" d="m272.652 222.542c1.843-5.162 1.398-18.662-3.001-26.635-1.65-3.044-3.13-6.598-4.127-6.815-1.03-0.207-2.779 1.823-2.626 2.126 0.167 0.52 8.396 12.341 6.94 28.511-0.09 1.416 2.376 4.001 2.814 2.813z"/>\r
+   <path fill="#131313" d="m272.558 221.779c1.78-4.976 1.279-18.09-2.889-25.626-1.595-2.923-2.994-6.34-3.97-6.557-1-0.205-2.664 1.756-2.527 2.045 0.137 0.506 7.993 11.861 6.679 27.432-0.078 1.392 2.285 3.849 2.707 2.706z"/>\r
+   <path fill="#161616" d="m272.463 221.016c1.721-4.79 1.163-17.518-2.773-24.617-1.539-2.802-2.858-6.081-3.814-6.299-0.969-0.203-2.548 1.691-2.427 1.965 0.106 0.492 7.59 11.383 6.415 26.352-0.065 1.369 2.194 3.697 2.599 2.599z"/>\r
+   <path fill="#1a1a1a" d="m272.369 220.254c1.659-4.605 1.044-16.946-2.66-23.609-1.483-2.681-2.723-5.822-3.658-6.04-0.938-0.201-2.434 1.624-2.326 1.884 0.074 0.478 7.185 10.904 6.15 25.271-0.051 1.344 2.106 3.546 2.494 2.494z"/>\r
+   <path fill="#1e1e1e" d="m272.274 219.491c1.598-4.42 0.926-16.373-2.546-22.6-1.43-2.56-2.587-5.564-3.501-5.782-0.908-0.198-2.319 1.558-2.229 1.804 0.044 0.464 6.782 10.425 5.889 24.19-0.037 1.321 2.016 3.396 2.387 2.388z"/>\r
+   <path fill="#222" d="m272.18 218.728c1.535-4.233 0.807-15.802-2.434-21.59-1.373-2.439-2.452-5.306-3.345-5.524-0.877-0.197-2.203 1.491-2.128 1.723 0.014 0.45 6.379 9.947 5.625 23.111-0.023 1.297 1.927 3.242 2.282 2.28z"/>\r
+   <path fill="#262626" d="m272.085 217.965c1.476-4.049 0.69-15.229-2.318-20.582-1.317-2.317-2.316-5.046-3.188-5.265-0.847-0.194-2.088 1.425-2.029 1.642-0.016 0.436 5.977 9.468 5.362 22.032-0.011 1.272 1.835 3.089 2.173 2.173z"/>\r
+   <path fill="#2a2a2a" d="m271.991 217.202c1.413-3.861 0.571-14.656-2.206-19.572-1.262-2.196-2.18-4.788-3.032-5.007-0.815-0.191-1.973 1.36-1.929 1.562-0.047 0.422 5.573 8.988 5.099 20.951 0.003 1.247 1.746 2.939 2.068 2.066z"/>\r
+   <path fill="#2d2d2d" d="m271.896 216.439c1.353-3.677 0.453-14.084-2.091-18.563-1.207-2.076-2.045-4.529-2.876-4.749-0.786-0.19-1.858 1.293-1.83 1.481-0.077 0.408 5.17 8.51 4.836 19.87 0.017 1.226 1.656 2.789 1.961 1.961z"/>\r
+   <path fill="#313131" d="m271.802 215.676c1.29-3.491 0.334-13.512-1.979-17.553-1.151-1.956-1.909-4.272-2.72-4.493-0.755-0.187-1.742 1.227-1.73 1.401-0.106 0.394 4.768 8.031 4.573 18.791 0.031 1.202 1.567 2.637 1.856 1.854z"/>\r
+   <path fill="#353535" d="m271.707 214.915c1.23-3.307 0.217-12.94-1.864-16.545-1.096-1.834-1.773-4.014-2.563-4.234-0.725-0.185-1.627 1.16-1.631 1.32-0.138 0.38 4.364 7.552 4.311 17.71 0.042 1.176 1.475 2.485 1.747 1.749z"/>\r
+   <path fill="#393939" d="m271.613 214.151c1.168-3.119 0.098-12.367-1.751-15.535-1.04-1.714-1.638-3.755-2.407-3.976-0.693-0.183-1.512 1.094-1.531 1.24-0.168 0.366 3.962 7.074 4.049 16.63 0.055 1.153 1.384 2.332 1.64 1.641z"/>\r
+   <path fill="#3d3d3d" d="m271.518 213.388c1.106-2.935-0.021-11.796-1.638-14.527-0.983-1.592-1.502-3.496-2.25-3.716-0.664-0.181-1.396 1.028-1.432 1.159-0.198 0.352 3.558 6.594 3.785 15.549 0.07 1.13 1.296 2.183 1.535 1.535z"/>\r
+   <path fill="#414141" d="m271.424 212.625c1.047-2.75-0.139-11.223-1.522-13.518-0.93-1.471-1.367-3.238-2.094-3.459-0.635-0.178-1.282 0.962-1.333 1.079-0.229 0.338 3.153 6.114 3.521 14.47 0.083 1.106 1.205 2.03 1.428 1.428z"/>\r
+   <path fill="#444" d="m271.329 211.862c0.985-2.563-0.256-10.65-1.409-12.508-0.874-1.35-1.23-2.979-1.938-3.2-0.604-0.177-1.166 0.895-1.233 0.998-0.259 0.324 2.751 5.636 3.259 13.39 0.096 1.082 1.115 1.876 1.321 1.32z"/>\r
+   <path fill="#484848" d="m271.235 211.099c0.923-2.377-0.375-10.077-1.296-11.499-0.818-1.229-1.097-2.72-1.781-2.942-0.573-0.174-1.052 0.829-1.134 0.918-0.289 0.309 2.348 5.156 2.996 12.309 0.11 1.058 1.025 1.726 1.215 1.214z"/>\r
+   <path fill="#4c4c4c" d="m271.14 210.336c0.861-2.192-0.493-9.506-1.183-10.49-0.763-1.107-0.96-2.463-1.625-2.684-0.542-0.172-0.936 0.762-1.034 0.836-0.319 0.297 1.945 4.68 2.733 11.229 0.124 1.035 0.936 1.576 1.109 1.109z"/>\r
+  </g>\r
+  <g transform="translate(-12.4048,10.0005)">\r
+   <path d="m264.822 187.073c-10.224-13.968-23.472-18.504-22.104-14.112 0 0 10.152 5.76 19.08 16.56 1.728 2.088 4.608-0.288 3.024-2.448z"/>\r
+   <path fill="#030303" d="m264.372 186.687c-9.924-13.495-22.894-17.912-21.539-13.7 0.018 0.012 9.901 5.614 18.609 16.071 1.678 2.016 4.467-0.285 2.93-2.371z"/>\r
+   <path fill="#070707" d="m263.922 186.3c-9.624-13.022-22.316-17.319-20.975-13.287 0.036 0.023 9.652 5.467 18.139 15.582 1.628 1.943 4.325-0.283 2.836-2.295z"/>\r
+   <path fill="#0b0b0b" d="m263.472 185.913c-9.324-12.549-21.739-16.726-20.41-12.874 0.053 0.034 9.4 5.32 17.668 15.093 1.578 1.871 4.183-0.28 2.742-2.219z"/>\r
+   <path fill="#0f0f0f" d="m263.022 185.527c-9.024-12.077-21.162-16.134-19.847-12.462 0.071 0.045 9.151 5.174 17.198 14.604 1.529 1.798 4.043-0.278 2.649-2.142z"/>\r
+   <path fill="#131313" d="m262.571 185.14c-8.723-11.603-20.583-15.541-19.28-12.049 0.088 0.056 8.901 5.027 16.728 14.114 1.477 1.726 3.899-0.275 2.552-2.065z"/>\r
+   <path fill="#161616" d="m262.121 184.753c-8.423-11.13-20.006-14.948-18.716-11.636 0.106 0.067 8.651 4.88 16.257 13.625 1.428 1.654 3.759-0.272 2.459-1.989z"/>\r
+   <path fill="#1a1a1a" d="m261.671 184.367c-8.124-10.658-19.428-14.356-18.15-11.224 0.124 0.078 8.399 4.734 15.785 13.136 1.378 1.581 3.617-0.27 2.365-1.912z"/>\r
+   <path fill="#1e1e1e" d="m261.221 183.98c-7.824-10.185-18.851-13.763-17.588-10.811 0.143 0.089 8.151 4.587 15.315 12.647 1.33 1.508 3.477-0.267 2.273-1.836z"/>\r
+   <path fill="#222" d="m260.771 183.593c-7.524-9.711-18.273-13.17-17.022-10.398 0.159 0.1 7.9 4.44 14.844 12.158 1.279 1.436 3.335-0.265 2.178-1.76z"/>\r
+   <path fill="#262626" d="m260.321 183.206c-7.224-9.238-17.695-12.578-16.458-9.985 0.177 0.111 7.65 4.293 14.374 11.668 1.229 1.364 3.193-0.262 2.084-1.683z"/>\r
+   <path fill="#2a2a2a" d="m259.871 182.82c-6.924-8.766-17.118-11.986-15.893-9.573 0.193 0.122 7.398 4.147 13.902 11.179 1.18 1.291 3.053-0.259 1.991-1.606z"/>\r
+   <path fill="#2d2d2d" d="m259.42 182.433c-6.623-8.293-16.539-11.393-15.328-9.16 0.214 0.133 7.15 4 13.434 10.69 1.128 1.219 2.909-0.257 1.894-1.53z"/>\r
+   <path fill="#313131" d="m258.97 182.046c-6.323-7.819-15.963-10.8-14.764-8.747 0.23 0.144 6.899 3.853 12.962 10.201 1.08 1.146 2.769-0.254 1.802-1.454z"/>\r
+   <path fill="#353535" d="m258.52 181.66c-6.023-7.347-15.384-10.208-14.199-8.335 0.248 0.155 6.649 3.707 12.492 9.712 1.028 1.073 2.627-0.252 1.707-1.377z"/>\r
+   <path fill="#393939" d="m258.07 181.273c-5.723-6.874-14.807-9.615-13.634-7.922 0.265 0.166 6.398 3.56 12.021 9.222 0.978 1.002 2.485-0.249 1.613-1.3z"/>\r
+   <path fill="#3d3d3d" d="m257.62 180.886c-5.423-6.401-14.229-9.021-13.07-7.509 0.283 0.177 6.149 3.413 11.552 8.733 0.927 0.929 2.343-0.246 1.518-1.224z"/>\r
+   <path fill="#414141" d="m257.17 180.5c-5.124-5.928-13.65-8.43-12.505-7.097 0.301 0.188 5.898 3.267 11.079 8.244 0.879 0.856 2.203-0.244 1.426-1.147z"/>\r
+   <path fill="#444" d="m256.719 180.113c-4.823-5.455-13.073-7.837-11.94-6.684 0.319 0.199 5.649 3.12 10.609 7.755 0.829 0.784 2.061-0.241 1.331-1.071z"/>\r
+   <path fill="#484848" d="m256.269 179.726c-4.523-4.982-12.495-7.244-11.375-6.271 0.336 0.21 5.397 2.973 10.138 7.266 0.779 0.711 1.92-0.239 1.237-0.995z"/>\r
+   <path fill="#4c4c4c" d="m255.819 179.339c-4.223-4.509-11.918-6.652-10.812-5.859 0.354 0.222 5.148 2.827 9.668 6.777 0.73 0.639 1.779-0.236 1.144-0.918z"/>\r
+  </g>\r
+  <g transform="translate(-12.4048,10.0005)">\r
+   <path d="m273.03 225.592c0.144 6.265-5.76 22.248-7.992 21.673-2.52-0.576 0.504-5.257 2.809-13.177 0.936-3.312 1.655-11.447 1.943-11.735 0.936-0.936 3.24 1.728 3.24 3.239z"/>\r
+   <path fill="#050505" d="m272.846 226.116c0.103 6.082-5.638 21.594-7.797 21.018-2.422-0.567 0.548-5.146 2.814-12.928 0.899-3.178 1.595-10.947 1.887-11.25 0.914-0.927 3.147 1.527 3.096 3.16z"/>\r
+   <path fill="#0a0a0a" d="m272.662 226.637c0.063 5.902-5.514 20.939-7.601 20.363-2.323-0.558 0.592-5.035 2.82-12.681 0.861-3.041 1.534-10.446 1.83-10.761 0.89-0.917 3.054 1.327 2.951 3.079z"/>\r
+   <path fill="#0f0f0f" d="m272.478 227.159c0.021 5.721-5.392 20.284-7.405 19.711-2.224-0.55 0.635-4.927 2.827-12.434 0.824-2.907 1.473-9.945 1.773-10.273 0.866-0.911 2.959 1.125 2.805 2.996z"/>\r
+   <path fill="#141414" d="m272.294 227.68c-0.02 5.539-5.268 19.63-7.21 19.057-2.125-0.541 0.68-4.816 2.835-12.186 0.786-2.771 1.412-9.445 1.717-9.786 0.84-0.901 2.863 0.924 2.658 2.915z"/>\r
+   <path fill="#191919" d="m272.11 228.202c-0.063 5.359-5.146 18.977-7.014 18.403-2.027-0.532 0.722-4.706 2.841-11.938 0.748-2.637 1.351-8.944 1.659-9.299 0.818-0.893 2.77 0.722 2.514 2.834z"/>\r
+   <path fill="#1e1e1e" d="m271.926 228.723c-0.103 5.179-5.021 18.322-6.818 17.75-1.928-0.523 0.766-4.596 2.848-11.691 0.711-2.5 1.29-8.443 1.603-8.811 0.792-0.885 2.674 0.522 2.367 2.752z"/>\r
+   <path fill="#232323" d="m271.742 229.245c-0.144 4.998-4.9 17.668-6.623 17.096-1.83-0.514 0.811-4.485 2.854-11.442 0.673-2.366 1.229-7.944 1.545-8.325 0.771-0.876 2.583 0.32 2.224 2.671z"/>\r
+   <path fill="#282828" d="m271.558 229.766c-0.186 4.816-4.777 17.013-6.428 16.443-1.731-0.506 0.854-4.377 2.86-11.196 0.636-2.231 1.168-7.443 1.488-7.837 0.749-0.866 2.49 0.119 2.08 2.59z"/>\r
+   <path fill="#2d2d2d" d="m271.374 230.288c-0.226 4.637-4.653 16.359-6.231 15.789-1.632-0.496 0.896-4.266 2.866-10.947 0.6-2.098 1.107-6.943 1.433-7.351 0.722-0.859 2.393-0.081 1.932 2.509z"/>\r
+   <path fill="#333" d="m271.19 230.809c-0.268 4.456-4.531 15.705-6.036 15.136-1.534-0.489 0.94-4.155 2.873-10.7 0.561-1.961 1.046-6.443 1.375-6.863 0.7-0.848 2.3-0.282 1.788 2.427z"/>\r
+   <path fill="#383838" d="m271.006 231.331c-0.308 4.275-4.407 15.051-5.841 14.482-1.435-0.48 0.984-4.046 2.88-10.453 0.524-1.826 0.985-5.941 1.318-6.375 0.676-0.84 2.206-0.483 1.643 2.346z"/>\r
+   <path fill="#3d3d3d" d="m270.822 231.853c-0.35 4.093-4.285 14.396-5.645 13.828-1.338-0.472 1.027-3.937 2.886-10.206 0.485-1.691 0.924-5.441 1.261-5.887 0.653-0.832 2.113-0.684 1.498 2.265z"/>\r
+   <path fill="#424242" d="m270.638 232.374c-0.392 3.914-4.162 13.742-5.45 13.176-1.238-0.463 1.072-3.826 2.893-9.959 0.449-1.555 0.863-4.94 1.204-5.399 0.629-0.824 2.019-0.886 1.353 2.182z"/>\r
+   <path fill="#474747" d="m270.454 232.896c-0.432 3.731-4.039 13.087-5.254 12.521-1.14-0.453 1.115-3.715 2.9-9.711 0.411-1.42 0.802-4.439 1.146-4.912 0.606-0.815 1.925-1.086 1.208 2.102z"/>\r
+   <path fill="#4c4c4c" d="m270.27 233.417c-0.474 3.553-3.916 12.434-5.06 11.869-1.041-0.445 1.159-3.606 2.907-9.463 0.373-1.287 0.741-3.941 1.089-4.427 0.583-0.806 1.832-1.287 1.064 2.021z"/>\r
+   <path fill="#515151" d="m270.086 233.939c-0.514 3.37-3.793 11.778-4.862 11.214-0.942-0.436 1.201-3.496 2.913-9.216 0.336-1.151 0.68-3.438 1.032-3.938 0.558-0.797 1.736-1.489 0.917 1.94z"/>\r
+   <path fill="#565656" d="m269.902 234.461c-0.555 3.188-3.671 11.123-4.667 10.56-0.844-0.429 1.246-3.386 2.919-8.968 0.298-1.016 0.619-2.939 0.976-3.451 0.535-0.788 1.643-1.689 0.772 1.859z"/>\r
+   <path fill="#5b5b5b" d="m269.718 234.982c-0.597 3.009-3.548 10.47-4.472 9.907-0.745-0.42 1.29-3.275 2.926-8.721 0.262-0.881 0.559-2.438 0.919-2.963 0.511-0.781 1.549-1.89 0.627 1.777z"/>\r
+   <path fill="#606060" d="m269.534 235.504c-0.638 2.828-3.425 9.814-4.276 9.252-0.646-0.409 1.333-3.166 2.933-8.473 0.224-0.746 0.497-1.938 0.862-2.476 0.487-0.769 1.454-2.09 0.481 1.697z"/>\r
+   <path fill="#666" d="m269.35 236.025c-0.68 2.647-3.303 9.161-4.081 8.599-0.548-0.4 1.377-3.056 2.938-8.225 0.187-0.611 0.437-1.438 0.806-1.988 0.464-0.763 1.361-2.293 0.337 1.614z"/>\r
+  </g>\r
+  <g transform="translate(-12.4048,10.0005)">\r
+   <path d="m251.07 187.865c-1.537 1.622-2.903 9.991 0.938 12.893 3.844 2.818 10.59-2.391 10.59-5.379-0.086-6.746-9.991-9.222-11.528-7.514z"/>\r
+   <path fill="#010101" d="m251.207 188.006c-1.559 1.611-2.876 9.823 0.857 12.667 3.731 2.764 10.349-2.273 10.384-5.279-0.047-6.576-9.681-9.083-11.241-7.388z"/>\r
+   <path fill="#030303" d="m251.344 188.146c-1.582 1.601-2.85 9.653 0.774 12.438 3.62 2.709 10.109-2.154 10.178-5.177-0.007-6.404-9.37-8.943-10.952-7.261z"/>\r
+   <path fill="#050505" d="m251.481 188.287c-1.604 1.589-2.823 9.484 0.691 12.211 3.511 2.653 9.869-2.037 9.975-5.078 0.031-6.232-9.061-8.802-10.666-7.133z"/>\r
+   <path fill="#070707" d="m251.617 188.427c-1.626 1.579-2.795 9.316 0.611 11.984 3.397 2.6 9.629-1.918 9.768-4.976 0.071-6.062-8.751-8.664-10.379-7.008z"/>\r
+   <path fill="#090909" d="m251.754 188.567c-1.648 1.568-2.768 9.146 0.529 11.758 3.287 2.543 9.389-1.802 9.563-4.875 0.109-5.892-8.441-8.525-10.092-6.883z"/>\r
+   <path fill="#0b0b0b" d="m251.891 188.708c-1.671 1.557-2.741 8.978 0.445 11.529 3.177 2.489 9.15-1.683 9.358-4.774 0.15-5.72-8.13-8.385-9.803-6.755z"/>\r
+   <path fill="#0d0d0d" d="m252.028 188.848c-1.694 1.546-2.715 8.809 0.364 11.302 3.064 2.435 8.908-1.565 9.152-4.673 0.189-5.549-7.82-8.245-9.516-6.629z"/>\r
+   <path fill="#0f0f0f" d="m252.165 188.989c-1.716 1.535-2.688 8.64 0.282 11.074 2.953 2.38 8.669-1.447 8.948-4.572 0.226-5.378-7.512-8.106-9.23-6.502z"/>\r
+   <path fill="#111" d="m252.301 189.129c-1.737 1.524-2.659 8.471 0.2 10.847 2.844 2.325 8.431-1.33 8.743-4.471 0.266-5.207-7.201-7.966-8.943-6.376z"/>\r
+   <path fill="#131313" d="m252.438 189.269c-1.76 1.514-2.633 8.304 0.118 10.619 2.73 2.271 8.189-1.212 8.538-4.369 0.305-5.036-6.892-7.827-8.656-6.25z"/>\r
+   <path fill="#151515" d="m252.575 189.41c-1.783 1.503-2.606 8.133 0.036 10.391 2.62 2.216 7.949-1.094 8.332-4.268 0.344-4.865-6.581-7.687-8.368-6.123z"/>\r
+   <path fill="#161616" d="m252.712 189.55c-1.805 1.492-2.58 7.965-0.046 10.164 2.508 2.162 7.709-0.975 8.127-4.167 0.383-4.694-6.272-7.548-8.081-5.997z"/>\r
+   <path fill="#181818" d="m252.849 189.691c-1.828 1.481-2.554 7.796-0.129 9.937 2.397 2.105 7.47-0.857 7.922-4.066 0.423-4.524-5.961-7.409-7.793-5.871z"/>\r
+   <path fill="#1a1a1a" d="m252.985 189.831c-1.85 1.47-2.525 7.626-0.21 9.708 2.286 2.053 7.229-0.74 7.717-3.964 0.461-4.352-5.652-7.269-7.507-5.744z"/>\r
+   <path fill="#1c1c1c" d="m253.122 189.971c-1.872 1.46-2.499 7.459-0.292 9.482 2.175 1.996 6.989-0.623 7.511-3.865 0.501-4.18-5.341-7.128-7.219-5.617z"/>\r
+   <path fill="#1e1e1e" d="m253.259 190.112c-1.895 1.448-2.472 7.289-0.375 9.254 2.064 1.942 6.75-0.504 7.308-3.763 0.539-4.01-5.033-6.99-6.933-5.491z"/>\r
+   <path fill="#202020" d="m253.396 190.252c-1.917 1.438-2.445 7.122-0.457 9.027 1.953 1.888 6.51-0.386 7.102-3.662 0.578-3.839-4.722-6.85-6.645-5.365z"/>\r
+   <path fill="#222" d="m253.533 190.393c-1.939 1.426-2.418 6.951-0.539 8.799 1.841 1.832 6.271-0.268 6.896-3.561 0.618-3.668-4.412-6.711-6.357-5.238z"/>\r
+   <path fill="#242424" d="m253.669 190.533c-1.961 1.416-2.391 6.783-0.621 8.572 1.731 1.776 6.03-0.149 6.692-3.46 0.657-3.497-4.102-6.571-6.071-5.112z"/>\r
+   <path fill="#262626" d="m253.806 190.673c-1.984 1.405-2.364 6.615-0.703 8.344 1.619 1.724 5.79-0.032 6.485-3.358 0.697-3.326-3.791-6.432-5.782-4.986z"/>\r
+  </g>\r
+  <g transform="translate(-12.4048,10.0005)">\r
+   <path d="m250.71 256.698c1.513 1.512 2.809-2.232 4.32-3.457 1.512-1.224 3.96-3.888 8.856-3.888s4.535-0.144 4.319-2.017c-0.144-1.799-1.584-1.655-5.903-1.008-4.32 0.576-7.2 2.809-8.929 4.824-1.654 1.945-3.527 4.681-2.663 5.546z"/>\r
+   <path fill="#050505" d="m251.043 256.331c1.459 1.449 2.703-2.121 4.205-3.308 1.501-1.187 3.931-3.731 8.64-3.731 4.71-0.002 4.415-0.129 4.209-1.94-0.139-1.743-1.543-1.593-5.749-0.979-4.207 0.543-7.029 2.703-8.712 4.648-1.616 1.877-3.438 4.474-2.593 5.31z"/>\r
+   <path fill="#0a0a0a" d="m251.376 255.961c1.406 1.389 2.6-2.008 4.089-3.156 1.491-1.148 3.901-3.576 8.425-3.577 4.521-0.001 4.293-0.11 4.096-1.862-0.132-1.688-1.501-1.531-5.594-0.951-4.093 0.51-6.857 2.596-8.495 4.471-1.575 1.812-3.348 4.271-2.521 5.075z"/>\r
+   <path fill="#0f0f0f" d="m251.709 255.594c1.354 1.326 2.494-1.895 3.975-3.008 1.479-1.111 3.871-3.42 8.207-3.422s4.171-0.094 3.984-1.785c-0.126-1.631-1.46-1.467-5.438-0.924-3.979 0.479-6.687 2.492-8.28 4.297-1.533 1.747-3.257 4.066-2.448 4.842z"/>\r
+   <path fill="#141414" d="m252.042 255.226c1.302 1.265 2.39-1.783 3.858-2.856 1.47-1.076 3.842-3.266 7.991-3.268s4.05-0.077 3.873-1.709c-0.119-1.575-1.419-1.404-5.284-0.895-3.865 0.444-6.515 2.385-8.063 4.121-1.49 1.678-3.165 3.861-2.375 4.607z"/>\r
+   <path fill="#191919" d="m252.374 254.859c1.249 1.202 2.285-1.671 3.744-2.708 1.458-1.037 3.813-3.109 7.774-3.111 3.963-0.004 3.929-0.061 3.761-1.633-0.113-1.519-1.377-1.341-5.128-0.867-3.751 0.414-6.344 2.279-7.847 3.945-1.449 1.613-3.075 3.656-2.304 4.374z"/>\r
+   <path fill="#1e1e1e" d="m252.707 254.491c1.196 1.141 2.182-1.559 3.628-2.558 1.448-1 3.783-2.954 7.56-2.957 3.775-0.003 3.806-0.043 3.648-1.556-0.106-1.461-1.336-1.277-4.974-0.838-3.637 0.379-6.172 2.174-7.63 3.77-1.408 1.546-2.984 3.451-2.232 4.139z"/>\r
+   <path fill="#232323" d="m253.04 254.124c1.144 1.078 2.076-1.447 3.514-2.41 1.437-0.961 3.753-2.797 7.342-2.799 3.589-0.004 3.685-0.027 3.537-1.48-0.101-1.405-1.294-1.215-4.818-0.811-3.524 0.349-6.001 2.068-7.415 3.594-1.367 1.48-2.894 3.245-2.16 3.906z"/>\r
+   <path fill="#282828" d="m253.373 253.754c1.09 1.018 1.972-1.334 3.398-2.258 1.426-0.926 3.723-2.642 7.125-2.646 3.402-0.005 3.563-0.011 3.426-1.403-0.095-1.349-1.253-1.15-4.664-0.781-3.409 0.314-5.829 1.961-7.198 3.418-1.325 1.415-2.803 3.041-2.087 3.67z"/>\r
+   <path fill="#2d2d2d" d="m253.706 253.388c1.038 0.954 1.866-1.222 3.282-2.11 1.416-0.887 3.694-2.486 6.909-2.49 3.217-0.004 3.441 0.008 3.313-1.326-0.088-1.293-1.212-1.088-4.508-0.754-3.296 0.283-5.658 1.857-6.981 3.244-1.284 1.345-2.712 2.836-2.015 3.436z"/>\r
+   <path fill="#333" d="m254.039 253.02c0.985 0.893 1.762-1.109 3.167-1.96s3.664-2.33 6.693-2.335c3.029-0.006 3.32 0.023 3.202-1.249-0.082-1.237-1.171-1.024-4.354-0.726-3.182 0.25-5.485 1.75-6.765 3.066-1.243 1.282-2.622 2.634-1.943 3.204z"/>\r
+   <path fill="#383838" d="m254.372 252.652c0.933 0.831 1.657-0.998 3.051-1.81 1.396-0.813 3.636-2.174 6.478-2.18 2.843-0.006 3.199 0.039 3.09-1.172-0.076-1.181-1.129-0.963-4.198-0.697-3.068 0.217-5.314 1.644-6.55 2.891-1.202 1.214-2.531 2.427-1.871 2.968z"/>\r
+   <path fill="#3d3d3d" d="m254.704 252.284c0.88 0.771 1.553-0.885 2.938-1.66 1.383-0.775 3.604-2.019 6.26-2.024 2.656-0.007 3.078 0.058 2.979-1.095-0.069-1.125-1.088-0.899-4.044-0.67-2.954 0.185-5.144 1.539-6.333 2.715-1.16 1.148-2.441 2.222-1.8 2.734z"/>\r
+   <path fill="#424242" d="m255.037 251.917c0.827 0.707 1.448-0.773 2.821-1.51 1.373-0.738 3.576-1.863 6.044-1.871 2.47-0.007 2.956 0.074 2.867-1.018-0.063-1.068-1.046-0.836-3.889-0.641-2.841 0.151-4.973 1.433-6.116 2.539-1.119 1.083-2.35 2.018-1.727 2.501z"/>\r
+   <path fill="#474747" d="m255.37 251.549c0.774 0.645 1.344-0.661 2.706-1.362 1.362-0.7 3.545-1.706 5.828-1.713 2.283-0.009 2.834 0.09 2.754-0.942-0.057-1.012-1.005-0.773-3.733-0.613-2.727 0.119-4.801 1.328-5.899 2.365-1.078 1.013-2.26 1.81-1.656 2.265z"/>\r
+   <path fill="#4c4c4c" d="m255.703 251.181c0.721 0.584 1.239-0.55 2.59-1.212 1.353-0.663 3.517-1.551 5.612-1.559 2.096-0.009 2.713 0.107 2.643-0.865-0.051-0.955-0.964-0.709-3.577-0.584-2.613 0.086-4.631 1.222-5.686 2.188-1.035 0.949-2.168 1.607-1.582 2.032z"/>\r
+   <path fill="#515151" d="m256.036 250.813c0.669 0.521 1.134-0.436 2.476-1.063 1.341-0.625 3.485-1.395 5.395-1.402 1.91-0.01 2.591 0.124 2.531-0.789-0.044-0.898-0.922-0.646-3.423-0.557-2.499 0.055-4.458 1.117-5.469 2.014-0.994 0.882-2.077 1.402-1.51 1.797z"/>\r
+   <path fill="#565656" d="m256.369 250.446c0.616 0.459 1.029-0.324 2.36-0.912 1.33-0.589 3.456-1.24 5.178-1.248 1.723-0.01 2.47 0.141 2.42-0.713-0.039-0.842-0.881-0.582-3.268-0.527-2.387 0.021-4.287 1.01-5.252 1.836-0.953 0.816-1.987 1.199-1.438 1.564z"/>\r
+   <path fill="#5b5b5b" d="m256.701 250.079c0.564 0.397 0.926-0.213 2.245-0.764s3.427-1.084 4.963-1.093c1.536-0.011 2.348 0.157 2.307-0.636-0.031-0.785-0.839-0.52-3.112-0.5-2.271-0.01-4.116 0.906-5.035 1.662-0.913 0.751-1.898 0.993-1.368 1.331z"/>\r
+   <path fill="#606060" d="m257.034 249.709c0.511 0.336 0.821-0.1 2.13-0.612 1.309-0.515 3.397-0.929 4.746-0.938 1.351-0.01 2.227 0.176 2.196-0.558-0.026-0.729-0.799-0.457-2.958-0.472-2.158-0.043-3.945 0.799-4.82 1.486-0.87 0.682-1.805 0.788-1.294 1.094z"/>\r
+   <path fill="#666" d="m257.367 249.342c0.458 0.273 0.716 0.012 2.014-0.465 1.299-0.476 3.368-0.771 4.53-0.781 1.163-0.012 2.105 0.191 2.084-0.482-0.02-0.673-0.757-0.393-2.803-0.443-2.044-0.076-3.773 0.693-4.604 1.311-0.828 0.616-1.714 0.582-1.221 0.86z"/>\r
+  </g>\r
+  <g transform="translate(-12.4048,10.0005)">\r
+   <path d="m270.222 247.265c0 2.304 4.68 3.096 9.144 3.743 4.392 0.648 7.92 1.513 8.136 6.121 0.216 4.535-0.936 7.775 1.08 7.416 4.32-0.793 5.904-5.473 5.832-7.633 0-2.16-3.168-6.047-8.855-8.207-4.177-1.584-7.2-2.305-10.872-2.449-4.897-0.214-4.465 1.009-4.465 1.009z"/>\r
+   <path fill="#030303" d="m270.348 247.304c0.012 2.239 4.643 2.97 9.049 3.639 4.352 0.675 7.75 1.511 8.11 6.002 0.345 4.415-0.854 7.515 1.136 7.188 4.145-0.739 5.688-5.2 5.607-7.332-0.015-2.151-3.118-5.91-8.733-8.038-4.129-1.563-7.11-2.298-10.74-2.452-4.754-0.217-4.438 0.952-4.429 0.993z"/>\r
+   <path fill="#070707" d="m270.474 247.342c0.023 2.174 4.605 2.845 8.953 3.533 4.312 0.701 7.58 1.508 8.087 5.885 0.471 4.295-0.771 7.252 1.189 6.963 3.97-0.689 5.472-4.93 5.384-7.033-0.028-2.145-3.068-5.773-8.61-7.87-4.082-1.543-7.022-2.291-10.609-2.456-4.612-0.218-4.412 0.896-4.394 0.978z"/>\r
+   <path fill="#0b0b0b" d="m270.6 247.379c0.035 2.109 4.568 2.721 8.857 3.431 4.271 0.728 7.411 1.506 8.063 5.767 0.599 4.174-0.689 6.988 1.245 6.735 3.795-0.638 5.257-4.66 5.159-6.733-0.044-2.137-3.019-5.636-8.488-7.701-4.035-1.522-6.933-2.285-10.478-2.459-4.469-0.219-4.384 0.837-4.358 0.96z"/>\r
+   <path fill="#0f0f0f" d="m270.726 247.418c0.047 2.043 4.531 2.595 8.762 3.324 4.232 0.754 7.242 1.504 8.039 5.649 0.725 4.052-0.608 6.728 1.299 6.509 3.621-0.586 5.041-4.389 4.937-6.436-0.06-2.127-2.971-5.496-8.367-7.53-3.988-1.502-6.842-2.278-10.346-2.464-4.328-0.22-4.359 0.784-4.324 0.948z"/>\r
+   <path fill="#131313" d="m270.852 247.458c0.059 1.978 4.495 2.469 8.667 3.219 4.19 0.781 7.071 1.502 8.014 5.531 0.854 3.932-0.526 6.465 1.354 6.283 3.445-0.535 4.824-4.119 4.712-6.137-0.074-2.119-2.92-5.359-8.245-7.361-3.941-1.482-6.753-2.271-10.213-2.469-4.186-0.221-4.333 0.726-4.289 0.934z"/>\r
+   <path fill="#161616" d="m270.978 247.495c0.07 1.914 4.458 2.344 8.57 3.115 4.151 0.807 6.903 1.5 7.99 5.413 0.98 3.812-0.443 6.202 1.409 6.056 3.271-0.482 4.609-3.848 4.488-5.836-0.088-2.111-2.871-5.222-8.123-7.193-3.894-1.461-6.663-2.264-10.081-2.471-4.043-0.223-4.306 0.67-4.253 0.916z"/>\r
+   <path fill="#1a1a1a" d="m271.104 247.534c0.082 1.848 4.422 2.219 8.476 3.01 4.111 0.832 6.734 1.498 7.965 5.295 1.108 3.69-0.36 5.94 1.464 5.83 3.097-0.433 4.394-3.578 4.265-5.537-0.104-2.104-2.821-5.084-8-7.023-3.848-1.441-6.575-2.26-9.949-2.477-3.905-0.223-4.283 0.613-4.221 0.902z"/>\r
+   <path fill="#1e1e1e" d="m271.23 247.573c0.094 1.781 4.385 2.092 8.381 2.904 4.07 0.859 6.563 1.495 7.939 5.178 1.236 3.568-0.278 5.678 1.52 5.602 2.921-0.381 4.177-3.305 4.04-5.237-0.118-2.097-2.771-4.946-7.878-6.854-3.8-1.42-6.485-2.252-9.817-2.479-3.762-0.226-4.256 0.556-4.185 0.886z"/>\r
+   <path fill="#222" d="m271.356 247.61c0.105 1.717 4.348 1.969 8.285 2.801 4.029 0.885 6.395 1.493 7.916 5.059 1.362 3.449-0.197 5.416 1.573 5.377 2.746-0.329 3.962-3.036 3.816-4.939-0.133-2.087-2.722-4.808-7.756-6.684-3.753-1.4-6.396-2.247-9.686-2.484-3.618-0.227-4.227 0.499-4.148 0.87z"/>\r
+   <path fill="#262626" d="m271.482 247.648c0.118 1.651 4.311 1.843 8.189 2.694 3.99 0.914 6.226 1.492 7.892 4.943 1.491 3.328-0.115 5.152 1.629 5.149 2.571-0.278 3.746-2.765 3.592-4.64-0.146-2.08-2.672-4.672-7.634-6.516-3.705-1.38-6.306-2.24-9.553-2.488-3.478-0.225-4.203 0.446-4.115 0.858z"/>\r
+   <path fill="#2a2a2a" d="m271.608 247.687c0.129 1.587 4.273 1.716 8.094 2.59 3.95 0.938 6.056 1.489 7.867 4.825 1.618 3.206-0.033 4.891 1.684 4.922 2.396-0.227 3.53-2.494 3.368-4.34-0.162-2.072-2.623-4.534-7.512-6.348-3.658-1.358-6.216-2.232-9.421-2.492-3.336-0.226-4.177 0.39-4.08 0.843z"/>\r
+   <path fill="#2d2d2d" d="m271.734 247.725c0.141 1.521 4.237 1.591 7.999 2.484 3.909 0.967 5.886 1.488 7.842 4.707 1.746 3.086 0.05 4.629 1.739 4.697 2.221-0.176 3.313-2.225 3.144-4.041-0.177-2.064-2.572-4.396-7.389-6.178-3.612-1.339-6.128-2.227-9.29-2.497-3.194-0.227-4.151 0.334-4.045 0.828z"/>\r
+   <path fill="#313131" d="m271.86 247.763c0.153 1.456 4.2 1.466 7.903 2.381 3.869 0.991 5.717 1.485 7.817 4.589 1.873 2.965 0.132 4.365 1.794 4.469 2.046-0.123 3.099-1.953 2.92-3.742-0.191-2.055-2.523-4.258-7.267-6.008-3.565-1.318-6.038-2.22-9.158-2.5-3.051-0.229-4.124 0.276-4.009 0.811z"/>\r
+   <path fill="#353535" d="m271.986 247.801c0.165 1.392 4.163 1.341 7.808 2.275 3.829 1.018 5.547 1.483 7.794 4.473 2 2.843 0.213 4.103 1.848 4.242 1.873-0.074 2.883-1.683 2.696-3.443-0.206-2.047-2.474-4.12-7.145-5.838-3.517-1.299-5.948-2.215-9.026-2.506-2.91-0.229-4.099 0.221-3.975 0.797z"/>\r
+   <path fill="#393939" d="m272.112 247.84c0.176 1.326 4.126 1.215 7.712 2.17 3.789 1.045 5.378 1.482 7.771 4.354 2.127 2.723 0.295 3.842 1.901 4.016 1.698-0.021 2.667-1.412 2.474-3.144-0.222-2.038-2.426-3.981-7.023-5.669-3.47-1.277-5.859-2.208-8.894-2.509-2.769-0.231-4.074 0.164-3.941 0.782z"/>\r
+   <path fill="#3d3d3d" d="m272.238 247.877c0.188 1.262 4.089 1.09 7.617 2.066 3.749 1.071 5.208 1.479 7.745 4.236 2.255 2.602 0.377 3.578 1.957 3.789 1.522 0.029 2.451-1.141 2.249-2.844-0.236-2.033-2.375-3.846-6.901-5.5-3.423-1.258-5.769-2.203-8.762-2.514-2.626-0.231-4.046 0.109-3.905 0.767z"/>\r
+   <path fill="#414141" d="m272.364 247.917c0.2 1.195 4.052 0.965 7.522 1.961 3.708 1.098 5.037 1.477 7.72 4.119 2.383 2.479 0.46 3.315 2.012 3.562 1.348 0.081 2.236-0.87 2.025-2.545-0.251-2.022-2.325-3.707-6.778-5.331-3.377-1.237-5.681-2.195-8.631-2.518-2.485-0.233-4.02 0.05-3.87 0.752z"/>\r
+   <path fill="#444" d="m272.49 247.956c0.212 1.129 4.015 0.838 7.426 1.855 3.668 1.124 4.869 1.475 7.696 4 2.51 2.359 0.542 3.055 2.067 3.336 1.173 0.133 2.02-0.6 1.801-2.246-0.266-2.016-2.276-3.568-6.656-5.16-3.329-1.218-5.591-2.189-8.499-2.521-2.343-0.235-3.994-0.007-3.835 0.736z"/>\r
+   <path fill="#484848" d="m272.616 247.993c0.223 1.065 3.979 0.715 7.331 1.752 3.628 1.15 4.699 1.473 7.671 3.883 2.638 2.238 0.624 2.791 2.122 3.108 0.998 0.185 1.804-0.329 1.577-1.946-0.28-2.008-2.227-3.432-6.534-4.992-3.282-1.196-5.501-2.182-8.367-2.525-2.201-0.235-3.968-0.064-3.8 0.72z"/>\r
+   <path fill="#4c4c4c" d="m272.742 248.032c0.235 1 3.941 0.588 7.235 1.645 3.588 1.178 4.529 1.472 7.646 3.766 2.766 2.118 0.706 2.529 2.177 2.883 0.823 0.235 1.589-0.059 1.354-1.648-0.295-1.998-2.177-3.293-6.412-4.822-3.235-1.176-5.412-2.176-8.235-2.529-2.059-0.239-3.942-0.119-3.765 0.705z"/>\r
+  </g>\r
+  <g transform="translate(-12.4048,10.0005)">\r
+   <path fill="#4c4c4c" d="m287.565 252.854c1.646 1 1.353 2.059 2.412 2.766 0.528 0.352 1.412 0.352 0.882-1-0.706-1.588-1.294-2.472-4.941-3.943-2.353-0.941-1.882 0.059 1.647 2.177z"/>\r
+   <path fill="#505050" d="m287.609 252.868c1.605 0.975 1.32 2.008 2.353 2.696 0.517 0.343 1.377 0.343 0.86-0.976-0.689-1.549-1.262-2.41-4.82-3.846-2.295-0.917-1.836 0.059 1.607 2.126z"/>\r
+   <path fill="#545454" d="m287.652 252.879c1.567 0.951 1.287 1.957 2.294 2.629 0.503 0.334 1.343 0.334 0.839-0.951-0.671-1.51-1.23-2.35-4.699-3.749-2.238-0.893-1.79 0.058 1.566 2.071z"/>\r
+   <path fill="#575757" d="m287.696 252.891c1.526 0.926 1.254 1.908 2.235 2.563 0.489 0.325 1.308 0.325 0.816-0.928-0.653-1.471-1.199-2.289-4.578-3.652-2.179-0.872-1.743 0.055 1.527 2.017z"/>\r
+   <path fill="#5b5b5b" d="m287.74 252.903c1.485 0.902 1.22 1.857 2.175 2.494 0.479 0.318 1.274 0.318 0.796-0.902-0.637-1.432-1.167-2.229-4.457-3.556-2.122-0.849-1.697 0.054 1.486 1.964z"/>\r
+   <path fill="#5f5f5f" d="m287.783 252.915c1.446 0.879 1.188 1.808 2.117 2.426 0.464 0.31 1.239 0.31 0.773-0.877-0.619-1.394-1.136-2.168-4.336-3.459-2.064-0.826-1.65 0.052 1.446 1.91z"/>\r
+   <path fill="#636363" d="m287.827 252.926c1.405 0.854 1.154 1.758 2.057 2.359 0.452 0.301 1.205 0.301 0.754-0.853-0.603-1.354-1.104-2.108-4.216-3.363-2.007-0.801-1.605 0.053 1.405 1.857z"/>\r
+   <path fill="#676767" d="m287.871 252.94c1.364 0.828 1.121 1.705 1.998 2.29 0.438 0.292 1.17 0.292 0.731-0.828-0.585-1.315-1.072-2.047-4.095-3.267-1.948-0.779-1.558 0.05 1.366 1.805z"/>\r
+   <path fill="#6b6b6b" d="m287.914 252.952c1.325 0.805 1.088 1.655 1.94 2.223 0.425 0.283 1.135 0.283 0.709-0.803-0.568-1.277-1.041-1.988-3.974-3.17-1.891-0.757-1.512 0.047 1.325 1.75z"/>\r
+   <path fill="#6e6e6e" d="m287.958 252.963c1.284 0.779 1.056 1.605 1.88 2.156 0.413 0.274 1.102 0.274 0.688-0.779-0.551-1.238-1.009-1.926-3.853-3.073-1.833-0.733-1.466 0.046 1.285 1.696z"/>\r
+   <path fill="#727272" d="m288.002 252.976c1.243 0.755 1.021 1.554 1.821 2.087 0.399 0.266 1.066 0.266 0.666-0.755-0.533-1.199-0.977-1.865-3.731-2.976-1.776-0.71-1.421 0.045 1.244 1.644z"/>\r
+   <path fill="#767676" d="m288.045 252.989c1.203 0.73 0.989 1.504 1.763 2.02 0.387 0.257 1.031 0.257 0.645-0.731-0.516-1.159-0.946-1.805-3.61-2.879-1.72-0.688-1.376 0.042 1.202 1.59z"/>\r
+   <path fill="#7a7a7a" d="m288.089 253c1.163 0.705 0.955 1.453 1.703 1.951 0.373 0.25 0.997 0.25 0.623-0.705-0.499-1.121-0.914-1.744-3.489-2.783-1.661-0.664-1.329 0.041 1.163 1.537z"/>\r
+   <path fill="#7e7e7e" d="m288.133 253.012c1.122 0.682 0.923 1.404 1.644 1.885 0.361 0.24 0.962 0.24 0.602-0.682-0.481-1.082-0.882-1.684-3.367-2.687-1.605-0.64-1.285 0.041 1.121 1.484z"/>\r
+   <path fill="#828282" d="m288.176 253.025c1.082 0.657 0.89 1.353 1.586 1.815 0.348 0.232 0.927 0.232 0.578-0.656-0.463-1.043-0.85-1.623-3.245-2.59-1.547-0.618-1.237 0.039 1.081 1.431z"/>\r
+   <path fill="#858585" d="m288.22 253.038c1.042 0.631 0.855 1.301 1.524 1.748 0.335 0.223 0.894 0.223 0.559-0.633-0.446-1.005-0.818-1.563-3.125-2.492-1.488-0.596-1.19 0.037 1.042 1.377z"/>\r
+   <path fill="#898989" d="m288.264 253.049c1.001 0.607 0.821 1.252 1.466 1.681 0.322 0.214 0.857 0.214 0.536-0.608-0.43-0.965-0.786-1.502-3.004-2.396-1.43-0.573-1.144 0.034 1.002 1.323z"/>\r
+   <path fill="#8d8d8d" d="m288.307 253.061c0.961 0.584 0.79 1.201 1.407 1.613 0.309 0.205 0.823 0.205 0.515-0.584-0.412-0.926-0.755-1.441-2.883-2.299-1.373-0.548-1.098 0.034 0.961 1.27z"/>\r
+   <path fill="#919191" d="m288.351 253.073c0.921 0.559 0.756 1.151 1.348 1.547 0.296 0.196 0.789 0.196 0.493-0.56-0.395-0.888-0.723-1.381-2.762-2.204-1.315-0.525-1.052 0.033 0.921 1.217z"/>\r
+   <path fill="#959595" d="m288.395 253.084c0.88 0.535 0.723 1.102 1.289 1.479 0.282 0.189 0.754 0.189 0.471-0.534-0.377-0.849-0.691-1.321-2.641-2.106-1.257-0.505-1.006 0.031 0.881 1.161z"/>\r
+   <path fill="#999" d="m288.438 253.097c0.84 0.51 0.689 1.05 1.229 1.409 0.271 0.181 0.721 0.181 0.45-0.51-0.36-0.81-0.66-1.26-2.52-2.01-1.199-0.48-0.959 0.031 0.841 1.111z"/>\r
+  </g>\r
+  <g transform="translate(-12.4048,10.0005)">\r
+   <path d="m222.275 107.427c-0.738 0.902 0.574 8.365 5.412 13.285 4.839 4.838 7.791 4.838 9.759 2.706 3.771-4.018 0.738-7.791-1.558-10.415-2.297-2.624-5.248-1.722-7.955-4.346-2.706-2.624-4.592-2.46-5.658-1.23z"/>\r
+   <path fill="#050505" d="m222.345 107.494c-0.732 0.895 0.569 8.3 5.369 13.182 4.803 4.801 7.731 4.801 9.685 2.685 3.742-3.987 0.731-7.73-1.546-10.334-2.278-2.604-5.208-1.709-7.894-4.312-2.684-2.604-4.556-2.441-5.614-1.221z"/>\r
+   <path fill="#0a0a0a" d="m222.416 107.561c-0.727 0.888 0.565 8.235 5.328 13.079 4.763 4.763 7.67 4.763 9.607 2.664 3.713-3.956 0.727-7.67-1.534-10.253-2.26-2.584-5.166-1.696-7.831-4.279-2.664-2.583-4.521-2.422-5.57-1.211z"/>\r
+   <path fill="#0f0f0f" d="m222.486 107.628c-0.721 0.881 0.561 8.17 5.286 12.976 4.726 4.725 7.608 4.725 9.532 2.642 3.684-3.924 0.72-7.609-1.522-10.172-2.243-2.563-5.126-1.682-7.77-4.244-2.643-2.563-4.485-2.403-5.526-1.202z"/>\r
+   <path fill="#141414" d="m222.556 107.695c-0.716 0.874 0.557 8.105 5.243 12.872 4.689 4.688 7.55 4.688 9.456 2.622 3.655-3.893 0.716-7.549-1.51-10.091-2.224-2.543-5.085-1.669-7.707-4.211-2.621-2.543-4.449-2.384-5.482-1.192z"/>\r
+   <path fill="#191919" d="m222.627 107.762c-0.71 0.867 0.552 8.04 5.202 12.769 4.65 4.65 7.488 4.65 9.379 2.601 3.626-3.862 0.71-7.489-1.497-10.011s-5.044-1.655-7.646-4.177-4.414-2.364-5.438-1.182z"/>\r
+   <path fill="#1e1e1e" d="m222.697 107.829c-0.704 0.86 0.547 7.975 5.16 12.666 4.613 4.612 7.428 4.612 9.304 2.579 3.596-3.83 0.703-7.427-1.486-9.929-2.188-2.502-5.003-1.642-7.584-4.143-2.579-2.502-4.378-2.346-5.394-1.173z"/>\r
+   <path fill="#232323" d="m222.767 107.896c-0.697 0.853 0.543 7.91 5.117 12.562 4.576 4.575 7.367 4.575 9.229 2.559 3.567-3.8 0.698-7.367-1.473-9.848-2.171-2.482-4.963-1.629-7.522-4.11s-4.343-2.326-5.351-1.163z"/>\r
+   <path fill="#282828" d="m222.838 107.963c-0.691 0.846 0.538 7.845 5.076 12.459 4.537 4.537 7.307 4.537 9.152 2.538 3.537-3.769 0.691-7.307-1.461-9.768-2.154-2.461-4.922-1.615-7.461-4.076-2.538-2.461-4.307-2.307-5.306-1.153z"/>\r
+   <path fill="#2d2d2d" d="m222.908 108.03c-0.686 0.839 0.534 7.78 5.034 12.355 4.5 4.499 7.246 4.499 9.076 2.516 3.509-3.737 0.686-7.246-1.449-9.686-2.135-2.441-4.881-1.602-7.399-4.042-2.516-2.44-4.271-2.287-5.262-1.143z"/>\r
+   <path fill="#333" d="m222.978 108.096c-0.681 0.832 0.529 7.715 4.992 12.253 4.463 4.462 7.186 4.462 9.001 2.496 3.479-3.706 0.68-7.186-1.438-9.605-2.118-2.42-4.841-1.588-7.337-4.008s-4.235-2.27-5.218-1.136z"/>\r
+   <path fill="#383838" d="m223.048 108.163c-0.674 0.825 0.526 7.65 4.95 12.15 4.425 4.425 7.125 4.425 8.925 2.475 3.45-3.675 0.676-7.125-1.425-9.525-2.099-2.4-4.8-1.575-7.274-3.975-2.475-2.399-4.201-2.25-5.176-1.125z"/>\r
+   <path fill="#3d3d3d" d="m223.119 108.23c-0.669 0.818 0.521 7.585 4.908 12.047 4.387 4.387 7.063 4.387 8.849 2.453 3.42-3.643 0.669-7.064-1.413-9.443-2.082-2.38-4.759-1.562-7.214-3.941-2.453-2.38-4.164-2.231-5.13-1.116z"/>\r
+   <path fill="#424242" d="m223.189 108.297c-0.663 0.811 0.516 7.52 4.866 11.944 4.35 4.349 7.004 4.349 8.772 2.432 3.392-3.612 0.663-7.004-1.4-9.363-2.064-2.359-4.719-1.548-7.151-3.907-2.433-2.359-4.129-2.212-5.087-1.106z"/>\r
+   <path fill="#474747" d="m223.259 108.364c-0.656 0.804 0.513 7.455 4.824 11.84 4.313 4.312 6.944 4.312 8.697 2.412 3.362-3.582 0.658-6.944-1.388-9.282-2.046-2.339-4.679-1.535-7.09-3.874-2.411-2.338-4.093-2.192-5.043-1.096z"/>\r
+   <path fill="#4c4c4c" d="m223.33 108.431c-0.651 0.797 0.507 7.39 4.782 11.737 4.274 4.274 6.882 4.274 8.621 2.39 3.332-3.55 0.651-6.883-1.377-9.201s-4.636-1.521-7.028-3.839c-2.39-2.318-4.057-2.174-4.998-1.087z"/>\r
+   <path fill="#515151" d="m223.4 108.498c-0.646 0.79 0.503 7.325 4.74 11.634 4.236 4.236 6.821 4.236 8.545 2.369 3.304-3.519 0.646-6.822-1.364-9.12s-4.596-1.508-6.966-3.806c-2.369-2.298-4.022-2.154-4.955-1.077z"/>\r
+   <path fill="#565656" d="m223.47 108.565c-0.641 0.783 0.499 7.26 4.697 11.53 4.199 4.199 6.763 4.199 8.471 2.349 3.273-3.488 0.64-6.762-1.353-9.039-1.993-2.278-4.556-1.495-6.905-3.773-2.347-2.277-3.985-2.135-4.91-1.067z"/>\r
+   <path fill="#5b5b5b" d="m223.541 108.632c-0.635 0.776 0.493 7.195 4.656 11.427 4.161 4.161 6.701 4.161 8.393 2.327 3.245-3.456 0.636-6.701-1.34-8.958-1.975-2.257-4.514-1.48-6.843-3.738-2.327-2.257-3.95-2.116-4.866-1.058z"/>\r
+   <path fill="#606060" d="m223.611 108.699c-0.629 0.769 0.489 7.13 4.614 11.324 4.124 4.123 6.64 4.123 8.317 2.306 3.215-3.425 0.628-6.641-1.328-8.877-1.957-2.237-4.474-1.468-6.78-3.705-2.306-2.236-3.915-2.097-4.823-1.048z"/>\r
+   <path fill="#666" d="m223.681 108.765c-0.623 0.762 0.484 7.065 4.571 11.221 4.086 4.086 6.58 4.086 8.242 2.285 3.187-3.394 0.623-6.58-1.315-8.796-1.939-2.217-4.434-1.455-6.72-3.671-2.284-2.216-3.878-2.078-4.778-1.039z"/>\r
+  </g>\r
+  <g transform="translate(-12.4048,10.0005)">\r
+   <path fill="#fc0" d="m137.79 109.277c1.978 1.366 2.031 1.607 4.948 3.514 4.64 3.768 12.885 4.616 16.922 4.75 9.233 1.467 25.738-7.161 32.273-11.111 3.291-2.463 9.38-7.551 11.659-7.637 1.405 1.485-0.66 1.792-3.587 3.775-3.906 2.779-7.25 5.156-13.172 8.515-6.338 3.316-16.078 8.794-28.548 8.054-6.542-0.959-6.566-1.024-10.606-3.086-2.4-1.732-7.901-4.608-9.889-6.774z"/>\r
+   <linearGradient id="al" x1="129.342" gradientUnits="userSpaceOnUse" x2="195.598" gradientTransform="matrix(1,0,0,-1,8.3999,368.3)" y1="259.305" y2="259.305">\r
+    <stop stop-color="#FAC700" offset="0"/>\r
+    <stop stop-color="#F7C400" offset=".415"/>\r
+    <stop stop-color="#F7C400" offset="1"/>\r
+   </linearGradient>\r
+   <path fill="url(#al)" d="m137.742 109.259c1.926 1.274 2.165 1.643 5.083 3.554 4.616 3.734 12.716 4.616 16.796 4.763 9.365 1.452 26.05-7.294 32.356-11.159 3.357-2.506 9.344-7.498 11.595-7.604 1.365 1.472-0.728 1.768-3.688 3.814-3.889 2.753-7.119 5.065-12.972 8.383-6.29 3.291-16.078 8.795-28.536 8.104-6.561-0.945-6.851-1.07-10.758-3.079-2.468-1.755-7.876-4.587-9.876-6.776z"/>\r
+   <linearGradient id="am" x1="129.293" gradientUnits="userSpaceOnUse" x2="195.554" gradientTransform="matrix(1,0,0,-1,8.3999,368.3)" y1="259.311" y2="259.311">\r
+    <stop stop-color="#F6C200" offset="0"/>\r
+    <stop stop-color="#EFBC00" offset=".415"/>\r
+    <stop stop-color="#EFBC00" offset="1"/>\r
+   </linearGradient>\r
+   <path fill="url(#am)" d="m137.693 109.24c1.876 1.183 2.3 1.68 5.218 3.595 4.593 3.7 12.548 4.616 16.67 4.776 9.498 1.437 26.364-7.428 32.44-11.207 3.425-2.55 9.308-7.444 11.528-7.57 1.326 1.457-0.795 1.743-3.788 3.854-3.87 2.725-6.99 4.973-12.771 8.25-6.243 3.266-16.078 8.796-28.525 8.154-6.579-0.931-7.134-1.117-10.908-3.073-2.536-1.779-7.852-4.567-9.864-6.779z"/>\r
+   <linearGradient id="an" x1="129.245" gradientUnits="userSpaceOnUse" x2="195.51" gradientTransform="matrix(1,0,0,-1,8.3999,368.3)" y1="259.317" y2="259.317">\r
+    <stop stop-color="#F1BD00" offset="0"/>\r
+    <stop stop-color="#E8B500" offset=".415"/>\r
+    <stop stop-color="#E8B500" offset="1"/>\r
+   </linearGradient>\r
+   <path fill="url(#an)" d="m137.645 109.222c1.825 1.091 2.434 1.715 5.352 3.635 4.569 3.665 12.38 4.615 16.544 4.789 9.631 1.422 26.677-7.562 32.524-11.255 3.491-2.594 9.271-7.392 11.463-7.538 1.287 1.442-0.861 1.718-3.889 3.893-3.853 2.699-6.86 4.882-12.57 8.119-6.195 3.241-16.078 8.797-28.513 8.203-6.6-0.916-7.418-1.163-11.061-3.066-2.603-1.801-7.826-4.546-9.85-6.78z"/>\r
+   <linearGradient id="ao" x1="129.196" gradientUnits="userSpaceOnUse" x2="195.465" gradientTransform="matrix(1,0,0,-1,8.3999,368.3)" y1="259.322" y2="259.322">\r
+    <stop stop-color="#EDB800" offset="0"/>\r
+    <stop stop-color="#E0AD00" offset=".415"/>\r
+    <stop stop-color="#E0AD00" offset="1"/>\r
+   </linearGradient>\r
+   <path fill="url(#ao)" d="m137.596 109.203c1.774 1 2.568 1.752 5.487 3.676 4.545 3.631 12.211 4.615 16.418 4.801 9.764 1.408 26.989-7.695 32.608-11.302 3.557-2.637 9.236-7.338 11.396-7.505 1.247 1.427-0.928 1.693-3.99 3.932-3.833 2.672-6.729 4.791-12.369 7.986-6.148 3.217-16.078 8.799-28.501 8.254-6.619-0.902-7.702-1.21-11.21-3.059-2.672-1.825-7.803-4.525-9.839-6.783z"/>\r
+   <linearGradient id="ap" x1="129.148" gradientUnits="userSpaceOnUse" x2="195.422" gradientTransform="matrix(1,0,0,-1,8.3999,368.3)" y1="259.327" y2="259.327">\r
+    <stop stop-color="#E9B300" offset="0"/>\r
+    <stop stop-color="#D8A500" offset=".415"/>\r
+    <stop stop-color="#D8A500" offset="1"/>\r
+   </linearGradient>\r
+   <path fill="url(#ap)" d="m137.548 109.184c1.724 0.909 2.703 1.788 5.622 3.717 4.522 3.597 12.043 4.615 16.292 4.814 9.896 1.393 27.303-7.829 32.692-11.35 3.624-2.681 9.2-7.286 11.331-7.472 1.208 1.412-0.995 1.668-4.092 3.972-3.814 2.644-6.6 4.698-12.168 7.853-6.101 3.192-16.077 8.8-28.489 8.303-6.638-0.887-7.986-1.256-11.361-3.052-2.741-1.848-7.779-4.504-9.827-6.785z"/>\r
+   <linearGradient id="aq" x1="129.099" gradientUnits="userSpaceOnUse" x2="195.379" gradientTransform="matrix(1,0,0,-1,8.3999,368.3)" y1="259.332" y2="259.332">\r
+    <stop stop-color="#E4AE00" offset="0"/>\r
+    <stop stop-color="#D19E00" offset=".415"/>\r
+    <stop stop-color="#D19E00" offset="1"/>\r
+   </linearGradient>\r
+   <path fill="url(#aq)" d="m137.499 109.166c1.673 0.817 2.838 1.824 5.757 3.757 4.499 3.562 11.875 4.614 16.166 4.827 10.029 1.378 27.615-7.963 32.776-11.398 3.691-2.725 9.164-7.232 11.265-7.439 1.169 1.397-1.061 1.644-4.191 4.01-3.796 2.618-6.469 4.608-11.968 7.722-6.053 3.167-16.077 8.801-28.478 8.353-6.657-0.873-8.27-1.303-11.512-3.046-2.809-1.87-7.755-4.483-9.815-6.786z"/>\r
+   <linearGradient id="ar" x1="129.051" gradientUnits="userSpaceOnUse" x2="195.338" gradientTransform="matrix(1,0,0,-1,8.3999,368.3)" y1="259.336" y2="259.336">\r
+    <stop stop-color="#E0A900" offset="0"/>\r
+    <stop stop-color="#C99600" offset=".415"/>\r
+    <stop stop-color="#C99600" offset="1"/>\r
+   </linearGradient>\r
+   <path fill="url(#ar)" d="m137.451 109.147c1.622 0.726 2.972 1.86 5.892 3.798 4.475 3.528 11.705 4.614 16.04 4.84 10.161 1.362 27.929-8.097 32.859-11.447 3.757-2.767 9.128-7.178 11.2-7.405 1.13 1.382-1.128 1.619-4.294 4.049-3.777 2.592-6.339 4.517-11.767 7.589-6.005 3.143-16.077 8.803-28.466 8.404-6.676-0.859-8.553-1.35-11.663-3.039-2.876-1.894-7.729-4.462-9.801-6.789z"/>\r
+   <linearGradient id="w" x1="129.003" gradientUnits="userSpaceOnUse" x2="195.296" gradientTransform="matrix(1,0,0,-1,8.3999,368.3)" y1="259.34" y2="259.34">\r
+    <stop stop-color="#DCA400" offset="0"/>\r
+    <stop stop-color="#C18E00" offset=".415"/>\r
+    <stop stop-color="#C18E00" offset="1"/>\r
+   </linearGradient>\r
+   <path fill="url(#w)" d="m137.403 109.129c1.571 0.634 3.105 1.896 6.026 3.838 4.45 3.494 11.536 4.614 15.913 4.852 10.295 1.348 28.241-8.23 32.943-11.494 3.824-2.811 9.092-7.125 11.134-7.373 1.092 1.367-1.194 1.594-4.394 4.088-3.759 2.565-6.209 4.425-11.566 7.457-5.957 3.118-16.077 8.804-28.454 8.453-6.694-0.844-8.837-1.396-11.813-3.032-2.945-1.916-7.706-4.44-9.789-6.789z"/>\r
+   <linearGradient id="x" x1="128.954" gradientUnits="userSpaceOnUse" x2="195.255" gradientTransform="matrix(1,0,0,-1,8.3999,368.3)" y1="259.343" y2="259.343">\r
+    <stop stop-color="#D79F00" offset="0"/>\r
+    <stop stop-color="#BA8700" offset=".415"/>\r
+    <stop stop-color="#BA8700" offset="1"/>\r
+   </linearGradient>\r
+   <path fill="url(#x)" d="m137.354 109.11c1.521 0.543 3.241 1.932 6.161 3.879 4.428 3.459 11.368 4.613 15.788 4.865 10.427 1.333 28.554-8.364 33.026-11.542 3.892-2.855 9.057-7.073 11.068-7.339 1.052 1.353-1.261 1.569-4.495 4.127-3.74 2.538-6.078 4.334-11.365 7.325-5.91 3.093-16.077 8.805-28.441 8.503-6.716-0.83-9.121-1.443-11.966-3.026-3.012-1.939-7.68-4.42-9.776-6.792z"/>\r
+   <linearGradient id="y" x1="128.906" gradientUnits="userSpaceOnUse" x2="195.216" gradientTransform="matrix(1,0,0,-1,8.3999,368.3)" y1="259.348" y2="259.348">\r
+    <stop stop-color="#D39B00" offset="0"/>\r
+    <stop stop-color="#B27F00" offset=".415"/>\r
+    <stop stop-color="#B27F00" offset="1"/>\r
+   </linearGradient>\r
+   <path fill="url(#y)" d="m137.306 109.091c1.47 0.451 3.375 1.969 6.296 3.919 4.403 3.426 11.2 4.614 15.662 4.879 10.559 1.318 28.865-8.498 33.109-11.59 3.958-2.899 9.021-7.02 11.003-7.306 1.013 1.337-1.328 1.544-4.596 4.167-3.722 2.511-5.949 4.242-11.165 7.192-5.862 3.068-16.077 8.807-28.43 8.553-6.734-0.816-9.405-1.489-12.116-3.019-3.08-1.963-7.656-4.4-9.763-6.795z"/>\r
+   <linearGradient id="z" x1="128.857" gradientUnits="userSpaceOnUse" x2="195.176" gradientTransform="matrix(1,0,0,-1,8.3999,368.3)" y1="259.35" y2="259.35">\r
+    <stop stop-color="#CF9600" offset="0"/>\r
+    <stop stop-color="#a70" offset=".415"/>\r
+    <stop stop-color="#a70" offset="1"/>\r
+   </linearGradient>\r
+   <path fill="url(#z)" d="m137.257 109.073c1.421 0.359 3.511 2.005 6.432 3.959 4.38 3.392 11.032 4.614 15.536 4.892 10.691 1.303 29.179-8.631 33.193-11.638 4.024-2.942 8.984-6.967 10.938-7.274 0.973 1.323-1.396 1.521-4.697 4.206-3.703 2.484-5.818 4.151-10.964 7.06-5.814 3.043-16.077 8.808-28.418 8.603-6.753-0.802-9.689-1.535-12.268-3.012-3.149-1.986-7.632-4.378-9.752-6.796z"/>\r
+   <linearGradient id="aa" x1="128.809" gradientUnits="userSpaceOnUse" x2="195.137" gradientTransform="matrix(1,0,0,-1,8.3999,368.3)" y1="259.354" y2="259.354">\r
+    <stop stop-color="#CA9100" offset="0"/>\r
+    <stop stop-color="#A37000" offset=".415"/>\r
+    <stop stop-color="#A37000" offset="1"/>\r
+   </linearGradient>\r
+   <path fill="url(#aa)" d="m137.209 109.054c1.368 0.268 3.645 2.041 6.565 4 4.356 3.357 10.864 4.613 15.41 4.904 10.824 1.289 29.493-8.764 33.277-11.685 4.092-2.986 8.948-6.914 10.871-7.241 0.935 1.308-1.461 1.496-4.797 4.245-3.685 2.457-5.688 4.06-10.763 6.928-5.768 3.018-16.077 8.809-28.407 8.653-6.771-0.788-9.972-1.582-12.418-3.006-3.216-2.008-7.607-4.357-9.738-6.798z"/>\r
+   <linearGradient id="ab" x1="128.76" gradientUnits="userSpaceOnUse" x2="195.099" gradientTransform="matrix(1,0,0,-1,8.3999,368.3)" y1="259.356" y2="259.356">\r
+    <stop stop-color="#C68C00" offset="0"/>\r
+    <stop stop-color="#9B6800" offset=".415"/>\r
+    <stop stop-color="#9B6800" offset="1"/>\r
+   </linearGradient>\r
+   <path fill="url(#ab)" d="m137.16 109.036c1.318 0.176 3.779 2.077 6.701 4.04 4.333 3.323 10.695 4.613 15.284 4.917 10.957 1.274 29.805-8.898 33.36-11.733 4.158-3.03 8.912-6.86 10.807-7.208 0.894 1.292-1.528 1.471-4.899 4.284-3.666 2.43-5.558 3.968-10.562 6.796-5.72 2.993-16.077 8.81-28.396 8.702-6.791-0.773-10.256-1.628-12.568-2.999-3.285-2.031-7.583-4.336-9.727-6.799z"/>\r
+   <linearGradient id="ac" x1="128.712" gradientUnits="userSpaceOnUse" x2="195.062" gradientTransform="matrix(1,0,0,-1,8.3999,368.3)" y1="259.358" y2="259.358">\r
+    <stop stop-color="#C28700" offset="0"/>\r
+    <stop stop-color="#936000" offset=".415"/>\r
+    <stop stop-color="#936000" offset="1"/>\r
+   </linearGradient>\r
+   <path fill="url(#ac)" d="m137.112 109.017c1.267 0.085 3.912 2.113 6.835 4.081 4.31 3.289 10.527 4.613 15.158 4.93 11.09 1.258 30.118-9.032 33.445-11.782 4.225-3.072 8.877-6.807 10.739-7.174 0.855 1.278-1.595 1.446-5 4.323-3.647 2.404-5.427 3.877-10.36 6.663-5.673 2.969-16.077 8.812-28.384 8.753-6.811-0.759-10.54-1.675-12.719-2.992-3.353-2.055-7.559-4.315-9.714-6.802z"/>\r
+   <linearGradient id="ad" x1="128.663" gradientUnits="userSpaceOnUse" x2="195.025" gradientTransform="matrix(1,0,0,-1,8.3999,368.3)" y1="259.36" y2="259.36">\r
+    <stop stop-color="#BD8200" offset="0"/>\r
+    <stop stop-color="#8C5900" offset=".415"/>\r
+    <stop stop-color="#8C5900" offset="1"/>\r
+   </linearGradient>\r
+   <path fill="url(#ad)" d="m137.063 108.998c1.217-0.006 4.047 2.15 6.97 4.122 4.286 3.254 10.359 4.612 15.032 4.943 11.223 1.243 30.431-9.166 33.53-11.83 4.289-3.116 8.84-6.753 10.673-7.141 0.815 1.263-1.661 1.421-5.101 4.362-3.63 2.377-5.298 3.785-10.161 6.531-5.624 2.944-16.075 8.813-28.37 8.802-6.83-0.744-10.824-1.721-12.87-2.985-3.422-2.077-7.535-4.294-9.703-6.804z"/>\r
+   <linearGradient id="ae" x1="128.615" gradientUnits="userSpaceOnUse" x2="194.989" gradientTransform="matrix(1,0,0,-1,8.3999,368.3)" y1="259.362" y2="259.362">\r
+    <stop stop-color="#B97D00" offset="0"/>\r
+    <stop stop-color="#845100" offset=".415"/>\r
+    <stop stop-color="#845100" offset="1"/>\r
+   </linearGradient>\r
+   <path fill="url(#ae)" d="m137.015 108.98c1.166-0.098 4.181 2.185 7.104 4.162 4.262 3.22 10.19 4.612 14.906 4.955 11.354 1.229 30.743-9.299 33.613-11.877 4.356-3.16 8.804-6.7 10.607-7.108 0.776 1.248-1.728 1.397-5.202 4.401-3.61 2.35-5.167 3.694-9.96 6.399-5.576 2.919-16.076 8.814-28.358 8.852-6.85-0.73-11.108-1.768-13.021-2.979-3.489-2.1-7.51-4.273-9.689-6.805z"/>\r
+   <linearGradient id="af" x1="128.567" gradientUnits="userSpaceOnUse" x2="194.954" gradientTransform="matrix(1,0,0,-1,8.3999,368.3)" y1="259.364" y2="259.364">\r
+    <stop stop-color="#B57800" offset="0"/>\r
+    <stop stop-color="#7C4900" offset=".415"/>\r
+    <stop stop-color="#7C4900" offset="1"/>\r
+   </linearGradient>\r
+   <path fill="url(#af)" d="m136.967 108.961c1.115-0.189 4.315 2.222 7.239 4.203 4.239 3.186 10.021 4.612 14.78 4.968 11.488 1.214 31.057-9.433 33.697-11.925 4.424-3.203 8.768-6.647 10.542-7.075 0.736 1.233-1.795 1.372-5.303 4.44-3.593 2.323-5.038 3.603-9.759 6.266-5.529 2.895-16.077 8.816-28.348 8.903-6.868-0.716-11.392-1.815-13.172-2.972-3.557-2.124-7.485-4.252-9.676-6.808z"/>\r
+   <linearGradient id="ah" x1="128.518" gradientUnits="userSpaceOnUse" x2="194.918" gradientTransform="matrix(1,0,0,-1,8.3999,368.3)" y1="259.366" y2="259.366">\r
+    <stop stop-color="#B07300" offset="0"/>\r
+    <stop stop-color="#754200" offset=".415"/>\r
+    <stop stop-color="#754200" offset="1"/>\r
+   </linearGradient>\r
+   <path fill="url(#ah)" d="m136.918 108.943c1.064-0.281 4.45 2.257 7.374 4.243 4.216 3.151 9.854 4.611 14.654 4.981 11.621 1.199 31.37-9.567 33.781-11.973 4.49-3.247 8.731-6.594 10.476-7.042 0.698 1.218-1.861 1.347-5.403 4.479-3.573 2.296-4.906 3.511-9.558 6.134-5.48 2.87-16.076 8.817-28.336 8.952-6.887-0.701-11.675-1.861-13.323-2.965-3.626-2.146-7.462-4.231-9.665-6.809z"/>\r
+   <linearGradient id="ai" x1="128.47" gradientUnits="userSpaceOnUse" x2="194.886" gradientTransform="matrix(1,0,0,-1,8.3999,368.3)" y1="259.367" y2="259.367">\r
+    <stop stop-color="#AC6E00" offset="0"/>\r
+    <stop stop-color="#6D3A00" offset=".415"/>\r
+    <stop stop-color="#6D3A00" offset="1"/>\r
+   </linearGradient>\r
+   <path fill="url(#ai)" d="m136.87 108.924c1.013-0.372 4.584 2.294 7.509 4.284 4.191 3.117 9.685 4.611 14.528 4.994 11.753 1.184 31.682-9.701 33.864-12.021 4.557-3.291 8.695-6.542 10.411-7.009 0.657 1.203-1.929 1.322-5.504 4.518-3.557 2.269-4.777 3.42-9.358 6.002-5.434 2.845-16.076 8.818-28.324 9.002-6.907-0.687-11.959-1.908-13.474-2.959-3.694-2.169-7.437-4.21-9.652-6.811z"/>\r
+   <linearGradient id="aj" x1="128.421" gradientUnits="userSpaceOnUse" x2="194.85" gradientTransform="matrix(1,0,0,-1,8.3999,368.3)" y1="259.369" y2="259.369">\r
+    <stop stop-color="#A86A00" offset="0"/>\r
+    <stop stop-color="#663200" offset=".415"/>\r
+    <stop stop-color="#663200" offset="1"/>\r
+   </linearGradient>\r
+   <path fill="url(#aj)" d="m136.821 108.905c0.963-0.464 4.719 2.33 7.644 4.324 4.168 3.083 9.517 4.611 14.402 5.007 11.886 1.169 31.995-9.834 33.948-12.069 4.624-3.334 8.66-6.487 10.345-6.976 0.619 1.188-1.995 1.298-5.604 4.558-3.537 2.242-4.647 3.328-9.157 5.869-5.386 2.82-16.076 8.82-28.313 9.052-6.926-0.673-12.243-1.954-13.625-2.952-3.762-2.192-7.413-4.189-9.64-6.813z"/>\r
+  </g>\r
+ </g>\r
+</svg>\r
index 686ab93..c749598 100644 (file)
@@ -5,7 +5,11 @@
  */
 class ArticleTablesTest extends MediaWikiLangTestCase {
 
-       function testbug14404() {
+       /**
+        * @covers Title::getTemplateLinksFrom
+        * @covers Title::getLinksFrom
+        */
+       public function testbug14404() {
                global $wgContLang, $wgLanguageCode, $wgLang;
 
                $title = Title::newFromText( 'Bug 14404' );
index 867c4f0..84f900f 100644 (file)
@@ -25,28 +25,37 @@ class ArticleTest extends MediaWikiTestCase {
                $this->article = null;
        }
 
-       function testImplementsGetMagic() {
+       /**
+        * @covers Article::__get
+        */
+       public function testImplementsGetMagic() {
                $this->assertEquals( false, $this->article->mLatest, "Article __get magic" );
        }
 
        /**
         * @depends testImplementsGetMagic
+        * @covers Article::__set
         */
-       function testImplementsSetMagic() {
+       public function testImplementsSetMagic() {
                $this->article->mLatest = 2;
                $this->assertEquals( 2, $this->article->mLatest, "Article __set magic" );
        }
 
        /**
         * @depends testImplementsSetMagic
+        * @covers Article::__call
         */
-       function testImplementsCallMagic() {
+       public function testImplementsCallMagic() {
                $this->article->mLatest = 33;
                $this->article->mDataLoaded = true;
                $this->assertEquals( 33, $this->article->getLatest(), "Article __call magic" );
        }
 
-       function testGetOrSetOnNewProperty() {
+       /**
+        * @covers Article::__get
+        * @covers Article::__set
+        */
+       public function testGetOrSetOnNewProperty() {
                $this->article->ext_someNewProperty = 12;
                $this->assertEquals( 12, $this->article->ext_someNewProperty,
                        "Article get/set magic on new field" );
@@ -58,8 +67,13 @@ class ArticleTest extends MediaWikiTestCase {
 
        /**
         * Checks for the existence of the backwards compatibility static functions (forwarders to WikiPage class)
+        * @covers Article::selectFields
+        * @covers Article::onArticleCreate
+        * @covers Article::onArticleDelete
+        * @covers Article::onArticleEdit
+        * @covers Article::getAutosummary
         */
-       function testStaticFunctions() {
+       public function testStaticFunctions() {
                $this->hideDeprecated( 'Article::getAutosummary' );
                $this->hideDeprecated( 'WikiPage::getAutosummary' );
                $this->hideDeprecated( 'CategoryPage::getAutosummary' ); // Inherited from Article
@@ -75,18 +89,4 @@ class ArticleTest extends MediaWikiTestCase {
                $this->assertTrue( is_string( CategoryPage::getAutosummary( '', '', 0 ) ),
                        "Article static functions" );
        }
-
-       function testWikiPageFactory() {
-               $title = Title::makeTitle( NS_FILE, 'Someimage.png' );
-               $page = WikiPage::factory( $title );
-               $this->assertEquals( 'WikiFilePage', get_class( $page ) );
-
-               $title = Title::makeTitle( NS_CATEGORY, 'SomeCategory' );
-               $page = WikiPage::factory( $title );
-               $this->assertEquals( 'WikiCategoryPage', get_class( $page ) );
-
-               $title = Title::makeTitle( NS_MAIN, 'SomePage' );
-               $page = WikiPage::factory( $title );
-               $this->assertEquals( 'WikiPage', get_class( $page ) );
-       }
 }
index 32fc2c5..f0049fe 100644 (file)
@@ -6,7 +6,9 @@
  */
 class BlockTest extends MediaWikiLangTestCase {
 
-       private $block, $madeAt;
+       /** @var Block */
+       private $block;
+       private $madeAt;
 
        /* variable used to save up the blockID we insert in this test suite */
        private $blockId;
@@ -66,18 +68,24 @@ class BlockTest extends MediaWikiLangTestCase {
                }
        }
 
-       function testInitializerFunctionsReturnCorrectBlock() {
-               // $this->dumpBlocks();
-
+       /**
+        * @covers Block::newFromTarget
+        */
+       public function testINewFromTargetReturnsCorrectBlock() {
                $this->assertTrue( $this->block->equals( Block::newFromTarget( 'UTBlockee' ) ), "newFromTarget() returns the same block as the one that was made" );
+       }
 
+       /**
+        * @covers Block::newFromID
+        */
+       public function testINewFromIDReturnsCorrectBlock() {
                $this->assertTrue( $this->block->equals( Block::newFromID( $this->blockId ) ), "newFromID() returns the same block as the one that was made" );
        }
 
        /**
         * per bug 26425
         */
-       function testBug26425BlockTimestampDefaultsToTime() {
+       public function testBug26425BlockTimestampDefaultsToTime() {
                // delta to stop one-off errors when things happen to go over a second mark.
                $delta = abs( $this->madeAt - $this->block->mTimestamp );
                $this->assertLessThan( 2, $delta, "If no timestamp is specified, the block is recorded as time()" );
@@ -90,8 +98,9 @@ class BlockTest extends MediaWikiLangTestCase {
         * This stopped working with r84475 and friends: regression being fixed for bug 29116.
         *
         * @dataProvider provideBug29116Data
+        * @covers Block::load
         */
-       function testBug29116LoadWithEmptyIp( $vagueTarget ) {
+       public function testBug29116LoadWithEmptyIp( $vagueTarget ) {
                $this->hideDeprecated( 'Block::load' );
 
                $uid = User::idFromName( 'UTBlockee' );
@@ -110,8 +119,9 @@ class BlockTest extends MediaWikiLangTestCase {
         * had. Regression bug 29116.
         *
         * @dataProvider provideBug29116Data
+        * @covers Block::newFromTarget
         */
-       function testBug29116NewFromTargetWithEmptyIp( $vagueTarget ) {
+       public function testBug29116NewFromTargetWithEmptyIp( $vagueTarget ) {
                $block = Block::newFromTarget( 'UTBlockee', $vagueTarget );
                $this->assertTrue( $this->block->equals( $block ), "newFromTarget() returns the same block as the one that was made when given empty vagueTarget param " . var_export( $vagueTarget, true ) );
        }
@@ -124,7 +134,10 @@ class BlockTest extends MediaWikiLangTestCase {
                );
        }
 
-       function testBlockedUserCanNotCreateAccount() {
+       /**
+        * @covers Block::prevents
+        */
+       public function testBlockedUserCanNotCreateAccount() {
                $username = 'BlockedUserToCreateAccountWith';
                $u = User::newFromName( $username );
                $u->setPassword( 'NotRandomPass' );
@@ -184,7 +197,10 @@ class BlockTest extends MediaWikiLangTestCase {
                );
        }
 
-       function testCrappyCrossWikiBlocks() {
+       /**
+        * @covers Block::insert
+        */
+       public function testCrappyCrossWikiBlocks() {
                // Delete the last round's block if it's still there
                $oldBlock = Block::newFromTarget( 'UserOnForeignWiki' );
                if ( $oldBlock ) {
@@ -343,8 +359,10 @@ class BlockTest extends MediaWikiLangTestCase {
 
        /**
         * @dataProvider providerXff
+        * @covers Block::getBlocksForIPList
+        * @covers Block::chooseBlock
         */
-       function testBlocksOnXff( $xff, $exCount, $exResult ) {
+       public function testBlocksOnXff( $xff, $exCount, $exResult ) {
                $list = array_map( 'trim', explode( ',', $xff ) );
                $xffblocks = Block::getBlocksForIPList( $list, true );
                $this->assertEquals( $exCount, count( $xffblocks ), 'Number of blocks for ' . $xff );
index e3d9da7..4832ada 100644 (file)
@@ -2,6 +2,8 @@
 
 /**
  * Test the CDB reader/writer
+ * @covers CdbWriter_PHP
+ * @covers CdbWriter_DBA
  */
 class CdbTest extends MediaWikiTestCase {
 
index f1004fb..c6a7169 100644 (file)
@@ -1,4 +1,12 @@
 <?php
+
+/**
+ * Class CollationTest
+ * @covers Collation
+ * @covers IcuCollation
+ * @covers IdentityCollation
+ * @covers UppercaseCollation
+ */
 class CollationTest extends MediaWikiLangTestCase {
        protected function setUp() {
                parent::setUp();
@@ -20,7 +28,7 @@ class CollationTest extends MediaWikiLangTestCase {
         *
         * @dataProvider prefixDataProvider
         */
-       function testIsPrefix( $lang, $base, $extended ) {
+       public function testIsPrefix( $lang, $base, $extended ) {
                $cp = Collator::create( $lang );
                $cp->setStrength( Collator::PRIMARY );
                $baseBin = $cp->getSortKey( $base );
@@ -30,7 +38,7 @@ class CollationTest extends MediaWikiLangTestCase {
                $this->assertStringStartsWith( $baseBin, $extendedBin, "$base is not a prefix of $extended" );
        }
 
-       function prefixDataProvider() {
+       public static function prefixDataProvider() {
                return array(
                        array( 'en', 'A', 'AA' ),
                        array( 'en', 'A', 'AAA' ),
@@ -53,7 +61,7 @@ class CollationTest extends MediaWikiLangTestCase {
         *
         * @dataProvider notPrefixDataProvider
         */
-       function testNotIsPrefix( $lang, $base, $extended ) {
+       public function testNotIsPrefix( $lang, $base, $extended ) {
                $cp = Collator::create( $lang );
                $cp->setStrength( Collator::PRIMARY );
                $baseBin = $cp->getSortKey( $base );
@@ -63,7 +71,7 @@ class CollationTest extends MediaWikiLangTestCase {
                $this->assertStringStartsNotWith( $baseBin, $extendedBin, "$base is a prefix of $extended" );
        }
 
-       function notPrefixDataProvider() {
+       public static function notPrefixDataProvider() {
                return array(
                        array( 'en', 'A', 'B' ),
                        array( 'en', 'AC', 'ABC' ),
@@ -81,7 +89,7 @@ class CollationTest extends MediaWikiLangTestCase {
         *
         * @dataProvider firstLetterProvider
         */
-       function testGetFirstLetter( $collation, $string, $firstLetter ) {
+       public function testGetFirstLetter( $collation, $string, $firstLetter ) {
                $col = Collation::factory( $collation );
                $this->assertEquals( $firstLetter, $col->getFirstLetter( $string ) );
        }
index d927b7a..76a9a10 100644 (file)
@@ -23,8 +23,9 @@ class DiffHistoryBlobTest extends MediaWikiTestCase {
        /**
         * Test for DiffHistoryBlob::xdiffAdler32()
         * @dataProvider provideXdiffAdler32
+        * @covers DiffHistoryBlob::xdiffAdler32
         */
-       function testXdiffAdler32( $input ) {
+       public function testXdiffAdler32( $input ) {
                $xdiffHash = substr( xdiff_string_rabdiff( $input, '' ), 0, 4 );
                $dhb = new DiffHistoryBlob;
                $myHash = $dhb->xdiffAdler32( $input );
index 76ef782..7d2b04f 100644 (file)
@@ -13,8 +13,9 @@ class EditPageTest extends MediaWikiLangTestCase {
 
        /**
         * @dataProvider provideExtractSectionTitle
+        * @covers EditPage::extractSectionTitle
         */
-       function testExtractSectionTitle( $section, $title ) {
+       public function testExtractSectionTitle( $section, $title ) {
                $extracted = EditPage::extractSectionTitle( $section );
                $this->assertEquals( $title, $extracted );
        }
@@ -59,7 +60,7 @@ class EditPageTest extends MediaWikiLangTestCase {
         * wrapper around assertEquals() which calls rrtrim() to normalize the
         * expected and actual texts.
         */
-       function assertEditedTextEquals( $expected, $actual, $msg = '' ) {
+       protected function assertEditedTextEquals( $expected, $actual, $msg = '' ) {
                return $this->assertEquals( rtrim( $expected ), rtrim( $actual ), $msg );
        }
 
@@ -172,6 +173,10 @@ class EditPageTest extends MediaWikiLangTestCase {
                return $page;
        }
 
+       /**
+        * @todo split into a dataprovider and test method
+        * @covers EditPage
+        */
        public function testCreatePage() {
                $this->assertEdit(
                        'EditPageTest_testCreatePage',
@@ -338,6 +343,7 @@ hello
 
        /**
         * @dataProvider provideSectionEdit
+        * @covers EditPage
         */
        public function testSectionEdit( $base, $section, $text, $summary, $expected ) {
                $edit = array(
@@ -432,6 +438,7 @@ hello
 
        /**
         * @dataProvider provideAutoMerge
+        * @covers EditPage
         */
        public function testAutoMerge( $baseUser, $text, $adamsEdit, $bertasEdit,
                $expectedCode, $expectedText, $message = null
index 99544e7..ba155a4 100644 (file)
@@ -5,7 +5,10 @@
 
 class ExternalStoreTest extends MediaWikiTestCase {
 
-       function testExternalFetchFromURL() {
+       /**
+        * @covers ExternalStore::fetchFromURL
+        */
+       public function testExternalFetchFromURL() {
                $this->setMwGlobals( 'wgExternalStores', false );
 
                $this->assertFalse(
index 07215c1..dc19154 100644 (file)
@@ -5,6 +5,11 @@
  */
 class ExtraParserTest extends MediaWikiTestCase {
 
+       /** @var ParserOptions */
+       protected $options;
+       /** @var Parser */
+       protected $parser;
+
        protected function setUp() {
                parent::setUp();
 
@@ -26,8 +31,11 @@ class ExtraParserTest extends MediaWikiTestCase {
                MagicWord::clearCache();
        }
 
-       // Bug 8689 - Long numeric lines kill the parser
-       function testBug8689() {
+       /**
+        * Bug 8689 - Long numeric lines kill the parser
+        * @covers Parser::parse
+        */
+       public function testBug8689() {
                global $wgUser;
                $longLine = '1.' . str_repeat( '1234567890', 100000 ) . "\n";
 
@@ -37,14 +45,20 @@ class ExtraParserTest extends MediaWikiTestCase {
                        $this->parser->parse( $longLine, $t, $options )->getText() );
        }
 
-       /* Test the parser entry points */
-       function testParse() {
+       /**
+        * Test the parser entry points
+        * @covers Parser::parse
+        */
+       public function testParse() {
                $title = Title::newFromText( __FUNCTION__ );
                $parserOutput = $this->parser->parse( "Test\n{{Foo}}\n{{Bar}}", $title, $this->options );
                $this->assertEquals( "<p>Test\nContent of <i>Template:Foo</i>\nContent of <i>Template:Bar</i>\n</p>", $parserOutput->getText() );
        }
 
-       function testPreSaveTransform() {
+       /**
+        * @covers Parser::preSaveTransform
+        */
+       public function testPreSaveTransform() {
                global $wgUser;
                $title = Title::newFromText( __FUNCTION__ );
                $outputText = $this->parser->preSaveTransform( "Test\r\n{{subst:Foo}}\n{{Bar}}", $title, $wgUser, $this->options );
@@ -52,7 +66,10 @@ class ExtraParserTest extends MediaWikiTestCase {
                $this->assertEquals( "Test\nContent of ''Template:Foo''\n{{Bar}}", $outputText );
        }
 
-       function testPreprocess() {
+       /**
+        * @covers Parser::preprocess
+        */
+       public function testPreprocess() {
                $title = Title::newFromText( __FUNCTION__ );
                $outputText = $this->parser->preprocess( "Test\n{{Foo}}\n{{Bar}}", $title, $this->options );
 
@@ -61,8 +78,9 @@ class ExtraParserTest extends MediaWikiTestCase {
 
        /**
         * cleanSig() makes all templates substs and removes tildes
+        * @covers Parser::cleanSig
         */
-       function testCleanSig() {
+       public function testCleanSig() {
                $title = Title::newFromText( __FUNCTION__ );
                $outputText = $this->parser->cleanSig( "{{Foo}} ~~~~" );
 
@@ -71,8 +89,9 @@ class ExtraParserTest extends MediaWikiTestCase {
 
        /**
         * cleanSig() should do nothing if disabled
+        * @covers Parser::cleanSig
         */
-       function testCleanSigDisabled() {
+       public function testCleanSigDisabled() {
                $this->setMwGlobals( 'wgCleanSignatures', false );
 
                $title = Title::newFromText( __FUNCTION__ );
@@ -84,8 +103,9 @@ class ExtraParserTest extends MediaWikiTestCase {
        /**
         * cleanSigInSig() just removes tildes
         * @dataProvider provideStringsForCleanSigInSig
+        * @covers Parser::cleanSigInSig
         */
-       function testCleanSigInSig( $in, $out ) {
+       public function testCleanSigInSig( $in, $out ) {
                $this->assertEquals( Parser::cleanSigInSig( $in ), $out );
        }
 
@@ -97,7 +117,10 @@ class ExtraParserTest extends MediaWikiTestCase {
                );
        }
 
-       function testGetSection() {
+       /**
+        * @covers Parser::getSection
+        */
+       public function testGetSection() {
                $outputText2 = $this->parser->getSection( "Section 0\n== Heading 1 ==\nSection 1\n=== Heading 2 ===\nSection 2\n== Heading 3 ==\nSection 3\n", 2 );
                $outputText1 = $this->parser->getSection( "Section 0\n== Heading 1 ==\nSection 1\n=== Heading 2 ===\nSection 2\n== Heading 3 ==\nSection 3\n", 1 );
 
@@ -105,7 +128,10 @@ class ExtraParserTest extends MediaWikiTestCase {
                $this->assertEquals( "== Heading 1 ==\nSection 1\n=== Heading 2 ===\nSection 2", $outputText1 );
        }
 
-       function testReplaceSection() {
+       /**
+        * @covers Parser::replaceSection
+        */
+       public function testReplaceSection() {
                $outputText = $this->parser->replaceSection( "Section 0\n== Heading 1 ==\nSection 1\n=== Heading 2 ===\nSection 2\n== Heading 3 ==\nSection 3\n", 1, "New section 1" );
 
                $this->assertEquals( "Section 0\nNew section 1\n\n== Heading 3 ==\nSection 3", $outputText );
@@ -113,8 +139,9 @@ class ExtraParserTest extends MediaWikiTestCase {
 
        /**
         * Templates and comments are not affected, but noinclude/onlyinclude is.
+        * @covers Parser::getPreloadText
         */
-       function testGetPreloadText() {
+       public function testGetPreloadText() {
                $title = Title::newFromText( __FUNCTION__ );
                $outputText = $this->parser->getPreloadText( "{{Foo}}<noinclude> censored</noinclude> information <!-- is very secret -->", $title, $this->options );
 
@@ -133,8 +160,9 @@ class ExtraParserTest extends MediaWikiTestCase {
 
        /**
         * @group Database
+        * @covers Parser::parse
         */
-       function testTrackingCategory() {
+       public function testTrackingCategory() {
                $title = Title::newFromText( __FUNCTION__ );
                $catName = wfMessage( 'broken-file-category' )->inContentLanguage()->text();
                $cat = Title::makeTitleSafe( NS_CATEGORY, $catName );
@@ -146,8 +174,9 @@ class ExtraParserTest extends MediaWikiTestCase {
 
        /**
         * @group Database
+        * @covers Parser::parse
         */
-       function testTrackingCategorySpecial() {
+       public function testTrackingCategorySpecial() {
                // Special pages shouldn't have tracking cats.
                $title = SpecialPage::getTitleFor( 'Contributions' );
                $parserOutput = $this->parser->parse( "[[file:nonexistent]]", $title, $this->options );
diff --git a/tests/phpunit/includes/FallbackTest.php b/tests/phpunit/includes/FallbackTest.php
new file mode 100644 (file)
index 0000000..f408f47
--- /dev/null
@@ -0,0 +1,73 @@
+<?php
+
+/**
+ * @covers Fallback
+ */
+class FallbackTest extends MediaWikiTestCase {
+
+       public function testFallbackMbstringFunctions() {
+
+               if ( !extension_loaded( 'mbstring' ) ) {
+                       $this->markTestSkipped( "The mb_string functions must be installed to test the fallback functions" );
+               }
+
+               $sampleUTF = "Östergötland_coat_of_arms.png";
+
+               //mb_substr
+               $substr_params = array(
+                       array( 0, 0 ),
+                       array( 5, -4 ),
+                       array( 33 ),
+                       array( 100, -5 ),
+                       array( -8, 10 ),
+                       array( 1, 1 ),
+                       array( 2, -1 )
+               );
+
+               foreach ( $substr_params as $param_set ) {
+                       $old_param_set = $param_set;
+                       array_unshift( $param_set, $sampleUTF );
+
+                       $this->assertEquals(
+                               call_user_func_array( 'mb_substr', $param_set ),
+                               call_user_func_array( 'Fallback::mb_substr', $param_set ),
+                               'Fallback mb_substr with params ' . implode( ', ', $old_param_set )
+                       );
+               }
+
+               //mb_strlen
+               $this->assertEquals(
+                       mb_strlen( $sampleUTF ),
+                       Fallback::mb_strlen( $sampleUTF ),
+                       'Fallback mb_strlen'
+               );
+
+               //mb_str(r?)pos
+               $strpos_params = array(
+                       //array( 'ter' ),
+                       //array( 'Ö' ),
+                       //array( 'Ö', 3 ),
+                       //array( 'oat_', 100 ),
+                       //array( 'c', -10 ),
+                       //Broken for now
+               );
+
+               foreach ( $strpos_params as $param_set ) {
+                       $old_param_set = $param_set;
+                       array_unshift( $param_set, $sampleUTF );
+
+                       $this->assertEquals(
+                               call_user_func_array( 'mb_strpos', $param_set ),
+                               call_user_func_array( 'Fallback::mb_strpos', $param_set ),
+                               'Fallback mb_strpos with params ' . implode( ', ', $old_param_set )
+                       );
+
+                       $this->assertEquals(
+                               call_user_func_array( 'mb_strrpos', $param_set ),
+                               call_user_func_array( 'Fallback::mb_strrpos', $param_set ),
+                               'Fallback mb_strrpos with params ' . implode( ', ', $old_param_set )
+                       );
+               }
+       }
+
+}
\ No newline at end of file
index dfb0f13..3246410 100644 (file)
@@ -2,7 +2,11 @@
 
 class FauxRequestTest extends MediaWikiTestCase {
 
-       function testGetSetHeader() {
+       /**
+        * @covers FauxRequest::setHeader
+        * @covers FauxRequest::getHeader
+        */
+       public function testGetSetHeader() {
                $value = 'test/test';
 
                $request = new FauxRequest();
index 977c22b..7f41cfd 100644 (file)
  */
 
 class FauxResponseTest extends MediaWikiTestCase {
-       var $response;
+       /** @var FauxResponse */
+       protected $response;
 
        protected function setUp() {
                parent::setUp();
                $this->response = new FauxResponse;
        }
 
-       function testCookie() {
+       /**
+        * @covers FauxResponse::getcookie
+        * @covers FauxResponse::setcookie
+        */
+       public function testCookie() {
                $this->assertEquals( null, $this->response->getcookie( 'key' ), 'Non-existing cookie' );
                $this->response->setcookie( 'key', 'val' );
                $this->assertEquals( 'val', $this->response->getcookie( 'key' ), 'Existing cookie' );
        }
 
-       function testHeader() {
+       /**
+        * @covers FauxResponse::getheader
+        * @covers FauxResponse::header
+        */
+       public function testHeader() {
                $this->assertEquals( null, $this->response->getheader( 'Location' ), 'Non-existing header' );
 
                $this->response->header( 'Location: http://localhost/' );
@@ -52,7 +61,10 @@ class FauxResponseTest extends MediaWikiTestCase {
                $this->assertEquals( 'http://localhost/', $this->response->getheader( 'LOCATION' ), 'Get header case insensitive' );
        }
 
-       function testResponseCode() {
+       /**
+        * @covers FauxResponse::getStatusCode
+        */
+       public function testResponseCode() {
                $this->response->header( 'HTTP/1.1 200' );
                $this->assertEquals( 200, $this->response->getStatusCode(), 'Header with no message' );
 
index fb2304d..1531b56 100644 (file)
@@ -35,7 +35,6 @@ class FormOptionsInitializationTest extends MediaWikiTestCase {
         */
        protected $object;
 
-
        /**
         * A new fresh and empty FormOptions object to test initialization
         * with.
@@ -45,6 +44,9 @@ class FormOptionsInitializationTest extends MediaWikiTestCase {
                $this->object = new FormOptionsExposed();
        }
 
+       /**
+        * @covers FormOptionsExposed::add
+        */
        public function testAddStringOption() {
                $this->object->add( 'foo', 'string value' );
                $this->assertEquals(
@@ -60,6 +62,9 @@ class FormOptionsInitializationTest extends MediaWikiTestCase {
                );
        }
 
+       /**
+        * @covers FormOptionsExposed::add
+        */
        public function testAddIntegers() {
                $this->object->add( 'one', 1 );
                $this->object->add( 'negone', -1 );
index 0a13cfe..08d6ba8 100644 (file)
@@ -60,6 +60,7 @@ class FormOptionsTest extends MediaWikiTestCase {
 
        /**
         * Reuse helpers above assertGuessBoolean assertGuessInt assertGuessString
+        * @covers FormOptions::guessType
         */
        public function testGuessTypeDetection() {
                $this->assertGuessBoolean( true );
@@ -78,12 +79,14 @@ class FormOptionsTest extends MediaWikiTestCase {
 
        /**
         * @expectedException MWException
+        * @covers FormOptions::guessType
         */
        public function testGuessTypeOnArrayThrowException() {
                $this->object->guessType( array( 'foo' ) );
        }
        /**
         * @expectedException MWException
+        * @covers FormOptions::guessType
         */
        public function testGuessTypeOnNullThrowException() {
                $this->object->guessType( null );
index 244b100..6154df1 100644 (file)
@@ -29,7 +29,10 @@ class GlobalTest extends MediaWikiTestCase {
                parent::tearDown();
        }
 
-       /** @dataProvider provideForWfArrayDiff2 */
+       /**
+        * @dataProvider provideForWfArrayDiff2
+        * @covers ::wfArrayDiff2
+        */
        public function testWfArrayDiff2( $a, $b, $expected ) {
                $this->assertEquals(
                        wfArrayDiff2( $a, $b ), $expected
@@ -53,25 +56,37 @@ class GlobalTest extends MediaWikiTestCase {
                );
        }
 
-       function testRandom() {
+       /**
+        * @covers ::wfRandom
+        */
+       public function testRandom() {
                # This could hypothetically fail, but it shouldn't ;)
                $this->assertFalse(
                        wfRandom() == wfRandom() );
        }
 
-       function testUrlencode() {
+       /**
+        * @covers ::wfUrlencode
+        */
+       public function testUrlencode() {
                $this->assertEquals(
                        "%E7%89%B9%E5%88%A5:Contributions/Foobar",
                        wfUrlencode( "\xE7\x89\xB9\xE5\x88\xA5:Contributions/Foobar" ) );
        }
 
-       function testExpandIRI() {
+       /**
+        * @covers ::wfExpandIRI
+        */
+       public function testExpandIRI() {
                $this->assertEquals(
                        "https://te.wikibooks.org/wiki/ఉబుంటు_వాడుకరి_మార్గదర్శని",
                        wfExpandIRI( "https://te.wikibooks.org/wiki/%E0%B0%89%E0%B0%AC%E0%B1%81%E0%B0%82%E0%B0%9F%E0%B1%81_%E0%B0%B5%E0%B0%BE%E0%B0%A1%E0%B1%81%E0%B0%95%E0%B0%B0%E0%B0%BF_%E0%B0%AE%E0%B0%BE%E0%B0%B0%E0%B1%8D%E0%B0%97%E0%B0%A6%E0%B0%B0%E0%B1%8D%E0%B0%B6%E0%B0%A8%E0%B0%BF" ) );
        }
 
-       function testReadOnlyEmpty() {
+       /**
+        * @covers ::wfReadOnly
+        */
+       public function testReadOnlyEmpty() {
                global $wgReadOnly;
                $wgReadOnly = null;
 
@@ -79,7 +94,10 @@ class GlobalTest extends MediaWikiTestCase {
                $this->assertFalse( wfReadOnly() );
        }
 
-       function testReadOnlySet() {
+       /**
+        * @covers ::wfReadOnly
+        */
+       public function testReadOnlySet() {
                global $wgReadOnly, $wgReadOnlyFile;
 
                $f = fopen( $wgReadOnlyFile, "wt" );
@@ -97,12 +115,6 @@ class GlobalTest extends MediaWikiTestCase {
                $this->assertFalse( wfReadOnly() );
        }
 
-       function testQuotedPrintable() {
-               $this->assertEquals(
-                       "=?UTF-8?Q?=C4=88u=20legebla=3F?=",
-                       UserMailer::quotedPrintable( "\xc4\x88u legebla?", "UTF-8" ) );
-       }
-
        public static function provideArrayToCGI() {
                return array(
                        array( array(), '' ), // empty
@@ -123,13 +135,17 @@ class GlobalTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provideArrayToCGI
+        * @covers ::wfArrayToCgi
         */
-       function testArrayToCGI( $array, $result ) {
+       public function testArrayToCGI( $array, $result ) {
                $this->assertEquals( $result, wfArrayToCgi( $array ) );
        }
 
 
-       function testArrayToCGI2() {
+       /**
+        * @covers ::testWfArrayDiff2
+        */
+       public function testArrayToCGI2() {
                $this->assertEquals(
                        "baz=bar&foo=bar",
                        wfArrayToCgi(
@@ -154,8 +170,9 @@ class GlobalTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provideCgiToArray
+        * @covers ::wfCgiToArray
         */
-       function testCgiToArray( $cgi, $result ) {
+       public function testCgiToArray( $cgi, $result ) {
                $this->assertEquals( $result, wfCgiToArray( $cgi ) );
        }
 
@@ -174,12 +191,16 @@ class GlobalTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provideCgiRoundTrip
+        * @covers ::wfArrayToCgi
         */
-       function testCgiRoundTrip( $cgi ) {
+       public function testCgiRoundTrip( $cgi ) {
                $this->assertEquals( $cgi, wfArrayToCgi( wfCgiToArray( $cgi ) ) );
        }
 
-       function testMimeTypeMatch() {
+       /**
+        * @covers ::mimeTypeMatch
+        */
+       public function testMimeTypeMatch() {
                $this->assertEquals(
                        'text/html',
                        mimeTypeMatch( 'text/html',
@@ -201,7 +222,10 @@ class GlobalTest extends MediaWikiTestCase {
                                        'image/svg+xml' => 0.5 ) ) );
        }
 
-       function testNegotiateType() {
+       /**
+        * @covers ::wfNegotiateType
+        */
+       public function testNegotiateType() {
                $this->assertEquals(
                        'text/html',
                        wfNegotiateType(
@@ -242,73 +266,11 @@ class GlobalTest extends MediaWikiTestCase {
                                array( 'application/xhtml+xml' => 1.0 ) ) );
        }
 
-       function testFallbackMbstringFunctions() {
-
-               if ( !extension_loaded( 'mbstring' ) ) {
-                       $this->markTestSkipped( "The mb_string functions must be installed to test the fallback functions" );
-               }
-
-               $sampleUTF = "Östergötland_coat_of_arms.png";
-
-               //mb_substr
-               $substr_params = array(
-                       array( 0, 0 ),
-                       array( 5, -4 ),
-                       array( 33 ),
-                       array( 100, -5 ),
-                       array( -8, 10 ),
-                       array( 1, 1 ),
-                       array( 2, -1 )
-               );
-
-               foreach ( $substr_params as $param_set ) {
-                       $old_param_set = $param_set;
-                       array_unshift( $param_set, $sampleUTF );
-
-                       $this->assertEquals(
-                               call_user_func_array( 'mb_substr', $param_set ),
-                               call_user_func_array( 'Fallback::mb_substr', $param_set ),
-                               'Fallback mb_substr with params ' . implode( ', ', $old_param_set )
-                       );
-               }
-
-               //mb_strlen
-               $this->assertEquals(
-                       mb_strlen( $sampleUTF ),
-                       Fallback::mb_strlen( $sampleUTF ),
-                       'Fallback mb_strlen'
-               );
-
-               //mb_str(r?)pos
-               $strpos_params = array(
-                       //array( 'ter' ),
-                       //array( 'Ö' ),
-                       //array( 'Ö', 3 ),
-                       //array( 'oat_', 100 ),
-                       //array( 'c', -10 ),
-                       //Broken for now
-               );
-
-               foreach ( $strpos_params as $param_set ) {
-                       $old_param_set = $param_set;
-                       array_unshift( $param_set, $sampleUTF );
-
-                       $this->assertEquals(
-                               call_user_func_array( 'mb_strpos', $param_set ),
-                               call_user_func_array( 'Fallback::mb_strpos', $param_set ),
-                               'Fallback mb_strpos with params ' . implode( ', ', $old_param_set )
-                       );
-
-                       $this->assertEquals(
-                               call_user_func_array( 'mb_strrpos', $param_set ),
-                               call_user_func_array( 'Fallback::mb_strrpos', $param_set ),
-                               'Fallback mb_strrpos with params ' . implode( ', ', $old_param_set )
-                       );
-               }
-       }
-
-
-       function testDebugFunctionTest() {
+       /**
+        * @covers ::wfDebug
+        * @covers ::wfDebugMem
+        */
+       public function testDebugFunctionTest() {
 
                global $wgDebugLogFile, $wgDebugTimestamps;
 
@@ -342,7 +304,10 @@ class GlobalTest extends MediaWikiTestCase {
                $wgDebugTimestamps = $old_wgDebugTimestamps;
        }
 
-       function testClientAcceptsGzipTest() {
+       /**
+        * @covers ::wfClientAcceptsGzip
+        */
+       public function testClientAcceptsGzipTest() {
 
                $settings = array(
                        'gzip' => true,
@@ -373,7 +338,10 @@ class GlobalTest extends MediaWikiTestCase {
                }
        }
 
-       function testSwapVarsTest() {
+       /**
+        * @covers ::swap
+        */
+       public function testSwapVarsTest() {
                $var1 = 1;
                $var2 = 2;
 
@@ -386,7 +354,10 @@ class GlobalTest extends MediaWikiTestCase {
                $this->assertEquals( $var2, 1, 'var2 is swapped' );
        }
 
-       function testWfPercentTest() {
+       /**
+        * @covers ::wfPercent
+        */
+       public function testWfPercentTest() {
 
                $pcts = array(
                        array( 6 / 7, '0.86%', 2, false ),
@@ -414,6 +385,7 @@ class GlobalTest extends MediaWikiTestCase {
        /**
         * test @see wfShorthandToInteger()
         * @dataProvider provideShorthand
+        * @covers ::wfShorthandToInteger
         */
        public function testWfShorthandToInteger( $shorthand, $expected ) {
                $this->assertEquals( $expected,
@@ -474,6 +446,7 @@ class GlobalTest extends MediaWikiTestCase {
         *
         * @dataProvider provideMerge()
         * @group medium
+        * @covers ::wfMerge
         */
        public function testMerge( $old, $mine, $yours, $expectedMergeResult, $expectedText ) {
                $this->checkHasDiff3();
@@ -549,8 +522,9 @@ class GlobalTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provideMakeUrlIndexes()
+        * @covers ::wfMakeUrlIndexes
         */
-       function testMakeUrlIndexes( $url, $expected ) {
+       public function testMakeUrlIndexes( $url, $expected ) {
                $index = wfMakeUrlIndexes( $url );
                $this->assertEquals( $expected, $index, "wfMakeUrlIndexes(\"$url\")" );
        }
@@ -606,8 +580,9 @@ class GlobalTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provideWfMatchesDomainList
+        * @covers ::wfMatchesDomainList
         */
-       function testWfMatchesDomainList( $url, $domains, $expected, $description ) {
+       public function testWfMatchesDomainList( $url, $domains, $expected, $description ) {
                $actual = wfMatchesDomainList( $url, $domains );
                $this->assertEquals( $expected, $actual, $description );
        }
@@ -630,7 +605,10 @@ class GlobalTest extends MediaWikiTestCase {
                return $a;
        }
 
-       function testWfMkdirParents() {
+       /**
+        * @covers ::wfMkdirParents
+        */
+       public function testWfMkdirParents() {
                // Should not return true if file exists instead of directory
                $fname = $this->getNewTempFile();
                wfSuppressWarnings();
@@ -641,8 +619,9 @@ class GlobalTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provideWfShellMaintenanceCmdList
+        * @covers ::wfShellMaintenanceCmd
         */
-       function testWfShellMaintenanceCmd( $script, $parameters, $options, $expected, $description ) {
+       public function testWfShellMaintenanceCmd( $script, $parameters, $options, $expected, $description ) {
                if ( wfIsWindows() ) {
                        // Approximation that's good enough for our purposes just now
                        $expected = str_replace( "'", '"', $expected );
@@ -669,5 +648,5 @@ class GlobalTest extends MediaWikiTestCase {
                                "Called eval.php --help --test with wrapper and php option" ),
                );
        }
-       /* TODO: many more! */
+       /* @TODO many more! */
 }
index 8bd0849..cf891e7 100644 (file)
@@ -6,8 +6,9 @@
 class GlobalWithDBTest extends MediaWikiTestCase {
        /**
         * @dataProvider provideWfIsBadImageList
+        * @covers ::wfIsBadImage
         */
-       function testWfIsBadImage( $name, $title, $blacklist, $expected, $desc ) {
+       public function testWfIsBadImage( $name, $title, $blacklist, $expected, $desc ) {
                $this->assertEquals( $expected, wfIsBadImage( $name, $title, $blacklist ), $desc );
        }
 
index 4184d15..9bb7487 100644 (file)
@@ -1,9 +1,11 @@
 <?php
 /**
- * Tests for wfAssembleUrl()
+ * @covers ::wfAssembleUrl
  */
 class WfAssembleUrlTest extends MediaWikiTestCase {
-       /** @dataProvider provideURLParts */
+       /**
+        * @dataProvider provideURLParts
+        */
        public function testWfAssembleUrl( $parts, $output ) {
                $partsDump = print_r( $parts, true );
                $this->assertEquals(
index baf9cee..a01c0d4 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /**
- * Tests for wfBCP47()
+ * @covers ::wfBCP47
  */
 class WfBCP47Test extends MediaWikiTestCase {
        /**
@@ -13,7 +13,7 @@ class WfBCP47Test extends MediaWikiTestCase {
         * @see http://tools.ietf.org/html/bcp47
         * @dataProvider provideLanguageCodes()
         */
-       function testBCP47( $code, $expected ) {
+       public function testBCP47( $code, $expected ) {
                $code = strtolower( $code );
                $this->assertEquals( $expected, wfBCP47( $code ),
                        "Applying BCP47 standard to lower case '$code'"
index c60f223..7da804e 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /**
- * Tests for wfBaseConvert()
+ * @covers ::wfBaseConvert
  */
 class WfBaseConvertTest extends MediaWikiTestCase {
        public static function provideSingleDigitConversions() {
index 3c4fa20..8c54804 100644 (file)
@@ -1,12 +1,12 @@
 <?php
 /**
- * Tests for wfBaseName()
+ * @covers ::wfBaseName
  */
 class WfBaseNameTest extends MediaWikiTestCase {
        /**
         * @dataProvider providePaths
         */
-       function testBaseName( $fullpath, $basename ) {
+       public function testBaseName( $fullpath, $basename ) {
                $this->assertEquals( $basename, wfBaseName( $fullpath ),
                        "wfBaseName('$fullpath') => '$basename'" );
        }
index 5b622c1..41230a1 100644 (file)
@@ -1,9 +1,11 @@
 <?php
 /**
- * Tests for wfExpandUrl()
+ * @covers ::wfExpandUrl
  */
 class WfExpandUrlTest extends MediaWikiTestCase {
-       /** @dataProvider provideExpandableUrls */
+       /**
+        * @dataProvider provideExpandableUrls
+        */
        public function testWfExpandUrl( $fullUrl, $shortUrl, $defaultProto, $server, $canServer, $httpsMode, $message ) {
                // Fake $wgServer and $wgCanonicalServer
                $this->setMwGlobals( array(
index 3521d18..6229624 100644 (file)
@@ -1,8 +1,11 @@
 <?php
 
+/**
+ * @covers ::wfGetCaller
+ */
 class WfGetCallerTest extends MediaWikiTestCase {
 
-       function testZero() {
+       public function testZero() {
                $this->assertEquals( __METHOD__, wfGetCaller( 1 ) );
        }
 
@@ -10,7 +13,7 @@ class WfGetCallerTest extends MediaWikiTestCase {
                return wfGetCaller();
        }
 
-       function testOne() {
+       public function testOne() {
                $this->assertEquals( 'WfGetCallerTest::testOne', self::callerOne() );
        }
 
@@ -22,11 +25,11 @@ class WfGetCallerTest extends MediaWikiTestCase {
                return wfGetCaller( $level );
        }
 
-       function testTwo() {
+       public function testTwo() {
                $this->assertEquals( 'WfGetCallerTest::testTwo', self::intermediateFunction() );
        }
 
-       function testN() {
+       public function testN() {
                $this->assertEquals( 'WfGetCallerTest::testN', self::intermediateFunction( 2, 0 ) );
                $this->assertEquals( 'WfGetCallerTest::intermediateFunction', self::intermediateFunction( 1, 0 ) );
 
index 841a1b1..5032dc1 100644 (file)
@@ -1,7 +1,5 @@
 <?php
 /**
- * Tests for wfParseUrl()
- *
  * Copyright © 2013 Alexandre Emsenhuber
  *
  * This program is free software; you can redistribute it and/or modify
@@ -22,6 +20,9 @@
  * @file
  */
 
+/**
+ * @covers ::wfParseUrl
+ */
 class WfParseUrlTest extends MediaWikiTestCase {
        protected function setUp() {
                parent::setUp();
@@ -31,7 +32,9 @@ class WfParseUrlTest extends MediaWikiTestCase {
                ) );
        }
 
-       /** @dataProvider provideURLs */
+       /**
+        * @dataProvider provideURLs
+        */
        public function testWfParseUrl( $url, $parts ) {
                $partsDump = var_export( $parts, true );
                $this->assertEquals(
index 67861ee..238a2c9 100644 (file)
@@ -1,9 +1,11 @@
 <?php
 /**
- * Tests for wfRemoveDotSegments()
+ *@covers ::wfRemoveDotSegments
  */
 class WfRemoveDotSegmentsTest extends MediaWikiTestCase {
-       /** @dataProvider providePaths */
+       /**
+        * @dataProvider providePaths
+        */
        public function testWfRemoveDotSegments( $inputPath, $outputPath ) {
                $this->assertEquals(
                        $outputPath,
index 604f901..aadec87 100644 (file)
@@ -1,10 +1,13 @@
 <?php
 
+/**
+ * @covers ::wfShorthandToInteger
+ */
 class WfShorthandToIntegerTest extends MediaWikiTestCase {
        /**
         * @dataProvider provideABunchOfShorthands
         */
-       function testWfShorthandToInteger( $input, $output, $description ) {
+       public function testWfShorthandToInteger( $input, $output, $description ) {
                $this->assertEquals(
                        wfShorthandToInteger( $input ),
                        $output,
index 32bb49d..5998f18 100644 (file)
@@ -1,12 +1,12 @@
 <?php
 /*
- * Tests for wfTimestamp()
+ * @covers ::wfTimestamp
  */
 class WfTimestampTest extends MediaWikiTestCase {
        /**
         * @dataProvider provideNormalTimestamps
         */
-       function testNormalTimestamps( $input, $format, $output, $desc ) {
+       public function testNormalTimestamps( $input, $format, $output, $desc ) {
                $this->assertEquals( $output, wfTimestamp( $format, $input ), $desc );
        }
 
@@ -57,7 +57,7 @@ class WfTimestampTest extends MediaWikiTestCase {
         * See r74778 and bug 25451
         * @dataProvider provideOldTimestamps
         */
-       function testOldTimestamps( $input, $format, $output, $desc ) {
+       public function testOldTimestamps( $input, $format, $output, $desc ) {
                $this->assertEquals( $output, wfTimestamp( $format, $input ), $desc );
        }
 
@@ -96,7 +96,7 @@ class WfTimestampTest extends MediaWikiTestCase {
         * @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1
         * @dataProvider provideHttpDates
         */
-       function testHttpDate( $input, $output, $desc ) {
+       public function testHttpDate( $input, $output, $desc ) {
                $this->assertEquals( $output, wfTimestamp( TS_MW, $input ), $desc );
        }
 
@@ -114,7 +114,7 @@ class WfTimestampTest extends MediaWikiTestCase {
         * There are a number of assumptions in our codebase where wfTimestamp()
         * should give the current date but it is not given a 0 there. See r71751 CR
         */
-       function testTimestampParameter() {
+       public function testTimestampParameter() {
                $now = wfTimestamp( TS_UNIX );
                // We check that wfTimestamp doesn't return false (error) and use a LessThan assert
                // for the cases where the test is run in a second boundary.
index 77685d5..ce6c82c 100644 (file)
@@ -1,18 +1,21 @@
 <?php
 /**
- * Tests for wfUrlencode()
- *
  * The function only need a string parameter and might react to IIS7.0
+ * @covers ::wfUrlencode
  */
 class WfUrlencodeTest extends MediaWikiTestCase {
        #### TESTS ##############################################################
 
-       /** @dataProvider provideURLS */
+       /**
+        * @dataProvider provideURLS
+        */
        public function testEncodingUrlWith( $input, $expected ) {
                $this->verifyEncodingFor( 'Apache', $input, $expected );
        }
 
-       /** @dataProvider provideURLS */
+       /**
+        * @dataProvider provideURLS
+        */
        public function testEncodingUrlWithMicrosoftIis7( $input, $expected ) {
                $this->verifyEncodingFor( 'Microsoft-IIS/7', $input, $expected );
        }
index 5bbafd3..39c3959 100644 (file)
@@ -1,7 +1,8 @@
 <?php
 
 /**
- * Unit tests for the HTMLCheckMatrix form field
+ * Unit tests for the HTMLCheckMatrix + HTMLFormField
+ * @todo the tests for the two classes could be split up
  */
 class HtmlCheckMatrixTest extends MediaWikiTestCase {
        static private $defaultOptions = array(
@@ -10,6 +11,9 @@ class HtmlCheckMatrixTest extends MediaWikiTestCase {
                'fieldname' => 'test',
        );
 
+       /**
+        * @covers HTMLCheckMatrix::__construct
+        */
        public function testPlainInstantiation() {
                try {
                        $form = new HTMLCheckMatrix( array() );
@@ -21,11 +25,17 @@ class HtmlCheckMatrixTest extends MediaWikiTestCase {
                $this->fail( 'Expected MWException indicating missing parameters but none was thrown.' );
        }
 
+       /**
+        * @covers HTMLCheckMatrix::__construct
+        */
        public function testInstantiationWithMinimumRequiredParameters() {
                $form = new HTMLCheckMatrix( self::$defaultOptions );
                $this->assertTrue( true ); // form instantiation must throw exception on failure
        }
 
+       /**
+        * @covers HTMLFormField::validate
+        */
        public function testValidateCallsUserDefinedValidationCallback() {
                $called = false;
                $field = new HTMLCheckMatrix( self::$defaultOptions + array(
@@ -38,6 +48,9 @@ class HtmlCheckMatrixTest extends MediaWikiTestCase {
                $this->assertTrue( $called );
        }
 
+       /**
+        * @covers HTMLFormField::validate
+        */
        public function testValidateRequiresArrayInput() {
                $field = new HTMLCheckMatrix( self::$defaultOptions );
                $this->assertEquals( false, $this->validate( $field, null ) );
@@ -47,11 +60,17 @@ class HtmlCheckMatrixTest extends MediaWikiTestCase {
                $this->assertEquals( true, $this->validate( $field, array() ) );
        }
 
+       /**
+        * @covers HTMLFormField::validate
+        */
        public function testValidateAllowsOnlyKnownTags() {
                $field = new HTMLCheckMatrix( self::$defaultOptions );
                $this->assertInternalType( 'string', $this->validate( $field, array( 'foo' ) ) );
        }
 
+       /**
+        * @covers HTMLFormField::validate
+        */
        public function testValidateAcceptsPartialTagList() {
                $field = new HTMLCheckMatrix( self::$defaultOptions );
                $this->assertTrue( $this->validate( $field, array() ) );
@@ -65,6 +84,7 @@ class HtmlCheckMatrixTest extends MediaWikiTestCase {
         * foreach ( $field->filterDataForSubmit( $data ) as $k => $v ) {
         *     $user->setOption( $k, $v );
         * }
+        * @covers HTMLFormField::filterDataForSubmit
         */
        public function testValuesForcedOnRemainOn() {
                $field = new HTMLCheckMatrix( self::$defaultOptions + array(
@@ -79,6 +99,9 @@ class HtmlCheckMatrixTest extends MediaWikiTestCase {
                $this->assertEquals( $expected, $field->filterDataForSubmit( array() ) );
        }
 
+       /**
+        * @covers HTMLFormField::filterDataForSubmit
+        */
        public function testValuesForcedOffRemainOff() {
                $field = new HTMLCheckMatrix( self::$defaultOptions + array(
                        'force-options-off' => array( 'c1-r2', 'c2-r2' ),
index bc4e499..68dfea1 100644 (file)
@@ -4,7 +4,10 @@
  * @group HashRing
  */
 class HashRingTest extends MediaWikiTestCase {
-       function testHashRing() {
+       /**
+        * @covers HashRing
+        */
+       public function testHashRing() {
                $ring = new HashRing( array( 's1' => 1, 's2' => 1, 's3' => 2, 's4' => 2, 's5' => 2, 's6' => 3 ) );
 
                $locations = array();
index 81dd487..87af6c1 100644 (file)
@@ -35,6 +35,7 @@ class HooksTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provideHooks
+        * @covers ::wfRunHooks
         */
        public function testOldStyleHooks( $msg, array $hook, $expectedFoo, $expectedBar ) {
                global $wgHooks;
@@ -49,6 +50,8 @@ class HooksTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provideHooks
+        * @covers Hooks::register
+        * @covers Hooks::run
         */
        public function testNewStyleHooks( $msg, $hook, $expectedFoo, $expectedBar ) {
                $foo = $bar = 'original';
@@ -60,6 +63,12 @@ class HooksTest extends MediaWikiTestCase {
                $this->assertSame( $expectedBar, $bar, $msg );
        }
 
+       /**
+        * @covers Hooks::isRegistered
+        * @covers Hooks::register
+        * @covers Hooks::getHandlers
+        * @covers Hooks::run
+        */
        public function testNewStyleHookInteraction() {
                global $wgHooks;
 
@@ -82,12 +91,16 @@ class HooksTest extends MediaWikiTestCase {
 
        /**
         * @expectedException MWException
+        * @covers Hooks::run
         */
        public function testUncallableFunction() {
                Hooks::register( 'MediaWikiHooksTest001', 'ThisFunctionDoesntExist' );
                Hooks::run( 'MediaWikiHooksTest001', array() );
        }
 
+       /**
+        * @covers Hooks::run
+        */
        public function testFalseReturn() {
                Hooks::register( 'MediaWikiHooksTest001', function ( &$foo ) {
                        return false;
@@ -104,6 +117,7 @@ class HooksTest extends MediaWikiTestCase {
 
        /**
         * @expectedException FatalError
+        * @covers Hooks::run
         */
        public function testFatalError() {
                Hooks::register( 'MediaWikiHooksTest001', function () {
index a37df74..7ef0b60 100644 (file)
@@ -6,6 +6,7 @@
 class HtmlFormatterTest extends MediaWikiTestCase {
        /**
         * @dataProvider getHtmlData
+        * @covers HtmlFormatter::getText
         */
        public function testTransform( $input, $expected, $callback = false ) {
                $input = self::normalize( $input );
index ecfe418..21372a0 100644 (file)
@@ -41,6 +41,9 @@ class HtmlTest extends MediaWikiTestCase {
                ) );
        }
 
+       /**
+        * @covers Html::element
+        */
        public function testElementBasics() {
                $this->assertEquals(
                        '<img>',
@@ -89,11 +92,15 @@ class HtmlTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider dataXmlMimeType
+        * @covers Html::isXmlMimeType
         */
        public function testXmlMimeType( $mimetype, $isXmlMimeType ) {
                $this->assertEquals( $isXmlMimeType, Html::isXmlMimeType( $mimetype ) );
        }
 
+       /**
+        * @covers HTML::expandAttributes
+        */
        public function testExpandAttributesSkipsNullAndFalse() {
 
                ### EMPTY ########
@@ -111,6 +118,9 @@ class HtmlTest extends MediaWikiTestCase {
                );
        }
 
+       /**
+        * @covers HTML::expandAttributes
+        */
        public function testExpandAttributesForBooleans() {
                $this->assertEquals(
                        '',
@@ -146,6 +156,7 @@ class HtmlTest extends MediaWikiTestCase {
        /**
         * Test for Html::expandAttributes()
         * Please note it output a string prefixed with a space!
+        * @covers Html::expandAttributes
         */
        public function testExpandAttributesVariousExpansions() {
                ### NOT EMPTY ####
@@ -198,6 +209,7 @@ class HtmlTest extends MediaWikiTestCase {
         * Html::expandAttributes has special features for HTML
         * attributes that use space separated lists and also
         * allows arrays to be used as values.
+        * @covers Html::expandAttributes
         */
        public function testExpandAttributesListValueAttributes() {
                ### STRING VALUES
@@ -249,8 +261,9 @@ class HtmlTest extends MediaWikiTestCase {
        /**
         * Test feature added by r96188, let pass attributes values as
         * a PHP array. Restricted to class,rel, accesskey.
+        * @covers Html::expandAttributes
         */
-       function testExpandAttributesSpaceSeparatedAttributesWithBoolean() {
+       public function testExpandAttributesSpaceSeparatedAttributesWithBoolean() {
                $this->assertEquals(
                        ' class="booltrue one"',
                        Html::expandAttributes( array( 'class' => array(
@@ -273,8 +286,9 @@ class HtmlTest extends MediaWikiTestCase {
         * The later will take precedence.
         *
         * Feature added by r96188
+        * @covers Html::expandAttributes
         */
-       function testValueIsAuthoritativeInSpaceSeparatedAttributesArrays() {
+       public function testValueIsAuthoritativeInSpaceSeparatedAttributesArrays() {
                $this->assertEquals(
                        ' class=""',
                        Html::expandAttributes( array( 'class' => array(
@@ -285,7 +299,10 @@ class HtmlTest extends MediaWikiTestCase {
                );
        }
 
-       function testNamespaceSelector() {
+       /**
+        * @covers Html::namespaceSelector
+        */
+       public function testNamespaceSelector() {
                $this->assertEquals(
                        '<select id=namespace name=namespace>' . "\n" .
                                '<option value=0>(Main)</option>' . "\n" .
@@ -364,7 +381,7 @@ class HtmlTest extends MediaWikiTestCase {
                );
        }
 
-       function testCanFilterOutNamespaces() {
+       public function testCanFilterOutNamespaces() {
                $this->assertEquals(
                        '<select id=namespace name=namespace>' . "\n" .
                                '<option value=2>User</option>' . "\n" .
@@ -386,7 +403,7 @@ class HtmlTest extends MediaWikiTestCase {
                );
        }
 
-       function testCanDisableANamespaces() {
+       public function testCanDisableANamespaces() {
                $this->assertEquals(
                        '<select id=namespace name=namespace>' . "\n" .
                                '<option disabled value=0>(Main)</option>' . "\n" .
@@ -415,8 +432,9 @@ class HtmlTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provideHtml5InputTypes
+        * @covers Html::element
         */
-       function testHtmlElementAcceptsNewHtml5TypesInHtml5Mode( $HTML5InputType ) {
+       public function testHtmlElementAcceptsNewHtml5TypesInHtml5Mode( $HTML5InputType ) {
                $this->assertEquals(
                        '<input type=' . $HTML5InputType . '>',
                        Html::element( 'input', array( 'type' => $HTML5InputType ) ),
@@ -457,7 +475,7 @@ class HtmlTest extends MediaWikiTestCase {
         * @covers Html::dropDefaults
         * @dataProvider provideElementsWithAttributesHavingDefaultValues
         */
-       function testDropDefaults( $expected, $element, $attribs, $message = '' ) {
+       public function testDropDefaults( $expected, $element, $attribs, $message = '' ) {
                $this->assertEquals( $expected, Html::element( $element, $attribs ), $message );
        }
 
@@ -617,6 +635,9 @@ class HtmlTest extends MediaWikiTestCase {
                return $ret;
        }
 
+       /**
+        * @covers Html::expandAttributes
+        */
        public function testFormValidationBlacklist() {
                $this->assertEmpty(
                        Html::expandAttributes( array( 'min' => 1, 'max' => 100, 'pattern' => 'abc', 'required' => true, 'step' => 2 ) ),
index d8a0f74..11d8ed6 100644 (file)
@@ -5,8 +5,9 @@
 class HttpTest extends MediaWikiTestCase {
        /**
         * @dataProvider cookieDomains
+        * @covers Cookie::validateCookieDomain
         */
-       function testValidateCookieDomain( $expected, $domain, $origin = null ) {
+       public function testValidateCookieDomain( $expected, $domain, $origin = null ) {
                if ( $origin ) {
                        $ok = Cookie::validateCookieDomain( $domain, $origin );
                        $msg = "$domain against origin $origin";
@@ -50,8 +51,9 @@ class HttpTest extends MediaWikiTestCase {
         * Test Http::isValidURI()
         * @bug 27854 : Http::isValidURI is too lax
         * @dataProvider provideURI
+        * @covers Http::isValidURI
         */
-       function testIsValidUri( $expect, $URI, $message = '' ) {
+       public function testIsValidUri( $expect, $URI, $message = '' ) {
                $this->assertEquals(
                        $expect,
                        (bool)Http::isValidURI( $URI ),
@@ -132,7 +134,7 @@ class HttpTest extends MediaWikiTestCase {
         * rewritten when bug 29232 is taken care of (high-level handling of
         * HTTP redirects).
         */
-       function testRelativeRedirections() {
+       public function testRelativeRedirections() {
                $h = MWHttpRequestTester::factory( 'http://oldsite/file.ext' );
 
                # Forge a Location header
index e18295f..c074eea 100644 (file)
@@ -1,7 +1,12 @@
 <?php
 /**
- * Tests for IP validity functions. Ported from /t/inc/IP.t by avar.
+ * Tests for IP validity functions.
+ *
+ * Ported from /t/inc/IP.t by avar.
+ *
  * @group IP
+ * @todo Test methods in this call should be split into a method and a
+ * dataprovider.
  */
 
 class IPTest extends MediaWikiTestCase {
@@ -387,7 +392,7 @@ class IPTest extends MediaWikiTestCase {
         * representing the network mask and the bit mask.
         * @covers IP::parseCIDR
         */
-       function testCIDRParsing() {
+       public function testCIDRParsing() {
                $this->assertFalseCIDR( '192.0.2.0', "missing mask" );
                $this->assertFalseCIDR( '192.0.2.0/', "missing bitmask" );
 
@@ -484,7 +489,7 @@ class IPTest extends MediaWikiTestCase {
         * Test for IP::splitHostAndPort().
         * @dataProvider provideSplitHostAndPort
         */
-       function testSplitHostAndPort( $expected, $input, $description ) {
+       public function testSplitHostAndPort( $expected, $input, $description ) {
                $this->assertEquals( $expected, IP::splitHostAndPort( $input ), $description );
        }
 
@@ -511,7 +516,7 @@ class IPTest extends MediaWikiTestCase {
         * Test for IP::combineHostAndPort()
         * @dataProvider provideCombineHostAndPort
         */
-       function testCombineHostAndPort( $expected, $input, $description ) {
+       public function testCombineHostAndPort( $expected, $input, $description ) {
                list( $host, $port, $defaultPort ) = $input;
                $this->assertEquals(
                        $expected,
@@ -535,7 +540,7 @@ class IPTest extends MediaWikiTestCase {
         * Test for IP::sanitizeRange()
         * @dataProvider provideIPCIDRs
         */
-       function testSanitizeRange( $input, $expected, $description ) {
+       public function testSanitizeRange( $input, $expected, $description ) {
                $this->assertEquals( $expected, IP::sanitizeRange( $input ), $description );
        }
 
@@ -559,7 +564,7 @@ class IPTest extends MediaWikiTestCase {
         * Test for IP::prettifyIP()
         * @dataProvider provideIPsToPrettify
         */
-       function testPrettifyIP( $ip, $prettified ) {
+       public function testPrettifyIP( $ip, $prettified ) {
                $this->assertEquals( $prettified, IP::prettifyIP( $ip ), "Prettify of $ip" );
        }
 
index 070a680..d4ccca9 100644 (file)
@@ -1,7 +1,9 @@
 <?php
 
 class LanguageConverterTest extends MediaWikiLangTestCase {
+       /** @var LanguageToTest */
        protected $lang = null;
+       /** @var TestConverter */
        protected $lc = null;
 
        protected function setUp() {
@@ -30,39 +32,61 @@ class LanguageConverterTest extends MediaWikiLangTestCase {
                parent::tearDown();
        }
 
-       function testGetPreferredVariantDefaults() {
+       /**
+        * @covers LanguageConverter::getPreferredVariant
+        */
+       public function testGetPreferredVariantDefaults() {
                $this->assertEquals( 'tg', $this->lc->getPreferredVariant() );
        }
 
-       function testGetPreferredVariantHeaders() {
+       /**
+        * @covers LanguageConverter::getPreferredVariant
+        * @covers LanguageConverter::getHeaderVariant
+        */
+       public function testGetPreferredVariantHeaders() {
                global $wgRequest;
                $wgRequest->setHeader( 'Accept-Language', 'tg-latn' );
 
                $this->assertEquals( 'tg-latn', $this->lc->getPreferredVariant() );
        }
 
-       function testGetPreferredVariantHeaderWeight() {
+       /**
+        * @covers LanguageConverter::getPreferredVariant
+        * @covers LanguageConverter::getHeaderVariant
+        */
+       public function testGetPreferredVariantHeaderWeight() {
                global $wgRequest;
                $wgRequest->setHeader( 'Accept-Language', 'tg;q=1' );
 
                $this->assertEquals( 'tg', $this->lc->getPreferredVariant() );
        }
 
-       function testGetPreferredVariantHeaderWeight2() {
+       /**
+        * @covers LanguageConverter::getPreferredVariant
+        * @covers LanguageConverter::getHeaderVariant
+        */
+       public function testGetPreferredVariantHeaderWeight2() {
                global $wgRequest;
                $wgRequest->setHeader( 'Accept-Language', 'tg-latn;q=1' );
 
                $this->assertEquals( 'tg-latn', $this->lc->getPreferredVariant() );
        }
 
-       function testGetPreferredVariantHeaderMulti() {
+       /**
+        * @covers LanguageConverter::getPreferredVariant
+        * @covers LanguageConverter::getHeaderVariant
+        */
+       public function testGetPreferredVariantHeaderMulti() {
                global $wgRequest;
                $wgRequest->setHeader( 'Accept-Language', 'en, tg-latn;q=1' );
 
                $this->assertEquals( 'tg-latn', $this->lc->getPreferredVariant() );
        }
 
-       function testGetPreferredVariantUserOption() {
+       /**
+        * @covers LanguageConverter::getPreferredVariant
+        */
+       public function testGetPreferredVariantUserOption() {
                global $wgUser;
 
                $wgUser = new User;
@@ -75,7 +99,11 @@ class LanguageConverterTest extends MediaWikiLangTestCase {
                $this->assertEquals( 'tg-latn', $this->lc->getPreferredVariant() );
        }
 
-       function testGetPreferredVariantUserOptionForForeignLanguage() {
+       /**
+        * @covers LanguageConverter::getPreferredVariant
+        * @covers LanguageConverter::getUserVariant
+        */
+       public function testGetPreferredVariantUserOptionForForeignLanguage() {
                global $wgContLang, $wgUser;
 
                $wgContLang = Language::factory( 'en' );
@@ -89,7 +117,12 @@ class LanguageConverterTest extends MediaWikiLangTestCase {
                $this->assertEquals( 'tg-latn', $this->lc->getPreferredVariant() );
        }
 
-       function testGetPreferredVariantHeaderUserVsUrl() {
+       /**
+        * @covers LanguageConverter::getPreferredVariant
+        * @covers LanguageConverter::getUserVariant
+        * @covers LanguageConverter::getURLVariant
+        */
+       public function testGetPreferredVariantHeaderUserVsUrl() {
                global $wgContLang, $wgRequest, $wgUser;
 
                $wgContLang = Language::factory( 'tg-latn' );
@@ -103,15 +136,21 @@ class LanguageConverterTest extends MediaWikiLangTestCase {
                $this->assertEquals( 'tg', $this->lc->getPreferredVariant() );
        }
 
-
-       function testGetPreferredVariantDefaultLanguageVariant() {
+       /**
+        * @covers LanguageConverter::getPreferredVariant
+        */
+       public function testGetPreferredVariantDefaultLanguageVariant() {
                global $wgDefaultLanguageVariant;
 
                $wgDefaultLanguageVariant = 'tg-latn';
                $this->assertEquals( 'tg-latn', $this->lc->getPreferredVariant() );
        }
 
-       function testGetPreferredVariantDefaultLanguageVsUrlVariant() {
+       /**
+        * @covers LanguageConverter::getPreferredVariant
+        * @covers LanguageConverter::getURLVariant
+        */
+       public function testGetPreferredVariantDefaultLanguageVsUrlVariant() {
                global $wgDefaultLanguageVariant, $wgRequest, $wgContLang;
 
                $wgContLang = Language::factory( 'tg-latn' );
index 212b3b3..63b2c39 100644 (file)
@@ -1,8 +1,11 @@
 <?php
 
+/**
+ * @covers Licenses
+ */
 class LicensesTest extends MediaWikiTestCase {
 
-       function testLicenses() {
+       public function testLicenses() {
                $str = "
 * Free licenses:
 ** GFDL|Debian disagrees
index ec4d98e..b605f08 100644 (file)
@@ -6,7 +6,7 @@ class LinkerTest extends MediaWikiLangTestCase {
         * @dataProvider provideCasesForUserLink
         * @covers Linker::userLink
         */
-       function testUserLink( $expected, $userId, $userName, $altUserName = false, $msg = '' ) {
+       public function testUserLink( $expected, $userId, $userName, $altUserName = false, $msg = '' ) {
                $this->setMwGlobals( array(
                        'wgArticlePath' => '/wiki/$1',
                        'wgWellFormedXml' => true,
index 5ade250..b37ff2e 100644 (file)
@@ -52,6 +52,9 @@ class LinksUpdateTest extends MediaWikiTestCase {
                return array( $t, $po );
        }
 
+       /**
+        * @covers LinksUpdate::addLink
+        */
        public function testUpdate_pagelinks() {
                list( $t, $po ) = $this->makeTitleAndParserOutput( "Testing", 111 );
 
@@ -86,6 +89,9 @@ class LinksUpdateTest extends MediaWikiTestCase {
                ), $update->getRemovedLinks() );
        }
 
+       /**
+        * @covers LinksUpdate::addExternalLink
+        */
        public function testUpdate_externallinks() {
                list( $t, $po ) = $this->makeTitleAndParserOutput( "Testing", 111 );
 
@@ -96,6 +102,9 @@ class LinksUpdateTest extends MediaWikiTestCase {
                ) );
        }
 
+       /**
+        * @covers LinksUpdate::addCategory
+        */
        public function testUpdate_categorylinks() {
                $this->setMwGlobals( 'wgCategoryCollation', 'uppercase' );
 
@@ -108,6 +117,9 @@ class LinksUpdateTest extends MediaWikiTestCase {
                ) );
        }
 
+       /**
+        * @covers LinksUpdate::addInterwikiLink
+        */
        public function testUpdate_iwlinks() {
                list( $t, $po ) = $this->makeTitleAndParserOutput( "Testing", 111 );
 
@@ -119,6 +131,9 @@ class LinksUpdateTest extends MediaWikiTestCase {
                ) );
        }
 
+       /**
+        * @covers LinksUpdate::addTemplate
+        */
        public function testUpdate_templatelinks() {
                list( $t, $po ) = $this->makeTitleAndParserOutput( "Testing", 111 );
 
@@ -129,6 +144,9 @@ class LinksUpdateTest extends MediaWikiTestCase {
                ) );
        }
 
+       /**
+        * @covers LinksUpdate::addImage
+        */
        public function testUpdate_imagelinks() {
                list( $t, $po ) = $this->makeTitleAndParserOutput( "Testing", 111 );
 
@@ -139,6 +157,9 @@ class LinksUpdateTest extends MediaWikiTestCase {
                ) );
        }
 
+       /**
+        * @covers LinksUpdate::addLanguageLink
+        */
        public function testUpdate_langlinks() {
                list( $t, $po ) = $this->makeTitleAndParserOutput( "Testing", 111 );
 
@@ -149,6 +170,9 @@ class LinksUpdateTest extends MediaWikiTestCase {
                ) );
        }
 
+       /**
+        * @covers LinksUpdate::setProperty
+        */
        public function testUpdate_page_props() {
                list( $t, $po ) = $this->makeTitleAndParserOutput( "Testing", 111 );
 
index d6f0d2e..694f4ae 100644 (file)
@@ -3,6 +3,7 @@
 /**
  * These tests should work regardless of $wgCapitalLinks
  * @group Database
+ * @todo Split tests into providers and test methods
  */
 
 class LocalFileTest extends MediaWikiTestCase {
@@ -35,72 +36,105 @@ class LocalFileTest extends MediaWikiTestCase {
                $this->file_lc = $this->repo_lc->newFile( 'test!' );
        }
 
-       function testGetHashPath() {
+       /**
+        * @covers File::getHashPath
+        */
+       public function testGetHashPath() {
                $this->assertEquals( '', $this->file_hl0->getHashPath() );
                $this->assertEquals( 'a/a2/', $this->file_hl2->getHashPath() );
                $this->assertEquals( 'c/c4/', $this->file_lc->getHashPath() );
        }
 
-       function testGetRel() {
+       /**
+        * @covers File::getRel
+        */
+       public function testGetRel() {
                $this->assertEquals( 'Test!', $this->file_hl0->getRel() );
                $this->assertEquals( 'a/a2/Test!', $this->file_hl2->getRel() );
                $this->assertEquals( 'c/c4/test!', $this->file_lc->getRel() );
        }
 
-       function testGetUrlRel() {
+       /**
+        * @covers File::getUrlRel
+        */
+       public function testGetUrlRel() {
                $this->assertEquals( 'Test%21', $this->file_hl0->getUrlRel() );
                $this->assertEquals( 'a/a2/Test%21', $this->file_hl2->getUrlRel() );
                $this->assertEquals( 'c/c4/test%21', $this->file_lc->getUrlRel() );
        }
 
-       function testGetArchivePath() {
+       /**
+        * @covers File::getArchivePath
+        */
+       public function testGetArchivePath() {
                $this->assertEquals( 'mwstore://local-backend/test-public/archive', $this->file_hl0->getArchivePath() );
                $this->assertEquals( 'mwstore://local-backend/test-public/archive/a/a2', $this->file_hl2->getArchivePath() );
                $this->assertEquals( 'mwstore://local-backend/test-public/archive/!', $this->file_hl0->getArchivePath( '!' ) );
                $this->assertEquals( 'mwstore://local-backend/test-public/archive/a/a2/!', $this->file_hl2->getArchivePath( '!' ) );
        }
 
-       function testGetThumbPath() {
+       /**
+        * @covers File::getThumbPath
+        */
+       public function testGetThumbPath() {
                $this->assertEquals( 'mwstore://local-backend/test-thumb/Test!', $this->file_hl0->getThumbPath() );
                $this->assertEquals( 'mwstore://local-backend/test-thumb/a/a2/Test!', $this->file_hl2->getThumbPath() );
                $this->assertEquals( 'mwstore://local-backend/test-thumb/Test!/x', $this->file_hl0->getThumbPath( 'x' ) );
                $this->assertEquals( 'mwstore://local-backend/test-thumb/a/a2/Test!/x', $this->file_hl2->getThumbPath( 'x' ) );
        }
 
-       function testGetArchiveUrl() {
+       /**
+        * @covers File::getArchiveUrl
+        */
+       public function testGetArchiveUrl() {
                $this->assertEquals( '/testurl/archive', $this->file_hl0->getArchiveUrl() );
                $this->assertEquals( '/testurl/archive/a/a2', $this->file_hl2->getArchiveUrl() );
                $this->assertEquals( '/testurl/archive/%21', $this->file_hl0->getArchiveUrl( '!' ) );
                $this->assertEquals( '/testurl/archive/a/a2/%21', $this->file_hl2->getArchiveUrl( '!' ) );
        }
 
-       function testGetThumbUrl() {
+       /**
+        * @covers File::getThumbUrl
+        */
+       public function testGetThumbUrl() {
                $this->assertEquals( '/testurl/thumb/Test%21', $this->file_hl0->getThumbUrl() );
                $this->assertEquals( '/testurl/thumb/a/a2/Test%21', $this->file_hl2->getThumbUrl() );
                $this->assertEquals( '/testurl/thumb/Test%21/x', $this->file_hl0->getThumbUrl( 'x' ) );
                $this->assertEquals( '/testurl/thumb/a/a2/Test%21/x', $this->file_hl2->getThumbUrl( 'x' ) );
        }
 
-       function testGetArchiveVirtualUrl() {
+       /**
+        * @covers File::getArchiveVirtualUrl
+        */
+       public function testGetArchiveVirtualUrl() {
                $this->assertEquals( 'mwrepo://test/public/archive', $this->file_hl0->getArchiveVirtualUrl() );
                $this->assertEquals( 'mwrepo://test/public/archive/a/a2', $this->file_hl2->getArchiveVirtualUrl() );
                $this->assertEquals( 'mwrepo://test/public/archive/%21', $this->file_hl0->getArchiveVirtualUrl( '!' ) );
                $this->assertEquals( 'mwrepo://test/public/archive/a/a2/%21', $this->file_hl2->getArchiveVirtualUrl( '!' ) );
        }
 
-       function testGetThumbVirtualUrl() {
+       /**
+        * @covers File::getThumbVirtualUrl
+        */
+       public function testGetThumbVirtualUrl() {
                $this->assertEquals( 'mwrepo://test/thumb/Test%21', $this->file_hl0->getThumbVirtualUrl() );
                $this->assertEquals( 'mwrepo://test/thumb/a/a2/Test%21', $this->file_hl2->getThumbVirtualUrl() );
                $this->assertEquals( 'mwrepo://test/thumb/Test%21/%21', $this->file_hl0->getThumbVirtualUrl( '!' ) );
                $this->assertEquals( 'mwrepo://test/thumb/a/a2/Test%21/%21', $this->file_hl2->getThumbVirtualUrl( '!' ) );
        }
 
-       function testGetUrl() {
+       /**
+        * @covers File::getUrl
+        */
+       public function testGetUrl() {
                $this->assertEquals( '/testurl/Test%21', $this->file_hl0->getUrl() );
                $this->assertEquals( '/testurl/a/a2/Test%21', $this->file_hl2->getUrl() );
        }
 
-       function testWfLocalFile() {
+       /**
+        * @covers ::wfLocalFile
+        */
+       public function testWfLocalFile() {
                $file = wfLocalFile( "File:Some_file_that_probably_doesn't exist.png" );
                $this->assertThat( $file, $this->isInstanceOf( 'LocalFile' ), 'wfLocalFile() returns LocalFile for valid Titles' );
        }
index b34847a..f98410a 100644 (file)
@@ -1,5 +1,8 @@
 <?php
 
+/**
+ * @covers LocalisationCache
+ */
 class LocalisationCacheTest extends MediaWikiTestCase {
        public function testPuralRulesFallback() {
                $cache = Language::getLocalisationCache();
diff --git a/tests/phpunit/includes/MWExceptionHandlerTest.php b/tests/phpunit/includes/MWExceptionHandlerTest.php
new file mode 100644 (file)
index 0000000..aebd65f
--- /dev/null
@@ -0,0 +1,76 @@
+<?php
+/**
+ * Tests for includes/Exception.php.
+ *
+ * @author Antoine Musso
+ * @copyright Copyright © 2013, Antoine Musso
+ * @copyright Copyright © 2013, Wikimedia Foundation Inc.
+ * @file
+ */
+
+class MWExceptionHandlerTest extends MediaWikiTestCase {
+
+       /**
+        * @covers MWExceptionHandler::getRedactedTrace
+        */
+       function testGetRedactedTrace() {
+               $refvar = 'value';
+               try {
+                       $array = array( 'a', 'b' );
+                       $object = new StdClass();
+                       self::helperThrowAnException( $array, $object, $refvar );
+               } catch (Exception $e) {
+               }
+
+               # Make sure our strack trace contains an array and an object passed to
+               # some function in the stacktrace. Else, we can not assert the trace
+               # redaction achieved its job.
+               $trace = $e->getTrace();
+               $hasObject = false;
+               $hasArray = false;
+               foreach ( $trace as $frame ) {
+                       if ( ! isset( $frame['args'] ) ) {
+                               continue;
+                       }
+                       foreach ( $frame['args'] as $arg ) {
+                               $hasObject = $hasObject || is_object( $arg );
+                               $hasArray = $hasArray || is_array( $arg );
+                       }
+
+                       if( $hasObject && $hasArray ) {
+                               break;
+                       }
+               }
+               $this->assertTrue( $hasObject,
+                       "The stacktrace must have a function having an object has parameter" );
+               $this->assertTrue( $hasArray,
+                       "The stacktrace must have a function having an array has parameter" );
+
+               # Now we redact the trace.. and make sure no function arguments are
+               # arrays or objects.
+               $redacted = MWExceptionHandler::getRedactedTrace( $e );
+
+               foreach ( $redacted as $frame ) {
+                       if ( ! isset( $frame['args'] ) ) {
+                               continue;
+                       }
+                       foreach ( $frame['args'] as $arg ) {
+                               $this->assertNotInternalType( 'array', $arg);
+                               $this->assertNotInternalType( 'object', $arg);
+                       }
+               }
+
+               $this->assertEquals( 'value', $refvar, 'Ensuring reference variable wasn\'t changed' );
+       }
+
+       /**
+        * Helper function for testExpandArgumentsInCall
+        *
+        * Pass it an object and an array, and something by reference :-)
+        *
+        * @throws Exception
+        */
+       protected static function helperThrowAnException( $a, $b, &$c ) {
+               throw new Exception();
+       }
+}
index a44f69e..f2a720e 100644 (file)
@@ -1,7 +1,10 @@
 <?php
 
+/**
+ * @covers MWFunction
+ */
 class MWFunctionTest extends MediaWikiTestCase {
-       function testNewObjFunction() {
+       public function testNewObjFunction() {
                $arg1 = 'Foo';
                $arg2 = 'Bar';
                $arg3 = array( 'Baz' );
index 10e9db6..b6fa139 100644 (file)
@@ -8,6 +8,8 @@
 /**
  * Test class for MWNamespace.
  * Generated by PHPUnit on 2011-02-20 at 21:01:55.
+ * @todo covers tags
+ * @FIXME this test file is a mess
  *
  */
 class MWNamespaceTest extends MediaWikiTestCase {
@@ -31,6 +33,7 @@ class MWNamespaceTest extends MediaWikiTestCase {
 
        /**
         * @todo Write more texts, handle $wgAllowImageMoving setting
+        * @covers MWNamespace::isMovable
         */
        public function testIsMovable() {
                $this->assertFalse( MWNamespace::isMovable( NS_CATEGORY ) );
@@ -39,6 +42,7 @@ class MWNamespaceTest extends MediaWikiTestCase {
 
        /**
         * Please make sure to change testIsTalk() if you change the assertions below
+        * @covers MWNamespace::isSubject
         */
        public function testIsSubject() {
                // Special namespaces
@@ -59,6 +63,7 @@ class MWNamespaceTest extends MediaWikiTestCase {
        /**
         * Reverse of testIsSubject().
         * Please update testIsSubject() if you change assertions below
+        * @covers MWNamespace::isTalk
         */
        public function testIsTalk() {
                // Special namespaces
@@ -77,6 +82,7 @@ class MWNamespaceTest extends MediaWikiTestCase {
        }
 
        /**
+        * @covers MWNamespace::getSubject
         */
        public function testGetSubject() {
                // Special namespaces are their own subjects
@@ -91,6 +97,7 @@ class MWNamespaceTest extends MediaWikiTestCase {
         * Regular getTalk() calls
         * Namespaces without a talk page (NS_MEDIA, NS_SPECIAL) are tested in
         * the function testGetTalkExceptions()
+        * @covers MWNamespace::getTalk
         */
        public function testGetTalk() {
                $this->assertEquals( NS_TALK, MWNamespace::getTalk( NS_MAIN ) );
@@ -103,6 +110,7 @@ class MWNamespaceTest extends MediaWikiTestCase {
         * Exceptions with getTalk()
         * NS_MEDIA does not have talk pages. MediaWiki raise an exception for them.
         * @expectedException MWException
+        * @covers MWNamespace::getTalk
         */
        public function testGetTalkExceptionsForNsMedia() {
                $this->assertNull( MWNamespace::getTalk( NS_MEDIA ) );
@@ -112,6 +120,7 @@ class MWNamespaceTest extends MediaWikiTestCase {
         * Exceptions with getTalk()
         * NS_SPECIAL does not have talk pages. MediaWiki raise an exception for them.
         * @expectedException MWException
+        * @covers MWNamespace::getTalk
         */
        public function testGetTalkExceptionsForNsSpecial() {
                $this->assertNull( MWNamespace::getTalk( NS_SPECIAL ) );
@@ -121,6 +130,7 @@ class MWNamespaceTest extends MediaWikiTestCase {
         * Regular getAssociated() calls
         * Namespaces without an associated page (NS_MEDIA, NS_SPECIAL) are tested in
         * the function testGetAssociatedExceptions()
+        * @covers MWNamespace::getAssociated
         */
        public function testGetAssociated() {
                $this->assertEquals( NS_TALK, MWNamespace::getAssociated( NS_MAIN ) );
@@ -132,6 +142,7 @@ class MWNamespaceTest extends MediaWikiTestCase {
        ### an exception for them.
        /**
         * @expectedException MWException
+        * @covers MWNamespace::getAssociated
         */
        public function testGetAssociatedExceptionsForNsMedia() {
                $this->assertNull( MWNamespace::getAssociated( NS_MEDIA ) );
@@ -139,6 +150,7 @@ class MWNamespaceTest extends MediaWikiTestCase {
 
        /**
         * @expectedException MWException
+        * @covers MWNamespace::getAssociated
         */
        public function testGetAssociatedExceptionsForNsSpecial() {
                $this->assertNull( MWNamespace::getAssociated( NS_SPECIAL ) );
@@ -161,6 +173,7 @@ class MWNamespaceTest extends MediaWikiTestCase {
         * Note if we add a namespace registration system with keys like 'MAIN'
         * we should add tests here for equivilance on things like 'MAIN' == 0
         * and 'MAIN' == NS_MAIN.
+        * @covers MWNamespace::equals
         */
        public function testEquals() {
                $this->assertTrue( MWNamespace::equals( NS_MAIN, NS_MAIN ) );
@@ -175,7 +188,7 @@ class MWNamespaceTest extends MediaWikiTestCase {
        }
 
        /**
-        * Test MWNamespace::subjectEquals
+        * @covers MWNamespace::subjectEquals
         */
        public function testSubjectEquals() {
                $this->assertSameSubject( NS_MAIN, NS_MAIN );
@@ -191,6 +204,9 @@ class MWNamespaceTest extends MediaWikiTestCase {
                $this->assertDifferentSubject( NS_SPECIAL, NS_MAIN );
        }
 
+       /**
+        * @covers MWNamespace::subjectEquals
+        */
        public function testSpecialAndMediaAreDifferentSubjects() {
                $this->assertDifferentSubject(
                        NS_MEDIA, NS_SPECIAL,
@@ -249,6 +265,7 @@ class MWNamespaceTest extends MediaWikiTestCase {
        */
 
        /**
+        * @covers MWNamespace::canTalk
         */
        public function testCanTalk() {
                $this->assertCanNotTalk( NS_MEDIA );
@@ -265,6 +282,7 @@ class MWNamespaceTest extends MediaWikiTestCase {
        }
 
        /**
+        * @covers MWNamespace::isContent
         */
        public function testIsContent() {
                // NS_MAIN is a content namespace per DefaultSettings.php
@@ -285,6 +303,7 @@ class MWNamespaceTest extends MediaWikiTestCase {
        /**
         * Similar to testIsContent() but alters the $wgContentNamespaces
         * global variable.
+        * @covers MWNamespace::isContent
         */
        public function testIsContentAdvanced() {
                global $wgContentNamespaces;
@@ -301,6 +320,9 @@ class MWNamespaceTest extends MediaWikiTestCase {
                $this->assertIsContent( NS_MAIN );
        }
 
+       /**
+        * @covers MWNamespace::isWatchable
+        */
        public function testIsWatchable() {
                // Specials namespaces are not watchable
                $this->assertIsNotWatchable( NS_MEDIA );
@@ -315,6 +337,9 @@ class MWNamespaceTest extends MediaWikiTestCase {
                $this->assertIsWatchable( 101 );
        }
 
+       /**
+        * @covers MWNamespace::hasSubpages
+        */
        public function testHasSubpages() {
                global $wgNamespacesWithSubpages;
 
@@ -338,6 +363,7 @@ class MWNamespaceTest extends MediaWikiTestCase {
        }
 
        /**
+        * @covers MWNamespace::getContentNamespaces
         */
        public function testGetContentNamespaces() {
                global $wgContentNamespaces;
@@ -388,6 +414,7 @@ class MWNamespaceTest extends MediaWikiTestCase {
        }
 
        /**
+        * @covers MWNamespace::getSubjectNamespaces
         */
        public function testGetSubjectNamespaces() {
                $subjectsNS = MWNamespace::getSubjectNamespaces();
@@ -403,6 +430,7 @@ class MWNamespaceTest extends MediaWikiTestCase {
        }
 
        /**
+        * @covers MWNamespace::getTalkNamespaces
         */
        public function testGetTalkNamespaces() {
                $talkNS = MWNamespace::getTalkNamespaces();
@@ -420,6 +448,7 @@ class MWNamespaceTest extends MediaWikiTestCase {
        /**
         * Some namespaces are always capitalized per code definition
         * in MWNamespace::$alwaysCapitalizedNamespaces
+        * @covers MWNamespace::isCapitalized
         */
        public function testIsCapitalizedHardcodedAssertions() {
                // NS_MEDIA and NS_FILE are treated the same
@@ -451,6 +480,7 @@ class MWNamespaceTest extends MediaWikiTestCase {
         *
         * Global setting correctness is tested against the NS_PROJECT and
         * NS_PROJECT_TALK namespaces since they are not hardcoded nor specials
+        * @covers MWNamespace::isCapitalized
         */
        public function testIsCapitalizedWithWgCapitalLinks() {
                global $wgCapitalLinks;
@@ -475,6 +505,7 @@ class MWNamespaceTest extends MediaWikiTestCase {
         * testing the $wgCapitalLinkOverrides global.
         *
         * @todo split groups of assertions in autonomous testing functions
+        * @covers MWNamespace::isCapitalized
         */
        public function testIsCapitalizedWithWgCapitalLinkOverrides() {
                global $wgCapitalLinkOverrides;
@@ -507,6 +538,9 @@ class MWNamespaceTest extends MediaWikiTestCase {
                $this->assertIsCapitalized( NS_PROJECT );
        }
 
+       /**
+        * @covers MWNamespace::hasGenderDistinction
+        */
        public function testHasGenderDistinction() {
                // Namespaces with gender distinctions
                $this->assertTrue( MWNamespace::hasGenderDistinction( NS_USER ) );
@@ -519,6 +553,9 @@ class MWNamespaceTest extends MediaWikiTestCase {
                $this->assertFalse( MWNamespace::hasGenderDistinction( NS_TALK ) );
        }
 
+       /**
+        * @covers MWNamespace::isNonincludable
+        */
        public function testIsNonincludable() {
                global $wgNonincludableNamespaces;
 
index 1ba2d40..71ebd6a 100644 (file)
@@ -10,7 +10,10 @@ class MessageTest extends MediaWikiLangTestCase {
                ) );
        }
 
-       function testExists() {
+       /**
+        * @covers Message::exists
+        */
+       public function testExists() {
                $this->assertTrue( wfMessage( 'mainpage' )->exists() );
                $this->assertTrue( wfMessage( 'mainpage' )->params( array() )->exists() );
                $this->assertTrue( wfMessage( 'mainpage' )->rawParams( 'foo', 123 )->exists() );
@@ -19,7 +22,10 @@ class MessageTest extends MediaWikiLangTestCase {
                $this->assertFalse( wfMessage( 'i-dont-exist-evar' )->rawParams( 'foo', 123 )->exists() );
        }
 
-       function testKey() {
+       /**
+        * @covers Message::__construct
+        */
+       public function testKey() {
                $this->assertInstanceOf( 'Message', wfMessage( 'mainpage' ) );
                $this->assertInstanceOf( 'Message', wfMessage( 'i-dont-exist-evar' ) );
                $this->assertEquals( 'Main Page', wfMessage( 'mainpage' )->text() );
@@ -28,28 +34,42 @@ class MessageTest extends MediaWikiLangTestCase {
                $this->assertEquals( '&lt;i-dont-exist-evar&gt;', wfMessage( 'i-dont-exist-evar' )->escaped() );
        }
 
-       function testInLanguage() {
+       /**
+        * @covers Message::inLanguage
+        */
+       public function testInLanguage() {
                $this->assertEquals( 'Main Page', wfMessage( 'mainpage' )->inLanguage( 'en' )->text() );
                $this->assertEquals( 'Заглавная страница', wfMessage( 'mainpage' )->inLanguage( 'ru' )->text() );
                $this->assertEquals( 'Main Page', wfMessage( 'mainpage' )->inLanguage( Language::factory( 'en' ) )->text() );
                $this->assertEquals( 'Заглавная страница', wfMessage( 'mainpage' )->inLanguage( Language::factory( 'ru' ) )->text() );
        }
 
-       function testMessageParams() {
+       /**
+        * @covers Message::__construct
+        */
+       public function testMessageParams() {
                $this->assertEquals( 'Return to $1.', wfMessage( 'returnto' )->text() );
                $this->assertEquals( 'Return to $1.', wfMessage( 'returnto', array() )->text() );
                $this->assertEquals( 'You have foo (bar).', wfMessage( 'youhavenewmessages', 'foo', 'bar' )->text() );
                $this->assertEquals( 'You have foo (bar).', wfMessage( 'youhavenewmessages', array( 'foo', 'bar' ) )->text() );
        }
 
-       function testMessageParamSubstitution() {
+       /**
+        * @covers Message::__construct
+        * @covers Message::rawParams
+        */
+       public function testMessageParamSubstitution() {
                $this->assertEquals( '(Заглавная страница)', wfMessage( 'parentheses', 'Заглавная страница' )->plain() );
                $this->assertEquals( '(Заглавная страница $1)', wfMessage( 'parentheses', 'Заглавная страница $1' )->plain() );
                $this->assertEquals( '(Заглавная страница)', wfMessage( 'parentheses' )->rawParams( 'Заглавная страница' )->plain() );
                $this->assertEquals( '(Заглавная страница $1)', wfMessage( 'parentheses' )->rawParams( 'Заглавная страница $1' )->plain() );
        }
 
-       function testDeliciouslyManyParams() {
+       /**
+        * @covers Message::__construct
+        * @covers Message::params
+        */
+       public function testDeliciouslyManyParams() {
                $msg = new RawMessage( '$1$2$3$4$5$6$7$8$9$10$11$12' );
                // One less than above has placeholders
                $params = array( 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k' );
@@ -59,8 +79,15 @@ class MessageTest extends MediaWikiLangTestCase {
        /**
         * FIXME: This should not need database, but Language#formatExpiry does (bug 55912)
         * @group Database
+        * @todo this should be split up into multiple test methods
+        * @covers Message::numParams
+        * @covers Message::durationParams
+        * @covers Message::expiryParams
+        * @covers Message::timeperiodParams
+        * @covers Message::sizeParams
+        * @covers Message::bitrateParams
         */
-       function testMessageParamTypes() {
+       public function testMessageParamTypes() {
                $lang = Language::factory( 'en' );
 
                $msg = new RawMessage( '$1' );
@@ -106,13 +133,19 @@ class MessageTest extends MediaWikiLangTestCase {
                );
        }
 
-       function testInContentLanguageDisabled() {
+       /**
+        * @covers Message::inContentLanguage
+        */
+       public function testInContentLanguageDisabled() {
                $this->setMwGlobals( 'wgLang', Language::factory( 'fr' ) );
 
                $this->assertEquals( 'Main Page', wfMessage( 'mainpage' )->inContentLanguage()->plain(), 'ForceUIMsg disabled' );
        }
 
-       function testInContentLanguageEnabled() {
+       /**
+        * @covers Message::inContentLanguage
+        */
+       public function testInContentLanguageEnabled() {
                $this->setMwGlobals( array(
                        'wgLang' => Language::factory( 'fr' ),
                        'wgForceUIMsgAsContentMsg' => array( 'mainpage' ),
@@ -123,8 +156,9 @@ class MessageTest extends MediaWikiLangTestCase {
 
        /**
         * @expectedException MWException
+        * @covers Message::inLanguage
         */
-       function testInLanguageThrows() {
+       public function testInLanguageThrows() {
                wfMessage( 'foo' )->inLanguage( 123 );
        }
 }
index 56bb0fc..385cee5 100644 (file)
@@ -6,6 +6,8 @@
  *
  * @group Output
  *
+ * @todo factor tests in this class into providers and test methods
+ *
  */
 class OutputPageTest extends MediaWikiTestCase {
        const SCREEN_MEDIA_QUERY = 'screen and (min-width: 982px)';
@@ -46,6 +48,7 @@ class OutputPageTest extends MediaWikiTestCase {
 
        /**
         * Tests print requests
+        * @covers OutputPage::transformCssMedia
         */
        public function testPrintRequests() {
                $this->assertTransformCssMediaCase( array(
@@ -79,6 +82,7 @@ class OutputPageTest extends MediaWikiTestCase {
 
        /**
         * Tests screen requests, without either query parameter set
+        * @covers OutputPage::transformCssMedia
         */
        public function testScreenRequests() {
                $this->assertTransformCssMediaCase( array(
@@ -114,6 +118,7 @@ class OutputPageTest extends MediaWikiTestCase {
 
        /**
         * Tests handheld behavior
+        * @covers OutputPage::transformCssMedia
         */
        public function testHandheld() {
                $this->assertTransformCssMediaCase( array(
index 386ddb8..8fea8cc 100644 (file)
@@ -1,10 +1,17 @@
 <?php
+
 /**
- * Tests for the PathRouter parsing
+ * Tests for the PathRouter parsing.
+ *
+ * @covers PathRouter
  */
-
 class PathRouterTest extends MediaWikiTestCase {
 
+       /**
+        * @var PathRouter
+        */
+       protected $basicRouter;
+
        protected function setUp() {
                parent::setUp();
                $router = new PathRouter;
index 392ad08..3dec2da 100644 (file)
@@ -4,11 +4,16 @@
  * @group Database
  */
 class PreferencesTest extends MediaWikiTestCase {
-       /** Array of User objects */
+       /**
+        * @var User[]
+        */
        private $prefUsers;
+       /**
+        * @var RequestContext
+        */
        private $context;
 
-       function __construct() {
+       public function __construct() {
                parent::__construct();
 
                $this->prefUsers['noemail'] = new User;
@@ -40,7 +45,7 @@ class PreferencesTest extends MediaWikiTestCase {
         * Placeholder to verify bug 34302
         * @covers Preferences::profilePreferences
         */
-       function testEmailFieldsWhenUserHasNoEmail() {
+       public function testEmailFieldsWhenUserHasNoEmail() {
                $prefs = $this->prefsFor( 'noemail' );
                $this->assertArrayHasKey( 'cssclass',
                        $prefs['emailaddress']
@@ -52,7 +57,7 @@ class PreferencesTest extends MediaWikiTestCase {
         * Placeholder to verify bug 34302
         * @covers Preferences::profilePreferences
         */
-       function testEmailFieldsWhenUserEmailNotAuthenticated() {
+       public function testEmailFieldsWhenUserEmailNotAuthenticated() {
                $prefs = $this->prefsFor( 'notauth' );
                $this->assertArrayHasKey( 'cssclass',
                        $prefs['emailaddress']
@@ -64,7 +69,7 @@ class PreferencesTest extends MediaWikiTestCase {
         * Placeholder to verify bug 34302
         * @covers Preferences::profilePreferences
         */
-       function testEmailFieldsWhenUserEmailIsAuthenticated() {
+       public function testEmailFieldsWhenUserEmailIsAuthenticated() {
                $prefs = $this->prefsFor( 'auth' );
                $this->assertArrayHasKey( 'cssclass',
                        $prefs['emailaddress']
@@ -73,7 +78,7 @@ class PreferencesTest extends MediaWikiTestCase {
        }
 
        /** Helper */
-       function prefsFor( $user_key ) {
+       protected function prefsFor( $user_key ) {
                $preferences = array();
                Preferences::profilePreferences(
                        $this->prefUsers[$user_key]
diff --git a/tests/phpunit/includes/Providers.php b/tests/phpunit/includes/Providers.php
deleted file mode 100644 (file)
index 4ddc0b0..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-<?php
-/**
- * Generic providers for the MediaWiki PHPUnit test suite
- *
- * @author Antoine Musso
- * @copyright Copyright © 2011, Antoine Musso
- * @file
- */
-
-/** */
-class MediaWikiProvide {
-
-       /* provide an array of numbers from 1 up to @param $num */
-       private static function createProviderUpTo( $num ) {
-               $ret = array();
-               for ( $i = 1; $i <= $num; $i++ ) {
-                       $ret[] = array( $i );
-               }
-
-               return $ret;
-       }
-
-       /* array of months numbers (as an integer) */
-       public static function Months() {
-               return self::createProviderUpTo( 12 );
-       }
-
-       /* array of days numbers (as an integer) */
-       public static function Days() {
-               return self::createProviderUpTo( 31 );
-       }
-
-       public static function DaysMonths() {
-               $ret = array();
-
-               $months = self::Months();
-               $days = self::Days();
-               foreach ( $months as $month ) {
-                       foreach ( $days as $day ) {
-                               $ret[] = array( $day[0], $month[0] );
-                       }
-               }
-
-               return $ret;
-       }
-}
index f01fb23..cfa3e77 100644 (file)
@@ -9,7 +9,7 @@ class RecentChangeTest extends MediaWikiTestCase {
        protected $user_comment;
        protected $context;
 
-       function __construct() {
+       public function __construct() {
                parent::__construct();
 
                $this->title = Title::newFromText( 'SomeTitle' );
@@ -56,7 +56,7 @@ class RecentChangeTest extends MediaWikiTestCase {
        /**
         * @covers LogFormatter::getIRCActionText
         */
-       function testIrcMsgForLogTypeBlock() {
+       public function testIrcMsgForLogTypeBlock() {
                $sep = $this->context->msg( 'colon-separator' )->text();
 
                # block/block
@@ -78,7 +78,7 @@ class RecentChangeTest extends MediaWikiTestCase {
        /**
         * @covers LogFormatter::getIRCActionText
         */
-       function testIrcMsgForLogTypeDelete() {
+       public function testIrcMsgForLogTypeDelete() {
                $sep = $this->context->msg( 'colon-separator' )->text();
 
                # delete/delete
@@ -101,7 +101,7 @@ class RecentChangeTest extends MediaWikiTestCase {
        /**
         * @covers LogFormatter::getIRCActionText
         */
-       function testIrcMsgForLogTypeNewusers() {
+       public function testIrcMsgForLogTypeNewusers() {
                $this->assertIRCComment(
                        'New user account',
                        'newusers', 'newusers',
@@ -127,7 +127,7 @@ class RecentChangeTest extends MediaWikiTestCase {
        /**
         * @covers LogFormatter::getIRCActionText
         */
-       function testIrcMsgForLogTypeMove() {
+       public function testIrcMsgForLogTypeMove() {
                $move_params = array(
                        '4::target' => $this->target->getPrefixedText(),
                        '5::noredir' => 0,
@@ -154,7 +154,7 @@ class RecentChangeTest extends MediaWikiTestCase {
        /**
         * @covers LogFormatter::getIRCActionText
         */
-       function testIrcMsgForLogTypePatrol() {
+       public function testIrcMsgForLogTypePatrol() {
                # patrol/patrol
                $this->assertIRCComment(
                        $this->context->msg( 'patrol-log-line', 'revision 777', '[[SomeTitle]]', '' )->plain(),
@@ -170,7 +170,7 @@ class RecentChangeTest extends MediaWikiTestCase {
        /**
         * @covers LogFormatter::getIRCActionText
         */
-       function testIrcMsgForLogTypeProtect() {
+       public function testIrcMsgForLogTypeProtect() {
                $protectParams = array(
                        '[edit=sysop] (indefinite) ‎[move=sysop] (indefinite)'
                );
@@ -204,7 +204,7 @@ class RecentChangeTest extends MediaWikiTestCase {
        /**
         * @covers LogFormatter::getIRCActionText
         */
-       function testIrcMsgForLogTypeUpload() {
+       public function testIrcMsgForLogTypeUpload() {
                $sep = $this->context->msg( 'colon-separator' )->text();
 
                # upload/upload
@@ -230,19 +230,19 @@ class RecentChangeTest extends MediaWikiTestCase {
         * --
         */
        /*
-       function testIrcMsgForBlankingAES() {
+       public function testIrcMsgForBlankingAES() {
                // $this->context->msg( 'autosumm-blank', .. );
        }
 
-       function testIrcMsgForReplaceAES() {
+       public function testIrcMsgForReplaceAES() {
                // $this->context->msg( 'autosumm-replace', .. );
        }
 
-       function testIrcMsgForRollbackAES() {
+       public function testIrcMsgForRollbackAES() {
                // $this->context->msg( 'revertpage', .. );
        }
 
-       function testIrcMsgForUndoAES() {
+       public function testIrcMsgForUndoAES() {
                // $this->context->msg( 'undo-summary', .. );
        }
        */
@@ -251,10 +251,11 @@ class RecentChangeTest extends MediaWikiTestCase {
         * @param $expected String Expected IRC text without colors codes
         * @param $type String Log type (move, delete, suppress, patrol ...)
         * @param $action String A log type action
+        * @param $params
         * @param $comment String (optional) A comment for the log action
         * @param $msg String (optional) A message for PHPUnit :-)
         */
-       function assertIRCComment( $expected, $type, $action, $params, $comment = null, $msg = '' ) {
+       protected function assertIRCComment( $expected, $type, $action, $params, $comment = null, $msg = '' ) {
 
                $logEntry = new ManualLogEntry( $type, $action );
                $logEntry->setPerformer( $this->user );
index e3a9cfb..1776b5d 100644 (file)
@@ -7,6 +7,8 @@ class RequestContextTest extends MediaWikiTestCase {
 
        /**
         * Test the relationship between title and wikipage in RequestContext
+        * @covers RequestContext::getWikiPage
+        * @covers RequestContext::getTitle
         */
        public function testWikiPageTitle() {
                $context = new RequestContext();
@@ -27,6 +29,9 @@ class RequestContextTest extends MediaWikiTestCase {
                        "When a title is updated the WikiPage should be purged and recreated on-demand with the new title." );
        }
 
+       /**
+        * @covers RequestContext::importScopedSession
+        */
        public function testImportScopedSession() {
                $context = RequestContext::getMain();
 
index c30c441..ca8b2b6 100644 (file)
@@ -99,6 +99,7 @@ class ResourceLoaderTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider providePackedModules
+        * @covers ResourceLoader::makePackedModulesString
         */
        public function testMakePackedModulesString( $desc, $modules, $packed ) {
                $this->assertEquals( $packed, ResourceLoader::makePackedModulesString( $modules ), $desc );
@@ -106,6 +107,7 @@ class ResourceLoaderTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider providePackedModules
+        * @covers ResourceLoaderContext::expandModuleNames
         */
        public function testexpandModuleNames( $desc, $modules, $packed ) {
                $this->assertEquals( $modules, ResourceLoaderContext::expandModuleNames( $packed ), $desc );
index 00b1f29..e17c7b0 100644 (file)
@@ -38,7 +38,7 @@ class RevisionStorageTest extends MediaWikiTestCase {
                                'iwlinks' ) );
        }
 
-       public function setUp() {
+       protected function setUp() {
                global $wgExtraNamespaces, $wgNamespaceContentModels, $wgContentHandlers, $wgContLang;
 
                parent::setUp();
@@ -456,15 +456,15 @@ class RevisionStorageTest extends MediaWikiTestCase {
         * @dataProvider provideUserWasLastToEdit
         */
        public function testUserWasLastToEdit( $sinceIdx, $expectedLast ) {
-               $userA = \User::newFromName( "RevisionStorageTest_userA" );
-               $userB = \User::newFromName( "RevisionStorageTest_userB" );
+               $userA = User::newFromName( "RevisionStorageTest_userA" );
+               $userB = User::newFromName( "RevisionStorageTest_userB" );
 
                if ( $userA->getId() === 0 ) {
-                       $userA = \User::createNew( $userA->getName() );
+                       $userA = User::createNew( $userA->getName() );
                }
 
                if ( $userB->getId() === 0 ) {
-                       $userB = \User::createNew( $userB->getName() );
+                       $userB = User::createNew( $userB->getName() );
                }
 
                $ns = $this->getDefaultWikitextNS();
index f35a05f..4e83e35 100644 (file)
@@ -7,7 +7,7 @@
  */
 class RevisionTest_ContentHandlerUseDB extends RevisionStorageTest {
 
-       function setUp() {
+       protected function setUp() {
                $this->setMwGlobals( 'wgContentHandlerUseDB', false );
 
                $dbw = wfGetDB( DB_MASTER );
index e3b0844..b5819ff 100644 (file)
@@ -54,7 +54,10 @@ class RevisionTest extends MediaWikiTestCase {
                parent::tearDown();
        }
 
-       function testGetRevisionText() {
+       /**
+        * @covers Revision::getRevisionText
+        */
+       public function testGetRevisionText() {
                $row = new stdClass;
                $row->old_flags = '';
                $row->old_text = 'This is a bunch of revision text.';
@@ -63,7 +66,10 @@ class RevisionTest extends MediaWikiTestCase {
                        Revision::getRevisionText( $row ) );
        }
 
-       function testGetRevisionTextGzip() {
+       /**
+        * @covers Revision::getRevisionText
+        */
+       public function testGetRevisionTextGzip() {
                $this->checkPHPExtension( 'zlib' );
 
                $row = new stdClass;
@@ -74,7 +80,10 @@ class RevisionTest extends MediaWikiTestCase {
                        Revision::getRevisionText( $row ) );
        }
 
-       function testGetRevisionTextUtf8Native() {
+       /**
+        * @covers Revision::getRevisionText
+        */
+       public function testGetRevisionTextUtf8Native() {
                $row = new stdClass;
                $row->old_flags = 'utf-8';
                $row->old_text = "Wiki est l'\xc3\xa9cole superieur !";
@@ -84,7 +93,10 @@ class RevisionTest extends MediaWikiTestCase {
                        Revision::getRevisionText( $row ) );
        }
 
-       function testGetRevisionTextUtf8Legacy() {
+       /**
+        * @covers Revision::getRevisionText
+        */
+       public function testGetRevisionTextUtf8Legacy() {
                $row = new stdClass;
                $row->old_flags = '';
                $row->old_text = "Wiki est l'\xe9cole superieur !";
@@ -94,7 +106,10 @@ class RevisionTest extends MediaWikiTestCase {
                        Revision::getRevisionText( $row ) );
        }
 
-       function testGetRevisionTextUtf8NativeGzip() {
+       /**
+        * @covers Revision::getRevisionText
+        */
+       public function testGetRevisionTextUtf8NativeGzip() {
                $this->checkPHPExtension( 'zlib' );
 
                $row = new stdClass;
@@ -106,7 +121,10 @@ class RevisionTest extends MediaWikiTestCase {
                        Revision::getRevisionText( $row ) );
        }
 
-       function testGetRevisionTextUtf8LegacyGzip() {
+       /**
+        * @covers Revision::getRevisionText
+        */
+       public function testGetRevisionTextUtf8LegacyGzip() {
                $this->checkPHPExtension( 'zlib' );
 
                $row = new stdClass;
@@ -118,7 +136,10 @@ class RevisionTest extends MediaWikiTestCase {
                        Revision::getRevisionText( $row ) );
        }
 
-       function testCompressRevisionTextUtf8() {
+       /**
+        * @covers Revision::compressRevisionText
+        */
+       public function testCompressRevisionTextUtf8() {
                $row = new stdClass;
                $row->old_text = "Wiki est l'\xc3\xa9cole superieur !";
                $row->old_flags = Revision::compressRevisionText( $row->old_text );
@@ -132,7 +153,10 @@ class RevisionTest extends MediaWikiTestCase {
                        Revision::getRevisionText( $row ), "getRevisionText" );
        }
 
-       function testCompressRevisionTextUtf8Gzip() {
+       /**
+        * @covers Revision::compressRevisionText
+        */
+       public function testCompressRevisionTextUtf8Gzip() {
                $this->checkPHPExtension( 'zlib' );
                $this->setMwGlobals( 'wgCompressRevisions', true );
 
@@ -155,6 +179,8 @@ class RevisionTest extends MediaWikiTestCase {
         * @param string $text
         * @param string $title
         * @param string $model
+        * @param null $format
+        *
         * @return Revision
         */
        function newTestRevision( $text, $title = "Test", $model = CONTENT_MODEL_WIKITEXT, $format = null ) {
@@ -194,8 +220,9 @@ class RevisionTest extends MediaWikiTestCase {
        /**
         * @group Database
         * @dataProvider dataGetContentModel
+        * @covers Revision::getContentModel
         */
-       function testGetContentModel( $text, $title, $model, $format, $expectedModel ) {
+       public function testGetContentModel( $text, $title, $model, $format, $expectedModel ) {
                $rev = $this->newTestRevision( $text, $title, $model, $format );
 
                $this->assertEquals( $expectedModel, $rev->getContentModel() );
@@ -214,8 +241,9 @@ class RevisionTest extends MediaWikiTestCase {
        /**
         * @group Database
         * @dataProvider dataGetContentFormat
+        * @covers Revision::getContentFormat
         */
-       function testGetContentFormat( $text, $title, $model, $format, $expectedFormat ) {
+       public function testGetContentFormat( $text, $title, $model, $format, $expectedFormat ) {
                $rev = $this->newTestRevision( $text, $title, $model, $format );
 
                $this->assertEquals( $expectedFormat, $rev->getContentFormat() );
@@ -233,8 +261,9 @@ class RevisionTest extends MediaWikiTestCase {
        /**
         * @group Database
         * @dataProvider dataGetContentHandler
+        * @covers Revision::getContentHandler
         */
-       function testGetContentHandler( $text, $title, $model, $format, $expectedClass ) {
+       public function testGetContentHandler( $text, $title, $model, $format, $expectedClass ) {
                $rev = $this->newTestRevision( $text, $title, $model, $format );
 
                $this->assertEquals( $expectedClass, get_class( $rev->getContentHandler() ) );
@@ -252,8 +281,9 @@ class RevisionTest extends MediaWikiTestCase {
        /**
         * @group Database
         * @dataProvider dataGetContent
+        * @covers Revision::getContent
         */
-       function testGetContent( $text, $title, $model, $format, $audience, $expectedSerialization ) {
+       public function testGetContent( $text, $title, $model, $format, $audience, $expectedSerialization ) {
                $rev = $this->newTestRevision( $text, $title, $model, $format );
                $content = $rev->getContent( $audience );
 
@@ -272,8 +302,9 @@ class RevisionTest extends MediaWikiTestCase {
        /**
         * @group Database
         * @dataProvider dataGetText
+        * @covers Revision::getText
         */
-       function testGetText( $text, $title, $model, $format, $audience, $expectedText ) {
+       public function testGetText( $text, $title, $model, $format, $audience, $expectedText ) {
                $this->hideDeprecated( 'Revision::getText' );
 
                $rev = $this->newTestRevision( $text, $title, $model, $format );
@@ -284,8 +315,9 @@ class RevisionTest extends MediaWikiTestCase {
        /**
         * @group Database
         * @dataProvider dataGetText
+        * @covers Revision::getRawText
         */
-       function testGetRawText( $text, $title, $model, $format, $audience, $expectedText ) {
+       public function testGetRawText( $text, $title, $model, $format, $audience, $expectedText ) {
                $this->hideDeprecated( 'Revision::getRawText' );
 
                $rev = $this->newTestRevision( $text, $title, $model, $format );
@@ -328,6 +360,9 @@ class RevisionTest extends MediaWikiTestCase {
                $this->assertEquals( $expected_hash, $rev->getSha1() );
        }
 
+       /**
+        * @covers Revision::__construct
+        */
        public function testConstructWithText() {
                $this->hideDeprecated( "Revision::getText" );
 
@@ -342,6 +377,9 @@ class RevisionTest extends MediaWikiTestCase {
                $this->assertEquals( CONTENT_MODEL_JAVASCRIPT, $rev->getContentModel() );
        }
 
+       /**
+        * @covers Revision::__construct
+        */
        public function testConstructWithContent() {
                $this->hideDeprecated( "Revision::getText" );
 
@@ -361,8 +399,9 @@ class RevisionTest extends MediaWikiTestCase {
         * Tests whether $rev->getContent() returns a clone when needed.
         *
         * @group Database
+        * @covers Revision::getContent
         */
-       function testGetContentClone() {
+       public function testGetContentClone() {
                $content = new RevisionTestModifyableContent( "foo" );
 
                $rev = new Revision(
@@ -394,8 +433,9 @@ class RevisionTest extends MediaWikiTestCase {
         * Tests whether $rev->getContent() returns the same object repeatedly if appropriate.
         *
         * @group Database
+        * @covers Revision::getContent
         */
-       function testGetContentUncloned() {
+       public function testGetContentUncloned() {
                $rev = $this->newTestRevision( "hello", "testGetContentUncloned_dummy", CONTENT_MODEL_WIKITEXT );
                $content = $rev->getContent( Revision::RAW );
                $content2 = $rev->getContent( Revision::RAW );
index 8a88191..8516a4c 100644 (file)
@@ -33,7 +33,7 @@ class TestSample extends MediaWikiLangTestCase {
         * "Agile Documentation" at
         * http://www.phpunit.de/manual/3.4/en/other-uses-for-tests.html
         */
-       function testTitleObjectStringConversion() {
+       public function testTitleObjectStringConversion() {
                $title = Title::newFromText( "text" );
                $this->assertInstanceOf( 'Title', $title, "Title creation" );
                $this->assertEquals( "Text", $title, "Automatic string conversion" );
@@ -98,7 +98,7 @@ class TestSample extends MediaWikiLangTestCase {
         * @expectedException MWException object
         * See http://www.phpunit.de/manual/3.4/en/appendixes.annotations.html#appendixes.annotations.expectedException
         */
-       function testTitleObjectFromObject() {
+       public function testTitleObjectFromObject() {
                $title = Title::newFromText( Title::newFromText( "test" ) );
                $this->assertEquals( "Test", $title->isLocal() );
        }
index 38c15ee..97abf80 100644 (file)
@@ -1,5 +1,9 @@
 <?php
 
+/**
+ * @todo Tests covering decodeCharReferences can be refactored into a single
+ * method and dataprovider.
+ */
 class SanitizerTest extends MediaWikiTestCase {
 
        protected function setUp() {
@@ -8,7 +12,10 @@ class SanitizerTest extends MediaWikiTestCase {
                AutoLoader::loadClass( 'Sanitizer' );
        }
 
-       function testDecodeNamedEntities() {
+       /**
+        * @covers Sanitizer::decodeCharReferences
+        */
+       public function testDecodeNamedEntities() {
                $this->assertEquals(
                        "\xc3\xa9cole",
                        Sanitizer::decodeCharReferences( '&eacute;cole' ),
@@ -16,7 +23,10 @@ class SanitizerTest extends MediaWikiTestCase {
                );
        }
 
-       function testDecodeNumericEntities() {
+       /**
+        * @covers Sanitizer::decodeCharReferences
+        */
+       public function testDecodeNumericEntities() {
                $this->assertEquals(
                        "\xc4\x88io bonas dans l'\xc3\xa9cole!",
                        Sanitizer::decodeCharReferences( "&#x108;io bonas dans l'&#233;cole!" ),
@@ -24,7 +34,10 @@ class SanitizerTest extends MediaWikiTestCase {
                );
        }
 
-       function testDecodeMixedEntities() {
+       /**
+        * @covers Sanitizer::decodeCharReferences
+        */
+       public function testDecodeMixedEntities() {
                $this->assertEquals(
                        "\xc4\x88io bonas dans l'\xc3\xa9cole!",
                        Sanitizer::decodeCharReferences( "&#x108;io bonas dans l'&eacute;cole!" ),
@@ -32,7 +45,10 @@ class SanitizerTest extends MediaWikiTestCase {
                );
        }
 
-       function testDecodeMixedComplexEntities() {
+       /**
+        * @covers Sanitizer::decodeCharReferences
+        */
+       public function testDecodeMixedComplexEntities() {
                $this->assertEquals(
                        "\xc4\x88io bonas dans l'\xc3\xa9cole! (mais pas &#x108;io dans l'&eacute;cole)",
                        Sanitizer::decodeCharReferences(
@@ -42,7 +58,10 @@ class SanitizerTest extends MediaWikiTestCase {
                );
        }
 
-       function testInvalidAmpersand() {
+       /**
+        * @covers Sanitizer::decodeCharReferences
+        */
+       public function testInvalidAmpersand() {
                $this->assertEquals(
                        'a & b',
                        Sanitizer::decodeCharReferences( 'a & b' ),
@@ -50,7 +69,10 @@ class SanitizerTest extends MediaWikiTestCase {
                );
        }
 
-       function testInvalidEntities() {
+       /**
+        * @covers Sanitizer::decodeCharReferences
+        */
+       public function testInvalidEntities() {
                $this->assertEquals(
                        '&foo;',
                        Sanitizer::decodeCharReferences( '&foo;' ),
@@ -58,7 +80,10 @@ class SanitizerTest extends MediaWikiTestCase {
                );
        }
 
-       function testInvalidNumberedEntities() {
+       /**
+        * @covers Sanitizer::decodeCharReferences
+        */
+       public function testInvalidNumberedEntities() {
                $this->assertEquals( UTF8_REPLACEMENT, Sanitizer::decodeCharReferences( "&#88888888888888;" ), 'Invalid numbered entity' );
        }
 
@@ -69,7 +94,7 @@ class SanitizerTest extends MediaWikiTestCase {
         * @param String $tag Name of an HTML5 element (ie: 'video')
         * @param Boolean $escaped Wheter sanitizer let the tag in or escape it (ie: '&lt;video&gt;')
         */
-       function testRemovehtmltagsOnHtml5Tags( $tag, $escaped ) {
+       public function testRemovehtmltagsOnHtml5Tags( $tag, $escaped ) {
                $this->setMwGlobals( array(
                        'wgUseTidy' => false
                ) );
@@ -131,8 +156,9 @@ class SanitizerTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider dataRemoveHTMLtags
+        * @covers Sanitizer::removeHTMLtags
         */
-       function testRemoveHTMLtags( $input, $output, $msg = null ) {
+       public function testRemoveHTMLtags( $input, $output, $msg = null ) {
                $GLOBALS['wgUseTidy'] = false;
                $this->assertEquals( $output, Sanitizer::removeHTMLtags( $input ), $msg );
        }
@@ -141,7 +167,7 @@ class SanitizerTest extends MediaWikiTestCase {
         * @dataProvider provideTagAttributesToDecode
         * @covers Sanitizer::decodeTagAttributes
         */
-       function testDecodeTagAttributes( $expected, $attributes, $message = '' ) {
+       public function testDecodeTagAttributes( $expected, $attributes, $message = '' ) {
                $this->assertEquals( $expected,
                        Sanitizer::decodeTagAttributes( $attributes ),
                        $message
@@ -189,7 +215,7 @@ class SanitizerTest extends MediaWikiTestCase {
         * @dataProvider provideDeprecatedAttributes
         * @covers Sanitizer::fixTagAttributes
         */
-       function testDeprecatedAttributesUnaltered( $inputAttr, $inputEl, $message = '' ) {
+       public function testDeprecatedAttributesUnaltered( $inputAttr, $inputEl, $message = '' ) {
                $this->assertEquals( " $inputAttr",
                        Sanitizer::fixTagAttributes( $inputAttr, $inputEl ),
                        $message
@@ -209,7 +235,7 @@ class SanitizerTest extends MediaWikiTestCase {
                        array( 'align="left"', 'tr' ),
                        array( 'align="center"', 'div' ),
                        array( 'align="left"', 'h1' ),
-                       array( 'align="left"', 'span' ),
+                       array( 'align="left"', 'p' ),
                );
        }
 
@@ -217,7 +243,7 @@ class SanitizerTest extends MediaWikiTestCase {
         * @dataProvider provideCssCommentsFixtures
         * @covers Sanitizer::checkCss
         */
-       function testCssCommentsChecking( $expected, $css, $message = '' ) {
+       public function testCssCommentsChecking( $expected, $css, $message = '' ) {
                $this->assertEquals( $expected,
                        Sanitizer::checkCss( $css ),
                        $message
@@ -265,8 +291,9 @@ class SanitizerTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provideAttributeSupport
+        * @covers Sanitizer::fixTagAttributes
         */
-       function testAttributeSupport( $tag, $attributes, $expected, $message ) {
+       public function testAttributeSupport( $tag, $attributes, $expected, $message ) {
                $this->assertEquals( $expected,
                        Sanitizer::fixTagAttributes( $attributes, $tag ),
                        $message
index fe0bc64..f13e838 100644 (file)
@@ -1,5 +1,10 @@
 <?php
 
+/**
+ * @covers Sanitizer::validateEmail
+ * @TODO all test methods in this class should be refactored and...
+ *    use a single test method and a single data provider...
+ */
 class SanitizerValidateEmailTest extends MediaWikiTestCase {
 
        private function checkEmail( $addr, $expected = true, $msg = '' ) {
@@ -22,54 +27,56 @@ class SanitizerValidateEmailTest extends MediaWikiTestCase {
                $this->checkEmail( $addr, false, $msg );
        }
 
-       function testEmailWellKnownUserAtHostDotTldAreValid() {
+       public function testEmailWellKnownUserAtHostDotTldAreValid() {
                $this->valid( 'user@example.com' );
                $this->valid( 'user@example.museum' );
        }
 
-       function testEmailWithUpperCaseCharactersAreValid() {
+       public function testEmailWithUpperCaseCharactersAreValid() {
                $this->valid( 'USER@example.com' );
                $this->valid( 'user@EXAMPLE.COM' );
                $this->valid( 'user@Example.com' );
                $this->valid( 'USER@eXAMPLE.com' );
        }
 
-       function testEmailWithAPlusInUserName() {
+       public function testEmailWithAPlusInUserName() {
                $this->valid( 'user+sub@example.com' );
                $this->valid( 'user+@example.com' );
        }
 
-       function testEmailDoesNotNeedATopLevelDomain() {
+       public function testEmailDoesNotNeedATopLevelDomain() {
                $this->valid( "user@localhost" );
                $this->valid( "FooBar@localdomain" );
                $this->valid( "nobody@mycompany" );
        }
 
-       function testEmailWithWhiteSpacesBeforeOrAfterAreInvalids() {
+       public function testEmailWithWhiteSpacesBeforeOrAfterAreInvalids() {
                $this->invalid( " user@host.com" );
                $this->invalid( "user@host.com " );
                $this->invalid( "\tuser@host.com" );
                $this->invalid( "user@host.com\t" );
        }
 
-       function testEmailWithWhiteSpacesAreInvalids() {
+       public function testEmailWithWhiteSpacesAreInvalids() {
                $this->invalid( "User user@host" );
                $this->invalid( "first last@mycompany" );
                $this->invalid( "firstlast@my company" );
        }
 
-       // bug 26948 : comma were matched by an incorrect regexp range
-       function testEmailWithCommasAreInvalids() {
+       /**
+        * bug 26948 : comma were matched by an incorrect regexp range
+        */
+       public function testEmailWithCommasAreInvalids() {
                $this->invalid( "user,foo@example.org" );
                $this->invalid( "userfoo@ex,ample.org" );
        }
 
-       function testEmailWithHyphens() {
+       public function testEmailWithHyphens() {
                $this->valid( "user-foo@example.org" );
                $this->valid( "userfoo@ex-ample.org" );
        }
 
-       function testEmailDomainCanNotBeginWithDot() {
+       public function testEmailDomainCanNotBeginWithDot() {
                $this->invalid( "user@." );
                $this->invalid( "user@.localdomain" );
                $this->invalid( "user@localdomain." );
@@ -78,19 +85,19 @@ class SanitizerValidateEmailTest extends MediaWikiTestCase {
                $this->invalid( ".@a............" );
        }
 
-       function testEmailWithFunnyCharacters() {
+       public function testEmailWithFunnyCharacters() {
                $this->valid( "\$user!ex{this}@123.com" );
        }
 
-       function testEmailTopLevelDomainCanBeNumerical() {
+       public function testEmailTopLevelDomainCanBeNumerical() {
                $this->valid( "user@example.1234" );
        }
 
-       function testEmailWithoutAtSignIsInvalid() {
+       public function testEmailWithoutAtSignIsInvalid() {
                $this->invalid( 'useràexample.com' );
        }
 
-       function testEmailWithOneCharacterDomainIsValid() {
+       public function testEmailWithOneCharacterDomainIsValid() {
                $this->valid( 'user@a' );
        }
 }
index 181a913..053d8a7 100644 (file)
@@ -24,7 +24,11 @@ function getSiteParams( $conf, $wiki ) {
 }
 
 class SiteConfigurationTest extends MediaWikiTestCase {
-       var $mConf;
+
+       /**
+        * @var SiteConfiguration
+        */
+       protected $mConf;
 
        protected function setUp() {
                parent::setUp();
@@ -95,7 +99,10 @@ class SiteConfigurationTest extends MediaWikiTestCase {
                $GLOBALS['global'] = array( 'global' => 'global' );
        }
 
-       function testSiteFromDb() {
+       /**
+        * @covers SiteConfiguration::siteFromDB
+        */
+       public function testSiteFromDb() {
                $this->assertEquals(
                        array( 'wikipedia', 'en' ),
                        $this->mConf->siteFromDB( 'enwiki' ),
@@ -120,7 +127,10 @@ class SiteConfigurationTest extends MediaWikiTestCase {
                );
        }
 
-       function testGetLocalDatabases() {
+       /**
+        * @covers SiteConfiguration::getLocalDatabases
+        */
+       public function testGetLocalDatabases() {
                $this->assertEquals(
                        array( 'enwiki', 'dewiki', 'frwiki' ),
                        $this->mConf->getLocalDatabases(),
@@ -128,7 +138,10 @@ class SiteConfigurationTest extends MediaWikiTestCase {
                );
        }
 
-       function testGetConfVariables() {
+       /**
+        * @covers SiteConfiguration::get
+        */
+       public function testGetConfVariables() {
                $this->assertEquals(
                        'enwiki',
                        $this->mConf->get( 'simple', 'enwiki', 'wiki' ),
@@ -240,7 +253,10 @@ class SiteConfigurationTest extends MediaWikiTestCase {
                );
        }
 
-       function testSiteFromDbWithCallback() {
+       /**
+        * @covers SiteConfiguration::siteFromDB
+        */
+       public function testSiteFromDbWithCallback() {
                $this->mConf->siteParamsCallback = 'getSiteParams';
 
                $this->assertEquals(
@@ -260,7 +276,10 @@ class SiteConfigurationTest extends MediaWikiTestCase {
                );
        }
 
-       function testParameterReplacement() {
+       /**
+        * @covers SiteConfiguration::get
+        */
+       public function testParameterReplacement() {
                $this->mConf->siteParamsCallback = 'getSiteParams';
 
                $this->assertEquals(
@@ -290,7 +309,10 @@ class SiteConfigurationTest extends MediaWikiTestCase {
                );
        }
 
-       function testGetAllGlobals() {
+       /**
+        * @covers SiteConfiguration::getAll
+        */
+       public function testGetAllGlobals() {
                $this->mConf->siteParamsCallback = 'getSiteParams';
 
                $getall = array(
diff --git a/tests/phpunit/includes/StatusTest.php b/tests/phpunit/includes/StatusTest.php
new file mode 100644 (file)
index 0000000..0a7cc04
--- /dev/null
@@ -0,0 +1,202 @@
+<?php
+
+/**
+ * @author Adam Shorland
+ */
+class StatusTest extends MediaWikiTestCase {
+
+       public function testCanConstruct(){
+               new Status();
+               $this->assertTrue( true );
+       }
+
+       /**
+        * @dataProvider provideValues
+        * @covers Status::newGood
+        * @covers Status::getValue
+        * @covers Status::isGood
+        * @covers Status::isOK
+        */
+       public function testNewGood( $value = null ){
+               $status = Status::newGood( $value );
+               $this->assertTrue( $status->isGood() );
+               $this->assertTrue( $status->isOK() );
+               $this->assertEquals( $value, $status->getValue() );
+       }
+
+       public static function provideValues(){
+               return array(
+                       array(),
+                       array( 'foo' ),
+                       array( array( 'foo' => 'bar' ) ),
+                       array( new Exception() ),
+                       array( 1234 ),
+               );
+       }
+
+       /**
+        * @covers Status::newFatal
+        * @covers Status::isGood
+        * @covers Status::isOK
+        * @covers Status::getMessage
+        */
+       public function testNewFatalWithMessage() {
+               $message = $this->getMockBuilder( 'Message' )
+                       ->disableOriginalConstructor()
+                       ->getMock();
+
+               $status = Status::newFatal( $message );
+               $this->assertFalse( $status->isGood() );
+               $this->assertFalse( $status->isOK() );
+               $this->assertEquals( $message, $status->getMessage() );
+       }
+
+       /**
+        * @covers Status::newFatal
+        * @covers Status::isGood
+        * @covers Status::isOK
+        * @covers Status::getMessage
+        */
+       public function testNewFatalWithString() {
+               $message = 'foo';
+               $status = Status::newFatal( $message );
+               $this->assertFalse( $status->isGood() );
+               $this->assertFalse( $status->isOK() );
+               $newMessage = $status->getMessage();
+               $this->assertEquals( $message, $newMessage->getKey() );
+       }
+
+       /**
+        * @dataProvider provideSetResult
+        * @covers Status::getValue
+        * @covers Status::isOK
+        */
+       public function testSetResult( $ok, $value = null ) {
+               $status = new Status();
+               $status->setResult( $ok, $value );
+               $this->assertEquals( $ok, $status->isOK() );
+               $this->assertEquals( $value, $status->getValue() );
+       }
+
+       public static function provideSetResult() {
+               return array(
+                       array( true ),
+                       array( false ),
+                       array( true, 'value' ),
+                       array( false, 'value' ),
+               );
+       }
+
+       /**
+        * @dataProvider provideMockMessageDetails
+        * @covers Status::warning
+        * @covers Status::getWarningsArray
+        */
+       public function testWarningWithMessage( $mockDetails ) {
+               $status = new Status();
+               $messages = $this->getMockMessages( $mockDetails );
+
+               foreach( $messages as $message ){
+                       $status->warning( $message );
+               }
+               $warnings = $status->getWarningsArray();
+
+               $this->assertEquals( count( $messages ), count( $warnings ) );
+               foreach( $messages as $key => $message ) {
+                       $expectedArray = array_merge( array( $message->getKey() ), $message->getParams() );
+                       $this->assertEquals( $warnings[$key], $expectedArray );
+               }
+       }
+
+       /**
+        * @dataProvider provideMockMessageDetails
+        * @covers Status::error
+        * @covers Status::getErrorsArray
+        */
+       public function testErrorWithMessage( $mockDetails ) {
+               $status = new Status();
+               $messages = $this->getMockMessages( $mockDetails );
+
+               foreach( $messages as $message ){
+                       $status->error( $message );
+               }
+               $errors = $status->getErrorsArray();
+
+               $this->assertEquals( count( $messages ), count( $errors ) );
+               foreach( $messages as $key => $message ) {
+                       $expectedArray = array_merge( array( $message->getKey() ), $message->getParams() );
+                       $this->assertEquals( $errors[$key], $expectedArray );
+               }
+       }
+
+       protected function getMockMessage( $key = 'key', $params = array() ) {
+               $message = $this->getMockBuilder( 'Message' )
+                       ->disableOriginalConstructor()
+                       ->getMock();
+               $message->expects( $this->atLeastOnce() )
+                       ->method( 'getKey' )
+                       ->will( $this->returnValue( $key ) );
+               $message->expects( $this->atLeastOnce() )
+                       ->method( 'getParams' )
+                       ->will( $this->returnValue( $params ) );
+               return $message;
+       }
+
+       /**
+        * @param array $messageDetails eg. array( 'KEY' => array(/PARAMS/) )
+        * @return Message[]
+        */
+       protected function getMockMessages( $messageDetails ){
+               $messages = array();
+               foreach( $messageDetails as $key => $paramsArray ){
+                       $messages[] = $this->getMockMessage( $key, $paramsArray );
+               }
+               return $messages;
+       }
+
+       public static function provideMockMessageDetails(){
+               return array(
+                       array( array( 'key1' => array( 'foo' => 'bar' ) ) ),
+                       array( array( 'key1' => array( 'foo' => 'bar' ), 'key2' => array( 'foo2' => 'bar2' ) ) ),
+               );
+       }
+
+       /**
+        * @covers Status::merge
+        * @todo test merge with $overwriteValue true
+        */
+       public function testMerge(){
+               $status1 = new Status();
+               $status2 = new Status();
+               $message1 = $this->getMockMessage( 'warn1' );
+               $message2 = $this->getMockMessage( 'error2' );
+               $status1->warning( $message1 );
+               $status2->error( $message2 );
+
+               $status1->merge( $status2 );
+               $this->assertEquals( 2, count( $status1->getWarningsArray() ) + count( $status1->getErrorsArray() ) );
+       }
+
+       /**
+        * @covers Status::hasMessage
+        */
+       public function testHasMessage() {
+               $status = new Status();
+               $status->fatal( 'bad' );
+               $this->assertTrue( $status->hasMessage( 'bad' ) );
+               $this->assertFalse( $status->hasMessage( 'good' ) );
+
+       }
+
+       //todo test cleanParams
+       //todo test getWikiText
+       //todo test getMessage
+       //todo test getErrorMessage
+       //todo test getHTML
+       //todo test getErrorMessageArray
+       //todo test getStatusArray
+       //todo test getErrorsByType
+       //todo test replaceMessage
+       //todo test replaceMessage
+
+}
index b222812..89759e5 100644 (file)
@@ -9,7 +9,7 @@ class StringUtilsTest extends MediaWikiTestCase {
         * @covers StringUtils::isUtf8
         * @dataProvider provideStringsForIsUtf8Check
         */
-       function testIsUtf8WithMbstring( $expected, $string ) {
+       public function testIsUtf8WithMbstring( $expected, $string ) {
                if ( !function_exists( 'mb_check_encoding' ) ) {
                        $this->markTestSkipped( 'Test requires the mbstring PHP extension' );
                }
@@ -27,7 +27,7 @@ class StringUtilsTest extends MediaWikiTestCase {
         * @covers StringUtils::isUtf8
         * @dataProvider provideStringsForIsUtf8Check
         */
-       function testIsUtf8WithPhpFallbackImplementation( $expected, $string ) {
+       public function testIsUtf8WithPhpFallbackImplementation( $expected, $string ) {
                $this->assertEquals( $expected,
                        StringUtils::isUtf8( $string, /** disable mbstring: */true ),
                        'Testing string "' . $this->escaped( $string ) . '" with pure PHP implementation'
index ffa8c42..fb63a56 100644 (file)
@@ -7,20 +7,37 @@ require __DIR__ . "/../../../maintenance/runJobs.php";
 
 class TemplateCategoriesTest extends MediaWikiLangTestCase {
 
-       function testTemplateCategories() {
+       /**
+        * @covers Title::getParentCategories
+        */
+       public function testTemplateCategories() {
                $title = Title::newFromText( "Categorized from template" );
                $page = WikiPage::factory( $title );
                $user = new User();
                $user->mRights = array( 'createpage', 'edit', 'purge' );
 
-               $page->doEditContent( new WikitextContent( '{{Categorising template}}' ), 'Create a page with a template', 0, false, $user );
+               $page->doEditContent(
+                       new WikitextContent( '{{Categorising template}}' ),
+                       'Create a page with a template',
+                       0,
+                       false,
+                       $user
+               );
+
                $this->assertEquals(
                        array()
                        , $title->getParentCategories()
                );
 
                $template = WikiPage::factory( Title::newFromText( 'Template:Categorising template' ) );
-               $template->doEditContent( new WikitextContent( '[[Category:Solved bugs]]' ), 'Add a category through a template', 0, false, $user );
+
+               $template->doEditContent(
+                       new WikitextContent( '[[Category:Solved bugs]]' ),
+                       'Add a category through a template',
+                       0,
+                       false,
+                       $user
+               );
 
                // Run the job queue
                JobQueueGroup::destroySingletons();
index 2fb0f49..23e6503 100644 (file)
@@ -1,6 +1,8 @@
 <?php
 
-/* Wraps the user object, so we can also retain full access to properties like password if we log in via the API */
+/**
+ * Wraps the user object, so we can also retain full access to properties like password if we log in via the API
+ */
 class TestUser {
        public $username;
        public $password;
@@ -8,7 +10,7 @@ class TestUser {
        public $groups;
        public $user;
 
-       function __construct( $username, $realname = 'Real Name', $email = 'sample@example.com', $groups = array() ) {
+       public function __construct( $username, $realname = 'Real Name', $email = 'sample@example.com', $groups = array() ) {
                $this->username = $username;
                $this->realname = $realname;
                $this->email = $email;
index 3a2c62a..0b368c2 100644 (file)
@@ -8,8 +8,9 @@ class TimeAdjustTest extends MediaWikiLangTestCase {
        }
 
        /**
-        * Test offset usage for a given language::userAdjust
+        * Test offset usage for a given Language::userAdjust
         * @dataProvider dataUserAdjust
+        * @covers Language::userAdjust
         */
        public function testUserAdjust( $date, $localTZoffset, $expected ) {
                global $wgContLang;
index 3668046..5338839 100644 (file)
@@ -14,8 +14,9 @@ class TimestampTest extends MediaWikiLangTestCase {
        /**
         * Test parsing of valid timestamps and outputing to MW format.
         * @dataProvider provideValidTimestamps
+        * @covers MWTimestamp::getTimestamp
         */
-       function testValidParse( $format, $original, $expected ) {
+       public function testValidParse( $format, $original, $expected ) {
                $timestamp = new MWTimestamp( $original );
                $this->assertEquals( $expected, $timestamp->getTimestamp( TS_MW ) );
        }
@@ -23,8 +24,9 @@ class TimestampTest extends MediaWikiLangTestCase {
        /**
         * Test outputting valid timestamps to different formats.
         * @dataProvider provideValidTimestamps
+        * @covers MWTimestamp::getTimestamp
         */
-       function testValidOutput( $format, $expected, $original ) {
+       public function testValidOutput( $format, $expected, $original ) {
                $timestamp = new MWTimestamp( $original );
                $this->assertEquals( $expected, (string)$timestamp->getTimestamp( $format ) );
        }
@@ -32,16 +34,18 @@ class TimestampTest extends MediaWikiLangTestCase {
        /**
         * Test an invalid timestamp.
         * @expectedException TimestampException
+        * @covers MWTimestamp
         */
-       function testInvalidParse() {
+       public function testInvalidParse() {
                new MWTimestamp( "This is not a timestamp." );
        }
 
        /**
         * Test requesting an invalid output format.
         * @expectedException TimestampException
+        * @covers MWTimestamp::getTimestamp
         */
-       function testInvalidOutput() {
+       public function testInvalidOutput() {
                $timestamp = new MWTimestamp( '1343761268' );
                $timestamp->getTimestamp( 98 );
        }
@@ -69,8 +73,8 @@ class TimestampTest extends MediaWikiLangTestCase {
        }
 
        /**
-        * @test
         * @dataProvider provideHumanTimestampTests
+        * @covers MWTimestamp::getHumanTimestamp
         */
        public function testHumanTimestamp(
                $tsTime, // The timestamp to format
@@ -202,8 +206,8 @@ class TimestampTest extends MediaWikiLangTestCase {
        }
 
        /**
-        * @test
         * @dataProvider provideRelativeTimestampTests
+        * @covers MWTimestamp::getRelativeTimestamp
         */
        public function testRelativeTimestamp(
                $tsTime, // The timestamp to format
index a11c3d9..3079d73 100644 (file)
@@ -6,7 +6,6 @@
  *
  * @note: We don't make assumptions about the main namespace.
  *        But we do expect the Help namespace to contain Wikitext.
- *
  */
 class TitleMethodsTest extends MediaWikiTestCase {
 
@@ -57,6 +56,7 @@ class TitleMethodsTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provideEquals
+        * @covers Title::equals
         */
        public function testEquals( $titleA, $titleB, $expectedBool ) {
                $titleA = Title::newFromText( $titleA );
@@ -81,12 +81,16 @@ class TitleMethodsTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provideInNamespace
+        * @covers Title::inNamespace
         */
        public function testInNamespace( $title, $ns, $expectedBool ) {
                $title = Title::newFromText( $title );
                $this->assertEquals( $expectedBool, $title->inNamespace( $ns ) );
        }
 
+       /**
+        * @covers Title::inNamespaces
+        */
        public function testInNamespaces() {
                $mainpage = Title::newFromText( 'Main Page' );
                $this->assertTrue( $mainpage->inNamespaces( NS_MAIN, NS_USER ) );
@@ -110,6 +114,7 @@ class TitleMethodsTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provideHasSubjectNamespace
+        * @covers Title::hasSubjectNamespace
         */
        public function testHasSubjectNamespace( $title, $ns, $expectedBool ) {
                $title = Title::newFromText( $title );
@@ -143,6 +148,7 @@ class TitleMethodsTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider dataGetContentModel
+        * @covers Title::getContentModel
         */
        public function testGetContentModel( $title, $expectedModelId ) {
                $title = Title::newFromText( $title );
@@ -151,6 +157,7 @@ class TitleMethodsTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider dataGetContentModel
+        * @covers Title::hasContentModel
         */
        public function testHasContentModel( $title, $expectedModelId ) {
                $title = Title::newFromText( $title );
@@ -181,13 +188,13 @@ class TitleMethodsTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provideIsCssOrJsPage
+        * @covers Title::isCssOrJsPage
         */
        public function testIsCssOrJsPage( $title, $expectedBool ) {
                $title = Title::newFromText( $title );
                $this->assertEquals( $expectedBool, $title->isCssOrJsPage() );
        }
 
-
        public static function provideIsCssJsSubpage() {
                return array(
                        array( 'Help:Foo', false ),
@@ -210,6 +217,7 @@ class TitleMethodsTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provideIsCssJsSubpage
+        * @covers Title::isCssJsSubpage
         */
        public function testIsCssJsSubpage( $title, $expectedBool ) {
                $title = Title::newFromText( $title );
@@ -230,6 +238,7 @@ class TitleMethodsTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provideIsCssSubpage
+        * @covers Title::isCssSubpage
         */
        public function testIsCssSubpage( $title, $expectedBool ) {
                $title = Title::newFromText( $title );
@@ -250,6 +259,7 @@ class TitleMethodsTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provideIsJsSubpage
+        * @covers Title::isJsSubpage
         */
        public function testIsJsSubpage( $title, $expectedBool ) {
                $title = Title::newFromText( $title );
@@ -281,6 +291,7 @@ class TitleMethodsTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provideIsWikitextPage
+        * @covers Title::isWikitextPage
         */
        public function testIsWikitextPage( $title, $expectedBool ) {
                $title = Title::newFromText( $title );
index 9144d0c..4a0a755 100644 (file)
@@ -2,6 +2,9 @@
 
 /**
  * @group Database
+ *
+ * @covers Title::getUserPermissionsErrors
+ * @covers Title::getUserPermissionsErrorsInternal
  */
 class TitlePermissionTest extends MediaWikiLangTestCase {
 
@@ -68,7 +71,7 @@ class TitlePermissionTest extends MediaWikiLangTestCase {
                }
        }
 
-       function setUserPerm( $perm ) {
+       protected function setUserPerm( $perm ) {
                // Setting member variables is evil!!!
 
                if ( is_array( $perm ) ) {
@@ -78,11 +81,11 @@ class TitlePermissionTest extends MediaWikiLangTestCase {
                }
        }
 
-       function setTitle( $ns, $title = "Main_Page" ) {
+       protected function setTitle( $ns, $title = "Main_Page" ) {
                $this->title = Title::makeTitle( $ns, $title );
        }
 
-       function setUser( $userName = null ) {
+       protected function setUser( $userName = null ) {
                if ( $userName === 'anon' ) {
                        $this->user = $this->anonUser;
                } elseif ( $userName === null || $userName === $this->userName ) {
@@ -92,7 +95,11 @@ class TitlePermissionTest extends MediaWikiLangTestCase {
                }
        }
 
-       function testQuickPermissions() {
+       /**
+        * @todo This test method should be split up into separate test methods and
+        * data providers
+        */
+       public function testQuickPermissions() {
                global $wgContLang;
                $prefix = $wgContLang->getFormattedNsText( NS_PROJECT );
 
@@ -320,7 +327,7 @@ class TitlePermissionTest extends MediaWikiLangTestCase {
                }
        }
 
-       function runGroupPermissions( $action, $result, $result2 = null ) {
+       protected function runGroupPermissions( $action, $result, $result2 = null ) {
                global $wgGroupPermissions;
 
                if ( $result2 === null ) {
@@ -348,7 +355,11 @@ class TitlePermissionTest extends MediaWikiLangTestCase {
                $this->assertEquals( $result2, $res );
        }
 
-       function testSpecialsAndNSPermissions() {
+       /**
+        * @todo This test method should be split up into separate test methods and
+        * data providers
+        */
+       public function testSpecialsAndNSPermissions() {
                global $wgNamespaceProtection;
                $this->setUser( $this->userName );
 
@@ -399,7 +410,11 @@ class TitlePermissionTest extends MediaWikiLangTestCase {
                        $this->title->userCan( 'bogus', $this->user ) );
        }
 
-       function testCssAndJavascriptPermissions() {
+       /**
+        * @todo This test method should be split up into separate test methods and
+        * data providers
+        */
+       public function testCssAndJavascriptPermissions() {
                $this->setUser( $this->userName );
 
                $this->setTitle( NS_USER, $this->userName . '/test.js' );
@@ -448,7 +463,7 @@ class TitlePermissionTest extends MediaWikiLangTestCase {
                );
        }
 
-       function runCSSandJSPermissions( $result0, $result1, $result2, $result3, $result4 ) {
+       protected function runCSSandJSPermissions( $result0, $result1, $result2, $result3, $result4 ) {
                $this->setUserPerm( '' );
                $this->assertEquals( $result0,
                        $this->title->getUserPermissionsErrors( 'bogus',
@@ -485,7 +500,11 @@ class TitlePermissionTest extends MediaWikiLangTestCase {
                                $this->user ) );
        }
 
-       function testPageRestrictions() {
+       /**
+        * @todo This test method should be split up into separate test methods and
+        * data providers
+        */
+       public function testPageRestrictions() {
                global $wgContLang;
 
                $prefix = $wgContLang->getFormattedNsText( NS_PROJECT );
@@ -576,7 +595,7 @@ class TitlePermissionTest extends MediaWikiLangTestCase {
                                $this->user ) );
        }
 
-       function testCascadingSourcesRestrictions() {
+       public function testCascadingSourcesRestrictions() {
                $this->setTitle( NS_MAIN, "test page" );
                $this->setUserPerm( array( "edit", "bogus" ) );
 
@@ -596,7 +615,11 @@ class TitlePermissionTest extends MediaWikiLangTestCase {
                        $this->title->getUserPermissionsErrors( 'edit', $this->user ) );
        }
 
-       function testActionPermissions() {
+       /**
+        * @todo This test method should be split up into separate test methods and
+        * data providers
+        */
+       public function testActionPermissions() {
                $this->setUserPerm( array( "createpage" ) );
                $this->setTitle( NS_MAIN, "test page" );
                $this->title->mTitleProtection['pt_create_perm'] = '';
@@ -667,7 +690,7 @@ class TitlePermissionTest extends MediaWikiLangTestCase {
                        $this->title->userCan( 'move-target', $this->user ) );
        }
 
-       function testUserBlock() {
+       public function testUserBlock() {
                global $wgEmailConfirmToEdit, $wgEmailAuthentication;
                $wgEmailConfirmToEdit = true;
                $wgEmailAuthentication = true;
index da663c4..6bfe545 100644 (file)
@@ -1,7 +1,6 @@
 <?php
 
 /**
- *
  * @group Database
  *        ^--- needed for language cache stuff
  */
@@ -19,7 +18,10 @@ class TitleTest extends MediaWikiTestCase {
                ) );
        }
 
-       function testLegalChars() {
+       /**
+        * @covers Title::legalChars
+        */
+       public function testLegalChars() {
                $titlechars = Title::legalChars();
 
                foreach ( range( 1, 255 ) as $num ) {
@@ -34,8 +36,10 @@ class TitleTest extends MediaWikiTestCase {
 
        /**
         * See also mediawiki.Title.test.js
+        * @covers Title::secureAndSplit
+        * @todo This method should be split into 2 separate tests each with a provider
         */
-       function testSecureAndSplit() {
+       public function testSecureAndSplit() {
                // Valid
                foreach ( array(
                        'Sandbox',
@@ -171,15 +175,17 @@ class TitleTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provideConvertByteClassToUnicodeClass
+        * @covers Title::convertByteClassToUnicodeClass
         */
-       function testConvertByteClassToUnicodeClass( $byteClass, $unicodeClass ) {
+       public function testConvertByteClassToUnicodeClass( $byteClass, $unicodeClass ) {
                $this->assertEquals( $unicodeClass, Title::convertByteClassToUnicodeClass( $byteClass ) );
        }
 
        /**
         * @dataProvider provideBug31100
+        * @covers Title::fixSpecialName
         */
-       function testBug31100FixSpecialName( $text, $expectedParam ) {
+       public function testBug31100FixSpecialName( $text, $expectedParam ) {
                $title = Title::newFromText( $text );
                $fixed = $title->fixSpecialName();
                $stuff = explode( '/', $fixed->getDBkey(), 2 );
@@ -205,10 +211,11 @@ class TitleTest extends MediaWikiTestCase {
         * @group Database
         * @param string $source
         * @param string $target
-        * @param array|string|true $expected Required error
+        * @param array|string|bool $expected Required error
         * @dataProvider provideTestIsValidMoveOperation
+        * @covers Title::isValidMoveOperation
         */
-       function testIsValidMoveOperation( $source, $target, $expected ) {
+       public function testIsValidMoveOperation( $source, $target, $expected ) {
                $title = Title::newFromText( $source );
                $nt = Title::newFromText( $target );
                $errors = $title->isValidMoveOperation( $nt, false );
@@ -225,7 +232,7 @@ class TitleTest extends MediaWikiTestCase {
        /**
         * Provides test parameter values for testIsValidMoveOperation()
         */
-       function dataTestIsValidMoveOperation() {
+       public function dataTestIsValidMoveOperation() {
                return array(
                        array( 'Test', 'Test', 'selfmove' ),
                        array( 'File:Test.jpg', 'Page', 'imagenocrossnamespace' )
@@ -238,12 +245,12 @@ class TitleTest extends MediaWikiTestCase {
         * @param array $whitelistRegexp
         * @param string $source
         * @param string $action
-        * @param array|string|true $expected Required error
+        * @param array|string|bool $expected Required error
         *
         * @covers Title::checkReadPermissions
         * @dataProvider dataWgWhitelistReadRegexp
         */
-       function testWgWhitelistReadRegexp( $whitelistRegexp, $source, $action, $expected ) {
+       public function testWgWhitelistReadRegexp( $whitelistRegexp, $source, $action, $expected ) {
                // $wgWhitelistReadRegexp must be an array. Since the provided test cases
                // usually have only one regex, it is more concise to write the lonely regex
                // as a string. Thus we cast to an array() to honor $wgWhitelistReadRegexp
@@ -300,7 +307,7 @@ class TitleTest extends MediaWikiTestCase {
        /**
         * Provides test parameter values for testWgWhitelistReadRegexp()
         */
-       function dataWgWhitelistReadRegexp() {
+       public function dataWgWhitelistReadRegexp() {
                $ALLOWED = true;
                $DISALLOWED = false;
 
@@ -336,7 +343,7 @@ class TitleTest extends MediaWikiTestCase {
                );
        }
 
-       function flattenErrorsArray( $errors ) {
+       public function flattenErrorsArray( $errors ) {
                $result = array();
                foreach ( $errors as $error ) {
                        $result[] = $error[0];
@@ -353,9 +360,10 @@ class TitleTest extends MediaWikiTestCase {
        }
 
        /**
-        * @dataProvider provideCasesForGetpageviewlanguage
+        * @dataProvider provideGetPageViewLanguage
+        * @covers Title::getPageViewLanguage
         */
-       function testGetpageviewlanguage( $expected, $titleText, $contLang, $lang, $variant, $msg = '' ) {
+       public function testGetPageViewLanguage( $expected, $titleText, $contLang, $lang, $variant, $msg = '' ) {
                global $wgLanguageCode, $wgContLang, $wgLang, $wgDefaultLanguageVariant, $wgAllowUserJs;
 
                // Setup environnement for this test
@@ -375,7 +383,7 @@ class TitleTest extends MediaWikiTestCase {
                );
        }
 
-       public static function provideCasesForGetpageviewlanguage() {
+       public static function provideGetPageViewLanguage() {
                # Format:
                # - expected
                # - Title name
@@ -416,8 +424,9 @@ class TitleTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provideBaseTitleCases
+        * @covers Title::getBaseText
         */
-       function testExtractingBaseTextFromTitle( $title, $expected, $msg = '' ) {
+       public function testGetBaseText( $title, $expected, $msg = '' ) {
                $title = Title::newFromText( $title );
                $this->assertEquals( $expected,
                        $title->getBaseText(),
@@ -435,8 +444,9 @@ class TitleTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provideRootTitleCases
+        * @covers Title::getRootText
         */
-       function testExtractingRootTextFromTitle( $title, $expected, $msg = '' ) {
+       public function testGetRootText( $title, $expected, $msg = '' ) {
                $title = Title::newFromText( $title );
                $this->assertEquals( $expected,
                        $title->getRootText(),
@@ -455,8 +465,9 @@ class TitleTest extends MediaWikiTestCase {
        /**
         * @todo Handle $wgNamespacesWithSubpages cases
         * @dataProvider provideSubpageTitleCases
+        * @covers Title::getSubpageText
         */
-       function testExtractingSubpageTextFromTitle( $title, $expected, $msg = '' ) {
+       public function testGetSubpageText( $title, $expected, $msg = '' ) {
                $title = Title::newFromText( $title );
                $this->assertEquals( $expected,
                        $title->getSubpageText(),
index 23553ca..8f78ae5 100644 (file)
@@ -1,8 +1,11 @@
 <?php
 
 class UIDGeneratorTest extends MediaWikiTestCase {
+
        /**
         * @dataProvider provider_testTimestampedUID
+        * @covers UIDGenerator::newTimestampedUID128
+        * @covers UIDGenerator::newTimestampedUID88
         */
        public function testTimestampedUID( $method, $digitlen, $bits, $tbits, $hostbits ) {
                $id = call_user_func( array( 'UIDGenerator', $method ) );
@@ -46,6 +49,7 @@ class UIDGeneratorTest extends MediaWikiTestCase {
 
        /**
         * array( method, length, bits, hostbits )
+        * NOTE: When adding a new method name here please update the covers tags for the tests!
         */
        public static function provider_testTimestampedUID() {
                return array(
@@ -55,22 +59,40 @@ class UIDGeneratorTest extends MediaWikiTestCase {
                );
        }
 
+       /**
+        * @covers UIDGenerator::newUUIDv4
+        */
        public function testUUIDv4() {
                for ( $i = 0; $i < 100; $i++ ) {
                        $id = UIDGenerator::newUUIDv4();
                        $this->assertEquals( true,
                                preg_match( '!^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$!', $id ),
                                "UID $id has the right format" );
+               }
+       }
 
+       /**
+        * @covers UIDGenerator::newRawUUIDv4
+        */
+       public function testRawUUIDv4() {
+               for ( $i = 0; $i < 100; $i++ ) {
                        $id = UIDGenerator::newRawUUIDv4();
                        $this->assertEquals( true,
                                preg_match( '!^[0-9a-f]{12}4[0-9a-f]{3}[89ab][0-9a-f]{15}$!', $id ),
                                "UID $id has the right format" );
+               }
+       }
 
+       /**
+        * @covers UIDGenerator::newRawUUIDv4
+        */
+       public function testRawUUIDv4QuickRand() {
+               for ( $i = 0; $i < 100; $i++ ) {
                        $id = UIDGenerator::newRawUUIDv4( UIDGenerator::QUICK_RAND );
                        $this->assertEquals( true,
                                preg_match( '!^[0-9a-f]{12}4[0-9a-f]{3}[89ab][0-9a-f]{15}$!', $id ),
                                "UID $id has the right format" );
                }
        }
+
 }
diff --git a/tests/phpunit/includes/UserMailerTest.php b/tests/phpunit/includes/UserMailerTest.php
new file mode 100644 (file)
index 0000000..278edfa
--- /dev/null
@@ -0,0 +1,14 @@
+<?php
+
+class UserMailerTest extends MediaWikiLangTestCase {
+
+       /**
+        * @covers UserMailer::quotedPrintable
+        */
+       public function testQuotedPrintable() {
+               $this->assertEquals(
+                       "=?UTF-8?Q?=C4=88u=20legebla=3F?=",
+                       UserMailer::quotedPrintable( "\xc4\x88u legebla?", "UTF-8" ) );
+       }
+
+}
\ No newline at end of file
index 0113cab..ff33e82 100644 (file)
@@ -53,6 +53,9 @@ class UserTest extends MediaWikiTestCase {
                );
        }
 
+       /**
+        * @covers User::getGroupPermissions
+        */
        public function testGroupPermissions() {
                $rights = User::getGroupPermissions( array( 'unittesters' ) );
                $this->assertContains( 'runtest', $rights );
@@ -67,6 +70,9 @@ class UserTest extends MediaWikiTestCase {
                $this->assertNotContains( 'nukeworld', $rights );
        }
 
+       /**
+        * @covers User::getGroupPermissions
+        */
        public function testRevokePermissions() {
                $rights = User::getGroupPermissions( array( 'unittesters', 'formertesters' ) );
                $this->assertNotContains( 'runtest', $rights );
@@ -75,6 +81,9 @@ class UserTest extends MediaWikiTestCase {
                $this->assertNotContains( 'nukeworld', $rights );
        }
 
+       /**
+        * @covers User::getRights
+        */
        public function testUserPermissions() {
                $rights = $this->user->getRights();
                $this->assertContains( 'runtest', $rights );
@@ -85,6 +94,7 @@ class UserTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provideGetGroupsWithPermission
+        * @covers User::getGroupsWithPermission
         */
        public function testGetGroupsWithPermission( $expected, $right ) {
                $result = User::getGroupsWithPermission( $right );
@@ -117,6 +127,7 @@ class UserTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provideUserNames
+        * @covers User::isValidUserName
         */
        public function testIsValidUserName( $username, $result, $message ) {
                $this->assertEquals( $this->user->isValidUserName( $username ), $result, $message );
@@ -171,6 +182,7 @@ class UserTest extends MediaWikiTestCase {
        /**
         * Test User::editCount
         * @group medium
+        * @covers User::getEditCount
         */
        public function testEditCount() {
                $user = User::newFromName( 'UnitTestUser' );
@@ -195,6 +207,8 @@ class UserTest extends MediaWikiTestCase {
 
        /**
         * Test changing user options.
+        * @covers User::setOption
+        * @covers User::getOption
         */
        public function testOptions() {
                $user = User::newFromName( 'UnitTestUser' );
@@ -212,6 +226,7 @@ class UserTest extends MediaWikiTestCase {
        /**
         * Bug 37963
         * Make sure defaults are loaded when setOption is called.
+        * @covers User::loadOptions
         */
        public function testAnonOptions() {
                global $wgDefaultUserOptions;
index 4f5322e..f8ed14b 100644 (file)
@@ -20,8 +20,9 @@ class WebRequestTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provideDetectServer
+        * @covers WebRequest::detectServer
         */
-       function testDetectServer( $expected, $input, $description ) {
+       public function testDetectServer( $expected, $input, $description ) {
                $_SERVER = $input;
                $result = WebRequest::detectServer();
                $this->assertEquals( $expected, $result, $description );
@@ -103,8 +104,9 @@ class WebRequestTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provideGetIP
+        * @covers WebRequest::getIP
         */
-       function testGetIP( $expected, $input, $squid, $xffList, $private, $description ) {
+       public function testGetIP( $expected, $input, $squid, $xffList, $private, $description ) {
                $_SERVER = $input;
                $this->setMwGlobals( array(
                        'wgSquidServersNoPurge' => $squid,
@@ -272,8 +274,9 @@ class WebRequestTest extends MediaWikiTestCase {
 
        /**
         * @expectedException MWException
+        * @covers WebRequest::getIP
         */
-       function testGetIpLackOfRemoteAddrThrowAnException() {
+       public function testGetIpLackOfRemoteAddrThrowAnException() {
                $request = new WebRequest();
                # Next call throw an exception about lacking an IP
                $request->getIP();
@@ -297,8 +300,9 @@ class WebRequestTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provideLanguageData
+        * @covers WebRequest::getAcceptLang
         */
-       function testAcceptLang( $acceptLanguageHeader, $expectedLanguages, $description ) {
+       public function testAcceptLang( $acceptLanguageHeader, $expectedLanguages, $description ) {
                $_SERVER = array( 'HTTP_ACCEPT_LANGUAGE' => $acceptLanguageHeader );
                $request = new WebRequest();
                $this->assertSame( $request->getAcceptLang(), $expectedLanguages, $description );
index bf8cd37..1258eb1 100644 (file)
@@ -1,14 +1,14 @@
 <?php
+
 /**
  * @group ContentHandler
  * @group Database
  * ^--- important, causes temporary tables to be used instead of the real database
  * @group medium
  **/
-
 class WikiPageTest extends MediaWikiLangTestCase {
 
-       var $pages_to_delete;
+       protected $pages_to_delete;
 
        function __construct( $name = null, array $data = array(), $dataName = '' ) {
                parent::__construct( $name, $data, $dataName );
@@ -90,6 +90,9 @@ class WikiPageTest extends MediaWikiLangTestCase {
                return $page;
        }
 
+       /**
+        * @covers WikiPage::doEditContent
+        */
        public function testDoEditContent() {
                $page = $this->newPage( "WikiPageTest_testDoEditContent" );
                $title = $page->getTitle();
@@ -143,6 +146,9 @@ class WikiPageTest extends MediaWikiLangTestCase {
                $this->assertEquals( 2, $n, 'pagelinks should contain two links from the page' );
        }
 
+       /**
+        * @covers WikiPage::doEdit
+        */
        public function testDoEdit() {
                $this->hideDeprecated( "WikiPage::doEdit" );
                $this->hideDeprecated( "WikiPage::getText" );
@@ -200,6 +206,9 @@ class WikiPageTest extends MediaWikiLangTestCase {
                $this->assertEquals( 2, $n, 'pagelinks should contain two links from the page' );
        }
 
+       /**
+        * @covers WikiPage::doQuickEdit
+        */
        public function testDoQuickEdit() {
                global $wgUser;
 
@@ -216,6 +225,9 @@ class WikiPageTest extends MediaWikiLangTestCase {
                $this->assertEquals( $text, $page->getText() );
        }
 
+       /**
+        * @covers WikiPage::doQuickEditContent
+        */
        public function testDoQuickEditContent() {
                global $wgUser;
 
@@ -229,6 +241,9 @@ class WikiPageTest extends MediaWikiLangTestCase {
                $this->assertTrue( $content->equals( $page->getContent() ) );
        }
 
+       /**
+        * @covers WikiPage::doDeleteArticle
+        */
        public function testDoDeleteArticle() {
                $page = $this->createPage( "WikiPageTest_testDoDeleteArticle", "[[original text]] foo", CONTENT_MODEL_WIKITEXT );
                $id = $page->getId();
@@ -253,6 +268,9 @@ class WikiPageTest extends MediaWikiLangTestCase {
                $this->assertEquals( 0, $n, 'pagelinks should contain no more links from the page' );
        }
 
+       /**
+        * @covers WikiPage::doDeleteUpdates
+        */
        public function testDoDeleteUpdates() {
                $page = $this->createPage( "WikiPageTest_testDoDeleteArticle", "[[original text]] foo", CONTENT_MODEL_WIKITEXT );
                $id = $page->getId();
@@ -268,6 +286,9 @@ class WikiPageTest extends MediaWikiLangTestCase {
                $this->assertEquals( 0, $n, 'pagelinks should contain no more links from the page' );
        }
 
+       /**
+        * @covers WikiPage::getRevision
+        */
        public function testGetRevision() {
                $page = $this->newPage( "WikiPageTest_testGetRevision" );
 
@@ -283,6 +304,9 @@ class WikiPageTest extends MediaWikiLangTestCase {
                $this->assertEquals( "some text", $rev->getContent()->getNativeData() );
        }
 
+       /**
+        * @covers WikiPage::getContent
+        */
        public function testGetContent() {
                $page = $this->newPage( "WikiPageTest_testGetContent" );
 
@@ -296,6 +320,9 @@ class WikiPageTest extends MediaWikiLangTestCase {
                $this->assertEquals( "some text", $content->getNativeData() );
        }
 
+       /**
+        * @covers WikiPage::getText
+        */
        public function testGetText() {
                $this->hideDeprecated( "WikiPage::getText" );
 
@@ -311,6 +338,9 @@ class WikiPageTest extends MediaWikiLangTestCase {
                $this->assertEquals( "some text", $text );
        }
 
+       /**
+        * @covers WikiPage::getRawText
+        */
        public function testGetRawText() {
                $this->hideDeprecated( "WikiPage::getRawText" );
 
@@ -326,6 +356,9 @@ class WikiPageTest extends MediaWikiLangTestCase {
                $this->assertEquals( "some text", $text );
        }
 
+       /**
+        * @covers WikiPage::getContentModel
+        */
        public function testGetContentModel() {
                global $wgContentHandlerUseDB;
 
@@ -339,6 +372,9 @@ class WikiPageTest extends MediaWikiLangTestCase {
                $this->assertEquals( CONTENT_MODEL_JAVASCRIPT, $page->getContentModel() );
        }
 
+       /**
+        * @covers WikiPage::getContentHandler
+        */
        public function testGetContentHandler() {
                global $wgContentHandlerUseDB;
 
@@ -352,6 +388,9 @@ class WikiPageTest extends MediaWikiLangTestCase {
                $this->assertEquals( 'JavaScriptContentHandler', get_class( $page->getContentHandler() ) );
        }
 
+       /**
+        * @covers WikiPage::exists
+        */
        public function testExists() {
                $page = $this->newPage( "WikiPageTest_testExists" );
                $this->assertFalse( $page->exists() );
@@ -383,6 +422,7 @@ class WikiPageTest extends MediaWikiLangTestCase {
 
        /**
         * @dataProvider provideHasViewableContent
+        * @covers WikiPage::hasViewableContent
         */
        public function testHasViewableContent( $title, $viewable, $create = false ) {
                $page = $this->newPage( $title );
@@ -406,6 +446,7 @@ class WikiPageTest extends MediaWikiLangTestCase {
 
        /**
         * @dataProvider provideGetRedirectTarget
+        * @covers WikiPage::getRedirectTarget
         */
        public function testGetRedirectTarget( $title, $model, $text, $target ) {
                $page = $this->createPage( $title, $text, $model );
@@ -421,6 +462,7 @@ class WikiPageTest extends MediaWikiLangTestCase {
 
        /**
         * @dataProvider provideGetRedirectTarget
+        * @covers WikiPage::isRedirect
         */
        public function testIsRedirect( $title, $model, $text, $target ) {
                $page = $this->createPage( $title, $text, $model );
@@ -537,6 +579,7 @@ class WikiPageTest extends MediaWikiLangTestCase {
 
        /**
         * @dataProvider provideIsCountable
+        * @covers WikiPage::isCountable
         */
        public function testIsCountable( $title, $model, $text, $mode, $expected ) {
                global $wgContentHandlerUseDB;
@@ -575,6 +618,7 @@ class WikiPageTest extends MediaWikiLangTestCase {
 
        /**
         * @dataProvider provideGetParserOutput
+        * @covers WikiPage::getParserOutput
         */
        public function testGetParserOutput( $model, $text, $expectedHtml ) {
                $page = $this->createPage( 'WikiPageTest_testGetParserOutput', $text, $model );
@@ -591,6 +635,9 @@ class WikiPageTest extends MediaWikiLangTestCase {
                return $po;
        }
 
+       /**
+        * @covers WikiPage::getParserOutput
+        */
        public function testGetParserOutput_nonexisting() {
                static $count = 0;
                $count++;
@@ -603,6 +650,9 @@ class WikiPageTest extends MediaWikiLangTestCase {
                $this->assertFalse( $po, "getParserOutput() shall return false for non-existing pages." );
        }
 
+       /**
+        * @covers WikiPage::getParserOutput
+        */
        public function testGetParserOutput_badrev() {
                $page = $this->createPage( 'WikiPageTest_testGetParserOutput', "dummy", CONTENT_MODEL_WIKITEXT );
 
@@ -679,6 +729,7 @@ more stuff
 
        /**
         * @dataProvider dataReplaceSection
+        * @covers WikiPage::replaceSection
         */
        public function testReplaceSection( $title, $model, $text, $section, $with, $sectionTitle, $expected ) {
                $this->hideDeprecated( "WikiPage::replaceSection" );
@@ -692,6 +743,7 @@ more stuff
 
        /**
         * @dataProvider dataReplaceSection
+        * @covers WikiPage::replaceSectionContent
         */
        public function testReplaceSectionContent( $title, $model, $text, $section, $with, $sectionTitle, $expected ) {
                $page = $this->createPage( $title, $text, $model );
@@ -802,6 +854,7 @@ more stuff
 
        /**
         * @todo FIXME: the above rollback test is better, but it keeps failing in jenkins for some reason.
+        * @covers WikiPage::doRollback
         */
        public function testDoRollback() {
                $admin = new User();
@@ -878,6 +931,7 @@ more stuff
 
        /**
         * @dataProvider provideGetAutoSummary
+        * @covers WikiPage::getAutosummary
         */
        public function testGetAutosummary( $old, $new, $flags, $expected ) {
                $this->hideDeprecated( "WikiPage::getAutosummary" );
@@ -950,6 +1004,7 @@ more stuff
 
        /**
         * @dataProvider provideGetAutoDeleteReason
+        * @covers WikiPage::getAutoDeleteReason
         */
        public function testGetAutoDeleteReason( $edits, $expectedResult, $expectedHistory ) {
                global $wgUser;
@@ -1003,6 +1058,7 @@ more stuff
 
        /**
         * @dataProvider providePreSaveTransform
+        * @covers WikiPage::preSaveTransform
         */
        public function testPreSaveTransform( $text, $expected ) {
                $this->hideDeprecated( 'WikiPage::preSaveTransform' );
@@ -1015,4 +1071,21 @@ more stuff
 
                $this->assertEquals( $expected, $text );
        }
+
+       /**
+        * @covers WikiPage::factory
+        */
+       public function testWikiPageFactory() {
+               $title = Title::makeTitle( NS_FILE, 'Someimage.png' );
+               $page = WikiPage::factory( $title );
+               $this->assertEquals( 'WikiFilePage', get_class( $page ) );
+
+               $title = Title::makeTitle( NS_CATEGORY, 'SomeCategory' );
+               $page = WikiPage::factory( $title );
+               $this->assertEquals( 'WikiCategoryPage', get_class( $page ) );
+
+               $title = Title::makeTitle( NS_MAIN, 'SomePage' );
+               $page = WikiPage::factory( $title );
+               $this->assertEquals( 'WikiPage', get_class( $page ) );
+       }
 }
index 41f3572..2a723e8 100644 (file)
@@ -7,7 +7,7 @@
  */
 class WikiPageTest_ContentHandlerUseDB extends WikiPageTest {
 
-       function setUp() {
+       protected function setUp() {
                parent::setUp();
                $this->setMwGlobals( 'wgContentHandlerUseDB', false );
 
@@ -26,6 +26,9 @@ class WikiPageTest_ContentHandlerUseDB extends WikiPageTest {
                }
        }
 
+       /**
+        * @covers WikiPage::getContentModel
+        */
        public function testGetContentModel() {
                $page = $this->createPage( "WikiPageTest_testGetContentModel", "some text", CONTENT_MODEL_JAVASCRIPT );
 
@@ -36,6 +39,9 @@ class WikiPageTest_ContentHandlerUseDB extends WikiPageTest {
                $this->assertEquals( CONTENT_MODEL_WIKITEXT, $page->getContentModel() );
        }
 
+       /**
+        * @covers WikiPage::getContentHandler
+        */
        public function testGetContentHandler() {
                $page = $this->createPage( "WikiPageTest_testGetContentHandler", "some text", CONTENT_MODEL_JAVASCRIPT );
 
index c5b411f..161468e 100644 (file)
@@ -1,9 +1,24 @@
 <?php
+
+/**
+ * @group Xml
+ */
 class XmlJs extends MediaWikiTestCase {
-       public function testConstruction() {
-               $obj = new XmlJsCode( null );
-               $this->assertNull( $obj->value );
-               $obj = new XmlJsCode( '' );
-               $this->assertSame( $obj->value, '' );
+
+       /**
+        * @covers XmlJsCode::__construct
+        * @dataProvider provideConstruction
+        */
+       public function testConstruction( $value ) {
+               $obj = new XmlJsCode( $value );
+               $this->assertEquals( $value, $obj->value );
        }
+
+       public function provideConstruction(){
+               return array(
+                       array( null ),
+                       array( '' ),
+               );
+       }
+
 }
index 08c031f..56d28b5 100644 (file)
@@ -1,7 +1,13 @@
 <?php
 
-// TODO
+/**
+ * @group Xml
+ */
 class XmlSelectTest extends MediaWikiTestCase {
+
+       /**
+        * @var XmlSelect
+        */
        protected $select;
 
        protected function setUp() {
@@ -17,8 +23,9 @@ class XmlSelectTest extends MediaWikiTestCase {
                $this->select = null;
        }
 
-       ### START OF TESTS ###
-
+       /**
+        * @covers XmlSelect::__construct
+        */
        public function testConstructWithoutParameters() {
                $this->assertEquals( '<select></select>', $this->select->getHTML() );
        }
@@ -26,6 +33,7 @@ class XmlSelectTest extends MediaWikiTestCase {
        /**
         * Parameters are $name (false), $id (false), $default (false)
         * @dataProvider provideConstructionParameters
+        * @covers XmlSelect::__construct
         */
        public function testConstructParameters( $name, $id, $default, $expected ) {
                $this->select = new XmlSelect( $name, $id, $default );
@@ -39,7 +47,6 @@ class XmlSelectTest extends MediaWikiTestCase {
         *  - $id      (default: false)
         *  - $default (default: false)
         * Provides a fourth parameters representing the expected HTML output
-        *
         */
        public static function provideConstructionParameters() {
                return array(
@@ -60,29 +67,41 @@ class XmlSelectTest extends MediaWikiTestCase {
                );
        }
 
-       # Begin XmlSelect::addOption() similar to Xml::option
+       /**
+        * @covers XmlSelect::addOption
+        */
        public function testAddOption() {
                $this->select->addOption( 'foo' );
                $this->assertEquals( '<select><option value="foo">foo</option></select>', $this->select->getHTML() );
        }
 
+       /**
+        * @covers XmlSelect::addOption
+        */
        public function testAddOptionWithDefault() {
                $this->select->addOption( 'foo', true );
                $this->assertEquals( '<select><option value="1">foo</option></select>', $this->select->getHTML() );
        }
 
+       /**
+        * @covers XmlSelect::addOption
+        */
        public function testAddOptionWithFalse() {
                $this->select->addOption( 'foo', false );
                $this->assertEquals( '<select><option value="foo">foo</option></select>', $this->select->getHTML() );
        }
 
+       /**
+        * @covers XmlSelect::addOption
+        */
        public function testAddOptionWithValueZero() {
                $this->select->addOption( 'foo', 0 );
                $this->assertEquals( '<select><option value="0">foo</option></select>', $this->select->getHTML() );
        }
 
-       # End XmlSelect::addOption() similar to Xml::option
-
+       /**
+        * @covers XmlSelect::setDefault
+        */
        public function testSetDefault() {
                $this->select->setDefault( 'bar1' );
                $this->select->addOption( 'foo1' );
@@ -98,6 +117,7 @@ class XmlSelectTest extends MediaWikiTestCase {
         * Adding default later on should set the correct selection or
         * raise an exception.
         * To handle this, we need to render the options in getHtml()
+        * @covers XmlSelect::setDefault
         */
        public function testSetDefaultAfterAddingOptions() {
                $this->select->addOption( 'foo1' );
@@ -110,6 +130,10 @@ class XmlSelectTest extends MediaWikiTestCase {
                                '<option value="foo2">foo2</option></select>', $this->select->getHTML() );
        }
 
+       /**
+        * @covers XmlSelect::setAttribute
+        * @covers XmlSelect::getAttribute
+        */
        public function testGetAttributes() {
                # create some attributes
                $this->select->setAttribute( 'dummy', 0x777 );
index 2294804..8205029 100644 (file)
@@ -1,8 +1,9 @@
 <?php
 
+/**
+ * @group Xml
+ */
 class XmlTest extends MediaWikiTestCase {
-       private static $oldLang;
-       private static $oldNamespaces;
 
        protected function setUp() {
                parent::setUp();
@@ -33,6 +34,9 @@ class XmlTest extends MediaWikiTestCase {
                ) );
        }
 
+       /**
+        * @covers Xml::expandAttributes
+        */
        public function testExpandAttributes() {
                $this->assertNull( Xml::expandAttributes( null ),
                        'Converting a null list of attributes'
@@ -42,12 +46,18 @@ class XmlTest extends MediaWikiTestCase {
                );
        }
 
+       /**
+        * @covers Xml::expandAttributes
+        */
        public function testExpandAttributesException() {
                $this->setExpectedException( 'MWException' );
                Xml::expandAttributes( 'string' );
        }
 
-       function testElementOpen() {
+       /**
+        * @covers Xml::element
+        */
+       public function testElementOpen() {
                $this->assertEquals(
                        '<element>',
                        Xml::element( 'element', null, null ),
@@ -55,7 +65,10 @@ class XmlTest extends MediaWikiTestCase {
                );
        }
 
-       function testElementEmpty() {
+       /**
+        * @covers Xml::element
+        */
+       public function testElementEmpty() {
                $this->assertEquals(
                        '<element />',
                        Xml::element( 'element', null, '' ),
@@ -63,7 +76,10 @@ class XmlTest extends MediaWikiTestCase {
                );
        }
 
-       function testElementInputCanHaveAValueOfZero() {
+       /**
+        * @covers Xml::input
+        */
+       public function testElementInputCanHaveAValueOfZero() {
                $this->assertEquals(
                        '<input name="name" value="0" />',
                        Xml::input( 'name', false, 0 ),
@@ -71,7 +87,10 @@ class XmlTest extends MediaWikiTestCase {
                );
        }
 
-       function testElementEscaping() {
+       /**
+        * @covers Xml::element
+        */
+       public function testElementEscaping() {
                $this->assertEquals(
                        '<element>hello &lt;there&gt; you &amp; you</element>',
                        Xml::element( 'element', null, 'hello <there> you & you' ),
@@ -79,13 +98,19 @@ class XmlTest extends MediaWikiTestCase {
                );
        }
 
+       /**
+        * @covers Xml::escapeTagsOnly
+        */
        public function testEscapeTagsOnly() {
                $this->assertEquals( '&quot;&gt;&lt;', Xml::escapeTagsOnly( '"><' ),
                        'replace " > and < with their HTML entitites'
                );
        }
 
-       function testElementAttributes() {
+       /**
+        * @covers Xml::element
+        */
+       public function testElementAttributes() {
                $this->assertEquals(
                        '<element key="value" <>="&lt;&gt;">',
                        Xml::element( 'element', array( 'key' => 'value', '<>' => '<>' ), null ),
@@ -93,7 +118,10 @@ class XmlTest extends MediaWikiTestCase {
                );
        }
 
-       function testOpenElement() {
+       /**
+        * @covers Xml::openElement
+        */
+       public function testOpenElement() {
                $this->assertEquals(
                        '<element k="v">',
                        Xml::openElement( 'element', array( 'k' => 'v' ) ),
@@ -101,10 +129,16 @@ class XmlTest extends MediaWikiTestCase {
                );
        }
 
-       function testCloseElement() {
+       /**
+        * @covers Xml::closeElement
+        */
+       public function testCloseElement() {
                $this->assertEquals( '</element>', Xml::closeElement( 'element' ), 'closeElement() shortcut' );
        }
 
+       /**
+        * @covers Xml::dateMenu
+        */
        public function testDateMenu() {
                $curYear = intval( gmdate( 'Y' ) );
                $prevYear = $curYear - 1;
@@ -185,10 +219,10 @@ class XmlTest extends MediaWikiTestCase {
                );
        }
 
-       #
-       textarea
-       #
-       function testTextareaNoContent() {
+       /**
+        * @covers Xml::textarea
+        */
+       public function testTextareaNoContent() {
                $this->assertEquals(
                        '<textarea name="name" id="name" cols="40" rows="5"></textarea>',
                        Xml::textarea( 'name', '' ),
@@ -196,7 +230,10 @@ class XmlTest extends MediaWikiTestCase {
                );
        }
 
-       function testTextareaAttribs() {
+       /**
+        * @covers Xml::textarea
+        */
+       public function testTextareaAttribs() {
                $this->assertEquals(
                        '<textarea name="name" id="name" cols="20" rows="10">&lt;txt&gt;</textarea>',
                        Xml::textarea( 'name', '<txt>', 20, 10 ),
@@ -204,10 +241,10 @@ class XmlTest extends MediaWikiTestCase {
                );
        }
 
-       #
-       # input and label
-       #
-       function testLabelCreation() {
+       /**
+        * @covers Xml::label
+        */
+       public function testLabelCreation() {
                $this->assertEquals(
                        '<label for="id">name</label>',
                        Xml::label( 'name', 'id' ),
@@ -215,7 +252,10 @@ class XmlTest extends MediaWikiTestCase {
                );
        }
 
-       function testLabelAttributeCanOnlyBeClassOrTitle() {
+       /**
+        * @covers Xml::label
+        */
+       public function testLabelAttributeCanOnlyBeClassOrTitle() {
                $this->assertEquals(
                        '<label for="id">name</label>',
                        Xml::label( 'name', 'id', array( 'generated' => true ) ),
@@ -244,7 +284,10 @@ class XmlTest extends MediaWikiTestCase {
                );
        }
 
-       function testLanguageSelector() {
+       /**
+        * @covers Xml::languageSelector
+        */
+       public function testLanguageSelector() {
                $select = Xml::languageSelector( 'en', true, null,
                        array( 'id' => 'testlang' ), wfMessage( 'yourlanguage' ) );
                $this->assertEquals(
@@ -253,10 +296,10 @@ class XmlTest extends MediaWikiTestCase {
                );
        }
 
-       #
-       # JS
-       #
-       function testEscapeJsStringSpecialChars() {
+       /**
+        * @covers Xml::escapeJsString
+        */
+       public function testEscapeJsStringSpecialChars() {
                $this->assertEquals(
                        '\\\\\r\n',
                        Xml::escapeJsString( "\\\r\n" ),
@@ -264,7 +307,10 @@ class XmlTest extends MediaWikiTestCase {
                );
        }
 
-       function testEncodeJsVarBoolean() {
+       /**
+        * @covers Xml::encodeJsVar
+        */
+       public function testEncodeJsVarBoolean() {
                $this->assertEquals(
                        'true',
                        Xml::encodeJsVar( true ),
@@ -272,7 +318,10 @@ class XmlTest extends MediaWikiTestCase {
                );
        }
 
-       function testEncodeJsVarNull() {
+       /**
+        * @covers Xml::encodeJsVar
+        */
+       public function testEncodeJsVarNull() {
                $this->assertEquals(
                        'null',
                        Xml::encodeJsVar( null ),
@@ -280,7 +329,10 @@ class XmlTest extends MediaWikiTestCase {
                );
        }
 
-       function testEncodeJsVarArray() {
+       /**
+        * @covers Xml::encodeJsVar
+        */
+       public function testEncodeJsVarArray() {
                $this->assertEquals(
                        '["a",1]',
                        Xml::encodeJsVar( array( 'a', 1 ) ),
@@ -293,7 +345,10 @@ class XmlTest extends MediaWikiTestCase {
                );
        }
 
-       function testEncodeJsVarObject() {
+       /**
+        * @covers Xml::encodeJsVar
+        */
+       public function testEncodeJsVarObject() {
                $this->assertEquals(
                        '{"a":"a","b":1}',
                        Xml::encodeJsVar( (object)array( 'a' => 'a', 'b' => 1 ) ),
@@ -301,7 +356,10 @@ class XmlTest extends MediaWikiTestCase {
                );
        }
 
-       function testEncodeJsVarInt() {
+       /**
+        * @covers Xml::encodeJsVar
+        */
+       public function testEncodeJsVarInt() {
                $this->assertEquals(
                        '123456',
                        Xml::encodeJsVar( 123456 ),
@@ -309,7 +367,10 @@ class XmlTest extends MediaWikiTestCase {
                );
        }
 
-       function testEncodeJsVarFloat() {
+       /**
+        * @covers Xml::encodeJsVar
+        */
+       public function testEncodeJsVarFloat() {
                $this->assertEquals(
                        '1.23456',
                        Xml::encodeJsVar( 1.23456 ),
@@ -317,7 +378,10 @@ class XmlTest extends MediaWikiTestCase {
                );
        }
 
-       function testEncodeJsVarIntString() {
+       /**
+        * @covers Xml::encodeJsVar
+        */
+       public function testEncodeJsVarIntString() {
                $this->assertEquals(
                        '"123456"',
                        Xml::encodeJsVar( '123456' ),
@@ -325,7 +389,10 @@ class XmlTest extends MediaWikiTestCase {
                );
        }
 
-       function testEncodeJsVarFloatString() {
+       /**
+        * @covers Xml::encodeJsVar
+        */
+       public function testEncodeJsVarFloatString() {
                $this->assertEquals(
                        '"1.23456"',
                        Xml::encodeJsVar( '1.23456' ),
index 6a9b44f..8d6f1ed 100644 (file)
@@ -2,7 +2,7 @@
 /**
  * PHPUnit tests for XMLTypeCheck.
  * @author physikerwelt
- * @group ?
+ * @group Xml
  * @covers XMLTypeCheck
  */
 class XmlTypeCheckTest extends MediaWikiTestCase {
index 3fea57a..2627a41 100644 (file)
@@ -1,7 +1,12 @@
 <?php
 
+/**
+ * @covers ZipDirectoryReader
+ * NOTE: this test is more like an integration test than a unit test
+ */
 class ZipDirectoryReaderTest extends MediaWikiTestCase {
-       var $zipDir, $entries;
+       protected $zipDir;
+       protected $entries;
 
        protected function setUp() {
                parent::setUp();
@@ -24,21 +29,21 @@ class ZipDirectoryReaderTest extends MediaWikiTestCase {
                $this->assertTrue( $status->isOK(), $assertMessage );
        }
 
-       function testEmpty() {
+       public function testEmpty() {
                $this->readZipAssertSuccess( 'empty.zip', 'Empty zip' );
        }
 
-       function testMultiDisk0() {
+       public function testMultiDisk0() {
                $this->readZipAssertError( 'split.zip', 'zip-unsupported',
                        'Split zip error' );
        }
 
-       function testNoSignature() {
+       public function testNoSignature() {
                $this->readZipAssertError( 'nosig.zip', 'zip-wrong-format',
                        'No signature should give "wrong format" error' );
        }
 
-       function testSimple() {
+       public function testSimple() {
                $this->readZipAssertSuccess( 'class.zip', 'Simple ZIP' );
                $this->assertEquals( $this->entries, array( array(
                        'name' => 'Class.class',
@@ -47,33 +52,33 @@ class ZipDirectoryReaderTest extends MediaWikiTestCase {
                ) ) );
        }
 
-       function testBadCentralEntrySignature() {
+       public function testBadCentralEntrySignature() {
                $this->readZipAssertError( 'wrong-central-entry-sig.zip', 'zip-bad',
                        'Bad central entry error' );
        }
 
-       function testTrailingBytes() {
+       public function testTrailingBytes() {
                $this->readZipAssertError( 'trail.zip', 'zip-bad',
                        'Trailing bytes error' );
        }
 
-       function testWrongCDStart() {
+       public function testWrongCDStart() {
                $this->readZipAssertError( 'wrong-cd-start-disk.zip', 'zip-unsupported',
                        'Wrong CD start disk error' );
        }
 
 
-       function testCentralDirectoryGap() {
+       public function testCentralDirectoryGap() {
                $this->readZipAssertError( 'cd-gap.zip', 'zip-bad',
                        'CD gap error' );
        }
 
-       function testCentralDirectoryTruncated() {
+       public function testCentralDirectoryTruncated() {
                $this->readZipAssertError( 'cd-truncated.zip', 'zip-bad',
                        'CD truncated error (should hit unpack() overrun)' );
        }
 
-       function testLooksLikeZip64() {
+       public function testLooksLikeZip64() {
                $this->readZipAssertError( 'looks-like-zip64.zip', 'zip-unsupported',
                        'A file which looks like ZIP64 but isn\'t, should give error' );
        }
diff --git a/tests/phpunit/includes/api/ApiAccountCreationTest.php b/tests/phpunit/includes/api/ApiAccountCreationTest.php
deleted file mode 100644 (file)
index 50638ca..0000000
+++ /dev/null
@@ -1,159 +0,0 @@
-<?php
-
-/**
- * @group Database
- * @group API
- * @group medium
- */
-class ApiCreateAccountTest extends ApiTestCase {
-       function setUp() {
-               parent::setUp();
-               LoginForm::setCreateaccountToken();
-               $this->setMwGlobals( array( 'wgEnableEmail' => true ) );
-       }
-
-       /**
-        * Test the account creation API with a valid request. Also
-        * make sure the new account can log in and is valid.
-        *
-        * This test does multiple API requests so it might end up being
-        * a bit slow. Raise the default timeout.
-        * @group medium
-        */
-       function testValid() {
-               global $wgServer;
-
-               if ( !isset( $wgServer ) ) {
-                       $this->markTestIncomplete( 'This test needs $wgServer to be set in LocalSettings.php' );
-               }
-
-               $password = User::randomPassword();
-
-               $ret = $this->doApiRequest( array(
-                       'action' => 'createaccount',
-                       'name' => 'Apitestnew',
-                       'password' => $password,
-                       'email' => 'test@domain.test',
-                       'realname' => 'Test Name'
-               ) );
-
-               $result = $ret[0];
-               $this->assertNotInternalType( 'bool', $result );
-               $this->assertNotInternalType( 'null', $result['createaccount'] );
-
-               // Should first ask for token.
-               $a = $result['createaccount'];
-               $this->assertEquals( 'needtoken', $a['result'] );
-               $token = $a['token'];
-
-               // Finally create the account
-               $ret = $this->doApiRequest(
-                       array(
-                               'action' => 'createaccount',
-                               'name' => 'Apitestnew',
-                               'password' => $password,
-                               'token' => $token,
-                               'email' => 'test@domain.test',
-                               'realname' => 'Test Name'
-                       ),
-                       $ret[2]
-               );
-
-               $result = $ret[0];
-               $this->assertNotInternalType( 'bool', $result );
-               $this->assertEquals( 'success', $result['createaccount']['result'] );
-
-               // Try logging in with the new user.
-               $ret = $this->doApiRequest( array(
-                       'action' => 'login',
-                       'lgname' => 'Apitestnew',
-                       'lgpassword' => $password,
-               ) );
-
-               $result = $ret[0];
-               $this->assertNotInternalType( 'bool', $result );
-               $this->assertNotInternalType( 'null', $result['login'] );
-
-               $a = $result['login']['result'];
-               $this->assertEquals( 'NeedToken', $a );
-               $token = $result['login']['token'];
-
-               $ret = $this->doApiRequest(
-                       array(
-                               'action' => 'login',
-                               'lgtoken' => $token,
-                               'lgname' => 'Apitestnew',
-                               'lgpassword' => $password,
-                       ),
-                       $ret[2]
-               );
-
-               $result = $ret[0];
-
-               $this->assertNotInternalType( 'bool', $result );
-               $a = $result['login']['result'];
-
-               $this->assertEquals( 'Success', $a );
-
-               // log out to destroy the session
-               $ret = $this->doApiRequest(
-                       array(
-                               'action' => 'logout',
-                       ),
-                       $ret[2]
-               );
-               $this->assertEquals( array(), $ret[0] );
-       }
-
-       /**
-        * Make sure requests with no names are invalid.
-        * @expectedException UsageException
-        */
-       function testNoName() {
-               $this->doApiRequest( array(
-                       'action' => 'createaccount',
-                       'token' => LoginForm::getCreateaccountToken(),
-                       'password' => 'password',
-               ) );
-       }
-
-       /**
-        * Make sure requests with no password are invalid.
-        * @expectedException UsageException
-        */
-       function testNoPassword() {
-               $this->doApiRequest( array(
-                       'action' => 'createaccount',
-                       'name' => 'testName',
-                       'token' => LoginForm::getCreateaccountToken(),
-               ) );
-       }
-
-       /**
-        * Make sure requests with existing users are invalid.
-        * @expectedException UsageException
-        */
-       function testExistingUser() {
-               $this->doApiRequest( array(
-                       'action' => 'createaccount',
-                       'name' => 'Apitestsysop',
-                       'token' => LoginForm::getCreateaccountToken(),
-                       'password' => 'password',
-                       'email' => 'test@domain.test',
-               ) );
-       }
-
-       /**
-        * Make sure requests with invalid emails are invalid.
-        * @expectedException UsageException
-        */
-       function testInvalidEmail() {
-               $this->doApiRequest( array(
-                       'action' => 'createaccount',
-                       'name' => 'Test User',
-                       'token' => LoginForm::getCreateaccountToken(),
-                       'password' => 'password',
-                       'email' => 'invalid',
-               ) );
-       }
-}
diff --git a/tests/phpunit/includes/api/ApiBaseTest.php b/tests/phpunit/includes/api/ApiBaseTest.php
new file mode 100644 (file)
index 0000000..bfb75ef
--- /dev/null
@@ -0,0 +1,46 @@
+<?php
+
+/**
+ * @group API
+ * @group Database
+ * @group medium
+ */
+class ApiBaseTest extends ApiTestCase {
+
+       /**
+        * @covers ApiBase::requireOnlyOneParameter
+        */
+       public function testRequireOnlyOneParameterDefault() {
+               $mock = new MockApi();
+               $mock->requireOnlyOneParameter(
+                       array( "filename" => "foo.txt", "enablechunks" => false ),
+                       "filename", "enablechunks"
+               );
+               $this->assertTrue( true );
+       }
+
+       /**
+        * @expectedException UsageException
+        * @covers ApiBase::requireOnlyOneParameter
+        */
+       public function testRequireOnlyOneParameterZero() {
+               $mock = new MockApi();
+               $mock->requireOnlyOneParameter(
+                       array( "filename" => "foo.txt","enablechunks" => 0 ),
+                       "filename", "enablechunks"
+               );
+       }
+
+       /**
+        * @expectedException UsageException
+        * @covers ApiBase::requireOnlyOneParameter
+        */
+       public function testRequireOnlyOneParameterTrue() {
+               $mock = new MockApi();
+               $mock->requireOnlyOneParameter(
+                       array( "filename" => "foo.txt", "enablechunks" => true ),
+                       "filename", "enablechunks"
+               );
+       }
+
+}
index d0eb18a..d98eec6 100644 (file)
@@ -4,6 +4,8 @@
  * @group API
  * @group Database
  * @group medium
+ *
+ * @covers ApiBlock
  */
 class ApiBlockTest extends ApiTestCase {
        protected function setUp() {
@@ -11,7 +13,7 @@ class ApiBlockTest extends ApiTestCase {
                $this->doLogin();
        }
 
-       function getTokens() {
+       protected function getTokens() {
                return $this->getTokenList( self::$users['sysop'] );
        }
 
@@ -34,7 +36,7 @@ class ApiBlockTest extends ApiTestCase {
         * Which made the Block/Unblock API to actually verify the token
         * previously always considered valid (bug 34212).
         */
-       function testMakeNormalBlock() {
+       public function testMakeNormalBlock() {
                $tokens = $this->getTokens();
 
                $user = User::newFromName( 'UTApiBlockee' );
@@ -63,17 +65,13 @@ class ApiBlockTest extends ApiTestCase {
        }
 
        /**
-        * Attempting to block without a token should give a UsageException with
-        * error message:
-        *   "The token parameter must be set"
-        *
-        * @dataProvider provideBlockUnblockAction
         * @expectedException UsageException
+        * @expectedExceptionMessage The token parameter must be set
         */
-       function testBlockingActionWithNoToken( $action ) {
+       public function testBlockingActionWithNoToken( ) {
                $this->doApiRequest(
                        array(
-                               'action' => $action,
+                               'action' => 'block',
                                'user' => 'UTApiBlockee',
                                'reason' => 'Some reason',
                        ),
@@ -82,14 +80,4 @@ class ApiBlockTest extends ApiTestCase {
                        self::$users['sysop']->user
                );
        }
-
-       /**
-        * Just provide the 'block' and 'unblock' action to test both API calls
-        */
-       public static function provideBlockUnblockAction() {
-               return array(
-                       array( 'block' ),
-                       array( 'unblock' ),
-               );
-       }
 }
diff --git a/tests/phpunit/includes/api/ApiCreateAccountTest.php b/tests/phpunit/includes/api/ApiCreateAccountTest.php
new file mode 100644 (file)
index 0000000..a723245
--- /dev/null
@@ -0,0 +1,161 @@
+<?php
+
+/**
+ * @group Database
+ * @group API
+ * @group medium
+ *
+ * @covers ApiCreateAccount
+ */
+class ApiCreateAccountTest extends ApiTestCase {
+       protected function setUp() {
+               parent::setUp();
+               LoginForm::setCreateaccountToken();
+               $this->setMwGlobals( array( 'wgEnableEmail' => true ) );
+       }
+
+       /**
+        * Test the account creation API with a valid request. Also
+        * make sure the new account can log in and is valid.
+        *
+        * This test does multiple API requests so it might end up being
+        * a bit slow. Raise the default timeout.
+        * @group medium
+        */
+       public function testValid() {
+               global $wgServer;
+
+               if ( !isset( $wgServer ) ) {
+                       $this->markTestIncomplete( 'This test needs $wgServer to be set in LocalSettings.php' );
+               }
+
+               $password = User::randomPassword();
+
+               $ret = $this->doApiRequest( array(
+                       'action' => 'createaccount',
+                       'name' => 'Apitestnew',
+                       'password' => $password,
+                       'email' => 'test@domain.test',
+                       'realname' => 'Test Name'
+               ) );
+
+               $result = $ret[0];
+               $this->assertNotInternalType( 'bool', $result );
+               $this->assertNotInternalType( 'null', $result['createaccount'] );
+
+               // Should first ask for token.
+               $a = $result['createaccount'];
+               $this->assertEquals( 'needtoken', $a['result'] );
+               $token = $a['token'];
+
+               // Finally create the account
+               $ret = $this->doApiRequest(
+                       array(
+                               'action' => 'createaccount',
+                               'name' => 'Apitestnew',
+                               'password' => $password,
+                               'token' => $token,
+                               'email' => 'test@domain.test',
+                               'realname' => 'Test Name'
+                       ),
+                       $ret[2]
+               );
+
+               $result = $ret[0];
+               $this->assertNotInternalType( 'bool', $result );
+               $this->assertEquals( 'success', $result['createaccount']['result'] );
+
+               // Try logging in with the new user.
+               $ret = $this->doApiRequest( array(
+                       'action' => 'login',
+                       'lgname' => 'Apitestnew',
+                       'lgpassword' => $password,
+               ) );
+
+               $result = $ret[0];
+               $this->assertNotInternalType( 'bool', $result );
+               $this->assertNotInternalType( 'null', $result['login'] );
+
+               $a = $result['login']['result'];
+               $this->assertEquals( 'NeedToken', $a );
+               $token = $result['login']['token'];
+
+               $ret = $this->doApiRequest(
+                       array(
+                               'action' => 'login',
+                               'lgtoken' => $token,
+                               'lgname' => 'Apitestnew',
+                               'lgpassword' => $password,
+                       ),
+                       $ret[2]
+               );
+
+               $result = $ret[0];
+
+               $this->assertNotInternalType( 'bool', $result );
+               $a = $result['login']['result'];
+
+               $this->assertEquals( 'Success', $a );
+
+               // log out to destroy the session
+               $ret = $this->doApiRequest(
+                       array(
+                               'action' => 'logout',
+                       ),
+                       $ret[2]
+               );
+               $this->assertEquals( array(), $ret[0] );
+       }
+
+       /**
+        * Make sure requests with no names are invalid.
+        * @expectedException UsageException
+        */
+       public function testNoName() {
+               $this->doApiRequest( array(
+                       'action' => 'createaccount',
+                       'token' => LoginForm::getCreateaccountToken(),
+                       'password' => 'password',
+               ) );
+       }
+
+       /**
+        * Make sure requests with no password are invalid.
+        * @expectedException UsageException
+        */
+       public function testNoPassword() {
+               $this->doApiRequest( array(
+                       'action' => 'createaccount',
+                       'name' => 'testName',
+                       'token' => LoginForm::getCreateaccountToken(),
+               ) );
+       }
+
+       /**
+        * Make sure requests with existing users are invalid.
+        * @expectedException UsageException
+        */
+       public function testExistingUser() {
+               $this->doApiRequest( array(
+                       'action' => 'createaccount',
+                       'name' => 'Apitestsysop',
+                       'token' => LoginForm::getCreateaccountToken(),
+                       'password' => 'password',
+                       'email' => 'test@domain.test',
+               ) );
+       }
+
+       /**
+        * Make sure requests with invalid emails are invalid.
+        * @expectedException UsageException
+        */
+       public function testInvalidEmail() {
+               $this->doApiRequest( array(
+                       'action' => 'createaccount',
+                       'name' => 'Test User',
+                       'token' => LoginForm::getCreateaccountToken(),
+                       'password' => 'password',
+                       'email' => 'invalid',
+               ) );
+       }
+}
index c19b54e..7521dcf 100644 (file)
@@ -8,6 +8,8 @@
  * @group API
  * @group Database
  * @group medium
+ *
+ * @covers ApiEditPage
  */
 class ApiEditPageTest extends ApiTestCase {
 
@@ -43,7 +45,7 @@ class ApiEditPageTest extends ApiTestCase {
                parent::tearDown();
        }
 
-       function testEdit() {
+       public function testEdit() {
                $name = 'Help:ApiEditPageTest_testEdit'; // assume Help namespace to default to wikitext
 
                // -- test new page --------------------------------------------
@@ -97,7 +99,7 @@ class ApiEditPageTest extends ApiTestCase {
                );
        }
 
-       function testNonTextEdit() {
+       public function testNonTextEdit() {
                $name = 'Dummy:ApiEditPageTest_testNonTextEdit';
                $data = serialize( 'some bla bla text' );
 
@@ -150,7 +152,7 @@ class ApiEditPageTest extends ApiTestCase {
        /**
         * @dataProvider provideEditAppend
         */
-       function testEditAppend( $text, $op, $append, $expected ) {
+       public function testEditAppend( $text, $op, $append, $expected ) {
                static $count = 0;
                $count++;
 
@@ -196,7 +198,7 @@ class ApiEditPageTest extends ApiTestCase {
        /**
         * Test editing of sections
         */
-       function testEditSection() {
+       public function testEditSection() {
                $name = 'Help:ApiEditPageTest_testEditSection';
                $page = WikiPage::factory( Title::newFromText( $name ) );
                $text = "==section 1==\ncontent 1\n==section 2==\ncontent2";
@@ -233,7 +235,7 @@ class ApiEditPageTest extends ApiTestCase {
         * page that doesn't exist (bug 52830) and one that
         * does exist
         */
-       function testEditNewSection() {
+       public function testEditNewSection() {
                $name = 'Help:ApiEditPageTest_testEditNewSection';
 
                // Test on a page that does not already exist
@@ -266,7 +268,7 @@ class ApiEditPageTest extends ApiTestCase {
                $this->assertEquals( $text, "== header ==\n\ntest\n\n== header ==\n\ntest" );
        }
 
-       function testEditConflict() {
+       public function testEditConflict() {
                static $count = 0;
                $count++;
 
@@ -302,7 +304,7 @@ class ApiEditPageTest extends ApiTestCase {
                }
        }
 
-       function testEditConflict_redirect() {
+       public function testEditConflict_redirect() {
                static $count = 0;
                $count++;
 
@@ -358,13 +360,13 @@ class ApiEditPageTest extends ApiTestCase {
                }
        }
 
-       function testEditConflict_bug41990() {
+       public function testEditConflict_bug41990() {
                static $count = 0;
                $count++;
 
                /*
                * bug 41990: if the target page has a newer revision than the redirect, then editing the
-               * redirect while specifying 'redirect' and *not* specifying 'basetimestamp' erronously
+               * redirect while specifying 'redirect' and *not* specifying 'basetimestamp' erroneously
                * caused an edit conflict to be detected.
                */
 
diff --git a/tests/phpunit/includes/api/ApiLoginTest.php b/tests/phpunit/includes/api/ApiLoginTest.php
new file mode 100644 (file)
index 0000000..f1199e0
--- /dev/null
@@ -0,0 +1,177 @@
+<?php
+
+/**
+ * @group API
+ * @group Database
+ * @group medium
+ *
+ * @covers ApiLogin
+ */
+class ApiLoginTest extends ApiTestCase {
+
+       /**
+        * Test result of attempted login with an empty username
+        */
+       public function testApiLoginNoName() {
+               $data = $this->doApiRequest( array( 'action' => 'login',
+                       'lgname' => '', 'lgpassword' => self::$users['sysop']->password,
+               ) );
+               $this->assertEquals( 'NoName', $data[0]['login']['result'] );
+       }
+
+       public function testApiLoginBadPass() {
+               global $wgServer;
+
+               $user = self::$users['sysop'];
+               $user->user->logOut();
+
+               if ( !isset( $wgServer ) ) {
+                       $this->markTestIncomplete( 'This test needs $wgServer to be set in LocalSettings.php' );
+               }
+               $ret = $this->doApiRequest( array(
+                       "action" => "login",
+                       "lgname" => $user->username,
+                       "lgpassword" => "bad",
+               ) );
+
+               $result = $ret[0];
+
+               $this->assertNotInternalType( "bool", $result );
+               $a = $result["login"]["result"];
+               $this->assertEquals( "NeedToken", $a );
+
+               $token = $result["login"]["token"];
+
+               $ret = $this->doApiRequest(
+                       array(
+                               "action" => "login",
+                               "lgtoken" => $token,
+                               "lgname" => $user->username,
+                               "lgpassword" => "badnowayinhell",
+                       ),
+                       $ret[2]
+               );
+
+               $result = $ret[0];
+
+               $this->assertNotInternalType( "bool", $result );
+               $a = $result["login"]["result"];
+
+               $this->assertEquals( "WrongPass", $a );
+       }
+
+       public function testApiLoginGoodPass() {
+               global $wgServer;
+
+               if ( !isset( $wgServer ) ) {
+                       $this->markTestIncomplete( 'This test needs $wgServer to be set in LocalSettings.php' );
+               }
+
+               $user = self::$users['sysop'];
+               $user->user->logOut();
+
+               $ret = $this->doApiRequest( array(
+                               "action" => "login",
+                               "lgname" => $user->username,
+                               "lgpassword" => $user->password,
+                       )
+               );
+
+               $result = $ret[0];
+               $this->assertNotInternalType( "bool", $result );
+               $this->assertNotInternalType( "null", $result["login"] );
+
+               $a = $result["login"]["result"];
+               $this->assertEquals( "NeedToken", $a );
+               $token = $result["login"]["token"];
+
+               $ret = $this->doApiRequest(
+                       array(
+                               "action" => "login",
+                               "lgtoken" => $token,
+                               "lgname" => $user->username,
+                               "lgpassword" => $user->password,
+                       ),
+                       $ret[2]
+               );
+
+               $result = $ret[0];
+
+               $this->assertNotInternalType( "bool", $result );
+               $a = $result["login"]["result"];
+
+               $this->assertEquals( "Success", $a );
+       }
+
+       /**
+        * @group Broken
+        */
+       public function testApiLoginGotCookie() {
+               $this->markTestIncomplete( "The server can't do external HTTP requests, and the internal one won't give cookies" );
+
+               global $wgServer, $wgScriptPath;
+
+               if ( !isset( $wgServer ) ) {
+                       $this->markTestIncomplete( 'This test needs $wgServer to be set in LocalSettings.php' );
+               }
+               $user = self::$users['sysop'];
+
+               $req = MWHttpRequest::factory( self::$apiUrl . "?action=login&format=xml",
+                       array( "method" => "POST",
+                               "postData" => array(
+                                       "lgname" => $user->username,
+                                       "lgpassword" => $user->password
+                               )
+                       )
+               );
+               $req->execute();
+
+               libxml_use_internal_errors( true );
+               $sxe = simplexml_load_string( $req->getContent() );
+               $this->assertNotInternalType( "bool", $sxe );
+               $this->assertThat( $sxe, $this->isInstanceOf( "SimpleXMLElement" ) );
+               $this->assertNotInternalType( "null", $sxe->login[0] );
+
+               $a = $sxe->login[0]->attributes()->result[0];
+               $this->assertEquals( ' result="NeedToken"', $a->asXML() );
+               $token = (string)$sxe->login[0]->attributes()->token;
+
+               $req->setData( array(
+                       "lgtoken" => $token,
+                       "lgname" => $user->username,
+                       "lgpassword" => $user->password ) );
+               $req->execute();
+
+               $cj = $req->getCookieJar();
+               $serverName = parse_url( $wgServer, PHP_URL_HOST );
+               $this->assertNotEquals( false, $serverName );
+               $serializedCookie = $cj->serializeToHttpRequest( $wgScriptPath, $serverName );
+               $this->assertNotEquals( '', $serializedCookie );
+               $this->assertRegexp( '/_session=[^;]*; .*UserID=[0-9]*; .*UserName=' . $user->userName . '; .*Token=/', $serializedCookie );
+       }
+
+       public function testRunLogin() {
+               $sysopUser = self::$users['sysop'];
+               $data = $this->doApiRequest( array(
+                       'action' => 'login',
+                       'lgname' => $sysopUser->username,
+                       'lgpassword' => $sysopUser->password ) );
+
+               $this->assertArrayHasKey( "login", $data[0] );
+               $this->assertArrayHasKey( "result", $data[0]['login'] );
+               $this->assertEquals( "NeedToken", $data[0]['login']['result'] );
+               $token = $data[0]['login']['token'];
+
+               $data = $this->doApiRequest( array(
+                       'action' => 'login',
+                       "lgtoken" => $token,
+                       "lgname" => $sysopUser->username,
+                       "lgpassword" => $sysopUser->password ), $data[2] );
+
+               $this->assertArrayHasKey( "login", $data[0] );
+               $this->assertArrayHasKey( "result", $data[0]['login'] );
+               $this->assertEquals( "Success", $data[0]['login']['result'] );
+               $this->assertArrayHasKey( 'lgtoken', $data[0]['login'] );
+       }
+
+}
diff --git a/tests/phpunit/includes/api/ApiMainTest.php b/tests/phpunit/includes/api/ApiMainTest.php
new file mode 100644 (file)
index 0000000..4ed5aa9
--- /dev/null
@@ -0,0 +1,33 @@
+<?php
+
+/**
+ * @group API
+ * @group Database
+ * @group medium
+ *
+ * @covers ApiMain
+ */
+class ApiMainTest extends ApiTestCase {
+
+       /**
+        * Test that the API will accept a FauxRequest and execute. The help action
+        * (default) throws a UsageException. Just validate we're getting proper XML
+        *
+        * @expectedException UsageException
+        */
+       public function testApi() {
+               $api = new ApiMain(
+                       new FauxRequest( array( 'action' => 'help', 'format' => 'xml' ) )
+               );
+               $api->execute();
+               $api->getPrinter()->setBufferResult( true );
+               $api->printResult( false );
+               $resp = $api->getPrinter()->getBuffer();
+
+               libxml_use_internal_errors( true );
+               $sxe = simplexml_load_string( $resp );
+               $this->assertNotInternalType( "bool", $sxe );
+               $this->assertThat( $sxe, $this->isInstanceOf( "SimpleXMLElement" ) );
+       }
+
+}
index ad1e73a..3168f32 100644 (file)
@@ -4,10 +4,18 @@
  * @group API
  * @group Database
  * @group medium
+ *
+ * @covers ApiOptions
  */
 class ApiOptionsTest extends MediaWikiLangTestCase {
 
-       private $mTested, $mUserMock, $mContext, $mSession;
+       /** @var PHPUnit_Framework_MockObject_MockObject */
+       private $mUserMock ;
+       /** @var ApiOptions */
+       private $mTested;
+       private $mSession;
+       /** @var DerivativeContext */
+       private $mContext;
 
        private $mOldGetPreferencesHooks = false;
 
index b408875..d303d4b 100644 (file)
@@ -4,6 +4,8 @@
  * @group API
  * @group Database
  * @group medium
+ *
+ * @covers ApiParse
  */
 class ApiParseTest extends ApiTestCase {
 
@@ -12,7 +14,7 @@ class ApiParseTest extends ApiTestCase {
                $this->doLogin();
        }
 
-       function testParseNonexistentPage() {
+       public function testParseNonexistentPage() {
                $somePage = mt_rand();
 
                try {
index 881eb3f..d25a4c1 100644 (file)
@@ -4,6 +4,8 @@
  * @group API
  * @group Database
  * @group medium
+ *
+ * @covers ApiPurge
  */
 class ApiPurgeTest extends ApiTestCase {
 
@@ -15,7 +17,7 @@ class ApiPurgeTest extends ApiTestCase {
        /**
         * @group Broken
         */
-       function testPurgeMainPage() {
+       public function testPurgeMainPage() {
                if ( !Title::newFromText( 'UTPage' )->exists() ) {
                        $this->markTestIncomplete( "The article [[UTPage]] does not exist" );
                }
diff --git a/tests/phpunit/includes/api/ApiTest.php b/tests/phpunit/includes/api/ApiTest.php
deleted file mode 100644 (file)
index 5106be5..0000000
+++ /dev/null
@@ -1,259 +0,0 @@
-<?php
-
-/**
- * @group API
- * @group Database
- * @group medium
- */
-class ApiTest extends ApiTestCase {
-
-       function testRequireOnlyOneParameterDefault() {
-               $mock = new MockApi();
-
-               $this->assertEquals(
-                       null, $mock->requireOnlyOneParameter( array( "filename" => "foo.txt",
-                       "enablechunks" => false ), "filename", "enablechunks" ) );
-       }
-
-       /**
-        * @expectedException UsageException
-        */
-       function testRequireOnlyOneParameterZero() {
-               $mock = new MockApi();
-
-               $this->assertEquals(
-                       null, $mock->requireOnlyOneParameter( array( "filename" => "foo.txt",
-                       "enablechunks" => 0 ), "filename", "enablechunks" ) );
-       }
-
-       /**
-        * @expectedException UsageException
-        */
-       function testRequireOnlyOneParameterTrue() {
-               $mock = new MockApi();
-
-               $this->assertEquals(
-                       null, $mock->requireOnlyOneParameter( array( "filename" => "foo.txt",
-                       "enablechunks" => true ), "filename", "enablechunks" ) );
-       }
-
-       /**
-        * Test that the API will accept a FauxRequest and execute. The help action
-        * (default) throws a UsageException. Just validate we're getting proper XML
-        *
-        * @expectedException UsageException
-        */
-       function testApi() {
-               $api = new ApiMain(
-                       new FauxRequest( array( 'action' => 'help', 'format' => 'xml' ) )
-               );
-               $api->execute();
-               $api->getPrinter()->setBufferResult( true );
-               $api->printResult( false );
-               $resp = $api->getPrinter()->getBuffer();
-
-               libxml_use_internal_errors( true );
-               $sxe = simplexml_load_string( $resp );
-               $this->assertNotInternalType( "bool", $sxe );
-               $this->assertThat( $sxe, $this->isInstanceOf( "SimpleXMLElement" ) );
-       }
-
-       /**
-        * Test result of attempted login with an empty username
-        */
-       function testApiLoginNoName() {
-               $data = $this->doApiRequest( array( 'action' => 'login',
-                       'lgname' => '', 'lgpassword' => self::$users['sysop']->password,
-               ) );
-               $this->assertEquals( 'NoName', $data[0]['login']['result'] );
-       }
-
-       function testApiLoginBadPass() {
-               global $wgServer;
-
-               $user = self::$users['sysop'];
-               $user->user->logOut();
-
-               if ( !isset( $wgServer ) ) {
-                       $this->markTestIncomplete( 'This test needs $wgServer to be set in LocalSettings.php' );
-               }
-               $ret = $this->doApiRequest( array(
-                       "action" => "login",
-                       "lgname" => $user->username,
-                       "lgpassword" => "bad",
-               ) );
-
-               $result = $ret[0];
-
-               $this->assertNotInternalType( "bool", $result );
-               $a = $result["login"]["result"];
-               $this->assertEquals( "NeedToken", $a );
-
-               $token = $result["login"]["token"];
-
-               $ret = $this->doApiRequest(
-                       array(
-                               "action" => "login",
-                               "lgtoken" => $token,
-                               "lgname" => $user->username,
-                               "lgpassword" => "badnowayinhell",
-                       ),
-                       $ret[2]
-               );
-
-               $result = $ret[0];
-
-               $this->assertNotInternalType( "bool", $result );
-               $a = $result["login"]["result"];
-
-               $this->assertEquals( "WrongPass", $a );
-       }
-
-       function testApiLoginGoodPass() {
-               global $wgServer;
-
-               if ( !isset( $wgServer ) ) {
-                       $this->markTestIncomplete( 'This test needs $wgServer to be set in LocalSettings.php' );
-               }
-
-               $user = self::$users['sysop'];
-               $user->user->logOut();
-
-               $ret = $this->doApiRequest( array(
-                               "action" => "login",
-                               "lgname" => $user->username,
-                               "lgpassword" => $user->password,
-                       )
-               );
-
-               $result = $ret[0];
-               $this->assertNotInternalType( "bool", $result );
-               $this->assertNotInternalType( "null", $result["login"] );
-
-               $a = $result["login"]["result"];
-               $this->assertEquals( "NeedToken", $a );
-               $token = $result["login"]["token"];
-
-               $ret = $this->doApiRequest(
-                       array(
-                               "action" => "login",
-                               "lgtoken" => $token,
-                               "lgname" => $user->username,
-                               "lgpassword" => $user->password,
-                       ),
-                       $ret[2]
-               );
-
-               $result = $ret[0];
-
-               $this->assertNotInternalType( "bool", $result );
-               $a = $result["login"]["result"];
-
-               $this->assertEquals( "Success", $a );
-       }
-
-       /**
-        * @group Broken
-        */
-       function testApiGotCookie() {
-               $this->markTestIncomplete( "The server can't do external HTTP requests, and the internal one won't give cookies" );
-
-               global $wgServer, $wgScriptPath;
-
-               if ( !isset( $wgServer ) ) {
-                       $this->markTestIncomplete( 'This test needs $wgServer to be set in LocalSettings.php' );
-               }
-               $user = self::$users['sysop'];
-
-               $req = MWHttpRequest::factory( self::$apiUrl . "?action=login&format=xml",
-                       array( "method" => "POST",
-                               "postData" => array(
-                                       "lgname" => $user->username,
-                                       "lgpassword" => $user->password
-                               )
-                       )
-               );
-               $req->execute();
-
-               libxml_use_internal_errors( true );
-               $sxe = simplexml_load_string( $req->getContent() );
-               $this->assertNotInternalType( "bool", $sxe );
-               $this->assertThat( $sxe, $this->isInstanceOf( "SimpleXMLElement" ) );
-               $this->assertNotInternalType( "null", $sxe->login[0] );
-
-               $a = $sxe->login[0]->attributes()->result[0];
-               $this->assertEquals( ' result="NeedToken"', $a->asXML() );
-               $token = (string)$sxe->login[0]->attributes()->token;
-
-               $req->setData( array(
-                       "lgtoken" => $token,
-                       "lgname" => $user->username,
-                       "lgpassword" => $user->password ) );
-               $req->execute();
-
-               $cj = $req->getCookieJar();
-               $serverName = parse_url( $wgServer, PHP_URL_HOST );
-               $this->assertNotEquals( false, $serverName );
-               $serializedCookie = $cj->serializeToHttpRequest( $wgScriptPath, $serverName );
-               $this->assertNotEquals( '', $serializedCookie );
-               $this->assertRegexp( '/_session=[^;]*; .*UserID=[0-9]*; .*UserName=' . $user->userName . '; .*Token=/', $serializedCookie );
-
-               return $cj;
-       }
-
-       function testRunLogin() {
-               $sysopUser = self::$users['sysop'];
-               $data = $this->doApiRequest( array(
-                       'action' => 'login',
-                       'lgname' => $sysopUser->username,
-                       'lgpassword' => $sysopUser->password ) );
-
-               $this->assertArrayHasKey( "login", $data[0] );
-               $this->assertArrayHasKey( "result", $data[0]['login'] );
-               $this->assertEquals( "NeedToken", $data[0]['login']['result'] );
-               $token = $data[0]['login']['token'];
-
-               $data = $this->doApiRequest( array(
-                       'action' => 'login',
-                       "lgtoken" => $token,
-                       "lgname" => $sysopUser->username,
-                       "lgpassword" => $sysopUser->password ), $data[2] );
-
-               $this->assertArrayHasKey( "login", $data[0] );
-               $this->assertArrayHasKey( "result", $data[0]['login'] );
-               $this->assertEquals( "Success", $data[0]['login']['result'] );
-               $this->assertArrayHasKey( 'lgtoken', $data[0]['login'] );
-
-               return $data;
-       }
-
-       function testGettingToken() {
-               foreach ( self::$users as $user ) {
-                       $this->runTokenTest( $user );
-               }
-       }
-
-       function runTokenTest( $user ) {
-               $tokens = $this->getTokenList( $user );
-
-               $rights = $user->user->getRights();
-
-               $this->assertArrayHasKey( 'edittoken', $tokens );
-               $this->assertArrayHasKey( 'movetoken', $tokens );
-
-               if ( isset( $rights['delete'] ) ) {
-                       $this->assertArrayHasKey( 'deletetoken', $tokens );
-               }
-
-               if ( isset( $rights['block'] ) ) {
-                       $this->assertArrayHasKey( 'blocktoken', $tokens );
-                       $this->assertArrayHasKey( 'unblocktoken', $tokens );
-               }
-
-               if ( isset( $rights['protect'] ) ) {
-                       $this->assertArrayHasKey( 'protecttoken', $tokens );
-               }
-
-               return $tokens;
-       }
-}
index 94ef9c6..ad297dd 100644 (file)
@@ -117,7 +117,7 @@ abstract class ApiTestCase extends MediaWikiLangTestCase {
         * @param $params Array: key-value API params
         * @param $session Array|null: session array
         * @param $user User|null A User object for the context
-        * @return result of the API call
+        * @return mixed result of the API call
         * @throws Exception in case wsToken is not set in the session
         */
        protected function doApiRequestWithToken( array $params, array $session = null, User $user = null ) {
@@ -188,66 +188,3 @@ abstract class ApiTestCase extends MediaWikiLangTestCase {
                );
        }
 }
-
-class UserWrapper {
-       public $userName;
-       public $password;
-       public $user;
-
-       public function __construct( $userName, $password, $group = '' ) {
-               $this->userName = $userName;
-               $this->password = $password;
-
-               $this->user = User::newFromName( $this->userName );
-               if ( !$this->user->getID() ) {
-                       $this->user = User::createNew( $this->userName, array(
-                               "email" => "test@example.com",
-                               "real_name" => "Test User" ) );
-               }
-               $this->user->setPassword( $this->password );
-
-               if ( $group !== '' ) {
-                       $this->user->addGroup( $group );
-               }
-               $this->user->saveSettings();
-       }
-}
-
-class MockApi extends ApiBase {
-       public function execute() {
-       }
-
-       public function getVersion() {
-       }
-
-       public function __construct() {
-       }
-
-       public function getAllowedParams() {
-               return array(
-                       'filename' => null,
-                       'enablechunks' => false,
-                       'sessionkey' => null,
-               );
-       }
-}
-
-class ApiTestContext extends RequestContext {
-
-       /**
-        * Returns a DerivativeContext with the request variables in place
-        *
-        * @param $request WebRequest request object including parameters and session
-        * @param $user User or null
-        * @return DerivativeContext
-        */
-       public function newTestContext( WebRequest $request, User $user = null ) {
-               $context = new DerivativeContext( $this );
-               $context->setRequest( $request );
-               if ( $user !== null ) {
-                       $context->setUser( $user );
-               }
-
-               return $context;
-       }
-}
diff --git a/tests/phpunit/includes/api/ApiTestContext.php b/tests/phpunit/includes/api/ApiTestContext.php
new file mode 100644 (file)
index 0000000..43637c2
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+
+class ApiTestContext extends RequestContext {
+
+       /**
+        * Returns a DerivativeContext with the request variables in place
+        *
+        * @param $request WebRequest request object including parameters and session
+        * @param $user User or null
+        * @return DerivativeContext
+        */
+       public function newTestContext( WebRequest $request, User $user = null ) {
+               $context = new DerivativeContext( $this );
+               $context->setRequest( $request );
+               if ( $user !== null ) {
+                       $context->setUser( $user );
+               }
+
+               return $context;
+       }
+}
\ No newline at end of file
diff --git a/tests/phpunit/includes/api/ApiTokensTest.php b/tests/phpunit/includes/api/ApiTokensTest.php
new file mode 100644 (file)
index 0000000..fbe9789
--- /dev/null
@@ -0,0 +1,40 @@
+<?php
+
+/**
+ * @group API
+ * @group Database
+ * @group medium
+ *
+ * @covers ApiTokens
+ */
+class ApiTokensTest extends ApiTestCase {
+
+       public function testGettingToken() {
+               foreach ( self::$users as $user ) {
+                       $this->runTokenTest( $user );
+               }
+       }
+
+       protected function runTokenTest( $user ) {
+               $tokens = $this->getTokenList( $user );
+
+               $rights = $user->user->getRights();
+
+               $this->assertArrayHasKey( 'edittoken', $tokens );
+               $this->assertArrayHasKey( 'movetoken', $tokens );
+
+               if ( isset( $rights['delete'] ) ) {
+                       $this->assertArrayHasKey( 'deletetoken', $tokens );
+               }
+
+               if ( isset( $rights['block'] ) ) {
+                       $this->assertArrayHasKey( 'blocktoken', $tokens );
+                       $this->assertArrayHasKey( 'unblocktoken', $tokens );
+               }
+
+               if ( isset( $rights['protect'] ) ) {
+                       $this->assertArrayHasKey( 'protecttoken', $tokens );
+               }
+       }
+
+}
diff --git a/tests/phpunit/includes/api/ApiUnblockTest.php b/tests/phpunit/includes/api/ApiUnblockTest.php
new file mode 100644 (file)
index 0000000..2c2370a
--- /dev/null
@@ -0,0 +1,31 @@
+<?php
+
+/**
+ * @group API
+ * @group Database
+ * @group medium
+ *
+ * @covers ApiUnblock
+ */
+class ApiUnblockTest extends ApiTestCase {
+       protected function setUp() {
+               parent::setUp();
+               $this->doLogin();
+       }
+
+       /**
+        * @expectedException UsageException
+        */
+       public function testWithNoToken( ) {
+               $this->doApiRequest(
+                       array(
+                               'action' => 'unblock',
+                               'user' => 'UTApiBlockee',
+                               'reason' => 'Some reason',
+                       ),
+                       null,
+                       false,
+                       self::$users['sysop']->user
+               );
+       }
+}
index ae3a5e9..1540af5 100644 (file)
@@ -31,7 +31,7 @@ class ApiUploadTest extends ApiTestCaseUpload {
         * Testing login
         * XXX this is a funny way of getting session context
         */
-       function testLogin() {
+       public function testLogin() {
                $user = self::$users['uploader'];
 
                $params = array(
index 8253989..028ea9f 100644 (file)
@@ -18,7 +18,7 @@ class ApiWatchTest extends ApiTestCase {
 
        /**
         */
-       function testWatchEdit() {
+       public function testWatchEdit() {
                $tokens = $this->getTokens();
 
                $data = $this->doApiRequest( array(
@@ -37,7 +37,7 @@ class ApiWatchTest extends ApiTestCase {
        /**
         * @depends testWatchEdit
         */
-       function testWatchClear() {
+       public function testWatchClear() {
                $tokens = $this->getTokens();
 
                $data = $this->doApiRequest( array(
@@ -67,7 +67,7 @@ class ApiWatchTest extends ApiTestCase {
 
        /**
         */
-       function testWatchProtect() {
+       public function testWatchProtect() {
                $tokens = $this->getTokens();
 
                $data = $this->doApiRequest( array(
@@ -85,7 +85,7 @@ class ApiWatchTest extends ApiTestCase {
 
        /**
         */
-       function testGetRollbackToken() {
+       public function testGetRollbackToken() {
                $this->getTokens();
 
                if ( !Title::newFromText( 'Help:UTPage' )->exists() ) {
@@ -121,7 +121,7 @@ class ApiWatchTest extends ApiTestCase {
         *
         * @depends testGetRollbackToken
         */
-       function testWatchRollback( $data ) {
+       public function testWatchRollback( $data ) {
                $keys = array_keys( $data[0]['query']['pages'] );
                $key = array_pop( $keys );
                $pageinfo = $data[0]['query']['pages'][$key];
diff --git a/tests/phpunit/includes/api/MockApi.php b/tests/phpunit/includes/api/MockApi.php
new file mode 100644 (file)
index 0000000..3686048
--- /dev/null
@@ -0,0 +1,20 @@
+<?php
+
+class MockApi extends ApiBase {
+       public function execute() {
+       }
+
+       public function getVersion() {
+       }
+
+       public function __construct() {
+       }
+
+       public function getAllowedParams() {
+               return array(
+                       'filename' => null,
+                       'enablechunks' => false,
+                       'sessionkey' => null,
+               );
+       }
+}
\ No newline at end of file
index d9be85e..a1fc2c2 100644 (file)
@@ -6,6 +6,7 @@
  * @group API
  */
 class PrefixUniquenessTest extends MediaWikiTestCase {
+
        public function testPrefixes() {
                $main = new ApiMain( new FauxRequest() );
                $query = new ApiQuery( $main, 'foo', 'bar' );
@@ -13,6 +14,7 @@ class PrefixUniquenessTest extends MediaWikiTestCase {
                $prefixes = array();
 
                foreach ( $modules as $name => $class ) {
+                       /** @var ApiMain $module */
                        $module = new $class( $main, $name );
                        $prefix = $module->getModulePrefix();
                        if ( isset( $prefixes[$prefix] ) ) {
diff --git a/tests/phpunit/includes/api/UserWrapper.php b/tests/phpunit/includes/api/UserWrapper.php
new file mode 100644 (file)
index 0000000..3262e6c
--- /dev/null
@@ -0,0 +1,25 @@
+<?php
+
+class UserWrapper {
+       public $userName;
+       public $password;
+       public $user;
+
+       public function __construct( $userName, $password, $group = '' ) {
+               $this->userName = $userName;
+               $this->password = $password;
+
+               $this->user = User::newFromName( $this->userName );
+               if ( !$this->user->getID() ) {
+                       $this->user = User::createNew( $this->userName, array(
+                               "email" => "test@example.com",
+                               "real_name" => "Test User" ) );
+               }
+               $this->user->setPassword( $this->password );
+
+               if ( $group !== '' ) {
+                       $this->user->addGroup( $group );
+               }
+               $this->user->saveSettings();
+       }
+}
\ No newline at end of file
diff --git a/tests/phpunit/includes/api/format/ApiFormatJsonTest.php b/tests/phpunit/includes/api/format/ApiFormatJsonTest.php
new file mode 100644 (file)
index 0000000..c71faec
--- /dev/null
@@ -0,0 +1,17 @@
+<?php
+
+/**
+ * @group API
+ * @group Database
+ * @group medium
+ * @covers ApiFormatJson
+ */
+class ApiFormatJsonTest extends ApiFormatTestBase {
+
+       public function testValidSyntax( ) {
+               $data = $this->apiRequest( 'json', array( 'action' => 'query', 'meta' => 'siteinfo' ) );
+
+               $this->assertInternalType( 'array', json_decode( $data, true ) );
+               $this->assertGreaterThan( 0, count( (array)$data ) );
+       }
+}
index 802a0e1..759dfc7 100644 (file)
@@ -4,11 +4,11 @@
  * @group API
  * @group Database
  * @group medium
+ * @covers ApiFormatPhp
  */
 class ApiFormatPhpTest extends ApiFormatTestBase {
 
-       function testValidPhpSyntax() {
-
+       public function testValidyntax( ) {
                $data = $this->apiRequest( 'php', array( 'action' => 'query', 'meta' => 'siteinfo' ) );
 
                $this->assertInternalType( 'array', unserialize( $data ) );
index 153f2cf..24d8cb8 100644 (file)
@@ -1,9 +1,18 @@
 <?php
 
 abstract class ApiFormatTestBase extends ApiTestCase {
+
+       /**
+        * @param string $format
+        * @param array $params
+        * @param $data
+        *
+        * @return string
+        */
        protected function apiRequest( $format, $params, $data = null ) {
                $data = parent::doApiRequest( $params, $data, true );
 
+               /** @var ApiMain $module */
                $module = $data[3];
 
                $printer = $module->createPrinterByName( $format );
@@ -19,4 +28,5 @@ abstract class ApiFormatTestBase extends ApiTestCase {
 
                return $out;
        }
+
 }
diff --git a/tests/phpunit/includes/api/format/ApiFormatWddxTest.php b/tests/phpunit/includes/api/format/ApiFormatWddxTest.php
new file mode 100644 (file)
index 0000000..d075f54
--- /dev/null
@@ -0,0 +1,20 @@
+<?php
+
+/**
+ * @group API
+ * @group Database
+ * @group medium
+ * @covers ApiFormatWddx
+ */
+class ApiFormatWddxTest extends ApiFormatTestBase {
+
+       /**
+        * @requires function wddx_deserialize
+        */
+       public function testValidSyntax( ) {
+               $data = $this->apiRequest( 'wddx', array( 'action' => 'query', 'meta' => 'siteinfo' ) );
+
+               $this->assertInternalType( 'array', wddx_deserialize( $data ) );
+               $this->assertGreaterThan( 0, count( (array)$data ) );
+       }
+}
index 1a2aa83..a68c830 100644 (file)
 
 require_once 'ApiQueryTestBase.php';
 
-/** These tests validate basic functionality of the api query module
+/**
+ * These tests validate basic functionality of the api query module
  *
  * @group API
  * @group Database
  * @group medium
+ * @covers ApiQuery
  */
 class ApiQueryBasicTest extends ApiQueryTestBase {
        /**
index 4d5ddba..2116cd3 100644 (file)
@@ -24,6 +24,7 @@ require_once 'ApiQueryContinueTestBase.php';
  * @group API
  * @group Database
  * @group medium
+ * @covers ApiQuery
  */
 class ApiQueryContinue2Test extends ApiQueryContinueTestBase {
        /**
index f494e9c..7797522 100644 (file)
@@ -28,6 +28,7 @@ require_once 'ApiQueryContinueTestBase.php';
  * @group API
  * @group Database
  * @group medium
+ * @covers ApiQuery
  */
 class ApiQueryContinueTest extends ApiQueryContinueTestBase {
        /**
index fbb1e64..3b55b13 100644 (file)
@@ -1,7 +1,5 @@
 <?php
 /**
- *
- *
  * Created on Jan 1, 2013
  *
  * Copyright © 2013 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
index 7f5fe91..74ceff9 100644 (file)
@@ -4,13 +4,14 @@
  * @group API
  * @group Database
  * @group medium
+ * @covers ApiQueryRevisions
  */
 class ApiQueryRevisionsTest extends ApiTestCase {
 
        /**
         * @group medium
         */
-       function testContentComesWithContentModelAndFormat() {
+       public function testContentComesWithContentModelAndFormat() {
                $pageName = 'Help:' . __METHOD__;
                $title = Title::newFromText( $pageName );
                $page = WikiPage::factory( $title );
index bc01ec2..2ec5fe3 100644 (file)
@@ -4,6 +4,7 @@
  * @group API
  * @group Database
  * @group medium
+ * @covers ApiQuery
  */
 class ApiQueryTest extends ApiTestCase {
 
@@ -12,7 +13,7 @@ class ApiQueryTest extends ApiTestCase {
                $this->doLogin();
        }
 
-       function testTitlesGetNormalized() {
+       public function testTitlesGetNormalized() {
 
                global $wgMetaNamespace;
 
@@ -43,7 +44,7 @@ class ApiQueryTest extends ApiTestCase {
                );
        }
 
-       function testTitlesAreRejectedIfInvalid() {
+       public function testTitlesAreRejectedIfInvalid() {
                $title = false;
                while ( !$title || Title::newFromText( $title )->exists() ) {
                        $title = md5( mt_rand( 0, 10000 ) + rand( 0, 999000 ) );
index 8ee8ea9..9cfab41 100644 (file)
@@ -1,7 +1,5 @@
 <?php
 /**
- *
- *
  * Created on Feb 10, 2013
  *
  * Copyright © 2013 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
index 2ac2942..ce2db5d 100644 (file)
@@ -48,7 +48,7 @@ class GenderCacheTest extends MediaWikiLangTestCase {
         * @dataProvider provideUserGenders
         * @covers GenderCache::getGenderOf
         */
-       function testUserName( $username, $expectedGender ) {
+       public function testUserName( $username, $expectedGender ) {
                $genderCache = GenderCache::singleton();
                $gender = $genderCache->getGenderOf( $username );
                $this->assertEquals( $gender, $expectedGender, "GenderCache normal" );
@@ -60,7 +60,7 @@ class GenderCacheTest extends MediaWikiLangTestCase {
         * @dataProvider provideUserGenders
         * @covers GenderCache::getGenderOf
         */
-       function testUserObjects( $username, $expectedGender ) {
+       public function testUserObjects( $username, $expectedGender ) {
                $genderCache = GenderCache::singleton();
                $user = User::newFromName( $username );
                $gender = $genderCache->getGenderOf( $user );
@@ -86,7 +86,7 @@ class GenderCacheTest extends MediaWikiLangTestCase {
         * @dataProvider provideStripSubpages
         * @covers GenderCache::getGenderOf
         */
-       function testStripSubpages( $pageWithSubpage, $expectedGender ) {
+       public function testStripSubpages( $pageWithSubpage, $expectedGender ) {
                $genderCache = GenderCache::singleton();
                $gender = $genderCache->getGenderOf( $pageWithSubpage );
                $this->assertEquals( $gender, $expectedGender, "GenderCache must strip of subpages" );
index 3559f15..803acf7 100644 (file)
@@ -84,7 +84,7 @@ class MessageCacheTest extends MediaWikiLangTestCase {
         *
         * @dataProvider provideMessagesForFallback
         */
-       function testMessageFallbacks( $message, $lang, $expectedContent ) {
+       public function testMessageFallbacks( $message, $lang, $expectedContent ) {
                $result = MessageCache::singleton()->get( $message, true, $lang );
                $this->assertEquals( $expectedContent, $result, "Message fallback failed." );
        }
@@ -112,7 +112,7 @@ class MessageCacheTest extends MediaWikiLangTestCase {
         *
         * @dataProvider provideMessagesForFullKeys
         */
-       function testFullKeyBehaviour( $message, $lang, $expectedContent ) {
+       public function testFullKeyBehaviour( $message, $lang, $expectedContent ) {
                $result = MessageCache::singleton()->get( $message, true, $lang, true );
                $this->assertEquals( $expectedContent, $result, "Full key message fallback failed." );
        }
index 1c81ea7..d3793d8 100644 (file)
@@ -59,7 +59,7 @@ class ProcessCacheLRUTest extends MediaWikiTestCase {
        /**
         * Highlight diff between assertEquals and assertNotSame
         */
-       function testPhpUnitArrayEquality() {
+       public function testPhpUnitArrayEquality() {
                $one = array( 'A' => 1, 'B' => 2 );
                $two = array( 'B' => 2, 'A' => 1 );
                $this->assertEquals( $one, $two ); // ==
@@ -70,7 +70,7 @@ class ProcessCacheLRUTest extends MediaWikiTestCase {
         * @dataProvider provideInvalidConstructorArg
         * @expectedException MWException
         */
-       function testConstructorGivenInvalidValue( $maxSize ) {
+       public function testConstructorGivenInvalidValue( $maxSize ) {
                new ProcessCacheLRUTestable( $maxSize );
        }
 
@@ -88,7 +88,7 @@ class ProcessCacheLRUTest extends MediaWikiTestCase {
                );
        }
 
-       function testAddAndGetAKey() {
+       public function testAddAndGetAKey() {
                $oneCache = new ProcessCacheLRUTestable( 1 );
                $this->assertCacheEmpty( $oneCache );
 
@@ -99,7 +99,7 @@ class ProcessCacheLRUTest extends MediaWikiTestCase {
                $this->assertEquals( 'value1', $oneCache->get( 'cache-key', 'prop1' ) );
        }
 
-       function testDeleteOldKey() {
+       public function testDeleteOldKey() {
                $oneCache = new ProcessCacheLRUTestable( 1 );
                $this->assertCacheEmpty( $oneCache );
 
@@ -117,7 +117,7 @@ class ProcessCacheLRUTest extends MediaWikiTestCase {
         * @param $cacheMaxEntries Maximum entry the created cache will hold
         * @param $entryToFill Number of entries to insert in the created cache.
         */
-       function testFillingCache( $cacheMaxEntries, $entryToFill, $msg = '' ) {
+       public function testFillingCache( $cacheMaxEntries, $entryToFill, $msg = '' ) {
                $cache = new ProcessCacheLRUTestable( $cacheMaxEntries );
                $this->fillCache( $cache, $entryToFill );
 
@@ -145,7 +145,7 @@ class ProcessCacheLRUTest extends MediaWikiTestCase {
         * Create a cache with only one remaining entry then update
         * the first inserted entry. Should bump it to the top.
         */
-       function testReplaceExistingKeyShouldBumpEntryToTop() {
+       public function testReplaceExistingKeyShouldBumpEntryToTop() {
                $maxEntries = 3;
 
                $cache = new ProcessCacheLRUTestable( $maxEntries );
@@ -164,7 +164,7 @@ class ProcessCacheLRUTest extends MediaWikiTestCase {
                );
        }
 
-       function testRecentlyAccessedKeyStickIn() {
+       public function testRecentlyAccessedKeyStickIn() {
                $cache = new ProcessCacheLRUTestable( 2 );
                $cache->set( 'first', 'prop1', 'value1' );
                $cache->set( 'second', 'prop2', 'value2' );
@@ -183,7 +183,7 @@ class ProcessCacheLRUTest extends MediaWikiTestCase {
         * Given a cache having 1,2,3 as key, updating 2 should bump 2 to
         * the top of the queue with the new value: 1,3,2* (* = updated).
         */
-       function testReplaceExistingKeyInAFullCacheShouldBumpToTop() {
+       public function testReplaceExistingKeyInAFullCacheShouldBumpToTop() {
                $maxEntries = 3;
 
                $cache = new ProcessCacheLRUTestable( $maxEntries );
@@ -204,7 +204,7 @@ class ProcessCacheLRUTest extends MediaWikiTestCase {
                );
        }
 
-       function testBumpExistingKeyToTop() {
+       public function testBumpExistingKeyToTop() {
                $cache = new ProcessCacheLRUTestable( 3 );
                $this->fillCache( $cache, 3 );
 
index 9f20073..1d133f3 100644 (file)
@@ -245,26 +245,6 @@ just a test"
                );
        }
 
-       /**
-        * @todo Test needs database! Should be done by a test class in the Database group.
-        */
-       /*
-       public function getRedirectChain() {
-               $text = $this->getNativeData();
-               return Title::newFromRedirectArray( $text );
-       }
-       */
-
-       /**
-        * @todo Test needs database! Should be done by a test class in the Database group.
-        */
-       /*
-       public function getUltimateRedirectTarget() {
-               $text = $this->getNativeData();
-               return Title::newFromRedirectRecurse( $text );
-       }
-       */
-
        public static function dataIsCountable() {
                return array(
                        array( '',
index 7d7a2bd..134f856 100644 (file)
@@ -60,7 +60,7 @@ class DatabaseMysqlBaseTest extends MediaWikiTestCase {
         * @dataProvider provideDiapers
         * @covers DatabaseMysqlBase::addIdentifierQuotes
         */
-       function testAddIdentifierQuotes( $expected, $in ) {
+       public function testAddIdentifierQuotes( $expected, $in ) {
                $db = new FakeDatabaseMysqlBase();
                $quoted = $db->addIdentifierQuotes( $in );
                $this->assertEquals($expected, $quoted);
index 726d63a..bdd567e 100644 (file)
@@ -27,7 +27,7 @@ class DatabaseSQLTest extends MediaWikiTestCase {
         * @dataProvider provideSelect
         * @covers DatabaseBase::select
         */
-       function testSelect( $sql, $sqlText ) {
+       public function testSelect( $sql, $sqlText ) {
                $this->database->select(
                        $sql['tables'],
                        $sql['fields'],
@@ -129,7 +129,7 @@ class DatabaseSQLTest extends MediaWikiTestCase {
         * @dataProvider provideUpdate
         * @covers DatabaseBase::update
         */
-       function testUpdate( $sql, $sqlText ) {
+       public function testUpdate( $sql, $sqlText ) {
                $this->database->update(
                        $sql['table'],
                        $sql['values'],
@@ -181,7 +181,7 @@ class DatabaseSQLTest extends MediaWikiTestCase {
         * @dataProvider provideDelete
         * @covers DatabaseBase::delete
         */
-       function testDelete( $sql, $sqlText ) {
+       public function testDelete( $sql, $sqlText ) {
                $this->database->delete(
                        $sql['table'],
                        $sql['conds'],
@@ -214,7 +214,7 @@ class DatabaseSQLTest extends MediaWikiTestCase {
         * @dataProvider provideUpsert
         * @covers DatabaseBase::upsert
         */
-       function testUpsert( $sql, $sqlText ) {
+       public function testUpsert( $sql, $sqlText ) {
                $this->database->upsert(
                        $sql['table'],
                        $sql['rows'],
@@ -250,7 +250,7 @@ class DatabaseSQLTest extends MediaWikiTestCase {
         * @dataProvider provideDeleteJoin
         * @covers DatabaseBase::deleteJoin
         */
-       function testDeleteJoin( $sql, $sqlText ) {
+       public function testDeleteJoin( $sql, $sqlText ) {
                $this->database->deleteJoin(
                        $sql['delTable'],
                        $sql['joinTable'],
@@ -297,7 +297,7 @@ class DatabaseSQLTest extends MediaWikiTestCase {
         * @dataProvider provideInsert
         * @covers DatabaseBase::insert
         */
-       function testInsert( $sql, $sqlText ) {
+       public function testInsert( $sql, $sqlText ) {
                $this->database->insert(
                        $sql['table'],
                        $sql['rows'],
@@ -350,7 +350,7 @@ class DatabaseSQLTest extends MediaWikiTestCase {
         * @dataProvider provideInsertSelect
         * @covers DatabaseBase::insertSelect
         */
-       function testInsertSelect( $sql, $sqlText ) {
+       public function testInsertSelect( $sql, $sqlText ) {
                $this->database->insertSelect(
                        $sql['destTable'],
                        $sql['srcTable'],
@@ -413,7 +413,7 @@ class DatabaseSQLTest extends MediaWikiTestCase {
         * @dataProvider provideReplace
         * @covers DatabaseBase::replace
         */
-       function testReplace( $sql, $sqlText ) {
+       public function testReplace( $sql, $sqlText ) {
                $this->database->replace(
                        $sql['table'],
                        $sql['uniqueIndexes'],
@@ -528,7 +528,7 @@ class DatabaseSQLTest extends MediaWikiTestCase {
         * @dataProvider provideNativeReplace
         * @covers DatabaseBase::nativeReplace
         */
-       function testNativeReplace( $sql, $sqlText ) {
+       public function testNativeReplace( $sql, $sqlText ) {
                $this->database->nativeReplace(
                        $sql['table'],
                        $sql['rows'],
@@ -555,7 +555,7 @@ class DatabaseSQLTest extends MediaWikiTestCase {
         * @dataProvider provideConditional
         * @covers DatabaseBase::conditional
         */
-       function testConditional( $sql, $sqlText ) {
+       public function testConditional( $sql, $sqlText ) {
                $this->assertEquals( trim( $this->database->conditional(
                        $sql['conds'],
                        $sql['true'],
@@ -596,7 +596,7 @@ class DatabaseSQLTest extends MediaWikiTestCase {
         * @dataProvider provideBuildConcat
         * @covers DatabaseBase::buildConcat
         */
-       function testBuildConcat( $stringList, $sqlText ) {
+       public function testBuildConcat( $stringList, $sqlText ) {
                $this->assertEquals( trim( $this->database->buildConcat(
                        $stringList
                ) ), $sqlText );
@@ -619,7 +619,7 @@ class DatabaseSQLTest extends MediaWikiTestCase {
         * @dataProvider provideBuildLike
         * @covers DatabaseBase::buildLike
         */
-       function testBuildLike( $array, $sqlText ) {
+       public function testBuildLike( $array, $sqlText ) {
                $this->assertEquals( trim( $this->database->buildLike(
                        $array
                ) ), $sqlText );
@@ -650,7 +650,7 @@ class DatabaseSQLTest extends MediaWikiTestCase {
         * @dataProvider provideUnionQueries
         * @covers DatabaseBase::unionQueries
         */
-       function testUnionQueries( $sql, $sqlText ) {
+       public function testUnionQueries( $sql, $sqlText ) {
                $this->assertEquals( trim( $this->database->unionQueries(
                        $sql['sqls'],
                        $sql['all']
@@ -686,7 +686,7 @@ class DatabaseSQLTest extends MediaWikiTestCase {
        /**
         * @covers DatabaseBase::commit
         */
-       function testTransactionCommit() {
+       public function testTransactionCommit() {
                $this->database->begin( __METHOD__ );
                $this->database->commit( __METHOD__ );
                $this->assertLastSql( 'BEGIN; COMMIT' );
@@ -695,7 +695,7 @@ class DatabaseSQLTest extends MediaWikiTestCase {
        /**
         * @covers DatabaseBase::rollback
         */
-       function testTransactionRollback() {
+       public function testTransactionRollback() {
                $this->database->begin( __METHOD__ );
                $this->database->rollback( __METHOD__ );
                $this->assertLastSql( 'BEGIN; ROLLBACK' );
@@ -704,7 +704,7 @@ class DatabaseSQLTest extends MediaWikiTestCase {
        /**
         * @covers DatabaseBase::dropTable
         */
-       function testDropTable() {
+       public function testDropTable() {
                $this->database->setExistingTables( array( 'table' ) );
                $this->database->dropTable( 'table', __METHOD__ );
                $this->assertLastSql( 'DROP TABLE table' );
@@ -713,7 +713,7 @@ class DatabaseSQLTest extends MediaWikiTestCase {
        /**
         * @covers DatabaseBase::dropTable
         */
-       function testDropNonExistingTable() {
+       public function testDropNonExistingTable() {
                $this->assertFalse(
                        $this->database->dropTable( 'non_existing', __METHOD__ )
                );
index f151fb2..65726eb 100644 (file)
@@ -149,6 +149,14 @@ class DatabaseSqliteTest extends MediaWikiTestCase {
                $this->assertEquals( "ALTER TABLE foo ADD COLUMN foo_bar INTEGER DEFAULT 42",
                        $this->replaceVars( "ALTER TABLE foo\nADD COLUMN foo_bar int(10) unsigned DEFAULT 42" )
                );
+
+               $this->assertEquals( "DROP INDEX foo",
+                       $this->replaceVars( "DROP INDEX /*i*/foo ON /*_*/bar" )
+               );
+
+               $this->assertEquals( "DROP INDEX foo -- dropping index",
+                       $this->replaceVars( "DROP INDEX /*i*/foo ON /*_*/bar -- dropping index" )
+               );
        }
 
        /**
@@ -411,7 +419,7 @@ class DatabaseSqliteTest extends MediaWikiTestCase {
                return $indexes;
        }
 
-       function testCaseInsensitiveLike() {
+       public function testCaseInsensitiveLike() {
                // TODO: Test this for all databases
                $db = new DatabaseSqliteStandalone( ':memory:' );
                $res = $db->query( 'SELECT "a" LIKE "A" AS a' );
index 5e8e7a4..301fc99 100644 (file)
@@ -26,7 +26,7 @@ class DatabaseTest extends MediaWikiTestCase {
        /**
         * @covers DatabaseBase::dropTable
         */
-       function testAddQuotesNull() {
+       public function testAddQuotesNull() {
                $check = "NULL";
                if ( $this->db->getType() === 'sqlite' || $this->db->getType() === 'oracle' ) {
                        $check = "''";
@@ -34,7 +34,7 @@ class DatabaseTest extends MediaWikiTestCase {
                $this->assertEquals( $check, $this->db->addQuotes( null ) );
        }
 
-       function testAddQuotesInt() {
+       public function testAddQuotesInt() {
                # returning just "1234" should be ok too, though...
                # maybe
                $this->assertEquals(
@@ -42,20 +42,20 @@ class DatabaseTest extends MediaWikiTestCase {
                        $this->db->addQuotes( 1234 ) );
        }
 
-       function testAddQuotesFloat() {
+       public function testAddQuotesFloat() {
                # returning just "1234.5678" would be ok too, though
                $this->assertEquals(
                        "'1234.5678'",
                        $this->db->addQuotes( 1234.5678 ) );
        }
 
-       function testAddQuotesString() {
+       public function testAddQuotesString() {
                $this->assertEquals(
                        "'string'",
                        $this->db->addQuotes( 'string' ) );
        }
 
-       function testAddQuotesStringQuote() {
+       public function testAddQuotesStringQuote() {
                $check = "'string''s cause trouble'";
                if ( $this->db->getType() === 'mysql' ) {
                        $check = "'string\'s cause trouble'";
@@ -115,21 +115,21 @@ class DatabaseTest extends MediaWikiTestCase {
                }
        }
 
-       function testTableNameLocal() {
+       public function testTableNameLocal() {
                $this->assertEquals(
                        $this->prefixAndQuote( 'tablename' ),
                        $this->db->tableName( 'tablename' )
                );
        }
 
-       function testTableNameRawLocal() {
+       public function testTableNameRawLocal() {
                $this->assertEquals(
                        $this->prefixAndQuote( 'tablename', null, null, 'raw' ),
                        $this->db->tableName( 'tablename', 'raw' )
                );
        }
 
-       function testTableNameShared() {
+       public function testTableNameShared() {
                $this->assertEquals(
                        $this->prefixAndQuote( 'tablename', 'sharedatabase', 'sh_' ),
                        $this->getSharedTableName( 'tablename', 'sharedatabase', 'sh_' )
@@ -141,7 +141,7 @@ class DatabaseTest extends MediaWikiTestCase {
                );
        }
 
-       function testTableNameRawShared() {
+       public function testTableNameRawShared() {
                $this->assertEquals(
                        $this->prefixAndQuote( 'tablename', 'sharedatabase', 'sh_', 'raw' ),
                        $this->getSharedTableName( 'tablename', 'sharedatabase', 'sh_', 'raw' )
@@ -153,21 +153,21 @@ class DatabaseTest extends MediaWikiTestCase {
                );
        }
 
-       function testTableNameForeign() {
+       public function testTableNameForeign() {
                $this->assertEquals(
                        $this->prefixAndQuote( 'tablename', 'databasename', '' ),
                        $this->db->tableName( 'databasename.tablename' )
                );
        }
 
-       function testTableNameRawForeign() {
+       public function testTableNameRawForeign() {
                $this->assertEquals(
                        $this->prefixAndQuote( 'tablename', 'databasename', '', 'raw' ),
                        $this->db->tableName( 'databasename.tablename', 'raw' )
                );
        }
 
-       function testFillPreparedEmpty() {
+       public function testFillPreparedEmpty() {
                $sql = $this->db->fillPrepared(
                        'SELECT * FROM interwiki', array() );
                $this->assertEquals(
@@ -175,7 +175,7 @@ class DatabaseTest extends MediaWikiTestCase {
                        $sql );
        }
 
-       function testFillPreparedQuestion() {
+       public function testFillPreparedQuestion() {
                $sql = $this->db->fillPrepared(
                        'SELECT * FROM cur WHERE cur_namespace=? AND cur_title=?',
                        array( 4, "Snicker's_paradox" ) );
@@ -187,7 +187,7 @@ class DatabaseTest extends MediaWikiTestCase {
                $this->assertEquals( $check, $sql );
        }
 
-       function testFillPreparedBang() {
+       public function testFillPreparedBang() {
                $sql = $this->db->fillPrepared(
                        'SELECT user_id FROM ! WHERE user_name=?',
                        array( '"user"', "Slash's Dot" ) );
@@ -199,7 +199,7 @@ class DatabaseTest extends MediaWikiTestCase {
                $this->assertEquals( $check, $sql );
        }
 
-       function testFillPreparedRaw() {
+       public function testFillPreparedRaw() {
                $sql = $this->db->fillPrepared(
                        "SELECT * FROM cur WHERE cur_title='This_\\&_that,_WTF\\?\\!'",
                        array( '"user"', "Slash's Dot" ) );
@@ -208,7 +208,7 @@ class DatabaseTest extends MediaWikiTestCase {
                        $sql );
        }
 
-       function testStoredFunctions() {
+       public function testStoredFunctions() {
                if ( !in_array( wfGetDB( DB_MASTER )->getType(), array( 'mysql', 'postgres' ) ) ) {
                        $this->markTestSkipped( 'MySQL or Postgres required' );
                }
@@ -226,7 +226,7 @@ class DatabaseTest extends MediaWikiTestCase {
                );
        }
 
-       function testUnknownTableCorruptsResults() {
+       public function testUnknownTableCorruptsResults() {
                $res = $this->db->select( 'page', '*', array( 'page_id' => 1 ) );
                $this->assertFalse( $this->db->tableExists( 'foobarbaz' ) );
                $this->assertInternalType( 'int', $res->numRows() );
index e583d1b..7171ee5 100644 (file)
  * @author Jeroen De Dauw < jeroendedauw@gmail.com >
  * @author Daniel Kinzler
  */
+
+/**
+ * @covers PageORMTableForTesting
+ */
 class ORMTableTest extends MediaWikiTestCase {
 
        /**
index f65642b..c9459c9 100644 (file)
@@ -40,6 +40,9 @@
  */
 require_once __DIR__ . "/ORMRowTest.php";
 
+/**
+ * @covers TestORMRow
+ */
 class TestORMRowTest extends ORMRowTest {
 
        /**
index 9026cb9..df73000 100644 (file)
@@ -21,7 +21,10 @@ class MWDebugTest extends MediaWikiTestCase {
                parent::tearDown();
        }
 
-       function testAddLog() {
+       /**
+        * @covers MWDebug::log
+        */
+       public function testAddLog() {
                MWDebug::log( 'logging a string' );
                $this->assertEquals(
                        array( array(
@@ -33,7 +36,10 @@ class MWDebugTest extends MediaWikiTestCase {
                );
        }
 
-       function testAddWarning() {
+       /**
+        * @covers MWDebug::warning
+        */
+       public function testAddWarning() {
                MWDebug::warning( 'Warning message' );
                $this->assertEquals(
                        array( array(
@@ -45,7 +51,10 @@ class MWDebugTest extends MediaWikiTestCase {
                );
        }
 
-       function testAvoidDuplicateDeprecations() {
+       /**
+        * @covers MWDebug::deprecated
+        */
+       public function testAvoidDuplicateDeprecations() {
                MWDebug::deprecated( 'wfOldFunction', '1.0', 'component' );
                MWDebug::deprecated( 'wfOldFunction', '1.0', 'component' );
 
@@ -56,7 +65,10 @@ class MWDebugTest extends MediaWikiTestCase {
                );
        }
 
-       function testAvoidNonConsecutivesDuplicateDeprecations() {
+       /**
+        * @covers MWDebug::deprecated
+        */
+       public function testAvoidNonConsecutivesDuplicateDeprecations() {
                MWDebug::deprecated( 'wfOldFunction', '1.0', 'component' );
                MWDebug::warning( 'some warning' );
                MWDebug::log( 'we could have logged something too' );
diff --git a/tests/phpunit/includes/diff/DifferenceEngineTest.php b/tests/phpunit/includes/diff/DifferenceEngineTest.php
new file mode 100644 (file)
index 0000000..738121a
--- /dev/null
@@ -0,0 +1,120 @@
+<?php
+
+/**
+ * @covers DifferenceEngine
+ *
+ * @todo tests for the rest of DifferenceEngine!
+ *
+ * @group Database
+ *
+ * @licence GNU GPL v2+
+ * @author Katie Filbert < aude.wiki@gmail.com >
+ */
+class DifferenceEngineTest extends MediaWikiTestCase {
+
+       protected $context;
+
+       private static $revisions;
+
+       public function setUp() {
+               parent::setUp();
+
+               $title = $this->getTitle();
+
+               $this->context = new RequestContext();
+               $this->context->setTitle( $title );
+
+               if ( !self::$revisions ) {
+                       self::$revisions = $this->doEdits();
+               }
+       }
+
+       /**
+        * @return Title
+        */
+       protected function getTitle() {
+               $namespace = $this->getDefaultWikitextNS();
+               return Title::newFromText( 'Kitten', $namespace );
+       }
+
+       /**
+        * @return int[] revision ids
+        */
+       protected function doEdits() {
+               $title = $this->getTitle();
+               $page = WikiPage::factory( $title );
+
+               $strings = array( "it is a kitten", "two kittens", "three kittens", "four kittens" );
+               $revisions = array();
+
+               foreach( $strings as $string ) {
+                       $content = ContentHandler::makeContent( $string, $title );
+                       $page->doEditContent( $content, 'edit page' );
+                       $revisions[] = $page->getLatest();
+               }
+
+               return $revisions;
+       }
+
+       public function testMapDiffPrevNext() {
+               $cases = $this->getMapDiffPrevNextCases();
+
+               foreach( $cases as $case ) {
+                       list( $expected, $old, $new, $message ) = $case;
+
+                       $diffEngine = new DifferenceEngine( $this->context, $old, $new, 2, true, false );
+                       $diffMap = $diffEngine->mapDiffPrevNext( $old, $new );
+                       $this->assertEquals( $expected, $diffMap, $message );
+               }
+       }
+
+       private function getMapDiffPrevNextCases() {
+               $revs = self::$revisions;
+
+               return array(
+                       array( array( $revs[1], $revs[2] ), $revs[2], 'prev', 'diff=prev' ),
+                       array( array( $revs[2], $revs[3] ), $revs[2], 'next', 'diff=next' ),
+                       array( array( $revs[1], $revs[3] ), $revs[1], $revs[3], 'diff=' . $revs[3] )
+               );
+       }
+
+       public function testLoadRevisionData() {
+               $cases = $this->getLoadRevisionDataCases();
+
+               foreach( $cases as $case ) {
+                       list( $expectedOld, $expectedNew, $old, $new, $message ) = $case;
+
+                       $diffEngine = new DifferenceEngine( $this->context, $old, $new, 2, true, false );
+                       $diffEngine->loadRevisionData();
+
+                       $this->assertEquals( $diffEngine->getOldid(), $expectedOld, $message );
+                       $this->assertEquals( $diffEngine->getNewid(), $expectedNew, $message );
+               }
+       }
+
+       private function getLoadRevisionDataCases() {
+               $revs = self::$revisions;
+
+               return array(
+                       array( $revs[2], $revs[3], $revs[3], 'prev', 'diff=prev' ),
+                       array( $revs[2], $revs[3], $revs[2], 'next', 'diff=next' ),
+                       array( $revs[1], $revs[3], $revs[1], $revs[3], 'diff=' . $revs[3] ),
+                       array( $revs[1], $revs[3], $revs[1], 0, 'diff=0' )
+               );
+       }
+
+       public function testGetOldid() {
+               $revs = self::$revisions;
+
+               $diffEngine = new DifferenceEngine( $this->context, $revs[1], $revs[2], 2, true, false );
+               $this->assertEquals( $revs[1], $diffEngine->getOldid(), 'diff get old id' );
+       }
+
+       public function testGetNewid() {
+               $revs = self::$revisions;
+
+               $diffEngine = new DifferenceEngine( $this->context, $revs[1], $revs[2], 2, true, false );
+               $this->assertEquals( $revs[2], $diffEngine->getNewid(), 'diff get new id' );
+       }
+
+}
index 013bbe2..fcfa724 100644 (file)
@@ -6,7 +6,13 @@
  * @group medium
  */
 class FileBackendTest extends MediaWikiTestCase {
-       private $backend, $multiBackend;
+
+       /** @var FileBackend */
+       private $backend;
+       /** @var FileBackendMultiWrite */
+       private $multiBackend;
+       /** @var FSFileBackend */
+       public $singleBackend;
        private $filesToPrune = array();
        private static $backendToUse;
 
@@ -85,6 +91,7 @@ class FileBackendTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provider_testIsStoragePath
+        * @covers FileBackend::isStoragePath
         */
        public function testIsStoragePath( $path, $isStorePath ) {
                $this->assertEquals( $isStorePath, FileBackend::isStoragePath( $path ),
@@ -109,6 +116,7 @@ class FileBackendTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provider_testSplitStoragePath
+        * @covers FileBackend::splitStoragePath
         */
        public function testSplitStoragePath( $path, $res ) {
                $this->assertEquals( $res, FileBackend::splitStoragePath( $path ),
@@ -133,6 +141,7 @@ class FileBackendTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provider_normalizeStoragePath
+        * @covers FileBackend::normalizeStoragePath
         */
        public function testNormalizeStoragePath( $path, $res ) {
                $this->assertEquals( $res, FileBackend::normalizeStoragePath( $path ),
@@ -159,6 +168,7 @@ class FileBackendTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provider_testParentStoragePath
+        * @covers FileBackend::parentStoragePath
         */
        public function testParentStoragePath( $path, $res ) {
                $this->assertEquals( $res, FileBackend::parentStoragePath( $path ),
@@ -180,6 +190,7 @@ class FileBackendTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provider_testExtensionFromPath
+        * @covers FileBackend::extensionFromPath
         */
        public function testExtensionFromPath( $path, $res ) {
                $this->assertEquals( $res, FileBackend::extensionFromPath( $path ),
@@ -213,6 +224,9 @@ class FileBackendTest extends MediaWikiTestCase {
                $this->tearDownFiles();
        }
 
+       /**
+        * @covers FileBackend::doOperation
+        */
        private function doTestStore( $op ) {
                $backendName = $this->backendClass();
 
@@ -284,6 +298,7 @@ class FileBackendTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provider_testCopy
+        * @covers FileBackend::doOperation
         */
        public function testCopy( $op ) {
                $this->backend = $this->singleBackend;
@@ -404,6 +419,7 @@ class FileBackendTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provider_testMove
+        * @covers FileBackend::doOperation
         */
        public function testMove( $op ) {
                $this->backend = $this->singleBackend;
@@ -525,6 +541,7 @@ class FileBackendTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provider_testDelete
+        * @covers FileBackend::doOperation
         */
        public function testDelete( $op, $withSource, $okStatus ) {
                $this->backend = $this->singleBackend;
@@ -616,6 +633,7 @@ class FileBackendTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provider_testDescribe
+        * @covers FileBackend::doOperation
         */
        public function testDescribe( $op, $withSource, $okStatus ) {
                $this->backend = $this->singleBackend;
@@ -683,6 +701,7 @@ class FileBackendTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provider_testCreate
+        * @covers FileBackend::doOperation
         */
        public function testCreate( $op, $alreadyExists, $okStatus, $newSize ) {
                $this->backend = $this->singleBackend;
@@ -803,6 +822,9 @@ class FileBackendTest extends MediaWikiTestCase {
                return $cases;
        }
 
+       /**
+        * @covers FileBackend::doQuickOperations
+        */
        public function testDoQuickOperations() {
                $this->backend = $this->singleBackend;
                $this->doTestDoQuickOperations();
@@ -1019,6 +1041,7 @@ class FileBackendTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provider_testGetFileStat
+        * @covers FileBackend::getFileStat
         */
        public function testGetFileStat( $path, $content, $alreadyExists ) {
                $this->backend = $this->singleBackend;
@@ -1094,6 +1117,8 @@ class FileBackendTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provider_testGetFileContents
+        * @covers FileBackend::getFileContents
+        * @covers FileBackend::getFileContentsMulti
         */
        public function testGetFileContents( $source, $content ) {
                $this->backend = $this->singleBackend;
@@ -1153,6 +1178,7 @@ class FileBackendTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provider_testGetLocalCopy
+        * @covers FileBackend::getLocalCopy
         */
        public function testGetLocalCopy( $source, $content ) {
                $this->backend = $this->singleBackend;
@@ -1222,6 +1248,7 @@ class FileBackendTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provider_testGetLocalReference
+        * @covers FileBackend::getLocalReference
         */
        public function testGetLocalReference( $source, $content ) {
                $this->backend = $this->singleBackend;
@@ -1286,6 +1313,10 @@ class FileBackendTest extends MediaWikiTestCase {
                return $cases;
        }
 
+       /**
+        * @covers FileBackend::getLocalCopy
+        * @covers FileBackend::getLocalReference
+        */
        public function testGetLocalCopyAndReference404() {
                $this->backend = $this->singleBackend;
                $this->tearDownFiles();
@@ -1314,6 +1345,7 @@ class FileBackendTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provider_testGetFileHttpUrl
+        * @covers FileBackend::getFileHttpUrl
         */
        public function testGetFileHttpUrl( $source, $content ) {
                $this->backend = $this->singleBackend;
@@ -1358,6 +1390,8 @@ class FileBackendTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provider_testPrepareAndClean
+        * @covers FileBackend::prepare
+        * @covers FileBackend::clean
         */
        public function testPrepareAndClean( $path, $isOK ) {
                $this->backend = $this->singleBackend;
@@ -1416,6 +1450,9 @@ class FileBackendTest extends MediaWikiTestCase {
                $this->tearDownFiles();
        }
 
+       /**
+        * @covers FileBackend::clean
+        */
        private function doTestRecursiveClean() {
                $backendName = $this->backendClass();
 
@@ -1462,6 +1499,9 @@ class FileBackendTest extends MediaWikiTestCase {
 
        // @todo testSecure
 
+       /**
+        * @covers FileBackend::doOperations
+        */
        public function testDoOperations() {
                $this->backend = $this->singleBackend;
                $this->tearDownFiles();
@@ -1549,6 +1589,9 @@ class FileBackendTest extends MediaWikiTestCase {
                        "Correct file SHA-1 of $fileC" );
        }
 
+       /**
+        * @covers FileBackend::doOperations
+        */
        public function testDoOperationsPipeline() {
                $this->backend = $this->singleBackend;
                $this->tearDownFiles();
@@ -1648,6 +1691,9 @@ class FileBackendTest extends MediaWikiTestCase {
                        "Correct file SHA-1 of $fileC" );
        }
 
+       /**
+        * @covers FileBackend::doOperations
+        */
        public function testDoOperationsFailing() {
                $this->backend = $this->singleBackend;
                $this->tearDownFiles();
@@ -1722,6 +1768,9 @@ class FileBackendTest extends MediaWikiTestCase {
                        "Correct file SHA-1 of $fileA" );
        }
 
+       /**
+        * @covers FileBackend::getFileList
+        */
        public function testGetFileList() {
                $this->backend = $this->singleBackend;
                $this->tearDownFiles();
@@ -1886,6 +1935,10 @@ class FileBackendTest extends MediaWikiTestCase {
                }
        }
 
+       /**
+        * @covers FileBackend::getTopDirectoryList
+        * @covers FileBackend::getDirectoryList
+        */
        public function testGetDirectoryList() {
                $this->backend = $this->singleBackend;
                $this->tearDownFiles();
@@ -2092,6 +2145,10 @@ class FileBackendTest extends MediaWikiTestCase {
                $this->assertEquals( array(), $items, "Directory listing is empty." );
        }
 
+       /**
+        * @covers FileBackend::lockFiles
+        * @covers FileBackend::unlockFiles
+        */
        public function testLockCalls() {
                $this->backend = $this->singleBackend;
                $this->doTestLockCalls();
index 033ae0b..e3a7556 100644 (file)
@@ -1,24 +1,28 @@
 <?php
 
 class FileRepoTest extends MediaWikiTestCase {
+
        /**
         * @expectedException MWException
+        * @covers FileRepo::__construct
         */
-       function testFileRepoConstructionOptionCanNotBeNull() {
+       public function testFileRepoConstructionOptionCanNotBeNull() {
                new FileRepo();
        }
 
        /**
         * @expectedException MWException
+        * @covers FileRepo::__construct
         */
-       function testFileRepoConstructionOptionCanNotBeAnEmptyArray() {
+       public function testFileRepoConstructionOptionCanNotBeAnEmptyArray() {
                new FileRepo( array() );
        }
 
        /**
         * @expectedException MWException
+        * @covers FileRepo::__construct
         */
-       function testFileRepoConstructionOptionNeedNameKey() {
+       public function testFileRepoConstructionOptionNeedNameKey() {
                new FileRepo( array(
                        'backend' => 'foobar'
                ) );
@@ -26,14 +30,18 @@ class FileRepoTest extends MediaWikiTestCase {
 
        /**
         * @expectedException MWException
+        * @covers FileRepo::__construct
         */
-       function testFileRepoConstructionOptionNeedBackendKey() {
+       public function testFileRepoConstructionOptionNeedBackendKey() {
                new FileRepo( array(
                        'name' => 'foobar'
                ) );
        }
 
-       function testFileRepoConstructionWithRequiredOptions() {
+       /**
+        * @covers FileRepo::__construct
+        */
+       public function testFileRepoConstructionWithRequiredOptions() {
                $f = new FileRepo( array(
                        'name' => 'FileRepoTestRepository',
                        'backend' => new FSFileBackend( array(
index 71a585e..b33c1bb 100644 (file)
@@ -1,10 +1,16 @@
 <?php
+
 /**
  * @group FileRepo
  * @group medium
  */
 class StoreBatchTest extends MediaWikiTestCase {
 
+       protected $createdFiles;
+       protected $date;
+       /** @var FileRepo */
+       protected $repo;
+
        protected function setUp() {
                global $wgFileBackends;
                parent::setUp();
@@ -60,6 +66,7 @@ class StoreBatchTest extends MediaWikiTestCase {
         * @param $originalName string The title of the image
         * @param $srcPath string The filepath or virtual URL
         * @param $flags integer Flags to pass into repo::store().
+        * @return FileRepoStatus
         */
        private function storeit( $originalName, $srcPath, $flags ) {
                $hashPath = $this->repo->getHashPath( $originalName );
@@ -79,7 +86,7 @@ class StoreBatchTest extends MediaWikiTestCase {
         * @param $fn string The title of the image
         * @param $infn string The name of the file (in the filesystem)
         * @param $otherfn string The name of the different file (in the filesystem)
-        * @param $fromrepo logical 'true' if we want to copy from a virtual URL out of the Repo.
+        * @param $fromrepo bool 'true' if we want to copy from a virtual URL out of the Repo.
         */
        private function storecohort( $fn, $infn, $otherfn, $fromrepo ) {
                $f = $this->storeit( $fn, $infn, 0 );
@@ -116,6 +123,9 @@ class StoreBatchTest extends MediaWikiTestCase {
                $this->assertEquals( $f->successCount, 0, "counts wrong {$f->successCount} {$f->failCount}" );
        }
 
+       /**
+        * @covers FileRepo::store
+        */
        public function teststore() {
                global $IP;
                $this->storecohort( "Test1.png", "$IP/skins/monobook/wiki.png", "$IP/skins/monobook/video.png", false );
index ea87ede..0e5f267 100644 (file)
@@ -9,7 +9,7 @@ class InstallDocFormatterTest extends MediaWikiTestCase {
         * @covers InstallDocFormatter::format
         * @dataProvider provideDocFormattingTests
         */
-       function testFormat( $expected, $unformattedText, $message = '' ) {
+       public function testFormat( $expected, $unformattedText, $message = '' ) {
                $this->assertEquals(
                        $expected,
                        InstallDocFormatter::format( $unformattedText ),
index 7c37f98..66e6559 100644 (file)
@@ -11,8 +11,9 @@ class OracleInstallerTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provideOracleConnectStrings
+        * @covers OracleInstaller::checkConnectStringFormat
         */
-       function testCheckConnectStringFormat( $expected, $connectString, $msg = '' ) {
+       public function testCheckConnectStringFormat( $expected, $connectString, $msg = '' ) {
                $validity = $expected ? 'should be valid' : 'should NOT be valid';
                $msg = "'$connectString' ($msg) $validity.";
                $this->assertEquals( $expected,
index 6990153..3319490 100644 (file)
@@ -70,21 +70,33 @@ class JobQueueTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provider_queueLists
+        * @covers JobQueue::getWiki
         */
-       function testProperties( $queue, $recycles, $desc ) {
+       public function testGetWiki( $queue, $recycles, $desc ) {
                $queue = $this->$queue;
                if ( !$queue ) {
                        $this->markTestSkipped( $desc );
                }
-
                $this->assertEquals( wfWikiID(), $queue->getWiki(), "Proper wiki ID ($desc)" );
+       }
+
+       /**
+        * @dataProvider provider_queueLists
+        * @covers JobQueue::getType
+        */
+       public function testGetType( $queue, $recycles, $desc ) {
+               $queue = $this->$queue;
+               if ( !$queue ) {
+                       $this->markTestSkipped( $desc );
+               }
                $this->assertEquals( 'null', $queue->getType(), "Proper job type ($desc)" );
        }
 
        /**
         * @dataProvider provider_queueLists
+        * @covers JobQueue
         */
-       function testBasicOperations( $queue, $recycles, $desc ) {
+       public function testBasicOperations( $queue, $recycles, $desc ) {
                $queue = $this->$queue;
                if ( !$queue ) {
                        $this->markTestSkipped( $desc );
@@ -157,8 +169,9 @@ class JobQueueTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provider_queueLists
+        * @covers JobQueue
         */
-       function testBasicDeduplication( $queue, $recycles, $desc ) {
+       public function testBasicDeduplication( $queue, $recycles, $desc ) {
                $queue = $this->$queue;
                if ( !$queue ) {
                        $this->markTestSkipped( $desc );
@@ -214,8 +227,9 @@ class JobQueueTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provider_queueLists
+        * @covers JobQueue
         */
-       function testRootDeduplication( $queue, $recycles, $desc ) {
+       public function testRootDeduplication( $queue, $recycles, $desc ) {
                $queue = $this->$queue;
                if ( !$queue ) {
                        $this->markTestSkipped( $desc );
@@ -267,8 +281,9 @@ class JobQueueTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provider_fifoQueueLists
+        * @covers JobQueue
         */
-       function testJobOrder( $queue, $recycles, $desc ) {
+       public function testJobOrder( $queue, $recycles, $desc ) {
                $queue = $this->$queue;
                if ( !$queue ) {
                        $this->markTestSkipped( $desc );
index eb024ab..8359f0d 100644 (file)
@@ -1,5 +1,8 @@
 <?php
 
+/**
+ * @covers FormatJson
+ */
 class FormatJsonTest extends MediaWikiTestCase {
 
        public function testEncoderPrettyPrinting() {
@@ -12,28 +15,30 @@ class FormatJsonTest extends MediaWikiTestCase {
                                        123,
                                        456,
                                ),
+                               // Nested json works without problems
                                '"7":["8",{"9":"10"}]',
+                               // Whitespace clean up doesn't touch strings that look alike
+                               "{\n\t\"emptyObject\": {\n\t},\n\t\"emptyArray\": [ ]\n}",
                        ),
                );
 
                // 4 space indent, no trailing whitespace, no trailing linefeed
                $json = '{
-    "emptyObject": {
-    },
-    "emptyArray": [
-    ],
+    "emptyObject": {},
+    "emptyArray": [],
     "string": "foobar\\\\",
     "filledArray": [
         [
             123,
             456
         ],
-        "\"7\":[\"8\",{\"9\":\"10\"}]"
+        "\"7\":[\"8\",{\"9\":\"10\"}]",
+        "{\n\t\"emptyObject\": {\n\t},\n\t\"emptyArray\": [ ]\n}"
     ]
 }';
 
                $json = str_replace( "\r", '', $json ); // Windows compat
-               $this->assertSame( $json, str_replace("\n\n", "\n", FormatJson::encode( $obj, true ) ));
+               $this->assertSame( $json, FormatJson::encode( $obj, true ) );
        }
 
        public static function provideEncodeDefault() {
index 1c898f0..5a3c161 100644 (file)
@@ -4,12 +4,14 @@
  * CSSJanus libary:
  * http://code.google.com/p/cssjanus/source/browse/trunk/cssjanus_test.py
  * Ported to PHP for ResourceLoader and has been extended since.
+ *
+ * @covers CSSJanus
  */
 class CSSJanusTest extends MediaWikiTestCase {
        /**
         * @dataProvider provideTransformCases
         */
-       function testTransform( $cssA, $cssB = null ) {
+       public function testTransform( $cssA, $cssB = null ) {
 
                if ( $cssB ) {
                        $transformedA = CSSJanus::transform( $cssA );
@@ -28,7 +30,7 @@ class CSSJanusTest extends MediaWikiTestCase {
        /**
         * @dataProvider provideTransformAdvancedCases
         */
-       function testTransformAdvanced( $code, $expectedOutput, $options = array() ) {
+       public function testTransformAdvanced( $code, $expectedOutput, $options = array() ) {
                $swapLtrRtlInURL = isset( $options['swapLtrRtlInURL'] ) ? $options['swapLtrRtlInURL'] : false;
                $swapLeftRightInURL = isset( $options['swapLeftRightInURL'] ) ? $options['swapLeftRightInURL'] : false;
 
@@ -44,7 +46,7 @@ class CSSJanusTest extends MediaWikiTestCase {
         * @dataProvider provideTransformBrokenCases
         * @group Broken
         */
-       function testTransformBroken( $code, $expectedOutput ) {
+       public function testTransformBroken( $code, $expectedOutput ) {
                $flipped = CSSJanus::transform( $code );
 
                $this->assertEquals( $expectedOutput, $flipped, 'Test flipping' );
index e9901ce..43df5eb 100644 (file)
@@ -20,8 +20,9 @@ class CSSMinTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provideMinifyCases
+        * @covers CSSMin::minify
         */
-       function testMinify( $code, $expectedOutput ) {
+       public function testMinify( $code, $expectedOutput ) {
                $minified = CSSMin::minify( $code );
 
                $this->assertEquals( $expectedOutput, $minified, 'Minified output should be in the form expected.' );
@@ -69,8 +70,9 @@ class CSSMinTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provideRemapCases
+        * @covers CSSMin::remap
         */
-       function testRemap( $message, $params, $expectedOutput ) {
+       public function testRemap( $message, $params, $expectedOutput ) {
                $remapped = call_user_func_array( 'CSSMin::remap', $params );
 
                $messageAdd = " Case: $message";
@@ -114,8 +116,9 @@ class CSSMinTest extends MediaWikiTestCase {
         *
         * @group Broken
         * @dataProvider provideStringCases
+        * @covers CSSMin::remap
         */
-       function testMinifyWithCSSStringValues( $code, $expectedOutput ) {
+       public function testMinifyWithCSSStringValues( $code, $expectedOutput ) {
                $this->testMinifyOutput( $code, $expectedOutput );
        }
 
index 7436c43..e2ec474 100644 (file)
@@ -83,6 +83,8 @@ abstract class GenericArrayObjectTest extends MediaWikiTestCase {
         * @since 1.20
         *
         * @param array $elements
+        *
+        * @covers GenericArrayObject::__construct
         */
        public function testConstructor( array $elements ) {
                $arrayObject = $this->getNew( $elements );
@@ -96,6 +98,8 @@ abstract class GenericArrayObjectTest extends MediaWikiTestCase {
         * @since 1.20
         *
         * @param array $elements
+        *
+        * @covers GenericArrayObject::isEmpty
         */
        public function testIsEmpty( array $elements ) {
                $arrayObject = $this->getNew( $elements );
@@ -109,6 +113,8 @@ abstract class GenericArrayObjectTest extends MediaWikiTestCase {
         * @since 1.20
         *
         * @param GenericArrayObject $list
+        *
+        * @covers GenericArrayObject::offsetUnset
         */
        public function testUnset( GenericArrayObject $list ) {
                if ( $list->isEmpty() ) {
@@ -134,6 +140,8 @@ abstract class GenericArrayObjectTest extends MediaWikiTestCase {
         * @since 1.20
         *
         * @param array $elements
+        *
+        * @covers GenericArrayObject::append
         */
        public function testAppend( array $elements ) {
                $list = $this->getNew();
@@ -163,6 +171,8 @@ abstract class GenericArrayObjectTest extends MediaWikiTestCase {
         * @since 1.20
         *
         * @param callback $function
+        *
+        * @covers GenericArrayObject::getObjectType
         */
        protected function checkTypeChecks( $function ) {
                $excption = null;
@@ -194,6 +204,8 @@ abstract class GenericArrayObjectTest extends MediaWikiTestCase {
         * @since 1.20
         *
         * @param array $elements
+        *
+        * @covers GenericArrayObject::offsetSet
         */
        public function testOffsetSet( array $elements ) {
                if ( $elements === array() ) {
@@ -247,6 +259,10 @@ abstract class GenericArrayObjectTest extends MediaWikiTestCase {
         * @since 1.21
         *
         * @param GenericArrayObject $list
+        *
+        * @covers GenericArrayObject::getSerializationData
+        * @covers GenericArrayObject::serialize
+        * @covers GenericArrayObject::unserialize
         */
        public function testSerialization( GenericArrayObject $list ) {
                $serialization = serialize( $list );
index d04dd7d..b707123 100644 (file)
@@ -2,9 +2,14 @@
 
 /**
  * Tests for IEUrlExtension::findIE6Extension
+ * @todo tests below for findIE6Extension should be split into...
+ *    ...a dataprovider and test method.
  */
 class IEUrlExtensionTest extends MediaWikiTestCase {
-       function testSimple() {
+       /**
+        * @covers IEUrlExtension::findIE6Extension
+        */
+       public function testSimple() {
                $this->assertEquals(
                        'y',
                        IEUrlExtension::findIE6Extension( 'x.y' ),
@@ -12,7 +17,10 @@ class IEUrlExtensionTest extends MediaWikiTestCase {
                );
        }
 
-       function testSimpleNoExt() {
+       /**
+        * @covers IEUrlExtension::findIE6Extension
+        */
+       public function testSimpleNoExt() {
                $this->assertEquals(
                        '',
                        IEUrlExtension::findIE6Extension( 'x' ),
@@ -20,7 +28,10 @@ class IEUrlExtensionTest extends MediaWikiTestCase {
                );
        }
 
-       function testEmpty() {
+       /**
+        * @covers IEUrlExtension::findIE6Extension
+        */
+       public function testEmpty() {
                $this->assertEquals(
                        '',
                        IEUrlExtension::findIE6Extension( '' ),
@@ -28,7 +39,10 @@ class IEUrlExtensionTest extends MediaWikiTestCase {
                );
        }
 
-       function testQuestionMark() {
+       /**
+        * @covers IEUrlExtension::findIE6Extension
+        */
+       public function testQuestionMark() {
                $this->assertEquals(
                        '',
                        IEUrlExtension::findIE6Extension( '?' ),
@@ -36,7 +50,10 @@ class IEUrlExtensionTest extends MediaWikiTestCase {
                );
        }
 
-       function testExtQuestionMark() {
+       /**
+        * @covers IEUrlExtension::findIE6Extension
+        */
+       public function testExtQuestionMark() {
                $this->assertEquals(
                        'x',
                        IEUrlExtension::findIE6Extension( '.x?' ),
@@ -44,7 +61,10 @@ class IEUrlExtensionTest extends MediaWikiTestCase {
                );
        }
 
-       function testQuestionMarkExt() {
+       /**
+        * @covers IEUrlExtension::findIE6Extension
+        */
+       public function testQuestionMarkExt() {
                $this->assertEquals(
                        'x',
                        IEUrlExtension::findIE6Extension( '?.x' ),
@@ -52,7 +72,10 @@ class IEUrlExtensionTest extends MediaWikiTestCase {
                );
        }
 
-       function testInvalidChar() {
+       /**
+        * @covers IEUrlExtension::findIE6Extension
+        */
+       public function testInvalidChar() {
                $this->assertEquals(
                        '',
                        IEUrlExtension::findIE6Extension( '.x*' ),
@@ -60,7 +83,10 @@ class IEUrlExtensionTest extends MediaWikiTestCase {
                );
        }
 
-       function testInvalidCharThenExtension() {
+       /**
+        * @covers IEUrlExtension::findIE6Extension
+        */
+       public function testInvalidCharThenExtension() {
                $this->assertEquals(
                        'x',
                        IEUrlExtension::findIE6Extension( '*.x' ),
@@ -68,7 +94,10 @@ class IEUrlExtensionTest extends MediaWikiTestCase {
                );
        }
 
-       function testMultipleQuestionMarks() {
+       /**
+        * @covers IEUrlExtension::findIE6Extension
+        */
+       public function testMultipleQuestionMarks() {
                $this->assertEquals(
                        'c',
                        IEUrlExtension::findIE6Extension( 'a?b?.c?.d?e?f' ),
@@ -76,7 +105,10 @@ class IEUrlExtensionTest extends MediaWikiTestCase {
                );
        }
 
-       function testExeException() {
+       /**
+        * @covers IEUrlExtension::findIE6Extension
+        */
+       public function testExeException() {
                $this->assertEquals(
                        'd',
                        IEUrlExtension::findIE6Extension( 'a?b?.exe?.d?.e' ),
@@ -84,7 +116,10 @@ class IEUrlExtensionTest extends MediaWikiTestCase {
                );
        }
 
-       function testExeException2() {
+       /**
+        * @covers IEUrlExtension::findIE6Extension
+        */
+       public function testExeException2() {
                $this->assertEquals(
                        'exe',
                        IEUrlExtension::findIE6Extension( 'a?b?.exe' ),
@@ -92,7 +127,10 @@ class IEUrlExtensionTest extends MediaWikiTestCase {
                );
        }
 
-       function testHash() {
+       /**
+        * @covers IEUrlExtension::findIE6Extension
+        */
+       public function testHash() {
                $this->assertEquals(
                        '',
                        IEUrlExtension::findIE6Extension( 'a#b.c' ),
@@ -100,7 +138,10 @@ class IEUrlExtensionTest extends MediaWikiTestCase {
                );
        }
 
-       function testHash2() {
+       /**
+        * @covers IEUrlExtension::findIE6Extension
+        */
+       public function testHash2() {
                $this->assertEquals(
                        '',
                        IEUrlExtension::findIE6Extension( 'a?#b.c' ),
@@ -108,7 +149,10 @@ class IEUrlExtensionTest extends MediaWikiTestCase {
                );
        }
 
-       function testDotAtEnd() {
+       /**
+        * @covers IEUrlExtension::findIE6Extension
+        */
+       public function testDotAtEnd() {
                $this->assertEquals(
                        '',
                        IEUrlExtension::findIE6Extension( '.' ),
@@ -116,7 +160,10 @@ class IEUrlExtensionTest extends MediaWikiTestCase {
                );
        }
 
-       function testTwoDots() {
+       /**
+        * @covers IEUrlExtension::findIE6Extension
+        */
+       public function testTwoDots() {
                $this->assertEquals(
                        'z',
                        IEUrlExtension::findIE6Extension( 'x.y.z' ),
index eb64a64..62ee45a 100644 (file)
@@ -118,8 +118,9 @@ class JavaScriptMinifierTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provideCases
+        * @covers JavaScriptMinifier::minify
         */
-       function testJavaScriptMinifierOutput( $code, $expectedOutput ) {
+       public function testJavaScriptMinifierOutput( $code, $expectedOutput ) {
                $minified = JavaScriptMinifier::minify( $code );
 
                // JSMin+'s parser will throw an exception if output is not valid JS.
@@ -152,8 +153,9 @@ class JavaScriptMinifierTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provideBug32548
+        * @covers JavaScriptMinifier::minify
         */
-       function testBug32548Exponent( $num ) {
+       public function testBug32548Exponent( $num ) {
                // Long line breaking was being incorrectly done between the base and
                // exponent part of a number, causing a syntax error. The line should
                // instead break at the start of the number.
index e8ccf43..9650fb1 100755 (executable)
@@ -61,6 +61,9 @@ class LogFormatterTest extends MediaWikiLangTestCase {
                return $logEntry;
        }
 
+       /**
+        * @covers LogFormatter::newFromEntry
+        */
        public function testNormalLogParams() {
                $entry = $this->newLogEntry( 'test', array() );
                $formatter = LogFormatter::newFromEntry( $entry );
@@ -99,6 +102,10 @@ class LogFormatterTest extends MediaWikiLangTestCase {
                $this->assertEquals( $titleLink, $paramsWithoutTools[2]['raw'] );
        }
 
+       /**
+        * @covers LogFormatter::newFromEntry
+        * @covers LogFormatter::getActionText
+        */
        public function testLogParamsTypeRaw() {
                $params = array( '4:raw:raw' => Linker::link( $this->title, null, array(), array() ) );
                $expected = Linker::link( $this->title, null, array(), array() );
@@ -112,6 +119,10 @@ class LogFormatterTest extends MediaWikiLangTestCase {
                $this->assertEquals( $expected, $logParam );
        }
 
+       /**
+        * @covers LogFormatter::newFromEntry
+        * @covers LogFormatter::getActionText
+        */
        public function testLogParamsTypeMsg() {
                $params = array( '4:msg:msg' => 'log-description-phpunit' );
                $expected = wfMessage( 'log-description-phpunit' )->text();
@@ -125,6 +136,10 @@ class LogFormatterTest extends MediaWikiLangTestCase {
                $this->assertEquals( $expected, $logParam );
        }
 
+       /**
+        * @covers LogFormatter::newFromEntry
+        * @covers LogFormatter::getActionText
+        */
        public function testLogParamsTypeMsgContent() {
                $params = array( '4:msg-content:msgContent' => 'log-description-phpunit' );
                $expected = wfMessage( 'log-description-phpunit' )->inContentLanguage()->text();
@@ -138,6 +153,10 @@ class LogFormatterTest extends MediaWikiLangTestCase {
                $this->assertEquals( $expected, $logParam );
        }
 
+       /**
+        * @covers LogFormatter::newFromEntry
+        * @covers LogFormatter::getActionText
+        */
        public function testLogParamsTypeNumber() {
                global $wgLang;
 
@@ -153,6 +172,10 @@ class LogFormatterTest extends MediaWikiLangTestCase {
                $this->assertEquals( $expected, $logParam );
        }
 
+       /**
+        * @covers LogFormatter::newFromEntry
+        * @covers LogFormatter::getActionText
+        */
        public function testLogParamsTypeUserLink() {
                $params = array( '4:user-link:userLink' => $this->user->getName() );
                $expected = Linker::userLink(
@@ -169,6 +192,10 @@ class LogFormatterTest extends MediaWikiLangTestCase {
                $this->assertEquals( $expected, $logParam );
        }
 
+       /**
+        * @covers LogFormatter::newFromEntry
+        * @covers LogFormatter::getActionText
+        */
        public function testLogParamsTypeTitleLink() {
                $params = array( '4:title-link:titleLink' => $this->title->getText() );
                $expected = Linker::link( $this->title, null, array(), array() );
@@ -182,6 +209,10 @@ class LogFormatterTest extends MediaWikiLangTestCase {
                $this->assertEquals( $expected, $logParam );
        }
 
+       /**
+        * @covers LogFormatter::newFromEntry
+        * @covers LogFormatter::getActionText
+        */
        public function testLogParamsTypePlain() {
                $params = array( '4:plain:plain' => 'Some plain text' );
                $expected = 'Some plain text';
@@ -195,6 +226,10 @@ class LogFormatterTest extends MediaWikiLangTestCase {
                $this->assertEquals( $expected, $logParam );
        }
 
+       /**
+        * @covers LogFormatter::newFromEntry
+        * @covers LogFormatter::getComment
+        */
        public function testLogComment() {
                $entry = $this->newLogEntry( 'test', array() );
                $formatter = LogFormatter::newFromEntry( $entry );
index 43792c1..a0e63a8 100644 (file)
@@ -16,6 +16,7 @@ class BitmapMetadataHandlerTest extends MediaWikiTestCase {
         * Basically the file has IPTC and XMP metadata, the
         * IPTC should override the XMP, except for the multilingual
         * translation (to en) where XMP should win.
+        * @covers BitmapMetadataHandler::Jpeg
         */
        public function testMultilingualCascade() {
                if ( !extension_loaded( 'exif' ) ) {
@@ -48,6 +49,7 @@ class BitmapMetadataHandlerTest extends MediaWikiTestCase {
         *
         * There's more extensive tests of comment extraction in
         * JpegMetadataExtractorTests.php
+        * @covers BitmapMetadataHandler::Jpeg
         */
        public function testJpegComment() {
                $meta = BitmapMetadataHandler::Jpeg( $this->filePath .
@@ -60,6 +62,7 @@ class BitmapMetadataHandlerTest extends MediaWikiTestCase {
        /**
         * Make sure a bad iptc block doesn't stop the other metadata
         * from being extracted.
+        * @covers BitmapMetadataHandler::Jpeg
         */
        public function testBadIPTC() {
                $meta = BitmapMetadataHandler::Jpeg( $this->filePath .
@@ -67,6 +70,9 @@ class BitmapMetadataHandlerTest extends MediaWikiTestCase {
                $this->assertEquals( 'Created with GIMP', $meta['JPEGFileComment'][0] );
        }
 
+       /**
+        * @covers BitmapMetadataHandler::Jpeg
+        */
        public function testIPTCDates() {
                $meta = BitmapMetadataHandler::Jpeg( $this->filePath .
                        'iptc-timetest.jpg' );
@@ -78,6 +84,7 @@ class BitmapMetadataHandlerTest extends MediaWikiTestCase {
        /**
         * File has an invalid time (+ one valid but really weird time)
         * that shouldn't be included
+        * @covers BitmapMetadataHandler::Jpeg
         */
        public function testIPTCDatesInvalid() {
                $meta = BitmapMetadataHandler::Jpeg( $this->filePath .
@@ -91,6 +98,8 @@ class BitmapMetadataHandlerTest extends MediaWikiTestCase {
         * XMP data should take priority over iptc data
         * when hash has been updated, but not when
         * the hash is wrong.
+        * @covers BitmapMetadataHandler::addMetadata
+        * @covers BitmapMetadataHandler::getMetadataArray
         */
        public function testMerging() {
                $merger = new BitmapMetadataHandler();
@@ -114,6 +123,9 @@ class BitmapMetadataHandlerTest extends MediaWikiTestCase {
                $this->assertEquals( $expected, $actual );
        }
 
+       /**
+        * @covers BitmapMetadataHandler::png
+        */
        public function testPNGXMP() {
                if ( !extension_loaded( 'xml' ) ) {
                        $this->markTestSkipped( "This test needs the xml extension." );
@@ -134,6 +146,9 @@ class BitmapMetadataHandlerTest extends MediaWikiTestCase {
                $this->assertEquals( $expected, $result );
        }
 
+       /**
+        * @covers BitmapMetadataHandler::png
+        */
        public function testPNGNative() {
                $handler = new BitmapMetadataHandler();
                $result = $handler->png( $this->filePath . 'Png-native-test.png' );
@@ -141,6 +156,9 @@ class BitmapMetadataHandlerTest extends MediaWikiTestCase {
                $this->assertEquals( $expected, $result['metadata']['Identifier']['x-default'] );
        }
 
+       /**
+        * @covers BitmapMetadataHandler::getTiffByteOrder
+        */
        public function testTiffByteOrder() {
                $handler = new BitmapMetadataHandler();
                $res = $handler->getTiffByteOrder( $this->filePath . 'test.tiff' );
index c4706bf..9395b66 100644 (file)
@@ -13,8 +13,9 @@ class BitmapScalingTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provideNormaliseParams
+        * @covers BitmapHandler::normaliseParams
         */
-       function testNormaliseParams( $fileDimensions, $expectedParams, $params, $msg ) {
+       public function testNormaliseParams( $fileDimensions, $expectedParams, $params, $msg ) {
                $file = new FakeDimensionFile( $fileDimensions );
                $handler = new BitmapHandler;
                $valid = $handler->normaliseParams( $file, $params );
@@ -102,7 +103,10 @@ class BitmapScalingTest extends MediaWikiTestCase {
                );
        }
 
-       function testTooBigImage() {
+       /**
+        * @covers BitmapHandler::doTransform
+        */
+       public function testTooBigImage() {
                $file = new FakeDimensionFile( array( 4000, 4000 ) );
                $handler = new BitmapHandler;
                $params = array( 'width' => '3700' ); // Still bigger than max size.
@@ -110,7 +114,10 @@ class BitmapScalingTest extends MediaWikiTestCase {
                        get_class( $handler->doTransform( $file, 'dummy path', '', $params ) ) );
        }
 
-       function testTooBigMustRenderImage() {
+       /**
+        * @covers BitmapHandler::doTransform
+        */
+       public function testTooBigMustRenderImage() {
                $file = new FakeDimensionFile( array( 4000, 4000 ) );
                $file->mustRender = true;
                $handler = new BitmapHandler;
@@ -119,36 +126,12 @@ class BitmapScalingTest extends MediaWikiTestCase {
                        get_class( $handler->doTransform( $file, 'dummy path', '', $params ) ) );
        }
 
-       function testImageArea() {
+       /**
+        * @covers BitmapHandler::getImageArea
+        */
+       public function testImageArea() {
                $file = new FakeDimensionFile( array( 7, 9 ) );
                $handler = new BitmapHandler;
                $this->assertEquals( 63, $handler->getImageArea( $file ) );
        }
 }
-
-class FakeDimensionFile extends File {
-       public $mustRender = false;
-
-       public function __construct( $dimensions ) {
-               parent::__construct( Title::makeTitle( NS_FILE, 'Test' ),
-                       new NullRepo( null ) );
-
-               $this->dimensions = $dimensions;
-       }
-
-       public function getWidth( $page = 1 ) {
-               return $this->dimensions[0];
-       }
-
-       public function getHeight( $page = 1 ) {
-               return $this->dimensions[1];
-       }
-
-       public function mustRender() {
-               return $this->mustRender;
-       }
-
-       public function getPath() {
-               return '';
-       }
-}
index 532182c..a2e0eb6 100644 (file)
@@ -2,6 +2,11 @@
 
 class ExifBitmapTest extends MediaWikiTestCase {
 
+       /**
+        * @var ExifBitmapHandler
+        */
+       protected $handler;
+
        protected function setUp() {
                parent::setUp();
                if ( !extension_loaded( 'exif' ) ) {
@@ -14,42 +19,62 @@ class ExifBitmapTest extends MediaWikiTestCase {
 
        }
 
+       /**
+        * @covers ExifBitmapHandler::isMetadataValid
+        */
        public function testIsOldBroken() {
                $res = $this->handler->isMetadataValid( null, ExifBitmapHandler::OLD_BROKEN_FILE );
                $this->assertEquals( ExifBitmapHandler::METADATA_COMPATIBLE, $res );
        }
 
+       /**
+        * @covers ExifBitmapHandler::isMetadataValid
+        */
        public function testIsBrokenFile() {
                $res = $this->handler->isMetadataValid( null, ExifBitmapHandler::BROKEN_FILE );
                $this->assertEquals( ExifBitmapHandler::METADATA_GOOD, $res );
        }
 
+       /**
+        * @covers ExifBitmapHandler::isMetadataValid
+        */
        public function testIsInvalid() {
                $res = $this->handler->isMetadataValid( null, 'Something Invalid Here.' );
                $this->assertEquals( ExifBitmapHandler::METADATA_BAD, $res );
        }
 
+       /**
+        * @covers ExifBitmapHandler::isMetadataValid
+        */
        public function testGoodMetadata() {
                $meta = 'a:16:{s:10:"ImageWidth";i:20;s:11:"ImageLength";i:20;s:13:"BitsPerSample";a:3:{i:0;i:8;i:1;i:8;i:2;i:8;}s:11:"Compression";i:5;s:25:"PhotometricInterpretation";i:2;s:16:"ImageDescription";s:17:"Created with GIMP";s:12:"StripOffsets";i:8;s:11:"Orientation";i:1;s:15:"SamplesPerPixel";i:3;s:12:"RowsPerStrip";i:64;s:15:"StripByteCounts";i:238;s:11:"XResolution";s:19:"1207959552/16777216";s:11:"YResolution";s:19:"1207959552/16777216";s:19:"PlanarConfiguration";i:1;s:14:"ResolutionUnit";i:2;s:22:"MEDIAWIKI_EXIF_VERSION";i:2;}';
                $res = $this->handler->isMetadataValid( null, $meta );
                $this->assertEquals( ExifBitmapHandler::METADATA_GOOD, $res );
        }
 
+       /**
+        * @covers ExifBitmapHandler::isMetadataValid
+        */
        public function testIsOldGood() {
                $meta = 'a:16:{s:10:"ImageWidth";i:20;s:11:"ImageLength";i:20;s:13:"BitsPerSample";a:3:{i:0;i:8;i:1;i:8;i:2;i:8;}s:11:"Compression";i:5;s:25:"PhotometricInterpretation";i:2;s:16:"ImageDescription";s:17:"Created with GIMP";s:12:"StripOffsets";i:8;s:11:"Orientation";i:1;s:15:"SamplesPerPixel";i:3;s:12:"RowsPerStrip";i:64;s:15:"StripByteCounts";i:238;s:11:"XResolution";s:19:"1207959552/16777216";s:11:"YResolution";s:19:"1207959552/16777216";s:19:"PlanarConfiguration";i:1;s:14:"ResolutionUnit";i:2;s:22:"MEDIAWIKI_EXIF_VERSION";i:1;}';
                $res = $this->handler->isMetadataValid( null, $meta );
                $this->assertEquals( ExifBitmapHandler::METADATA_COMPATIBLE, $res );
        }
 
-       // Handle metadata from paged tiff handler (gotten via instant commons)
-       // gracefully.
+       /**
+        * Handle metadata from paged tiff handler (gotten via instant commons) gracefully.
+        * @covers ExifBitmapHandler::isMetadataValid
+        */
        public function testPagedTiffHandledGracefully() {
                $meta = 'a:6:{s:9:"page_data";a:1:{i:1;a:5:{s:5:"width";i:643;s:6:"height";i:448;s:5:"alpha";s:4:"true";s:4:"page";i:1;s:6:"pixels";i:288064;}}s:10:"page_count";i:1;s:10:"first_page";i:1;s:9:"last_page";i:1;s:4:"exif";a:9:{s:10:"ImageWidth";i:643;s:11:"ImageLength";i:448;s:11:"Compression";i:5;s:25:"PhotometricInterpretation";i:2;s:11:"Orientation";i:1;s:15:"SamplesPerPixel";i:4;s:12:"RowsPerStrip";i:50;s:19:"PlanarConfiguration";i:1;s:22:"MEDIAWIKI_EXIF_VERSION";i:1;}s:21:"TIFF_METADATA_VERSION";s:3:"1.4";}';
                $res = $this->handler->isMetadataValid( null, $meta );
                $this->assertEquals( ExifBitmapHandler::METADATA_BAD, $res );
        }
 
-       function testConvertMetadataLatest() {
+       /**
+        * @covers ExifBitmapHandler::convertMetadataVersion
+        */
+       public function testConvertMetadataLatest() {
                $metadata = array(
                        'foo' => array( 'First', 'Second', '_type' => 'ol' ),
                        'MEDIAWIKI_EXIF_VERSION' => 2
@@ -58,7 +83,10 @@ class ExifBitmapTest extends MediaWikiTestCase {
                $this->assertEquals( $metadata, $res );
        }
 
-       function testConvertMetadataToOld() {
+       /**
+        * @covers ExifBitmapHandler::convertMetadataVersion
+        */
+       public function testConvertMetadataToOld() {
                $metadata = array(
                        'foo' => array( 'First', 'Second', '_type' => 'ol' ),
                        'bar' => array( 'First', 'Second', '_type' => 'ul' ),
@@ -77,7 +105,10 @@ class ExifBitmapTest extends MediaWikiTestCase {
                $this->assertEquals( $expected, $res );
        }
 
-       function testConvertMetadataSoftware() {
+       /**
+        * @covers ExifBitmapHandler::convertMetadataVersion
+        */
+       public function testConvertMetadataSoftware() {
                $metadata = array(
                        'Software' => array( array( 'GIMP', '1.1' ) ),
                        'MEDIAWIKI_EXIF_VERSION' => 2,
@@ -90,7 +121,10 @@ class ExifBitmapTest extends MediaWikiTestCase {
                $this->assertEquals( $expected, $res );
        }
 
-       function testConvertMetadataSoftwareNormal() {
+       /**
+        * @covers ExifBitmapHandler::convertMetadataVersion
+        */
+       public function testConvertMetadataSoftwareNormal() {
                $metadata = array(
                        'Software' => array( "GIMP 1.2", "vim" ),
                        'MEDIAWIKI_EXIF_VERSION' => 2,
index c16de8b..64276d9 100644 (file)
@@ -3,6 +3,8 @@
  * Tests related to auto rotation.
  *
  * @group medium
+ *
+ * @todo covers tags
  */
 class ExifRotationTest extends MediaWikiTestCase {
 
@@ -34,10 +36,9 @@ class ExifRotationTest extends MediaWikiTestCase {
        }
 
        /**
-        *
         * @dataProvider provideFiles
         */
-       function testMetadata( $name, $type, $info ) {
+       public function testMetadata( $name, $type, $info ) {
                if ( !BitmapHandler::canRotate() ) {
                        $this->markTestSkipped( "This test needs a rasterizer that can auto-rotate." );
                }
@@ -50,7 +51,7 @@ class ExifRotationTest extends MediaWikiTestCase {
         *
         * @dataProvider provideFiles
         */
-       function testRotationRendering( $name, $type, $info, $thumbs ) {
+       public function testRotationRendering( $name, $type, $info, $thumbs ) {
                if ( !BitmapHandler::canRotate() ) {
                        $this->markTestSkipped( "This test needs a rasterizer that can auto-rotate." );
                }
@@ -129,7 +130,7 @@ class ExifRotationTest extends MediaWikiTestCase {
         * Same as before, but with auto-rotation disabled.
         * @dataProvider provideFilesNoAutoRotate
         */
-       function testMetadataNoAutoRotate( $name, $type, $info ) {
+       public function testMetadataNoAutoRotate( $name, $type, $info ) {
                $this->setMwGlobals( 'wgEnableAutoRotation', false );
 
                $file = $this->dataFile( $name, $type );
@@ -141,7 +142,7 @@ class ExifRotationTest extends MediaWikiTestCase {
         *
         * @dataProvider provideFilesNoAutoRotate
         */
-       function testRotationRenderingNoAutoRotate( $name, $type, $info, $thumbs ) {
+       public function testRotationRenderingNoAutoRotate( $name, $type, $info, $thumbs ) {
                $this->setMwGlobals( 'wgEnableAutoRotation', false );
 
                foreach ( $thumbs as $size => $out ) {
@@ -216,7 +217,7 @@ class ExifRotationTest extends MediaWikiTestCase {
        /**
         * @dataProvider provideBitmapExtractPreRotationDimensions
         */
-       function testBitmapExtractPreRotationDimensions( $rotation, $expected ) {
+       public function testBitmapExtractPreRotationDimensions( $rotation, $expected ) {
                $result = $this->handler->extractPreRotationDimensions( array(
                        'physicalWidth' => self::TEST_WIDTH,
                        'physicalHeight' => self::TEST_HEIGHT,
index b84ed56..4cd2e8e 100644 (file)
@@ -1,6 +1,13 @@
 <?php
+
+/**
+ * @covers Exif
+ */
 class ExifTest extends MediaWikiTestCase {
 
+       /** @var string */
+       protected $mediaPath;
+
        protected function setUp() {
                parent::setUp();
                if ( !extension_loaded( 'exif' ) ) {
diff --git a/tests/phpunit/includes/media/FakeDimensionFile.php b/tests/phpunit/includes/media/FakeDimensionFile.php
new file mode 100644 (file)
index 0000000..7926000
--- /dev/null
@@ -0,0 +1,28 @@
+<?php
+
+class FakeDimensionFile extends File {
+       public $mustRender = false;
+
+       public function __construct( $dimensions ) {
+               parent::__construct( Title::makeTitle( NS_FILE, 'Test' ),
+                       new NullRepo( null ) );
+
+               $this->dimensions = $dimensions;
+       }
+
+       public function getWidth( $page = 1 ) {
+               return $this->dimensions[0];
+       }
+
+       public function getHeight( $page = 1 ) {
+               return $this->dimensions[1];
+       }
+
+       public function mustRender() {
+               return $this->mustRender;
+       }
+
+       public function getPath() {
+               return '';
+       }
+}
\ No newline at end of file
index bee0906..6ff928e 100644 (file)
@@ -1,6 +1,12 @@
 <?php
+
 class FormatMetadataTest extends MediaWikiTestCase {
 
+       /** @var FSFileBackend */
+       protected $backend;
+       /** @var FSRepo */
+       protected $repo;
+
        protected function setUp() {
                parent::setUp();
 
@@ -22,6 +28,9 @@ class FormatMetadataTest extends MediaWikiTestCase {
                $this->setMwGlobals( 'wgShowEXIF', true );
        }
 
+       /**
+        * @covers File::formatMetadata
+        */
        public function testInvalidDate() {
                $file = $this->dataFile( 'broken_exif_date.jpg', 'image/jpeg' );
 
@@ -43,6 +52,39 @@ class FormatMetadataTest extends MediaWikiTestCase {
                        'File with invalid date metadata (bug 29471)' );
        }
 
+       /**
+        * @param $filename String
+        * @param $expected Integer Total image area
+        * @dataProvider provideFlattenArray
+        * @covers FormatMetadata::flattenArray
+        */
+       public function testFlattenArray( $vals, $type, $noHtml, $ctx, $expected ) {
+               $actual = FormatMetadata::flattenArray( $vals, $type, $noHtml, $ctx );
+               $this->assertEquals( $expected, $actual );
+       }
+
+       public static function provideFlattenArray() {
+               return array(
+                       array (
+                               array(1 ,2 ,3), 'ul', false, false,
+                               "<ul><li>1</li>\n<li>2</li>\n<li>3</li></ul>",
+                       ),
+                       array (
+                               array(1 ,2 ,3), 'ol', false, false,
+                               "<ol><li>1</li>\n<li>2</li>\n<li>3</li></ol>",
+                       ),
+                       array (
+                               array(1 ,2 ,3), 'ul', true, false,
+                               "\n*1\n*2\n*3",
+                       ),
+                       array (
+                               array(1 ,2 ,3), 'ol', true, false,
+                               "\n#1\n#2\n#3",
+                       ),
+                       // TODO: more test cases
+               );
+       }
+
        private function dataFile( $name, $type ) {
                return new UnregisteredLocalFile( false, $this->repo,
                        "mwstore://localtesting/data/$name", $type );
index 86cf346..9e3f924 100644 (file)
@@ -12,6 +12,7 @@ class GIFMetadataExtractorTest extends MediaWikiTestCase {
         * @param $filename String
         * @param $expected Array The extracted metadata.
         * @dataProvider provideGetMetadata
+        * @covers GIFMetadataExtractor::getMetadata
         */
        public function testGetMetadata( $filename, $expected ) {
                $actual = GIFMetadataExtractor::getMetadata( $this->mediaPath . $filename );
index 7ea6b7e..4350cbb 100644 (file)
@@ -1,6 +1,15 @@
 <?php
 class GIFHandlerTest extends MediaWikiTestCase {
 
+       /** @var FSFileBackend */
+       protected $backend;
+       /** @var GIFHandler */
+       protected $handler;
+       /** @var FSRepo */
+       protected $repo;
+       /** @var string */
+       protected $filePath;
+
        protected function setUp() {
                parent::setUp();
 
@@ -18,6 +27,9 @@ class GIFHandlerTest extends MediaWikiTestCase {
                $this->handler = new GIFHandler();
        }
 
+       /**
+        * @covers GIFHandler::getMetadata
+        */
        public function testInvalidFile() {
                $res = $this->handler->getMetadata( null, $this->filePath . '/README' );
                $this->assertEquals( GIFHandler::BROKEN_FILE, $res );
@@ -27,6 +39,7 @@ class GIFHandlerTest extends MediaWikiTestCase {
         * @param $filename String basename of the file to check
         * @param $expected boolean Expected result.
         * @dataProvider provideIsAnimated
+        * @covers GIFHandler::isAnimatedImage
         */
        public function testIsAnimanted( $filename, $expected ) {
                $file = $this->dataFile( $filename, 'image/gif' );
@@ -45,6 +58,7 @@ class GIFHandlerTest extends MediaWikiTestCase {
         * @param $filename String
         * @param $expected Integer Total image area
         * @dataProvider provideGetImageArea
+        * @covers GIFHandler::getImageArea
         */
        public function testGetImageArea( $filename, $expected ) {
                $file = $this->dataFile( $filename, 'image/gif' );
@@ -63,6 +77,7 @@ class GIFHandlerTest extends MediaWikiTestCase {
         * @param $metadata String Serialized metadata
         * @param $expected Integer One of the class constants of GIFHandler
         * @dataProvider provideIsMetadataValid
+        * @covers GIFHandler::isMetadataValid
         */
        public function testIsMetadataValid( $metadata, $expected ) {
                $actual = $this->handler->isMetadataValid( null, $metadata );
@@ -83,6 +98,7 @@ class GIFHandlerTest extends MediaWikiTestCase {
         * @param $filename String
         * @param $expected String Serialized array
         * @dataProvider provideGetMetadata
+        * @covers GIFHandler::getMetadata
         */
        public function testGetMetadata( $filename, $expected ) {
                $file = $this->dataFile( $filename, 'image/gif' );
@@ -97,6 +113,42 @@ class GIFHandlerTest extends MediaWikiTestCase {
                );
        }
 
+       /**
+        * @param $filename String
+        * @param $expected String Serialized array
+        * @dataProvider provideGetIndependentMetaArray
+        * @covers GIFHandler::getCommonMetaArray
+        */
+       public function testGetIndependentMetaArray( $filename, $expected ) {
+               $file = $this->dataFile( $filename, 'image/gif' );
+               $actual = $this->handler->getCommonMetaArray( $file );
+               $this->assertEquals( $expected, $actual );
+       }
+
+       public function provideGetIndependentMetaArray() {
+               return array(
+                       array( 'nonanimated.gif', array(
+                               'GIFFileComment' => array(
+                                       'GIF test file ⁕ Created with GIMP',
+                               ),
+                       ) ),
+                       array( 'animated-xmp.gif',
+                               array(
+                                       'Artist' => 'Bawolff',
+                                       'ImageDescription' => array(
+                                               'x-default' => 'A file to test GIF',
+                                               '_type' => 'lang',
+                                       ),
+                                       'SublocationDest' => 'The interwebs',
+                                       'GIFFileComment' =>
+                                       array(
+                                               'GIƒ·test·file',
+                                       ),
+                               )
+                       ),
+               );
+       }
+
        private function dataFile( $name, $type ) {
                return new UnregisteredLocalFile( false, $this->repo,
                        "mwstore://localtesting/data/$name", $type );
index 81a58dd..81c1d28 100644 (file)
@@ -1,11 +1,19 @@
 <?php
+
 class IPTCTest extends MediaWikiTestCase {
+
+       /**
+        * @covers IPTC::getCharset
+        */
        public function testRecognizeUtf8() {
                // utf-8 is the only one used in practise.
                $res = IPTC::getCharset( "\x1b%G" );
                $this->assertEquals( 'UTF-8', $res );
        }
 
+       /**
+        * @covers IPTC::Parse
+        */
        public function testIPTCParseNoCharset88591() {
                // basically IPTC for keyword with value of 0xBC which is 1/4 in iso-8859-1
                // This data doesn't specify a charset. We're supposed to guess
@@ -15,17 +23,22 @@ class IPTCTest extends MediaWikiTestCase {
                $this->assertEquals( array( '¼' ), $res['Keywords'] );
        }
 
-       /* This one contains a sequence that's valid iso 8859-1 but not valid utf8 */
-       /* \xC3 = Ã, \xB8 = ¸  */
+       /**
+        * @covers IPTC::Parse
+        */
        public function testIPTCParseNoCharset88591b() {
+               /* This one contains a sequence that's valid iso 8859-1 but not valid utf8 */
+               /* \xC3 = Ã, \xB8 = ¸  */
                $iptcData = "Photoshop 3.0\08BIM\4\4\0\0\0\0\0\x09\x1c\x02\x19\x00\x04\xC3\xC3\xC3\xB8";
                $res = IPTC::Parse( $iptcData );
                $this->assertEquals( array( 'ÃÃø' ), $res['Keywords'] );
        }
 
-       /* Same as testIPTCParseNoCharset88591b, but forcing the charset to utf-8.
+       /**
+        * Same as testIPTCParseNoCharset88591b, but forcing the charset to utf-8.
         * What should happen is the first "\xC3\xC3" should be dropped as invalid,
         * leaving \xC3\xB8, which is ø
+        * @covers IPTC::Parse
         */
        public function testIPTCParseForcedUTFButInvalid() {
                $iptcData = "Photoshop 3.0\08BIM\4\4\0\0\0\0\0\x11\x1c\x02\x19\x00\x04\xC3\xC3\xC3\xB8"
@@ -34,13 +47,19 @@ class IPTCTest extends MediaWikiTestCase {
                $this->assertEquals( array( 'ø' ), $res['Keywords'] );
        }
 
+       /**
+        * @covers IPTC::Parse
+        */
        public function testIPTCParseNoCharsetUTF8() {
                $iptcData = "Photoshop 3.0\08BIM\4\4\0\0\0\0\0\x07\x1c\x02\x19\x00\x02¼";
                $res = IPTC::Parse( $iptcData );
                $this->assertEquals( array( '¼' ), $res['Keywords'] );
        }
 
-       // Testing something that has 2 values for keyword
+       /**
+        * Testing something that has 2 values for keyword
+        * @covers IPTC::Parse
+        */
        public function testIPTCParseMulti() {
                $iptcData = /* identifier */ "Photoshop 3.0\08BIM\4\4"
                        /* length */ . "\0\0\0\0\0\x0D"
@@ -50,6 +69,9 @@ class IPTCTest extends MediaWikiTestCase {
                $this->assertEquals( array( '¼', '¼½' ), $res['Keywords'] );
        }
 
+       /**
+        * @covers IPTC::Parse
+        */
        public function testIPTCParseUTF8() {
                // This has the magic "\x1c\x01\x5A\x00\x03\x1B\x25\x47" which marks content as UTF8.
                $iptcData = "Photoshop 3.0\08BIM\4\4\0\0\0\0\0\x0F\x1c\x02\x19\x00\x02¼\x1c\x01\x5A\x00\x03\x1B\x25\x47";
index cae7137..6d1d681 100644 (file)
@@ -5,9 +5,13 @@
  * serve as a very good "test". (Adobe photoshop probably creates such files
  * but it costs money). The implementation of it currently in MediaWiki is based
  * solely on reading the standard, without any real world test files.
+ *
+ * @covers JpegMetadataExtractor
  */
 class JpegMetadataExtractorTest extends MediaWikiTestCase {
 
+       protected $filePath;
+
        protected function setUp() {
                parent::setUp();
 
@@ -18,7 +22,7 @@ class JpegMetadataExtractorTest extends MediaWikiTestCase {
         * We also use this test to test padding bytes don't
         * screw stuff up
         *
-        * @param $file filename
+        * @param string $file filename
         *
         * @dataProvider provideUtf8Comment
         */
index 7775c41..5157228 100644 (file)
@@ -1,6 +1,11 @@
 <?php
+/**
+ * @covers JpegHandler
+ */
 class JpegTest extends MediaWikiTestCase {
 
+       protected $filePath;
+
        protected function setUp() {
                parent::setUp();
                if ( !extension_loaded( 'exif' ) ) {
@@ -11,20 +16,58 @@ class JpegTest extends MediaWikiTestCase {
 
 
                $this->setMwGlobals( 'wgShowEXIF', true );
+
+               $this->backend = new FSFileBackend( array(
+                       'name' => 'localtesting',
+                       'lockManager' => 'nullLockManager',
+                       'containerPaths' => array( 'data' => $this->filePath )
+               ) );
+               $this->repo = new FSRepo( array(
+                       'name' => 'temp',
+                       'url' => 'http://localhost/thumbtest',
+                       'backend' => $this->backend
+               ) );
+
+               $this->handler = new JpegHandler;
        }
 
        public function testInvalidFile() {
-               $jpeg = new JpegHandler;
-               $res = $jpeg->getMetadata( null, $this->filePath . 'README' );
+               $file = $this->dataFile( 'README', 'image/jpeg' );
+               $res = $this->handler->getMetadata( $file, $this->filePath . 'README' );
                $this->assertEquals( ExifBitmapHandler::BROKEN_FILE, $res );
        }
 
        public function testJpegMetadataExtraction() {
-               $h = new JpegHandler;
-               $res = $h->getMetadata( null, $this->filePath . 'test.jpg' );
+               $file = $this->dataFile( 'test.jpg', 'image/jpeg' );
+               $res = $this->handler->getMetadata( $file, $this->filePath . 'test.jpg' );
                $expected = 'a:7:{s:16:"ImageDescription";s:9:"Test file";s:11:"XResolution";s:4:"72/1";s:11:"YResolution";s:4:"72/1";s:14:"ResolutionUnit";i:2;s:16:"YCbCrPositioning";i:1;s:15:"JPEGFileComment";a:1:{i:0;s:17:"Created with GIMP";}s:22:"MEDIAWIKI_EXIF_VERSION";i:2;}';
 
                // Unserialize in case serialization format ever changes.
                $this->assertEquals( unserialize( $expected ), unserialize( $res ) );
        }
+
+       /**
+        * @covers JpegHandler::getCommonMetaArray
+        */
+       public function testGetIndependentMetaArray() {
+               $file = $this->dataFile( 'test.jpg', 'image/jpeg' );
+               $res = $this->handler->getCommonMetaArray( $file );
+               $expected = array(
+                       'ImageDescription' => 'Test file',
+                       'XResolution' => '72/1',
+                       'YResolution' => '72/1',
+                       'ResolutionUnit' => 2,
+                       'YCbCrPositioning' => 1,
+                       'JPEGFileComment' => array(
+                               'Created with GIMP',
+                       ),
+               );
+
+               $this->assertEquals( $res, $expected );
+       }
+
+       private function dataFile( $name, $type ) {
+               return new UnregisteredLocalFile( false, $this->repo,
+                       "mwstore://localtesting/data/$name", $type );
+       }
 }
index 4e4c649..c28898b 100644 (file)
@@ -1,7 +1,12 @@
 <?php
 
 class MediaHandlerTest extends MediaWikiTestCase {
-       function testFitBoxWidth() {
+
+       /**
+        * @covers MediaHandler::fitBoxWidth
+        * @todo split into a dataprovider and test method
+        */
+       public function testFitBoxWidth() {
                $vals = array(
                        array(
                                'width' => 50,
index 58d791f..33a03a2 100644 (file)
@@ -1,4 +1,8 @@
 <?php
+
+/**
+ * @covers PNGMetadataExtractor
+ */
 class PNGMetadataExtractorTest extends MediaWikiTestCase {
 
        protected function setUp() {
@@ -9,7 +13,7 @@ class PNGMetadataExtractorTest extends MediaWikiTestCase {
        /**
         * Tests zTXt tag (compressed textual metadata)
         */
-       function testPngNativetZtxt() {
+       public function testPngNativetZtxt() {
                $this->checkPHPExtension( 'zlib' );
 
                $meta = PNGMetadataExtractor::getMetadata( $this->filePath .
@@ -26,7 +30,7 @@ class PNGMetadataExtractorTest extends MediaWikiTestCase {
        /**
         * Test tEXt tag (Uncompressed textual metadata)
         */
-       function testPngNativeText() {
+       public function testPngNativeText() {
                $meta = PNGMetadataExtractor::getMetadata( $this->filePath .
                        'Png-native-test.png' );
                $expected = "Some long image desc";
@@ -43,7 +47,7 @@ class PNGMetadataExtractorTest extends MediaWikiTestCase {
         * tEXt tags must be encoded iso-8859-1 (vs iTXt which are utf-8)
         * Make sure non-ascii characters get converted properly
         */
-       function testPngNativeTextNonAscii() {
+       public function testPngNativeTextNonAscii() {
                $meta = PNGMetadataExtractor::getMetadata( $this->filePath .
                        'Png-native-test.png' );
 
@@ -65,7 +69,7 @@ class PNGMetadataExtractorTest extends MediaWikiTestCase {
         * actual resolution of the image is (aka in dots per meter).
         */
        /*
-       function testPngPhysTag() {
+       public function testPngPhysTag() {
                $meta = PNGMetadataExtractor::getMetadata( $this->filePath .
                        'Png-native-test.png' );
 
@@ -81,7 +85,7 @@ class PNGMetadataExtractorTest extends MediaWikiTestCase {
        /**
         * Given a normal static PNG, check the animation metadata returned.
         */
-       function testStaticPngAnimationMetadata() {
+       public function testStaticPngAnimationMetadata() {
                $meta = PNGMetadataExtractor::getMetadata( $this->filePath .
                        'Png-native-test.png' );
 
@@ -94,7 +98,7 @@ class PNGMetadataExtractorTest extends MediaWikiTestCase {
         * Given an animated APNG image file
         * check it gets animated metadata right.
         */
-       function testApngAnimationMetadata() {
+       public function testApngAnimationMetadata() {
                $meta = PNGMetadataExtractor::getMetadata( $this->filePath .
                        'Animated_PNG_example_bouncing_beach_ball.png' );
 
@@ -104,46 +108,46 @@ class PNGMetadataExtractorTest extends MediaWikiTestCase {
                $this->assertEquals( 1.5, $meta['duration'], '', 0.00001 );
        }
 
-       function testPngBitDepth8() {
+       public function testPngBitDepth8() {
                $meta = PNGMetadataExtractor::getMetadata( $this->filePath .
                        'Png-native-test.png' );
 
                $this->assertEquals( 8, $meta['bitDepth'] );
        }
 
-       function testPngBitDepth1() {
+       public function testPngBitDepth1() {
                $meta = PNGMetadataExtractor::getMetadata( $this->filePath .
                        '1bit-png.png' );
                $this->assertEquals( 1, $meta['bitDepth'] );
        }
 
 
-       function testPngIndexColour() {
+       public function testPngIndexColour() {
                $meta = PNGMetadataExtractor::getMetadata( $this->filePath .
                        'Png-native-test.png' );
 
                $this->assertEquals( 'index-coloured', $meta['colorType'] );
        }
 
-       function testPngRgbColour() {
+       public function testPngRgbColour() {
                $meta = PNGMetadataExtractor::getMetadata( $this->filePath .
                        'rgb-png.png' );
                $this->assertEquals( 'truecolour-alpha', $meta['colorType'] );
        }
 
-       function testPngRgbNoAlphaColour() {
+       public function testPngRgbNoAlphaColour() {
                $meta = PNGMetadataExtractor::getMetadata( $this->filePath .
                        'rgb-na-png.png' );
                $this->assertEquals( 'truecolour', $meta['colorType'] );
        }
 
-       function testPngGreyscaleColour() {
+       public function testPngGreyscaleColour() {
                $meta = PNGMetadataExtractor::getMetadata( $this->filePath .
                        'greyscale-png.png' );
                $this->assertEquals( 'greyscale-alpha', $meta['colorType'] );
        }
 
-       function testPngGreyscaleNoAlphaColour() {
+       public function testPngGreyscaleNoAlphaColour() {
                $meta = PNGMetadataExtractor::getMetadata( $this->filePath .
                        'greyscale-na-png.png' );
                $this->assertEquals( 'greyscale', $meta['colorType'] );
index 855780d..2cb7426 100644 (file)
@@ -1,6 +1,15 @@
 <?php
 class PNGHandlerTest extends MediaWikiTestCase {
 
+       /** @var PNGHandler */
+       protected $handler;
+       /** @var FSRepo */
+       protected $repo;
+       /** @var FSFileBackend */
+       protected $backend;
+       /** @var string */
+       protected $filePath;
+
        protected function setUp() {
                parent::setUp();
 
@@ -18,6 +27,9 @@ class PNGHandlerTest extends MediaWikiTestCase {
                $this->handler = new PNGHandler();
        }
 
+       /**
+        * @covers PNGHandler::getMetadata
+        */
        public function testInvalidFile() {
                $res = $this->handler->getMetadata( null, $this->filePath . '/README' );
                $this->assertEquals( PNGHandler::BROKEN_FILE, $res );
@@ -27,6 +39,7 @@ class PNGHandlerTest extends MediaWikiTestCase {
         * @param $filename String basename of the file to check
         * @param $expected boolean Expected result.
         * @dataProvider provideIsAnimated
+        * @covers PNGHandler::isAnimatedImage
         */
        public function testIsAnimanted( $filename, $expected ) {
                $file = $this->dataFile( $filename, 'image/png' );
@@ -45,6 +58,7 @@ class PNGHandlerTest extends MediaWikiTestCase {
         * @param $filename String
         * @param $expected Integer Total image area
         * @dataProvider provideGetImageArea
+        * @covers PNGHandler::getImageArea
         */
        public function testGetImageArea( $filename, $expected ) {
                $file = $this->dataFile( $filename, 'image/png' );
@@ -65,6 +79,7 @@ class PNGHandlerTest extends MediaWikiTestCase {
         * @param $metadata String Serialized metadata
         * @param $expected Integer One of the class constants of PNGHandler
         * @dataProvider provideIsMetadataValid
+        * @covers PNGHandler::isMetadataValid
         */
        public function testIsMetadataValid( $metadata, $expected ) {
                $actual = $this->handler->isMetadataValid( null, $metadata );
@@ -85,6 +100,7 @@ class PNGHandlerTest extends MediaWikiTestCase {
         * @param $filename String
         * @param $expected String Serialized array
         * @dataProvider provideGetMetadata
+        * @covers PNGHandler::getMetadata
         */
        public function testGetMetadata( $filename, $expected ) {
                $file = $this->dataFile( $filename, 'image/png' );
@@ -100,6 +116,29 @@ class PNGHandlerTest extends MediaWikiTestCase {
                );
        }
 
+       /**
+        * @param $filename String
+        * @param $expected Array Expected standard metadata
+        * @dataProvider provideGetIndependentMetaArray
+        * @covers PNGHandler::getCommonMetaArray
+        */
+       public function testGetIndependentMetaArray( $filename, $expected ) {
+               $file = $this->dataFile( $filename, 'image/png' );
+               $actual = $this->handler->getCommonMetaArray( $file );
+               $this->assertEquals( $expected, $actual );
+       }
+
+       public function provideGetIndependentMetaArray() {
+               return array(
+                       array( 'rgb-na-png.png', array() ),
+                       array( 'xmp.png',
+                               array(
+                                       'SerialNumber' => '123456789',
+                               )
+                       ),
+               );
+       }
+
        private function dataFile( $name, $type ) {
                return new UnregisteredLocalFile( false, $this->repo,
                        "mwstore://localtesting/data/$name", $type );
index 3bf9c59..d00a33d 100644 (file)
@@ -1,5 +1,8 @@
 <?php
 
+/**
+ * @covers SVGMetadataExtractor
+ */
 class SVGMetadataExtractorTest extends MediaWikiTestCase {
 
        protected function setUp() {
@@ -10,14 +13,14 @@ class SVGMetadataExtractorTest extends MediaWikiTestCase {
        /**
         * @dataProvider provideSvgFiles
         */
-       function testGetMetadata( $infile, $expected ) {
+       public function testGetMetadata( $infile, $expected ) {
                $this->assertMetadata( $infile, $expected );
        }
 
        /**
         * @dataProvider provideSvgFilesWithXMLMetadata
         */
-       function testGetXMLMetadata( $infile, $expected ) {
+       public function testGetXMLMetadata( $infile, $expected ) {
                $r = new XMLReader();
                if ( !method_exists( $r, 'readInnerXML' ) ) {
                        $this->markTestSkipped( 'XMLReader::readInnerXML() does not exist (libxml >2.6.20 needed).' );
@@ -80,6 +83,17 @@ class SVGMetadataExtractorTest extends MediaWikiTestCase {
                                        'originalWidth' => '385',
                                        'originalHeight' => '385.0004883',
                                )
+                       ),
+                       array(
+                               "$base/Tux.svg",
+                               array(
+                                       'width' => 512,
+                                       'height' => 594,
+                                       'originalWidth' => '100%',
+                                       'originalHeight' => '100%',
+                                       'title' => 'Tux',
+                                       'description' => 'For more information see: http://commons.wikimedia.org/wiki/Image:Tux.svg',
+                               )
                        )
                );
        }
diff --git a/tests/phpunit/includes/media/SVGTest.php b/tests/phpunit/includes/media/SVGTest.php
new file mode 100644 (file)
index 0000000..b28ee56
--- /dev/null
@@ -0,0 +1,52 @@
+<?php
+class SVGTest extends MediaWikiTestCase {
+
+       protected function setUp() {
+               parent::setUp();
+
+               $this->filePath = __DIR__ . '/../../data/media/';
+
+               $this->setMwGlobals( 'wgShowEXIF', true );
+
+               $this->backend = new FSFileBackend( array(
+                       'name' => 'localtesting',
+                       'lockManager' => 'nullLockManager',
+                       'containerPaths' => array( 'data' => $this->filePath )
+               ) );
+               $this->repo = new FSRepo( array(
+                       'name' => 'temp',
+                       'url' => 'http://localhost/thumbtest',
+                       'backend' => $this->backend
+               ) );
+
+               $this->handler = new SVGHandler;
+       }
+
+       /**
+        * @param $filename String
+        * @param $expected Array The expected independent metadata
+        * @dataProvider providerGetIndependentMetaArray
+        * @covers SvgHandler::getCommonMetaArray
+        */
+       public function testGetIndependentMetaArray( $filename, $expected ) {
+               $file = $this->dataFile( $filename, 'image/svg+xml' );
+               $res = $this->handler->getCommonMetaArray( $file );
+
+               $this->assertEquals( $res, $expected );
+       }
+
+       public function providerGetIndependentMetaArray() {
+               return array(
+                       array( 'Tux.svg', array(
+                               'ObjectName' => 'Tux',
+                               'ImageDescription' => 'For more information see: http://commons.wikimedia.org/wiki/Image:Tux.svg',
+                       ) ),
+                       array( 'Wikimedia-logo.svg', array() )
+               );
+       }
+
+       private function dataFile( $name, $type ) {
+               return new UnregisteredLocalFile( false, $this->repo,
+                       "mwstore://localtesting/data/$name", $type );
+       }
+}
index 1ec34d5..8d74b98 100644 (file)
@@ -1,6 +1,11 @@
 <?php
 class TiffTest extends MediaWikiTestCase {
 
+       /** @var TiffHandler */
+       protected $handler;
+       /** @var string */
+       protected $filePath;
+
        protected function setUp() {
                parent::setUp();
                if ( !extension_loaded( 'exif' ) ) {
@@ -13,11 +18,17 @@ class TiffTest extends MediaWikiTestCase {
                $this->handler = new TiffHandler;
        }
 
+       /**
+        * @covers TiffHandler::getMetadata
+        */
        public function testInvalidFile() {
                $res = $this->handler->getMetadata( null, $this->filePath . 'README' );
                $this->assertEquals( ExifBitmapHandler::BROKEN_FILE, $res );
        }
 
+       /**
+        * @covers TiffHandler::getMetadata
+        */
        public function testTiffMetadataExtraction() {
                $res = $this->handler->getMetadata( null, $this->filePath . 'test.tiff' );
                $expected = 'a:16:{s:10:"ImageWidth";i:20;s:11:"ImageLength";i:20;s:13:"BitsPerSample";a:3:{i:0;i:8;i:1;i:8;i:2;i:8;}s:11:"Compression";i:5;s:25:"PhotometricInterpretation";i:2;s:16:"ImageDescription";s:17:"Created with GIMP";s:12:"StripOffsets";i:8;s:11:"Orientation";i:1;s:15:"SamplesPerPixel";i:3;s:12:"RowsPerStrip";i:64;s:15:"StripByteCounts";i:238;s:11:"XResolution";s:19:"1207959552/16777216";s:11:"YResolution";s:19:"1207959552/16777216";s:19:"PlanarConfiguration";i:1;s:14:"ResolutionUnit";i:2;s:22:"MEDIAWIKI_EXIF_VERSION";i:2;}';
index aa0cdd2..9ec5796 100644 (file)
@@ -1,4 +1,8 @@
 <?php
+
+/**
+ * @todo covers tags
+ */
 class XMPTest extends MediaWikiTestCase {
 
        protected function setUp() {
@@ -15,7 +19,10 @@ class XMPTest extends MediaWikiTestCase {
         * @param $expected Array expected result of parsing the xmp.
         * @param $info String Short sentence on what's being tested.
         *
+        * @throws Exception
         * @dataProvider provideXMPParse
+        *
+        * @covers XMPReader::parse
         */
        public function testXMPParse( $xmp, $expected, $info ) {
                if ( !is_string( $xmp ) || !is_array( $expected ) ) {
@@ -74,8 +81,10 @@ class XMPTest extends MediaWikiTestCase {
         *
         * @todo This is based on what the standard says. Need to find a real
         * world example file to double check the support for this is right.
+        *
+        * @covers XMPReader::parseExtended
         */
-       function testExtendedXMP() {
+       public function testExtendedXMP() {
                $xmpPath = __DIR__ . '/../../data/xmp/';
                $standardXMP = file_get_contents( $xmpPath . 'xmpExt.xmp' );
                $extendedXMP = file_get_contents( $xmpPath . 'xmpExt2.xmp' );
@@ -104,8 +113,10 @@ class XMPTest extends MediaWikiTestCase {
        /**
         * This test has an extended XMP block with a wrong guid (md5sum)
         * and thus should only return the StandardXMP, not the ExtendedXMP.
+        *
+        * @covers XMPReader::parseExtended
         */
-       function testExtendedXMPWithWrongGUID() {
+       public function testExtendedXMPWithWrongGUID() {
                $xmpPath = __DIR__ . '/../../data/xmp/';
                $standardXMP = file_get_contents( $xmpPath . 'xmpExt.xmp' );
                $extendedXMP = file_get_contents( $xmpPath . 'xmpExt2.xmp' );
@@ -133,8 +144,10 @@ class XMPTest extends MediaWikiTestCase {
        /**
         * Have a high offset to simulate a missing packet,
         * which should cause it to ignore the ExtendedXMP packet.
+        *
+        * @covers XMPReader::parseExtended
         */
-       function testExtendedXMPMissingPacket() {
+       public function testExtendedXMPMissingPacket() {
                $xmpPath = __DIR__ . '/../../data/xmp/';
                $standardXMP = file_get_contents( $xmpPath . 'xmpExt.xmp' );
                $extendedXMP = file_get_contents( $xmpPath . 'xmpExt2.xmp' );
index 257c40a..96bf5e4 100644 (file)
@@ -3,8 +3,9 @@ class XMPValidateTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provideDates
+        * @covers XMPValidate::validateDate
         */
-       function testValidateDate( $value, $expected ) {
+       public function testValidateDate( $value, $expected ) {
                // The method should modify $value.
                XMPValidate::validateDate( array(), $value, true );
                $this->assertEquals( $expected, $value );
index 68efd86..a495700 100644 (file)
  * @ingroup UtfNormal
  * @group Large
  *
+ * @todo covers tags, will be UtfNormal::cleanUp once the below is resolved
+ * @todo split me into test methods and providers per the below comment
+ *
  * We ignore code coverage for this test suite until they are rewritten
  * to use data providers (bug 46561).
  * @codeCoverageIgnore
  */
 class CleanUpTest extends MediaWikiTestCase {
        /** @todo document */
-       function testAscii() {
+       public function testAscii() {
                $text = 'This is plain ASCII text.';
                $this->assertEquals( $text, UtfNormal::cleanUp( $text ) );
        }
 
        /** @todo document */
-       function testNull() {
+       public function testNull() {
                $text = "a \x00 null";
                $expect = "a \xef\xbf\xbd null";
                $this->assertEquals(
@@ -52,13 +55,13 @@ class CleanUpTest extends MediaWikiTestCase {
        }
 
        /** @todo document */
-       function testLatin() {
+       public function testLatin() {
                $text = "L'\xc3\xa9cole";
                $this->assertEquals( $text, UtfNormal::cleanUp( $text ) );
        }
 
        /** @todo document */
-       function testLatinNormal() {
+       public function testLatinNormal() {
                $text = "L'e\xcc\x81cole";
                $expect = "L'\xc3\xa9cole";
                $this->assertEquals( $expect, UtfNormal::cleanUp( $text ) );
@@ -105,7 +108,7 @@ class CleanUpTest extends MediaWikiTestCase {
        }
 
        /** @todo document */
-       function testAllBytes() {
+       public function testAllBytes() {
                $this->doTestBytes( '', '' );
                $this->doTestBytes( 'x', '' );
                $this->doTestBytes( '', 'x' );
@@ -145,7 +148,7 @@ class CleanUpTest extends MediaWikiTestCase {
        }
 
        /** @todo document */
-       function testDoubleBytes() {
+       public function testDoubleBytes() {
                $this->doTestDoubleBytes( '', '' );
                $this->doTestDoubleBytes( 'x', '' );
                $this->doTestDoubleBytes( '', 'x' );
@@ -198,7 +201,7 @@ class CleanUpTest extends MediaWikiTestCase {
        }
 
        /** @todo document */
-       function testTripleBytes() {
+       public function testTripleBytes() {
                $this->doTestTripleBytes( '', '' );
                $this->doTestTripleBytes( 'x', '' );
                $this->doTestTripleBytes( '', 'x' );
@@ -276,7 +279,7 @@ class CleanUpTest extends MediaWikiTestCase {
        }
 
        /** @todo document */
-       function testChunkRegression() {
+       public function testChunkRegression() {
                # Check for regression against a chunking bug
                $text = "\x46\x55\xb8" .
                        "\xdc\x96" .
@@ -299,7 +302,7 @@ class CleanUpTest extends MediaWikiTestCase {
        }
 
        /** @todo document */
-       function testInterposeRegression() {
+       public function testInterposeRegression() {
                $text = "\x4e\x30" .
                        "\xb1" . # bad tail
                        "\x3a" .
@@ -334,7 +337,7 @@ class CleanUpTest extends MediaWikiTestCase {
        }
 
        /** @todo document */
-       function testOverlongRegression() {
+       public function testOverlongRegression() {
                $text = "\x67" .
                        "\x1a" . # forbidden ascii
                        "\xea" . # bad head
@@ -359,7 +362,7 @@ class CleanUpTest extends MediaWikiTestCase {
        }
 
        /** @todo document */
-       function testSurrogateRegression() {
+       public function testSurrogateRegression() {
                $text = "\xed\xb4\x96" . # surrogate 0xDD16
                        "\x83" . # bad tail
                        "\xb4" . # bad tail
@@ -374,7 +377,7 @@ class CleanUpTest extends MediaWikiTestCase {
        }
 
        /** @todo document */
-       function testBomRegression() {
+       public function testBomRegression() {
                $text = "\xef\xbf\xbe" . # U+FFFE, illegal char
                        "\xb2" . # bad tail
                        "\xef" . # bad head
@@ -389,7 +392,7 @@ class CleanUpTest extends MediaWikiTestCase {
        }
 
        /** @todo document */
-       function testForbiddenRegression() {
+       public function testForbiddenRegression() {
                $text = "\xef\xbf\xbf"; # U+FFFF, illegal char
                $expect = "\xef\xbf\xbd";
                $this->assertEquals(
@@ -398,7 +401,7 @@ class CleanUpTest extends MediaWikiTestCase {
        }
 
        /** @todo document */
-       function testHangulRegression() {
+       public function testHangulRegression() {
                $text = "\xed\x9c\xaf" . # Hangul char
                        "\xe1\x87\x81"; # followed by another final jamo
                $expect = $text; # Should *not* change.
index be603e5..aa78394 100644 (file)
@@ -118,6 +118,18 @@ class BagOStuffTest extends MediaWikiTestCase {
                $this->assertEquals( $this->cache->get( $key ), $value );
        }
 
+       /**
+        * @covers BagOStuff::incr
+        */
+       public function testIncr() {
+               $key = wfMemcKey( 'test' );
+               $this->cache->add( $key, 0 );
+               $this->cache->incr( $key );
+               $expectedValue = 1;
+               $actualValue = $this->cache->get( $key );
+               $this->assertEquals( $expectedValue, $actualValue, 'Value should be 1 after incrementing' );
+       }
+
        public function testGetMulti() {
                $value1 = array( 'this' => 'is', 'a' => 'test' );
                $value2 = array( 'this' => 'is', 'another' => 'test' );
index 263df5f..c2c97c0 100644 (file)
@@ -9,11 +9,13 @@
  * @author Antoine Musso
  * @copyright Copyright © 2011, Antoine Musso
  * @file
+ * @todo covers tags
  */
 
-/** */
 class MagicVariableTest extends MediaWikiTestCase {
-       /** Will contains a parser object*/
+       /**
+        * @var Parser
+        */
        private $testParser = null;
 
        /**
@@ -51,11 +53,31 @@ class MagicVariableTest extends MediaWikiTestCase {
                $this->testParser->setTitle( $title );
        }
 
-       /** destroy parser (TODO: is it really neded?)*/
-       protected function tearDown() {
-               unset( $this->testParser );
+       /**
+        * @param int $num upper limit for numbers
+        * @return array of numbers from 1 up to $num
+        */
+       private static function createProviderUpTo( $num ) {
+               $ret = array();
+               for ( $i = 1; $i <= $num; $i++ ) {
+                       $ret[] = array( $i );
+               }
+
+               return $ret;
+       }
+
+       /**
+        * @return array of months numbers (as an integer)
+        */
+       public static function provideMonths() {
+               return self::createProviderUpTo( 12 );
+       }
 
-               parent::tearDown();
+       /**
+        * @return array of days numbers (as an integer)
+        */
+       public static function provideDays() {
+               return self::createProviderUpTo( 31 );
        }
 
        ############### TESTS #############################################
@@ -65,70 +87,69 @@ class MagicVariableTest extends MediaWikiTestCase {
 
        # day
 
-       /** @dataProvider MediaWikiProvide::Days */
-       function testCurrentdayIsUnPadded( $day ) {
+       /** @dataProvider provideDays */
+       public function testCurrentdayIsUnPadded( $day ) {
                $this->assertUnPadded( 'currentday', $day );
        }
 
-       /** @dataProvider MediaWikiProvide::Days */
-       function testCurrentdaytwoIsZeroPadded( $day ) {
+       /** @dataProvider provideDays */
+       public function testCurrentdaytwoIsZeroPadded( $day ) {
                $this->assertZeroPadded( 'currentday2', $day );
        }
 
-       /** @dataProvider MediaWikiProvide::Days */
-       function testLocaldayIsUnPadded( $day ) {
+       /** @dataProvider provideDays */
+       public function testLocaldayIsUnPadded( $day ) {
                $this->assertUnPadded( 'localday', $day );
        }
 
-       /** @dataProvider MediaWikiProvide::Days */
-       function testLocaldaytwoIsZeroPadded( $day ) {
+       /** @dataProvider provideDays */
+       public function testLocaldaytwoIsZeroPadded( $day ) {
                $this->assertZeroPadded( 'localday2', $day );
        }
 
        # month
 
-       /** @dataProvider MediaWikiProvide::Months */
-       function testCurrentmonthIsZeroPadded( $month ) {
+       /** @dataProvider provideMonths */
+       public function testCurrentmonthIsZeroPadded( $month ) {
                $this->assertZeroPadded( 'currentmonth', $month );
        }
 
-       /** @dataProvider MediaWikiProvide::Months */
-       function testCurrentmonthoneIsUnPadded( $month ) {
+       /** @dataProvider provideMonths */
+       public function testCurrentmonthoneIsUnPadded( $month ) {
                $this->assertUnPadded( 'currentmonth1', $month );
        }
 
-       /** @dataProvider MediaWikiProvide::Months */
-       function testLocalmonthIsZeroPadded( $month ) {
+       /** @dataProvider provideMonths */
+       public function testLocalmonthIsZeroPadded( $month ) {
                $this->assertZeroPadded( 'localmonth', $month );
        }
 
-       /** @dataProvider MediaWikiProvide::Months */
-       function testLocalmonthoneIsUnPadded( $month ) {
+       /** @dataProvider provideMonths */
+       public function testLocalmonthoneIsUnPadded( $month ) {
                $this->assertUnPadded( 'localmonth1', $month );
        }
 
-
        # revision day
 
-       /** @dataProvider MediaWikiProvide::Days */
-       function testRevisiondayIsUnPadded( $day ) {
+       /** @dataProvider provideDays */
+       public function testRevisiondayIsUnPadded( $day ) {
                $this->assertUnPadded( 'revisionday', $day );
        }
 
-       /** @dataProvider MediaWikiProvide::Days */
-       function testRevisiondaytwoIsZeroPadded( $day ) {
+       /** @dataProvider provideDays */
+       public function testRevisiondaytwoIsZeroPadded( $day ) {
                $this->assertZeroPadded( 'revisionday2', $day );
        }
 
        # revision month
 
-       /** @dataProvider MediaWikiProvide::Months */
-       function testRevisionmonthIsZeroPadded( $month ) {
+       /** @dataProvider provideMonths */
+       public function testRevisionmonthIsZeroPadded( $month ) {
                $this->assertZeroPadded( 'revisionmonth', $month );
        }
 
-       /** @dataProvider MediaWikiProvide::Months */
-       function testRevisionmonthoneIsUnPadded( $month ) {
+       /** @dataProvider provideMonths */
+       public function testRevisionmonthoneIsUnPadded( $month ) {
                $this->assertUnPadded( 'revisionmonth1', $month );
        }
 
@@ -136,15 +157,15 @@ class MagicVariableTest extends MediaWikiTestCase {
         * Rough tests for {{SERVERNAME}} magic word
         * Bug 31176
         * @group Database
-        * @dataProvider dataServernameFromDifferentProtocols
+        * @dataProvider provideDataServernameFromDifferentProtocols
         */
-       function testServernameFromDifferentProtocols( $server ) {
+       public function testServernameFromDifferentProtocols( $server ) {
                $this->setMwGlobals( 'wgServer', $server );
 
                $this->assertMagic( 'localhost', 'servername' );
        }
 
-       function dataServernameFromDifferentProtocols() {
+       public static function provideDataServernameFromDifferentProtocols() {
                return array(
                        array( 'http://localhost/' ),
                        array( 'https://localhost/' ),
@@ -155,12 +176,12 @@ class MagicVariableTest extends MediaWikiTestCase {
        ############### HELPERS ############################################
 
        /** assertion helper expecting a magic output which is zero padded */
-       PUBLIC function assertZeroPadded( $magic, $value ) {
+       public function assertZeroPadded( $magic, $value ) {
                $this->assertMagicPadding( $magic, $value, '%02d' );
        }
 
        /** assertion helper expecting a magic output which is unpadded */
-       PUBLIC function assertUnPadded( $magic, $value ) {
+       public function assertUnPadded( $magic, $value ) {
                $this->assertMagicPadding( $magic, $value, '%d' );
        }
 
index ab8e77b..eac4de5 100644 (file)
@@ -6,6 +6,8 @@
  * @group Database
  * @group Parser
  * @group Stub
+ *
+ * @todo covers tags
  */
 class NewParserTest extends MediaWikiTestCase {
        static protected $articles = array(); // Array of test articles defined by the tests
@@ -629,6 +631,7 @@ class NewParserTest extends MediaWikiTestCase {
                        $out = $parser->getPreloadText( $input, $title, $options );
                } else {
                        $output = $parser->parse( $input, $title, $options, true, true, 1337 );
+                       $output->setTOCEnabled( !isset( $opts['notoc'] ) );
                        $out = $output->getText();
 
                        if ( isset( $opts['showtitle'] ) ) {
@@ -670,7 +673,7 @@ class NewParserTest extends MediaWikiTestCase {
         *
         * @group ParserFuzz
         */
-       function testFuzzTests() {
+       public function testFuzzTests() {
                global $wgParserTestFiles;
 
                $files = $wgParserTestFiles;
index 3cdbf15..e5c5cb2 100644 (file)
@@ -15,6 +15,7 @@ class ParserMethodsTest extends MediaWikiLangTestCase {
 
        /**
         * @dataProvider providePreSaveTransform
+        * @covers Parser::preSaveTransform
         */
        public function testPreSaveTransform( $text, $expected ) {
                global $wgParser;
@@ -28,6 +29,9 @@ class ParserMethodsTest extends MediaWikiLangTestCase {
                $this->assertEquals( $expected, $text );
        }
 
+       /**
+        * @covers Parser::callParserFunction
+        */
        public function testCallParserFunction() {
                global $wgParser;
 
@@ -45,6 +49,10 @@ class ParserMethodsTest extends MediaWikiLangTestCase {
                ), $ret, 'callParserFunction works for {{#tag:pre|foo|style=margin-left: 1.6em}}' );
        }
 
+       /**
+        * @covers Parser::parse
+        * @covers ParserOutput::getSections
+        */
        public function testGetSections() {
                global $wgParser;
 
@@ -83,5 +91,5 @@ class ParserMethodsTest extends MediaWikiLangTestCase {
                        ),
                ), $out->getSections(), 'getSections() with proper value when <h2> is used' );
        }
-       // TODO: Add tests for cleanSig() / cleanSigInSig(), getSection(), replaceSection(), getPreloadText()
+       //@Todo Add tests for cleanSig() / cleanSigInSig(), getSection(), replaceSection(), getPreloadText()
 }
index 68f77ab..c73666d 100644 (file)
@@ -2,7 +2,7 @@
 
 class ParserOutputTest extends MediaWikiTestCase {
 
-       function dataIsLinkInternal() {
+       public static function provideIsLinkInternal() {
                return array(
                        // Different domains
                        array( false, 'http://example.org', 'http://mediawiki.org' ),
@@ -29,13 +29,17 @@ class ParserOutputTest extends MediaWikiTestCase {
 
        /**
         * Test to make sure ParserOutput::isLinkInternal behaves properly
-        * @dataProvider dataIsLinkInternal
+        * @dataProvider provideIsLinkInternal
+        * @covers ParserOutput::isLinkInternal
         */
-       function testIsLinkInternal( $shouldMatch, $server, $url ) {
-
+       public function testIsLinkInternal( $shouldMatch, $server, $url ) {
                $this->assertEquals( $shouldMatch, ParserOutput::isLinkInternal( $server, $url ) );
        }
 
+       /**
+        * @covers ParserOutput::setExtensionData
+        * @covers ParserOutput::getExtensionData
+        */
        public function testExtensionData() {
                $po = new ParserOutput();
 
index c609164..d12fee3 100644 (file)
@@ -4,8 +4,17 @@
  * @author Antoine Musso
  */
 class ParserPreloadTest extends MediaWikiTestCase {
+       /**
+        * @var Parser
+        */
        private $testParser;
+       /**
+        * @var ParserOptions
+        */
        private $testParserOptions;
+       /**
+        * @var Title
+        */
        private $title;
 
        protected function setUp() {
@@ -31,14 +40,14 @@ class ParserPreloadTest extends MediaWikiTestCase {
        /**
         * @covers Parser::getPreloadText
         */
-       function testPreloadSimpleText() {
+       public function testPreloadSimpleText() {
                $this->assertPreloaded( 'simple', 'simple' );
        }
 
        /**
         * @covers Parser::getPreloadText
         */
-       function testPreloadedPreIsUnstripped() {
+       public function testPreloadedPreIsUnstripped() {
                $this->assertPreloaded(
                        '<pre>monospaced</pre>',
                        '<pre>monospaced</pre>',
@@ -49,7 +58,7 @@ class ParserPreloadTest extends MediaWikiTestCase {
        /**
         * @covers Parser::getPreloadText
         */
-       function testPreloadedNowikiIsUnstripped() {
+       public function testPreloadedNowikiIsUnstripped() {
                $this->assertPreloaded(
                        '<nowiki>[[Dummy title]]</nowiki>',
                        '<nowiki>[[Dummy title]]</nowiki>',
@@ -57,7 +66,7 @@ class ParserPreloadTest extends MediaWikiTestCase {
                );
        }
 
-       function assertPreloaded( $expected, $text, $msg = '' ) {
+       protected function assertPreloaded( $expected, $text, $msg = '' ) {
                $this->assertEquals(
                        $expected,
                        $this->testParser->getPreloadText(
index 7e9c9d4..8aee937 100644 (file)
@@ -1,9 +1,16 @@
 <?php
 
 class PreprocessorTest extends MediaWikiTestCase {
-       var $mTitle = 'Page title';
-       var $mPPNodeCount = 0;
-       var $mOptions;
+       protected $mTitle = 'Page title';
+       protected $mPPNodeCount = 0;
+       /**
+        * @var ParserOptions
+        */
+       protected $mOptions;
+       /**
+        * @var Preprocessor
+        */
+       protected $mPreprocessor;
 
        protected function setUp() {
                global $wgParserConf, $wgContLang;
@@ -115,7 +122,7 @@ class PreprocessorTest extends MediaWikiTestCase {
         * @param string $wikiText
         * @return string
         */
-       function preprocessToXml( $wikiText ) {
+       protected function preprocessToXml( $wikiText ) {
                if ( method_exists( $this->mPreprocessor, 'preprocessToXml' ) ) {
                        return $this->normalizeXml( $this->mPreprocessor->preprocessToXml( $wikiText ) );
                }
@@ -134,14 +141,15 @@ class PreprocessorTest extends MediaWikiTestCase {
         * @param string $xml
         * @return string
         */
-       function normalizeXml( $xml ) {
+       protected function normalizeXml( $xml ) {
                return preg_replace( '!<([a-z]+)/>!', '<$1></$1>', str_replace( ' />', '/>', $xml ) );
        }
 
        /**
         * @dataProvider provideCases
+        * @covers Preprocessor_DOM::preprocessToXml
         */
-       function testPreprocessorOutput( $wikiText, $expectedXml ) {
+       public function testPreprocessorOutput( $wikiText, $expectedXml ) {
                $this->assertEquals( $this->normalizeXml( $expectedXml ), $this->preprocessToXml( $wikiText ) );
        }
 
@@ -160,8 +168,9 @@ class PreprocessorTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provideFiles
+        * @covers Preprocessor_DOM::preprocessToXml
         */
-       function testPreprocessorOutputFiles( $filename ) {
+       public function testPreprocessorOutputFiles( $filename ) {
                $folder = __DIR__ . "/../../../parser/preprocess";
                $wikiText = file_get_contents( "$folder/$filename.txt" );
                $output = $this->preprocessToXml( $wikiText );
@@ -222,8 +231,9 @@ class PreprocessorTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provideHeadings
+        * @covers Preprocessor_DOM::preprocessToXml
         */
-       function testHeadings( $wikiText, $expectedXml ) {
+       public function testHeadings( $wikiText, $expectedXml ) {
                $this->assertEquals( $this->normalizeXml( $expectedXml ), $this->preprocessToXml( $wikiText ) );
        }
 }
index ed60079..259a9e2 100644 (file)
@@ -20,8 +20,9 @@ class TagHookTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provideValidNames
+        * @covers Parser::setHook
         */
-       function testTagHooks( $tag ) {
+       public function testTagHooks( $tag ) {
                global $wgParserConf, $wgContLang;
                $parser = new Parser( $wgParserConf );
 
@@ -35,8 +36,9 @@ class TagHookTest extends MediaWikiTestCase {
        /**
         * @dataProvider provideBadNames
         * @expectedException MWException
+        * @covers Parser::setHook
         */
-       function testBadTagHooks( $tag ) {
+       public function testBadTagHooks( $tag ) {
                global $wgParserConf, $wgContLang;
                $parser = new Parser( $wgParserConf );
 
@@ -47,8 +49,9 @@ class TagHookTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provideValidNames
+        * @covers Parser::setFunctionTagHook
         */
-       function testFunctionTagHooks( $tag ) {
+       public function testFunctionTagHooks( $tag ) {
                global $wgParserConf, $wgContLang;
                $parser = new Parser( $wgParserConf );
 
@@ -62,8 +65,9 @@ class TagHookTest extends MediaWikiTestCase {
        /**
         * @dataProvider provideBadNames
         * @expectedException MWException
+        * @covers Parser::setFunctionTagHook
         */
-       function testBadFunctionTagHooks( $tag ) {
+       public function testBadFunctionTagHooks( $tag ) {
                global $wgParserConf, $wgContLang;
                $parser = new Parser( $wgParserConf );
 
diff --git a/tests/phpunit/includes/parser/TidyTest.php b/tests/phpunit/includes/parser/TidyTest.php
new file mode 100644 (file)
index 0000000..57a88b9
--- /dev/null
@@ -0,0 +1,44 @@
+<?php
+
+/**
+ * @group Parser
+ */
+class TidyTest extends MediaWikiTestCase {
+       public function setUp() {
+               parent::setUp();
+               $check = MWTidy::tidy( '' );
+               if ( strpos( $check, '<!--' ) !== false ) {
+                       $this->markTestSkipped( 'Tidy not found' );
+               }
+       }
+
+       /**
+        * @dataProvider provideTestWrapping
+        */
+       public function testTidyWrapping( $expected, $text, $msg = '' ) {
+               $text = MWTidy::tidy( $text );
+               // We don't care about where Tidy wants to stick is <p>s
+               $text = trim( preg_replace( '#</?p>#', '', $text ) );
+               // Windows, we love you!
+               $text = str_replace( "\r", '', $text );
+               $this->assertEquals( $expected, $text, $msg );
+       }
+
+       public function provideTestWrapping() {
+               return array(
+                       array(
+                               '<mw:editsection page="foo" section="bar">foo</mw:editsection>',
+                               '<mw:editsection page="foo" section="bar">foo</mw:editsection>',
+                               '<mw:editsection> should survive tidy'
+                       ),
+                       array(
+                               '<editsection page="foo" section="bar">foo</editsection>',
+                               '<editsection page="foo" section="bar">foo</editsection>',
+                               '<editsection> should survive tidy'
+                       ),
+                       array( '<mw:toc>foo</mw:toc>', '<mw:toc>foo</mw:toc>', '<mw:toc> should survive tidy' ),
+                       array( "<link foo=\"bar\" />\nfoo", '<link foo="bar"/>foo', '<link> should survive tidy' ),
+                       array( "<meta foo=\"bar\" />\nfoo", '<meta foo="bar"/>foo', '<meta> should survive tidy' ),
+               );
+       }
+}
\ No newline at end of file
index 8957a2f..e460591 100644 (file)
@@ -3,9 +3,16 @@
 /**
  * @group Search
  * @group Database
+ *
+ * @covers SearchEngine<extended>
+ * @note Coverage will only ever show one of on of the Search* classes
  */
 class SearchEngineTest extends MediaWikiLangTestCase {
-       protected $search, $pageList;
+       /**
+        * @var SearchEngine
+        */
+       protected $search;
+       protected $pageList;
 
        /**
         * Checks for database type & version.
@@ -115,7 +122,7 @@ class SearchEngineTest extends MediaWikiLangTestCase {
                return true;
        }
 
-       function testFullWidth() {
+       public function testFullWidth() {
                $this->assertEquals(
                        array( 'FullOneUp', 'FullTwoLow', 'HalfOneUp', 'HalfTwoLow' ),
                        $this->fetchIds( $this->search->searchText( 'AZ' ) ),
@@ -134,14 +141,14 @@ class SearchEngineTest extends MediaWikiLangTestCase {
                        "Search for normalized from Full-width Lower" );
        }
 
-       function testTextSearch() {
+       public function testTextSearch() {
                $this->assertEquals(
                        array( 'Smithee' ),
                        $this->fetchIds( $this->search->searchText( 'smithee' ) ),
                        "Plain search failed" );
        }
 
-       function testTextPowerSearch() {
+       public function testTextPowerSearch() {
                $this->search->setNamespaces( array( 0, 1, 4 ) );
                $this->assertEquals(
                        array(
@@ -152,7 +159,7 @@ class SearchEngineTest extends MediaWikiLangTestCase {
                        "Power search failed" );
        }
 
-       function testTitleSearch() {
+       public function testTitleSearch() {
                $this->assertEquals(
                        array(
                                'Alan Smithee',
@@ -162,7 +169,7 @@ class SearchEngineTest extends MediaWikiLangTestCase {
                        "Title search failed" );
        }
 
-       function testTextTitlePowerSearch() {
+       public function testTextTitlePowerSearch() {
                $this->search->setNamespaces( array( 0, 1, 4 ) );
                $this->assertEquals(
                        array(
index e947c34..b913af8 100644 (file)
@@ -30,7 +30,10 @@ class SearchUpdateTest extends MediaWikiTestCase {
                return trim( SearchUpdate::updateText( $text ) );
        }
 
-       function testUpdateText() {
+       /**
+        * @covers SearchUpdate::updateText
+        */
+       public function testUpdateText() {
                $this->assertEquals(
                        'test',
                        $this->updateText( '<div>TeSt</div>' ),
@@ -62,7 +65,10 @@ EOT
                );
        }
 
-       function testBug32712() {
+       /**
+        * @covers SearchUpdate::updateText
+        */
+       public function testBug32712() {
                $text = "text „http://example.com“ text";
                $result = $this->updateText( $text );
                $processed = preg_replace( '/Q/u', 'Q', $result );
index e0092a5..c5d52d3 100644 (file)
@@ -54,6 +54,7 @@ class MediaWikiSiteTest extends SiteTest {
 
        /**
         * @dataProvider fileUrlProvider
+        * @covers MediaWikiSite::getFileUrl
         */
        public function testGetFileUrl( $url, $filePath, $pathArgument, $expected ) {
                $site = new MediaWikiSite();
@@ -77,6 +78,7 @@ class MediaWikiSiteTest extends SiteTest {
 
        /**
         * @dataProvider provideGetPageUrl
+        * @covers MediaWikiSite::getPageUrl
         */
        public function testGetPageUrl( $path, $page, $expected ) {
                $site = new MediaWikiSite();
index bd2ae07..8af2fc1 100644 (file)
@@ -68,6 +68,7 @@ class SiteListTest extends MediaWikiTestCase {
        /**
         * @dataProvider siteListProvider
         * @param SiteList $sites
+        * @covers SiteList::isEmpty
         */
        public function testIsEmpty( SiteList $sites ) {
                $this->assertEquals( count( $sites ) === 0, $sites->isEmpty() );
@@ -76,6 +77,7 @@ class SiteListTest extends MediaWikiTestCase {
        /**
         * @dataProvider siteListProvider
         * @param SiteList $sites
+        * @covers SiteList::getSite
         */
        public function testGetSiteByGlobalId( SiteList $sites ) {
                if ( $sites->isEmpty() ) {
@@ -93,6 +95,7 @@ class SiteListTest extends MediaWikiTestCase {
        /**
         * @dataProvider siteListProvider
         * @param SiteList $sites
+        * @covers SiteList::getSiteByInternalId
         */
        public function testGetSiteByInternalId( $sites ) {
                /**
@@ -110,6 +113,7 @@ class SiteListTest extends MediaWikiTestCase {
        /**
         * @dataProvider siteListProvider
         * @param SiteList $sites
+        * @covers SiteList::hasSite
         */
        public function testHasGlobalId( $sites ) {
                $this->assertFalse( $sites->hasSite( 'non-existing-global-id' ) );
@@ -128,6 +132,7 @@ class SiteListTest extends MediaWikiTestCase {
        /**
         * @dataProvider siteListProvider
         * @param SiteList $sites
+        * @covers SiteList::hasInternalId
         */
        public function testHasInternallId( $sites ) {
                /**
@@ -145,6 +150,7 @@ class SiteListTest extends MediaWikiTestCase {
        /**
         * @dataProvider siteListProvider
         * @param SiteList $sites
+        * @covers SiteList::getGlobalIdentifiers
         */
        public function testGetGlobalIdentifiers( SiteList $sites ) {
                $identifiers = $sites->getGlobalIdentifiers();
@@ -169,6 +175,8 @@ class SiteListTest extends MediaWikiTestCase {
         * @since 1.21
         *
         * @param SiteList $list
+        * @covers SiteList::getSerializationData
+        * @covers SiteList::unserialize
         */
        public function testSerialization( SiteList $list ) {
                $serialization = serialize( $list );
index cf652e9..6002c1a 100644 (file)
@@ -32,6 +32,9 @@
  */
 class SiteSQLStoreTest extends MediaWikiTestCase {
 
+       /**
+        * @covers SiteSQLStore::getSites
+        */
        public function testGetSites() {
                $expectedSites = TestSites::getSites();
                TestSites::insertIntoDb();
@@ -56,6 +59,9 @@ class SiteSQLStoreTest extends MediaWikiTestCase {
                }
        }
 
+       /**
+        * @covers SiteSQLStore::saveSites
+        */
        public function testSaveSites() {
                $store = SiteSQLStore::newInstance();
 
@@ -86,6 +92,9 @@ class SiteSQLStoreTest extends MediaWikiTestCase {
                $this->assertTrue( $site->getInternalId() >= 0 );
        }
 
+       /**
+        * @covers SiteSQLStore::reset
+        */
        public function testReset() {
                $store1 = SiteSQLStore::newInstance();
                $store2 = SiteSQLStore::newInstance();
@@ -109,6 +118,9 @@ class SiteSQLStoreTest extends MediaWikiTestCase {
                $this->assertNull( $site );
        }
 
+       /**
+        * @covers SiteSQLStore::clear
+        */
        public function testClear() {
                $store = SiteSQLStore::newInstance();
                $this->assertTrue( $store->clear() );
index b453e74..29c1ff3 100644 (file)
@@ -38,6 +38,7 @@ class SiteTest extends MediaWikiTestCase {
        /**
         * @dataProvider instanceProvider
         * @param Site $site
+        * @covers Site::getInterwikiIds
         */
        public function testGetInterwikiIds( Site $site ) {
                $this->assertInternalType( 'array', $site->getInterwikiIds() );
@@ -46,6 +47,7 @@ class SiteTest extends MediaWikiTestCase {
        /**
         * @dataProvider instanceProvider
         * @param Site $site
+        * @covers Site::getNavigationIds
         */
        public function testGetNavigationIds( Site $site ) {
                $this->assertInternalType( 'array', $site->getNavigationIds() );
@@ -54,6 +56,7 @@ class SiteTest extends MediaWikiTestCase {
        /**
         * @dataProvider instanceProvider
         * @param Site $site
+        * @covers Site::addNavigationId
         */
        public function testAddNavigationId( Site $site ) {
                $site->addNavigationId( 'foobar' );
@@ -63,6 +66,7 @@ class SiteTest extends MediaWikiTestCase {
        /**
         * @dataProvider instanceProvider
         * @param Site $site
+        * @covers Site::addInterwikiId
         */
        public function testAddInterwikiId( Site $site ) {
                $site->addInterwikiId( 'foobar' );
@@ -72,6 +76,7 @@ class SiteTest extends MediaWikiTestCase {
        /**
         * @dataProvider instanceProvider
         * @param Site $site
+        * @covers Site::getLanguageCode
         */
        public function testGetLanguageCode( Site $site ) {
                $this->assertTypeOrValue( 'string', $site->getLanguageCode(), null );
@@ -80,6 +85,7 @@ class SiteTest extends MediaWikiTestCase {
        /**
         * @dataProvider instanceProvider
         * @param Site $site
+        * @covers Site::setLanguageCode
         */
        public function testSetLanguageCode( Site $site ) {
                $site->setLanguageCode( 'en' );
@@ -89,6 +95,7 @@ class SiteTest extends MediaWikiTestCase {
        /**
         * @dataProvider instanceProvider
         * @param Site $site
+        * @covers Site::normalizePageName
         */
        public function testNormalizePageName( Site $site ) {
                $this->assertInternalType( 'string', $site->normalizePageName( 'Foobar' ) );
@@ -97,6 +104,7 @@ class SiteTest extends MediaWikiTestCase {
        /**
         * @dataProvider instanceProvider
         * @param Site $site
+        * @covers Site::getGlobalId
         */
        public function testGetGlobalId( Site $site ) {
                $this->assertTypeOrValue( 'string', $site->getGlobalId(), null );
@@ -105,6 +113,7 @@ class SiteTest extends MediaWikiTestCase {
        /**
         * @dataProvider instanceProvider
         * @param Site $site
+        * @covers Site::setGlobalId
         */
        public function testSetGlobalId( Site $site ) {
                $site->setGlobalId( 'foobar' );
@@ -114,6 +123,7 @@ class SiteTest extends MediaWikiTestCase {
        /**
         * @dataProvider instanceProvider
         * @param Site $site
+        * @covers Site::getType
         */
        public function testGetType( Site $site ) {
                $this->assertInternalType( 'string', $site->getType() );
@@ -122,6 +132,7 @@ class SiteTest extends MediaWikiTestCase {
        /**
         * @dataProvider instanceProvider
         * @param Site $site
+        * @covers Site::getPath
         */
        public function testGetPath( Site $site ) {
                $this->assertTypeOrValue( 'string', $site->getPath( 'page_path' ), null );
@@ -132,6 +143,7 @@ class SiteTest extends MediaWikiTestCase {
        /**
         * @dataProvider instanceProvider
         * @param Site $site
+        * @covers Site::getAllPaths
         */
        public function testGetAllPaths( Site $site ) {
                $this->assertInternalType( 'array', $site->getAllPaths() );
@@ -140,6 +152,8 @@ class SiteTest extends MediaWikiTestCase {
        /**
         * @dataProvider instanceProvider
         * @param Site $site
+        * @covers Site::setPath
+        * @covers Site::removePath
         */
        public function testSetAndRemovePath( Site $site ) {
                $count = count( $site->getAllPaths() );
@@ -162,6 +176,9 @@ class SiteTest extends MediaWikiTestCase {
                $this->assertNull( $site->getPath( 'spam' ) );
        }
 
+       /**
+        * @covers Site::setLinkPath
+        */
        public function testSetLinkPath() {
                $site = new Site();
                $path = "TestPath/$1";
@@ -170,6 +187,9 @@ class SiteTest extends MediaWikiTestCase {
                $this->assertEquals( $path, $site->getLinkPath() );
        }
 
+       /**
+        * @covers Site::getLinkPathType
+        */
        public function testGetLinkPathType() {
                $site = new Site();
 
@@ -182,6 +202,9 @@ class SiteTest extends MediaWikiTestCase {
                $this->assertEquals( $path, $site->getLinkPath() );
        }
 
+       /**
+        * @covers Site::setPath
+        */
        public function testSetPath() {
                $site = new Site();
 
@@ -191,6 +214,10 @@ class SiteTest extends MediaWikiTestCase {
                $this->assertEquals( $path, $site->getPath( 'foo' ) );
        }
 
+       /**
+        * @covers Site::setPath
+        * @covers Site::getProtocol
+        */
        public function testProtocolRelativePath() {
                $site = new Site();
 
@@ -228,6 +255,7 @@ class SiteTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provideGetPageUrl
+        * @covers Site::getPageUrl
         */
        public function testGetPageUrl( $path, $page, $expected ) {
                $site = new Site();
@@ -252,6 +280,8 @@ class SiteTest extends MediaWikiTestCase {
        /**
         * @dataProvider instanceProvider
         * @param Site $site
+        * @covers Site::serialize
+        * @covers Site::unserialize
         */
        public function testSerialization( Site $site ) {
                $this->assertInstanceOf( 'Serializable', $site );
index 3b82e07..ba845eb 100644 (file)
@@ -15,6 +15,9 @@ if ( !defined( 'MEDIAWIKI' ) ) {
 global $IP;
 require_once "$IP/includes/QueryPage.php"; // Needed to populate $wgQueryPages
 
+/**
+ * @covers QueryPage<extended>
+ */
 class QueryAllSpecialPagesTest extends MediaWikiTestCase {
 
        /** List query pages that can not be tested automatically */
@@ -51,7 +54,7 @@ class QueryAllSpecialPagesTest extends MediaWikiTestCase {
         * Test SQL for each of our QueryPages objects
         * @group Database
         */
-       function testQuerypageSqlQuery() {
+       public function testQuerypageSqlQuery() {
                global $wgDBtype;
 
                foreach ( $this->queryPages as $page ) {
index c7a4828..8a92daf 100644 (file)
@@ -7,6 +7,9 @@
  *
  */
 
+/**
+ * @covers SpecialPreferences
+ */
 class SpecialPreferencesTest extends MediaWikiTestCase {
 
        /**
@@ -15,7 +18,7 @@ class SpecialPreferencesTest extends MediaWikiTestCase {
         *
         * Test specifications by Alexandre "ialex" Emsenhuber.
         */
-       function testBug41337() {
+       public function testBug41337() {
 
                // Set a low limit
                $this->setMwGlobals( 'wgMaxSigChars', 2 );
index 436eb2e..b1ba152 100644 (file)
@@ -6,6 +6,8 @@
  *
  * @author Antoine Musso
  * @group Database
+ *
+ * @covers SpecialRecentChanges
  */
 class SpecialRecentchangesTest extends MediaWikiTestCase {
 
index c737f05..17e883f 100644 (file)
@@ -18,7 +18,7 @@ class SpecialSearchTest extends MediaWikiTestCase {
         * @param $expectedProfile An expected search profile name
         * @param $expectedNs Array Expected namespaces
         */
-       function testProfileAndNamespaceLoading(
+       public function testProfileAndNamespaceLoading(
                $requested, $userOptions, $expectedProfile, $expectedNS,
                $message = 'Profile name and namespaces mismatches!'
        ) {
@@ -112,7 +112,7 @@ class SpecialSearchTest extends MediaWikiTestCase {
         * Verify we do not expand search term in <title> on search result page
         * https://gerrit.wikimedia.org/r/4841
         */
-       function testSearchTermIsNotExpanded() {
+       public function testSearchTermIsNotExpanded() {
 
                # Initialize [[Special::Search]]
                $search = new SpecialSearch();
index 298420b..982b46b 100644 (file)
@@ -1,10 +1,12 @@
 <?php
+
 /**
  * @group Upload
  */
 class UploadBaseTest extends MediaWikiTestCase {
-       protected $upload;
 
+       /** @var UploadTestHandler */
+       protected $upload;
 
        protected function setUp() {
                global $wgHooks;
@@ -30,6 +32,7 @@ class UploadBaseTest extends MediaWikiTestCase {
         * of UploadBase::getTitle() and then the actual returned title
         *
         * @dataProvider provideTestTitleValidation
+        * @covers UploadBase::getTitle
         */
        public function testTitleValidation( $srcFilename, $dstFilename, $code, $msg ) {
                /* Check the result code */
@@ -82,6 +85,7 @@ class UploadBaseTest extends MediaWikiTestCase {
 
        /**
         * Test the upload verification functions
+        * @covers UploadBase::verifyUpload
         */
        public function testVerifyUpload() {
                /* Setup with zero file size */
@@ -108,7 +112,6 @@ class UploadBaseTest extends MediaWikiTestCase {
         *
         * This method should be abstracted so we can test different settings.
         */
-
        public function testMaxUploadSize() {
                global $wgMaxUploadSize;
                $savedGlobal = $wgMaxUploadSize; // save global
index a75fba6..397c100 100644 (file)
@@ -4,6 +4,8 @@
  * @group Broken
  * @group Upload
  * @group Database
+ *
+ * @covers UploadFromUrl
  */
 class UploadFromUrlTest extends ApiTestCase {
        protected function setUp() {
@@ -46,35 +48,6 @@ class UploadFromUrlTest extends ApiTestCase {
        }
 
        /**
-        * @todo Document why we test login, since the $wgUser hack used doesn't
-        * require login
-        */
-       public function testLogin() {
-               $data = $this->doApiRequest( array(
-                       'action' => 'login',
-                       'lgname' => $this->user->userName,
-                       'lgpassword' => $this->user->passWord ) );
-               $this->assertArrayHasKey( "login", $data[0] );
-               $this->assertArrayHasKey( "result", $data[0]['login'] );
-               $this->assertEquals( "NeedToken", $data[0]['login']['result'] );
-               $token = $data[0]['login']['token'];
-
-               $data = $this->doApiRequest( array(
-                       'action' => 'login',
-                       "lgtoken" => $token,
-                       'lgname' => $this->user->userName,
-                       'lgpassword' => $this->user->passWord ) );
-
-               $this->assertArrayHasKey( "login", $data[0] );
-               $this->assertArrayHasKey( "result", $data[0]['login'] );
-               $this->assertEquals( "Success", $data[0]['login']['result'] );
-               $this->assertArrayHasKey( 'lgtoken', $data[0]['login'] );
-
-               return $data;
-       }
-
-       /**
-        * @depends testLogin
         * @depends testClearQueue
         */
        public function testSetupUrlDownload( $data ) {
@@ -148,7 +121,6 @@ class UploadFromUrlTest extends ApiTestCase {
        }
 
        /**
-        * @depends testLogin
         * @depends testClearQueue
         */
        public function testAsyncUpload( $data ) {
@@ -167,7 +139,6 @@ class UploadFromUrlTest extends ApiTestCase {
        }
 
        /**
-        * @depends testLogin
         * @depends testClearQueue
         */
        public function testAsyncUploadWarning( $data ) {
@@ -197,7 +168,6 @@ class UploadFromUrlTest extends ApiTestCase {
        }
 
        /**
-        * @depends testLogin
         * @depends testClearQueue
         */
        public function testSyncDownload( $data ) {
@@ -329,9 +299,6 @@ class UploadFromUrlTest extends ApiTestCase {
                return $data;
        }
 
-       /**
-        *
-        */
        protected function deleteFile( $name ) {
                $t = Title::newFromText( $name, NS_FILE );
                $this->assertTrue( $t->exists(), "File '$name' exists" );
index 7a0fea4..1c89377 100644 (file)
@@ -1,6 +1,8 @@
 <?php
 /**
  * @group Database
+ *
+ * @covers UploadStash
  */
 class UploadStashTest extends MediaWikiTestCase {
        /**
index fdf3347..a644f5e 100644 (file)
@@ -7,14 +7,20 @@
 
 /** Tests for MediaWiki languages/LanguageAm.php */
 class LanguageAmTest extends LanguageClassesTestCase {
-       /** @dataProvider providePlural */
-       function testPlural( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::convertPlural
+        */
+       public function testPlural( $result, $value ) {
                $forms = array( 'one', 'other' );
                $this->assertEquals( $result, $this->getLang()->convertPlural( $value, $forms ) );
        }
 
-       /** @dataProvider providePlural */
-       function testGetPluralRuleType( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::getPluralRuleType
+        */
+       public function testGetPluralRuleType( $result, $value ) {
                $this->assertEquals( $result, $this->getLang()->getPluralRuleType( $value ) );
        }
 
index a623912..7b48f23 100644 (file)
@@ -6,7 +6,11 @@
 
 /** Tests for MediaWiki languages/LanguageAr.php */
 class LanguageArTest extends LanguageClassesTestCase {
-       function testFormatNum() {
+       /**
+        * @covers Language::formatNum
+        * @todo split into a test and a dataprovider
+        */
+       public function testFormatNum() {
                $this->assertEquals( '١٬٢٣٤٬٥٦٧', $this->getLang()->formatNum( '1234567' ) );
                $this->assertEquals( '-١٢٫٨٩', $this->getLang()->formatNum( -12.89 ) );
        }
@@ -14,8 +18,9 @@ class LanguageArTest extends LanguageClassesTestCase {
        /**
         * Mostly to test the raw ascii feature.
         * @dataProvider providerSprintfDate
+        * @covers Language::sprintfDate
         */
-       function testSprintfDate( $format, $date, $expected ) {
+       public function testSprintfDate( $format, $date, $expected ) {
                $this->assertEquals( $expected, $this->getLang()->sprintfDate( $format, $date ) );
        }
 
@@ -44,14 +49,20 @@ class LanguageArTest extends LanguageClassesTestCase {
                );
        }
 
-       /** @dataProvider providePlural */
-       function testPlural( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::convertPlural
+        */
+       public function testPlural( $result, $value ) {
                $forms = array( 'zero', 'one', 'two', 'few', 'many', 'other' );
                $this->assertEquals( $result, $this->getLang()->convertPlural( $value, $forms ) );
        }
 
-       /** @dataProvider providePlural */
-       function testGetPluralRuleType( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::getPluralRuleType
+        */
+       public function testGetPluralRuleType( $result, $value ) {
                $this->assertEquals( $result, $this->getLang()->getPluralRuleType( $value ) );
        }
 
index a88356a..7bd586a 100644 (file)
@@ -7,14 +7,20 @@
 
 /** Tests for MediaWiki languages/LanguageBe.php */
 class LanguageBeTest extends LanguageClassesTestCase {
-       /** @dataProvider providePlural */
-       function testPlural( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::convertPlural
+        */
+       public function testPlural( $result, $value ) {
                $forms = array( 'one', 'few', 'many', 'other' );
                $this->assertEquals( $result, $this->getLang()->convertPlural( $value, $forms ) );
        }
 
-       /** @dataProvider providePlural */
-       function testGetPluralRuleType( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::getPluralRuleType
+        */
+       public function testGetPluralRuleType( $result, $value ) {
                $this->assertEquals( $result, $this->getLang()->getPluralRuleType( $value ) );
        }
 
index 3fcc0e5..d5822f4 100644 (file)
@@ -6,14 +6,17 @@ class LanguageBe_taraskTest extends LanguageClassesTestCase {
         * be-tarask. This is to ensure LanguageClassesTestCase
         * does not give us the wrong language.
         */
-       function testBeTaraskTestsUsesBeTaraskCode() {
+       public function testBeTaraskTestsUsesBeTaraskCode() {
                $this->assertEquals( 'be-tarask',
                        $this->getLang()->getCode()
                );
        }
 
-       /** see bug 23156 & r64981 */
-       function testSearchRightSingleQuotationMarkAsApostroph() {
+       /**
+        * @see bug 23156 & r64981
+        * @covers Language::commafy
+        */
+       public function testSearchRightSingleQuotationMarkAsApostroph() {
                $this->assertEquals(
                        "'",
                        $this->getLang()->normalizeForSearch( '’' ),
@@ -21,25 +24,37 @@ class LanguageBe_taraskTest extends LanguageClassesTestCase {
                );
        }
 
-       /** see bug 23156 & r64981 */
-       function testCommafy() {
+       /**
+        * @see bug 23156 & r64981
+        * @covers Language::commafy
+        */
+       public function testCommafy() {
                $this->assertEquals( '1,234,567', $this->getLang()->commafy( '1234567' ) );
                $this->assertEquals( '12,345', $this->getLang()->commafy( '12345' ) );
        }
 
-       /** see bug 23156 & r64981 */
-       function testDoesNotCommafyFourDigitsNumber() {
+       /**
+        * @see bug 23156 & r64981
+        * @covers Language::commafy
+        */
+       public function testDoesNotCommafyFourDigitsNumber() {
                $this->assertEquals( '1234', $this->getLang()->commafy( '1234' ) );
        }
 
-       /** @dataProvider providePlural */
-       function testPlural( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::convertPlural
+        */
+       public function testPlural( $result, $value ) {
                $forms = array( 'one', 'few', 'many', 'other' );
                $this->assertEquals( $result, $this->getLang()->convertPlural( $value, $forms ) );
        }
 
-       /** @dataProvider providePlural */
-       function testGetPluralRuleType( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::getPluralRuleType
+        */
+       public function testGetPluralRuleType( $result, $value ) {
                $this->assertEquals( $result, $this->getLang()->getPluralRuleType( $value ) );
        }
 
@@ -59,8 +74,11 @@ class LanguageBe_taraskTest extends LanguageClassesTestCase {
                );
        }
 
-       /** @dataProvider providePluralTwoForms */
-       function testPluralTwoForms( $result, $value ) {
+       /**
+        * @dataProvider providePluralTwoForms
+        * @covers Language::convertPlural
+        */
+       public function testPluralTwoForms( $result, $value ) {
                $forms = array( 'one', 'other' );
                $this->assertEquals( $result, $this->getLang()->convertPlural( $value, $forms ) );
        }
index 3cdde36..187bfbb 100644 (file)
@@ -7,14 +7,20 @@
 
 /** Tests for MediaWiki languages/LanguageBho.php */
 class LanguageBhoTest extends LanguageClassesTestCase {
-       /** @dataProvider providePlural */
-       function testPlural( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::convertPlural
+        */
+       public function testPlural( $result, $value ) {
                $forms = array( 'one', 'other' );
                $this->assertEquals( $result, $this->getLang()->convertPlural( $value, $forms ) );
        }
 
-       /** @dataProvider providePlural */
-       function testGetPluralRuleType( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::getPluralRuleType
+        */
+       public function testGetPluralRuleType( $result, $value ) {
                $this->assertEquals( $result, $this->getLang()->getPluralRuleType( $value ) );
        }
 
index 83a0ef6..fb965b8 100644 (file)
@@ -7,14 +7,20 @@
 
 /** Tests for MediaWiki languages/LanguageBs.php */
 class LanguageBsTest extends LanguageClassesTestCase {
-       /** @dataProvider providePlural */
-       function testPlural( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::convertPlural
+        */
+       public function testPlural( $result, $value ) {
                $forms = array( 'one', 'few', 'many', 'other' );
                $this->assertEquals( $result, $this->getLang()->convertPlural( $value, $forms ) );
        }
 
-       /** @dataProvider providePlural */
-       function testGetPluralRuleType( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::getPluralRuleType
+        */
+       public function testGetPluralRuleType( $result, $value ) {
                $this->assertEquals( $result, $this->getLang()->getPluralRuleType( $value ) );
        }
 
index 03bb898..632e037 100644 (file)
@@ -35,6 +35,9 @@ abstract class LanguageClassesTestCase extends MediaWikiTestCase {
         */
        private $languageObject;
 
+       /**
+        * @return Language
+        */
        protected function getLang() {
                return $this->languageObject;
        }
@@ -56,7 +59,7 @@ abstract class LanguageClassesTestCase extends MediaWikiTestCase {
                                        . "out of " . get_called_class() . " failling back to 'en'\n"
                        );
                }
-               // TODO: validate $m[1] which should be a valid language code
+               // @todo validate $m[1] which should be a valid language code
                $this->languageObject = Language::factory( $m[1] );
        }
 
index 93ee0f0..da9e6b8 100644 (file)
@@ -7,14 +7,20 @@
 
 /** Tests for MediaWiki languages/classes/Languagecs.php */
 class LanguageCsTest extends LanguageClassesTestCase {
-       /** @dataProvider providePlural */
-       function testPlural( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::convertPlural
+        */
+       public function testPlural( $result, $value ) {
                $forms = array( 'one', 'few', 'other' );
                $this->assertEquals( $result, $this->getLang()->convertPlural( $value, $forms ) );
        }
 
-       /** @dataProvider providePlural */
-       function testGetPluralRuleType( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::getPluralRuleType
+        */
+       public function testGetPluralRuleType( $result, $value ) {
                $this->assertEquals( $result, $this->getLang()->getPluralRuleType( $value ) );
        }
 
index 562d6d9..0719317 100644 (file)
@@ -7,14 +7,20 @@
 
 /** Tests for MediaWiki languages/LanguageCu.php */
 class LanguageCuTest extends LanguageClassesTestCase {
-       /** @dataProvider providePlural */
-       function testPlural( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::convertPlural
+        */
+       public function testPlural( $result, $value ) {
                $forms = array( 'one', 'two', 'few', 'other' );
                $this->assertEquals( $result, $this->getLang()->convertPlural( $value, $forms ) );
        }
 
-       /** @dataProvider providePlural */
-       function testGetPluralRuleType( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::getPluralRuleType
+        */
+       public function testGetPluralRuleType( $result, $value ) {
                $this->assertEquals( $result, $this->getLang()->getPluralRuleType( $value ) );
        }
 
index 435da4f..eaf663a 100644 (file)
@@ -7,14 +7,20 @@
 
 /** Tests for MediaWiki languages/classes/LanguageCy.php */
 class LanguageCyTest extends LanguageClassesTestCase {
-       /** @dataProvider providePlural */
-       function testPlural( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::convertPlural
+        */
+       public function testPlural( $result, $value ) {
                $forms = array( 'zero', 'one', 'two', 'few', 'many', 'other' );
                $this->assertEquals( $result, $this->getLang()->convertPlural( $value, $forms ) );
        }
 
-       /** @dataProvider providePlural */
-       function testGetPluralRuleType( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::getPluralRuleType
+        */
+       public function testGetPluralRuleType( $result, $value ) {
                $this->assertEquals( $result, $this->getLang()->getPluralRuleType( $value ) );
        }
 
index be42124..94c11bc 100644 (file)
@@ -7,14 +7,20 @@
 
 /** Tests for MediaWiki languages/classes/LanguageDsb.php */
 class LanguageDsbTest extends LanguageClassesTestCase {
-       /** @dataProvider providePlural */
-       function testPlural( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::convertPlural
+        */
+       public function testPlural( $result, $value ) {
                $forms = array( 'one', 'two', 'few', 'other' );
                $this->assertEquals( $result, $this->getLang()->convertPlural( $value, $forms ) );
        }
 
-       /** @dataProvider providePlural */
-       function testGetPluralRuleType( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::getPluralRuleType
+        */
+       public function testGetPluralRuleType( $result, $value ) {
                $this->assertEquals( $result, $this->getLang()->getPluralRuleType( $value ) );
        }
 
index 4f96b48..46b6501 100644 (file)
@@ -7,14 +7,20 @@
 
 /** Tests for MediaWiki languages/classes/LanguageFr.php */
 class LanguageFrTest extends LanguageClassesTestCase {
-       /** @dataProvider providePlural */
-       function testPlural( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::convertPlural
+        */
+       public function testPlural( $result, $value ) {
                $forms = array( 'one', 'other' );
                $this->assertEquals( $result, $this->getLang()->convertPlural( $value, $forms ) );
        }
 
-       /** @dataProvider providePlural */
-       function testGetPluralRuleType( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::getPluralRuleType
+        */
+       public function testGetPluralRuleType( $result, $value ) {
                $this->assertEquals( $result, $this->getLang()->getPluralRuleType( $value ) );
        }
 
index 73c4800..c009f56 100644 (file)
@@ -7,14 +7,20 @@
 
 /** Tests for MediaWiki languages/classes/LanguageGa.php */
 class LanguageGaTest extends LanguageClassesTestCase {
-       /** @dataProvider providePlural */
-       function testPlural( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::convertPlural
+        */
+       public function testPlural( $result, $value ) {
                $forms = array( 'one', 'two', 'other' );
                $this->assertEquals( $result, $this->getLang()->convertPlural( $value, $forms ) );
        }
 
-       /** @dataProvider providePlural */
-       function testGetPluralRuleType( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::getPluralRuleType
+        */
+       public function testGetPluralRuleType( $result, $value ) {
                $this->assertEquals( $result, $this->getLang()->getPluralRuleType( $value ) );
        }
 
index edf8e50..0b2612b 100644 (file)
@@ -7,8 +7,11 @@
 
 /** Tests for MediaWiki languages/classes/LanguageGd.php */
 class LanguageGdTest extends LanguageClassesTestCase {
-       /** @dataProvider providerPlural */
-       function testPlural( $result, $value ) {
+       /**
+        * @dataProvider providerPlural
+        * @covers Language::convertPlural
+       */
+       public function testPlural( $result, $value ) {
                $forms = array( 'one', 'two', 'few', 'other' );
                $this->assertEquals( $result, $this->getLang()->convertPlural( $value, $forms ) );
        }
@@ -26,8 +29,11 @@ class LanguageGdTest extends LanguageClassesTestCase {
                );
        }
 
-       /** @dataProvider providerPluralExplicit */
-       function testExplicitPlural( $result, $value ) {
+       /**
+        * @dataProvider providerPluralExplicit
+        * @covers Language::convertPlural
+        */
+       public function testExplicitPlural( $result, $value ) {
                $forms = array( 'one', 'two', 'few', 'other', '11=Form11', '12=Form12' );
                $this->assertEquals( $result, $this->getLang()->convertPlural( $value, $forms ) );
        }
index 71c0160..a0def62 100644 (file)
@@ -7,16 +7,22 @@
 
 /** Tests for MediaWiki languages/classes/LanguageGv.php */
 class LanguageGvTest extends LanguageClassesTestCase {
-       /** @dataProvider providePlural */
-       function testPlural( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::convertPlural
+        */
+       public function testPlural( $result, $value ) {
                // This is not compatible with CLDR plural rules http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html#gv
                // What does this mean? Is there a hard-coded override for gv somewhere? -Ryan Kaldari 2013-01-28
                $forms = array( 'Form 1', 'Form 2', 'Form 3', 'Form 4' );
                $this->assertEquals( $result, $this->getLang()->convertPlural( $value, $forms ) );
        }
 
-       /** @dataProvider providePlural */
-       function testGetPluralRuleType( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::getPluralRuleType
+        */
+       public function testGetPluralRuleType( $result, $value ) {
                $this->markTestSkipped( "This test won't work since convertPlural for gv doesn't seem to actually follow our plural rules." );
                $this->assertEquals( $result, $this->getLang()->getPluralRuleType( $value ) );
        }
index a8524bb..8edc6dd 100644 (file)
@@ -7,38 +7,53 @@
 
 /** Tests for MediaWiki languages/classes/LanguageHe.php */
 class LanguageHeTest extends LanguageClassesTestCase {
-       /*
-       The most common usage for the plural forms is two forms,
-       for singular and plural. In this case, the second form
-       is technically dual, but in practice it's used as plural.
-       In some cases, usually with expressions of time, three forms
-       are needed - singular, dual and plural.
-       CLDR also specifies a fourth form for multiples of 10,
-       which is very rare. It also has a mistake, because
-       the number 10 itself is supposed to be just plural,
-       so currently it's overridden in MediaWiki.
+       /**
+        * The most common usage for the plural forms is two forms,
+        * for singular and plural. In this case, the second form
+        * is technically dual, but in practice it's used as plural.
+        * In some cases, usually with expressions of time, three forms
+        * are needed - singular, dual and plural.
+        * CLDR also specifies a fourth form for multiples of 10,
+        * which is very rare. It also has a mistake, because
+        * the number 10 itself is supposed to be just plural,
+        * so currently it's overridden in MediaWiki.
        */
 
-       /** @dataProvider provideTwoPluralForms */
-       function testTwoPluralForms( $result, $value ) {
+       // @todo the below test*PluralForms test methods can be refactored
+       //  to use a single test method and data provider..
+
+       /**
+        * @dataProvider provideTwoPluralForms
+        * @covers Language::convertPlural
+        */
+       public function testTwoPluralForms( $result, $value ) {
                $forms = array( 'one', 'other' );
                $this->assertEquals( $result, $this->getLang()->convertPlural( $value, $forms ) );
        }
 
-       /** @dataProvider provideThreePluralForms */
-       function testThreePluralForms( $result, $value ) {
+       /**
+        * @dataProvider provideThreePluralForms
+        * @covers Language::convertPlural
+        */
+       public function testThreePluralForms( $result, $value ) {
                $forms = array( 'one', 'two', 'other' );
                $this->assertEquals( $result, $this->getLang()->convertPlural( $value, $forms ) );
        }
 
-       /** @dataProvider provideFourPluralForms */
-       function testFourPluralForms( $result, $value ) {
+       /**
+        * @dataProvider provideFourPluralForms
+        * @covers Language::convertPlural
+        */
+       public function testFourPluralForms( $result, $value ) {
                $forms = array( 'one', 'two', 'many', 'other' );
                $this->assertEquals( $result, $this->getLang()->convertPlural( $value, $forms ) );
        }
 
-       /** @dataProvider provideFourPluralForms */
-       function testGetPluralRuleType( $result, $value ) {
+       /**
+        * @dataProvider provideFourPluralForms
+        * @covers Language::convertPlural
+        */
+       public function testGetPluralRuleType( $result, $value ) {
                $this->assertEquals( $result, $this->getLang()->getPluralRuleType( $value ) );
        }
 
@@ -75,8 +90,11 @@ class LanguageHeTest extends LanguageClassesTestCase {
                );
        }
 
-       /** @dataProvider provideGrammar */
-       function testGrammar( $result, $word, $case ) {
+       /**
+        * @dataProvider provideGrammar
+        * @covers Language::convertGrammar
+        */
+       public function testGrammar( $result, $word, $case ) {
                $this->assertEquals( $result, $this->getLang()->convertGrammar( $word, $case ) );
        }
 
index 9502d6a..f6d2c9e 100644 (file)
@@ -7,14 +7,20 @@
 
 /** Tests for MediaWiki languages/LanguageHi.php */
 class LanguageHiTest extends LanguageClassesTestCase {
-       /** @dataProvider providePlural */
-       function testPlural( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::convertPlural
+        */
+       public function testPlural( $result, $value ) {
                $forms = array( 'one', 'other' );
                $this->assertEquals( $result, $this->getLang()->convertPlural( $value, $forms ) );
        }
 
-       /** @dataProvider providePlural */
-       function testGetPluralRuleType( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::getPluralRuleType
+        */
+       public function testGetPluralRuleType( $result, $value ) {
                $this->assertEquals( $result, $this->getLang()->getPluralRuleType( $value ) );
        }
 
index 7516bac..6ce4aff 100644 (file)
@@ -7,14 +7,20 @@
 
 /** Tests for MediaWiki languages/classes/LanguageHr.php */
 class LanguageHrTest extends LanguageClassesTestCase {
-       /** @dataProvider providePlural */
-       function testPlural( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::convertPlural
+        */
+       public function testPlural( $result, $value ) {
                $forms = array( 'one', 'few', 'many', 'other' );
                $this->assertEquals( $result, $this->getLang()->convertPlural( $value, $forms ) );
        }
 
-       /** @dataProvider providePlural */
-       function testGetPluralRuleType( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::getPluralRuleType
+        */
+       public function testGetPluralRuleType( $result, $value ) {
                $this->assertEquals( $result, $this->getLang()->getPluralRuleType( $value ) );
        }
 
index bae4542..f95a43b 100644 (file)
@@ -7,14 +7,20 @@
 
 /** Tests for MediaWiki languages/classes/LanguageHsb.php */
 class LanguageHsbTest extends LanguageClassesTestCase {
-       /** @dataProvider providePlural */
-       function testPlural( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::convertPlural
+        */
+       public function testPlural( $result, $value ) {
                $forms = array( 'one', 'two', 'few', 'other' );
                $this->assertEquals( $result, $this->getLang()->convertPlural( $value, $forms ) );
        }
 
-       /** @dataProvider providePlural */
-       function testGetPluralRuleType( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::getPluralRuleType
+        */
+       public function testGetPluralRuleType( $result, $value ) {
                $this->assertEquals( $result, $this->getLang()->getPluralRuleType( $value ) );
        }
 
index 40ae108..ee9197d 100644 (file)
@@ -7,14 +7,20 @@
 
 /** Tests for MediaWiki languages/LanguageHu.php */
 class LanguageHuTest extends LanguageClassesTestCase {
-       /** @dataProvider providePlural */
-       function testPlural( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::convertPlural
+        */
+       public function testPlural( $result, $value ) {
                $forms = array( 'one', 'other' );
                $this->assertEquals( $result, $this->getLang()->convertPlural( $value, $forms ) );
        }
 
-       /** @dataProvider providePlural */
-       function testGetPluralRuleType( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::getPluralRuleType
+        */
+       public function testGetPluralRuleType( $result, $value ) {
                $this->assertEquals( $result, $this->getLang()->getPluralRuleType( $value ) );
        }
 
index 1abc375..896522b 100644 (file)
@@ -7,14 +7,20 @@
 
 /** Tests for MediaWiki languages/LanguageHy.php */
 class LanguageHyTest extends LanguageClassesTestCase {
-       /** @dataProvider providePlural */
-       function testPlural( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::convertPlural
+        */
+       public function testPlural( $result, $value ) {
                $forms = array( 'one', 'other' );
                $this->assertEquals( $result, $this->getLang()->convertPlural( $value, $forms ) );
        }
 
-       /** @dataProvider providePlural */
-       function testGetPluralRuleType( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::getPluralRuleType
+        */
+       public function testGetPluralRuleType( $result, $value ) {
                // This fails for 0, but I'm not sure why. Some voodoo going on here.
                $this->assertEquals( $result, $this->getLang()->getPluralRuleType( $value ) );
        }
index 291c59b..568a378 100644 (file)
@@ -7,14 +7,20 @@
 
 /** Tests for MediaWiki languages/classes/LanguageKsh.php */
 class LanguageKshTest extends LanguageClassesTestCase {
-       /** @dataProvider providePlural */
-       function testPlural( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::convertPlural
+        */
+       public function testPlural( $result, $value ) {
                $forms = array( 'one', 'other', 'zero' );
                $this->assertEquals( $result, $this->getLang()->convertPlural( $value, $forms ) );
        }
 
-       /** @dataProvider providePlural */
-       function testGetPluralRuleType( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::getPluralRuleType
+        */
+       public function testGetPluralRuleType( $result, $value ) {
                $this->assertEquals( $result, $this->getLang()->getPluralRuleType( $value ) );
        }
 
index 2fa40b5..10b3234 100644 (file)
@@ -7,14 +7,20 @@
 
 /** Tests for MediaWiki languages/classes/LanguageLn.php */
 class LanguageLnTest extends LanguageClassesTestCase {
-       /** @dataProvider providePlural */
-       function testPlural( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::convertPlural
+        */
+       public function testPlural( $result, $value ) {
                $forms = array( 'one', 'other' );
                $this->assertEquals( $result, $this->getLang()->convertPlural( $value, $forms ) );
        }
 
-       /** @dataProvider providePlural */
-       function testGetPluralRuleType( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::getPluralRuleType
+        */
+       public function testGetPluralRuleType( $result, $value ) {
                $this->assertEquals( $result, $this->getLang()->getPluralRuleType( $value ) );
        }
 
index e5a10b5..30642f6 100644 (file)
@@ -7,14 +7,20 @@
 
 /** Tests for MediaWiki languages/LanguageLt.php */
 class LanguageLtTest extends LanguageClassesTestCase {
-       /** @dataProvider providePlural */
-       function testPlural( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::convertPlural
+        */
+       public function testPlural( $result, $value ) {
                $forms = array( 'one', 'few', 'other' );
                $this->assertEquals( $result, $this->getLang()->convertPlural( $value, $forms ) );
        }
 
-       /** @dataProvider providePlural */
-       function testGetPluralRuleType( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::getPluralRuleType
+        */
+       public function testGetPluralRuleType( $result, $value ) {
                $this->assertEquals( $result, $this->getLang()->getPluralRuleType( $value ) );
        }
 
@@ -34,8 +40,11 @@ class LanguageLtTest extends LanguageClassesTestCase {
                );
        }
 
-       /** @dataProvider providePluralTwoForms */
-       function testOneFewPlural( $result, $value ) {
+       /**
+        * @dataProvider providePluralTwoForms
+        * @covers Language::convertPlural
+        */
+       public function testOneFewPlural( $result, $value ) {
                $forms = array( 'one', 'other' );
                // This fails for 21, but not sure why.
                $this->assertEquals( $result, $this->getLang()->convertPlural( $value, $forms ) );
index 368ac8c..c4d8a6f 100644 (file)
@@ -7,14 +7,20 @@
 
 /** Tests for MediaWiki languages/classes/LanguageLv.php */
 class LanguageLvTest extends LanguageClassesTestCase {
-       /** @dataProvider providePlural */
-       function testPlural( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::convertPlural
+        */
+       public function testPlural( $result, $value ) {
                $forms = array( 'zero', 'one', 'other' );
                $this->assertEquals( $result, $this->getLang()->convertPlural( $value, $forms ) );
        }
 
-       /** @dataProvider providePlural */
-       function testGetPluralRuleType( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::getPluralRuleType
+        */
+       public function testGetPluralRuleType( $result, $value ) {
                $this->assertEquals( $result, $this->getLang()->getPluralRuleType( $value ) );
        }
 
index f4eb99a..65e8fd7 100644 (file)
@@ -7,14 +7,20 @@
 
 /** Tests for MediaWiki languages/classes/LanguageMg.php */
 class LanguageMgTest extends LanguageClassesTestCase {
-       /** @dataProvider providePlural */
-       function testPlural( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::convertPlural
+        */
+       public function testPlural( $result, $value ) {
                $forms = array( 'one', 'other' );
                $this->assertEquals( $result, $this->getLang()->convertPlural( $value, $forms ) );
        }
 
-       /** @dataProvider providePlural */
-       function testGetPluralRuleType( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::getPluralRuleType
+        */
+       public function testGetPluralRuleType( $result, $value ) {
                $this->assertEquals( $result, $this->getLang()->getPluralRuleType( $value ) );
        }
 
index 0ae533d..7d47b37 100644 (file)
@@ -7,14 +7,20 @@
 
 /** Tests for MediaWiki languages/classes/LanguageMk.php */
 class LanguageMkTest extends LanguageClassesTestCase {
-       /** @dataProvider providePlural */
-       function testPlural( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::convertPlural
+        */
+       public function testPlural( $result, $value ) {
                $forms = array( 'one', 'other' );
                $this->assertEquals( $result, $this->getLang()->convertPlural( $value, $forms ) );
        }
 
-       /** @dataProvider providePlural */
-       function testGetPluralRuleType( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::getPluralRuleType
+        */
+       public function testGetPluralRuleType( $result, $value ) {
                $this->assertEquals( $result, $this->getLang()->getPluralRuleType( $value ) );
        }
 
index 057ca67..4fa45ce 100644 (file)
@@ -8,9 +8,12 @@
 /** Tests for MediaWiki languages/LanguageMl.php */
 class LanguageMlTest extends LanguageClassesTestCase {
 
-       /** see bug 29495 */
-       /** @dataProvider providerFormatNum */
-       function testFormatNum( $result, $value ) {
+       /**
+        * @dataProvider providerFormatNum
+        * @see bug 29495
+        * @covers Language::formatNum
+        */
+       public function testFormatNum( $result, $value ) {
                $this->assertEquals( $result, $this->getLang()->formatNum( $value ) );
        }
 
index 3b162b7..e0e54ca 100644 (file)
@@ -7,14 +7,20 @@
 
 /** Tests for MediaWiki languages/classes/LanguageMo.php */
 class LanguageMoTest extends LanguageClassesTestCase {
-       /** @dataProvider providePlural */
-       function testPlural( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::convertPlural
+        */
+       public function testPlural( $result, $value ) {
                $forms = array( 'one', 'few', 'other' );
                $this->assertEquals( $result, $this->getLang()->convertPlural( $value, $forms ) );
        }
 
-       /** @dataProvider providePlural */
-       function testGetPluralRuleType( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::getPluralRuleType
+        */
+       public function testGetPluralRuleType( $result, $value ) {
                $this->assertEquals( $result, $this->getLang()->getPluralRuleType( $value ) );
        }
 
index 12af2e8..96d2bc9 100644 (file)
@@ -7,14 +7,20 @@
 
 /** Tests for MediaWiki languages/classes/LanguageMt.php */
 class LanguageMtTest extends LanguageClassesTestCase {
-       /** @dataProvider providePlural */
-       function testPlural( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::convertPlural
+        */
+       public function testPlural( $result, $value ) {
                $forms = array( 'one', 'few', 'many', 'other' );
                $this->assertEquals( $result, $this->getLang()->convertPlural( $value, $forms ) );
        }
 
-       /** @dataProvider providePlural */
-       function testGetPluralRuleType( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::getPluralRuleType
+        */
+       public function testGetPluralRuleType( $result, $value ) {
                $this->assertEquals( $result, $this->getLang()->getPluralRuleType( $value ) );
        }
 
@@ -39,13 +45,16 @@ class LanguageMtTest extends LanguageClassesTestCase {
                );
        }
 
-       /** @dataProvider providerPluralTwoForms */
-       function testPluralTwoForms( $result, $value ) {
+       /**
+        * @dataProvider providePluralTwoForms
+        * @covers Language::convertPlural
+        */
+       public function testPluralTwoForms( $result, $value ) {
                $forms = array( 'one', 'other' );
                $this->assertEquals( $result, $this->getLang()->convertPlural( $value, $forms ) );
        }
 
-       public static function providerPluralTwoForms() {
+       public static function providePluralTwoForms() {
                return array(
                        array( 'other', 0 ),
                        array( 'one', 1 ),
index f783f2c..26bd691 100644 (file)
@@ -8,7 +8,11 @@
 /** Tests for MediaWiki languages/LanguageNl.php */
 class LanguageNlTest extends LanguageClassesTestCase {
 
-       function testFormatNum() {
+       /**
+        * @covers Language::formatNum
+        * @todo split into a test and a dataprovider
+        */
+       public function testFormatNum() {
                $this->assertEquals( '1.234.567', $this->getLang()->formatNum( '1234567' ) );
                $this->assertEquals( '12.345', $this->getLang()->formatNum( '12345' ) );
                $this->assertEquals( '1', $this->getLang()->formatNum( '1' ) );
index 1d62567..18efd73 100644 (file)
@@ -7,14 +7,20 @@
 
 /** Tests for MediaWiki languages/classes/LanguageNso.php */
 class LanguageNsoTest extends LanguageClassesTestCase {
-       /** @dataProvider providePlural */
-       function testPlural( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::convertPlural
+        */
+       public function testPlural( $result, $value ) {
                $forms = array( 'one', 'other' );
                $this->assertEquals( $result, $this->getLang()->convertPlural( $value, $forms ) );
        }
 
-       /** @dataProvider providePlural */
-       function testGetPluralRuleType( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::getPluralRuleType
+        */
+       public function testGetPluralRuleType( $result, $value ) {
                $this->assertEquals( $result, $this->getLang()->getPluralRuleType( $value ) );
        }
 
index 9ec2a88..d180037 100644 (file)
@@ -7,14 +7,20 @@
 
 /** Tests for MediaWiki languages/classes/LanguagePl.php */
 class LanguagePlTest extends LanguageClassesTestCase {
-       /** @dataProvider providePlural */
-       function testPlural( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::convertPlural
+        */
+       public function testPlural( $result, $value ) {
                $forms = array( 'one', 'few', 'many' );
                $this->assertEquals( $result, $this->getLang()->convertPlural( $value, $forms ) );
        }
 
-       /** @dataProvider providePlural */
-       function testGetPluralRuleType( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::getPluralRuleType
+        */
+       public function testGetPluralRuleType( $result, $value ) {
                $this->assertEquals( $result, $this->getLang()->getPluralRuleType( $value ) );
        }
 
@@ -39,13 +45,16 @@ class LanguagePlTest extends LanguageClassesTestCase {
                );
        }
 
-       /** @dataProvider providerPluralTwoForms */
-       function testPluralTwoForms( $result, $value ) {
+       /**
+        * @dataProvider providePluralTwoForms
+        * @covers Language::convertPlural
+        */
+       public function testPluralTwoForms( $result, $value ) {
                $forms = array( 'one', 'other' );
                $this->assertEquals( $result, $this->getLang()->convertPlural( $value, $forms ) );
        }
 
-       public static function providerPluralTwoForms() {
+       public static function providePluralTwoForms() {
                return array(
                        array( 'other', 0 ),
                        array( 'one', 1 ),
index 919a744..ae7816b 100644 (file)
@@ -7,14 +7,20 @@
 
 /** Tests for MediaWiki languages/classes/LanguageRo.php */
 class LanguageRoTest extends LanguageClassesTestCase {
-       /** @dataProvider providePlural */
-       function testPlural( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::convertPlural
+        */
+       public function testPlural( $result, $value ) {
                $forms = array( 'one', 'few', 'other' );
                $this->assertEquals( $result, $this->getLang()->convertPlural( $value, $forms ) );
        }
 
-       /** @dataProvider providePlural */
-       function testGetPluralRuleType( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::getPluralRuleType
+        */
+       public function testGetPluralRuleType( $result, $value ) {
                $this->assertEquals( $result, $this->getLang()->getPluralRuleType( $value ) );
        }
 
index bfd5074..e938be7 100644 (file)
@@ -8,14 +8,20 @@
 
 /** Tests for MediaWiki languages/classes/LanguageRu.php */
 class LanguageRuTest extends LanguageClassesTestCase {
-       /** @dataProvider providePlural */
-       function testPlural( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::convertPlural
+        */
+       public function testPlural( $result, $value ) {
                $forms = array( 'one', 'few', 'many', 'other' );
                $this->assertEquals( $result, $this->getLang()->convertPlural( $value, $forms ) );
        }
 
-       /** @dataProvider providePlural */
-       function testGetPluralRuleType( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::getPluralRuleType
+        */
+       public function testGetPluralRuleType( $result, $value ) {
                $this->assertEquals( $result, $this->getLang()->getPluralRuleType( $value ) );
        }
 
@@ -35,8 +41,11 @@ class LanguageRuTest extends LanguageClassesTestCase {
                );
        }
 
-       /** @dataProvider providePluralTwoForms */
-       function testPluralTwoForms( $result, $value ) {
+       /**
+        * @dataProvider providePluralTwoForms
+        * @covers Language::convertPlural
+        */
+       public function testPluralTwoForms( $result, $value ) {
                $forms = array( 'one', 'other' );
                $this->assertEquals( $result, $this->getLang()->convertPlural( $value, $forms ) );
        }
@@ -50,8 +59,11 @@ class LanguageRuTest extends LanguageClassesTestCase {
                );
        }
 
-       /** @dataProvider providerGrammar */
-       function testGrammar( $result, $word, $case ) {
+       /**
+        * @dataProvider providerGrammar
+        * @covers Language::convertGrammar
+        */
+       public function testGrammar( $result, $word, $case ) {
                $this->assertEquals( $result, $this->getLang()->convertGrammar( $word, $case ) );
        }
 
index 70efa3b..533aa2b 100644 (file)
@@ -7,14 +7,20 @@
 
 /** Tests for MediaWiki languages/classes/LanguageSe.php */
 class LanguageSeTest extends LanguageClassesTestCase {
-       /** @dataProvider providePlural */
-       function testPlural( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::convertPlural
+        */
+       public function testPlural( $result, $value ) {
                $forms = array( 'one', 'two', 'other' );
                $this->assertEquals( $result, $this->getLang()->convertPlural( $value, $forms ) );
        }
 
-       /** @dataProvider providePlural */
-       function testGetPluralRuleType( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::getPluralRuleType
+        */
+       public function testGetPluralRuleType( $result, $value ) {
                $this->assertEquals( $result, $this->getLang()->getPluralRuleType( $value ) );
        }
 
@@ -27,13 +33,16 @@ class LanguageSeTest extends LanguageClassesTestCase {
                );
        }
 
-       /** @dataProvider providerPluralTwoForms */
-       function testPluralTwoForms( $result, $value ) {
+       /**
+        * @dataProvider providePluralTwoForms
+        * @covers Language::convertPlural
+        */
+       public function testPluralTwoForms( $result, $value ) {
                $forms = array( 'one', 'other' );
                $this->assertEquals( $result, $this->getLang()->convertPlural( $value, $forms ) );
        }
 
-       public static function providerPluralTwoForms() {
+       public static function providePluralTwoForms() {
                return array(
                        array( 'other', 0 ),
                        array( 'one', 1 ),
index 589a369..bf6a14b 100644 (file)
@@ -7,14 +7,20 @@
 
 /** Tests for MediaWiki languages/classes/LanguageSgs.php */
 class LanguageSgsTest extends LanguageClassesTestCase {
-       /** @dataProvider providePluralAllForms */
-       function testPluralAllForms( $result, $value ) {
+       /**
+        * @dataProvider providePluralAllForms
+        * @covers Language::convertPlural
+        */
+       public function testPluralAllForms( $result, $value ) {
                $forms = array( 'one', 'two', 'few', 'other' );
                $this->assertEquals( $result, $this->getLang()->convertPlural( $value, $forms ) );
        }
 
-       /** @dataProvider providePluralAllForms */
-       function testGetPluralRuleType( $result, $value ) {
+       /**
+        * @dataProvider providePluralAllForms
+        * @covers Language::getPluralRuleType
+        */
+       public function testGetPluralRuleType( $result, $value ) {
                $this->assertEquals( $result, $this->getLang()->getPluralRuleType( $value ) );
        }
 
@@ -36,8 +42,11 @@ class LanguageSgsTest extends LanguageClassesTestCase {
                );
        }
 
-       /** @dataProvider providePluralTwoForms */
-       function testPluralTwoForms( $result, $value ) {
+       /**
+        * @dataProvider providePluralTwoForms
+        * @covers Language::convertPlural
+        */
+       public function testPluralTwoForms( $result, $value ) {
                $forms = array( 'one', 'other' );
                $this->assertEquals( $result, $this->getLang()->convertPlural( $value, $forms ) );
        }
index 9f34db5..6d2e25a 100644 (file)
@@ -7,14 +7,20 @@
 
 /** Tests for MediaWiki languages/classes/LanguageSh.php */
 class LanguageShTest extends LanguageClassesTestCase {
-       /** @dataProvider providePlural */
-       function testPlural( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::convertPlural
+        */
+       public function testPlural( $result, $value ) {
                $forms = array( 'one', 'few', 'many', 'other' );
                $this->assertEquals( $result, $this->getLang()->convertPlural( $value, $forms ) );
        }
 
-       /** @dataProvider providePlural */
-       function testGetPluralRuleType( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::getPluralRuleType
+        */
+       public function testGetPluralRuleType( $result, $value ) {
                $this->assertEquals( $result, $this->getLang()->getPluralRuleType( $value ) );
        }
 
index 3f32c66..cb8a13b 100644 (file)
@@ -8,14 +8,20 @@
 
 /** Tests for MediaWiki languages/classes/LanguageSk.php */
 class LanguageSkTest extends LanguageClassesTestCase {
-       /** @dataProvider providePlural */
-       function testPlural( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::convertPlural
+        */
+       public function testPlural( $result, $value ) {
                $forms = array( 'one', 'few', 'other' );
                $this->assertEquals( $result, $this->getLang()->convertPlural( $value, $forms ) );
        }
 
-       /** @dataProvider providePlural */
-       function testGetPluralRuleType( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::getPluralRuleType
+        */
+       public function testGetPluralRuleType( $result, $value ) {
                $this->assertEquals( $result, $this->getLang()->getPluralRuleType( $value ) );
        }
 
index 84c2e01..9783dd8 100644 (file)
@@ -8,14 +8,20 @@
 
 /** Tests for MediaWiki languages/classes/LanguageSl.php */
 class LanguageSlTest extends LanguageClassesTestCase {
-       /** @dataProvider providerPlural */
-       function testPlural( $result, $value ) {
+       /**
+        * @dataProvider providerPlural
+        * @covers Language::convertPlural
+        */
+       public function testPlural( $result, $value ) {
                $forms = array( 'one', 'two', 'few', 'other' );
                $this->assertEquals( $result, $this->getLang()->convertPlural( $value, $forms ) );
        }
 
-       /** @dataProvider providerPlural */
-       function testGetPluralRuleType( $result, $value ) {
+       /**
+        * @dataProvider providerPlural
+        * @covers Language::getPluralRuleType
+        */
+       public function testGetPluralRuleType( $result, $value ) {
                $this->assertEquals( $result, $this->getLang()->getPluralRuleType( $value ) );
        }
 
index 5819831..95cb333 100644 (file)
@@ -7,14 +7,20 @@
 
 /** Tests for MediaWiki languages/classes/LanguageSma.php */
 class LanguageSmaTest extends LanguageClassesTestCase {
-       /** @dataProvider providePlural */
-       function testPlural( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::convertPlural
+        */
+       public function testPlural( $result, $value ) {
                $forms = array( 'one', 'two', 'other' );
                $this->assertEquals( $result, $this->getLang()->convertPlural( $value, $forms ) );
        }
 
-       /** @dataProvider providePlural */
-       function testGetPluralRuleType( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::getPluralRuleType
+        */
+       public function testGetPluralRuleType( $result, $value ) {
                $this->assertEquals( $result, $this->getLang()->getPluralRuleType( $value ) );
        }
 
@@ -27,13 +33,16 @@ class LanguageSmaTest extends LanguageClassesTestCase {
                );
        }
 
-       /** @dataProvider providerPluralTwoForms */
-       function testPluralTwoForms( $result, $value ) {
+       /**
+        * @dataProvider providePluralTwoForms
+        * @covers Language::convertPlural
+        */
+       public function testPluralTwoForms( $result, $value ) {
                $forms = array( 'one', 'other' );
                $this->assertEquals( $result, $this->getLang()->convertPlural( $value, $forms ) );
        }
 
-       public static function providerPluralTwoForms() {
+       public static function providePluralTwoForms() {
                return array(
                        array( 'other', 0 ),
                        array( 'one', 1 ),
index 40d14e3..ab4d4ab 100644 (file)
  * @author Antoine Musso <hashar at free dot fr>
  * @copyright Copyright © 2011, Antoine Musso <hashar at free dot fr>
  * @file
+ *
+ * @todo methods in test class should be tidied:
+ *  - Should be split into separate test methods and data providers
+ *  - Tests for LanguageConverter and Language should probably be separate..
  */
 
 /** Tests for MediaWiki languages/LanguageSr.php */
 class LanguageSrTest extends LanguageClassesTestCase {
-       function testEasyConversions() {
+       /**
+        * @covers LanguageConverter::convertTo
+        */
+       public function testEasyConversions() {
                $this->assertCyrillic(
                        'шђчћжШЂЧЋЖ',
                        'Cyrillic guessing characters'
@@ -25,7 +32,10 @@ class LanguageSrTest extends LanguageClassesTestCase {
                );
        }
 
-       function testMixedConversions() {
+       /**
+        * @covers LanguageConverter::convertTo
+        */
+       public function testMixedConversions() {
                $this->assertCyrillic(
                        'шђчћжШЂЧЋЖ - šđčćž',
                        'Mostly cyrillic characters'
@@ -36,7 +46,10 @@ class LanguageSrTest extends LanguageClassesTestCase {
                );
        }
 
-       function testSameAmountOfLatinAndCyrillicGetConverted() {
+       /**
+        * @covers LanguageConverter::convertTo
+        */
+       public function testSameAmountOfLatinAndCyrillicGetConverted() {
                $this->assertConverted(
                        '4 latin: šđčć | 4 cyrillic: шђчћ',
                        'sr-ec'
@@ -49,8 +62,9 @@ class LanguageSrTest extends LanguageClassesTestCase {
 
        /**
         * @author Nikola Smolenski
+        * @covers LanguageConverter::convertTo
         */
-       function testConversionToCyrillic() {
+       public function testConversionToCyrillic() {
                //A simple convertion of Latin to Cyrillic
                $this->assertEquals( 'абвг',
                        $this->convertToCyrillic( 'abvg' )
@@ -89,7 +103,10 @@ class LanguageSrTest extends LanguageClassesTestCase {
                );
        }
 
-       function testConversionToLatin() {
+       /**
+        * @covers LanguageConverter::convertTo
+        */
+       public function testConversionToLatin() {
                //A simple convertion of Latin to Latin
                $this->assertEquals( 'abcd',
                        $this->convertToLatin( 'abcd' )
@@ -108,14 +125,20 @@ class LanguageSrTest extends LanguageClassesTestCase {
                );
        }
 
-       /** @dataProvider providePlural */
-       function testPlural( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::convertPlural
+        */
+       public function testPlural( $result, $value ) {
                $forms = array( 'one', 'few', 'many', 'other' );
                $this->assertEquals( $result, $this->getLang()->convertPlural( $value, $forms ) );
        }
 
-       /** @dataProvider providePlural */
-       function testGetPluralRuleType( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::getPluralRuleType
+        */
+       public function testGetPluralRuleType( $result, $value ) {
                $this->assertEquals( $result, $this->getLang()->getPluralRuleType( $value ) );
        }
 
@@ -135,8 +158,11 @@ class LanguageSrTest extends LanguageClassesTestCase {
                );
        }
 
-       /** @dataProvider providePluralTwoForms */
-       function testPluralTwoForms( $result, $value ) {
+       /**
+        * @dataProvider providePluralTwoForms
+        * @covers Language::convertPlural
+        */
+       public function testPluralTwoForms( $result, $value ) {
                $forms = array( 'one', 'other' );
                $this->assertEquals( $result, $this->getLang()->convertPlural( $value, $forms ) );
        }
@@ -157,7 +183,7 @@ class LanguageSrTest extends LanguageClassesTestCase {
         * @param $variant string Language variant 'sr-ec' or 'sr-el'
         * @param $msg string Optional message
         */
-       function assertUnConverted( $text, $variant, $msg = '' ) {
+       protected function assertUnConverted( $text, $variant, $msg = '' ) {
                $this->assertEquals(
                        $text,
                        $this->convertTo( $text, $variant ),
@@ -171,7 +197,7 @@ class LanguageSrTest extends LanguageClassesTestCase {
         * @param $variant string Language variant 'sr-ec' or 'sr-el'
         * @param $msg string Optional message
         */
-       function assertConverted( $text, $variant, $msg = '' ) {
+       protected function assertConverted( $text, $variant, $msg = '' ) {
                $this->assertNotEquals(
                        $text,
                        $this->convertTo( $text, $variant ),
@@ -184,7 +210,7 @@ class LanguageSrTest extends LanguageClassesTestCase {
         * using the cyrillic variant and converted to Latin when using
         * the Latin variant.
         */
-       function assertCyrillic( $text, $msg = '' ) {
+       protected function assertCyrillic( $text, $msg = '' ) {
                $this->assertUnConverted( $text, 'sr-ec', $msg );
                $this->assertConverted( $text, 'sr-el', $msg );
        }
@@ -194,14 +220,14 @@ class LanguageSrTest extends LanguageClassesTestCase {
         * using the Latin variant and converted to Cyrillic when using
         * the Cyrillic variant.
         */
-       function assertLatin( $text, $msg = '' ) {
+       protected function assertLatin( $text, $msg = '' ) {
                $this->assertUnConverted( $text, 'sr-el', $msg );
                $this->assertConverted( $text, 'sr-ec', $msg );
        }
 
 
        /** Wrapper for converter::convertTo() method*/
-       function convertTo( $text, $variant ) {
+       protected function convertTo( $text, $variant ) {
                return $this->getLang()
                        ->mConverter
                        ->convertTo(
@@ -209,11 +235,11 @@ class LanguageSrTest extends LanguageClassesTestCase {
                        );
        }
 
-       function convertToCyrillic( $text ) {
+       protected function convertToCyrillic( $text ) {
                return $this->convertTo( $text, 'sr-ec' );
        }
 
-       function convertToLatin( $text ) {
+       protected function convertToLatin( $text ) {
                return $this->convertTo( $text, 'sr-el' );
        }
 }
index 7a26780..78929e2 100644 (file)
@@ -1,7 +1,11 @@
 <?php
 
 class LanguageTest extends LanguageClassesTestCase {
-       function testLanguageConvertDoubleWidthToSingleWidth() {
+       /**
+        * @covers Language::convertDoubleWidth
+        * @covers Language::normalizeForSearch
+        */
+       public function testLanguageConvertDoubleWidthToSingleWidth() {
                $this->assertEquals(
                        "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
                        $this->getLang()->normalizeForSearch(
@@ -12,9 +16,10 @@ class LanguageTest extends LanguageClassesTestCase {
        }
 
        /**
-        * @dataProvider provideFormattableTimes
+        * @dataProvider provideFormattableTimes#
+        * @covers Language::formatTimePeriod
         */
-       function testFormatTimePeriod( $seconds, $format, $expected, $desc ) {
+       public function testFormatTimePeriod( $seconds, $format, $expected, $desc ) {
                $this->assertEquals( $expected, $this->getLang()->formatTimePeriod( $seconds, $format ), $desc );
        }
 
@@ -203,7 +208,10 @@ class LanguageTest extends LanguageClassesTestCase {
                );
        }
 
-       function testTruncate() {
+       /**
+        * @covers Language::truncate
+        */
+       public function testTruncate() {
                $this->assertEquals(
                        "XXX",
                        $this->getLang()->truncate( "1234567890", 0, 'XXX' ),
@@ -236,9 +244,10 @@ class LanguageTest extends LanguageClassesTestCase {
        }
 
        /**
-        * @dataProvider provideHTMLTruncateData()
+        * @dataProvider provideHTMLTruncateData
+        * @covers Language::truncateHTML
         */
-       function testTruncateHtml( $len, $ellipsis, $input, $expected ) {
+       public function testTruncateHtml( $len, $ellipsis, $input, $expected ) {
                // Actual HTML...
                $this->assertEquals(
                        $expected,
@@ -247,7 +256,7 @@ class LanguageTest extends LanguageClassesTestCase {
        }
 
        /**
-        * Array format is ($len, $ellipsis, $input, $expected)
+        * @return array format is ($len, $ellipsis, $input, $expected)
         */
        public static function provideHTMLTruncateData() {
                return array(
@@ -308,8 +317,9 @@ class LanguageTest extends LanguageClassesTestCase {
        /**
         * Test Language::isWellFormedLanguageTag()
         * @dataProvider provideWellFormedLanguageTags
+        * @covers Language::isWellFormedLanguageTag
         */
-       function testWellFormedLanguageTag( $code, $message = '' ) {
+       public function testWellFormedLanguageTag( $code, $message = '' ) {
                $this->assertTrue(
                        Language::isWellFormedLanguageTag( $code ),
                        "validating code $code $message"
@@ -359,8 +369,9 @@ class LanguageTest extends LanguageClassesTestCase {
        /**
         * Negative test for Language::isWellFormedLanguageTag()
         * @dataProvider provideMalformedLanguageTags
+        * @covers Language::isWellFormedLanguageTag
         */
-       function testMalformedLanguageTag( $code, $message = '' ) {
+       public function testMalformedLanguageTag( $code, $message = '' ) {
                $this->assertFalse(
                        Language::isWellFormedLanguageTag( $code ),
                        "validating that code $code is a malformed language tag - $message"
@@ -409,8 +420,9 @@ class LanguageTest extends LanguageClassesTestCase {
 
        /**
         * Negative test for Language::isWellFormedLanguageTag()
+        * @covers Language::isWellFormedLanguageTag
         */
-       function testLenientLanguageTag() {
+       public function testLenientLanguageTag() {
                $this->assertTrue(
                        Language::isWellFormedLanguageTag( 'pa_guru', true ),
                        'pa_guru is a well-formed language tag in lenient mode'
@@ -420,15 +432,19 @@ class LanguageTest extends LanguageClassesTestCase {
        /**
         * Test Language::isValidBuiltInCode()
         * @dataProvider provideLanguageCodes
+        * @covers Language::isValidBuiltInCode
         */
-       function testBuiltInCodeValidation( $code, $message = '' ) {
+       public function testBuiltInCodeValidation( $code, $message = '' ) {
                $this->assertTrue(
                        (bool)Language::isValidBuiltInCode( $code ),
                        "validating code $code $message"
                );
        }
 
-       function testBuiltInCodeValidationRejectUnderscore() {
+       /**
+        * @covers Language::isValidBuiltInCode
+        */
+       public function testBuiltInCodeValidationRejectUnderscore() {
                $this->assertFalse(
                        (bool)Language::isValidBuiltInCode( 'be_tarask' ),
                        "reject underscore in language code"
@@ -450,8 +466,9 @@ class LanguageTest extends LanguageClassesTestCase {
        /**
         * Test Language::isKnownLanguageTag()
         * @dataProvider provideKnownLanguageTags
+        * @covers Language::isKnownLanguageTag
         */
-       function testKnownLanguageTag( $code, $message = '' ) {
+       public function testKnownLanguageTag( $code, $message = '' ) {
                $this->assertTrue(
                        (bool)Language::isKnownLanguageTag( $code ),
                        "validating code $code - $message"
@@ -467,9 +484,9 @@ class LanguageTest extends LanguageClassesTestCase {
        }
 
        /**
-        * Test Language::isKnownLanguageTag()
+        * @covers Language::isKnownLanguageTag
         */
-       function testKnownCldrLanguageTag() {
+       public function testKnownCldrLanguageTag() {
                if ( !class_exists( 'LanguageNames' ) ) {
                        $this->markTestSkipped( 'The LanguageNames class is not available. The cldr extension is probably not installed.' );
                }
@@ -483,8 +500,9 @@ class LanguageTest extends LanguageClassesTestCase {
        /**
         * Negative tests for Language::isKnownLanguageTag()
         * @dataProvider provideUnKnownLanguageTags
+        * @covers Language::isKnownLanguageTag
         */
-       function testUnknownLanguageTag( $code, $message = '' ) {
+       public function testUnknownLanguageTag( $code, $message = '' ) {
                $this->assertFalse(
                        (bool)Language::isKnownLanguageTag( $code ),
                        "checking that code $code is invalid - $message"
@@ -501,31 +519,35 @@ class LanguageTest extends LanguageClassesTestCase {
        /**
         * Test too short timestamp
         * @expectedException MWException
+        * @covers Language::sprintfDate
         */
-       function testSprintfDateTooShortTimestamp() {
+       public function testSprintfDateTooShortTimestamp() {
                $this->getLang()->sprintfDate( 'xiY', '1234567890123' );
        }
 
        /**
         * Test too long timestamp
         * @expectedException MWException
+        * @covers Language::sprintfDate
         */
-       function testSprintfDateTooLongTimestamp() {
+       public function testSprintfDateTooLongTimestamp() {
                $this->getLang()->sprintfDate( 'xiY', '123456789012345' );
        }
 
        /**
         * Test too short timestamp
         * @expectedException MWException
+        * @covers Language::sprintfDate
         */
-       function testSprintfDateNotAllDigitTimestamp() {
+       public function testSprintfDateNotAllDigitTimestamp() {
                $this->getLang()->sprintfDate( 'xiY', '-1234567890123' );
        }
 
        /**
         * @dataProvider provideSprintfDateSamples
+        * @covers Language::sprintfDate
         */
-       function testSprintfDate( $format, $ts, $expected, $msg ) {
+       public function testSprintfDate( $format, $ts, $expected, $msg ) {
                $this->assertEquals(
                        $expected,
                        $this->getLang()->sprintfDate( $format, $ts ),
@@ -536,8 +558,9 @@ class LanguageTest extends LanguageClassesTestCase {
        /**
         * sprintfDate should always use UTC when no zone is given.
         * @dataProvider provideSprintfDateSamples
+        * @covers Language::sprintfDate
         */
-       function testSprintfDateNoZone( $format, $ts, $expected, $ignore, $msg ) {
+       public function testSprintfDateNoZone( $format, $ts, $expected, $ignore, $msg ) {
                $oldTZ = date_default_timezone_get();
                $res = date_default_timezone_set( 'Asia/Seoul' );
                if ( !$res ) {
@@ -556,8 +579,9 @@ class LanguageTest extends LanguageClassesTestCase {
        /**
         * sprintfDate should use passed timezone
         * @dataProvider provideSprintfDateSamples
+        * @covers Language::sprintfDate
         */
-       function testSprintfDateTZ( $format, $ts, $ignore, $expected, $msg ) {
+       public function testSprintfDateTZ( $format, $ts, $ignore, $expected, $msg ) {
                $tz = new DateTimeZone( 'Asia/Seoul' );
                if ( !$tz ) {
                        $this->markTestSkipped( "Error getting Timezone" );
@@ -957,8 +981,9 @@ class LanguageTest extends LanguageClassesTestCase {
 
        /**
         * @dataProvider provideFormatSizes
+        * @covers Language::formatSize
         */
-       function testFormatSize( $size, $expected, $msg ) {
+       public function testFormatSize( $size, $expected, $msg ) {
                $this->assertEquals(
                        $expected,
                        $this->getLang()->formatSize( $size ),
@@ -1019,8 +1044,9 @@ class LanguageTest extends LanguageClassesTestCase {
 
        /**
         * @dataProvider provideFormatBitrate
+        * @covers Language::formatBitrate
         */
-       function testFormatBitrate( $bps, $expected, $msg ) {
+       public function testFormatBitrate( $bps, $expected, $msg ) {
                $this->assertEquals(
                        $expected,
                        $this->getLang()->formatBitrate( $bps ),
@@ -1091,8 +1117,9 @@ class LanguageTest extends LanguageClassesTestCase {
 
        /**
         * @dataProvider provideFormatDuration
+        * @covers Language::formatDuration
         */
-       function testFormatDuration( $duration, $expected, $intervals = array() ) {
+       public function testFormatDuration( $duration, $expected, $intervals = array() ) {
                $this->assertEquals(
                        $expected,
                        $this->getLang()->formatDuration( $duration, $intervals ),
@@ -1227,8 +1254,9 @@ class LanguageTest extends LanguageClassesTestCase {
 
        /**
         * @dataProvider provideCheckTitleEncodingData
+        * @covers Language::checkTitleEncoding
         */
-       function testCheckTitleEncoding( $s ) {
+       public function testCheckTitleEncoding( $s ) {
                $this->assertEquals(
                        $s,
                        $this->getLang()->checkTitleEncoding( $s ),
@@ -1291,8 +1319,9 @@ class LanguageTest extends LanguageClassesTestCase {
 
        /**
         * @dataProvider provideRomanNumeralsData
+        * @covers Language::romanNumeral
         */
-       function testRomanNumerals( $num, $numerals ) {
+       public function testRomanNumerals( $num, $numerals ) {
                $this->assertEquals(
                        $numerals,
                        Language::romanNumeral( $num ),
@@ -1349,8 +1378,9 @@ class LanguageTest extends LanguageClassesTestCase {
 
        /**
         * @dataProvider providePluralData
+        * @covers Language::convertPlural
         */
-       function testConvertPlural( $expected, $number, $forms ) {
+       public function testConvertPlural( $expected, $number, $forms ) {
                $chosen = $this->getLang()->convertPlural( $number, $forms );
                $this->assertEquals( $expected, $chosen );
        }
@@ -1395,7 +1425,7 @@ class LanguageTest extends LanguageClassesTestCase {
         * @covers Language::translateBlockExpiry()
         * @dataProvider provideTranslateBlockExpiry
         */
-       function testTranslateBlockExpiry( $expectedData, $str, $desc ) {
+       public function testTranslateBlockExpiry( $expectedData, $str, $desc ) {
                $lang = $this->getLang();
                if ( is_array( $expectedData ) ) {
                        list( $func, $arg ) = $expectedData;
@@ -1427,7 +1457,7 @@ class LanguageTest extends LanguageClassesTestCase {
         * @covers Language::commafy()
         * @dataProvider provideCommafyData
         */
-       function testCommafy( $number, $numbersWithCommas ) {
+       public function testCommafy( $number, $numbersWithCommas ) {
                $this->assertEquals(
                        $numbersWithCommas,
                        $this->getLang()->commafy( $number ),
@@ -1454,7 +1484,10 @@ class LanguageTest extends LanguageClassesTestCase {
                );
        }
 
-       function testListToText() {
+       /**
+        * @covers Language::listToText
+        */
+       public function testListToText() {
                $lang = $this->getLang();
                $and = $lang->getMessageFromDB( 'and' );
                $s = $lang->getMessageFromDB( 'word-separator' );
@@ -1469,8 +1502,9 @@ class LanguageTest extends LanguageClassesTestCase {
 
        /**
         * @dataProvider provideIsSupportedLanguage
+        * @covers Language::isSupportedLanguage
         */
-       function testIsSupportedLanguage( $code, $expected, $comment ) {
+       public function testIsSupportedLanguage( $code, $expected, $comment ) {
                $this->assertEquals( $expected, Language::isSupportedLanguage( $code ), $comment );
        }
 
@@ -1485,8 +1519,9 @@ class LanguageTest extends LanguageClassesTestCase {
 
        /**
         * @dataProvider provideGetParentLanguage
+        * @covers Language::getParentLanguage
         */
-       function testGetParentLanguage( $code, $expected, $comment ) {
+       public function testGetParentLanguage( $code, $expected, $comment ) {
                $lang = Language::factory( $code );
                if ( is_null( $expected ) ) {
                        $this->assertNull( $lang->getParentLanguage(), $comment );
@@ -1507,8 +1542,9 @@ class LanguageTest extends LanguageClassesTestCase {
 
        /**
         * @dataProvider provideGetNamespaceAliases
+        * @covers Language::getNamespaceAliases
         */
-       function testGetNamespaceAliases( $languageCode, $subset ) {
+       public function testGetNamespaceAliases( $languageCode, $subset ) {
                $language = Language::factory( $languageCode );
                $aliases = $language->getNamespaceAliases();
                foreach ( $subset as $alias => $nsId ) {
@@ -1516,7 +1552,7 @@ class LanguageTest extends LanguageClassesTestCase {
                }
        }
 
-       function provideGetNamespaceAliases() {
+       public static function provideGetNamespaceAliases() {
                // TODO: Add tests for NS_PROJECT_TALK and GenderNamespaces
                return array(
                        array(
index 36446c4..e225af9 100644 (file)
@@ -7,14 +7,20 @@
 
 /** Tests for MediaWiki languages/classes/LanguageTi.php */
 class LanguageTiTest extends LanguageClassesTestCase {
-       /** @dataProvider providePlural */
-       function testPlural( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::convertPlural
+        */
+       public function testPlural( $result, $value ) {
                $forms = array( 'one', 'other' );
                $this->assertEquals( $result, $this->getLang()->convertPlural( $value, $forms ) );
        }
 
-       /** @dataProvider providePlural */
-       function testGetPluralRuleType( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::getPluralRuleType
+        */
+       public function testGetPluralRuleType( $result, $value ) {
                $this->assertEquals( $result, $this->getLang()->getPluralRuleType( $value ) );
        }
 
index 422ad43..7ac51c6 100644 (file)
@@ -7,14 +7,20 @@
 
 /** Tests for MediaWiki languages/classes/LanguageTl.php */
 class LanguageTlTest extends LanguageClassesTestCase {
-       /** @dataProvider providePlural */
-       function testPlural( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::convertPlural
+        */
+       public function testPlural( $result, $value ) {
                $forms = array( 'one', 'other' );
                $this->assertEquals( $result, $this->getLang()->convertPlural( $value, $forms ) );
        }
 
-       /** @dataProvider providePlural */
-       function testGetPluralRuleType( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::getPluralRuleType
+        */
+       public function testGetPluralRuleType( $result, $value ) {
                $this->assertEquals( $result, $this->getLang()->getPluralRuleType( $value ) );
        }
 
index 6358ac0..2c9905f 100644 (file)
@@ -16,8 +16,10 @@ class LanguageTrTest extends LanguageClassesTestCase {
         *  - Emperyan
         * @see http://en.wikipedia.org/wiki/Dotted_and_dotless_I
         * @dataProvider provideDottedAndDotlessI
+        * @covers Language::ucfirst
+        * @covers Language::lcfirst
         */
-       function testDottedAndDotlessI( $func, $input, $inputCase, $expected ) {
+       public function testDottedAndDotlessI( $func, $input, $inputCase, $expected ) {
                if ( $func == 'ucfirst' ) {
                        $res = $this->getLang()->ucfirst( $input );
                } elseif ( $func == 'lcfirst' ) {
index 3180d30..0783fcf 100644 (file)
@@ -8,14 +8,20 @@
 
 /** Tests for MediaWiki languages/classes/LanguageUk.php */
 class LanguageUkTest extends LanguageClassesTestCase {
-       /** @dataProvider providePlural */
-       function testPlural( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::convertPlural
+        */
+       public function testPlural( $result, $value ) {
                $forms = array( 'one', 'few', 'many', 'other' );
                $this->assertEquals( $result, $this->getLang()->convertPlural( $value, $forms ) );
        }
 
-       /** @dataProvider providePlural */
-       function testGetPluralRuleType( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::getPluralRuleType
+        */
+       public function testGetPluralRuleType( $result, $value ) {
                $this->assertEquals( $result, $this->getLang()->getPluralRuleType( $value ) );
        }
 
@@ -35,8 +41,11 @@ class LanguageUkTest extends LanguageClassesTestCase {
                );
        }
 
-       /** @dataProvider providePluralTwoForms */
-       function testPluralTwoForms( $result, $value ) {
+       /**
+        * @dataProvider providePluralTwoForms
+        * @covers Language::convertPlural
+        */
+       public function testPluralTwoForms( $result, $value ) {
                $forms = array( 'one', 'other' );
                $this->assertEquals( $result, $this->getLang()->convertPlural( $value, $forms ) );
        }
index 8ee95b7..13f57c1 100644 (file)
  * @copyright Copyright © 2012, Robin Pepermans
  * @copyright Copyright © 2011, Antoine Musso <hashar at free dot fr>
  * @file
+ *
+ * @todo methods in test class should be tidied:
+ *  - Should be split into separate test methods and data providers
+ *  - Tests for LanguageConverter and Language should probably be separate..
  */
 
 /** Tests for MediaWiki languages/LanguageUz.php */
@@ -17,8 +21,9 @@ class LanguageUzTest extends LanguageClassesTestCase {
 
        /**
         * @author Nikola Smolenski
+        * @covers LanguageConverter::convertTo
         */
-       function testConversionToCyrillic() {
+       public function testConversionToCyrillic() {
                // A convertion of Latin to Cyrillic
                $this->assertEquals( 'абвгғ',
                        $this->convertToCyrillic( 'abvggʻ' )
@@ -37,7 +42,10 @@ class LanguageUzTest extends LanguageClassesTestCase {
                );
        }
 
-       function testConversionToLatin() {
+       /**
+        * @covers LanguageConverter::convertTo
+        */
+       public function testConversionToLatin() {
                // A simple convertion of Latin to Latin
                $this->assertEquals( 'abdef',
                        $this->convertToLatin( 'abdef' )
@@ -55,7 +63,7 @@ class LanguageUzTest extends LanguageClassesTestCase {
         * @param $variant string Language variant 'uz-cyrl' or 'uz-latn'
         * @param $msg string Optional message
         */
-       function assertUnConverted( $text, $variant, $msg = '' ) {
+       protected function assertUnConverted( $text, $variant, $msg = '' ) {
                $this->assertEquals(
                        $text,
                        $this->convertTo( $text, $variant ),
@@ -69,7 +77,7 @@ class LanguageUzTest extends LanguageClassesTestCase {
         * @param $variant string Language variant 'uz-cyrl' or 'uz-latn'
         * @param $msg string Optional message
         */
-       function assertConverted( $text, $variant, $msg = '' ) {
+       protected function assertConverted( $text, $variant, $msg = '' ) {
                $this->assertNotEquals(
                        $text,
                        $this->convertTo( $text, $variant ),
@@ -82,7 +90,7 @@ class LanguageUzTest extends LanguageClassesTestCase {
         * using the cyrillic variant and converted to Latin when using
         * the Latin variant.
         */
-       function assertCyrillic( $text, $msg = '' ) {
+       protected function assertCyrillic( $text, $msg = '' ) {
                $this->assertUnConverted( $text, 'uz-cyrl', $msg );
                $this->assertConverted( $text, 'uz-latn', $msg );
        }
@@ -92,22 +100,22 @@ class LanguageUzTest extends LanguageClassesTestCase {
         * using the Latin variant and converted to Cyrillic when using
         * the Cyrillic variant.
         */
-       function assertLatin( $text, $msg = '' ) {
+       protected function assertLatin( $text, $msg = '' ) {
                $this->assertUnConverted( $text, 'uz-latn', $msg );
                $this->assertConverted( $text, 'uz-cyrl', $msg );
        }
 
 
        /** Wrapper for converter::convertTo() method*/
-       function convertTo( $text, $variant ) {
+       protected function convertTo( $text, $variant ) {
                return $this->getLang()->mConverter->convertTo( $text, $variant );
        }
 
-       function convertToCyrillic( $text ) {
+       protected function convertToCyrillic( $text ) {
                return $this->convertTo( $text, 'uz-cyrl' );
        }
 
-       function convertToLatin( $text ) {
+       protected function convertToLatin( $text ) {
                return $this->convertTo( $text, 'uz-latn' );
        }
 }
index ffa3375..d05196c 100644 (file)
@@ -7,14 +7,20 @@
 
 /** Tests for MediaWiki languages/classes/LanguageWa.php */
 class LanguageWaTest extends LanguageClassesTestCase {
-       /** @dataProvider providePlural */
-       function testPlural( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::convertPlural
+        */
+       public function testPlural( $result, $value ) {
                $forms = array( 'one', 'other' );
                $this->assertEquals( $result, $this->getLang()->convertPlural( $value, $forms ) );
        }
 
-       /** @dataProvider providePlural */
-       function testGetPluralRuleType( $result, $value ) {
+       /**
+        * @dataProvider providePlural
+        * @covers Language::getPluralRuleType
+        */
+       public function testGetPluralRuleType( $result, $value ) {
                $this->assertEquals( $result, $this->getLang()->getPluralRuleType( $value ) );
        }
 
index 3bf7414..7b3fb68 100644 (file)
@@ -4,6 +4,9 @@
  * @file
  */
 
+/**
+ * @covers CLDRPluralRuleEvaluator
+ */
 class CLDRPluralRuleEvaluatorTest extends MediaWikiTestCase {
        /**
         * @dataProvider validTestCases
index 163314e..83d8c71 100644 (file)
@@ -117,7 +117,7 @@ abstract class DumpTestCase extends MediaWikiLangTestCase {
         * @param $name string: name of the closing element to look for
         *           (e.g.: "mediawiki" when looking for </mediawiki>)
         *
-        * @return bool: true iff the end node could be found. false otherwise.
+        * @return bool: true if the end node could be found. false otherwise.
         */
        protected function skipToNodeEnd( $name ) {
                while ( $this->xml->read() ) {
index f4b61af..83d7701 100644 (file)
@@ -43,7 +43,7 @@ class MaintenanceFixup extends Maintenance {
        private $testCase;
 
        /**
-        * shutdownSimulated === true iff simulateShutdown has done it's work
+        * shutdownSimulated === true if simulateShutdown has done it's work
         *
         * @var bool
         */
@@ -130,6 +130,9 @@ class MaintenanceFixup extends Maintenance {
        }
 }
 
+/**
+ * @covers Maintenance
+ */
 class MaintenanceTest extends MediaWikiTestCase {
 
 
index bc2d737..0f36bc4 100644 (file)
@@ -6,6 +6,7 @@ require_once __DIR__ . "/../../../maintenance/backupPrefetch.inc";
  * Tests for BaseDump
  *
  * @group Dump
+ * @covers BaseDump
  */
 class BaseDumpTest extends MediaWikiTestCase {
 
index 653a114..15a928e 100644 (file)
@@ -7,6 +7,7 @@ require_once __DIR__ . "/../../../maintenance/backupTextPass.inc";
  *
  * @group Database
  * @group Dump
+ * @covers TextPassDumper
  */
 class TextPassDumperTest extends DumpTestCase {
 
index 98d8165..438281d 100644 (file)
@@ -4,6 +4,7 @@
  *
  * @group Database
  * @group Dump
+ * @covers BackupDumper
  */
 class BackupDumperLoggerTest extends DumpTestCase {
 
index 99bd270..c6094d9 100644 (file)
@@ -4,6 +4,7 @@
  *
  * @group Database
  * @group Dump
+ * @covers BackupDumper
  */
 class BackupDumperPageTest extends DumpTestCase {
 
index e8df199..42792d5 100644 (file)
@@ -70,6 +70,7 @@ class SemiMockedFetchText extends FetchText {
  *
  * @group Database
  * @group Dump
+ * @covers FetchText
  */
 class FetchTextTest extends MediaWikiTestCase {
 
index 2c84886..bb678af 100644 (file)
@@ -6,6 +6,7 @@ require_once __DIR__ . "/../../../maintenance/getSlaveServer.php";
  * Tests for getSlaveServer
  *
  * @group Database
+ * @covers GetSlaveServer
  */
 class GetSlaveServerTest extends MediaWikiTestCase {
 
index 850d39c..a385320 100644 (file)
@@ -5,7 +5,10 @@
  */
 class SideBarTest extends MediaWikiLangTestCase {
 
-       /** A skin template, reinitialized before each test */
+       /**
+        * A skin template, reinitialized before each test
+        * @var SkinTemplate
+        */
        private $skin;
        /** Local cache for sidebar messages */
        private $messages;
@@ -36,16 +39,12 @@ class SideBarTest extends MediaWikiLangTestCase {
                $this->skin->getContext()->setLanguage( Language::factory( 'en' ) );
        }
 
-       protected function tearDown() {
-               parent::tearDown();
-               $this->skin = null;
-       }
-
        /**
         * Internal helper to test the sidebar
         * @param $expected
         * @param $text
         * @param $message (Default: '')
+        * @todo this assert method to should be converted to a test using a dataprovider..
         */
        private function assertSideBar( $expected, $text, $message = '' ) {
                $bar = array();
@@ -53,7 +52,10 @@ class SideBarTest extends MediaWikiLangTestCase {
                $this->assertEquals( $expected, $bar, $message );
        }
 
-       function testSidebarWithOnlyTwoTitles() {
+       /**
+        * @covers SkinTemplate::addToSidebarPlain
+        */
+       public function testSidebarWithOnlyTwoTitles() {
                $this->assertSideBar(
                        array(
                                'Title1' => array(),
@@ -65,7 +67,10 @@ class SideBarTest extends MediaWikiLangTestCase {
                );
        }
 
-       function testExpandMessages() {
+       /**
+        * @covers SkinTemplate::addToSidebarPlain
+        */
+       public function testExpandMessages() {
                $this->assertSidebar(
                        array( 'Title' => array(
                                array(
@@ -81,7 +86,10 @@ class SideBarTest extends MediaWikiLangTestCase {
                );
        }
 
-       function testExternalUrlsRequireADescription() {
+       /**
+        * @covers SkinTemplate::addToSidebarPlain
+        */
+       public function testExternalUrlsRequireADescription() {
                $this->assertSidebar(
                        array( 'Title' => array(
                                # ** http://www.mediawiki.org/| Home
@@ -105,8 +113,9 @@ class SideBarTest extends MediaWikiLangTestCase {
        /**
         * bug 33321 - Make sure there's a | after transforming.
         * @group Database
+        * @covers SkinTemplate::addToSidebarPlain
         */
-       function testTrickyPipe() {
+       public function testTrickyPipe() {
                $this->assertSidebar(
                        array( 'Title' => array(
                                # The first 2 are skipped
@@ -151,7 +160,7 @@ class SideBarTest extends MediaWikiLangTestCase {
        /**
         * Simple test to verify our helper assertAttribs() is functional
         */
-       function testTestAttributesAssertionHelper() {
+       public function testTestAttributesAssertionHelper() {
                $this->setMwGlobals( array(
                        'wgNoFollowLinks' => true,
                        'wgExternalLinkTarget' => false,
@@ -167,7 +176,7 @@ class SideBarTest extends MediaWikiLangTestCase {
        /**
         * Test $wgNoFollowLinks in sidebar
         */
-       function testRespectWgnofollowlinks() {
+       public function testRespectWgnofollowlinks() {
                $this->setMwGlobals( 'wgNoFollowLinks', false );
 
                $attribs = $this->getAttribs();
@@ -180,7 +189,7 @@ class SideBarTest extends MediaWikiLangTestCase {
         * Test $wgExternaLinkTarget in sidebar
         * @dataProvider dataRespectExternallinktarget
         */
-       function testRespectExternallinktarget( $externalLinkTarget ) {
+       public function testRespectExternallinktarget( $externalLinkTarget ) {
                $this->setMwGlobals( 'wgExternalLinkTarget', $externalLinkTarget );
 
                $attribs = $this->getAttribs();
@@ -188,7 +197,7 @@ class SideBarTest extends MediaWikiLangTestCase {
                $this->assertEquals( $attribs['target'], $externalLinkTarget );
        }
 
-       function dataRespectExternallinktarget() {
+       public static function dataRespectExternallinktarget() {
                return array(
                        array( '_blank' ),
                        array( '_self' ),
index 205ea36..f5ff1d9 100644 (file)
@@ -1,6 +1,26 @@
 <?php
 class AutoLoaderTest extends MediaWikiTestCase {
 
+       protected function setUp() {
+               global $wgAutoloadLocalClasses, $wgAutoloadClasses;
+
+               parent::setUp();
+
+               // Fancy dance to trigger a rebuild of AutoLoader::$autoloadLocalClassesLower
+               $this->testLocalClasses = array(
+                       'TestAutoloadedLocalClass' => __DIR__ . '/../data/autoloader/TestAutoloadedLocalClass.php',
+                       'TestAutoloadedCamlClass' => __DIR__ . '/../data/autoloader/TestAutoloadedCamlClass.php',
+                       'TestAutoloadedSerializedClass' => __DIR__ . '/../data/autoloader/TestAutoloadedSerializedClass.php',
+               );
+               $this->setMwGlobals( 'wgAutoloadLocalClasses', $this->testLocalClasses + $wgAutoloadLocalClasses );
+               InstrumentedAutoLoader::resetAutoloadLocalClassesLower();
+
+               $this->testExtensionClasses = array(
+                       'TestAutoloadedClass' => __DIR__ . '/../data/autoloader/TestAutoloadedClass.php',
+               );
+               $this->setMwGlobals( 'wgAutoloadClasses', $this->testExtensionClasses + $wgAutoloadClasses );
+       }
+
        /**
         * Assert that there were no classes loaded that are not registered with the AutoLoader.
         *
@@ -53,4 +73,32 @@ class AutoLoaderTest extends MediaWikiTestCase {
                        'actual' => $actual,
                );
        }
+
+       function testCoreClass() {
+               $this->assertTrue( class_exists( 'TestAutoloadedLocalClass' ) );
+       }
+
+       function testExtensionClass() {
+               $this->assertTrue( class_exists( 'TestAutoloadedClass' ) );
+       }
+
+       function testWrongCaseClass() {
+               $this->assertTrue( class_exists( 'testautoLoadedcamlCLASS' ) );
+       }
+
+       function testWrongCaseSerializedClass() {
+               $dummyCereal = 'O:29:"testautoloadedserializedclass":0:{}';
+               $uncerealized = unserialize( $dummyCereal );
+               $this->assertFalse( $uncerealized instanceof __PHP_Incomplete_Class,
+                       "unserialize() can load classes case-insensitively.");
+       }
+}
+
+/**
+ * Cheater to poke protected members
+ */
+class InstrumentedAutoLoader extends AutoLoader {
+       static function resetAutoloadLocalClassesLower() {
+               self::$autoloadLocalClassesLower = null;
+       }
 }
index fe823fa..746cb70 100644 (file)
@@ -26,27 +26,98 @@ class ResourcesTest extends MediaWikiTestCase {
        }
 
        /**
-        * This ask the ResouceLoader for all registered files from modules
-        * created by ResourceLoaderFileModule (or one of its descendants).
-        *
-        *
-        * Since the raw data is stored in protected properties, we have to
-        * overrride this through ReflectionObject methods.
+        * @dataProvider provideMediaStylesheets
         */
-       public static function provideResourceFiles() {
+       public function testStyleMedia( $moduleName, $media, $filename, $css ) {
+               $cssText = CSSMin::minify( $css->cssText );
+
+               $this->assertTrue( strpos( $cssText, '@media' ) === false, 'Stylesheets should not both specify "media" and contain @media' );
+       }
+
+       /**
+        * Get all registered modules from ResouceLoader.
+        */
+       protected static function getAllModules() {
                global $wgEnableJavaScriptTest;
 
                // Test existance of test suite files as well
                // (can't use setUp or setMwGlobals because providers are static)
-               $live_wgEnableJavaScriptTest = $wgEnableJavaScriptTest;
+               $org_wgEnableJavaScriptTest = $wgEnableJavaScriptTest;
                $wgEnableJavaScriptTest = true;
 
-               // Array with arguments for the test function
-               $cases = array();
-
                // Initialize ResourceLoader
                $rl = new ResourceLoader();
 
+               $modules = array();
+
+               foreach ( $rl->getModuleNames() as $moduleName ) {
+                       $modules[$moduleName] = $rl->getModule( $moduleName );
+               }
+
+               // Restore settings
+               $wgEnableJavaScriptTest = $org_wgEnableJavaScriptTest;
+
+               return array(
+                       'modules' => $modules,
+                       'resourceloader' => $rl,
+                       'context' => new ResourceLoaderContext( $rl, new FauxRequest() )
+               );
+       }
+
+       /**
+        * Get all stylesheet files from modules that are an instance of
+        * ResourceLoaderFileModule (or one of its subclasses).
+        */
+       public static function provideMediaStylesheets() {
+               $data = self::getAllModules();
+               $cases = array();
+
+               foreach ( $data['modules'] as $moduleName => $module ) {
+                       if ( !$module instanceof ResourceLoaderFileModule ) {
+                               continue;
+                       }
+
+                       $reflectedModule = new ReflectionObject( $module );
+
+                       $getStyleFiles = $reflectedModule->getMethod( 'getStyleFiles' );
+                       $getStyleFiles->setAccessible( true );
+
+                       $readStyleFile = $reflectedModule->getMethod( 'readStyleFile' );
+                       $readStyleFile->setAccessible( true );
+
+                       $styleFiles = $getStyleFiles->invoke( $module, $data['context'] );
+
+                       $flip = $module->getFlip( $data['context'] );
+
+                       foreach ( $styleFiles as $media => $files ) {
+                               if ( $media && $media !== 'all' ) {
+                                       foreach ( $files as $file ) {
+                                               $cases[] = array(
+                                                       $moduleName,
+                                                       $media,
+                                                       $file,
+                                                       // XXX: Wrapped in an object to keep it out of PHPUnit output
+                                                       (object) array( 'cssText' => $readStyleFile->invoke( $module, $file, $flip ) ),
+                                               );
+                                       }
+                               }
+                       }
+               }
+
+               return $cases;
+       }
+
+       /**
+        * Get all resource files from modules that are an instance of
+        * ResourceLoaderFileModule (or one of its subclasses).
+        *
+        * Since the raw data is stored in protected properties, we have to
+        * overrride this through ReflectionObject methods.
+        */
+       public static function provideResourceFiles() {
+               $data = self::getAllModules();
+               $cases = array();
+
                // See also ResourceLoaderFileModule::__construct
                $filePathProps = array(
                        // Lists of file paths
@@ -65,8 +136,7 @@ class ResourcesTest extends MediaWikiTestCase {
                        ),
                );
 
-               foreach ( $rl->getModuleNames() as $moduleName ) {
-                       $module = $rl->getModule( $moduleName );
+               foreach ( $data['modules'] as $moduleName => $module ) {
                        if ( !$module instanceof ResourceLoaderFileModule ) {
                                continue;
                        }
@@ -117,14 +187,12 @@ class ResourcesTest extends MediaWikiTestCase {
                        foreach ( $files as $file ) {
                                $cases[] = array(
                                        $method->invoke( $module, $file ),
-                                       $module->getName(),
+                                       $moduleName,
                                        $file,
                                );
                        }
                }
 
-               // Restore settings
-               $wgEnableJavaScriptTest = $live_wgEnableJavaScriptTest;
 
                return $cases;
        }
index d3877e0..3ef2790 100644 (file)
@@ -38,7 +38,7 @@
                // making sure it is actually using text() and attr() (or something with the same effect)
 
                // Text escaping
-               html = '<div><span><html:msg key="properfoo"></span></div>';
+               html = '<div><span><html:msg key="properfoo" /></span></div>';
                $lc = $( html ).localize().find( 'span' );
 
                assert.strictEqual( $lc.text(), mw.msg( 'properfoo' ), 'Content is inserted as text, not as html.' );
@@ -63,7 +63,7 @@
                var html, $lc, x, sitename = 'Wikipedia';
 
                // Message key prefix
-               html = '<div><span title-msg="lorem"><html:msg key="ipsum"></span></div>';
+               html = '<div><span title-msg="lorem"><html:msg key="ipsum" /></span></div>';
                $lc = $( html ).localize( {
                        prefix: 'foo-'
                } ).find( 'span' );
@@ -73,7 +73,7 @@
 
                // Variable keys mapping
                x = 'bar';
-               html = '<div><span title-msg="title"><html:msg key="label"></span></div>';
+               html = '<div><span title-msg="title"><html:msg key="label" /></span></div>';
                $lc = $( html ).localize( {
                        keys: {
                                'title': 'foo-' + x + '-title',
@@ -85,7 +85,7 @@
                assert.strictEqual( $lc.text(), 'The Bars', 'Variable keys mapping - text' );
 
                // Passing parameteters to mw.msg
-               html = '<div><span><html:msg key="foo-welcome"></span></div>';
+               html = '<div><span><html:msg key="foo-welcome" /></span></div>';
                $lc = $( html ).localize( {
                        params: {
                                'foo-welcome': [sitename, 'yesterday']
@@ -96,7 +96,7 @@
 
                // Combination of options prefix, params and keys
                x = 'bazz';
-               html = '<div><span title-msg="title"><html:msg key="label"></span></div>';
+               html = '<div><span title-msg="title"><html:msg key="label" /></span></div>';
                $lc = $( html ).localize( {
                        prefix: 'foo-',
                        keys: {
index ba15fba..13c0efc 100644 (file)
                assert.equal( title.getUrl(), '/wiki/User_talk:John_Doe', 'Escaping in title and namespace for urls' );
        } );
 
-       QUnit.test( 'newFromImg', 28, function ( assert ) {
+       QUnit.test( 'newFromImg', 36, function ( assert ) {
                var title, i, thisCase, prefix,
                        cases = [
                                {
                                        prefixedText: 'File:Anticlockwise heliotrope\'s.jpg'
                                },
 
+                               {
+                                       url: '/wiki/images/thumb/8/80/Wikipedia-logo-v2.svg/langde-150px-Wikipedia-logo-v2.svg.png',
+                                       typeOfUrl: 'Normal hashed directory thumbnail with complex thumbnail parameters',
+                                       nameText: 'Wikipedia-logo-v2',
+                                       prefixedText: 'File:Wikipedia-logo-v2.svg'
+                               },
+
                                {
                                        url: '//upload.wikimedia.org/wikipedia/commons/thumb/8/80/Wikipedia-logo-v2.svg/150px-Wikipedia-logo-v2.svg.png',
                                        typeOfUrl: 'Commons thumbnail',
                                        prefixedText: 'File:Wikipedia-logo-v2.svg'
                                },
 
+                               {
+                                       url: '/wikipedia/commons/thumb/Wikipedia-logo-v2.svg/langde-150px-Wikipedia-logo-v2.svg.png',
+                                       typeOfUrl: 'Commons unhashed thumbnail with complex thumbnail parameters',
+                                       nameText: 'Wikipedia-logo-v2',
+                                       prefixedText: 'File:Wikipedia-logo-v2.svg'
+                               },
+
                                {
                                        url: '/wiki/images/Anticlockwise_heliotrope%27s.jpg',
                                        typeOfUrl: 'Unhashed local file',
index 96be3d1..f422bc1 100644 (file)
                } );
        } );
 
-       QUnit.asyncTest( 'getRights', 1, function ( assert ) {
+       QUnit.test( 'getRights', 2, function ( assert ) {
+               QUnit.stop();
+               QUnit.stop();
+
                mw.user.getRights( function ( rights ) {
                        assert.equal( $.type( rights ), 'array', 'Callback gets an array' );
                        QUnit.start();
                } );
+
+               mw.user.getRights().done( function ( rights ) {
+                       assert.equal( $.type( rights ), 'array', 'Using promise interface instead of callback' );
+                       QUnit.start();
+               } );
        } );
 }( mediaWiki, jQuery ) );