Merge "Update namespaces for Sindhi"
authorNikerabbit <niklas.laxstrom@gmail.com>
Wed, 28 Feb 2018 11:58:55 +0000 (11:58 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Wed, 28 Feb 2018 11:58:55 +0000 (11:58 +0000)
588 files changed:
.gitignore
.phpcs.xml
RELEASE-NOTES-1.31
autoload.php
composer.json
docs/hooks.txt
includes/ActorMigration.php [new file with mode: 0644]
includes/Block.php
includes/DefaultSettings.php
includes/DevelopmentSettings.php
includes/EditPage.php
includes/FileDeleteForm.php
includes/GlobalFunctions.php
includes/Linker.php
includes/MediaWikiServices.php
includes/Message.php
includes/OutputPage.php
includes/ProtectionForm.php
includes/Revision.php
includes/RevisionList.php
includes/ServiceWiring.php
includes/Storage/BlobStore.php
includes/Storage/RevisionStore.php
includes/Storage/SqlBlobStore.php
includes/Title.php
includes/actions/InfoAction.php
includes/api/ApiBase.php
includes/api/ApiCSPReport.php
includes/api/ApiMain.php
includes/api/ApiQueryAllDeletedRevisions.php
includes/api/ApiQueryAllImages.php
includes/api/ApiQueryAllRevisions.php
includes/api/ApiQueryAllUsers.php
includes/api/ApiQueryBase.php
includes/api/ApiQueryBlocks.php
includes/api/ApiQueryContributors.php
includes/api/ApiQueryDeletedRevisions.php
includes/api/ApiQueryDeletedrevs.php
includes/api/ApiQueryLogEvents.php
includes/api/ApiQueryRecentChanges.php
includes/api/ApiQueryRevisions.php
includes/api/ApiQueryUserContributions.php
includes/api/ApiStashEdit.php
includes/api/i18n/lt.json
includes/api/i18n/pt.json
includes/api/i18n/ru.json
includes/auth/AuthManager.php
includes/auth/AuthPluginPrimaryAuthenticationProvider.php
includes/cache/MessageCache.php
includes/cache/UserCache.php
includes/changes/ChangesList.php
includes/changes/RecentChange.php
includes/changetags/ChangeTags.php
includes/changetags/ChangeTagsLogItem.php
includes/content/AbstractContent.php
includes/content/ContentHandler.php
includes/db/DatabaseOracle.php
includes/deferred/SiteStatsUpdate.php
includes/exception/CannotCreateActorException.php [new file with mode: 0644]
includes/export/WikiExporter.php
includes/externalstore/ExternalStore.php
includes/externalstore/ExternalStoreDB.php
includes/externalstore/ExternalStoreHttp.php
includes/externalstore/ExternalStoreMedium.php
includes/externalstore/ExternalStoreMwstore.php
includes/filerepo/ForeignAPIRepo.php
includes/filerepo/file/ArchivedFile.php
includes/filerepo/file/LocalFile.php
includes/filerepo/file/OldLocalFile.php
includes/htmlform/fields/HTMLSelectAndOtherField.php
includes/import/ImportableOldRevision.php [new file with mode: 0644]
includes/import/ImportableOldRevisionImporter.php [new file with mode: 0644]
includes/import/ImportableUploadRevision.php [new file with mode: 0644]
includes/import/ImportableUploadRevisionImporter.php [new file with mode: 0644]
includes/import/OldRevisionImporter.php [new file with mode: 0644]
includes/import/UploadRevisionImporter.php [new file with mode: 0644]
includes/import/WikiRevision.php
includes/installer/DatabaseUpdater.php
includes/installer/MssqlUpdater.php
includes/installer/MysqlUpdater.php
includes/installer/OracleUpdater.php
includes/installer/PostgresUpdater.php
includes/installer/SqliteUpdater.php
includes/installer/i18n/bg.json
includes/installer/i18n/es.json
includes/installer/i18n/fi.json
includes/installer/i18n/ja.json
includes/installer/i18n/nan.json
includes/jobqueue/jobs/RecentChangesUpdateJob.php
includes/libs/CSSMin.php
includes/libs/objectcache/WANObjectCache.php
includes/libs/rdbms/ChronologyProtector.php
includes/libs/rdbms/database/Database.php
includes/libs/rdbms/database/IDatabase.php
includes/libs/rdbms/database/IMaintainableDatabase.php
includes/libs/rdbms/database/position/DBMasterPos.php
includes/libs/rdbms/database/position/MySQLMasterPos.php
includes/libs/rdbms/loadbalancer/ILoadBalancer.php
includes/libs/rdbms/loadbalancer/LoadBalancer.php
includes/libs/rdbms/loadmonitor/LoadMonitor.php
includes/logging/LogEntry.php
includes/logging/LogPage.php
includes/logging/LogPager.php
includes/logging/WikitextLogFormatter.php [new file with mode: 0644]
includes/media/Bitmap.php
includes/media/JpegMetadataExtractor.php
includes/page/Article.php
includes/page/WikiPage.php
includes/parser/CoreParserFunctions.php
includes/parser/DateFormatter.php
includes/parser/ParserOptions.php
includes/parser/StripState.php
includes/preferences/DefaultPreferencesFactory.php
includes/profiler/Profiler.php
includes/resourceloader/ResourceLoaderStartUpModule.php
includes/resourceloader/ResourceLoaderWikiModule.php
includes/revisiondelete/RevDelArchiveItem.php
includes/revisiondelete/RevDelArchivedFileItem.php
includes/revisiondelete/RevDelFileItem.php
includes/revisiondelete/RevDelList.php
includes/revisiondelete/RevDelLogItem.php
includes/revisiondelete/RevDelLogList.php
includes/revisiondelete/RevDelRevisionItem.php
includes/revisiondelete/RevisionDeleteUser.php
includes/shell/Command.php
includes/shell/FirejailCommand.php
includes/skins/QuickTemplate.php
includes/skins/Skin.php
includes/specialpage/ChangesListSpecialPage.php
includes/specials/SpecialBlock.php
includes/specials/SpecialContributions.php
includes/specials/SpecialEditTags.php
includes/specials/SpecialEditWatchlist.php
includes/specials/SpecialFileDuplicateSearch.php
includes/specials/SpecialLog.php
includes/specials/SpecialMIMEsearch.php
includes/specials/SpecialMovepage.php
includes/specials/SpecialNewpages.php
includes/specials/SpecialRecentchangeslinked.php
includes/specials/SpecialRedirect.php
includes/specials/SpecialRevisiondelete.php
includes/specials/SpecialUndelete.php
includes/specials/SpecialUserrights.php
includes/specials/pagers/ActiveUsersPager.php
includes/specials/pagers/BlockListPager.php
includes/specials/pagers/ContribsPager.php
includes/specials/pagers/DeletedContribsPager.php
includes/specials/pagers/ImageListPager.php
includes/specials/pagers/NewFilesPager.php
includes/specials/pagers/NewPagesPager.php
includes/specials/pagers/ProtectedPagesPager.php
includes/tidy/Balancer.php
includes/user/ExternalUserNames.php
includes/user/User.php
includes/user/UserIdentity.php
includes/user/UserIdentityValue.php
includes/watcheditem/WatchedItemQueryService.php
jsduck.json
languages/Language.php
languages/data/Names.php
languages/data/ZhConversion.php
languages/i18n/aeb-arab.json
languages/i18n/af.json
languages/i18n/ais.json
languages/i18n/aln.json
languages/i18n/am.json
languages/i18n/an.json
languages/i18n/ar.json
languages/i18n/arq.json
languages/i18n/ary.json
languages/i18n/arz.json
languages/i18n/as.json
languages/i18n/ast.json
languages/i18n/awa.json
languages/i18n/az.json
languages/i18n/azb.json
languages/i18n/ba.json
languages/i18n/bcc.json
languages/i18n/bcl.json
languages/i18n/be-tarask.json
languages/i18n/be.json
languages/i18n/bg.json
languages/i18n/bgn.json
languages/i18n/bho.json
languages/i18n/bjn.json
languages/i18n/bn.json
languages/i18n/bpy.json
languages/i18n/br.json
languages/i18n/bs.json
languages/i18n/ca.json
languages/i18n/ce.json
languages/i18n/ckb.json
languages/i18n/crh-cyrl.json
languages/i18n/crh-latn.json
languages/i18n/cs.json
languages/i18n/csb.json
languages/i18n/cy.json
languages/i18n/da.json
languages/i18n/de-ch.json
languages/i18n/de-formal.json
languages/i18n/de.json
languages/i18n/diq.json
languages/i18n/dsb.json
languages/i18n/dtp.json
languages/i18n/dty.json
languages/i18n/egl.json
languages/i18n/el.json
languages/i18n/en.json
languages/i18n/eo.json
languages/i18n/es-formal.json
languages/i18n/es.json
languages/i18n/et.json
languages/i18n/eu.json
languages/i18n/ext.json
languages/i18n/fa.json
languages/i18n/fi.json
languages/i18n/fo.json
languages/i18n/fr.json
languages/i18n/frc.json
languages/i18n/frp.json
languages/i18n/frr.json
languages/i18n/fy.json
languages/i18n/ga.json
languages/i18n/gan-hans.json
languages/i18n/gan-hant.json
languages/i18n/gd.json
languages/i18n/gl.json
languages/i18n/grc.json
languages/i18n/gsw.json
languages/i18n/gu.json
languages/i18n/hak.json
languages/i18n/he.json
languages/i18n/hi.json
languages/i18n/hif-latn.json
languages/i18n/hil.json
languages/i18n/hr.json
languages/i18n/hrx.json
languages/i18n/hsb.json
languages/i18n/ht.json
languages/i18n/hu.json
languages/i18n/hy.json
languages/i18n/ia.json
languages/i18n/id.json
languages/i18n/ie.json
languages/i18n/ilo.json
languages/i18n/io.json
languages/i18n/is.json
languages/i18n/it.json
languages/i18n/ja.json
languages/i18n/jam.json
languages/i18n/jut.json
languages/i18n/jv.json
languages/i18n/ka.json
languages/i18n/kab.json
languages/i18n/kbd-cyrl.json
languages/i18n/kiu.json
languages/i18n/kk-arab.json
languages/i18n/kk-cyrl.json
languages/i18n/kk-latn.json
languages/i18n/km.json
languages/i18n/ko.json
languages/i18n/krc.json
languages/i18n/ksh.json
languages/i18n/ku-latn.json
languages/i18n/kum.json
languages/i18n/lb.json
languages/i18n/lfn.json
languages/i18n/lg.json
languages/i18n/li.json
languages/i18n/lij.json
languages/i18n/lki.json
languages/i18n/lmo.json
languages/i18n/lrc.json
languages/i18n/lt.json
languages/i18n/lus.json
languages/i18n/lv.json
languages/i18n/lzh.json
languages/i18n/mai.json
languages/i18n/map-bms.json
languages/i18n/mdf.json
languages/i18n/mg.json
languages/i18n/min.json
languages/i18n/mk.json
languages/i18n/ml.json
languages/i18n/mn.json
languages/i18n/mr.json
languages/i18n/ms.json
languages/i18n/mt.json
languages/i18n/mwl.json
languages/i18n/myv.json
languages/i18n/nan.json
languages/i18n/nap.json
languages/i18n/nb.json
languages/i18n/nds-nl.json
languages/i18n/nds.json
languages/i18n/ne.json
languages/i18n/nl-informal.json
languages/i18n/nl.json
languages/i18n/nn.json
languages/i18n/oc.json
languages/i18n/or.json
languages/i18n/os.json
languages/i18n/pa.json
languages/i18n/pam.json
languages/i18n/pl.json
languages/i18n/pms.json
languages/i18n/pnb.json
languages/i18n/prg.json
languages/i18n/ps.json
languages/i18n/pt-br.json
languages/i18n/pt.json
languages/i18n/qqq.json
languages/i18n/qu.json
languages/i18n/rm.json
languages/i18n/ro.json
languages/i18n/roa-tara.json
languages/i18n/ru.json
languages/i18n/rue.json
languages/i18n/sa.json
languages/i18n/sah.json
languages/i18n/sat.json
languages/i18n/scn.json
languages/i18n/sco.json
languages/i18n/sd.json
languages/i18n/sdc.json
languages/i18n/sei.json
languages/i18n/ses.json
languages/i18n/sgs.json
languages/i18n/sh.json
languages/i18n/shn.json
languages/i18n/si.json
languages/i18n/sk.json
languages/i18n/skr-arab.json
languages/i18n/sl.json
languages/i18n/sli.json
languages/i18n/sq.json
languages/i18n/sr-ec.json
languages/i18n/sr-el.json
languages/i18n/stq.json
languages/i18n/su.json
languages/i18n/sv.json
languages/i18n/sw.json
languages/i18n/szl.json
languages/i18n/ta.json
languages/i18n/te.json
languages/i18n/tg-cyrl.json
languages/i18n/tg-latn.json
languages/i18n/th.json
languages/i18n/tk.json
languages/i18n/tl.json
languages/i18n/tr.json
languages/i18n/tt-cyrl.json
languages/i18n/tt-latn.json
languages/i18n/ug-arab.json
languages/i18n/uk.json
languages/i18n/ur.json
languages/i18n/uz.json
languages/i18n/vec.json
languages/i18n/vep.json
languages/i18n/vi.json
languages/i18n/vo.json
languages/i18n/vro.json
languages/i18n/wa.json
languages/i18n/war.json
languages/i18n/wo.json
languages/i18n/wuu.json
languages/i18n/xal.json
languages/i18n/xmf.json
languages/i18n/yi.json
languages/i18n/yo.json
languages/i18n/yue.json
languages/i18n/zea.json
languages/i18n/zgh.json [new file with mode: 0644]
languages/i18n/zh-hans.json
languages/i18n/zh-hant.json
languages/messages/MessagesCs.php
languages/messages/MessagesEs_formal.php [new file with mode: 0644]
maintenance/Maintenance.php
maintenance/archives/patch-actor-table.sql [new file with mode: 0644]
maintenance/checkLess.php
maintenance/cleanupUsersWithNoId.php
maintenance/deleteDefaultMessages.php
maintenance/deleteSelfExternals.php
maintenance/fixUserRegistration.php
maintenance/initEditCount.php
maintenance/jsduck/categories.json
maintenance/language/languages.inc
maintenance/language/zhtable/simp2trad.manual
maintenance/language/zhtable/toCN.manual
maintenance/language/zhtable/toHK.manual
maintenance/language/zhtable/toSimp.manual
maintenance/language/zhtable/toTW.manual
maintenance/language/zhtable/toTrad.manual
maintenance/language/zhtable/trad2simp.manual
maintenance/language/zhtable/tradphrases.manual
maintenance/migrateActors.php [new file with mode: 0644]
maintenance/migrateComments.php
maintenance/migrateUserGroup.php
maintenance/moveBatch.php
maintenance/mssql/archives/patch-actor-table.sql [new file with mode: 0644]
maintenance/mssql/tables.sql
maintenance/oracle/archives/patch-actor-table.sql [new file with mode: 0644]
maintenance/oracle/tables.sql
maintenance/orphans.php
maintenance/populateIpChanges.php
maintenance/populateLogSearch.php
maintenance/populateLogUsertext.php
maintenance/populateRevisionLength.php
maintenance/populateRevisionSha1.php
maintenance/postgres/archives/patch-actor-table.sql [new file with mode: 0644]
maintenance/postgres/tables.sql
maintenance/reassignEdits.php
maintenance/rebuildrecentchanges.php
maintenance/removeUnusedAccounts.php
maintenance/rollbackEdits.php
maintenance/sql.php
maintenance/sqlite/archives/patch-actor-table.sql [new file with mode: 0644]
maintenance/storage/compressOld.php
maintenance/storage/fixT22757.php
maintenance/tables.sql
maintenance/updateCollation.php
resources/Resources.php
resources/src/jquery/jquery.byteLength.js
resources/src/jquery/jquery.byteLimit.js [deleted file]
resources/src/jquery/jquery.lengthLimit.js [new file with mode: 0644]
resources/src/mediawiki.action/mediawiki.action.delete.file.js [new file with mode: 0644]
resources/src/mediawiki.action/mediawiki.action.delete.js [new file with mode: 0644]
resources/src/mediawiki.action/mediawiki.action.edit.js
resources/src/mediawiki.language/mediawiki.cldr.js
resources/src/mediawiki.legacy/protect.js
resources/src/mediawiki.less/mediawiki.mixins.less
resources/src/mediawiki.rcfilters/mw.rcfilters.Controller.js
resources/src/mediawiki.rcfilters/mw.rcfilters.init.js
resources/src/mediawiki.rcfilters/styles/mw.rcfilters.less
resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.ChangesListWrapperWidget.js
resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.MenuSelectWidget.js
resources/src/mediawiki.special/mediawiki.special.apisandbox.js
resources/src/mediawiki.special/mediawiki.special.edittags.js
resources/src/mediawiki.special/mediawiki.special.movePage.js
resources/src/mediawiki.special/mediawiki.special.revisionDelete.js [new file with mode: 0644]
resources/src/mediawiki.special/mediawiki.special.undelete.js
resources/src/mediawiki.special/mediawiki.special.unwatchedPages.css
resources/src/mediawiki.special/mediawiki.special.unwatchedPages.js
resources/src/mediawiki.special/mediawiki.special.upload.js
resources/src/mediawiki.special/mediawiki.special.userrights.js
resources/src/mediawiki.widgets.visibleByteLimit/mediawiki.widgets.visibleByteLimit.js [deleted file]
resources/src/mediawiki.widgets.visibleLengthLimit/mediawiki.widgets.visibleLengthLimit.js [new file with mode: 0644]
resources/src/mediawiki.widgets/mw.widgets.TitleInputWidget.js
resources/src/mediawiki/htmlform/selectandother.js
resources/src/mediawiki/mediawiki.String.js [new file with mode: 0644]
resources/src/mediawiki/mediawiki.Title.js
resources/src/mediawiki/mediawiki.inspect.js
resources/src/mediawiki/mediawiki.js
resources/src/startup.js
tests/integration/includes/http/MWHttpRequestTestCase.php
tests/integration/includes/shell/FirejailCommandTest.php
tests/parser/ParserTestRunner.php
tests/parser/PhpunitTestRecorder.php
tests/phpunit/MediaWikiPHPUnitTestListener.php
tests/phpunit/MediaWikiTestCase.php
tests/phpunit/data/media/jpeg-segment-loop1.jpg [new file with mode: 0644]
tests/phpunit/data/media/jpeg-segment-loop2.jpg [new file with mode: 0644]
tests/phpunit/includes/ActorMigrationTest.php [new file with mode: 0644]
tests/phpunit/includes/BlockTest.php
tests/phpunit/includes/CommentStoreTest.php
tests/phpunit/includes/FauxRequestTest.php
tests/phpunit/includes/GlobalFunctions/wfArrayFilterTest.php
tests/phpunit/includes/MediaWikiVersionFetcherTest.php
tests/phpunit/includes/PageArchiveTest.php
tests/phpunit/includes/RevisionDbTestBase.php
tests/phpunit/includes/RevisionTest.php
tests/phpunit/includes/SanitizerValidateEmailTest.php
tests/phpunit/includes/Storage/RevisionStoreDbTest.php
tests/phpunit/includes/Storage/RevisionStoreRecordTest.php
tests/phpunit/includes/Storage/RevisionStoreTest.php
tests/phpunit/includes/TitleArrayFromResultTest.php
tests/phpunit/includes/TitleMethodsTest.php
tests/phpunit/includes/TitlePermissionTest.php
tests/phpunit/includes/WikiReferenceTest.php
tests/phpunit/includes/XmlJsTest.php
tests/phpunit/includes/api/ApiBaseTest.php
tests/phpunit/includes/api/ApiPageSetTest.php
tests/phpunit/includes/api/ApiQueryRecentChangesIntegrationTest.php
tests/phpunit/includes/api/ApiQueryWatchlistIntegrationTest.php
tests/phpunit/includes/api/query/ApiQueryContinueTestBase.php
tests/phpunit/includes/api/query/ApiQueryUserContributionsTest.php [new file with mode: 0644]
tests/phpunit/includes/auth/AuthManagerTest.php
tests/phpunit/includes/auth/CheckBlocksSecondaryAuthenticationProviderTest.php
tests/phpunit/includes/auth/EmailNotificationSecondaryAuthenticationProviderTest.php
tests/phpunit/includes/changes/RecentChangeTest.php
tests/phpunit/includes/composer/ComposerVersionNormalizerTest.php
tests/phpunit/includes/config/EtcdConfigTest.php
tests/phpunit/includes/debug/logger/monolog/LogstashFormatterTest.php
tests/phpunit/includes/deferred/MWCallableUpdateTest.php
tests/phpunit/includes/deferred/TransactionRoundDefiningUpdateTest.php
tests/phpunit/includes/externalstore/ExternalStoreFactoryTest.php
tests/phpunit/includes/htmlform/HTMLRestrictionsFieldTest.php
tests/phpunit/includes/import/ImportTest.php
tests/phpunit/includes/jobqueue/JobQueueMemoryTest.php
tests/phpunit/includes/libs/ArrayUtilsTest.php
tests/phpunit/includes/libs/CSSMinTest.php
tests/phpunit/includes/libs/DeferredStringifierTest.php
tests/phpunit/includes/libs/DnsSrvDiscovererTest.php
tests/phpunit/includes/libs/GenericArrayObjectTest.php
tests/phpunit/includes/libs/HashRingTest.php
tests/phpunit/includes/libs/HtmlArmorTest.php
tests/phpunit/includes/libs/IEUrlExtensionTest.php
tests/phpunit/includes/libs/IPTest.php
tests/phpunit/includes/libs/JavaScriptMinifierTest.php
tests/phpunit/includes/libs/MWMessagePackTest.php
tests/phpunit/includes/libs/MapCacheLRUTest.php
tests/phpunit/includes/libs/MemoizedCallableTest.php
tests/phpunit/includes/libs/ProcessCacheLRUTest.php
tests/phpunit/includes/libs/SamplingStatsdClientTest.php
tests/phpunit/includes/libs/StringUtilsTest.php
tests/phpunit/includes/libs/TimingTest.php
tests/phpunit/includes/libs/XhprofDataTest.php
tests/phpunit/includes/libs/XhprofTest.php
tests/phpunit/includes/libs/XmlTypeCheckTest.php
tests/phpunit/includes/libs/http/HttpAcceptNegotiatorTest.php
tests/phpunit/includes/libs/http/HttpAcceptParserTest.php
tests/phpunit/includes/libs/mime/MimeAnalyzerTest.php
tests/phpunit/includes/libs/objectcache/CachedBagOStuffTest.php
tests/phpunit/includes/libs/objectcache/HashBagOStuffTest.php
tests/phpunit/includes/libs/objectcache/WANObjectCacheTest.php
tests/phpunit/includes/libs/rdbms/TransactionProfilerTest.php
tests/phpunit/includes/libs/rdbms/connectionmanager/ConnectionManagerTest.php
tests/phpunit/includes/libs/rdbms/connectionmanager/SessionConsistentConnectionManagerTest.php
tests/phpunit/includes/libs/rdbms/database/DBConnRefTest.php
tests/phpunit/includes/libs/rdbms/database/DatabaseDomainTest.php
tests/phpunit/includes/libs/rdbms/database/DatabaseMysqlBaseTest.php
tests/phpunit/includes/libs/rdbms/database/DatabaseSQLTest.php
tests/phpunit/includes/libs/rdbms/database/DatabaseTest.php
tests/phpunit/includes/libs/xmp/XMPTest.php
tests/phpunit/includes/libs/xmp/XMPValidateTest.php
tests/phpunit/includes/logging/LogFormatterTestCase.php
tests/phpunit/includes/media/JpegMetadataExtractorTest.php
tests/phpunit/includes/objectcache/RedisBagOStuffTest.php
tests/phpunit/includes/page/WikiPageDbTestBase.php
tests/phpunit/includes/parser/ParserIntegrationTest.php
tests/phpunit/includes/password/UserPasswordPolicyTest.php
tests/phpunit/includes/registration/VersionCheckerTest.php
tests/phpunit/includes/resourceloader/DerivativeResourceLoaderContextTest.php
tests/phpunit/includes/resourceloader/MessageBlobStoreTest.php
tests/phpunit/includes/resourceloader/ResourceLoaderClientHtmlTest.php
tests/phpunit/includes/resourceloader/ResourceLoaderContextTest.php
tests/phpunit/includes/resourceloader/ResourceLoaderSkinModuleTest.php
tests/phpunit/includes/search/SearchSuggestionSetTest.php
tests/phpunit/includes/services/ServiceContainerTest.php
tests/phpunit/includes/shell/CommandFactoryTest.php
tests/phpunit/includes/shell/CommandTest.php
tests/phpunit/includes/shell/FirejailCommandTest.php
tests/phpunit/includes/shell/ShellTest.php
tests/phpunit/includes/site/FileBasedSiteLookupTest.php
tests/phpunit/includes/site/MediaWikiPageNameNormalizerTest.php
tests/phpunit/includes/site/SiteExporterTest.php
tests/phpunit/includes/site/SiteImporterTest.php
tests/phpunit/includes/site/SitesCacheFileBuilderTest.php
tests/phpunit/includes/sparql/SparqlClientTest.php
tests/phpunit/includes/specialpage/ChangesListSpecialPageTest.php
tests/phpunit/includes/user/UserGroupMembershipTest.php
tests/phpunit/includes/user/UserTest.php
tests/phpunit/includes/utils/AvroValidatorTest.php
tests/phpunit/includes/utils/ClassCollectorTest.php
tests/phpunit/includes/utils/FileContentsHasherTest.php
tests/phpunit/includes/utils/MWCryptHashTest.php
tests/phpunit/includes/utils/MWRestrictionsTest.php
tests/phpunit/includes/utils/UIDGeneratorTest.php
tests/phpunit/includes/utils/ZipDirectoryReaderTest.php
tests/phpunit/includes/watcheditem/WatchedItemQueryServiceUnitTest.php
tests/phpunit/includes/watcheditem/WatchedItemStoreUnitTest.php
tests/phpunit/languages/LanguageCodeTest.php
tests/phpunit/languages/LanguageTest.php
tests/phpunit/languages/SpecialPageAliasTest.php
tests/phpunit/maintenance/BenchmarkerTest.php
tests/phpunit/maintenance/fetchTextTest.php
tests/phpunit/phpunit.php
tests/phpunit/structure/AvailableRightsTest.php
tests/phpunit/structure/ExtensionJsonValidationTest.php
tests/phpunit/structure/StructureTest.php
tests/qunit/QUnitTestResources.php
tests/qunit/suites/resources/jquery/jquery.byteLength.test.js [deleted file]
tests/qunit/suites/resources/jquery/jquery.byteLimit.test.js [deleted file]
tests/qunit/suites/resources/jquery/jquery.lengthLimit.test.js [new file with mode: 0644]
tests/qunit/suites/resources/mediawiki/mediawiki.String.byteLength.test.js [new file with mode: 0644]
tests/qunit/suites/resources/mediawiki/mediawiki.String.trimByteLength.test.js [new file with mode: 0644]
tests/qunit/suites/resources/startup.test.js
tests/selenium/README.md

index bb3a946..0112cf3 100644 (file)
@@ -35,6 +35,8 @@ sftp-config.json
 /images/timeline
 ## Extension:Score
 /images/lilypond
+## Extension:TimedMediaHandler
+/images/transcoded
 /images/tmp
 /maintenance/.mweval_history
 /maintenance/.mwsql_history
index 7f90f27..cd55428 100644 (file)
@@ -7,7 +7,6 @@
                <exclude name="MediaWiki.Commenting.FunctionComment.MissingDocumentationPublic" />
                <exclude name="MediaWiki.Commenting.FunctionComment.MissingParamTag" />
                <exclude name="MediaWiki.Commenting.FunctionComment.MissingReturn" />
-               <exclude name="MediaWiki.Commenting.FunctionComment.ParamNameNoMatch" />
                <exclude name="MediaWiki.Commenting.FunctionComment.ExtraParamComment" />
                <exclude name="MediaWiki.Commenting.FunctionComment.WrongStyle" />
                <exclude name="MediaWiki.Commenting.IllegalSingleLineComment.IllegalSingleLineCommentStart" />
@@ -29,6 +28,8 @@
                <exclude name="MediaWiki.Commenting.FunctionComment.SpacingDocTag" />
                <exclude name="Squiz.Scope.MethodScope.Missing" />
                <exclude name="Squiz.Scope.MemberVarScope.Missing" />
+               <exclude name="MediaWiki.Commenting.MissingCovers.MissingCovers" />
+               <exclude name="MediaWiki.Usage.AssignmentInReturn.AssignmentInReturn" />
        </rule>
        <rule ref="MediaWiki.NamingConventions.PrefixedGlobalFunctions">
                <properties>
index f2d0af4..8113314 100644 (file)
@@ -51,6 +51,18 @@ production.
 * Style tags with a 'data-mw-deduplicate' attribute will be deduplicated as a
   ParserOutput::getText() post-cache transformation. This may be disabled by
   passing 'deduplicateStyles' => false to that method.
+* The identity of the logged-in or IP "actor" for logged actions is being moved
+  into a new actor table, with the rows in tables such as revision and logging
+  referring to the actor ID instead of storing the user ID and name/IP in
+  every row.
+  * This is currently gated by $wgActorTableSchemaMigrationStage. Most wikis
+    can set this to MIGRATION_NEW and run maintenance/migrateActors.php as
+    soon as any necessary extensions are updated.
+  * Most code accessing rows for logged actions from the database should use
+    the relevant getQueryInfo() methods to get the information needed to build
+    the SQL query. The ActorMigration class may also be used to get feature-flagged
+    information needed to access actor-related fields during the migration
+    period.
 
 === External library changes in 1.31 ===
 
@@ -103,8 +115,10 @@ changes to languages because of Phabricator reports.
 * (T186359) New language support: Siberian Tatar [cебертатар] (sty).
 * (T186635) New language support: Guianan Creole (gcr).
 * (T186647) New language support: Kumyk [къумукъ] (kum).
+* (T187750) New language support: Spanish formal address (es-formal).
 
 === Other changes in 1.31 ===
+* Browser support for Internet Explorer 10 was lowered from Grade A to Grade C.
 * Introducing multi-content-revision capability into the storage layer. For details,
   see <https://www.mediawiki.org/wiki/Requests_for_comment/Multi-Content_Revisions>.
 * The Revision class was deprecated in favor of RevisionStore, BlobStore, and
@@ -236,8 +250,22 @@ changes to languages because of Phabricator reports.
   * CommentStore::getCommentLegacy
   * CommentStore::insert
   * CommentStore::insertWithTemplate
+* The following methods in Title have been renamed, and the old ones are deprecated:
+  * Title::getSkinFromCssJsSubpage – use ::getSkinFromConfigSubpage
+  * Title::isCssOrJsPage – use ::isSiteConfigPage
+  * Title::isCssJsSubpage – use ::isUserConfigPage
+  * Title::isCssSubpage – use ::isUserCssConfigPage
+  * Title::isJsSubpage – use ::isUserJsConfigPage
+* The following variables and method in EditPage, deprecated in MediaWiki 1.30, were removed:
+  * $isCssJsSubpage — use ::isUserConfigPage()
+  * $isCssSubpage — use ::isUserCssConfigPage()
+  * $isJsSubpage — use ::isUserJsConfigPage()
+  * $isWrongCaseCssJsPage – use ::isWrongCaseUserConfigPage()
 * The method ResourceLoaderModule::getPosition(), deprecated in 1.29, has been removed.
 * The DeferredStringifier class is deprecated, use Message::listParam() instead.
+* The type string for the parameter $lang of DateFormatter::getInstance is
+  deprecated.
+* The global functions wfProfileIn and wfProfileOut, deprecated in 1.25, have been removed.
 
 == Compatibility ==
 MediaWiki 1.31 requires PHP 5.5.9 or later. Although HHVM 3.18.5 or later is supported,
index 9042f7b..d8283d6 100644 (file)
@@ -11,6 +11,7 @@ $wgAutoloadLocalClasses = [
        'Action' => __DIR__ . '/includes/actions/Action.php',
        'ActiveUsersPager' => __DIR__ . '/includes/specials/pagers/ActiveUsersPager.php',
        'ActivityUpdateJob' => __DIR__ . '/includes/jobqueue/jobs/ActivityUpdateJob.php',
+       'ActorMigration' => __DIR__ . '/includes/ActorMigration.php',
        'AddRFCAndPMIDInterwiki' => __DIR__ . '/maintenance/addRFCandPMIDInterwiki.php',
        'AddSite' => __DIR__ . '/maintenance/addSite.php',
        'AjaxDispatcher' => __DIR__ . '/includes/AjaxDispatcher.php',
@@ -220,6 +221,7 @@ $wgAutoloadLocalClasses = [
        'CachedAction' => __DIR__ . '/includes/actions/CachedAction.php',
        'CachedBagOStuff' => __DIR__ . '/includes/libs/objectcache/CachedBagOStuff.php',
        'CachingSiteStore' => __DIR__ . '/includes/site/CachingSiteStore.php',
+       'CannotCreateActorException' => __DIR__ . '/includes/exception/CannotCreateActorException.php',
        'CapsCleanup' => __DIR__ . '/maintenance/cleanupCaps.php',
        'CategoriesRdf' => __DIR__ . '/includes/CategoriesRdf.php',
        'Category' => __DIR__ . '/includes/Category.php',
@@ -647,6 +649,10 @@ $wgAutoloadLocalClasses = [
        'ImportStringSource' => __DIR__ . '/includes/import/ImportStringSource.php',
        'ImportTextFiles' => __DIR__ . '/maintenance/importTextFiles.php',
        'ImportTitleFactory' => __DIR__ . '/includes/title/ImportTitleFactory.php',
+       'ImportableOldRevision' => __DIR__ . '/includes/import/ImportableOldRevision.php',
+       'ImportableOldRevisionImporter' => __DIR__ . '/includes/import/ImportableOldRevisionImporter.php',
+       'ImportableUploadRevision' => __DIR__ . '/includes/import/ImportableUploadRevision.php',
+       'ImportableUploadRevisionImporter' => __DIR__ . '/includes/import/ImportableUploadRevisionImporter.php',
        'IncludableSpecialPage' => __DIR__ . '/includes/specialpage/IncludableSpecialPage.php',
        'IndexPager' => __DIR__ . '/includes/pager/IndexPager.php',
        'InfoAction' => __DIR__ . '/includes/actions/InfoAction.php',
@@ -1016,6 +1022,7 @@ $wgAutoloadLocalClasses = [
        'MessageContent' => __DIR__ . '/includes/content/MessageContent.php',
        'MessageLocalizer' => __DIR__ . '/languages/MessageLocalizer.php',
        'MessageSpecifier' => __DIR__ . '/includes/libs/MessageSpecifier.php',
+       'MigrateActors' => __DIR__ . '/maintenance/migrateActors.php',
        'MigrateArchiveText' => __DIR__ . '/maintenance/migrateArchiveText.php',
        'MigrateComments' => __DIR__ . '/maintenance/migrateComments.php',
        'MigrateFileRepoLayout' => __DIR__ . '/maintenance/migrateFileRepoLayout.php',
@@ -1075,6 +1082,7 @@ $wgAutoloadLocalClasses = [
        'ObjectFactory' => __DIR__ . '/includes/compat/ObjectFactory.php',
        'OldChangesList' => __DIR__ . '/includes/changes/OldChangesList.php',
        'OldLocalFile' => __DIR__ . '/includes/filerepo/file/OldLocalFile.php',
+       'OldRevisionImporter' => __DIR__ . '/includes/import/OldRevisionImporter.php',
        'OracleInstaller' => __DIR__ . '/includes/installer/OracleInstaller.php',
        'OracleUpdater' => __DIR__ . '/includes/installer/OracleUpdater.php',
        'OrderedStreamingForkController' => __DIR__ . '/includes/OrderedStreamingForkController.php',
@@ -1570,6 +1578,7 @@ $wgAutoloadLocalClasses = [
        'UploadFromStash' => __DIR__ . '/includes/upload/UploadFromStash.php',
        'UploadFromUrl' => __DIR__ . '/includes/upload/UploadFromUrl.php',
        'UploadLogFormatter' => __DIR__ . '/includes/logging/UploadLogFormatter.php',
+       'UploadRevisionImporter' => __DIR__ . '/includes/import/UploadRevisionImporter.php',
        'UploadSourceAdapter' => __DIR__ . '/includes/import/UploadSourceAdapter.php',
        'UploadSourceField' => __DIR__ . '/includes/specials/formfields/UploadSourceField.php',
        'UploadStash' => __DIR__ . '/includes/upload/UploadStash.php',
@@ -1718,6 +1727,7 @@ $wgAutoloadLocalClasses = [
        'Wikimedia\\Rdbms\\TransactionProfiler' => __DIR__ . '/includes/libs/rdbms/TransactionProfiler.php',
        'WikitextContent' => __DIR__ . '/includes/content/WikitextContent.php',
        'WikitextContentHandler' => __DIR__ . '/includes/content/WikitextContentHandler.php',
+       'WikitextLogFormatter' => __DIR__ . '/includes/logging/WikitextLogFormatter.php',
        'WinCacheBagOStuff' => __DIR__ . '/includes/libs/objectcache/WinCacheBagOStuff.php',
        'WithoutInterwikiPage' => __DIR__ . '/includes/specials/SpecialWithoutinterwiki.php',
        'WordLevelDiff' => __DIR__ . '/includes/diff/WordLevelDiff.php',
index 56d1633..c96374f 100644 (file)
@@ -55,7 +55,7 @@
                "jakub-onderka/php-parallel-lint": "0.9.2",
                "jetbrains/phpstorm-stubs": "dev-master#1b9906084d6635456fcf3f3a01f0d7d5b99a578a",
                "justinrainbow/json-schema": "~5.2",
-               "mediawiki/mediawiki-codesniffer": "15.0.0",
+               "mediawiki/mediawiki-codesniffer": "16.0.0",
                "monolog/monolog": "~1.22.1",
                "nikic/php-parser": "3.1.3",
                "nmred/kafka-php": "0.1.5",
index 7084b51..4e8474b 100644 (file)
@@ -2333,6 +2333,7 @@ $wikiPage: the WikiPage edited
 $rev: the new revision
 $baseID: the revision ID this was based off, if any
 $user: the editing user
+&$tags: tags to apply to the edit and recent change
 
 'OldChangesListRecentChangesLine': Customize entire recent changes line, or
 return false to omit the line from RecentChanges and Watchlist special pages.
diff --git a/includes/ActorMigration.php b/includes/ActorMigration.php
new file mode 100644 (file)
index 0000000..161c7a9
--- /dev/null
@@ -0,0 +1,383 @@
+<?php
+/**
+ * Methods to help with the actor table migration
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
+
+use MediaWiki\MediaWikiServices;
+use MediaWiki\User\UserIdentity;
+use Wikimedia\Rdbms\IDatabase;
+
+/**
+ * This class handles the logic for the actor table migration.
+ *
+ * This is not intended to be a long-term part of MediaWiki; it will be
+ * deprecated and removed along with $wgActorTableSchemaMigrationStage.
+ *
+ * @since 1.31
+ */
+class ActorMigration {
+
+       /**
+        * Define fields that use temporary tables for transitional purposes
+        * @var array Keys are '$key', values are arrays with four fields:
+        *  - table: Temporary table name
+        *  - pk: Temporary table column referring to the main table's primary key
+        *  - field: Temporary table column referring actor.actor_id
+        *  - joinPK: Main table's primary key
+        */
+       private static $tempTables = [
+               'rev_user' => [
+                       'table' => 'revision_actor_temp',
+                       'pk' => 'revactor_rev',
+                       'field' => 'revactor_actor',
+                       'joinPK' => 'rev_id',
+                       'extra' => [
+                               'revactor_timestamp' => 'rev_timestamp',
+                               'revactor_page' => 'rev_page',
+                       ],
+               ],
+       ];
+
+       /**
+        * Fields that formerly used $tempTables
+        * @var array Key is '$key', value is the MediaWiki version in which it was
+        *  removed from $tempTables.
+        */
+       private static $formerTempTables = [];
+
+       /**
+        * Define fields that use non-standard mapping
+        * @var array Keys are the user id column name, values are arrays with two
+        *  elements (the user text column name and the actor id column name)
+        */
+       private static $specialFields = [
+               'ipb_by' => [ 'ipb_by_text', 'ipb_by_actor' ],
+       ];
+
+       /** @var array|null Cache for `self::getJoin()` */
+       private $joinCache = null;
+
+       /** @var int One of the MIGRATION_* constants */
+       private $stage;
+
+       /** @private */
+       public function __construct( $stage ) {
+               $this->stage = $stage;
+       }
+
+       /**
+        * Static constructor
+        * @return ActorMigration
+        */
+       public static function newMigration() {
+               return MediaWikiServices::getInstance()->getActorMigration();
+       }
+
+       /**
+        * Return an SQL condition to test if a user field is anonymous
+        * @param string $field Field name or SQL fragment
+        * @return string
+        */
+       public function isAnon( $field ) {
+               return $this->stage === MIGRATION_NEW ? "$field IS NULL" : "$field = 0";
+       }
+
+       /**
+        * Return an SQL condition to test if a user field is non-anonymous
+        * @param string $field Field name or SQL fragment
+        * @return string
+        */
+       public function isNotAnon( $field ) {
+               return $this->stage === MIGRATION_NEW ? "$field IS NOT NULL" : "$field != 0";
+       }
+
+       /**
+        * @param string $key A key such as "rev_user" identifying the actor
+        *  field being fetched.
+        * @return string[] [ $text, $actor ]
+        */
+       private static function getFieldNames( $key ) {
+               if ( isset( self::$specialFields[$key] ) ) {
+                       return self::$specialFields[$key];
+               }
+
+               return [ $key . '_text', substr( $key, 0, -5 ) . '_actor' ];
+       }
+
+       /**
+        * Get SELECT fields and joins for the actor key
+        *
+        * @param string $key A key such as "rev_user" identifying the actor
+        *  field being fetched.
+        * @return array With three keys:
+        *   - tables: (string[]) to include in the `$table` to `IDatabase->select()`
+        *   - fields: (string[]) to include in the `$vars` to `IDatabase->select()`
+        *   - joins: (array) to include in the `$join_conds` to `IDatabase->select()`
+        *  All tables, fields, and joins are aliased, so `+` is safe to use.
+        */
+       public function getJoin( $key ) {
+               if ( !isset( $this->joinCache[$key] ) ) {
+                       $tables = [];
+                       $fields = [];
+                       $joins = [];
+
+                       list( $text, $actor ) = self::getFieldNames( $key );
+
+                       if ( $this->stage === MIGRATION_OLD ) {
+                               $fields[$key] = $key;
+                               $fields[$text] = $text;
+                               $fields[$actor] = 'NULL';
+                       } else {
+                               $join = $this->stage === MIGRATION_NEW ? 'JOIN' : 'LEFT JOIN';
+
+                               if ( isset( self::$tempTables[$key] ) ) {
+                                       $t = self::$tempTables[$key];
+                                       $alias = "temp_$key";
+                                       $tables[$alias] = $t['table'];
+                                       $joins[$alias] = [ $join, "{$alias}.{$t['pk']} = {$t['joinPK']}" ];
+                                       $joinField = "{$alias}.{$t['field']}";
+                               } else {
+                                       $joinField = $actor;
+                               }
+
+                               $alias = "actor_$key";
+                               $tables[$alias] = 'actor';
+                               $joins[$alias] = [ $join, "{$alias}.actor_id = {$joinField}" ];
+
+                               if ( $this->stage === MIGRATION_NEW ) {
+                                       $fields[$key] = "{$alias}.actor_user";
+                                       $fields[$text] = "{$alias}.actor_name";
+                               } else {
+                                       $fields[$key] = "COALESCE( {$alias}.actor_user, $key )";
+                                       $fields[$text] = "COALESCE( {$alias}.actor_name, $text )";
+                               }
+                               $fields[$actor] = $joinField;
+                       }
+
+                       $this->joinCache[$key] = [
+                               'tables' => $tables,
+                               'fields' => $fields,
+                               'joins' => $joins,
+                       ];
+               }
+
+               return $this->joinCache[$key];
+       }
+
+       /**
+        * Get UPDATE fields for the actor
+        *
+        * @param IDatabase $dbw Database to use for creating an actor ID, if necessary
+        * @param string $key A key such as "rev_user" identifying the actor
+        *  field being fetched.
+        * @param UserIdentity $user User to set in the update
+        * @return array to merge into `$values` to `IDatabase->update()` or `$a` to `IDatabase->insert()`
+        */
+       public function getInsertValues( IDatabase $dbw, $key, UserIdentity $user ) {
+               if ( isset( self::$tempTables[$key] ) ) {
+                       throw new InvalidArgumentException( "Must use getInsertValuesWithTempTable() for $key" );
+               }
+
+               list( $text, $actor ) = self::getFieldNames( $key );
+               $ret = [];
+               if ( $this->stage <= MIGRATION_WRITE_BOTH ) {
+                       $ret[$key] = $user->getId();
+                       $ret[$text] = $user->getName();
+               }
+               if ( $this->stage >= MIGRATION_WRITE_BOTH ) {
+                       // We need to be able to assign an actor ID if none exists
+                       if ( !$user instanceof User && !$user->getActorId() ) {
+                               $user = User::newFromAnyId( $user->getId(), $user->getName(), null );
+                       }
+                       $ret[$actor] = $user->getActorId( $dbw );
+               }
+               return $ret;
+       }
+
+       /**
+        * Get UPDATE fields for the actor
+        *
+        * @param IDatabase $dbw Database to use for creating an actor ID, if necessary
+        * @param string $key A key such as "rev_user" identifying the actor
+        *  field being fetched.
+        * @param UserIdentity $user User to set in the update
+        * @return array with two values:
+        *  - array to merge into `$values` to `IDatabase->update()` or `$a` to `IDatabase->insert()`
+        *  - callback to call with the the primary key for the main table insert
+        *    and extra fields needed for the temp table.
+        */
+       public function getInsertValuesWithTempTable( IDatabase $dbw, $key, UserIdentity $user ) {
+               if ( isset( self::$formerTempTables[$key] ) ) {
+                       wfDeprecated( __METHOD__ . " for $key", self::$formerTempTables[$key] );
+               } elseif ( !isset( self::$tempTables[$key] ) ) {
+                       throw new InvalidArgumentException( "Must use getInsertValues() for $key" );
+               }
+
+               list( $text, $actor ) = self::getFieldNames( $key );
+               $ret = [];
+               $callback = null;
+               if ( $this->stage <= MIGRATION_WRITE_BOTH ) {
+                       $ret[$key] = $user->getId();
+                       $ret[$text] = $user->getName();
+               }
+               if ( $this->stage >= MIGRATION_WRITE_BOTH ) {
+                       // We need to be able to assign an actor ID if none exists
+                       if ( !$user instanceof User && !$user->getActorId() ) {
+                               $user = User::newFromAnyId( $user->getId(), $user->getName(), null );
+                       }
+                       $id = $user->getActorId( $dbw );
+
+                       if ( isset( self::$tempTables[$key] ) ) {
+                               $func = __METHOD__;
+                               $callback = function ( $pk, array $extra ) use ( $dbw, $key, $id, $func ) {
+                                       $t = self::$tempTables[$key];
+                                       $set = [ $t['field'] => $id ];
+                                       foreach ( $t['extra'] as $to => $from ) {
+                                               if ( !array_key_exists( $from, $extra ) ) {
+                                                       throw new InvalidArgumentException( "$func callback: \$extra[$from] is not provided" );
+                                               }
+                                               $set[$to] = $extra[$from];
+                                       }
+                                       $dbw->upsert(
+                                               $t['table'],
+                                               [ $t['pk'] => $pk ] + $set,
+                                               [ $t['pk'] ],
+                                               $set,
+                                               $func
+                                       );
+                               };
+                       } else {
+                               $ret[$actor] = $id;
+                               $callback = function ( $pk, array $extra ) {
+                               };
+                       }
+               } elseif ( isset( self::$tempTables[$key] ) ) {
+                       $func = __METHOD__;
+                       $callback = function ( $pk, array $extra ) use ( $key, $func ) {
+                               $t = self::$tempTables[$key];
+                               foreach ( $t['extra'] as $to => $from ) {
+                                       if ( !array_key_exists( $from, $extra ) ) {
+                                               throw new InvalidArgumentException( "$func callback: \$extra[$from] is not provided" );
+                                       }
+                               }
+                       };
+               } else {
+                       $callback = function ( $pk, array $extra ) {
+                       };
+               }
+               return [ $ret, $callback ];
+       }
+
+       /**
+        * Get WHERE condition for the actor
+        *
+        * @param IDatabase $db Database to use for quoting and list-making
+        * @param string $key A key such as "rev_user" identifying the actor
+        *  field being fetched.
+        * @param UserIdentity|UserIdentity[] $users Users to test for
+        * @param bool $useId If false, don't try to query by the user ID.
+        *  Intended for use with rc_user since it has an index on
+        *  (rc_user_text,rc_timestamp) but not (rc_user,rc_timestamp).
+        * @return array With three keys:
+        *   - tables: (string[]) to include in the `$table` to `IDatabase->select()`
+        *   - conds: (string) to include in the `$cond` to `IDatabase->select()`
+        *   - orconds: (array[]) array of alternatives in case a union of multiple
+        *     queries would be more efficient than a query with OR. May have keys
+        *     'actor', 'userid', 'username'.
+        *   - joins: (array) to include in the `$join_conds` to `IDatabase->select()`
+        *  All tables and joins are aliased, so `+` is safe to use.
+        */
+       public function getWhere( IDatabase $db, $key, $users, $useId = true ) {
+               $tables = [];
+               $conds = [];
+               $joins = [];
+
+               if ( $users instanceof UserIdentity ) {
+                       $users = [ $users ];
+               }
+
+               // Get information about all the passed users
+               $ids = [];
+               $names = [];
+               $actors = [];
+               foreach ( $users as $user ) {
+                       if ( $useId && $user->getId() ) {
+                               $ids[] = $user->getId();
+                       } else {
+                               $names[] = $user->getName();
+                       }
+                       $actorId = $user->getActorId();
+                       if ( $actorId ) {
+                               $actors[] = $actorId;
+                       }
+               }
+
+               list( $text, $actor ) = self::getFieldNames( $key );
+
+               // Combine data into conditions to be ORed together
+               $actorNotEmpty = [];
+               if ( $this->stage === MIGRATION_OLD ) {
+                       $actors = [];
+                       $actorEmpty = [];
+               } elseif ( isset( self::$tempTables[$key] ) ) {
+                       $t = self::$tempTables[$key];
+                       $alias = "temp_$key";
+                       $tables[$alias] = $t['table'];
+                       $joins[$alias] = [
+                               $this->stage === MIGRATION_NEW ? 'JOIN' : 'LEFT JOIN',
+                               "{$alias}.{$t['pk']} = {$t['joinPK']}"
+                       ];
+                       $joinField = "{$alias}.{$t['field']}";
+                       $actorEmpty = [ $joinField => null ];
+                       if ( $this->stage !== MIGRATION_NEW ) {
+                               // Otherwise the resulting test can evaluate to NULL, and
+                               // NOT(NULL) is NULL rather than true.
+                               $actorNotEmpty = [ "$joinField IS NOT NULL" ];
+                       }
+               } else {
+                       $joinField = $actor;
+                       $actorEmpty = [ $joinField => 0 ];
+               }
+
+               if ( $actors ) {
+                       $conds['actor'] = $db->makeList(
+                               $actorNotEmpty + [ $joinField => $actors ], IDatabase::LIST_AND
+                       );
+               }
+               if ( $this->stage < MIGRATION_NEW && $ids ) {
+                       $conds['userid'] = $db->makeList(
+                               $actorEmpty + [ $key => $ids ], IDatabase::LIST_AND
+                       );
+               }
+               if ( $this->stage < MIGRATION_NEW && $names ) {
+                       $conds['username'] = $db->makeList(
+                               $actorEmpty + [ $text => $names ], IDatabase::LIST_AND
+                       );
+               }
+
+               return [
+                       'tables' => $tables,
+                       'conds' => $conds ? $db->makeList( array_values( $conds ), IDatabase::LIST_OR ) : '1=0',
+                       'orconds' => $conds,
+                       'joins' => $joins,
+               ];
+       }
+
+}
index bdc6702..4e878d1 100644 (file)
@@ -206,12 +206,25 @@ class Block {
         * @return array
         */
        public static function selectFields() {
+               global $wgActorTableSchemaMigrationStage;
+
+               if ( $wgActorTableSchemaMigrationStage > MIGRATION_WRITE_BOTH ) {
+                       // If code is using this instead of self::getQueryInfo(), there's a
+                       // decent chance it's going to try to directly access
+                       // $row->ipb_by or $row->ipb_by_text and we can't give it
+                       // useful values here once those aren't being written anymore.
+                       throw new BadMethodCallException(
+                               'Cannot use ' . __METHOD__ . ' when $wgActorTableSchemaMigrationStage > MIGRATION_WRITE_BOTH'
+                       );
+               }
+
                wfDeprecated( __METHOD__, '1.31' );
                return [
                        'ipb_id',
                        'ipb_address',
                        'ipb_by',
                        'ipb_by_text',
+                       'ipb_by_actor' => $wgActorTableSchemaMigrationStage > MIGRATION_OLD ? 'ipb_by_actor' : null,
                        'ipb_timestamp',
                        'ipb_auto',
                        'ipb_anon_only',
@@ -236,13 +249,12 @@ class Block {
         */
        public static function getQueryInfo() {
                $commentQuery = CommentStore::getStore()->getJoin( 'ipb_reason' );
+               $actorQuery = ActorMigration::newMigration()->getJoin( 'ipb_by' );
                return [
-                       'tables' => [ 'ipblocks' ] + $commentQuery['tables'],
+                       'tables' => [ 'ipblocks' ] + $commentQuery['tables'] + $actorQuery['tables'],
                        'fields' => [
                                'ipb_id',
                                'ipb_address',
-                               'ipb_by',
-                               'ipb_by_text',
                                'ipb_timestamp',
                                'ipb_auto',
                                'ipb_anon_only',
@@ -253,8 +265,8 @@ class Block {
                                'ipb_block_email',
                                'ipb_allow_usertalk',
                                'ipb_parent_block_id',
-                       ] + $commentQuery['fields'],
-                       'joins' => $commentQuery['joins'],
+                       ] + $commentQuery['fields'] + $actorQuery['fields'],
+                       'joins' => $commentQuery['joins'] + $actorQuery['joins'],
                ];
        }
 
@@ -445,11 +457,9 @@ class Block {
         */
        protected function initFromRow( $row ) {
                $this->setTarget( $row->ipb_address );
-               if ( $row->ipb_by ) { // local user
-                       $this->setBlocker( User::newFromId( $row->ipb_by ) );
-               } else { // foreign user
-                       $this->setBlocker( $row->ipb_by_text );
-               }
+               $this->setBlocker( User::newFromAnyId(
+                       $row->ipb_by, $row->ipb_by_text, isset( $row->ipb_by_actor ) ? $row->ipb_by_actor : null
+               ) );
 
                $this->mTimestamp = wfTimestamp( TS_MW, $row->ipb_timestamp );
                $this->mAuto = $row->ipb_auto;
@@ -519,6 +529,9 @@ class Block {
                if ( $this->getSystemBlockType() !== null ) {
                        throw new MWException( 'Cannot insert a system block into the database' );
                }
+               if ( !$this->getBlocker() || $this->getBlocker()->getName() === '' ) {
+                       throw new MWException( 'Cannot insert a block without a blocker set' );
+               }
 
                wfDebug( "Block::insert; timestamp {$this->mTimestamp}\n" );
 
@@ -526,10 +539,7 @@ class Block {
                        $dbw = wfGetDB( DB_MASTER );
                }
 
-               # Periodic purge via commit hooks
-               if ( mt_rand( 0, 9 ) == 0 ) {
-                       self::purgeExpired();
-               }
+               self::purgeExpired();
 
                $row = $this->getDatabaseArray( $dbw );
 
@@ -640,8 +650,6 @@ class Block {
                $a = [
                        'ipb_address'          => (string)$this->target,
                        'ipb_user'             => $uid,
-                       'ipb_by'               => $this->getBy(),
-                       'ipb_by_text'          => $this->getByName(),
                        'ipb_timestamp'        => $dbw->timestamp( $this->mTimestamp ),
                        'ipb_auto'             => $this->mAuto,
                        'ipb_anon_only'        => !$this->isHardblock(),
@@ -654,7 +662,8 @@ class Block {
                        'ipb_block_email'      => $this->prevents( 'sendemail' ),
                        'ipb_allow_usertalk'   => !$this->prevents( 'editownusertalk' ),
                        'ipb_parent_block_id'  => $this->mParentBlockId
-               ] + CommentStore::getStore()->insert( $dbw, 'ipb_reason', $this->mReason );
+               ] + CommentStore::getStore()->insert( $dbw, 'ipb_reason', $this->mReason )
+                       + ActorMigration::newMigration()->getInsertValues( $dbw, 'ipb_by', $this->getBlocker() );
 
                return $a;
        }
@@ -665,12 +674,11 @@ class Block {
         */
        protected function getAutoblockUpdateArray( IDatabase $dbw ) {
                return [
-                       'ipb_by'               => $this->getBy(),
-                       'ipb_by_text'          => $this->getByName(),
                        'ipb_create_account'   => $this->prevents( 'createaccount' ),
                        'ipb_deleted'          => (int)$this->mHideName, // typecast required for SQLite
                        'ipb_allow_usertalk'   => !$this->prevents( 'editownusertalk' ),
-               ] + CommentStore::getStore()->insert( $dbw, 'ipb_reason', $this->mReason );
+               ] + CommentStore::getStore()->insert( $dbw, 'ipb_reason', $this->mReason )
+                       + ActorMigration::newMigration()->getInsertValues( $dbw, 'ipb_by', $this->getBlocker() );
        }
 
        /**
@@ -710,16 +718,27 @@ class Block {
                        return;
                }
 
+               $target = $block->getTarget();
+               if ( is_string( $target ) ) {
+                       $target = User::newFromName( $target, false );
+               }
+
                $dbr = wfGetDB( DB_REPLICA );
+               $rcQuery = ActorMigration::newMigration()->getWhere( $dbr, 'rc_user', $target, false );
 
                $options = [ 'ORDER BY' => 'rc_timestamp DESC' ];
-               $conds = [ 'rc_user_text' => (string)$block->getTarget() ];
 
                // Just the last IP used.
                $options['LIMIT'] = 1;
 
-               $res = $dbr->select( 'recentchanges', [ 'rc_ip' ], $conds,
-                       __METHOD__, $options );
+               $res = $dbr->select(
+                       [ 'recentchanges' ] + $rcQuery['tables'],
+                       [ 'rc_ip' ],
+                       $rcQuery['conds'],
+                       __METHOD__,
+                       $options,
+                       $rcQuery['joins']
+               );
 
                if ( !$res->numRows() ) {
                        # No results, don't autoblock anything
@@ -1119,11 +1138,14 @@ class Block {
                        wfGetDB( DB_MASTER ),
                        __METHOD__,
                        function ( IDatabase $dbw, $fname ) {
-                               $dbw->delete(
-                                       'ipblocks',
+                               $ids = $dbw->selectFieldValues( 'ipblocks',
+                                       'ipb_id',
                                        [ 'ipb_expiry < ' . $dbw->addQuotes( $dbw->timestamp() ) ],
                                        $fname
                                );
+                               if ( $ids ) {
+                                       $dbw->delete( 'ipblocks', [ 'ipb_id' => $ids ], $fname );
+                               }
                        }
                ) );
        }
@@ -1471,7 +1493,7 @@ class Block {
 
        /**
         * Get the user who implemented this block
-        * @return User|string Local User object or string for a foreign user
+        * @return User User object. May name a foreign user.
         */
        public function getBlocker() {
                return $this->blocker;
index ae5cef5..33418d8 100644 (file)
@@ -5127,8 +5127,6 @@ $wgGroupPermissions['*']['edit'] = true;
 $wgGroupPermissions['*']['createpage'] = true;
 $wgGroupPermissions['*']['createtalk'] = true;
 $wgGroupPermissions['*']['writeapi'] = true;
-$wgGroupPermissions['*']['editmyusercss'] = true;
-$wgGroupPermissions['*']['editmyuserjs'] = true;
 $wgGroupPermissions['*']['viewmywatchlist'] = true;
 $wgGroupPermissions['*']['editmywatchlist'] = true;
 $wgGroupPermissions['*']['viewmyprivateinfo'] = true;
@@ -5151,6 +5149,8 @@ $wgGroupPermissions['user']['upload'] = true;
 $wgGroupPermissions['user']['reupload'] = true;
 $wgGroupPermissions['user']['reupload-shared'] = true;
 $wgGroupPermissions['user']['minoredit'] = true;
+$wgGroupPermissions['user']['editmyusercss'] = true;
+$wgGroupPermissions['user']['editmyuserjs'] = true;
 $wgGroupPermissions['user']['purge'] = true;
 $wgGroupPermissions['user']['sendemail'] = true;
 $wgGroupPermissions['user']['applychangetags'] = true;
@@ -5813,6 +5813,7 @@ $wgGrantPermissions['editpage']['changetags'] = true;
 $wgGrantPermissions['editprotected'] = $wgGrantPermissions['editpage'];
 $wgGrantPermissions['editprotected']['editprotected'] = true;
 
+// FIXME: Rename editmycssjs to editmyconfig
 $wgGrantPermissions['editmycssjs'] = $wgGrantPermissions['editpage'];
 $wgGrantPermissions['editmycssjs']['editmyusercss'] = true;
 $wgGrantPermissions['editmycssjs']['editmyuserjs'] = true;
@@ -8829,6 +8830,13 @@ $wgInterwikiPrefixDisplayTypes = [];
  */
 $wgCommentTableSchemaMigrationStage = MIGRATION_OLD;
 
+/**
+ * Actor table schema migration stage.
+ * @since 1.31
+ * @var int One of the MIGRATION_* constants
+ */
+$wgActorTableSchemaMigrationStage = MIGRATION_OLD;
+
 /**
  * For really cool vim folding this needs to be at the end:
  * vim: foldmarker=@{,@} foldmethod=marker
index a74353a..96ed56b 100644 (file)
@@ -38,11 +38,6 @@ $wgShowExceptionDetails = true;
 $wgShowSQLErrors = true;
 $wgDebugRawPage = true; // T49960
 
-// Enable verbose logging
-$wgDebugComments = true;
-$wgDebugDumpSql = true;
-$wgDebugTimestamps = true;
-
 // Enable log files
 $logDir = getenv( 'MW_LOG_DIR' );
 if ( $logDir ) {
index f9c7fb2..08c4a72 100644 (file)
@@ -238,30 +238,6 @@ class EditPage {
        /** @var bool */
        public $isConflict = false;
 
-       /**
-        * @deprecated since 1.30 use Title::isCssJsSubpage()
-        * @var bool
-        */
-       public $isCssJsSubpage = false;
-
-       /**
-        * @deprecated since 1.30 use Title::isCssSubpage()
-        * @var bool
-        */
-       public $isCssSubpage = false;
-
-       /**
-        * @deprecated since 1.30 use Title::isJsSubpage()
-        * @var bool
-        */
-       public $isJsSubpage = false;
-
-       /**
-        * @deprecated since 1.30
-        * @var bool
-        */
-       public $isWrongCaseCssJsPage = false;
-
        /** @var bool New page or new section */
        public $isNew = false;
 
@@ -660,13 +636,6 @@ class EditPage {
                }
 
                $this->isConflict = false;
-               // css / js subpages of user pages get a special treatment
-               // The following member variables are deprecated since 1.30,
-               // the functions should be used instead.
-               $this->isCssJsSubpage = $this->mTitle->isCssJsSubpage();
-               $this->isCssSubpage = $this->mTitle->isCssSubpage();
-               $this->isJsSubpage = $this->mTitle->isJsSubpage();
-               $this->isWrongCaseCssJsPage = $this->isWrongCaseCssJsPage();
 
                # Show applicable editing introductions
                if ( $this->formtype == 'initial' || $this->firsttime ) {
@@ -877,9 +846,9 @@ class EditPage {
         *
         * @return bool
         */
-       protected function isWrongCaseCssJsPage() {
-               if ( $this->mTitle->isCssJsSubpage() ) {
-                       $name = $this->mTitle->getSkinFromCssJsSubpage();
+       protected function isWrongCaseUserConfigPage() {
+               if ( $this->mTitle->isUserConfigPage() ) {
+                       $name = $this->mTitle->getSkinFromConfigSubpage();
                        $skins = array_merge(
                                array_keys( Skin::getSkinNames() ),
                                [ 'common' ]
@@ -2879,7 +2848,7 @@ ERROR;
                        $out->addHTML( $editConflictHelper->getEditFormHtmlBeforeContent() );
                }
 
-               if ( !$this->mTitle->isCssJsSubpage() && $showToolbar && $user->getOption( 'showtoolbar' ) ) {
+               if ( !$this->mTitle->isUserConfigPage() && $showToolbar && $user->getOption( 'showtoolbar' ) ) {
                        $out->addHTML( self::getEditToolbar( $this->mTitle ) );
                }
 
@@ -3116,22 +3085,26 @@ ERROR;
                                );
                        }
                } else {
-                       if ( $this->mTitle->isCssJsSubpage() ) {
+                       if ( $this->mTitle->isUserConfigPage() ) {
                                # Check the skin exists
-                               if ( $this->isWrongCaseCssJsPage() ) {
+                               if ( $this->isWrongCaseUserConfigPage() ) {
                                        $out->wrapWikiMsg(
-                                               "<div class='error' id='mw-userinvalidcssjstitle'>\n$1\n</div>",
-                                               [ 'userinvalidcssjstitle', $this->mTitle->getSkinFromCssJsSubpage() ]
+                                               "<div class='error' id='mw-userinvalidconfigtitle'>\n$1\n</div>",
+                                               [ 'userinvalidconfigtitle', $this->mTitle->getSkinFromConfigSubpage() ]
                                        );
                                }
                                if ( $this->getTitle()->isSubpageOf( $user->getUserPage() ) ) {
-                                       $isCssSubpage = $this->mTitle->isCssSubpage();
-                                       $out->wrapWikiMsg( '<div class="mw-usercssjspublic">$1</div>',
-                                               $isCssSubpage ? 'usercssispublic' : 'userjsispublic'
-                                       );
+                                       $isUserCssConfig = $this->mTitle->isUserCssConfigPage();
+
+                                       $warning = $isUserCssConfig
+                                               ? 'usercssispublic'
+                                               : 'userjsispublic';
+
+                                       $out->wrapWikiMsg( '<div class="mw-userconfigpublic">$1</div>', $warning );
+
                                        if ( $this->formtype !== 'preview' ) {
                                                $config = $this->context->getConfig();
-                                               if ( $isCssSubpage && $config->get( 'AllowUserCss' ) ) {
+                                               if ( $isUserCssConfig && $config->get( 'AllowUserCss' ) ) {
                                                        $out->wrapWikiMsg(
                                                                "<div id='mw-usercssyoucanpreview'>\n$1\n</div>",
                                                                [ 'usercssyoucanpreview' ]
@@ -3165,11 +3138,15 @@ ERROR;
         * @return array
         */
        private function getSummaryInputAttributes( array $inputAttrs = null ) {
-               // Note: the maxlength is overridden in JS to 255 and to make it use UTF-8 bytes, not characters.
+               $conf = $this->context->getConfig();
+               $oldCommentSchema = $conf->get( 'CommentTableSchemaMigrationStage' ) === MIGRATION_OLD;
+               // HTML maxlength uses "UTF-16 code units", which means that characters outside BMP
+               // (e.g. emojis) count for two each. This limit is overridden in JS to instead count
+               // Unicode codepoints (or 255 UTF-8 bytes for old schema).
                return ( is_array( $inputAttrs ) ? $inputAttrs : [] ) + [
                        'id' => 'wpSummary',
                        'name' => 'wpSummary',
-                       'maxlength' => '200',
+                       'maxlength' => $oldCommentSchema ? 200 : CommentStore::COMMENT_CHARACTER_LIMIT,
                        'tabindex' => 1,
                        'size' => 60,
                        'spellcheck' => 'true',
@@ -3702,7 +3679,7 @@ ERROR;
                $out->addHTML( $this->editFormTextAfterWarn );
 
                $out->addHTML( "<div class='editButtons'>\n" );
-               $out->addHTML( implode( $this->getEditButtons( $tabindex ), "\n" ) . "\n" );
+               $out->addHTML( implode( "\n", $this->getEditButtons( $tabindex ) ) . "\n" );
 
                $cancel = $this->getCancelLink();
 
@@ -3812,30 +3789,30 @@ ERROR;
        protected function getLastDelete() {
                $dbr = wfGetDB( DB_REPLICA );
                $commentQuery = CommentStore::getStore()->getJoin( 'log_comment' );
+               $actorQuery = ActorMigration::newMigration()->getJoin( 'log_user' );
                $data = $dbr->selectRow(
-                       [ 'logging', 'user' ] + $commentQuery['tables'],
+                       array_merge( [ 'logging' ], $commentQuery['tables'], $actorQuery['tables'], [ 'user' ] ),
                        [
                                'log_type',
                                'log_action',
                                'log_timestamp',
-                               'log_user',
                                'log_namespace',
                                'log_title',
                                'log_params',
                                'log_deleted',
                                'user_name'
-                       ] + $commentQuery['fields'], [
+                       ] + $commentQuery['fields'] + $actorQuery['fields'],
+                       [
                                'log_namespace' => $this->mTitle->getNamespace(),
                                'log_title' => $this->mTitle->getDBkey(),
                                'log_type' => 'delete',
                                'log_action' => 'delete',
-                               'user_id=log_user'
                        ],
                        __METHOD__,
                        [ 'LIMIT' => 1, 'ORDER BY' => 'log_timestamp DESC' ],
                        [
-                               'user' => [ 'JOIN', 'user_id=log_user' ],
-                       ] + $commentQuery['joins']
+                               'user' => [ 'JOIN', 'user_id=' . $actorQuery['fields']['log_user'] ],
+                       ] + $commentQuery['joins'] + $actorQuery['joins']
                );
                // Quick paranoid permission checks...
                if ( is_object( $data ) ) {
@@ -3913,10 +3890,10 @@ ERROR;
                        }
 
                        # don't parse non-wikitext pages, show message about preview
-                       if ( $this->mTitle->isCssJsSubpage() || $this->mTitle->isCssOrJsPage() ) {
-                               if ( $this->mTitle->isCssJsSubpage() ) {
+                       if ( $this->mTitle->isUserConfigPage() || $this->mTitle->isSiteConfigPage() ) {
+                               if ( $this->mTitle->isUserConfigPage() ) {
                                        $level = 'user';
-                               } elseif ( $this->mTitle->isCssOrJsPage() ) {
+                               } elseif ( $this->mTitle->isSiteConfigPage() ) {
                                        $level = 'site';
                                } else {
                                        $level = false;
index 8c843c4..783de1c 100644 (file)
@@ -246,6 +246,9 @@ class FileDeleteForm {
        private function showForm() {
                global $wgOut, $wgUser, $wgRequest;
 
+               $conf = RequestContext::getMain()->getConfig();
+               $oldCommentSchema = $conf->get( 'CommentTableSchemaMigrationStage' ) === MIGRATION_OLD;
+
                if ( $wgUser->isAllowed( 'suppressrevision' ) ) {
                        $suppress = "<tr id=\"wpDeleteSuppressRow\">
                                        <td></td>
@@ -258,6 +261,8 @@ class FileDeleteForm {
                        $suppress = '';
                }
 
+               $wgOut->addModules( 'mediawiki.action.delete.file' );
+
                $checkWatch = $wgUser->getBoolOption( 'watchdeletion' ) || $wgUser->isWatched( $this->title );
                $form = Xml::openElement( 'form', [ 'method' => 'post', 'action' => $this->getAction(),
                        'id' => 'mw-img-deleteconfirm' ] ) .
@@ -286,8 +291,15 @@ class FileDeleteForm {
                                        Xml::label( wfMessage( 'filedelete-otherreason' )->text(), 'wpReason' ) .
                                "</td>
                                <td class='mw-input'>" .
-                                       Xml::input( 'wpReason', 60, $wgRequest->getText( 'wpReason' ),
-                                               [ 'type' => 'text', 'maxlength' => '255', 'tabindex' => '2', 'id' => 'wpReason' ] ) .
+                                       Xml::input( 'wpReason', 60, $wgRequest->getText( 'wpReason' ), [
+                                               'type' => 'text',
+                                               // HTML maxlength uses "UTF-16 code units", which means that characters outside BMP
+                                               // (e.g. emojis) count for two each. This limit is overridden in JS to instead count
+                                               // Unicode codepoints (or 255 UTF-8 bytes for old schema).
+                                               'maxlength' => $oldCommentSchema ? 255 : CommentStore::COMMENT_CHARACTER_LIMIT,
+                                               'tabindex' => '2',
+                                               'id' => 'wpReason'
+                                       ] ) .
                                "</td>
                        </tr>
                        {$suppress}";
index 884c3f0..5b809e4 100644 (file)
@@ -3527,19 +3527,3 @@ function wfGetRusage() {
                return getrusage( 0 /* RUSAGE_SELF */ );
        }
 }
-
-/**
- * Begin profiling of a function
- * @param string $functionname Name of the function we will profile
- * @deprecated since 1.25
- */
-function wfProfileIn( $functionname ) {
-}
-
-/**
- * Stop profiling of a function
- * @param string $functionname Name of the function we have profiled
- * @deprecated since 1.25
- */
-function wfProfileOut( $functionname = 'missing' ) {
-}
index fb446b4..5fc5eb1 100644 (file)
@@ -1752,9 +1752,10 @@ class Linker {
                $dbr = wfGetDB( DB_REPLICA );
 
                // Up to the value of $wgShowRollbackEditCount revisions are counted
+               $revQuery = Revision::getQueryInfo();
                $res = $dbr->select(
-                       'revision',
-                       [ 'rev_user_text', 'rev_deleted' ],
+                       $revQuery['tables'],
+                       [ 'rev_user_text' => $revQuery['fields']['rev_user_text'], 'rev_deleted' ],
                        // $rev->getPage() returns null sometimes
                        [ 'rev_page' => $rev->getTitle()->getArticleID() ],
                        __METHOD__,
@@ -1762,7 +1763,8 @@ class Linker {
                                'USE INDEX' => [ 'revision' => 'page_timestamp' ],
                                'ORDER BY' => 'rev_timestamp DESC',
                                'LIMIT' => $wgShowRollbackEditCount + 1
-                       ]
+                       ],
+                       $revQuery['joins']
                );
 
                $editCount = 0;
index 9077666..3c8ce65 100644 (file)
@@ -690,6 +690,30 @@ class MediaWikiServices extends ServiceContainer {
                return $this->getService( 'ReadOnlyMode' );
        }
 
+       /**
+        * @since 1.31
+        * @return \UploadRevisionImporter
+        */
+       public function getWikiRevisionUploadImporter() {
+               return $this->getService( 'UploadRevisionImporter' );
+       }
+
+       /**
+        * @since 1.31
+        * @return \OldRevisionImporter
+        */
+       public function getWikiRevisionOldRevisionImporter() {
+               return $this->getService( 'OldRevisionImporter' );
+       }
+
+       /**
+        * @since 1.31
+        * @return \OldRevisionImporter
+        */
+       public function getWikiRevisionOldRevisionImporterNoUpdates() {
+               return $this->getService( 'WikiRevisionOldRevisionImporterNoUpdates' );
+       }
+
        /**
         * @since 1.30
         * @return CommandFactory
@@ -770,6 +794,14 @@ class MediaWikiServices extends ServiceContainer {
                return $this->getService( 'CommentStore' );
        }
 
+       /**
+        * @since 1.31
+        * @return ActorMigration
+        */
+       public function getActorMigration() {
+               return $this->getService( 'ActorMigration' );
+       }
+
        ///////////////////////////////////////////////////////////////////////////
        // NOTE: When adding a service getter here, don't forget to add a test
        // case for it in MediaWikiServicesTest::provideGetters() and in
index fac9a59..7d05f41 100644 (file)
@@ -1105,7 +1105,7 @@ class Message implements MessageSpecifier, Serializable {
        public static function listParam( array $list, $type = 'text' ) {
                if ( !isset( self::$listTypeMap[$type] ) ) {
                        throw new InvalidArgumentException(
-                               "Invalid type '$type'. Known types are: " . join( ', ', array_keys( self::$listTypeMap ) )
+                               "Invalid type '$type'. Known types are: " . implode( ', ', array_keys( self::$listTypeMap ) )
                        );
                }
                return [ 'list' => $list, 'type' => $type ];
index f95327a..4d6db4c 100644 (file)
@@ -2945,14 +2945,14 @@ class OutputPage extends ContextSource {
        private function isUserJsPreview() {
                return $this->getConfig()->get( 'AllowUserJs' )
                        && $this->getTitle()
-                       && $this->getTitle()->isJsSubpage()
+                       && $this->getTitle()->isUserJsConfigPage()
                        && $this->userCanPreview();
        }
 
        protected function isUserCssPreview() {
                return $this->getConfig()->get( 'AllowUserCss' )
                        && $this->getTitle()
-                       && $this->getTitle()->isCssSubpage()
+                       && $this->getTitle()->isUserCssConfigPage()
                        && $this->userCanPreview();
        }
 
@@ -3204,7 +3204,10 @@ class OutputPage extends ContextSource {
                }
 
                $title = $this->getTitle();
-               if ( !$title->isJsSubpage() && !$title->isCssSubpage() ) {
+               if (
+                       !$title->isUserJsConfigPage()
+                       && !$title->isUserCssConfigPage()
+               ) {
                        return false;
                }
                if ( !$title->isSubpageOf( $user->getUserPage() ) ) {
index 53608e8..51c2923 100644 (file)
@@ -349,7 +349,9 @@ class ProtectionForm {
                $user = $context->getUser();
                $output = $context->getOutput();
                $lang = $context->getLanguage();
-               $cascadingRestrictionLevels = $context->getConfig()->get( 'CascadingRestrictionLevels' );
+               $conf = $context->getConfig();
+               $cascadingRestrictionLevels = $conf->get( 'CascadingRestrictionLevels' );
+               $oldCommentSchema = $conf->get( 'CommentTableSchemaMigrationStage' ) === MIGRATION_OLD;
                $out = '';
                if ( !$this->disabled ) {
                        $output->addModules( 'mediawiki.legacy.protect' );
@@ -494,6 +496,13 @@ class ProtectionForm {
                                $this->mReasonSelection,
                                'mwProtect-reason', 4 );
 
+                       // HTML maxlength uses "UTF-16 code units", which means that characters outside BMP
+                       // (e.g. emojis) count for two each. This limit is overridden in JS to instead count
+                       // Unicode codepoints (or 180 UTF-8 bytes for old schema).
+                       // Subtract arbitrary 75 to leave some space for the autogenerated null edit's summary
+                       // and other texts chosen by dropdown menus on this page.
+                       $maxlength = $oldCommentSchema ? 180 : CommentStore::COMMENT_CHARACTER_LIMIT - 75;
+
                        $out .= Xml::openElement( 'table', [ 'id' => 'mw-protect-table3' ] ) .
                                Xml::openElement( 'tbody' );
                        $out .= "
@@ -511,10 +520,7 @@ class ProtectionForm {
                                        </td>
                                        <td class='mw-input'>" .
                                                Xml::input( 'mwProtect-reason', 60, $this->mReason, [ 'type' => 'text',
-                                                       'id' => 'mwProtect-reason', 'maxlength' => 180 ] ) .
-                                                       // Limited maxlength as the database trims at 255 bytes and other texts
-                                                       // chosen by dropdown menus on this page are also included in this database field.
-                                                       // The byte limit of 180 bytes is enforced in javascript
+                                                       'id' => 'mwProtect-reason', 'maxlength' => $maxlength ] ) .
                                        "</td>
                                </tr>";
                        # Disallow watching is user is not logged in
index d9d3149..f8a3fcc 100644 (file)
@@ -29,7 +29,6 @@ use MediaWiki\Storage\RevisionStore;
 use MediaWiki\Storage\RevisionStoreRecord;
 use MediaWiki\Storage\SlotRecord;
 use MediaWiki\Storage\SqlBlobStore;
-use MediaWiki\User\UserIdentityValue;
 use Wikimedia\Rdbms\IDatabase;
 use MediaWiki\Linker\LinkTarget;
 use MediaWiki\MediaWikiServices;
@@ -316,7 +315,18 @@ class Revision implements IDBAccessObject {
         * @return array
         */
        public static function userJoinCond() {
+               global $wgActorTableSchemaMigrationStage;
+
                wfDeprecated( __METHOD__, '1.31' );
+               if ( $wgActorTableSchemaMigrationStage > MIGRATION_WRITE_BOTH ) {
+                       // If code is using this instead of self::getQueryInfo(), there's
+                       // no way the join it's trying to do can work once the old fields
+                       // aren't being written anymore.
+                       throw new BadMethodCallException(
+                               'Cannot use ' . __METHOD__ . ' when $wgActorTableSchemaMigrationStage > MIGRATION_WRITE_BOTH'
+                       );
+               }
+
                return [ 'LEFT JOIN', [ 'rev_user != 0', 'user_id = rev_user' ] ];
        }
 
@@ -339,7 +349,17 @@ class Revision implements IDBAccessObject {
         * @return array
         */
        public static function selectFields() {
-               global $wgContentHandlerUseDB;
+               global $wgContentHandlerUseDB, $wgActorTableSchemaMigrationStage;
+
+               if ( $wgActorTableSchemaMigrationStage > MIGRATION_WRITE_BOTH ) {
+                       // If code is using this instead of self::getQueryInfo(), there's a
+                       // decent chance it's going to try to directly access
+                       // $row->rev_user or $row->rev_user_text and we can't give it
+                       // useful values here once those aren't being written anymore.
+                       throw new BadMethodCallException(
+                               'Cannot use ' . __METHOD__ . ' when $wgActorTableSchemaMigrationStage > MIGRATION_WRITE_BOTH'
+                       );
+               }
 
                wfDeprecated( __METHOD__, '1.31' );
 
@@ -350,6 +370,7 @@ class Revision implements IDBAccessObject {
                        'rev_timestamp',
                        'rev_user_text',
                        'rev_user',
+                       'rev_actor' => 'NULL',
                        'rev_minor_edit',
                        'rev_deleted',
                        'rev_len',
@@ -374,7 +395,17 @@ class Revision implements IDBAccessObject {
         * @return array
         */
        public static function selectArchiveFields() {
-               global $wgContentHandlerUseDB;
+               global $wgContentHandlerUseDB, $wgActorTableSchemaMigrationStage;
+
+               if ( $wgActorTableSchemaMigrationStage > MIGRATION_WRITE_BOTH ) {
+                       // If code is using this instead of self::getQueryInfo(), there's a
+                       // decent chance it's going to try to directly access
+                       // $row->ar_user or $row->ar_user_text and we can't give it
+                       // useful values here once those aren't being written anymore.
+                       throw new BadMethodCallException(
+                               'Cannot use ' . __METHOD__ . ' when $wgActorTableSchemaMigrationStage > MIGRATION_WRITE_BOTH'
+                       );
+               }
 
                wfDeprecated( __METHOD__, '1.31' );
 
@@ -387,6 +418,7 @@ class Revision implements IDBAccessObject {
                        'ar_timestamp',
                        'ar_user_text',
                        'ar_user',
+                       'ar_actor' => 'NULL',
                        'ar_minor_edit',
                        'ar_deleted',
                        'ar_len',
@@ -623,7 +655,7 @@ class Revision implements IDBAccessObject {
         */
        public function setUserIdAndName( $id, $name ) {
                if ( $this->mRecord instanceof MutableRevisionRecord ) {
-                       $user = new UserIdentityValue( intval( $id ), $name );
+                       $user = User::newFromAnyId( intval( $id ), $name, null );
                        $this->mRecord->setUser( $user );
                } else {
                        throw new MWException( __METHOD__ . ' is not supported on this instance' );
index fa454e0..5243cc6 100644 (file)
@@ -203,6 +203,16 @@ abstract class RevisionItemBase {
                return false;
        }
 
+       /**
+        * Get the DB field name storing actor ids.
+        * Override this function.
+        * @since 1.31
+        * @return bool
+        */
+       public function getAuthorActorField() {
+               return false;
+       }
+
        /**
         * Get the ID, as it would appear in the ids URL parameter
         * @return int
@@ -257,6 +267,16 @@ abstract class RevisionItemBase {
                return strval( $this->row->$field );
        }
 
+       /**
+        * Get the author actor ID
+        * @since 1.31
+        * @return string
+        */
+       public function getAuthorActor() {
+               $field = $this->getAuthorActorField();
+               return strval( $this->row->$field );
+       }
+
        /**
         * Returns true if the current user can view the item
         */
index 8b0452d..3e3c897 100644 (file)
@@ -179,7 +179,8 @@ return [
        'WatchedItemQueryService' => function ( MediaWikiServices $services ) {
                return new WatchedItemQueryService(
                        $services->getDBLoadBalancer(),
-                       $services->getCommentStore()
+                       $services->getCommentStore(),
+                       $services->getActorMigration()
                );
        },
 
@@ -442,6 +443,29 @@ return [
                );
        },
 
+       'UploadRevisionImporter' => function ( MediaWikiServices $services ) {
+               return new ImportableUploadRevisionImporter(
+                       $services->getMainConfig()->get( 'EnableUploads' ),
+                       LoggerFactory::getInstance( 'UploadRevisionImporter' )
+               );
+       },
+
+       'OldRevisionImporter' => function ( MediaWikiServices $services ) {
+               return new ImportableOldRevisionImporter(
+                       true,
+                       LoggerFactory::getInstance( 'OldRevisionImporter' ),
+                       $services->getDBLoadBalancer()
+               );
+       },
+
+       'WikiRevisionOldRevisionImporterNoUpdates' => function ( MediaWikiServices $services ) {
+               return new ImportableOldRevisionImporter(
+                       false,
+                       LoggerFactory::getInstance( 'OldRevisionImporter' ),
+                       $services->getDBLoadBalancer()
+               );
+       },
+
        'ShellCommandFactory' => function ( MediaWikiServices $services ) {
                $config = $services->getMainConfig();
 
@@ -477,7 +501,8 @@ return [
                        $services->getDBLoadBalancer(),
                        $blobStore,
                        $services->getMainWANObjectCache(),
-                       $services->getCommentStore()
+                       $services->getCommentStore(),
+                       $services->getActorMigration()
                );
 
                $store->setLogger( LoggerFactory::getInstance( 'RevisionStore' ) );
@@ -532,7 +557,13 @@ return [
                        $wgContLang,
                        $services->getMainConfig()->get( 'CommentTableSchemaMigrationStage' )
                );
-       }
+       },
+
+       'ActorMigration' => function ( MediaWikiServices $services ) {
+               return new ActorMigration(
+                       $services->getMainConfig()->get( 'ActorTableSchemaMigrationStage' )
+               );
+       },
 
        ///////////////////////////////////////////////////////////////////////////
        // NOTE: When adding a service here, don't forget to add a getter function
index 28caf3a..8b1112b 100644 (file)
@@ -110,4 +110,10 @@ interface BlobStore {
         */
        public function storeBlob( $data, $hints = [] );
 
+       /**
+        * Check if the blob metadata or backing blob data store is read-only
+        *
+        * @return bool
+        */
+       public function isReadOnly();
 }
index e7c9060..e13fc1f 100644 (file)
@@ -26,6 +26,7 @@
 
 namespace MediaWiki\Storage;
 
+use ActorMigration;
 use CommentStore;
 use CommentStoreComment;
 use Content;
@@ -97,6 +98,11 @@ class RevisionStore
         */
        private $commentStore;
 
+       /**
+        * @var ActorMigration
+        */
+       private $actorMigration;
+
        /**
         * @var LoggerInterface
         */
@@ -109,6 +115,7 @@ class RevisionStore
         * @param SqlBlobStore $blobStore
         * @param WANObjectCache $cache
         * @param CommentStore $commentStore
+        * @param ActorMigration $actorMigration
         * @param bool|string $wikiId
         */
        public function __construct(
@@ -116,6 +123,7 @@ class RevisionStore
                SqlBlobStore $blobStore,
                WANObjectCache $cache,
                CommentStore $commentStore,
+               ActorMigration $actorMigration,
                $wikiId = false
        ) {
                Assert::parameterType( 'string|boolean', $wikiId, '$wikiId' );
@@ -124,6 +132,7 @@ class RevisionStore
                $this->blobStore = $blobStore;
                $this->cache = $cache;
                $this->commentStore = $commentStore;
+               $this->actorMigration = $actorMigration;
                $this->wikiId = $wikiId;
                $this->logger = new NullLogger();
        }
@@ -132,6 +141,13 @@ class RevisionStore
                $this->logger = $logger;
        }
 
+       /**
+        * @return bool Whether the store is read-only
+        */
+       public function isReadOnly() {
+               return $this->blobStore->isReadOnly();
+       }
+
        /**
         * @return bool
         */
@@ -381,14 +397,16 @@ class RevisionStore
                $user = $this->failOnNull( $rev->getUser( RevisionRecord::RAW ), 'user' );
                $timestamp = $this->failOnEmpty( $rev->getTimestamp(), 'timestamp field' );
 
+               // Checks.
+               $this->failOnNull( $user->getId(), 'user field' );
+               $this->failOnEmpty( $user->getName(), 'user_text field' );
+
                # Record the edit in revisions
                $row = [
                        'rev_page'       => $pageId,
                        'rev_parent_id'  => $parentId,
                        'rev_text_id'    => $textId,
                        'rev_minor_edit' => $rev->isMinor() ? 1 : 0,
-                       'rev_user'       => $this->failOnNull( $user->getId(), 'user field' ),
-                       'rev_user_text'  => $this->failOnEmpty( $user->getName(), 'user_text field' ),
                        'rev_timestamp'  => $dbw->timestamp( $timestamp ),
                        'rev_deleted'    => $rev->getVisibility(),
                        'rev_len'        => $size,
@@ -404,6 +422,10 @@ class RevisionStore
                        $this->commentStore->insertWithTempTable( $dbw, 'rev_comment', $comment );
                $row += $commentFields;
 
+               list( $actorFields, $actorCallback ) =
+                       $this->actorMigration->getInsertValuesWithTempTable( $dbw, 'rev_user', $user );
+               $row += $actorFields;
+
                if ( $this->contentHandlerUseDB ) {
                        // MCR migration note: rev_content_model and rev_content_format will go away
 
@@ -421,13 +443,14 @@ class RevisionStore
                        $row['rev_id'] = intval( $dbw->insertId() );
                }
                $commentCallback( $row['rev_id'] );
+               $actorCallback( $row['rev_id'], $row );
 
                // Insert IP revision into ip_changes for use when querying for a range.
-               if ( $row['rev_user'] === 0 && IP::isValid( $row['rev_user_text'] ) ) {
+               if ( $user->getId() === 0 && IP::isValid( $user->getName() ) ) {
                        $ipcRow = [
                                'ipc_rev_id'        => $row['rev_id'],
                                'ipc_rev_timestamp' => $row['rev_timestamp'],
-                               'ipc_hex'           => IP::toHex( $row['rev_user_text'] ),
+                               'ipc_hex'           => IP::toHex( $user->getName() ),
                        ];
                        $dbw->insert( 'ip_changes', $ipcRow, __METHOD__ );
                }
@@ -435,8 +458,6 @@ class RevisionStore
                $newSlot = SlotRecord::newSaved( $row['rev_id'], $blobAddress, $slot );
                $slots = new RevisionSlots( [ 'main' => $newSlot ] );
 
-               $user = new UserIdentityValue( intval( $row['rev_user'] ), $row['rev_user_text'] );
-
                $rev = new RevisionStoreRecord(
                        $title,
                        $user,
@@ -576,6 +597,7 @@ class RevisionStore
                                'page'       => $title->getArticleID(),
                                'user_text'  => $user->getName(),
                                'user'       => $user->getId(),
+                               'actor'      => $user->getActorId(),
                                'comment'    => $comment,
                                'minor_edit' => $minor,
                                'text_id'    => $current->rev_text_id,
@@ -647,9 +669,10 @@ class RevisionStore
                }
 
                // TODO: Select by rc_this_oldid alone - but as of Nov 2017, there is no index on that!
+               $actorWhere = $this->actorMigration->getWhere( $dbr, 'rc_user', $rev->getUser(), false );
                $rc = RecentChange::newFromConds(
                        [
-                               'rc_user_text' => $userIdentity->getName(),
+                               $actorWhere['conds'],
                                'rc_timestamp' => $dbr->timestamp( $rev->getTimestamp() ),
                                'rc_this_oldid' => $rev->getId()
                        ],
@@ -684,6 +707,7 @@ class RevisionStore
                        'ar_timestamp'      => 'rev_timestamp',
                        'ar_user_text'      => 'rev_user_text',
                        'ar_user'           => 'rev_user',
+                       'ar_actor'          => 'rev_actor',
                        'ar_minor_edit'     => 'rev_minor_edit',
                        'ar_deleted'        => 'rev_deleted',
                        'ar_len'            => 'rev_len',
@@ -734,14 +758,12 @@ class RevisionStore
 
                if ( is_object( $row ) ) {
                        // archive row
-                       if ( !isset( $row->rev_id ) && isset( $row->ar_user ) ) {
+                       if ( !isset( $row->rev_id ) && ( isset( $row->ar_user ) || isset( $row->ar_actor ) ) ) {
                                $row = $this->mapArchiveFields( $row );
                        }
 
                        if ( isset( $row->rev_text_id ) && $row->rev_text_id > 0 ) {
                                $mainSlotRow->cont_address = 'tt:' . $row->rev_text_id;
-                       } elseif ( isset( $row->ar_id ) ) {
-                               $mainSlotRow->cont_address = 'ar:' . $row->ar_id;
                        }
 
                        if ( isset( $row->old_text ) ) {
@@ -1075,7 +1097,16 @@ class RevisionStore
                        $row->$field = $value;
                }
 
-               $user = $this->getUserIdentityFromRowObject( $row, 'ar_' );
+               try {
+                       $user = User::newFromAnyId(
+                               isset( $row->ar_user ) ? $row->ar_user : null,
+                               isset( $row->ar_user_text ) ? $row->ar_user_text : null,
+                               isset( $row->ar_actor ) ? $row->ar_actor : null
+                       );
+               } catch ( InvalidArgumentException $ex ) {
+                       wfWarn( __METHOD__ . ': ' . $ex->getMessage() );
+                       $user = new UserIdentityValue( 0, '', 0 );
+               }
 
                $comment = $this->commentStore
                        // Legacy because $row may have come from self::selectFields()
@@ -1087,34 +1118,6 @@ class RevisionStore
                return new RevisionArchiveRecord( $title, $user, $comment, $row, $slots, $this->wikiId );
        }
 
-       /**
-        * @param object $row
-        * @param string $prefix Field prefix, such as 'rev_' or 'ar_'.
-        *
-        * @return UserIdentityValue
-        */
-       private function getUserIdentityFromRowObject( $row, $prefix = 'rev_' ) {
-               $idField = "{$prefix}user";
-               $nameField = "{$prefix}user_text";
-
-               $userId = intval( $row->$idField );
-
-               if ( isset( $row->user_name ) ) {
-                       $userName = $row->user_name;
-               } elseif ( isset( $row->$nameField ) ) {
-                       $userName = $row->$nameField;
-               } else {
-                       $userName = User::whoIs( $userId );
-               }
-
-               if ( $userName === false ) {
-                       wfWarn( __METHOD__ . ': Cannot determine user name for user ID ' . $userId );
-                       $userName = '';
-               }
-
-               return new UserIdentityValue( $userId, $userName );
-       }
-
        /**
         * @see RevisionFactory::newRevisionFromRow_1_29
         *
@@ -1145,7 +1148,16 @@ class RevisionStore
                        }
                }
 
-               $user = $this->getUserIdentityFromRowObject( $row );
+               try {
+                       $user = User::newFromAnyId(
+                               isset( $row->rev_user ) ? $row->rev_user : null,
+                               isset( $row->rev_user_text ) ? $row->rev_user_text : null,
+                               isset( $row->rev_actor ) ? $row->rev_actor : null
+                       );
+               } catch ( InvalidArgumentException $ex ) {
+                       wfWarn( __METHOD__ . ': ' . $ex->getMessage() );
+                       $user = new UserIdentityValue( 0, '', 0 );
+               }
 
                $comment = $this->commentStore
                        // Legacy because $row may have come from self::selectFields()
@@ -1224,27 +1236,6 @@ class RevisionStore
                        }
                }
 
-               // Replaces old lazy loading logic in Revision::getUserText.
-               if ( !isset( $fields['user_text'] ) && isset( $fields['user'] ) ) {
-                       if ( $fields['user'] instanceof UserIdentity ) {
-                               /** @var User $user */
-                               $user = $fields['user'];
-                               $fields['user_text'] = $user->getName();
-                               $fields['user'] = $user->getId();
-                       } else {
-                               // TODO: wrap this in a callback to make it lazy again.
-                               $name = $fields['user'] === 0 ? false : User::whoIs( $fields['user'] );
-
-                               if ( $name === false ) {
-                                       throw new MWException(
-                                               'user_text not given, and unknown user ID ' . $fields['user']
-                                       );
-                               }
-
-                               $fields['user_text'] = $name;
-                       }
-               }
-
                if (
                        isset( $fields['comment'] )
                        && !( $fields['comment'] instanceof CommentStoreComment )
@@ -1287,16 +1278,15 @@ class RevisionStore
 
                if ( isset( $fields['user'] ) && ( $fields['user'] instanceof UserIdentity ) ) {
                        $user = $fields['user'];
-               } elseif ( isset( $fields['user'] ) && isset( $fields['user_text'] ) ) {
-                       $user = new UserIdentityValue( intval( $fields['user'] ), $fields['user_text'] );
-               } elseif ( isset( $fields['user'] ) ) {
-                       $user = User::newFromId( intval( $fields['user'] ) );
-               } elseif ( isset( $fields['user_text'] ) ) {
-                       $user = User::newFromName( $fields['user_text'] );
-
-                       // User::newFromName will return false for IP addresses (and invalid names)
-                       if ( $user == false ) {
-                               $user = new UserIdentityValue( 0, $fields['user_text'] );
+               } else {
+                       try {
+                               $user = User::newFromAnyId(
+                                       isset( $fields['user'] ) ? $fields['user'] : null,
+                                       isset( $fields['user_text'] ) ? $fields['user_text'] : null,
+                                       isset( $fields['actor'] ) ? $fields['actor'] : null
+                               );
+                       } catch ( InvalidArgumentException $ex ) {
+                               $user = null;
                        }
                }
 
@@ -1613,8 +1603,6 @@ class RevisionStore
                        'rev_page',
                        'rev_text_id',
                        'rev_timestamp',
-                       'rev_user_text',
-                       'rev_user',
                        'rev_minor_edit',
                        'rev_deleted',
                        'rev_len',
@@ -1627,6 +1615,11 @@ class RevisionStore
                $ret['fields'] = array_merge( $ret['fields'], $commentQuery['fields'] );
                $ret['joins'] = array_merge( $ret['joins'], $commentQuery['joins'] );
 
+               $actorQuery = $this->actorMigration->getJoin( 'rev_user' );
+               $ret['tables'] = array_merge( $ret['tables'], $actorQuery['tables'] );
+               $ret['fields'] = array_merge( $ret['fields'], $actorQuery['fields'] );
+               $ret['joins'] = array_merge( $ret['joins'], $actorQuery['joins'] );
+
                if ( $this->contentHandlerUseDB ) {
                        $ret['fields'][] = 'rev_content_format';
                        $ret['fields'][] = 'rev_content_model';
@@ -1650,7 +1643,8 @@ class RevisionStore
                        $ret['fields'] = array_merge( $ret['fields'], [
                                'user_name',
                        ] );
-                       $ret['joins']['user'] = [ 'LEFT JOIN', [ 'rev_user != 0', 'user_id = rev_user' ] ];
+                       $u = $actorQuery['fields']['rev_user'];
+                       $ret['joins']['user'] = [ 'LEFT JOIN', [ "$u != 0", "user_id = $u" ] ];
                }
 
                if ( in_array( 'text', $options, true ) ) {
@@ -1680,8 +1674,9 @@ class RevisionStore
         */
        public function getArchiveQueryInfo() {
                $commentQuery = $this->commentStore->getJoin( 'ar_comment' );
+               $actorQuery = $this->actorMigration->getJoin( 'ar_user' );
                $ret = [
-                       'tables' => [ 'archive' ] + $commentQuery['tables'],
+                       'tables' => [ 'archive' ] + $commentQuery['tables'] + $actorQuery['tables'],
                        'fields' => [
                                        'ar_id',
                                        'ar_page_id',
@@ -1691,15 +1686,13 @@ class RevisionStore
                                        'ar_text',
                                        'ar_text_id',
                                        'ar_timestamp',
-                                       'ar_user_text',
-                                       'ar_user',
                                        'ar_minor_edit',
                                        'ar_deleted',
                                        'ar_len',
                                        'ar_parent_id',
                                        'ar_sha1',
-                               ] + $commentQuery['fields'],
-                       'joins' => $commentQuery['joins'],
+                               ] + $commentQuery['fields'] + $actorQuery['fields'],
+                       'joins' => $commentQuery['joins'] + $actorQuery['joins'],
                ];
 
                if ( $this->contentHandlerUseDB ) {
@@ -1922,15 +1915,19 @@ class RevisionStore
                        return false;
                }
 
+               $revQuery = self::getQueryInfo();
                $res = $db->select(
-                       'revision',
-                       'rev_user',
+                       $revQuery['tables'],
+                       [
+                               'rev_user' => $revQuery['fields']['rev_user'],
+                       ],
                        [
                                'rev_page' => $pageId,
                                'rev_timestamp > ' . $db->addQuotes( $db->timestamp( $since ) )
                        ],
                        __METHOD__,
-                       [ 'ORDER BY' => 'rev_timestamp ASC', 'LIMIT' => 50 ]
+                       [ 'ORDER BY' => 'rev_timestamp ASC', 'LIMIT' => 50 ],
+                       $revQuery['joins']
                );
                foreach ( $res as $row ) {
                        if ( $row->rev_user != $userId ) {
index 031cb58..0ff7c13 100644 (file)
@@ -299,7 +299,6 @@ class SqlBlobStore implements IDBAccessObject, BlobStore {
                list( $schema, $id, ) = self::splitBlobAddress( $blobAddress );
 
                //TODO: MCR: also support 'ex' schema with ExternalStore URLs, plus flags encoded in the URL!
-               //TODO: MCR: also support 'ar' schema for content blobs in old style archive rows!
                if ( $schema === 'tt' ) {
                        $textId = intval( $id );
                } else {
@@ -591,4 +590,11 @@ class SqlBlobStore implements IDBAccessObject, BlobStore {
                return [ $schema, $id, $parameters ];
        }
 
+       public function isReadOnly() {
+               if ( $this->useExternalStore && ExternalStore::defaultStoresAreReadOnly() ) {
+                       return true;
+               }
+
+               return ( $this->getDBLoadBalancer()->getReadOnlyReason() !== false );
+       }
 }
index 1be9863..6dc7db5 100644 (file)
@@ -1303,22 +1303,52 @@ class Title implements LinkTarget {
         * show "inactive" CSS or JS.
         *
         * @return bool
-        * @todo FIXME: Rename to isSiteConfigPage() and remove deprecated hook
+        * @since 1.31
+        */
+       public function isSiteConfigPage() {
+               return (
+                       NS_MEDIAWIKI == $this->mNamespace
+                       && (
+                               $this->hasContentModel( CONTENT_MODEL_CSS )
+                               || $this->hasContentModel( CONTENT_MODEL_JAVASCRIPT )
+                       )
+               );
+       }
+
+       /**
+        * @return bool
+        * @deprecated Since 1.31; use ::isSiteConfigPage() instead
         */
        public function isCssOrJsPage() {
-               $isCssOrJsPage = NS_MEDIAWIKI == $this->mNamespace
-                       && ( $this->hasContentModel( CONTENT_MODEL_CSS )
-                               || $this->hasContentModel( CONTENT_MODEL_JAVASCRIPT ) );
+               // wfDeprecated( __METHOD__, '1.31' );
+               return ( NS_MEDIAWIKI == $this->mNamespace
+                               && ( $this->hasContentModel( CONTENT_MODEL_CSS )
+                                       || $this->hasContentModel( CONTENT_MODEL_JAVASCRIPT ) ) );
+       }
 
-               return $isCssOrJsPage;
+       /**
+        * Is this a "config" (.css or .js) sub-page of a user page?
+        *
+        * @return bool
+        * @since 1.31
+        */
+       public function isUserConfigPage() {
+               return (
+                       NS_USER == $this->mNamespace
+                       && $this->isSubpage()
+                       && (
+                               $this->hasContentModel( CONTENT_MODEL_CSS )
+                               || $this->hasContentModel( CONTENT_MODEL_JAVASCRIPT )
+                       )
+               );
        }
 
        /**
-        * Is this a .css or .js subpage of a user page?
         * @return bool
-        * @todo FIXME: Rename to isUserConfigPage()
+        * @deprecated Since 1.31; use ::isUserConfigPage() instead
         */
        public function isCssJsSubpage() {
+               // wfDeprecated( __METHOD__, '1.31' );
                return ( NS_USER == $this->mNamespace && $this->isSubpage()
                                && ( $this->hasContentModel( CONTENT_MODEL_CSS )
                                        || $this->hasContentModel( CONTENT_MODEL_JAVASCRIPT ) ) );
@@ -1328,8 +1358,9 @@ class Title implements LinkTarget {
         * Trim down a .css or .js subpage title to get the corresponding skin name
         *
         * @return string Containing skin name from .css or .js subpage title
+        * @since 1.31
         */
-       public function getSkinFromCssJsSubpage() {
+       public function getSkinFromConfigSubpage() {
                $subpage = explode( '/', $this->mTextform );
                $subpage = $subpage[count( $subpage ) - 1];
                $lastdot = strrpos( $subpage, '.' );
@@ -1340,23 +1371,58 @@ class Title implements LinkTarget {
        }
 
        /**
-        * Is this a .css subpage of a user page?
+        * @deprecated Since 1.31; use ::getSkinFromConfigSubpage() instead
+        * @return string Containing skin name from .css or .js subpage title
+        */
+       public function getSkinFromCssJsSubpage() {
+               wfDeprecated( __METHOD__, '1.31' );
+               return $this->getSkinFromConfigSubpage();
+       }
+
+       /**
+        * Is this a CSS "config" sub-page of a user page?
         *
         * @return bool
+        * @since 1.31
+        */
+       public function isUserCssConfigPage() {
+               return (
+                       NS_USER == $this->mNamespace
+                       && $this->isSubpage()
+                       && $this->hasContentModel( CONTENT_MODEL_CSS )
+               );
+       }
+
+       /**
+        * @deprecated Since 1.31; use ::isUserCssConfigPage()
+        * @return bool
         */
        public function isCssSubpage() {
-               return ( NS_USER == $this->mNamespace && $this->isSubpage()
-                       && $this->hasContentModel( CONTENT_MODEL_CSS ) );
+               // wfDeprecated( __METHOD__, '1.31' );
+               return $this->isUserCssConfigPage();
        }
 
        /**
         * Is this a .js subpage of a user page?
         *
         * @return bool
+        * @since 1.31
+        */
+       public function isUserJsConfigPage() {
+               return (
+                       NS_USER == $this->mNamespace
+                       && $this->isSubpage()
+                       && $this->hasContentModel( CONTENT_MODEL_JAVASCRIPT )
+               );
+       }
+
+       /**
+        * @deprecated Since 1.31; use ::isUserCssConfigPage()
+        * @return bool
         */
        public function isJsSubpage() {
-               return ( NS_USER == $this->mNamespace && $this->isSubpage()
-                       && $this->hasContentModel( CONTENT_MODEL_JAVASCRIPT ) );
+               // wfDeprecated( __METHOD__, '1.31' );
+               return $this->isUserJsConfigPage();
        }
 
        /**
@@ -2260,20 +2326,33 @@ class Title implements LinkTarget {
         *
         * @return array List of errors
         */
-       private function checkCSSandJSPermissions( $action, $user, $errors, $rigor, $short ) {
+       private function checkUserConfigPermissions( $action, $user, $errors, $rigor, $short ) {
                # Protect css/js subpages of user pages
                # XXX: this might be better using restrictions
+
                if ( $action != 'patrol' ) {
                        if ( preg_match( '/^' . preg_quote( $user->getName(), '/' ) . '\//', $this->mTextform ) ) {
-                               if ( $this->isCssSubpage() && !$user->isAllowedAny( 'editmyusercss', 'editusercss' ) ) {
+                               if (
+                                       $this->isUserCssConfigPage()
+                                       && !$user->isAllowedAny( 'editmyusercss', 'editusercss' )
+                               ) {
                                        $errors[] = [ 'mycustomcssprotected', $action ];
-                               } elseif ( $this->isJsSubpage() && !$user->isAllowedAny( 'editmyuserjs', 'edituserjs' ) ) {
+                               } elseif (
+                                       $this->isUserJsConfigPage()
+                                       && !$user->isAllowedAny( 'editmyuserjs', 'edituserjs' )
+                               ) {
                                        $errors[] = [ 'mycustomjsprotected', $action ];
                                }
                        } else {
-                               if ( $this->isCssSubpage() && !$user->isAllowed( 'editusercss' ) ) {
+                               if (
+                                       $this->isUserCssConfigPage()
+                                       && !$user->isAllowed( 'editusercss' )
+                               ) {
                                        $errors[] = [ 'customcssprotected', $action ];
-                               } elseif ( $this->isJsSubpage() && !$user->isAllowed( 'edituserjs' ) ) {
+                               } elseif (
+                                       $this->isUserJsConfigPage()
+                                       && !$user->isAllowed( 'edituserjs' )
+                               ) {
                                        $errors[] = [ 'customjsprotected', $action ];
                                }
                        }
@@ -2330,7 +2409,7 @@ class Title implements LinkTarget {
         * @return array List of errors
         */
        private function checkCascadingSourcesRestrictions( $action, $user, $errors, $rigor, $short ) {
-               if ( $rigor !== 'quick' && !$this->isCssJsSubpage() ) {
+               if ( $rigor !== 'quick' && !$this->isUserConfigPage() ) {
                        # We /could/ use the protection level on the source page, but it's
                        # fairly ugly as we have to establish a precedence hierarchy for pages
                        # included by multiple cascade-protected pages. So just restrict
@@ -2611,7 +2690,7 @@ class Title implements LinkTarget {
                                'checkReadPermissions',
                                'checkUserBlock', // for wgBlockDisablesLogin
                        ];
-               # Don't call checkSpecialsAndNSPermissions or checkCSSandJSPermissions
+               # Don't call checkSpecialsAndNSPermissions or checkUserConfigPermissions
                # here as it will lead to duplicate error messages. This is okay to do
                # since anywhere that checks for create will also check for edit, and
                # those checks are called for edit.
@@ -2629,7 +2708,7 @@ class Title implements LinkTarget {
                                'checkQuickPermissions',
                                'checkPermissionHooks',
                                'checkSpecialsAndNSPermissions',
-                               'checkCSSandJSPermissions',
+                               'checkUserConfigPermissions',
                                'checkPageRestrictions',
                                'checkCascadingSourcesRestrictions',
                                'checkActionPermissions',
@@ -3743,9 +3822,9 @@ class Title implements LinkTarget {
                }
 
                // If we are looking at a css/js user subpage, purge the action=raw.
-               if ( $this->isJsSubpage() ) {
+               if ( $this->isUserJsConfigPage() ) {
                        $urls[] = $this->getInternalURL( 'action=raw&ctype=text/javascript' );
-               } elseif ( $this->isCssSubpage() ) {
+               } elseif ( $this->isUserCssConfigPage() ) {
                        $urls[] = $this->getInternalURL( 'action=raw&ctype=text/css' );
                }
 
@@ -4389,17 +4468,18 @@ class Title implements LinkTarget {
                        return $authors;
                }
                $dbr = wfGetDB( DB_REPLICA );
-               $res = $dbr->select( 'revision', 'DISTINCT rev_user_text',
+               $revQuery = Revision::getQueryInfo();
+               $authors = $dbr->selectFieldValues(
+                       $revQuery['tables'],
+                       $revQuery['fields']['rev_user_text'],
                        [
                                'rev_page' => $this->getArticleID(),
                                "rev_timestamp $old_cmp " . $dbr->addQuotes( $dbr->timestamp( $old->getTimestamp() ) ),
                                "rev_timestamp $new_cmp " . $dbr->addQuotes( $dbr->timestamp( $new->getTimestamp() ) )
                        ], __METHOD__,
-                       [ 'LIMIT' => $limit + 1 ] // add one so caller knows it was truncated
+                       [ 'DISTINCT', 'LIMIT' => $limit + 1 ], // add one so caller knows it was truncated
+                       $revQuery['joins']
                );
-               foreach ( $res as $row ) {
-                       $authors[] = $row->rev_user_text;
-               }
                return $authors;
        }
 
index 1165a26..0988f73 100644 (file)
@@ -718,6 +718,8 @@ class InfoAction extends FormlessAction {
                        self::getCacheKey( $cache, $page->getTitle(), $page->getLatest() ),
                        WANObjectCache::TTL_WEEK,
                        function ( $oldValue, &$ttl, &$setOpts ) use ( $page, $config, $fname ) {
+                               global $wgActorTableSchemaMigrationStage;
+
                                $title = $page->getTitle();
                                $id = $title->getArticleID();
 
@@ -725,6 +727,29 @@ class InfoAction extends FormlessAction {
                                $dbrWatchlist = wfGetDB( DB_REPLICA, 'watchlist' );
                                $setOpts += Database::getCacheSetOptions( $dbr, $dbrWatchlist );
 
+                               if ( $wgActorTableSchemaMigrationStage === MIGRATION_NEW ) {
+                                       $tables = [ 'revision_actor_temp' ];
+                                       $field = 'revactor_actor';
+                                       $pageField = 'revactor_page';
+                                       $tsField = 'revactor_timestamp';
+                                       $joins = [];
+                               } elseif ( $wgActorTableSchemaMigrationStage === MIGRATION_OLD ) {
+                                       $tables = [ 'revision' ];
+                                       $field = 'rev_user_text';
+                                       $pageField = 'rev_page';
+                                       $tsField = 'rev_timestamp';
+                                       $joins = [];
+                               } else {
+                                       $tables = [ 'revision', 'revision_actor_temp', 'actor' ];
+                                       $field = 'COALESCE( actor_name, rev_user_text)';
+                                       $pageField = 'rev_page';
+                                       $tsField = 'rev_timestamp';
+                                       $joins = [
+                                               'revision_actor_temp' => [ 'LEFT JOIN', 'revactor_rev = rev_id' ],
+                                               'actor' => [ 'LEFT JOIN', 'revactor_actor = actor_id' ],
+                                       ];
+                               }
+
                                $watchedItemStore = MediaWikiServices::getInstance()->getWatchedItemStore();
 
                                $result = [];
@@ -752,10 +777,12 @@ class InfoAction extends FormlessAction {
                                        $result['authors'] = 0;
                                } else {
                                        $result['authors'] = (int)$dbr->selectField(
-                                               'revision',
-                                               'COUNT(DISTINCT rev_user_text)',
-                                               [ 'rev_page' => $id ],
-                                               $fname
+                                               $tables,
+                                               "COUNT(DISTINCT $field)",
+                                               [ $pageField => $id ],
+                                               $fname,
+                                               [],
+                                               $joins
                                        );
                                }
 
@@ -776,13 +803,15 @@ class InfoAction extends FormlessAction {
 
                                // Recent number of distinct authors
                                $result['recent_authors'] = (int)$dbr->selectField(
-                                       'revision',
-                                       'COUNT(DISTINCT rev_user_text)',
+                                       $tables,
+                                       "COUNT(DISTINCT $field)",
                                        [
-                                               'rev_page' => $id,
-                                               "rev_timestamp >= " . $dbr->addQuotes( $threshold )
+                                               $pageField => $id,
+                                               "$tsField >= " . $dbr->addQuotes( $threshold )
                                        ],
-                                       $fname
+                                       $fname,
+                                       [],
+                                       $joins
                                );
 
                                // Subpages (if enabled)
index 62d73f4..228d319 100644 (file)
@@ -897,7 +897,7 @@ abstract class ApiBase extends ContextSource {
 
                if ( $badParams ) {
                        $this->dieWithError(
-                               [ 'apierror-mustpostparams', join( ', ', $badParams ), count( $badParams ) ]
+                               [ 'apierror-mustpostparams', implode( ', ', $badParams ), count( $badParams ) ]
                        );
                }
        }
@@ -1152,7 +1152,7 @@ abstract class ApiBase extends ContextSource {
                                if ( $multi ) {
                                        // This loses the potential $wgContLang->checkTitleEncoding() transformation
                                        // done by WebRequest for $_GET. Let's call that a feature.
-                                       $value = join( "\x1f", $request->normalizeUnicode( explode( "\x1f", $rawValue ) ) );
+                                       $value = implode( "\x1f", $request->normalizeUnicode( explode( "\x1f", $rawValue ) ) );
                                } else {
                                        $this->dieWithError( 'apierror-badvalue-notmultivalue', 'badvalue_notmultivalue' );
                                }
@@ -1829,7 +1829,7 @@ abstract class ApiBase extends ContextSource {
                $msgs = [ $this->msg( 'api-usage-mailinglist-ref' ) ];
                Hooks::run( 'ApiDeprecationHelp', [ &$msgs ] );
                if ( count( $msgs ) > 1 ) {
-                       $key = '$' . join( ' $', range( 1, count( $msgs ) ) );
+                       $key = '$' . implode( ' $', range( 1, count( $msgs ) ) );
                        $msg = ( new RawMessage( $key ) )->params( $msgs );
                } else {
                        $msg = reset( $msgs );
index 0df0ca9..af040d1 100644 (file)
@@ -162,7 +162,7 @@ class ApiCSPReport extends ApiBase {
        private function generateLogLine( $flags, $report ) {
                $flagText = '';
                if ( $flags ) {
-                       $flagText = '[' . implode( $flags, ', ' ) . ']';
+                       $flagText = '[' . implode( ', ', $flags ) . ']';
                }
 
                $blockedFile = isset( $report['blocked-uri'] ) ? $report['blocked-uri'] : 'n/a';
index 008015b..a7e3c1b 100644 (file)
@@ -593,7 +593,7 @@ class ApiMain extends ApiBase {
                $this->setCacheMode( 'private' );
 
                $response = $this->getRequest()->response();
-               $headerStr = 'MediaWiki-API-Error: ' . join( ', ', $errCodes );
+               $headerStr = 'MediaWiki-API-Error: ' . implode( ', ', $errCodes );
                $response->header( $headerStr );
 
                // Reset and print just the error message
index 32d081e..f885b72 100644 (file)
@@ -224,10 +224,19 @@ class ApiQueryAllDeletedRevisions extends ApiQueryRevisionsBase {
                }
 
                if ( !is_null( $params['user'] ) ) {
-                       $this->addWhereFld( 'ar_user_text', $params['user'] );
+                       // Don't query by user ID here, it might be able to use the ar_usertext_timestamp index.
+                       $actorQuery = ActorMigration::newMigration()
+                               ->getWhere( $db, 'ar_user', User::newFromName( $params['user'], false ), false );
+                       $this->addTables( $actorQuery['tables'] );
+                       $this->addJoinConds( $actorQuery['joins'] );
+                       $this->addWhere( $actorQuery['conds'] );
                } elseif ( !is_null( $params['excludeuser'] ) ) {
-                       $this->addWhere( 'ar_user_text != ' .
-                               $db->addQuotes( $params['excludeuser'] ) );
+                       // Here there's no chance of using ar_usertext_timestamp.
+                       $actorQuery = ActorMigration::newMigration()
+                               ->getWhere( $db, 'ar_user', User::newFromName( $params['excludeuser'], false ) );
+                       $this->addTables( $actorQuery['tables'] );
+                       $this->addJoinConds( $actorQuery['joins'] );
+                       $this->addWhere( 'NOT(' . $actorQuery['conds'] . ')' );
                }
 
                if ( !is_null( $params['user'] ) || !is_null( $params['excludeuser'] ) ) {
index dde22d8..14f1cc4 100644 (file)
@@ -85,7 +85,6 @@ class ApiQueryAllImages extends ApiQueryGeneratorBase {
                $db = $this->getDB();
 
                $params = $this->extractRequestParams();
-               $userId = !is_null( $params['user'] ) ? User::idFromName( $params['user'] ) : null;
 
                // Table and return fields
                $prop = array_flip( $params['prop'] );
@@ -192,19 +191,22 @@ class ApiQueryAllImages extends ApiQueryGeneratorBase {
 
                        // Image filters
                        if ( !is_null( $params['user'] ) ) {
-                               if ( $userId ) {
-                                       $this->addWhereFld( 'img_user', $userId );
-                               } else {
-                                       $this->addWhereFld( 'img_user_text', $params['user'] );
-                               }
+                               $actorQuery = ActorMigration::newMigration()
+                                       ->getWhere( $db, 'img_user', User::newFromName( $params['user'], false ) );
+                               $this->addTables( $actorQuery['tables'] );
+                               $this->addJoinConds( $actorQuery['joins'] );
+                               $this->addWhere( $actorQuery['conds'] );
                        }
                        if ( $params['filterbots'] != 'all' ) {
+                               $actorQuery = ActorMigration::newMigration()->getJoin( 'img_user' );
+                               $this->addTables( $actorQuery['tables'] );
                                $this->addTables( 'user_groups' );
+                               $this->addJoinConds( $actorQuery['joins'] );
                                $this->addJoinConds( [ 'user_groups' => [
                                        'LEFT JOIN',
                                        [
                                                'ug_group' => User::getGroupsWithPermission( 'bot' ),
-                                               'ug_user = img_user',
+                                               'ug_user = ' . $actorQuery['fields']['img_user'],
                                                'ug_expiry IS NULL OR ug_expiry >= ' . $db->addQuotes( $db->timestamp() )
                                        ]
                                ] ] );
@@ -273,15 +275,6 @@ class ApiQueryAllImages extends ApiQueryGeneratorBase {
                }
                if ( $params['sort'] == 'timestamp' ) {
                        $this->addOption( 'ORDER BY', 'img_timestamp' . $sortFlag );
-                       if ( !is_null( $params['user'] ) ) {
-                               if ( $userId ) {
-                                       $this->addOption( 'USE INDEX', [ 'image' => 'img_user_timestamp' ] );
-                               } else {
-                                       $this->addOption( 'USE INDEX', [ 'image' => 'img_usertext_timestamp' ] );
-                               }
-                       } else {
-                               $this->addOption( 'USE INDEX', [ 'image' => 'img_timestamp' ] );
-                       }
                } else {
                        $this->addOption( 'ORDER BY', 'img_name' . $sortFlag );
                }
index 6823646..3af2459 100644 (file)
@@ -104,19 +104,17 @@ class ApiQueryAllRevisions extends ApiQueryRevisionsBase {
                }
 
                if ( $params['user'] !== null ) {
-                       $id = User::idFromName( $params['user'] );
-                       if ( $id ) {
-                               $this->addWhereFld( 'rev_user', $id );
-                       } else {
-                               $this->addWhereFld( 'rev_user_text', $params['user'] );
-                       }
+                       $actorQuery = ActorMigration::newMigration()
+                               ->getWhere( $db, 'rev_user', User::newFromName( $params['user'], false ) );
+                       $this->addTables( $actorQuery['tables'] );
+                       $this->addJoinConds( $actorQuery['joins'] );
+                       $this->addWhere( $actorQuery['conds'] );
                } elseif ( $params['excludeuser'] !== null ) {
-                       $id = User::idFromName( $params['excludeuser'] );
-                       if ( $id ) {
-                               $this->addWhere( 'rev_user != ' . $id );
-                       } else {
-                               $this->addWhere( 'rev_user_text != ' . $db->addQuotes( $params['excludeuser'] ) );
-                       }
+                       $actorQuery = ActorMigration::newMigration()
+                               ->getWhere( $db, 'rev_user', User::newFromName( $params['excludeuser'], false ) );
+                       $this->addTables( $actorQuery['tables'] );
+                       $this->addJoinConds( $actorQuery['joins'] );
+                       $this->addWhere( 'NOT(' . $actorQuery['conds'] . ')' );
                }
 
                if ( $params['user'] !== null || $params['excludeuser'] !== null ) {
index 26844f3..9652f81 100644 (file)
@@ -41,6 +41,8 @@ class ApiQueryAllUsers extends ApiQueryBase {
        }
 
        public function execute() {
+               global $wgActorTableSchemaMigrationStage;
+
                $params = $this->extractRequestParams();
                $activeUserDays = $this->getConfig()->get( 'ActiveUserDays' );
 
@@ -178,17 +180,36 @@ class ApiQueryAllUsers extends ApiQueryBase {
                        ] ] );
 
                        // Actually count the actions using a subquery (T66505 and T66507)
+                       $tables = [ 'recentchanges' ];
+                       $joins = [];
+                       if ( $wgActorTableSchemaMigrationStage === MIGRATION_OLD ) {
+                               $userCond = 'rc_user_text = user_name';
+                       } else {
+                               $tables[] = 'actor';
+                               $joins['actor'] = [
+                                       $wgActorTableSchemaMigrationStage === MIGRATION_NEW ? 'JOIN' : 'LEFT JOIN',
+                                       'rc_actor = actor_id'
+                               ];
+                               if ( $wgActorTableSchemaMigrationStage === MIGRATION_NEW ) {
+                                       $userCond = 'actor_user = user_id';
+                               } else {
+                                       $userCond = 'actor_user = user_id OR (rc_actor = 0 AND rc_user_text = user_name)';
+                               }
+                       }
                        $timestamp = $db->timestamp( wfTimestamp( TS_UNIX ) - $activeUserSeconds );
                        $this->addFields( [
                                'recentactions' => '(' . $db->selectSQLText(
-                                       'recentchanges',
+                                       $tables,
                                        'COUNT(*)',
                                        [
-                                               'rc_user_text = user_name',
+                                               $userCond,
                                                'rc_type != ' . $db->addQuotes( RC_EXTERNAL ), // no wikidata
                                                'rc_log_type IS NULL OR rc_log_type != ' . $db->addQuotes( 'newusers' ),
                                                'rc_timestamp >= ' . $db->addQuotes( $timestamp ),
-                                       ]
+                                       ],
+                                       __METHOD__,
+                                       [],
+                                       $joins
                                ) . ')'
                        ] );
                }
index 84169cb..3ad45bb 100644 (file)
@@ -446,11 +446,13 @@ abstract class ApiQueryBase extends ApiBase {
                if ( $showBlockInfo ) {
                        $this->addFields( [
                                'ipb_id',
-                               'ipb_by',
-                               'ipb_by_text',
                                'ipb_expiry',
                                'ipb_timestamp'
                        ] );
+                       $actorQuery = ActorMigration::newMigration()->getJoin( 'ipb_by' );
+                       $this->addTables( $actorQuery['tables'] );
+                       $this->addFields( $actorQuery['fields'] );
+                       $this->addJoinConds( $actorQuery['joins'] );
                        $commentQuery = CommentStore::getStore()->getJoin( 'ipb_reason' );
                        $this->addTables( $commentQuery['tables'] );
                        $this->addFields( $commentQuery['fields'] );
index 10695b3..08c13e7 100644 (file)
@@ -55,8 +55,12 @@ class ApiQueryBlocks extends ApiQueryBase {
                $this->addFields( [ 'ipb_auto', 'ipb_id', 'ipb_timestamp' ] );
 
                $this->addFieldsIf( [ 'ipb_address', 'ipb_user' ], $fld_user || $fld_userid );
-               $this->addFieldsIf( 'ipb_by_text', $fld_by );
-               $this->addFieldsIf( 'ipb_by', $fld_byid );
+               if ( $fld_by || $fld_byid ) {
+                       $actorQuery = ActorMigration::newMigration()->getJoin( 'ipb_by' );
+                       $this->addTables( $actorQuery['tables'] );
+                       $this->addFields( $actorQuery['fields'] );
+                       $this->addJoinConds( $actorQuery['joins'] );
+               }
                $this->addFieldsIf( 'ipb_expiry', $fld_expiry );
                $this->addFieldsIf( [ 'ipb_range_start', 'ipb_range_end' ], $fld_range );
                $this->addFieldsIf( [ 'ipb_anon_only', 'ipb_create_account', 'ipb_enable_autoblock',
index 25b7c84..48516a7 100644 (file)
@@ -43,6 +43,8 @@ class ApiQueryContributors extends ApiQueryBase {
        }
 
        public function execute() {
+               global $wgActorTableSchemaMigrationStage;
+
                $db = $this->getDB();
                $params = $this->extractRequestParams();
                $this->requireMaxOneParameter( $params, 'group', 'excludegroup', 'rights', 'excluderights' );
@@ -73,17 +75,27 @@ class ApiQueryContributors extends ApiQueryBase {
                }
 
                $result = $this->getResult();
+               $revQuery = Revision::getQueryInfo();
+
+               // For MIGRATION_NEW, target indexes on the revision_actor_temp table.
+               // Otherwise, revision is fine because it'll have to check all revision rows anyway.
+               $pageField = $wgActorTableSchemaMigrationStage === MIGRATION_NEW ? 'revactor_page' : 'rev_page';
+               $idField = $wgActorTableSchemaMigrationStage === MIGRATION_NEW
+                       ? 'revactor_actor' : $revQuery['fields']['rev_user'];
+               $countField = $wgActorTableSchemaMigrationStage === MIGRATION_NEW
+                       ? 'revactor_actor' : $revQuery['fields']['rev_user_text'];
 
                // First, count anons
-               $this->addTables( 'revision' );
+               $this->addTables( $revQuery['tables'] );
+               $this->addJoinConds( $revQuery['joins'] );
                $this->addFields( [
-                       'page' => 'rev_page',
-                       'anons' => 'COUNT(DISTINCT rev_user_text)',
+                       'page' => $pageField,
+                       'anons' => "COUNT(DISTINCT $countField)",
                ] );
-               $this->addWhereFld( 'rev_page', $pages );
-               $this->addWhere( 'rev_user = 0' );
+               $this->addWhereFld( $pageField, $pages );
+               $this->addWhere( ActorMigration::newMigration()->isAnon( $revQuery['fields']['rev_user'] ) );
                $this->addWhere( $db->bitAnd( 'rev_deleted', Revision::DELETED_USER ) . ' = 0' );
-               $this->addOption( 'GROUP BY', 'rev_page' );
+               $this->addOption( 'GROUP BY', $pageField );
                $res = $this->select( __METHOD__ );
                foreach ( $res as $row ) {
                        $fit = $result->addValue( [ 'query', 'pages', $row->page ],
@@ -103,24 +115,27 @@ class ApiQueryContributors extends ApiQueryBase {
 
                // Next, add logged-in users
                $this->resetQueryParams();
-               $this->addTables( 'revision' );
+               $this->addTables( $revQuery['tables'] );
+               $this->addJoinConds( $revQuery['joins'] );
                $this->addFields( [
-                       'page' => 'rev_page',
-                       'user' => 'rev_user',
-                       'username' => 'MAX(rev_user_text)', // Non-MySQL databases don't like partial group-by
+                       'page' => $pageField,
+                       'id' => $idField,
+                       // Non-MySQL databases don't like partial group-by
+                       'userid' => 'MAX(' . $revQuery['fields']['rev_user'] . ')',
+                       'username' => 'MAX(' . $revQuery['fields']['rev_user_text'] . ')',
                ] );
-               $this->addWhereFld( 'rev_page', $pages );
-               $this->addWhere( 'rev_user != 0' );
+               $this->addWhereFld( $pageField, $pages );
+               $this->addWhere( ActorMigration::newMigration()->isNotAnon( $revQuery['fields']['rev_user'] ) );
                $this->addWhere( $db->bitAnd( 'rev_deleted', Revision::DELETED_USER ) . ' = 0' );
-               $this->addOption( 'GROUP BY', 'rev_page, rev_user' );
+               $this->addOption( 'GROUP BY', [ $pageField, $idField ] );
                $this->addOption( 'LIMIT', $params['limit'] + 1 );
 
                // Force a sort order to ensure that properties are grouped by page
-               // But only if pp_page is not constant in the WHERE clause.
+               // But only if rev_page is not constant in the WHERE clause.
                if ( count( $pages ) > 1 ) {
-                       $this->addOption( 'ORDER BY', 'rev_page, rev_user' );
+                       $this->addOption( 'ORDER BY', [ 'page', 'id' ] );
                } else {
-                       $this->addOption( 'ORDER BY', 'rev_user' );
+                       $this->addOption( 'ORDER BY', 'id' );
                }
 
                $limitGroups = [];
@@ -159,7 +174,7 @@ class ApiQueryContributors extends ApiQueryBase {
                        $this->addJoinConds( [ 'user_groups' => [
                                $excludeGroups ? 'LEFT OUTER JOIN' : 'INNER JOIN',
                                [
-                                       'ug_user=rev_user',
+                                       'ug_user=' . $actorQuery['fields']['rev_user'],
                                        'ug_group' => $limitGroups,
                                        'ug_expiry IS NULL OR ug_expiry >= ' . $db->addQuotes( $db->timestamp() )
                                ]
@@ -171,11 +186,11 @@ class ApiQueryContributors extends ApiQueryBase {
                        $cont = explode( '|', $params['continue'] );
                        $this->dieContinueUsageIf( count( $cont ) != 2 );
                        $cont_page = (int)$cont[0];
-                       $cont_user = (int)$cont[1];
+                       $cont_id = (int)$cont[1];
                        $this->addWhere(
-                               "rev_page > $cont_page OR " .
-                               "(rev_page = $cont_page AND " .
-                               "rev_user >= $cont_user)"
+                               "$pageField > $cont_page OR " .
+                               "($pageField = $cont_page AND " .
+                               "$idField >= $cont_id)"
                        );
                }
 
@@ -185,18 +200,16 @@ class ApiQueryContributors extends ApiQueryBase {
                        if ( ++$count > $params['limit'] ) {
                                // We've reached the one extra which shows that
                                // there are additional pages to be had. Stop here...
-                               $this->setContinueEnumParameter( 'continue', $row->page . '|' . $row->user );
-
+                               $this->setContinueEnumParameter( 'continue', $row->page . '|' . $row->id );
                                return;
                        }
 
                        $fit = $this->addPageSubItem( $row->page,
-                               [ 'userid' => (int)$row->user, 'name' => $row->username ],
+                               [ 'userid' => (int)$row->userid, 'name' => $row->username ],
                                'user'
                        );
                        if ( !$fit ) {
-                               $this->setContinueEnumParameter( 'continue', $row->page . '|' . $row->user );
-
+                               $this->setContinueEnumParameter( 'continue', $row->page . '|' . $row->id );
                                return;
                        }
                }
index f579065..b7fd8d4 100644 (file)
@@ -117,10 +117,19 @@ class ApiQueryDeletedRevisions extends ApiQueryRevisionsBase {
                }
 
                if ( !is_null( $params['user'] ) ) {
-                       $this->addWhereFld( 'ar_user_text', $params['user'] );
+                       // Don't query by user ID here, it might be able to use the ar_usertext_timestamp index.
+                       $actorQuery = ActorMigration::newMigration()
+                               ->getWhere( $db, 'ar_user', User::newFromName( $params['user'], false ), false );
+                       $this->addTables( $actorQuery['tables'] );
+                       $this->addJoinConds( $actorQuery['joins'] );
+                       $this->addWhere( $actorQuery['conds'] );
                } elseif ( !is_null( $params['excludeuser'] ) ) {
-                       $this->addWhere( 'ar_user_text != ' .
-                               $db->addQuotes( $params['excludeuser'] ) );
+                       // Here there's no chance of using ar_usertext_timestamp.
+                       $actorQuery = ActorMigration::newMigration()
+                               ->getWhere( $db, 'ar_user', User::newFromName( $params['excludeuser'], false ) );
+                       $this->addTables( $actorQuery['tables'] );
+                       $this->addJoinConds( $actorQuery['joins'] );
+                       $this->addWhere( 'NOT(' . $actorQuery['conds'] . ')' );
                }
 
                if ( !is_null( $params['user'] ) || !is_null( $params['excludeuser'] ) ) {
index 6e6757e..2d50741 100644 (file)
@@ -110,8 +110,12 @@ class ApiQueryDeletedrevs extends ApiQueryBase {
 
                $this->addFieldsIf( 'ar_parent_id', $fld_parentid );
                $this->addFieldsIf( 'ar_rev_id', $fld_revid );
-               $this->addFieldsIf( 'ar_user_text', $fld_user );
-               $this->addFieldsIf( 'ar_user', $fld_userid );
+               if ( $fld_user || $fld_userid ) {
+                       $actorQuery = ActorMigration::newMigration()->getJoin( 'ar_user' );
+                       $this->addTables( $actorQuery['tables'] );
+                       $this->addFields( $actorQuery['fields'] );
+                       $this->addJoinConds( $actorQuery['joins'] );
+               }
                $this->addFieldsIf( 'ar_minor_edit', $fld_minor );
                $this->addFieldsIf( 'ar_len', $fld_len );
                $this->addFieldsIf( 'ar_sha1', $fld_sha1 );
@@ -199,10 +203,19 @@ class ApiQueryDeletedrevs extends ApiQueryBase {
                }
 
                if ( !is_null( $params['user'] ) ) {
-                       $this->addWhereFld( 'ar_user_text', $params['user'] );
+                       // Don't query by user ID here, it might be able to use the ar_usertext_timestamp index.
+                       $actorQuery = ActorMigration::newMigration()
+                               ->getWhere( $db, 'ar_user', User::newFromName( $params['user'], false ), false );
+                       $this->addTables( $actorQuery['tables'] );
+                       $this->addJoinConds( $actorQuery['joins'] );
+                       $this->addWhere( $actorQuery['conds'] );
                } elseif ( !is_null( $params['excludeuser'] ) ) {
-                       $this->addWhere( 'ar_user_text != ' .
-                               $db->addQuotes( $params['excludeuser'] ) );
+                       // Here there's no chance of using ar_usertext_timestamp.
+                       $actorQuery = ActorMigration::newMigration()
+                               ->getWhere( $db, 'ar_user', User::newFromName( $params['excludeuser'], false ) );
+                       $this->addTables( $actorQuery['tables'] );
+                       $this->addJoinConds( $actorQuery['joins'] );
+                       $this->addWhere( 'NOT(' . $actorQuery['conds'] . ')' );
                }
 
                if ( !is_null( $params['user'] ) || !is_null( $params['excludeuser'] ) ) {
@@ -251,10 +264,6 @@ class ApiQueryDeletedrevs extends ApiQueryBase {
                }
 
                $this->addOption( 'LIMIT', $limit + 1 );
-               $this->addOption(
-                       'USE INDEX',
-                       [ 'archive' => ( $mode == 'user' ? 'ar_usertext_timestamp' : 'name_title_timestamp' ) ]
-               );
                if ( $mode == 'all' ) {
                        if ( $params['unique'] ) {
                                // @todo Does this work on non-MySQL?
index f345300..68902a3 100644 (file)
@@ -62,11 +62,15 @@ class ApiQueryLogEvents extends ApiQueryBase {
                        $this->addWhere( $hideLogs );
                }
 
-               // Order is significant here
-               $this->addTables( [ 'logging', 'user', 'page' ] );
+               $actorMigration = ActorMigration::newMigration();
+               $actorQuery = $actorMigration->getJoin( 'log_user' );
+               $this->addTables( 'logging' );
+               $this->addTables( $actorQuery['tables'] );
+               $this->addTables( [ 'user', 'page' ] );
+               $this->addJoinConds( $actorQuery['joins'] );
                $this->addJoinConds( [
                        'user' => [ 'LEFT JOIN',
-                               'user_id=log_user' ],
+                               'user_id=' . $actorQuery['fields']['log_user'] ],
                        'page' => [ 'LEFT JOIN',
                                [ 'log_namespace=page_namespace',
                                        'log_title=page_title' ] ] ] );
@@ -84,8 +88,8 @@ class ApiQueryLogEvents extends ApiQueryBase {
                // join at query time.  This leads to different results in various
                // scenarios, e.g. deletion, recreation.
                $this->addFieldsIf( 'log_page', $this->fld_ids );
-               $this->addFieldsIf( [ 'log_user', 'log_user_text', 'user_name' ], $this->fld_user );
-               $this->addFieldsIf( 'log_user', $this->fld_userid );
+               $this->addFieldsIf( $actorQuery['fields'] + [ 'user_name' ], $this->fld_user );
+               $this->addFieldsIf( $actorQuery['fields'], $this->fld_userid );
                $this->addFieldsIf(
                        [ 'log_namespace', 'log_title' ],
                        $this->fld_title || $this->fld_parsedcomment
@@ -166,12 +170,14 @@ class ApiQueryLogEvents extends ApiQueryBase {
 
                $user = $params['user'];
                if ( !is_null( $user ) ) {
-                       $userid = User::idFromName( $user );
-                       if ( $userid ) {
-                               $this->addWhereFld( 'log_user', $userid );
-                       } else {
-                               $this->addWhereFld( 'log_user_text', $user );
-                       }
+                       // Note the joins in $q are the same as those from ->getJoin() above
+                       // so we only need to add 'conds' here.
+                       // Don't query by user ID here, it might be able to use the
+                       // log_user_text_time or log_user_text_type_time index.
+                       $q = $actorMigration->getWhere(
+                               $db, 'log_user', User::newFromName( $params['user'], false ), false
+                       );
+                       $this->addWhere( $q['conds'] );
                }
 
                $title = $params['title'];
index e289e42..e431202 100644 (file)
@@ -211,8 +211,18 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
                        $this->addWhereIf( 'rc_minor != 0', isset( $show['minor'] ) );
                        $this->addWhereIf( 'rc_bot = 0', isset( $show['!bot'] ) );
                        $this->addWhereIf( 'rc_bot != 0', isset( $show['bot'] ) );
-                       $this->addWhereIf( 'rc_user = 0', isset( $show['anon'] ) );
-                       $this->addWhereIf( 'rc_user != 0', isset( $show['!anon'] ) );
+                       if ( isset( $show['anon'] ) || isset( $show['!anon'] ) ) {
+                               $actorMigration = ActorMigration::newMigration();
+                               $actorQuery = $actorMigration->getJoin( 'rc_user' );
+                               $this->addTables( $actorQuery['tables'] );
+                               $this->addJoinConds( $actorQuery['joins'] );
+                               $this->addWhereIf(
+                                       $actorMigration->isAnon( $actorQuery['fields']['rc_user'] ), isset( $show['anon'] )
+                               );
+                               $this->addWhereIf(
+                                       $actorMigration->isNotAnon( $actorQuery['fields']['rc_user'] ), isset( $show['!anon'] )
+                               );
+                       }
                        $this->addWhereIf( 'rc_patrolled = 0', isset( $show['!patrolled'] ) );
                        $this->addWhereIf( 'rc_patrolled != 0', isset( $show['patrolled'] ) );
                        $this->addWhereIf( 'page_is_redirect = 1', isset( $show['redirect'] ) );
@@ -237,14 +247,21 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
                $this->requireMaxOneParameter( $params, 'user', 'excludeuser' );
 
                if ( !is_null( $params['user'] ) ) {
-                       $this->addWhereFld( 'rc_user_text', $params['user'] );
+                       // Don't query by user ID here, it might be able to use the rc_user_text index.
+                       $actorQuery = ActorMigration::newMigration()
+                               ->getWhere( $this->getDB(), 'rc_user', User::newFromName( $params['user'], false ), false );
+                       $this->addTables( $actorQuery['tables'] );
+                       $this->addJoinConds( $actorQuery['joins'] );
+                       $this->addWhere( $actorQuery['conds'] );
                }
 
                if ( !is_null( $params['excludeuser'] ) ) {
-                       // We don't use the rc_user_text index here because
-                       // * it would require us to sort by rc_user_text before rc_timestamp
-                       // * the != condition doesn't throw out too many rows anyway
-                       $this->addWhere( 'rc_user_text != ' . $this->getDB()->addQuotes( $params['excludeuser'] ) );
+                       // Here there's no chance to use the rc_user_text index, so allow ID to be used.
+                       $actorQuery = ActorMigration::newMigration()
+                               ->getWhere( $this->getDB(), 'rc_user', User::newFromName( $params['excludeuser'], false ) );
+                       $this->addTables( $actorQuery['tables'] );
+                       $this->addJoinConds( $actorQuery['joins'] );
+                       $this->addWhere( 'NOT(' . $actorQuery['conds'] . ')' );
                }
 
                /* Add the fields we're concerned with to our query. */
@@ -272,8 +289,12 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
 
                        /* Add fields to our query if they are specified as a needed parameter. */
                        $this->addFieldsIf( [ 'rc_this_oldid', 'rc_last_oldid' ], $this->fld_ids );
-                       $this->addFieldsIf( 'rc_user', $this->fld_user || $this->fld_userid );
-                       $this->addFieldsIf( 'rc_user_text', $this->fld_user );
+                       if ( $this->fld_user || $this->fld_userid ) {
+                               $actorQuery = ActorMigration::newMigration()->getJoin( 'rc_user' );
+                               $this->addTables( $actorQuery['tables'] );
+                               $this->addFields( $actorQuery['fields'] );
+                               $this->addJoinConds( $actorQuery['joins'] );
+                       }
                        $this->addFieldsIf( [ 'rc_minor', 'rc_type', 'rc_bot' ], $this->fld_flags );
                        $this->addFieldsIf( [ 'rc_old_len', 'rc_new_len' ], $this->fld_sizes );
                        $this->addFieldsIf( [ 'rc_patrolled', 'rc_log_type' ], $this->fld_patrolled );
index ef0223a..5858bc7 100644 (file)
@@ -286,20 +286,17 @@ class ApiQueryRevisions extends ApiQueryRevisionsBase {
                        $this->addWhereFld( 'rev_page', reset( $ids ) );
 
                        if ( $params['user'] !== null ) {
-                               $user = User::newFromName( $params['user'] );
-                               if ( $user && $user->getId() > 0 ) {
-                                       $this->addWhereFld( 'rev_user', $user->getId() );
-                               } else {
-                                       $this->addWhereFld( 'rev_user_text', $params['user'] );
-                               }
+                               $actorQuery = ActorMigration::newMigration()
+                                       ->getWhere( $db, 'rev_user', User::newFromName( $params['user'], false ) );
+                               $this->addTables( $actorQuery['tables'] );
+                               $this->addJoinConds( $actorQuery['joins'] );
+                               $this->addWhere( $actorQuery['conds'] );
                        } elseif ( $params['excludeuser'] !== null ) {
-                               $user = User::newFromName( $params['excludeuser'] );
-                               if ( $user && $user->getId() > 0 ) {
-                                       $this->addWhere( 'rev_user != ' . $user->getId() );
-                               } else {
-                                       $this->addWhere( 'rev_user_text != ' .
-                                               $db->addQuotes( $params['excludeuser'] ) );
-                               }
+                               $actorQuery = ActorMigration::newMigration()
+                                       ->getWhere( $db, 'rev_user', User::newFromName( $params['excludeuser'], false ) );
+                               $this->addTables( $actorQuery['tables'] );
+                               $this->addJoinConds( $actorQuery['joins'] );
+                               $this->addWhere( 'NOT(' . $actorQuery['conds'] . ')' );
                        }
                        if ( $params['user'] !== null || $params['excludeuser'] !== null ) {
                                // Paranoia: avoid brute force searches (T19342)
index 1705c57..e587ef4 100644 (file)
@@ -31,13 +31,14 @@ class ApiQueryContributions extends ApiQueryBase {
                parent::__construct( $query, $moduleName, 'uc' );
        }
 
-       private $params, $prefixMode, $userprefix, $multiUserMode, $idMode, $usernames, $userids,
-               $parentLens, $commentStore;
+       private $params, $multiUserMode, $orderBy, $parentLens;
        private $fld_ids = false, $fld_title = false, $fld_timestamp = false,
                $fld_comment = false, $fld_parsedcomment = false, $fld_flags = false,
                $fld_patrolled = false, $fld_tags = false, $fld_size = false, $fld_sizediff = false;
 
        public function execute() {
+               global $wgActorTableSchemaMigrationStage;
+
                // Parse some parameters
                $this->params = $this->extractRequestParams();
 
@@ -63,36 +64,173 @@ class ApiQueryContributions extends ApiQueryBase {
                // TODO: if the query is going only against the revision table, should this be done?
                $this->selectNamedDB( 'contributions', DB_REPLICA, 'contributions' );
 
-               $this->requireOnlyOneParameter( $this->params, 'userprefix', 'userids', 'user' );
+               $sort = ( $this->params['dir'] == 'newer' ? '' : ' DESC' );
+               $op = ( $this->params['dir'] == 'older' ? '<' : '>' );
 
-               $this->idMode = false;
+               // Create an Iterator that produces the UserIdentity objects we need, depending
+               // on which of the 'userprefix', 'userids', or 'user' params was
+               // specified.
+               $this->requireOnlyOneParameter( $this->params, 'userprefix', 'userids', 'user' );
                if ( isset( $this->params['userprefix'] ) ) {
-                       $this->prefixMode = true;
                        $this->multiUserMode = true;
-                       $this->userprefix = $this->params['userprefix'];
-               } elseif ( isset( $this->params['userids'] ) ) {
-                       $this->userids = [];
+                       $this->orderBy = 'name';
+                       $fname = __METHOD__;
+
+                       // Because 'userprefix' might produce a huge number of users (e.g.
+                       // a wiki with users "Test00000001" to "Test99999999"), use a
+                       // generator with batched lookup and continuation.
+                       $userIter = call_user_func( function () use ( $dbSecondary, $sort, $op, $fname ) {
+                               global $wgActorTableSchemaMigrationStage;
+
+                               $from = $fromName = false;
+                               if ( !is_null( $this->params['continue'] ) ) {
+                                       $continue = explode( '|', $this->params['continue'] );
+                                       $this->dieContinueUsageIf( count( $continue ) != 4 );
+                                       $this->dieContinueUsageIf( $continue[0] !== 'name' );
+                                       $fromName = $continue[1];
+                                       $from = "$op= " . $dbSecondary->addQuotes( $fromName );
+                               }
+                               $like = $dbSecondary->buildLike( $this->params['userprefix'], $dbSecondary->anyString() );
+
+                               $limit = 501;
+
+                               do {
+                                       // For the new schema, pull from the actor table. For the
+                                       // old, pull from rev_user. For migration a FULL [OUTER]
+                                       // JOIN would be what we want, except MySQL doesn't support
+                                       // that so we have to UNION instead.
+                                       if ( $wgActorTableSchemaMigrationStage === MIGRATION_NEW ) {
+                                               $res = $dbSecondary->select(
+                                                       'actor',
+                                                       [ 'actor_id', 'user_id' => 'COALESCE(actor_user,0)', 'user_name' => 'actor_name' ],
+                                                       array_merge( [ "actor_name$like" ], $from ? [ "actor_name $from" ] : [] ),
+                                                       $fname,
+                                                       [ 'ORDER BY' => [ "user_name $sort" ], 'LIMIT' => $limit ]
+                                               );
+                                       } elseif ( $wgActorTableSchemaMigrationStage === MIGRATION_OLD ) {
+                                               $res = $dbSecondary->select(
+                                                       'revision',
+                                                       [ 'actor_id' => 'NULL', 'user_id' => 'rev_user', 'user_name' => 'rev_user_text' ],
+                                                       array_merge( [ "rev_user_text$like" ], $from ? [ "rev_user_text $from" ] : [] ),
+                                                       $fname,
+                                                       [ 'DISTINCT', 'ORDER BY' => [ "rev_user_text $sort" ], 'LIMIT' => $limit ]
+                                               );
+                                       } else {
+                                               // There are three queries we have to combine to be sure of getting all results:
+                                               //  - actor table (any rows that have been migrated will have empty rev_user_text)
+                                               //  - revision+actor by user id
+                                               //  - revision+actor by name for anons
+                                               $options = $dbSecondary->unionSupportsOrderAndLimit()
+                                                       ? [ 'ORDER BY' => [ "user_name $sort" ], 'LIMIT' => $limit ] : [];
+                                               $subsql = [];
+                                               $subsql[] = $dbSecondary->selectSQLText(
+                                                       'actor',
+                                                       [ 'actor_id', 'user_id' => 'COALESCE(actor_user,0)', 'user_name' => 'actor_name' ],
+                                                       array_merge( [ "actor_name$like" ], $from ? [ "actor_name $from" ] : [] ),
+                                                       $fname,
+                                                       $options
+                                               );
+                                               $subsql[] = $dbSecondary->selectSQLText(
+                                                       [ 'revision', 'actor' ],
+                                                       [ 'actor_id', 'user_id' => 'rev_user', 'user_name' => 'rev_user_text' ],
+                                                       array_merge(
+                                                               [ "rev_user_text$like", 'rev_user != 0' ],
+                                                               $from ? [ "rev_user_text $from" ] : []
+                                                       ),
+                                                       $fname,
+                                                       array_merge( [ 'DISTINCT' ], $options ),
+                                                       [ 'actor' => [ 'LEFT JOIN', 'rev_user = actor_user' ] ]
+                                               );
+                                               $subsql[] = $dbSecondary->selectSQLText(
+                                                       [ 'revision', 'actor' ],
+                                                       [ 'actor_id', 'user_id' => 'rev_user', 'user_name' => 'rev_user_text' ],
+                                                       array_merge(
+                                                               [ "rev_user_text$like", 'rev_user = 0' ],
+                                                               $from ? [ "rev_user_text $from" ] : []
+                                                       ),
+                                                       $fname,
+                                                       array_merge( [ 'DISTINCT' ], $options ),
+                                                       [ 'actor' => [ 'LEFT JOIN', 'rev_user_text = actor_name' ] ]
+                                               );
+                                               $sql = $dbSecondary->unionQueries( $subsql, false ) . " ORDER BY user_name $sort";
+                                               $sql = $dbSecondary->limitResult( $sql, $limit );
+                                               $res = $dbSecondary->query( $sql, $fname );
+                                       }
 
+                                       $count = 0;
+                                       $from = null;
+                                       foreach ( $res as $row ) {
+                                               if ( ++$count >= $limit ) {
+                                                       $from = $row->user_name;
+                                                       break;
+                                               }
+                                               yield User::newFromRow( $row );
+                                       }
+                               } while ( $from !== null );
+                       } );
+                       // Do the actual sorting client-side, because otherwise
+                       // prepareQuery might try to sort by actor and confuse everything.
+                       $batchSize = 1;
+               } elseif ( isset( $this->params['userids'] ) ) {
                        if ( !count( $this->params['userids'] ) ) {
                                $encParamName = $this->encodeParamName( 'userids' );
                                $this->dieWithError( [ 'apierror-paramempty', $encParamName ], "paramempty_$encParamName" );
                        }
 
+                       $ids = [];
                        foreach ( $this->params['userids'] as $uid ) {
                                if ( $uid <= 0 ) {
                                        $this->dieWithError( [ 'apierror-invaliduserid', $uid ], 'invaliduserid' );
                                }
+                               $ids[] = $uid;
+                       }
+
+                       $this->orderBy = 'id';
+                       $this->multiUserMode = count( $ids ) > 1;
 
-                               $this->userids[] = $uid;
+                       $from = $fromId = false;
+                       if ( $this->multiUserMode && !is_null( $this->params['continue'] ) ) {
+                               $continue = explode( '|', $this->params['continue'] );
+                               $this->dieContinueUsageIf( count( $continue ) != 4 );
+                               $this->dieContinueUsageIf( $continue[0] !== 'id' && $continue[0] !== 'actor' );
+                               $fromId = (int)$continue[1];
+                               $this->dieContinueUsageIf( $continue[1] !== (string)$fromId );
+                               $from = "$op= $fromId";
                        }
 
-                       $this->prefixMode = false;
-                       $this->multiUserMode = ( count( $this->params['userids'] ) > 1 );
-                       $this->idMode = true;
+                       // For the new schema, just select from the actor table. For the
+                       // old and transitional schemas, select from user and left join
+                       // actor if it exists.
+                       if ( $wgActorTableSchemaMigrationStage === MIGRATION_NEW ) {
+                               $res = $dbSecondary->select(
+                                       'actor',
+                                       [ 'actor_id', 'user_id' => 'actor_user', 'user_name' => 'actor_name' ],
+                                       array_merge( [ 'actor_user' => $ids ], $from ? [ "actor_id $from" ] : [] ),
+                                       __METHOD__,
+                                       [ 'ORDER BY' => "user_id $sort" ]
+                               );
+                       } elseif ( $wgActorTableSchemaMigrationStage === MIGRATION_OLD ) {
+                               $res = $dbSecondary->select(
+                                       'user',
+                                       [ 'actor_id' => 'NULL', 'user_id' => 'user_id', 'user_name' => 'user_name' ],
+                                       array_merge( [ 'user_id' => $ids ], $from ? [ "user_id $from" ] : [] ),
+                                       __METHOD__,
+                                       [ 'ORDER BY' => "user_id $sort" ]
+                               );
+                       } else {
+                               $res = $dbSecondary->select(
+                                       [ 'user', 'actor' ],
+                                       [ 'actor_id', 'user_id', 'user_name' ],
+                                       array_merge( [ 'user_id' => $ids ], $from ? [ "user_id $from" ] : [] ),
+                                       __METHOD__,
+                                       [ 'ORDER BY' => "user_id $sort" ],
+                                       [ 'actor' => [ 'LEFT JOIN', 'actor_user = user_id' ] ]
+                               );
+                       }
+                       $userIter = UserArray::newFromResult( $res );
+                       $batchSize = count( $ids );
                } else {
-                       $anyIPs = false;
-                       $this->userids = [];
-                       $this->usernames = [];
+                       $names = [];
                        if ( !count( $this->params['user'] ) ) {
                                $encParamName = $this->encodeParamName( 'user' );
                                $this->dieWithError(
@@ -108,8 +246,7 @@ class ApiQueryContributions extends ApiQueryBase {
                                }
 
                                if ( User::isIP( $u ) ) {
-                                       $anyIPs = true;
-                                       $this->usernames[] = $u;
+                                       $names[$u] = null;
                                } else {
                                        $name = User::getCanonicalName( $u, 'valid' );
                                        if ( $name === false ) {
@@ -118,94 +255,218 @@ class ApiQueryContributions extends ApiQueryBase {
                                                        [ 'apierror-baduser', $encParamName, wfEscapeWikiText( $u ) ], "baduser_$encParamName"
                                                );
                                        }
-                                       $this->usernames[] = $name;
+                                       $names[$name] = null;
                                }
                        }
-                       $this->prefixMode = false;
-                       $this->multiUserMode = ( count( $this->params['user'] ) > 1 );
 
-                       if ( !$anyIPs ) {
-                               $dbr = $this->getDB();
-                               $res = $dbr->select( 'user', 'user_id', [ 'user_name' => $this->usernames ], __METHOD__ );
+                       $this->orderBy = 'name';
+                       $this->multiUserMode = count( $names ) > 1;
+
+                       $from = $fromName = false;
+                       if ( $this->multiUserMode && !is_null( $this->params['continue'] ) ) {
+                               $continue = explode( '|', $this->params['continue'] );
+                               $this->dieContinueUsageIf( count( $continue ) != 4 );
+                               $this->dieContinueUsageIf( $continue[0] !== 'name' && $continue[0] !== 'actor' );
+                               $fromName = $continue[1];
+                               $from = "$op= " . $dbSecondary->addQuotes( $fromName );
+                       }
+
+                       // For the new schema, just select from the actor table. For the
+                       // old and transitional schemas, select from user and left join
+                       // actor if it exists then merge in any unknown users (IPs and imports).
+                       if ( $wgActorTableSchemaMigrationStage === MIGRATION_NEW ) {
+                               $res = $dbSecondary->select(
+                                       'actor',
+                                       [ 'actor_id', 'user_id' => 'actor_user', 'user_name' => 'actor_name' ],
+                                       array_merge( [ 'actor_name' => array_keys( $names ) ], $from ? [ "actor_id $from" ] : [] ),
+                                       __METHOD__,
+                                       [ 'ORDER BY' => "actor_name $sort" ]
+                               );
+                               $userIter = UserArray::newFromResult( $res );
+                       } else {
+                               if ( $wgActorTableSchemaMigrationStage === MIGRATION_OLD ) {
+                                       $res = $dbSecondary->select(
+                                               'user',
+                                               [ 'actor_id' => 'NULL', 'user_id', 'user_name' ],
+                                               array_merge( [ 'user_name' => array_keys( $names ) ], $from ? [ "user_name $from" ] : [] ),
+                                               __METHOD__
+                                       );
+                               } else {
+                                       $res = $dbSecondary->select(
+                                               [ 'user', 'actor' ],
+                                               [ 'actor_id', 'user_id', 'user_name' ],
+                                               array_merge( [ 'user_name' => array_keys( $names ) ], $from ? [ "user_name $from" ] : [] ),
+                                               __METHOD__,
+                                               [],
+                                               [ 'actor' => [ 'LEFT JOIN', 'actor_user = user_id' ] ]
+                                       );
+                               }
                                foreach ( $res as $row ) {
-                                       $this->userids[] = $row->user_id;
+                                       $names[$row->user_name] = $row;
                                }
-                               $this->idMode = count( $this->userids ) === count( $this->usernames );
+                               call_user_func_array(
+                                       $this->params['dir'] == 'newer' ? 'ksort' : 'krsort', [ &$names, SORT_STRING ]
+                               );
+                               $neg = $op === '>' ? -1 : 1;
+                               $userIter = call_user_func( function () use ( $names, $fromName, $neg ) {
+                                       foreach ( $names as $name => $row ) {
+                                               if ( $fromName === false || $neg * strcmp( $name, $fromName ) <= 0 ) {
+                                                       $user = $row ? User::newFromRow( $row ) : User::newFromName( $name, false );
+                                                       yield $user;
+                                               }
+                                       }
+                               } );
                        }
+                       $batchSize = count( $names );
                }
 
-               $this->prepareQuery();
-
-               $hookData = [];
-               // Do the actual query.
-               $res = $this->select( __METHOD__, [], $hookData );
+               // During migration, force ordering on the client side because we're
+               // having to combine multiple queries that would otherwise have
+               // different sort orders.
+               if ( $wgActorTableSchemaMigrationStage === MIGRATION_WRITE_BOTH ||
+                       $wgActorTableSchemaMigrationStage === MIGRATION_WRITE_NEW
+               ) {
+                       $batchSize = 1;
+               }
 
-               if ( $this->fld_sizediff ) {
-                       $revIds = [];
-                       foreach ( $res as $row ) {
-                               if ( $row->rev_parent_id ) {
-                                       $revIds[] = $row->rev_parent_id;
-                               }
-                       }
-                       $this->parentLens = Revision::getParentLengths( $dbSecondary, $revIds );
-                       $res->rewind(); // reset
+               // With the new schema, the DB query will order by actor so update $this->orderBy to match.
+               if ( $batchSize > 1 && $wgActorTableSchemaMigrationStage === MIGRATION_NEW ) {
+                       $this->orderBy = 'actor';
                }
 
-               // Initialise some variables
                $count = 0;
                $limit = $this->params['limit'];
+               $userIter->rewind();
+               while ( $userIter->valid() ) {
+                       $users = [];
+                       while ( count( $users ) < $batchSize && $userIter->valid() ) {
+                               $users[] = $userIter->current();
+                               $userIter->next();
+                       }
+
+                       // Ugh. We have to run the query three times, once for each
+                       // possible 'orcond' from ActorMigration, and then merge them all
+                       // together in the proper order. And preserving the correct
+                       // $hookData for each one.
+                       // @todo When ActorMigration is removed, this can go back to a
+                       //  single prepare and select.
+                       $merged = [];
+                       foreach ( [ 'actor', 'userid', 'username' ] as $which ) {
+                               if ( $this->prepareQuery( $users, $limit - $count, $which ) ) {
+                                       $hookData = [];
+                                       $res = $this->select( __METHOD__, [], $hookData );
+                                       foreach ( $res as $row ) {
+                                               $merged[] = [ $row, &$hookData ];
+                                       }
+                               }
+                       }
+                       $neg = $this->params['dir'] == 'newer' ? 1 : -1;
+                       usort( $merged, function ( $a, $b ) use ( $neg, $batchSize ) {
+                               if ( $batchSize === 1 ) { // One user, can't be different
+                                       $ret = 0;
+                               } elseif ( $this->orderBy === 'id' ) {
+                                       $ret = $a[0]->rev_user - $b[0]->rev_user;
+                               } elseif ( $this->orderBy === 'name' ) {
+                                       $ret = strcmp( $a[0]->rev_user_text, $b[0]->rev_user_text );
+                               } else {
+                                       $ret = $a[0]->rev_actor - $b[0]->rev_actor;
+                               }
+
+                               if ( !$ret ) {
+                                       $ret = strcmp(
+                                               wfTimestamp( TS_MW, $a[0]->rev_timestamp ),
+                                               wfTimestamp( TS_MW, $b[0]->rev_timestamp )
+                                       );
+                               }
+
+                               if ( !$ret ) {
+                                       $ret = $a[0]->rev_id - $b[0]->rev_id;
+                               }
 
-               // Fetch each row
-               foreach ( $res as $row ) {
-                       if ( ++$count > $limit ) {
-                               // We've reached the one extra which shows that there are
-                               // additional pages to be had. Stop here...
-                               $this->setContinueEnumParameter( 'continue', $this->continueStr( $row ) );
-                               break;
+                               return $neg * $ret;
+                       } );
+                       $merged = array_slice( $merged, 0, $limit - $count + 1 );
+                       // (end "Ugh")
+
+                       if ( $this->fld_sizediff ) {
+                               $revIds = [];
+                               foreach ( $merged as $data ) {
+                                       if ( $data[0]->rev_parent_id ) {
+                                               $revIds[] = $data[0]->rev_parent_id;
+                                       }
+                               }
+                               $this->parentLens = Revision::getParentLengths( $dbSecondary, $revIds );
                        }
 
-                       $vals = $this->extractRowInfo( $row );
-                       $fit = $this->processRow( $row, $vals, $hookData ) &&
-                               $this->getResult()->addValue( [ 'query', $this->getModuleName() ], null, $vals );
-                       if ( !$fit ) {
-                               $this->setContinueEnumParameter( 'continue', $this->continueStr( $row ) );
-                               break;
+                       foreach ( $merged as $data ) {
+                               $row = $data[0];
+                               $hookData = &$data[1];
+                               if ( ++$count > $limit ) {
+                                       // We've reached the one extra which shows that there are
+                                       // additional pages to be had. Stop here...
+                                       $this->setContinueEnumParameter( 'continue', $this->continueStr( $row ) );
+                                       break 2;
+                               }
+
+                               $vals = $this->extractRowInfo( $row );
+                               $fit = $this->processRow( $row, $vals, $hookData ) &&
+                                       $this->getResult()->addValue( [ 'query', $this->getModuleName() ], null, $vals );
+                               if ( !$fit ) {
+                                       $this->setContinueEnumParameter( 'continue', $this->continueStr( $row ) );
+                                       break 2;
+                               }
                        }
                }
 
-               $this->getResult()->addIndexedTagName(
-                       [ 'query', $this->getModuleName() ],
-                       'item'
-               );
+               $this->getResult()->addIndexedTagName( [ 'query', $this->getModuleName() ], 'item' );
        }
 
        /**
         * Prepares the query and returns the limit of rows requested
+        * @param User[] $users
+        * @param int $limit
+        * @param string $which 'actor', 'userid', or 'username'
+        * @return bool
         */
-       private function prepareQuery() {
-               // We're after the revision table, and the corresponding page
-               // row for anything we retrieve. We may also need the
-               // recentchanges row and/or tag summary row.
-               $user = $this->getUser();
-               $tables = [ 'page', 'revision' ]; // Order may change
-               $this->addWhere( 'page_id=rev_page' );
+       private function prepareQuery( array $users, $limit, $which ) {
+               global $wgActorTableSchemaMigrationStage;
+
+               $this->resetQueryParams();
+               $db = $this->getDB();
+
+               $revQuery = Revision::getQueryInfo( [ 'page' ] );
+               $this->addTables( $revQuery['tables'] );
+               $this->addJoinConds( $revQuery['joins'] );
+               $this->addFields( $revQuery['fields'] );
+
+               $revWhere = ActorMigration::newMigration()->getWhere( $db, 'rev_user', $users );
+               if ( !isset( $revWhere['orconds'][$which] ) ) {
+                       return false;
+               }
+               $this->addWhere( $revWhere['orconds'][$which] );
+
+               if ( $wgActorTableSchemaMigrationStage === MIGRATION_NEW ) {
+                       $orderUserField = 'rev_actor';
+                       $userField = $this->orderBy === 'actor' ? 'revactor_actor' : 'actor_name';
+               } else {
+                       $orderUserField = $this->orderBy === 'id' ? 'rev_user' : 'rev_user_text';
+                       $userField = $revQuery['fields'][$orderUserField];
+               }
+               if ( $which === 'actor' ) {
+                       $tsField = 'revactor_timestamp';
+                       $idField = 'revactor_rev';
+               } else {
+                       $tsField = 'rev_timestamp';
+                       $idField = 'rev_id';
+               }
 
                // Handle continue parameter
                if ( !is_null( $this->params['continue'] ) ) {
                        $continue = explode( '|', $this->params['continue'] );
-                       $db = $this->getDB();
                        if ( $this->multiUserMode ) {
                                $this->dieContinueUsageIf( count( $continue ) != 4 );
                                $modeFlag = array_shift( $continue );
-                               $this->dieContinueUsageIf( !in_array( $modeFlag, [ 'id', 'name' ] ) );
-                               if ( $this->idMode && $modeFlag === 'name' ) {
-                                       // The users were created since this query started, but we
-                                       // can't go back and change modes now. So just keep on with
-                                       // name mode.
-                                       $this->idMode = false;
-                               }
-                               $this->dieContinueUsageIf( ( $modeFlag === 'id' ) !== $this->idMode );
-                               $userField = $this->idMode ? 'rev_user' : 'rev_user_text';
+                               $this->dieContinueUsageIf( $modeFlag !== $this->orderBy );
                                $encUser = $db->addQuotes( array_shift( $continue ) );
                        } else {
                                $this->dieContinueUsageIf( count( $continue ) != 2 );
@@ -218,21 +479,22 @@ class ApiQueryContributions extends ApiQueryBase {
                                $this->addWhere(
                                        "$userField $op $encUser OR " .
                                        "($userField = $encUser AND " .
-                                       "(rev_timestamp $op $encTS OR " .
-                                       "(rev_timestamp = $encTS AND " .
-                                       "rev_id $op= $encId)))"
+                                       "($tsField $op $encTS OR " .
+                                       "($tsField = $encTS AND " .
+                                       "$idField $op= $encId)))"
                                );
                        } else {
                                $this->addWhere(
-                                       "rev_timestamp $op $encTS OR " .
-                                       "(rev_timestamp = $encTS AND " .
-                                       "rev_id $op= $encId)"
+                                       "$tsField $op $encTS OR " .
+                                       "($tsField = $encTS AND " .
+                                       "$idField $op= $encId)"
                                );
                        }
                }
 
                // Don't include any revisions where we're not supposed to be able to
                // see the username.
+               $user = $this->getUser();
                if ( !$user->isAllowed( 'deletedhistory' ) ) {
                        $bitmask = Revision::DELETED_USER;
                } elseif ( !$user->isAllowedAny( 'suppressrevision', 'viewsuppressed' ) ) {
@@ -241,29 +503,20 @@ class ApiQueryContributions extends ApiQueryBase {
                        $bitmask = 0;
                }
                if ( $bitmask ) {
-                       $this->addWhere( $this->getDB()->bitAnd( 'rev_deleted', $bitmask ) . " != $bitmask" );
+                       $this->addWhere( $db->bitAnd( 'rev_deleted', $bitmask ) . " != $bitmask" );
                }
 
-               // We only want pages by the specified users.
-               if ( $this->prefixMode ) {
-                       $this->addWhere( 'rev_user_text' .
-                               $this->getDB()->buildLike( $this->userprefix, $this->getDB()->anyString() ) );
-               } elseif ( $this->idMode ) {
-                       $this->addWhereFld( 'rev_user', $this->userids );
-               } else {
-                       $this->addWhereFld( 'rev_user_text', $this->usernames );
-               }
-               // ... and in the specified timeframe.
-               // Ensure the same sort order for rev_user/rev_user_text and rev_timestamp
-               // so our query is indexed
-               if ( $this->multiUserMode ) {
-                       $this->addWhereRange( $this->idMode ? 'rev_user' : 'rev_user_text',
-                               $this->params['dir'], null, null );
+               // Add the user field to ORDER BY if there are multiple users
+               if ( count( $users ) > 1 ) {
+                       $this->addWhereRange( $orderUserField, $this->params['dir'], null, null );
                }
-               $this->addTimestampWhereRange( 'rev_timestamp',
+
+               // Then timestamp
+               $this->addTimestampWhereRange( $tsField,
                        $this->params['dir'], $this->params['start'], $this->params['end'] );
-               // Include in ORDER BY for uniqueness
-               $this->addWhereRange( 'rev_id', $this->params['dir'], null, null );
+
+               // Then rev_id for a total ordering
+               $this->addWhereRange( $idField, $this->params['dir'], null, null );
 
                $this->addWhereFld( 'page_namespace', $this->params['namespace'] );
 
@@ -286,25 +539,12 @@ class ApiQueryContributions extends ApiQueryBase {
                        $this->addWhereIf( 'rev_minor_edit != 0', isset( $show['minor'] ) );
                        $this->addWhereIf( 'rc_patrolled = 0', isset( $show['!patrolled'] ) );
                        $this->addWhereIf( 'rc_patrolled != 0', isset( $show['patrolled'] ) );
-                       $this->addWhereIf( 'rev_id != page_latest', isset( $show['!top'] ) );
-                       $this->addWhereIf( 'rev_id = page_latest', isset( $show['top'] ) );
+                       $this->addWhereIf( $idField . ' != page_latest', isset( $show['!top'] ) );
+                       $this->addWhereIf( $idField . ' = page_latest', isset( $show['top'] ) );
                        $this->addWhereIf( 'rev_parent_id != 0', isset( $show['!new'] ) );
                        $this->addWhereIf( 'rev_parent_id = 0', isset( $show['new'] ) );
                }
-               $this->addOption( 'LIMIT', $this->params['limit'] + 1 );
-
-               // Mandatory fields: timestamp allows request continuation
-               // ns+title checks if the user has access rights for this page
-               // user_text is necessary if multiple users were specified
-               $this->addFields( [
-                       'rev_id',
-                       'rev_timestamp',
-                       'page_namespace',
-                       'page_title',
-                       'rev_user',
-                       'rev_user_text',
-                       'rev_deleted'
-               ] );
+               $this->addOption( 'LIMIT', $limit + 1 );
 
                if ( isset( $show['patrolled'] ) || isset( $show['!patrolled'] ) ||
                        $this->fld_patrolled
@@ -313,48 +553,25 @@ class ApiQueryContributions extends ApiQueryBase {
                                $this->dieWithError( 'apierror-permissiondenied-patrolflag', 'permissiondenied' );
                        }
 
-                       // Use a redundant join condition on both
-                       // timestamp and ID so we can use the timestamp
-                       // index
-                       $index['recentchanges'] = 'rc_user_text';
-                       if ( isset( $show['patrolled'] ) || isset( $show['!patrolled'] ) ) {
-                               // Put the tables in the right order for
-                               // STRAIGHT_JOIN
-                               $tables = [ 'revision', 'recentchanges', 'page' ];
-                               $this->addOption( 'STRAIGHT_JOIN' );
-                               $this->addWhere( 'rc_user_text=rev_user_text' );
-                               $this->addWhere( 'rc_timestamp=rev_timestamp' );
-                               $this->addWhere( 'rc_this_oldid=rev_id' );
-                       } else {
-                               $tables[] = 'recentchanges';
-                               $this->addJoinConds( [ 'recentchanges' => [
-                                       'LEFT JOIN', [
-                                               'rc_user_text=rev_user_text',
-                                               'rc_timestamp=rev_timestamp',
-                                               'rc_this_oldid=rev_id' ] ] ] );
-                       }
+                       $this->addTables( 'recentchanges' );
+                       $this->addJoinConds( [ 'recentchanges' => [
+                               isset( $show['patrolled'] ) || isset( $show['!patrolled'] ) ? 'JOIN' : 'LEFT JOIN',
+                               [
+                                       // This is a crazy hack. recentchanges has no index on rc_this_oldid, so instead of adding
+                                       // one T19237 did a join using rc_user_text and rc_timestamp instead. Now rc_user_text is
+                                       // probably unavailable, so just do rc_timestamp.
+                                       'rc_timestamp = ' . $tsField,
+                                       'rc_this_oldid = ' . $idField,
+                               ]
+                       ] ] );
                }
 
-               $this->addTables( $tables );
-               $this->addFieldsIf( 'rev_page', $this->fld_ids );
-               $this->addFieldsIf( 'page_latest', $this->fld_flags );
-               // $this->addFieldsIf( 'rev_text_id', $this->fld_ids ); // Should this field be exposed?
-               $this->addFieldsIf( 'rev_len', $this->fld_size || $this->fld_sizediff );
-               $this->addFieldsIf( 'rev_minor_edit', $this->fld_flags );
-               $this->addFieldsIf( 'rev_parent_id', $this->fld_flags || $this->fld_sizediff || $this->fld_ids );
                $this->addFieldsIf( 'rc_patrolled', $this->fld_patrolled );
 
-               if ( $this->fld_comment || $this->fld_parsedcomment ) {
-                       $commentQuery = $this->commentStore->getJoin( 'rev_comment' );
-                       $this->addTables( $commentQuery['tables'] );
-                       $this->addFields( $commentQuery['fields'] );
-                       $this->addJoinConds( $commentQuery['joins'] );
-               }
-
                if ( $this->fld_tags ) {
                        $this->addTables( 'tag_summary' );
                        $this->addJoinConds(
-                               [ 'tag_summary' => [ 'LEFT JOIN', [ 'rev_id=ts_rev_id' ] ] ]
+                               [ 'tag_summary' => [ 'LEFT JOIN', [ $idField . ' = ts_rev_id' ] ] ]
                        );
                        $this->addFields( 'ts_tags' );
                }
@@ -362,14 +579,12 @@ class ApiQueryContributions extends ApiQueryBase {
                if ( isset( $this->params['tag'] ) ) {
                        $this->addTables( 'change_tag' );
                        $this->addJoinConds(
-                               [ 'change_tag' => [ 'INNER JOIN', [ 'rev_id=ct_rev_id' ] ] ]
+                               [ 'change_tag' => [ 'INNER JOIN', [ $idField . ' = ct_rev_id' ] ] ]
                        );
                        $this->addWhereFld( 'ct_tag', $this->params['tag'] );
                }
 
-               if ( isset( $index ) ) {
-                       $this->addOption( 'USE INDEX', $index );
-               }
+               return true;
        }
 
        /**
@@ -480,10 +695,13 @@ class ApiQueryContributions extends ApiQueryBase {
 
        private function continueStr( $row ) {
                if ( $this->multiUserMode ) {
-                       if ( $this->idMode ) {
-                               return "id|$row->rev_user|$row->rev_timestamp|$row->rev_id";
-                       } else {
-                               return "name|$row->rev_user_text|$row->rev_timestamp|$row->rev_id";
+                       switch ( $this->orderBy ) {
+                               case 'id':
+                                       return "id|$row->rev_user|$row->rev_timestamp|$row->rev_id";
+                               case 'name':
+                                       return "name|$row->rev_user_text|$row->rev_timestamp|$row->rev_id";
+                               case 'actor':
+                                       return "actor|$row->rev_actor|$row->rev_timestamp|$row->rev_id";
                        }
                } else {
                        return "$row->rev_timestamp|$row->rev_id";
index b4b9321..23163c2 100644 (file)
@@ -340,11 +340,15 @@ class ApiStashEdit extends ApiBase {
         * @return string|null TS_MW timestamp or null
         */
        private static function lastEditTime( User $user ) {
-               $time = wfGetDB( DB_REPLICA )->selectField(
-                       'recentchanges',
+               $db = wfGetDB( DB_REPLICA );
+               $actorQuery = ActorMigration::newMigration()->getWhere( $db, 'rc_user', $user, false );
+               $time = $db->selectField(
+                       [ 'recentchanges' ] + $actorQuery['tables'],
                        'MAX(rc_timestamp)',
-                       [ 'rc_user_text' => $user->getName() ],
-                       __METHOD__
+                       [ $actorQuery['conds'] ],
+                       __METHOD__,
+                       [],
+                       $actorQuery['joins']
                );
 
                return wfTimestampOrNull( TS_MW, $time );
index 9d39405..6f2a72c 100644 (file)
@@ -2,7 +2,8 @@
        "@metadata": {
                "authors": [
                        "Zygimantus",
-                       "Eitvys200"
+                       "Eitvys200",
+                       "Hugo.arg"
                ]
        },
        "apihelp-main-param-action": "Kurį veiksmą atlikti.",
@@ -78,7 +79,7 @@
        "apihelp-feedwatchlist-param-feedformat": "Srauto formatas.",
        "apihelp-feedwatchlist-example-default": "Rodyti stebimųjų sąrašo srautą.",
        "apihelp-feedwatchlist-example-all6hrs": "Rodyti visus pakeitimus stebimuose puslapiuose per paskutines 6 valandas.",
-       "apihelp-filerevert-param-comment": "Įkėlimo komentaras.",
+       "apihelp-filerevert-param-comment": "Įkėlimo pastabos.",
        "apihelp-help-summary": "Rodyti pagalbą pasirinktiems moduliams.",
        "apihelp-help-example-main": "Pagalba pagrindiniam moduliui.",
        "apihelp-help-example-recursive": "Visa pagalba viename puslapyje.",
index c18160a..f69fb08 100644 (file)
        "apihelp-parse-param-disablepp": "Em vez deste, usar <var>$1disablelimitreport</var>.",
        "apihelp-parse-param-disableeditsection": "Omitir as hiperligações para edição da secção no resultado da análise sintática.",
        "apihelp-parse-param-disabletidy": "Não fazer a limpeza do HTML (isto é, o ''tidy'') no resultado da análise sintática.",
-       "apihelp-parse-param-disablestylededuplication": "Não desduplica as folhas de estilo incluídas na saída do analisador sintático.",
+       "apihelp-parse-param-disablestylededuplication": "Não desduplicar as folhas de estilo internas (etiquetas <nowiki><style></nowiki>) na saída do analisador sintático.",
        "apihelp-parse-param-generatexml": "Gerar a árvore de análise XML (requer o modelo de conteúdo <code>$1</code>; substituído por <kbd>$2prop=parsetree</kbd>).",
        "apihelp-parse-param-preview": "Executar a análise em modo de antevisão.",
        "apihelp-parse-param-sectionpreview": "Executar a análise em modo de antevisão (também ativa o modo de antevisão).",
index 215e2ff..7e33e42 100644 (file)
@@ -27,7 +27,8 @@
                        "Redredsonia",
                        "Alexey zakharenkov",
                        "Facenapalm",
-                       "Jack who built the house"
+                       "Jack who built the house",
+                       "Mouse21"
                ]
        },
        "apihelp-main-extended-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:Special:MyLanguage/API:Main_page|Документация]]\n* [[mw:Special:MyLanguage/API:FAQ|ЧаВО]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api Почтовая рассылка]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce Новости API]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Ошибки и запросы]\n</div>\n<strong>Статус:</strong> Все отображаемые на этой странице функции должны работать, однако API находится в статусе активной разработки и может измениться в любой момент. Подпишитесь на [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ почтовую рассылку mediawiki-api-announce], чтобы быть в курсе обновлений.\n\n<strong>Ошибочные запросы:</strong> Если API получает запрос с ошибкой, вернётся заголовок HTTP с ключом «MediaWiki-API-Error», после чего значение заголовка и код ошибки будут отправлены обратно и установлены в то же значение. Более подробную информацию см. [[mw:Special:MyLanguage/API:Errors_and_warnings|API: Ошибки и предупреждения]].\n\n<p class=\"mw-apisandbox-link\"><strong>Тестирование:</strong> для удобства тестирования API-запросов, см. [[Special:ApiSandbox]].</p>",
        "apihelp-parse-param-disablepp": "Вместо этого используйте <var>$1disablelimitreport</var>.",
        "apihelp-parse-param-disableeditsection": "Опустить ссылки на редактирование разделов из результата парсинга.",
        "apihelp-parse-param-disabletidy": "Не проводить очистку HTML (например, с помощью tidy) результатов парсинга.",
+       "apihelp-parse-param-disablestylededuplication": "Не дедуплицируйте встроенные таблицы стилей в выходе парсера.",
        "apihelp-parse-param-generatexml": "Сгенерировать дерево парсинга XML (требуется модель содержимого <code>$1</code>, замещено <kbd>$2prop=parsetree</kbd>).",
        "apihelp-parse-param-preview": "Проанализировать в режиме препросмотра.",
        "apihelp-parse-param-sectionpreview": "Распарсить в режиме предпросмотра раздела (также активирует режим предпросмотра).",
index 47c0df5..9ed6d13 100644 (file)
@@ -1416,7 +1416,7 @@ class AuthManager implements LoggerAwareInterface {
                                $state['userid'] = $user->getId();
 
                                // Update user count
-                               \DeferredUpdates::addUpdate( new \SiteStatsUpdate( 0, 0, 0, 0, 1 ) );
+                               \DeferredUpdates::addUpdate( \SiteStatsUpdate::factory( [ 'users' => 1 ] ) );
 
                                // Watch user's userpage and talk page
                                $user->addWatch( $user->getUserPage(), User::IGNORE_USER_RIGHTS );
@@ -1730,7 +1730,7 @@ class AuthManager implements LoggerAwareInterface {
                $user->saveSettings();
 
                // Update user count
-               \DeferredUpdates::addUpdate( new \SiteStatsUpdate( 0, 0, 0, 0, 1 ) );
+               \DeferredUpdates::addUpdate( \SiteStatsUpdate::factory( [ 'users' => 1 ] ) );
                // Watch user's userpage and talk page
                \DeferredUpdates::addCallableUpdate( function () use ( $user ) {
                        $user->addWatch( $user->getUserPage(), User::IGNORE_USER_RIGHTS );
index b8e36bc..cd0734d 100644 (file)
@@ -272,7 +272,7 @@ class AuthPluginPrimaryAuthenticationProvider
                        if ( $failed ) {
                                throw new \UnexpectedValueException(
                                        "AuthPlugin failed to reset password for $username in the following domains: "
-                                               . join( ' ', $failed )
+                                               . implode( ' ', $failed )
                                );
                        }
                }
index d5ff6cb..71fcd8b 100644 (file)
@@ -300,7 +300,7 @@ class MessageCache {
                }
 
                if ( !$success ) {
-                       $cacheKey = $this->clusterCache->makeKey( 'messages', $code ); # Key in memc for messages
+                       $cacheKey = $this->clusterCache->makeKey( 'messages', $code );
                        # Try the global cache. If it is empty, try to acquire a lock. If
                        # the lock can't be acquired, wait for the other thread to finish
                        # and then try the global cache a second time.
@@ -613,7 +613,7 @@ class MessageCache {
                                // load() calls do try to refresh the cache with replica DB data
                                $this->mCache[$code]['LATEST'] = time();
                                // Pre-emptively update the local datacenter cache so things like edit filter and
-                               // blacklist changes are reflect immediately, as these often use MediaWiki: pages.
+                               // blacklist changes are reflected immediately; these often use MediaWiki: pages.
                                // The datacenter handling replace() calls should be the same one handling edits
                                // as they require HTTP POST.
                                $this->saveToCaches( $this->mCache[$code], 'all', $code );
@@ -622,19 +622,7 @@ class MessageCache {
 
                                // Relay the purge. Touching this check key expires cache contents
                                // and local cache (APC) validation hash across all datacenters.
-                               $this->wanCache->touchCheckKey( $this->wanCache->makeKey( 'messages', $code ) );
-                               // Also delete cached sidebar... just in case it is affected
-                               // @TODO: shouldn't this be $code === $wgLanguageCode?
-                               if ( $code === 'en' ) {
-                                       // Purge all language sidebars, e.g. on ?action=purge to the sidebar messages
-                                       $codes = array_keys( Language::fetchLanguageNames() );
-                               } else {
-                                       // Purge only the sidebar for this language
-                                       $codes = [ $code ];
-                               }
-                               foreach ( $codes as $code ) {
-                                       $this->wanCache->delete( $this->wanCache->makeKey( 'sidebar', $code ) );
-                               }
+                               $this->wanCache->touchCheckKey( $this->getCheckKey( $code ) );
 
                                // Purge the message in the message blob store
                                $resourceloader = RequestContext::getMain()->getOutput()->getResourceLoader();
@@ -701,7 +689,7 @@ class MessageCache {
                $value = $this->wanCache->get(
                        $this->wanCache->makeKey( 'messages', $code, 'hash', 'v1' ),
                        $curTTL,
-                       [ $this->wanCache->makeKey( 'messages', $code ) ]
+                       [ $this->getCheckKey( $code ) ]
                );
 
                if ( $value ) {
@@ -906,7 +894,7 @@ class MessageCache {
                if ( $useDB ) {
                        $uckey = $wgContLang->ucfirst( $lckey );
 
-                       if ( !isset( $alreadyTried[ $langcode ] ) ) {
+                       if ( !isset( $alreadyTried[$langcode] ) ) {
                                $message = $this->getMsgFromNamespace(
                                        $this->getMessagePageName( $langcode, $uckey ),
                                        $langcode
@@ -915,7 +903,7 @@ class MessageCache {
                                if ( $message !== false ) {
                                        return $message;
                                }
-                               $alreadyTried[ $langcode ] = true;
+                               $alreadyTried[$langcode] = true;
                        }
                } else {
                        $uckey = null;
@@ -932,7 +920,7 @@ class MessageCache {
                        $fallbackChain = Language::getFallbacksFor( $langcode );
 
                        foreach ( $fallbackChain as $code ) {
-                               if ( isset( $alreadyTried[ $code ] ) ) {
+                               if ( isset( $alreadyTried[$code] ) ) {
                                        continue;
                                }
 
@@ -942,7 +930,7 @@ class MessageCache {
                                if ( $message !== false ) {
                                        return $message;
                                }
-                               $alreadyTried[ $code ] = true;
+                               $alreadyTried[$code] = true;
                        }
                }
 
@@ -985,13 +973,12 @@ class MessageCache {
                if ( isset( $this->mCache[$code][$title] ) ) {
                        $entry = $this->mCache[$code][$title];
                        if ( substr( $entry, 0, 1 ) === ' ' ) {
-                               // The message exists, so make sure a string is returned.
+                               // The message exists and is not '!TOO BIG'
                                return (string)substr( $entry, 1 );
                        } elseif ( $entry === '!NONEXISTENT' ) {
                                return false;
-                       } elseif ( $entry === '!TOO BIG' ) {
-                               // Fall through and try invididual message cache below
                        }
+                       // Fall through and try invididual message cache below
                } else {
                        // XXX: This is not cached in process cache, should it?
                        $message = false;
@@ -1077,11 +1064,11 @@ class MessageCache {
        /**
         * @param string $message
         * @param bool $interface
-        * @param string $language Language code
+        * @param Language $language
         * @param Title $title
         * @return string
         */
-       function transform( $message, $interface = false, $language = null, $title = null ) {
+       public function transform( $message, $interface = false, $language = null, $title = null ) {
                // Avoid creating parser if nothing to transform
                if ( strpos( $message, '{{' ) === false ) {
                        return $message;
@@ -1110,7 +1097,7 @@ class MessageCache {
        /**
         * @return Parser
         */
-       function getParser() {
+       public function getParser() {
                global $wgParser, $wgParserConf;
 
                if ( !$this->mParser && isset( $wgParser ) ) {
@@ -1174,11 +1161,11 @@ class MessageCache {
                return $res;
        }
 
-       function disable() {
+       public function disable() {
                $this->mDisable = true;
        }
 
-       function enable() {
+       public function enable() {
                $this->mDisable = false;
        }
 
@@ -1199,13 +1186,14 @@ class MessageCache {
        }
 
        /**
-        * Clear all stored messages. Mainly used after a mass rebuild.
+        * Clear all stored messages in global and local cache
+        *
+        * Mainly used after a mass rebuild
         */
        function clear() {
                $langs = Language::fetchLanguageNames( null, 'mw' );
                foreach ( array_keys( $langs ) as $code ) {
-                       # Global and local caches
-                       $this->wanCache->touchCheckKey( $this->wanCache->makeKey( 'messages', $code ) );
+                       $this->wanCache->touchCheckKey( $this->getCheckKey( $code ) );
                }
 
                $this->mLoadedLanguages = [];
@@ -1283,6 +1271,14 @@ class MessageCache {
                }
        }
 
+       /**
+        * @param string $code Language code
+        * @return string WAN cache key usable as a "check key" against language page edits
+        */
+       public function getCheckKey( $code ) {
+               return $this->wanCache->makeKey( 'messages', $code );
+       }
+
        /**
         * @param Content|null $content Content or null if the message page does not exist
         * @return string|bool|null Returns false if $content is null and null on error
index 5c75292..cb68571 100644 (file)
@@ -80,6 +80,8 @@ class UserCache {
         * @param string $caller The calling method
         */
        public function doQuery( array $userIds, $options = [], $caller = '' ) {
+               global $wgActorTableSchemaMigrationStage;
+
                $usersToCheck = [];
                $usersToQuery = [];
 
@@ -100,21 +102,34 @@ class UserCache {
                // Lookup basic info for users not yet loaded...
                if ( count( $usersToQuery ) ) {
                        $dbr = wfGetDB( DB_REPLICA );
-                       $table = [ 'user' ];
+                       $tables = [ 'user' ];
                        $conds = [ 'user_id' => $usersToQuery ];
                        $fields = [ 'user_name', 'user_real_name', 'user_registration', 'user_id' ];
+                       $joinConds = [];
+
+                       if ( $wgActorTableSchemaMigrationStage > MIGRATION_OLD ) {
+                               $tables[] = 'actor';
+                               $fields[] = 'actor_id';
+                               $joinConds['actor'] = [
+                                       $wgActorTableSchemaMigrationStage === MIGRATION_NEW ? 'JOIN' : 'LEFT JOIN',
+                                       [ 'actor_user = user_id' ]
+                               ];
+                       }
 
                        $comment = __METHOD__;
                        if ( strval( $caller ) !== '' ) {
                                $comment .= "/$caller";
                        }
 
-                       $res = $dbr->select( $table, $fields, $conds, $comment );
+                       $res = $dbr->select( $tables, $fields, $conds, $comment, [], $joinConds );
                        foreach ( $res as $row ) { // load each user into cache
                                $userId = (int)$row->user_id;
                                $this->cache[$userId]['name'] = $row->user_name;
                                $this->cache[$userId]['real_name'] = $row->user_real_name;
                                $this->cache[$userId]['registration'] = $row->user_registration;
+                               if ( $wgActorTableSchemaMigrationStage > MIGRATION_OLD ) {
+                                       $this->cache[$userId]['actor'] = $row->actor_id;
+                               }
                                $usersToCheck[$userId] = $row->user_name;
                        }
                }
index 5b8559e..ac029a2 100644 (file)
@@ -646,6 +646,7 @@ class ChangesList extends ContextSource {
                                        'id' => $rc->mAttribs['rc_this_oldid'],
                                        'user' => $rc->mAttribs['rc_user'],
                                        'user_text' => $rc->mAttribs['rc_user_text'],
+                                       'actor' => isset( $rc->mAttribs['rc_actor'] ) ? $rc->mAttribs['rc_actor'] : null,
                                        'deleted' => $rc->mAttribs['rc_deleted']
                                ] );
                                $s .= ' ' . Linker::generateRollback( $rev, $this->getContext() );
index dfaa398..3dacf6a 100644 (file)
@@ -34,6 +34,7 @@
  *  rc_cur_id       page_id of associated page entry
  *  rc_user         user id who made the entry
  *  rc_user_text    user name who made the entry
+ *  rc_actor        actor id who made the entry
  *  rc_comment      edit summary
  *  rc_this_oldid   rev_id associated with this entry (or zero)
  *  rc_last_oldid   rev_id associated with the entry before this one (or zero)
@@ -210,12 +211,25 @@ class RecentChange {
         * @return array
         */
        public static function selectFields() {
+               global $wgActorTableSchemaMigrationStage;
+
                wfDeprecated( __METHOD__, '1.31' );
+               if ( $wgActorTableSchemaMigrationStage > MIGRATION_WRITE_BOTH ) {
+                       // If code is using this instead of self::getQueryInfo(), there's a
+                       // decent chance it's going to try to directly access
+                       // $row->rc_user or $row->rc_user_text and we can't give it
+                       // useful values here once those aren't being written anymore.
+                       throw new BadMethodCallException(
+                               'Cannot use ' . __METHOD__ . ' when $wgActorTableSchemaMigrationStage > MIGRATION_WRITE_BOTH'
+                       );
+               }
+
                return [
                        'rc_id',
                        'rc_timestamp',
                        'rc_user',
                        'rc_user_text',
+                       'rc_actor' => 'NULL',
                        'rc_namespace',
                        'rc_title',
                        'rc_minor',
@@ -249,13 +263,12 @@ class RecentChange {
         */
        public static function getQueryInfo() {
                $commentQuery = CommentStore::getStore()->getJoin( 'rc_comment' );
+               $actorQuery = ActorMigration::newMigration()->getJoin( 'rc_user' );
                return [
-                       'tables' => [ 'recentchanges' ] + $commentQuery['tables'],
+                       'tables' => [ 'recentchanges' ] + $commentQuery['tables'] + $actorQuery['tables'],
                        'fields' => [
                                'rc_id',
                                'rc_timestamp',
-                               'rc_user',
-                               'rc_user_text',
                                'rc_namespace',
                                'rc_title',
                                'rc_minor',
@@ -275,8 +288,8 @@ class RecentChange {
                                'rc_log_type',
                                'rc_log_action',
                                'rc_params',
-                       ] + $commentQuery['fields'],
-                       'joins' => $commentQuery['joins'],
+                       ] + $commentQuery['fields'] + $actorQuery['fields'],
+                       'joins' => $commentQuery['joins'] + $actorQuery['joins'],
                ];
        }
 
@@ -314,10 +327,14 @@ class RecentChange {
         */
        public function getPerformer() {
                if ( $this->mPerformer === false ) {
-                       if ( $this->mAttribs['rc_user'] ) {
+                       if ( !empty( $this->mAttribs['rc_actor'] ) ) {
+                               $this->mPerformer = User::newFromActorId( $this->mAttribs['rc_actor'] );
+                       } elseif ( !empty( $this->mAttribs['rc_user'] ) ) {
                                $this->mPerformer = User::newFromId( $this->mAttribs['rc_user'] );
-                       } else {
+                       } elseif ( !empty( $this->mAttribs['rc_user_text'] ) ) {
                                $this->mPerformer = User::newFromName( $this->mAttribs['rc_user_text'], false );
+                       } else {
+                               throw new MWException( 'RecentChange object lacks rc_actor, rc_user, and rc_user_text' );
                        }
                }
 
@@ -368,12 +385,22 @@ class RecentChange {
                        unset( $this->mAttribs['rc_cur_id'] );
                }
 
-               # Convert mAttribs['rc_comment'] for CommentStore
                $row = $this->mAttribs;
+
+               # Convert mAttribs['rc_comment'] for CommentStore
                $comment = $row['rc_comment'];
                unset( $row['rc_comment'], $row['rc_comment_text'], $row['rc_comment_data'] );
                $row += CommentStore::getStore()->insert( $dbw, 'rc_comment', $comment );
 
+               # Convert mAttribs['rc_user'] etc for ActorMigration
+               $user = User::newFromAnyId(
+                       isset( $row['rc_user'] ) ? $row['rc_user'] : null,
+                       isset( $row['rc_user_text'] ) ? $row['rc_user_text'] : null,
+                       isset( $row['rc_actor'] ) ? $row['rc_actor'] : null
+               );
+               unset( $row['rc_user'], $row['rc_user_text'], $row['rc_actor'] );
+               $row += ActorMigration::newMigration()->getInsertValues( $dbw, 'rc_user', $user );
+
                # Don't reuse an existing rc_id for the new row, if one happens to be
                # set for some reason.
                unset( $row['rc_id'] );
@@ -642,6 +669,7 @@ class RecentChange {
                        'rc_cur_id' => $title->getArticleID(),
                        'rc_user' => $user->getId(),
                        'rc_user_text' => $user->getName(),
+                       'rc_actor' => $user->getActorId(),
                        'rc_comment' => &$comment,
                        'rc_comment_text' => &$comment,
                        'rc_comment_data' => null,
@@ -717,6 +745,7 @@ class RecentChange {
                        'rc_cur_id' => $title->getArticleID(),
                        'rc_user' => $user->getId(),
                        'rc_user_text' => $user->getName(),
+                       'rc_actor' => $user->getActorId(),
                        'rc_comment' => &$comment,
                        'rc_comment_text' => &$comment,
                        'rc_comment_data' => null,
@@ -849,6 +878,7 @@ class RecentChange {
                        'rc_cur_id' => $target->getArticleID(),
                        'rc_user' => $user->getId(),
                        'rc_user_text' => $user->getName(),
+                       'rc_actor' => $user->getActorId(),
                        'rc_comment' => &$logComment,
                        'rc_comment_text' => &$logComment,
                        'rc_comment_data' => null,
@@ -934,6 +964,7 @@ class RecentChange {
                        'rc_cur_id' => $pageTitle->getArticleID(),
                        'rc_user' => $user ? $user->getId() : 0,
                        'rc_user_text' => $user ? $user->getName() : '',
+                       'rc_actor' => $user ? $user->getActorId() : null,
                        'rc_comment' => &$comment,
                        'rc_comment_text' => &$comment,
                        'rc_comment_data' => null,
@@ -1002,6 +1033,15 @@ class RecentChange {
                $this->mAttribs['rc_comment'] = &$comment;
                $this->mAttribs['rc_comment_text'] = &$comment;
                $this->mAttribs['rc_comment_data'] = null;
+
+               $user = User::newFromAnyId(
+                       isset( $this->mAttribs['rc_user'] ) ? $this->mAttribs['rc_user'] : null,
+                       isset( $this->mAttribs['rc_user_text'] ) ? $this->mAttribs['rc_user_text'] : null,
+                       isset( $this->mAttribs['rc_actor'] ) ? $this->mAttribs['rc_actor'] : null
+               );
+               $this->mAttribs['rc_user'] = $user->getId();
+               $this->mAttribs['rc_user_text'] = $user->getName();
+               $this->mAttribs['rc_actor'] = $user->getActorId();
        }
 
        /**
@@ -1015,6 +1055,24 @@ class RecentChange {
                        return CommentStore::getStore()
                                ->getComment( 'rc_comment', $this->mAttribs, true )->text;
                }
+
+               if ( $name === 'rc_user' || $name === 'rc_user_text' || $name === 'rc_actor' ) {
+                       $user = User::newFromAnyId(
+                               isset( $this->mAttribs['rc_user'] ) ? $this->mAttribs['rc_user'] : null,
+                               isset( $this->mAttribs['rc_user_text'] ) ? $this->mAttribs['rc_user_text'] : null,
+                               isset( $this->mAttribs['rc_actor'] ) ? $this->mAttribs['rc_actor'] : null
+                       );
+                       if ( $name === 'rc_user' ) {
+                               return $user->getId();
+                       }
+                       if ( $name === 'rc_user_text' ) {
+                               return $user->getName();
+                       }
+                       if ( $name === 'rc_actor' ) {
+                               return $user->getActorId();
+                       }
+               }
+
                return isset( $this->mAttribs[$name] ) ? $this->mAttribs[$name] : null;
        }
 
index 7e4dd00..b30b82d 100644 (file)
@@ -181,6 +181,28 @@ class ChangeTags {
                return $msg;
        }
 
+       /**
+        * Get truncated message for the tag's long description.
+        *
+        * @param string $tag Tag name.
+        * @param int $length Maximum length of truncated message, including ellipsis.
+        * @param IContextSource $context
+        *
+        * @return string Truncated long tag description.
+        */
+       public static function truncateTagDescription( $tag, $length, IContextSource $context ) {
+               $originalDesc = self::tagLongDescriptionMessage( $tag, $context );
+               // If there is no tag description, return empty string
+               if ( !$originalDesc ) {
+                       return '';
+               }
+
+               $taglessDesc = Sanitizer::stripAllTags( $originalDesc->parse() );
+               $escapedDesc = Sanitizer::escapeHtmlAllowEntities( $taglessDesc );
+
+               return $context->getLanguage()->truncateForVisual( $escapedDesc, $length );
+       }
+
        /**
         * Add tags to a change given its rc_id, rev_id and/or log_id
         *
index b78efaf..a248c6e 100644 (file)
@@ -44,6 +44,10 @@ class ChangeTagsLogItem extends RevisionItemBase {
                return 'log_user_text';
        }
 
+       public function getAuthorActorField() {
+               return 'log_actor';
+       }
+
        public function canView() {
                return LogEventsList::userCan( $this->row, Revision::DELETED_RESTRICTED, $this->list->getUser() );
        }
index 65dc3bb..b21eadc 100644 (file)
@@ -426,7 +426,7 @@ abstract class AbstractContent implements Content {
         * @param WikiPage $page
         * @param ParserOutput|null $parserOutput
         *
-        * @return LinksDeletionUpdate[]
+        * @return DeferrableUpdate[]
         *
         * @see Content::getDeletionUpdates
         */
index edfc81c..eab3afb 100644 (file)
@@ -986,13 +986,17 @@ abstract class ContentHandler {
 
                // Find out if there was only one contributor
                // Only scan the last 20 revisions
-               $res = $dbr->select( 'revision', 'rev_user_text',
+               $revQuery = Revision::getQueryInfo();
+               $res = $dbr->select(
+                       $revQuery['tables'],
+                       [ 'rev_user_text' => $revQuery['fields']['rev_user_text'] ],
                        [
                                'rev_page' => $title->getArticleID(),
                                $dbr->bitAnd( 'rev_deleted', Revision::DELETED_USER ) . ' = 0'
                        ],
                        __METHOD__,
-                       [ 'LIMIT' => 20 ]
+                       [ 'LIMIT' => 20 ],
+                       $revQuery['joins']
                );
 
                if ( $res === false ) {
index 156e315..225a36c 100644 (file)
@@ -1179,13 +1179,16 @@ class DatabaseOracle extends Database {
        }
 
        public function delete( $table, $conds, $fname = __METHOD__ ) {
+               global $wgActorTableSchemaMigrationStage;
+
                if ( is_array( $conds ) ) {
                        $conds = $this->wrapConditionsForWhere( $table, $conds );
                }
                // a hack for deleting pages, users and images (which have non-nullable FKs)
                // all deletions on these tables have transactions so final failure rollbacks these updates
+               // @todo: Normalize the schema to match MySQL, no special FKs and such
                $table = $this->tableName( $table );
-               if ( $table == $this->tableName( 'user' ) ) {
+               if ( $table == $this->tableName( 'user' ) && $wgActorTableSchemaMigrationStage < MIGRATION_NEW ) {
                        $this->update( 'archive', [ 'ar_user' => 0 ],
                                [ 'ar_user' => $conds['user_id'] ], $fname );
                        $this->update( 'ipblocks', [ 'ipb_user' => 0 ],
index 2f882b8..79aab7d 100644 (file)
@@ -148,18 +148,21 @@ class SiteStatsUpdate implements DeferrableUpdate, MergeableUpdate {
                $dbr = $services->getDBLoadBalancer()->getConnection( DB_REPLICA, '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.
+               $rcQuery = RecentChange::getQueryInfo();
                $activeUsers = $dbr->selectField(
-                       'recentchanges',
-                       'COUNT( DISTINCT rc_user_text )',
+                       $rcQuery['tables'],
+                       'COUNT( DISTINCT ' . $rcQuery['fields']['rc_user_text'] . ' )',
                        [
                                'rc_type != ' . $dbr->addQuotes( RC_EXTERNAL ), // Exclude external (Wikidata)
-                               'rc_user != 0',
+                               ActorMigration::newMigration()->isNotAnon( $rcQuery['fields']['rc_user'] ),
                                'rc_bot' => 0,
                                'rc_log_type != ' . $dbr->addQuotes( 'newusers' ) . ' OR rc_log_type IS NULL',
                                'rc_timestamp >= ' . $dbr->addQuotes(
                                        $dbr->timestamp( time() - $config->get( 'ActiveUserDays' ) * 24 * 3600 ) ),
                        ],
-                       __METHOD__
+                       __METHOD__,
+                       [],
+                       $rcQuery['joins']
                );
                $dbw->update(
                        'site_stats',
diff --git a/includes/exception/CannotCreateActorException.php b/includes/exception/CannotCreateActorException.php
new file mode 100644 (file)
index 0000000..7c7ccfc
--- /dev/null
@@ -0,0 +1,29 @@
+<?php
+/**
+ * Exception thrown when some operation failed
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ *
+ * @since 1.31
+ */
+
+/**
+ * Exception thrown when an actor can't be created.
+ */
+class CannotCreateActorException extends RuntimeException {
+}
index 6ce55ea..6c7a449 100644 (file)
@@ -227,15 +227,20 @@ class WikiExporter {
                $this->author_list = "<contributors>";
                // rev_deleted
 
+               $revQuery = Revision::getQueryInfo( [ 'page' ] );
                $res = $this->db->select(
-                       [ 'page', 'revision' ],
-                       [ 'DISTINCT rev_user_text', 'rev_user' ],
+                       $revQuery['tables'],
+                       [
+                               'rev_user_text' => $revQuery['fields']['rev_user_text'],
+                               'rev_user' => $revQuery['fields']['rev_user'],
+                       ],
                        [
                                $this->db->bitAnd( 'rev_deleted', Revision::DELETED_USER ) . ' = 0',
                                $cond,
-                               'page_id = rev_id',
                        ],
-                       __METHOD__
+                       __METHOD__,
+                       [ 'DISTINCT' ],
+                       $revQuery['joins']
                );
 
                foreach ( $res as $row ) {
@@ -279,14 +284,18 @@ class WikiExporter {
                        $result = null; // Assuring $result is not undefined, if exception occurs early
 
                        $commentQuery = CommentStore::getStore()->getJoin( 'log_comment' );
+                       $actorQuery = ActorMigration::newMigration()->getJoin( 'log_user' );
 
                        try {
-                               $result = $this->db->select( [ 'logging', 'user' ] + $commentQuery['tables'],
-                                       [ "{$logging}.*", 'user_name' ] + $commentQuery['fields'], // grab the user name
+                               $result = $this->db->select(
+                                       array_merge( [ 'logging' ], $commentQuery['tables'], $actorQuery['tables'], [ 'user' ] ),
+                                       [ "{$logging}.*", 'user_name' ] + $commentQuery['fields'] + $actorQuery['fields'],
                                        $where,
                                        __METHOD__,
                                        [ 'ORDER BY' => 'log_id', 'USE INDEX' => [ 'logging' => 'PRIMARY' ] ],
-                                       [ 'user' => [ 'JOIN', 'user_id = log_user' ] ] + $commentQuery['joins']
+                                       [
+                                               'user' => [ 'JOIN', 'user_id = ' . $actorQuery['fields']['log_user'] ]
+                                       ] + $commentQuery['joins'] + $actorQuery['joins']
                                );
                                $this->outputLogStream( $result );
                                if ( $this->buffer == self::STREAM ) {
@@ -321,13 +330,29 @@ class WikiExporter {
                        }
                # For page dumps...
                } else {
-                       $tables = [ 'page', 'revision' ];
+                       $revOpts = [ 'page' ];
+                       if ( $this->text != self::STUB ) {
+                               $revOpts[] = 'text';
+                       }
+                       $revQuery = Revision::getQueryInfo( $revOpts );
+
+                       // We want page primary rather than revision
+                       $tables = array_merge( [ 'page' ], array_diff( $revQuery['tables'], [ 'page' ] ) );
+                       $join = $revQuery['joins'] + [
+                               'revision' => $revQuery['joins']['page']
+                       ];
+                       unset( $join['page'] );
+
+                       $fields = array_merge( $revQuery['fields'], [ 'page_restrictions' ] );
+
+                       $conds = [];
+                       if ( $cond !== '' ) {
+                               $conds[] = $cond;
+                       }
                        $opts = [ 'ORDER BY' => 'page_id ASC' ];
                        $opts['USE INDEX'] = [];
-                       $join = [];
                        if ( is_array( $this->history ) ) {
                                # Time offset/limit for all pages/history...
-                               $revJoin = 'page_id=rev_page';
                                # Set time order
                                if ( $this->history['dir'] == 'asc' ) {
                                        $op = '>';
@@ -338,10 +363,9 @@ class WikiExporter {
                                }
                                # Set offset
                                if ( !empty( $this->history['offset'] ) ) {
-                                       $revJoin .= " AND rev_timestamp $op " .
+                                       $conds[] = "rev_timestamp $op " .
                                                $this->db->addQuotes( $this->db->timestamp( $this->history['offset'] ) );
                                }
-                               $join['revision'] = [ 'INNER JOIN', $revJoin ];
                                # Set query limit
                                if ( !empty( $this->history['limit'] ) ) {
                                        $opts['LIMIT'] = intval( $this->history['limit'] );
@@ -350,13 +374,11 @@ class WikiExporter {
                                # Full history dumps...
                                # query optimization for history stub dumps
                                if ( $this->text == self::STUB && $orderRevs ) {
-                                       $tables = [ 'revision', 'page' ];
-                                       $opts[] = 'STRAIGHT_JOIN';
+                                       $tables = $revQuery['tables'];
                                        $opts['ORDER BY'] = [ 'rev_page ASC', 'rev_id ASC' ];
                                        $opts['USE INDEX']['revision'] = 'rev_page_id';
+                                       unset( $join['revision'] );
                                        $join['page'] = [ 'INNER JOIN', 'rev_page=page_id' ];
-                               } else {
-                                       $join['revision'] = [ 'INNER JOIN', 'page_id=rev_page' ];
                                }
                        } elseif ( $this->history & self::CURRENT ) {
                                # Latest revision dumps...
@@ -374,22 +396,11 @@ class WikiExporter {
                                }
                        } elseif ( $this->history & self::RANGE ) {
                                # Dump of revisions within a specified range
-                               $join['revision'] = [ 'INNER JOIN', 'page_id=rev_page' ];
                                $opts['ORDER BY'] = [ 'rev_page ASC', 'rev_id ASC' ];
                        } else {
                                # Unknown history specification parameter?
                                throw new MWException( __METHOD__ . " given invalid history dump type." );
                        }
-                       # Query optimization hacks
-                       if ( $cond == '' ) {
-                               $opts[] = 'STRAIGHT_JOIN';
-                               $opts['USE INDEX']['page'] = 'PRIMARY';
-                       }
-                       # Build text join options
-                       if ( $this->text != self::STUB ) { // 1-pass
-                               $tables[] = 'text';
-                               $join['text'] = [ 'INNER JOIN', 'rev_text_id=old_id' ];
-                       }
 
                        if ( $this->buffer == self::STREAM ) {
                                $prev = $this->db->bufferResults( false );
@@ -399,16 +410,14 @@ class WikiExporter {
                                Hooks::run( 'ModifyExportQuery',
                                                [ $this->db, &$tables, &$cond, &$opts, &$join ] );
 
-                               $commentQuery = CommentStore::getStore()->getJoin( 'rev_comment' );
-
                                # Do the query!
                                $result = $this->db->select(
-                                       $tables + $commentQuery['tables'],
-                                       [ '*' ] + $commentQuery['fields'],
-                                       $cond,
+                                       $tables,
+                                       $fields,
+                                       $conds,
                                        __METHOD__,
                                        $opts,
-                                       $join + $commentQuery['joins']
+                                       $join
                                );
                                # Output dump results
                                $this->outputPageStream( $result );
index 3beab29..de7d1a4 100644 (file)
@@ -92,6 +92,7 @@ class ExternalStore {
         * @param array $urls The URLs of the text to get
         * @return array Map from url to its data.  Data is either string when found
         *     or false on failure.
+        * @throws MWException
         */
        public static function batchFetchFromURLs( array $urls ) {
                $batches = [];
@@ -157,7 +158,7 @@ class ExternalStore {
         * provided by $wgDefaultExternalStore.
         *
         * @param string $data
-        * @param array $params Associative array of ExternalStoreMedium parameters
+        * @param array $params Map of ExternalStoreMedium::__construct context parameters
         * @return string|bool The URL of the stored data item, or false on error
         * @throws MWException
         */
@@ -175,7 +176,7 @@ class ExternalStore {
         *
         * @param array $tryStores Refer to $wgDefaultExternalStore
         * @param string $data
-        * @param array $params Associative array of ExternalStoreMedium parameters
+        * @param array $params Map of ExternalStoreMedium::__construct context parameters
         * @return string|bool The URL of the stored data item, or false on error
         * @throws MWException
         */
@@ -190,19 +191,25 @@ class ExternalStore {
                        if ( $store === false ) {
                                throw new MWException( "Invalid external storage protocol - $storeUrl" );
                        }
+
                        try {
-                               $url = $store->store( $path, $data ); // Try to save the object
+                               if ( $store->isReadOnly( $path ) ) {
+                                       $msg = 'read only';
+                               } else {
+                                       $url = $store->store( $path, $data );
+                                       if ( strlen( $url ) ) {
+                                               return $url; // a store accepted the write; done!
+                                       }
+                                       $msg = 'operation failed';
+                               }
                        } catch ( Exception $error ) {
-                               $url = false;
-                       }
-                       if ( strlen( $url ) ) {
-                               return $url; // Done!
-                       } else {
-                               unset( $tryStores[$index] ); // Don't try this one again!
-                               $tryStores = array_values( $tryStores ); // Must have consecutive keys
-                               wfDebugLog( 'ExternalStorage',
-                                       "Unable to store text to external storage $storeUrl" );
+                               $msg = 'caught exception';
                        }
+
+                       unset( $tryStores[$index] ); // Don't try this one again!
+                       $tryStores = array_values( $tryStores ); // Must have consecutive keys
+                       wfDebugLog( 'ExternalStorage',
+                               "Unable to store text to external storage $storeUrl ($msg)" );
                }
                // All stores failed
                if ( $error ) {
@@ -212,6 +219,29 @@ class ExternalStore {
                }
        }
 
+       /**
+        * @return bool Whether all the default insertion stores are marked as read-only
+        * @since 1.31
+        */
+       public static function defaultStoresAreReadOnly() {
+               global $wgDefaultExternalStore;
+
+               $tryStores = (array)$wgDefaultExternalStore;
+               if ( !$tryStores ) {
+                       return false; // no stores exists which can be "read only"
+               }
+
+               foreach ( $tryStores as $storeUrl ) {
+                       list( $proto, $path ) = explode( '://', $storeUrl, 2 );
+                       $store = self::getStoreObject( $proto, [] );
+                       if ( !$store->isReadOnly( $path ) ) {
+                               return false; // at least one store is not read-only
+                       }
+               }
+
+               return true; // all stores are read-only
+       }
+
        /**
         * @param string $data
         * @param string $wiki
index e5d36e1..b43bd21 100644 (file)
@@ -103,6 +103,10 @@ class ExternalStoreDB extends ExternalStoreMedium {
                return "DB://$location/$id";
        }
 
+       public function isReadOnly( $location ) {
+               return ( $this->getLoadBalancer( $location )->getReadOnlyReason() !== false );
+       }
+
        /**
         * Get a LoadBalancer for the specified cluster
         *
index 8e1e49f..3d812c9 100644 (file)
  * @ingroup ExternalStorage
  */
 class ExternalStoreHttp extends ExternalStoreMedium {
-       /**
-        * @see ExternalStoreMedium::fetchFromURL()
-        * @param string $url
-        * @return string|bool
-        * @throws MWException
-        */
        public function fetchFromURL( $url ) {
                return Http::get( $url, [], __METHOD__ );
        }
 
-       /**
-        * @see ExternalStoreMedium::store()
-        * @param string $cluster
-        * @param string $data
-        * @return string|bool
-        * @throws MWException
-        */
-       public function store( $cluster, $data ) {
+       public function store( $location, $data ) {
                throw new MWException( "ExternalStoreHttp is read-only and does not support store()." );
        }
+
+       public function isReadOnly( $location ) {
+               return true;
+       }
 }
index 6cfa083..da7752b 100644 (file)
@@ -32,7 +32,8 @@ abstract class ExternalStoreMedium {
        protected $params = [];
 
        /**
-        * @param array $params Options
+        * @param array $params Usage context options:
+        *   - wiki: the domain ID of the wiki this is being used for [optional]
         */
        public function __construct( array $params = [] ) {
                $this->params = $params;
@@ -76,4 +77,15 @@ abstract class ExternalStoreMedium {
         * @throws MWException
         */
        abstract public function store( $location, $data );
+
+       /**
+        * Check if a given location is read-only
+        *
+        * @param string $location The location name
+        * @return bool Whether this location is read-only
+        * @since 1.31
+        */
+       public function isReadOnly( $location ) {
+               return false;
+       }
 }
index 5395f56..0c6d022 100644 (file)
@@ -73,13 +73,6 @@ class ExternalStoreMwstore extends ExternalStoreMedium {
                return $blobs;
        }
 
-       /**
-        * @see ExternalStoreMedium::store()
-        * @param string $backend
-        * @param string $data
-        * @return string|bool
-        * @throws MWException
-        */
        public function store( $backend, $data ) {
                $be = FileBackendGroup::singleton()->get( $backend );
                if ( $be instanceof FileBackend ) {
@@ -103,4 +96,10 @@ class ExternalStoreMwstore extends ExternalStoreMedium {
 
                return false;
        }
+
+       public function isReadOnly( $backend ) {
+               $be = FileBackendGroup::singleton()->get( $backend );
+
+               return $be ? $be->isReadOnly() : false;
+       }
 }
index 5a37701..1a86648 100644 (file)
@@ -143,7 +143,7 @@ class ForeignAPIRepo extends FileRepo {
                }
 
                $data = $this->fetchImageQuery( [
-                       'titles' => implode( $files, '|' ),
+                       'titles' => implode( '|', $files ),
                        'prop' => 'imageinfo' ]
                );
 
index 6577ab6..982fea4 100644 (file)
@@ -63,12 +63,9 @@ class ArchivedFile {
        /** @var string Upload description */
        private $description;
 
-       /** @var int User ID of uploader */
+       /** @var User|null Uploader */
        private $user;
 
-       /** @var string User name of uploader */
-       private $user_text;
-
        /** @var string Time of upload */
        private $timestamp;
 
@@ -116,8 +113,7 @@ class ArchivedFile {
                $this->mime = "unknown/unknown";
                $this->media_type = '';
                $this->description = '';
-               $this->user = 0;
-               $this->user_text = '';
+               $this->user = null;
                $this->timestamp = null;
                $this->deleted = 0;
                $this->dataLoaded = false;
@@ -221,6 +217,18 @@ class ArchivedFile {
         * @return array
         */
        static function selectFields() {
+               global $wgActorTableSchemaMigrationStage;
+
+               if ( $wgActorTableSchemaMigrationStage > MIGRATION_WRITE_BOTH ) {
+                       // If code is using this instead of self::getQueryInfo(), there's a
+                       // decent chance it's going to try to directly access
+                       // $row->fa_user or $row->fa_user_text and we can't give it
+                       // useful values here once those aren't being written anymore.
+                       throw new BadMethodCallException(
+                               'Cannot use ' . __METHOD__ . ' when $wgActorTableSchemaMigrationStage > MIGRATION_WRITE_BOTH'
+                       );
+               }
+
                wfDeprecated( __METHOD__, '1.31' );
                return [
                        'fa_id',
@@ -238,6 +246,7 @@ class ArchivedFile {
                        'fa_minor_mime',
                        'fa_user',
                        'fa_user_text',
+                       'fa_actor' => $wgActorTableSchemaMigrationStage > MIGRATION_OLD ? 'fa_actor' : null,
                        'fa_timestamp',
                        'fa_deleted',
                        'fa_deleted_timestamp', /* Used by LocalFileRestoreBatch */
@@ -256,8 +265,9 @@ class ArchivedFile {
         */
        public static function getQueryInfo() {
                $commentQuery = CommentStore::getStore()->getJoin( 'fa_description' );
+               $actorQuery = ActorMigration::newMigration()->getJoin( 'fa_user' );
                return [
-                       'tables' => [ 'filearchive' ] + $commentQuery['tables'],
+                       'tables' => [ 'filearchive' ] + $commentQuery['tables'] + $actorQuery['tables'],
                        'fields' => [
                                'fa_id',
                                'fa_name',
@@ -272,14 +282,12 @@ class ArchivedFile {
                                'fa_media_type',
                                'fa_major_mime',
                                'fa_minor_mime',
-                               'fa_user',
-                               'fa_user_text',
                                'fa_timestamp',
                                'fa_deleted',
                                'fa_deleted_timestamp', /* Used by LocalFileRestoreBatch */
                                'fa_sha1',
-                       ] + $commentQuery['fields'],
-                       'joins' => $commentQuery['joins'],
+                       ] + $commentQuery['fields'] + $actorQuery['fields'],
+                       'joins' => $commentQuery['joins'] + $actorQuery['joins'],
                ];
        }
 
@@ -305,8 +313,7 @@ class ArchivedFile {
                $this->description = CommentStore::getStore()
                        // Legacy because $row may have come from self::selectFields()
                        ->getCommentLegacy( wfGetDB( DB_REPLICA ), 'fa_description', $row )->text;
-               $this->user = $row->fa_user;
-               $this->user_text = $row->fa_user_text;
+               $this->user = User::newFromAnyId( $row->fa_user, $row->fa_user_text, $row->fa_actor );
                $this->timestamp = $row->fa_timestamp;
                $this->deleted = $row->fa_deleted;
                if ( isset( $row->fa_sha1 ) ) {
@@ -519,17 +526,20 @@ class ArchivedFile {
         * @note Prior to MediaWiki 1.23, this method always
         *   returned the user id, and was inconsistent with
         *   the rest of the file classes.
-        * @param string $type 'text' or 'id'
-        * @return int|string
+        * @param string $type 'text', 'id', or 'object'
+        * @return int|string|User|null
         * @throws MWException
+        * @since 1.31 added 'object'
         */
        public function getUser( $type = 'text' ) {
                $this->load();
 
-               if ( $type == 'text' ) {
-                       return $this->user_text;
-               } elseif ( $type == 'id' ) {
-                       return (int)$this->user;
+               if ( $type === 'object' ) {
+                       return $this->user;
+               } elseif ( $type === 'text' ) {
+                       return $this->user ? $this->user->getName() : '';
+               } elseif ( $type === 'id' ) {
+                       return $this->user ? $this->user->getId() : 0;
                }
 
                throw new MWException( "Unknown type '$type'." );
@@ -555,9 +565,7 @@ class ArchivedFile {
         * @return int
         */
        public function getRawUser() {
-               $this->load();
-
-               return $this->user;
+               return $this->getUser( 'id' );
        }
 
        /**
@@ -566,9 +574,7 @@ class ArchivedFile {
         * @return string
         */
        public function getRawUserText() {
-               $this->load();
-
-               return $this->user_text;
+               return $this->getUser( 'text' );
        }
 
        /**
index 7cf2749..ec4a5fb 100644 (file)
@@ -24,6 +24,7 @@
 use MediaWiki\Logger\LoggerFactory;
 use Wikimedia\Rdbms\Database;
 use Wikimedia\Rdbms\IDatabase;
+use MediaWiki\MediaWikiServices;
 
 /**
  * Class to represent a local file in the wiki's own database
@@ -101,12 +102,9 @@ class LocalFile extends File {
        /** @var string Upload timestamp */
        private $timestamp;
 
-       /** @var int User ID of uploader */
+       /** @var User Uploader */
        private $user;
 
-       /** @var string User name of uploader */
-       private $user_text;
-
        /** @var string Description of current revision of the file */
        private $description;
 
@@ -200,7 +198,19 @@ class LocalFile extends File {
         * @return array
         */
        static function selectFields() {
+               global $wgActorTableSchemaMigrationStage;
+
                wfDeprecated( __METHOD__, '1.31' );
+               if ( $wgActorTableSchemaMigrationStage > MIGRATION_WRITE_BOTH ) {
+                       // If code is using this instead of self::getQueryInfo(), there's a
+                       // decent chance it's going to try to directly access
+                       // $row->img_user or $row->img_user_text and we can't give it
+                       // useful values here once those aren't being written anymore.
+                       throw new BadMethodCallException(
+                               'Cannot use ' . __METHOD__ . ' when $wgActorTableSchemaMigrationStage > MIGRATION_WRITE_BOTH'
+                       );
+               }
+
                return [
                        'img_name',
                        'img_size',
@@ -213,6 +223,7 @@ class LocalFile extends File {
                        'img_minor_mime',
                        'img_user',
                        'img_user_text',
+                       'img_actor' => $wgActorTableSchemaMigrationStage > MIGRATION_OLD ? 'img_actor' : null,
                        'img_timestamp',
                        'img_sha1',
                ] + CommentStore::getStore()->getFields( 'img_description' );
@@ -231,8 +242,9 @@ class LocalFile extends File {
         */
        public static function getQueryInfo( array $options = [] ) {
                $commentQuery = CommentStore::getStore()->getJoin( 'img_description' );
+               $actorQuery = ActorMigration::newMigration()->getJoin( 'img_user' );
                $ret = [
-                       'tables' => [ 'image' ] + $commentQuery['tables'],
+                       'tables' => [ 'image' ] + $commentQuery['tables'] + $actorQuery['tables'],
                        'fields' => [
                                'img_name',
                                'img_size',
@@ -243,12 +255,10 @@ class LocalFile extends File {
                                'img_media_type',
                                'img_major_mime',
                                'img_minor_mime',
-                               'img_user',
-                               'img_user_text',
                                'img_timestamp',
                                'img_sha1',
-                       ] + $commentQuery['fields'],
-                       'joins' => $commentQuery['joins'],
+                       ] + $commentQuery['fields'] + $actorQuery['fields'],
+                       'joins' => $commentQuery['joins'] + $actorQuery['joins'],
                ];
 
                if ( in_array( 'omit-nonlazy', $options, true ) ) {
@@ -329,6 +339,10 @@ class LocalFile extends File {
                                                $cacheVal[$field] = $this->$field;
                                        }
                                }
+                               $cacheVal['user'] = $this->user ? $this->user->getId() : 0;
+                               $cacheVal['user_text'] = $this->user ? $this->user->getName() : '';
+                               $cacheVal['actor'] = $this->user ? $this->user->getActorId() : null;
+
                                // Strip off excessive entries from the subset of fields that can become large.
                                // If the cache value gets to large it will not fit in memcached and nothing will
                                // get cached at all, causing master queries for any file access.
@@ -406,8 +420,7 @@ class LocalFile extends File {
                // and self::loadFromCache() for the caching, and self::setProps() for
                // populating the object from an array of data.
                return [ 'size', 'width', 'height', 'bits', 'media_type',
-                       'major_mime', 'minor_mime', 'metadata', 'timestamp', 'sha1', 'user',
-                       'user_text', 'description' ];
+                       'major_mime', 'minor_mime', 'metadata', 'timestamp', 'sha1', 'description' ];
        }
 
        /**
@@ -569,6 +582,13 @@ class LocalFile extends File {
                $decoded['description'] = CommentStore::getStore()
                        ->getComment( 'description', (object)$decoded )->text;
 
+               $decoded['user'] = User::newFromAnyId(
+                       isset( $decoded['user'] ) ? $decoded['user'] : null,
+                       isset( $decoded['user_text'] ) ? $decoded['user_text'] : null,
+                       isset( $decoded['actor'] ) ? $decoded['actor'] : null
+               );
+               unset( $decoded['user_text'], $decoded['actor'] );
+
                $decoded['timestamp'] = wfTimestamp( TS_MW, $decoded['timestamp'] );
 
                $decoded['metadata'] = $this->repo->getReplicaDB()->decodeBlob( $decoded['metadata'] );
@@ -750,6 +770,14 @@ class LocalFile extends File {
                        }
                }
 
+               if ( isset( $info['user'] ) || isset( $info['user_text'] ) || isset( $info['actor'] ) ) {
+                       $this->user = User::newFromAnyId(
+                               isset( $info['user'] ) ? $info['user'] : null,
+                               isset( $info['user_text'] ) ? $info['user_text'] : null,
+                               isset( $info['actor'] ) ? $info['actor'] : null
+                       );
+               }
+
                // Fix up mime fields
                if ( isset( $info['major_mime'] ) ) {
                        $this->mime = "{$info['major_mime']}/{$info['minor_mime']}";
@@ -844,19 +872,24 @@ class LocalFile extends File {
        }
 
        /**
-        * Returns ID or name of user who uploaded the file
+        * Returns user who uploaded the file
         *
-        * @param string $type 'text' or 'id'
-        * @return int|string
+        * @param string $type 'text', 'id', or 'object'
+        * @return int|string|User
+        * @since 1.31 Added 'object'
         */
        function getUser( $type = 'text' ) {
                $this->load();
 
-               if ( $type == 'text' ) {
-                       return $this->user_text;
-               } else { // id
-                       return (int)$this->user;
+               if ( $type === 'object' ) {
+                       return $this->user;
+               } elseif ( $type === 'text' ) {
+                       return $this->user->getName();
+               } elseif ( $type === 'id' ) {
+                       return $this->user->getId();
                }
+
+               throw new MWException( "Unknown type '$type'." );
        }
 
        /**
@@ -1275,6 +1308,10 @@ class LocalFile extends File {
        ) {
                if ( $this->getRepo()->getReadOnlyReason() !== false ) {
                        return $this->readOnlyFatalStatus();
+               } elseif ( MediaWikiServices::getInstance()->getRevisionStore()->isReadOnly() ) {
+                       // Check this in advance to avoid writing to FileBackend and the file tables,
+                       // only to fail on insert the revision due to the text store being unavailable.
+                       return $this->readOnlyFatalStatus();
                }
 
                $srcPath = ( $src instanceof FSFile ) ? $src->getPath() : $src;
@@ -1387,7 +1424,7 @@ class LocalFile extends File {
        function recordUpload2(
                $oldver, $comment, $pageText, $props = false, $timestamp = false, $user = null, $tags = []
        ) {
-               global $wgCommentTableSchemaMigrationStage;
+               global $wgCommentTableSchemaMigrationStage, $wgActorTableSchemaMigrationStage;
 
                if ( is_null( $user ) ) {
                        global $wgUser;
@@ -1409,6 +1446,7 @@ class LocalFile extends File {
                $props['description'] = $comment;
                $props['user'] = $user->getId();
                $props['user_text'] = $user->getName();
+               $props['actor'] = $user->getActorId( $dbw );
                $props['timestamp'] = wfTimestamp( TS_MW, $timestamp ); // DB -> TS_MW
                $this->setProps( $props );
 
@@ -1427,6 +1465,8 @@ class LocalFile extends File {
                $commentStore = CommentStore::getStore();
                list( $commentFields, $commentCallback ) =
                        $commentStore->insertWithTempTable( $dbw, 'img_description', $comment );
+               $actorMigration = ActorMigration::newMigration();
+               $actorFields = $actorMigration->getInsertValues( $dbw, 'img_user', $user );
                $dbw->insert( 'image',
                        [
                                'img_name' => $this->getName(),
@@ -1438,11 +1478,9 @@ class LocalFile extends File {
                                'img_major_mime' => $this->major_mime,
                                'img_minor_mime' => $this->minor_mime,
                                'img_timestamp' => $timestamp,
-                               'img_user' => $user->getId(),
-                               'img_user_text' => $user->getName(),
                                'img_metadata' => $dbw->encodeBlob( $this->metadata ),
                                'img_sha1' => $this->sha1
-                       ] + $commentFields,
+                       ] + $commentFields + $actorFields,
                        __METHOD__,
                        'IGNORE'
                );
@@ -1485,8 +1523,6 @@ class LocalFile extends File {
                                'oi_height' => 'img_height',
                                'oi_bits' => 'img_bits',
                                'oi_timestamp' => 'img_timestamp',
-                               'oi_user' => 'img_user',
-                               'oi_user_text' => 'img_user_text',
                                'oi_metadata' => 'img_metadata',
                                'oi_media_type' => 'img_media_type',
                                'oi_major_mime' => 'img_major_mime',
@@ -1529,6 +1565,37 @@ class LocalFile extends File {
                                }
                        }
 
+                       if ( $wgActorTableSchemaMigrationStage <= MIGRATION_WRITE_BOTH ) {
+                               $fields['oi_user'] = 'img_user';
+                               $fields['oi_user_text'] = 'img_user_text';
+                       }
+                       if ( $wgActorTableSchemaMigrationStage >= MIGRATION_WRITE_BOTH ) {
+                               $fields['oi_actor'] = 'img_actor';
+                       }
+
+                       if ( $wgActorTableSchemaMigrationStage !== MIGRATION_OLD &&
+                               $wgActorTableSchemaMigrationStage !== MIGRATION_NEW
+                       ) {
+                               // Upgrade any rows that are still old-style. Otherwise an upgrade
+                               // might be missed if a deletion happens while the migration script
+                               // is running.
+                               $res = $dbw->select(
+                                       [ 'image' ],
+                                       [ 'img_name', 'img_user', 'img_user_text' ],
+                                       [ 'img_name' => $this->getName(), 'img_actor' => 0 ],
+                                       __METHOD__
+                               );
+                               foreach ( $res as $row ) {
+                                       $actorId = User::newFromAnyId( $row->img_user, $row->img_user_text, null )->getActorId( $dbw );
+                                       $dbw->update(
+                                               'image',
+                                               [ 'img_actor' => $actorId ],
+                                               [ 'img_name' => $row->img_name, 'img_actor' => 0 ],
+                                               __METHOD__
+                                       );
+                               }
+                       }
+
                        # (T36993) Note: $oldver can be empty here, if the previous
                        # version of the file was broken. Allow registration of the new
                        # version to continue anyway, because that's better than having
@@ -1549,11 +1616,9 @@ class LocalFile extends File {
                                        'img_major_mime' => $this->major_mime,
                                        'img_minor_mime' => $this->minor_mime,
                                        'img_timestamp' => $timestamp,
-                                       'img_user' => $user->getId(),
-                                       'img_user_text' => $user->getName(),
                                        'img_metadata' => $dbw->encodeBlob( $this->metadata ),
                                        'img_sha1' => $this->sha1
-                               ] + $commentFields,
+                               ] + $commentFields + $actorFields,
                                [ 'img_name' => $this->getName() ],
                                __METHOD__
                        );
@@ -2400,12 +2465,13 @@ class LocalFileDeleteBatch {
        }
 
        protected function doDBInserts() {
-               global $wgCommentTableSchemaMigrationStage;
+               global $wgCommentTableSchemaMigrationStage, $wgActorTableSchemaMigrationStage;
 
                $now = time();
                $dbw = $this->file->repo->getMasterDB();
 
                $commentStore = CommentStore::getStore();
+               $actorMigration = ActorMigration::newMigration();
 
                $encTimestamp = $dbw->addQuotes( $dbw->timestamp( $now ) );
                $encUserId = $dbw->addQuotes( $this->user->getId() );
@@ -2444,8 +2510,6 @@ class LocalFileDeleteBatch {
                                'fa_media_type' => 'img_media_type',
                                'fa_major_mime' => 'img_major_mime',
                                'fa_minor_mime' => 'img_minor_mime',
-                               'fa_user' => 'img_user',
-                               'fa_user_text' => 'img_user_text',
                                'fa_timestamp' => 'img_timestamp',
                                'fa_sha1' => 'img_sha1'
                        ];
@@ -2490,6 +2554,37 @@ class LocalFileDeleteBatch {
                                }
                        }
 
+                       if ( $wgActorTableSchemaMigrationStage <= MIGRATION_WRITE_BOTH ) {
+                               $fields['fa_user'] = 'img_user';
+                               $fields['fa_user_text'] = 'img_user_text';
+                       }
+                       if ( $wgActorTableSchemaMigrationStage >= MIGRATION_WRITE_BOTH ) {
+                               $fields['fa_actor'] = 'img_actor';
+                       }
+
+                       if ( $wgActorTableSchemaMigrationStage !== MIGRATION_OLD &&
+                               $wgActorTableSchemaMigrationStage !== MIGRATION_NEW
+                       ) {
+                               // Upgrade any rows that are still old-style. Otherwise an upgrade
+                               // might be missed if a deletion happens while the migration script
+                               // is running.
+                               $res = $dbw->select(
+                                       [ 'image' ],
+                                       [ 'img_name', 'img_user', 'img_user_text' ],
+                                       [ 'img_name' => $this->file->getName(), 'img_actor' => 0 ],
+                                       __METHOD__
+                               );
+                               foreach ( $res as $row ) {
+                                       $actorId = User::newFromAnyId( $row->img_user, $row->img_user_text, null )->getActorId( $dbw );
+                                       $dbw->update(
+                                               'image',
+                                               [ 'img_actor' => $actorId ],
+                                               [ 'img_name' => $row->img_name, 'img_actor' => 0 ],
+                                               __METHOD__
+                                       );
+                               }
+                       }
+
                        $dbw->insertSelect( 'filearchive', $tables, $fields,
                                [ 'img_name' => $this->file->getName() ], __METHOD__, [], [], $joins );
                }
@@ -2512,6 +2607,7 @@ class LocalFileDeleteBatch {
                                $reason = $commentStore->createComment( $dbw, $this->reason );
                                foreach ( $res as $row ) {
                                        $comment = $commentStore->getComment( 'oi_description', $row );
+                                       $user = User::newFromAnyId( $row->oi_user, $row->oi_user_text, $row->oi_actor );
                                        $rowsInsert[] = [
                                                // Deletion-specific fields
                                                'fa_storage_group' => 'deleted',
@@ -2532,12 +2628,11 @@ class LocalFileDeleteBatch {
                                                'fa_media_type' => $row->oi_media_type,
                                                'fa_major_mime' => $row->oi_major_mime,
                                                'fa_minor_mime' => $row->oi_minor_mime,
-                                               'fa_user' => $row->oi_user,
-                                               'fa_user_text' => $row->oi_user_text,
                                                'fa_timestamp' => $row->oi_timestamp,
                                                'fa_sha1' => $row->oi_sha1
                                        ] + $commentStore->insert( $dbw, 'fa_deleted_reason', $reason )
-                                       + $commentStore->insert( $dbw, 'fa_description', $comment );
+                                       + $commentStore->insert( $dbw, 'fa_description', $comment )
+                                       + $actorMigration->getInsertValues( $dbw, 'fa_user', $user );
                                }
                        }
 
@@ -2736,6 +2831,7 @@ class LocalFileRestoreBatch {
                $dbw = $this->file->repo->getMasterDB();
 
                $commentStore = CommentStore::getStore();
+               $actorMigration = ActorMigration::newMigration();
 
                $status = $this->file->repo->newGood();
 
@@ -2824,11 +2920,13 @@ class LocalFileRestoreBatch {
                        }
 
                        $comment = $commentStore->getComment( 'fa_description', $row );
+                       $user = User::newFromAnyId( $row->fa_user, $row->fa_user_text, $row->fa_actor );
                        if ( $first && !$exists ) {
                                // This revision will be published as the new current version
                                $destRel = $this->file->getRel();
                                list( $commentFields, $commentCallback ) =
                                        $commentStore->insertWithTempTable( $dbw, 'img_description', $comment );
+                               $actorFields = $actorMigration->getInsertValues( $dbw, 'img_user', $user );
                                $insertCurrent = [
                                        'img_name' => $row->fa_name,
                                        'img_size' => $row->fa_size,
@@ -2839,11 +2937,9 @@ class LocalFileRestoreBatch {
                                        'img_media_type' => $props['media_type'],
                                        'img_major_mime' => $props['major_mime'],
                                        'img_minor_mime' => $props['minor_mime'],
-                                       'img_user' => $row->fa_user,
-                                       'img_user_text' => $row->fa_user_text,
                                        'img_timestamp' => $row->fa_timestamp,
                                        'img_sha1' => $sha1
-                               ] + $commentFields;
+                               ] + $commentFields + $actorFields;
 
                                // The live (current) version cannot be hidden!
                                if ( !$this->unsuppress && $row->fa_deleted ) {
@@ -2875,8 +2971,6 @@ class LocalFileRestoreBatch {
                                        'oi_width' => $row->fa_width,
                                        'oi_height' => $row->fa_height,
                                        'oi_bits' => $row->fa_bits,
-                                       'oi_user' => $row->fa_user,
-                                       'oi_user_text' => $row->fa_user_text,
                                        'oi_timestamp' => $row->fa_timestamp,
                                        'oi_metadata' => $props['metadata'],
                                        'oi_media_type' => $props['media_type'],
@@ -2884,7 +2978,8 @@ class LocalFileRestoreBatch {
                                        'oi_minor_mime' => $props['minor_mime'],
                                        'oi_deleted' => $this->unsuppress ? 0 : $row->fa_deleted,
                                        'oi_sha1' => $sha1
-                               ] + $commentStore->insert( $dbw, 'oi_description', $comment );
+                               ] + $commentStore->insert( $dbw, 'oi_description', $comment )
+                               + $actorMigration->getInsertValues( $dbw, 'oi_user', $user );
                        }
 
                        $deleteIds[] = $row->fa_id;
index d08d0ae..65f0fb1 100644 (file)
@@ -110,7 +110,19 @@ class OldLocalFile extends LocalFile {
         * @return array
         */
        static function selectFields() {
+               global $wgActorTableSchemaMigrationStage;
+
                wfDeprecated( __METHOD__, '1.31' );
+               if ( $wgActorTableSchemaMigrationStage > MIGRATION_WRITE_BOTH ) {
+                       // If code is using this instead of self::getQueryInfo(), there's a
+                       // decent chance it's going to try to directly access
+                       // $row->oi_user or $row->oi_user_text and we can't give it
+                       // useful values here once those aren't being written anymore.
+                       throw new BadMethodCallException(
+                               'Cannot use ' . __METHOD__ . ' when $wgActorTableSchemaMigrationStage > MIGRATION_WRITE_BOTH'
+                       );
+               }
+
                return [
                        'oi_name',
                        'oi_archive_name',
@@ -124,6 +136,7 @@ class OldLocalFile extends LocalFile {
                        'oi_minor_mime',
                        'oi_user',
                        'oi_user_text',
+                       'oi_actor' => $wgActorTableSchemaMigrationStage > MIGRATION_OLD ? 'oi_actor' : null,
                        'oi_timestamp',
                        'oi_deleted',
                        'oi_sha1',
@@ -143,8 +156,9 @@ class OldLocalFile extends LocalFile {
         */
        public static function getQueryInfo( array $options = [] ) {
                $commentQuery = CommentStore::getStore()->getJoin( 'oi_description' );
+               $actorQuery = ActorMigration::newMigration()->getJoin( 'oi_user' );
                $ret = [
-                       'tables' => [ 'oldimage' ] + $commentQuery['tables'],
+                       'tables' => [ 'oldimage' ] + $commentQuery['tables'] + $actorQuery['tables'],
                        'fields' => [
                                'oi_name',
                                'oi_archive_name',
@@ -155,13 +169,11 @@ class OldLocalFile extends LocalFile {
                                'oi_media_type',
                                'oi_major_mime',
                                'oi_minor_mime',
-                               'oi_user',
-                               'oi_user_text',
                                'oi_timestamp',
                                'oi_deleted',
                                'oi_sha1',
-                       ] + $commentQuery['fields'],
-                       'joins' => $commentQuery['joins'],
+                       ] + $commentQuery['fields'] + $actorQuery['fields'],
+                       'joins' => $commentQuery['joins'] + $actorQuery['joins'],
                ];
 
                if ( in_array( 'omit-nonlazy', $options, true ) ) {
@@ -435,6 +447,7 @@ class OldLocalFile extends LocalFile {
                }
 
                $commentFields = CommentStore::getStore()->insert( $dbw, 'oi_description', $comment );
+               $actorFields = ActorMigration::newMigration()->getInsertValues( $dbw, 'oi_user', $user );
                $dbw->insert( 'oldimage',
                        [
                                'oi_name' => $this->getName(),
@@ -444,14 +457,12 @@ class OldLocalFile extends LocalFile {
                                'oi_height' => intval( $props['height'] ),
                                'oi_bits' => $props['bits'],
                                'oi_timestamp' => $dbw->timestamp( $timestamp ),
-                               'oi_user' => $user->getId(),
-                               'oi_user_text' => $user->getName(),
                                'oi_metadata' => $props['metadata'],
                                'oi_media_type' => $props['media_type'],
                                'oi_major_mime' => $props['major_mime'],
                                'oi_minor_mime' => $props['minor_mime'],
                                'oi_sha1' => $props['sha1'],
-                       ] + $commentFields, __METHOD__
+                       ] + $commentFields + $actorFields, __METHOD__
                );
 
                return true;
index cdb8f5b..610b509 100644 (file)
@@ -47,6 +47,10 @@ class HTMLSelectAndOtherField extends HTMLSelectField {
                        $textAttribs['class'][] = $this->mClass;
                }
 
+               if ( isset( $this->mParams['maxlength-unit'] ) ) {
+                       $textAttribs['data-mw-maxlength-unit'] = $this->mParams['maxlength-unit'];
+               }
+
                $allowedParams = [
                        'required',
                        'autofocus',
@@ -54,6 +58,7 @@ class HTMLSelectAndOtherField extends HTMLSelectField {
                        'disabled',
                        'tabindex',
                        'maxlength', // gets dynamic with javascript, see mediawiki.htmlform.js
+                       'maxlength-unit', // 'bytes' or 'codepoints', see mediawiki.htmlform.js
                ];
 
                $textAttribs += $this->getAttributes( $allowedParams );
@@ -73,9 +78,6 @@ class HTMLSelectAndOtherField extends HTMLSelectField {
                # TextInput
                $textAttribs = [
                        'name' => $this->mName . '-other',
-                       'size' => $this->getSize(),
-                       'class' => [ 'mw-htmlform-select-and-other-field' ],
-                       'data-id-select' => $this->mID . '-select',
                        'value' => $value[2],
                ];
 
@@ -122,6 +124,11 @@ class HTMLSelectAndOtherField extends HTMLSelectField {
                        'textinput' => $textAttribs,
                        'dropdowninput' => $dropdownInputAttribs,
                        'or' => false,
+                       'classes' => [ 'mw-htmlform-select-and-other-field' ],
+                       'data' => [
+                               'maxlengthUnit' => isset( $this->mParams['maxlength-unit'] )
+                                       ? $this->mParams['maxlength-unit'] : 'bytes'
+                       ],
                ] );
        }
 
diff --git a/includes/import/ImportableOldRevision.php b/includes/import/ImportableOldRevision.php
new file mode 100644 (file)
index 0000000..6d1e242
--- /dev/null
@@ -0,0 +1,68 @@
+<?php
+
+/**
+ * @since 1.31
+ */
+interface ImportableOldRevision {
+
+       /**
+        * @since 1.31
+        * @return User
+        */
+       public function getUserObj();
+
+       /**
+        * @since 1.31
+        * @return string
+        */
+       public function getUser();
+
+       /**
+        * @since 1.31
+        * @return Title
+        */
+       public function getTitle();
+
+       /**
+        * @since 1.31
+        * @return string
+        */
+       public function getTimestamp();
+
+       /**
+        * @since 1.31
+        * @return string
+        */
+       public function getComment();
+
+       /**
+        * @since 1.31
+        * @return string
+        */
+       public function getModel();
+
+       /**
+        * @since 1.31
+        * @return string
+        */
+       public function getFormat();
+
+       /**
+        * @since 1.31
+        * @return Content
+        */
+       public function getContent();
+
+       /**
+        * @since 1.31
+        * @return bool
+        */
+       public function getMinor();
+
+       /**
+        * @since 1.31
+        * @return bool|string
+        */
+       public function getSha1Base36();
+
+}
diff --git a/includes/import/ImportableOldRevisionImporter.php b/includes/import/ImportableOldRevisionImporter.php
new file mode 100644 (file)
index 0000000..33fad3e
--- /dev/null
@@ -0,0 +1,143 @@
+<?php
+
+use Psr\Log\LoggerInterface;
+use Wikimedia\Rdbms\LoadBalancer;
+
+/**
+ * @since 1.31
+ */
+class ImportableOldRevisionImporter implements OldRevisionImporter {
+
+       /**
+        * @var LoggerInterface
+        */
+       private $logger;
+
+       /**
+        * @var bool
+        */
+       private $doUpdates;
+
+       /**
+        * @var LoadBalancer
+        */
+       private $loadBalancer;
+
+       /**
+        * @param bool $doUpdates
+        * @param LoggerInterface $logger
+        * @param LoadBalancer $loadBalancer
+        */
+       public function __construct(
+               $doUpdates,
+               LoggerInterface $logger,
+               LoadBalancer $loadBalancer
+       ) {
+               $this->doUpdates = $doUpdates;
+               $this->logger = $logger;
+               $this->loadBalancer = $loadBalancer;
+       }
+
+       public function import( ImportableOldRevision $importableRevision, $doUpdates = true ) {
+               $dbw = $this->loadBalancer->getConnectionRef( DB_MASTER );
+
+               # Sneak a single revision into place
+               $user = $importableRevision->getUserObj() ?: User::newFromName( $importableRevision->getUser() );
+               if ( $user ) {
+                       $userId = intval( $user->getId() );
+                       $userText = $user->getName();
+               } else {
+                       $userId = 0;
+                       $userText = $importableRevision->getUser();
+                       $user = new User;
+               }
+
+               // avoid memory leak...?
+               Title::clearCaches();
+
+               $page = WikiPage::factory( $importableRevision->getTitle() );
+               $page->loadPageData( 'fromdbmaster' );
+               if ( !$page->exists() ) {
+                       // must create the page...
+                       $pageId = $page->insertOn( $dbw );
+                       $created = true;
+                       $oldcountable = null;
+               } else {
+                       $pageId = $page->getId();
+                       $created = false;
+
+                       // Note: sha1 has been in XML dumps since 2012. If you have an
+                       // older dump, the duplicate detection here won't work.
+                       $prior = $dbw->selectField( 'revision', '1',
+                               [ 'rev_page' => $pageId,
+                                       'rev_timestamp' => $dbw->timestamp( $importableRevision->getTimestamp() ),
+                                       'rev_sha1' => $importableRevision->getSha1Base36() ],
+                               __METHOD__
+                       );
+                       if ( $prior ) {
+                               // @todo FIXME: This could fail slightly for multiple matches :P
+                               $this->logger->debug( __METHOD__ . ": skipping existing revision for [[" .
+                                       $importableRevision->getTitle()->getPrefixedText() . "]], timestamp " .
+                                       $importableRevision->getTimestamp() . "\n" );
+                               return false;
+                       }
+               }
+
+               if ( !$pageId ) {
+                       // This seems to happen if two clients simultaneously try to import the
+                       // same page
+                       $this->logger->debug( __METHOD__ . ': got invalid $pageId when importing revision of [[' .
+                               $importableRevision->getTitle()->getPrefixedText() . ']], timestamp ' .
+                               $importableRevision->getTimestamp() . "\n" );
+                       return false;
+               }
+
+               // Select previous version to make size diffs correct
+               // @todo This assumes that multiple revisions of the same page are imported
+               // in order from oldest to newest.
+               $prevId = $dbw->selectField( 'revision', 'rev_id',
+                       [
+                               'rev_page' => $pageId,
+                               'rev_timestamp <= ' . $dbw->addQuotes( $dbw->timestamp( $importableRevision->getTimestamp() ) ),
+                       ],
+                       __METHOD__,
+                       [ 'ORDER BY' => [
+                               'rev_timestamp DESC',
+                               'rev_id DESC', // timestamp is not unique per page
+                       ]
+                       ]
+               );
+
+               # @todo FIXME: Use original rev_id optionally (better for backups)
+               # Insert the row
+               $revision = new Revision( [
+                       'title' => $importableRevision->getTitle(),
+                       'page' => $pageId,
+                       'content_model' => $importableRevision->getModel(),
+                       'content_format' => $importableRevision->getFormat(),
+                       // XXX: just set 'content' => $wikiRevision->getContent()?
+                       'text' => $importableRevision->getContent()->serialize( $importableRevision->getFormat() ),
+                       'comment' => $importableRevision->getComment(),
+                       'user' => $userId,
+                       'user_text' => $userText,
+                       'timestamp' => $importableRevision->getTimestamp(),
+                       'minor_edit' => $importableRevision->getMinor(),
+                       'parent_id' => $prevId,
+               ] );
+               $revision->insertOn( $dbw );
+               $changed = $page->updateIfNewerOn( $dbw, $revision );
+
+               if ( $changed !== false && $this->doUpdates ) {
+                       $this->logger->debug( __METHOD__ . ": running updates\n" );
+                       // countable/oldcountable stuff is handled in WikiImporter::finishImportPage
+                       $page->doEditUpdates(
+                               $revision,
+                               $user,
+                               [ 'created' => $created, 'oldcountable' => 'no-change' ]
+                       );
+               }
+
+               return true;
+       }
+
+}
diff --git a/includes/import/ImportableUploadRevision.php b/includes/import/ImportableUploadRevision.php
new file mode 100644 (file)
index 0000000..3f60112
--- /dev/null
@@ -0,0 +1,68 @@
+<?php
+
+/**
+ * @since 1.31
+ */
+interface ImportableUploadRevision {
+
+       /**
+        * @since 1.31
+        * @return string Archive name of a revision if archived.
+        */
+       public function getArchiveName();
+
+       /**
+        * @since 1.31
+        * @return Title
+        */
+       public function getTitle();
+
+       /**
+        * @since 1.31
+        * @return string
+        */
+       public function getTimestamp();
+
+       /**
+        * @since 1.31
+        * @return string|null HTTP source of revision to be used for downloading.
+        */
+       public function getSrc();
+
+       /**
+        * @since 1.31
+        * @return string Local file source of the revision.
+        */
+       public function getFileSrc();
+
+       /**
+        * @since 1.31
+        * @return bool Is the return of getFileSrc only temporary?
+        */
+       public function isTempSrc();
+
+       /**
+        * @since 1.31
+        * @return string|bool sha1 of the revision, false if not set or errors occour.
+        */
+       public function getSha1();
+
+       /**
+        * @since 1.31
+        * @return User
+        */
+       public function getUserObj();
+
+       /**
+        * @since 1.31
+        * @return string The username of the user that created this revision
+        */
+       public function getUser();
+
+       /**
+        * @since 1.31
+        * @return string
+        */
+       public function getComment();
+
+}
diff --git a/includes/import/ImportableUploadRevisionImporter.php b/includes/import/ImportableUploadRevisionImporter.php
new file mode 100644 (file)
index 0000000..495b3d6
--- /dev/null
@@ -0,0 +1,154 @@
+<?php
+
+use Psr\Log\LoggerInterface;
+
+/**
+ * @since 1.31
+ */
+class ImportableUploadRevisionImporter implements UploadRevisionImporter {
+
+       /**
+        * @var LoggerInterface
+        */
+       private $logger;
+
+       /**
+        * @var bool
+        */
+       private $enableUploads;
+
+       /**
+        * @param bool $enableUploads
+        * @param LoggerInterface $logger
+        */
+       public function __construct(
+               $enableUploads,
+               LoggerInterface $logger
+       ) {
+               $this->enableUploads = $enableUploads;
+               $this->logger = $logger;
+       }
+
+       /**
+        * @return StatusValue
+        */
+       private function newNotOkStatus() {
+               $statusValue = new StatusValue();
+               $statusValue->setOK( false );
+               return $statusValue;
+       }
+
+       public function import( ImportableUploadRevision $importableRevision ) {
+               # Construct a file
+               $archiveName = $importableRevision->getArchiveName();
+               if ( $archiveName ) {
+                       $this->logger->debug( __METHOD__ . "Importing archived file as $archiveName\n" );
+                       $file = OldLocalFile::newFromArchiveName( $importableRevision->getTitle(),
+                               RepoGroup::singleton()->getLocalRepo(), $archiveName );
+               } else {
+                       $file = wfLocalFile( $importableRevision->getTitle() );
+                       $file->load( File::READ_LATEST );
+                       $this->logger->debug( __METHOD__ . 'Importing new file as ' . $file->getName() . "\n" );
+                       if ( $file->exists() && $file->getTimestamp() > $importableRevision->getTimestamp() ) {
+                               $archiveName = $file->getTimestamp() . '!' . $file->getName();
+                               $file = OldLocalFile::newFromArchiveName( $importableRevision->getTitle(),
+                                       RepoGroup::singleton()->getLocalRepo(), $archiveName );
+                               $this->logger->debug( __METHOD__ . "File already exists; importing as $archiveName\n" );
+                       }
+               }
+               if ( !$file ) {
+                       $this->logger->debug( __METHOD__ . ': Bad file for ' . $importableRevision->getTitle() . "\n" );
+                       return $this->newNotOkStatus();
+               }
+
+               # Get the file source or download if necessary
+               $source = $importableRevision->getFileSrc();
+               $autoDeleteSource = $importableRevision->isTempSrc();
+               if ( !strlen( $source ) ) {
+                       $source = $this->downloadSource( $importableRevision );
+                       $autoDeleteSource = true;
+               }
+               if ( !strlen( $source ) ) {
+                       $this->logger->debug( __METHOD__ . ": Could not fetch remote file.\n" );
+                       return $this->newNotOkStatus();
+               }
+
+               $tmpFile = new TempFSFile( $source );
+               if ( $autoDeleteSource ) {
+                       $tmpFile->autocollect();
+               }
+
+               $sha1File = ltrim( sha1_file( $source ), '0' );
+               $sha1 = $importableRevision->getSha1();
+               if ( $sha1 && ( $sha1 !== $sha1File ) ) {
+                       $this->logger->debug( __METHOD__ . ": Corrupt file $source.\n" );
+                       return $this->newNotOkStatus();
+               }
+
+               $user = $importableRevision->getUserObj() ?: User::newFromName( $importableRevision->getUser() );
+
+               # Do the actual upload
+               if ( $archiveName ) {
+                       $status = $file->uploadOld( $source, $archiveName,
+                               $importableRevision->getTimestamp(), $importableRevision->getComment(), $user );
+               } else {
+                       $flags = 0;
+                       $status = $file->upload(
+                               $source,
+                               $importableRevision->getComment(),
+                               $importableRevision->getComment(),
+                               $flags,
+                               false,
+                               $importableRevision->getTimestamp(),
+                               $user
+                       );
+               }
+
+               if ( $status->isGood() ) {
+                       $this->logger->debug( __METHOD__ . ": Successful\n" );
+               } else {
+                       $this->logger->debug( __METHOD__ . ': failed: ' . $status->getHTML() . "\n" );
+               }
+
+               return $status;
+       }
+
+       /**
+        * @deprecated DO NOT CALL ME.
+        * This method was introduced when factoring UploadImporter out of WikiRevision.
+        * It only has 1 use by the deprecated downloadSource method in WikiRevision.
+        * Do not use this in new code.
+        *
+        * @param ImportableUploadRevision $wikiRevision
+        *
+        * @return bool|string
+        */
+       public function downloadSource( ImportableUploadRevision $wikiRevision ) {
+               if ( !$this->enableUploads ) {
+                       return false;
+               }
+
+               $tempo = tempnam( wfTempDir(), 'download' );
+               $f = fopen( $tempo, 'wb' );
+               if ( !$f ) {
+                       $this->logger->debug( "IMPORT: couldn't write to temp file $tempo\n" );
+                       return false;
+               }
+
+               // @todo FIXME!
+               $src = $wikiRevision->getSrc();
+               $data = Http::get( $src, [], __METHOD__ );
+               if ( !$data ) {
+                       $this->logger->debug( "IMPORT: couldn't fetch source $src\n" );
+                       fclose( $f );
+                       unlink( $tempo );
+                       return false;
+               }
+
+               fwrite( $f, $data );
+               fclose( $f );
+
+               return $tempo;
+       }
+
+}
diff --git a/includes/import/OldRevisionImporter.php b/includes/import/OldRevisionImporter.php
new file mode 100644 (file)
index 0000000..72af43b
--- /dev/null
@@ -0,0 +1,17 @@
+<?php
+
+/**
+ * @since 1.31
+ */
+interface OldRevisionImporter {
+
+       /**
+        * @since 1.31
+        *
+        * @param ImportableOldRevision $importableRevision
+        *
+        * @return bool Success
+        */
+       public function import( ImportableOldRevision $importableRevision );
+
+}
diff --git a/includes/import/UploadRevisionImporter.php b/includes/import/UploadRevisionImporter.php
new file mode 100644 (file)
index 0000000..966fc11
--- /dev/null
@@ -0,0 +1,18 @@
+<?php
+
+/**
+ * @since 1.31
+ */
+interface UploadRevisionImporter {
+
+       /**
+        * @since 1.31
+        *
+        * @param ImportableUploadRevision $importableUploadRevision
+        *
+        * @return StatusValue On success, the value member contains the
+        *     archive name, or an empty string if it was a new file.
+        */
+       public function import( ImportableUploadRevision $importableUploadRevision );
+
+}
index 3513f8c..55a7b2d 100644 (file)
@@ -23,6 +23,8 @@
  * @file
  * @ingroup SpecialPage
  */
+use MediaWiki\Logger\LoggerFactory;
+use MediaWiki\MediaWikiServices;
 
 /**
  * Represents a revision, log entry or upload during the import process.
@@ -32,7 +34,7 @@
  *
  * @ingroup SpecialPage
  */
-class WikiRevision {
+class WikiRevision implements ImportableUploadRevision, ImportableOldRevision {
 
        /**
         * @since 1.17
@@ -170,9 +172,9 @@ class WikiRevision {
 
        /**
         * @since 1.12.2
-        * @var mixed
+        * @var string|null
         */
-       protected $src;
+       protected $src = null;
 
        /**
         * @since 1.18
@@ -298,7 +300,7 @@ class WikiRevision {
 
        /**
         * @since 1.12.2
-        * @param mixed $src
+        * @param string|null $src
         */
        public function setSrc( $src ) {
                $this->src = $src;
@@ -494,7 +496,7 @@ class WikiRevision {
 
        /**
         * @since 1.12.2
-        * @return mixed
+        * @return string|null
         */
        public function getSrc() {
                return $this->src;
@@ -511,6 +513,17 @@ class WikiRevision {
                return false;
        }
 
+       /**
+        * @since 1.31
+        * @return bool|string
+        */
+       public function getSha1Base36() {
+               if ( $this->sha1base36 ) {
+                       return $this->sha1base36;
+               }
+               return false;
+       }
+
        /**
         * @since 1.17
         * @return string
@@ -577,106 +590,16 @@ class WikiRevision {
 
        /**
         * @since 1.4.1
+        * @deprecated in 1.31. Use OldRevisionImporter::import
         * @return bool
         */
        public function importOldRevision() {
-               $dbw = wfGetDB( DB_MASTER );
-
-               # Sneak a single revision into place
-               $user = $this->getUserObj() ?: User::newFromName( $this->getUser() );
-               if ( $user ) {
-                       $userId = intval( $user->getId() );
-                       $userText = $user->getName();
-               } else {
-                       $userId = 0;
-                       $userText = $this->getUser();
-                       $user = new User;
-               }
-
-               // avoid memory leak...?
-               Title::clearCaches();
-
-               $page = WikiPage::factory( $this->title );
-               $page->loadPageData( 'fromdbmaster' );
-               if ( !$page->exists() ) {
-                       // must create the page...
-                       $pageId = $page->insertOn( $dbw );
-                       $created = true;
-                       $oldcountable = null;
+               if ( $this->mNoUpdates ) {
+                       $importer = MediaWikiServices::getInstance()->getWikiRevisionOldRevisionImporterNoUpdates();
                } else {
-                       $pageId = $page->getId();
-                       $created = false;
-
-                       // Note: sha1 has been in XML dumps since 2012. If you have an
-                       // older dump, the duplicate detection here won't work.
-                       $prior = $dbw->selectField( 'revision', '1',
-                               [ 'rev_page' => $pageId,
-                                       'rev_timestamp' => $dbw->timestamp( $this->timestamp ),
-                                       'rev_sha1' => $this->sha1base36 ],
-                               __METHOD__
-                       );
-                       if ( $prior ) {
-                               // @todo FIXME: This could fail slightly for multiple matches :P
-                               wfDebug( __METHOD__ . ": skipping existing revision for [[" .
-                                       $this->title->getPrefixedText() . "]], timestamp " . $this->timestamp . "\n" );
-                               return false;
-                       }
+                       $importer = MediaWikiServices::getInstance()->getWikiRevisionOldRevisionImporter();
                }
-
-               if ( !$pageId ) {
-                       // This seems to happen if two clients simultaneously try to import the
-                       // same page
-                       wfDebug( __METHOD__ . ': got invalid $pageId when importing revision of [[' .
-                               $this->title->getPrefixedText() . ']], timestamp ' . $this->timestamp . "\n" );
-                       return false;
-               }
-
-               // Select previous version to make size diffs correct
-               // @todo This assumes that multiple revisions of the same page are imported
-               // in order from oldest to newest.
-               $prevId = $dbw->selectField( 'revision', 'rev_id',
-                       [
-                               'rev_page' => $pageId,
-                               'rev_timestamp <= ' . $dbw->addQuotes( $dbw->timestamp( $this->timestamp ) ),
-                       ],
-                       __METHOD__,
-                       [ 'ORDER BY' => [
-                                       'rev_timestamp DESC',
-                                       'rev_id DESC', // timestamp is not unique per page
-                               ]
-                       ]
-               );
-
-               # @todo FIXME: Use original rev_id optionally (better for backups)
-               # Insert the row
-               $revision = new Revision( [
-                       'title' => $this->title,
-                       'page' => $pageId,
-                       'content_model' => $this->getModel(),
-                       'content_format' => $this->getFormat(),
-                       // XXX: just set 'content' => $this->getContent()?
-                       'text' => $this->getContent()->serialize( $this->getFormat() ),
-                       'comment' => $this->getComment(),
-                       'user' => $userId,
-                       'user_text' => $userText,
-                       'timestamp' => $this->timestamp,
-                       'minor_edit' => $this->minor,
-                       'parent_id' => $prevId,
-                       ] );
-               $revision->insertOn( $dbw );
-               $changed = $page->updateIfNewerOn( $dbw, $revision );
-
-               if ( $changed !== false && !$this->mNoUpdates ) {
-                       wfDebug( __METHOD__ . ": running updates\n" );
-                       // countable/oldcountable stuff is handled in WikiImporter::finishImportPage
-                       $page->doEditUpdates(
-                               $revision,
-                               $user,
-                               [ 'created' => $created, 'oldcountable' => 'no-change' ]
-                       );
-               }
-
-               return true;
+               return $importer->import( $this );
        }
 
        /**
@@ -686,14 +609,7 @@ class WikiRevision {
        public function importLogItem() {
                $dbw = wfGetDB( DB_MASTER );
 
-               $user = $this->getUserObj() ?: User::newFromName( $this->getUser() );
-               if ( $user ) {
-                       $userId = intval( $user->getId() );
-                       $userText = $user->getName();
-               } else {
-                       $userId = 0;
-                       $userText = $this->getUser();
-               }
+               $user = $this->getUserObj() ?: User::newFromName( $this->getUser(), false );
 
                # @todo FIXME: This will not record autoblocks
                if ( !$this->getTitle() ) {
@@ -709,7 +625,6 @@ class WikiRevision {
                                'log_timestamp' => $dbw->timestamp( $this->timestamp ),
                                'log_namespace' => $this->getTitle()->getNamespace(),
                                'log_title' => $this->getTitle()->getDBkey(),
-                               # 'log_user_text' => $this->user_text,
                                'log_params' => $this->params ],
                        __METHOD__
                );
@@ -724,12 +639,11 @@ class WikiRevision {
                        'log_type' => $this->type,
                        'log_action' => $this->action,
                        'log_timestamp' => $dbw->timestamp( $this->timestamp ),
-                       'log_user' => $userId,
-                       'log_user_text' => $userText,
                        'log_namespace' => $this->getTitle()->getNamespace(),
                        'log_title' => $this->getTitle()->getDBkey(),
                        'log_params' => $this->params
-               ] + CommentStore::getStore()->insert( $dbw, 'log_comment', $this->getComment() );
+               ] + CommentStore::getStore()->insert( $dbw, 'log_comment', $this->getComment() )
+                       + ActorMigration::newMigration()->getInsertValues( $dbw, 'log_user', $user );
                $dbw->insert( 'logging', $data, __METHOD__ );
 
                return true;
@@ -737,106 +651,26 @@ class WikiRevision {
 
        /**
         * @since 1.12.2
+        * @deprecated in 1.31. Use UploadImporter::import
         * @return bool
         */
        public function importUpload() {
-               # Construct a file
-               $archiveName = $this->getArchiveName();
-               if ( $archiveName ) {
-                       wfDebug( __METHOD__ . "Importing archived file as $archiveName\n" );
-                       $file = OldLocalFile::newFromArchiveName( $this->getTitle(),
-                               RepoGroup::singleton()->getLocalRepo(), $archiveName );
-               } else {
-                       $file = wfLocalFile( $this->getTitle() );
-                       $file->load( File::READ_LATEST );
-                       wfDebug( __METHOD__ . 'Importing new file as ' . $file->getName() . "\n" );
-                       if ( $file->exists() && $file->getTimestamp() > $this->getTimestamp() ) {
-                               $archiveName = $file->getTimestamp() . '!' . $file->getName();
-                               $file = OldLocalFile::newFromArchiveName( $this->getTitle(),
-                                       RepoGroup::singleton()->getLocalRepo(), $archiveName );
-                               wfDebug( __METHOD__ . "File already exists; importing as $archiveName\n" );
-                       }
-               }
-               if ( !$file ) {
-                       wfDebug( __METHOD__ . ': Bad file for ' . $this->getTitle() . "\n" );
-                       return false;
-               }
-
-               # Get the file source or download if necessary
-               $source = $this->getFileSrc();
-               $autoDeleteSource = $this->isTempSrc();
-               if ( !strlen( $source ) ) {
-                       $source = $this->downloadSource();
-                       $autoDeleteSource = true;
-               }
-               if ( !strlen( $source ) ) {
-                       wfDebug( __METHOD__ . ": Could not fetch remote file.\n" );
-                       return false;
-               }
-
-               $tmpFile = new TempFSFile( $source );
-               if ( $autoDeleteSource ) {
-                       $tmpFile->autocollect();
-               }
-
-               $sha1File = ltrim( sha1_file( $source ), '0' );
-               $sha1 = $this->getSha1();
-               if ( $sha1 && ( $sha1 !== $sha1File ) ) {
-                       wfDebug( __METHOD__ . ": Corrupt file $source.\n" );
-                       return false;
-               }
-
-               $user = $this->getUserObj() ?: User::newFromName( $this->getUser() );
-
-               # Do the actual upload
-               if ( $archiveName ) {
-                       $status = $file->uploadOld( $source, $archiveName,
-                               $this->getTimestamp(), $this->getComment(), $user );
-               } else {
-                       $flags = 0;
-                       $status = $file->upload( $source, $this->getComment(), $this->getComment(),
-                               $flags, false, $this->getTimestamp(), $user );
-               }
-
-               if ( $status->isGood() ) {
-                       wfDebug( __METHOD__ . ": Successful\n" );
-                       return true;
-               } else {
-                       wfDebug( __METHOD__ . ': failed: ' . $status->getHTML() . "\n" );
-                       return false;
-               }
+               $importer = MediaWikiServices::getInstance()->getWikiRevisionUploadImporter();
+               $statusValue = $importer->import( $this );
+               return $statusValue->isGood();
        }
 
        /**
         * @since 1.12.2
+        * @deprecated in 1.31. Use UploadImporter::downloadSource
         * @return bool|string
         */
        public function downloadSource() {
-               if ( !$this->config->get( 'EnableUploads' ) ) {
-                       return false;
-               }
-
-               $tempo = tempnam( wfTempDir(), 'download' );
-               $f = fopen( $tempo, 'wb' );
-               if ( !$f ) {
-                       wfDebug( "IMPORT: couldn't write to temp file $tempo\n" );
-                       return false;
-               }
-
-               // @todo FIXME!
-               $src = $this->getSrc();
-               $data = Http::get( $src, [], __METHOD__ );
-               if ( !$data ) {
-                       wfDebug( "IMPORT: couldn't fetch source $src\n" );
-                       fclose( $f );
-                       unlink( $tempo );
-                       return false;
-               }
-
-               fwrite( $f, $data );
-               fclose( $f );
-
-               return $tempo;
+               $importer = new ImportableUploadRevisionImporter(
+                       $this->config->get( 'EnableUploads' ),
+                       LoggerFactory::getInstance( 'UploadRevisionImporter' )
+               );
+               return $importer->downloadSource( $this );
        }
 
 }
index 2083500..500bc5a 100644 (file)
@@ -1228,7 +1228,27 @@ abstract class DatabaseUpdater {
                        );
                        $task = $this->maintenance->runChild( MigrateComments::class, 'migrateComments.php' );
                        $task->execute();
-                       $this->output( "done.\n" );
+                       $this->output( $ok ? "done.\n" : "errors were encountered.\n" );
+               }
+       }
+
+       /**
+        * Migrate actors to the new 'actor' table
+        * @since 1.31
+        */
+       protected function migrateActors() {
+               global $wgActorTableSchemaMigrationStage;
+               if ( $wgActorTableSchemaMigrationStage >= MIGRATION_WRITE_NEW &&
+                       !$this->updateRowExists( 'MigrateActors' )
+               ) {
+                       $this->output(
+                               "Migrating actors to the 'actor' table, printing progress markers. For large\n" .
+                               "databases, you may want to hit Ctrl-C and do this manually with\n" .
+                               "maintenance/migrateActors.php.\n"
+                       );
+                       $task = $this->maintenance->runChild( 'MigrateActors', 'migrateActors.php' );
+                       $ok = $task->execute();
+                       $this->output( $ok ? "done.\n" : "errors were encountered.\n" );
                }
        }
 
index 1fd1d9b..38a9ede 100644 (file)
@@ -116,6 +116,8 @@ class MssqlUpdater extends DatabaseUpdater {
                        [ 'addTable', 'slot_roles', 'patch-slot_roles.sql' ],
                        [ 'addTable', 'content_models', 'patch-content_models.sql' ],
                        [ 'migrateArchiveText' ],
+                       [ 'addTable', 'actor', 'patch-actor-table.sql' ],
+                       [ 'migrateActors' ],
                ];
        }
 
index bce5405..350962f 100644 (file)
@@ -336,6 +336,8 @@ class MysqlUpdater extends DatabaseUpdater {
                        [ 'addTable', 'slot_roles', 'patch-slot_roles.sql' ],
                        [ 'addTable', 'content_models', 'patch-content_models.sql' ],
                        [ 'migrateArchiveText' ],
+                       [ 'addTable', 'actor', 'patch-actor-table.sql' ],
+                       [ 'migrateActors' ],
                ];
        }
 
index 3ee51ea..60ac23c 100644 (file)
@@ -137,6 +137,8 @@ class OracleUpdater extends DatabaseUpdater {
                        [ 'addTable', 'slot_roles', 'patch-slot_roles.sql' ],
                        [ 'addTable', 'content_models', 'patch-content_models.sql' ],
                        [ 'migrateArchiveText' ],
+                       [ 'addTable', 'actor', 'patch-actor-table.sql' ],
+                       [ 'migrateActors' ],
 
                        // KEEP THIS AT THE BOTTOM!!
                        [ 'doRebuildDuplicateFunction' ],
index 8d12404..2bfadf4 100644 (file)
@@ -491,6 +491,39 @@ class PostgresUpdater extends DatabaseUpdater {
                        [ 'addTable', 'content_models', 'patch-content_models-table.sql' ],
                        [ 'addTable', 'slot_roles', 'patch-slot_roles-table.sql' ],
                        [ 'migrateArchiveText' ],
+                       [ 'addTable', 'actor', 'patch-actor-table.sql' ],
+                       [ 'setDefault', 'revision', 'rev_user', 0 ],
+                       [ 'setDefault', 'revision', 'rev_user_text', '' ],
+                       [ 'setDefault', 'archive', 'ar_user', 0 ],
+                       [ 'changeNullableField', 'archive', 'ar_user', 'NOT NULL', true ],
+                       [ 'setDefault', 'archive', 'ar_user_text', '' ],
+                       [ 'addPgField', 'archive', 'ar_actor', 'INTEGER NOT NULL DEFAULT 0' ],
+                       [ 'addPgIndex', 'archive', 'archive_actor', '( ar_actor )' ],
+                       [ 'setDefault', 'ipblocks', 'ipb_by', 0 ],
+                       [ 'addPgField', 'ipblocks', 'ipb_by_actor', 'INTEGER NOT NULL DEFAULT 0' ],
+                       [ 'setDefault', 'image', 'img_user', 0 ],
+                       [ 'changeNullableField', 'image', 'img_user', 'NOT NULL', true ],
+                       [ 'setDefault', 'image', 'img_user_text', '' ],
+                       [ 'addPgField', 'image', 'img_actor', 'INTEGER NOT NULL DEFAULT 0' ],
+                       [ 'setDefault', 'oldimage', 'oi_user', 0 ],
+                       [ 'changeNullableField', 'oldimage', 'oi_user', 'NOT NULL', true ],
+                       [ 'setDefault', 'oldimage', 'oi_user_text', '' ],
+                       [ 'addPgField', 'oldimage', 'oi_actor', 'INTEGER NOT NULL DEFAULT 0' ],
+                       [ 'setDefault', 'filearchive', 'fa_user', 0 ],
+                       [ 'changeNullableField', 'filearchive', 'fa_user', 'NOT NULL', true ],
+                       [ 'setDefault', 'filearchive', 'fa_user_text', '' ],
+                       [ 'addPgField', 'filearchive', 'fa_actor', 'INTEGER NOT NULL DEFAULT 0' ],
+                       [ 'setDefault', 'recentchanges', 'rc_user', 0 ],
+                       [ 'changeNullableField', 'recentchanges', 'rc_user', 'NOT NULL', true ],
+                       [ 'setDefault', 'recentchanges', 'rc_user_text', '' ],
+                       [ 'addPgField', 'recentchanges', 'rc_actor', 'INTEGER NOT NULL DEFAULT 0' ],
+                       [ 'setDefault', 'logging', 'log_user', 0 ],
+                       [ 'changeNullableField', 'logging', 'log_user', 'NOT NULL', true ],
+                       [ 'addPgField', 'logging', 'log_actor', 'INTEGER NOT NULL DEFAULT 0' ],
+                       [ 'addPgIndex', 'logging', 'logging_actor_time_backwards', '( log_timestamp, log_actor )' ],
+                       [ 'addPgIndex', 'logging', 'logging_actor_type_time', '( log_actor, log_type, log_timestamp )' ],
+                       [ 'addPgIndex', 'logging', 'logging_actor_time', '( log_actor, log_timestamp )' ],
+                       [ 'migrateActors' ],
                ];
        }
 
index afb8b22..3a755b6 100644 (file)
@@ -200,6 +200,8 @@ class SqliteUpdater extends DatabaseUpdater {
                        [ 'addTable', 'slots', 'patch-slots.sql' ],
                        [ 'addTable', 'slot_roles', 'patch-slot_roles.sql' ],
                        [ 'migrateArchiveText' ],
+                       [ 'addTable', 'actor', 'patch-actor-table.sql' ],
+                       [ 'migrateActors' ],
                ];
        }
 
index 9030feb..44c673b 100644 (file)
@@ -65,7 +65,7 @@
        "config-apc": "[http://www.php.net/apc APC] е инсталиран",
        "config-apcu": "[http://www.php.net/apc APC] е инсталиран",
        "config-wincache": "[https://www.iis.net/download/WinCacheForPhp WinCache] е инсталиран",
-       "config-no-cache-apcu": "<strong>Внимание:</strong> [http://www.php.net/apcu APCu], [http://xcache.lighttpd.net/ XCache] и [http://www.iis.net/download/WinCacheForPhp WinCache] не могат да бъдат открити.\nКеширането на обекти не е активирано.",
+       "config-no-cache-apcu": "<strong>Внимание:</strong> [http://www.php.net/apcu APCu] и [http://www.iis.net/download/WinCacheForPhp WinCache] не могат да бъдат открити.\nКеширането на обекти не е активирано.",
        "config-mod-security": "<strong>Предупреждение:</strong> [https://modsecurity.org/ mod_security]/mod_security2 е включено на вашия уеб сървър. Много от обичайните му конфигурации пораждат проблеми с МедияУики и друг софтуер, който позволява публикуване на произволно съдържание.\nАко е възможно, моля изключете го. В противен случай се обърнете към [https://modsecurity.org/documentation/ документацията на mod_security] или се свържете с поддръжката на хостинга си, ако се сблъскате със случайни грешки.",
        "config-diff3-bad": "GNU diff3 не беше намерен.",
        "config-git": "Налична е системата за контрол на версиите Git: <code>$1</code>.",
        "config-cache-options": "Настройки за обектното кеширане:",
        "config-cache-help": "Обектното кеширане се използва за подобряване на скоростта на МедияУики чрез кеширане на често използваните данни.\nСилно препоръчително е на средните и големите сайтове да включат тази настройка, но малките също могат да се възползват от нея.",
        "config-cache-none": "Без кеширане (не се премахва от функционалността, но това влияе на скоростта на по-големи уикита)",
-       "config-cache-accel": "PHP обектно кеширане (APC, APCu, XCache или WinCache)",
+       "config-cache-accel": "PHP обектно кеширане (APC, APCu или WinCache)",
        "config-cache-memcached": "Използване на Memcached (изисква допълнителни настройки и конфигуриране)",
        "config-memcached-servers": "Memcached сървъри:",
        "config-memcached-help": "Списък с IP адреси за използване за Memcached.\nНеобходимо е да бъдат разделени по един на ред, както и да е посочен порта. Пример:\n127.0.0.1:11211\n192.168.1.25:1234",
index 05df002..c07bdc6 100644 (file)
@@ -80,7 +80,7 @@
        "config-env-bad": "El entorno ha sido comprobado.\nNo puedes instalar MediaWiki.",
        "config-env-php": "PHP $1 está instalado.",
        "config-env-hhvm": "HHVM $1 está instalado.",
-       "config-unicode-using-intl": "Usando la [https://pecl.php.net/intl extensión intl PECL] para la normalización Unicode.",
+       "config-unicode-using-intl": "Se utiliza la [https://pecl.php.net/intl extensión «intl» de PECL] para la normalización Unicode.",
        "config-unicode-pure-php-warning": "<strong>Advertencia:</strong> la [https://pecl.php.net/intl extensión intl] no está disponible para efectuar la normalización Unicode. Se utilizará la implementación más lenta en PHP puro.\nSi tu web tiene mucho tráfico, te recomendamos leer acerca de la [https://www.mediawiki.org/wiki/Special:MyLanguage/Unicode_normalization_considerations normalización Unicode].",
        "config-unicode-update-warning": "<strong>Atención:</strong> la versión instalada del contenedor de normalización de Unicode utiliza una versión anticuada de la biblioteca del [http://site.icu-project.org/ proyecto ICU].\nDeberías [https://www.mediawiki.org/wiki/Special:MyLanguage/Unicode_normalization_considerations modernizarla] si te interesa utilizar Unicode.",
        "config-no-db": "No se encontró un controlador adecuado para la base de datos. Necesitas instalar un controlador de base de datos para PHP.\n{{PLURAL:$2|Se admite el siguiente gestor de bases de datos|Se admiten los siguientes gestores de bases de datos}}: $1.\n\nSi compilaste PHP por tu cuenta, debes reconfigurarlo activando un cliente de base de datos, por ejemplo, mediante <code>./configure --with-mysqli</code>.\nSi instalaste PHP desde un paquete de Debian o Ubuntu, también debes instalar, por ejemplo, el paquete <code>php5-mysql</code>.",
index c1332b1..1b9a146 100644 (file)
@@ -24,7 +24,7 @@
                        "Pyscowicz"
                ]
        },
-       "config-desc": "MediaWiki-asennin",
+       "config-desc": "Asennin MediaWikiä varten",
        "config-title": "MediaWikin version $1 asennus",
        "config-information": "Tiedot",
        "config-localsettings-upgrade": "<code>LocalSettings.php</code>-tiedosto havaittiin.\nKirjoita muuttujan <code>$wgUpgradeKey</code> arvo alla olevaan kenttään päivittääksesi asennuksen.\nLöydät sen <code>LocalSettings.php</code>-tiedostosta.",
@@ -37,7 +37,7 @@
        "config-session-error": "Istunnon aloittaminen epäonnistui: $1",
        "config-session-expired": "Istuntotietosi näyttävät olevan vanhentuneita.\nIstuntojen elinajaksi on määritelty $1.\nVoit muuttaa tätä asetusta vaihtamalla kohtaa <code>session.gc_maxlifetime</code> php.ini-tiedostossa.\nKäynnistä asennusprosessi uudelleen.",
        "config-no-session": "Istuntosi tiedot menetettiin!\nTarkista php.ini-tiedostosi ja varmista, että <code>session.save_path</code> on asetettu sopivaan kansioon.",
-       "config-your-language": "Asennuksen kieli",
+       "config-your-language": "Kielesi:",
        "config-your-language-help": "Valitse kieli, jota haluat käyttää asennuksen ajan.",
        "config-wiki-language": "Wikin kieli",
        "config-wiki-language-help": "Valitse kieli, jota wikissä tullaan etupäässä käyttämään.",
        "config-no-cli-uploads-check": "<strong>Varoitus:</strong> Tiedostojen lähetyshakemistoa (<code>$1</code>) ei ole tarkistettu haavoittuvuuksien varalta komentoriviasennuksen aikana.",
        "config-brokenlibxml": "Järjestelmässäsi on käytössä PHP:n ja libxml2:n versioyhdistelmä, joka ei toimi kunnolla ja voi aiheuttaa tiedon vahingoittumista MediaWikissä ja muissa web-sovelluksissa.\nPäivitä libxml2 versioon 2.7.3 tai uudempaan ([https://bugs.php.net/bug.php?id=45996 bug filed with PHP]).\nAsennus keskeytetty.",
        "config-suhosin-max-value-length": "Suhosin on asennettu ja se rajoittaa GET-parametrin <code>length</code> $1 tavuun.\nMediaWikin ResourceLoader-komponentti pystyy toimimaan tämän kanssa, mutta ohjelmiston suorituskyky heikkenee.\nMikäli mahdollista, aseta muuttuja <code>suhosin.get.max_value_length</code> arvoon 1024 (tai suurempaan) tiedostossa <code>php.ini</code> ja aseta myös <code>$wgResourceLoaderMaxQueryLength</code> samaksi arvoksi tiedostossa <code>LocalSettings.php</code>.",
-       "config-db-type": "Tietokannan tyyppi",
-       "config-db-host": "Tietokantapalvelin",
+       "config-db-type": "Tietokannan tyyppi:",
+       "config-db-host": "Tietokantapalvelin:",
        "config-db-host-help": "Jos tietokantapalvelimesi sijaitsee eri palvelimella, syötä palvelimen nimi tai ip-osoite tähän.\n\nJos käytössäsi on ulkoinen palveluntarjoaja, pitäisi palvelimen nimen löytyä yrityksen ohjesivuilta.\n\nJos asennat MediaWikiä Windows-palvelimelle ja käytät MySQL:ää ei palvelimen nimi \"localhost\" välttämättä toimi. Tässä tapauksessa koita käyttää osoitetta 127.0.0.1.\n\nJos käytät PostgreSQL:ää jätä tämä kenttä tyhjäksi.",
        "config-db-host-oracle": "Tietokannan TNS:",
        "config-db-wiki-settings": "Identifioi tämä wiki",
-       "config-db-name": "Tietokannan nimi",
+       "config-db-name": "Tietokannan nimi:",
        "config-db-name-help": "Valitse wikiäsi kuvaava nimi.\nNimessä ei saa olla välilyöntejä.\n\nMikäli et pysty itse hallitsemaan tietokantojasi, pyydä palveluntarjoajaasi luomaan tietokanta tai tee se palveluntarjoajasi hallintapaneelissa.",
        "config-db-name-oracle": "Tietokannan rakenne:",
        "config-db-install-account": "Asennuksessa käytettävä käyttäjätili",
-       "config-db-username": "Tietokannan käyttäjätunnus",
-       "config-db-password": "Tietokannan salasana",
+       "config-db-username": "Tietokannan käyttäjätunnus:",
+       "config-db-password": "Tietokannan salasana:",
        "config-db-install-username": "Syötä käyttäjänimi jota käytetään muodostettaessa yhteys tietokantaan asennuksen aikana.\nTämä ei ole MediaWiki tilin käyttäjänimi; tämä on tietokannan käyttäjänimi.",
        "config-db-install-password": "Syötä salasana jota käytetään muodostettaessa yhteys tietokantaan asennuksen aikana.\nTämä ei ole MediaWiki tilin salasana; tämä on tietokannan salasana.",
        "config-db-install-help": "Anna käyttäjätunnus ja salasana, joita käytetään asennuksen aikana.",
        "config-mssql-web-auth": "Valitse varmennuksen tyyppi, jota verkkopalvelin käyttää yhdistäessään tietokantapalvelimeen wikin tavallisen toiminnan aikana.\nJos valitset \"{{int:config-mssql-windowsauth}}\", käytetään verkkopalvelimen käyttäjän kirjautumistietoja.",
        "config-mssql-sqlauth": "SQL Server varmennus",
        "config-mssql-windowsauth": "Windows-varmennus",
-       "config-site-name": "Wikin nimi",
+       "config-site-name": "Wikin nimi:",
        "config-site-name-help": "Tämä näkyy selaimen otsikkona ja muissa kohdissa.",
        "config-site-name-blank": "Kirjoita sivuston nimi.",
-       "config-project-namespace": "Projektinimiavaruus",
+       "config-project-namespace": "Projektinimiavaruus:",
        "config-ns-generic": "Projekti",
        "config-ns-site-name": "Sama kuin wikin nimi: $1",
        "config-ns-other": "Muu (määritä)",
        "config-ns-conflict": "Määritelty nimiavaruus \"<nowiki>$1</nowiki>\" on ristiriidassa MediaWikin oletusnimiavaruuksien kanssa.\nSyötä joku muu nimiavaruus.",
        "config-admin-box": "Ylläpitäjän tili",
        "config-admin-name": "Käyttäjänimesi:",
-       "config-admin-password": "Salasana",
-       "config-admin-password-confirm": "Salasana uudelleen",
+       "config-admin-password": "Salasana:",
+       "config-admin-password-confirm": "Salasana uudelleen:",
        "config-admin-help": "Syötä käyttäjänimi tähän, esimerkiksi \"Matti Meikäläinen\".\nTätä nimeä käytetään kirjauduttaessa wikiin.",
        "config-admin-name-blank": "Anna ylläpitäjän käyttäjänimi.",
        "config-admin-name-invalid": "Annettu nimi \"<nowiki>$1</nowiki>\" on virheellinen.\nSyötä toinen nimi.",
        "config-admin-password-blank": "Syötä ylläpitäjän salasana.",
        "config-admin-password-mismatch": "Antamasi salasanat eivät täsmää.",
-       "config-admin-email": "Sähköpostiosoite",
+       "config-admin-email": "Sähköpostiosoite:",
        "config-admin-email-help": "Syötä sähköpostiosoite johon vastaanotetaan viestit muilta wikin käyttäjiltä, nollataan salasana ja ilmoitetaan tarkkailulistalla olevista sivuista. Kenttä voidaan jättää myös tyhjäksi.",
        "config-admin-error-user": "Sisäinen virhe luodessa ylläpitäjää nimellä \"<nowiki>$1</nowiki>\".",
        "config-admin-error-password": "Sisäinen virhe asetettaessa salasanaa ylläpitäjälle \"<nowiki>$1</nowiki>\":\n<pre>$2</pre>",
        "config-upload-enable": "Ota käyttöön tiedostojen lataaminen",
        "config-upload-deleted": "Poistettujen tiedostojen hakemisto:",
        "config-upload-deleted-help": "Valitse hakemisto johon poistetut tiedostot arkistoidaan.\nHakemiston ei tulisi olla käytettävissä internetverkosta.",
-       "config-logo": "Logon URL-osoite",
+       "config-logo": "Logon URL-osoite:",
        "config-logo-help": "MediaWikin oletusulkoasussa on paikka 135x160 pikselin kokoiselle logolle sivupalkin yläpuolella.\nTallenna sopivan kokoinen kuva ja lisää URL tähän.\n\nVoit käyttää muuttujia <code>$wgStylePath</code> tai <code>$wgScriptPath</code>, jos logosi on määritelty suhteessa näihin polkuihin.\n\nJos et halua logoa, jätä tämä kenttä tyhjäksi.",
        "config-instantcommons": "Aktivoi Instant Commons",
        "config-instantcommons-help": "[https://www.mediawiki.org/wiki/InstantCommons Instant Commons] on ominaisuus, joka antaa wikien käyttää kuvia, ääniä ja muuta mediaa [https://commons.wikimedia.org/ Wikimedia Commons] -sivustolta.\nTehdäkseen tämän MediaWiki tarvitsee Internet-yhteyden.\n\nLisätietoja tästä ominaisuudesta, mukaan lukien ohjeet, kuinka sen voi asettaa muille wikeille kuin Wikimedia Commons, löytyy [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:$wgForeignFileRepos ohjeista].",
        "config-install-keys": "Muodostetaan salausavaimia",
        "config-insecure-keys": "<strong>Varoitus:</strong> Asennuksen aikana {{PLURAL:$2|luotu turva-avain|luodut turva-avaimet}} ($1) {{PLURAL:$2|ei|eivät}} ole täysin {{PLURAL:$2|turvallinen|turvallisia}}. Harkitse {{PLURAL:$2|sen|niiden}} muuttamista manuaalisesti.",
        "config-install-updates": "Estä tarpeettomien päivitysten asennus",
+       "config-install-updates-failed": "<strong>Virhe:</strong> Päivitysavainten lisääminen taulukoihin epäonnistui seuraavalla virheellä: $1",
        "config-install-sysop": "Luodaan ylläpitäjän tiliä",
        "config-install-subscribe-fail": "Liittyminen mediawiki-announce listalle epäonnistui: $1",
        "config-install-subscribe-notpossible": "cURL-ohjelmaa ei ole asennettu eikä <code>allow_url_fopen</code> ole saatavilla.",
        "config-install-mainpage-failed": "Etusivun lisääminen ei onnistunut: $1",
        "config-install-done": "<strong>Onnittelut!</strong>\nOlet asentanut MediaWikin.\n\nAsennusohjelma on luonut <code>LocalSettings.php</code> -tiedoston.\nSiinä on kaikki MediaWikin asetukset.\n\nLataa tiedosto ja laita se MediaWikin asennushakemistoon (sama kuin missä on index.php). Lataamisen olisi pitänyt alkaa automaattisesti.\n\nMikäli latausta ei tarjottu tai keskeytit latauksen, käynnistä se uudestaan tästä linkistä:\n\n$3\n\n<strong>Huom:</strong> Mikäli et nyt lataa tiedostoa, luotu tiedosto ei ole saatavissa myöhemmin, jos poistut asennuksesta lataamatta sitä.\n\nKun olet laittanut tiedoston oikeaan paikkaan, voit <strong>[$2 mennä wikiisi]</strong>.",
        "config-install-done-path": "<strong>Onnittelut!</strong>\nOlet asentanut MediaWikin.\n\nAsennusohjelma on luonut <code>LocalSettings.php</code> -tiedoston.\nSiinä on kaikki MediaWikin asetukset.\n\nLataa tiedosto ja laita se sijaintiin <code>$4</code>. Lataamisen olisi pitänyt alkaa automaattisesti.\n\nMikäli latausta ei tarjottu tai keskeytit latauksen, käynnistä se uudestaan tästä linkistä:\n\n$3\n\n<strong>Huom:</strong> Mikäli et nyt lataa tiedostoa, luotu tiedosto ei ole saatavissa myöhemmin, jos poistut asennuksesta lataamatta sitä.\n\nKun olet laittanut tiedoston oikeaan paikkaan, voit <strong>[$2 mennä wikiisi]</strong>.",
+       "config-install-success": "MediaWiki on asennettu onnistuneesti. Voit nyt vierailla <$1$2> katsellaksesi wikiäsi. Jos sinulla on kysyttävää, \ntutustu usein kysyttyjen kysymysten luetteloon: <https://www.mediawiki.org/wiki/Manual:FAQ> tai käytä yhtä sivulle linkitettyä tukifoorumia.",
        "config-download-localsettings": "Lataa <code>LocalSettings.php</code>",
        "config-help": "ohje",
        "config-help-tooltip": "Klikkaa laajentaaksesi",
index 9262dd0..42ccdc5 100644 (file)
@@ -22,7 +22,8 @@
                        "Foresttttttt",
                        "ネイ",
                        "Suchichi02",
-                       "Omotecho"
+                       "Omotecho",
+                       "Yusuke1109"
                ]
        },
        "config-desc": "MediaWiki のインストーラー",
@@ -81,7 +82,7 @@
        "config-apc": "[http://www.php.net/apc APC] がインストール済み",
        "config-apcu": "[http://www.php.net/apc APC] がインストール済みです。",
        "config-wincache": "[https://www.iis.net/download/WinCacheForPhp WinCache] がインストール済み",
-       "config-no-cache-apcu": "<strong>警告:</strong> [http://www.php.net/apcu APCu]、 [http://xcache.lighttpd.net/ XCache]、 [http://www.iis.net/download/WinCacheForPhp WinCache] のいずれも見つかりませんでした。\nオブジェクトのキャッシュは有効化されません。",
+       "config-no-cache-apcu": "<strong>警告:</strong> [http://www.php.net/apcu APCu]、 [http://www.iis.net/download/WinCacheForPhp WinCache] のいずれも見つかりませんでした。\nオブジェクトのキャッシュは有効化されません。",
        "config-mod-security": "<strong>警告:</strong> あなたのウェブサーバーでは [https://modsecurity.org/ mod_security] が有効になっています。正しく構成されていない場合は、MediaWiki や利用者にコンテンツの投稿を許可するその他のソフトウェアに問題が発生する場合があります。\n[https://modsecurity.org/documentation/ mod_security の説明文書]を確認するか、ランダムなエラーが発生した場合はあなたのホストのサポートにお問い合わせください。",
        "config-diff3-bad": "GNU diff3 が見つかりません。",
        "config-git": "バージョン管理ソフトウェア Git が見つかりました: <code>$1</code>",
@@ -97,6 +98,7 @@
        "config-no-cli-uploads-check": "<strong>警告:</strong> アップロード用のデフォルトディレクトリ (<code>$1</code>) が、CLIでのインストール中に任意のスクリプト実行の脆弱性チェックを受けていません。",
        "config-brokenlibxml": "このシステムで使われているPHPとlibxml2のバージョンのこの組み合わせにはバグがあります。具体的には、MediaWikiやその他のウェブアプリケーションでhiddenデータが破損する可能性があります。\nlibxml2を2.7.3以降のバージョンにアップグレードしてください([https://bugs.php.net/bug.php?id=45996 PHPでのバグ情報])。\nインストールを終了します。",
        "config-suhosin-max-value-length": "Suhosin がインストールされており、GET パラメーターの <code>length</code> を $1 バイトに制限しています。\nMediaWiki の ResourceLoader コンポーネントはこの制限を回避しますが、パフォーマンスは低下します。\n可能な限り、<code>php.ini</code> で <code>suhosin.get.max_value_length</code> を 1024 以上に設定し、同じ値を <code>LocalSettings.php</code> 内で <code>$wgResourceLoaderMaxQueryLength</code> に設定してください。",
+       "config-using-32bit": "<strong>警告:</strong>システムが32ビットで動作しているようです。 これは[https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:32-bit 非推奨]です。",
        "config-db-type": "データベースの種類:",
        "config-db-host": "データベースのホスト:",
        "config-db-host-help": "異なるサーバー上にデータベースサーバーがある場合、ホスト名またはIPアドレスをここに入力してください。\n\nもし、共有されたウェブホスティングを使用している場合、ホスティングプロバイダーは正確なホスト名を解説しているはずです。\n\nWindowsでMySQLを使用している場合に、「localhost」は、サーバー名としてはうまく働かないでしょう。もしそのような場合は、ローカルIPアドレスとして「127.0.0.1」を試してみてください。\n\nPostgreSQLを使用している場合、UNIXソケットで接続するにはこの欄を空欄のままにしてください。",
        "config-cache-options": "オブジェクトのキャッシュの設定:",
        "config-cache-help": "オブジェクトのキャッシュを使用すると、頻繁に使用するデータをキャッシュするため MediaWiki の動作速度を改善できます。\n中〜大規模サイトではこれを有効にすることを強くお勧めします。小規模サイトでも同様に効果があります。",
        "config-cache-none": "キャッシングしない(機能は取り払われます、しかもより大きなウィキサイト上でスピードの問題が発生します)",
-       "config-cache-accel": "PHPオブジェクトキャッシュ (APC、APCu、XCache、WinCache のいずれか)",
+       "config-cache-accel": "PHPオブジェクトキャッシュ (APC、APCu、WinCache のいずれか)",
        "config-cache-memcached": "memcached を使用 (追加の設定が必要)",
        "config-memcached-servers": "memcached サーバー:",
        "config-memcached-help": "Memcachedを使用するIPアドレスの一覧。\nカンマ区切りで、利用する特定のポートの指定が必要です。例:\n127.0.0.1:11211\n192.168.1.25:1234",
        "config-install-mainpage-failed": "メインページを挿入できませんでした: $1",
        "config-install-done": "<strong>おめでとうございます!</strong>\nMediaWikiのインストールに成功しました。\n\n<code>LocalSettings.php</code>ファイルが生成されました。\nこのファイルはすべての設定を含んでいます。\n\nこれをダウンロードして、ウィキをインストールした基準ディレクトリ (index.phpと同じディレクトリ) に設置する必要があります。ダウンロードは自動的に開始されるはずです。\n\nダウンロードが開始されていない場合、またはダウンロードをキャンセルした場合は、下記のリンクをクリックしてダウンロードを再開できます:\n\n$3\n\n<strong>注意:</strong> この生成された設定ファイルをダウンロードせずにインストールを終了すると、このファイルは利用できなくなります。\n\n上記の作業が完了すると、<strong>[$2 ウィキに入る]</strong>ことができます。",
        "config-install-done-path": "<strong>おめでとうございます!</strong>\nMediaWikiのインストールに成功しました。\n\n<code>LocalSettings.php</code>ファイルが生成されました。\nこのファイルはすべての設定を含んでいます。\n\nこれをダウンロードして、<code>$4</code> に設置する必要があります。ダウンロードは自動的に開始されるはずです。\n\nダウンロードが開始されていない場合、またはダウンロードをキャンセルした場合は、下記のリンクをクリックしてダウンロードを再開できます:\n\n$3\n\n<strong>注意:</strong> この生成された設定ファイルをダウンロードせずにインストールを終了すると、このファイルは利用できなくなります。\n\n上記の作業が完了すると、<strong>[$2 ウィキに入る]</strong>ことができます。",
+       "config-install-success": "MediaWikiが正常にインストールされました。\n今すぐ<$1$2>にアクセスしてあなたのwikiを表示できます。\nご質問がある場合は、よくある質問リストをご覧ください:\n<https://www.mediawiki.org/wiki/Manual:FAQ>または\nそのページにリンクされているサポートフォーラム",
        "config-download-localsettings": "<code>LocalSettings.php</code> をダウンロード",
        "config-help": "ヘルプ",
        "config-help-tooltip": "クリックで展開",
index b3aa137..953b672 100644 (file)
@@ -2,7 +2,8 @@
        "@metadata": {
                "authors": [
                        "Ianbu",
-                       "唐吉訶德的侍從"
+                       "唐吉訶德的侍從",
+                       "Yoxem"
                ]
        },
        "config-desc": "MediaWiki的安裝程式",
@@ -13,7 +14,7 @@
        "config-localsettings-key": "Seng-kip--ê bi̍t-bé:",
        "config-localsettings-badkey": "Lí phah--ê bi̍t-bé bô chèng-khak.",
        "config-upgrade-key-missing": "已經有一个MediaWiki矣。若要升級,請共下面這逝加去<code>LocalSettings.php</code>的下跤:\n\n$1",
-       "config-localsettings-incomplete": "這馬的<code>LocalSettings.php</code>可能無齊全,因為無設變量$1。請佇<code>LocalSettings.php</code>設彼个變量,並且揤「{{int:Config-continue}}」。",
+       "config-localsettings-incomplete": "Chit-má--ê <code>LocalSettings.php</code> khó-lêng bô chiâu-chn̂g, in-ūi bô siat piān-liōng $1. Chhiáⁿ tī <code>LocalSettings.php</code> siat hit-ê piān-liōng, pēng-chhiá chhi̍h \"{{int:Config-continue}}\".",
        "config-localsettings-connection-error": "An error was encountered when connecting to the database 用<code>LocalSettings.php</code>的設定去連接資料庫的時陣有一个錯誤發生,請改遮的設定了,才閣試。\n\n$1",
        "config-session-error": "連線開始了的錯誤:$1",
        "config-session-expired": "你連線資料已經過時矣,連線的使用期限是設做$1。你會使改共php.ini的<code>session.gc_maxlifetime</code>改較長,並且重新安裝動作。",
        "config-help-restart": "你敢欲共你拍的佮保存的資料攏清掉,並且重開始安裝的動作?",
        "config-restart": "是,重來",
        "config-welcome": "=== 環境檢測 ===\n這馬欲做基本的檢測,看環境是毋是適合裝 MediaWiki。\n若你愛有支援,才裝會起來,請共遮的資訊記起來。",
-       "config-copyright": "=== 版權聲明佮授權條款 ===\n\n$1\n\n本程式是自由軟體;你會當照自由軟體基金會所發表的 GNU 通用公共授權條款規定,共本程式重新發佈抑是修改;無論你是照本授權條款的第二版抑第二版以後的任何版本(你會當家己選) 。\n\n本程式發佈的目的是希望會當提供幫助,但是 <strong>無負任何擔保責任</strong>;抑無表示講對 <strong>販賣性</strong> 抑 <strong>特定用途的適用性</strong> 的情形擔保。詳情請參照 GNU 通用公共授權。\n\n你應該已隨本程式收著 <doclink href=\"Copying\">GNU 通用公共授權條款的副本</doclink>;若無,請寫批通知自由軟體基金會,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA,或 [https://www.gnu.org/copyleft/gpl.html 線頂看]。",
+       "config-copyright": "=== Pán-koân seng-bêng kap siū-koân tiâu-khoán ===\n\n$1\n\nPún têng-sek sī chū-iû nńg-thé; lí thang chiàu Chū-iû Nńg-thé Ki-kim-hoē só͘ hoat-piáu--ê GNU Thong-iōng Kong-kiōng Siū-koân Tiâu-khoán kui-tēng, kā pún têng-sek têng hoat-pò͘ iah-sī siu-kái; bô-lūn lí sī chiàu pún siū-koân tiâu-khoán--ê tē 2 pán iah koh khah sin--ê pán-pún (lí thang ka-kī kéng).\n\nPún têng-sek hoat-pò͘--ê bo̍k-tek sī ǹg-bāng ē-tàng pang-chān, m̄-koh <strong>bô hù jīm-hô tam-pó͘ chek-jīm</strong>; iah bô piáu-sī kóng tùi <strong>hoàn-bē-sèng</strong> iah <strong>te̍k-tēng iōng-tô͘--ê sek-iōng-sèng</strong>--ê chêng-hêng tam-pó͘. Siông-sè chhiáⁿ chham-khó GNU Thong-iōng Kong-kiōng Siū-koân.\n\nLí èng-kai tùi pún têng-sek siu-tio̍h <doclink href=\"Copying\">GNU Thong-iōng Kong-kiōng Siū-koân--ê Hù-pún</doclink>; nā-bô, chhiáⁿ siá-phoe thong-tī Chū-iû Nńg-thé KI-kim-hoē, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA, iah-sī [https://www.gnu.org/copyleft/gpl.html soàⁿ-téng khoàⁿ].",
        "config-sidebar": "* [www.mediawiki.org/wiki/MediaWiki/zh-hant MediaWiki 頭頁]\n* [www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents/zh 使用者指南]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents/zh 管理者指南]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ/zh-hant 四常問題集]\n----\n* <doclink href=Readme>讀我說明</doclink>\n* <doclink href=ReleaseNotes>發行說明</doclink>\n* <doclink href=Copying>版權聲明</doclink>\n* <doclink href=UpgradeDoc>升級</doclink>",
        "config-env-good": "環境檢查已完成。\n你會當安裝 MediaWiki。",
-       "config-env-bad": "環境檢查已完成。\n你無法度安裝 MediaWiki。",
+       "config-env-bad": "Khoân-kèng kiám-cha oân-sêng--ah.\nLí bô-hoat-tō͘ an-chng MediaWiki.",
        "config-env-php": "PHP $1 已經安裝。",
        "config-unicode-using-intl": "用 [https://pecl.php.net/intl intl PECL 擴充套件] 做 Unicode 正規化。",
-       "config-unicode-pure-php-warning": "<strong>警告:</strong> 無法度用 [https://pecl.php.net/intl intl PECL 擴充套件] 處理 Unicode 正規化,所以退回用純 PHP 實作的正規化程式,這種方式處理速度較慢。\n\n若你的網站瀏覽人數誠濟,你應該先看 [https://www.mediawiki.org/wiki/Special:MyLanguage/Unicode_normalization_considerations/zh Unicode 正規化]。",
+       "config-unicode-pure-php-warning": "<strong>Kéng-kò:</strong> Bô-hoat-tō͘ iōng [https://pecl.php.net/intl intl PECL extension] chhú-lí Unicode chèng-kui-hoà, só͘-í thè kàu iōng sûn PHP si̍t-chok--ê chèng-kui-hoà têng-sek, chit khoán hong-sek chhú-lí sok-tō͘ khah bān. Nā lí--ê bāng-chām liú-lám--ê lâng chiâⁿ chē, lí èng-kai ài khoàⁿ [https://www.mediawiki.org/wiki/Special:MyLanguage/Unicode_normalization_considerations/zh Unicode chèng-kui-hoà].",
        "config-unicode-update-warning": "<strong>警告</strong>:這馬安裝的 Unicode 正規化包裝程式用舊版 [http://site.icu-project.org/ ICU 計劃] 的程式庫。\n若你需要用 Unicode,你應該先進行 [https://www.mediawiki.org/wiki/Special:MyLanguage/Unicode_normalization_considerations 升級]。",
        "config-no-db": "揣無適合的資料庫驅動程式!你需要安裝 PHP 資料庫驅動程式。\n這馬支援下跤類型的資料庫: $1 。\n\n若你是家己編譯 PHP,你需要重新設定並且開資料庫客戶端,譬如:用 <code>./configure --with-mysqli</code> 指令參數。\n如你是用 Debian 或 Ubuntu 的套件安裝,你著需要閣另外安裝,例:<code>php5-mysql</code> 套件。",
-       "config-outdated-sqlite": "<strong>警告:</strong>你已經安裝 SQLite $1,毋閣伊的版本比會當裝的版本 $2閣較舊。所以你無法度用 SQLite。",
-       "config-no-fts3": "<strong>警告:</strong> SQLite 佇編譯的時陣無包括 [//sqlite.org/fts3.html FTS3 模組],後台搜揣功能就會無法度用。",
+       "config-outdated-sqlite": "<strong>Kéng-kò:</strong> Lí í-keng an-chng SQLite $1, m̄-koh i--ê pán-pún pí thang-chng--ê pán-pún $2 khah kū. Só͘-í lí bô-hoat-tō͘ ēng SQLite.",
+       "config-no-fts3": "<strong>Kéng-kò: </strong> SQLite tī pian-e̍k--ê sî-chūn bô pau-koat  [//sqlite.org/fts3.html FTS3 module], āu-tâi chhiau-chhoē kong-lêng tiō ē bô-hoat-tō͘ iōng.",
        "mainpagetext": "'''MediaWiki已經裝好矣。'''",
        "mainpagedocfooter": "請查看[https://meta.wikimedia.org/wiki/Help:Contents 用者說明書]的資料通使用wiki 軟體\n\n== 入門 ==\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings 配置的設定]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ MediaWiki時常問答]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce MediaWiki的公布列單]"
 }
index d97e4f9..77daca7 100644 (file)
@@ -158,11 +158,15 @@ class RecentChangesUpdateJob extends Job {
                                $eTimestamp = min( $sTimestamp + $window, $nowUnix );
 
                                // Get all the users active since the last update
+                               $actorQuery = ActorMigration::newMigration()->getJoin( 'rc_user' );
                                $res = $dbw->select(
-                                       [ 'recentchanges' ],
-                                       [ 'rc_user_text', 'lastedittime' => 'MAX(rc_timestamp)' ],
+                                       [ 'recentchanges' ] + $actorQuery['tables'],
                                        [
-                                               'rc_user > 0', // actual accounts
+                                               'rc_user_text' => $actorQuery['fields']['rc_user_text'],
+                                               'lastedittime' => 'MAX(rc_timestamp)'
+                                       ],
+                                       [
+                                               $actorQuery['fields']['rc_user'] . ' > 0', // actual accounts
                                                'rc_type != ' . $dbw->addQuotes( RC_EXTERNAL ), // no wikidata
                                                'rc_log_type IS NULL OR rc_log_type != ' . $dbw->addQuotes( 'newusers' ),
                                                'rc_timestamp >= ' . $dbw->addQuotes( $dbw->timestamp( $sTimestamp ) ),
@@ -172,7 +176,8 @@ class RecentChangesUpdateJob extends Job {
                                        [
                                                'GROUP BY' => [ 'rc_user_text' ],
                                                'ORDER BY' => 'NULL' // avoid filesort
-                                       ]
+                                       ],
+                                       $actorQuery['joins']
                                );
                                $names = [];
                                foreach ( $res as $row ) {
index f2c7ed2..3d1c8b8 100644 (file)
@@ -535,8 +535,8 @@ class CSSMin {
        public static function minify( $css ) {
                return trim(
                        str_replace(
-                               [ '; ', ': ', ' {', '{ ', ', ', '} ', ';}' ],
-                               [ ';', ':', '{', '{', ',', '}', '}' ],
+                               [ '; ', ': ', ' {', '{ ', ', ', '} ', ';}', '( ', ' )', '[ ', ' ]' ],
+                               [ ';', ':', '{', '{', ',', '}', '}', '(', ')', '[', ']' ],
                                preg_replace( [ '/\s+/', '/\/\*.*?\*\//s' ], [ ' ', '' ], $css )
                        )
                );
index bcd95c1..17f596d 100644 (file)
@@ -517,18 +517,18 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
                        // Case B: any long-running transaction; ignore this set()
                        } elseif ( $age > self::MAX_READ_LAG ) {
                                $this->logger->info( 'Rejected set() for {cachekey} due to snapshot lag.',
-                                       [ 'cachekey' => $key ] );
+                                       [ 'cachekey' => $key, 'lag' => $lag, 'age' => $age ] );
 
                                return true; // no-op the write for being unsafe
                        // Case C: high replication lag; lower TTL instead of ignoring all set()s
                        } elseif ( $lag === false || $lag > self::MAX_READ_LAG ) {
                                $ttl = $ttl ? min( $ttl, self::TTL_LAGGED ) : self::TTL_LAGGED;
                                $this->logger->warning( 'Lowered set() TTL for {cachekey} due to replication lag.',
-                                       [ 'cachekey' => $key ] );
+                                       [ 'cachekey' => $key, 'lag' => $lag, 'age' => $age ] );
                        // Case D: medium length request with medium replication lag; ignore this set()
                        } else {
                                $this->logger->info( 'Rejected set() for {cachekey} due to high read lag.',
-                                       [ 'cachekey' => $key ] );
+                                       [ 'cachekey' => $key, 'lag' => $lag, 'age' => $age ] );
 
                                return true; // no-op the write for being unsafe
                        }
index 4fcd885..e115286 100644 (file)
@@ -75,7 +75,7 @@ class ChronologyProtector implements LoggerAwareInterface {
        public function __construct( BagOStuff $store, array $client, $posIndex = null ) {
                $this->store = $store;
                $this->clientId = md5( $client['ip'] . "\n" . $client['agent'] );
-               $this->key = $store->makeGlobalKey( __CLASS__, $this->clientId, 'v1' );
+               $this->key = $store->makeGlobalKey( __CLASS__, $this->clientId, 'v2' );
                $this->waitForPosIndex = $posIndex;
                $this->logger = new NullLogger();
        }
@@ -124,7 +124,7 @@ class ChronologyProtector implements LoggerAwareInterface {
                        $this->startupPositions[$masterName] instanceof DBMasterPos
                ) {
                        $pos = $this->startupPositions[$masterName];
-                       $this->logger->info( __METHOD__ . ": LB for '$masterName' set to pos $pos\n" );
+                       $this->logger->debug( __METHOD__ . ": LB for '$masterName' set to pos $pos\n" );
                        $lb->waitFor( $pos );
                }
        }
@@ -148,11 +148,11 @@ class ChronologyProtector implements LoggerAwareInterface {
                if ( $lb->getServerCount() > 1 ) {
                        $pos = $lb->getMasterPos();
                        if ( $pos ) {
-                               $this->logger->info( __METHOD__ . ": LB for '$masterName' has pos $pos\n" );
+                               $this->logger->debug( __METHOD__ . ": LB for '$masterName' has pos $pos\n" );
                                $this->shutdownPositions[$masterName] = $pos;
                        }
                } else {
-                       $this->logger->info( __METHOD__ . ": DB '$masterName' touched\n" );
+                       $this->logger->debug( __METHOD__ . ": DB '$masterName' touched\n" );
                }
                $this->shutdownTouchDBs[$masterName] = 1;
        }
@@ -186,7 +186,7 @@ class ChronologyProtector implements LoggerAwareInterface {
                        return []; // nothing to save
                }
 
-               $this->logger->info( __METHOD__ . ": saving master pos for " .
+               $this->logger->debug( __METHOD__ . ": saving master pos for " .
                        implode( ', ', array_keys( $this->shutdownPositions ) ) . "\n"
                );
 
@@ -299,10 +299,10 @@ class ChronologyProtector implements LoggerAwareInterface {
                        }
 
                        $this->startupPositions = $data ? $data['positions'] : [];
-                       $this->logger->info( __METHOD__ . ": key is {$this->key} (read)\n" );
+                       $this->logger->debug( __METHOD__ . ": key is {$this->key} (read)\n" );
                } else {
                        $this->startupPositions = [];
-                       $this->logger->info( __METHOD__ . ": key is {$this->key} (unread)\n" );
+                       $this->logger->debug( __METHOD__ . ": key is {$this->key} (unread)\n" );
                }
        }
 
index 9a8996c..572a798 100644 (file)
@@ -2244,37 +2244,51 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                        $rows = [ $rows ];
                }
 
-               $affectedRowCount = 0;
-               foreach ( $rows as $row ) {
-                       // Delete rows which collide with this one
-                       $indexWhereClauses = [];
-                       foreach ( $uniqueIndexes as $index ) {
-                               $indexColumns = (array)$index;
-                               $indexRowValues = array_intersect_key( $row, array_flip( $indexColumns ) );
-                               if ( count( $indexRowValues ) != count( $indexColumns ) ) {
-                                       throw new DBUnexpectedError(
-                                               $this,
-                                               'New record does not provide all values for unique key (' .
+               $useTrx = !$this->trxLevel;
+               if ( $useTrx ) {
+                       $this->begin( $fname, self::TRANSACTION_INTERNAL );
+               }
+               try {
+                       $affectedRowCount = 0;
+                       foreach ( $rows as $row ) {
+                               // Delete rows which collide with this one
+                               $indexWhereClauses = [];
+                               foreach ( $uniqueIndexes as $index ) {
+                                       $indexColumns = (array)$index;
+                                       $indexRowValues = array_intersect_key( $row, array_flip( $indexColumns ) );
+                                       if ( count( $indexRowValues ) != count( $indexColumns ) ) {
+                                               throw new DBUnexpectedError(
+                                                       $this,
+                                                       'New record does not provide all values for unique key (' .
                                                        implode( ', ', $indexColumns ) . ')'
-                                       );
-                               } elseif ( in_array( null, $indexRowValues, true ) ) {
-                                       throw new DBUnexpectedError(
-                                               $this,
-                                               'New record has a null value for unique key (' .
+                                               );
+                                       } elseif ( in_array( null, $indexRowValues, true ) ) {
+                                               throw new DBUnexpectedError(
+                                                       $this,
+                                                       'New record has a null value for unique key (' .
                                                        implode( ', ', $indexColumns ) . ')'
-                                       );
+                                               );
+                                       }
+                                       $indexWhereClauses[] = $this->makeList( $indexRowValues, LIST_AND );
                                }
-                               $indexWhereClauses[] = $this->makeList( $indexRowValues, LIST_AND );
-                       }
 
-                       if ( $indexWhereClauses ) {
-                               $this->delete( $table, $this->makeList( $indexWhereClauses, LIST_OR ), $fname );
+                               if ( $indexWhereClauses ) {
+                                       $this->delete( $table, $this->makeList( $indexWhereClauses, LIST_OR ), $fname );
+                                       $affectedRowCount += $this->affectedRows();
+                               }
+
+                               // Now insert the row
+                               $this->insert( $table, $row, $fname );
                                $affectedRowCount += $this->affectedRows();
                        }
-
-                       // Now insert the row
-                       $this->insert( $table, $row, $fname );
-                       $affectedRowCount += $this->affectedRows();
+               } catch ( Exception $e ) {
+                       if ( $useTrx ) {
+                               $this->rollback( $fname, self::FLUSHING_INTERNAL );
+                       }
+                       throw $e;
+               }
+               if ( $useTrx ) {
+                       $this->commit( $fname, self::FLUSHING_INTERNAL );
                }
 
                $this->affectedRowCount = $affectedRowCount;
index d59bee3..2922bce 100644 (file)
@@ -962,11 +962,11 @@ interface IDatabase {
         * Example usage:
         * @code
         *     $sql = $db->makeList( [
-        *         'rev_user' => $id,
+        *         'rev_page' => $id,
         *         $db->makeList( [ 'rev_minor' => 1, 'rev_len' < 500 ], $db::LIST_OR ] )
         *     ], $db::LIST_AND );
         * @endcode
-        * This would set $sql to "rev_user = '$id' AND (rev_minor = '1' OR rev_len < '500')"
+        * This would set $sql to "rev_page = '$id' AND (rev_minor = '1' OR rev_len < '500')"
         *
         * @param array $a Containing the data
         * @param int $mode IDatabase class constant:
index 66012da..d0c398e 100644 (file)
@@ -62,8 +62,8 @@ interface IMaintainableDatabase extends IDatabase {
         * This is handy when you need to construct SQL for joins
         *
         * Example:
-        * extract( $dbr->tableNames( 'user', 'watchlist' ) );
-        * $sql = "SELECT wl_namespace,wl_title FROM $watchlist,$user
+        * list( $user, $watchlist ) = $dbr->tableNames( 'user', 'watchlist' ) );
+        * $sql = "SELECT wl_namespace, wl_title FROM $watchlist, $user
         *         WHERE wl_user=user_id AND wl_user=$nameWithQuotes";
         *
         * @return array
index 2f79ea9..28d2a1b 100644 (file)
@@ -2,12 +2,14 @@
 
 namespace Wikimedia\Rdbms;
 
+use Serializable;
+
 /**
  * An object representing a master or replica DB position in a replicated setup.
  *
  * The implementation details of this opaque type are up to the database subclass.
  */
-interface DBMasterPos {
+interface DBMasterPos extends Serializable {
        /**
         * @return float UNIX timestamp
         * @since 1.25
index 2ee9068..cdcb79c 100644 (file)
@@ -3,6 +3,7 @@
 namespace Wikimedia\Rdbms;
 
 use InvalidArgumentException;
+use UnexpectedValueException;
 
 /**
  * DBMasterPos class for MySQL/MariaDB
@@ -27,6 +28,14 @@ class MySQLMasterPos implements DBMasterPos {
         * @param float $asOfTime UNIX timestamp
         */
        public function __construct( $position, $asOfTime ) {
+               $this->init( $position, $asOfTime );
+       }
+
+       /**
+        * @param string $position
+        * @param float $asOfTime
+        */
+       protected function init( $position, $asOfTime ) {
                $m = [];
                if ( preg_match( '!^(.+)\.(\d+)/(\d+)$!', $position, $m ) ) {
                        $this->binlog = $m[1]; // ideally something like host name
@@ -34,7 +43,7 @@ class MySQLMasterPos implements DBMasterPos {
                } else {
                        $gtids = array_filter( array_map( 'trim', explode( ',', $position ) ) );
                        foreach ( $gtids as $gtid ) {
-                               if ( !$this->parseGTID( $gtid ) ) {
+                               if ( !self::parseGTID( $gtid ) ) {
                                        throw new InvalidArgumentException( "Invalid GTID '$gtid'." );
                                }
                                $this->gtids[] = $gtid;
@@ -192,4 +201,17 @@ class MySQLMasterPos implements DBMasterPos {
                        ? [ 'binlog' => $this->binlog, 'pos' => $this->pos ]
                        : false;
        }
+
+       public function serialize() {
+               return serialize( [ 'position' => $this->__toString(), 'asOfTime' => $this->asOfTime ] );
+       }
+
+       public function unserialize( $serialized ) {
+               $data = unserialize( $serialized );
+               if ( !is_array( $data ) ) {
+                       throw new UnexpectedValueException( __METHOD__ . ": cannot unserialize position" );
+               }
+
+               $this->init( $data['position'], $data['asOfTime'] );
+       }
 }
index 088c3f2..7bbb530 100644 (file)
@@ -199,7 +199,7 @@ interface ILoadBalancer {
         * @param IDatabase $conn
         * @throws InvalidArgumentException
         */
-       public function reuseConnection( $conn );
+       public function reuseConnection( IDatabase $conn );
 
        /**
         * Get a database connection handle reference
index ccdb48e..569ea0e 100644 (file)
@@ -298,11 +298,13 @@ class LoadBalancer implements ILoadBalancer {
                                $host = $this->getServerName( $i );
                                if ( $lag === false && !is_infinite( $maxServerLag ) ) {
                                        $this->replLogger->error(
-                                               "Server {host} is not replicating?", [ 'host' => $host ] );
+                                               __METHOD__ .
+                                               ": server {host} is not replicating?", [ 'host' => $host ] );
                                        unset( $loads[$i] );
                                } elseif ( $lag > $maxServerLag ) {
                                        $this->replLogger->info(
-                                               "Server {host} has {lag} seconds of lag (>= {maxlag})",
+                                               __METHOD__ .
+                                               ": server {host} has {lag} seconds of lag (>= {maxlag})",
                                                [ 'host' => $host, 'lag' => $lag, 'maxlag' => $maxServerLag ]
                                        );
                                        unset( $loads[$i] );
@@ -426,7 +428,8 @@ class LoadBalancer implements ILoadBalancer {
                                }
                                if ( $i === false && count( $currentLoads ) != 0 ) {
                                        // All replica DBs lagged. Switch to read-only mode
-                                       $this->replLogger->error( "All replica DBs lagged. Switch to read-only mode" );
+                                       $this->replLogger->error(
+                                               __METHOD__ . ": all replica DBs lagged. Switch to read-only mode" );
                                        $i = ArrayUtils::pickRandom( $currentLoads );
                                        $laggedReplicaMode = true;
                                }
@@ -464,7 +467,7 @@ class LoadBalancer implements ILoadBalancer {
 
                // If all servers were down, quit now
                if ( !count( $currentLoads ) ) {
-                       $this->connLogger->error( "All servers down" );
+                       $this->connLogger->error( __METHOD__ . ": all servers down" );
                }
 
                return [ $i, $laggedReplicaMode ];
@@ -627,7 +630,7 @@ class LoadBalancer implements ILoadBalancer {
 
                $this->replLogger->info(
                        __METHOD__ .
-                       ': Waiting for replica DB {dbserver} to catch up...',
+                       ': waiting for replica DB {dbserver} to catch up...',
                        [ 'dbserver' => $server ]
                );
 
@@ -654,7 +657,7 @@ class LoadBalancer implements ILoadBalancer {
                        );
                        $ok = false;
                } else {
-                       $this->replLogger->info( __METHOD__ . ": Done" );
+                       $this->replLogger->debug( __METHOD__ . ": done waiting" );
                        $ok = true;
                        // Remember that the DB reached this point
                        $this->srvCache->set( $key, $this->waitForPos, BagOStuff::TTL_DAY );
@@ -736,7 +739,7 @@ class LoadBalancer implements ILoadBalancer {
                return $conn;
        }
 
-       public function reuseConnection( $conn ) {
+       public function reuseConnection( IDatabase $conn ) {
                $serverIndex = $conn->getLBInfo( 'serverIndex' );
                $refCount = $conn->getLBInfo( 'foreignPoolRefCount' );
                if ( $serverIndex === null || $refCount === null ) {
@@ -754,7 +757,8 @@ class LoadBalancer implements ILoadBalancer {
                } elseif ( $conn instanceof DBConnRef ) {
                        // DBConnRef already handles calling reuseConnection() and only passes the live
                        // Database instance to this method. Any caller passing in a DBConnRef is broken.
-                       $this->connLogger->error( __METHOD__ . ": got DBConnRef instance.\n" .
+                       $this->connLogger->error(
+                               __METHOD__ . ": got DBConnRef instance.\n" .
                                ( new RuntimeException() )->getTraceAsString() );
 
                        return;
@@ -855,10 +859,12 @@ class LoadBalancer implements ILoadBalancer {
                                $conn = $this->reallyOpenConnection( $server, $this->localDomain );
                                $host = $this->getServerName( $i );
                                if ( $conn->isOpen() ) {
-                                       $this->connLogger->debug( "Connected to database $i at '$host'." );
+                                       $this->connLogger->debug(
+                                               __METHOD__ . ": connected to database $i at '$host'." );
                                        $this->conns[$connKey][$i][0] = $conn;
                                } else {
-                                       $this->connLogger->warning( "Failed to connect to database $i at '$host'." );
+                                       $this->connLogger->warning(
+                                               __METHOD__ . ": failed to connect to database $i at '$host'." );
                                        $this->errorConnection = $conn;
                                        $conn = false;
                                }
@@ -1085,7 +1091,7 @@ class LoadBalancer implements ILoadBalancer {
                if ( $conn instanceof IDatabase ) {
                        $context['db_server'] = $conn->getServer();
                        $this->connLogger->warning(
-                               "Connection error: {last_error} ({db_server})",
+                               __METHOD__ . ": connection error: {last_error} ({db_server})",
                                $context
                        );
 
@@ -1094,7 +1100,8 @@ class LoadBalancer implements ILoadBalancer {
                } else {
                        // No last connection, probably due to all servers being too busy
                        $this->connLogger->error(
-                               "LB failure with no last connection. Connection error: {last_error}",
+                               __METHOD__ .
+                               ": LB failure with no last connection. Connection error: {last_error}",
                                $context
                        );
 
@@ -1162,7 +1169,8 @@ class LoadBalancer implements ILoadBalancer {
        public function closeAll() {
                $this->forEachOpenConnection( function ( IDatabase $conn ) {
                        $host = $conn->getServer();
-                       $this->connLogger->debug( "Closing connection to database '$host'." );
+                       $this->connLogger->debug(
+                               __METHOD__ . ": closing connection to database '$host'." );
                        $conn->close();
                } );
 
@@ -1187,7 +1195,8 @@ class LoadBalancer implements ILoadBalancer {
                        foreach ( $connsByServer[$serverIndex] as $i => $trackedConn ) {
                                if ( $conn === $trackedConn ) {
                                        $host = $this->getServerName( $i );
-                                       $this->connLogger->debug( "Closing connection to database $i at '$host'." );
+                                       $this->connLogger->debug(
+                                               __METHOD__ . ": closing connection to database $i at '$host'." );
                                        unset( $this->conns[$type][$serverIndex][$i] );
                                        --$this->connsOpened;
                                        break 2;
@@ -1683,7 +1692,7 @@ class LoadBalancer implements ILoadBalancer {
                if ( $pos instanceof DBMasterPos ) {
                        $result = $conn->masterPosWait( $pos, $timeout );
                        if ( $result == -1 || is_null( $result ) ) {
-                               $msg = __METHOD__ . ': Timed out waiting on {host} pos {pos}';
+                               $msg = __METHOD__ . ': timed out waiting on {host} pos {pos}';
                                $this->replLogger->warning( $msg, [
                                        'host' => $conn->getServer(),
                                        'pos' => $pos,
@@ -1691,7 +1700,7 @@ class LoadBalancer implements ILoadBalancer {
                                ] );
                                $ok = false;
                        } else {
-                               $this->replLogger->info( __METHOD__ . ': Done' );
+                               $this->replLogger->debug( __METHOD__ . ': done waiting' );
                                $ok = true;
                        }
                } else {
index 74c7765..50c878d 100644 (file)
@@ -81,13 +81,13 @@ class LoadMonitor implements ILoadMonitor {
                $this->replLogger = $logger;
        }
 
-       public function scaleLoads( array &$weightByServer, $domain ) {
+       final public function scaleLoads( array &$weightByServer, $domain ) {
                $serverIndexes = array_keys( $weightByServer );
                $states = $this->getServerStates( $serverIndexes, $domain );
-               $coefficientsByServer = $states['weightScales'];
+               $newScalesByServer = $states['weightScales'];
                foreach ( $weightByServer as $i => $weight ) {
-                       if ( isset( $coefficientsByServer[$i] ) ) {
-                               $weightByServer[$i] = $weight * $coefficientsByServer[$i];
+                       if ( isset( $newScalesByServer[$i] ) ) {
+                               $weightByServer[$i] = $weight * $newScalesByServer[$i];
                        } else { // server recently added to config?
                                $host = $this->parent->getServerName( $i );
                                $this->replLogger->error( __METHOD__ . ": host $host not in cache" );
@@ -95,10 +95,8 @@ class LoadMonitor implements ILoadMonitor {
                }
        }
 
-       public function getLagTimes( array $serverIndexes, $domain ) {
-               $states = $this->getServerStates( $serverIndexes, $domain );
-
-               return $states['lagTimes'];
+       final public function getLagTimes( array $serverIndexes, $domain ) {
+               return $this->getServerStates( $serverIndexes, $domain )['lagTimes'];
        }
 
        protected function getServerStates( array $serverIndexes, $domain ) {
index 35502c7..395110b 100644 (file)
@@ -171,20 +171,22 @@ class DatabaseLogEntry extends LogEntryBase {
         */
        public static function getSelectQueryData() {
                $commentQuery = CommentStore::getStore()->getJoin( 'log_comment' );
+               $actorQuery = ActorMigration::newMigration()->getJoin( 'log_user' );
 
-               $tables = [ 'logging', 'user' ] + $commentQuery['tables'];
+               $tables = array_merge(
+                       [ 'logging' ], $commentQuery['tables'], $actorQuery['tables'], [ 'user' ]
+               );
                $fields = [
                        'log_id', 'log_type', 'log_action', 'log_timestamp',
-                       'log_user', 'log_user_text',
                        'log_namespace', 'log_title', // unused log_page
                        'log_params', 'log_deleted',
                        'user_id', 'user_name', 'user_editcount',
-               ] + $commentQuery['fields'];
+               ] + $commentQuery['fields'] + $actorQuery['fields'];
 
                $joins = [
                        // IPs don't have an entry in user table
-                       'user' => [ 'LEFT JOIN', 'log_user=user_id' ],
-               ] + $commentQuery['joins'];
+                       'user' => [ 'LEFT JOIN', 'user_id=' . $actorQuery['fields']['log_user'] ],
+               ] + $commentQuery['joins'] + $actorQuery['joins'];
 
                return [
                        'tables' => $tables,
@@ -293,11 +295,14 @@ class DatabaseLogEntry extends LogEntryBase {
 
        public function getPerformer() {
                if ( !$this->performer ) {
+                       $actorId = isset( $this->row->log_actor ) ? (int)$this->row->log_actor : 0;
                        $userId = (int)$this->row->log_user;
-                       if ( $userId !== 0 ) {
+                       if ( $userId !== 0 || $actorId !== 0 ) {
                                // logged-in users
                                if ( isset( $this->row->user_name ) ) {
                                        $this->performer = User::newFromRow( $this->row );
+                               } elseif ( $actorId !== 0 ) {
+                                       $this->performer = User::newFromActorId( $actorId );
                                } else {
                                        $this->performer = User::newFromId( $userId );
                                }
@@ -356,8 +361,11 @@ class RCDatabaseLogEntry extends DatabaseLogEntry {
 
        public function getPerformer() {
                if ( !$this->performer ) {
+                       $actorId = isset( $this->row->rc_actor ) ? (int)$this->row->rc_actor : 0;
                        $userId = (int)$this->row->rc_user;
-                       if ( $userId !== 0 ) {
+                       if ( $actorId !== 0 ) {
+                               $this->performer = User::newFromActorId( $actorId );
+                       } elseif ( $userId !== 0 ) {
                                $this->performer = User::newFromId( $userId );
                        } else {
                                $userText = $this->row->rc_user_text;
@@ -593,6 +601,8 @@ class ManualLogEntry extends LogEntryBase {
         * @throws MWException
         */
        public function insert( IDatabase $dbw = null ) {
+               global $wgActorTableSchemaMigrationStage;
+
                $dbw = $dbw ?: wfGetDB( DB_MASTER );
 
                if ( $this->timestamp === null ) {
@@ -605,6 +615,31 @@ class ManualLogEntry extends LogEntryBase {
                $params = $this->getParameters();
                $relations = $this->relations;
 
+               // Ensure actor relations are set
+               if ( $wgActorTableSchemaMigrationStage >= MIGRATION_WRITE_BOTH &&
+                       empty( $relations['target_author_actor'] )
+               ) {
+                       $actorIds = [];
+                       if ( !empty( $relations['target_author_id'] ) ) {
+                               foreach ( $relations['target_author_id'] as $id ) {
+                                       $actorIds[] = User::newFromId( $id )->getActorId( $dbw );
+                               }
+                       }
+                       if ( !empty( $relations['target_author_ip'] ) ) {
+                               foreach ( $relations['target_author_ip'] as $ip ) {
+                                       $actorIds[] = User::newFromName( $ip, false )->getActorId( $dbw );
+                               }
+                       }
+                       if ( $actorIds ) {
+                               $relations['target_author_actor'] = $actorIds;
+                               $params['authorActors'] = $actorIds;
+                       }
+               }
+               if ( $wgActorTableSchemaMigrationStage >= MIGRATION_WRITE_NEW ) {
+                       unset( $relations['target_author_id'], $relations['target_author_ip'] );
+                       unset( $params['authorIds'], $params['authorIPs'] );
+               }
+
                // Additional fields for which there's no space in the database table schema
                $revId = $this->getAssociatedRevId();
                if ( $revId ) {
@@ -616,8 +651,6 @@ class ManualLogEntry extends LogEntryBase {
                        'log_type' => $this->getType(),
                        'log_action' => $this->getSubtype(),
                        'log_timestamp' => $dbw->timestamp( $this->getTimestamp() ),
-                       'log_user' => $this->getPerformer()->getId(),
-                       'log_user_text' => $this->getPerformer()->getName(),
                        'log_namespace' => $this->getTarget()->getNamespace(),
                        'log_title' => $this->getTarget()->getDBkey(),
                        'log_page' => $this->getTarget()->getArticleID(),
@@ -627,6 +660,8 @@ class ManualLogEntry extends LogEntryBase {
                        $data['log_deleted'] = $this->deleted;
                }
                $data += CommentStore::getStore()->insert( $dbw, 'log_comment', $comment );
+               $data += ActorMigration::newMigration()
+                       ->getInsertValues( $dbw, 'log_user', $this->getPerformer() );
 
                $dbw->insert( 'logging', $data, __METHOD__ );
                $this->id = $dbw->insertId();
index c84352e..28c1a87 100644 (file)
@@ -97,14 +97,13 @@ class LogPage {
                        'log_type' => $this->type,
                        'log_action' => $this->action,
                        'log_timestamp' => $dbw->timestamp( $now ),
-                       'log_user' => $this->doer->getId(),
-                       'log_user_text' => $this->doer->getName(),
                        'log_namespace' => $this->target->getNamespace(),
                        'log_title' => $this->target->getDBkey(),
                        'log_page' => $this->target->getArticleID(),
                        'log_params' => $this->params
                ];
                $data += CommentStore::getStore()->insert( $dbw, 'log_comment', $this->comment );
+               $data += ActorMigration::newMigration()->getInsertValues( $dbw, 'log_user', $this->doer );
                $dbw->insert( 'logging', $data, __METHOD__ );
                $newId = $dbw->insertId();
 
index 5404f35..dc9af5a 100644 (file)
@@ -176,13 +176,14 @@ class LogPager extends ReverseChronologicalPager {
                // Normalize username first so that non-existent users used
                // in maintenance scripts work
                $name = $usertitle->getText();
-               /* Fetch userid at first, if known, provides awesome query plan afterwards */
-               $userid = User::idFromName( $name );
-               if ( !$userid ) {
-                       $this->mConds['log_user_text'] = IP::sanitizeIP( $name );
-               } else {
-                       $this->mConds['log_user'] = $userid;
-               }
+
+               // Assume no joins required for log_user
+               // Don't query by user ID here, it might be able to use the
+               // log_user_text_time or log_user_text_type_time index.
+               $this->mConds[] = ActorMigration::newMigration()->getWhere(
+                       wfGetDB( DB_REPLICA ), 'log_user', User::newFromName( $name, false ), false
+               )['conds'];
+
                $this->enforcePerformerRestrictions();
 
                $this->performer = $name;
diff --git a/includes/logging/WikitextLogFormatter.php b/includes/logging/WikitextLogFormatter.php
new file mode 100644 (file)
index 0000000..13b5559
--- /dev/null
@@ -0,0 +1,35 @@
+<?php
+/**
+ * Formatter to allow log entries to contain formatted wikitext.
+ *
+ * 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
+ * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
+ */
+
+/**
+ * Log formatter specifically for log entries containing wikitext.
+ * @since 1.31
+ */
+class WikitextLogFormatter extends LogFormatter {
+       /**
+        * @return string
+        */
+       public function getActionMessage() {
+               return parent::getActionMessage()->parse();
+       }
+}
index 712906e..b88a34d 100644 (file)
@@ -440,6 +440,14 @@ class BitmapHandler extends TransformationalImageHandler {
                        return $this->getMediaTransformError( $params, $errMsg );
                }
 
+               if ( filesize( $params['srcPath'] ) === 0 ) {
+                       $err = "Image file size seems to be zero.";
+                       wfDebug( "$err\n" );
+                       $errMsg = wfMessage( 'thumbnail_image-size-zero', $params['srcPath'] )->text();
+
+                       return $this->getMediaTransformError( $params, $errMsg );
+               }
+
                $src_image = call_user_func( $loader, $params['srcPath'] );
 
                $rotation = function_exists( 'imagerotate' ) && !isset( $params['disableRotation'] ) ?
index 229a891..3c778f3 100644 (file)
@@ -82,7 +82,7 @@ class JpegMetadataExtractor {
                                // this is just a sanity check
                                throw new MWException( 'Too many jpeg segments. Aborting' );
                        }
-                       while ( $buffer !== "\xFF" ) {
+                       while ( $buffer !== "\xFF" && !feof( $fh ) ) {
                                // In theory JPEG files are not allowed to contain anything between the sections,
                                // but in practice they sometimes do. It's customary to ignore the garbage data.
                                $buffer = fread( $fh, 1 );
@@ -158,6 +158,8 @@ class JpegMetadataExtractor {
                                if ( $size['int'] < 2 ) {
                                        throw new MWException( "invalid marker size in jpeg" );
                                }
+                               // Note it's possible to seek beyond end of file if truncated.
+                               // fseek doesn't report a failure in this case.
                                fseek( $fh, $size['int'] - 2, SEEK_CUR );
                        }
                }
index 8eb3709..cd72267 100644 (file)
@@ -1683,6 +1683,7 @@ class Article implements Page {
                $outputPage->setPageTitle( wfMessage( 'delete-confirm', $title->getPrefixedText() ) );
                $outputPage->addBacklinkSubtitle( $title );
                $outputPage->setRobotPolicy( 'noindex,nofollow' );
+               $outputPage->addModules( 'mediawiki.action.delete' );
 
                $backlinkCache = $title->getBacklinkCache();
                if ( $backlinkCache->hasLinks( 'pagelinks' ) || $backlinkCache->hasLinks( 'templatelinks' ) ) {
@@ -1727,12 +1728,17 @@ class Article implements Page {
                        ]
                );
 
+               // HTML maxlength uses "UTF-16 code units", which means that characters outside BMP
+               // (e.g. emojis) count for two each. This limit is overridden in JS to instead count
+               // Unicode codepoints (or 255 UTF-8 bytes for old schema).
+               $conf = $this->getContext()->getConfig();
+               $oldCommentSchema = $conf->get( 'CommentTableSchemaMigrationStage' ) === MIGRATION_OLD;
                $fields[] = new OOUI\FieldLayout(
                        new OOUI\TextInputWidget( [
                                'name' => 'wpReason',
                                'inputId' => 'wpReason',
                                'tabIndex' => 2,
-                               'maxLength' => 255,
+                               'maxLength' => $oldCommentSchema ? 255 : CommentStore::COMMENT_CHARACTER_LIMIT,
                                'infusable' => true,
                                'value' => $reason,
                                'autofocus' => true,
index a47b001..d266e1d 100644 (file)
@@ -1036,11 +1036,15 @@ class WikiPage implements Page, IDBAccessObject {
 
                $dbr = wfGetDB( DB_REPLICA );
 
-               $tables = [ 'revision', 'user' ];
+               $actorMigration = ActorMigration::newMigration();
+               $actorQuery = $actorMigration->getJoin( 'rev_user' );
+
+               $tables = array_merge( [ 'revision' ], $actorQuery['tables'], [ 'user' ] );
 
                $fields = [
-                       'user_id' => 'rev_user',
-                       'user_name' => 'rev_user_text',
+                       'user_id' => $actorQuery['fields']['rev_user'],
+                       'user_name' => $actorQuery['fields']['rev_user_text'],
+                       'actor_id' => $actorQuery['fields']['rev_actor'],
                        'user_real_name' => 'MIN(user_real_name)',
                        'timestamp' => 'MAX(rev_timestamp)',
                ];
@@ -1049,22 +1053,20 @@ class WikiPage implements Page, IDBAccessObject {
 
                // The user who made the top revision gets credited as "this page was last edited by
                // John, based on contributions by Tom, Dick and Harry", so don't include them twice.
-               $user = $this->getUser();
-               if ( $user ) {
-                       $conds[] = "rev_user != $user";
-               } else {
-                       $conds[] = "rev_user_text != {$dbr->addQuotes( $this->getUserText() )}";
-               }
+               $user = $this->getUser()
+                       ? User::newFromId( $this->getUser() )
+                       : User::newFromName( $this->getUserText(), false );
+               $conds[] = 'NOT(' . $actorMigration->getWhere( $dbr, 'rev_user', $user )['conds'] . ')';
 
                // Username hidden?
                $conds[] = "{$dbr->bitAnd( 'rev_deleted', Revision::DELETED_USER )} = 0";
 
                $jconds = [
-                       'user' => [ 'LEFT JOIN', 'rev_user = user_id' ],
-               ];
+                       'user' => [ 'LEFT JOIN', $actorQuery['fields']['rev_user'] . ' = user_id' ],
+               ] + $actorQuery['joins'];
 
                $options = [
-                       'GROUP BY' => [ 'rev_user', 'rev_user_text' ],
+                       'GROUP BY' => [ $fields['user_id'], $fields['user_name'] ],
                        'ORDER BY' => 'timestamp DESC',
                ];
 
@@ -1771,8 +1773,9 @@ class WikiPage implements Page, IDBAccessObject {
                                throw new MWException( "Failed to update page row to use new revision." );
                        }
 
+                       $tags = $meta['tags'];
                        Hooks::run( 'NewRevisionFromEditComplete',
-                               [ $this, $revision, $meta['baseRevId'], $user ] );
+                               [ $this, $revision, $meta['baseRevId'], $user, &$tags ] );
 
                        // Update recentchanges
                        if ( !( $flags & EDIT_SUPPRESS_RC ) ) {
@@ -1794,7 +1797,7 @@ class WikiPage implements Page, IDBAccessObject {
                                        $newsize,
                                        $revisionId,
                                        $patrolled,
-                                       $meta['tags']
+                                       $tags
                                );
                        }
 
@@ -2782,7 +2785,8 @@ class WikiPage implements Page, IDBAccessObject {
                $reason, $suppress = false, $u1 = null, $u2 = null, &$error = '', User $user = null,
                $tags = [], $logsubtype = 'delete'
        ) {
-               global $wgUser, $wgContentHandlerUseDB, $wgCommentTableSchemaMigrationStage;
+               global $wgUser, $wgContentHandlerUseDB, $wgCommentTableSchemaMigrationStage,
+                       $wgActorTableSchemaMigrationStage;
 
                wfDebug( __METHOD__ . "\n" );
 
@@ -2847,6 +2851,7 @@ class WikiPage implements Page, IDBAccessObject {
                }
 
                $commentStore = CommentStore::getStore();
+               $actorMigration = ActorMigration::newMigration();
 
                $revQuery = Revision::getQueryInfo();
                $bitfield = false;
@@ -2883,11 +2888,10 @@ class WikiPage implements Page, IDBAccessObject {
 
                foreach ( $res as $row ) {
                        $comment = $commentStore->getComment( 'rev_comment', $row );
+                       $user = User::newFromAnyId( $row->rev_user, $row->rev_user_text, $row->rev_actor );
                        $rowInsert = [
                                'ar_namespace'  => $namespace,
                                'ar_title'      => $dbKey,
-                               'ar_user'       => $row->rev_user,
-                               'ar_user_text'  => $row->rev_user_text,
                                'ar_timestamp'  => $row->rev_timestamp,
                                'ar_minor_edit' => $row->rev_minor_edit,
                                'ar_rev_id'     => $row->rev_id,
@@ -2899,7 +2903,8 @@ class WikiPage implements Page, IDBAccessObject {
                                'ar_page_id'    => $id,
                                'ar_deleted'    => $suppress ? $bitfield : $row->rev_deleted,
                                'ar_sha1'       => $row->rev_sha1,
-                       ] + $commentStore->insert( $dbw, 'ar_comment', $comment );
+                       ] + $commentStore->insert( $dbw, 'ar_comment', $comment )
+                               + $actorMigration->getInsertValues( $dbw, 'ar_user', $user );
                        if ( $wgContentHandlerUseDB ) {
                                $rowInsert['ar_content_model'] = $row->rev_content_model;
                                $rowInsert['ar_content_format'] = $row->rev_content_format;
@@ -2929,6 +2934,9 @@ class WikiPage implements Page, IDBAccessObject {
                if ( $wgCommentTableSchemaMigrationStage > MIGRATION_OLD ) {
                        $dbw->delete( 'revision_comment_temp', [ 'revcomment_rev' => $revids ], __METHOD__ );
                }
+               if ( $wgActorTableSchemaMigrationStage > MIGRATION_OLD ) {
+                       $dbw->delete( 'revision_actor_temp', [ 'revactor_rev' => $revids ], __METHOD__ );
+               }
 
                // Also delete records from ip_changes as applicable.
                if ( count( $ipRevIds ) > 0 ) {
@@ -3160,16 +3168,31 @@ class WikiPage implements Page, IDBAccessObject {
 
                // Get the last edit not by this person...
                // Note: these may not be public values
-               $user = intval( $current->getUser( Revision::RAW ) );
-               $user_text = $dbw->addQuotes( $current->getUserText( Revision::RAW ) );
-               $s = $dbw->selectRow( 'revision',
+               $userId = intval( $current->getUser( Revision::RAW ) );
+               $userName = $current->getUserText( Revision::RAW );
+               if ( $userId ) {
+                       $user = User::newFromId( $userId );
+                       $user->setName( $userName );
+               } else {
+                       $user = User::newFromName( $current->getUserText( Revision::RAW ), false );
+               }
+
+               $actorWhere = ActorMigration::newMigration()->getWhere( $dbw, 'rev_user', $user );
+
+               $s = $dbw->selectRow(
+                       [ 'revision' ] + $actorWhere['tables'],
                        [ 'rev_id', 'rev_timestamp', 'rev_deleted' ],
-                       [ 'rev_page' => $current->getPage(),
-                               "rev_user != {$user} OR rev_user_text != {$user_text}"
-                       ], __METHOD__,
-                       [ 'USE INDEX' => 'page_timestamp',
-                               'ORDER BY' => 'rev_timestamp DESC' ]
-                       );
+                       [
+                               'rev_page' => $current->getPage(),
+                               'NOT(' . $actorWhere['conds'] . ')',
+                       ],
+                       __METHOD__,
+                       [
+                               'USE INDEX' => [ 'revision' => 'page_timestamp' ],
+                               'ORDER BY' => 'rev_timestamp DESC'
+                       ],
+                       $actorWhere['joins']
+               );
                if ( $s === false ) {
                        // No one else ever edited this page
                        return [ [ 'cantrollback' ] ];
@@ -3248,11 +3271,12 @@ class WikiPage implements Page, IDBAccessObject {
                }
 
                if ( count( $set ) ) {
+                       $actorWhere = ActorMigration::newMigration()->getWhere( $dbw, 'rc_user', $user, false );
                        $dbw->update( 'recentchanges', $set,
                                [ /* WHERE */
                                        'rc_cur_id' => $current->getPage(),
-                                       'rc_user_text' => $current->getUserText(),
                                        'rc_timestamp > ' . $dbw->addQuotes( $s->rev_timestamp ),
+                                       $actorWhere['conds'], // No tables/joins are needed for rc_user
                                ],
                                __METHOD__
                        );
index c6a10ae..d408c7f 100644 (file)
@@ -830,7 +830,7 @@ class CoreParserFunctions {
                        $restrictions = $titleObject->getRestrictions( strtolower( $type ) );
                        # Title::getRestrictions returns an array, its possible it may have
                        # multiple values in the future
-                       return implode( $restrictions, ',' );
+                       return implode( ',', $restrictions );
                }
                return '';
        }
@@ -1339,7 +1339,7 @@ class CoreParserFunctions {
                        foreach ( $sources[0] as $sourceTitle ) {
                                $names[] = $sourceTitle->getPrefixedText();
                        }
-                       return implode( $names, '|' );
+                       return implode( '|', $names );
                }
                return '';
        }
index 605a873..0a4a60e 100644 (file)
@@ -126,13 +126,16 @@ class DateFormatter {
        /**
         * Get a DateFormatter object
         *
-        * @param Language|string|null $lang In which language to format the date
+        * @param Language|null $lang In which language to format the date
         *     Defaults to the site content language
         * @return DateFormatter
         */
        public static function getInstance( $lang = null ) {
                global $wgContLang, $wgMainCacheType;
 
+               if ( is_string( $lang ) ) {
+                       wfDeprecated( __METHOD__ . ' with type string for $lang', '1.31' );
+               }
                $lang = $lang ? wfGetLangObj( $lang ) : $wgContLang;
                $cache = ObjectCache::getLocalServerInstance( $wgMainCacheType );
 
index bbddbe2..ff21ef0 100644 (file)
@@ -1254,7 +1254,7 @@ class ParserOptions {
                } elseif ( $value instanceof Language ) {
                        return $value->getCode();
                } elseif ( is_array( $value ) ) {
-                       return '[' . join( ',', array_map( [ $this, 'optionToString' ], $value ) ) . ']';
+                       return '[' . implode( ',', array_map( [ $this, 'optionToString' ], $value ) ) . ']';
                } else {
                        return (string)$value;
                }
@@ -1295,7 +1295,7 @@ class ParserOptions {
                        }
                }
 
-               $confstr = $values ? join( '!', $values ) : 'canonical';
+               $confstr = $values ? implode( '!', $values ) : 'canonical';
 
                // add in language specific options, if any
                // @todo FIXME: This is just a way of retrieving the url/user preferred variant
index 298aad3..5d6385e 100644 (file)
@@ -30,7 +30,6 @@ class StripState {
        protected $data;
        protected $regex;
 
-       protected $tempType, $tempMergePrefix;
        protected $circularRefGuard;
        protected $recursionLevel = 0;
 
@@ -122,44 +121,37 @@ class StripState {
                        return $text;
                }
 
-               $oldType = $this->tempType;
-               $this->tempType = $type;
-               $text = preg_replace_callback( $this->regex, [ $this, 'unstripCallback' ], $text );
-               $this->tempType = $oldType;
-               return $text;
-       }
-
-       /**
-        * @param array $m
-        * @return array
-        */
-       protected function unstripCallback( $m ) {
-               $marker = $m[1];
-               if ( isset( $this->data[$this->tempType][$marker] ) ) {
-                       if ( isset( $this->circularRefGuard[$marker] ) ) {
-                               return '<span class="error">'
-                                       . wfMessage( 'parser-unstrip-loop-warning' )->inContentLanguage()->text()
-                                       . '</span>';
-                       }
-                       if ( $this->recursionLevel >= self::UNSTRIP_RECURSION_LIMIT ) {
-                               return '<span class="error">' .
-                                       wfMessage( 'parser-unstrip-recursion-limit' )
-                                               ->numParams( self::UNSTRIP_RECURSION_LIMIT )->inContentLanguage()->text() .
-                                       '</span>';
+               $callback = function ( $m ) use ( $type ) {
+                       $marker = $m[1];
+                       if ( isset( $this->data[$type][$marker] ) ) {
+                               if ( isset( $this->circularRefGuard[$marker] ) ) {
+                                       return '<span class="error">'
+                                               . wfMessage( 'parser-unstrip-loop-warning' )->inContentLanguage()->text()
+                                               . '</span>';
+                               }
+                               if ( $this->recursionLevel >= self::UNSTRIP_RECURSION_LIMIT ) {
+                                       return '<span class="error">' .
+                                               wfMessage( 'parser-unstrip-recursion-limit' )
+                                                       ->numParams( self::UNSTRIP_RECURSION_LIMIT )->inContentLanguage()->text() .
+                                               '</span>';
+                               }
+                               $this->circularRefGuard[$marker] = true;
+                               $this->recursionLevel++;
+                               $value = $this->data[$type][$marker];
+                               if ( $value instanceof Closure ) {
+                                       $value = $value();
+                               }
+                               $ret = $this->unstripType( $type, $value );
+                               $this->recursionLevel--;
+                               unset( $this->circularRefGuard[$marker] );
+                               return $ret;
+                       } else {
+                               return $m[0];
                        }
-                       $this->circularRefGuard[$marker] = true;
-                       $this->recursionLevel++;
-                       $value = $this->data[$this->tempType][$marker];
-                       if ( $value instanceof Closure ) {
-                               $value = $value();
-                       }
-                       $ret = $this->unstripType( $this->tempType, $value );
-                       $this->recursionLevel--;
-                       unset( $this->circularRefGuard[$marker] );
-                       return $ret;
-               } else {
-                       return $m[0];
-               }
+               };
+
+               $text = preg_replace_callback( $this->regex, $callback, $text );
+               return $text;
        }
 
        /**
@@ -215,21 +207,14 @@ class StripState {
                        }
                }
 
-               $this->tempMergePrefix = $mergePrefix;
-               $texts = preg_replace_callback( $otherState->regex, [ $this, 'mergeCallback' ], $texts );
-               $this->tempMergePrefix = null;
+               $callback = function ( $m ) use ( $mergePrefix ) {
+                       $key = $m[1];
+                       return Parser::MARKER_PREFIX . $mergePrefix . '-' . $key . Parser::MARKER_SUFFIX;
+               };
+               $texts = preg_replace_callback( $otherState->regex, $callback, $texts );
                return $texts;
        }
 
-       /**
-        * @param array $m
-        * @return string
-        */
-       protected function mergeCallback( $m ) {
-               $key = $m[1];
-               return Parser::MARKER_PREFIX . $this->tempMergePrefix . '-' . $key . Parser::MARKER_SUFFIX;
-       }
-
        /**
         * Remove any strip markers found in the given text.
         *
index c1e9a59..478f373 100644 (file)
@@ -705,7 +705,7 @@ class DefaultPreferencesFactory implements PreferencesFactory {
                                'type' => 'info',
                                'raw' => true,
                                'default' => $context->getLanguage()->pipeList( $linkTools ),
-                               'label-message' => 'prefs-common-css-js',
+                               'label-message' => 'prefs-common-config',
                                'section' => 'rendering/skin',
                        ];
                }
index 9021652..d02011f 100644 (file)
@@ -188,7 +188,7 @@ abstract class Profiler {
         * Get all usable outputs.
         *
         * @throws MWException
-        * @return array Array of ProfilerOutput instances.
+        * @return ProfilerOutput[]
         * @since 1.25
         */
        private function getOutputs() {
index 8b9feeb..e5fe928 100644 (file)
@@ -66,6 +66,7 @@ class ResourceLoaderStartUpModule extends ResourceLoaderModule {
                }
 
                $illegalFileChars = $conf->get( 'IllegalFileChars' );
+               $oldCommentSchema = $conf->get( 'CommentTableSchemaMigrationStage' ) === MIGRATION_OLD;
 
                // Build list of variables
                $vars = [
@@ -113,6 +114,8 @@ class ResourceLoaderStartUpModule extends ResourceLoaderModule {
                        'wgResourceLoaderStorageEnabled' => $conf->get( 'ResourceLoaderStorageEnabled' ),
                        'wgForeignUploadTargets' => $conf->get( 'ForeignUploadTargets' ),
                        'wgEnableUploads' => $conf->get( 'EnableUploads' ),
+                       'wgCommentByteLimit' => $oldCommentSchema ? 255 : null,
+                       'wgCommentCodePointLimit' => $oldCommentSchema ? null : CommentStore::COMMENT_CHARACTER_LIMIT,
                ];
 
                Hooks::run( 'ResourceLoaderGetConfigVars', [ &$vars ] );
index 6eddfc0..5b512af 100644 (file)
@@ -29,8 +29,8 @@ use Wikimedia\Rdbms\IDatabase;
  * Abstraction for ResourceLoader modules which pull from wiki pages
  *
  * This can only be used for wiki pages in the MediaWiki and User namespaces,
- * because of its dependence on the functionality of Title::isCssJsSubpage
- * and Title::isCssOrJsPage().
+ * because of its dependence on the functionality of Title::isUserConfigPage()
+ * and Title::isSiteConfigPage().
  *
  * This module supports being used as a placeholder for a module on a remote wiki.
  * To do so, getDB() must be overloaded to return a foreign database object that
@@ -450,7 +450,7 @@ class ResourceLoaderWikiModule extends ResourceLoaderModule {
                } elseif ( $new && in_array( $new->getContentFormat(), $formats ) ) {
                        $purge = true;
                } else {
-                       $purge = ( $title->isCssOrJsPage() || $title->isCssJsSubpage() );
+                       $purge = ( $title->isSiteConfigPage() || $title->isUserConfigPage() );
                }
 
                if ( $purge ) {
index ab74dbd..679acc6 100644 (file)
@@ -45,6 +45,10 @@ class RevDelArchiveItem extends RevDelRevisionItem {
                return 'ar_user_text';
        }
 
+       public function getAuthorActorField() {
+               return 'ar_actor';
+       }
+
        public function getId() {
                # Convert DB timestamp to MW timestamp
                return $this->revision->getTimestamp();
index b098422..d36fac9 100644 (file)
@@ -50,6 +50,10 @@ class RevDelArchivedFileItem extends RevDelFileItem {
                return 'fa_user_text';
        }
 
+       public function getAuthorActorField() {
+               return 'fa_actor';
+       }
+
        public function getId() {
                return $this->row->fa_id;
        }
index 9beafc9..0ca84d7 100644 (file)
@@ -49,6 +49,10 @@ class RevDelFileItem extends RevDelItem {
                return 'oi_user_text';
        }
 
+       public function getAuthorActorField() {
+               return 'oi_actor';
+       }
+
        public function getId() {
                $parts = explode( '!', $this->row->oi_archive_name );
 
index 011c7b0..89025bc 100644 (file)
@@ -106,6 +106,8 @@ abstract class RevDelList extends RevisionListBase {
         * @since 1.23 Added 'perItemStatus' param
         */
        public function setVisibility( array $params ) {
+               global $wgActorTableSchemaMigrationStage;
+
                $status = Status::newGood();
 
                $bitPars = $params['value'];
@@ -134,7 +136,7 @@ abstract class RevDelList extends RevisionListBase {
                $missing = array_flip( $this->ids );
                $this->clearFileOps();
                $idsForLog = [];
-               $authorIds = $authorIPs = [];
+               $authorIds = $authorIPs = $authorActors = [];
 
                if ( $perItemStatus ) {
                        $status->itemStatuses = [];
@@ -216,10 +218,15 @@ abstract class RevDelList extends RevisionListBase {
                                $virtualOldBits |= $removedBits;
 
                                $status->successCount++;
-                               if ( $item->getAuthorId() > 0 ) {
-                                       $authorIds[] = $item->getAuthorId();
-                               } elseif ( IP::isIPAddress( $item->getAuthorName() ) ) {
-                                       $authorIPs[] = $item->getAuthorName();
+                               if ( $wgActorTableSchemaMigrationStage <= MIGRATION_WRITE_BOTH ) {
+                                       if ( $item->getAuthorId() > 0 ) {
+                                               $authorIds[] = $item->getAuthorId();
+                                       } elseif ( IP::isIPAddress( $item->getAuthorName() ) ) {
+                                               $authorIPs[] = $item->getAuthorName();
+                                       }
+                               }
+                               if ( $wgActorTableSchemaMigrationStage >= MIGRATION_WRITE_BOTH ) {
+                                       $authorActors[] = $item->getAuthorActor();
                                }
 
                                // Save the old and new bits in $visibilityChangeMap for
@@ -263,6 +270,14 @@ abstract class RevDelList extends RevisionListBase {
                }
 
                // Log it
+               $authorFields = [];
+               if ( $wgActorTableSchemaMigrationStage <= MIGRATION_WRITE_BOTH ) {
+                       $authorFields['authorIds'] = $authorIds;
+                       $authorFields['authorIPs'] = $authorIPs;
+               }
+               if ( $wgActorTableSchemaMigrationStage >= MIGRATION_WRITE_BOTH ) {
+                       $authorFields['authorActors'] = $authorActors;
+               }
                $this->updateLog(
                        $logType,
                        [
@@ -272,10 +287,8 @@ abstract class RevDelList extends RevisionListBase {
                                'oldBits' => $virtualOldBits,
                                'comment' => $comment,
                                'ids' => $idsForLog,
-                               'authorIds' => $authorIds,
-                               'authorIPs' => $authorIPs,
                                'tags' => isset( $params['tags'] ) ? $params['tags'] : [],
-                       ]
+                       ] + $authorFields
                );
 
                // Clear caches after commit
@@ -330,8 +343,9 @@ abstract class RevDelList extends RevisionListBase {
         *     title:           The target title
         *     ids:             The ID list
         *     comment:         The log comment
-        *     authorsIds:      The array of the user IDs of the offenders
-        *     authorsIPs:      The array of the IP/anon user offenders
+        *     authorIds:       The array of the user IDs of the offenders
+        *     authorIPs:       The array of the IP/anon user offenders
+        *     authorActors:    The array of the actor IDs of the offenders
         *     tags:            The array of change tags to apply to the log entry
         * @throws MWException
         */
@@ -350,11 +364,21 @@ abstract class RevDelList extends RevisionListBase {
                $logEntry->setParameters( $logParams );
                $logEntry->setPerformer( $this->getUser() );
                // Allow for easy searching of deletion log items for revision/log items
-               $logEntry->setRelations( [
+               $relations = [
                        $field => $params['ids'],
-                       'target_author_id' => $params['authorIds'],
-                       'target_author_ip' => $params['authorIPs'],
-               ] );
+               ];
+               if ( isset( $params['authorIds'] ) ) {
+                       $relations += [
+                               'target_author_id' => $params['authorIds'],
+                               'target_author_ip' => $params['authorIPs'],
+                       ];
+               }
+               if ( isset( $params['authorActors'] ) ) {
+                       $relations += [
+                               'target_author_actor' => $params['authorActors'],
+                       ];
+               }
+               $logEntry->setRelations( $relations );
                // Apply change tags to the log entry
                $logEntry->setTags( $params['tags'] );
                $logId = $logEntry->insert();
index b8b0c5c..198a28b 100644 (file)
@@ -39,6 +39,10 @@ class RevDelLogItem extends RevDelItem {
                return 'log_user_text';
        }
 
+       public function getAuthorActorField() {
+               return 'log_actor';
+       }
+
        public function canView() {
                return LogEventsList::userCan( $this->row, Revision::DELETED_RESTRICTED, $this->list->getUser() );
        }
index f4e4faf..b26fffd 100644 (file)
@@ -64,26 +64,25 @@ class RevDelLogList extends RevDelList {
                $ids = array_map( 'intval', $this->ids );
 
                $commentQuery = CommentStore::getStore()->getJoin( 'log_comment' );
+               $actorQuery = ActorMigration::newMigration()->getJoin( 'log_user' );
 
                return $db->select(
-                       [ 'logging' ] + $commentQuery['tables'],
+                       [ 'logging' ] + $commentQuery['tables'] + $actorQuery['tables'],
                        [
                                'log_id',
                                'log_type',
                                'log_action',
                                'log_timestamp',
-                               'log_user',
-                               'log_user_text',
                                'log_namespace',
                                'log_title',
                                'log_page',
                                'log_params',
                                'log_deleted'
-                       ] + $commentQuery['fields'],
+                       ] + $commentQuery['fields'] + $actorQuery['fields'],
                        [ 'log_id' => $ids ],
                        __METHOD__,
                        [ 'ORDER BY' => 'log_id DESC' ],
-                       $commentQuery['joins']
+                       $commentQuery['joins'] + $actorQuery['joins']
                );
        }
 
index a9753b4..cb5ce48 100644 (file)
@@ -47,6 +47,10 @@ class RevDelRevisionItem extends RevDelItem {
                return 'rev_user_text';
        }
 
+       public function getAuthorActorField() {
+               return 'rev_actor';
+       }
+
        public function canView() {
                return $this->revision->userCan( Revision::DELETED_RESTRICTED, $this->list->getUser() );
        }
index 7812fb9..6291e8d 100644 (file)
@@ -42,6 +42,8 @@ class RevisionDeleteUser {
         * @return bool
         */
        private static function setUsernameBitfields( $name, $userId, $op, $dbw ) {
+               global $wgActorTableSchemaMigrationStage;
+
                if ( !$userId || ( $op !== '|' && $op !== '&' ) ) {
                        return false; // sanity check
                }
@@ -65,43 +67,120 @@ class RevisionDeleteUser {
                $userTitle = Title::makeTitleSafe( NS_USER, $name );
                $userDbKey = $userTitle->getDBkey();
 
-               # Hide name from live edits
-               $dbw->update(
-                       'revision',
-                       [ self::buildSetBitDeletedField( 'rev_deleted', $op, $delUser, $dbw ) ],
-                       [ 'rev_user' => $userId ],
-                       __METHOD__ );
+               if ( $wgActorTableSchemaMigrationStage < MIGRATION_NEW ) {
+                       # Hide name from live edits
+                       $dbw->update(
+                               'revision',
+                               [ self::buildSetBitDeletedField( 'rev_deleted', $op, $delUser, $dbw ) ],
+                               [ 'rev_user' => $userId ],
+                               __METHOD__ );
 
-               # Hide name from deleted edits
-               $dbw->update(
-                       'archive',
-                       [ self::buildSetBitDeletedField( 'ar_deleted', $op, $delUser, $dbw ) ],
-                       [ 'ar_user_text' => $name ],
-                       __METHOD__
-               );
+                       # Hide name from deleted edits
+                       $dbw->update(
+                               'archive',
+                               [ self::buildSetBitDeletedField( 'ar_deleted', $op, $delUser, $dbw ) ],
+                               [ 'ar_user_text' => $name ],
+                               __METHOD__
+                       );
 
-               # Hide name from logs
-               $dbw->update(
-                       'logging',
-                       [ self::buildSetBitDeletedField( 'log_deleted', $op, $delUser, $dbw ) ],
-                       [ 'log_user' => $userId, 'log_type != ' . $dbw->addQuotes( 'suppress' ) ],
-                       __METHOD__
-               );
+                       # Hide name from logs
+                       $dbw->update(
+                               'logging',
+                               [ self::buildSetBitDeletedField( 'log_deleted', $op, $delUser, $dbw ) ],
+                               [ 'log_user' => $userId, 'log_type != ' . $dbw->addQuotes( 'suppress' ) ],
+                               __METHOD__
+                       );
+
+                       # Hide name from RC
+                       $dbw->update(
+                               'recentchanges',
+                               [ self::buildSetBitDeletedField( 'rc_deleted', $op, $delUser, $dbw ) ],
+                               [ 'rc_user_text' => $name ],
+                               __METHOD__
+                       );
+
+                       # Hide name from live images
+                       $dbw->update(
+                               'oldimage',
+                               [ self::buildSetBitDeletedField( 'oi_deleted', $op, $delUser, $dbw ) ],
+                               [ 'oi_user_text' => $name ],
+                               __METHOD__
+                       );
+
+                       # Hide name from deleted images
+                       $dbw->update(
+                               'filearchive',
+                               [ self::buildSetBitDeletedField( 'fa_deleted', $op, $delUser, $dbw ) ],
+                               [ 'fa_user_text' => $name ],
+                               __METHOD__
+                       );
+               }
+
+               if ( $wgActorTableSchemaMigrationStage > MIGRATION_OLD ) {
+                       $actorId = $dbw->selectField( 'actor', 'actor_id', [ 'actor_name' => $name ], __METHOD__ );
+                       if ( $actorId ) {
+                               # Hide name from live edits
+                               $subquery = $dbw->selectSQLText(
+                                       'revision_actor_temp', 'revactor_rev', [ 'revactor_actor' => $actorId ], __METHOD__
+                               );
+                               $dbw->update(
+                                       'revision',
+                                       [ self::buildSetBitDeletedField( 'rev_deleted', $op, $delUser, $dbw ) ],
+                                       [ "rev_id IN ($subquery)" ],
+                                       __METHOD__ );
+
+                               # Hide name from deleted edits
+                               $dbw->update(
+                                       'archive',
+                                       [ self::buildSetBitDeletedField( 'ar_deleted', $op, $delUser, $dbw ) ],
+                                       [ 'ar_actor' => $actorId ],
+                                       __METHOD__
+                               );
+
+                               # Hide name from logs
+                               $dbw->update(
+                                       'logging',
+                                       [ self::buildSetBitDeletedField( 'log_deleted', $op, $delUser, $dbw ) ],
+                                       [ 'log_actor' => $actorId, 'log_type != ' . $dbw->addQuotes( 'suppress' ) ],
+                                       __METHOD__
+                               );
+
+                               # Hide name from RC
+                               $dbw->update(
+                                       'recentchanges',
+                                       [ self::buildSetBitDeletedField( 'rc_deleted', $op, $delUser, $dbw ) ],
+                                       [ 'rc_actor' => $actorId ],
+                                       __METHOD__
+                               );
+
+                               # Hide name from live images
+                               $dbw->update(
+                                       'oldimage',
+                                       [ self::buildSetBitDeletedField( 'oi_deleted', $op, $delUser, $dbw ) ],
+                                       [ 'oi_actor' => $actorId ],
+                                       __METHOD__
+                               );
+
+                               # Hide name from deleted images
+                               $dbw->update(
+                                       'filearchive',
+                                       [ self::buildSetBitDeletedField( 'fa_deleted', $op, $delUser, $dbw ) ],
+                                       [ 'fa_actor' => $actorId ],
+                                       __METHOD__
+                               );
+                       }
+               }
+
+               # Hide log entries pointing to the user page
                $dbw->update(
                        'logging',
                        [ self::buildSetBitDeletedField( 'log_deleted', $op, $delAction, $dbw ) ],
                        [ 'log_namespace' => NS_USER, 'log_title' => $userDbKey,
-                               'log_type != ' . $dbw->addQuotes( 'suppress' ) ],
+                       'log_type != ' . $dbw->addQuotes( 'suppress' ) ],
                        __METHOD__
                );
 
-               # Hide name from RC
-               $dbw->update(
-                       'recentchanges',
-                       [ self::buildSetBitDeletedField( 'rc_deleted', $op, $delUser, $dbw ) ],
-                       [ 'rc_user_text' => $name ],
-                       __METHOD__
-               );
+               # Hide RC entries pointing to the user page
                $dbw->update(
                        'recentchanges',
                        [ self::buildSetBitDeletedField( 'rc_deleted', $op, $delAction, $dbw ) ],
@@ -109,21 +188,6 @@ class RevisionDeleteUser {
                        __METHOD__
                );
 
-               # Hide name from live images
-               $dbw->update(
-                       'oldimage',
-                       [ self::buildSetBitDeletedField( 'oi_deleted', $op, $delUser, $dbw ) ],
-                       [ 'oi_user_text' => $name ],
-                       __METHOD__
-               );
-
-               # Hide name from deleted images
-               $dbw->update(
-                       'filearchive',
-                       [ self::buildSetBitDeletedField( 'fa_deleted', $op, $delUser, $dbw ) ],
-                       [ 'fa_user_text' => $name ],
-                       __METHOD__
-               );
                # Done!
                return true;
        }
index d6f9578..d9fa82d 100644 (file)
@@ -463,6 +463,12 @@ class Command {
                                $isWrite = array_key_exists( $fd, $readPipes );
 
                                if ( $isWrite ) {
+                                       // Don't bother writing if the buffer is empty
+                                       if ( $buffers[$fd] === '' ) {
+                                               fclose( $pipes[$fd] );
+                                               unset( $pipes[$fd] );
+                                               continue;
+                                       }
                                        $res = fwrite( $pipe, $buffers[$fd], 65536 );
                                } else {
                                        $res = fread( $pipe, 65536 );
index a71b376..d818930 100644 (file)
@@ -123,22 +123,24 @@ class FirejailCommand extends Command {
                        $cmd[] = '--noroot';
                }
 
-               $seccomp = [];
-
-               if ( $this->hasRestriction( Shell::SECCOMP ) ) {
-                       $seccomp[] = '@default';
-               }
+               $useSeccomp = $this->hasRestriction( Shell::SECCOMP );
+               $extraSeccomp = [];
 
                if ( $this->hasRestriction( Shell::NO_EXECVE ) ) {
-                       $seccomp[] = 'execve';
+                       $extraSeccomp[] = 'execve';
                        // Normally firejail will run commands in a bash shell,
                        // but that won't work if we ban the execve syscall, so
                        // run the command without a shell.
                        $cmd[] = '--shell=none';
                }
 
-               if ( $seccomp ) {
-                       $cmd[] = '--seccomp=' . implode( ',', $seccomp );
+               if ( $useSeccomp ) {
+                       $seccomp = '--seccomp';
+                       if ( $extraSeccomp ) {
+                               // The "@default" seccomp group will always be enabled
+                               $seccomp .= '=' . implode( ',', $extraSeccomp );
+                       }
+                       $cmd[] = $seccomp;
                }
 
                if ( $this->hasRestriction( Shell::PRIVATE_DEV ) ) {
index e8466dc..1886746 100644 (file)
@@ -153,7 +153,7 @@ abstract class QuickTemplate {
        /**
         * An ugly, ugly hack.
         * @private
-        * @param string $str
+        * @param string $msgKey
         */
        function msgWiki( $msgKey ) {
                global $wgOut;
index 4f271c7..65a300a 100644 (file)
@@ -1253,7 +1253,7 @@ abstract class Skin extends ContextSource {
         *
         * @return array
         */
-       function buildSidebar() {
+       public function buildSidebar() {
                global $wgEnableSidebarCache, $wgSidebarCacheExpiry;
 
                $callback = function ( $old = null, &$ttl = null ) {
@@ -1267,13 +1267,22 @@ abstract class Skin extends ContextSource {
                        return $bar;
                };
 
-               $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
+               $msgCache = MessageCache::singleton();
+               $wanCache = MediaWikiServices::getInstance()->getMainWANObjectCache();
+
                $sidebar = $wgEnableSidebarCache
-                       ? $cache->getWithSetCallback(
-                               $cache->makeKey( 'sidebar', $this->getLanguage()->getCode() ),
+                       ? $wanCache->getWithSetCallback(
+                               $wanCache->makeKey( 'sidebar', $this->getLanguage()->getCode() ),
                                $wgSidebarCacheExpiry,
                                $callback,
-                               [ 'lockTSE' => 30 ]
+                               [
+                                       'checkKeys' => [
+                                               // Unless there is both no exact $code override nor an i18n definition
+                                               // in the the software, the only MediaWiki page to check is for $code.
+                                               $msgCache->getCheckKey( $this->getLanguage()->getCode() )
+                                       ],
+                                       'lockTSE' => 30
+                               ]
                        )
                        : $callback();
 
index cf990c2..b8d7063 100644 (file)
@@ -33,6 +33,12 @@ use Wikimedia\Rdbms\IDatabase;
  * @ingroup SpecialPage
  */
 abstract class ChangesListSpecialPage extends SpecialPage {
+       /**
+        * Maximum length of a tag description in UTF-8 characters.
+        * Longer descriptions will be truncated.
+        */
+       const TAG_DESC_CHARACTER_LIMIT = 120;
+
        /**
         * Preference name for saved queries. Subclasses that use saved queries should override this.
         * @var string
@@ -115,7 +121,11 @@ abstract class ChangesListSpecialPage extends SpecialPage {
                                                'queryCallable' => function ( $specialClassName, $ctx, $dbr, &$tables, &$fields, &$conds,
                                                        &$query_options, &$join_conds
                                                ) {
-                                                       $conds[] = 'rc_user = 0';
+                                                       $actorMigration = ActorMigration::newMigration();
+                                                       $actorQuery = $actorMigration->getJoin( 'rc_user' );
+                                                       $tables += $actorQuery['tables'];
+                                                       $join_conds += $actorQuery['joins'];
+                                                       $conds[] = $actorMigration->isAnon( $actorQuery['fields']['rc_user'] );
                                                },
                                                'isReplacedInStructuredUi' => true,
 
@@ -129,7 +139,11 @@ abstract class ChangesListSpecialPage extends SpecialPage {
                                                'queryCallable' => function ( $specialClassName, $ctx, $dbr, &$tables, &$fields, &$conds,
                                                        &$query_options, &$join_conds
                                                ) {
-                                                       $conds[] = 'rc_user != 0';
+                                                       $actorMigration = ActorMigration::newMigration();
+                                                       $actorQuery = $actorMigration->getJoin( 'rc_user' );
+                                                       $tables += $actorQuery['tables'];
+                                                       $join_conds += $actorQuery['joins'];
+                                                       $conds[] = $actorMigration->isNotAnon( $actorQuery['fields']['rc_user'] );
                                                },
                                                'isReplacedInStructuredUi' => true,
                                        ]
@@ -214,8 +228,10 @@ abstract class ChangesListSpecialPage extends SpecialPage {
                                                'queryCallable' => function ( $specialClassName, $ctx, $dbr, &$tables, &$fields, &$conds,
                                                        &$query_options, &$join_conds
                                                ) {
-                                                       $user = $ctx->getUser();
-                                                       $conds[] = 'rc_user_text != ' . $dbr->addQuotes( $user->getName() );
+                                                       $actorQuery = ActorMigration::newMigration()->getWhere( $dbr, 'rc_user', $ctx->getUser() );
+                                                       $tables += $actorQuery['tables'];
+                                                       $join_conds += $actorQuery['joins'];
+                                                       $conds[] = 'NOT(' . $actorQuery['conds'] . ')';
                                                },
                                                'cssClassSuffix' => 'self',
                                                'isRowApplicableCallable' => function ( $ctx, $rc ) {
@@ -230,8 +246,11 @@ abstract class ChangesListSpecialPage extends SpecialPage {
                                                'queryCallable' => function ( $specialClassName, $ctx, $dbr, &$tables, &$fields, &$conds,
                                                        &$query_options, &$join_conds
                                                ) {
-                                                       $user = $ctx->getUser();
-                                                       $conds[] = 'rc_user_text = ' . $dbr->addQuotes( $user->getName() );
+                                                       $actorQuery = ActorMigration::newMigration()
+                                                               ->getWhere( $dbr, 'rc_user', $ctx->getUser(), false );
+                                                       $tables += $actorQuery['tables'];
+                                                       $join_conds += $actorQuery['joins'];
+                                                       $conds[] = $actorQuery['conds'];
                                                },
                                                'cssClassSuffix' => 'others',
                                                'isRowApplicableCallable' => function ( $ctx, $rc ) {
@@ -794,15 +813,15 @@ abstract class ChangesListSpecialPage extends SpecialPage {
                                                isset( $explicitlyDefinedTags[ $tagName ] ) ||
                                                isset( $softwareActivatedTags[ $tagName ] )
                                        ) {
-                                               // Parse description
-                                               $desc = ChangeTags::tagLongDescriptionMessage( $tagName, $context );
-
                                                $result[] = [
                                                        'name' => $tagName,
                                                        'label' => Sanitizer::stripAllTags(
                                                                ChangeTags::tagDescription( $tagName, $context )
                                                        ),
-                                                       'description' => $desc ? Sanitizer::stripAllTags( $desc->parse() ) : '',
+                                                       'description' =>
+                                                               ChangeTags::truncateTagDescription(
+                                                                       $tagName, self::TAG_DESC_CHARACTER_LIMIT, $context
+                                                               ),
                                                        'cssClass' => Sanitizer::escapeClass( 'mw-tag-' . $tagName ),
                                                        'hits' => $hits,
                                                ];
@@ -838,7 +857,7 @@ abstract class ChangesListSpecialPage extends SpecialPage {
         */
        protected function outputTimeout() {
                $this->getOutput()->addHTML(
-                       '<div class="mw-changeslist-timeout">' .
+                       '<div class="mw-changeslist-empty mw-changeslist-timeout">' .
                        $this->msg( 'recentchanges-timeout' )->parse() .
                        '</div>'
                );
@@ -1696,22 +1715,27 @@ abstract class ChangesListSpecialPage extends SpecialPage {
                        return;
                }
 
+               $actorMigration = ActorMigration::newMigration();
+               $actorQuery = $actorMigration->getJoin( 'rc_user' );
+               $tables += $actorQuery['tables'];
+               $join_conds += $actorQuery['joins'];
+
                // 'registered' but not 'unregistered', experience levels, if any, are included in 'registered'
                if (
                        in_array( 'registered', $selectedExpLevels ) &&
                        !in_array( 'unregistered', $selectedExpLevels )
                ) {
-                       $conds[] = 'rc_user != 0';
+                       $conds[] = $actorMigration->isNotAnon( $actorQuery['fields']['rc_user'] );
                        return;
                }
 
                if ( $selectedExpLevels === [ 'unregistered' ] ) {
-                       $conds[] = 'rc_user = 0';
+                       $conds[] = $actorMigration->isAnon( $actorQuery['fields']['rc_user'] );
                        return;
                }
 
                $tables[] = 'user';
-               $join_conds['user'] = [ 'LEFT JOIN', 'rc_user = user_id' ];
+               $join_conds['user'] = [ 'LEFT JOIN', $actorQuery['fields']['rc_user'] . ' = user_id' ];
 
                if ( $now === 0 ) {
                        $now = time();
@@ -1741,7 +1765,7 @@ abstract class ChangesListSpecialPage extends SpecialPage {
 
                if ( in_array( 'unregistered', $selectedExpLevels ) ) {
                        $selectedExpLevels = array_diff( $selectedExpLevels, [ 'unregistered' ] );
-                       $conditions[] = 'rc_user = 0';
+                       $conditions[] = $actorMigration->isAnon( $actorQuery['fields']['rc_user'] );
                }
 
                if ( $selectedExpLevels === [ 'newcomer' ] ) {
@@ -1763,7 +1787,7 @@ abstract class ChangesListSpecialPage extends SpecialPage {
                } elseif ( $selectedExpLevels === [ 'experienced', 'learner' ] ) {
                        $conditions[] = $aboveNewcomer;
                } elseif ( $selectedExpLevels === [ 'experienced', 'learner', 'newcomer' ] ) {
-                       $conditions[] = 'rc_user != 0';
+                       $conditions[] = $actorMigration->isNotAnon( $actorQuery['fields']['rc_user'] );
                }
 
                if ( count( $conditions ) > 1 ) {
index 42e7040..23691b2 100644 (file)
@@ -135,6 +135,9 @@ class SpecialBlock extends FormSpecialPage {
 
                $suggestedDurations = self::getSuggestedDurations();
 
+               $conf = $this->getConfig();
+               $oldCommentSchema = $conf->get( 'CommentTableSchemaMigrationStage' ) === MIGRATION_OLD;
+
                $a = [
                        'Target' => [
                                'type' => 'user',
@@ -157,7 +160,11 @@ class SpecialBlock extends FormSpecialPage {
                        ],
                        'Reason' => [
                                'type' => 'selectandother',
-                               'maxlength' => 255,
+                               // HTML maxlength uses "UTF-16 code units", which means that characters outside BMP
+                               // (e.g. emojis) count for two each. This limit is overridden in JS to instead count
+                               // Unicode codepoints (or 255 UTF-8 bytes for old schema).
+                               'maxlength' => $oldCommentSchema ? 255 : CommentStore::COMMENT_CHARACTER_LIMIT,
+                               'maxlength-unit' => 'codepoints',
                                'label-message' => 'ipbreason',
                                'options-message' => 'ipbreason-dropdown',
                        ],
index 4775a7f..806713b 100644 (file)
@@ -40,14 +40,12 @@ class SpecialContributions extends IncludableSpecialPage {
                $this->setHeaders();
                $this->outputHeader();
                $out = $this->getOutput();
+               // Modules required for viewing the list of contributions (also when included on other pages)
                $out->addModuleStyles( [
                        'mediawiki.special',
                        'mediawiki.special.changeslist',
-                       'mediawiki.widgets.DateInputWidget.styles',
                ] );
-               $out->addModules( 'mediawiki.special.contributions' );
                $this->addHelpLink( 'Help:User contributions' );
-               $out->enableOOUI();
 
                $this->opts = [];
                $request = $this->getRequest();
@@ -497,6 +495,14 @@ class SpecialContributions extends IncludableSpecialPage {
                        $this->opts['hideMinor'] = false;
                }
 
+               // Modules required only for the form
+               $this->getOutput()->addModules( [
+                       'mediawiki.userSuggest',
+                       'mediawiki.special.contributions',
+               ] );
+               $this->getOutput()->addModuleStyles( 'mediawiki.widgets.DateInputWidget.styles' );
+               $this->getOutput()->enableOOUI();
+
                $form = Html::openElement(
                        'form',
                        [
@@ -544,8 +550,6 @@ class SpecialContributions extends IncludableSpecialPage {
                        $filterSelection = Html::rawElement( 'div', [], '' );
                }
 
-               $this->getOutput()->addModules( 'mediawiki.userSuggest' );
-
                $labelNewbies = Xml::radioLabel(
                        $this->msg( 'sp-contributions-newbies' )->text(),
                        'contribs',
index 9623953..60d5fd7 100644 (file)
@@ -239,6 +239,9 @@ class SpecialEditTags extends UnlistedSpecialPage {
 
                // Show form if the user can submit
                if ( $this->isAllowed ) {
+                       $conf = $this->getConfig();
+                       $oldCommentSchema = $conf->get( 'CommentTableSchemaMigrationStage' ) === MIGRATION_OLD;
+
                        $form = Xml::openElement( 'form', [ 'method' => 'post',
                                        'action' => $this->getPageTitle()->getLocalURL( [ 'action' => 'submit' ] ),
                                        'id' => 'mw-revdel-form-revisions' ] ) .
@@ -251,12 +254,14 @@ class SpecialEditTags extends UnlistedSpecialPage {
                                                Xml::label( $this->msg( 'tags-edit-reason' )->text(), 'wpReason' ) .
                                        '</td>' .
                                        '<td class="mw-input">' .
-                                               Xml::input(
-                                                       'wpReason',
-                                                       60,
-                                                       $this->reason,
-                                                       [ 'id' => 'wpReason', 'maxlength' => 100 ]
-                                               ) .
+                                               Xml::input( 'wpReason', 60, $this->reason, [
+                                                       'id' => 'wpReason',
+                                                       // HTML maxlength uses "UTF-16 code units", which means that characters outside BMP
+                                                       // (e.g. emojis) count for two each. This limit is overridden in JS to instead count
+                                                       // Unicode codepoints (or 255 UTF-8 bytes for old schema).
+                                                       // "- 155" is to leave room for the auto-generated part of the log entry.
+                                                       'maxlength' => $oldCommentSchema ? 100 : CommentStore::COMMENT_CHARACTER_LIMIT - 155,
+                                               ] ) .
                                        '</td>' .
                                "</tr><tr>\n" .
                                        '<td></td>' .
index 0a38ad1..f702bc0 100644 (file)
@@ -657,7 +657,7 @@ class SpecialEditWatchlist extends UnlistedSpecialPage {
         * @return HTMLForm
         */
        protected function getRawForm() {
-               $titles = implode( $this->getWatchlist(), "\n" );
+               $titles = implode( "\n", $this->getWatchlist() );
                $fields = [
                        'Titles' => [
                                'type' => 'textarea',
index 8021bc2..7694a61 100644 (file)
@@ -85,15 +85,17 @@ class FileDuplicateSearchPage extends QueryPage {
        }
 
        public function getQueryInfo() {
+               $imgQuery = LocalFile::getQueryInfo();
                return [
-                       'tables' => [ 'image' ],
+                       'tables' => $imgQuery['tables'],
                        'fields' => [
                                'title' => 'img_name',
                                'value' => 'img_sha1',
-                               'img_user_text',
+                               'img_user_text' => $imgQuery['fields']['img_user_text'],
                                'img_timestamp'
                        ],
-                       'conds' => [ 'img_sha1' => $this->hash ]
+                       'conds' => [ 'img_sha1' => $this->hash ],
+                       'join_conds' => $imgQuery['joins'],
                ];
        }
 
index cc43580..6a11bf4 100644 (file)
@@ -32,6 +32,8 @@ class SpecialLog extends SpecialPage {
        }
 
        public function execute( $par ) {
+               global $wgActorTableSchemaMigrationStage;
+
                $this->setHeaders();
                $this->outputHeader();
                $this->getOutput()->addModules( 'mediawiki.userSuggest' );
@@ -78,12 +80,33 @@ class SpecialLog extends SpecialPage {
                # Handle type-specific inputs
                $qc = [];
                if ( $opts->getValue( 'type' ) == 'suppress' ) {
-                       $offender = User::newFromName( $opts->getValue( 'offender' ), false );
+                       $offenderName = $opts->getValue( 'offender' );
+                       $offender = empty( $offenderName ) ? null : User::newFromName( $offenderName, false );
                        if ( $offender ) {
-                               if ( $offender->getId() > 0 ) {
-                                       $qc = [ 'ls_field' => 'target_author_id', 'ls_value' => $offender->getId() ];
-                               } elseif ( !empty( $opts->getValue( 'offender' ) ) ) {
-                                       $qc = [ 'ls_field' => 'target_author_ip', 'ls_value' => $offender->getName() ];
+                               if ( $wgActorTableSchemaMigrationStage === MIGRATION_NEW ) {
+                                       $qc = [ 'ls_field' => 'target_author_actor', 'ls_value' => $offender->getActorId() ];
+                               } else {
+                                       if ( $offender->getId() > 0 ) {
+                                               $field = 'target_author_id';
+                                               $value = $offender->getId();
+                                       } else {
+                                               $field = 'target_author_ip';
+                                               $value = $offender->getName();
+                                       }
+                                       if ( !$offender->getActorId() ) {
+                                               $qc = [ 'ls_field' => $field, 'ls_value' => $value ];
+                                       } else {
+                                               $db = wfGetDB( DB_REPLICA );
+                                               $qc = [
+                                                       'ls_field' => [ 'target_author_actor', $field ], // So LogPager::getQueryInfo() works right
+                                                       $db->makeList( [
+                                                               $db->makeList(
+                                                                       [ 'ls_field' => 'target_author_actor', 'ls_value' => $offender->getActorId() ], LIST_AND
+                                                               ),
+                                                               $db->makeList( [ 'ls_field' => $field, 'ls_value' => $value ], LIST_AND ),
+                                                       ], LIST_OR ),
+                                               ];
+                                       }
                                }
                        }
                } else {
index 3290abd..a54d72d 100644 (file)
@@ -56,8 +56,9 @@ class MIMEsearchPage extends QueryPage {
                        // Allow wildcard searching
                        $minorType['img_minor_mime'] = $this->minor;
                }
+               $imgQuery = LocalFile::getQueryInfo();
                $qi = [
-                       'tables' => [ 'image' ],
+                       'tables' => $imgQuery['tables'],
                        'fields' => [
                                'namespace' => NS_FILE,
                                'title' => 'img_name',
@@ -67,7 +68,7 @@ class MIMEsearchPage extends QueryPage {
                                'img_size',
                                'img_width',
                                'img_height',
-                               'img_user_text',
+                               'img_user_text' => $imgQuery['fields']['img_user_text'],
                                'img_timestamp'
                        ],
                        'conds' => [
@@ -89,6 +90,7 @@ class MIMEsearchPage extends QueryPage {
                                        MEDIATYPE_3D,
                                ],
                        ] + $minorType,
+                       'join_conds' => $imgQuery['joins'],
                ];
 
                return $qi;
index 02d6d00..d30ff43 100644 (file)
@@ -287,8 +287,8 @@ class MovePageForm extends UnlistedSpecialPage {
                        $out->addHTML( "</div>\n" );
                }
 
-               // Byte limit (not string length limit) for wpReason and wpNewTitleMain
-               // is enforced in the mediawiki.special.movePage module
+               // Length limit for wpReason and wpNewTitleMain is enforced in the
+               // mediawiki.special.movePage module
 
                $immovableNamespaces = [];
                foreach ( array_keys( $this->getLanguage()->getNamespaces() ) as $nsId ) {
@@ -326,11 +326,16 @@ class MovePageForm extends UnlistedSpecialPage {
                        ]
                );
 
+               // HTML maxlength uses "UTF-16 code units", which means that characters outside BMP
+               // (e.g. emojis) count for two each. This limit is overridden in JS to instead count
+               // Unicode codepoints (or 255 UTF-8 bytes for old schema).
+               $conf = $this->getConfig();
+               $oldCommentSchema = $conf->get( 'CommentTableSchemaMigrationStage' ) === MIGRATION_OLD;
                $fields[] = new OOUI\FieldLayout(
                        new OOUI\TextInputWidget( [
                                'name' => 'wpReason',
                                'id' => 'wpReason',
-                               'maxLength' => 200,
+                               'maxLength' => $oldCommentSchema ? 200 : CommentStore::COMMENT_CHARACTER_LIMIT,
                                'infusable' => true,
                                'value' => $this->reason,
                        ] ),
index ea0f0ed..46d5276 100644 (file)
@@ -299,6 +299,7 @@ class SpecialNewpages extends IncludableSpecialPage {
                        'deleted' => $result->rc_deleted,
                        'user_text' => $result->rc_user_text,
                        'user' => $result->rc_user,
+                       'actor' => $result->rc_actor,
                ], 0, $title );
        }
 
index d4aef6c..181b4db 100644 (file)
@@ -65,7 +65,6 @@ class SpecialRecentChangesLinked extends SpecialRecentChanges {
                        $outputPage->addHTML(
                                Html::errorBox( $this->msg( 'allpagesbadtitle' )->parse() )
                        );
-
                        return false;
                }
 
@@ -295,12 +294,19 @@ class SpecialRecentChangesLinked extends SpecialRecentChanges {
        }
 
        protected function outputNoResults() {
-               if ( $this->getTargetTitle() === false ) {
+               $targetTitle = $this->getTargetTitle();
+               if ( $targetTitle === false ) {
                        $this->getOutput()->addHTML(
-                               '<div class="mw-changeslist-notargetpage">' .
+                               '<div class="mw-changeslist-empty mw-changeslist-notargetpage">' .
                                $this->msg( 'recentchanges-notargetpage' )->parse() .
                                '</div>'
                        );
+               } elseif ( !$targetTitle || $targetTitle->isExternal() ) {
+                       $this->getOutput()->addHTML(
+                               '<div class="mw-changeslist-empty mw-changeslist-invalidtargetpage">' .
+                               $this->msg( 'allpagesbadtitle' )->parse() .
+                               '</div>'
+                       );
                } else {
                        parent::outputNoResults();
                }
index 3273046..36e7779 100644 (file)
@@ -177,11 +177,13 @@ class SpecialRedirect extends FormSpecialPage {
                        return null;
                }
 
+               $logQuery = ActorMigration::newMigration()->getJoin( 'log_user' );
+
                $logparams = [
-                       'log_id',
-                       'log_timestamp',
-                       'log_type',
-                       'log_user_text',
+                       'log_id' => 'log_id',
+                       'log_timestamp' => 'log_timestamp',
+                       'log_type' => 'log_type',
+                       'log_user_text' => $logQuery['fields']['log_user_text'],
                ];
 
                $dbr = wfGetDB( DB_REPLICA );
@@ -197,9 +199,12 @@ class SpecialRedirect extends FormSpecialPage {
                // Returns all fields mentioned in $logparams of the logs
                // with the same timestamp as the one returned by the statement above
                $logsSameTimestamps = $dbr->select(
-                       'logging',
+                       [ 'logging' ] + $logQuery['tables'],
                        $logparams,
-                       [ "log_timestamp = ($inner)" ]
+                       [ "log_timestamp = ($inner)" ],
+                       __METHOD__,
+                       [],
+                       $logQuery['joins']
                );
                if ( $logsSameTimestamps->numRows() === 0 ) {
                        return null;
@@ -217,10 +222,10 @@ class SpecialRedirect extends FormSpecialPage {
 
                // Stores all the rows with the same values in each column
                // as $rowMain
-               foreach ( $logparams as $cond ) {
+               foreach ( $logparams as $key => $dummy ) {
                        $matchedRows = [];
                        foreach ( $logsSameTimestamps as $row ) {
-                               if ( $row->$cond === $rowMain->$cond ) {
+                               if ( $row->$key === $rowMain->$key ) {
                                        $matchedRows[] = $row;
                                }
                        }
@@ -238,7 +243,7 @@ class SpecialRedirect extends FormSpecialPage {
                        'log_user_text' => 'user'
                ];
 
-               foreach ( $logparams as $logKey ) {
+               foreach ( $logparams as $logKey => $dummy ) {
                        $query[$keys[$logKey]] = $matchedRows[0]->$logKey;
                }
                $query['offset'] = $query['offset'] + 1;
index aec21dc..e7db9f5 100644 (file)
@@ -418,8 +418,12 @@ class SpecialRevisionDelete extends UnlistedSpecialPage {
 
                // Show form if the user can submit
                if ( $this->mIsAllowed ) {
+                       $out->addModules( [ 'mediawiki.special.revisionDelete' ] );
                        $out->addModuleStyles( 'mediawiki.special' );
 
+                       $conf = $this->getConfig();
+                       $oldCommentSchema = $conf->get( 'CommentTableSchemaMigrationStage' ) === MIGRATION_OLD;
+
                        $form = Xml::openElement( 'form', [ 'method' => 'post',
                                        'action' => $this->getPageTitle()->getLocalURL( [ 'action' => 'submit' ] ),
                                        'id' => 'mw-revdel-form-revisions' ] ) .
@@ -442,12 +446,14 @@ class SpecialRevisionDelete extends UnlistedSpecialPage {
                                                Xml::label( $this->msg( 'revdelete-otherreason' )->text(), 'wpReason' ) .
                                        '</td>' .
                                        '<td class="mw-input">' .
-                                               Xml::input(
-                                                       'wpReason',
-                                                       60,
-                                                       $this->otherReason,
-                                                       [ 'id' => 'wpReason', 'maxlength' => 100 ]
-                                               ) .
+                                               Xml::input( 'wpReason', 60, $this->otherReason, [
+                                                       'id' => 'wpReason',
+                                                       // HTML maxlength uses "UTF-16 code units", which means that characters outside BMP
+                                                       // (e.g. emojis) count for two each. This limit is overridden in JS to instead count
+                                                       // Unicode codepoints (or 255 UTF-8 bytes for old schema).
+                                                       // "- 155" is to leave room for the 'wpRevDeleteReasonList' value.
+                                                       'maxlength' => $oldCommentSchema ? 100 : CommentStore::COMMENT_CHARACTER_LIMIT - 155,
+                                               ] ) .
                                        '</td>' .
                                "</tr><tr>\n" .
                                        '<td></td>' .
index 127a36b..6e6ad77 100644 (file)
@@ -739,6 +739,9 @@ class SpecialUndelete extends SpecialPage {
                                'content' => new OOUI\HtmlSnippet( $this->msg( 'undeleteextrahelp' )->parseAsBlock() )
                        ] );
 
+                       $conf = $this->getConfig();
+                       $oldCommentSchema = $conf->get( 'CommentTableSchemaMigrationStage' ) === MIGRATION_OLD;
+
                        $fields[] = new OOUI\FieldLayout(
                                new OOUI\TextInputWidget( [
                                        'name' => 'wpComment',
@@ -746,6 +749,10 @@ class SpecialUndelete extends SpecialPage {
                                        'infusable' => true,
                                        'value' => $this->mComment,
                                        'autofocus' => true,
+                                       // HTML maxlength uses "UTF-16 code units", which means that characters outside BMP
+                                       // (e.g. emojis) count for two each. This limit is overridden in JS to instead count
+                                       // Unicode codepoints (or 255 UTF-8 bytes for old schema).
+                                       'maxLength' => $oldCommentSchema ? 255 : CommentStore::COMMENT_CHARACTER_LIMIT,
                                ] ),
                                [
                                        'label' => $this->msg( 'undeletecomment' )->text(),
index e62731f..40f02a5 100644 (file)
@@ -716,6 +716,8 @@ class UserrightsPage extends SpecialPage {
                                ->rawParams( $userToolLinks )->parse()
                );
                if ( $canChangeAny ) {
+                       $conf = $this->getConfig();
+                       $oldCommentSchema = $conf->get( 'CommentTableSchemaMigrationStage' ) === MIGRATION_OLD;
                        $this->getOutput()->addHTML(
                                $this->msg( 'userrights-groups-help', $user->getName() )->parse() .
                                $grouplist .
@@ -726,8 +728,13 @@ class UserrightsPage extends SpecialPage {
                                                        Xml::label( $this->msg( 'userrights-reason' )->text(), 'wpReason' ) .
                                                "</td>
                                                <td class='mw-input'>" .
-                                                       Xml::input( 'user-reason', 60, $this->getRequest()->getVal( 'user-reason', false ),
-                                                               [ 'id' => 'wpReason', 'maxlength' => 255 ] ) .
+                                                       Xml::input( 'user-reason', 60, $this->getRequest()->getVal( 'user-reason', false ), [
+                                                               'id' => 'wpReason',
+                                                               // HTML maxlength uses "UTF-16 code units", which means that characters outside BMP
+                                                               // (e.g. emojis) count for two each. This limit is overridden in JS to instead count
+                                                               // Unicode codepoints (or 255 UTF-8 bytes for old schema).
+                                                               'maxlength' => $oldCommentSchema ? 255 : CommentStore::COMMENT_CHARACTER_LIMIT,
+                                                       ] ) .
                                                "</td>
                                        </tr>
                                        <tr>
index 64af71a..26ed499 100644 (file)
@@ -79,14 +79,17 @@ class ActiveUsersPager extends UsersPager {
        function getQueryInfo() {
                $dbr = $this->getDatabase();
 
+               $rcQuery = ActorMigration::newMigration()->getJoin( 'rc_user' );
+
                $activeUserSeconds = $this->getConfig()->get( 'ActiveUserDays' ) * 86400;
                $timestamp = $dbr->timestamp( wfTimestamp( TS_UNIX ) - $activeUserSeconds );
-               $tables = [ 'querycachetwo', 'user', 'recentchanges' ];
+               $tables = [ 'querycachetwo', 'user', 'recentchanges' ] + $rcQuery['tables'];
+               $jconds = $rcQuery['joins'];
                $conds = [
                        'qcc_type' => 'activeusers',
                        'qcc_namespace' => NS_USER,
                        'user_name = qcc_title',
-                       'rc_user_text = qcc_title',
+                       $rcQuery['fields']['rc_user_text'] . ' = qcc_title',
                        'rc_type != ' . $dbr->addQuotes( RC_EXTERNAL ), // Don't count wikidata.
                        'rc_type != ' . $dbr->addQuotes( RC_CATEGORIZE ), // Don't count categorization changes.
                        'rc_log_type IS NULL OR rc_log_type != ' . $dbr->addQuotes( 'newusers' ),
@@ -127,7 +130,8 @@ class ActiveUsersPager extends UsersPager {
                                'recentedits' => 'COUNT(*)'
                        ],
                        'options' => [ 'GROUP BY' => [ 'qcc_title' ] ],
-                       'conds' => $conds
+                       'conds' => $conds,
+                       'join_conds' => $jconds,
                ];
        }
 
index fe7cac0..cac0736 100644 (file)
@@ -210,15 +210,16 @@ class BlockListPager extends TablePager {
 
        function getQueryInfo() {
                $commentQuery = CommentStore::getStore()->getJoin( 'ipb_reason' );
+               $actorQuery = ActorMigration::newMigration()->getJoin( 'ipb_by' );
 
                $info = [
-                       'tables' => [ 'ipblocks', 'user' ] + $commentQuery['tables'],
+                       'tables' => array_merge(
+                               [ 'ipblocks' ], $commentQuery['tables'], $actorQuery['tables'], [ 'user' ]
+                       ),
                        'fields' => [
                                'ipb_id',
                                'ipb_address',
                                'ipb_user',
-                               'ipb_by',
-                               'ipb_by_text',
                                'by_user_name' => 'user_name',
                                'ipb_timestamp',
                                'ipb_auto',
@@ -231,9 +232,11 @@ class BlockListPager extends TablePager {
                                'ipb_deleted',
                                'ipb_block_email',
                                'ipb_allow_usertalk',
-                       ] + $commentQuery['fields'],
+                       ] + $commentQuery['fields'], $actorQuery['fields'],
                        'conds' => $this->conds,
-                       'join_conds' => [ 'user' => [ 'LEFT JOIN', 'user_id = ipb_by' ] ] + $commentQuery['joins']
+                       'join_conds' => [
+                               'user' => [ 'LEFT JOIN', 'user_id = ' . $actorQuery['fields']['ipb_by'] ]
+                       ] + $commentQuery['joins'] + $actorQuery['joins']
                ];
 
                # Filter out any expired blocks
index e29467d..6fdf05f 100644 (file)
@@ -186,7 +186,7 @@ class ContribsPager extends RangeChronologicalPager {
 
                if ( $this->contribs == 'newbie' ) {
                        $max = $this->mDb->selectField( 'user', 'max(user_id)', false, __METHOD__ );
-                       $queryInfo['conds'][] = 'rev_user >' . (int)( $max - $max / 100 );
+                       $queryInfo['conds'][] = $revQuery['fields']['rev_user'] . ' >' . (int)( $max - $max / 100 );
                        # ignore local groups with the bot right
                        # @todo FIXME: Global groups may have 'bot' rights
                        $groupsWithBotPermission = User::getGroupsWithPermission( 'bot' );
@@ -195,7 +195,7 @@ class ContribsPager extends RangeChronologicalPager {
                                $queryInfo['conds'][] = 'ug_group IS NULL';
                                $queryInfo['join_conds']['user_groups'] = [
                                        'LEFT JOIN', [
-                                               'ug_user = rev_user',
+                                               'ug_user = ' . $revQuery['fields']['rev_user'],
                                                'ug_group' => $groupsWithBotPermission,
                                                'ug_expiry IS NULL OR ug_expiry >= ' .
                                                        $this->mDb->addQuotes( $this->mDb->timestamp() )
@@ -208,23 +208,18 @@ class ContribsPager extends RangeChronologicalPager {
                        $queryInfo['conds'][] = 'rev_timestamp > ' .
                                $this->mDb->addQuotes( $this->mDb->timestamp( wfTimestamp() - 30 * 24 * 60 * 60 ) );
                } else {
-                       $uid = User::idFromName( $this->target );
-                       if ( $uid ) {
-                               $queryInfo['conds']['rev_user'] = $uid;
-                               $queryInfo['options']['USE INDEX']['revision'] = 'user_timestamp';
+                       $user = User::newFromName( $this->target, false );
+                       $ipRangeConds = $user->isAnon() ? $this->getIpRangeConds( $this->mDb, $this->target ) : null;
+                       if ( $ipRangeConds ) {
+                               $queryInfo['tables'][] = 'ip_changes';
+                               $queryInfo['join_conds']['ip_changes'] = [
+                                       'LEFT JOIN', [ 'ipc_rev_id = rev_id' ]
+                               ];
+                               $queryInfo['conds'][] = $ipRangeConds;
                        } else {
-                               $ipRangeConds = $this->getIpRangeConds( $this->mDb, $this->target );
-
-                               if ( $ipRangeConds ) {
-                                       $queryInfo['tables'][] = 'ip_changes';
-                                       $queryInfo['join_conds']['ip_changes'] = [
-                                               'LEFT JOIN', [ 'ipc_rev_id = rev_id' ]
-                                       ];
-                                       $queryInfo['conds'][] = $ipRangeConds;
-                               } else {
-                                       $queryInfo['conds']['rev_user_text'] = $this->target;
-                                       $queryInfo['options']['USE INDEX']['revision'] = 'usertext_timestamp';
-                               }
+                               // tables and joins are already handled by Revision::getQueryInfo()
+                               $queryInfo['conds'][] = ActorMigration::newMigration()
+                                       ->getWhere( $this->mDb, 'rev_user', $user )['conds'];
                        }
                }
 
index 1c31724..d642e66 100644 (file)
@@ -58,7 +58,12 @@ class DeletedContribsPager extends IndexPager {
        }
 
        function getQueryInfo() {
-               list( $index, $userCond ) = $this->getUserCond();
+               $userCond = [
+                       // ->getJoin() below takes care of any joins needed
+                       ActorMigration::newMigration()->getWhere(
+                               wfGetDB( DB_REPLICA ), 'ar_user', User::newFromName( $this->target, false ), false
+                       )['conds']
+               ];
                $conds = array_merge( $userCond, $this->getNamespaceCond() );
                $user = $this->getUser();
                // Paranoia: avoid brute force searches (T19792)
@@ -70,16 +75,17 @@ class DeletedContribsPager extends IndexPager {
                }
 
                $commentQuery = CommentStore::getStore()->getJoin( 'ar_comment' );
+               $actorQuery = ActorMigration::newMigration()->getJoin( 'ar_user' );
 
                return [
-                       'tables' => [ 'archive' ] + $commentQuery['tables'],
+                       'tables' => [ 'archive' ] + $commentQuery['tables'] + $actorQuery['tables'],
                        'fields' => [
                                'ar_rev_id', 'ar_namespace', 'ar_title', 'ar_timestamp',
-                               'ar_minor_edit', 'ar_user', 'ar_user_text', 'ar_deleted'
-                       ] + $commentQuery['fields'],
+                               'ar_minor_edit', 'ar_deleted'
+                       ] + $commentQuery['fields'] + $actorQuery['fields'],
                        'conds' => $conds,
-                       'options' => [ 'USE INDEX' => [ 'archive' => $index ] ],
-                       'join_conds' => $commentQuery['joins'],
+                       'options' => [],
+                       'join_conds' => $commentQuery['joins'] + $actorQuery['joins'],
                ];
        }
 
@@ -128,15 +134,6 @@ class DeletedContribsPager extends IndexPager {
                return new FakeResultWrapper( $result );
        }
 
-       function getUserCond() {
-               $condition = [];
-
-               $condition['ar_user_text'] = $this->target;
-               $index = 'ar_usertext_timestamp';
-
-               return [ $index, $condition ];
-       }
-
        function getIndexField() {
                return 'ar_timestamp';
        }
@@ -259,6 +256,7 @@ class DeletedContribsPager extends IndexPager {
                        'comment' => CommentStore::getStore()->getComment( 'ar_comment', $row )->text,
                        'user' => $row->ar_user,
                        'user_text' => $row->ar_user_text,
+                       'actor' => isset( $row->ar_actor ) ? $row->ar_actor : null,
                        'timestamp' => $row->ar_timestamp,
                        'minor_edit' => $row->ar_minor_edit,
                        'deleted' => $row->ar_deleted,
index ae19d59..75c2f77 100644 (file)
@@ -193,9 +193,9 @@ class ImageListPager extends TablePager {
                }
                $sortable = [ 'img_timestamp', 'img_name', 'img_size' ];
                /* For reference, the indicies we can use for sorting are:
-                * On the image table: img_user_timestamp, img_usertext_timestamp,
+                * On the image table: img_user_timestamp/img_usertext_timestamp/img_actor_timestamp,
                 * img_size, img_timestamp
-                * On oldimage: oi_usertext_timestamp, oi_name_timestamp
+                * On oldimage: oi_usertext_timestamp/oi_actor_timestamp, oi_name_timestamp
                 *
                 * In particular that means we cannot sort by timestamp when not filtering
                 * by user and including old images in the results. Which is sad.
@@ -246,6 +246,7 @@ class ImageListPager extends TablePager {
                $tables = [ $table ];
                $fields = $this->getFieldNames();
                unset( $fields['img_description'] );
+               unset( $fields['img_user_text'] );
                $fields = array_keys( $fields );
 
                if ( $table === 'oldimage' ) {
@@ -261,7 +262,6 @@ class ImageListPager extends TablePager {
                                $fields[array_search( 'top', $fields )] = "'yes' AS top";
                        }
                }
-               $fields[] = $prefix . '_user AS img_user';
                $fields[array_search( 'thumb', $fields )] = $prefix . '_name AS thumb';
 
                $options = $join_conds = [];
@@ -273,6 +273,14 @@ class ImageListPager extends TablePager {
                $join_conds += $commentQuery['joins'];
                $fields['description_field'] = "'{$prefix}_description'";
 
+               # User fields
+               $actorQuery = ActorMigration::newMigration()->getJoin( $prefix . '_user' );
+               $tables += $actorQuery['tables'];
+               $join_conds += $actorQuery['joins'];
+               $fields['img_user'] = $actorQuery['fields'][$prefix . '_user'];
+               $fields['img_user_text'] = $actorQuery['fields'][$prefix . '_user_text'];
+               $fields['img_actor'] = $actorQuery['fields'][$prefix . '_actor'];
+
                # Depends on $wgMiserMode
                # Will also not happen if mShowAll is true.
                if ( isset( $this->mFieldNames['count'] ) ) {
@@ -287,7 +295,7 @@ class ImageListPager extends TablePager {
                        unset( $field );
 
                        $columnlist = preg_grep( '/^img/', array_keys( $this->getFieldNames() ) );
-                       $options = [ 'GROUP BY' => array_merge( [ 'img_user' ], $columnlist ) ];
+                       $options = [ 'GROUP BY' => array_merge( [ $fields['img_user'] ], $columnlist ) ];
                        $join_conds['oldimage'] = [ 'LEFT JOIN', 'oi_name = img_name' ];
                }
 
index 001c296..4815189 100644 (file)
@@ -59,26 +59,24 @@ class NewFilesPager extends RangeChronologicalPager {
 
        function getQueryInfo() {
                $opts = $this->opts;
-               $conds = $jconds = [];
-               $tables = [ 'image' ];
-               $fields = [ 'img_name', 'img_user', 'img_timestamp' ];
+               $conds = [];
+               $imgQuery = LocalFile::getQueryInfo();
+               $tables = $imgQuery['tables'];
+               $fields = [ 'img_name', 'img_timestamp' ] + $imgQuery['fields'];
                $options = [];
+               $jconds = $imgQuery['joins'];
 
                $user = $opts->getValue( 'user' );
                if ( $user !== '' ) {
-                       $userId = User::idFromName( $user );
-                       if ( $userId ) {
-                               $conds['img_user'] = $userId;
-                       } else {
-                               $conds['img_user_text'] = $user;
-                       }
+                       $conds[] = ActorMigration::newMigration()
+                               ->getWhere( wfGetDB( DB_REPLICA ), 'img_user', User::newFromName( $user, false ) )['conds'];
                }
 
                if ( $opts->getValue( 'newbies' ) ) {
                        // newbie = most recent 1% of users
                        $dbr = wfGetDB( DB_REPLICA );
                        $max = $dbr->selectField( 'user', 'max(user_id)', false, __METHOD__ );
-                       $conds[] = 'img_user >' . (int)( $max - $max / 100 );
+                       $conds[] = $imgQuery['fields']['img_user'] . ' >' . (int)( $max - $max / 100 );
 
                        // there's no point in looking for new user activity in a far past;
                        // beyond a certain point, we'd just end up scanning the rest of the
@@ -99,7 +97,7 @@ class NewFilesPager extends RangeChronologicalPager {
                                        'LEFT JOIN',
                                        [
                                                'ug_group' => $groupsWithBotPermission,
-                                               'ug_user = img_user',
+                                               'ug_user = ' . $imgQuery['fields']['img_user'],
                                                'ug_expiry IS NULL OR ug_expiry >= ' . $dbr->addQuotes( $dbr->timestamp() )
                                        ]
                                ];
@@ -107,16 +105,27 @@ class NewFilesPager extends RangeChronologicalPager {
                }
 
                if ( $opts->getValue( 'hidepatrolled' ) ) {
+                       global $wgActorTableSchemaMigrationStage;
+
                        $tables[] = 'recentchanges';
                        $conds['rc_type'] = RC_LOG;
                        $conds['rc_log_type'] = 'upload';
                        $conds['rc_patrolled'] = 0;
                        $conds['rc_namespace'] = NS_FILE;
+
+                       if ( $wgActorTableSchemaMigrationStage === MIGRATION_NEW ) {
+                               $jcond = 'rc_actor = ' . $imgQuery['fields']['img_actor'];
+                       } else {
+                               $rcQuery = ActorMigration::newMigration()->getJoin( 'rc_user' );
+                               $tables += $rcQuery['tables'];
+                               $joins += $rcQuery['joins'];
+                               $jcond = $rcQuery['fields']['rc_user'] . ' = ' . $imgQuery['fields']['img_user'];
+                       }
                        $jconds['recentchanges'] = [
                                'INNER JOIN',
                                [
                                        'rc_title = img_name',
-                                       'rc_user = img_user',
+                                       $jcond,
                                        'rc_timestamp = img_timestamp'
                                ]
                        ];
index 61b648d..9746334 100644 (file)
@@ -39,6 +39,8 @@ class NewPagesPager extends ReverseChronologicalPager {
        }
 
        function getQueryInfo() {
+               $rcQuery = RecentChange::getQueryInfo();
+
                $conds = [];
                $conds['rc_new'] = 1;
 
@@ -68,13 +70,14 @@ class NewPagesPager extends ReverseChronologicalPager {
                }
 
                if ( $user ) {
-                       $conds['rc_user_text'] = $user->getText();
-                       $rcIndexes = 'rc_user_text';
+                       $conds[] = ActorMigration::newMigration()->getWhere(
+                               $this->mDb, 'rc_user', User::newFromName( $user->getText(), false ), false
+                       )['conds'];
                } elseif ( User::groupHasPermission( '*', 'createpage' ) &&
                        $this->opts->getValue( 'hideliu' )
                ) {
                        # If anons cannot make new pages, don't "exclude logged in users"!
-                       $conds['rc_user'] = 0;
+                       $conds[] = ActorMigration::newMigration()->isAnon( $rcQuery['fields']['rc_user'] );
                }
 
                # If this user cannot see patrolled edits or they are off, don't do dumb queries!
@@ -90,17 +93,12 @@ class NewPagesPager extends ReverseChronologicalPager {
                        $conds['page_is_redirect'] = 0;
                }
 
-               $commentQuery = CommentStore::getStore()->getJoin( 'rc_comment' );
-
                // Allow changes to the New Pages query
-               $tables = [ 'recentchanges', 'page' ] + $commentQuery['tables'];
+               $tables = array_merge( $rcQuery['tables'], [ 'page' ] );
                $fields = [
-                       'rc_namespace', 'rc_title', 'rc_cur_id', 'rc_user', 'rc_user_text',
-                       'rc_timestamp', 'rc_patrolled', 'rc_id', 'rc_deleted',
-                       'length' => 'page_len', 'rev_id' => 'page_latest', 'rc_this_oldid',
-                       'page_namespace', 'page_title'
-               ] + $commentQuery['fields'];
-               $join_conds = [ 'page' => [ 'INNER JOIN', 'page_id=rc_cur_id' ] ] + $commentQuery['joins'];
+                       'length' => 'page_len', 'rev_id' => 'page_latest', 'page_namespace', 'page_title'
+               ] + $rcQuery['fields'];
+               $join_conds = [ 'page' => [ 'INNER JOIN', 'page_id=rc_cur_id' ] ] + $rcQuery['joins'];
 
                // Avoid PHP 7.1 warning from passing $this by reference
                $pager = $this;
index c4ea5f8..3b69698 100644 (file)
@@ -284,9 +284,13 @@ class ProtectedPagesPager extends TablePager {
                }
 
                $commentQuery = CommentStore::getStore()->getJoin( 'log_comment' );
+               $actorQuery = ActorMigration::newMigration()->getJoin( 'log_user' );
 
                return [
-                       'tables' => [ 'page', 'page_restrictions', 'log_search', 'logging' ] + $commentQuery['tables'],
+                       'tables' => [
+                               'page', 'page_restrictions', 'log_search',
+                               'logparen' => [ 'logging' ] + $commentQuery['tables'] + $actorQuery['tables'],
+                       ],
                        'fields' => [
                                'pr_id',
                                'page_namespace',
@@ -297,9 +301,8 @@ class ProtectedPagesPager extends TablePager {
                                'pr_expiry',
                                'pr_cascade',
                                'log_timestamp',
-                               'log_user',
                                'log_deleted',
-                       ] + $commentQuery['fields'],
+                       ] + $commentQuery['fields'] + $actorQuery['fields'],
                        'conds' => $conds,
                        'join_conds' => [
                                'log_search' => [
@@ -307,12 +310,12 @@ class ProtectedPagesPager extends TablePager {
                                                'ls_field' => 'pr_id', 'ls_value = ' . $this->mDb->buildStringCast( 'pr_id' )
                                        ]
                                ],
-                               'logging' => [
+                               'logparen' => [
                                        'LEFT JOIN', [
                                                'ls_log_id = log_id'
                                        ]
                                ]
-                       ] + $commentQuery['joins']
+                       ] + $commentQuery['joins'] + $actorQuery['joins']
                ];
        }
 
index fa89c1a..c7d9a26 100644 (file)
@@ -1366,7 +1366,7 @@ class BalanceStack implements IteratorAggregate {
                foreach ( $this->elements as $elt ) {
                        array_push( $r, $elt->localName );
                }
-               return implode( $r, ' ' );
+               return implode( ' ', $r );
        }
 }
 
@@ -1904,7 +1904,7 @@ class Balancer {
                                }
                        );
                        if ( count( $bad ) > 0 ) {
-                               $badstr = implode( array_keys( $bad ), ',' );
+                               $badstr = implode( ',', array_keys( $bad ) );
                                throw new ParameterAssertionException(
                                        '$config',
                                        'Balance attempted with sanitization including ' .
index 13ac6b2..f039d04 100644 (file)
@@ -56,7 +56,7 @@ class ExternalUserNames {
                        if ( $interwikiLookup->isValidInterwiki( $firstIw ) ) {
                                $title = MWNamespace::getCanonicalName( NS_USER ) . ':' . substr( $userName, $pos + 1 );
                                if ( $iw ) {
-                                       $title = join( ':', $iw ) . ':' . $title;
+                                       $title = implode( ':', $iw ) . ':' . $title;
                                }
                                return Title::makeTitle( NS_MAIN, $title, '', $firstIw );
                        }
index eeade49..3102cfc 100644 (file)
@@ -31,6 +31,7 @@ use Wikimedia\IPSet;
 use Wikimedia\ScopedCallback;
 use Wikimedia\Rdbms\Database;
 use Wikimedia\Rdbms\DBExpectedError;
+use Wikimedia\Rdbms\IDatabase;
 
 /**
  * String Some punctuation to prevent editing from broken text-mangling proxies.
@@ -70,7 +71,7 @@ class User implements IDBAccessObject, UserIdentity {
        /**
         * @const int Serialized record version.
         */
-       const VERSION = 11;
+       const VERSION = 12;
 
        /**
         * Exclude user options that are set to their default value.
@@ -111,6 +112,8 @@ class User implements IDBAccessObject, UserIdentity {
                'mGroupMemberships',
                // user_properties table
                'mOptionOverrides',
+               // actor table
+               'mActorId',
        ];
 
        /**
@@ -207,6 +210,8 @@ class User implements IDBAccessObject, UserIdentity {
        public $mId;
        /** @var string */
        public $mName;
+       /** @var int|null */
+       protected $mActorId;
        /** @var string */
        public $mRealName;
 
@@ -251,6 +256,7 @@ class User implements IDBAccessObject, UserIdentity {
         *  - 'defaults'   anonymous user initialised from class defaults
         *  - 'name'       initialise from mName
         *  - 'id'         initialise from mId
+        *  - 'actor'      initialise from mActorId
         *  - 'session'    log in from session if possible
         *
         * Use the User::newFrom*() family of functions to set this.
@@ -309,6 +315,7 @@ class User implements IDBAccessObject, UserIdentity {
         *
         * @see newFromName()
         * @see newFromId()
+        * @see newFromActorId()
         * @see newFromConfirmationCode()
         * @see newFromSession()
         * @see newFromRow()
@@ -399,8 +406,43 @@ class User implements IDBAccessObject, UserIdentity {
                                }
                                break;
                        case 'id':
+                               // Make sure this thread sees its own changes, if the ID isn't 0
+                               if ( $this->mId != 0 ) {
+                                       $lb = MediaWikiServices::getInstance()->getDBLoadBalancer();
+                                       if ( $lb->hasOrMadeRecentMasterChanges() ) {
+                                               $flags |= self::READ_LATEST;
+                                               $this->queryFlagsUsed = $flags;
+                                       }
+                               }
+
                                $this->loadFromId( $flags );
                                break;
+                       case 'actor':
+                               // Make sure this thread sees its own changes
+                               if ( wfGetLB()->hasOrMadeRecentMasterChanges() ) {
+                                       $flags |= self::READ_LATEST;
+                                       $this->queryFlagsUsed = $flags;
+                               }
+
+                               list( $index, $options ) = DBAccessObjectUtils::getDBOptions( $flags );
+                               $row = wfGetDB( $index )->selectRow(
+                                       'actor',
+                                       [ 'actor_user', 'actor_name' ],
+                                       [ 'actor_id' => $this->mActorId ],
+                                       __METHOD__,
+                                       $options
+                               );
+
+                               if ( !$row ) {
+                                       // Ugh.
+                                       $this->loadDefaults();
+                               } elseif ( $row->actor_user ) {
+                                       $this->mId = $row->actor_user;
+                                       $this->loadFromId( $flags );
+                               } else {
+                                       $this->loadDefaults( $row->actor_name );
+                               }
+                               break;
                        case 'session':
                                if ( !$this->loadFromSession() ) {
                                        // Loading from session failed. Load defaults.
@@ -575,6 +617,78 @@ class User implements IDBAccessObject, UserIdentity {
                return $u;
        }
 
+       /**
+        * Static factory method for creation from a given actor ID.
+        *
+        * @since 1.31
+        * @param int $id Valid actor ID
+        * @return User The corresponding User object
+        */
+       public static function newFromActorId( $id ) {
+               global $wgActorTableSchemaMigrationStage;
+
+               if ( $wgActorTableSchemaMigrationStage <= MIGRATION_OLD ) {
+                       throw new BadMethodCallException(
+                               'Cannot use ' . __METHOD__ . ' when $wgActorTableSchemaMigrationStage is MIGRATION_OLD'
+                       );
+               }
+
+               $u = new User;
+               $u->mActorId = $id;
+               $u->mFrom = 'actor';
+               $u->setItemLoaded( 'actor' );
+               return $u;
+       }
+
+       /**
+        * Static factory method for creation from an ID, name, and/or actor ID
+        *
+        * This does not check that the ID, name, and actor ID all correspond to
+        * the same user.
+        *
+        * @since 1.31
+        * @param int|null $userId User ID, if known
+        * @param string|null $userName User name, if known
+        * @param int|null $actorId Actor ID, if known
+        * @return User
+        */
+       public static function newFromAnyId( $userId, $userName, $actorId ) {
+               global $wgActorTableSchemaMigrationStage;
+
+               $user = new User;
+               $user->mFrom = 'defaults';
+
+               if ( $wgActorTableSchemaMigrationStage > MIGRATION_OLD && $actorId !== null ) {
+                       $user->mActorId = (int)$actorId;
+                       if ( $user->mActorId !== 0 ) {
+                               $user->mFrom = 'actor';
+                       }
+                       $user->setItemLoaded( 'actor' );
+               }
+
+               if ( $userName !== null && $userName !== '' ) {
+                       $user->mName = $userName;
+                       $user->mFrom = 'name';
+                       $user->setItemLoaded( 'name' );
+               }
+
+               if ( $userId !== null ) {
+                       $user->mId = (int)$userId;
+                       if ( $user->mId !== 0 ) {
+                               $user->mFrom = 'id';
+                       }
+                       $user->setItemLoaded( 'id' );
+               }
+
+               if ( $user->mFrom === 'defaults' ) {
+                       throw new InvalidArgumentException(
+                               'Cannot create a user with no name, no ID, and no actor ID'
+                       );
+               }
+
+               return $user;
+       }
+
        /**
         * Factory method to fetch whichever user has a given email confirmation code.
         * This code is generated when an account is created or its e-mail address
@@ -1164,6 +1278,7 @@ class User implements IDBAccessObject, UserIdentity {
        public function loadDefaults( $name = false ) {
                $this->mId = 0;
                $this->mName = $name;
+               $this->mActorId = null;
                $this->mRealName = '';
                $this->mEmail = '';
                $this->mOptionOverrides = null;
@@ -1320,11 +1435,29 @@ class User implements IDBAccessObject, UserIdentity {
         *  user_properties   Array with properties out of the user_properties table
         */
        protected function loadFromRow( $row, $data = null ) {
+               global $wgActorTableSchemaMigrationStage;
+
+               if ( !is_object( $row ) ) {
+                       throw new InvalidArgumentException( '$row must be an object' );
+               }
+
                $all = true;
 
                $this->mGroupMemberships = null; // deferred
 
-               if ( isset( $row->user_name ) ) {
+               if ( $wgActorTableSchemaMigrationStage > MIGRATION_OLD ) {
+                       if ( isset( $row->actor_id ) ) {
+                               $this->mActorId = (int)$row->actor_id;
+                               if ( $this->mActorId !== 0 ) {
+                                       $this->mFrom = 'actor';
+                               }
+                               $this->setItemLoaded( 'actor' );
+                       } else {
+                               $all = false;
+                       }
+               }
+
+               if ( isset( $row->user_name ) && $row->user_name !== '' ) {
                        $this->mName = $row->user_name;
                        $this->mFrom = 'name';
                        $this->setItemLoaded( 'name' );
@@ -1341,13 +1474,15 @@ class User implements IDBAccessObject, UserIdentity {
 
                if ( isset( $row->user_id ) ) {
                        $this->mId = intval( $row->user_id );
-                       $this->mFrom = 'id';
+                       if ( $this->mId !== 0 ) {
+                               $this->mFrom = 'id';
+                       }
                        $this->setItemLoaded( 'id' );
                } else {
                        $all = false;
                }
 
-               if ( isset( $row->user_id ) && isset( $row->user_name ) ) {
+               if ( isset( $row->user_id ) && isset( $row->user_name ) && $row->user_name !== '' ) {
                        self::$idCacheByName[$row->user_name] = $row->user_id;
                }
 
@@ -1555,7 +1690,7 @@ class User implements IDBAccessObject, UserIdentity {
         * data (i.e. self::$mCacheVars) is not cleared unless $reloadFrom is given.
         *
         * @param bool|string $reloadFrom Reload user and user_groups table data from a
-        *   given source. May be "name", "id", "defaults", "session", or false for no reload.
+        *   given source. May be "name", "id", "actor", "defaults", "session", or false for no reload.
         */
        public function clearInstanceCache( $reloadFrom = false ) {
                $this->mNewtalk = -1;
@@ -2284,6 +2419,67 @@ class User implements IDBAccessObject, UserIdentity {
                $this->mName = $str;
        }
 
+       /**
+        * Get the user's actor ID.
+        * @since 1.31
+        * @param IDatabase|null $dbw Assign a new actor ID, using this DB handle, if none exists
+        * @return int The actor's ID, or 0 if no actor ID exists and $dbw was null
+        */
+       public function getActorId( IDatabase $dbw = null ) {
+               global $wgActorTableSchemaMigrationStage;
+
+               if ( $wgActorTableSchemaMigrationStage <= MIGRATION_OLD ) {
+                       return 0;
+               }
+
+               if ( !$this->isItemLoaded( 'actor' ) ) {
+                       $this->load();
+               }
+
+               // Currently $this->mActorId might be null if $this was loaded from a
+               // cache entry that was written when $wgActorTableSchemaMigrationStage
+               // was MIGRATION_OLD. Once that is no longer a possibility (i.e. when
+               // User::VERSION is incremented after $wgActorTableSchemaMigrationStage
+               // has been removed), that condition may be removed.
+               if ( $this->mActorId === null || !$this->mActorId && $dbw ) {
+                       $q = [
+                               'actor_user' => $this->getId() ?: null,
+                               'actor_name' => (string)$this->getName(),
+                       ];
+                       if ( $dbw ) {
+                               if ( $q['actor_user'] === null && self::isUsableName( $q['actor_name'] ) ) {
+                                       throw new CannotCreateActorException(
+                                               'Cannot create an actor for a usable name that is not an existing user'
+                                       );
+                               }
+                               if ( $q['actor_name'] === '' ) {
+                                       throw new CannotCreateActorException( 'Cannot create an actor for a user with no name' );
+                               }
+                               $dbw->insert( 'actor', $q, __METHOD__, [ 'IGNORE' ] );
+                               if ( $dbw->affectedRows() ) {
+                                       $this->mActorId = (int)$dbw->insertId();
+                               } else {
+                                       // Outdated cache?
+                                       list( , $options ) = DBAccessObjectUtils::getDBOptions( $this->queryFlagsUsed );
+                                       $this->mActorId = (int)$dbw->selectField( 'actor', 'actor_id', $q, __METHOD__, $options );
+                                       if ( !$this->mActorId ) {
+                                               throw new CannotCreateActorException(
+                                                       "Cannot create actor ID for user_id={$this->getId()} user_name={$this->getName()}"
+                                               );
+                                       }
+                               }
+                               $this->invalidateCache();
+                       } else {
+                               list( $index, $options ) = DBAccessObjectUtils::getDBOptions( $this->queryFlagsUsed );
+                               $db = wfGetDB( $index );
+                               $this->mActorId = (int)$db->selectField( 'actor', 'actor_id', $q, __METHOD__, $options );
+                       }
+                       $this->setItemLoaded( 'actor' );
+               }
+
+               return (int)$this->mActorId;
+       }
+
        /**
         * Get the user's name escaped by underscores.
         * @return string Username escaped by underscores.
@@ -4040,31 +4236,44 @@ class User implements IDBAccessObject, UserIdentity {
                $newTouched = $this->newTouchedTimestamp();
 
                $dbw = wfGetDB( DB_MASTER );
-               $dbw->update( 'user',
-                       [ /* SET */
-                               'user_name' => $this->mName,
-                               'user_real_name' => $this->mRealName,
-                               'user_email' => $this->mEmail,
-                               'user_email_authenticated' => $dbw->timestampOrNull( $this->mEmailAuthenticated ),
-                               'user_touched' => $dbw->timestamp( $newTouched ),
-                               'user_token' => strval( $this->mToken ),
-                               'user_email_token' => $this->mEmailToken,
-                               'user_email_token_expires' => $dbw->timestampOrNull( $this->mEmailTokenExpires ),
-                       ], $this->makeUpdateConditions( $dbw, [ /* WHERE */
-                               'user_id' => $this->mId,
-                       ] ), __METHOD__
-               );
-
-               if ( !$dbw->affectedRows() ) {
-                       // Maybe the problem was a missed cache update; clear it to be safe
-                       $this->clearSharedCache( 'refresh' );
-                       // User was changed in the meantime or loaded with stale data
-                       $from = ( $this->queryFlagsUsed & self::READ_LATEST ) ? 'master' : 'replica';
-                       throw new MWException(
-                               "CAS update failed on user_touched for user ID '{$this->mId}' (read from $from);" .
-                               " the version of the user to be saved is older than the current version."
+               $dbw->doAtomicSection( __METHOD__, function ( $dbw, $fname ) use ( $newTouched ) {
+                       global $wgActorTableSchemaMigrationStage;
+
+                       $dbw->update( 'user',
+                               [ /* SET */
+                                       'user_name' => $this->mName,
+                                       'user_real_name' => $this->mRealName,
+                                       'user_email' => $this->mEmail,
+                                       'user_email_authenticated' => $dbw->timestampOrNull( $this->mEmailAuthenticated ),
+                                       'user_touched' => $dbw->timestamp( $newTouched ),
+                                       'user_token' => strval( $this->mToken ),
+                                       'user_email_token' => $this->mEmailToken,
+                                       'user_email_token_expires' => $dbw->timestampOrNull( $this->mEmailTokenExpires ),
+                               ], $this->makeUpdateConditions( $dbw, [ /* WHERE */
+                                       'user_id' => $this->mId,
+                               ] ), $fname
                        );
-               }
+
+                       if ( !$dbw->affectedRows() ) {
+                               // Maybe the problem was a missed cache update; clear it to be safe
+                               $this->clearSharedCache( 'refresh' );
+                               // User was changed in the meantime or loaded with stale data
+                               $from = ( $this->queryFlagsUsed & self::READ_LATEST ) ? 'master' : 'replica';
+                               throw new MWException(
+                                       "CAS update failed on user_touched for user ID '{$this->mId}' (read from $from);" .
+                                       " the version of the user to be saved is older than the current version."
+                               );
+                       }
+
+                       if ( $wgActorTableSchemaMigrationStage > MIGRATION_OLD ) {
+                               $dbw->update(
+                                       'actor',
+                                       [ 'actor_name' => $this->mName ],
+                                       [ 'actor_user' => $this->mId ],
+                                       $fname
+                               );
+                       }
+               } );
 
                $this->mTouched = $newTouched;
                $this->saveOptions();
@@ -4149,13 +4358,19 @@ class User implements IDBAccessObject, UserIdentity {
                foreach ( $params as $name => $value ) {
                        $fields["user_$name"] = $value;
                }
-               $dbw->insert( 'user', $fields, __METHOD__, [ 'IGNORE' ] );
-               if ( $dbw->affectedRows() ) {
-                       $newUser = self::newFromId( $dbw->insertId() );
-               } else {
-                       $newUser = null;
-               }
-               return $newUser;
+
+               return $dbw->doAtomicSection( __METHOD__, function ( $dbw, $fname ) use ( $fields ) {
+                       $dbw->insert( 'user', $fields, $fname, [ 'IGNORE' ] );
+                       if ( $dbw->affectedRows() ) {
+                               $newUser = self::newFromId( $dbw->insertId() );
+                               // Load the user from master to avoid replica lag
+                               $newUser->load( self::READ_LATEST );
+                               $newUser->updateActorId( $dbw );
+                       } else {
+                               $newUser = null;
+                       }
+                       return $newUser;
+               } );
        }
 
        /**
@@ -4196,55 +4411,79 @@ class User implements IDBAccessObject, UserIdentity {
 
                $this->mTouched = $this->newTouchedTimestamp();
 
-               $noPass = PasswordFactory::newInvalidPassword()->toString();
-
                $dbw = wfGetDB( DB_MASTER );
-               $dbw->insert( 'user',
-                       [
-                               'user_name' => $this->mName,
-                               'user_password' => $noPass,
-                               'user_newpassword' => $noPass,
-                               'user_email' => $this->mEmail,
-                               'user_email_authenticated' => $dbw->timestampOrNull( $this->mEmailAuthenticated ),
-                               'user_real_name' => $this->mRealName,
-                               'user_token' => strval( $this->mToken ),
-                               'user_registration' => $dbw->timestamp( $this->mRegistration ),
-                               'user_editcount' => 0,
-                               'user_touched' => $dbw->timestamp( $this->mTouched ),
-                       ], __METHOD__,
-                       [ 'IGNORE' ]
-               );
-               if ( !$dbw->affectedRows() ) {
-                       // Use locking reads to bypass any REPEATABLE-READ snapshot.
-                       $this->mId = $dbw->selectField(
-                               'user',
-                               'user_id',
-                               [ 'user_name' => $this->mName ],
-                               __METHOD__,
-                               [ 'LOCK IN SHARE MODE' ]
+               $status = $dbw->doAtomicSection( __METHOD__, function ( $dbw, $fname ) {
+                       $noPass = PasswordFactory::newInvalidPassword()->toString();
+                       $dbw->insert( 'user',
+                               [
+                                       'user_name' => $this->mName,
+                                       'user_password' => $noPass,
+                                       'user_newpassword' => $noPass,
+                                       'user_email' => $this->mEmail,
+                                       'user_email_authenticated' => $dbw->timestampOrNull( $this->mEmailAuthenticated ),
+                                       'user_real_name' => $this->mRealName,
+                                       'user_token' => strval( $this->mToken ),
+                                       'user_registration' => $dbw->timestamp( $this->mRegistration ),
+                                       'user_editcount' => 0,
+                                       'user_touched' => $dbw->timestamp( $this->mTouched ),
+                               ], $fname,
+                               [ 'IGNORE' ]
                        );
-                       $loaded = false;
-                       if ( $this->mId ) {
-                               if ( $this->loadFromDatabase( self::READ_LOCKING ) ) {
-                                       $loaded = true;
+                       if ( !$dbw->affectedRows() ) {
+                               // Use locking reads to bypass any REPEATABLE-READ snapshot.
+                               $this->mId = $dbw->selectField(
+                                       'user',
+                                       'user_id',
+                                       [ 'user_name' => $this->mName ],
+                                       __METHOD__,
+                                       [ 'LOCK IN SHARE MODE' ]
+                               );
+                               $loaded = false;
+                               if ( $this->mId ) {
+                                       if ( $this->loadFromDatabase( self::READ_LOCKING ) ) {
+                                               $loaded = true;
+                                       }
                                }
+                               if ( !$loaded ) {
+                                       throw new MWException( __METHOD__ . ": hit a key conflict attempting " .
+                                               "to insert user '{$this->mName}' row, but it was not present in select!" );
+                               }
+                               return Status::newFatal( 'userexists' );
                        }
-                       if ( !$loaded ) {
-                               throw new MWException( __METHOD__ . ": hit a key conflict attempting " .
-                                       "to insert user '{$this->mName}' row, but it was not present in select!" );
-                       }
-                       return Status::newFatal( 'userexists' );
+                       $this->mId = $dbw->insertId();
+                       self::$idCacheByName[$this->mName] = $this->mId;
+                       $this->updateActorId( $dbw );
+
+                       return Status::newGood();
+               } );
+               if ( !$status->isGood() ) {
+                       return $status;
                }
-               $this->mId = $dbw->insertId();
-               self::$idCacheByName[$this->mName] = $this->mId;
 
-               // Clear instance cache other than user table data, which is already accurate
+               // Clear instance cache other than user table data and actor, which is already accurate
                $this->clearInstanceCache();
 
                $this->saveOptions();
                return Status::newGood();
        }
 
+       /**
+        * Update the actor ID after an insert
+        * @param IDatabase $dbw Writable database handle
+        */
+       private function updateActorId( IDatabase $dbw ) {
+               global $wgActorTableSchemaMigrationStage;
+
+               if ( $wgActorTableSchemaMigrationStage > MIGRATION_OLD ) {
+                       $dbw->insert(
+                               'actor',
+                               [ 'actor_user' => $this->mId, 'actor_name' => $this->mName ],
+                               __METHOD__
+                       );
+                       $this->mActorId = (int)$dbw->insertId();
+               }
+       }
+
        /**
         * If this user is logged-in and blocked,
         * block any IP address they've successfully logged in from.
@@ -4733,10 +4972,14 @@ class User implements IDBAccessObject, UserIdentity {
                        return false; // anons
                }
                $dbr = wfGetDB( DB_REPLICA );
-               $time = $dbr->selectField( 'revision', 'rev_timestamp',
-                       [ 'rev_user' => $this->getId() ],
+               $actorWhere = ActorMigration::newMigration()->getWhere( $dbr, 'rev_user', $this );
+               $time = $dbr->selectField(
+                       [ 'revision' ] + $actorWhere['tables'],
+                       'rev_timestamp',
+                       [ $actorWhere['conds'] ],
                        __METHOD__,
-                       [ 'ORDER BY' => 'rev_timestamp ASC' ]
+                       [ 'ORDER BY' => 'rev_timestamp ASC' ],
+                       $actorWhere['joins']
                );
                if ( !$time ) {
                        return false; // no edits
@@ -5184,11 +5427,14 @@ class User implements IDBAccessObject, UserIdentity {
                // Pull from a replica DB to be less cruel to servers
                // Accuracy isn't the point anyway here
                $dbr = wfGetDB( DB_REPLICA );
+               $actorWhere = ActorMigration::newMigration()->getWhere( $dbr, 'rev_user', $this );
                $count = (int)$dbr->selectField(
-                       'revision',
-                       'COUNT(rev_user)',
-                       [ 'rev_user' => $this->getId() ],
-                       __METHOD__
+                       [ 'revision' ] + $actorWhere['tables'],
+                       'COUNT(*)',
+                       [ $actorWhere['conds'] ],
+                       __METHOD__,
+                       [],
+                       $actorWhere['joins']
                );
                $count = $count + $add;
 
@@ -5537,7 +5783,9 @@ class User implements IDBAccessObject, UserIdentity {
         *   - joins: (array) to include in the `$join_conds` to `IDatabase->select()`
         */
        public static function getQueryInfo() {
-               return [
+               global $wgActorTableSchemaMigrationStage;
+
+               $ret = [
                        'tables' => [ 'user' ],
                        'fields' => [
                                'user_id',
@@ -5554,6 +5802,15 @@ class User implements IDBAccessObject, UserIdentity {
                        ],
                        'joins' => [],
                ];
+               if ( $wgActorTableSchemaMigrationStage > MIGRATION_OLD ) {
+                       $ret['tables']['user_actor'] = 'actor';
+                       $ret['fields'][] = 'user_actor.actor_id';
+                       $ret['joins']['user_actor'] = [
+                               $wgActorTableSchemaMigrationStage === MIGRATION_NEW ? 'JOIN' : 'LEFT JOIN',
+                               [ 'user_actor.actor_user = user_id' ]
+                       ];
+               }
+               return $ret;
        }
 
        /**
index 57a0408..d02a678 100644 (file)
@@ -45,7 +45,13 @@ interface UserIdentity {
         */
        public function getName();
 
-       // TODO: in the future, we should also provide access to the actor ID here.
+       /**
+        * @since 1.31
+        *
+        * @return int The user's actor ID. May be 0 if no actor ID is set.
+        */
+       public function getActorId();
+
        // TODO: we may want to (optionally?) provide a global ID, see CentralIdLookup.
 
 }
index e728264..120f31f 100644 (file)
@@ -41,16 +41,24 @@ class UserIdentityValue implements UserIdentity {
         */
        private $name;
 
+       /**
+        * @var int
+        */
+       private $actor;
+
        /**
         * @param int $id
         * @param string $name
+        * @param int $actor
         */
-       public function __construct( $id, $name ) {
+       public function __construct( $id, $name, $actor ) {
                Assert::parameterType( 'integer', $id, '$id' );
                Assert::parameterType( 'string', $name, '$name' );
+               Assert::parameterType( 'integer', $actor, '$actor' );
 
                $this->id = $id;
                $this->name = $name;
+               $this->actor = $actor;
        }
 
        /**
@@ -67,4 +75,11 @@ class UserIdentityValue implements UserIdentity {
                return $this->name;
        }
 
+       /**
+        * @return int The user's actor ID. May be 0 if no actor ID has been assigned.
+        */
+       public function getActorId() {
+               return $this->actor;
+       }
+
 }
index abe9b89..412fdf5 100644 (file)
@@ -59,12 +59,17 @@ class WatchedItemQueryService {
        /** @var CommentStore */
        private $commentStore;
 
+       /** @var ActorMigration */
+       private $actorMigration;
+
        public function __construct(
                LoadBalancer $loadBalancer,
-               CommentStore $commentStore
+               CommentStore $commentStore,
+               ActorMigration $actorMigration
        ) {
                $this->loadBalancer = $loadBalancer;
                $this->commentStore = $commentStore;
+               $this->actorMigration = $actorMigration;
        }
 
        /**
@@ -335,6 +340,14 @@ class WatchedItemQueryService {
                if ( in_array( self::INCLUDE_TAGS, $options['includeFields'] ) ) {
                        $tables[] = 'tag_summary';
                }
+               if ( in_array( self::INCLUDE_USER, $options['includeFields'] ) ||
+                       in_array( self::INCLUDE_USER_ID, $options['includeFields'] ) ||
+                       in_array( self::FILTER_ANON, $options['filters'] ) ||
+                       in_array( self::FILTER_NOT_ANON, $options['filters'] ) ||
+                       array_key_exists( 'onlyByUser', $options ) || array_key_exists( 'notByUser', $options )
+               ) {
+                       $tables += $this->actorMigration->getJoin( 'rc_user' )['tables'];
+               }
                return $tables;
        }
 
@@ -367,10 +380,10 @@ class WatchedItemQueryService {
                        $fields = array_merge( $fields, [ 'rc_type', 'rc_minor', 'rc_bot' ] );
                }
                if ( in_array( self::INCLUDE_USER, $options['includeFields'] ) ) {
-                       $fields[] = 'rc_user_text';
+                       $fields['rc_user_text'] = $this->actorMigration->getJoin( 'rc_user' )['fields']['rc_user_text'];
                }
                if ( in_array( self::INCLUDE_USER_ID, $options['includeFields'] ) ) {
-                       $fields[] = 'rc_user';
+                       $fields['rc_user'] = $this->actorMigration->getJoin( 'rc_user' )['fields']['rc_user'];
                }
                if ( in_array( self::INCLUDE_COMMENT, $options['includeFields'] ) ) {
                        $fields += $this->commentStore->getJoin( 'rc_comment' )['fields'];
@@ -469,9 +482,13 @@ class WatchedItemQueryService {
                }
 
                if ( in_array( self::FILTER_ANON, $options['filters'] ) ) {
-                       $conds[] = 'rc_user = 0';
+                       $conds[] = $this->actorMigration->isAnon(
+                               $this->actorMigration->getJoin( 'rc_user' )['fields']['rc_user']
+                       );
                } elseif ( in_array( self::FILTER_NOT_ANON, $options['filters'] ) ) {
-                       $conds[] = 'rc_user != 0';
+                       $conds[] = $this->actorMigration->isNotAnon(
+                               $this->actorMigration->getJoin( 'rc_user' )['fields']['rc_user']
+                       );
                }
 
                if ( $user->useRCPatrol() || $user->useNPPatrol() ) {
@@ -523,9 +540,11 @@ class WatchedItemQueryService {
                $conds = [];
 
                if ( array_key_exists( 'onlyByUser', $options ) ) {
-                       $conds['rc_user_text'] = $options['onlyByUser'];
+                       $byUser = User::newFromName( $options['onlyByUser'], false );
+                       $conds[] = $this->actorMigration->getWhere( $db, 'rc_user', $byUser )['conds'];
                } elseif ( array_key_exists( 'notByUser', $options ) ) {
-                       $conds[] = 'rc_user_text != ' . $db->addQuotes( $options['notByUser'] );
+                       $byUser = User::newFromName( $options['notByUser'], false );
+                       $conds[] = 'NOT(' . $this->actorMigration->getWhere( $db, 'rc_user', $byUser )['conds'] . ')';
                }
 
                // Avoid brute force searches (T19342)
@@ -685,6 +704,14 @@ class WatchedItemQueryService {
                if ( in_array( self::INCLUDE_TAGS, $options['includeFields'] ) ) {
                        $joinConds['tag_summary'] = [ 'LEFT JOIN', [ 'rc_id=ts_rc_id' ] ];
                }
+               if ( in_array( self::INCLUDE_USER, $options['includeFields'] ) ||
+                       in_array( self::INCLUDE_USER_ID, $options['includeFields'] ) ||
+                       in_array( self::FILTER_ANON, $options['filters'] ) ||
+                       in_array( self::FILTER_NOT_ANON, $options['filters'] ) ||
+                       array_key_exists( 'onlyByUser', $options ) || array_key_exists( 'notByUser', $options )
+               ) {
+                       $joinConds += $this->actorMigration->getJoin( 'rc_user' )['joins'];
+               }
                return $joinConds;
        }
 
index 6966832..6fb4544 100644 (file)
                "resources/src/mediawiki.special",
                "resources/src/mediawiki.toolbar",
                "resources/src/mediawiki.widgets",
-               "resources/src/mediawiki.widgets.visibleByteLimit",
+               "resources/src/mediawiki.widgets.visibleLengthLimit",
                "resources/src/jquery/jquery.accessKeyLabel.js",
                "resources/src/jquery/jquery.byteLength.js",
-               "resources/src/jquery/jquery.byteLimit.js",
                "resources/src/jquery/jquery.checkboxShiftClick.js",
                "resources/src/jquery/jquery.colorUtil.js",
                "resources/src/jquery/jquery.confirmable.js",
                "resources/src/jquery/jquery.footHovzer.js",
                "resources/src/jquery/jquery.getAttrs.js",
                "resources/src/jquery/jquery.hidpi.js",
+               "resources/src/jquery/jquery.lengthLimit.js",
                "resources/src/jquery/jquery.localize.js",
                "resources/src/jquery/jquery.makeCollapsible.js",
                "resources/src/jquery/jquery.spinner.js",
index 084a2e7..fc8ef87 100644 (file)
@@ -3472,27 +3472,103 @@ class Language {
        }
 
        /**
-        * Truncate a string to a specified length in bytes, appending an optional
-        * string (e.g. for ellipses)
+        * This method is deprecated since 1.31 and kept as alias for truncateForDatabase, which
+        * has replaced it. This method provides truncation suitable for DB.
         *
         * The database offers limited byte lengths for some columns in the database;
         * multi-byte character sets mean we need to ensure that only whole characters
-        * are included, otherwise broken characters can be passed to the user
+        * are included, otherwise broken characters can be passed to the user.
         *
-        * If $length is negative, the string will be truncated from the beginning
+        * @deprecated since 1.31, use truncateForDatabase or truncateForVisual as appropriate.
         *
         * @param string $string String to truncate
-        * @param int $length Maximum length (including ellipses)
+        * @param int $length Maximum length (including ellipsis)
         * @param string $ellipsis String to append to the truncated text
         * @param bool $adjustLength Subtract length of ellipsis from $length.
         *      $adjustLength was introduced in 1.18, before that behaved as if false.
         * @return string
         */
        function truncate( $string, $length, $ellipsis = '...', $adjustLength = true ) {
+               return $this->truncateForDatabase( $string, $length, $ellipsis, $adjustLength );
+       }
+
+       /**
+        * Truncate a string to a specified length in bytes, appending an optional
+        * string (e.g. for ellipsis)
+        *
+        * If $length is negative, the string will be truncated from the beginning
+        *
+        * @since 1.31
+        *
+        * @param string $string String to truncate
+        * @param int $length Maximum length in bytes
+        * @param string $ellipsis String to append to the end of truncated text
+        * @param bool $adjustLength Subtract length of ellipsis from $length
+        *
+        * @return string
+        */
+       function truncateForDatabase( $string, $length, $ellipsis = '...', $adjustLength = true ) {
+               return $this->truncateInternal(
+                       $string, $length, $ellipsis, $adjustLength, 'strlen', 'substr'
+               );
+       }
+
+       /**
+        * Truncate a string to a specified number of characters, appending an optional
+        * string (e.g. for ellipsis).
+        *
+        * This provides multibyte version of truncate() method of this class, suitable for truncation
+        * based on number of characters, instead of number of bytes.
+        *
+        * If $length is negative, the string will be truncated from the beginning.
+        *
+        * @since 1.31
+        *
+        * @param string $string String to truncate
+        * @param int $length Maximum number of characters
+        * @param string $ellipsis String to append to the end of truncated text
+        * @param bool $adjustLength Subtract length of ellipsis from $length
+        *
+        * @return string
+        */
+       function truncateForVisual( $string, $length, $ellipsis = '...', $adjustLength = true ) {
+               // Passing encoding to mb_strlen and mb_substr is optional.
+               // Encoding defaults to mb_internal_encoding(), which is set to UTF-8 in Setup.php, so
+               // explicit specification of encoding is skipped.
+               // Note: Both multibyte methods are callables invoked in truncateInternal.
+               return $this->truncateInternal(
+                       $string, $length, $ellipsis, $adjustLength, 'mb_strlen', 'mb_substr'
+               );
+       }
+
+       /**
+        * Internal method used for truncation. This method abstracts text truncation into
+        * one common method, allowing users to provide length measurement function and
+        * function for finding substring.
+        *
+        * For usages, see truncateForDatabase and truncateForVisual.
+        *
+        * @param string $string String to truncate
+        * @param int $length Maximum length of final text
+        * @param string $ellipsis String to append to the end of truncated text
+        * @param bool $adjustLength Subtract length of ellipsis from $length
+        * @param callable $measureLength Callable function used for determining the length of text
+        * @param callable $getSubstring Callable function used for getting the substrings
+        *
+        * @return string
+        */
+       private function truncateInternal(
+               $string, $length, $ellipsis = '...', $adjustLength = true, $measureLength, $getSubstring
+       ) {
+               if ( !is_callable( $measureLength ) || !is_callable( $getSubstring ) ) {
+                       throw new InvalidArgumentException( 'Invalid callback provided' );
+               }
+
                # Check if there is no need to truncate
-               if ( strlen( $string ) <= abs( $length ) ) {
+               if ( $measureLength( $string ) <= abs( $length ) ) {
                        return $string; // no need to truncate
                }
+
                # Use the localized ellipsis character
                if ( $ellipsis == '...' ) {
                        $ellipsis = wfMessage( 'ellipsis' )->inLanguage( $this )->escaped();
@@ -3500,31 +3576,33 @@ class Language {
                if ( $length == 0 ) {
                        return $ellipsis; // convention
                }
+
                $stringOriginal = $string;
                # If ellipsis length is >= $length then we can't apply $adjustLength
-               if ( $adjustLength && strlen( $ellipsis ) >= abs( $length ) ) {
+               if ( $adjustLength && $measureLength( $ellipsis ) >= abs( $length ) ) {
                        $string = $ellipsis; // this can be slightly unexpected
                # Otherwise, truncate and add ellipsis...
                } else {
-                       $eLength = $adjustLength ? strlen( $ellipsis ) : 0;
+                       $ellipsisLength = $adjustLength ? $measureLength( $ellipsis ) : 0;
                        if ( $length > 0 ) {
-                               $length -= $eLength;
-                               $string = substr( $string, 0, $length ); // xyz...
+                               $length -= $ellipsisLength;
+                               $string = $getSubstring( $string, 0, $length ); // xyz...
                                $string = $this->removeBadCharLast( $string );
                                $string = rtrim( $string );
                                $string = $string . $ellipsis;
                        } else {
-                               $length += $eLength;
-                               $string = substr( $string, $length ); // ...xyz
+                               $length += $ellipsisLength;
+                               $string = $getSubstring( $string, $length ); // ...xyz
                                $string = $this->removeBadCharFirst( $string );
                                $string = ltrim( $string );
                                $string = $ellipsis . $string;
                        }
                }
+
                # Do not truncate if the ellipsis makes the string longer/equal (T24181).
                # This check is *not* redundant if $adjustLength, due to the single case where
                # LEN($ellipsis) > ABS($limit arg); $stringOriginal could be shorter than $string.
-               if ( strlen( $string ) < strlen( $stringOriginal ) ) {
+               if ( $measureLength( $string ) < $measureLength( $stringOriginal ) ) {
                        return $string;
                } else {
                        return $stringOriginal;
index 982f97a..2252645 100644 (file)
@@ -148,6 +148,7 @@ class Names {
                'en-gb' => 'British English', # British English
                'eo' => 'Esperanto', # Esperanto
                'es' => 'español', # Spanish
+               'es-formal' => 'español (formal)', # Spanish formal address
                'et' => 'eesti', # Estonian
                'eu' => 'euskara', # Basque
                'ext' => 'estremeñu', # Extremaduran
@@ -260,7 +261,7 @@ class Names {
                'ky' => 'Кыргызча', # Kirghiz
                'la' => 'Latina', # Latin
                'lad' => 'Ladino', # Ladino
-               'lb' => 'Lëtzebuergesch', # Luxemburguish
+               'lb' => 'Lëtzebuergesch', # Luxembourgish
                'lbe' => 'лакку', # Lak
                'lez' => 'лезги', # Lezgi
                'lfn' => 'Lingua Franca Nova', # Lingua Franca Nova
index 6633df2..c773b48 100644 (file)
@@ -89,6 +89,7 @@ public static $zh2Hant = [
 '䙌' => '䙡',
 '䙓' => '襬',
 '䜣' => '訢',
+'䜤' => '鿁',
 '䜥' => '𧩙',
 '䜧' => '䜀',
 '䜩' => '讌',
@@ -123,6 +124,7 @@ public static $zh2Hant = [
 '䲡' => '鰌',
 '䲢' => '鰧',
 '䲣' => '䱷',
+'䲤' => '鿐',
 '䴓' => '鳾',
 '䴔' => '鵁',
 '䴕' => '鴷',
@@ -2652,6 +2654,10 @@ public static $zh2Hant = [
 '龚' => '龔',
 '龛' => '龕',
 '龟' => '龜',
+'鿎' => '䃮',
+'鿏' => '䥑',
+'鿒' => '鿓',
+'鿔' => '鎶',
 '𠆲' => '儣',
 '𠆿' => '𠌥',
 '𠉂' => '㒓',
@@ -2696,6 +2702,8 @@ public static $zh2Hant = [
 '𣘴' => '檭',
 '𣘷' => '𣝕',
 '𣭤' => '𣯴',
+'𣲗' => '湋',
+'𣲘' => '潕',
 '𣶩' => '澅',
 '𣶫' => '𣿉',
 '𣸣' => '濆',
@@ -2712,6 +2720,7 @@ public static $zh2Hant = [
 '𤞤' => '玁',
 '𤠋' => '㺏',
 '𤦀' => '瓕',
+'𤩽' => '瓛',
 '𤳄' => '𤳸',
 '𤶧' => '𤸫',
 '𤽯' => '㿧',
@@ -2954,52 +2963,192 @@ public static $zh2Hant = [
 '𪚏' => '𪘀',
 '𪚐' => '𪘯',
 '𪞝' => '凙',
+'𪟝' => '勣',
 '𪡏' => '嗹',
 '𪢮' => '圞',
+'𪣻' => '塿',
 '𪨊' => '㞞',
 '𪨗' => '屩',
+'𪨶' => '輋',
+'𪩘' => '巘',
 '𪻐' => '瑽',
 '𪾢' => '睍',
 '𫁡' => '鴗',
 '𫂈' => '䉬',
+'𫄧' => '綖',
 '𫄨' => '絺',
+'𫄷' => '繶',
 '𫄸' => '纁',
+'𫇭' => '蔿',
 '𫌀' => '襀',
 '𫌨' => '覼',
 '𫍙' => '訑',
 '𫍢' => '譊',
+'𫍣' => '詷',
+'𫍯' => '諴',
 '𫍰' => '諰',
 '𫍲' => '謏',
+'𫍽' => '譞',
 '𫏋' => '蹻',
 '𫐄' => '軏',
 '𫐆' => '轣',
 '𫐉' => '軨',
 '𫐐' => '輗',
 '𫐓' => '輮',
+'𫑡' => '鄳',
 '𫓧' => '鈇',
 '𫓩' => '鏦',
+'𫓯' => '銈',
+'𫓶' => '鋗',
+'𫓹' => '錤',
+'𫔍' => '鐇',
 '𫔎' => '鐍',
+'𫔶' => '闑',
+'𫖮' => '顗',
+'𫖯' => '頫',
+'𫖳' => '頵',
 '𫖸' => '願',
 '𫗠' => '餦',
 '𫗦' => '餔',
 '𫗧' => '餗',
 '𫗮' => '餭',
 '𫗴' => '饘',
+'𫘜' => '馼',
 '𫘝' => '駃',
 '𫘣' => '駻',
 '𫘤' => '騃',
+'𫘦' => '騊',
+'𫘧' => '騄',
 '𫘨' => '騠',
+'𫘪' => '騵',
+'𫘬' => '騱',
 '𫚈' => '鱮',
 '𫚉' => '魟',
 '𫚒' => '鮄',
 '𫚔' => '鮰',
 '𫚕' => '鰤',
+'𫚖' => '鮆',
 '𫚙' => '鯆',
+'𫚭' => '鱲',
 '𫛛' => '鳷',
 '𫛞' => '鴃',
 '𫛢' => '鸋',
+'𫛭' => '鵟',
 '𫛶' => '鶒',
 '𫛸' => '鶗',
+'𫞩' => '璊',
+'𫟅' => '綡',
+'𫟦' => '䡵',
+'𫟹' => '鉷',
+'𫟼' => '鐽',
+'𫠆' => '頍',
+'𫠊' => '䮄',
+'𫠜' => '齯',
+'𫢸' => '僤',
+'𫫇' => '噁',
+'𫭟' => '塸',
+'𫭢' => '埨',
+'𫭼' => '𡑍',
+'𫮃' => '墠',
+'𫰛' => '娙',
+'𫵷' => '㠣',
+'𫶇' => '嵽',
+'𫷷' => '廞',
+'𫸩' => '彄',
+'𬀩' => '暐',
+'𬀪' => '晛',
+'𬂩' => '梜',
+'𬃊' => '櫍',
+'𬇕' => '澫',
+'𬇙' => '浿',
+'𬇹' => '漍',
+'𬉼' => '熰',
+'𬊈' => '燖',
+'𬊤' => '燀',
+'𬍛' => '瓅',
+'𬍡' => '璗',
+'𬍤' => '璕',
+'𬒈' => '礐',
+'𬒗' => '𥗽',
+'𬕂' => '篢',
+'𬘓' => '紃',
+'𬘘' => '紞',
+'𬘡' => '絪',
+'𬘩' => '綎',
+'𬘫' => '綄',
+'𬘬' => '綪',
+'𬘭' => '綝',
+'𬘯' => '綧',
+'𬙂' => '縯',
+'𬙊' => '纆',
+'𬙋' => '纕',
+'𬜬' => '蔄',
+'𬜯' => '䓣',
+'𬟁' => '虉',
+'𬟽' => '蝀',
+'𬣙' => '訏',
+'𬣞' => '詝',
+'𬣡' => '諓',
+'𬣳' => '詪',
+'𬤇' => '諲',
+'𬤊' => '諟',
+'𬤝' => '譓',
+'𬨂' => '軝',
+'𬨎' => '輶',
+'𬩽' => '鄩',
+'𬪩' => '醲',
+'𬬩' => '釴',
+'𬬭' => '錀',
+'𬬮' => '鋹',
+'𬬱' => '釿',
+'𬬸' => '鉥',
+'𬬹' => '鉮',
+'𬬻' => '鑪',
+'𬬿' => '鉊',
+'𬭁' => '鉧',
+'𬭊' => '𨧀',
+'𬭎' => '鋐',
+'𬭚' => '錞',
+'𬭛' => '𨨏',
+'𬭤' => '鍭',
+'𬭩' => '鎓',
+'𬭬' => '鏏',
+'𬭯' => '䥕',
+'𬭳' => '𨭎',
+'𬭶' => '𨭆',
+'𬭸' => '鏻',
+'𬭼' => '鐩',
+'𬮱' => '闉',
+'𬮿' => '隑',
+'𬯀' => '隮',
+'𬯎' => '隤',
+'𬱖' => '頔',
+'𬱟' => '頠',
+'𬳵' => '駓',
+'𬳶' => '駉',
+'𬳽' => '駪',
+'𬳿' => '駼',
+'𬴂' => '騑',
+'𬴃' => '騞',
+'𬴊' => '驎',
+'𬶋' => '鮈',
+'𬶍' => '鮀',
+'𬶏' => '鮠',
+'𬶐' => '鮡',
+'𬶟' => '鯻',
+'𬶠' => '鰊',
+'𬶨' => '鱀',
+'𬶭' => '鰶',
+'𬶮' => '鱚',
+'𬷕' => '鵏',
+'𬸘' => '鶠',
+'𬸚' => '鸑',
+'𬸣' => '鶱',
+'𬸦' => '鷟',
+'𬸪' => '鷭',
+'𬹼' => '齘',
+'𬺈' => '齮',
+'𬺓' => '齼',
 '0出现' => '0出現',
 '0出現' => '0出現',
 '0出線' => '0出線',
@@ -3239,6 +3388,7 @@ public static $zh2Hant = [
 '中岳' => '中嶽',
 '中庄子' => '中庄子',
 '中文里' => '中文裡',
+'中断发' => '中斷發',
 '中签了' => '中簽了',
 '中签名' => '中簽名',
 '中签字' => '中簽字',
@@ -3328,7 +3478,6 @@ public static $zh2Hant = [
 '干刻版' => '乾刻版',
 '干剥剥' => '乾剝剝',
 '干卦' => '乾卦',
-'干和' => '乾和',
 '干咳' => '乾咳',
 '干咽' => '乾咽',
 '干哥' => '乾哥',
@@ -3464,7 +3613,6 @@ public static $zh2Hant = [
 '干电' => '乾電',
 '干霍乱' => '乾霍亂',
 '干颡' => '乾顙',
-'干台' => '乾颱',
 '干食' => '乾食',
 '干饭' => '乾飯',
 '干馆' => '乾館',
@@ -3858,6 +4006,7 @@ public static $zh2Hant = [
 '倛丑' => '倛醜',
 '借鉴' => '借鑑',
 '倦游' => '倦遊',
+'假发票' => '假發票',
 '假里' => '假裡',
 '假托' => '假託',
 '假发' => '假髮',
@@ -4229,6 +4378,8 @@ public static $zh2Hant = [
 '南回铁路' => '南迴鐵路',
 '南游' => '南遊',
 '博采' => '博採',
+'博杰普尔' => '博杰普爾',
+'博杰普爾' => '博杰普爾',
 '博尔术' => '博爾朮',
 '卜云吉' => '卜云吉',
 '占了卜' => '占了卜',
@@ -5027,6 +5178,8 @@ public static $zh2Hant = [
 '几只' => '幾隻',
 '几出' => '幾齣',
 '广部' => '广部',
+'庄內' => '庄內',
+'庄内地方' => '庄內地方',
 '庄司' => '庄司',
 '床席' => '床蓆',
 '店里' => '店裡',
@@ -7162,7 +7315,7 @@ public static $zh2Hant = [
 '缠斗' => '纏鬥',
 '坛子' => '罈子',
 '坛坛罐罐' => '罈罈罐罐',
-'坛' => '罈騞',
+'坛𬴃' => '罈騞',
 '置言成范' => '置言成範',
 '罗马历' => '羅馬曆',
 '罗马历代' => '羅馬歷代',
@@ -7531,6 +7684,7 @@ public static $zh2Hant = [
 '行事历史' => '行事歷史',
 '行凶' => '行兇',
 '行家里手' => '行家裡手',
+'街庄' => '街庄',
 '卫后庄公' => '衛後莊公',
 '卫星钟' => '衛星鐘',
 '冲上' => '衝上',
@@ -8972,6 +9126,7 @@ public static $zh2Hant = [
 '松绑' => '鬆綁',
 '松紧' => '鬆緊',
 '松缓' => '鬆緩',
+'松耦合' => '鬆耦合',
 '松脆' => '鬆脆',
 '松脱' => '鬆脫',
 '松蛋' => '鬆蛋',
@@ -9310,6 +9465,7 @@ public static $zh2Hans = [
 '㞞' => '𪨊',
 '㠀' => '岛',
 '㠏' => '㟆',
+'㠣' => '𫵷',
 '㠯' => '以',
 '㠶' => '帆',
 '㡌' => '帽',
@@ -9343,6 +9499,7 @@ public static $zh2Hans = [
 '䀹' => '𥅴',
 '䁪' => '𥇢',
 '䁻' => '䀥',
+'䃮' => '鿎',
 '䈰' => '筲',
 '䉙' => '𥬀',
 '䉬' => '𫂈',
@@ -9363,6 +9520,7 @@ public static $zh2Hans = [
 '䌥' => '𦈠',
 '䌰' => '𦈙',
 '䎱' => '䎬',
+'䓣' => '𬜯',
 '䕳' => '𦰴',
 '䗬' => '蜂',
 '䗿' => '𧉞',
@@ -9379,8 +9537,11 @@ public static $zh2Hans = [
 '䞈' => '𧹑',
 '䠀' => '蹚',
 '䠶' => '射',
+'䡵' => '𫟦',
 '䢨' => '𨑹',
 '䥇' => '䦂',
+'䥑' => '鿏',
+'䥕' => '𬭯',
 '䥥' => '镰',
 '䥩' => '𨱖',
 '䥱' => '䥾',
@@ -9401,6 +9562,7 @@ public static $zh2Hans = [
 '䭃' => '𩠈',
 '䭾' => '驮',
 '䭿' => '𩧭',
+'䮄' => '𫠊',
 '䮝' => '𩧰',
 '䮞' => '𩨁',
 '䮠' => '𩧿',
@@ -9490,6 +9652,7 @@ public static $zh2Hans = [
 '僑' => '侨',
 '僕' => '仆',
 '僞' => '伪',
+'僤' => '𫢸',
 '僥' => '侥',
 '僨' => '偾',
 '僱' => '雇',
@@ -9578,6 +9741,7 @@ public static $zh2Hans = [
 '勝' => '胜',
 '勞' => '劳',
 '勢' => '势',
+'勣' => '𪟝',
 '勦' => '剿',
 '勩' => '勚',
 '勱' => '劢',
@@ -9666,6 +9830,7 @@ public static $zh2Hans = [
 '嘷' => '嗥',
 '嘸' => '呒',
 '嘽' => '啴',
+'噁' => '𫫇',
 '噅' => '𠯠',
 '噉' => '啖',
 '噓' => '嘘',
@@ -9717,6 +9882,7 @@ public static $zh2Hans = [
 '垜' => '垛',
 '垻' => '坝',
 '埡' => '垭',
+'埨' => '𫭢',
 '執' => '执',
 '堅' => '坚',
 '堊' => '垩',
@@ -9735,10 +9901,13 @@ public static $zh2Hans = [
 '塤' => '埙',
 '塲' => '场',
 '塵' => '尘',
+'塸' => '𫭟',
 '塹' => '堑',
+'塿' => '𪣻',
 '墊' => '垫',
 '墖' => '塔',
 '墜' => '坠',
+'墠' => '𫮃',
 '墮' => '堕',
 '墰' => '坛',
 '墳' => '坟',
@@ -9785,6 +9954,7 @@ public static $zh2Hans = [
 '姦' => '奸',
 '姪' => '侄',
 '姸' => '妍',
+'娙' => '𫰛',
 '娛' => '娱',
 '婁' => '娄',
 '婣' => '姻',
@@ -9877,6 +10047,7 @@ public static $zh2Hans = [
 '嵐' => '岚',
 '嵗' => '岁',
 '嵼' => '𡶴',
+'嵽' => '𫶇',
 '嶁' => '嵝',
 '嶃' => '崭',
 '嶄' => '崭',
@@ -9897,6 +10068,7 @@ public static $zh2Hans = [
 '巔' => '巅',
 '巖' => '岩',
 '巗' => '岩',
+'巘' => '𪩘',
 '巰' => '巯',
 '巵' => '卮',
 '帀' => '匝',
@@ -9932,6 +10104,7 @@ public static $zh2Hans = [
 '廕' => '荫',
 '廚' => '厨',
 '廝' => '厮',
+'廞' => '𫷷',
 '廟' => '庙',
 '廠' => '厂',
 '廡' => '庑',
@@ -9948,6 +10121,7 @@ public static $zh2Hans = [
 '弳' => '弪',
 '張' => '张',
 '強' => '强',
+'彄' => '𫸩',
 '彆' => '别',
 '彈' => '弹',
 '彌' => '弥',
@@ -10180,9 +10354,11 @@ public static $zh2Hans = [
 '昬' => '昏',
 '時' => '时',
 '晉' => '晋',
+'晛' => '𬀪',
 '晝' => '昼',
 '暈' => '晕',
 '暉' => '晖',
+'暐' => '𬀩',
 '暘' => '旸',
 '暢' => '畅',
 '暫' => '暂',
@@ -10221,6 +10397,7 @@ public static $zh2Hans = [
 '桿' => '杆',
 '梔' => '栀',
 '梘' => '枧',
+'梜' => '𬂩',
 '條' => '条',
 '梟' => '枭',
 '梲' => '棁',
@@ -10297,6 +10474,7 @@ public static $zh2Hans = [
 '檻' => '槛',
 '櫃' => '柜',
 '櫈' => '凳',
+'櫍' => '𬃊',
 '櫓' => '橹',
 '櫚' => '榈',
 '櫛' => '栉',
@@ -10373,6 +10551,7 @@ public static $zh2Hans = [
 '洩' => '泄',
 '洶' => '汹',
 '浹' => '浃',
+'浿' => '𬇙',
 '涇' => '泾',
 '涖' => '莅',
 '涼' => '凉',
@@ -10393,6 +10572,7 @@ public static $zh2Hans = [
 '測' => '测',
 '渾' => '浑',
 '湊' => '凑',
+'湋' => '𣲗',
 '湞' => '浈',
 '湧' => '涌',
 '湯' => '汤',
@@ -10420,6 +10600,7 @@ public static $zh2Hans = [
 '滿' => '满',
 '漁' => '渔',
 '漊' => '溇',
+'漍' => '𬇹',
 '漚' => '沤',
 '漢' => '汉',
 '漣' => '涟',
@@ -10451,6 +10632,7 @@ public static $zh2Hans = [
 '澤' => '泽',
 '澦' => '滪',
 '澩' => '泶',
+'澫' => '𬇕',
 '澮' => '浍',
 '澱' => '淀',
 '澾' => '㳠',
@@ -10528,14 +10710,17 @@ public static $zh2Hans = [
 '熓' => '𤆡',
 '熗' => '炝',
 '熡' => '𤋏',
+'熰' => '𬉼',
 '熱' => '热',
 '熲' => '颎',
 '熾' => '炽',
+'燀' => '𬊤',
 '燁' => '烨',
 '燄' => '焰',
 '燈' => '灯',
 '燉' => '炖',
 '燒' => '烧',
+'燖' => '𬊈',
 '燙' => '烫',
 '燜' => '焖',
 '營' => '营',
@@ -10618,6 +10803,9 @@ public static $zh2Hans = [
 '瑲' => '玱',
 '瑽' => '𪻐',
 '璉' => '琏',
+'璊' => '𫞩',
+'璕' => '𬍤',
+'璗' => '𬍡',
 '璡' => '琎',
 '璢' => '瑠',
 '璣' => '玑',
@@ -10628,11 +10816,13 @@ public static $zh2Hans = [
 '璵' => '玙',
 '璸' => '瑸',
 '璽' => '玺',
+'瓅' => '𬍛',
 '瓊' => '琼',
 '瓏' => '珑',
 '瓔' => '璎',
 '瓕' => '𤦀',
 '瓚' => '瓒',
+'瓛' => '𤩽',
 '甌' => '瓯',
 '甎' => '砖',
 '甕' => '瓮',
@@ -10766,6 +10956,7 @@ public static $zh2Hans = [
 '礄' => '硚',
 '礆' => '碱',
 '礎' => '础',
+'礐' => '𬒈',
 '礒' => '𥐟',
 '礙' => '碍',
 '礦' => '矿',
@@ -10854,6 +11045,7 @@ public static $zh2Hans = [
 '篔' => '筼',
 '篘' => '𥬠',
 '篛' => '箬',
+'篢' => '𬕂',
 '篤' => '笃',
 '篩' => '筛',
 '篳' => '筚',
@@ -10901,6 +11093,7 @@ public static $zh2Hans = [
 '糾' => '纠',
 '紀' => '纪',
 '紂' => '纣',
+'紃' => '𬘓',
 '約' => '约',
 '紅' => '红',
 '紆' => '纡',
@@ -10921,6 +11114,7 @@ public static $zh2Hans = [
 '紛' => '纷',
 '紜' => '纭',
 '紝' => '纴',
+'紞' => '𬘘',
 '紡' => '纺',
 '紥' => '扎',
 '紬' => '䌷',
@@ -10951,6 +11145,7 @@ public static $zh2Hans = [
 '絢' => '绚',
 '給' => '给',
 '絨' => '绒',
+'絪' => '𬘡',
 '絰' => '绖',
 '統' => '统',
 '絲' => '丝',
@@ -10961,20 +11156,27 @@ public static $zh2Hans = [
 '綀' => '𦈌',
 '綁' => '绑',
 '綃' => '绡',
+'綄' => '𬘫',
 '綆' => '绠',
 '綇' => '𦈋',
 '綈' => '绨',
 '綉' => '绣',
 '綌' => '绤',
+'綎' => '𬘩',
 '綏' => '绥',
 '綐' => '䌼',
 '綑' => '捆',
 '經' => '经',
+'綖' => '𫄧',
 '綜' => '综',
+'綝' => '𬘭',
 '綞' => '缍',
 '綠' => '绿',
+'綡' => '𫟅',
 '綢' => '绸',
 '綣' => '绻',
+'綧' => '𬘯',
+'綪' => '𬘬',
 '綫' => '线',
 '綬' => '绶',
 '維' => '维',
@@ -11046,6 +11248,7 @@ public static $zh2Hans = [
 '縬' => '𦈚',
 '縭' => '缡',
 '縮' => '缩',
+'縯' => '𬙂',
 '縱' => '纵',
 '縲' => '缧',
 '縳' => '䌸',
@@ -11082,6 +11285,7 @@ public static $zh2Hans = [
 '繯' => '缳',
 '繰' => '缲',
 '繳' => '缴',
+'繶' => '𫄷',
 '繸' => '䍁',
 '繹' => '绎',
 '繻' => '𦈡',
@@ -11090,6 +11294,7 @@ public static $zh2Hans = [
 '繾' => '缱',
 '繿' => '䍀',
 '纁' => '𫄸',
+'纆' => '𬙊',
 '纇' => '颣',
 '纈' => '缬',
 '纊' => '纩',
@@ -11098,6 +11303,7 @@ public static $zh2Hans = [
 '纏' => '缠',
 '纓' => '缨',
 '纔' => '才',
+'纕' => '𬙋',
 '纖' => '纤',
 '纘' => '缵',
 '纜' => '缆',
@@ -11251,6 +11457,7 @@ public static $zh2Hans = [
 '蓯' => '苁',
 '蓴' => '莼',
 '蓽' => '荜',
+'蔄' => '𬜬',
 '蔔' => '卜',
 '蔕' => '蒂',
 '蔘' => '参',
@@ -11259,6 +11466,7 @@ public static $zh2Hans = [
 '蔥' => '葱',
 '蔦' => '茑',
 '蔭' => '荫',
+'蔿' => '𫇭',
 '蕁' => '荨',
 '蕆' => '蒇',
 '蕎' => '荞',
@@ -11302,6 +11510,7 @@ public static $zh2Hans = [
 '蘆' => '芦',
 '蘇' => '苏',
 '蘊' => '蕴',
+'蘋' => '𬞟',
 '蘐' => '萱',
 '蘓' => '苏',
 '蘚' => '藓',
@@ -11312,6 +11521,7 @@ public static $zh2Hans = [
 '蘺' => '蓠',
 '蘿' => '萝',
 '虆' => '蔂',
+'虉' => '𬟁',
 '處' => '处',
 '虛' => '虚',
 '虜' => '虏',
@@ -11326,6 +11536,7 @@ public static $zh2Hans = [
 '蜋' => '螂',
 '蜖' => '蛔',
 '蜨' => '蝶',
+'蝀' => '𬟽',
 '蝕' => '蚀',
 '蝟' => '猬',
 '蝦' => '虾',
@@ -11447,6 +11658,7 @@ public static $zh2Hans = [
 '訊' => '讯',
 '訌' => '讧',
 '討' => '讨',
+'訏' => '𬣙',
 '訐' => '讦',
 '訑' => '𫍙',
 '訒' => '讱',
@@ -11482,6 +11694,7 @@ public static $zh2Hans = [
 '詗' => '诇',
 '詘' => '诎',
 '詛' => '诅',
+'詝' => '𬣞',
 '詞' => '词',
 '詠' => '咏',
 '詡' => '诩',
@@ -11489,6 +11702,7 @@ public static $zh2Hans = [
 '詣' => '诣',
 '試' => '试',
 '詩' => '诗',
+'詪' => '𬣳',
 '詫' => '诧',
 '詬' => '诟',
 '詭' => '诡',
@@ -11499,6 +11713,7 @@ public static $zh2Hans = [
 '詳' => '详',
 '詵' => '诜',
 '詶' => '酬',
+'詷' => '𫍣',
 '詼' => '诙',
 '詿' => '诖',
 '誄' => '诔',
@@ -11539,12 +11754,14 @@ public static $zh2Hans = [
 '諏' => '诹',
 '諑' => '诼',
 '諒' => '谅',
+'諓' => '𬣡',
 '論' => '论',
 '諗' => '谂',
 '諛' => '谀',
 '諜' => '谍',
 '諝' => '谞',
 '諞' => '谝',
+'諟' => '𬤊',
 '諡' => '谥',
 '諢' => '诨',
 '諤' => '谔',
@@ -11555,7 +11772,9 @@ public static $zh2Hans = [
 '諮' => '谘',
 '諰' => '𫍰',
 '諱' => '讳',
+'諲' => '𬤇',
 '諳' => '谙',
+'諴' => '𫍯',
 '諶' => '谌',
 '諷' => '讽',
 '諸' => '诸',
@@ -11595,12 +11814,14 @@ public static $zh2Hans = [
 '譌' => '讹',
 '譎' => '谲',
 '譏' => '讥',
+'譓' => '𬤝',
 '譔' => '撰',
 '譖' => '谮',
 '識' => '识',
 '譙' => '谯',
 '譚' => '谭',
 '譜' => '谱',
+'譞' => '𫍽',
 '譟' => '噪',
 '譫' => '谵',
 '譭' => '毁',
@@ -11776,6 +11997,7 @@ public static $zh2Hans = [
 '軔' => '轫',
 '軗' => '𨐅',
 '軛' => '轭',
+'軝' => '𬨂',
 '軟' => '软',
 '軤' => '轷',
 '軨' => '𫐉',
@@ -11794,6 +12016,7 @@ public static $zh2Hans = [
 '輈' => '辀',
 '載' => '载',
 '輊' => '轾',
+'輋' => '𪨶',
 '輒' => '辄',
 '輓' => '挽',
 '輔' => '辅',
@@ -11814,6 +12037,7 @@ public static $zh2Hans = [
 '輮' => '𫐓',
 '輯' => '辑',
 '輳' => '辏',
+'輶' => '𬨎',
 '輸' => '输',
 '輻' => '辐',
 '輼' => '辒',
@@ -11885,9 +12109,11 @@ public static $zh2Hans = [
 '鄔' => '邬',
 '鄖' => '郧',
 '鄧' => '邓',
+'鄩' => '𬩽',
 '鄭' => '郑',
 '鄰' => '邻',
 '鄲' => '郸',
+'鄳' => '𫑡',
 '鄴' => '邺',
 '鄶' => '郐',
 '鄺' => '邝',
@@ -11905,6 +12131,7 @@ public static $zh2Hans = [
 '醬' => '酱',
 '醯' => '酰',
 '醱' => '酦',
+'醲' => '𬪩',
 '醻' => '酬',
 '醼' => '宴',
 '釀' => '酿',
@@ -11927,11 +12154,13 @@ public static $zh2Hans = [
 '釩' => '钒',
 '釬' => '焊',
 '釳' => '𨰿',
+'釴' => '𬬩',
 '釵' => '钗',
 '釷' => '钍',
 '釹' => '钕',
 '釺' => '钎',
 '釾' => '䥺',
+'釿' => '𬬱',
 '鈀' => '钯',
 '鈁' => '钫',
 '鈃' => '钘',
@@ -11974,6 +12203,7 @@ public static $zh2Hans = [
 '鉆' => '钻',
 '鉈' => '铊',
 '鉉' => '铉',
+'鉊' => '𬬿',
 '鉋' => '铇',
 '鉍' => '铋',
 '鉑' => '铂',
@@ -11984,10 +12214,14 @@ public static $zh2Hans = [
 '鉞' => '钺',
 '鉢' => '钵',
 '鉤' => '钩',
+'鉥' => '𬬸',
 '鉦' => '钲',
+'鉧' => '𬭁',
 '鉬' => '钼',
 '鉭' => '钽',
+'鉮' => '𬬹',
 '鉶' => '铏',
+'鉷' => '𫟹',
 '鉸' => '铰',
 '鉺' => '铒',
 '鉻' => '铬',
@@ -11995,6 +12229,7 @@ public static $zh2Hans = [
 '銀' => '银',
 '銃' => '铳',
 '銅' => '铜',
+'銈' => '𫓯',
 '銍' => '铚',
 '銑' => '铣',
 '銓' => '铨',
@@ -12026,7 +12261,9 @@ public static $zh2Hans = [
 '鋉' => '𨱈',
 '鋌' => '铤',
 '鋏' => '铗',
+'鋐' => '𬭎',
 '鋒' => '锋',
+'鋗' => '𫓶',
 '鋙' => '铻',
 '鋝' => '锊',
 '鋟' => '锓',
@@ -12044,7 +12281,9 @@ public static $zh2Hans = [
 '鋱' => '铽',
 '鋶' => '锍',
 '鋸' => '锯',
+'鋹' => '𬬮',
 '鋼' => '钢',
+'錀' => '𬬭',
 '錁' => '锞',
 '錂' => '𨱋',
 '錄' => '录',
@@ -12059,10 +12298,12 @@ public static $zh2Hans = [
 '錙' => '锱',
 '錚' => '铮',
 '錛' => '锛',
+'錞' => '𬭚',
 '錟' => '锬',
 '錠' => '锭',
 '錡' => '锜',
 '錢' => '钱',
+'錤' => '𫓹',
 '錦' => '锦',
 '錨' => '锚',
 '錩' => '锠',
@@ -12093,6 +12334,7 @@ public static $zh2Hans = [
 '鍩' => '锘',
 '鍫' => '锹',
 '鍬' => '锹',
+'鍭' => '𬭤',
 '鍮' => '𨱎',
 '鍰' => '锾',
 '鍳' => '鉴',
@@ -12105,6 +12347,7 @@ public static $zh2Hans = [
 '鎇' => '镅',
 '鎊' => '镑',
 '鎌' => '镰',
+'鎓' => '𬭩',
 '鎔' => '镕',
 '鎖' => '锁',
 '鎗' => '枪',
@@ -12127,6 +12370,7 @@ public static $zh2Hans = [
 '鎲' => '镋',
 '鎳' => '镍',
 '鎵' => '镓',
+'鎶' => '鿔',
 '鎷' => '𨰾',
 '鎸' => '镌',
 '鎻' => '锁',
@@ -12138,6 +12382,7 @@ public static $zh2Hans = [
 '鏉' => '𨱒',
 '鏌' => '镆',
 '鏍' => '镙',
+'鏏' => '𬭬',
 '鏐' => '镠',
 '鏑' => '镝',
 '鏗' => '铿',
@@ -12157,9 +12402,11 @@ public static $zh2Hans = [
 '鏷' => '镤',
 '鏹' => '镪',
 '鏺' => '䥽',
+'鏻' => '𬭸',
 '鏽' => '锈',
 '鐃' => '铙',
 '鐄' => '𨱑',
+'鐇' => '𫔍',
 '鐋' => '铴',
 '鐍' => '𫔎',
 '鐎' => '𨱓',
@@ -12176,6 +12423,7 @@ public static $zh2Hans = [
 '鐦' => '锎',
 '鐧' => '锏',
 '鐨' => '镄',
+'鐩' => '𬭼',
 '鐫' => '镌',
 '鐮' => '镰',
 '鐯' => '䦃',
@@ -12185,6 +12433,7 @@ public static $zh2Hans = [
 '鐶' => '镮',
 '鐸' => '铎',
 '鐺' => '铛',
+'鐽' => '𫟼',
 '鐿' => '镱',
 '鑄' => '铸',
 '鑊' => '镬',
@@ -12200,6 +12449,7 @@ public static $zh2Hans = [
 '鑣' => '镳',
 '鑤' => '刨',
 '鑥' => '镥',
+'鑪' => '𬬻',
 '鑭' => '镧',
 '鑰' => '钥',
 '鑱' => '镵',
@@ -12255,11 +12505,13 @@ public static $zh2Hans = [
 '闆' => '板',
 '闇' => '暗',
 '闈' => '闱',
+'闉' => '𬮱',
 '闊' => '阔',
 '闋' => '阕',
 '闌' => '阑',
 '闍' => '阇',
 '闐' => '阗',
+'闑' => '𫔶',
 '闒' => '阘',
 '闓' => '闿',
 '闔' => '阖',
@@ -12291,12 +12543,15 @@ public static $zh2Hans = [
 '隉' => '陧',
 '隊' => '队',
 '階' => '阶',
+'隑' => '𬮿',
 '隕' => '陨',
 '隖' => '坞',
 '際' => '际',
 '隣' => '邻',
+'隤' => '𬯎',
 '隨' => '随',
 '險' => '险',
+'隮' => '𬯀',
 '隱' => '隐',
 '隴' => '陇',
 '隷' => '隶',
@@ -12358,24 +12613,29 @@ public static $zh2Hans = [
 '須' => '须',
 '頊' => '顼',
 '頌' => '颂',
+'頍' => '𫠆',
 '頎' => '颀',
 '頏' => '颃',
 '預' => '预',
 '頑' => '顽',
 '頒' => '颁',
 '頓' => '顿',
+'頔' => '𬱖',
 '頗' => '颇',
 '領' => '领',
 '頜' => '颌',
 '頟' => '额',
+'頠' => '𬱟',
 '頡' => '颉',
 '頤' => '颐',
 '頦' => '颏',
+'頫' => '𫖯',
 '頭' => '头',
 '頮' => '颒',
 '頰' => '颊',
 '頲' => '颋',
 '頴' => '颕',
+'頵' => '𫖳',
 '頷' => '颔',
 '頸' => '颈',
 '頹' => '颓',
@@ -12393,6 +12653,7 @@ public static $zh2Hans = [
 '顒' => '颙',
 '顓' => '颛',
 '顔' => '颜',
+'顗' => '𫖮',
 '願' => '愿',
 '顙' => '颡',
 '顛' => '颠',
@@ -12509,13 +12770,16 @@ public static $zh2Hans = [
 '馳' => '驰',
 '馴' => '驯',
 '馹' => '驲',
+'馼' => '𫘜',
 '駁' => '驳',
 '駃' => '𫘝',
 '駈' => '驱',
+'駉' => '𬳶',
 '駎' => '𩧨',
 '駐' => '驻',
 '駑' => '驽',
 '駒' => '驹',
+'駓' => '𬳵',
 '駔' => '驵',
 '駕' => '驾',
 '駘' => '骀',
@@ -12528,27 +12792,33 @@ public static $zh2Hans = [
 '駢' => '骈',
 '駧' => '𩧲',
 '駩' => '𩧴',
+'駪' => '𬳽',
 '駭' => '骇',
 '駰' => '骃',
 '駱' => '骆',
 '駶' => '𩧺',
 '駸' => '骎',
 '駻' => '𫘣',
+'駼' => '𬳿',
 '駿' => '骏',
 '騁' => '骋',
 '騂' => '骍',
 '騃' => '𫘤',
+'騄' => '𫘧',
 '騅' => '骓',
+'騊' => '𫘦',
 '騌' => '骔',
 '騍' => '骒',
 '騎' => '骑',
 '騏' => '骐',
 '騐' => '验',
+'騑' => '𬴂',
 '騔' => '𩨀',
 '騖' => '骛',
 '騙' => '骗',
 '騚' => '𩨊',
 '騝' => '𩨃',
+'騞' => '𬴃',
 '騟' => '𩨈',
 '騠' => '𫘨',
 '騣' => '鬃',
@@ -12559,6 +12829,8 @@ public static $zh2Hans = [
 '騭' => '骘',
 '騮' => '骝',
 '騰' => '腾',
+'騱' => '𫘬',
+'騵' => '𫘪',
 '騶' => '驺',
 '騷' => '骚',
 '騸' => '骟',
@@ -12573,6 +12845,7 @@ public static $zh2Hans = [
 '驋' => '𩧯',
 '驌' => '骕',
 '驍' => '骁',
+'驎' => '𬴊',
 '驏' => '骣',
 '驕' => '骄',
 '驗' => '验',
@@ -12625,9 +12898,12 @@ public static $zh2Hans = [
 '魴' => '鲂',
 '魷' => '鱿',
 '魺' => '鲄',
+'鮀' => '𬶍',
 '鮁' => '鲅',
 '鮃' => '鲆',
 '鮄' => '𫚒',
+'鮆' => '𫚖',
+'鮈' => '𬶋',
 '鮊' => '鲌',
 '鮋' => '鲉',
 '鮍' => '鲏',
@@ -12641,6 +12917,8 @@ public static $zh2Hans = [
 '鮝' => '鲞',
 '鮞' => '鲕',
 '鮟' => '𩽾',
+'鮠' => '𬶏',
+'鮡' => '𬶐',
 '鮣' => '䲟',
 '鮦' => '鲖',
 '鮪' => '鲔',
@@ -12678,6 +12956,7 @@ public static $zh2Hans = [
 '鯴' => '鲺',
 '鯶' => '𩽼',
 '鯷' => '鳀',
+'鯻' => '𬶟',
 '鯽' => '鲫',
 '鯿' => '鳊',
 '鰁' => '鳈',
@@ -12686,6 +12965,7 @@ public static $zh2Hans = [
 '鰆' => '䲠',
 '鰈' => '鲽',
 '鰉' => '鳇',
+'鰊' => '𬶠',
 '鰌' => '䲡',
 '鰍' => '鳅',
 '鰏' => '鲾',
@@ -12708,12 +12988,14 @@ public static $zh2Hans = [
 '鰲' => '鳌',
 '鰳' => '鳓',
 '鰵' => '鳘',
+'鰶' => '𬶭',
 '鰷' => '鲦',
 '鰹' => '鲣',
 '鰺' => '鲹',
 '鰻' => '鳗',
 '鰼' => '鳛',
 '鰾' => '鳔',
+'鱀' => '𬶨',
 '鱂' => '鳉',
 '鱅' => '鳙',
 '鱇' => '𩾌',
@@ -12724,6 +13006,7 @@ public static $zh2Hans = [
 '鱖' => '鳜',
 '鱗' => '鳞',
 '鱘' => '鲟',
+'鱚' => '𬶮',
 '鱝' => '鲼',
 '鱟' => '鲎',
 '鱠' => '鲙',
@@ -12774,6 +13057,7 @@ public static $zh2Hans = [
 '鵁' => '䴔',
 '鵂' => '鸺',
 '鵃' => '鸼',
+'鵏' => '𬷕',
 '鵐' => '鹀',
 '鵑' => '鹃',
 '鵒' => '鹆',
@@ -12782,6 +13066,7 @@ public static $zh2Hans = [
 '鵜' => '鹈',
 '鵝' => '鹅',
 '鵞' => '鹅',
+'鵟' => '𫛭',
 '鵠' => '鹄',
 '鵡' => '鹉',
 '鵪' => '鹌',
@@ -12803,12 +13088,14 @@ public static $zh2Hans = [
 '鶗' => '𫛸',
 '鶘' => '鹕',
 '鶚' => '鹗',
+'鶠' => '𬸘',
 '鶡' => '鹖',
 '鶥' => '鹛',
 '鶩' => '鹜',
 '鶪' => '䴗',
 '鶬' => '鸧',
 '鶯' => '莺',
+'鶱' => '𬸣',
 '鶲' => '鹟',
 '鶴' => '鹤',
 '鶹' => '鹠',
@@ -12828,10 +13115,12 @@ public static $zh2Hans = [
 '鷗' => '鸥',
 '鷙' => '鸷',
 '鷚' => '鹨',
+'鷟' => '𬸦',
 '鷥' => '鸶',
 '鷦' => '鹪',
 '鷨' => '𪉊',
 '鷫' => '鹔',
+'鷭' => '𬸪',
 '鷯' => '鹩',
 '鷰' => '燕',
 '鷲' => '鹫',
@@ -12848,6 +13137,7 @@ public static $zh2Hans = [
 '鸌' => '鹱',
 '鸎' => '莺',
 '鸏' => '鹲',
+'鸑' => '𬸚',
 '鸕' => '鸬',
 '鸘' => '鹴',
 '鸚' => '鹦',
@@ -12897,6 +13187,7 @@ public static $zh2Hans = [
 '齔' => '龀',
 '齕' => '龁',
 '齗' => '龂',
+'齘' => '𬹼',
 '齙' => '龅',
 '齜' => '龇',
 '齟' => '龃',
@@ -12908,9 +13199,12 @@ public static $zh2Hans = [
 '齩' => '咬',
 '齪' => '龊',
 '齬' => '龉',
+'齮' => '𬺈',
+'齯' => '𫠜',
 '齲' => '龋',
 '齶' => '腭',
 '齷' => '龌',
+'齼' => '𬺓',
 '龍' => '龙',
 '龎' => '厐',
 '龐' => '庞',
@@ -12920,6 +13214,9 @@ public static $zh2Hans = [
 '龜' => '龟',
 '龭' => '𩨎',
 '龯' => '𨱆',
+'鿁' => '䜤',
+'鿐' => '䲤',
+'鿓' => '鿒',
 '𠌥' => '𠆿',
 '𠏢' => '𠉗',
 '𠕂' => '再',
@@ -12930,6 +13227,7 @@ public static $zh2Hans = [
 '𡄔' => '𠴢',
 '𡄣' => '𠵸',
 '𡅏' => '𠲥',
+'𡑍' => '𫭼',
 '𡑭' => '𡋗',
 '𡓾' => '𡋀',
 '𡚁' => '弊',
@@ -12967,6 +13265,7 @@ public static $zh2Hans = [
 '𥌃' => '𥅘',
 '𥕥' => '𥐰',
 '𥖅' => '𥐯',
+'𥗽' => '𬒗',
 '𥢢' => '䅪',
 '𥨐' => '𥧂',
 '𥵃' => '𥱔',
@@ -13009,9 +13308,13 @@ public static $zh2Hans = [
 '𨤻' => '𨤰',
 '𨥛' => '𨱀',
 '𨦫' => '䦀',
+'𨧀' => '𬭊',
 '𨧜' => '䦁',
 '𨧱' => '𨱊',
+'𨨏' => '𬭛',
 '𨫒' => '𨱐',
+'𨭆' => '𬭶',
+'𨭎' => '𬭳',
 '𨮂' => '𨱕',
 '𨯅' => '䥿',
 '𨳑' => '𨸁',
@@ -13294,6 +13597,7 @@ public static $zh2Hans = [
 '彷彿' => '仿佛',
 '伊東豊雄' => '伊东丰雄',
 '夥計' => '伙计',
+'何光暐' => '何光暐',
 '佛頭著糞' => '佛头著粪',
 '偵蒐' => '侦搜',
 '倖一郎' => '倖一郎',
@@ -13598,6 +13902,7 @@ public static $zh2Hans = [
 '米泽瑠美' => '米泽瑠美',
 '米瀋' => '米渖',
 '餬口' => '糊口',
+'絪縕' => '絪缊',
 '繙㠾' => '繙㠾',
 '遶境' => '绕境',
 '線國安' => '缐国安',
@@ -13673,6 +13978,7 @@ public static $zh2Hans = [
 '銀鍊' => '银链',
 '鍊子' => '链子',
 '鍊條' => '链条',
+'鍊狀' => '链状',
 '鍊表' => '链表',
 '鍊鎖' => '链锁',
 '鍊錘' => '链锤',
@@ -13700,6 +14006,7 @@ public static $zh2Hans = [
 '讎校' => '雠校',
 '讎正' => '雠正',
 '讎問' => '雠问',
+'雪鍊' => '雪链',
 '項鍊' => '项链',
 '飛昇' => '飞升',
 '飭令' => '飭令',
@@ -14000,6 +14307,8 @@ public static $zh2TW = [
 '德里达' => '德希達',
 '特拉华' => '德拉瓦',
 '特拉華' => '德拉瓦',
+'文翠珊' => '德蕾莎·梅伊',
+'特蕾莎·梅' => '德蕾莎·梅伊',
 '快闪存储器' => '快閃記憶體',
 '闪存' => '快閃記憶體',
 '想象' => '想像',
@@ -15565,6 +15874,10 @@ public static $zh2HK = [
 '慣著者' => '慣著者',
 '慣著述' => '慣著述',
 '慣著錄' => '慣著錄',
+'憑著' => '憑着',
+'憑著作' => '憑著作',
+'憑著名' => '憑著名',
+'憑著者' => '憑著者',
 '宪法里' => '憲法裏',
 '应用程序' => '應用程式',
 '應著' => '應着',
@@ -15867,6 +16180,8 @@ public static $zh2HK = [
 '數著者' => '數著者',
 '數著述' => '數著述',
 '數著錄' => '數著錄',
+'德蕾莎·梅伊' => '文翠珊',
+'特蕾莎·梅' => '文翠珊',
 '斥著' => '斥着',
 '斥著作' => '斥著作',
 '斥著名' => '斥著名',
@@ -17913,6 +18228,10 @@ public static $zh2CN = [
 '涼著述' => '凉著述',
 '湊合著' => '凑合着',
 '幾內亞比索' => '几内亚比绍',
+'憑著' => '凭着',
+'憑著作' => '凭著作',
+'憑著名' => '凭著名',
+'憑著者' => '凭著者',
 '凱薩琳' => '凯瑟琳',
 '嘉芙蓮' => '凯瑟琳',
 '份內' => '分内',
@@ -18426,6 +18745,7 @@ public static $zh2CN = [
 '逕寄' => '径寄',
 '逕庭' => '径庭',
 '逕往' => '径往',
+'逕流' => '径流',
 '逕自' => '径自',
 '逕行' => '径行',
 '逕迎' => '径迎',
@@ -19164,6 +19484,8 @@ public static $zh2CN = [
 '千里達' => '特立尼达',
 '千里達及托巴哥' => '特立尼达和多巴哥',
 '千里達托貝哥' => '特立尼达和托巴哥',
+'德蕾莎·梅伊' => '特蕾莎·梅',
+'文翠珊' => '特蕾莎·梅',
 '狗隻' => '犬只',
 '猶豫著' => '犹豫着',
 '獨立國家國協' => '独立国家联合体',
index 56513ff..d449f5b 100644 (file)
        "userjspreview": "'''تذكر أنك فقط تجرب/تعاين جافاسكربت.'''\n'''لم يتم الحفظ بعد!'''",
        "sitecsspreview": "''' تذكر أنك فقط في وضع المعاينة لهذا CSS ''' \n''' ولم يتم حفظ الصفحة بعد! '''",
        "sitejspreview": "''' تذكر أنك فقط في وضع المعاينة لكود JavaScript هذا''' \n''' ولم يتم حفظه بعد! '''",
-       "userinvalidcssjstitle": "'''تحذير:''' لا توجد واجهة  \"$1\".\nتذكر أن ملفات ال.css و ال.js تستخدم حروف صغيرة في العنوان ، كمثال {{ns:user}}:Foo/vector.css و ليس {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "'''تحذير:''' لا توجد واجهة  \"$1\".\nتذكر أن ملفات ال.css و ال.js تستخدم حروف صغيرة في العنوان ، كمثال {{ns:user}}:Foo/vector.css و ليس {{ns:user}}:Foo/Vector.css.",
        "updated": "(محدثة)",
        "note": "'''ملاحظة:'''",
        "previewnote": "'''تذكر أن هذه مجرد معاينة أولية.'''\nلم تحفظ تغييراتك إلى الآن!",
index c467a1e..0710d40 100644 (file)
        "userjspreview": "'''Onthou hierdie is slegs 'n toets/voorskou van u gebruiker-JavaScript, dit is nog nie gestoor nie.'''",
        "sitecsspreview": "'''Onthou dat u na 'n voorskou van die CSS-kode kyk.'''\n'''Dit is nog nie gestoor nie!'''",
        "sitejspreview": "'''Onthou dat u na 'n voorskou van die JavaScript-kode kyk.'''\n'''Dit is nog nie gestoor nie!'''",
-       "userinvalidcssjstitle": "'''Waarskuwing:''' daar is nie 'n omslag \"$1\" nie.\nOnthou dat u eie .css- en .js-bladsye met 'n kleinletter begin, byvoorbeeld {{ns:user}}:Naam/vector.css in plaas van {{ns:user}}:Naam/Vector.css.",
+       "userinvalidconfigtitle": "'''Waarskuwing:''' daar is nie 'n omslag \"$1\" nie.\nOnthou dat u eie .css- en .js-bladsye met 'n kleinletter begin, byvoorbeeld {{ns:user}}:Naam/vector.css in plaas van {{ns:user}}:Naam/Vector.css.",
        "updated": "(Gewysig)",
        "note": "'''Nota:'''",
        "previewnote": "'''Onthou dat hierdie slegs 'n voorskou is.'''\nU teks is nog nie gestoor nie!",
        "prefs-files": "Lêers",
        "prefs-custom-css": "Persoonlike CSS",
        "prefs-custom-js": "Persoonlike JS",
-       "prefs-common-css-js": "Gedeelde CSS/JS vir al die omslae:",
+       "prefs-common-config": "Gedeelde CSS/JS vir al die omslae:",
        "prefs-reset-intro": "U kan die blad gebruik om u voorkeure terug te stel na die webwerf se verstekwaardes.\nDie aksie kan nie ongedaan gemaak word nie.",
        "prefs-emailconfirm-label": "E-posbevestiging:",
        "youremail": "E-posadres:",
index adae8ab..cc7b50c 100644 (file)
        "userjspreview": "strong>imhini pataayaway miazih kisu numisuay misaungayay a JavaScript.\nJavaScript caay henay misuped!</strong>",
        "sitecsspreview": "<strong>imhini kisu ayza i pataayaway miazih tina CSS, CSS caay henay suped!</strong>",
        "sitejspreview": "<strong> mipataayaway miazih tina JavaScript kisu ayza, JavaScript caay henay misuped!</strong>",
-       "userinvalidcssjstitle": "<strong>patalaw:</strong> inayi’ tina nuhekalan yangse \"$1\".\npakuniza misanga’ a .css atu .js kasabelih maydih pisaungay adidi’ay ku sulit satangahan, tinaku:{{ns:user}}:Foo/vector.css atu {{ns:user}}:Foo/Vector.css caay kalecad",
+       "userinvalidconfigtitle": "<strong>patalaw:</strong> inayi’ tina nuhekalan yangse \"$1\".\npakuniza misanga’ a .css atu .js kasabelih maydih pisaungay adidi’ay ku sulit satangahan, tinaku:{{ns:user}}:Foo/vector.css atu {{ns:user}}:Foo/Vector.css caay kalecad",
        "updated": "(misabaluh tuway)",
        "note": "<strong>azihen:</strong>",
        "previewnote": "<strong>imahini kisu mapataayaway miazih, misu a pisumad caay henay kasinga’</strong>",
        "prefs-files": "tangan",
        "prefs-custom-css": "pakuniza misanga’ CSS",
        "prefs-custom-js": "pakuniza misanga’ JavaScript",
-       "prefs-common-css-js": "sacahamin nuhekalan kapulungan a CSS/JavaScript:",
+       "prefs-common-config": "sacahamin nuhekalan kapulungan a CSS/JavaScript:",
        "prefs-reset-intro": "kapah kisu misaungay tina kasabelih miliyaw patizeng tunumisu misetin tu kanamuhan mala calay-kakacawan(wangcan) pataayaw sulyang.\ntina pisaungay a la’cus panukasan.",
        "prefs-emailconfirm-label": "imyiyo(email) malucekay tuway:",
        "youremail": "imyiyo(email):",
index 28265fc..9b570e5 100644 (file)
        "userjsyoucanpreview": "'''Këshillë:''' Përdorni butonin 'Trego parapâmjen' për me testue JS para se me i regjistrue ndryshimet.",
        "usercsspreview": "'''Vini re, jeni tue pâ veç parapâmjen e CSSit tuej.'''\n'''Ende nuk e keni ruejtë!'''",
        "userjspreview": "<strong>Vini re, jeni duke testuar/parë vetëm parapamjen e JavaScriptit tuaj.\nEnde nuk e keni ruajtur!</strong>",
-       "userinvalidcssjstitle": "'''Kujdes:''' Nuk ka pâmje me emën \"$1\".\nVini re që faqet .css dhe .js përdorin vetëm titull me germa të vogla, psh. {{ns:user}}:Foo/vector.css për dallim prej {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "'''Kujdes:''' Nuk ka pâmje me emën \"$1\".\nVini re që faqet .css dhe .js përdorin vetëm titull me germa të vogla, psh. {{ns:user}}:Foo/vector.css për dallim prej {{ns:user}}:Foo/Vector.css.",
        "updated": "(E ndryshueme)",
        "note": "'''Shenim:'''",
        "previewnote": "'''Kjo asht vetëm parapamje.'''\nNdryshimet e tua nuk janë ruejtë ende!",
index 7128e15..4b386e5 100644 (file)
        "userjspreview": "'''ማስታወሻ፦ JavaScriptዎን ለሙከራ ብቻ እያዩ ነው፤ ገና አልተቆጠበም!'''",
        "sitecsspreview": "'''ማስታወሻ፦ CSSዎን ለሙከራ ብቻ እያዩ ነው፤ ገና አልተቆጠበም!'''",
        "sitejspreview": "'''ማስታወሻ፦ JavaScriptዎን ለሙከራ ብቻ እያዩ ነው፤ ገና አልተቆጠበም!'''",
-       "userinvalidcssjstitle": "'''ማስጠንቀቂያ፦''' «$1» የሚባል መልክ የለም። ልዩ .css እና .js ገጾች በትንንሽ እንግሊዝኛ ፊደል መጀመር እንዳለባቸው ያስታውሱ። ለምሳሌ፦  {{ns:user}}:Foo/vector.css ልክ ነው እንጂ {{ns:user}}:Foo/Vector.css አይደለም።",
+       "userinvalidconfigtitle": "'''ማስጠንቀቂያ፦''' «$1» የሚባል መልክ የለም። ልዩ .css እና .js ገጾች በትንንሽ እንግሊዝኛ ፊደል መጀመር እንዳለባቸው ያስታውሱ። ለምሳሌ፦  {{ns:user}}:Foo/vector.css ልክ ነው እንጂ {{ns:user}}:Foo/Vector.css አይደለም።",
        "updated": "(የታደሰ)",
        "note": "'''ማሳሰቢያ፦'''",
        "previewnote": "ማስታወቂያ፦ '''ይህ ለሙከራ ብቻ ነው የሚታየው፣ ምንም ለውጦች ገና አልተላኩም!'''",
index 792b9e0..d30a0d0 100644 (file)
        "userjspreview": "'''Remere que sólo ye previsualizando o suyo javascript d'usuario y encara no ye grabato!'''",
        "sitecsspreview": "'''Remere que isto no ye soque previsualizando iste CSS.'''\n'''Encara no s'ha alzato!'''",
        "sitejspreview": "'''Remere que isto no ye soque previsualizando iste codigo de JavaScript.'''\n'''Encara no s'ha alzato!'''",
-       "userinvalidcssjstitle": "'''Pare cuenta:''' No bi ha garra aparencia clamata \"$1\". Remere que as pachinas presonalizatas .css y .js tienen un títol en minusclas, p.e. {{ns:user}}:Foo/vector.css en cuenta de {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "'''Pare cuenta:''' No bi ha garra aparencia clamata \"$1\". Remere que as pachinas presonalizatas .css y .js tienen un títol en minusclas, p.e. {{ns:user}}:Foo/vector.css en cuenta de {{ns:user}}:Foo/Vector.css.",
        "updated": "(Esviellato)",
        "note": "'''Nota:'''",
        "previewnote": "'''Pare cuenta que isto no ye que l'anvista previa.''' Os cambeos encara no s'ha alzato!",
        "prefs-files": "fichers",
        "prefs-custom-css": "CSS presonalizato",
        "prefs-custom-js": "JS presonalizato",
-       "prefs-common-css-js": "CSS/JS compartito ta todas as apariencias:",
+       "prefs-common-config": "CSS/JS compartito ta todas as apariencias:",
        "prefs-reset-intro": "Puet emplegar ista pachina ta restaurar as suyas preferencias a las valuras por defecto d'o sitio.\nNo se podrá desfer iste cambio.",
        "prefs-emailconfirm-label": "Confirmación de correu electronico:",
        "youremail": "Adreza de correu electronico:",
index 42ebab8..641edc7 100644 (file)
@@ -73,7 +73,8 @@
                        "Mr. Ibrahem",
                        "Aboulouei1",
                        "سامي الرحيلي",
-                       "Azouz.anis"
+                       "Azouz.anis",
+                       "Elbasyouny"
                ]
        },
        "tog-underline": "سطر تحت الوصلات:",
        "botpasswords-label-grants-column": "الممنوح",
        "botpasswords-bad-appid": "اسم البوت \"$1\" غير صحيح.",
        "botpasswords-insert-failed": "فشل في إضافة  اسم البوت \"$1\".هل أضيف بالفعل؟",
-       "botpasswords-update-failed": "فشل في تحديث اسم بوت \"$1\". هل تم حذفه؟",
+       "botpasswords-update-failed": "فشل في تحديث كلمة سر البوت \"$1\". هل تم حذفه؟",
        "botpasswords-created-title": "تم إنشاء كلمة سر بوت",
        "botpasswords-created-body": "كلمة سر البوت للبوت \"$1\" الخاص {{GENDER:$2|بالمستخدم|بالمستخدمة}} \"$2\" تم إنشاؤها.",
        "botpasswords-updated-title": "تم تحديث كلمة سر البوت",
        "userjspreview": "'''تذكر أنك فقط تجرب/تعاين جافاسكربت.'''\n'''لم يتم الحفظ بعد!'''",
        "sitecsspreview": "''' تذكر أنك فقط في وضع المعاينة لهذا CSS ''' \n''' ولم يتم حفظ الصفحة بعد! '''",
        "sitejspreview": "''' تذكر أنك فقط في وضع المعاينة لكود JavaScript هذا''' \n''' ولم يتم حفظه بعد! '''",
-       "userinvalidcssjstitle": "'''تحذير:''' لا توجد واجهة  \"$1\".\nتذكر أن ملفات ال.css و ال.js تستخدم حروف صغيرة في العنوان ، كمثال {{ns:user}}:Foo/vector.css و ليس {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "'''تحذير:''' لا توجد واجهة  \"$1\".\nتذكر أن ملفات ال.css و ال.js تستخدم حروف صغيرة في العنوان ، كمثال {{ns:user}}:Foo/vector.css و ليس {{ns:user}}:Foo/Vector.css.",
        "updated": "(محدثة)",
        "note": "'''ملاحظة:'''",
        "previewnote": "'''تذكر أن هذه مجرد معاينة أولية.'''\nلم تحفظ تغييراتك إلى الآن!",
        "prefs-files": "ملفات",
        "prefs-custom-css": "CSS مخصص",
        "prefs-custom-js": "جافاسكربت مخصص",
-       "prefs-common-css-js": "CSS وجافاسكربت مشترك لجميع الواجهات:",
+       "prefs-common-config": "CSS وجافاسكربت مشترك لجميع الواجهات:",
        "prefs-reset-intro": "يمكنك استخدام هذه الصفحة لإعادة تفضيلاتك للحالة الافتراضية للموقع.\nلن تستطيع استرجاع الحالة السابقة.",
        "prefs-emailconfirm-label": "تأكيد البريد الإلكتروني:",
        "youremail": "البريد:",
        "thumbnail_dest_directory": "غير قادر على إنشاء المجلد الهدف",
        "thumbnail_image-type": "نوع الصورة غير مدعوم",
        "thumbnail_gd-library": "ضبط مكتبة GD غير مكتمل: دالة مفقودة $1",
+       "thumbnail_image-size-zero": "حجم ملف الصورة يبدو أنه صفر.",
        "thumbnail_image-missing": "الملف يبدو أنه مفقود: $1",
        "thumbnail_image-failure-limit": "هناك الكثير من المحاولات الفاشلة مؤخراً ($1 أو أكثر) لتَصْيير هذه الصورة المصغرة. الرجاء المحاولة مرة أخرى لاحقاً.",
        "import": "استيراد صفحات",
index 38003ef..f841ab9 100644 (file)
        "userjspreview": "'''تذكر أنك فقط تجرب/تعاين جافاسكربت.'''\n'''مازال ما  صراش التسجيل!'''",
        "sitecsspreview": "'''تفكر أنك راك تعرض الأنماط المتراصة (CSS) الخاصة بيك فقط\nو تمش حفظها بعد!'''",
        "sitejspreview": "'''تذكر أنك فقط تجرب/تعاين جافاسكربت.'''\n'''مازال ما  صراش التسجيل!'''",
-       "userinvalidcssjstitle": "'''تحذير:''' ما كانش تلبيسة\"$1\".\nتفكر بلي ملفات ال.css و ال.js تستعمل حروف صغيرة في العنوان ، كمثال {{ns:user}}:Foo/vector.css و ليس {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "'''تحذير:''' ما كانش تلبيسة\"$1\".\nتفكر بلي ملفات ال.css و ال.js تستعمل حروف صغيرة في العنوان ، كمثال {{ns:user}}:Foo/vector.css و ليس {{ns:user}}:Foo/Vector.css.",
        "updated": "ميزاجور (تحديث)",
        "note": "'''ملاحظة:'''",
        "previewnote": "'''ما تنساش هذي معاينه قبليه.'''\nلدوك التبديلات دياولك ما تسجلوش!",
index 367890f..4c48f6f 100644 (file)
        "userjspreview": "'''ma tnsa-ċ billa JavaScript ṫaĝk mazal f-mrḫalt l-iĝdad o-ṫjriba.'''\n'''rah mazal ma ṫsjjal-ċ!'''",
        "sitecsspreview": "'''ma tnsa-ċ billa CSS ṫaĝk mazal ġir f-mrḫalt l-iĝdad.'''\n'''rah mazal ma ṫsjjal-ċ!'''",
        "sitejspreview": "'''ma tnsa-ċ billa JavaScript ṫaĝk mazal ġir f-mrḫalt l-iĝdad.'''\n'''rah mazal ma ṫsjjal-ċ!'''",
-       "userinvalidcssjstitle": "'''ṫḫdir:''' ma kayn ḫṫṫa ċi skin \"$1\".\nṣfaḫi .css o-.js l-moĥṣṣaṣa ĥddama b-ĝanawin minuscule, bḫal {{ns:user}}:Foo/vector.css o-maċi bḫal {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "'''ṫḫdir:''' ma kayn ḫṫṫa ċi skin \"$1\".\nṣfaḫi .css o-.js l-moĥṣṣaṣa ĥddama b-ĝanawin minuscule, bḫal {{ns:user}}:Foo/vector.css o-maċi bḫal {{ns:user}}:Foo/Vector.css.",
        "updated": "(mohdata)",
        "note": "'''molahada:'''",
        "previewnote": "'''Dir fe balek belli hadċi ġir prévizualizasyon.'''\nDakċi li beddelṫi mazal ma ṫċejjel !",
        "prefs-files": "milffat",
        "prefs-custom-css": "personalisé CSS",
        "prefs-custom-js": "personalisé JavaScript",
-       "prefs-common-css-js": "CSS/JavaScript l-moċṫarak bin jmiĝ s-skinaṫ:",
+       "prefs-common-config": "CSS/JavaScript l-moċṫarak bin jmiĝ s-skinaṫ:",
        "prefs-emailconfirm-label": "konfirmi l'email:",
        "youremail": "I-Méyl",
        "username": "smiṫ l-mosṫĥdim:",
index fbc25af..36e3e8a 100644 (file)
        "summary-preview": "بروفه للملخص:",
        "subject-preview": "بروفة للعنوان/للموضوع",
        "blockedtitle": "اليوزر ممنوع",
-       "blockedtext": "'''تم منع اسم اليوزر أو عنوان الااى بى بتاعك .'''\n\nسبب المنع هو: ''$2''. وقام بالمنع $1.\n\n* بداية المنع: $8\n* انتهاء المنع: $6\n* الممنوع المقصود: $7\n\nممكن التواصل مع $1 لمناقشة المنع، أو مع واحد من [[{{MediaWiki:Grouppage-sysop}}|الاداريين]] عن المنع>\nافتكر انه مش ممكن تبعت ايميل  لليوزرز الا اذا كنت سجلت عنوان ايميل صحيح فى صفحة [[Special:Preferences|التفضيلات]] بتاعتك.\nعنوان الااى بى بتاعك حاليا هو $3 وكود المنع هو #$5.من فضلك ضيف اى واحد منهم أو كلاهما فى اى رسالة للتساؤل عن المنع.",
+       "blockedtext": "'''تم منع اسم اليوزر أو عنوان الاى بى بتاعك .'''\n\nسبب المنع هو: ''$2''. وقام بالمنع $1.\n\n* بداية المنع: $8\n* انتهاء المنع: $6\n* الممنوع المقصود: $7\n\nممكن التواصل مع $1 لمناقشة المنع، أو مع واحد من [[{{MediaWiki:Grouppage-sysop}}|الاداريين]] عن المنع>\nافتكر انه مش ممكن تبعت ايميل  لليوزرز الا اذا كنت سجلت عنوان ايميل صحيح فى صفحة [[Special:Preferences|التفضيلات]] بتاعتك.\nعنوان الااى بى بتاعك حاليا هو $3 وكود المنع هو #$5.من فضلك ضيف اى واحد منهم أو كلاهما فى اى رسالة للتساؤل عن المنع.",
        "autoblockedtext": "عنوان الأيبى بتاعك اتمنع اتوماتيكى  علشان فى يوزر تانى استخدمه واللى هو كمان ممنوع بــ $1.\nالسبب هو:\n\n:''$2''\n\n* بداية المنع: $8\n* انهاية المنع: $6\n* الممنوع المقصود: $7\n\nممكن تتصل  ب $1 أو واحد من\n[[{{MediaWiki:Grouppage-sysop}}|الإداريين]] االتانيين لمناقشة المنع.\n\nلاحظ أنه مش ممكن استخدام خاصية \"ابعت رسالة لليوزر دا\" إلا اذا كان عندك ايميل صحيح متسجل فى [[Special:Preferences|تفضيلاتك]].\n\nعنوان الأيبى الحالى الخاص بك هو $3، رقم المنع هو $5. لو سمحت تذكر الرقم دا فى اى استفسار.",
        "blockednoreason": "ما فيش سبب",
        "whitelistedittext": "لازم $1 علشان تقدر تعدل الصفحات.",
        "userjsyoucanpreview": "'''ملاحظة:''' استعمل زرار \"{{int:showpreview}}\" علشان تجرب النمط (CSS) أو الجافا سكريبت الجديد قبل تسييڤ الصفحه.",
        "usercsspreview": "'''افتكر انك  بتعرض  (CSS) بتاع اليوزر بس.\nهى لسه ماتسييڤتش!'''",
        "userjspreview": "'''أفتكر أنك بس بتجرب/بتعرض الجافا سكريبت بتاع اليوزر بتاعك، و انها لسة ماتحفظتش!'''",
-       "userinvalidcssjstitle": "'''تحذير:'''مافيش واجهة  \"$1\".\nافتكر أن ملفات ال.css و ال.js بتستخدم حروف صغيرة فى العنوان ، مثلا {{ns:user}}:Foo/vector.css و مش {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "'''تحذير:'''مافيش واجهة  \"$1\".\nافتكر أن ملفات ال.css و ال.js بتستخدم حروف صغيرة فى العنوان ، مثلا {{ns:user}}:Foo/vector.css و مش {{ns:user}}:Foo/Vector.css.",
        "updated": "(متحدثة)",
        "note": "'''ملحوظه:'''",
        "previewnote": "'''دى بروفه للصفحه بس.'''\nولسه ما تسييفتش! ،",
        "ipb_already_blocked": "\"$1\" ممنوع فعلا",
        "ipb-needreblock": "$1 ممنوع فعلا. عايز تغير الإعدادات؟",
        "ipb-otherblocks-header": "{{PLURAL:$1||المنع التانى|المنعين التانيين|المنوعات التانيين}}",
-       "ipb_cant_unblock": "غلطه: عنوان الااى بى الممنوع  مش موجود  $1.\nيمكن اترفع منعه فعلا.",
+       "ipb_cant_unblock": "غلطه: عنوان الاى بى الممنوع  مش موجود  $1.\nيمكن اترفع منعه فعلا.",
        "ipb_blocked_as_range": "غلط: الأيبى $1 مش ممنوع مباشرةو مش ممكن رفع المنع عنه.\nبس هو، على الرغم من كدا،ممنوع لانه جزء من النطاق $2، و اللى ممكن رفع المنع عنه.",
        "ip_range_invalid": "نطاق عناوين الأيبى مش صحيح.",
        "ip_range_toolarge": "حدود المنع اللى اكبر من /$1 مش مسموح بيها.",
index 43e7591..8be15c8 100644 (file)
        "userjspreview": "'''মনত ৰাখিব আপুনি আপোনাৰ ব্যৱহাৰকাৰী জাভালিপি কেৱল পৰীক্ষা/প্ৰাকদৰ্শন কৰিছে ।'''\n '''এইটো এতিয়াও সাঁচি ৰখা হোৱা নাই ।'''",
        "sitecsspreview": "'''মনত ৰাখিব আপুনি কেৱল এইটো CSS  প্ৰাকদৰ্শন কৰিছে ।''' \n'''এইটো এতিয়াও সাঁচি ৰখা হোৱা নাই !'''",
        "sitejspreview": "'''মনত ৰাখিব আপুনি এই জাভালিপি ক’ড কেৱল প্ৰাকদৰ্শন কৰিছে ।'''\n '''এইটো এতিয়াও সাঁচি ৰখা হোৱা নাই !'''",
-       "userinvalidcssjstitle": "'''সতৰ্কবাণী:'''  \"$1\" নামৰ কোনো আৱৰণ নাই। Custom .css আৰু .js পৃষ্ঠাই সৰুফলাৰ শিৰোনামা ব্যৱহাৰ কৰে, যেনে-  {{ns:user}}:Foo/Vector.css ৰ সলনি {{ns:user}}:Foo/vector.css।",
+       "userinvalidconfigtitle": "'''সতৰ্কবাণী:'''  \"$1\" নামৰ কোনো আৱৰণ নাই। Custom .css আৰু .js পৃষ্ঠাই সৰুফলাৰ শিৰোনামা ব্যৱহাৰ কৰে, যেনে-  {{ns:user}}:Foo/Vector.css ৰ সলনি {{ns:user}}:Foo/vector.css।",
        "updated": "(আপডেট কৰা হ'ল)",
        "note": "'''টোকা:'''",
        "previewnote": "'''মনত ৰাখিব এয়া মাথোন প্ৰাক্‌দৰ্শনহে।'''\nআপোনাৰ সালসলনিসমূহ এতিয়াও সংৰক্ষণ কৰা হোৱা নাই!",
        "prefs-files": "ফাইলসমূহ",
        "prefs-custom-css": "স্বনিৰ্ধাৰিত CSS",
        "prefs-custom-js": "স্বনিৰ্ধাৰিত জাভা লিপি",
-       "prefs-common-css-js": "সকলো আৱৰণৰ বাবে উমৈহতীয়া চি.এচ.এচ./জাভালিপি",
+       "prefs-common-config": "সকলো আৱৰণৰ বাবে উমৈহতীয়া চি.এচ.এচ./জাভালিপি",
        "prefs-reset-intro": "আপুনি এই পৃষ্ঠা ব্যৱহাৰ কৰি আপোনাৰ পছন্দসমূহক চাইটৰ পূৰ্বনিৰ্ধাৰিত ছেটঙলৈ ঘূৰাই নিব পাৰে ।\nএই পৰিৱৰ্তন পিছত সলাব পৰা নাযাব ।",
        "prefs-emailconfirm-label": "ইমেইল নিশ্চিতকৰণ:",
        "youremail": "আপোনাৰ ই-মেইল *",
index 258e0c1..0404cf0 100644 (file)
        "userjspreview": "'''Recuerda que namái ye la prueba/vista previa del JavaScript d'usuariu.'''\n'''¡Inda nun ta guardáu!'''",
        "sitecsspreview": "'''Recuerda que namái tas previsualizando esti CSS.'''\n'''¡Tovía nun ta guardáu!'''",
        "sitejspreview": "'''Recuerda que namái tas probando esti códigu JavaScript.'''\n'''¡Inda nun tá guardáu!'''",
-       "userinvalidcssjstitle": "'''Avisu:''' Nun esiste'l tema «$1».\nLes páxines personalizaes de .css y .js usen un títulu en minúscules, p. ex. {{ns:user}}:Foo/vector.css y non {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "'''Avisu:''' Nun esiste'l tema «$1».\nLes páxines personalizaes de .css y .js usen un títulu en minúscules, p. ex. {{ns:user}}:Foo/vector.css y non {{ns:user}}:Foo/Vector.css.",
        "updated": "(Anovao)",
        "note": "'''Nota:'''",
        "previewnote": "'''Alcuerdate de qu'esto ye sólo una vista previa.'''\n¡Los cambios entá nun se guardaron!",
        "prefs-files": "Ficheros",
        "prefs-custom-css": "CSS personalizáu",
        "prefs-custom-js": "JavaScript personalizáu",
-       "prefs-common-css-js": "CSS/JavaScript compartíu pa toles pieles:",
+       "prefs-common-config": "CSS/JavaScript compartíu pa toles pieles:",
        "prefs-reset-intro": "Pues usar esta páxina pa reaniciar les preferencies a los valores predeterminaos del sitiu.\nEsto nun se pue desfacer.",
        "prefs-emailconfirm-label": "Confirmación del corréu:",
        "youremail": "Corréu electrónicu:",
        "thumbnail_dest_directory": "Nun pudo crease'l direutoriu de destín",
        "thumbnail_image-type": "Triba d'imaxe ensin sofitu",
        "thumbnail_gd-library": "Configuración incompleta de la biblioteca GD: falta la función $1",
+       "thumbnail_image-size-zero": "El tamañu del ficheru d'imaxe paez que ye cero.",
        "thumbnail_image-missing": "Paez que falta'l ficheru: $1",
        "thumbnail_image-failure-limit": "Hebo demasiaos intentos recientes que fallaron ($1 o más) al representar esta miniatura. Vuelva a intentalo más sero.",
        "import": "Importar páxines",
index 6849379..0db063c 100644 (file)
        "userjspreview": "'''ध्यान दिहा जाय कि आप आपन जावास्क्रिप्ट  कय झलक देखा जात है।'''\n'''इ अभीन तक नाई सहेजा है!'''",
        "sitecsspreview": "'''ध्यान दिहा जाय कि आप ई सी॰एस॰एस कय झलक देखा जात है।'''\n'''इ अभीन तक नाई सहेजा है!'''",
        "sitejspreview": "'''ध्यान दिहा जाय कि आप ई जावास्क्रिप्ट कय झलक देखा जात है।'''\n'''इ अभीन तक नाई सहेजा है!'''",
-       "userinvalidcssjstitle": "'''चेतावनी:''' \"$1\" नाव कय कवनो त्वचा नाइ है।\nबदलल .css औ .js पन्नन कय शीर्षक नीचे स्तर कय लिपि (lowercase) कय प्रयोग करत है। उदाहरण: {{ns:user}}:Foo/vector.css नाई की {{ns:user}}:Foo/Vector.css",
+       "userinvalidconfigtitle": "'''चेतावनी:''' \"$1\" नाव कय कवनो त्वचा नाइ है।\nबदलल .css औ .js पन्नन कय शीर्षक नीचे स्तर कय लिपि (lowercase) कय प्रयोग करत है। उदाहरण: {{ns:user}}:Foo/vector.css नाई की {{ns:user}}:Foo/Vector.css",
        "updated": "(अपडेट करल)",
        "note": "'''सूचना:'''",
        "previewnote": "'''याद रख्खा जाय, ई खाली एक झलक होय।'''\nआप कय बदलाव अभीन तक नाई सहेजा है!",
        "prefs-files": "फ़ाइल",
        "prefs-custom-css": "खासमखास सी॰एस॰एस",
        "prefs-custom-js": "खासमखास जावास्क्रिप्ट",
-       "prefs-common-css-js": "कुल स्किन कय लिए साझा सी॰एस॰एस/जावास्क्रिप्ट:",
+       "prefs-common-config": "कुल स्किन कय लिए साझा सी॰एस॰एस/जावास्क्रिप्ट:",
        "prefs-reset-intro": "आप इ पन्ना से अपने पसंद कय, साइट कय मूल पसंद कय जइसन बनाय सका जात है।\nएकरे बाद आप वापिस पुरान जगहि पे नाइ आय पावा जाइ।",
        "prefs-emailconfirm-label": "ईमेल सुनीश्चित करा जाय:",
        "youremail": "ई-मेल:",
index f428e9c..8d4c0d7 100644 (file)
        "prefs-files": "Fayllar",
        "prefs-custom-css": "Xüsusi CSS",
        "prefs-custom-js": "Xüsusi JavaScript",
-       "prefs-common-css-js": "Bütün skinlər üçün ümumi CSS/JavaScript:",
+       "prefs-common-config": "Bütün skinlər üçün ümumi CSS/JavaScript:",
        "prefs-reset-intro": "Bu səhifəni nizamlamalarınızı ilkin vəziyyətə gətirmək üçün istifadə edə bilərsiniz. Bu əməliyyat geri qaytarıla bilməz.",
        "prefs-emailconfirm-label": "E-poçtun təsdiqlənməsi:",
        "youremail": "E-məktub:",
index 3137638..35e89ee 100644 (file)
        "userjspreview": "'خاتیرلادیریق کی، سیز یالنیز سی اس اس -ده تئست/سیناق گؤستریشی ائتمیسینیز.'\n'بو هله یادداشدا ساخلانیلماییب!'",
        "sitecsspreview": "'''خاتیرلادیریق کی، سیز یالنیز سی‌اس‌اس-ده سیناق گؤستریشی ائتمیسینیز.'''\n'''بو هله یادداشدا ساخلانیلماییب!'''",
        "sitejspreview": "'خاتیرلادیریق کی، سیز یالنیز ژاواسجریپت کودوندا سیناق گؤستریشی ائتمیسینیز.'\n'بو هله یادداشدا ساخلانیلماییب!'",
-       "userinvalidcssjstitle": "خاتیرلادیرق:' '\"$1\" آدییلا بیر پوشه یوخ‌دور. پوشه-آدی.css و. js فایل‌لارینین آدلاری کیچیک حرف ایله یازماسی لازیم‌دیر، یعنی {{ns:user}}: تمل / vector.css دئییل، {{ns:user}}: تمل / Vector.css.",
+       "userinvalidconfigtitle": "خاتیرلادیرق:' '\"$1\" آدییلا بیر پوشه یوخ‌دور. پوشه-آدی.css و. js فایل‌لارینین آدلاری کیچیک حرف ایله یازماسی لازیم‌دیر، یعنی {{ns:user}}: تمل / vector.css دئییل، {{ns:user}}: تمل / Vector.css.",
        "updated": "(گونجل‌لندی)",
        "note": "'''دیقت:'''",
        "previewnote": "<strong>بونون تکجه بیر سیناق گؤستریشی اولدوغونو نظرده آلین.</strong>\nسیزین دییشیکلرینیز هله ذخیره اولونماییب!",
        "prefs-files": "فایل‌لار",
        "prefs-custom-css": "شخصی سی‌اس‌اس",
        "prefs-custom-js": "شخصی جاوااسکریپت",
-       "prefs-common-css-js": "بوتون قابیقلار اوچون پایلاشمیش سی‌اس‌اس/جاوااسکریپت:",
+       "prefs-common-config": "بوتون قابیقلار اوچون پایلاشمیش سی‌اس‌اس/جاوااسکریپت:",
        "prefs-reset-intro": "ترجیحلرینیزی سایتین ایلک فرض ائدیلنلرینه دؤندرمک اوچون، بو صحیفه‌دن ایستیفاده ائده بیلرسینیز.\nبو ایش قایتاریلا بیلمز.",
        "prefs-emailconfirm-label": "ایمیل دوغرولاماسی:",
        "youremail": "ایمیل:",
        "recentchanges-legend-heading": "<strong>قیسالتمالار:</strong>",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (بیرده [[Special:NewPages|یئنی صفحه‌لرین لیستینه]] باخین)",
        "rcfilters-other-review-tools": "داها یوخلاما آلتلری",
+       "rcfilters-activefilters": "چالیشقان فیلترلر",
        "rcfilters-savedqueries-defaultlabel": "ذخیره اوْلونموش فیلترلر",
+       "rcfilters-filter-humans-label": "اینسان (غئیر روْبات)",
+       "rcfilters-filter-pageedits-label": "صفحه دییشدیرمه‌لری",
+       "rcfilters-filter-newpages-label": "صفحه یاراتما",
        "rcnotefrom": "آشاغی داکی دَییشیک لرده <strong>$3, $4</strong> (دن <strong>$1</strong> {{PLURAL:$5|چان گوستریلیب|چان گوستریلیب دیر}}).",
        "rclistfrom": "$3 $2 واختیندان باشلایاراق یئنی دییشیکلری گؤستر",
        "rcshowhideminor": "کیچیک دَییشیکلری $1",
index a44a7c0..fbfde93 100644 (file)
        "userjspreview": "'''Был бары тик JavaScript файлын алдан ҡарау ғына, ул әле һаҡланмаған!'''",
        "sitecsspreview": "'''Һеҙ CSS файлын алдан ҡарайһығыҙ ғына икәнен иҫегеҙҙә тотоғоҙ.'''\n'''Ул әле һаҡланмаған!'''",
        "sitejspreview": "'''Һеҙ JavaScript кодын алдан ҡарайһығыҙ ғына икәнен иҫегеҙҙә тотоғоҙ.'''\n'''Ул әле һаҡланмаған!'''",
-       "userinvalidcssjstitle": "'''Иғтибар:''' \"$1\" биҙәү темаһы табылманы. Иҫтә тотоғоҙ, .css һәм .js ҡулланыусы биттәренең исемдәре тик бәләкәй хәрефтәрҙән генә торорға тейеш. Мәҫәлән: {{ns:user}}:Foo/vector.css, ә {{ns:user}}:Foo/Vector.css түгел!",
+       "userinvalidconfigtitle": "'''Иғтибар:''' \"$1\" биҙәү темаһы табылманы. Иҫтә тотоғоҙ, .css һәм .js ҡулланыусы биттәренең исемдәре тик бәләкәй хәрефтәрҙән генә торорға тейеш. Мәҫәлән: {{ns:user}}:Foo/vector.css, ә {{ns:user}}:Foo/Vector.css түгел!",
        "updated": "(Яңыртылды)",
        "note": "'''Иҫкәрмә:'''",
        "previewnote": "'''Ҡарап сығыу өлгөһө, әлегә үҙгәрештәр яҙҙырылмаған!'''\nҺеҙҙең үҙгәртеүҙәр әле яҙылмаған!",
        "prefs-files": "Файлдар",
        "prefs-custom-css": "Үҙ CSS",
        "prefs-custom-js": "Үҙ JS",
-       "prefs-common-css-js": "Бөтә күренештәр өсөн дөйөм CSS/JS:",
+       "prefs-common-config": "Бөтә күренештәр өсөн дөйөм CSS/JS:",
        "prefs-reset-intro": "Был битте, көйләүҙәрегеҙҙе ғәҙәттәге көйләүҙәргә ташлатыу өсөн ҡулланып була.\nРаҫлағандан һуң ғәмәлде кире ҡайтарып булмаясаҡ.",
        "prefs-emailconfirm-label": "Электрон почтаны раҫлау:",
        "youremail": "Электрон почта :",
index 22f8bbb..c1b64d7 100644 (file)
        "userjspreview": "''''په یاد دار که شما فقط وتی کاربری  JavaScript بازبینی/آزمایش کنگیت، هنگت ذخیره نه بوتت!''''",
        "sitecsspreview": "<strong> شمارء هیالداری ببیت که انیگء تهنا پیشچارگ چه ائ سی اس اسء رء گند ات.\nآئی انگت ذخیرگ نبیتگ انت </strong>",
        "sitejspreview": "<strong> شمارء هیالداری ببیت که انیگء تهنا پیشچارگ چه ائ جاوا اسکریپٹء رء گند ات.\nآئی انگت ذخیرگ نبیتگ انت </strong>",
-       "userinvalidcssjstitle": "'''هوژاری:''هچ جلدی نیست\"$1\".\nبزان که صفحات .css و .js چه عناوین گون هوردین حرف استفاده کننت، مثلا {{ns:user}}:Foo/vector.css بدل به په {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "'''هوژاری:''هچ جلدی نیست\"$1\".\nبزان که صفحات .css و .js چه عناوین گون هوردین حرف استفاده کننت، مثلا {{ns:user}}:Foo/vector.css بدل به په {{ns:user}}:Foo/Vector.css.",
        "updated": "(په روچ بیتگین)",
        "note": "'''یادداشت:'''",
        "previewnote": "<strong> بزان که ائ تهنا یک پیشچارگء انت</strong>\nشمئی ٹگلان انگتء ذخیرگ نبیتگ انت!",
        "prefs-files": "فایلان",
        "prefs-custom-css": "رسمی سی‌اس‌اس",
        "prefs-custom-js": "رسمی جی‌اس",
-       "prefs-common-css-js": "یک پیمین سی‌اس‌اس/جاوااسکریپٹ پر پهکین اسکین:",
+       "prefs-common-config": "یک پیمین سی‌اس‌اس/جاوااسکریپٹ پر پهکین اسکین:",
        "prefs-reset-intro": "شما توانت چه ای  صفحه په واترینگ تنظیمات وت په پیش‌فرض استفاده کنیت. ای کار بازگشت‌ناپذیر انت.",
        "prefs-emailconfirm-label": "تایید کتن ایمیل:",
        "youremail": "ایمیل:",
index 2208f5b..f234fe2 100644 (file)
        "userjspreview": "'''Giromdomon tabi na pigtetest/pighihiling mo sana an patanaw kan saimong JavaScript nin paragamit, dai pa ini naitagama!'''",
        "sitecsspreview": "'''Giromdoma baya na ika nagtatanaw pa sana kaining CSS.'''\n'''Ini dae pa tabi naitatagama!'''",
        "sitejspreview": "'''Giromdoma baya na ika nagtatatanaw pa sana kaining koda sa JavaScript.'''\n'''Ini dae pa tabi naitatagama!'''",
-       "userinvalidcssjstitle": "'''Patanid:''' Mayong ''skin'' na \"$1\". Giromdomon tabî na an .css asin .js na mga páhina naggagamit nin titulong nakasurat sa sadit na letras, halimbawa {{ns:user}}:Foo/vector.css bakong {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "'''Patanid:''' Mayong ''skin'' na \"$1\". Giromdomon tabî na an .css asin .js na mga páhina naggagamit nin titulong nakasurat sa sadit na letras, halimbawa {{ns:user}}:Foo/vector.css bakong {{ns:user}}:Foo/Vector.css.",
        "updated": "(Pinagsugpunan na)",
        "note": "'''Paisi:'''",
        "previewnote": "'''Giromdoma na ini sarong patanaw pa sana.'''\nAn saimong mga pinagriliwat dae pa tabi naitatagama!",
        "prefs-files": "Mga dokumento",
        "prefs-custom-css": "Kustombreng CSS",
        "prefs-custom-js": "Kustombreng JavaScript",
-       "prefs-common-css-js": "Pinagheras na CSS/JavaScript para sa gabos na mga kalapatan:",
+       "prefs-common-config": "Pinagheras na CSS/JavaScript para sa gabos na mga kalapatan:",
        "prefs-reset-intro": "Ika makakagamit kaining pahina tanganing ilapat giraray an saimong mga kabotan sa panugmad kan sayt.\nIni dae tabi matitingkog.",
        "prefs-emailconfirm-label": "Kumpirmasyon sa E-koreo",
        "youremail": "E-surat:",
index 5b863e7..8a14787 100644 (file)
        "userjspreview": "<strong>Памятайце, што гэта толькі папярэдні прагляд Вашага JavaScript. Ён яшчэ не запісаны!</strong>",
        "sitecsspreview": "<strong>Памятайце, што гэта толькі папярэдні прагляд гэтага CSS.\nЁн яшчэ не захаваны!</strong>",
        "sitejspreview": "<strong>Памятайце, што гэта толькі папярэдні прагляд гэтага коду JavaScript.\nЁн яшчэ не захаваны!</strong>",
-       "userinvalidcssjstitle": "<strong>Папярэджаньне:</strong> няма тэмы афармленьня «$1».\nПамятайце, што ўласныя старонкі .css і .js павінны мець назву, якая складаецца з малых літараў, напрыклад, {{ns:user}}:Хтосьці/vector.css, а не {{ns:user}}:Хтосьці/Vector.css.",
+       "userinvalidconfigtitle": "<strong>Папярэджаньне:</strong> няма тэмы афармленьня «$1».\nПамятайце, што ўласныя старонкі .css і .js павінны мець назву, якая складаецца з малых літараў, напрыклад, {{ns:user}}:Хтосьці/vector.css, а не {{ns:user}}:Хтосьці/Vector.css.",
        "updated": "(Абноўлена)",
        "note": "<strong>Заўвага:</strong>",
        "previewnote": "<strong>Гэта толькі папярэдні прагляд.</strong>\nВашыя зьмены яшчэ не былі захаваныя!",
        "prefs-files": "Файлы",
        "prefs-custom-css": "Уласны CSS",
        "prefs-custom-js": "Уласны JavaScript",
-       "prefs-common-css-js": "Агульны CSS/JavaScript для ўсіх тэмаў афармленьня:",
+       "prefs-common-config": "Агульны CSS/JavaScript для ўсіх тэмаў афармленьня:",
        "prefs-reset-intro": "Вы можаце выкарыстаць гэтую старонку для замены вашых наладаў на налады сайту па змоўчаньні.\nГэтае дзеяньне ня можа быць адмененае.",
        "prefs-emailconfirm-label": "Пацьверджаньне адрасу электроннай пошты:",
        "youremail": "Адрас электроннай пошты:",
        "right-undelete": "Аднаўленьне старонак",
        "right-suppressrevision": "праглядаць, хаваць і аднаўляць пэўныя вэрсіі старонак, зробленыя любым удзельнікам",
        "right-viewsuppressed": "праглядаць вэрсіі старонак, схаваныя ад усіх удзельнікаў",
-       "right-suppressionlog": "прагляд прыватных журналаў",
-       "right-block": "блякаваньне іншых удзельнікаў ад рэдагаваньняў",
-       "right-blockemail": "блÑ\8fкаванÑ\8cне Ñ\96нÑ\88Ñ\8bÑ\85 Ñ\9eдзелÑ\8cнÑ\96каÑ\9e Ð°Ð´ Ð´Ð°Ñ\81Ñ\8bлкÑ\96 Ñ\8dлекÑ\82Ñ\80оннай Ð¿Ð¾Ñ\88Ñ\82Ñ\8b",
-       "right-hideuser": "блякаваньне рахунку ўдзельніка і яго хаваньне",
-       "right-ipblock-exempt": "абÑ\85од Ð±Ð»Ñ\8fкаванÑ\8cнÑ\8fÑ\9e IP-адÑ\80аÑ\81оÑ\9e, Ð°Ñ\9eÑ\82а-блякаваньняў і блякаваньняў дыяпазонаў",
-       "right-unblockself": "разблякаваньне самога сябе",
-       "right-protect": "зьмена ўзроўню абароны старонак і рэдагаваньне каскадна абароненых старонак",
+       "right-suppressionlog": "Ð\9fрагляд прыватных журналаў",
+       "right-block": "Ð\91лякаваньне іншых удзельнікаў ад рэдагаваньняў",
+       "right-blockemail": "Ð\91лÑ\8fкаванÑ\8cне Ñ\9eдзелÑ\8cнÑ\96каÑ\9e Ð°Ð´ Ð°Ð´Ð¿Ñ\80аÑ\9eкÑ\96 Ð»Ñ\96Ñ\81Ñ\82оÑ\9e Ñ\8dлекÑ\82Ñ\80оннай Ð¿Ð¾Ñ\88Ñ\82ай",
+       "right-hideuser": "Ð\91лякаваньне рахунку ўдзельніка і яго хаваньне",
+       "right-ipblock-exempt": "Ð\90бÑ\85од Ð±Ð»Ñ\8fкаванÑ\8cнÑ\8fÑ\9e IP-адÑ\80аÑ\81оÑ\9e, Ð°Ñ\9eÑ\82аблякаваньняў і блякаваньняў дыяпазонаў",
+       "right-unblockself": "Разблякаваньне самога сябе",
+       "right-protect": "Ð\97ьмена ўзроўню абароны старонак і рэдагаваньне каскадна абароненых старонак",
        "right-editprotected": "рэдагаваньне старонак, абароненых у рэжыме «{{int:protect-level-sysop}}»",
        "right-editsemiprotected": "рэдагаваньне старонак, абароненых у рэжыме «{{int:protect-level-autoconfirmed}}»",
        "right-editcontentmodel": "рэдагаваньне мадэлі зьместу старонкі",
        "thumbnail_dest_directory": "Немагчыма стварыць мэтавую дырэкторыю",
        "thumbnail_image-type": "Тып выявы не падтрымліваецца",
        "thumbnail_gd-library": "Няпоўная канфігурацыя бібліятэкі GD: няма функцыі $1",
+       "thumbnail_image-size-zero": "Падобна, што файл мае нулявы памер.",
        "thumbnail_image-missing": "Верагодна няма файла $1",
        "thumbnail_image-failure-limit": "Было зроблена зашмат няўдалых спробаў ($1 ці болей) сфармаваць гэтую мініятуру. Калі ласка, паспрабуйце пазьней.",
        "import": "Імпартаваць старонкі",
index 795ab33..5a6d37c 100644 (file)
        "userjspreview": "<strong>Памятайце, што гэта толькі выпрабаванне/папярэдні паказ вашага JavaScript. Праўкі яшчэ не замацаваныя!</strong>",
        "sitecsspreview": "<strong>Памятайце, што гэта толькі папярэдні паказ вашага CSS.\nПраўкі яшчэ не замацаваныя!</strong>",
        "sitejspreview": "<strong>Памятайце, што гэта толькі папярэдні паказ вашага JavaScript.\nПраўкі яшчэ не замацаваныя!</strong>",
-       "userinvalidcssjstitle": "<strong>Увага:</strong> Няма вокладкі з назвай \"$1\". Памятайце, што свае старонкі .css і .js называюцца толькі малымі літарамі, такім чынам, напр., {{ns:user}}:Foo/vector.css, а не {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "<strong>Увага:</strong> Няма вокладкі з назвай \"$1\". Памятайце, што свае старонкі .css і .js называюцца толькі малымі літарамі, такім чынам, напр., {{ns:user}}:Foo/vector.css, а не {{ns:user}}:Foo/Vector.css.",
        "updated": "(Абноўлена)",
        "note": "<strong>Заўвага:</strong>",
        "previewnote": "<strong>Памятайце, гэта толькі папярэдні паказ.</strong> Праўкі яшчэ не замацаваныя!",
        "prefs-files": "Файлы",
        "prefs-custom-css": "Уласны CSS",
        "prefs-custom-js": "Уласны JS",
-       "prefs-common-css-js": "Агульны CSS/JavaScript для ўсіх вокладак:",
+       "prefs-common-config": "Агульны CSS/JavaScript для ўсіх вокладак:",
        "prefs-reset-intro": "Тут можна вярнуць свае настройкі да прадвызначэнняў, прынятых на гэтай пляцоўцы.\nАдкаціць гэтае дзеянне нельга.",
        "prefs-emailconfirm-label": "Пацвярджэнне адраса эл.пошты:",
        "youremail": "Эл.пошта *",
index 2b07773..c556e4f 100644 (file)
        "userjspreview": "<strong>Не забравяйте, че това е само изпробване/предварителен преглед на кода на JavaScript.\nСтраницата все още не е съхранена!</strong>",
        "sitecsspreview": "<strong>Не забравяйте, че това е само предварителен преглед на този CSS.\nТой все още не е съхранен!</strong>",
        "sitejspreview": "<strong>Не забравяйте, че това е само предварителен преглед на този JavaScript код.\nТой все още не е съхранен!</strong>",
-       "userinvalidcssjstitle": "<strong>Внимание:</strong> Не съществува облик „$1“. Необходимо е да се знае, че имената на потребителските ви страници за CSS и JavaScript трябва да се състоят от малки букви, например: „{{ns:user}}:Иван/vector.css“ (а не „{{ns:user}}:Иван/Vector.css“).",
+       "userinvalidconfigtitle": "<strong>Внимание:</strong> Не съществува облик „$1“. Необходимо е да се знае, че имената на потребителските ви страници за CSS и JavaScript трябва да се състоят от малки букви, например: „{{ns:user}}:Иван/vector.css“ (а не „{{ns:user}}:Иван/Vector.css“).",
        "updated": "(обновена)",
        "note": "<strong>Забележка:</strong>",
        "previewnote": "<strong>Обърнете внимание, че това е само предварителен преглед.</strong>\nПромените все още не са съхранени!",
        "prefs-files": "Файлове",
        "prefs-custom-css": "Личен CSS",
        "prefs-custom-js": "Личен JS",
-       "prefs-common-css-js": "Общи настройки на CSS/JS за всички облици:",
+       "prefs-common-config": "Общи настройки на CSS/JS за всички облици:",
        "prefs-reset-intro": "Тази страница може да се използва за възстановяване на потребителските настройки към стандартните за сайта.\nТова действие е необратимо.",
        "prefs-emailconfirm-label": "Потвърждаване на адрес за е-поща:",
        "youremail": "Е-поща:",
        "revertpage-nouser": "Връщане на редакции на скрит потребител до последната версия на [[User:$1|$1]]",
        "rollback-success": "Отменени редакции на {{GENDER:$3|$1}};\nвъзвръщане към последната версия на {{GENDER:$4|$2}}.",
        "sessionfailure-title": "Прекъсната сесия",
-       "sessionfailure": "Изглежда има проблем със сесията ви; действието беше отказано като предпазна мярка срещу крадене на сесията. Натиснете бутона за връщане на браузъра, презаредете страницата, от която сте дошли, и опитайте отново.",
+       "sessionfailure": "Изглежда има проблем със сесията ви;\nдействието беше отказано като предпазна мярка срещу крадене на сесията.\nМоля, изпратете формуляра повторно.",
        "changecontentmodel": "Промяна на модела на съдържанието на страница",
        "changecontentmodel-legend": "Промяна на модела на съдържанието",
        "changecontentmodel-title-label": "Заглавие на страницата",
        "version-poweredby-others": "други",
        "version-poweredby-translators": "преводачи в translatewiki.net",
        "version-credits-summary": "Бихме искали да изкажем признателност на следните хора за техните приноси към [[Special:Version|МедияУики]].",
-       "version-license-info": "МедияУики е свободен софтуер, можете да го разпространявате и/или променяте съгласно условията на GNU General Public License, както е публикуван от Free Software Foundation, версия 2 на лиценза или (по ваше усмотрение) която и да е следваща версия.\n\nМедияУики се разпространява с надеждата, че ще бъде полезен, но БЕЗ НИКАКВИ ГАРАНЦИИ, без дори косвена гаранция за ПРОДАВАЕМОСТ или ПРИГОДНОСТ ЗА КОНКРЕТНА УПОТРЕБА. Вижте GNU General Public License за повече подробности.\n\nТрябва да сте получили [{{SERVER}}{{SCRIPTPATH}}/COPYING копие на GNU General Public License] заедно с тази програма. Ако не сте, пишете на Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA или го [//www.gnu.org/licenses/old-licenses/gpl-2.0.html прочетете в мрежата].",
+       "version-license-info": "МедияУики е свободен софтуер, можете да го разпространявате и/или променяте съгласно условията на GNU General Public License, както е публикуван от Free Software Foundation, версия 2 на лиценза или (по ваше усмотрение) която и да е следваща версия.\n\nМедияУики се разпространява с надеждата, че ще бъде полезен, но <em>БЕЗ НИКАКВИ ГАРАНЦИИ</em>, без дори косвена гаранция за <strong>ПРОДАВАЕМОСТ</strong> или <strong>ПРИГОДНОСТ ЗА КОНКРЕТНА УПОТРЕБА</strong>. Вижте GNU General Public License за повече подробности.\n\nТрябва да сте получили [{{SERVER}}{{SCRIPTPATH}}/COPYING копие на GNU General Public License] заедно с тази програма. Ако не сте, пишете на Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA или го [//www.gnu.org/licenses/old-licenses/gpl-2.0.html прочетете в мрежата].",
        "version-software": "Инсталиран софтуер",
        "version-software-product": "Продукт",
        "version-software-version": "Версия",
        "limitreport-postexpandincludesize-value": "$1/$2 {{PLURAL:$2|байт|байта}}",
        "limitreport-templateargumentsize-value": "$1/$2 {{PLURAL:$2|байт|байта}}",
        "expandtemplates": "Разгръщане на шаблони",
-       "expand_templates_intro": "Тази специална страница взима текст и рекурсивно разгръща всички шаблони в нея.\nТя разгръща и всички поддържани парсерни функции като\n<code><nowiki>{{</nowiki>#language:…}}</code> и променливи като\n<code><nowiki>{{</nowiki>CURRENTDAY}}</code>.\nНа практика тя разгръща почти всичко в двойни скоби.",
+       "expand_templates_intro": "Тази Ñ\81пеÑ\86иална Ñ\81Ñ\82Ñ\80аниÑ\86а Ð²Ð·Ð¸Ð¼Ð° Ñ\83икиÑ\82екÑ\81Ñ\82 Ð¸ Ñ\80екÑ\83Ñ\80Ñ\81ивно Ñ\80азгÑ\80Ñ\8aÑ\89а Ð²Ñ\81иÑ\87ки Ñ\88аблони Ð² Ð½ÐµÑ\8f.\nТÑ\8f Ñ\80азгÑ\80Ñ\8aÑ\89а Ð¸ Ð²Ñ\81иÑ\87ки Ð¿Ð¾Ð´Ð´Ñ\8aÑ\80жани Ð¿Ð°Ñ\80Ñ\81еÑ\80ни Ñ\84Ñ\83нкÑ\86ии ÐºÐ°Ñ\82о\n<code><nowiki>{{</nowiki>#language:â\80¦}}</code> Ð¸ Ð¿Ñ\80оменливи ÐºÐ°Ñ\82о\n<code><nowiki>{{</nowiki>CURRENTDAY}}</code>.\nÐ\9dа Ð¿Ñ\80акÑ\82ика Ñ\82Ñ\8f Ñ\80азгÑ\80Ñ\8aÑ\89а Ð¿Ð¾Ñ\87Ñ\82и Ð²Ñ\81иÑ\87ко Ð² Ð´Ð²Ð¾Ð¹Ð½Ð¸ Ñ\81коби.",
        "expand_templates_title": "Заглавие на страницата (напр. за {{FULLPAGENAME}}):",
        "expand_templates_input": "Входящ уикитекст:",
        "expand_templates_output": "Резултат",
index c25ab3e..9c2cf41 100644 (file)
        "prefs-files": "فایل ئان",
        "prefs-custom-css": "سی‌اس‌اس شخصی",
        "prefs-custom-js": "شخصی ئین جاوااسکریپت",
-       "prefs-common-css-js": "سی‌اس‌اس/جاوااسکریپت مشترک په موچین پوسته‌ئان:",
+       "prefs-common-config": "سی‌اس‌اس/جاوااسکریپت مشترک په موچین پوسته‌ئان:",
        "prefs-emailconfirm-label": "ایمیلئ تائید کورتین:",
        "youremail": "ایمیل:",
        "username": "{{GENDER:$1|کار زوروکئ نام}}:",
index bda0e06..f03f266 100644 (file)
        "userjspreview": "<strong>याद रहे की आप अपनी सदस्य जावास्क्रिप्ट के खाली टेस्ट करत बानी/नमूना देखत बानी।\nई अबहिन सहेजल ना गइल बाटे।</strong>",
        "sitecsspreview": "<strong>याद रहे की आप ए CSS क खाली नमूना देखत बानी।\nई अबहिन ले सहेजल ना गइल बा!</strong>",
        "sitejspreview": "<strong>याद रहे की आप ए जावास्क्रिप्ट कोड क खाली नमूना देखत बानी।\nई अबहिन ले सहेजल ना गइल बा!</strong>",
-       "userinvalidcssjstitle": "<strong>चेतावनी:</strong> कौनों skin \"$1\"नइखे।\nCustom .css आ .js पन्ना सभ छोटका अक्षर में टाइटिल इस्तेमाल करे लें जइसे की, {{ns:user}}:Foo/vector.css ना की {{ns:user}}:Foo/Vector.css।",
+       "userinvalidconfigtitle": "<strong>चेतावनी:</strong> कौनों skin \"$1\"नइखे।\nCustom .css आ .js पन्ना सभ छोटका अक्षर में टाइटिल इस्तेमाल करे लें जइसे की, {{ns:user}}:Foo/vector.css ना की {{ns:user}}:Foo/Vector.css।",
        "updated": "(अपडेट करल गईल)",
        "note": "'''सूचना:'''",
        "previewnote": "'''याद रखीं, इ एगो झलक मात्र हो।'''\nराउर बदलाव अभी तक सुरक्षित नईखे करल गईल!",
        "prefs-files": "फाइल सब",
        "prefs-custom-css": "व्यक्तिगत CSS",
        "prefs-custom-js": "व्यक्तिगत जावास्क्रिप्ट",
-       "prefs-common-css-js": "सगरी जिल्द खातिर साझा CSS/जावास्क्रिप्ट:",
+       "prefs-common-config": "सगरी जिल्द खातिर साझा CSS/जावास्क्रिप्ट:",
        "prefs-reset-intro": "रउआँ आपन पसंद बदल के डिफाल्ट करे खातिर ए पन्ना के इस्तेमाल नइखीं कर सकत।\n\nई फिर से वापस ना हो पाई।",
        "prefs-emailconfirm-label": "ईमेल जाँच:",
        "youremail": "ईमेल:",
index e1a1d8e..264f129 100644 (file)
        "userjspreview": "'''Ingatakan bahwasa Pian tis/manilik pamakai JavaScript Pian.'''\n'''Nangini baluman tasimpan pulang!'''",
        "sitecsspreview": "'''Ingatakan bahwasa Pian manilik CSS ini haja.'''\n'''Nangini lagi baluman tasimpan!'''",
        "sitejspreview": "'''Ingatakan bahwasa Pian manilik JavaScript code ini haja.'''\n'''Nangini lagi baluman tasimpan!'''",
-       "userinvalidcssjstitle": "'''Paringatan:''' Kadada kulit \"$1\".\nInatakan bahwasa saragam  tungkaran-tungkaran .css wan .js mamuruk aksara halus, cuntuh {{ns:user}}:Foo/vector.css sawagai tandingan {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "'''Paringatan:''' Kadada kulit \"$1\".\nInatakan bahwasa saragam  tungkaran-tungkaran .css wan .js mamuruk aksara halus, cuntuh {{ns:user}}:Foo/vector.css sawagai tandingan {{ns:user}}:Foo/Vector.css.",
        "updated": "(Dihanyarakan)",
        "note": "'''Catatan:'''",
        "previewnote": "'''Ingatakanlah bahwasa ngini titilikan haja''' Paubahan Pian baluman disimpan!",
        "prefs-files": "Barakas",
        "prefs-custom-css": "Saragamakan CSS",
        "prefs-custom-js": "Saraganakan JavaScript",
-       "prefs-common-css-js": "Babagi CSS/JavaScript gasan samunyaan skin:",
+       "prefs-common-config": "Babagi CSS/JavaScript gasan samunyaan skin:",
        "prefs-reset-intro": "Pian kawa mamuruk tungkaran ini hagan setel bulik kakatujuan Pian ka default situs.\nIni kada kawa diwalangi.",
        "prefs-emailconfirm-label": "Payakinakan suril:",
        "youremail": "Suril:",
index e39abd1..003966a 100644 (file)
        "userjspreview": "'''মনে রাখুন আপনি আপনার ব্যবহারকারী জাভাস্ক্রিপ্ট কেবলমাত্র পরীক্ষা/প্রাকদর্শন করছেন। এটা এখনও সংরক্ষণ করা হয়নি!'''",
        "sitecsspreview": "'''মনে রাখবেন আপনি আপনার জন্য বরাদ্ধকৃত সিএসএস প্রাকদর্শন করছেন।\nএটা এখনও সংরক্ষণ করা হয়নি!'''",
        "sitejspreview": "'''মনে রাখুন আপনি আপনার ব্যবহারকারী জাভাস্ক্রিপ্ট কেবলমাত্র প্রাকদর্শন করছেন।'''\n'''এটা এখনও সংরক্ষণ করা হয়নি!'''",
-       "userinvalidcssjstitle": "'''সতর্কীকরণ:''' \"$1\" নামে কোন আবরণ নেই। মনে রাখবেন, পছন্দমাফিক .css এবং .js পাতাগুলি ছোট হাতের শিরোনাম ব্যবহার করে, যেমন {{ns:user}}:Foo/vector.css; কিন্তু এরকম শিরোনাম নয়: {{ns:user}}:Foo/Vector.css",
+       "userinvalidconfigtitle": "'''সতর্কীকরণ:''' \"$1\" নামে কোন আবরণ নেই। মনে রাখবেন, পছন্দমাফিক .css এবং .js পাতাগুলি ছোট হাতের শিরোনাম ব্যবহার করে, যেমন {{ns:user}}:Foo/vector.css; কিন্তু এরকম শিরোনাম নয়: {{ns:user}}:Foo/Vector.css",
        "updated": "(হালনাগাদ)",
        "note": "<strong>টীকা:</strong>",
        "previewnote": "'''খেয়াল করুন, এটি একটি প্রাকদর্শন মাত্র।'''\nআপনার পরিবর্তন এখনও সংরক্ষণ করা হয়নি!",
        "prefs-files": "ফাইল",
        "prefs-custom-css": "স্বনির্ধারিত CSS",
        "prefs-custom-js": "স্বনির্ধারিত JS",
-       "prefs-common-css-js": "সকল ক্ষেত্রের জন্য সিএসএস/জাভাস্ক্রিপ্ট",
+       "prefs-common-config": "সকল ক্ষেত্রের জন্য সিএসএস/জাভাস্ক্রিপ্ট",
        "prefs-reset-intro": "আপনি এই পাতা ব্যবহার করে আপনার পছন্দসমূহকে সাইটের পূর্বপ্রদত্ত সেটিংসে পরিবর্তন করতে পারেন।\nপরিবর্তন করার পর এটা  আর ফিরিয়ে আনা যাবে না।",
        "prefs-emailconfirm-label": "ই-মেইল নিশ্চিতকরণ:",
        "youremail": "ইমেইল *",
index bee4f0d..476c3a2 100644 (file)
        "userjsyoucanpreview": "'''টিপ:''' 'আগচা' গুথামগত যাতিয়া তর জাভাস্ক্রিপ্ট অতা চুমিসেতানা কিতা হবা করে চা।",
        "usercsspreview": "'''তি মনে থইস এহান তর সিএসএসর আগচাহান।'''\n'''এহান এপাগাউ ইতু নাইসে!'''",
        "userjspreview": "'''তি মনে থইস এহান তর জাভাস্ক্রিপ্টর পরীক্ষা/আগচাহান।'''\n'''এহান এপাগাউ ইতু নাইসে!'''",
-       "userinvalidcssjstitle": "'''সিঙুইস:''' \"$1\" নাঙর কোন সর নেই।\nমনে থইস .css বারো .js পাতার নাঙ এতা রূহিবৃত্তির মাতুঙে হুরকা আতর ইকার মেয়েকল অরতাহে, যেসাদে {{ns:user}}:Foo/vector.css; কিন্তু এসাদে চিঙনাঙ নাইব: {{ns:user}}:Foo/Vector.css",
+       "userinvalidconfigtitle": "'''সিঙুইস:''' \"$1\" নাঙর কোন সর নেই।\nমনে থইস .css বারো .js পাতার নাঙ এতা রূহিবৃত্তির মাতুঙে হুরকা আতর ইকার মেয়েকল অরতাহে, যেসাদে {{ns:user}}:Foo/vector.css; কিন্তু এসাদে চিঙনাঙ নাইব: {{ns:user}}:Foo/Vector.css",
        "updated": "(আপডেট)",
        "note": "'''নোট:'''",
        "previewnote": "'''খিয়াল কর, এহান হুদ্দা আগচাহান।'''\nফারাকহান এপাগাউ ইতু করানি নাইসে!",
        "prefs-files": "ফাইল",
        "prefs-custom-css": "স্বনির্ধারিত CSS",
        "prefs-custom-js": "স্বনির্ধারিত JS",
-       "prefs-common-css-js": "হাব্বি স্কিনর কা শেয়ারড CSS/JavaScript:",
+       "prefs-common-config": "হাব্বি স্কিনর কা শেয়ারড CSS/JavaScript:",
        "youremail": "ই-মেইল *:",
        "yourrealname": "আৱৈপা নাংহান *:",
        "yourlanguage": "ঠারহান:",
index 8f05d05..70594d3 100644 (file)
        "userjspreview": "'''Dalc'hit soñj emaoc'h o rakwelet pe o testiñ ho kod javascript deoc'h ha n'eo ket bet enrollet c'hoazh!'''",
        "sitecsspreview": "'''Dalc'hit soñj n'emaoc'h ken nemet o rakwelet ar follenn CSS-mañ.'''\n'''N'eo ket bet enrollet evit c'hoazh!'''",
        "sitejspreview": "'''Dalc'hit soñj n'emaoc'h ken nemet o rakwelet ar c'hod JavaScript-mañ.'''\n'''N'eo ket bet enrollet evit c'hoazh!'''",
-       "userinvalidcssjstitle": "'''Diwallit:''' N'eus tamm gwiskadur \"$1\" ebet. Ho pez soñj e vez implijet lizherennoù bihan goude an anv implijer hag ar veskell / gant ar pajennoù personel dezho un astenn .css ha .js; da skouer eo mat ar follenn stil {{ns:user}}:Foo/vector.css ha faziek an hini {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "'''Diwallit:''' N'eus tamm gwiskadur \"$1\" ebet. Ho pez soñj e vez implijet lizherennoù bihan goude an anv implijer hag ar veskell / gant ar pajennoù personel dezho un astenn .css ha .js; da skouer eo mat ar follenn stil {{ns:user}}:Foo/vector.css ha faziek an hini {{ns:user}}:Foo/Vector.css.",
        "updated": "(Hizivaet)",
        "note": "'''Notenn :'''",
        "previewnote": "'''Diwallit mat, n'eus ken ur rakweled eus an destenn-mañ.'''\nN'eo ket bet enrollet ho kemmoù evit c'hoazh !",
        "prefs-files": "Restroù",
        "prefs-custom-css": "CSS personelaet",
        "prefs-custom-js": "JS personelaet",
-       "prefs-common-css-js": "JavaScript ha CSS kenrannet evit an holl wiskadurioù :",
+       "prefs-common-config": "JavaScript ha CSS kenrannet evit an holl wiskadurioù :",
        "prefs-reset-intro": "Ober gant ar bajenn-mañ a c'hallit evit adlakaat ho penndibaboù dre ziouer evit al lec'hienn-mañ. Kement-se n'hallo ket bezañ disc'hraet da c'houde.",
        "prefs-emailconfirm-label": "Kadarnaat ar postel :",
        "youremail": "Postel :",
index 991dc1e..a015b14 100644 (file)
        "userjspreview": "<strong>Zapamtite da je ovo samo pregled Vašeg JavaScripta.\nStranica još nije sačuvana!</strong>",
        "sitecsspreview": "'''Zapamtite ovo je samo izgled ovog CSS-a.'''\n'''Još uvijek nije sačuvan!'''",
        "sitejspreview": "'''Zapamtite ovo je samo izgled ovog koda JavaScripte.'''\n'''Još uvijek nije sačuvan!'''",
-       "userinvalidcssjstitle": "<strong>Upozorenje:</strong> Ne postoji tema \"$1\".\nNe zaboravite da imena stranica s .css i .js kodom počinju malim slovom, npr, {{ns:user}}:Foo/vector.css, a ne {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "<strong>Upozorenje:</strong> Ne postoji tema \"$1\".\nNe zaboravite da imena stranica s .css i .js kodom počinju malim slovom, npr, {{ns:user}}:Foo/vector.css, a ne {{ns:user}}:Foo/Vector.css.",
        "updated": "(Osvježeno)",
        "note": "'''Pažnja:'''",
        "previewnote": "<strong>Ne zaboravite da je ovo samo pregled.</strong>\nVaše izmjene još nisu sačuvane!",
        "prefs-files": "Datoteke",
        "prefs-custom-css": "Prilagođeni CSS",
        "prefs-custom-js": "Prilagođeni JavaScript",
-       "prefs-common-css-js": "Dijeljeni CSS/JavaScript za sve teme:",
+       "prefs-common-config": "Dijeljeni CSS/JavaScript za sve teme:",
        "prefs-reset-intro": "Možete koristiti ovu stranicu da poništite Vaše postavke na ovom sajtu na pretpostavljene vrijednosti.\nOvo se ne može vratiti unazad.",
        "prefs-emailconfirm-label": "Potvrda e-pošte:",
        "youremail": "Adresa e-pošte:",
index 13918d8..4fe01e6 100644 (file)
        "userjspreview": "'''Recordeu que només estau provant/previsualitzant el vostre JavaScript, encara no ho heu desat!'''",
        "sitecsspreview": "'''Adoneu-vos que esteu veient una vista prèvia d'aquest full d'estil CSS.'''\n'''Encara no s'ha desat!'''",
        "sitejspreview": "'''Tingueu present que esteu previsualitzant aquest codi Javascript.'''\n'''Encara no s'ha desat!'''",
-       "userinvalidcssjstitle": "'''Atenció:''' No existeix l'aparença «$1». Recordeu que les subpàgines personalitzades amb extensions .css i .js utilitzen el títol en minúscules, per exemple, {{ns:user}}:NOM/vector.css no és el mateix que {{ns:user}}:NOM/Vector.css.",
+       "userinvalidconfigtitle": "'''Atenció:''' No existeix l'aparença «$1». Recordeu que les subpàgines personalitzades amb extensions .css i .js utilitzen el títol en minúscules, per exemple, {{ns:user}}:NOM/vector.css no és el mateix que {{ns:user}}:NOM/Vector.css.",
        "updated": "(Actualitzat)",
        "note": "'''Nota:'''",
        "previewnote": "<strong>Recordeu que això és només una previsualització.</strong>\nEls vostres canvis encara no s’han desat!",
        "prefs-files": "Fitxers",
        "prefs-custom-css": "CSS personalitzat",
        "prefs-custom-js": "JS personalitzat",
-       "prefs-common-css-js": "CSS/JS compartit per tots els skins:",
+       "prefs-common-config": "CSS/JS compartit per tots els skins:",
        "prefs-reset-intro": "Podeu usar aquesta pàgina per a restablir les vostres preferències als valors per defecte.\nNo es podrà desfer el canvi.",
        "prefs-emailconfirm-label": "Confirmació de correu electrònic:",
        "youremail": "Correu electrònic:",
index 66aa5b2..bd7efa5 100644 (file)
        "prefs-files": "Файлаш",
        "prefs-custom-css": "Долахь йолу CSS",
        "prefs-custom-js": "Долахь йолу JS",
-       "prefs-common-css-js": "Юкъара CSS/JS массо кеч даран темийн:",
+       "prefs-common-config": "Юкъара CSS/JS массо кечдаран темийн:",
        "prefs-reset-intro": "ХӀара агӀо лело мега ахьа нисбина гӀирс Ӏадбитаран кепаца юха бокхуш.\nХӀара дешдерг кхочушъ динчул  тӀехьа хьан йиш хир-яц и юха меттахӀотто.",
        "prefs-emailconfirm-label": "Электронан пошт бакъ яр:",
        "youremail": "Электронан пошт:",
        "uploadnewversion-linktext": "Чуяккха керла верси хӀокху файлан",
        "shared-repo-from": "$1 чура",
        "shared-repo": "юкъара Ӏалаше меттиг",
-       "shared-repo-name-wikimediacommons": "Ð\92икигÑ\83лам",
+       "shared-repo-name-wikimediacommons": "Ð\92икилаÑ\80ма",
        "upload-disallowed-here": "Хьан бакъо яц хӀара файл юху дӀаяздан.",
        "filerevert": "Тохарлера верси юхаерзор $1",
        "filerevert-legend": "Файлан верси юхаерзо",
        "move-leave-redirect": "Ӏадйита дӀасахьажорг",
        "protectedpagemovewarning": "'''ДӀахьедар.''' ХӀара агӀо ларйина ю; цӀе хийца я нисъян а бакъо йолуш куьйгалхой бен бац.\nЛахахь тептаро балийна тӀаьхьаралера дӀаязбина хаам:",
        "semiprotectedpagemovewarning": "'''ДӀахьедо.''' ХӀара агӀо ларйина ю; дӀабазбиначу декъашхошка бе цӀе хийцалуш яц.\nЛахахьа тептаро балийна тӀаьххьаралера дӀаязбина хаам:",
-       "move-over-sharedrepo": "== Файл йолуш ю ==\nВикигулам чохь йолуш ю [[:$1]]. ХӀокху файлан цӀе хийцича Викигулам чуьраниг дӀакъовлу.",
+       "move-over-sharedrepo": "Викиларма чохь йолуш ю [[:$1]]. ХӀокху файлан цӀе хийцича Викиларма чуьраниг дӀакъовлу.",
        "export": "АгӀонаш араяхар",
        "exporttext": "Шуьга далур ду кхечу меттера чудахарш, йоза а хийцаме тептарш билгалла йолу агӀонаш я гулдина йолу агӀонаш хӀокху XML барамца, юха тӀаьхьа чура [[Special:Import|хьаэцалурдолш]] кхечу вики-хьалхен, болх беш йолу хӀокху MediaWiki гӀирсаца.\n\nКхечу меттера яззамаш чуяха, чу язъе цӀе тадечу метте, цхьа могӀан цӀе могӀаршкахь, юха харжа лаьий шуна кхечу меттигера чуяха массо яззамашна истори хийцамбарш я тӀаьххьарлера яззаман верси.\n\nШуьга кхи далундерг, лелаеш йолу адресан хьажорг кхечу меттера чудаха тӀаьххьарлерачу версин яззамаш. Масала оцу яззаман [[{{MediaWiki:Mainpage}}]] хӀара хира ю хьажорг [[{{#Special:Export}}/{{MediaWiki:Mainpage}}]].",
        "exportall": "Массо агӀонаш экспорт ян",
        "diff-form-oldid": "Версин шира идентификатор (тӀехь дац)",
        "diff-form-revid": "Башхаллаш йолу версин идентификатор",
        "diff-form-submit": "Схьагайта башхаллаш",
+       "permanentlink": "Даиман йолу хьажорг",
+       "permanentlink-revid": "Нисдаран ID",
+       "permanentlink-submit": "Хьажа версега",
        "dberr-problems": "Бехк ма бил! ХӀокху сайтехь техникан халонаш хила.",
        "dberr-again": "Хьажа карла йаккха агlо массех минот йаьлча.",
        "dberr-info": "(аьтто ца хили зӀе хӀотта серверца бухара хаамашца: $1)",
index 76d1f58..63358cf 100644 (file)
        "userjspreview": "'''لەیادت بێ کە ئێستە تەنها پێشبینین\\تاقی‌کردنەوەی جاڤاسکریپتی بەکارهێنەریەکەت دەکەی.'''\n'''هێشتا پاشەکەوت نەبووه !'''",
        "sitecsspreview": "<strong>لە بیرت ببێت کە تەنھا خەریکی پێشبینینی ئەم CSSـە دەبینیت.\nھێشتا پاشەکەوەت نەکراوە!</strong>",
        "sitejspreview": "'''لە بیرت نەچێت ئەمە تەنیا پێشبینینی ئەم کۆدەی جاڤاسکریپتە.'''\n'''گۆڕانکارییەکانت ھێشتا پاشەکەوت نەکراون!'''",
-       "userinvalidcssjstitle": "'''ئاگادارکردنەوە:''' پێست نیە بۆ \"$1\".\nلەیادت بێ کە لاپەڕەکانی‌ .css و .js لە بابەت بە پیتی بچووک کەڵک وەر ئەگرن. وەک {{ns:user}}:Foo/vector.css نە وەک {{ns:user}}:Foo/Vector.css .",
+       "userinvalidconfigtitle": "'''ئاگادارکردنەوە:''' پێست نیە بۆ \"$1\".\nلەیادت بێ کە لاپەڕەکانی‌ .css و .js لە بابەت بە پیتی بچووک کەڵک وەر ئەگرن. وەک {{ns:user}}:Foo/vector.css نە وەک {{ns:user}}:Foo/Vector.css .",
        "updated": "(نوێ‌کراوە)",
        "note": "'''تێبینی:'''",
        "previewnote": "'''لە بیرت نەچێت ئەمە تەنیا پێشبینینە.'''\nگۆڕانکارییەکانت ھێشتا پاشەکەوت نەکراون!",
        "prefs-files": "پەڕگەکان",
        "prefs-custom-css": "CSSی دڵخواز",
        "prefs-custom-js": "جاڤاسکریپتی دڵخواز",
-       "prefs-common-css-js": "سی‌ئێس‌ئێس/جاڤاسکریپتی ھاوبەش بۆ گشت پێستەکان:",
+       "prefs-common-config": "سی‌ئێس‌ئێس/جاڤاسکریپتی ھاوبەش بۆ گشت پێستەکان:",
        "prefs-reset-intro": "دەتوانی لەم لاپەڕە بۆ گەڕانەوەی هەڵبژاردەکانت بۆ بنچینەیی ماڵپەر کەڵک وەرگریت.\nگەر ئەوە بکەی ئیتر گۆڕانەکەت ناگەڕێتەوە.",
        "prefs-emailconfirm-label": "پشتڕاستکردنەوەی ئیمەیل:",
        "youremail": "ئیمەیل:",
index 3eaeffe..df4f8fd 100644 (file)
        "userjsyoucanpreview": "'''Тевсие:''' Янъы JavaScript-инъизни тешкермек ичюн саифени сакъламаздан эвель «{{int:showpreview}}» дёгмесине басынъыз.",
        "usercsspreview": "'''Унутманъыз, бу тек бакъып чыкъув - къулланыджы CSS файлынъыз аля даа сакъланмады!'''",
        "userjspreview": "'''Унутманъыз, сиз шимди тек тест этесинъиз я да бакъып чыкъув коресинъиз - къулланыджы JavaScript'и шимдилик сакъланмады.'''",
-       "userinvalidcssjstitle": "'''Ихтар:''' \"$1\" адынен бир тема ёкътыр. тема-ады.css ве .js файлларынынъ адлары кичик афир иле язмакъ керек, яни {{ns:user}}:Темель/'''V'''ector.css дегиль, {{ns:user}}:Темель/'''v'''ector.css.",
+       "userinvalidconfigtitle": "'''Ихтар:''' \"$1\" адынен бир тема ёкътыр. тема-ады.css ве .js файлларынынъ адлары кичик афир иле язмакъ керек, яни {{ns:user}}:Темель/'''V'''ector.css дегиль, {{ns:user}}:Темель/'''v'''ector.css.",
        "updated": "(Янъарды)",
        "note": "'''Ихтар:'''",
        "previewnote": "'''Бу тек бакъып чыкъув, метин аля даа сакъланмагъан!'''",
index 13d8a41..7db085b 100644 (file)
        "userjsyoucanpreview": "'''Tevsiye:''' Yañı JavaScript-iñizni teşkermek içün saifeni saqlamazdan evel \"{{int:showpreview}}\" dögmesine basıñız.",
        "usercsspreview": "'''Unutmañız, bu tek baqıp çıquv - qullanıcı CSS faylıñız alâ daa saqlanmadı!'''",
        "userjspreview": "'''Unutmañız, siz şimdi tek test etesiñiz ya da baqıp çıquv köresiñiz - qullanıcı JavaScript'i şimdilik saqlanmadı.'''",
-       "userinvalidcssjstitle": "'''İhtar:''' \"$1\" adınen bir tema yoqtır. tema-adı.css ve .js fayllarınıñ adları kiçik afir ile yazmaq kerek, yani {{ns:user}}:Temel/'''V'''ector.css degil, {{ns:user}}:Temel/'''v'''ector.css.",
+       "userinvalidconfigtitle": "'''İhtar:''' \"$1\" adınen bir tema yoqtır. tema-adı.css ve .js fayllarınıñ adları kiçik afir ile yazmaq kerek, yani {{ns:user}}:Temel/'''V'''ector.css degil, {{ns:user}}:Temel/'''v'''ector.css.",
        "updated": "(Yañardı)",
        "note": "'''İhtar:'''",
        "previewnote": "'''Bu tek baqıp çıquv, metin alâ daa saqlanmağan!'''",
index d677ec2..e553d4a 100644 (file)
        "userjspreview": "<strong>Pamatujte, že testujete a prohlížíte pouze náhled svého uživatelského JavaScriptu, jelikož dosud nebyl uložen!</strong>",
        "sitecsspreview": "<strong>Pamatujte, že si prohlížíte jen náhled tohoto CSS, jelikož dosud nebylo uloženo!</strong>",
        "sitejspreview": "<strong>Pamatujte, že testujete a prohlížíte pouze náhled tohoto JavaScriptu, jelikož dosud nebyl uložen!</strong>",
-       "userinvalidcssjstitle": "<strong>Varování:</strong> Vzhled „$1“ neexistuje. Nezapomeňte, že uživatelské .css a .js soubory používají malá písmena, např. {{ns:user}}:{{BASEPAGENAME}}/vector.css, nikoli {{ns:user}}:{{BASEPAGENAME}}/Vector.css.",
+       "userinvalidconfigtitle": "<strong>Varování:</strong> Vzhled „$1“ neexistuje. Nezapomeňte, že uživatelské .css a .js soubory používají malá písmena, např. {{ns:user}}:{{BASEPAGENAME}}/vector.css, nikoli {{ns:user}}:{{BASEPAGENAME}}/Vector.css.",
        "updated": "(Změna uložena)",
        "note": "<strong>Poznámka:</strong>",
        "previewnote": "<strong>Pamatujte, že toto je pouze náhled.</strong>\nZměny dosud nebyly uloženy!",
        "prefs-files": "Soubory",
        "prefs-custom-css": "Uživatelské CSS",
        "prefs-custom-js": "Uživatelský JavaScript",
-       "prefs-common-css-js": "Sdílené CSS/JavaScript pro všechny styly:",
+       "prefs-common-config": "Sdílené CSS/JavaScript pro všechny styly:",
        "prefs-reset-intro": "Pomocí této stránky můžete všechna nastavení vrátit na implicitní hodnoty.\nTuto operaci nelze vrátit zpět.",
        "prefs-emailconfirm-label": "Ověření e-mailu:",
        "youremail": "E-mail:",
        "uploadstash-bad-path-invalid": "Cesta není platná.",
        "uploadstash-bad-path-unknown-type": "Neznámý typ „$1“.",
        "uploadstash-file-not-found-no-thumb": "Nepodařilo se získat náhled.",
+       "uploadstash-file-not-found-no-object": "Nepodařilo se vytvořit objekt lokálního souboru pro náhled.",
        "uploadstash-file-not-found-no-remote-thumb": "Načtení náhledu se nepodařilo: $1\nURL = $2",
+       "uploadstash-file-not-found-missing-content-type": "Chybí hlavička content-type.",
        "uploadstash-file-too-large": "Nelze poskytnout soubor větší než $1 bajtů.",
        "uploadstash-not-logged-in": "Není přihlášen žádný uživatel, soubory musí patřit uživatelům.",
        "uploadstash-wrong-owner": "Tento soubor ($1) nepatří aktuálnímu uživateli.",
        "thumbnail_dest_directory": "Nelze vytvořit cílový adresář",
        "thumbnail_image-type": "Nepodporovaný typ obrázku",
        "thumbnail_gd-library": "Neúplná konfigurace knihovny GD: chybí funkce $1",
+       "thumbnail_image-size-zero": "Velikost souboru je zřejmě nulová.",
        "thumbnail_image-missing": "Soubor patrně chybí: $1",
        "thumbnail_image-failure-limit": "V poslední době došlo k příliš mnoha neúspěšným pokusům (nejméně $1) o vytvoření tohoto náhledu. Zkuste to později.",
        "import": "Import stránek",
        "imported-log-entries": "{{PLURAL:$1|Naimportován 1 protokolovací záznam|Naimportovány $1 protokolovací záznamy|Naimportováno $1 protokolovacích záznamů}}.",
        "importfailed": "Import selhal: $1",
        "importunknownsource": "Neznámý typ zdroje importu",
+       "importnoprefix": "Nebyl zadán interwiki prefix",
        "importcantopen": "Nepodařilo se otevřít importní soubor",
        "importbadinterwiki": "Neplatný interwiki odkaz",
        "importsuccess": "Import skončil!",
index 38984f9..1131d85 100644 (file)
        "prefs-files": "Lopczi",
        "prefs-custom-css": "swój CSS",
        "prefs-custom-js": "swój JavaScript",
-       "prefs-common-css-js": "Wespólny CSS/JS dlô wszëtczich skórków:",
+       "prefs-common-config": "Wespólny CSS/JS dlô wszëtczich skórków:",
        "prefs-reset-intro": "Na ti starnie mòże doprowôdzëc nazôd domëslné nastôwë dlô ti starnë.\nNegò dzéjaniô ni mòżé pòzdze ju copnąc.",
        "prefs-emailconfirm-label": "Pòcwierdzenié e-mailowi adresë:",
        "youremail": "E-mail:",
index 10b4025..f4f377b 100644 (file)
        "tagline": "Oddi ar {{SITENAME}}",
        "help": "Cymorth",
        "search": "Chwilio",
+       "search-ignored-headings": " #<!-- gadewch y rhan hon fel ag y mae --> <pre>\n# Penawdau a gaiffeu hanwybyddu gan Chwilio.\n# bydd y newid yn cymryd lle cyn gynted ag y bydd y pennawd wedi cael ei fynegeio.\n# Gallwch ailfynegeio drwy wneud ''null edit''.\n# Dyma drefn y geiriau:\n#   * Mae popeth o'r lythyren \"#\" i ddiwedd y linell yn \n sylw.\nCyfeiriadau\nDolennau allanol\nGweler hefyd\n #</pre> <!-- gadewch y rhan hon fel ag y mae -->",
        "searchbutton": "Chwilier",
        "go": "Eler",
        "searcharticle": "Mynd",
        "nosuchusershort": "Does dim defnyddiwr o'r enw \"$1\". Gwiriwch eich sillafu.",
        "nouserspecified": "Mae'n rhaid nodi enw defnyddiwr.",
        "login-userblocked": "Mae'r defnyddiwr hwn wedi ei flocio. Ni ellir mewngofnodi.",
-       "wrongpassword": "Nid yw'r cyfrinair a deipiwyd yn gywir. Rhowch gynnig arall arni, os gwelwch yn dda.",
+       "wrongpassword": "Nid yw'r cyfrinair a deipiwyd yn gywir. Rhowch gynnig arall arni.",
        "wrongpasswordempty": "Roedd y cyfrinair yn wag. Rhowch gynnig arall arni.",
        "passwordtooshort": "Mae'n rhaid fod gan gyfrinair o leia $1 {{PLURAL:$1|nod}}.",
        "passwordtoolong": "Ni chaiff cyfrinair fod yn hirach na {{PLURAL:$1|1 llythyren|$1 llythyren}}.",
        "resetpass-no-info": "Ni allwch fynd at y dudalen hon yn uniongyrchol heblaw eich bod wedi mewngofnodi.",
        "resetpass-submit-loggedin": "Newidier y cyfrinair",
        "resetpass-submit-cancel": "Diddymu",
-       "resetpass-wrong-oldpass": "Mae'r cyfrinair dros dro neu gyfredol yn annilys.\nGall fod eich bod wedi llwyddo newid eich cyfrinair eisoes neu eich bod wedi gofyn am gyfrinair dros dro newydd.",
+       "resetpass-wrong-oldpass": "Mae'r cyfrinair dros dro neu gyfredol yn annilys.\nEfallai eich bod wedi newid eich cyfrinair neu wedi gofyn am gyfrinair dros dro newydd.",
        "resetpass-recycled": "Ailosodwch eich cyfrinair os gwelwch yn dda i rywbeth heblaw eich cyfrinair cyfredol.",
        "resetpass-temp-emailed": "Rydych wedi mewngofnodi gyda chod dros dro. I gwbwlhau hyn, mae'n rhaid i chi ailosod eich cyfrinair yma:",
        "resetpass-temp-password": "Cyfrinair dros dro:",
        "passwordreset-ignored": "Ailosod y cyfrinair nad ymdriniwyd â. Efallai y nid y darparwr yn osod?",
        "passwordreset-invalidemail": "Cyfeiriad e-bost annilys",
        "passwordreset-nodata": "Ni wnaethoch ddarparu ebost na chyfeiriad",
-       "changeemail": "Newid y cyfeiriad e-bost",
+       "changeemail": "Newid neu ddiddymu cyfeiriad e-bost",
        "changeemail-header": "Cwbwlhewch y ffurflen hon i newid cyfeiriad e-bost y cyfrifi. I ddileu pob cysylltiad i bob cyfeiriad ebost, gadewch e'n wag.",
        "changeemail-no-info": "Ni allwch fynd at y dudalen hon heblaw eich bod wedi mewngofnodi.",
        "changeemail-oldemail": "Y cyfeiriad e-bost presennol:",
        "anoneditwarning": "<strong>Dalier sylw</strong>: Nid ydych wedi mewngofnodi. Fe fydd eich cyfeiriad IP yn ymddangos ar hanes golygu'r dudalen hon. Gallwch ddewis cuddio'ch cyfeiriad IP drwy greu cyfrif (a mewngofnodi) cyn golygu.",
        "anonpreviewwarning": "''Nid ydych wedi mewngofnodi. Os y cadwch eich newidiadau caiff eich cyfeiriad IP ei gofnodi yn hanes golygu'r dudalen hon.''",
        "missingsummary": "'''Sylwer:''' Nid ydych wedi gosod nodyn yn y blwch 'Crynodeb'.\nOs y pwyswch eto ar 'Cadw'r dudalen' caiff y golygiad ei gadw heb nodyn.",
-       "selfredirect": "<strong>Gofal:</strong> Rydych yn ailgyfeirio'r dudalen hon ati hi ei hun!  Gwirwch yr hyn rydych yn ceisio'i wneud. Os cliciwch \"$1\" eto yna caiff y dudalen ailgyfeirio (wallus!) ei chreu beth bynnag.",
+       "selfredirect": "<strong>Gofal:</strong> Rydych yn ailgyfeirio'r ddalen hon ati hi ei hun! Efallai fod y targed yn wallus gennych, neu rydych yn golygu dalen anghywir.\n\n Os cliciwch \"$1\" eto yna caiff y ddalen ailgyfeirio (wallus!) ei chreu beth bynnag.",
        "missingcommenttext": "Rhowch eich sylwadau isod.",
        "missingcommentheader": "<strong>Nodyn atgoffa:</strong> \nNid ydych wedi cynnig unrhywbeth yn y blwch 'Pwnc:'. Os y cliciwch \"$1\" eto fe gedwir y golygiad heb bennawd.",
        "summary-preview": "Rhagolwg o'r crynodeb:",
        "userjspreview": "'''Cofiwch -- dim ond rhagolwg o'ch JavaScript yw hwn; nid yw wedi'i gadw eto!'''",
        "sitecsspreview": "'''Cofiwch - dim ond rhagolwg o'ch CSS yw hwn.'''\n'''Nid yw wedi'i gadw eto!'''",
        "sitejspreview": "'''Cofiwch - dim ond rhagolwg o'ch côd JavaScript yw hwn.'''\n'''Nid yw wedi'i rhoi ar gadw eto!'''",
-       "userinvalidcssjstitle": "'''Rhybudd:''' Nid oes gwedd o'r enw \"$1\".\nCofiwch bod y tudalennau .css a .js yn defnyddio llythrennau bach, e.e. {{ns:user}}:Foo/vector.css yn hytrach na {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "'''Rhybudd:''' Nid oes gwedd o'r enw \"$1\".\nCofiwch bod y tudalennau .css a .js yn defnyddio llythrennau bach, e.e. {{ns:user}}:Foo/vector.css yn hytrach na {{ns:user}}:Foo/Vector.css.",
        "updated": "(Diweddariad)",
        "note": "'''Dalier sylw:'''",
        "previewnote": "'''Cofiwch taw rhagolwg yw hwn.''' Nid yw eich gwaith wedi ei roi ar gadw eto!",
        "yourtext": "Eich testun",
        "storedversion": "Y golygiad diweddaraf yn y storfa",
        "editingold": "'''RHYBUDD: Rydych chi'n golygu hen ddiwygiad o'r dudalen hon. Os caiff ei chadw, bydd unrhyw newidiadau diweddarach yn cael eu colli.'''",
+       "unicode-support-fail": "Ymddengys nad yw eich porwr yn gyfarwydd ag Unicode. Mae'n angenrheidiol os ydych am olygu; oherwydd hyn, nid ydym wedi cadw eich gwaith.",
        "yourdiff": "Gwahaniaethau",
        "copyrightwarning": "Mae pob cyfraniad i {{SITENAME}} yn cael ei ryddhau o dan termau'r Drwydded Ddogfen Rhydd ($2) (gwelwch $1 am fanylion). Os nad ydych chi'n fodlon i'ch gwaith gael ei olygu heb drugaredd, neu i gopïau ymddangos ar draws y we, peidiwch a'i gyfrannu yma.<br />\nRydych chi'n cadarnhau mai chi yw awdur y cyfraniad, neu eich bod chi wedi'i gopïo o'r parth cyhoeddus (''public domain'') neu rywle rhydd tebyg. '''Nid''' yw'r mwyafrif o wefannau yn y parth cyhoeddus.\n\n'''PEIDIWCH Â CHYFRANNU GWAITH O DAN HAWLFRAINT HEB GANIATÂD!'''",
        "copyrightwarning2": "Sylwch fod pob cyfraniad i {{SITENAME}} yn cael ei ryddhau o dan termau'r Drwydded Ddogfen Rhydd (gwelwch $1 am fanylion).\nOs nad ydych chi'n fodlon i'ch gwaith gael ei olygu heb drugaredd, neu i gopïau ymddangos ar draws y we, peidiwch a'i gyfrannu yma.<br />\nRydych chi'n cadarnhau mai chi yw awdur y cyfraniad, neu eich bod chi wedi'i gopïo o'r parth cyhoeddus (''public domain'') neu rywle rhydd tebyg.<br />\n'''PEIDIWCH Â CHYFRANNU GWAITH O DAN HAWLFRAINT HEB GANIATÂD!'''",
        "readonlywarning": "<strong>Rhybudd: Mae'r gronfa ddata wedi'i chloi am gyfnod er mwyn cynnal a chadw, felly fyddwch chi ddim yn gallu cadw'ch golygiadau ar hyn o bryd.</strong>\nGallwch gopïo'r testun a'i gludo i ffeil destun er mwyn ei gadw tan yn hwyrach.\n\nCynigiodd y gweinyddwr a glodd y gronfa ddata y rheswm hwn dros ei chloi: $1",
        "protectedpagewarning": "'''RHYBUDD: Mae'r dudalen hon wedi'i diogelu. Dim ond gweinyddwyr sydd yn gallu ei golygu.'''\nDyma'r cofnod lòg diweddaraf, er gwybodaeth:",
        "semiprotectedpagewarning": "'''Sylwer:''' Mae'r dudalen hon wedi ei chloi; dim ond defnyddwyr cofrestredig a allant ei golygu.\nDyma'r cofnod lòg diweddaraf, er gwybodaeth:",
-       "cascadeprotectedwarning": "<strong>Dalier sylw:</strong> Mae'r dudalen hon wedi ei chloi fel mai dim ond defnyddwyr â galluoedd 'Gweinyddwyr' all 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 diogelu|eu diogelu}}.",
+       "cascadeprotectedwarning": "<strong>Gofal:</strong> Mae'r ddalen hon wedi ei chloi fel mai dim ond defnyddwyr â [[Special:ListGroupRights|hawliau arbennig]] all olygu, gan fod y ddalen yn cael ei dynnu i fewn i'r dalennau canlynol:",
        "titleprotectedwarning": "'''RHYBUDD:  Mae'r dudalen hon wedi ei chloi; dim ond rhai defnyddwyr sydd â'r [[Special:ListGroupRights|gallu]] i'w chreu.'''\nDyma'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:",
        "templatesusedpreview": "Defnyddir y {{PLURAL:$1|nodyn hwn|nodyn hwn|nodiadau hyn|nodiadau hyn|nodiadau hyn|nodiadau hyn}} yn y rhagolwg hwn:",
        "permissionserrorstext-withaction": "Nid yw'r gallu hwn ($2) ganddoch, am y {{PLURAL:$1|rheswm|rheswm|rhesymau|rhesymau|rhesymau|rhesymau}} canlynol:",
        "recreate-moveddeleted-warn": "'''Dalier sylw: Rydych yn ail-greu tudalen a ddilewyd rhywdro.'''\n\nYstyriwch a fyddai'n dda o beth i barhau i olygu'r dudalen hon.\nDyma'r logiau dileu a symud ar gyfer y dudalen, er gwybodaeth:",
        "moveddeleted-notice": "Dilëwyd y ddalen hon.\nDangosir y logiau dileu, cloi a symud ar gyfer y ddalen isod.",
-       "moveddeleted-notice-recent": "Ymddiheurwn! Dilewyd y ddalen hon yn ddiweddar (yn y 24 awr diwethaf).\nEr gwybodaeth, darperir isod yr holl wybodaeth berthnasol.",
+       "moveddeleted-notice-recent": "Ymddiheurwn! Dilewyd y ddalen hon yn ddiweddar (yn y 24 awr diwethaf).\nDarperir isod yr holl wybodaeth berthnasol.",
        "log-fulllog": "Gweld y lòg cyflawn",
        "edit-hook-aborted": "Terfynwyd y golygiad cyn pryd gan fachyn.\nNi roddodd eglurhad.",
        "edit-gone-missing": "Ni ellid diweddaru'r dudalen.\nYmddengys iddi gael ei dileu.",
        "postedit-confirmation-created": "Crewyd y dudalen.",
        "postedit-confirmation-restored": "Adferwyd y dudalen.",
        "postedit-confirmation-saved": "Rhoddwyd eich golygiad ar gadw.",
+       "postedit-confirmation-published": "Cyhoeddwy eich gwaith.",
        "edit-already-exists": "Ni ellid creu tudalen newydd.\nMae ar gael yn barod.",
        "defaultmessagetext": "Y testun rhagosodedig",
        "content-failed-to-parse": "Ni lwyddwyd i ddosrannu'r cynnwys sydd ar ffurf $2 yn ôl y model $1: $3",
        "recentchangesdays-max": "(hyd at $1 {{PLURAL:$1||diwrnod|ddiwrnod|diwrnod|diwrnod|diwrnod}})",
        "recentchangescount": "Nifer y golygiadau i'w dangos yn ddiofyn:",
        "prefs-help-recentchangescount": "Mae hwn yn cynnwys newidiadau diweddar, hanesion tudalennau, a logiau.",
-       "prefs-help-watchlist-token2": "Dyma'r tocyn cudd i borthiant gwe eich rhestr wylio.\nBydd unrhyw un sy'n gwybod hwn yn gallu darllen eich rhestr wylio, felly peidiwch a'i roi i neb.\n[[Special:ResetTokens|Cliciwch fan hyn os oes angen ailosod y tocyn]].",
+       "prefs-help-watchlist-token2": "Dyma'r tocyn cudd i borthiant gwe eich rhestr wylio.\nBydd unrhyw un sy'n ei wybod yn gallu darllen eich rhestr wylio, felly peidiwch a'i roi i neb.\n\nGallwch ei [[Special:ResetTokens|ailosod]].",
        "savedprefs": "Mae eich dewisiadau wedi cael eu cadw.",
        "savedrights": "Nid yw hawliau {{GENDER:$1|$1}} wedi'u harbed.",
        "timezonelegend": "Ardal amser:",
        "timezoneregion-europe": "Ewrop",
        "timezoneregion-indian": "Cefnfor yr India",
        "timezoneregion-pacific": "Y Môr Tawel",
-       "allowemail": "Galluogi e-bost oddi wrth ddefnyddwyr eraill",
+       "allowemail": "Caniatau e-bost oddi wrth defnyddwyr eraill",
+       "email-allow-new-users-label": "Caniatau e-bost oddi wrth defnyddwyr newydd",
+       "email-blacklist-label": "Atal y defnyddwyr canlynol rhag danfon ebost ataf:",
        "prefs-searchoptions": "Chwilio",
        "prefs-namespaces": "Parthau",
        "default": "rhagosodyn",
        "prefs-files": "Ffeiliau",
        "prefs-custom-css": "CSS o hunan-ddewis",
        "prefs-custom-js": "JS o hunan-ddewis",
-       "prefs-common-css-js": "CSS/JS ar y cyd ar gyfer pob gwedd:",
+       "prefs-common-config": "CSS/JS ar y cyd ar gyfer pob gwedd:",
        "prefs-reset-intro": "Gallwch ddefnyddio'r dudalen hon i ailosod eich dewisiadau i'r rhai diofyn.\nNi allwch ddadwneud y weithred hon.",
        "prefs-emailconfirm-label": "Cadarnhau'r e-bost:",
        "youremail": "Eich cyfeiriad e-bost",
        "rcfilters-group-results-by-page": "Canlyniadau'r grwp bob yn ddalen",
        "rcfilters-activefilters": "Hidlau sydd ar waith",
        "rcfilters-advancedfilters": "Ffiltrau ychwanegol",
-       "rcfilters-limit-title": "Newidiadau a ddangosir",
+       "rcfilters-limit-title": "Canlyniadau a ddangosir",
+       "rcfilters-date-popup-title": "Cyfnod (i'w chwilio)",
        "rcfilters-days-title": "Dyddiau diweddar",
        "rcfilters-hours-title": "Oriau diweddar",
        "rcfilters-days-show-days": "$1 {{PLURAL:$1|diwrnod}}",
        "rcfilters-days-show-hours": "$1 {{PLURAL:$1|awr}}",
+       "rcfilters-highlighted-filters-list": "Amlygwyd: $1",
        "rcfilters-quickfilters": "Ffiltrau a arbedwyd",
        "rcfilters-quickfilters-placeholder-title": "Dim dolenni wedi'u cadw hyd yma",
        "rcfilters-savedqueries-defaultlabel": "Ffiltrau a arbedwyd",
        "rcfilters-liveupdates-button": "Diweddariadau, byw (''Live updates'')",
        "rcfilters-liveupdates-button-title-on": "Diffod y 'Diweddariadau, byw'",
        "rcfilters-liveupdates-button-title-off": "Arddangos newidiadau wrth iddynt ddigwydd",
+       "rcfilters-watchlist-edit-watchlist-button": "Golygwch eich Rhestr Wylio",
+       "rcfilters-preference-label": "Cuddio'r fersiwn ddiweddaraf o Newidiadau Diweddaraf",
        "rcnotefrom": "Isod rhestrir pob newid er <strong>$3, $4</strong> (ymddengys <strong>$1</strong> ohonynt).",
        "rclistfromreset": "Ailosod dyddiad yr hyn rydych wedi'i ddewis",
        "rclistfrom": "Dangos newidiadau newydd, gan ddechrau ers $3 $2",
        "recentchangeslinked-feed": "Newidiadau perthnasol",
        "recentchangeslinked-toolbox": "Newidiadau perthnasol",
        "recentchangeslinked-title": "Newidiadau cysylltiedig â \"$1\"",
-       "recentchangeslinked-summary": "Mae'r dudalen arbennig hon yn dangos y newidiadau diweddaraf i'r tudalennau hynny y mae cyswllt yn arwain atynt ar y dudalen a enwir (neu newidiadau i dudalennau sy'n aelodau o'r categori a enwir). Dangosir tudalennau sydd ar [[Special:Watchlist|eich rhestr wylio]] mewn print '''trwm'''.",
+       "recentchangeslinked-summary": "Rhowch enw dalen i weld newidiadau ar ddalenau sy'n cyfeirio neu'n dolennu iddi. I weld aelodau o gategori, nodwch Categori:Enw'r categori. Bydd newidiadau i unrhyw ddalen sydd o fewn eich [[Special:Watchlist|Rhestr Wylio]] mewn ffont <strong>trwm</strong>.",
        "recentchangeslinked-page": "Tudalen:",
        "recentchangeslinked-to": "Dangos newidiadau i'r tudalennau â chyswllt arnynt sy'n arwain at y dudalen a enwir",
        "recentchanges-page-added-to-category": "Ychwanegwyd [[:$1]] at y categori",
        "lockmanager-fail-closelock": "Wedi methu cau'r ffeil cloi mynediad at \"$1\".",
        "lockmanager-fail-deletelock": "Wedi methu dileu'r ffeil cloi mynediad at \"$1\".",
        "lockmanager-fail-acquirelock": "Wedi methu cael clo ar \"$1\".",
-       "lockmanager-fail-openlock": "Wedi methu agor y ffeil cloi mynediad at \"$1\".",
+       "lockmanager-fail-openlock": "Methwyd agor \"$1\". Ydy eich cyfeiriadur uchlwytho (''upload directory'') yn gywir ac y gellwch sgwennu i'r cyfeiriadur. Gweler:  https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:$wgUploadDirectory am ragor o wybodaeth.",
        "lockmanager-fail-releaselock": "Wedi methu agor y clo ar \"$1\".",
        "lockmanager-fail-db-bucket": "Methwyd cysylltu â digon o gronfeydd data cloi yn y bwced $1.",
        "lockmanager-fail-db-release": "Wedi methu agor y cloion ar y gronfa ddata $1.",
        "uploadstash-errclear": "Ni lwyddwyd i glirio'r ffeiliau.",
        "uploadstash-refresh": "Adnewyddu rhestr y ffeiliau",
        "uploadstash-thumbnail": "gweld y ciplun",
+       "uploadstash-bad-path": "'Dyw'r llwybr ddim yn bodoli.",
+       "uploadstash-bad-path-invalid": "'Dyw'r llwybr ddim yn gywir.",
        "invalid-chunk-offset": "Atred annilys i'r talpiau",
        "img-auth-accessdenied": "Ni chaniatawyd mynediad",
        "img-auth-nopathinfo": "PATH_INFO yn eisiau.\nNid yw'ch gweinydd wedi ei osod i fedru pasio'r wybodaeth hon.\nEfallai ei fod wedi ei seilio ar CGI, ac heb fod yn gallu cynnal img_auth.\nGweler https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization.",
        "unwatchthispage": "Stopio gwylio",
        "notanarticle": "Ddim yn erthygl/ffeil",
        "notvisiblerev": "Y diwygiad wedi cael ei ddileu",
-       "watchlist-details": "{{PLURAL:$1|Nid oes dim tudalennau|Mae $1 dudalen|Mae $1 dudalen|Mae $1 tudalen|Mae $1 thudalen|Mae $1 o dudalennau}} ar eich rhestr wylio, heb gynnwys tudalennau sgwrs ar wahan.",
+       "watchlist-details": "{{PLURAL:$1|Nid oes dim tudalennau|Mae $1 dudalen|Mae $1 dudalen|Mae $1 tudalen|Mae $1 thudalen|Mae $1 o dudalennau}} ar eich rhestr wylio, heb gynnwys tudalennau sgwrs ar wahan.\n\nCeir {{PLURAL:$1|$1 dalen|dalen}} yn eich Rhestr Wylio.",
        "wlheader-enotif": "Galluogwyd hysbysiadau trwy e-bost.",
        "wlheader-showupdated": "Mae tudalennau sydd wedi newid ers i chi eu gweld ddiwethaf wedi'u '''hamlygu'''.",
        "wlnote": "Isod, {{PLURAL:$1|yw'r golygiad diweddaraf |yw'r golygiadau diweddaraf <strong>$1</strong> changes}} yn y {{PLURAL:$2|hour|<strong>$2</strong> awr}}, fel ag y mae ar $3, $4.",
        "sp-contributions-newbies-sub": "Ar gyfer cyfrifon newydd",
        "sp-contributions-newbies-title": "Cyfraniadau defnyddwyr ar gyfer cyfrifon newydd",
        "sp-contributions-blocklog": "lòg blocio",
-       "sp-contributions-suppresslog": "atal cyfraniadau'r defnyddiwr",
-       "sp-contributions-deleted": "cyfraniadau defnyddiwr dileedig",
+       "sp-contributions-suppresslog": "atal cyfraniadau'r {{GENDER:$1|defnyddiwr}}",
+       "sp-contributions-deleted": "cyfraniadau a ddilewyd gan y {{GENDER:$1|defnyddiwr}}",
        "sp-contributions-uploads": "uwchlwythiadau",
        "sp-contributions-logs": "logiau",
        "sp-contributions-talk": "sgwrs",
-       "sp-contributions-userrights": "rheoli galluoedd defnyddwyr",
+       "sp-contributions-userrights": "rheoli galluoedd {{GENDER:$1|defnyddwyr}}",
        "sp-contributions-blocked-notice": "Mae'r defnyddiwr hwn wedi'i rwystro ar hyn o bryd. \nMae'r cofnod diweddaraf yn y lòg blocio i'w weld isod:",
        "sp-contributions-blocked-notice-anon": "Mae'r cyfeiriad IP hwn wedi'i rwystro ar hyn o bryd.\nMae'r cofnod diweddaraf yn y lòg blocio i'w weld isod:",
        "sp-contributions-search": "Chwilio am gyfraniadau",
        "sp-contributions-username": "Cyfeiriad IP neu enw defnyddiwr:",
        "sp-contributions-toponly": "Dangos golygiadau sy'n olygiadau diweddaraf yn unig",
        "sp-contributions-newonly": "Dangos y golygiadau hynny sy'n dechrau tudalen yn unig",
+       "sp-contributions-hideminor": "Cuddio golygiadau bach",
        "sp-contributions-submit": "Chwilier",
        "whatlinkshere": "Beth sy'n cysylltu yma",
        "whatlinkshere-title": "Tudalennau sy'n cysylltu â \"$1\"",
        "whatlinkshere-hidelinks": "$1 dolennau",
        "whatlinkshere-hideimages": "$1 cysylltau ffeiliau",
        "whatlinkshere-filters": "Hidlau",
+       "whatlinkshere-submit": "Ewch!",
        "autoblockid": "Awtoflocio #$1",
        "block": "Rhwystro defnyddiwr",
        "unblock": "Dad-rwystro defnyddiwr",
        "blockip": "Rhwystro'r {{GENDER:$1|defnyddiwr}}",
-       "blockiptext": "Defnyddiwch y ffurflen hon i rwystro cyfeiriad IP neu ddefnyddiwr rhag ysgrifennu i'r gronfa ddata. \nDylech chi ddim ond gwneud hyn er mwyn rhwystro fandaliaeth, a chan ddilyn [[{{MediaWiki:Policy-url}}|polisi'r wici]]. \nRhowch reswm dros rwystro'r defnyddiwr (er enghraifft, dywedwch pa dudalen(au) a fandaleiddiwyd).",
+       "blockiptext": "Defnyddiwch y ffurflen hon i rwystro cyfeiriad IP neu ddefnyddiwr rhag ysgrifennu i'r gronfa ddata. \nDylech chi ddim ond gwneud hyn er mwyn rhwystro fandaliaeth, a chan ddilyn [[{{MediaWiki:Policy-url}}|polisi'r wici]]. \nRhowch reswm dros rwystro'r defnyddiwr (er enghraifft, dywedwch pa ddalen(au) a fandaleiddiwyd).",
        "ipaddressorusername": "Cyfeiriad IP neu enw defnyddiwr:",
        "ipbexpiry": "Am gyfnod:",
        "ipbreason": "Rheswm:",
        "tags-activate": "rhoi ar waith",
        "tags-deactivate": "ei atal",
        "tags-hitcount": "$1 {{PLURAL:$1|newid}}",
-       "tags-manage-blocked": "'Sdim modd rheoli newid tagiau ar ôl cael eich blocio.",
+       "tags-manage-blocked": "'Sdim modd rheoli newid tagiau ar ôl cael {{GENDER:$1|eich}} blocio.",
        "tags-create-heading": "Creu tag newydd",
        "tags-create-explanation": "Yn ddiofyn, bydd y tagiau newydd i'w gweld gan ddefnyddwyr a botiau.",
        "tags-create-tag-name": "Enw'r tag:",
        "compare-invalid-title": "Ysgrifennwyd teitl annilys.",
        "compare-title-not-exists": "Nid yw'r teitl a enwyd ar gael.",
        "compare-revision-not-exists": "Nid yw'r diwygiad a enwyd ar gael.",
-       "diff-form": "'''ffurflen'''",
+       "diff-form": "Gwahaniaethau",
        "dberr-problems": "Mae'n ddrwg gennym! Mae'r wefan hon yn dioddef anawsterau technegol.",
        "dberr-again": "Oedwch am ychydig funudau cyn ceisio ail-lwytho.",
        "dberr-info": "(Ni ellir cysylltu â chronfa ddata: $1)",
        "logentry-newusers-create2": "Dechreuwyd y cyfrif defnyddiwr $3 gan $1",
        "logentry-newusers-byemail": "{{GENDER:$2|Dechreuodd}} $1 y cyfrif defnyddiwr $3 ac anfonodd gyfrinair drwy e-bost",
        "logentry-newusers-autocreate": "{{GENDER:$2|Crëwyd}} y cyfrif $1 yn awtomatig",
-       "logentry-rights-rights": "{{GENDER:$2|Newidiodd}} $1 y grwpiau y mae $3 yn aelod ohonynt o $4 i $5",
+       "logentry-rights-rights": "{{GENDER:$2|Newidiodd}} $1 y grwpiau {{GENDER:$6|$3}} o $4 i $5",
        "logentry-rights-rights-legacy": "{{GENDER:$2|Newidiodd}} $1 y grwpiau y mae $3 yn aelod ohonynt",
        "logentry-rights-autopromote": "{{GENDER:$2|Dyrchafwyd}} $1 yn awtomatig o $4 i $5",
        "logentry-upload-upload": "Mae $1 {{GENDER:$2|wedi uwchlwytho}} $3",
        "pagelang-use-default": "Defnyddier yr iaith arferol",
        "pagelang-select-lang": "Dewis iaith",
        "pagelang-submit": "Ei wneud",
+       "pagelang-nonexistent-page": "Nid yw'r ddalen $1 yn bodoli.",
        "right-pagelang": "Newidiwch iaith y dudalen",
        "action-pagelang": "newidiwch iaith y dudalen",
-       "log-name-pagelang": "Newidiwch iaith y log",
+       "log-name-pagelang": "Log newidiadau iaith",
        "log-description-pagelang": "Dyma log o newidiadau yn nhudalen yr ieithoedd",
-       "logentry-pagelang-pagelang": "Newidiodd $1 {{GENDER:$2}} iaith ydudalen am $3 o $4 i $5.",
+       "logentry-pagelang-pagelang": "Newidiodd $1 {{GENDER:$2}} iaith $3 o $4 i $5.",
        "default-skin-not-found-row-enabled": "* <code>$1</code> / $2 (galluogi)",
        "default-skin-not-found-row-disabled": "* <code>$1</code> / $2 (<strong>diffoddwyd</strong>)",
        "mediastatistics": "Ystadegau cyfryngau",
index e065bb9..f30cdab 100644 (file)
        "userjspreview": "'''Husk at du kun tester/forhåndsviser dit eget javascript, det er ikke gemt endnu!'''",
        "sitecsspreview": "'''Husk, at dette kun er en forhåndsvisning af denne CSS.'''\n'''Det er endnu ikke gemt!'''",
        "sitejspreview": "'''Husk, at du kun ser en forhåndsvisning af denne JavaScriptkode.'''\n'''Det er endnu ikke gemt!'''",
-       "userinvalidcssjstitle": "'''Advarsel:''' Der findes intet skin „$1“. Tænk på, at brugerspecifikke .css- og .js-sider begynder med små bogstaver, altså f.eks. ''{{ns:user}}:Hansen/vector.css'' og ikke ''{{ns:user}}:Hansen/Vector.css''.",
+       "userinvalidconfigtitle": "'''Advarsel:''' Der findes intet skin „$1“. Tænk på, at brugerspecifikke .css- og .js-sider begynder med små bogstaver, altså f.eks. ''{{ns:user}}:Hansen/vector.css'' og ikke ''{{ns:user}}:Hansen/Vector.css''.",
        "updated": "(Opdateret)",
        "note": "'''Bemærk:'''",
        "previewnote": "'''Husk at dette er kun en forhåndsvisning.'''\nDine ændringer er endnu ikke blevet gemt!",
        "prefs-files": "Filer",
        "prefs-custom-css": "Personlig CSS",
        "prefs-custom-js": "Personlig JavaScript",
-       "prefs-common-css-js": "Fælles CSS/JS for alle udseender:",
+       "prefs-common-config": "Fælles CSS/JS for alle udseender:",
        "prefs-reset-intro": "Du kan bruge denne side til at tilbagestille alle dine indstillinger til standardindstillingerne.\nDet kan ikke gøres om.",
        "prefs-emailconfirm-label": "Bekræftelse af e-mail:",
        "youremail": "Din e-mailadresse:",
index 642130b..98438c2 100644 (file)
        "clearyourcache": "'''Hinweis:''' Leere nach dem Speichern den Browser-Cache, um die Änderungen sehen zu können.\n* '''Firefox/Safari:''' ''Umschalttaste'' drücken und gleichzeitig ''Aktualisieren'' anklicken oder entweder ''Ctrl+F5'' oder ''Ctrl+R'' (''⌘+R'' auf dem Mac) drücken\n* '''Google Chrome:''' ''Umschalttaste+Ctrl+R'' (''⌘+Umschalttaste+R'' auf dem Mac) drücken\n* '''Internet Explorer:''' ''Ctrl+F5'' drücken oder ''Ctrl'' drücken und gleichzeitig ''Aktualisieren'' anklicken\n* '''Opera:''' ''Extras → Internetspuren löschen … → Individuelle Auswahl → Den kompletten Cache löschen''",
        "usercssyoucanpreview": "'''Tipp:''' Benutze den «{{int:showpreview}}»-Button, um dein neues CSS vor dem Speichern zu testen.",
        "userjsyoucanpreview": "'''Tipp:''' Benutze den «{{int:showpreview}}»-Button, um dein neues JavaScript vor dem Speichern zu testen.",
-       "userinvalidcssjstitle": "'''Achtung:''' Die Benutzeroberfläche «$1» existiert nicht. Bedenke, dass benutzerspezifische .css- und .js-Seiten mit einem Kleinbuchstaben anfangen müssen, also beispielsweise ''{{ns:user}}:Mustermann/vector.css'' an Stelle von ''{{ns:user}}:Mustermann/Vector.css''.",
+       "userinvalidconfigtitle": "'''Achtung:''' Die Benutzeroberfläche «$1» existiert nicht. Bedenke, dass benutzerspezifische .css- und .js-Seiten mit einem Kleinbuchstaben anfangen müssen, also beispielsweise ''{{ns:user}}:Mustermann/vector.css'' an Stelle von ''{{ns:user}}:Mustermann/Vector.css''.",
        "editing": "Bearbeiten von «$1»",
        "creating": "Erstellen von «$1»",
        "editingsection": "Bearbeiten von «$1» (Abschnitt)",
index dfe7008..f7796da 100644 (file)
        "userjspreview": "'''Beachten Sie, dass Sie nur eine Vorschau Ihres Benutzer-JavaScript betrachten.'''\n'''Es wurde noch nicht gespeichert!'''",
        "sitecsspreview": "'''Beachten Sie, dass Sie nur eine Vorschau dieses CSS betrachten.'''\n'''Es wurde noch nicht gespeichert!'''",
        "sitejspreview": "'''Beachten Sie, dass Sie nur eine Vorschau dieses JavaScript betrachten.'''\n'''Es wurde noch nicht gespeichert!'''",
-       "userinvalidcssjstitle": "'''Achtung:''' Die Benutzeroberfläche „$1“ existiert nicht. Bedenken Sie, dass benutzerspezifische .css- und .js-Seiten mit einem Kleinbuchstaben anfangen müssen, also beispielsweise ''{{ns:user}}:Mustermann/vector.css'' an Stelle von ''{{ns:user}}:Mustermann/Vector.css''.",
+       "userinvalidconfigtitle": "'''Achtung:''' Die Benutzeroberfläche „$1“ existiert nicht. Bedenken Sie, dass benutzerspezifische .css- und .js-Seiten mit einem Kleinbuchstaben anfangen müssen, also beispielsweise ''{{ns:user}}:Mustermann/vector.css'' an Stelle von ''{{ns:user}}:Mustermann/Vector.css''.",
        "previewconflict": "Diese Vorschau gibt den Inhalt des oberen Textfeldes wieder. So wird die Seite aussehen, wenn Sie jetzt speichern.",
        "session_fail_preview": "'''Ihre Bearbeitung konnte nicht gespeichert werden, da Sitzungsdaten verloren gegangen sind.\nBitte versuchen Sie es erneut, indem Sie unter der folgenden Textvorschau nochmals auf „Seite speichern“ klicken.\nSollte das Problem bestehen bleiben, [[Special:UserLogout|melden Sie sich ab]] und danach wieder an.'''",
        "session_fail_preview_html": "'''Ihre Bearbeitung konnte nicht gespeichert werden, da Sitzungsdaten verloren gegangen sind.'''\n\n''Da in {{SITENAME}} das Speichern von reinem HTML aktiviert ist, wurde die Vorschau ausgeblendet, um JavaScript-Attacken vorzubeugen.''\n\n'''Bitte versuchen Sie es erneut, indem Sie unter der folgenden Textvorschau nochmals auf „Seite speichern“ klicken.\nSollte das Problem bestehen bleiben, [[Special:UserLogout|melden Sie sich ab]] und danach wieder an.'''",
index e51edde..ef9a2e8 100644 (file)
        "userjspreview": "'''Beachte, dass du nur eine Vorschau deines Benutzer-JavaScripts betrachtest.'''\n'''Es wurde noch nicht gespeichert!'''",
        "sitecsspreview": "'''Beachte, dass du nur eine Vorschau dieses CSS betrachtest.'''\n'''Es wurde noch nicht gespeichert!'''",
        "sitejspreview": "'''Beachte, dass du nur eine Vorschau dieses JavaScript betrachtest.'''\n'''Es wurde noch nicht gespeichert!'''",
-       "userinvalidcssjstitle": "'''Achtung:''' Die Benutzeroberfläche „$1“ existiert nicht. Bedenke, dass benutzerspezifische .css- und .js-Seiten mit einem Kleinbuchstaben anfangen müssen, also beispielsweise ''{{ns:user}}:Mustermann/vector.css'' an Stelle von ''{{ns:user}}:Mustermann/Vector.css''.",
+       "userinvalidconfigtitle": "'''Achtung:''' Die Benutzeroberfläche „$1“ existiert nicht. Bedenke, dass benutzerspezifische .css- und .js-Seiten mit einem Kleinbuchstaben anfangen müssen, also beispielsweise ''{{ns:user}}:Mustermann/vector.css'' an Stelle von ''{{ns:user}}:Mustermann/Vector.css''.",
        "updated": "(Geändert)",
        "note": "'''Hinweis:'''",
        "previewnote": "'''Dies ist nur eine Vorschau.'''\nDie Seite wurde noch nicht gespeichert!",
        "prefs-files": "Dateien",
        "prefs-custom-css": "Benutzerdefiniertes CSS",
        "prefs-custom-js": "Benutzerdefiniertes JavaScript",
-       "prefs-common-css-js": "Gemeinsames CSS/JavaScript aller Benutzeroberflächen:",
+       "prefs-common-config": "Gemeinsames CSS/JavaScript aller Benutzeroberflächen:",
        "prefs-reset-intro": "Du kannst diese Seite verwenden, um die Einstellungen auf die Standards zurückzusetzen.\nDies kann nicht mehr rückgängig gemacht werden.",
        "prefs-emailconfirm-label": "Bestätigung:",
        "youremail": "E-Mail-Adresse:",
        "uploaded-setting-handler-svg": "SVG, das das Attribut „handler“ mit Remote/Daten/Skript festlegt, ist gesperrt. <code>$1=\"$2\"</code> in der hochgeladenen SVG-Datei gefunden.",
        "uploaded-remote-url-svg": "SVG, das ein beliebiges Style-Attribut mit einer Remote-URL festlegt, ist gesperrt. <code>$1=\"$2\"</code> in der hochgeladenen SVG-Datei gefunden.",
        "uploaded-image-filter-svg": "Bildfilter mit der URL <code>&lt;$1 $2=\"$3\"&gt;</code> in der hochgeladenen SVG-Datei gefunden.",
-       "uploadscriptednamespace": "Diese SVG-Datei enthält den ungültigen Namensraum „<nowiki>$1</nowiki>“.",
+       "uploadscriptednamespace": "Diese SVG-Datei enthält den unzulässigen Namensraum „<nowiki>$1</nowiki>“.",
        "uploadinvalidxml": "Das XML in der hochgeladenen Datei konnte nicht geparst werden.",
        "uploadvirus": "Diese Datei enthält einen Virus! Details: $1",
        "uploadjava": "Dies ist eine ZIP-Datei, die ein CLASS-Datei von Java enthält.\nDas Hochladen von Java-Dateien ist nicht gestattet, da sie die Umgehung von Sicherheitseinschränkungen ermöglichen könnten.",
        "thumbnail_dest_directory": "Zielverzeichnis kann nicht erstellt werden.",
        "thumbnail_image-type": "Bildtyp nicht unterstützt",
        "thumbnail_gd-library": "Unvollständige Konfiguration der GD-Bibliothek: Fehlende Funktion $1",
+       "thumbnail_image-size-zero": "Die Dateigröße des Bildes scheint null zu sein.",
        "thumbnail_image-missing": "Datei scheint fehlend zu sein: $1",
        "thumbnail_image-failure-limit": "Es wurden in letzter Zeit zu viele Versuche ($1 oder mehr) unternommen, dieses Vorschaubild zu rendern. Bitte versuche es später erneut.",
        "import": "Seiten importieren",
index de56748..131d189 100644 (file)
        "userjspreview": "'''şıma tena test keni ya ziverqayn seyr keni - karberê JavaScript'i hema qayd nebiyo.'''",
        "sitecsspreview": "'''Şımayê enewke tenya verqaytê dosya da CSS vınenê.''' \n'''Hewna qayd nêbı!'''",
        "sitejspreview": "'''Şımayê enewke tenya verqaytê kodê dosya da JavaScriptê karberi vınenê.''' \n'''hewna qayd nebı!'''",
-       "userinvalidcssjstitle": "'''Teme:''' Mewzuyê \"$1\" çıniyo.\nDosyanê be namey .css u .js'i de herfa werdiye bıgurêne, mesela herında {{ns:user}}:Foo/Vector.css'i de {{ns:user}}:Foo/vector.css bınuse.",
+       "userinvalidconfigtitle": "'''Teme:''' Mewzuyê \"$1\" çıniyo.\nDosyanê be namey .css u .js'i de herfa werdiye bıgurêne, mesela herında {{ns:user}}:Foo/Vector.css'i de {{ns:user}}:Foo/vector.css bınuse.",
        "updated": "(Rozeneya)",
        "note": "'''Not:'''",
        "previewnote": "'''Şıma bızanê ke eno yew verqayto.'''\nVurnayışê şıma hewna qeyd nêbiyê!",
        "undo-summary": "Vırnayışê $1'i [[Special:Contributions/$2|$2i]] ([[User talk:$2|Werênayış]]) peyser gırewt",
        "undo-summary-username-hidden": "Rewizyona veri $1'i hewada",
        "cantcreateaccount-text": "Hesabvıraştışê na IP adrese ('''$1''') terefê [[User:$3|$3]] kılit biyo.\n\nSebebo ke terefê $3 ra diyao ''$2''",
-       "viewpagelogs": "Qeydanê na perrer bımotne",
+       "viewpagelogs": "Qeydanê na pele bımocne",
        "nohistory": "Verorê vurnayışanê na perer çıni yo.",
        "currentrev": "Çımraviyarnayışo rocane",
        "currentrev-asof": "$1 ra tepeya çım ra viyarnayışê cı'yo peyên",
        "prefs-files": "Dosyey",
        "prefs-custom-css": "CSSê xasi",
        "prefs-custom-js": "JSê xasi",
-       "prefs-common-css-js": "CSS/JavaScript pê şablonanê peran de pay biya:",
+       "prefs-common-config": "CSS/JavaScript pê şablonanê peran de pay biya:",
        "prefs-reset-intro": "ena pele de şıma tercihanê xo şenê bıçarnê be tercihanê keyepelê ke verê coy eyar biy.\nNa game tepeya nêerziyena.",
        "prefs-emailconfirm-label": "Tesdiqiya E-posta:",
        "youremail": "E-Mail (mecbur niyo) *:",
index 2919aed..e0326de 100644 (file)
        "userjspreview": "== Pśeglěd Wašogo wužywarskego JavaScripta ==\n'''Glědaj:''' Pó składowanju musyš swójomu browseroju kazaś, aby nowu wersiju pokazał: '''Mozilla/Firefox:''' ''Strg-Shift-R'', '''Internet Explorer:''' ''Strg-F5'', '''Opera:''' ''F5'', '''Safari:''' ''Cmd-Shift-R'', '''Konqueror:''' ''F5''.",
        "sitecsspreview": "'''Źiwaj na to, až wobglědujoš se jano pśeglěd toś ten CSS.'''\n'''Njejo se hyšći składował!'''",
        "sitejspreview": "'''Źiwaj na to, až wobglědujoš se jano pśeglěd toś togo koda JavaScript.'''\n'''Njejo se hyšći składował!'''",
-       "userinvalidcssjstitle": "'''Warnowanje:''' Njeeksistěrujo šat „$1“. Pšosym mysli na to, až wužywaju .css- a .js-boki mały pismik, na pś. ''{{ns:user}}:Pśikładowa/vector.css'' město ''{{ns:user}}:Pśikładowa/Vector.css''.",
+       "userinvalidconfigtitle": "'''Warnowanje:''' Njeeksistěrujo šat „$1“. Pšosym mysli na to, až wužywaju .css- a .js-boki mały pismik, na pś. ''{{ns:user}}:Pśikładowa/vector.css'' město ''{{ns:user}}:Pśikładowa/Vector.css''.",
        "updated": "(Zaktualizěrowane)",
        "note": "'''Pokazka:'''",
        "previewnote": "'''Wobmysli, až to jo jano pśeglěd.'''\nTwóje změny hyšći njejsu składowane!",
        "prefs-files": "Dataje",
        "prefs-custom-css": "Swójski CSS",
        "prefs-custom-js": "Swójski JS",
-       "prefs-common-css-js": "Zgromadny CSS/JS za wšykne suknje:",
+       "prefs-common-config": "Zgromadny CSS/JS za wšykne suknje:",
        "prefs-reset-intro": "You can use this page to reset your preferences to the site defaults. This cannot be undone.\nMóžoš toś ten bok wužywaś, aby slědk stajił swóje nastajenja na standardne gódnoty sedła. To njedajo se anulěrowaś.",
        "prefs-emailconfirm-label": "E-mailowe wobkšuśenje:",
        "youremail": "E-mail:",
index d0ab2c9..64052ed 100644 (file)
        "userjspreview": "'''Soroho no do mongintong kono tomod diti JawaSikrip momogunonu.'''\n'''Awu po nokogompi iti!'''",
        "sitecsspreview": "'''Soroho no do mongintong kono tomod diti CSS.'''\n'''Awu po nokogompi iti!'''",
        "sitejspreview": "'''Soroho no do mongintong kono tomod diti kod JawaSikrip.'''\n'''Awu po nokogompi iti!'''",
-       "userinvalidcssjstitle": "'''Pomisunudan:''' Ingaa kulit do \"$1\".\nBobolikon pinudali .css om .js momoguno do pimato tokoro, miagal pomitanan {{ns:user}}:Foo/vector.css sobaagi do ponuli di {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "'''Pomisunudan:''' Ingaa kulit do \"$1\".\nBobolikon pinudali .css om .js momoguno do pimato tokoro, miagal pomitanan {{ns:user}}:Foo/vector.css sobaagi do ponuli di {{ns:user}}:Foo/Vector.css.",
        "updated": "(Noinwoguan)",
        "note": "'''Pasoniba:'''",
        "previewnote": "'''Soroho no do iti nopo nga kopongintangan toomod.'''\nAwu po moti nokogompi iri nopingalanannu!",
index 5d5449a..ec399fd 100644 (file)
        "blocked-notice-logextract": "यो प्रयोगकर्ता अच्याल प्रतिवन्धित छ।\nसब है पछा: प्रतिबन्ध लग प्रविष्टि सन्दर्भ खिलाइ तल्तिर दियीरैछ:",
        "sitecsspreview": "<strong>येइ CSSलाई तम पूर्वावलोकन मात्तरी अद्दाछ: भणिबर फाम अर:।\nयो आँजि सङ्ग्रह अरिया: आथिन। </strong>",
        "sitejspreview": "<strong>येइ जावास्क्रिप्ट कोडलाई तम पूर्वावलोकन मात्तरी अद्दाछ: भणिबर फाम अर:।\nयो आँजि सङ्ग्रह अरिया: आथिन। </strong>",
-       "userinvalidcssjstitle": "<strong>चेतावनी:</strong> यहाँ कोइपनि \"$1\" नामको खोल नाइथिन् ।\nप्रचलित .css तथा .js पानाहरूले निम्नपद शीर्षक प्रयोग गद्दान्, जस्तै {{ns:user}}:Foo/Vector.css को सट्टामी {{ns:user}}:Foo/vector.css",
+       "userinvalidconfigtitle": "<strong>चेतावनी:</strong> यहाँ कोइपनि \"$1\" नामको खोल नाइथिन् ।\nप्रचलित .css तथा .js पानाहरूले निम्नपद शीर्षक प्रयोग गद्दान्, जस्तै {{ns:user}}:Foo/Vector.css को सट्टामी {{ns:user}}:Foo/vector.css",
        "updated": "नौला",
        "note": "'''सूचना:'''",
        "previewnote": "<strong>फाम अर: कि यो यक पूर्वावलोकन मात्तरी हो।</strong>\nतमले अर्‍या फेरबदेली आँजि सङ्ग्रहित भया: आथिन!",
        "prefs-files": "फाइलहरू",
        "prefs-custom-css": "अनुकुलित CSS",
        "prefs-custom-js": "अनुकुल जाभास्क्रिप्ट",
-       "prefs-common-css-js": "साझा CSS/जाभा स्क्रिप्ट सबै कि लेखा:",
+       "prefs-common-config": "साझा CSS/जाभा स्क्रिप्ट सबै कि लेखा:",
        "prefs-reset-intro": "तम ये पृष्ठलाई आफनो अभिरुचीहरू साइट पूर्वावस्थामी फर्काउनत फर्काउन प्रयोग गद्दु सकन्छौ । तै पाछा ये लाई रद्द गद्दु सकन्छौ ।",
        "prefs-emailconfirm-label": "इ-मेल एकिन प्रक्रिया:",
        "youremail": "ईमेल",
        "usermaildisabledtext": "यै विकिमी तम और प्रयोगकर्तानलाई ई-मेल पठाउन नाइसक्दा",
        "watchlist": "अवलोकनसूची",
        "mywatchlist": "मेरो ध्यान सूची",
+       "watchlistfor2": "$1 $2 खिलाइ",
        "nowatchlist": "तमरो ध्यान सूचीमी कोइ लै सामाग्री नाइथिन् ।",
        "watchlistanontext": "कृपया तमरो ध्यान सूची हेद्द या सम्पादन गद्द कीलाइ लगइन गर ।",
        "addedwatchtext-short": "\"$1\" पानो तमरो ध्यान सूचीमी जोडियाको छ ।",
        "pageinfo-header-basic": "नानबड़ि जानकारी",
        "pageinfo-header-edits": "इतिहास सम्पादन",
        "pageinfo-header-restrictions": "पन्ना सुरक्षा",
+       "pageinfo-header-properties": "पन्ना गुणअन",
        "pageinfo-display-title": "धेकिन्या शीर्षक",
        "pageinfo-default-sort": "पूर्वनिर्धारित अनुक्रमण साँचो",
        "pageinfo-length": "पन्ना लम्बाइ (बाइटअन मी)",
index 4d97f4b..b11a058 100644 (file)
        "userjspreview": "'''Còsta l'é sōl 'na guardêda préma 'd salvêr al mudéfichi per pruvêr al tó JavaScript personêl. Ricôrdet che al mudéfichi în mìa incòra stêdi salvêdi!'''",
        "sitecsspreview": "'''Còsta l'é sōl 'na guardêda 'l CSS préma 'd salvêrel. Ricôrdet che al mudéfichi în mìa incòra stêdi salvêdi!'''",
        "sitejspreview": "'''Còsta l'é sōl 'na guardêda préma per pruvêr al JavaScript. Ricôrdet che al mudéfichi în mìa incòra stêdi salvêdi!'''",
-       "userinvalidcssjstitle": "'''Atensiòun:''' An gh'é nisóna skin cun al nòm \"$1\". As fà nutêr che al pàgini per i .css e .js personêl a gh'àn la préma lètra dal tétol in céch, per eşèimpi {{ns:user}}:Eşèimpi/vector.css e non {{ns:user}}:Eşèimpi/Vector.css.",
+       "userinvalidconfigtitle": "'''Atensiòun:''' An gh'é nisóna skin cun al nòm \"$1\". As fà nutêr che al pàgini per i .css e .js personêl a gh'àn la préma lètra dal tétol in céch, per eşèimpi {{ns:user}}:Eşèimpi/vector.css e non {{ns:user}}:Eşèimpi/Vector.css.",
        "updated": "(Arnuvê)",
        "note": "'''Nôta:'''",
        "previewnote": "'''Ricôrdet che còsta l'é sōl 'na guardêda préma 'd salvêr.'''\nAl tō mudéfichi în MIA incòra stêdi salvêdi.",
        "prefs-files": "File",
        "prefs-custom-css": "Adâta al CSS al tō necesitê",
        "prefs-custom-js": "Adâta al JavaScript al tō necesitê",
-       "prefs-common-css-js": "CSS/JavaScript in comûn per tóti 'l skin:",
+       "prefs-common-config": "CSS/JavaScript in comûn per tóti 'l skin:",
        "prefs-reset-intro": "Es pōl druvêr cla pàgina ché per turnêr a impustêr al preferèinsi e cambiêr còli dichiarêdi int al sît. \nL'operasiòun l'an pōl mìa èser scanşlêda.",
        "prefs-emailconfirm-label": "Cunfèirma ed la pôsta eletrônica:",
        "youremail": "E-mail:",
index 5040de9..2ebb094 100644 (file)
        "userjspreview": "'''Σας υπενθυμίζουμε ότι κάνετε απλώς έλεγχο/προεπισκόπηση του JavaScript του χρήστη -δεν το έχετε ακόμα αποθηκεύσει!'''",
        "sitecsspreview": "<strong>Θυμηθείτε ότι είναι απλώς μια προεπισκόπηση αυτού του CSS.\nΔεν έχει αποθηκευτεί ακόμα!</strong>",
        "sitejspreview": "''' Θυμηθείτε ότι κάνετε μόνο προεπισκόπηση σ'αυτόν τον κώδικα JavaScript.'' '\n'' ' Δεν τον έχετε αποθηκεύσει ακόμη!'' '",
-       "userinvalidcssjstitle": "'''Προσοχή:''' Δεν υπάρχει skin με τίτλο \"$1\". Θυμηθείτε οι προσαρμοσμένες σελίδες .css και .js χρησιμοποιούν έναν τίτλο με μικρά γράμματα, π.χ. {{ns:user}}:Foo/vector.css σε αντίθεση με το {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "'''Προσοχή:''' Δεν υπάρχει skin με τίτλο \"$1\". Θυμηθείτε οι προσαρμοσμένες σελίδες .css και .js χρησιμοποιούν έναν τίτλο με μικρά γράμματα, π.χ. {{ns:user}}:Foo/vector.css σε αντίθεση με το {{ns:user}}:Foo/Vector.css.",
        "updated": "(Ενημερώθηκε)",
        "note": "'''Προσοχή: '''",
        "previewnote": "'''Να θυμάστε ότι αυτή είναι μόνο μια προεπισκόπηση.'''\nΟι αλλαγές σας δεν έχουν ακόμη αποθηκευτεί!",
        "prefs-files": "Αρχεία",
        "prefs-custom-css": "Προκαθορισμένη CSS",
        "prefs-custom-js": "Προσαρμοσμένη JavaScript",
-       "prefs-common-css-js": "Κοινά CSS/JavaScript για όλα τα θέματα εμφάνισης:",
+       "prefs-common-config": "Κοινά CSS/JavaScript για όλα τα θέματα εμφάνισης:",
        "prefs-reset-intro": "Μπορείτε να χρησιμοποιήσετε αυτήν την σελίδα για να επαναρρυθμίσετε τις προτιμήσεις σας στις προεπιλογές του ιστότοπου. Αυτό δεν μπορεί να αναστρεφθεί.",
        "prefs-emailconfirm-label": "Επιβεβαίωση διεύθυνσης ηλεκτρονικού ταχυδρομείου:",
        "youremail": "Διεύθυνση ηλεκτρονικού ταχυδρομείου:",
index b710762..d0ee41c 100644 (file)
        "userjspreview": "<strong>Remember that you are only testing/previewing your user JavaScript.\nIt has not yet been saved!</strong>",
        "sitecsspreview": "<strong>Remember that you are only previewing this CSS.\nIt has not yet been saved!</strong>",
        "sitejspreview": "<strong>Remember that you are only previewing this JavaScript code.\nIt has not yet been saved!</strong>",
-       "userinvalidcssjstitle": "<strong>Warning:</strong> There is no skin \"$1\".\nCustom .css and .js pages use a lowercase title, e.g. {{ns:user}}:Foo/vector.css as opposed to {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "<strong>Warning:</strong> There is no skin \"$1\".\nCustom .css and .js pages use a lowercase title, e.g. {{ns:user}}:Foo/vector.css as opposed to {{ns:user}}:Foo/Vector.css.",
        "updated": "(Updated)",
        "note": "<strong>Note:</strong>",
        "previewnote": "<strong>Remember that this is only a preview.</strong>\nYour changes have not yet been saved!",
        "prefs-files": "Files",
        "prefs-custom-css": "Custom CSS",
        "prefs-custom-js": "Custom JavaScript",
-       "prefs-common-css-js": "Shared CSS/JavaScript for all skins:",
+       "prefs-common-config": "Shared CSS/JavaScript for all skins:",
        "prefs-reset-intro": "You can use this page to reset your preferences to the site defaults.\nThis cannot be undone.",
        "prefs-emailconfirm-label": "Email confirmation:",
        "youremail": "Email:",
        "thumbnail_dest_directory": "Unable to create destination directory",
        "thumbnail_image-type": "Image type not supported",
        "thumbnail_gd-library": "Incomplete GD library configuration: Missing function $1",
+       "thumbnail_image-size-zero": "Image file size seems to be zero.",
        "thumbnail_image-missing": "File seems to be missing: $1",
        "thumbnail_image-failure-limit": "There have been too many recent failed attempts ($1 or more) to render this thumbnail. Please try again later.",
        "import": "Import pages",
index 1ebc823..4605bad 100644 (file)
        "userjspreview": "'''Memoru ke vi nun nur provas kaj antaŭrigardas vian uzantan Ĝavaskripton, ĝi ne estas jam konservita'''",
        "sitecsspreview": "'''Konsciu ke vi nur antaŭrigardas tiun ĉi CSS.'''\n'''Ĝi ne jam estis savita!''",
        "sitejspreview": "'''Konsciu ke vi nur antaŭrigardas tiun ĉi Ĝavaskripta kodon''. ''Ĝi ne jam estis konservita''.",
-       "userinvalidcssjstitle": "'''Averto:''' Ne ekzistas etoso \"$1\".\nRememoru ke individuaj .css-aj kaj .js-aj paĝoj uzas minusklan titolon, ekz. {{ns:user}}:Foo/vector.css kontraŭe al {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "'''Averto:''' Ne ekzistas etoso \"$1\".\nRememoru ke individuaj .css-aj kaj .js-aj paĝoj uzas minusklan titolon, ekz. {{ns:user}}:Foo/vector.css kontraŭe al {{ns:user}}:Foo/Vector.css.",
        "updated": "(Ĝisdatigita)",
        "note": "<strong>Noto:</strong>",
        "previewnote": "'''Memoru, ke ĉi tio estas nur antaŭrigardo.''' \nViaj ŝanĝoj ne ankoraŭ estas konservitaj!",
        "prefs-files": "Dosieroj",
        "prefs-custom-css": "Propra CSS",
        "prefs-custom-js": "Propra JS",
-       "prefs-common-css-js": "Komuna CSS/JS por ĉiuj etosoj:",
+       "prefs-common-config": "Komuna CSS/JS por ĉiuj etosoj:",
        "prefs-reset-intro": "Vi povas uzi ĉi tiun paĝon por restarigi viajn agordojn al la originalaj defaŭltoj.\nĈi tiel ne estus malfarebla.",
        "prefs-emailconfirm-label": "Retpoŝta konfirmado:",
        "youremail": "Retadreso:",
index a98ddae..99d0511 100644 (file)
        "wrongpasswordempty": "No ha introducido una contraseña.\nPor favor inténtelo de nuevo.",
        "password-name-match": "Su contraseña debe ser diferente de su nombre de usuario.",
        "passwordsent": "Se ha enviado una nueva contraseña al correo electrónico de «$1».\nPor favor, identifíquese de nuevo tras recibirla.",
+       "emailconfirmlink": "Confirme su dirección de correo electrónico",
+       "invalidemailaddress": "No se puede aceptar la dirección de correo electrónico, pues parece que tiene un formato no válido.\nPor favor, escriba una dirección bien formada o deje el campo en blanco.",
        "anoneditwarning": "<strong>Advertencia:</strong> no ha iniciado sesión. Su dirección IP se hará pública si hace cualquier edición en estas condiciones. Si <strong>[$1 inicia sesión]</strong> o <strong>[$2 crea una cuenta]</strong>, sus ediciones se atribuirán a su nombre de usuario, además de otros beneficios.",
        "newarticletext": "Ha seguido usted un enlace a una página que aún no existe.\nPara crear esta página, escriba en el campo a continuación. Para más información, consulte la [$1 página de ayuda].\nSi ha llegado aquí por error, vuelva a la página anterior.",
        "noarticletext": "En este momento no hay texto en esta página.\nPuede [[Special:Search/{{PAGENAME}}|buscar el título de esta página]] en otras páginas,\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} buscar en los registros relacionados],\no [{{fullurl:{{FULLPAGENAME}}|action=edit}} crear esta página]</span>.",
index d1989ac..3c5b4ab 100644 (file)
        "userjspreview": "<strong>¡Recuerda que solo estás previsualizando tu JavaScript de usuario.\n¡Aún no se ha guardado!</strong>",
        "sitecsspreview": "<strong>Recuerda que solo estás previsualizando este CSS.\n¡Aún no se ha guardado!</strong>",
        "sitejspreview": "<strong>Recuerda que solo estás previsualizando este código JavaScript.\n¡Aún no se ha guardado!</strong>",
-       "userinvalidcssjstitle": "<strong>Advertencia:</strong> no existe la apariencia «$1».\nRecuerda que las páginas personalizadas .css y .js tienen un título en minúsculas. Por ejemplo, se usa {{ns:user}}:Ejemplo/vector.css en vez de {{ns:user}}:Ejemplo/Vector.css.",
+       "userinvalidconfigtitle": "<strong>Advertencia:</strong> no existe la apariencia «$1».\nRecuerda que las páginas personalizadas .css y .js tienen un título en minúsculas. Por ejemplo, se usa {{ns:user}}:Ejemplo/vector.css en vez de {{ns:user}}:Ejemplo/Vector.css.",
        "updated": "(Actualizado)",
        "note": "<strong>Nota:</strong>",
        "previewnote": "<strong>Recuerda que esto no es más que una previsualización.</strong>\nAún no se han guardado tus cambios.",
        "prefs-files": "Archivos",
        "prefs-custom-css": "CSS personalizado",
        "prefs-custom-js": "JavaScript personalizado",
-       "prefs-common-css-js": "CSS/JavaScript compartido para todas las apariencias:",
+       "prefs-common-config": "CSS/JavaScript compartido para todas las apariencias:",
        "prefs-reset-intro": "Puedes usar esta página para restaurar los valores predeterminados de tus preferencias.\nNo podrás deshacer esta acción.",
        "prefs-emailconfirm-label": "Confirmación de correo electrónico:",
        "youremail": "Correo electrónico:",
        "thumbnail_dest_directory": "Incapaz de crear el directorio de destino",
        "thumbnail_image-type": "Tipo de imagen no contemplado",
        "thumbnail_gd-library": "Configuración de la biblioteca GD incompleta: falta la función $1",
+       "thumbnail_image-size-zero": "El tamaño del archivo de imagen aparenta ser cero.",
        "thumbnail_image-missing": "El archivo parece no existir: $1",
        "thumbnail_image-failure-limit": "Ha habido muchos intentos recientes ($1 o más) para representar esta miniatura. Inténtalo de nuevo más tarde.",
        "import": "Importar páginas",
index 152bfc4..08ab547 100644 (file)
        "userjspreview": "'''Ära unusta, et see versioon sinu isiklikust JavaScriptist on alles salvestamata!'''",
        "sitecsspreview": "'''Pea meeles, et see on vaid selle stiililehe eelvaade.'''\n'''Seda pole veel salvestatud!'''",
        "sitejspreview": "'''Pea meeles, et see on vaid selle JavaScripti-koodi eelvaade.'''\n'''Seda pole veel salvestatud!'''",
-       "userinvalidcssjstitle": "'''Hoiatus:''' Kujundust nimega \"$1\" ei ole.\nÄra unusta, et kasutaja isiklikud .css- ja .js-lehed kasutavad väiketähega algavaid nimesid, näiteks  {{ns:user}}:Juhan Julm/vector.css ja mitte {{ns:user}}:Juhan Julm/Vector.css.",
+       "userinvalidconfigtitle": "'''Hoiatus:''' Kujundust nimega \"$1\" ei ole.\nÄra unusta, et kasutaja isiklikud .css- ja .js-lehed kasutavad väiketähega algavaid nimesid, näiteks  {{ns:user}}:Juhan Julm/vector.css ja mitte {{ns:user}}:Juhan Julm/Vector.css.",
        "updated": "(Värskendatud)",
        "note": "'''Meeldetuletus:'''",
        "previewnote": "'''Ära unusta, et see on kõigest eelvaade!'''\nSinu muudatused pole veel salvestatud!",
        "prefs-files": "Failid",
        "prefs-custom-css": "kohandatud CSS",
        "prefs-custom-js": "kohandatud JavaScript",
-       "prefs-common-css-js": "Kõigi kujunduste ühine CSS/JavaScript:",
+       "prefs-common-config": "Kõigi kujunduste ühine CSS/JavaScript:",
        "prefs-reset-intro": "Sellel leheküljel saad oma eelistused lähtestada võrgukoha vaike-eelistusteks.\nToimingut ei saa hiljem tühistada.",
        "prefs-emailconfirm-label": "E-posti kinnitus:",
        "youremail": "E-posti aadress:",
index 3100c46..e375fd9 100644 (file)
        "userjspreview": "'''Gogoratu zure JavaScript kodea probatu/aurreikusten zabiltzala, oraindik ez da gorde!'''",
        "sitecsspreview": "'''Ez ahaztu zure CSS kodea aurreikusten zabiltzala.'''\n'''Oraindik gorde gabe dago!'''",
        "sitejspreview": "'''Gogoratu zure JavaScript kodea probatu/aurreikusten zabiltzala'''\n'''Oraindik ez da gorde!'''",
-       "userinvalidcssjstitle": "'''Oharra:''' Ez da \"$1\" itxura existitzen. Kontuan izan .css eta .js fitxategi pertsonalizatuen izenak letra xehez idatzi behar direla; adibidez, {{ns:user}}:Adibide/vector.css, eta ez {{ns:user}}:Adibide/Vector.css.",
+       "userinvalidconfigtitle": "'''Oharra:''' Ez da \"$1\" itxura existitzen. Kontuan izan .css eta .js fitxategi pertsonalizatuen izenak letra xehez idatzi behar direla; adibidez, {{ns:user}}:Adibide/vector.css, eta ez {{ns:user}}:Adibide/Vector.css.",
        "updated": "(Eguneratua)",
        "note": "'''Oharra:'''",
        "previewnote": "'''Gogoratu hau aurrikuspen bat dela.'''\nZure aldaketak ez dira oraindik gorde!",
        "prefs-files": "Fitxategiak",
        "prefs-custom-css": "CSS pertsonalizatua",
        "prefs-custom-js": "JS pertsonalizatua",
-       "prefs-common-css-js": "Azal mota guztietan elkarbanatutako CSS/JS:",
+       "prefs-common-config": "Azal mota guztietan elkarbanatutako CSS/JS:",
        "prefs-reset-intro": "Orrialde hau erabil dezakezu zure guneko berezko hobespenak berreskuratzeko.\nHau ezin da desegin.",
        "prefs-emailconfirm-label": "E-posta baieztapena:",
        "youremail": "E-posta:",
index 0b1ecfa..54a380d 100644 (file)
        "userjsyoucanpreview": "'''Consehu:''' Gasta el botón 'Previsoreal' pa prebal el tu nuevu JS enantis d´emburacal.",
        "usercsspreview": "'''Alcuerdati que solu estás previsoreandu el tu CSS d'usuáriu.'''\n'''Entovia nu está emburacau!'''",
        "userjspreview": "<strong>Recuerda que solu estás prebandu/previsoreandu el tu JavaScript d’usuáriu.\nEntovia nu está emburacau!</strong>",
-       "userinvalidcssjstitle": "'''Avisu:''' Nu desisti el skin \"$1\". Alcuerdati que las páhinas presonalizás .css i .js tienin el su entítulu en menúsculas, p.s. {{ns:user}}:Foo/vector.css en lugal de {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "'''Avisu:''' Nu desisti el skin \"$1\". Alcuerdati que las páhinas presonalizás .css i .js tienin el su entítulu en menúsculas, p.s. {{ns:user}}:Foo/vector.css en lugal de {{ns:user}}:Foo/Vector.css.",
        "updated": "(Atualizau)",
        "note": "'''Nota:'''",
        "previewnote": "'''Agora solu estás previsoreandu; entovia nu están emburacaus los chambus!'''",
index 9c1f343..f5d07ce 100644 (file)
        "userjspreview": "'''به یاد داشته باشید که شما فقط دارید جاوااسکریپت کاربری‌تان را امتحان می‌کنید/پیش‌نمایش آن را می‌بینید.'''\n'''این جاوااسکریپت هنوز ذخیره نشده‌است!'''",
        "sitecsspreview": "'''به یاد داشته باشید که شما فقط دارید پیش‌نمایش این سی‌اس‌اس را می‌بینید.'''\n'''این سی‌اس‌اس هنوز ذخیره نشده‌است!'''",
        "sitejspreview": "'''به یاد داشته باشید که شما فقط دارید پیش‌نمایش این جاوااسکریپت را می‌بینید.'''\n'''این جاوااسکریپت هنوز ذخیره نشده‌است!'''",
-       "userinvalidcssjstitle": "'''هشدار:''' پوسته‌ای به نام «$1» وجود ندارد.\nبه یاد داشته باشید که صفحه‌های شخصی ‎.css و ‎.js باید عنوانی با حروف کوچک داشته باشند؛ نمونه: {{ns:user}}:فو/vector.css در مقابل {{ns:user}}:فو/Vector.css.",
+       "userinvalidconfigtitle": "'''هشدار:''' پوسته‌ای به نام «$1» وجود ندارد.\nبه یاد داشته باشید که صفحه‌های شخصی ‎.css و ‎.js باید عنوانی با حروف کوچک داشته باشند؛ نمونه: {{ns:user}}:فو/vector.css در مقابل {{ns:user}}:فو/Vector.css.",
        "updated": "(به‌روز شد)",
        "note": "'''نکته:'''",
        "previewnote": "'''به یاد داشته باشید که این فقط پیش‌نمایش است.'''\nتغییرات شما هنوز ذخیره نشده‌است!",
        "prefs-files": "پرونده‌ها",
        "prefs-custom-css": "سی‌اس‌اس شخصی",
        "prefs-custom-js": "جاوااسکریپت شخصی",
-       "prefs-common-css-js": "سی‌اس‌اس/جاوااسکریپت مشترک برای تمام پوسته‌ها:",
+       "prefs-common-config": "سی‌اس‌اس/جاوااسکریپت مشترک برای تمام پوسته‌ها:",
        "prefs-reset-intro": "شما می‌توانید از این صفحه برای بازگرداندن تنظیمات خود به پیش‌فرض تارنما استفاده کنید.\nاین کار بازگشت‌ناپذیر است.",
        "prefs-emailconfirm-label": "تأیید ایمیل:",
        "youremail": "ایمیل:",
        "rollback-success": "ویرایش‌های {{GENDER:$3|$1}} واگردانی شد؛\nصفحه به آخرین ویرایش {{GENDER:$4|$2}} برگردانده شد.",
        "rollback-success-notify": "ویرایش‌های توسط $1 واگردانی شد؛\nبه آخرین نسخه توسط $2 بازگردانی شد. [$3 نمایش تغییرات]",
        "sessionfailure-title": "خطای نشست کاربری",
-       "sessionfailure": "به نظر می‌رسد مشکلی در مورد نشست کاربری شما وجود دارد؛\nعمل درخواست شده در اقدامی پیشگیرانه در برابر ربوده‌شدن اطلاعات نشست کاربری، لغو شد.\nلطفاً دکمهٔ «بازگشت» را در مرورگر خود بفشارید و صفحه‌ای که از آن به اینجا رسیده‌اید را دوباره فراخوانی کنید، سپس مجدداً سعی کنید.",
+       "sessionfailure": "به نظر می‌رسد مشکلی در مورد نشست کاربری شما وجود دارد؛\nعمل درخواست شده در اقدامی پیشگیرانه در برابر ربوده‌شدن اطلاعات نشست کاربری، لغو شد.\nلطفاً فرم را از نو بارگذاری کنید.",
        "changecontentmodel": "ویرایش نمونه محتوای یک صفحه",
        "changecontentmodel-legend": "تغییر نوع محتوی",
        "changecontentmodel-title-label": "عنوان صفحه",
        "thumbnail_dest_directory": "اشکال در ایجاد پوشهٔ مقصد",
        "thumbnail_image-type": "تصویر از نوع پشتیبانی نشده",
        "thumbnail_gd-library": "تنظیمات ناقص کتابخانهٔ GD: عملکرد $1 وجود ندارد",
+       "thumbnail_image-size-zero": "به نظر می‌رسد حجم پرونده صفر است.",
        "thumbnail_image-missing": "پرونده به نظر گم شده‌است: $1",
        "thumbnail_image-failure-limit": "تلاش‌های ناموفق اخیر بسیاری ($1 یا بیشتر) برای ارائهٔ این تصویر کوچک وجود داشته‌ است. لطفأ بعداً دوباره تلاش کنید.",
        "import": "درون‌ریزی صفحات",
        "watchlistedit-clear-titles": "عنوان‌ها:",
        "watchlistedit-clear-submit": "پاک کردن فهرست پیگیریها (این دائم است!)",
        "watchlistedit-clear-done": "فهرست پیگیریهای شما پاک شد.",
+       "watchlistedit-clear-jobqueue": "فهرست پیگیریتان در حال خالی شدن است. این کار به کمی زمان نیاز دارد!",
        "watchlistedit-clear-removed": "$1 عنوان حذف {{PLURAL:$1|شد|شدند}}:",
        "watchlistedit-too-many": "تعداد زیادی صفحه برای نمایش در اینجا وجود دارد.",
        "watchlisttools-clear": "پاکسازی فهرست پیگیری",
index 2849ffe..b89abac 100644 (file)
        "userjspreview": "'''Tämä on JavaScriptin esikatselu.'''",
        "sitecsspreview": "'''Huomaa, että tämä on vasta CSS:n esikatselu.''' \n'''Muutoksia ei ole vielä tallennettu.'''",
        "sitejspreview": "'''Huomaa, että tämä on vasta JavaScript-koodin esikatselu.'''\n'''Muutoksia ei ole vielä tallennettu.'''",
-       "userinvalidcssjstitle": "'''Varoitus:''' Tyyliä nimeltä ”$1” ei ole olemassa. Muista, että käyttäjän määrittelemät .css- ja .js-sivut alkavat pienellä alkukirjaimella, esim. {{ns:user}}:Matti Meikäläinen/vector.css eikä {{ns:user}}:Matti Meikäläinen/Vector.css.",
+       "userinvalidconfigtitle": "'''Varoitus:''' Tyyliä nimeltä ”$1” ei ole olemassa. Muista, että käyttäjän määrittelemät .css- ja .js-sivut alkavat pienellä alkukirjaimella, esim. {{ns:user}}:Matti Meikäläinen/vector.css eikä {{ns:user}}:Matti Meikäläinen/Vector.css.",
        "updated": "(Päivitetty)",
        "note": "'''Huomautus:'''",
        "previewnote": "'''Tämä on vasta sivun esikatselu.'''\nTekemiäsi muutoksia ei ole vielä tallennettu.",
        "post-expand-template-argument-warning": "'''Varoitus:''' Tällä sivulla on ainakin yksi mallineen muuttuja, jonka sisällytetty koko on liian suuri.\nNämä muuttujat on jätetty käsittelemättä.",
        "post-expand-template-argument-category": "Käsittelemättömiä mallinemuuttujia sisältävät sivut",
        "parser-template-loop-warning": "Mallinesilmukka havaittu: [[$1]]",
-       "template-loop-category": "Sivut joilla on mallinesilmukoita",
+       "template-loop-category": "Sivut, joissa on mallinesilmukoita",
+       "template-loop-warning": "<strong>Varoitus:</strong> Tämä sivu kutsuu mallinetta [[:$1]] joka aiheuttaa mallinesilmukan (loputon rekursiivinen kutsu).",
        "parser-template-recursion-depth-warning": "Mallineen rekursioraja ylittyi ($1)",
        "language-converter-depth-warning": "Kielimuuntimen syvyysraja ylittyi ($1)",
        "node-count-exceeded-category": "Sivut, joissa solmumäärä on ylitetty",
        "diff-multi-sameuser": "({{PLURAL:$1|Yhtä välissä olevaa versiota|$1 välissä olevaa versiota}} samalta käyttäjältä ei näytetä)",
        "diff-multi-otherusers": "({{PLURAL:$1|Yhtä välissä olevaa versiota|$1 välissä olevaa versiota}} {{PLURAL:$2|toisen käyttäjän tekemänä|$2 käyttäjän tekeminä}} ei näytetä)",
        "diff-multi-manyusers": "(Versioiden välissä on {{PLURAL:$1|yksi muu muokkaus|$1 muuta muokkausta, jotka on tehnyt {{PLURAL:$2|yksi käyttäjä|yli $2 eri käyttäjää}}}}.)",
+       "diff-paragraph-moved-tonew": "Kappale siirrettiin. Klikkaa hypätäksesi uuteen sijaintiin.",
+       "diff-paragraph-moved-toold": "Kappale siirrettiin. Klikkaa hypätäksesi vanhaan sijaintiin.",
        "difference-missing-revision": "{{PLURAL:$2|Yhtä versiota|$2 versiota}} tästä vertailusta ($1) {{PLURAL:$2|ei}} löytynyt.\n\nUseimmiten tämä johtuu vanhentuneesta vertailulinkistä poistettuun sivuun.\nLisätietoja löytyy [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} poistolokista].",
        "searchresults": "Hakutulokset",
        "searchresults-title": "Haun tulokset hakusanalle ”$1”",
        "search-external": "Ulkoinen haku",
        "searchdisabled": "Tekstihaku on poistettu toistaiseksi käytöstä suuren kuorman vuoksi. Voit käyttää alla olevaa Googlen hakukenttää sivujen etsimiseen, kunnes haku tulee taas käyttöön. <small>Huomaa, että ulkopuoliset kopiot {{GRAMMAR:genitive|{{SITENAME}}}} sisällöstä eivät välttämättä ole ajan tasalla.</small>",
        "search-error": "Haku epäonnistui: $1",
+       "search-warning": "Haun aikana tapahtui varoitus: $1",
        "preferences": "Asetukset",
        "mypreferences": "Asetukset",
        "prefs-edits": "Muokkauksia",
        "prefs-files": "Tiedostot",
        "prefs-custom-css": "Käyttäjäkohtainen CSS-tyylisivu",
        "prefs-custom-js": "Käyttäjäkohtainen JavaScript-sivu",
-       "prefs-common-css-js": "Yhteiset CSS- ja JavaScript-sivut kaikille ulkoasuille",
+       "prefs-common-config": "Yhteiset CSS- ja JavaScript-sivut kaikille ulkoasuille",
        "prefs-reset-intro": "Voit käyttää tätä sivua palauttaaksesi kaikki asetuksesi sivuston oletusasetuksiin. Tätä ei voi kumota.",
        "prefs-emailconfirm-label": "Sähköpostin varmistus",
        "youremail": "Sähköpostiosoite",
        "recentchanges-noresult": "Ei muutoksia, jotka täyttävät nämä kriteerit valitun ajanjakson aikana.",
        "recentchanges-timeout": "Tämä haku aikakatkaistiin. Saatat haluta kokeilla toisia hakuehtoja.",
        "recentchanges-network": "Teknisen virheen vuoksi tuloksia ei voitu ladata. Yritä sivun päivittämistä.",
+       "recentchanges-notargetpage": "Anna yllä sivun nimi nähdäksesi sivuun liittyvät muutokset.",
        "recentchanges-feed-description": "Tällä sivulla voi seurata tuoreita {{GRAMMAR:illative|{{SITENAME}}}} tehtyjä muutoksia.",
        "recentchanges-label-newpage": "Tämä muutos loi uuden sivun",
        "recentchanges-label-minor": "Tämä on pieni muutos",
        "uploadbtn": "Tallenna tiedosto",
        "reuploaddesc": "Peruuta tallennus ja palaa tallennuslomakkeelle.",
        "upload-tryagain": "Lähetä muutettu tiedostokuvaus",
+       "upload-tryagain-nostash": "Lähetä uudelleenlähetetty tiedosto ja muokattu kuvaus",
        "uploadnologin": "Et ole kirjautunut sisään",
        "uploadnologintext": "Sinun pitää $1, jotta voit tallentaa tiedostoja.",
        "upload_directory_missing": "Tallennushakemisto $1 puuttuu, eikä palvelin pysty luomaan sitä.",
        "file-deleted-duplicate-notitle": "Tämän tiedoston kanssa samanlainen tiedosto on aikaisemmin poistettu ja tiedoston nimi on häivytetty.\nSinun on syytä pyytää jotakuta häivytettyjen tietojen näkemiseen oikeutettua käyttäjää katsomaan tiedoston tiedot asian arvioimiseksi ennen kuin jatkat tiedoston lataamista tietokantaan.",
        "uploadwarning": "Tallennusvaroitus",
        "uploadwarning-text": "Muuta alla olevaa tiedostokuvausta ja yritä uudelleen.",
+       "uploadwarning-text-nostash": "Uudelleenlähetä tiedosto, muokkaa kuvausta alla ja yritä uudelleen.",
        "savefile": "Tallenna",
        "uploaddisabled": "Tiedostojen tallennus ei ole käytössä.",
        "copyuploaddisabled": "Tallennus URL:n kautta on poistettu käytöstä.",
        "php-uploaddisabledtext": "PHP:n tiedostojen lähetys ei ole käytössä. Tarkista asetukset kohdasta file_uploads.",
        "uploadscripted": "Tämä tiedosto sisältää HTML-koodia tai skriptejä, jotka selain saattaa virheellisesti suorittaa.",
        "upload-scripted-pi-callback": "Ei voida tallentaa tiedostoa, joka sisältää XML-tyylimääritteen käsittelyohjeen.",
+       "upload-scripted-dtd": "Ei voida lähettää SVG-tiedostoja, jotka sisältävät ei-standardin DTD-selityksen.",
        "uploaded-script-svg": "Found scriptable element \"$1\" in the uploaded SVG file.",
        "uploaded-hostile-svg": "Tallennetun SVG-tiedoston tyylielementissä löytyi turvaton CSS.",
        "uploaded-event-handler-on-svg": "Setting event-handler attributes <code>$1=\"$2\"</code> is not allowed in SVG files.",
        "uploadstash-not-logged-in": "Käyttäjää ei ole kirjautunut sisään, tiedostojen on kuuluttava käyttäjille.",
        "uploadstash-no-such-key": "Ei tälläistä avainta ($1), ei voi poistaa.",
        "uploadstash-no-extension": "Laajennus on tyhjä.",
+       "uploadstash-zero-length": "Tiedoston pituus on nolla.",
        "invalid-chunk-offset": "Kelpaamaton siirtymä lohkoissa",
        "img-auth-accessdenied": "Pääsy estetty",
        "img-auth-nopathinfo": "PATH_INFO puuttuu.\nPalvelintasi ei ole asetettu välittämään tätä tietoa.\nSe saattaa olla CGI-pohjainen eikä voi tukea img_authia.\nLisätietoja löytyy sivulta https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization.",
        "pageswithprop-legend": "Sivut sivun ominaisuuden mukaan",
        "pageswithprop-text": "Tällä sivulla on lueteltu sivut, jotka käyttävät erityistä sivun ominaisuutta.",
        "pageswithprop-prop": "Ominaisuuden nimi",
+       "pageswithprop-reverse": "Lajittele päinvastaisessa järjestyksessä",
+       "pageswithprop-sortbyvalue": "Lajittele ominaisuuden arvon mukaan",
        "pageswithprop-submit": "Siirry",
        "pageswithprop-prophidden-long": "Pitkä tekstimuotoinen ominaisuuden arvo piilotettu ($1)",
        "pageswithprop-prophidden-binary": "ominaisuuden binääriarvo on piilotettu ($1)",
        "doubleredirects": "Kaksinkertaiset ohjaukset",
        "doubleredirectstext": "Tällä sivulla on lueteltu ne sivut, jotka ohjaavat toiseen ohjaussivuun.\nJokaisella rivillä on linkit ensimmäiseen ja toiseen ohjaukseen sekä toisen ohjauksen kohteeseen, joka on yleensä ”oikea” kohdesivu, johon ensimmäisen ohjauksen pitäisi johtaa.\n<del>Yliviivatut</del> kohteet on korjattu.",
        "double-redirect-fixed-move": "[[$1]] on siirretty.\nSe on automaattisesti päivitetty ja se ohjaa nyt sivulle [[$2]].",
-       "double-redirect-fixed-maintenance": "Korjataan automaattisesti kaksinkertainen ohjaus sivulta [[$1]] sivulle [[$2]].",
+       "double-redirect-fixed-maintenance": "Korjataan automaattisesti kaksinkertainen ohjaus sivulta [[$1]] sivulle [[$2]] huoltotyössä",
        "double-redirect-fixer": "Ohjausten korjaaja",
        "brokenredirects": "Virheelliset ohjaukset",
        "brokenredirectstext": "Seuraavat ohjaukset osoittavat sivuihin, joita ei ole olemassa.",
        "linksearch": "Etsi ulkoisia linkkejä",
        "linksearch-pat": "Hakuehto:",
        "linksearch-ns": "Nimiavaruus:",
-       "linksearch-ok": "Etsi",
+       "linksearch-ok": "Hae",
        "linksearch-text": "Jokerimerkkejä, kuten \"*.wikipedia.org\", voidaan käyttää.\nVaaditaan vähintään ylätason verkkotunnus, esimerkiksi \"*.org\".<br />\n{{PLURAL:$2|Tuettu protokolla|Tuetut protokollat}}: $1 (oletuksena on <code>http://</code>, jos protokollaa ei määritetä).",
        "linksearch-line": "$1 on linkitetty sivulta $2",
        "linksearch-error": "Jokerimerkkiä voi käyttää ainoastaan osoitteen alussa.",
        "sp-contributions-newonly": "Näytä vain muokkaukset, joilla on luotu sivu",
        "sp-contributions-hideminor": "Piilota pienet muutokset",
        "sp-contributions-submit": "Hae",
+       "sp-contributions-outofrange": "Tuloksia ei voi näyttää. Pyydetty IP-alue on suurempi kuin /$1 CIDR-raja.",
        "whatlinkshere": "Tänne viittaavat sivut",
        "whatlinkshere-title": "Sivut, jotka viittaavat sivulle $1",
        "whatlinkshere-page": "Sivu:",
        "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.",
+       "ip_range_exceeded": "IP-alue ylittää sen enimmäisalueen. Sallittu alue: /$1.",
        "ip_range_toolow": "IP-alueet eivät käytännöllisesti katsoen ole sallittuja.",
        "proxyblocker": "Välityspalvelinesto",
        "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.",
        "thumbnail_dest_directory": "Kohdehakemiston luominen ei onnistunut",
        "thumbnail_image-type": "Kuvamuoto ei ole tuettu",
        "thumbnail_gd-library": "GD-kirjastoa ei ole asennettu oikein. Funktio $1 puuttuu.",
+       "thumbnail_image-size-zero": "Kuvatiedoston koko näyttäisi olevan nolla.",
        "thumbnail_image-missing": "Tiedosto näyttää puuttuvan: $1",
        "thumbnail_image-failure-limit": "Tätä kuvaketta on yritetty tulkita epäonnistuneesti liian monta kertaa ($1 tai enemmän). Ole hyvä ja yritä myöhemmin uudelleen.",
        "import": "Tuo sivuja",
        "pageinfo-category-subcats": "Alaluokkien määrä",
        "pageinfo-category-files": "Tiedostojen määrä",
        "pageinfo-user-id": "Käyttäjän tunnistenumero",
+       "pageinfo-file-hash": "Hash-arvo",
        "markaspatrolleddiff": "Merkitse tarkastetuksi",
        "markaspatrolledtext": "Merkitse muutos tarkastetuksi",
        "markaspatrolledtext-file": "Merkitse tämä tiedoston versio tarkastetuksi",
        "watchlistedit-clear-titles": "Sivujen nimet:",
        "watchlistedit-clear-submit": "Tyhjennä tarkkailulista (pysyvästi ja peruuttamattomasti!)",
        "watchlistedit-clear-done": "Tarkkailulistasi on tyhjennetty.",
+       "watchlistedit-clear-jobqueue": "Tarkkailulistaasi ollaan tyhjentämässä. Tämä voi kestää jonkin aikaa!",
        "watchlistedit-clear-removed": "{{PLURAL:$1|Yksi sivu|$1 sivua}} on poistettu:",
        "watchlistedit-too-many": "Luettelossa on liikaa sivuja näytettäväksi tässä.",
        "watchlisttools-clear": "Tyhjennä tarkkailulista",
        "restrictionsfield-badip": "Virheellinen IP-osoite tai alue: $1",
        "restrictionsfield-label": "Sallitut IP-alueet:",
        "restrictionsfield-help": "Yksi IP-osoite tai CIDR-alue per rivi. Ottaaksesi kaiken käyttöön, käytä:<pre>0.0.0.0/0\n::/0</pre>",
-       "edit-error-short": "$1",
+       "edit-error-short": "Virhe: $1",
        "edit-error-long": "Virheet:\n\n$1",
        "revid": "versio $1",
        "pageid": "sivun tunnistenumero $1",
index d1142f8..1667b4f 100644 (file)
        "userjspreview": "'''Minst til at hetta bert er ein royndarvísing av tínum brúkara JavaScript.'''\n'''Tú hevur ikki goymt tað enn!'''",
        "sitecsspreview": "'''Minst til at hetta bert er ein royndar vísing av hesum CSS.'''\n'''Tú hevur ikki goymt tað enn!'''",
        "sitejspreview": "'''Minst til at hetta bert er ein royndar vísing av hesi JavaScript kotuni.'''\n'''Tað er ikki goymt enn!'''",
-       "userinvalidcssjstitle": "'''Ávaring:''' Tað er onki skinn \"$1\".\nTilevnaðar .css og .js síður brúka heiti sum byrja við lítlum bókstavi, t.d.  {{ns:user}}:Foo/vector.css í mun til {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "'''Ávaring:''' Tað er onki skinn \"$1\".\nTilevnaðar .css og .js síður brúka heiti sum byrja við lítlum bókstavi, t.d.  {{ns:user}}:Foo/vector.css í mun til {{ns:user}}:Foo/Vector.css.",
        "updated": "(Dagført)",
        "note": "'''Viðmerking:'''",
        "previewnote": "<strong>Minst til at hetta bara er ein forskoðan.</strong>\nTínar broytingar eru ikki goymdar enn!",
        "prefs-files": "Fílur",
        "prefs-custom-css": "Tilpassað CSS",
        "prefs-custom-js": "Tilpassað JavaScript",
-       "prefs-common-css-js": "Møgulig CSS/JavaScript fyri allar útsjóndir:",
+       "prefs-common-config": "Møgulig CSS/JavaScript fyri allar útsjóndir:",
        "prefs-reset-intro": "Tú kanst brúka hesa síðuna til at nullstilla allar tínar valdu innstillingar, so tað kemur aftur til standard.\nTú kanst ikki angra, tá tað fyrst er gjørt.",
        "prefs-emailconfirm-label": "Vátta tína t-post adressu:",
        "youremail": "T-postur (sjálvboðið)*:",
index 7a34010..f8fc0fd 100644 (file)
        "userjspreview": "<strong>Rappelez-vous que vous ne faites que visualiser ou tester votre code JavaScript.\nIl n’a pas encore été enregistré !</strong>",
        "sitecsspreview": "<strong>Rappelez-vous que vous ne faites que prévisualiser cette feuille de style. \nElle n’a pas encore été enregistrée !</strong>",
        "sitejspreview": "<strong>Rappelez-vous que vous ne faites que prévisualiser ce code JavaScript. \nIl n’a pas encore été enregistré !</strong>",
-       "userinvalidcssjstitle": "<strong>Attention :</strong> il n’existe pas d’habillage « $1 ». \nLes pages personnelles avec extensions .css et .js utilisent des titres en minuscules, par exemple {{ns:user}}:Foo/vector.css et non {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "<strong>Attention :</strong> il n’existe pas d’habillage « $1 ». \nLes pages personnelles avec extensions .css et .js utilisent des titres en minuscules, par exemple {{ns:user}}:Foo/vector.css et non {{ns:user}}:Foo/Vector.css.",
        "updated": "(Mis à jour)",
        "note": "<strong>Note :</strong>",
        "previewnote": "<strong>Rappelez-vous que ce n’est qu’une prévisualisation.</strong>\nVos modifications n’ont pas encore été enregistrées !",
        "prefs-files": "Fichiers",
        "prefs-custom-css": "CSS personnalisé",
        "prefs-custom-js": "JavaScript personnalisé",
-       "prefs-common-css-js": "CSS et JavaScript communs à tous les habillages :",
+       "prefs-common-config": "CSS et JavaScript communs à tous les habillages :",
        "prefs-reset-intro": "Vous pouvez utiliser cette page pour restaurer vos préférences aux valeurs par défaut du site. Ceci ne peut pas être défait.",
        "prefs-emailconfirm-label": "Confirmation du courriel :",
        "youremail": "Courriel :",
        "thumbnail_dest_directory": "Impossible de créer le répertoire de destination",
        "thumbnail_image-type": "Type d’image non pris en charge",
        "thumbnail_gd-library": "Configuration incomplète de la bibliothèque GD : fonction $1 introuvable",
+       "thumbnail_image-size-zero": "La taille du fichier image semble être de zéro.",
        "thumbnail_image-missing": "Le fichier suivant est introuvable : $1",
        "thumbnail_image-failure-limit": "Il y a eu récemment trop de tentatives échouées ($1 ou plus) pour restituer cette vignette. Veuillez réessayer ultérieurement.",
        "import": "Importer des pages",
index 44d8d52..c0de588 100644 (file)
        "userjsyoucanpreview": "'''Conseil:''' Usez le bouton \"Vue d'avance\" pour tester votre nouvelle feuille JS avant de la sauver.",
        "usercsspreview": "'''Rappelez-vous que vous êtes après regarder votre feuille CSS qu'a pas encore été sauvée!'''",
        "userjspreview": "'''Rappelez-vous que vous êtes juste après regarder ou tester votre code JavaScript qu'a pas encore été sauvé!'''",
-       "userinvalidcssjstitle": "'''Attention:''' Y a pas de style \"$1\".  Rappelez-vous qu'il faut user les petites lettres dans le sujet des pages personnelles avec les extensions .css et .js.\nExemple:  {{ns:user}}:Foo/vector.css (bon)  {{ns:user}}:Foo/Vector.css (mauvais)",
+       "userinvalidconfigtitle": "'''Attention:''' Y a pas de style \"$1\".  Rappelez-vous qu'il faut user les petites lettres dans le sujet des pages personnelles avec les extensions .css et .js.\nExemple:  {{ns:user}}:Foo/vector.css (bon)  {{ns:user}}:Foo/Vector.css (mauvais)",
        "updated": "(Renouvelé)",
        "note": "'''Notez:'''",
        "previewnote": "'''Ça ici, c'est juste une vue d'avance; les changements ont pas encore été sauvés!'''",
index bebff59..c9ea180 100644 (file)
        "userjspreview": "<strong>Rapelâd-vos que vos éte ren qu’aprés èprovar prèvêre voutron code JavaScript.\nIl est p’oncor étâ encartâ !</strong>",
        "sitecsspreview": "<strong>Rapelâd-vos que vos éte ren qu’aprés prèvêre cela fôlye CSS.\nEl est p’oncor étâye encartâye !</strong>",
        "sitejspreview": "<strong>Rapelâd-vos que vos éte ren qu’aprés prèvêre cél code JavaScript.\nIl est p’oncor étâ encartâ !</strong>",
-       "userinvalidcssjstitle": "<strong>Atencion :</strong> ègziste gins d’habelyâjo « $1 ».\nRapelâd-vos que les pâges a sè avouéc èxtensions .css et .js emplèyont de titros en petiôtes lètres, per ègzemplo {{ns:user}}:Foo/vector.css et pas {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "<strong>Atencion :</strong> ègziste gins d’habelyâjo « $1 ».\nRapelâd-vos que les pâges a sè avouéc èxtensions .css et .js emplèyont de titros en petiôtes lètres, per ègzemplo {{ns:user}}:Foo/vector.css et pas {{ns:user}}:Foo/Vector.css.",
        "updated": "(Betâ a jorn)",
        "note": "<strong>Nota :</strong>",
        "previewnote": "<strong>Rapelâd-vos qu’o est ren qu’un apèrçu.</strong>\nVoutros changements sont p’oncor étâs encartâs !",
        "prefs-files": "Fichiérs",
        "prefs-custom-css": "CSS pèrsonalisâ",
        "prefs-custom-js": "JavaScript pèrsonalisâ",
-       "prefs-common-css-js": "CSS et JavaScript partagiê por tôs los habelyâjos :",
+       "prefs-common-config": "CSS et JavaScript partagiê por tôs los habelyâjos :",
        "prefs-reset-intro": "Vos pouede empleyér cela pâge por rètablir voutres prèferences a les valors per dèfôt du seto.\nCen pôt pas étre dèfêt.",
        "prefs-emailconfirm-label": "Confirmacion de l’adrèce èlèctronica :",
        "youremail": "Adrèce èlèctronica :",
index 396ff16..97ef571 100644 (file)
@@ -15,7 +15,7 @@
                ]
        },
        "tog-underline": "Ferwisangen onerstrik:",
-       "tog-hideminor": "Letj anrangen fersteeg",
+       "tog-hideminor": "Letj feranrangen fersteeg",
        "tog-hidepatrolled": "Letj anrangen fersteeg",
        "tog-newpageshidepatrolled": "Kontroliaret sidjen bi a \"Nei sidjen\" fersteeg",
        "tog-hidecategorization": "Kategorisiarang faan sidjen fersteeg",
@@ -48,6 +48,7 @@
        "tog-watchlisthideminor": "Letj feranrangen bi a sidjen, diar ik uun't uug behual wal, fersteeg",
        "tog-watchlisthideliu": "Feranrangen faan uunmeldet brükern bi sidjen, diar ik uun't uug behual wal, fersteeg",
        "tog-watchlistreloadautomatically": "List mä sidjen, diar dü uun't uug behual wel, nei loose, wan en filter anert wurden as (brükt JavaScript)",
+       "tog-watchlistunwatchlinks": "Direkt ferwisangen tu Uunlukin/Ei uunlukin bi iindracher uun det list faan sidjen, diar dü uun't uug behual wel (brükt JavaScript)",
        "tog-watchlisthideanons": "Feranrangen faan anonüüm brükern (IPs) bi sidjen, diar ik uun't uug behual wal, fersteeg",
        "tog-watchlisthidepatrolled": "Kontroliaret feranrangen bi a sidjen, diar ik uun't uug behual wal, fersteeg",
        "tog-watchlisthidecategorization": "Kategorisiarang faan sidjen fersteeg",
        "userjspreview": "'''Seenk diaram, dat det bluas en föörskau faan din JavaScript as.'''\n'''Det as noch ei seekert wurden!'''",
        "sitecsspreview": "<strong>Paase üüb! Det as bluas en föörskau faan't CSS. Det as noch ei seekert wurden!</strong>",
        "sitejspreview": "<strong>Paase üüb! Det as bluas en föörskau faan di JavaScript code. Det as noch ei seekert wurden!</strong>",
-       "userinvalidcssjstitle": "''Paase üüb:''' Skak \"$1\" jaft at ei.\nSeenk diaram, dat faan en brüker iinracht .css- an .js-sidjen mä en letjen buksteew began skel. Bispal:\n''{{ns:user}}:Münsterkjarl/vector.css'' uunsteed faan ''{{ns:user}}:Münsterkjarl/Vector.css''.",
+       "userinvalidconfigtitle": "''Paase üüb:''' Skak \"$1\" jaft at ei.\nSeenk diaram, dat faan en brüker iinracht .css- an .js-sidjen mä en letjen buksteew began skel. Bispal:\n''{{ns:user}}:Münsterkjarl/vector.css'' uunsteed faan ''{{ns:user}}:Münsterkjarl/Vector.css''.",
        "updated": "(Feranert)",
        "note": "'''Paase üüb:'''",
        "previewnote": "'''Heer könst dü sä, hü det sidj wurd skal.'''\nDet sidj as oober noch ei seekert!",
        "timezoneregion-indian": "Indik",
        "timezoneregion-pacific": "Pasiifik",
        "allowemail": "E-mail faan ööder brükern tuläät",
+       "email-allow-new-users-label": "E-mails uk faan gans nei brükern tuläät",
        "email-blacklist-label": "Jodiar brükern mut mi nian e-mails schüür:",
        "prefs-searchoptions": "Schük",
        "prefs-namespaces": "Nöömrümer",
        "prefs-files": "Datein",
        "prefs-custom-css": "Salew maaget CSS",
        "prefs-custom-js": "Salew maaget JavaScript",
-       "prefs-common-css-js": "CSS / JavaScript för arke skak:",
+       "prefs-common-config": "CSS / JavaScript för arke skak:",
        "prefs-reset-intro": "Üüb detdiar sidj könst dü weder a normool iinstelangen iinracht.\nDo san jo ual iinstelangen wech.",
        "prefs-emailconfirm-label": "E-Mail gudkäänd:",
        "youremail": "E-mail:",
        "prefs-help-email": "Dü säärst din e-mail-adres ei uundu, oober do könst dü uk nian mädialangen fu, wan dü ans din paaswurd ferjiden heest.",
        "prefs-help-email-others": "Mä ööder brükern könst dü uk auer hör an din brükersidj kontakt apnem. Diarför woort din e-mail-adres ei brükt.",
        "prefs-help-email-required": "Du en rocht E-Mail-adres uun.",
-       "prefs-info": "Baasisdooten",
+       "prefs-info": "Grünjdooten",
        "prefs-i18n": "Spriak",
        "prefs-signature": "Signatuur",
        "prefs-dateformat": "Formaat faan't dootem",
        "recentchangeslinked-feed": "Feranrangen bi ferlinket sidjen",
        "recentchangeslinked-toolbox": "Feranrangen bi ferlinket sidjen",
        "recentchangeslinked-title": "Feranrangen bi sidjen, huar faan \"$1\" üüb ferwiset woort",
-       "recentchangeslinked-summary": "Detdiar spezial-sidj wiset a leetst feranrangen faan ferwiset sidjen (of faan sidjen uun en was kategorii). Sidjen, diar dü [[Special:Watchlist|uun't uug behual]] wel, san '''fäät''' skrewen.",
+       "recentchangeslinked-summary": "Skriiw en sidjennööm hen, am feranrangen üüb sidjen tu sen, diar üüb det sidj henwise of faan det sidj wechwise.\nAm iindracher uun en kategorii tu sen, skriiw 'Kategorie:Nööm faan't kategorii' hen.\nFeranrangen bi sidjen, diar dü [[Special:Watchlist|uun't uug behual]] wel, san <strong>fäät</strong> skrewen.",
        "recentchangeslinked-page": "Sidjennööm:",
        "recentchangeslinked-to": "Wise feranrangen üüb sidjen, diar heerhen ferwise.",
        "recentchanges-page-added-to-category": "[[:$1]] tu kategorii saat",
        "sp-contributions-newbies": "Wise bluas bidracher faan nei brükern",
        "sp-contributions-newbies-sub": "Faan nei brükern",
        "sp-contributions-newbies-title": "Brükerbidracher faan nei brükern",
-       "sp-contributions-blocklog": "Sper-logbuk",
+       "sp-contributions-blocklog": "Sper-Logbuk",
        "sp-contributions-suppresslog": "Fersteecht {{GENDER:$1|brükerbidracher}}",
-       "sp-contributions-deleted": "Stregen {{GENDER:$1|brüker}} bidtacher",
+       "sp-contributions-deleted": "Stregen {{GENDER:$1|brüker}} bidracher",
        "sp-contributions-uploads": "Huuchschüürd bilen",
        "sp-contributions-logs": "Logbuken",
        "sp-contributions-talk": "Diskuschuun",
        "sp-contributions-username": "IP-adres of brükernööm:",
        "sp-contributions-toponly": "Bluas aktuel werjuunen wise",
        "sp-contributions-newonly": "Bluas nei maaget sidjen wise",
+       "sp-contributions-hideminor": "Letj feranrangen fersteeg",
        "sp-contributions-submit": "Schük",
        "whatlinkshere": "Ferwisangen üüb detdiar sidj",
        "whatlinkshere-title": "Sidjen, diar üüb \"$1\" ferwise",
        "contribslink": "Bidracher",
        "emaillink": "E-mail schüür",
        "autoblocker": "Automaatisk speret, auer dü en gemiansoom IP-adres mä [[User:$1|brüker:$1]] brükst. Grünj för det brükersper: „$2“.",
-       "blocklogpage": "Brükersper-logbuk",
+       "blocklogpage": "Brükersper-Logbuk",
        "blocklog-showlog": "Didiar brüker as al ans speret wurden.\nUun't sperlogbuk stäänt:",
        "blocklog-showsuppresslog": "Didiar brüker as al ans speret an ferbürgen wurden.\nUun't logbuk stäänt:",
        "blocklogentry": "hää „[[$1]]“ speret för di tidjrüm: $2 $3",
        "tag-mw-new-redirect-description": "Feranrangen, diar en nei widjerfeerang iinracht.",
        "tag-mw-removed-redirect": "Widjerfeerang wechnimen",
        "tag-mw-changed-redirect-target": "Widjerfeerang feranert",
+       "tag-mw-rollback": "Turagsaat",
        "tags-title": "Kääntiaken",
        "tags-intro": "Det sidj wiset kääntiaken, diar för't bewerkin brükt wurd, an wat jo men.",
        "tags-tag": "Kääntiaken-nööm",
index 3a25100..7de676d 100644 (file)
        "userjsyoucanpreview": "<strong>Tip:</strong> Brûk de knop \"{{int:showpreview}}\" om jo nije JS te testen foar it fêstlizzen.",
        "usercsspreview": "<strong>Dit is allinne mar it oerlêzen fan jo persoanlike CSS. Hy is noch net fêstlein!</strong>",
        "userjspreview": "<strong>Tink derom: jo besjogge no jo persoanlike JavaScript. De side is net fêstlein!</strong>",
-       "userinvalidcssjstitle": "<strong>Warskôging:</strong> der is gjin skin \"$1\".\nTink derom: jo eigen .css- en .js-siden begjinne mei in lytse letter, bygelyks {{ns:user}}:Namme/vector.css ynsté fan {{ns:user}}:Namme/Vector.css.",
+       "userinvalidconfigtitle": "<strong>Warskôging:</strong> der is gjin skin \"$1\".\nTink derom: jo eigen .css- en .js-siden begjinne mei in lytse letter, bygelyks {{ns:user}}:Namme/vector.css ynsté fan {{ns:user}}:Namme/Vector.css.",
        "updated": "(Bewurke)",
        "note": "<strong>Opmerking:</strong>",
        "previewnote": "<strong>Tink der om dat dizze side noch net fêstlein is!</strong>",
index 40b2919..b4ee44f 100644 (file)
        "userjsyoucanpreview": "'''Leid:''' Sula sábhálann tú, úsáid an cnaipe \"{{int:showpreview}}\" chun do JavaScript nua a thástáil.",
        "usercsspreview": "'''Cuimhnigh nach bhfuil seo ach réamhamharc do CSS úsáideora -\nníor sábháladh é go fóill!'''",
        "userjspreview": "'''Cuimhnigh nach bhfuil seo ach réamhamharc do JavaScript úsáideora\n- níor sábháladh é go fóill!'''",
-       "userinvalidcssjstitle": "'''Rabhadh:''' Níl an craiceann \"$1\" ann.\nCuimhnigh go n-úsáideann leathanaigh shaincheaptha .css agus .js teideal i gcás íochtar, m.sh. {{ns:user}}:Foo/vector.css i leapa {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "'''Rabhadh:''' Níl an craiceann \"$1\" ann.\nCuimhnigh go n-úsáideann leathanaigh shaincheaptha .css agus .js teideal i gcás íochtar, m.sh. {{ns:user}}:Foo/vector.css i leapa {{ns:user}}:Foo/Vector.css.",
        "updated": "(Leasaithe)",
        "note": "'''Tabhair faoi deara:'''",
        "previewnote": "'''Cuimhnigh nach bhfuil ach réamhamharc sa leathanach seo.'''\nNíl do chuid athruithe sábháilte fós!",
index 0764f30..7f6fc3d 100644 (file)
        "userjsyoucanpreview": "'''提示:''' 存到前请用'望吖起'来测吖倷𠮶新JS 。",
        "usercsspreview": "'''注意倷单系到预览倷个人𠮶 CSS,内容哈冇保存!'''",
        "userjspreview": "'''注意倷单系到测试/预览倷个人𠮶 JavaScript,内容哈冇保存!'''",
-       "userinvalidcssjstitle": "'''警告:''' 冇\"$1\"𠮶皮肤。请记到自定义𠮶 .css 同 .js 页要用小写。就话,{{ns:user}}:Foo/vector.css 伓等同 {{ns:user}}:Foo/Vector.css。",
+       "userinvalidconfigtitle": "'''警告:''' 冇\"$1\"𠮶皮肤。请记到自定义𠮶 .css 同 .js 页要用小写。就话,{{ns:user}}:Foo/vector.css 伓等同 {{ns:user}}:Foo/Vector.css。",
        "updated": "(更新正喽)",
        "note": "'''注意:'''",
        "previewnote": "'''请记到个光系预览,内容哈冇保存!'''",
index a683acb..e249240 100644 (file)
        "userjsyoucanpreview": "'''提示:''' 存到前請用'望吖起'來測吖倷嗰新JS 。",
        "usercsspreview": "'''注意倷單係到預覽倷個人嗰 CSS,內容哈冇保存!'''",
        "userjspreview": "'''注意倷單係到測試/預覽倷個人嗰 JavaScript,內容哈冇保存!'''",
-       "userinvalidcssjstitle": "'''警告:''' 冇\"$1\"嗰皮膚。請記到自定義嗰 .css 同 .js 頁要用小寫。就話,{{ns:user}}:Foo/vector.css 伓等同 {{ns:user}}:Foo/Vector.css。",
+       "userinvalidconfigtitle": "'''警告:''' 冇\"$1\"嗰皮膚。請記到自定義嗰 .css 同 .js 頁要用小寫。就話,{{ns:user}}:Foo/vector.css 伓等同 {{ns:user}}:Foo/Vector.css。",
        "updated": "(更新正哩)",
        "note": "'''注意:'''",
        "previewnote": "'''請記到箇光係預覽,內容哈冇保存!'''",
index b30a5bd..036ef01 100644 (file)
        "userjspreview": "<strong>Cuimhnich nach e seo ach ro-shealladh/deuchainn air a' JavaScript agad.\nCha deach a shàbhaladh fhathast!</strong>",
        "sitecsspreview": "<strong>Cuimhnich nach e seo ach ro-shealladh air a' CSS agad.\nCha deach a shàbhaladh fhathast!</strong>",
        "sitejspreview": "<strong>Cuimhnich nach e seo ach ro-shealladh air còd a' JavaScript agad.\nCha deach a shàbhaladh fhathast!</strong>",
-       "userinvalidcssjstitle": "<strong>Rabhadh:</strong> Chan eil an craiceann \"$1\" ann.\nCleachdaidh duilleagan gnàthaichte .css agus .js tiotal ann an litrichean beaga, m.e. {{ns:user}}:Foo/vector.css seach {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "<strong>Rabhadh:</strong> Chan eil an craiceann \"$1\" ann.\nCleachdaidh duilleagan gnàthaichte .css agus .js tiotal ann an litrichean beaga, m.e. {{ns:user}}:Foo/vector.css seach {{ns:user}}:Foo/Vector.css.",
        "updated": "(Air ùrachadh)",
        "note": "<strong>An aire:</strong>",
        "previewnote": "<strong>Cuimhnich nach eil ann ach ro-shealladh.</strong>\nCha deach na mùthaidhean agad a shàbhaladh fhathast!",
        "prefs-files": "Faidhlichean",
        "prefs-custom-css": "CSS gnàthaichte",
        "prefs-custom-js": "JavaScript gnàthaichte",
-       "prefs-common-css-js": "CSS/JavaScript ann an coitcheann do gach craiceann:",
+       "prefs-common-config": "CSS/JavaScript ann an coitcheann do gach craiceann:",
        "prefs-reset-intro": "'S urrainn dhut bun-roghainnean na làraich ath-shuidheachadh air an duilleag seo. Cha ghabh seo a neo-dhèanamh.",
        "prefs-emailconfirm-label": "Dearbhadh puist-d:",
        "youremail": "Post-dealain:",
index fb44ca6..0e7a58e 100644 (file)
        "userjspreview": "'''Lembre que só está probando/previsualizando o seu JavaScript de usuario.'''\n'''Este aínda non foi gardado!'''",
        "sitecsspreview": "'''Lembre que só está vendo a vista previa deste CSS.'''\n'''Este aínda non foi gardado!'''",
        "sitejspreview": "'''Lembre que só está vendo a vista previa deste código JavaScript.'''\n'''Este aínda non foi gardado!'''",
-       "userinvalidcssjstitle": "<strong>Aviso:</strong> Non hai ningunha aparencia chamada \"$1\".\nLembre que as páxinas .css e .js personalizadas utilizan un título en minúsculas, como por exemplo \"{{ns:user}}:Exemplo/vector.css\" no canto de \"{{ns:user}}:Exemplo/Vector.css\".",
+       "userinvalidconfigtitle": "<strong>Aviso:</strong> Non hai ningunha aparencia chamada \"$1\".\nLembre que as páxinas .css e .js personalizadas utilizan un título en minúsculas, como por exemplo \"{{ns:user}}:Exemplo/vector.css\" no canto de \"{{ns:user}}:Exemplo/Vector.css\".",
        "updated": "(Actualizado)",
        "note": "'''Nota:'''",
        "previewnote": "<strong>Lembre que esta é só unha vista previa.</strong>\nAínda non gardou os seus cambios!",
        "prefs-files": "Ficheiros",
        "prefs-custom-css": "CSS personalizado",
        "prefs-custom-js": "JavaScript personalizado",
-       "prefs-common-css-js": "CSS/JavaScript compartido por todas as aparencias:",
+       "prefs-common-config": "CSS/JavaScript compartido por todas as aparencias:",
        "prefs-reset-intro": "Pode usar esta páxina para restablecer as súas preferencias ás que veñen dadas por defecto.\nEste cambio non se poderá desfacer.",
        "prefs-emailconfirm-label": "Confirmación do correo:",
        "youremail": "Correo electrónico:",
index c9d4291..c56250e 100644 (file)
        "clearyourcache": "'''Ἐπισημείωσις - Μετὰ τὸ καταγράφειν, ἐνδεχομένως δεῖ σε παρακάμψειν τὴν λανθάνουσαν μνήμην τοῦ πλοηγητηρίου σου πρὸ τοῦ ὁρᾶν τὰς μεταβολάς.'''\n'''Mozilla Safari:''' ἐρητύειν τὸ ''Shift'' ἐνῷ θλίβεις τὸ ''Reload'', ἢ πίεσον εἴτε ''Ctrl-F5'' ἢ ''Ctrl-R'' (''Command-R'' ἐν Mac);\n'''Google Chrome:''' θλίψον ''Ctrl-Shift-R'' (''Command-Shift-R'' ἐν Mac)\n'''Konqueror: '''θλίψον τὸ ''Reload'' ἢ πίεσον ''F5''\n'''Opera:''' καθαίρειν τὴν λανθάνουσαν μνήμην ἐν ''Tools → Preferences''\n'''Internet Explorer:''' ἐρητύειν τὸ ''Ctrl'' ἐνῷ θλίβεις τὸ ''Refresh,'' ἢ πίεσον ''Ctrl-F5''.",
        "usercssyoucanpreview": "'''Βουλή:''' Χρῆσον τῷ κομβίῳ 'Δεικνύναι προθεώρησιν' ἵνα δοκιμάσῃς τὴν νέαν σου CSS πρὸ τοῦ καταγράφειν.",
        "userjsyoucanpreview": "'''Βουλή:''' Χρῆσον τῷ κομβίῳ 'Δεικνύναι προθεώρησιν' ἵνα δοκιμάσῃς τὴν νέαν σου JS πρὸ τοῦ καταγράφειν.",
-       "userinvalidcssjstitle": "'''Προσοχή:''' Οὐχ ὑφίσταται ''skin'' \"$1\". Μέμνησο: αἱ προσηρμοσμέναι δέλτοι .css καὶ .js χρῶνται ἐπώνυμον τι ἔχον πεζὰ γράμματα, π.χ. {{ns:user}}:Foo/vector.css ἐν ἀντίθεσει πρὸς τὸν {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "'''Προσοχή:''' Οὐχ ὑφίσταται ''skin'' \"$1\". Μέμνησο: αἱ προσηρμοσμέναι δέλτοι .css καὶ .js χρῶνται ἐπώνυμον τι ἔχον πεζὰ γράμματα, π.χ. {{ns:user}}:Foo/vector.css ἐν ἀντίθεσει πρὸς τὸν {{ns:user}}:Foo/Vector.css.",
        "updated": "(Ἐνημερωθέν)",
        "note": "'''Ἐπισήμανσις:'''",
        "previewnote": "'''Ἥδε ἐστὶ προθεώρησις, οὐχὶ καταγραφὴ τῶν μεταβολῶν!'''",
index f16e09e..9c1dcfe 100644 (file)
        "userjspreview": "== Vorschau vu Dyynem Benutzer-Javascript. ==\n'''Gib acht:''' Noch em Spychere muesch Dyy Browser aawyse di nej Version z lade: '''Mozilla:''' ''Strg-Shift-R'', '''IE:''' ''Strg-F5'', '''Safari:''' ''Cmd-Shift-R'', '''Konqueror:''' ''F5''.",
        "sitecsspreview": "'''Obacht: Du luegsch nume ne Vorschau vu däm CSS aa.'''\n'''S isch nonig gspycheret wore!'''",
        "sitejspreview": "'''Obacht: Du luegsch nume ne Vorschau vu däm JavaScript aa.'''\n'''S isch nonig gspycheret wore!'''",
-       "userinvalidcssjstitle": "'''Achtig:''' D Skin „$1“ git s nid. Dänk dra, ass benutzerspezifischi .css- und .js-Syte mit eme Chleibuechstabe mien aafange, also z B. ''{{ns:user}}:Mustermann/vector.css'' statt ''{{ns:user}}:Mustermann/Vector.css''.",
+       "userinvalidconfigtitle": "'''Achtig:''' D Skin „$1“ git s nid. Dänk dra, ass benutzerspezifischi .css- und .js-Syte mit eme Chleibuechstabe mien aafange, also z B. ''{{ns:user}}:Mustermann/vector.css'' statt ''{{ns:user}}:Mustermann/Vector.css''.",
        "updated": "(Gänderet)",
        "note": "'''Obacht: '''",
        "previewnote": "'''Das isch numen e Vorschau und nonig gspycheret!'''\nDie Syte isch nonig gspycheret wore!",
        "prefs-files": "Bilder",
        "prefs-custom-css": "Benutzerdefinierti CSS",
        "prefs-custom-js": "Benutzerdefiniert JS",
-       "prefs-common-css-js": "Gmeinsam CSS/JS fir alli Skin:",
+       "prefs-common-config": "Gmeinsam CSS/JS fir alli Skin:",
        "prefs-reset-intro": "Du chasch die Syte verwände go d Yystellige uf dr Standard zrucksetze.\nDes cha nimmi ruckgängig gmacht wäre.",
        "prefs-emailconfirm-label": "E-Mail-Bstätigung:",
        "youremail": "E-Mail-Adräss:",
index a75b84c..01be1ab 100644 (file)
        "userjspreview": "'''યાદ રહે કે તમે તમારા સભ્ય JavaScript નું અવલોકન કરો છે.'''\n'''હજી સીધું તે સચવાયું નથી!'''",
        "sitecsspreview": "'''યાદ રહે કે તમે તમારા સભ્ય CSS નું અવલોકન કરો છે.'''\n'''હજી સીધું તે સચવાયું નથી!'''",
        "sitejspreview": "'''યાદ રહે કે તમે તમારા સભ્ય JavaScript નું અવલોકન કરો છે.'''\n'''હજી સીધું તે સચવાયું નથી!'''",
-       "userinvalidcssjstitle": "'''ચેતવણી:''' કોઇ પણ \"$1\" પટલ નથી.\nસભ્ય રચિત .css અને  .js પાના બીજી અંગ્રેજી બારખડી શીર્ષક વાપરે છે, દા. ત. {{ns:user}}:Foo/vector.css નહીં કે {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "'''ચેતવણી:''' કોઇ પણ \"$1\" પટલ નથી.\nસભ્ય રચિત .css અને  .js પાના બીજી અંગ્રેજી બારખડી શીર્ષક વાપરે છે, દા. ત. {{ns:user}}:Foo/vector.css નહીં કે {{ns:user}}:Foo/Vector.css.",
        "updated": "(સંવર્ધીત)",
        "note": "'''નોંધ:'''",
        "previewnote": "<strong>ધ્યાનમાં રાખો કે આ ફક્ત પૂર્વાવલોકન છે.</strong>\nતમારા ફેરફારો હજુ સાચવવામાં આવ્યા નથી!",
        "prefs-files": "ફાઇલ",
        "prefs-custom-css": "ખાસ  CSS",
        "prefs-custom-js": "સભ્ય નિર્મિત JavaScript",
-       "prefs-common-css-js": "બધા જ ફલક માટે સહીયારી CSS/JavaScript",
+       "prefs-common-config": "બધા જ ફલક માટે સહીયારી CSS/JavaScript",
        "prefs-reset-intro": "તમે આ પાનાનો ઉપયોગ કરીને તમારા પસંદ કરેલા વિકલ્પોને પાછા સાઇટના મૂળ વિકલ્પો સમાન ગોઠવી શકો છો.\n\nઆને ઉલટાવી નહિ શકાય.",
        "prefs-emailconfirm-label": "ઇ-મેલ પુષ્ટી",
        "youremail": "ઇ-મેઇલ:",
index c4819f7..26e605d 100644 (file)
        "userjsyoucanpreview": "'''Thì-sṳ:''' Chhai pó-chhùn chhièn chhiáng yung 'hién-sṳ yi-ko' on-néu lòi chhet-chhṳ ngì sîn-ke JS.",
        "usercsspreview": "'''Chu-yi ngì chak-he chhai yi-liau ngì ke-ngìn CSS, hàn-mò tú-chhùn!'''",
        "userjspreview": "'''記緊汝單單係在測試/預覽汝嘅用戶JavaScript。'''\n'''還吂保存!'''",
-       "userinvalidcssjstitle": "'''警告:''' 毋存在外皮“$1”。\n注意自定嘅.css撈.js頁愛使用小寫標題,例如,{{ns:user}}:Foo/vector.css撈 {{ns:user}}:Foo/Vector.css毋同。",
+       "userinvalidconfigtitle": "'''警告:''' 毋存在外皮“$1”。\n注意自定嘅.css撈.js頁愛使用小寫標題,例如,{{ns:user}}:Foo/vector.css撈 {{ns:user}}:Foo/Vector.css毋同。",
        "updated": "(Yí-kîn kiên-sîn)",
        "note": "<strong>Chu-yi:</strong>",
        "previewnote": "'''請記到邇單淨係預覽。'''\n汝嘅更改還吂保存!",
index 15197b7..a291093 100644 (file)
        "mytalk": "שיחה",
        "anontalk": "שיחה",
        "navigation": "ניווט",
-       "and": "&#32;ו",
+       "and": "&#32;וגם",
        "faq": "שאלות ותשובות",
        "actions": "פעולות",
        "namespaces": "מרחבי שם",
        "userjspreview": "<strong>זִכרו שזו רק בדיקה/תצוגה מקדימה של סקריפט ה־JavaScript שלכם.\nהוא עדיין לא נשמר!</strong>",
        "sitecsspreview": "'''זכרו שזו רק תצוגה מקדימה של גיליון ה־CSS הזה.'''\n'''הוא טרם נשמר!'''",
        "sitejspreview": "'''זכרו שזו רק תצוגה מקדימה של קוד ה־JavaScript הזה.'''\n'''הוא טרם נשמר!'''",
-       "userinvalidcssjstitle": "'''אזהרה:''' העיצוב \"$1\" אינו קיים.\nדפי .css ו־.js מותאמים אישית משתמשים בכותרת עם אותיות קטנות – למשל, {{ns:user}}:דוגמה/vector.css ולא {{ns:user}}:דוגמה/Vector.css.",
+       "userinvalidconfigtitle": "'''אזהרה:''' העיצוב \"$1\" אינו קיים.\nדפי .css ו־.js מותאמים אישית משתמשים בכותרת עם אותיות קטנות – למשל, {{ns:user}}:דוגמה/vector.css ולא {{ns:user}}:דוגמה/Vector.css.",
        "updated": "(מעודכן)",
        "note": "'''הערה:'''",
        "previewnote": "<strong>{{GENDER:|זכור|זִכרי|זִכרו}} שזו רק תצוגה מקדימה.</strong>\nהשינויים {{GENDER:|שלך|שלך|שלכם}} עדיין לא נשמרו!",
        "prefs-files": "קבצים",
        "prefs-custom-css": "קובץ CSS מותאם אישית",
        "prefs-custom-js": "קובץ JavaScript מותאם אישית",
-       "prefs-common-css-js": "קובצי CSS/JavaScript משותפים לכל העיצובים:",
+       "prefs-common-config": "קובצי CSS/JavaScript משותפים לכל העיצובים:",
        "prefs-reset-intro": "באפשרותך להשתמש באפשרות זו כדי להחזיר את ההעדפות שלך להגדרות ברירת המחדל של האתר.\nלא ניתן לבטל פעולה זו.",
        "prefs-emailconfirm-label": "אימות כתובת דוא\"ל:",
        "youremail": "דואר אלקטרוני:",
        "thumbnail_dest_directory": "לא ניתן היה ליצור את תיקיית היעד",
        "thumbnail_image-type": "סוג התמונה אינו נתמך",
        "thumbnail_gd-library": "הגדרת הספריה GD אינה שלמה: חסרה הפונקציה $1",
+       "thumbnail_image-size-zero": "נראה שקובץ התמונה הוא בגודל אפס.",
        "thumbnail_image-missing": "נראה שהקובץ הבא חסר: $1",
        "thumbnail_image-failure-limit": "היו לאחרונה ניסיונות רבים מדי ($1 או יותר) ליצור את התמונה הממוזערת הזאת. נא לנסות שוב מאוחר יותר.",
        "import": "ייבוא דפים",
index 7e0397d..a36d1d3 100644 (file)
        "userjspreview": "'''ध्यान दें कि आप अपनी जावास्क्रिप्ट की झलक देख रहे हैं।'''\n'''यह अभी तक संजोई नहीं गई है!'''",
        "sitecsspreview": "''''ध्यान दें कि आप इस सी॰एस॰एस की झलक देख रहे हैं।'''\n'''यह अभी तक संजोई नहीं गई है!'''",
        "sitejspreview": "'''ध्यान दें कि आप इस जावास्क्रिप्ट कोड की झलक देख रहे हैं।'''\n'''यह अभी तक संजोया नहीं गया है!'''",
-       "userinvalidcssjstitle": "'''चेतावनी:''' \"$1\" नाम की कोई त्वचा नहीं है।\nबदले हुए .css और .js पृष्ठों के शीर्षक नीचे स्तर की लिपि (lowercase) का प्रयोग करते हैं। उदाहरण: {{ns:user}}:Foo/vector.css न की {{ns:user}}:Foo/Vector.css",
+       "userinvalidconfigtitle": "'''चेतावनी:''' \"$1\" नाम की कोई त्वचा नहीं है।\nबदले हुए .css और .js पृष्ठों के शीर्षक नीचे स्तर की लिपि (lowercase) का प्रयोग करते हैं। उदाहरण: {{ns:user}}:Foo/vector.css न की {{ns:user}}:Foo/Vector.css",
        "updated": "(अद्यतनीत)",
        "note": "'''सूचना:'''",
        "previewnote": "'''याद रखें, यह केवल एक झलक है।'''\nआपके बदलाव अभी तक संजोये नहीं गए हैं!",
        "prefs-files": "फ़ाइलें",
        "prefs-custom-css": "खासमखास सी॰एस॰एस",
        "prefs-custom-js": "खासमखास जावास्क्रिप्ट",
-       "prefs-common-css-js": "सभी त्वचाओं के लिए साझा सी॰एस॰एस/जावास्क्रिप्ट:",
+       "prefs-common-config": "सभी त्वचाओं के लिए साझा सी॰एस॰एस/जावास्क्रिप्ट:",
        "prefs-reset-intro": "आप इस पृष्ठ के ज़रिए अपनी वरीयताओं को साइट की मूल वरीयताओं के समान बना सकते हैं।\nइसके बाद आप वापस पुरानी स्थिति पर नहीं आ सकेंगे।",
        "prefs-emailconfirm-label": "ई-मेल पुष्टिकरण:",
        "youremail": "आपका ई-मेल पता:",
index c60a534..b96f5d0 100644 (file)
        "userjspreview": "'''Yaad rakhna ki aap khali aapan JavaScript ke testing/previewing  karta hai, iske abhi save nai karaa gais hai!'''",
        "sitecsspreview": " '''Yaad rakhna ki aap ii CSS ke khaali preview kartaa hae.'''\n'''Iske abhi talak bachawa nai gais hae!'''",
        "sitejspreview": " '''Yaad rakhna ki aap ii JavaScript code ke khaali preview kartaa hae.'''\n'''Iske abhi talak bachawa nai gais hae!'''",
-       "userinvalidcssjstitle": "'''Warning:''' Koi skin \"$1\" nai hai.\nYaad rakhna ki custom .css aur .js panna owercase title use kare hai, jaise ki {{ns:user}}:Foo/vector.css aur{{ns:user}}:Foo/Vector.css nai.",
+       "userinvalidconfigtitle": "'''Warning:''' Koi skin \"$1\" nai hai.\nYaad rakhna ki custom .css aur .js panna owercase title use kare hai, jaise ki {{ns:user}}:Foo/vector.css aur{{ns:user}}:Foo/Vector.css nai.",
        "updated": "(Update kar dewa gais hai)",
        "note": "'''Dhyan rakkho:'''",
        "previewnote": "'''Ii khaali ek jhalak dekhae hai'''\nTumar badlao abhi bachawa nai gais hai!",
        "prefs-files": "File ke naam",
        "prefs-custom-css": "CSS ke aapan khatir badlo",
        "prefs-custom-js": "Ruchi ke anusar JS",
-       "prefs-common-css-js": "Sab skins ke khatir, baata gais CSS/JavaScript",
+       "prefs-common-config": "Sab skins ke khatir, baata gais CSS/JavaScript",
        "prefs-reset-intro": "Aap ii panna ke kaam me laae ke site defaults ke aapan preferences ke reset kare sakta hai.\nIske pahile jaise nai karaa jaawe sake hai.",
        "prefs-emailconfirm-label": "E-mail ke confirm karaa jaawe hai:",
        "youremail": "E-mail:",
index 4829231..79d086a 100644 (file)
        "userjspreview": "'''Tandai nga ginalantaw/ginatilawan mo pa lang ang imo JavaScript sang manuggamit.'''\n'''Wala pa ini matipon!'''",
        "sitecsspreview": "'''Tandai nga ginalantaw mo pa lang ang ini nga CSS.'''\n'''Wala pa ini matipon!'''",
        "sitejspreview": "'''Tandai nga ginalantaw mo pa lang ang ini nga kodigo sang JavaScript.'''\n'''Wala pa ini matipon!'''",
-       "userinvalidcssjstitle": "'''Aviso:''' Wala sang panit nga \"$1\".\nSa mga pahungod nga mga panid nga .css and .js magamit sang titulo nga may gagmay nga letra (lowercase), e.g. {{ns:user}}:Foo/vector.css kontra sa {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "'''Aviso:''' Wala sang panit nga \"$1\".\nSa mga pahungod nga mga panid nga .css and .js magamit sang titulo nga may gagmay nga letra (lowercase), e.g. {{ns:user}}:Foo/vector.css kontra sa {{ns:user}}:Foo/Vector.css.",
        "updated": "(Suno sa oras)",
        "note": "'''Pansinon:'''",
        "previewnote": "'''Tandaan nga prebyu lamang ini.'''\nWala pa nabutang ang imo nga ginbag-o!",
        "prefs-files": "Mga hilera",
        "prefs-custom-css": "Ginhungod nga CSS",
        "prefs-custom-js": "Ginhungod nga JavaScript",
-       "prefs-common-css-js": "Ginaparte nga CSS?JavaScript para sa tanan nga panit:",
+       "prefs-common-config": "Ginaparte nga CSS?JavaScript para sa tanan nga panit:",
        "prefs-reset-intro": "Indi mo magamit ang ini nga panid agod mailisan ang imo mga pagpalabi sa mga default sang site.\nIndi na ini maliwat pa.",
        "prefs-emailconfirm-label": "Paagkumperma sang e-mail:",
        "youremail": "E-mail:",
index 63b9bce..5b96d4d 100644 (file)
        "userjspreview": "'''Ne zaboravite: samo isprobavate/pregledavate svoj suradnički JavaScript, i da još nije snimljen!'''",
        "sitecsspreview": "'''Ne zaboravite ovo je samo pregled ovog CSS-a.'''\n'''Još uvijek nije sačuvan!'''",
        "sitejspreview": "'''Ne zaboravite ovo je samo pregled JavaScript kôda.'''\n'''Još uvijek nije sačuvan!'''",
-       "userinvalidcssjstitle": "'''Upozorenje:''' Nema sučelja pod imenom \"$1\". Ne zaboravite da imena stranica s .css and .js kodom počinju malim slovom, npr. {{ns:user}}:Mate/vector.css, a ne {{ns:user}}:Mate/Vector.css.",
+       "userinvalidconfigtitle": "'''Upozorenje:''' Nema sučelja pod imenom \"$1\". Ne zaboravite da imena stranica s .css and .js kodom počinju malim slovom, npr. {{ns:user}}:Mate/vector.css, a ne {{ns:user}}:Mate/Vector.css.",
        "updated": "(Ažurirano)",
        "note": "'''Napomena:'''",
        "previewnote": "<strong>Ne zaboravite da je ovo samo pregled kako će stranica izgledati.</strong>\nVaše uređivanje još nije snimljeno!",
        "prefs-files": "Datoteke",
        "prefs-custom-css": "Prilagođen CSS",
        "prefs-custom-js": "Prilagođen JS",
-       "prefs-common-css-js": "Dijeljeni CSS/JS za sve izglede:",
+       "prefs-common-config": "Dijeljeni CSS/JS za sve izglede:",
        "prefs-reset-intro": "Možete koristiti ovu stranicu za povrat Vaših postavki na prvotne postavke. Ovo se ne može poništiti.",
        "prefs-emailconfirm-label": "Potvrda e-mail adrese:",
        "youremail": "Vaša adresa e-pošte:",
        "protect-cascadeon": "Ova stranica je zaštićena jer je uključena u {{PLURAL:$1|stranicu, koja ima|stranice, koje imaju|stranice, koje imaju}} uključenu prenosivu zaštitu. Možete promijeniti stupanj zaštite ove stranice, no to neće utjecati na prenosivu zaštitu.",
        "protect-default": "Omogućeno svim suradnicima",
        "protect-fallback": "Potrebno je imati \"$1\" ovlasti",
-       "protect-level-autoconfirmed": "Dopušteno samo autopotvrđenima",
-       "protect-level-sysop": "Samo administratori",
+       "protect-level-autoconfirmed": "dopušteno samo autopotvrđenima",
+       "protect-level-sysop": "samo administratori",
        "protect-summary-cascade": "prenosiva zaštita",
        "protect-expiring": "istječe $1 (UTC)",
        "protect-expiring-local": "istječe $2 u $3",
index 09ee5b6..b9e8dcf 100644 (file)
        "userjspreview": "'''Beacht, dass du nuar en Voarschau von dein Benutzer-JavaScripts betrachte tust.'''\n'''Das woor noch net gespeichert!'''",
        "sitecsspreview": "'''Beachte, dass du nuar en Voarschau von das CSS betrachte tust.'''\n'''Das woard noch net gespeichert!'''",
        "sitejspreview": "'''Beacht, dass du nuar en Vorschau von das JavaScript betrachte tust.'''\n'''Das woor noch net gespeichert!'''",
-       "userinvalidcssjstitle": "'''Achtung:''' Die Benutzerowerfläch \"$1\" existiert net. Bedenke, dass benutzerspezifisch .css- und .js-Seite mit em Klenbuchstoob oonfänge müsse, also beispielsweis ''{{ns:user}}:Mustermann/vector.css'' an Stell von ''{{ns:user}}:Mustermann/Vector.css''.",
+       "userinvalidconfigtitle": "'''Achtung:''' Die Benutzerowerfläch \"$1\" existiert net. Bedenke, dass benutzerspezifisch .css- und .js-Seite mit em Klenbuchstoob oonfänge müsse, also beispielsweis ''{{ns:user}}:Mustermann/vector.css'' an Stell von ''{{ns:user}}:Mustermann/Vector.css''.",
        "updated": "(Geännert)",
        "note": "'''Hinweis:'''",
        "previewnote": "'''Dies ist nuar en Voarschau.'''\nDie Seit woard noch net gespeichert!",
        "prefs-files": "Dateie",
        "prefs-custom-css": "Benutzerdefinierte CSS",
        "prefs-custom-js": "Benutzerdefiniertes JavaScript",
-       "prefs-common-css-js": "Gemeinsames CSS/JavaScript von aller Benutzerowerfläche:",
+       "prefs-common-config": "Gemeinsames CSS/JavaScript von aller Benutzerowerfläche:",
        "prefs-reset-intro": "Du kannst die Seit verwenne, um die Einstellunge uff die Standards zurückzusetzen.\nDas kann net meh rückgängich gemacht sin.",
        "prefs-emailconfirm-label": "E-Mail-Bestätichung:",
        "youremail": "E-Mail-Adress:",
index 1a19adb..2412e0f 100644 (file)
        "userjspreview": "'''Dźiwaj na to, zo jenož swój wužiwarski JavaScript testuješ/sej wobhladuješ.'''\n'''Hišće njeje so składował!'''",
        "sitecsspreview": "'''Wobkedźbujće, zo sej jenož přehlad tutoho CSS wobhladuješ.'''\n'''Wón hišće składowany njeje!'''",
        "sitejspreview": "'''Wobkedźbujće, zo sej jenož přehlad tutoho JavaScriptoweho koda wobhladuješ.'''\n'''Wón hišće składowany njeje!'''",
-       "userinvalidcssjstitle": "'''Warnowanje:''' Drasta z mjenom „$1” njeeksistuje. Prošu mysli na to, zo wosobinske strony .css a .js titul z małym pismikom wuwziwaja, na př. {{ns:user}}:Foo/vector.css město {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "'''Warnowanje:''' Drasta z mjenom „$1” njeeksistuje. Prošu mysli na to, zo wosobinske strony .css a .js titul z małym pismikom wuwziwaja, na př. {{ns:user}}:Foo/vector.css město {{ns:user}}:Foo/Vector.css.",
        "updated": "(Zaktualizowany)",
        "note": "'''Kedźbu:'''",
        "previewnote": "'''Wobmysl, zo to je jenož přehlad.'''\nTwoje změny hišće njejsu składowane!",
        "prefs-files": "Dataje",
        "prefs-custom-css": "Swójski CSS",
        "prefs-custom-js": "Swójski JS",
-       "prefs-common-css-js": "Zhromadny CSS/JS za w32 šaty:",
+       "prefs-common-config": "Zhromadny CSS/JS za w32 šaty:",
        "prefs-reset-intro": "You can use this page to reset your preferences to the site defaults. This cannot be undone.\nMóžeš tutu stronu wužiwać, zo by swoje nastajenja na standardne hódnoty sydła wróćo stajić. To njeda so anulować.",
        "prefs-emailconfirm-label": "E-mejlowe wobkrućenje:",
        "youremail": "E-mejl:",
index 3c6e441..4ce20c8 100644 (file)
        "userjspreview": "'''Sonje ke ou ap voye kout je sou fèy JavaScript ou ekri a, li poko anrejistre !'''",
        "sitecsspreview": "'''Sonje ke w ap voye yon kout je sou sa w ekri nan fèy CSS sa a.'''\n'''Li poko anrejistre !'''",
        "sitejspreview": "'''Sonje ke w ap voye yon kout je sou kòd JavaScript sa a.'''\n'''Li poko anrejistre !'''",
-       "userinvalidcssjstitle": "'''Atansyon :''' estil \"$1\" pa egziste. Paj pèsonalize ak ekstansyon .css epi .js yo ap gen tit/sijè an lèt miniskil, pa egzanp {{ns:user}}:Foo/vector.css men pa {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "'''Atansyon :''' estil \"$1\" pa egziste. Paj pèsonalize ak ekstansyon .css epi .js yo ap gen tit/sijè an lèt miniskil, pa egzanp {{ns:user}}:Foo/vector.css men pa {{ns:user}}:Foo/Vector.css.",
        "updated": "(Li gen dènye vèsyon sou li)",
        "note": "'''Nòt :'''",
        "previewnote": "'''Atansyon, tèks sa a se yon kout je, li poko anrejistre !'''",
index ffe80ec..822d64e 100644 (file)
        "userjspreview": "'''Ne felejtsd el, hogy még csak teszteled a felhasználói JavaScriptedet, és még nincs elmentve!'''",
        "sitecsspreview": "'''Ne feledd, hogy csak a CSS előnézetét látod.'''\n'''Még nincs elmentve!'''",
        "sitejspreview": "'''Ne feledd, hogy a JavaScript-kódnak csak az előnézetét látod.'''\n'''Még nincs elmentve!'''",
-       "userinvalidcssjstitle": "'''Figyelem:''' Nincs „$1” nevű felület. A felületekhez tartozó .css/.js oldalak kisbetűvel kezdődnek, például ''{{ns:user}}:Gipsz Jakab/vector.css'' és nem ''{{ns:user}}:Gipsz Jakab/Vector.css''.",
+       "userinvalidconfigtitle": "'''Figyelem:''' Nincs „$1” nevű felület. A felületekhez tartozó .css/.js oldalak kisbetűvel kezdődnek, például ''{{ns:user}}:Gipsz Jakab/vector.css'' és nem ''{{ns:user}}:Gipsz Jakab/Vector.css''.",
        "updated": "(frissítve)",
        "note": "'''Megjegyzés:'''",
        "previewnote": "'''Ne feledd, hogy ez csak egy előnézet.''' A változtatásaid még nincsenek elmentve!",
        "prefs-files": "Fájlok",
        "prefs-custom-css": "saját CSS",
        "prefs-custom-js": "saját JS",
-       "prefs-common-css-js": "Közös CSS/JS az összes felület számára:",
+       "prefs-common-config": "Közös CSS/JS az összes felület számára:",
        "prefs-reset-intro": "Ezen a lapon állíthatod vissza a beállításaidat az oldal alapértelmezett értékeire.\nA műveletet nem lehet visszavonni.",
        "prefs-emailconfirm-label": "E-mail-cím megerősítése:",
        "youremail": "Az e-mail címed:",
        "thumbnail_dest_directory": "Nem hozható létre a célkönyvtár",
        "thumbnail_image-type": "A képformátum nem támogatott",
        "thumbnail_gd-library": "A GD-könyvtár nincs megfelelően beállítva: a(z) $1 függvény hiányzik",
+       "thumbnail_image-size-zero": "A képfájl mérete nullának tűnik.",
        "thumbnail_image-missing": "Úgy tűnik, hogy a fájl hiányzik: $1",
        "thumbnail_image-failure-limit": "Túl sok hibás bélyegkép létrehozás (több mint $1). Próbáld meg később!",
        "import": "Lapok importálása",
index e12da15..2383ede 100644 (file)
        "userlogin-resetpassword-link": "Մոռացե՞լ եք գաղտնաբառը",
        "userlogin-helplink2": "Մուտք գործելու օգնություն",
        "userlogin-loggedin": "Դուք արդեն մտել է որպես {{GENDER:$1|$1}}.\nՕգտագործեք ստորև բերված ձևը մուտք գործելու համար այլ հաշից",
-       "userlogin-reauth": "Ô´Õ¸Ö\82Ö\84 ÕºÕ¥Õ¿Ö\84 Õ§ Õ¯Ö\80Õ¯Õ«Õ¶ Õ°Õ¡Õ´Õ¡Õ¯Õ¡Ö\80Õ£ Õ´Õ¸Ö\82Õ¿Ö\84 Õ£Õ¸Ö\80Õ®Õ¥Ö\84  Õ°Õ¡Õ½Õ¿Õ¡Õ¿Õ¥Õ¬Õ¸Ö\82 Õ¸Ö\80 Õ¤Õ¸Ö\82Ö\84 Õ¤Õ¸Ö\82Ö\84 Õ¥Ö\84 {{GENDER:$1|$1}}.",
+       "userlogin-reauth": "Ô´Õ¸Ö\82Ö\84 ÕºÕ¥Õ¿Ö\84 Õ§ Õ¯Ö\80Õ¯Õ«Õ¶ Õ´Õ¸Ö\82Õ¿Ö\84 Õ£Õ¸Ö\80Õ®Õ¥Ö\84 Õ°Õ¡Õ´Õ¡Õ¯Õ¡Ö\80Õ£` Õ°Õ¡Õ½Õ¿Õ¡Õ¿Õ¥Õ¬Õ¸Ö\82, Õ¸Ö\80 Õ¤Õ¸Ö\82Ö\84 Õ¤Õ¸Ö\82Ö\84 Õ¥Ö\84 {{GENDER:$1|$1}}Ö\89",
        "userlogin-createanother": "Ստեղծել այլ հաշիվ",
        "createacct-emailrequired": "Էլ–փոստի հասցե",
        "createacct-emailoptional": "Էլ–փոստի հասցե (ոչ պարտադիր)",
        "userjsyoucanpreview": "'''Հուշում.''' Էջը հիշելուց առաջ օգտվեք «{{int:showpreview}}» կոճակից՝ ձեր նոր JS-նիշքը ստուգելու համար։",
        "usercsspreview": "'''Նկատի ունեցեք, որ դուք միայն նախադիտում եք ձեր մասնակցի CSS-նիշքը. այն դեռ հիշված չէ՛։'''",
        "userjspreview": "'''Նկատի ունեցեք, որ դուք միայն նախադիտում եք ձեր մասնակցի JavaScript-նիշքը. այն դեռ հիշված չէ՛։'''",
-       "userinvalidcssjstitle": "'''Զգուշացում.''' «$1» տեսք չի գտնվել։ Ի նկատի ունեցեք, որ մասնակցային .css և .js էջերը ունեն փոքրատառ անվանումներ, օր.՝ «{{ns:user}}:Ոմն/vector.css», և ոչ թե «{{ns:user}}:Ոմն/Vector.css»։",
+       "userinvalidconfigtitle": "'''Զգուշացում.''' «$1» տեսք չի գտնվել։ Ի նկատի ունեցեք, որ մասնակցային .css և .js էջերը ունեն փոքրատառ անվանումներ, օր.՝ «{{ns:user}}:Ոմն/vector.css», և ոչ թե «{{ns:user}}:Ոմն/Vector.css»։",
        "updated": "(Թարմացված)",
        "note": "'''Ծանուցում.'''",
        "previewnote": "'''Սա միայն նախադիտումն է. ձեր կատարած փոփոխությունները դեռ չե՛ն հիշվել։'''",
        "rcfilters-filtergroup-changetype": "Փոփոխության տեսակ",
        "rcfilters-filter-pageedits-label": "Էջի խմբագրումներ",
        "rcfilters-filter-newpages-label": "Նոր էջեր",
-       "rcfilters-filter-logactions-label": "Մուտ գործած գործողություններ",
+       "rcfilters-filter-logactions-label": "Մուտք գործած գործողություններ",
        "rcfilters-filtergroup-lastRevision": "Ամենավերջին տարբերակ",
        "rcfilters-filter-previousrevision-label": "Ոչ վերջին տարբերակ",
        "rcfilters-view-tags": "Պիտակված խմբագրումներ",
index 7703ba2..fbb1bec 100644 (file)
        "userjspreview": "'''Non oblida que isto es solmente un test/previsualisation de tu JavaScript personalisate.'''\n'''Illo non ha ancora essite salveguardate!'''",
        "sitecsspreview": "'''Non oblida que isto es solmente un previsualisation de iste CSS.'''\n'''Le modificationes non ha ancora essite salveguardate!'''",
        "sitejspreview": "'''Non oblida que isto es solmente un previsualisation de iste codice JavaScript.'''\n'''Le modificationes non ha ancora essite salveguardate!'''",
-       "userinvalidcssjstitle": "'''Attention:''' Le apparentia \"$1\" non existe.\nMemora que le paginas .css and .js personalisate usa un titulo in minusculas, p.ex. {{ns:user}}:Foo/vector.css e non {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "'''Attention:''' Le apparentia \"$1\" non existe.\nMemora que le paginas .css and .js personalisate usa un titulo in minusculas, p.ex. {{ns:user}}:Foo/vector.css e non {{ns:user}}:Foo/Vector.css.",
        "updated": "(Actualisate)",
        "note": "'''Nota:'''",
        "previewnote": "'''Isto es solmente un previsualisation.'''\nLe modificationes non ha ancora essite publicate!",
        "prefs-files": "Files",
        "prefs-custom-css": "CSS personalisate",
        "prefs-custom-js": "JS personalisate",
-       "prefs-common-css-js": "CSS/JS commun a tote le apparentias:",
+       "prefs-common-config": "CSS/JS commun a tote le apparentias:",
        "prefs-reset-intro": "Iste pagina es pro reinitialisar tu preferentias al valores predefinite del sito.\nLe operation non pote esser disfacite.",
        "prefs-emailconfirm-label": "Confirmation del e-mail:",
        "youremail": "E-mail:",
index 62e0e97..1525a1f 100644 (file)
        "userjspreview": "'''Ingatlah bahwa yang Anda lihat hanyalah pratayang JavaScript Anda, dan bahwa pratayang tersebut belum disimpan!'''",
        "sitecsspreview": "'''Ingatlah bahwa Anda hanya menampilkan pratayang dari CSS ini.'''\n'''Perubahan belum disimpan!'''",
        "sitejspreview": "'''Ingatlah bahwa Anda hanya menampilkan pratayang dari kode JavaScript ini.'''\n'''Perubahan belum disimpan!'''",
-       "userinvalidcssjstitle": "'''Peringatan:''' Kulit \"$1\" tidak ditemukan. Harap diingat bahwa halaman .css dan .js menggunakan huruf kecil, contoh {{ns:user}}:Foo/vector.css dan bukannya {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "'''Peringatan:''' Kulit \"$1\" tidak ditemukan. Harap diingat bahwa halaman .css dan .js menggunakan huruf kecil, contoh {{ns:user}}:Foo/vector.css dan bukannya {{ns:user}}:Foo/Vector.css.",
        "updated": "(Diperbarui)",
        "note": "'''Catatan:'''",
        "previewnote": "'''Ingatlah bahwa ini hanya pratayang.'''\nPerubahan Anda belum disimpan!",
        "prefs-files": "Berkas",
        "prefs-custom-css": "CSS pribadi",
        "prefs-custom-js": "JS pribadi",
-       "prefs-common-css-js": "CSS/JS berbagi untuk semua kulit:",
+       "prefs-common-config": "CSS/JS berbagi untuk semua kulit:",
        "prefs-reset-intro": "Anda dapat menggunakan halaman ini untuk mengembalikan preferensi Anda ke setelan baku situs.\nPengembalian preferensi tidak dapat dibatalkan.",
        "prefs-emailconfirm-label": "Konfirmasi surel:",
        "youremail": "Surel:",
index 4acbcf7..6b155aa 100644 (file)
        "userjsyoucanpreview": "'''Punta:''' Usa li buton \"{{int:showpreview}}\" por provar tui nov JavaScript ante de conservar.",
        "usercsspreview": "'''Memora que vu es solmen vident un prevision de tui CSS de usator.'''\n'''It ne have esset conservat ancor!'''",
        "userjspreview": "'''Memora que vu es solmen provant/monstrant tui JavaScript de usator.'''\n'''It ne ha esset conservat ancor!'''",
-       "userinvalidcssjstitle": "'''Advertiment:''' Ne vi pelle \"$1\".\nMemora que hábitu .css e págines .js usa un titul plu bass, e.g. {{ns:user}}:Foo/vector.css quam oposit por {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "'''Advertiment:''' Ne vi pelle \"$1\".\nMemora que hábitu .css e págines .js usa un titul plu bass, e.g. {{ns:user}}:Foo/vector.css quam oposit por {{ns:user}}:Foo/Vector.css.",
        "updated": "(Modernisat)",
        "note": "<strong>Note:</strong>",
        "previewnote": "'''Memora se que ti es solmen un prevision.'''\nTui changes ancor ne ha esset conservat!",
index abed7eb..679152d 100644 (file)
        "userjspreview": "<strong>Laglagipem a subsubokam/ipadpadasmo ti bukodmo a JavaScript ti agar-aramat.\nSaan pay a naidulin!</strong>",
        "sitecsspreview": "<strong>Laglagipem nga ipadpadasmo laeng daytoy a CSS.\nSaan pay a naidulin!</strong>",
        "sitejspreview": "<strong>Laglagipem nga ipadpadasmo laeng daytoy a kodigo ti JavaScript.\nSaan pay nga naidulin!</strong>",
-       "userinvalidcssjstitle": "<strong>Ballaag:</strong> Awan ti kudil a \"$1\".\nDagiti panid ti naiduma a .css ken .js ket agus-usar iti titulo ti bassit a letra, kas ti {{ns:user}}:Foo/vector.css saan a kas ti {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "<strong>Ballaag:</strong> Awan ti kudil a \"$1\".\nDagiti panid ti naiduma a .css ken .js ket agus-usar iti titulo ti bassit a letra, kas ti {{ns:user}}:Foo/vector.css saan a kas ti {{ns:user}}:Foo/Vector.css.",
        "updated": "(Napabaro)",
        "note": "<strong>Nota:</strong>",
        "previewnote": "<strong>Laglagipem a daytoy ket panagipadas laeng.</strong>\nDagiti sinukatam ket saan pay a naidulin!",
        "prefs-files": "Dagiti papeles",
        "prefs-custom-css": "Naiduma a CSS",
        "prefs-custom-js": "Naiduma a JavaScript",
-       "prefs-common-css-js": "Bingay a CSS/JavaScript para kadagiti amin a kudil:",
+       "prefs-common-config": "Bingay a CSS/JavaScript para kadagiti amin a kudil:",
        "prefs-reset-intro": "Mabalinmo nga usaren daytoy a panid tapno maisublim dagita kakaykayatam iti kasisigud iti daytoy a wiki.\nNgem saanto a mabalinen nga ipasubli.",
        "prefs-emailconfirm-label": "Pammasingked ti esurat:",
        "youremail": "Esurat:",
index 648c280..58a5c60 100644 (file)
        "yourtext": "Vua texto",
        "storedversion": "Gardita versiono",
        "editingold": "'''EGARDEZ: Vu redaktas anciena versiono di ca pagino.\nSe vu gardus ol, la chanji facita pos ita revizo perdesos.'''",
+       "unicode-support-fail": "Semblas ke vua retnavigilo ne suportas Unicode. To bezonesas por redaktar ica pagino e, pro to, vua redakto ne konservesis.",
        "yourdiff": "Diferi",
        "copyrightwarning": "Voluntez memorar ke omna kontributi a {{SITENAME}} esas sub la $2 (Videz $1 por detali).\nSe vu ne deziras ke altri modifikez vua artikli od oli distributesez libere, lore voluntez ne skribar oli hike.<br />\nPublikigante vua skribajo hike, vu asertas ke olu skribesis da vu ipsa o kopiesis de libera fonto.\n'''NE SENDEZ ARTIKLI KUN ''COPYRIGHT'' SEN PERMISO!'''",
        "protectedpagewarning": "<strong>Averto: Ica pagino esas protektita por ke nur uzeri kun administero-yuri povas redaktar ol.</strong>\nLa maxim recenta en-registrago provizesas:",
        "recreate-moveddeleted-warn": "<strong>Atencez: Vu rikreos pagino qua antee efacesis.</strong>\n\nVu bezonas konsiderar se esas konvenanta durar lua redakto, o ne.\nPor konveno, la motivo dil antea efaco montresas hike:",
        "moveddeleted-notice": "Ica pagino efacabis.\nL'efaco-registraro e la movo-registraro di la pagino povas videsar sequante, por konsulto.",
        "edit-conflict": "Konflikto di editi.",
+       "postedit-confirmation-saved": "Vua redakto konservesis",
        "content-model-wikitext": "texto Wiki",
        "content-model-javascript": "JavaScript",
        "content-json-empty-object": "vakua objekto",
        "userrights-groupsmember": "Membro di:",
        "group": "Grupo:",
        "group-user": "Uzanti",
+       "group-autoconfirmed": "Uzeri automatale konfirmita",
        "group-bot": "Roboti",
        "group-sysop": "Administranti",
        "group-bureaucrat": "Burokrati",
        "group-all": "(omna)",
        "group-user-member": "{{GENDER:$1|uzero}}",
+       "group-autoconfirmed-member": "{{GENDER:$1|Uzero automatale konfirmita}}",
        "group-bot-member": "{{GENDER:$1|roboto}}",
        "group-sysop-member": "{{GENDER:$1|administrero}}",
        "group-bureaucrat-member": "{{GENDER:$1|burokrato}}",
        "grouppage-user": "{{ns:project}}:Uzanti",
+       "grouppage-autoconfirmed": "{{ns:project}}:Uzeri automatale konfirmita",
        "grouppage-bot": "{{ns:project}}:Roboti",
        "grouppage-sysop": "{{ns:project}}:Administranti",
        "grouppage-bureaucrat": "{{ns:project}}:Burokrati",
        "recentchanges-label-plusminus": "La pagino modifikesis segun ica quanto di *bicoki",
        "recentchanges-legend-heading": "<strong>Noto:</strong>",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (videz anke la [[Special:NewPages|listo di nova pagini]])",
+       "rcfilters-other-review-tools": "Altra instrumenti por revizo",
+       "rcfilters-activefilters": "Agiva filtrili",
+       "rcfilters-advancedfilters": "Rafinita filtrili",
+       "rcfilters-search-placeholder": "Filtrar la modifikuri (uzez la menuo o serchez segun la nomo dil filtrilo)",
+       "rcfilters-filter-editsbyself-label": "Vua modifikuri",
+       "rcfilters-filter-user-experience-level-unregistered-label": "Sen registro",
+       "rcfilters-filter-user-experience-level-unregistered-description": "Redakteri qui ne facis 'log in'.",
+       "rcfilters-filter-user-experience-level-newcomer-label": "Nova uzeri",
+       "rcfilters-filter-user-experience-level-learner-label": "Lernanti",
+       "rcfilters-filter-user-experience-level-learner-description": "Redakteri enrejistrita kun konoco inter \"Nova uzeri\" ed \"experta uzeri.\"",
+       "rcfilters-filter-user-experience-level-experienced-label": "Experta uzeri",
        "rcfilters-filter-user-experience-level-experienced-description": "Plu kam 30 dii di agemeso e 500 redakti.",
+       "rcfilters-filter-humans-label": "Homala (ne 'bot')",
+       "rcfilters-filtergroup-significance": "Senco",
+       "rcfilters-filter-pageedits-label": "Redakti di pagini",
+       "rcfilters-filter-newpages-label": "Kreado di pagini",
+       "rcfilters-filter-categorization-label": "Modifiki di la kategorio",
+       "rcfilters-filter-logactions-label": "Agadi enrejistrata",
        "rcnotefrom": "Infre {{PLURAL:$5|esas la chanjo|esas la chanji}} de <strong>$3, $4</strong> (montrata til <strong>$1</strong>).",
        "rclistfrom": "Montrar nova chanji startante de $3 $2",
        "rcshowhideminor": "$1 mikra redakti",
        "filepage-nofile": "Nula arkivo kun ica nomo existas.",
        "uploadnewversion-linktext": "Adkargez nova versiono dil arkivo",
        "shared-repo-from": "ek $1",
+       "shared-repo-name-wikimediacommons": "Wikimedia Commons",
        "upload-disallowed-here": "Vu ne povas modifikar ica arkivo.",
        "filerevert-comment": "Motivo:",
        "filedelete": "Efacar $1",
        "randompage": "Hazarda pagino",
        "randomredirect": "Hazarda ridirektilo",
        "statistics": "Statistiko",
-       "statistics-header-users": "Statistiki di uzero",
+       "statistics-header-pages": "Statistiki di la pagini",
+       "statistics-header-edits": "Statistiki pri redakto",
+       "statistics-header-users": "Statistiki pri l'uzeri",
        "statistics-header-hooks": "Altra statistiki",
+       "statistics-articles": "Temala pagini",
        "statistics-pages": "Pagini",
+       "statistics-pages-desc": "Omna pagini dil Wiki, inkluzite pagini por facar diskuti, ridirektadi, edc.",
+       "statistics-edits": "Quanto di redakti pos ke {{SITENAME}} kreesis",
+       "statistics-edits-average": "Mezavalora quanto di redakti per pagino",
+       "statistics-users-active": "Aktiva uzeri",
        "doubleredirects": "Duopla ridirektili",
        "double-redirect-fixer": "Reparar ridirekti",
        "brokenredirects": "Ridirektili nekorekta",
        "linksearch-ns": "Nomaro:",
        "linksearch-ok": "Serchez",
        "listusers-submit": "Montrez",
+       "activeusers": "Listo pri aktiva uzeri",
        "activeusers-noresult": "Nula uzero trovesis.",
        "listgrouprights-group": "Grupo",
        "listgrouprights-members": "(listo di membri)",
        "delete-confirm": "Efacar \"$1\"",
        "delete-legend": "Efacar",
        "historywarning": "<strong>Averto:</strong> La pagino quan vu efaceskas havas historio kun $1 {{PLURAL:$1|revizo|revizi}}:",
+       "confirmdeletetext": "Vu selektis efacar ica pagino komplete, inkluzite omna modifiki en ol e la kronologio di la modifiki.\n\nVoluntez konfirmar ke vu fakte deziras facar to, ke vu komprenas omna konsequi dil efaco, e ke vu efacos ol segun [[{{MediaWiki:Policy-url}}|la normi pri l'efaco di artikli]].",
        "actioncomplete": "Ago kompletigita",
        "deletedtext": "\"$1\" efacesis.\nVidez $2 por obtenar registro di recenta efaci.",
        "dellogpage": "Efaco-registraro",
        "sp-contributions-uploads": "sendita arkivi",
        "sp-contributions-logs": "registrari",
        "sp-contributions-talk": "diskutez",
+       "sp-contributions-userrights": "yuri dil {{GENDER:$1|uzero}} pri administrado",
        "sp-contributions-search": "Serchar kontributadi",
        "sp-contributions-username": "IP-adreso od uzantonomo:",
        "sp-contributions-toponly": "Montrar nur la maxim recenta revizi",
        "sp-contributions-newonly": "Montrar nur redakti qui esas kreado di pagini",
        "sp-contributions-submit": "Serchez",
-       "whatlinkshere": "Quo ligas hike",
+       "whatlinkshere": "Quo ligesas adhike",
        "whatlinkshere-title": "Pagini qui ligas ad \"$1\"",
        "whatlinkshere-page": "Pagino:",
        "linkshere": "Ca pagini esas ligilizita ad '''[[:$1]]''':",
        "reblock-logentry": "modifikis la tempo di blokuso [[$1]] por durado di $2 $3",
        "unblocklogentry": "desblokusis \"$1\"",
        "block-log-flags-nocreate": "ne povas krear konto",
+       "block-log-flags-noemail": "e-posto blokuzita",
        "ipb_expiry_invalid": "Nevalida expiro-tempo.",
        "ip_range_invalid": "Nevalida IP-rango.",
        "proxyblocker": "Blokuso di 'Proxy'",
        "movepagebtn": "Movar pagino",
        "pagemovedsub": "Rinomizita sucese",
        "movepage-moved": "'''\"$1\" esas movata ad \"$2\"'''",
+       "movepage-moved-redirect": "Kreita ridirekto.",
+       "movepage-moved-noredirect": "La kreado di ridirekto nuligesis.",
        "articleexists": "Pagino kun sama nomo ja existas od la nomo\nqua vu selektis ne esas valida.\nVoluntez selektar altra nomo.",
        "movetalk": "Rinomizar la debato-pagino se to esas aplikebla.",
        "movelogpage": "Movo-registraro",
        "newimages-legend": "Filtrilo",
        "ilsubmit": "Serchar",
        "bydate": "per dato",
+       "months": "{{PLURAL:$1|$1 monato|$1 monati}}",
+       "years": "{{PLURAL:$1|$1 yaro|$1 yari}}",
        "metadata": "Metadonaji",
        "metadata-help": "Ca arkivo kontenas plusa informo, probable furnisita per la kamero elektronikala o per la \"scanner\" uzata por krear o kopiar l'imajo.\nSe l'arkivo modifikesos de lua originala stando, kelka detali povos ne reprezentar exakte l'arkivo modifikata.",
        "metadata-fields": "Image metadata fields listed in this message will be included on image page display when the metadata table is collapsed.\nOthers will be hidden by default.\n* make\n* model\n* datetimeoriginal\n* exposuretime\n* fnumber\n* isospeedratings\n* focallength\n* artist\n* copyright\n* imagedescription\n* gpslatitude\n* gpslongitude\n* gpsaltitude",
        "tag-filter": "[[Special:Tags|etiketo]] filtrilo:",
        "tag-filter-submit": "Filtrez",
        "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|Etikedo|Etikedi}}]]: $2)",
+       "tags-description-header": "Kompleta deskripto dil senco",
        "tags-active-yes": "Yes",
        "tags-active-no": "No",
        "tags-edit": "redaktar",
        "logentry-delete-restore": "$1 {{GENDER:$2|restauris}} la pagino $3 ($4)",
        "logentry-delete-revision": "$1 {{GENDER:$2|modifikis}} videbleso di {{PLURAL:$5|la revizo|$5 revizi}} di la pagino $3: $4",
        "revdelete-content-hid": "celita kontenajo",
+       "logentry-block-block": "$1 {{GENDER:$2|blokuzis}} {{GENDER:$4|$3}} dum $5 $6",
        "logentry-move-move": "$1 {{GENDER:$2|movis}} la pagino $3 a $4",
        "logentry-move-move-noredirect": "$1 {{GENDER:$2|movis}} la pagino $3 a $4 sen lasar ridirektilo",
        "logentry-move-move_redir": "$1 {{GENDER:$2|movis}} la pagino $3 a $4 sen lasar ridirekto",
        "searchsuggest-search": "Serchez en {{SITENAME}}",
        "searchsuggest-containing": "quan kontenas...",
        "duration-days": "($1 {{PLURAL:$1|dio|dii}})",
+       "duration-years": "$1 {{PLURAL:$1|yaro|yari}}",
        "expand_templates_output": "Rezulto",
        "expand_templates_ok": "O.K.",
        "expand_templates_preview": "Previdar",
index 5fac894..6cd24ec 100644 (file)
@@ -63,6 +63,7 @@
        "tog-watchlisthideminor": "Ekki sýna minniháttar breytingar á vaktlistanum",
        "tog-watchlisthideliu": "Ekki sýna breytingar innskráðra notenda á vaktlistanum",
        "tog-watchlistreloadautomatically": "Endurhlaða vaktlista sjálfkrafa þegar síu er breytt (krefst JavaScript)",
+       "tog-watchlistunwatchlinks": "Bæta við beinum vakta/ekki vakta tenglum við færslur á vöktunarlista (krefst JavaScript til að víxla af/á)",
        "tog-watchlisthideanons": "Ekki sýna breytingar óþekktra notenda á vaktlistanum",
        "tog-watchlisthidepatrolled": "Fela yfirfarnar breytingar í vaktlistanum",
        "tog-watchlisthidecategorization": "Fela flokkun á síðum",
        "noindex-category": "Óraðaðar skrár",
        "broken-file-category": "Síður með brotna skráartengla",
        "categoryviewer-pagedlinks": "($1) ($2)",
+       "category-header-numerals": "$1–$2",
        "about": "Um",
        "article": "Efnissíða",
        "newwindow": "(opnast í nýjum glugga)",
        "tagline": "Úr {{SITENAME}}",
        "help": "Hjálp",
        "search": "Leit",
+       "search-ignored-headings": " #<!-- leave this line exactly as it is --> <pre>\n# Headings that will be ignored by search.\n# Changes to this take effect as soon as the page with the heading is indexed.\n# You can force page reindexing by doing a null edit.\n# The syntax is as follows:\n#   * Everything from a \"#\" character to the end of the line is a comment.\n#   * Every non-blank line is the exact title to ignore, case and everything.\nReferences\nExternal links\nSee also\n #</pre> <!-- leave this line exactly as it is -->",
        "searchbutton": "Leita",
        "go": "Áfram",
        "searcharticle": "Áfram",
-       "history": "Breytingaskrá",
+       "history": "Breytingaskrá síðu",
        "history_short": "Breytingaskrá",
-       "history_small": "skrá",
+       "history_small": "breytingaskrá",
        "updatedmarker": "uppfært frá síðustu heimsókn minni",
        "printableversion": "Prentvæn útgáfa",
        "permalink": "Varanlegur tengill",
        "missingarticle-rev": "(breyting#: $1)",
        "missingarticle-diff": "(Munur: $1, $2)",
        "readonly_lag": "Gagnagrunninum hefur verið læst sjálfkrafa á meðan undirvefþjónarnir reyna að hafa í við aðalvefþjóninn",
+       "nonwrite-api-promise-error": "HTTP-hausinn 'Promise-Non-Write-API-Action' var sendur en beiðnin var til API-skrifeiningar.",
        "internalerror": "Kerfisvilla",
        "internalerror_info": "Innri villa: $1",
        "internalerror-fatal-exception": "Banvæn undantekning af gerðinni \"$1\"",
        "badtitletext": "Umbeðinn síðutitill er ógildur.",
        "title-invalid-empty": "Umbeðinn síðutitill er auður eða inniheldur aðeins heiti nafnrýmis.",
        "title-invalid-utf8": "Umbeðinn síðutitill inniheldur ógilda UTF-8 runu.",
+       "title-invalid-interwiki": "Umbeðinn síðutitill inniheldur interwiki-tengil sem ekki er hægt að nota í titlum.",
+       "title-invalid-talk-namespace": "Umbeðinn síðutitill vísar í spjallsíðu sem ekki getur verið til.",
        "title-invalid-characters": "Umbeðinn síðutitill inniheldur ógilda stafi: \"$1\".",
+       "title-invalid-relative": "Titillinn er með afstæða slóð. Afstæðir síðutitlar (./, ../) eru ekki gildir, því þeir verða oft ekki tiltækir í meðhöndlun í vöfrum notenda.",
        "title-invalid-magic-tilde": "Umbeðinn síðutitill inniheldur ógilda tildurunu (<nowiki>~~~</nowiki>).",
        "title-invalid-too-long": "Umbeðinn síðutitill er of langur. Hann má ekki vera lengri en $1 {{PLURAL:$1|bæti}} í UTF-8 stafatöflu.",
        "title-invalid-leading-colon": "Umbeðinn síðutitill inniheldur ógildan tvípunkt í byrjun.",
        "cannotloginnow-title": "Get ekki skráð inn núna",
        "cannotloginnow-text": "Innskráning er ekki möguleg þegar verið er að nota $1.",
        "cannotcreateaccount-title": "Ekki hægt að búa til aðganga",
+       "cannotcreateaccount-text": "Bein stofnun aðgangs er ekki virk á þessu wiki.",
        "yourdomainname": "Þitt lén:",
        "password-change-forbidden": "Þú getur ekki breytt lykilorðum á þessum wiki.",
        "externaldberror": "Uppfærsla mistókst. Annaðhvort varð villa í gagnasafninu eða að þér sé óheimilt að uppfæra aðra aðganga.",
        "createacct-email-ph": "Settu inn 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",
+       "createaccountmail-help": "Er hægt að nota til að útbúa aðgang fyrir einhvern annann án þess að sjá lykilorðið.",
        "createacct-realname": "Raunverulegt nafn (valfrjálst)",
        "createacct-reason": "Ástæða",
        "createacct-reason-ph": "Afhverju ertu að búa til annan aðgang",
+       "createacct-reason-help": "Skilaboð sem birtast í atvikaskrá við gerð notandaaðgangs",
        "createacct-submit": "Búa til aðganginn",
        "createacct-another-submit": "Stofna aðgang",
        "createacct-continue-submit": "Halda áfram við að búa til aðgang",
        "nocookiesnew": "Notandaaðgangur var búin til, en þú ert ekki skráð(ur) inn.\n{{SITENAME}} notar vefkökur til að skrá inn notendur.\nÞú hefur lokað fyrir vefkökur.\nEndilega opnaðu fyrir þær, skráðu þig svo inn með notandanafni og lykilorði.",
        "nocookieslogin": "{{SITENAME}} notar vefkökur til innskráningar. Vafrinn þinn er ekki að taka á móti þeim. Virkjaðu móttöku á vefkökum í vafranum þínum til að geta skráð þig inn.",
        "nocookiesfornew": "Notandaaðgangurinn var ekki stofnaður, því ekki var hægt að staðfesta uppruna beiðnarinnar.\nGakktu úr skugga um að vefkökur séu virkar, endurlestu þessa síðu og reyndu aftur.",
+       "nocookiesforlogin": "{{int:nocookieslogin}}",
+       "createacct-loginerror": "Það tókst að útbúa notandaaðganginn en hins vegar var ekki hægt að skrá þig sjálfkrafa inn. Farðu áfram á [[Special:UserLogin|handvirka innskráningu]].",
        "noname": "Þú hefur ekki tilgreint gilt notandanafn.",
        "loginsuccesstitle": "Innskráning tókst",
        "loginsuccess": "'''Þú ert nú innskráð(ur) á {{SITENAME}} sem „$1“.'''",
        "pt-login-button": "Skrá inn",
        "pt-login-continue-button": "Halda áfram við að skrá þig inn",
        "pt-createaccount": "Stofna aðgang",
-       "pt-userlogout": "Útskrá",
+       "pt-userlogout": "Útskráning",
        "php-mail-error-unknown": "Óþekkt villa í PHP mail() aðgerð.",
        "user-mail-no-addy": "Gat ekki sent tölvupóst því ekkert tölvupóstfang fannst.",
        "user-mail-no-body": "Reyndi að senda tölvupóst með engu eða verulega stuttu meginmáli.",
        "resetpass_submit": "Skrifaðu aðgangsorðið og skráðu þig inn",
        "changepassword-success": "Það tókst að breyta lykilorðinu þínu!",
        "changepassword-throttled": "Þú hefur gert of margar tilraunir til innskráningar að undanförnu.\nBíddu í $1 áður en þú reynir aftur.",
-       "botpasswords": "Lykilorð róbóta",
-       "botpasswords-label-appid": "Nafn vélmennis:",
+       "botpasswords": "Lykilorð vélmenna",
+       "botpasswords-disabled": "Lykilorð vélmenna eru óvirk.",
+       "botpasswords-existing": "Fyrirliggjandi lykilorð vélmenna",
+       "botpasswords-createnew": "Búa til nýtt lykilorð vélmennis",
+       "botpasswords-editexisting": "Breyta fyrirliggjandi lykilorði vélmennis",
+       "botpasswords-label-appid": "Heiti vélmennis:",
        "botpasswords-label-create": "Búa til",
        "botpasswords-label-update": "Uppfæra",
        "botpasswords-label-cancel": "Hætta við",
        "botpasswords-label-delete": "Eyða",
        "botpasswords-label-resetpassword": "Endurstilla lykilorðið",
        "botpasswords-bad-appid": "Vélmennanafnið „$1“ er ógilt.",
+       "botpasswords-created-title": "Vélmennalykilorð var búið til",
+       "botpasswords-updated-title": "Vélmennalykilorð var uppfært",
+       "botpasswords-deleted-title": "Vélmennalykilorði var eytt",
        "resetpass_forbidden": "Ekki er hægt að breyta lykilorðum",
        "resetpass_forbidden-reason": "Ekki er hægt að breyta lykilorðum: $1",
        "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",
        "resetpass-submit-cancel": "Hætta við",
        "resetpass-wrong-oldpass": "Vitlaust bráðabirgða- eða núverandi lykilorð.\nÞú gætir þegar verið búin/n að breyta lykilorðinu eða sótt um nýtt bráðabirgðalykilorð",
+       "resetpass-recycled": "Settu lykilorðið þitt til að vera eitthvað annað en lykilorðið sem þú ert núna með.",
        "resetpass-temp-emailed": "Þú skráðir þig inn með bráðabirgðakóða úr tölvupósti.\nTil að klára að skrá þig inn, verður þú að velja nýtt lykilorð hér:",
        "resetpass-temp-password": "Bráðabirgðalykilorð:",
        "resetpass-abort-generic": "Breytingum á lykilorðum hefur verið hætt með viðbót.",
        "resetpass-expired": "Lykilorðið þitt er útrunnið. Skráðu nýtt lykilorð til að skrá þig inn.",
+       "resetpass-expired-soft": "Lykilorðið þitt er útrunnið og þarf að endurstilla það. Veldu núna nýtt lykilorð, eða smelltu á \"{{int:authprovider-resetpass-skip-label}}\" til að endurstilla það síðar.",
+       "resetpass-validity-soft": "Lykilorðið þitt er ekki lengur gilt: $1\n\nVeldu núna nýtt lykilorð, eða smelltu á \"{{int:authprovider-resetpass-skip-label}}\" til að endurstilla það síðar.",
        "passwordreset": "Endurstilla lykilorð",
        "passwordreset-text-one": "Útfylltu þetta eyðublað til þess að endursetja lykilorðið.",
+       "passwordreset-text-many": "{{PLURAL:$1|Fylltu í einn af reitunum hér fyrir neðan til að fá tölvupóst með bráðabirgðalykilorði.}}",
        "passwordreset-disabled": "Lokað hefur verið fyrir að endurstilla lykilorð á þessum wiki.",
        "passwordreset-emaildisabled": "Tölvupósteiginleikar hafa verið gerðir óvirkir á þessum wiki.",
        "passwordreset-username": "Notandanafn:",
        "passwordreset-emailsentemail": "Ef þetta netfang er skráð fyrir aðganginum þínum þá hefur töluvpóstur verið sendur til að endursetja lykilorðið.",
        "passwordreset-emailsentusername": "Ef eitthvað netfang er skráð fyrir aðganginum þínum, þá mun verða sendur töluvpóstur til að endursetja lykilorðið.",
        "passwordreset-invalidemail": "Ógilt tölvupóstfang",
+       "passwordreset-nodata": "Hvorki notandanafn né tölvupóstfang var gefið upp",
        "changeemail": "Breyta eða fjarlægja netfang",
        "changeemail-header": "Fylltu út þetta eyðublað til að breyta netfanginu þínu. Ef þú vilt fjarlægja tengingu allra netfanga frá aðganginum þínum skildu þá netfangs reitinn eftir tóman.",
        "changeemail-no-info": "Þú verður að vera skráð(ur) inn til að hafa aðgang að þessari síðu.",
        "changeemail-oldemail": "Núverandi netfang:",
        "changeemail-newemail": "Nýtt netfang:",
+       "changeemail-newemail-help": "Þessi reitur ætti að vera auður ef þú vilt fjarlægja tölvupóstfangið þitt. Þú munt þá ekki geta endurstillt gleymt lykilorð og munt ekki fá tilkynningu í tölvupósti ef tölvupóstfangið er fjarlægt.",
        "changeemail-none": "(ekkert)",
        "changeemail-password": "{{SITENAME}} lykilorðið þitt:",
        "changeemail-submit": "Breyta netfangi",
        "missingcommentheader": "<strong>Áminning:</strong> Þú hefur ekki gefið upp umræðuefni.\nEf þú smellir á \"$1\" aftur, verður breyting þín vistuð án þess.",
        "summary-preview": "Forskoða breytingarágrip:",
        "subject-preview": "Forskoðun viðfangsefnis:",
+       "previewerrortext": "Óvænt villa kom upp þegar reynt var að forskoða breytingarnar þínar.",
        "blockedtitle": "Notandi er bannaður",
        "blockedtext": "'''Notandanafn þitt eða vistfang hefur verið bannað.'''\n\nBannið var sett af $1.\nÁstæðan er eftirfarandi: ''$2''.\n\n* Bannið hófst: $8\n* Banninu lýkur: $6\n* Sá sem banna átti: $7\n\nÞú getur haft samband við $1 eða annan [[{{MediaWiki:Grouppage-sysop}}|stjórnanda]] til að ræða bannið.\nÞú getur ekki notað „Senda þessum notanda tölvupóst“ aðgerðina nema gilt netfang sé skráð í [[Special:Preferences|kjörstillingum þínum]] og að þér hafi ekki verið óheimilað það.\nNúverandi vistfang þitt er $3, og bannnúmerið er #$5.\nHafðu með allar þessar upplýsingar hér fyrir ofan í fyrirspurnum þínum.",
        "autoblockedtext": "Vistfang þitt hefur verið sjálfvirkt bannað því það var notað af öðrum notanda, sem var bannaður af $1.\nÁstæðan er eftirfarandi:\n\n:''$2''\n\n* Bannið hófst: $8\n* Banninu lýkur: $6\n* Sá sem banna átti: $7\n\nÞú getur haft samband við $1 eða annan [[{{MediaWiki:Grouppage-sysop}}|stjórnanda]] til að ræða bannið.\n\nAthugaðu að þú getur ekki notað „Senda þessum notanda tölvupóst“ aðgerðina nema gilt netfang sé skráð í [[Special:Preferences|kjörstillingum þínum]] og að þér hafi ekki verið óheimilað það.\n\nNúverandi vistfang þitt er $3, og bannnúmerið er #$5.\nHafðu með allar þessar upplýsingar hér fyrir ofan í fyrirspurnum þínum.",
+       "systemblockedtext": "Notandanafnið þitt eða IP-vistfangið hafa verið útilokuð sjálfvirkt af MediaWiki.\nUppgefin ástæða er:\n\n:<em>$2</em>\n\n* Upphaf útilokunar: $8\n* Útilokun rennur út: $6\n* Sá sem átti að útiloka: $7\n\nNúverandi IP-vistfang þitt er$3.\nHafðu allar þessar upplýsingar með í öllum þeim fyrirspurnum sem þú gætir gert vegna þessa.",
        "blockednoreason": "engin ástæða gefin",
        "whitelistedittext": "Þú þarft að $1 þig til að breyta síðum.",
        "confirmedittext": "Þú verður að staðfesta netfangið þitt áður en þú getur breytt síðum. Stilltu og staðfestu netfangið þitt í gegnum [[Special:Preferences|kjörstillingarnar]].",
        "userjspreview": "'''Mundu að þú ert aðeins að prófa/forskoða JavaScript-kóðann þinn.'''\n'''Hann hefur ekki enn verið vistaður!'''",
        "sitecsspreview": "'''Mundu að þú ert aðeins að forskoða CSS-kóðann þinn.'''\n'''Hann hefur ekki enn verið vistaður!'''",
        "sitejspreview": "'''Mundu að þú ert aðeins að prófa/forskoða JavaScript-kóðann.'''\n'''Hann hefur ekki enn verið vistaður!'''",
-       "userinvalidcssjstitle": "<strong>Viðvörun:</strong> Skinnið \"$1\" er ekki til. Sérsniðin CSS og JavaScript útlit nota lágstafi, t.d.  {{ns:user}}:Foo/vector.css en alls ekki {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "<strong>Viðvörun:</strong> Skinnið \"$1\" er ekki til. Sérsniðin CSS og JavaScript útlit nota lágstafi, t.d.  {{ns:user}}:Foo/vector.css en alls ekki {{ns:user}}:Foo/Vector.css.",
        "updated": "(Uppfært)",
        "note": "'''Athugið:'''",
        "previewnote": "'''Það sem sést hér er aðeins forskoðun og hefur ekki enn verið vistað!'''",
        "yourtext": "Þinn texti",
        "storedversion": "Geymd útgáfa",
        "editingold": "'''ATH: Þú ert að breyta gamalli útgáfu þessarar síðu og munu allar breytingar sem gerðar hafa verið á henni frá þeirri útgáfu vera fjarlægðar ef þú vistar.'''",
+       "unicode-support-fail": "Það lítur út fyrir að vafrinn þinn styðji ekki Unicode. Það er nauðsynlegt til að geta breytt síðum, þannig að breytingarnar þínar voru ekki vistaðar.",
        "yourdiff": "Mismunur",
        "copyrightwarning": "Vinsamlegast athugaðu að öll framlög á {{SITENAME}} eru álitin leyfisbundin samkvæmt $2 (sjá $1 fyrir frekari upplýsingar).  Ef þú vilt ekki að skrif þín falli undir þetta leyfi og öllum verði frjálst að breyta og endurútgefa efnið samkvæmt því skaltu ekki leggja þau fram hér.<br />\nÞú berð ábyrgð á framlögum þínum, þau verða að vera þín skrif eða afrit texta í almannaeigu eða sambærilegs frjáls texta.\n<strong>AFRITIРEKKI HÖFUNDARRÉTTARVARIN VERK Á ÞESSA SÍÐU ÁN LEYFIS</strong>",
        "copyrightwarning2": "Vinsamlegast athugið að aðrir notendur geta breytt eða fjarlægt öll framlög til {{SITENAME}}.\nEf þú vilt ekki að textanum verði breytt skaltu ekki senda hann inn hér.<br />\nÞú lofar okkur einnig að þú hafir skrifað þetta sjálfur, að efnið sé í almannaeigu eða að það heyri undir frjálst leyfi. (sjá $1).\n<strong>EKKI SENDA INN HÖFUNDARRÉTTARVARIРEFNI ÁN LEYFIS RÉTTHAFA!</strong>",
        "postedit-confirmation-created": "Síðan hefur verið búin til.",
        "postedit-confirmation-restored": "Síðan hefur verið endurheimt.",
        "postedit-confirmation-saved": "Breytingin þín hefur verið vistuð.",
+       "postedit-confirmation-published": "Breytingin þín var gefin út.",
        "edit-already-exists": "Gat ekki skapað nýja síðu.\nHún er nú þegar til.",
        "defaultmessagetext": "Sjálfgefinn texti skilaboða",
        "content-failed-to-parse": "Gat ekki þáttað $2 efni samkvæmt $1 líkani: $3",
        "content-model-text": "hreinn texti",
        "content-model-javascript": "JavaScript",
        "content-model-css": "CSS",
+       "content-model-json": "JSON",
        "content-json-empty-object": "Tómur hlutur",
        "content-json-empty-array": "Tómt fylki",
        "duplicate-args-category": "Síður sem nota tvíteknar breytur við ítengingu sniðmáts",
        "history-feed-description": "Breytingaskrá fyrir þessa síðu á wiki-síðunni",
        "history-feed-item-nocomment": "$1 á $2",
        "history-feed-empty": "Síðan sem þú leitaðir að er ekki til.\nMöglegt er að henni hafi verið eytt út af þessari wiki síðu, eða endurnefnd.\nPrófaðu [[Special:Search|að leita á þessari wiki síðu]] að svipuðum síðum.",
+       "history-edit-tags": "Breyta merkjum á völdum breytingaútgáfum",
        "rev-deleted-comment": "(breytingarágrip fjarlægt)",
        "rev-deleted-user": "(notandanafn fjarlægt)",
        "rev-deleted-event": "(smáatriði atriðs fjarlægt)",
        "mergehistory-header": "Þessi síða gerir þér kleift að sameina breytingaskrá tveggja síðna.\nSjáðu til þess að þessi breyting sameini breytingaskrárnar samfellt.",
        "mergehistory-box": "Sameina breytingaskrár tveggja síðna:",
        "mergehistory-from": "Upprunaleg síða:",
-       "mergehistory-into": "Áætlunarsíða:",
+       "mergehistory-into": "Marksíða:",
        "mergehistory-list": "Breytingaskrár sem hægt er að sameina",
        "mergehistory-merge": "Eftirtaldar útgáfur [[:$1]] má sameina [[:$2]].\nNotaðu valreitadálkinn til þess að sameina aðeins þær útgáfur sem stofnaðar voru fyrir uppgefið tímamark.\nAthugaðu að með því að nota flakktenglana er þessi dálkur endurstilltur.",
        "mergehistory-go": "Sýna breytingar sem hægt er að sameina",
        "mergehistory-fail": "Gat ekki sameinað breytingaskrár. Athugaðu vel síðuna og tímabreyturnar.",
        "mergehistory-fail-bad-timestamp": "Tímamerkið er ógilt.",
        "mergehistory-fail-invalid-source": "Frumsíðan er ógild.",
+       "mergehistory-fail-invalid-dest": "Marksíða er ógild.",
+       "mergehistory-fail-self-merge": "Upprunasíðan og marksíðan eru sú sama.",
        "mergehistory-no-source": "Upprunasíðan $1 er ekki til.",
        "mergehistory-no-destination": "Marksíðan $1 er ekki til.",
        "mergehistory-invalid-source": "Upprunasíðan verður að hafa gildan titil.",
        "mergehistory-comment": "Sameinaði [[:$1]] inn í [[:$2]]: $3",
        "mergehistory-same-destination": "Upprunasíðan og marksíðan mega ekki vera sú sama",
        "mergehistory-reason": "Ástæða:",
+       "mergehistory-revisionrow": "$1 ($2) $3 . . $4 $5 $6",
        "mergelog": "Sameiningar skrá",
        "revertmerge": "Taka aftur sameiningu",
        "mergelogpagetext": "Þetta er skrá yfir síðustu sameiningar einnar síðu við aðra.",
        "diff-multi-sameuser": "($1 {{PLURAL:$1|millibreyting ekki sýnd|millibreytingar ekki sýndar}} frá sama notandanum)",
        "diff-multi-otherusers": "($1 {{PLURAL:$1|millibreyting ekki sýnd|millibreytingar ekki sýndar}} frá $2 {{PLURAL:$2|notanda|notendum}})",
        "diff-multi-manyusers": "($1 {{PLURAL:$1|millibreyting ekki sýnd|millibreytingar ekki sýndar}} frá fleiri en $2 {{PLURAL:$2|notanda|notendum}}.)",
+       "diff-paragraph-moved-tonew": "Málsgrein var færð. Smelltu til að hoppa á nýju staðsetninguna.",
+       "diff-paragraph-moved-toold": "Málsgrein var færð. Smelltu til að hoppa á gömlu staðsetninguna.",
        "difference-missing-revision": "$2 {{PLURAL:$2|útgáfa|útgáfur}} samanburðarins ($1) {{PLURAL:$2|fannst|fundust}} ekki.\n\nÞetta gerist oftast þegar úreldur samanburðartengill tengir á síðu sem hefur verið eytt.\nFrekari upplýsingar eru í [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} eyðingarskránni].",
        "searchresults": "Leitarniðurstöður",
        "searchresults-title": "Leitarniðurstöður fyrir „$1“",
        "timezoneregion-pacific": "Kyrrahaf",
        "allowemail": "Leyfa öðrum notendum að senda mér tölvupóst",
        "email-allow-new-users-label": "Leyfa tölvupóst frá nýskráðum notendum",
+       "email-blacklist-label": "Banna þessum notendum að senda mér tölvupóst:",
        "prefs-searchoptions": "Leit",
        "prefs-namespaces": "Nafnrými",
        "default": "sjálfgefið",
        "prefs-files": "Skrár",
        "prefs-custom-css": "Sérsniðið CSS-útlit",
        "prefs-custom-js": "Sérsniðin JavaScript",
-       "prefs-common-css-js": "Sameiginleg CSS/JavaScript fyrir öll skinn:",
+       "prefs-common-config": "Sameiginleg CSS/JavaScript fyrir öll skinn:",
        "prefs-reset-intro": "Þessi síða er til að endurstilla kjörstillingarnar í sjálfgefin gildi.\nEkki er hægt að taka þessa breytingu til baka.",
        "prefs-emailconfirm-label": "Staðfesting netfangs:",
        "youremail": "Netfang:",
        "prefs-diffs": "Breytingar",
        "prefs-help-prefershttps": "Þessi stilling tekur gildi í næsta skiptið sem þú skráir þig inn.",
        "prefswarning-warning": "Þú hefur gert breytingar á kjörstillingum þínum sem ekki er búið að vista.\nEf þú ferð af þessari síðu án þess að smella á \"$1\" verða kjörstillingar þínar ekki uppfærðar.",
+       "prefs-tabs-navigation-hint": "Ábending: Þú getur notað vinstri og hægri örvalyklana til að flakka á milli flipa í flipalistanum.",
        "userrights": "Notandaréttindi",
        "userrights-lookup-user": "Velja notanda",
        "userrights-user-editname": "Skráðu notandanafn:",
        "editusergroup": "Hlaða inn notanda hópum",
        "editinguser": "Breyti réttindum {{GENDER:$1|notandans}} <strong>[[User:$1|$1]]</strong> $2",
+       "viewinguserrights": "Skoða notandaréttindi {{GENDER:$1|notandans}} <strong>[[User:$1|$1]]</strong> $2",
        "userrights-editusergroup": "Breyta hópum {{GENDER:$1|notanda}}",
        "userrights-viewusergroup": "Skoða hópa {{GENDER:$1|notanda}}",
        "saveusergroups": "Vista {{GENDER:$1|notanda}} hópa",
        "userrights-changeable-col": "Hópar sem þú getur breytt",
        "userrights-unchangeable-col": "Hópar sem þú getur ekki breytt",
        "userrights-irreversible-marker": "$1*",
+       "userrights-no-shorten-expiry-marker": "$1#",
        "userrights-expiry-current": "Rennur út $1",
        "userrights-expiry-none": "Rennur ekki út",
        "userrights-expiry": "Rennur út:",
        "right-sendemail": "Senda tölvupóst til annara notenda",
        "right-managechangetags": "Búa til og (af)virkja [[Special:Tags|merki]] úr gagnagrunni",
        "right-applychangetags": "Virkja [[Special:Tags|merki]] ásamt öðrum breytingum",
+       "grant-group-page-interaction": "Gagnvirkni með síður",
+       "grant-group-file-interaction": "Gagnvirkni með gögn",
+       "grant-group-watchlist-interaction": "Gagnvirkni með vaktlistann þinn",
        "grant-group-email": "Senda tölvupóst",
        "grant-group-high-volume": "Framkvæma magnaðgerðir",
        "grant-group-customization": "Sérsníðing og kjörstillingar",
        "action-deletechangetags": "eyða merkjum úr gagnagrunni",
        "action-purge": "hreinsa þessa síðu",
        "nchanges": "$1 {{PLURAL:$1|breyting|breytingar}}",
+       "ntimes": "$1×",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|síðan síðustu heimsókn}}",
        "enhancedrc-history": "breytingaskrá",
        "recentchanges": "Nýlegar breytingar",
        "uploadstash-bad-path-unknown-type": "Óþekkt gerð \"$1\".",
        "uploadstash-bad-path-unrecognized-thumb-name": "Óþekkt heiti á smámynd.",
        "uploadstash-file-not-found-no-thumb": "Gat ekki náð í smámynd.",
+       "uploadstash-zero-length": "Lengd skráar er núll.",
        "invalid-chunk-offset": "Ógild raðbreyting bunka",
        "img-auth-accessdenied": "Aðgangur óheimill",
        "img-auth-nopathinfo": "PATH_INFO vantar.\nBiðlarinn þínn er ekki stilltur til að gefa upp þessar upplýsingar.\nÞær mega vera CGI-byggðar og mega ekki styðja img_auth.\nhttps://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization",
        "apisandbox-deprecated-parameters": "Úrelt viðföng",
        "apisandbox-submit-invalid-fields-title": "Sumir reitir eru ógildir",
        "apisandbox-results": "Niðurstöður",
+       "apisandbox-request-url-label": "Slóð á beiðni:",
+       "apisandbox-request-format-json-label": "JSON",
+       "apisandbox-request-json-label": "JSON beiðni:",
+       "apisandbox-request-time": "Tími beiðnar: {{PLURAL:$1|$1 ms}}",
        "apisandbox-alert-field": "Gildi þessa reits er ekki leyfilegt.",
        "apisandbox-continue": "Halda áfram",
        "apisandbox-continue-clear": "Hreinsa",
        "listgrouprights-namespaceprotection-namespace": "Nafnrými",
        "listgrouprights-namespaceprotection-restrictedto": "Réttindi sem leyfa notanda að breyta",
        "listgrants-rights": "Réttindi",
+       "listgrants-grant-display": "$1 <code>($2)</code>",
        "trackingcategories-name": "Heiti skilaboða",
        "restricted-displaytitle-ignored": "Síður með hunsaða sýnda titla",
        "trackingcategories-nodesc": "Enginn lýsing tiltæk.",
        "revertpage-nouser": "Tók aftur breytingar falins notanda til síðustu útgáfu {{GENDER:$1|[[User:$1|$1]]}}",
        "rollback-success": "Tók til baka breytingar eftir {{GENDER:$3|$1}};\nsetti yfir á síðustu útgáfu eftir {{GENDER:$4|$2}}.",
        "sessionfailure-title": "Mistök í setu",
-       "sessionfailure": "Líklega er vandamál með innskráningarsetuna þína;\nhætt hefur verið við þessa aðgerð sem vörn gegn mögulegu samskiptaráni setunar.\nFarðu aftur á fyrri síðu, endurhladdu hana og reyndu aftur.",
+       "sessionfailure": "Líklega er vandamál með innskráningarsetuna þína;\nhætt hefur verið við þessa aðgerð sem vörn gegn mögulegu samskiptaráni setunar.\nReyndu að senda upplýsingarnar aftur inn.",
        "changecontentmodel-title-label": "Titill síðu",
        "changecontentmodel-reason-label": "Ástæða:",
        "changecontentmodel-submit": "Breyta",
        "movenosubpage": "Þessi síða hefur engar undirsíður.",
        "movereason": "Ástæða:",
        "revertmove": "taka til baka",
-       "delete_and_move_text": "Úttakssíðan „[[:$1]]“ er þegar til. Viltu eyða henni til þess að rýma til fyrir flutningi?",
+       "delete_and_move_text": "Marksíðan „[[:$1]]“ er þegar til. Viltu eyða henni til þess að rýma til fyrir flutningi?",
        "delete_and_move_confirm": "Já, eyða síðunni",
        "delete_and_move_reason": "Eytt til að rýma til fyrir flutning frá \"[[$1]]\"",
        "selfmove": "Nýi titillinn er sá sami og gamli, þú verður að velja annan titil.",
        "immobile-target-namespace": "Get ekki fært síður inn í nafnrýmið „$1“",
        "immobile-target-namespace-iw": "Óheimilt er að færa síðu með tungumálatengli.",
        "immobile-source-page": "Þessi síða er ekki færanleg.",
-       "immobile-target-page": "Get ekki fært Ã¡ Ã¡Ã¦tlaðan titil.",
+       "immobile-target-page": "Get ekki fært Ã¡ Ã¾ennan Ãºttakstitil.",
        "bad-target-model": "Markstaðurinn sem þú valdir notast við annað innihaldslíkan. Get ekki umbreytt frá $1 í $2.",
        "imagenocrossnamespace": "Get ekki fært skrá í skrálaust nafnrými",
        "nonfile-cannot-move-to-file": "Get ekki fært annað en skrár í nafnrými skráa.",
        "thumbnail-dest-create": "Gat ekki vistað smámynd á markstað",
        "thumbnail_invalid_params": "Breytur smámyndarinnar eru rangar",
        "thumbnail_toobigimagearea": "Skrá með málsetningar stærri en $1",
-       "thumbnail_dest_directory": "Mistókst að búa til niðurhals möppu",
+       "thumbnail_dest_directory": "Mistókst að búa til úttaksmöppu",
        "thumbnail_image-type": "Enginn stuðningur er við þetta skráarsnið",
        "thumbnail_gd-library": "Ófullkomin stilling GD-aðgerðasafns: Vantar aðgerðina $1",
        "thumbnail_image-missing": "Skrána virðist vanta: $1",
        "sunday-at": "Sunnudag klukkan $1",
        "yesterday-at": "Í gær klukkan $1",
        "bad_image_list": "Sniðið er eftirfarandi:\n\nAðeins listaeigindi (línur sem byrja á *) eru meðtalin.\nFyrsti tengillinn í hverri línu verður að tengja í slæma skrá.\nAllir síðari tenglar á sömu línu eru taldir vera undantekningar, þ.e. síður þar sem að skráin kann að koma fyrir innfelld.",
+       "variantname-zh-hans": "hans",
+       "variantname-zh-hant": "hant",
+       "variantname-zh-cn": "cn",
+       "variantname-zh-tw": "tw",
+       "variantname-zh-hk": "hk",
+       "variantname-zh-mo": "mo",
+       "variantname-zh-sg": "sg",
+       "variantname-zh-my": "my",
+       "variantname-zh": "zh",
+       "variantname-gan-hans": "hans",
+       "variantname-gan-hant": "hant",
+       "variantname-gan": "gan",
+       "variantname-sr-ec": "sr-ec",
+       "variantname-sr-el": "sr-el",
+       "variantname-sr": "sr",
+       "variantname-kk-kz": "kk-kz",
+       "variantname-kk-tr": "kk-tr",
+       "variantname-kk-cn": "kk-cn",
+       "variantname-kk-cyrl": "kk-cyrl",
+       "variantname-kk-latn": "kk-latn",
+       "variantname-kk-arab": "kk-arab",
+       "variantname-kk": "kk",
+       "variantname-ku-arab": "ku-Arab",
+       "variantname-ku-latn": "ku-Latn",
+       "variantname-ku": "ku",
+       "variantname-tg-cyrl": "tg-Cyrl",
+       "variantname-tg-latn": "tg-Latn",
+       "variantname-tg": "tg",
+       "variantname-ike-cans": "ike-Cans",
+       "variantname-ike-latn": "ike-Latn",
+       "variantname-iu": "iu",
+       "variantname-shi-tfng": "shi-Tfng",
+       "variantname-shi-latn": "shi-Latn",
+       "variantname-shi": "shi",
+       "variantname-uz": "uz",
+       "variantname-uz-latn": "uz-Latn",
+       "variantname-uz-cyrl": "uz-Cyrl",
+       "variantname-crh": "crh",
+       "variantname-crh-latn": "crh-Latn",
+       "variantname-crh-cyrl": "crh-Cyrl",
        "metadata": "Lýsigögn",
        "metadata-help": "Þessi skrá inniheldur viðbótarupplýsingar, líklega frá stafrænu myndavélinni eða skannanum sem notaður var til að gera eða stafræna hana.\nEf skránni hefur verið breytt, kann að vera að einhverjar upplýsingar eigi ekki við um hana.",
        "metadata-expand": "Sýna frekari upplýsingar",
        "exif-gpstrack": "Átt hreyfingar",
        "exif-gpsimgdirection": "Stefna myndarinnar",
        "exif-gpsmapdatum": "Landmælingagögn",
+       "exif-gpsdestlatituderef": "Tilvísun breiddargráðu áfangastaðar",
        "exif-gpsdestlatitude": "Breiddargráða áfangastaðar",
+       "exif-gpsdestlongituderef": "Tilvísun lengdargráðu áfangastaðar",
        "exif-gpsdestlongitude": "Lengdargráða áfangastaðar",
+       "exif-gpsdestbearingref": "Tilvísun stefnu áfangastaðar",
        "exif-gpsdestbearing": "Stefna til áfangastaðar",
+       "exif-gpsdestdistanceref": "Tilvísun fyrir fjarlægð á áfangastað",
        "exif-gpsdestdistance": "Fjarlægð á áfangastað",
        "exif-gpsprocessingmethod": "GPS vinnsluaðferð",
        "exif-gpsareainformation": "Heiti GPS-svæðis",
        "version-libraries-description": "Lýsing",
        "version-libraries-authors": "Höfundar",
        "redirect": "Endurbeining miðað við skrá, notanda, síðu, útgáfu eða innskráningarauðkenni",
+       "redirect-summary": "Þessi kerfissíða endurbeinir á skrá (sé uppgefið skráarheiti), síðu (sé uppgefið auðkenni útgáfu eða auðkenni síðu), notandasíðu (sé uppgefið tölulegt gildi notanda), eða færslu í aðgerðaskrá (sé uppgefið auðkenni færslu). Notkun: [[{{#Special:Redirect}}/file/Dæmi.jpg]], [[{{#Special:Redirect}}/page/64308]], [[{{#Special:Redirect}}/revision/328429]], [[{{#Special:Redirect}}/user/101]], or [[{{#Special:Redirect}}/logid/186]].",
        "redirect-submit": "Áfram",
        "redirect-lookup": "Fletta upp:",
        "redirect-value": "Gildi:",
        "tag-filter": "[[Special:Tags|Merkja]]sía:",
        "tag-filter-submit": "Sía",
        "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|Merki}}]]: $2)",
+       "tag-mw-new-redirect": "Ný endurbeining",
+       "tag-mw-removed-redirect": "Fjarlægði endurbeiningu",
        "tag-mw-replace": "Skipt út",
        "tag-mw-undo": "Afturkalla",
        "tags-title": "Merki",
        "mediastatistics-header-text": "Textar",
        "mediastatistics-header-executable": "Uppsetninga skrár",
        "mediastatistics-header-archive": "Þjappaðar skrár",
+       "mediastatistics-header-3d": "3D",
        "mediastatistics-header-total": "Allar skrár",
        "json-warn-trailing-comma": "$1 eftirfylgjandi {{PLURAL:$1|komma var fjarlægð|kommur voru fjarlægðar}} úr JSON",
        "json-error-unknown": "Það varð villa í JSON. Villa: $1",
+       "json-error-depth": "Hámarki stafladýptar hefur verið náð",
        "json-error-state-mismatch": "Ógild eða ranglega uppsett JSON",
        "json-error-ctrl-char": "Villa í stýritákni, hugsanlega röng stafatafla (encoding)",
        "json-error-syntax": "Formvilla í málsetningu",
        "sessionprovider-nocookies": "Vefkökur gætu verið óvirkar. Gakktu úr skugga um að smákökur séu virkar og byrjaðu svo aftur.",
        "randomrootpage": "Handahófsvalin rótarsíða",
        "log-action-filter-all": "Allt",
-       "log-action-filter-delete-delete": "Eyðing síðu"
+       "log-action-filter-delete-delete": "Eyðing síðu",
+       "authmanager-email-label": "Tölvupóstur",
+       "authmanager-email-help": "Tölvupóstfang",
+       "authmanager-realname-label": "Raunverulegt nafn",
+       "authmanager-realname-help": "Raunverulegt nafn notandans",
+       "authmanager-provider-password": "Auðkenning með lykilorði",
+       "authmanager-provider-password-domain": "Auðkenning með lykilorði og léni",
+       "authmanager-provider-temporarypassword": "Bráðabirgðalykilorð",
+       "authprovider-confirmlink-option": "$1 ($2)",
+       "authprovider-confirmlink-request-label": "Aðgangar sem ættu að tengjast",
+       "authprovider-confirmlink-success-line": "$1: Tókst að tengja.",
+       "authprovider-confirmlink-failed-line": "$1: $2",
+       "authprovider-resetpass-skip-label": "Sleppa",
+       "authprovider-resetpass-skip-help": "Sleppa að endursetja lykilorð.",
+       "authform-newtoken": "Teikn vantar. $1",
+       "authform-notoken": "Teikn vantar",
+       "authform-wrongtoken": "Röng teikn",
+       "specialpage-securitylevel-not-allowed-title": "Ekki leyft",
+       "cannotauth-not-allowed-title": "Heimild hafnað",
+       "cannotauth-not-allowed": "Þú hefur ekki heimild til að nota þessa síðu",
+       "changecredentials": "Breyta auðkennum",
+       "changecredentials-submit": "Breyta auðkennum",
+       "credentialsform-provider": "Gerð auðkenna:",
+       "credentialsform-account": "Heiti aðgangs:",
+       "linkaccounts": "Tengja aðganga",
+       "linkaccounts-success-text": "Notandaaðgangurinn var tengdur.",
+       "linkaccounts-submit": "Tengja aðganga",
+       "unlinkaccounts": "Aftengja aðganga",
+       "unlinkaccounts-success": "Notandaaðgangurinn var aftengdur.",
+       "restrictionsfield-badip": "Ógilt IP-vistfang eða vistfangasvið: $1",
+       "restrictionsfield-label": "Leyfð svið IP-vistfanga:",
+       "edit-error-short": "Villa: $1",
+       "edit-error-long": "Villur: \n\n$1",
+       "revid": "útgáfa $1",
+       "pageid": "auðkennisnúmer síðu $1",
+       "gotointerwiki": "Fer af {{SITENAME}}",
+       "pagedata-title": "Síðugögn",
+       "pagedata-bad-title": "Ógildur titill: $1."
 }
index 73568c1..621d4d2 100644 (file)
                        "Pierpao",
                        "Sakretsu",
                        "Yiyi",
-                       "Manvydasz"
+                       "Manvydasz",
+                       "S4b1nuz E.656"
                ]
        },
        "tog-underline": "Sottolinea i collegamenti:",
        "userjspreview": "'''Questa è solo un'anteprima per provare il proprio JavaScript personale; le modifiche non sono ancora state salvate!'''",
        "sitecsspreview": "Questa è solo un'anteprima del CSS. Le modifiche non sono ancora state salvate!'''",
        "sitejspreview": "Questa è solo un'anteprima per provare il JavaScript; le modifiche non sono ancora state salvate!'''",
-       "userinvalidcssjstitle": "<strong>Attenzione:</strong> non esiste alcun tema con nome \"$1\". Si noti che le pagine per i .css e .js personalizzati hanno l'iniziale del titolo minuscola, ad esempio {{ns:user}}:Esempio/vector.css e non {{ns:user}}:Esempio/Vector.css.",
+       "userinvalidconfigtitle": "<strong>Attenzione:</strong> non esiste alcun tema con nome \"$1\". Si noti che le pagine per i .css e .js personalizzati hanno l'iniziale del titolo minuscola, ad esempio {{ns:user}}:Esempio/vector.css e non {{ns:user}}:Esempio/Vector.css.",
        "updated": "(Aggiornato)",
        "note": "'''Nota:'''",
        "previewnote": "'''Ricorda che questa è solo un'anteprima.'''\nLe tue modifiche NON sono ancora state salvate!",
        "timezoneregion-indian": "Oceano Indiano",
        "timezoneregion-pacific": "Oceano Pacifico",
        "allowemail": "Consenti ad altri utenti di inviarmi email",
+       "email-allow-new-users-label": "Consenti email da nuovi utenti",
        "email-blacklist-label": "Impedisci a questi utenti di inviarmi email:",
        "prefs-searchoptions": "Ricerca",
        "prefs-namespaces": "Namespace",
        "prefs-files": "File",
        "prefs-custom-css": "CSS personalizzato",
        "prefs-custom-js": "JavaScript personalizzato",
-       "prefs-common-css-js": "CSS/JavaScript condiviso per tutti i temi:",
+       "prefs-common-config": "CSS/JavaScript condiviso per tutti i temi:",
        "prefs-reset-intro": "È possibile usare questa pagina per reimpostare le proprie preferenze a quelle predefinite del sito.\nL'operazione non può essere annullata.",
        "prefs-emailconfirm-label": "Conferma dell'email:",
        "youremail": "Indirizzo email:",
        "recentchanges-legend": "Opzioni ultime modifiche",
        "recentchanges-summary": "Questa pagina presenta le modifiche più recenti ai contenuti del sito.",
        "recentchanges-noresult": "Nessuna modifica durante il periodo inserito che soddisfa questi criteri.",
+       "recentchanges-timeout": "Questa ricerca è scaduta. Potresti voler provare diversi parametri di ricerca.",
        "recentchanges-network": "A causa di un errore tecnico, non è possibile caricare alcun risultato. Aggiorna la pagina.",
        "recentchanges-notargetpage": "Inserisci sopra il nome di una pagina per vedere le modifiche relative a quella pagina.",
        "recentchanges-feed-description": "Questo feed riporta le modifiche più recenti ai contenuti del sito.",
        "rcfilters-view-namespaces-tooltip": "Filtra risultati per namespace",
        "rcfilters-view-tags-tooltip": "Filtra risultati per etichette di modifica",
        "rcfilters-view-return-to-default-tooltip": "Torna al menu filtri principale",
+       "rcfilters-view-tags-help-icon-tooltip": "Ulteriori informazioni sulle modifiche etichettate",
        "rcfilters-liveupdates-button": "Aggiornamenti in tempo reale",
        "rcfilters-liveupdates-button-title-on": "Disabilita gli aggiornamenti in tempo reale",
        "rcfilters-liveupdates-button-title-off": "Mostra le nuove modifiche appena avvengono",
        "thumbnail_dest_directory": "Impossibile creare la directory di destinazione",
        "thumbnail_image-type": "Tipo di immagine non supportato",
        "thumbnail_gd-library": "Configurazione incompleta della libreria GD: funzione $1 mancante",
+       "thumbnail_image-size-zero": "La dimensione del file di immagine sembra essere zero.",
        "thumbnail_image-missing": "Sembra essere mancante il file: $1",
        "thumbnail_image-failure-limit": "Ci sono stati recentemente troppi tentativi falliti ($1 o più) di generare questa miniatura. Riprova più tardi.",
        "import": "Importa pagine",
        "confirmrecreate": "L'utente [[User:$1|$1]] ([[User talk:$1|discussioni]]) ha cancellato questa pagina dopo che hai iniziato a modificarla, per il seguente motivo: ''$2''\nPer favore, conferma che vuoi veramente ricreare questa pagina.",
        "confirmrecreate-noreason": "L'utente [[User:$1|$1]] ([[User talk:$1|discussioni]]) {{GENDER:$1|ha cancellato}} questa pagina dopo che hai iniziato a modificarla. Per favore, conferma che vuoi veramente ricreare questa pagina.",
        "recreate": "Ricrea",
+       "confirm-purge-title": "Aggiorna questa pagina",
        "confirm_purge_button": "OK",
        "confirm-purge-top": "Vuoi pulire la cache di questa pagina?",
        "confirm-purge-bottom": "Ripulire la cache di una pagina consente di mostrare la sua versione più aggiornata.",
        "pagelang-reason": "Motivo",
        "pagelang-submit": "Invia",
        "pagelang-nonexistent-page": "La pagina $1 non esiste.",
+       "pagelang-unchanged-language": "La pagina $1 è già impostata alla lingua $2.",
+       "pagelang-unchanged-language-default": "La pagina $1 è gia impostata alla lingua del contenuto predefinito del wiki.",
        "pagelang-db-failed": "Il database non è stato in grado di modificare la lingua della pagina.",
        "right-pagelang": "Modifica la lingua della pagina",
        "action-pagelang": "modificare la lingua della pagina",
index 0381d19..a9f1877 100644 (file)
        "userjspreview": "<strong>利用者JavaScriptを試験/プレビューしているだけに過ぎません。</strong>\n<strong>まだ保存されていません!</strong>",
        "sitecsspreview": "<strong>ここでは、CSSをプレビューしているだけに過ぎません。</strong>\n<strong>まだ保存されていません!</strong>",
        "sitejspreview": "<strong>ここでは、JavaScriptをプレビューしているだけに過ぎません。</strong>\n<strong>まだ保存されていません!</strong>",
-       "userinvalidcssjstitle": "<strong>警告:</strong>「$1」という外装はありません。\nカスタム .css/.js ページではページ名を小文字にしてください。例: {{ns:user}}:Hoge/Vector.css ではなく {{ns:user}}:Hoge/vector.css",
+       "userinvalidconfigtitle": "<strong>警告:</strong>「$1」という外装はありません。\nカスタム .css/.js ページではページ名を小文字にしてください。例: {{ns:user}}:Hoge/Vector.css ではなく {{ns:user}}:Hoge/vector.css",
        "updated": "(更新)",
        "note": "<strong>お知らせ:</strong>",
        "previewnote": "<strong>これはプレビューです。</strong>\n変更内容はまだ保存されていません!",
        "postedit-confirmation-created": "ページを作成しました。",
        "postedit-confirmation-restored": "ページを復元しました。",
        "postedit-confirmation-saved": "編集を保存しました。",
+       "postedit-confirmation-published": "あなたの編集は公開されました。",
        "edit-already-exists": "新しいページを作成できませんでした。\nそのページは既に存在します。",
        "defaultmessagetext": "既定のメッセージ文",
        "content-failed-to-parse": "$2のコンテンツを$1モデルとして構文解析できませんでした: $3",
        "prefs-files": "ファイル",
        "prefs-custom-css": "カスタムCSS",
        "prefs-custom-js": "カスタムJavaScript",
-       "prefs-common-css-js": "すべての外装に共通のCSSとJavaScript:",
+       "prefs-common-config": "すべての外装に共通のCSSとJavaScript:",
        "prefs-reset-intro": "このページを使用すると、自分の個人設定をこのサイトの初期設定に戻せます。\nこの操作は取り消せません。",
        "prefs-emailconfirm-label": "メールアドレスの確認:",
        "youremail": "メールアドレス:",
        "rcfilters-preference-label": "最近の更新の改善版を隠す",
        "rcfilters-preference-help": "2017年のインターフェース更新、当時追加したや以来の新しいツールの使用を断る。",
        "rcfilters-filter-showlinkedfrom-label": "リンク先ページの変更を表示する",
+       "rcfilters-filter-showlinkedfrom-option-label": "選択されているページから<strong>リンクされているページ</strong>",
+       "rcfilters-filter-showlinkedto-label": "リンク先ページの変更を表示する",
+       "rcfilters-filter-showlinkedto-option-label": "選択されているページに<strong>リンクしているページ</strong>",
        "rcfilters-target-page-placeholder": "ページ名(またはカテゴリ名)を入力",
        "rcnotefrom": "以下は<strong>$3 $4</strong>以降の{{PLURAL:$5|更新です}} (最大 <strong>$1</strong> 件)。",
        "rclistfromreset": "日時指定をリセット",
        "uploadbtn": "ファイルをアップロード",
        "reuploaddesc": "アップロードを中止してアップロードフォームへ戻る",
        "upload-tryagain": "修正したファイル解説を投稿",
+       "upload-tryagain-nostash": "再アップロードされたファイルと変更された説明の送信",
        "uploadnologin": "ログインしていません",
        "uploadnologintext": "ファイルをアップロードするには$1してください。",
        "upload_directory_missing": "アップロード先ディレクトリ ($1) が見つかりませんでした。ウェブ サーバーによる作成もできませんでした。",
        "file-deleted-duplicate-notitle": "このファイルと同一のファイルが、以前に削除されページ名が秘匿されました。\n再度アップロードをする前に、秘匿されたファイルのデータを閲覧する権限を持った利用者に連絡して、状況を精査してもらってください。",
        "uploadwarning": "アップロード警告",
        "uploadwarning-text": "下記のファイル解説を修正して再試行してください。",
+       "uploadwarning-text-nostash": "ファイルを再アップロードし、下の説明を変更してもう一度お試しください。",
        "savefile": "ファイルを保存",
        "uploaddisabled": "アップロード機能は無効になっています。",
        "copyuploaddisabled": "URL からのアップロードは無効になっています。",
        "uploadstash-bad-path-unrecognized-thumb-name": "サムネイル名が認識できません。",
        "uploadstash-bad-path-no-handler": "ファイル $2 の MIME $1 に対するハンドラが見つかりません。",
        "uploadstash-bad-path-bad-format": "キー「$1」は適切な形式ではありません。",
+       "uploadstash-file-not-found-no-thumb": "サムネイルを取得できませんでした。",
+       "uploadstash-file-not-found-no-local-path": "スケーリングされた項目のローカルパスはありません。",
+       "uploadstash-file-not-found-no-object": "サムネイルのローカルファイルオブジェクトを作成できませんでした。",
+       "uploadstash-file-not-found-no-remote-thumb": "サムネイルの取得に失敗しました:$1\nURL = $2",
+       "uploadstash-file-not-found-missing-content-type": "コンテンツタイプヘッダーがありません。",
+       "uploadstash-file-not-found-not-exists": "パスが見つからないか、またはプレーンファイルではありません。",
+       "uploadstash-file-too-large": "$1バイトを超えるファイルは提供できません。",
+       "uploadstash-not-logged-in": "ユーザーはログインしていません。ファイルはユーザーに属している必要があります。",
+       "uploadstash-wrong-owner": "このファイル($1)は現在のユーザーに属していません。",
+       "uploadstash-no-such-key": "そのような鍵($1)は削除できません。",
+       "uploadstash-no-extension": "拡張機能がnullです。",
        "uploadstash-zero-length": "ファイルのサイズがゼロです。",
        "invalid-chunk-offset": "無効なチャンクオフセット",
        "img-auth-accessdenied": "アクセスが拒否されました",
        "pageswithprop-legend": "ページプロパティがあるページ",
        "pageswithprop-text": "このページでは、特定のページプロパティを持つページを列挙します。",
        "pageswithprop-prop": "プロパティ名:",
+       "pageswithprop-reverse": "逆順にソートする",
+       "pageswithprop-sortbyvalue": "プロパティの値でソート",
        "pageswithprop-submit": "実行",
        "pageswithprop-prophidden-long": "プロパティ値のテキストが長いため非表示 ($1)",
        "pageswithprop-prophidden-binary": "プロパティ値のバイナリが長いため非表示 ($1)",
        "apisandbox-sending-request": "API要求を送信中...",
        "apisandbox-loading-results": "API結果を受信中...",
        "apisandbox-results-error": "API 問い合わせの応答を読み込み中にエラーが発生しました: $1。",
+       "apisandbox-results-login-suppressed": "このリクエストは、ブラウザの同じOriginのセキュリティをバイパスするために使用されるため、ログアウトしたユーザーとして処理されています。 APIサンドボックスの自動トークン処理は、このようなリクエストでは正しく機能しないため、手動で入力してください。",
        "apisandbox-request-selectformat-label": "リクエストデータを次の形式で表示",
        "apisandbox-request-format-url-label": "URLクエリ文字列",
        "apisandbox-request-url-label": "リクエスト URL:",
        "apisandbox-alert-field": "この欄の値が有効ではありません。",
        "apisandbox-continue": "続行",
        "apisandbox-continue-clear": "消去",
+       "apisandbox-continue-help": "{{int:apisandbox-continue}}は最後のリクエストを[https://www.mediawiki.org/wiki/API:Query#Continuing_queries 継続]します。{{int:apisandbox-continue-clear}}は、継続関連のパラメータをクリアします。",
        "apisandbox-param-limit": "最大限度を利用するには<kbd>max</kbd>と入力してください。",
        "apisandbox-multivalue-all-namespaces": "$1 (全ての名前空間)",
        "apisandbox-multivalue-all-values": "$1 (全ての値)",
        "delete-warning-toobig": "このページには、 $1版を超える編集履歴があります。\n削除すると、{{SITENAME}}のデータベース処理に大きな負荷がかかります。\n十分に注意してください。",
        "deleteprotected": "このページは保護されているため削除できません。",
        "deleting-backlinks-warning": "<strong>警告:</strong> 削除しようとしているページは、[[Special:WhatLinksHere/{{FULLPAGENAME}}|他のページ]]からリンクまたは参照読み込みされています。",
+       "deleting-subpages-warning": "<strong>警告:</strong>削除しようとしているページは、[[Special:PrefixIndex/{{FULLPAGENAME}}/|{{PLURAL:$1|サブページ|$1 個のサブページ|51=50 個以上のサブページ}}]]があります。",
        "rollback": "編集を巻き戻し",
        "rollbacklink": "巻き戻し",
        "rollbacklinkcount": "$1{{PLURAL:$1|編集}}を巻き戻し",
        "rollback-success": "{{GENDER:$3|$1}}による編集を差し戻しました。\n{{GENDER:$4|$2}}による直前の版へ変更されました。",
        "rollback-success-notify": "$1による編集を差し戻しました。\n$2による直前の版へ変更されました。[$3 変更を表示]",
        "sessionfailure-title": "セッションの失敗",
-       "sessionfailure": "ログインのセッションに問題が発生しました。\nセッション乗っ取りを防ぐため、操作を取り消しました。\n前のページへ戻って再度読み込んだ後に、もう一度試してください。",
+       "sessionfailure": "ログインのセッションに問題が発生しました。\nセッション乗っ取りを防ぐため、操作を取り消しました。\nフォームを再送信してください。",
        "changecontentmodel": "ページのコンテンツ・モデルの変更",
        "changecontentmodel-legend": "コンテンツモデルを変更",
        "changecontentmodel-title-label": "ページ名",
        "undelete-search-title": "削除されたページの検索",
        "undelete-search-box": "削除されたページの検索",
        "undelete-search-prefix": "表示を開始するページ名:",
+       "undelete-search-full": "次の内容を含むページタイトルを表示する:",
        "undelete-search-submit": "検索",
        "undelete-no-results": "削除の保存版に、一致するページが見つかりませんでした。",
        "undelete-filename-mismatch": "ファイルの $1 時点の版を復元できません: ファイル名が一致しません。",
        "ipb_blocked_as_range": "エラー: IPアドレス$1は直接ブロックされておらず、ブロック解除できませんでした。\nただし、$2の範囲でブロックされており、こちらのブロックは別途解除できます。",
        "ip_range_invalid": "IP範囲が無効です。",
        "ip_range_toolarge": "/$1より広範囲の範囲ブロックは許可されていません。",
+       "ip_range_exceeded": "IP範囲が最大範囲を超えています。許可される範囲:/$1",
+       "ip_range_toolow": "IP範囲は事実上許可されません。",
        "proxyblocker": "プロキシブロック係",
        "proxyblockreason": "このIPアドレスは公開プロキシであるためブロックされています。\nご使用中のインターネットサービスプロバイダーまたは所属組織の技術担当者に連絡して、これが深刻なセキュリティ問題であることを伝えてください。",
        "sorbs": "DNSBL",
        "thumbnail_dest_directory": "出力ディレクトリを作成できません",
        "thumbnail_image-type": "対応していない画像形式です",
        "thumbnail_gd-library": "GDライブラリの構成が不完全です: 関数$1が不足",
+       "thumbnail_image-size-zero": "イメージファイルのサイズがゼロのようです。",
        "thumbnail_image-missing": "ファイルが見つかりません: $1",
        "thumbnail_image-failure-limit": "このサムネイルの描画に失敗した回数($1 回以上)が上限を超えました。しばらく後でもう一度お試しください。",
        "import": "ページデータの取り込み",
        "import-mapping-subpage": "次のページの下位ページとしてインポート:",
        "import-upload-filename": "ファイル名:",
        "import-upload-username-prefix": "インターウィキ接頭辞:",
+       "import-assign-known-users": "指定されたユーザーがこのウィキに存在する場合そのユーザーに編集を割り当てる",
        "import-comment": "コメント:",
        "importtext": "元のウィキで[[Special:Export|書き出し機能]]を使用してファイルに書き出してください。\nそれをコンピューターに保存した後、こちらへアップロードしてください。",
        "importstart": "ページを取り込み中...",
        "imported-log-entries": "$1 件の{{PLURAL:$1|記録項目}}を取り込みました。",
        "importfailed": "取り込みに失敗しました: <nowiki>$1</nowiki>",
        "importunknownsource": "取り込み元のタイプが不明です",
+       "importnoprefix": "インターウィキ接頭辞が記入されていません",
        "importcantopen": "取り込みファイルが開けませんでした",
        "importbadinterwiki": "ウィキ間リンクが正しくありません",
        "importsuccess": "取り込みが完了しました!",
        "size-kilobytes": "$1キロバイト",
        "size-megabytes": "$1メガバイト",
        "size-gigabytes": "$1ギガバイト",
-       "size-terabytes": "$1 TB",
-       "size-petabytes": "$1 PB",
-       "size-exabytes": "$1 EB",
-       "size-zetabytes": "$1 ZB",
-       "size-yottabytes": "$1 YB",
+       "size-terabytes": "$1テラバイト",
+       "size-petabytes": "$1ペタバイト",
+       "size-exabytes": "$1エクサバイト",
+       "size-zetabytes": "$1ゼタバイト",
+       "size-yottabytes": "$1ヨタバイト",
        "size-pixel": "$1{{PLURAL:$1|ピクセル}}",
        "size-kilopixel": "$1キロピクセル",
        "size-megapixel": "$1メガピクセル",
        "watchlistedit-clear-titles": "ページ名:",
        "watchlistedit-clear-submit": "ウォッチリストの全消去 (この操作は取り消せません!)",
        "watchlistedit-clear-done": "ウォッチリストを全消去しました。",
+       "watchlistedit-clear-jobqueue": "あなたのウォッチリストはクリアされています。 これには時間がかかることがあります。",
        "watchlistedit-clear-removed": "{{PLURAL:$1|$1 件のページ}}を除去しました:",
        "watchlistedit-too-many": "ページ数が多すぎるため表示できません。",
        "watchlisttools-clear": "ウォッチリストの全消去",
index f7a4fd1..198dd1d 100644 (file)
        "userjsyoucanpreview": "'''Tip:''' Yuuz di \"{{int:showpreview}}\" botn fi tes yu nyuu JavaScript bifuo yu sieb.",
        "usercsspreview": "'''Memba se yu onggl a priivyuu yu yuuza CSS.'''\n'''Ino sieb yet!'''",
        "userjspreview": "'''Memba se yu onggl a tes/priivyuu yu yuuza JavaScript.'''\n'''Ino sieb yet!'''",
-       "userinvalidcssjstitle": "'''Waanin:''' No skin \"$1\" no de.\nMemba se kostom .css ahn .js piej yuuz a luwakies taikl, e.g. {{ns:user}}:Foo/vector.css az opuoz tu {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "'''Waanin:''' No skin \"$1\" no de.\nMemba se kostom .css ahn .js piej yuuz a luwakies taikl, e.g. {{ns:user}}:Foo/vector.css az opuoz tu {{ns:user}}:Foo/Vector.css.",
        "updated": "(Opdiet)",
        "note": "'''Nuot:'''",
        "previewnote": "'''Memb se dis a onggl priivyuu.'''\nYu chienjdem no sieb yet!",
index 37b61f1..82ace80 100644 (file)
        "prefs-files": "File",
        "prefs-custom-css": "Personli CSS",
        "prefs-custom-js": "Personli JavaScript",
-       "prefs-common-css-js": "Fælls CSS/JS for åll utsienje:",
+       "prefs-common-config": "Fælls CSS/JS for åll utsienje:",
        "prefs-reset-intro": "Du kan brug siden te å tebagstell åll din instellenge te standardinstellenger.\nDä kan ett djendjörs.",
        "prefs-emailconfirm-label": "Bekräftels å e-mail:",
        "youremail": "E-mail:",
index b67539c..2307512 100644 (file)
        "userjspreview": "'''Pèngeten yèn sing panjenengan pirsani namung pratilik JavaScript panjenengan, lan menawa pratilik iku dèrèng kasimpen!'''",
        "sitecsspreview": "<strong>Élinga yèn Sampéyan mung mratuduh CSS iki.\nIki durung kasimpen!</strong>",
        "sitejspreview": "<strong>Élinga yèn Sampéyan mung mratuduh kodhé JavaScript iki.\nIki durung kasimpen!</strong>",
-       "userinvalidcssjstitle": "<strong>Pènget:</strong> Ora ana ules \"$1\".\nKaca .css lan .js padatan nganggo sesirah mawa huruf cilik, contoné {{ns:user}}:Foo/vector.css, dudu {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "<strong>Pènget:</strong> Ora ana ules \"$1\".\nKaca .css lan .js padatan nganggo sesirah mawa huruf cilik, contoné {{ns:user}}:Foo/vector.css, dudu {{ns:user}}:Foo/Vector.css.",
        "updated": "(Kaanyaran)",
        "note": "<strong>Cathetan:</strong>",
        "previewnote": "<strong>Élinga yèn iki mung pratuduh.</strong>\nOwahanmu durung kasimpen!",
        "prefs-files": "Barkas",
        "prefs-custom-css": "CSS priangga",
        "prefs-custom-js": "JavaScript priangga",
-       "prefs-common-css-js": "CSS/JS barengan kabèh ules:",
+       "prefs-common-config": "CSS/JS barengan kabèh ules:",
        "prefs-reset-intro": "Panjenengan bisa migunakaké kaca iki kanggo mbalèkaké préferensi panjenengan marang setèlan baku situs.\nPembalikan ora bisa dibatalaké.",
        "prefs-emailconfirm-label": "Konfirmasi layang-èl:",
        "youremail": "Layang-èl:",
index 58fc69d..12d4f00 100644 (file)
        "userjspreview": "'''გახსოვდეთ, რომ თქვენ მხოლოდ ტესტირებას უკეთებთ ან აკვირდებით წინასწარ ხედს თქვენი მომხმარებლის ჯავასკრიპტს - ის ჯერ არ არის დამახსოვრებული!'''",
        "sitecsspreview": "'''გახსოვდეთ, რომ თქვენ ამ CSS-ის მხოლოდ წინასწარ გადახედვას უყურებთ.'''\n'''ის ჯერ არ არის დამახსოვრებული!'''",
        "sitejspreview": "'''გახსოვდეთ, რომ თქვენ ამ JavaScript კოდის მხოლოდ წინასწარ გადახედვას უყურებთ.'''\n'''ის ჯერ არ არის დამახსოვრებული!'''",
-       "userinvalidcssjstitle": "'''ყურადღება:''' გაფორმების თემა «$1» არ არის ნაპოვნი. გახსოვდეთ, რომ .css და .js გვერდებს უნდა ჰქონდეს მხოლოდ ზეხაზური სათაური, მაგ. «{{ns:user}}:ვიღაცა/vector.css», და არა «{{ns:user}}:ვიღაცა/Vector.css».",
+       "userinvalidconfigtitle": "'''ყურადღება:''' გაფორმების თემა «$1» არ არის ნაპოვნი. გახსოვდეთ, რომ .css და .js გვერდებს უნდა ჰქონდეს მხოლოდ ზეხაზური სათაური, მაგ. «{{ns:user}}:ვიღაცა/vector.css», და არა «{{ns:user}}:ვიღაცა/Vector.css».",
        "updated": "(განახლდა)",
        "note": "'''შენიშვნა:'''",
        "previewnote": "'''დაიმახსოვრეთ, ეს მხოლოდ წინასწარი გადახედვაა.'''\nთქვენი ცვლილებები ჯერ არ შენახულა!",
        "prefs-files": "ფაილები",
        "prefs-custom-css": "მომხმარებლის CSS",
        "prefs-custom-js": "მომხმარებლის JS",
-       "prefs-common-css-js": "ზოგადი CSS/JS ყველა თემისთვის:",
+       "prefs-common-config": "ზოგადი CSS/JS ყველა თემისთვის:",
        "prefs-reset-intro": "ეს გვერდი შეიძლება გამოყენებული იქნეს თქვენი კონფიგურაციის შესაცვლელად საწყის კონფიგურაციაზე. ამ მოქმედების დადასტურების შემთხვევაში, თქვენ ვეღარ შეძლებთ მის გაუქმებას.",
        "prefs-emailconfirm-label": "ელ-ფოსტის დადასტურება:",
        "youremail": "ელექტრონული ფოსტა:",
index e47ef2c..79e1c1a 100644 (file)
        "userjspreview": "'''Smekti belli aql-ak tɛerḍeḍ JavaScript inek kan, mazal ur yettusmekti ara!'''",
        "sitecsspreview": "'''Smekti belli aql-ak tɛerḍeḍ asebter CSS agi inek kan.'''\n'''Mazal ur yettusmekti ara!'''",
        "sitejspreview": "'''Smekti belli aql-ak tɛerḍeḍ angal agi JavaScript inek kan.'''\n'''Mazal ur yettusmekti ara!'''",
-       "userinvalidcssjstitle": "'''Aɣtal:''' Aglim \"$1\" ulac-it. Ur tettuḍ ara belli isebtar \".css\" d \".js\" i txedmeḍ sseqdacen azwel i yesɛan isekkilen imecṭuḥen, s umedya: {{ns:user}}:Foo/vector.css akk d {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "'''Aɣtal:''' Aglim \"$1\" ulac-it. Ur tettuḍ ara belli isebtar \".css\" d \".js\" i txedmeḍ sseqdacen azwel i yesɛan isekkilen imecṭuḥen, s umedya: {{ns:user}}:Foo/vector.css akk d {{ns:user}}:Foo/Vector.css.",
        "updated": "(Yettubeddel)",
        "note": "'''Tamawt:'''",
        "previewnote": "'''Ttagi d azar-timeẓriwt kan, ibeddlen mazal ur ttusmektin ara!'''\n\n'''Cfut, ttagi d azar-timeẓriwt kan.'''\nIbeddlen mazal ur ttusmektin ara!",
        "prefs-files": "Ifayluwen",
        "prefs-custom-css": "CSS asagen",
        "prefs-custom-js": "JavaScript asagen",
-       "prefs-common-css-js": "JavaScript  d CSS azduklan i akkw lebsa :",
+       "prefs-common-config": "JavaScript  d CSS azduklan i akkw lebsa :",
        "prefs-reset-intro": "Tzemreḍ ad seqdeceḍ asebter agi iwakken ad erreḍ iɣewwaren inek/inem ar azalen n lexṣas n usmel.\nWagi ur yezmer ara ad yetwekkes.",
        "prefs-emailconfirm-label": "Aragag n tirawt :",
        "youremail": "E-mail *:",
index 0c29faf..14b3f39 100644 (file)
        "userjspreview": "'''Гу лъытэ, мыр япэ-еплъ къуэдейщ уи javascript-теплъэм, иджыри итхауэ щыткъым!'''",
        "sitecsspreview": "'''Зыщумгъэгъупшэ, мыр япэ-еплъ къуэдеуэ щытщ мы CSS-м.'''\n'''Иджыри итхауэ щыткъым!'''",
        "sitejspreview": "'''Зыщумгъэгъупшэ, мыр япэ-еплъ къуэдеуэ щытщ мы JavaScript-кодым.'''\n'''Иджыри итхауэ щыткъым!'''",
-       "userinvalidcssjstitle": "'''Гу лъытэ:''' лэжьыгъэр зтеухуа «$1»-р къэгъуэтауэ щыткъым. Зыщумыгъэгъупшэ, лэжьыгъэ напэкӀуэцӀ .css, .js-хэм цӀэ яӀэн хуэйхэ, сэтыр хьэрыф нэхъ яхэмыту, мыбым хуэду «{{ns:user}}:Згуэрэ/vector.css», щытын хуэйхэкъым мыбым хуэду: «{{ns:user}}:Згуэрэ/Vector.css».",
+       "userinvalidconfigtitle": "'''Гу лъытэ:''' лэжьыгъэр зтеухуа «$1»-р къэгъуэтауэ щыткъым. Зыщумыгъэгъупшэ, лэжьыгъэ напэкӀуэцӀ .css, .js-хэм цӀэ яӀэн хуэйхэ, сэтыр хьэрыф нэхъ яхэмыту, мыбым хуэду «{{ns:user}}:Згуэрэ/vector.css», щытын хуэйхэкъым мыбым хуэду: «{{ns:user}}:Згуэрэ/Vector.css».",
        "updated": "(КъэгъэщӀырыщӀащ)",
        "note": "'''Гулъытыгъуэ:'''",
        "previewnote": "'''Мыр япэ-еплъ къуэдей, тхылъыр иджыри итхакъым!'''",
index f412f1d..02fb2a5 100644 (file)
        "noarticletext-nopermission": "Na pele de hona thowa çino.\nTı şikina zerrê pelunê binu de [[Special:Search/{{PAGENAME}}|seba sernamê na pele cıfeteliyê]],\nya ki <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} cıkotena aide rê cıfeteliyê].</span>",
        "userpage-userdoesnotexist": "Hesabê karberi \"<nowiki>$1</nowiki>\" qeyd nêbiyo.\nKerem ke, tı ke wazena na pele vırazê/bıvurnê, qontrol ke.",
        "userpage-userdoesnotexist-view": "Hesabê karberi \"$1\" qeyd nêbiyo.",
-       "userinvalidcssjstitle": "'''Teme:''' Mewzuyê \"$1\" çino.\nDosyunê ebe namê .css u .js'y de herfa hurdiye bıgurêne, mesela hurêndia {{ns:user}}:Foo/Vector.css'i de {{ns:user}}:Foo/vector.css bınuse.",
+       "userinvalidconfigtitle": "'''Teme:''' Mewzuyê \"$1\" çino.\nDosyunê ebe namê .css u .js'y de herfa hurdiye bıgurêne, mesela hurêndia {{ns:user}}:Foo/Vector.css'i de {{ns:user}}:Foo/vector.css bınuse.",
        "updated": "(Bi rozane)",
        "note": "'''Not:'''",
        "previewnote": "Teme! ena teyna verqeyda.'''\nVurnayışê tu hama qeyd nıbiyo!",
index bdc3318..642ad37 100644 (file)
        "userjsyoucanpreview": "'''اقىل-كەڭەس:''' جاڭا JS فايلىن ساقتاۋ الدىندا «قاراپ شىعۋ» باتىرماسىن قولدانىپ سىناقتاڭىز.",
        "usercsspreview": "'''مىناۋ CSS ٴماتىنىن تەك قاراپ شىعۋ ەكەنىن ۇمىتپاڭىز, ول ٴالى ساقتالعان جوق!'''",
        "userjspreview": "'''مىناۋ JavaScript قاتىسۋشى باعدارلاماسىن تەكسەرۋ/قاراپ شىعۋ ەكەنىن ۇمىتپاڭىز, ول ٴالى ساقتالعان جوق!'''",
-       "userinvalidcssjstitle": "'''قۇلاقتاندىرۋ:''' وسى ارادا «$1» دەگەن ەش مانەر جوق.\nقاتىسۋشىنىڭ .css جانە .js فايل اتاۋى كىشى ارىپپپەن جازىلۋ ٴتىيىستى ەكەنىن ۇمىتپاڭىز, مىسالعا {{ns:user}}:Foo/vector.css دەگەندى {{ns:user}}:Foo/Vector.css دەگەنمەن سالىستىرىپ قاراڭىز.",
+       "userinvalidconfigtitle": "'''قۇلاقتاندىرۋ:''' وسى ارادا «$1» دەگەن ەش مانەر جوق.\nقاتىسۋشىنىڭ .css جانە .js فايل اتاۋى كىشى ارىپپپەن جازىلۋ ٴتىيىستى ەكەنىن ۇمىتپاڭىز, مىسالعا {{ns:user}}:Foo/vector.css دەگەندى {{ns:user}}:Foo/Vector.css دەگەنمەن سالىستىرىپ قاراڭىز.",
        "updated": "(جاڭارتىلعان)",
        "note": "'''اڭعارتپا:'''",
        "previewnote": "'''مىناۋ تەك قاراپ شىعۋ ەكەنىن ۇمىتپاڭىز;\nوزگەرىستەر ٴالى ساقتالعان جوق!'''",
index ca4071e..5c06c7c 100644 (file)
        "userjspreview": "<strong>Мынау JavaScript қатысушы бағдарламасын тынау/қарап шығу екенін ұмытпаңыз, ол әлі сақталған жоқ!</strong>",
        "sitecsspreview": "<strong>Мынау тек бұл CSS файлын қарап шығуыңыз екенін ұмытпаңыз, ол әлі сақталған жоқ!</strong>",
        "sitejspreview": "<strong>Мынау тек бұл JavaScript кодын алдын-ала қарап алу екенін ұмытпаңыз, ол әлі сақталған жоқ!</strong>",
-       "userinvalidcssjstitle": "</strong>Ескерту:</strong> Осы арада «$1» деген еш мәнер жоқ.\nҚалыпты .css және .js беттерінің атауына кіші әріп қолданыңыз, мысалы {{ns:user}}:Foo/vector.css дегенді {{ns:user}}:Foo/Vector.css дегенмен салыстырып қараңыз.",
+       "userinvalidconfigtitle": "</strong>Ескерту:</strong> Осы арада «$1» деген еш мәнер жоқ.\nҚалыпты .css және .js беттерінің атауына кіші әріп қолданыңыз, мысалы {{ns:user}}:Foo/vector.css дегенді {{ns:user}}:Foo/Vector.css дегенмен салыстырып қараңыз.",
        "updated": "(Жаңартылған)",
        "note": "'''Ескерту:'''",
        "previewnote": "<strong>Бұл тек қарап шығу екенін ұмытпаңыз.</strong> \nӨзгертулеріңіз әлі сақталған жоқ!",
        "prefs-files": "Файлдар",
        "prefs-custom-css": "CSS өзгертпелі",
        "prefs-custom-js": "JavaScript өзгертпелі",
-       "prefs-common-css-js": "Барлық skin-дер үшін CSS/JavaScript бөлісілді:",
+       "prefs-common-config": "Барлық skin-дер үшін CSS/JavaScript бөлісілді:",
        "prefs-reset-intro": "Сіз сайт әдепкі баптауларыңызды қайта орнату үшін осы бетті пайдалана аласыз.\nБұны болдырмау мүмкін емес.",
        "prefs-emailconfirm-label": "Е-поштаның расталуы:",
        "youremail": "Е-поштаңыз:",
index 6b8e784..393d138 100644 (file)
        "userjsyoucanpreview": "'''Aqıl-keñes:''' Jaña JS faýlın saqtaw aldında «Qarap şığw» batırmasın qoldanıp sınaqtañız.",
        "usercsspreview": "'''Mınaw CSS mätinin tek qarap şığw ekenin umıtpañız, ol äli saqtalğan joq!'''",
        "userjspreview": "'''Mınaw JavaScript qatıswşı bağdarlamasın tekserw/qarap şığw ekenin umıtpañız, ol äli saqtalğan joq!'''",
-       "userinvalidcssjstitle": "'''Qulaqtandırw:''' Osı arada «$1» degen eş mäner joq.\nQatıswşınıñ .css jäne .js faýl atawı kişi äripppen jazılw tïisti ekenin umıtpañız, mısalğa {{ns:user}}:Foo/vector.css degendi {{ns:user}}:Foo/Vector.css degenmen salıstırıp qarañız.",
+       "userinvalidconfigtitle": "'''Qulaqtandırw:''' Osı arada «$1» degen eş mäner joq.\nQatıswşınıñ .css jäne .js faýl atawı kişi äripppen jazılw tïisti ekenin umıtpañız, mısalğa {{ns:user}}:Foo/vector.css degendi {{ns:user}}:Foo/Vector.css degenmen salıstırıp qarañız.",
        "updated": "(Jañartılğan)",
        "note": "'''Añğartpa:'''",
        "previewnote": "'''Mınaw tek qarap şığw ekenin umıtpañız;\nözgerister äli saqtalğan joq!'''",
index f467d58..5837584 100644 (file)
        "userjspreview": "'កុំភ្លេចថាអ្នកគ្រាន់តែកំពុង ធ្វើតេស្ត/មើលមុន ទំព័រអ្នកប្រើប្រាស់  JavaScript របស់អ្នក។ វាមិនទាន់ត្រូវបានរក្សាទុកទេ!'''",
        "sitecsspreview": "\"កុំភ្លេចថាអ្នកកំពុងតែមើលមុន CSS នេះប៉ុណ្ណោះ។\"\n\"វាមិនទាន់ត្រូវបានរក្សាទុកទេ!\"",
        "sitejspreview": "\"កុំភ្លេចថាអ្នកកំពុងតែមើលមុន កូដJavaScript  នេះប៉ុណ្ណោះ។\"\n\"វាមិនទាន់ត្រូវបានរក្សាទុកទេ!\"",
-       "userinvalidcssjstitle": "'''ប្រយ័ត្ន៖''' គ្មានសំបក \"$1\"។ ចងចាំថា ទំព័រផ្ទាល់ខ្លួន .css និង .js ប្រើប្រាស់ ចំណងជើង ជាអក្សរតូច, ឧទាហរណ៍  {{ns:user}}:Foo/vector.css ត្រឹមត្រូវ, រីឯ {{ns:user}}:Foo/Vector.css មិនត្រឹមត្រូវ។",
+       "userinvalidconfigtitle": "'''ប្រយ័ត្ន៖''' គ្មានសំបក \"$1\"។ ចងចាំថា ទំព័រផ្ទាល់ខ្លួន .css និង .js ប្រើប្រាស់ ចំណងជើង ជាអក្សរតូច, ឧទាហរណ៍  {{ns:user}}:Foo/vector.css ត្រឹមត្រូវ, រីឯ {{ns:user}}:Foo/Vector.css មិនត្រឹមត្រូវ។",
        "updated": "(បានបន្ទាន់សម័យ)",
        "note": "'''ចំណាំ៖'''",
        "previewnote": "'''សូមចាំថានេះគ្រាន់តែជា​ការបង្ហាញការមើលជាមុនប៉ុណ្ណោះ។ បន្លាស់ប្ដូរ​របស់អ្នកមិនទាន់បាន​រក្សាទុកទេ!'''",
        "prefs-files": "ឯកសារ",
        "prefs-custom-css": "CSSកម្លាយ",
        "prefs-custom-js": "JavaScriptកម្លាយ",
-       "prefs-common-css-js": "CSS/JavaScriptរួមសម្រាប់សំបកទាំងអស់៖",
+       "prefs-common-config": "CSS/JavaScriptរួមសម្រាប់សំបកទាំងអស់៖",
        "prefs-reset-intro": "អ្នក​អាច​ប្រើ​ទំព័រ​នេះ​ដើម្បី​កំណត់​ចំណង់ចំណូល​ចិត្ត​របស់​អ្នក​ដូច​លំនាំ​ដើម​ឡើង​វិញ​។\nសកម្មភាព​នេះ​មិន​អាច​ឈប់ធ្វើ​ឡើង​វិញ​បាន​ទេ​។",
        "prefs-emailconfirm-label": "បញ្ជាក់ទទួលស្គាល់អ៊ីមែល៖",
        "youremail": "អ៊ីមែល៖",
index ecf9ce8..7812d03 100644 (file)
        "userjspreview": "'''사용자 자바스크립트 미리 보기입니다.'''\n'''아직 저장하지 않았습니다!'''",
        "sitecsspreview": "'''이 CSS의 미리 보기입니다.'''\n'''아직 저장하지 않았습니다!'''",
        "sitejspreview": "'''이 자바스크립트 코드의 미리 보기입니다.'''\n'''아직 저장하지 않았습니다!'''",
-       "userinvalidcssjstitle": "<strong>경고:</strong> \"$1\" 스킨은 없습니다.\n.css와 .js 문서의 제목은 {{ns:user}}:Foo/vector.css 처럼 소문자로 써야 합니다. {{ns:user}}:Foo/Vector.css 와 같이 대문자로 쓸 경우 작동하지 않습니다.",
+       "userinvalidconfigtitle": "<strong>경고:</strong> \"$1\" 스킨은 없습니다.\n.css와 .js 문서의 제목은 {{ns:user}}:Foo/vector.css 처럼 소문자로 써야 합니다. {{ns:user}}:Foo/Vector.css 와 같이 대문자로 쓸 경우 작동하지 않습니다.",
        "updated": "(바뀜)",
        "note": "<strong>참고:</strong>",
        "previewnote": "'''이 화면은 미리 보기입니다.'''\n편집한 내용은 아직 저장하지 않았습니다!",
        "prefs-files": "파일",
        "prefs-custom-css": "사용자 CSS",
        "prefs-custom-js": "사용자 자바스크립트",
-       "prefs-common-css-js": "모든 스킨에 공유된 CSS/자바스크립트:",
+       "prefs-common-config": "모든 스킨에 공유된 CSS/자바스크립트:",
        "prefs-reset-intro": "이 페이지를 사용해 사이트 기본값으로 환경 설정을 재설정할 수 있습니다.\n이는 되돌릴 수 없습니다.",
        "prefs-emailconfirm-label": "이메일 인증:",
        "youremail": "이메일:",
        "thumbnail_dest_directory": "새 목적 디렉터리를 만들 수 없습니다.",
        "thumbnail_image-type": "그림 형식이 지원되지 않습니다",
        "thumbnail_gd-library": "GD 라이브러리 설정이 잘못되었습니다: $1 함수를 찾을 수 없습니다.",
+       "thumbnail_image-size-zero": "그림 파일 크기가 0인 것으로 보입니다.",
        "thumbnail_image-missing": "파일을 찾을 수 없습니다: $1",
        "thumbnail_image-failure-limit": "여기에 이 섬네일을 렌더하는 데 최근에 너무 많이 실패한 시도($1 이상)가 있습니다.\n나중에 다시 시도하세요.",
        "import": "문서 가져오기",
index 89e9c6a..0ad036c 100644 (file)
        "userjspreview": "'''Эсде тутугъуз, бу къуру ал къарауду, javascript файлыгъыз алкъын сакъланмагъанды!'''",
        "sitecsspreview": "'''Эслегиз, бу CSS-ни къуру ал къараууду.'''\n '''Ол алкъын сакъланмагъанды!'''",
        "sitejspreview": "'''Эслегиз, бу JavaScript-кодну къуру ал къараууду.'''\n '''Ол алкъын сакъланмагъанды!'''",
-       "userinvalidcssjstitle": "'''Эс бёлюгюз:''' «$1» атлы тема джокъду. Эсде тутугъуз, .css эм .js бетле атлары ажымсыз къуру гитче харифледен болургъа керекди, сёз ючюн: {{ns:user}}:Foo/vector.css, былай  {{ns:user}}:Foo/Vector.css тюйюл!",
+       "userinvalidconfigtitle": "'''Эс бёлюгюз:''' «$1» атлы тема джокъду. Эсде тутугъуз, .css эм .js бетле атлары ажымсыз къуру гитче харифледен болургъа керекди, сёз ючюн: {{ns:user}}:Foo/vector.css, былай  {{ns:user}}:Foo/Vector.css тюйюл!",
        "updated": "(Джангыртылды)",
        "note": "'''Белги:'''",
        "previewnote": "'''Бу къуру ал къарауду.'''\nСиз этген тюрлениуле алкъын сакъланмагъандыла!",
        "prefs-files": "Файлла",
        "prefs-custom-css": "Энчи CSS",
        "prefs-custom-js": "Энчи JS",
-       "prefs-common-css-js": "Бир CSS/JS-ле, халны бары темаларына да:",
+       "prefs-common-config": "Бир CSS/JS-ле, халны бары темаларына да:",
        "prefs-reset-intro": "Бу бетни джарашдырыуларыгъызны тынгылыау бла салыннган джарашдырыулагъа кёчюрюрге хайырланаллыкъсыз.\nБу ишлемни къабыл этсегиз, ызына къайтараллыкъ тюлсюз.",
        "prefs-emailconfirm-label": "Электрон почтаны бегитиу:",
        "youremail": "Электрон почта:",
index a2df772..5c91541 100644 (file)
        "userjspreview": "<strong>Opjepass:</strong> Do bes heh nor am Usprobeere, wat Ding\nMetmaacher_Java_Skripp mäht, et es noch nit jesechert!\n\n<strong>Opjepass:</strong> Noh dem Avspeichere moß de Dingem Brauser noch singe Cache fottschmiiße.\nDat jeit je noh Bauser met ongerscheidleje Knöpp —\nbeim '''Mozilla''' un em '''Firefox''': ''Strg-Shift-R'' —\nem '''Internet Explorer''': ''Strg-F5'' —\nför der '''Opera''': ''F5'' —\nmem '''Safari''': ''Cmd-Shift-R'' —\nun em '''Konqueror''': ''F5'' —\net ess en bunte Welt!",
        "sitecsspreview": "'''Opjepass:''' Do bes heh nor am Usprobeere, wat Ding CSS mäht,\net es noch nit jesechert!",
        "sitejspreview": "<strong>Opjepass:</strong> Do bes heh nor am Usprobeere, wat Ding\nJava_Skripp mäht, et es noch nit jesechert!",
-       "userinvalidcssjstitle": "<strong>Opjepass:</strong> Et jitt keij Ußsinn met dämm Nahme: „<strong>$1</strong>“ -\ndängk drahn, dat ene Metmaacher eije Datteije för et Ußsinn han kann, un dat di met kleijne Bohchstahve\naanfange dun, alsu etwa: „<code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">{{ns:user}}:Name/vector.css</code>“, un \n „<code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">{{ns:user}}:Name/vector.js</code>“ heijße.",
+       "userinvalidconfigtitle": "<strong>Opjepass:</strong> Et jitt keij Ußsinn met dämm Nahme: „<strong>$1</strong>“ -\ndängk drahn, dat ene Metmaacher eije Datteije för et Ußsinn han kann, un dat di met kleijne Bohchstahve\naanfange dun, alsu etwa: „<code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">{{ns:user}}:Name/vector.css</code>“, un \n „<code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">{{ns:user}}:Name/vector.js</code>“ heijße.",
        "updated": "(Aanjepack)",
        "note": "'''Opjepass:'''",
        "previewnote": "<strong>Heh kütt blohß en Aanseesch vöraff — Ding Änderonge sin noch nidd em Wikki faßjehallde!</strong>",
        "prefs-files": "Dateie",
        "prefs-custom-css": "Selfsjemaat <i lang=\"en\">Cascading Style Sheet</i>",
        "prefs-custom-js": "Selfsjemaat JavaSkripp",
-       "prefs-common-css-js": "Gemeinsam CSS un JavaSkrepp för all de Bovverfläshe:",
+       "prefs-common-config": "Gemeinsam CSS un JavaSkrepp för all de Bovverfläshe:",
        "prefs-reset-intro": "Op dä Sigg kanns De Ding Ennschtällong op dämm Wikki singe Schandatt setze lohße. Ävver Opjepaß: Do jidd et keine „Retuhr“-Knopp för!",
        "prefs-emailconfirm-label": "Beshtätejung övver <i lang=\"en\">e-mail</i>:",
        "youremail": "E-Mail *",
index 6ef7b8e..1554705 100644 (file)
        "delete_and_move_reason": "Jêbir ji bo navguherandinê",
        "immobile-source-page": "Navê vê rûpelê nikare were guherandin.",
        "move-leave-redirect": "Beralîkirinekê bihêle",
-       "export": "Rûpelan eksport bike",
+       "export": "Rûpelan derxîne",
        "export-addcat": "Zêde bike",
        "export-addns": "Zêde bike",
        "export-download": "Weka dosyeyê qeyd bike",
index dc85920..75b6996 100644 (file)
@@ -61,8 +61,9 @@
        "category-media-header": "\"$1\" категориядагъы сапламлар",
        "category-empty": "\"Бу категория буссагьат бош.\"",
        "hidden-categories": "{{PLURAL:$1|Яшырылгъан категория|Яшырылгъан категориялар}}‎",
-       "category-subcat-count": "{{PLURAL:$2|Ð\91Ñ\83 ÐºÐ°Ñ\82егоÑ\80иÑ\8fда Ñ\8fнгÑ\8bз Ð³ÐµÐ»ÐµÐ³ÐµÐ½ Ñ\82Ñ\8eп ÐºÐ°Ñ\82егоÑ\80иÑ\8fÑ\81Ñ\8b Ð±Ð°Ñ\80.|Ð\91Ñ\83 ÐºÐ°Ñ\82егоÑ\80иÑ\8fда Ñ\8fнгÑ\8bз Ð³ÐµÐ»ÐµÐ³ÐµÐ½ $2 Ñ\82Ñ\8eп ÐºÐ°Ñ\82егоÑ\80Ñ\8fдан {{PLURAL:$1|Ñ\82Ñ\8eп ÐºÐ°Ñ\82егоÑ\80иÑ\8f}} Ð³Ñ\91Ñ\80Ñ\81еÑ\82илген.}}",
+       "category-subcat-count": "{{PLURAL:$2|Ð\91Ñ\83 ÐºÐ°Ñ\82егоÑ\80иÑ\8fда Ñ\82ек Ñ\81онггÑ\8aÑ\83 Ð³ÐµÐ»ÐµÐ³ÐµÐ½ Ñ\82Ñ\8eпкаÑ\82егоÑ\80иÑ\8fÑ\81Ñ\8b Ð±Ð°Ñ\80.|Ð\91Ñ\83 ÐºÐ°Ñ\82егоÑ\80иÑ\8fда {{PLURAL:$1|Ñ\82Ñ\8eпкаÑ\82егоÑ\80иÑ\8f|$1 Ñ\82Ñ\8eпкаÑ\82егоÑ\80иÑ\8fлаÑ\80}} Ð±Ð°Ñ\80, Ð¶Ð°Ð¼Ð´Ð° $2 Ñ\82Ñ\8eпкаÑ\82егоÑ\80иÑ\8fдан.}}",
        "category-article-count": "{{PLURAL:$2|Бу категорияда янгыз гелеген бир сагьифа бар.|Бу категорияда бар $2 сагьифаны {{PLURAL:$1|сагьифасы}} гёрсетилген.}}",
+       "category-file-count": "{{PLURAL:$2|Бу категорияда бир тек сонггъу гелеген сапламы бар.|Сонггъу гелеген {{PLURAL:$1|саплам|$1 сапламлар}}{{PLURAL:$2|категориядадыр|категориядадырлар}}}}.",
        "listingcontinuesabbrev": "давам",
        "noindex-category": "Индексленмейген сагьифалар",
        "broken-file-category": "Ишлеймейген саплам байланывлары булан сагьифалар",
        "viewsourcelink": "кюрчю кодуна къарамакъ",
        "editsectionhint": "Бёлмени тюзлемек: $1",
        "toc": "Ичделик",
-       "site-atom-feed": "$1 Atom-агъышы",
+       "site-atom-feed": "$1 Atom агъышы",
        "page-atom-feed": "\"$1\" Atom агъышы",
        "red-link-title": "$1 (олай сагьифасы ёкъдур)",
        "nstab-main": "Макъала",
        "nosuchspecialpage": "Олай хас сагьифасы ёкъдур",
        "nospecialpagetext": "<strong>Сен талап этген хас сагьифа ёкъ.</strong>\n\nДурус хас сагьифалар булан тизме мунда: [[Special:SpecialPages|{{int:specialpages}}]].",
        "badtitle": "Къыйышмайгъан ат",
+       "badtitletext": "Чакъырылгъан сагьифаны аты бузукъ эди, яда бош, яда тил-ара яда вики-ара байланывлары янгылыш кюйде берилген эди.\nБалики, атында бир-эки ярамайгъан гьарп берилген эди.",
        "viewsource": "Кюрчю кодуна къарамакъ",
        "viewsource-title": "$1 сагьифаны аслу текстине къарамакъ",
        "viewsourcetext": "Бу сагьифаны аслу кодуна къарап ва ону кёплеп боласан.",
        "userlogin-yourname": "Къоллавчу аты",
-       "userlogin-yourname-ph": "Гьисабынг атын бер",
+       "userlogin-yourname-ph": "Гьисабынгны атын бер",
        "userlogin-yourpassword": "Чечил",
        "userlogin-yourpassword-ph": "Чечилингни бер",
        "createacct-yourpassword-ph": "Чечилингни бер",
        "createaccount": "Янгы бет этмек",
        "userlogin-resetpassword-link": "Чечилингни унутгъанмысан?",
        "userlogin-helplink2": "Гириш саялы кёмек",
-       "createacct-emailoptional": "ЭлекÑ\82Ñ\80он Ð¿Ð¾Ñ\87нÑ\83 ÐµÑ\80леÑ\88ими (гÑ\8cажаÑ\82 Ñ\91кÑ\8a)",
+       "createacct-emailoptional": "ЭлекÑ\82Ñ\80он Ð¿Ð¾Ñ\87нÑ\83 ÐµÑ\80леÑ\88ими (гÑ\91нгÑ\8eллÑ\8e)",
        "createacct-email-ph": "Электрон почну ерлешимин бер",
        "createacct-submit": "Бетинг яса",
        "createacct-benefit-heading": "{{SITENAME}} сени йимик адамланы ортакъ загьматы.",
        "passwordreset": "Чечилни янгыдан бегетмек",
        "bold_sample": "Къалын матын",
        "bold_tip": "Къалын матын",
-       "italic_sample": "Авункъу текст",
-       "italic_tip": "Авункъу текст",
+       "italic_sample": "Авункъу матын",
+       "italic_tip": "Авункъу матын",
        "link_sample": "Байланывну аты",
        "link_tip": "Ич байланыв",
        "extlink_sample": "http://www.example.com байланывну аты",
        "extlink_tip": "Тыш байланыв (бу префиксни http:// эсде сакъла)",
-       "headline_sample": "Язывбашны тексти",
+       "headline_sample": "Язывбашны матыны",
        "headline_tip": "2 даражаны язывбашы",
        "nowiki_sample": "Форматланмагъа гере тюгюл матынны бери сал",
        "nowiki_tip": "Вики форматлавну сан этме",
        "preview": "Ингкъарав",
        "showpreview": "Ингкъарав",
        "showdiff": "Тюзлевлени гёрсетмек",
+       "anoneditwarning": "<strong>Тергев:</strong> Сен гириш этмединг. Тюзлевлер этсенг сени IP адресинг публикли гёрюнер. Эгер <strong>[$1 гириш]</strong> яда <strong>[$2 къайыт]</strong> этсенг, тюзлевлеринг сени ортакъчы аты булан гьисап этилер, оьзге пайдалардан да къайры.",
+       "blockedtext": "Сени къоллавчу атынг яда IP адресинг къамалгъан эди.''\n\nКъамав этген $1 ($2).\nСебеп берилген: ''$3'.\n\n* Къамав башлады: $4\n* Къамав бите: $5\n* Къамавну мурады: $7\n\n$1 булан яда къайсы оьзге [[{{MediaWiki:Grouppage-sysop}}|администратор]] булан къатнап къамавну гьакъында сёйлешмеге боласан.\nТергеп къой, сени [[Special:Preferences|энчили кюйлемлерингде]] e-mail адресинг тюз бермединг буса яда герти этмединг буса, яда къамав шартлагъа мактуп язывуну къадагъа гире буса, \"къоллавчугъа мактуп\" функцияны къоллап болмассан.\nСени IP адресинг — $3, къамавну идентификатору — $5. Тилев, бу маълюматланы бары талапларынга гийир.",
        "loginreqlink": "гирмек",
+       "newarticletext": "Сен гьалиде яратылмагъан сагьифагъа гёчдинг.\nБу сагьифаны яратмакъ учун, тюбюндеги къутугъунда язып башлагъыз (артыкъ маълюмат учун [$1 кёмек сагьифагъа] къара).\nЯнгылыш этип бери гёчген бусанг, сени браузеринг <strong>артгъа</strong> тюймесине бас.",
+       "anontalkpagetext": "<em>Бу гьали де бет яратмагъан яда бетин къолламайгъан аноним къоллавчуну учун сёйлешив.</em>\nШолай буса биз ону белгилемек учун санавлу IP адресни пайдаламакъ герекбиз.\nБу IP адрес бир нече къоллавчуланы арасында пайланып турмагъа бола.\nСен аноним къоллавчу бусанг, ва сагъа тюгюл язывлар бакъдырылгъан деп ойлаша бусанг, тилев, [[Special:CreateAccount|янгы бет ярат]] яда [[Special:UserLogin|гириш эт]], гележекде оьзге аноним къоллавчулар булан сен къарашыкъ болмас учун.",
        "noarticletext": "Бу сагьифа гьали де матынсыз. Сен башгъа сагьифаларда [[Special:Search/{{PAGENAME}}|булай атын эсгеривлерини излемеге]]  боласан, <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} тийишли гюнделик язывланы тапмагъа] яда '''[{{fullurl:{{FULLPAGENAME}}|action=edit}} булай аты булан сагьифа яратмагъа боласан]'''</span>.",
        "noarticletext-nopermission": "Бу сагьифа гьали де текстсиз. Сен башгъа сагьифаларда [[Special:Search/{{PAGENAME}}|булай атын эсгеривлерини излемеге]], яда <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} тийишли гюнделиклени тапмагъа боласан]. </span> Тек бу сагьифаны яратмагъа ихтиярынг ёкъ.",
        "userpage-userdoesnotexist-view": "\"$1\" гьисап къайытланмагъан.",
+       "clearyourcache": "<strong>Note:</strong> Сакълангъан сонг, тюрлевлени гёрмек саялы сагъа браузерингни кэшин янгыртмагъа яда тазаламагъа герек болма бола \n* <strong>Firefox / Safari:</strong> <em>Shift</em> тутуп <em>Reload</em> бас, башгъалай <em>Ctrl-F5</em> яда <em>Ctrl-R</em> бас (Mac буса <em>⌘-R</em>)\n* <strong>Google Chrome:</strong> <em>Ctrl-Shift-R</em> бас(Mac буса <em>⌘-Shift-R</em>)\n* <strong>Internet Explorer:</strong> <em>Ctrl</em> тутуп <em>Refresh</em> бас, яда <em>Ctrl-F5</em> бас\n* <strong>Opera:</strong> <em>Menu → Settings</em> бёлмеге гёч (Mac буса <em>Opera → Preferences</em>) ва сонг <em>Privacy & security → Clear browsing data → Cached images and files</em>.",
        "previewnote": "<strong>Бу ингкъарав экенни эсде сакъла.</strong>\nАлышынывларынг гьали де сакъланмагъан!",
        "continue-editing": "Тюзлевню давамламакъ",
        "editing": "Тюзлев $1",
        "permissionserrors": "Гириш ихтиярланы хатасы",
        "permissionserrorstext-withaction": "Бугъар $2 гелеген {{PLURAL:$1|себепге|себеплеге}} гёре ихтиярынг ёкъ:",
        "recreate-moveddeleted-warn": "<strong>Тергев бер: Алдын тайдырылгъан сагьифаны ярашдырма айланасан.</strong>\n\nБашлап тергеп къара, гертиден де тарыкъмы экен ол сагьифаны ярашдырмагъа.\nТайдырыв ва атын алышдырыв гюнделиги тюпде берилген:",
+       "moveddeleted-notice": "Бу сагьифа тайдырылгъан эди.\nБу сагьифаны тайдырыв, къорув ва атянгыртыв гюнделиги маълюмат саялы тюпде гёрсетилген.",
        "content-model-wikitext": "викиматын",
        "undo-failure": "Аралыкъ алышынывланы къыйышывсызлгъы учун тюзлев гери алынмай.",
        "viewpagelogs": "Бу сагьифа учун гюнделиклени гёрсетмек",
        "currentrevisionlink": "Ахырынчы тюрю",
        "cur": "гьал.",
        "last": "алкъ.",
+       "histlegend": "Тюзлевлерден сайлав: тенглешдирмеге сюеген бары тюрлерин белгилеп '''{{int:compare-submit}}''' бас.<br />\nАчыкълавлар: '''({{int:cur}})''' — гьалиги тюрюнден башгъалыкъ; '''({{int:last}})''' — алдагъы тюрюнден башгъалыкъ; '''{{int:minoreditletter}}''' — увакъ тюзелтив.",
        "history-fieldset-title": "Тюзлевлеге излев",
        "histfirst": "инг эсгилер",
        "histlast": "инг янгылар",
        "prevn": "алдагъы {{PLURAL:$1|$1}}",
        "nextn": "сонггъу {{PLURAL:$1|$1}}",
        "prevn-title": "Алдагъы $1 {{PLURAL:$1|гьасил}}",
-       "nextn-title": "Гелеген $1 {{PLURAL:$1|гьасил}}",
+       "nextn-title": "Гелеген $1 {{PLURAL:$1|гьасил|гьасиллер}}",
        "shown-title": "Айры бир сагьифада $1 {{PLURAL:$1|языв|язывланы}} гёрсетмек",
        "viewprevnext": "($1 {{int:pipe-separator}} $2) ($3) къарамакъ",
        "searchmenu-exists": "<strong>Бу викиде \"[[:$1]]\" деп аты булан сагьифа бар.</strong> {{PLURAL:$2|0=|Дагъы да табылгъан натижалагъа да къара.}}",
        "search-file-match": "(сапламны ичделигине рас геле)",
        "search-suggest": "Буну ойлаймысан экен: $1",
        "searchall": "бары да",
+       "search-showingresults": "{{PLURAL:$4|<strong>$1</strong> натижа <strong>$3</strong> натижалардан|<strong>$1 – $2</strong> натижа <strong>$3</strong> натижалардан}}",
        "search-nonefound": "Бу талапгъа тийишли натижалар табулмады.",
        "mypreferences": "Кюйлемлер",
        "group-bot": "Ботлар",
        "recentchanges-label-plusminus": "Сагьифаны гёлеми шунчакъы байт булан алышынгъан",
        "recentchanges-legend-heading": "<strong>Уйдурма:</strong>",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (шунда да  [[Special:NewPages|янгы сагьифаланы сиягьына къарагъыз]])",
+       "rcnotefrom": "Тюбюндеги — <strong>$3, $4</strong> гюнден берли {{PLURAL:$5|тюзлевюдюр|тюзлевлеридир}}.",
        "rclistfrom": "$2, $3 тюзлевлерден башлап гёрсетмек",
        "rcshowhideminor": "$1 увакъ тюзлевлер",
        "rcshowhideminor-show": "Гёрсетмек",
        "recentchangeslinked-feed": "Байлавлу тюзлевлер",
        "recentchangeslinked-toolbox": "Байлавлу тюзлевлер",
        "recentchangeslinked-title": "\"$1\" сагьифагъа байлавлу тюзлевлер",
-       "recentchangeslinked-summary": "Бу сагьифагъа байлавлу тюзлевлери булан сагьифаланы яда о сагьифа байлавлуланы гёрмек учун атын яз. (Категорияны ортакъчаларын гёрмек учун Category:категорияны атын яз). Сени [[Special:Watchlist|гьызарлав тизменгдеги]] алышынывлар <strong>къалын</strong> гьарплылар.",
+       "recentchangeslinked-summary": "Бу сагьифагъа байлавлу тюзлевлери булан сагьифаланы, яда о сагьифадан байлавлуланы гёрмек учун атын яз. (Категорияны ортакъчаларын гёрмек учун Category:Категорияны атын яз). Сени [[Special:Watchlist|Гьызарлав тизменгдеги]] алышынывлар <strong>къалын</strong> гьарплылар.",
        "recentchangeslinked-page": "Сагьифаны аты:",
        "recentchangeslinked-to": "Къайта, бу сагьифагъа байлавлу сагьифаланы алышынывларын гёрсетмек",
        "upload": "Сапламны юклемек",
        "imagelinks": "Сапламны къоллаву",
        "linkstoimage": "Шундан сонггъу {{PLURAL:$1|сагьифа|$1 сагьифалар}} бу сапламгъа байлангъан:",
        "linkstoimage-more": "$1 {{PLURAL:$1|сагьифадан}} артыкъ иг сапламгъа байланывлу.\nБу тизме янгыз бу сапламгъа {{PLURAL:$1|first page link|биринчи $1 байланывлу сагьифаны}} гёрсете.\n[[Special:WhatLinksHere/$2|Толу тизме де]] бар.",
-       "nolinkstoimage": "Бу сапламгъа байлавлу сагьифалар ёкъ",
+       "nolinkstoimage": "Бу сапламгъа байлавлу сагьифалар ёкъдур",
        "linkstoimage-redirect": "$1 (саплам ёллав) $2",
        "sharedupload-desc-here": "Бу саплам $1 проектдендир, ва башгъасында да къолланма бола. Ону [$2 тасвир сагьифасындан] маълюматы тюпде бериле.‎",
        "filepage-nofile": "Булай аты булан саплам ёкъдур.",
        "unwatch": "Гери гьызарламакъ",
        "watchlist-details": "{{PLURAL:$1|$1 сагьифа}} сени тергев тизменгде (пикирлешив сагьифаланы санап).",
        "wlheader-showupdated": "Сени ахырынчы геливюнгден сонг алышынгъан сагьифалар <strong>къалын</strong> шрифт булан гёрсетилген.",
+       "wlnote": "Тюбюндеги — {{PLURAL:$1|ахырынчы тюзлевюдюр|<strong>$1</strong> ахырынчы тюзлевлеридир}} алдагъы {{PLURAL:$2|сагьат аралыкъда|<strong>$2</strong> сагьат аралыкъда}} этилген, $3, $4 тархындан.",
        "wlshowlast": "Артдагъы $1 сагьат $2 гюннюкин гёрсетмек",
        "watchlist-options": "Тергев тизмени кюйлевлери",
        "enotif_reset": "Бары да сагьифаланы къаралгъандай белгилемек",
        "restriction-move": "Атын алышдырмакъ",
        "namespace": "Атлар аралыгъы:",
        "invert": "Сайлангъанын айландырмакъ",
+       "tooltip-invert": "Сайлангъан ат аралыкъдагъы (ва белгиленген буса огъар байланышлы ат аралыкъдагъы да) тюзлевлерин яшырмакъ учун бу белгини салып къой.",
        "namespace_association": "Байланышлы атлар аралыгъы",
        "tooltip-namespace_association": "Сайлангъан атысакъламагъа байлавлу атсакъламаны, пикирлешивлени къошмакъ учун бу белгини де сал.",
        "blanknamespace": "(Аслу)",
        "contributions": "{{GENDER:$1|Къоллавчуну}} къошуму",
        "contributions-title": "Ортакъчыны ярдымы $1",
-       "mycontris": "Къошуму",
-       "anoncontribs": "Къошуму",
+       "mycontris": "Къошум",
+       "anoncontribs": "Къошум",
        "contribsub2": "Ярдым {{GENDER:$3|$1}} ($2)",
        "nocontribs": "Берилген усуллагъа къыйышывлу алышынывлар табылмагъан.",
        "uctop": "(гьалиги)",
        "ipboptions": "2 сагьат:2 hours,1 гюн:1 day,3 гюн:3 days,1 жума:1 week,2 жума:2 weeks,1 ай:1 month,3 ай:3 months,6 ай:6 months,1 йыл:1 year,болжалсыз:infinite",
        "infiniteblock": "болжалсыз",
        "blocklink": "къамамакъ",
-       "contribslink": "Ð\9aъошум",
+       "contribslink": "къошум",
        "blocklogpage": "къамав гюнделиги",
        "blocklogentry": "къамагъан [[$1]] бу мюгьлетге $2 $3",
        "reblock-logentry": "къамавну [[$1]]бу болжалгъа $2 $3 алышдыргъан",
        "show-big-image-other": "Башгъа {{PLURAL:$2|айырым|айырымлар}}: $1.",
        "show-big-image-size": "$1 × $2 пиксел",
        "metadata": "Мета маълюматлар",
+       "metadata-help": "Бу саплап къошум маълюмат сакълай, балики, санавлу камера яда сканнер табакъ яратылгъан яда санавлашгъан.\nЭгер саплам аслу гьалдан тюрленген буса, маълюматлардан бирлери алышынгъан тюрюн толу кюйде къыйышмайлы болмагъа ярай.",
+       "metadata-fields": "Эгер метамаълюмат табел дагъылса, бу мактупда тизилген суратланы метамаълюмат аралыкълары сурат сагьифаны дисплейине гийирилер.\nКъалгъандал кюрчю кюйлемлеге гёре яшырыларлар.\n* make\n* model\n* datetimeoriginal\n* exposuretime\n* fnumber\n* isospeedratings\n* focallength\n* artist\n* copyright\n* imagedescription\n* gpslatitude\n* gpslongitude\n* gpsaltitude",
        "exif-orientation": "Онгарылым",
        "exif-xresolution": "Гёнделен айырым",
        "exif-yresolution": "Тик айырым",
        "watchlisttools-raw": "Саяв текстни тюзлемек",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|сёйлешив]])",
        "redirect": "Сапламны, къоллавчуну, сагьифаны, тюрню яда гюнделикни менлигине гёре ёллав",
+       "redirect-summary": "Бу хас сагьифасы сапламгъа (самламны атындан), сагьифагъа (тюзлевню ID яда сагьифаны ID санавлардан), къоллавчу сагьифасына (санавлу къоллавчу ID нёмюрден) яда гюнделик язывгъа (гюнделикни IDден) ёллай. Къоллав:\n[[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/page/64308]], [[{{#Special:Redirect}}/revision/328429]], [[{{#Special:Redirect}}/user/101]] или [[{{#Special:Redirect}}/logid/186]].",
        "redirect-submit": "Гёчмек",
        "redirect-lookup": "Излев:",
        "redirect-value": "Къыймат:",
index 6f98e93..3913c06 100644 (file)
        "userjspreview": "'''Denkt drun datt Dir Äre Javascript nëmmen test.'''\n'''En ass nach net gespäichert!'''",
        "sitecsspreview": "'''Denkt drun datt Dir dësen CSS just kuckt.\nE gouf nach net gespäichert!'''",
        "sitejspreview": "'''Denkt drun datt Dir dëse JavaScript-Code just kuckt.\nE gouf nach net gespäichert!'''",
-       "userinvalidcssjstitle": "'''Opgepasst:''' Et gëtt keen Ausgesinn (skin) \"$1\".\nDenkt drun datt eegen .css an .js Säiten e kleng geschriwwenen Titel benotzen, z. Bsp. {{ns:user}}:Foo/vector.css am Géigesaz zu {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "'''Opgepasst:''' Et gëtt keen Ausgesinn (skin) \"$1\".\nDenkt drun datt eegen .css an .js Säiten e kleng geschriwwenen Titel benotzen, z. Bsp. {{ns:user}}:Foo/vector.css am Géigesaz zu {{ns:user}}:Foo/Vector.css.",
        "updated": "(Geännert)",
        "note": "'''Notiz:'''",
        "previewnote": "'''Denkt drun datt dëst nëmmen eng net gespäichert Versioun ass.'''\nÄr Ännerunge sinn nach net gespäichert!",
        "prefs-files": "Fichieren",
        "prefs-custom-css": "Benotzerdefinéierten CSS",
        "prefs-custom-js": "Benotzerdefinéierte JS",
-       "prefs-common-css-js": "Gemeinsam CSS/JS fir all Ausgesinn (skins):",
+       "prefs-common-config": "Gemeinsam CSS/JS fir all Ausgesinn (skins):",
        "prefs-reset-intro": "Dir kënnt dës Säit benotze fir Är Astellungen zréck op d'Standard-Astllungen ze setzen.\nDëst kann net réckgängeg gemaach ginn.",
        "prefs-emailconfirm-label": "E-Mail Confirmatioun:",
        "youremail": "E-Mail-Adress:",
        "thumbnail_dest_directory": "Den Zilepertoire konnt net ugeluecht ginn.",
        "thumbnail_image-type": "Bildtyp gëtt net ënnerstëtzt",
        "thumbnail_gd-library": "D'Konfiguratioun vun der GD-Bibliothéik (GD library) ass net komplett: D'Funktioun $1 feelt",
+       "thumbnail_image-size-zero": "D'Gréisst vum Fichier vum Bild schéngt null ze sinn.",
        "thumbnail_image-missing": "De Fichier schengt ze feelen: $1",
        "import": "Säiten importéieren",
        "importinterwiki": "Vun enger anerer Wiki importéieren",
index 6cf48a3..daeb9d6 100644 (file)
        "userjspreview": "<strong>Recorda ce tu regarda mera un proba/previde de tua JavaScript de usor.\nLo es ancora no fisada!</strong>",
        "sitecsspreview": "<strong>Recorda ce tu regarda mera un previde de esta CSS.\nLo es ancora no fisada!</strong>",
        "sitejspreview": "<strong>Recorda ce tu regarda mera un previde de esta codigo JavaScript.\nLo es ancora no fisada!</strong>",
-       "userinvalidcssjstitle": "<strong>Avisa:</strong> No pel \"$1\" esiste.\nPajes .css e .js personal usa un titulo con leteras minor, pe {{ns:user}}:Foo/vector.css en loca de {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "<strong>Avisa:</strong> No pel \"$1\" esiste.\nPajes .css e .js personal usa un titulo con leteras minor, pe {{ns:user}}:Foo/vector.css en loca de {{ns:user}}:Foo/Vector.css.",
        "updated": "(Cambiada)",
        "note": "<strong>Nota:</strong>",
        "previewnote": "<strong>Recorda ce esta es mera un previde.</strong>\nTua cambias es ancora no fisada!",
        "postedit-confirmation-created": "La paje es creada.",
        "postedit-confirmation-restored": "La paje es restorada.",
        "postedit-confirmation-saved": "Tua edita es fisada.",
+       "postedit-confirmation-published": "Tua edita ia es publicida.",
        "edit-already-exists": "Un paje nova no ia pote es creada.\nLo esiste ja.",
        "defaultmessagetext": "Testo inisial de mesaje",
        "content-failed-to-parse": "La contenida $2 per model $1 no ia pote es analiseda: $3",
        "diff-multi-sameuser": "({{PLURAL:$1|Un revisa|$1 revisas}} media par la mesma usor no es mostrada)",
        "diff-multi-otherusers": "({{PLURAL:$1|Un revisa|$1 revisas}} media par {{PLURAL:$2|un otra usor|$2 usores}} no es mostrada)",
        "diff-multi-manyusers": "({{PLURAL:$1|Un revisa|$1 revisas}} media par plu ca $2 {{PLURAL:$2|usor|$2 usores}} no es mostrada)",
+       "diff-paragraph-moved-tonew": "La paragraf ia move. Clica per salta a la loca nova.",
+       "diff-paragraph-moved-toold": "La paragraf ia move. Clica per salta a la loca vea.",
        "difference-missing-revision": "{{PLURAL:$2|Un revisa|$2 revisas}} de esta difere ($1) no ia es trovada.\n\nEsta es usual causada par segue un lia nonfresca de istoria a un paje cual on ia sutrae.\nDetalias es trovable en la [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} rejistra de sutraes].",
        "searchresults": "Resultas de xerca",
        "searchresults-title": "Resultas de xerca \"$1\"",
        "timezoneregion-indian": "Mar Indian",
        "timezoneregion-pacific": "Mar Pasifica",
        "allowemail": "Permete ce otra usores pote eposta me",
+       "email-allow-new-users-label": "Permete epostas de usores intera nova.",
        "email-blacklist-label": "Proibi esta usores de eposta a me:",
        "prefs-searchoptions": "Xerca",
        "prefs-namespaces": "Spasios de nom",
        "prefs-files": "Fixes",
        "prefs-custom-css": "CSS personal",
        "prefs-custom-js": "JavaScript personal",
-       "prefs-common-css-js": "CSS/JavaScript comun per tota peles:",
+       "prefs-common-config": "CSS/JavaScript comun per tota peles:",
        "prefs-reset-intro": "Tu pote usa esta paje per reinisia tua preferes a la inisiales per esta pajeria.\nTu no pote desfa esta.",
        "prefs-emailconfirm-label": "Confirma de eposta:",
        "youremail": "Eposta:",
        "recentchanges-summary": "Segue asi la cambias la plu resente a la vici.",
        "recentchanges-noresult": "No cambias en la periodo spesifada coresponde a esta criterios.",
        "recentchanges-timeout": "Esta xerca ia es abandonada. Posible, tu desira atenta otra parametres de xerca.",
+       "recentchanges-network": "Par un era tecnical, no resultas pote es cargada. Proba refresci la paje, per favore.",
+       "recentchanges-notargetpage": "Tape un nom de paje a supra per vide cambias relatada a acel paje.",
        "recentchanges-feed-description": "Segue la cambias la plu resente a la vici en esta flue.",
        "recentchanges-label-newpage": "Esta edita ia crea un paje nova",
        "recentchanges-label-minor": "Esta es un edita minor",
        "rcfilters-group-results-by-page": "Grupi resultas par paje",
        "rcfilters-activefilters": "Filtros ativa",
        "rcfilters-advancedfilters": "Filtros avansada",
-       "rcfilters-limit-title": "Mostra cuanto cambias",
+       "rcfilters-limit-title": "Mostra cuanto resultas",
+       "rcfilters-limit-and-date-label": "$1 {{PLURAL:$1|cambia|cambias}}, $2‎",
+       "rcfilters-date-popup-title": "Periodo de tempo per la xerca",
        "rcfilters-days-title": "Dias resente",
        "rcfilters-hours-title": "Oras resente",
        "rcfilters-days-show-days": "$1 {{PLURAL:$1|dia|dias}}",
        "rcfilters-savedqueries-apply-and-setdefault-label": "Crea filtro inisial",
        "rcfilters-savedqueries-cancel-label": "Cansela",
        "rcfilters-savedqueries-add-new-title": "Fisa ajustas presente de filtro",
+       "rcfilters-savedqueries-already-saved": "Esta filtros es ja fisada. Cambia tua ajustas per crea un filtro fisada nova.",
        "rcfilters-restore-default-filters": "Restora filtros inisial",
        "rcfilters-clear-all-filters": "Vacui tota filtros",
        "rcfilters-show-new-changes": "Regarda cambias la plu resente",
-       "rcfilters-search-placeholder": "Filtri cambias resente (eleje o comensa tape)",
+       "rcfilters-search-placeholder": "Filtri cambias (usa la menu o xerca un nom de filtro)",
        "rcfilters-invalid-filter": "Filtro nonvalida",
        "rcfilters-empty-filter": "No filtros es ativa. Tota contribuis es mostrada.",
        "rcfilters-filterlist-title": "Filtros",
        "rcfilters-watchlist-showupdated": "Cambias a pajes cual tu no ia visita pos cuando los ia aveni es en\n<strong>testo spesa</strong>, con indicadores solida.",
        "rcfilters-preference-label": "Asconde la varia bonida de Cambias Resente",
        "rcfilters-preference-help": "Desfa la redesinia de interfas de 2017 e tota utiles ajuntada alora e\na pos.",
+       "rcfilters-filter-showlinkedfrom-label": "Mostra cambias en pajes liada de",
+       "rcfilters-filter-showlinkedfrom-option-label": "<strong>Pajes liada de</strong> la paje elejeda",
+       "rcfilters-filter-showlinkedto-label": "Mostra cambias en pajes liante a",
+       "rcfilters-filter-showlinkedto-option-label": "<strong>Pajes liante a</strong> la paje elejeda",
+       "rcfilters-target-page-placeholder": "Tape un nom de paje (o categoria)",
        "rcnotefrom": "A su es la {{PLURAL:$5|cambia|cambias}} de <strong>$3, $4</strong> (asta <strong>$1</strong>).",
        "rclistfromreset": "Reinisia la eleje de data",
        "rclistfrom": "Mostra cambias nova, comensante de $2, $3",
        "recentchangeslinked-feed": "Cambias relatada",
        "recentchangeslinked-toolbox": "Cambias relatada",
        "recentchangeslinked-title": "Cambias relatada a \"$1\"",
-       "recentchangeslinked-summary": "Esta es un lista de cambias resente a pajes liada de un paje spesifada (o a membros de un categoria spesifada).\nPajes en [[Special:Watchlist|tua lista monitorida]] es <strong>spesa</strong>.",
+       "recentchangeslinked-summary": "Tape un nom de paje per vide cambias en pajes liada a o de acel paje. (Per vide membros de un categoria, tape <strong>bold</strong>.) Cambias a pajes en [[Special:Watchlist|tua lista monitorida]] es <strong>spesa</strong>.",
        "recentchangeslinked-page": "Nom de paje:",
        "recentchangeslinked-to": "Mostra cambias a pajes cual lia a la paje indicada, en loca",
        "recentchanges-page-added-to-category": "[[:$1]] ajuntada a categoria",
        "lockmanager-fail-closelock": "La fix de semafor per \"$1\" no ia pote es cluida.",
        "lockmanager-fail-deletelock": "La fix de semafor per \"$1\" no ia pote es sutraeda.",
        "lockmanager-fail-acquirelock": "La semafor per \"$1\" no ia pote es otenida.",
-       "lockmanager-fail-openlock": "La fix de semafor per \"$1\" no ia pote es abrida.",
+       "lockmanager-fail-openlock": "La fix de semafor per \"$1\" no ia pote es abrida. Serti ce tua arcivo de carga es bon ajustada e ce tua servador es permeteda a scrive en acel arcivo. Vide https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:$wgUploadDirectory per plu informa.",
        "lockmanager-fail-releaselock": "La semafor per \"$1\" no ia pote es relasada.",
        "lockmanager-fail-db-bucket": "Tro poca bancos de semafor ia pote es contatada en balde $1.",
        "lockmanager-fail-db-release": "Semafores per banco de datos $1 no ia pote es relasada.",
        "uploadstash-refresh": "Refresci la lista de fixes",
        "uploadstash-thumbnail": "regarda imajeta",
        "uploadstash-exception": "Carga no ia pote es ajuntada a la cargas pendente ($1): \"$2\".",
+       "uploadstash-bad-path": "La adirije no esiste.",
+       "uploadstash-bad-path-invalid": "La adirije no es valida.",
+       "uploadstash-bad-path-unknown-type": "Tipo \"$1\" nonconoseda.",
+       "uploadstash-bad-path-unrecognized-thumb-name": "Imajeta nonreconoseda.",
+       "uploadstash-bad-path-no-handler": "No tratador ia es trovada per tipo $1 de fix $2.",
+       "uploadstash-bad-path-bad-format": "Clave \"$1\" no es en forma coreta.",
+       "uploadstash-file-not-found": "Clave \"$1\" no ia es trovada en la pendentes.",
+       "uploadstash-file-not-found-no-thumb": "La imajeta no ia pote es otenida.",
+       "uploadstash-file-not-found-no-local-path": "No adirije local per la ojeto regrandida.",
+       "uploadstash-file-not-found-no-object": "La fix local per la imajeta no ia pote es creada.",
+       "uploadstash-file-not-found-no-remote-thumb": "Retrae de imajeta ia fali: $1 URL = $2",
+       "uploadstash-file-not-found-missing-content-type": "La xapo \"Content-Type\" manca.",
+       "uploadstash-file-not-found-not-exists": "La adirije no es trovable, o la fix no es plata.",
+       "uploadstash-file-too-large": "Fixes plu grande ca $1 baites no pote es servida.",
+       "uploadstash-not-logged-in": "No usor es autenticida; fixes debe parteni a usores.",
+       "uploadstash-wrong-owner": "Esta fix ($1) no parteni a la usor presente.",
+       "uploadstash-no-such-key": "No tal clave ($1); sutrae no es posible.",
+       "uploadstash-no-extension": "La sufisa manca.",
+       "uploadstash-zero-length": "La fix ave zero baites.",
        "invalid-chunk-offset": "Bloco con numero nonvalida",
        "img-auth-accessdenied": "Nonasedable",
        "img-auth-nopathinfo": "PATH_INFO manca.\nTua servador no es ajustada per envia esta informa.Cisa lo usa CGI e no pote suporta img_auth.\nVide https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization.",
        "doubleredirects": "Redirijes duple",
        "doubleredirectstext": "Esta paje lista pajes cual redirije a otra pajes redirijente.\nCada linia conteni linias a la redirije prima e du, como ance la gol de la redirije du, cual es usual la paje \"vera\" a cual la redirije prima debe punta.\nEntradas <del>barida</del> es ja solveda.",
        "double-redirect-fixed-move": "[[$1]] es moveda.\nLo ia es automata cambiada e aora lo redirije a [[$2]].",
-       "double-redirect-fixed-maintenance": "Automata reparante redirije duple de [[$1]] a [[$2]] en taxe de manteni.",
+       "double-redirect-fixed-maintenance": "Automata reparante redirije duple de [[$1]] a [[$2]] en taxe de manteni",
        "double-redirect-fixer": "Reparor de redirijes",
        "brokenredirects": "Redirijes rompeda",
        "brokenredirectstext": "La redirijes seguente lia a pajes nonesistente:",
        "rollback-success": "Editas par {{GENDER:$3|$1}} ia es reversada e cambiada a la revisa la plu resente par {{GENDER:$4|$2}}",
        "rollback-success-notify": "Editas par $1 ia es reversada e cambiada a la revisa la plu resente par $2. [$3 Mostra cambias]",
        "sessionfailure-title": "Fali de sesion",
-       "sessionfailure": "Lo pare ce tua sesion de autentici ave un problem;\nesta ata ia es canselada per proteje contra saisis de sesion.\nRevade a la paje presedente, recarga acel paje, e atenta alora denova.",
+       "sessionfailure": "Lo pare ce tua sesion de autentici ave un problem; esta ata ia es canselada per proteje contra saisis de sesion. Reenvia la formulario, per favore.",
        "changecontentmodel": "Cambia model de contenida de un paje",
        "changecontentmodel-legend": "Cambia model de contenida",
        "changecontentmodel-title-label": "Titulo de paje",
        "thumbnail_dest_directory": "Arcivo destinal no pote es creada",
        "thumbnail_image-type": "Tipo nonsuportada de imaje",
        "thumbnail_gd-library": "Ajusta noncompleta de biblioteca GD: Funsiona $1 manca",
+       "thumbnail_image-size-zero": "La grandia de la fix de imaje pare es zero.",
        "thumbnail_image-missing": "Fix pare manca: $1",
        "thumbnail_image-failure-limit": "Tro multe atentas resente ($1 o plu) ia fali rendere esta imajeta. Atenta denova plu tarda, per favore.",
        "import": "Emporta pajes",
        "import-mapping-namespace": "Emporta a un spasio de nom:",
        "import-mapping-subpage": "Emporta como supajes de la paje seguente:",
        "import-upload-filename": "Nom de fix:",
+       "import-upload-username-prefix": "Prefisa intervical:",
+       "import-assign-known-users": "Asinia editas a usores local do la usor nomida esiste local",
        "import-comment": "Comenta:",
        "importtext": "Per favore, esporta la fix de la vici fontal par usa la [[Special:Export|util de esporta]].\nFisa lo a tu computador e carga lo asi.",
        "importstart": "Emportante pajes...",
        "imported-log-entries": "$1 {{PLURAL:$1|entrada|entradas}} de rejistra ia es emportada.",
        "importfailed": "Emporta ia fali: <nowiki>$1</nowiki>",
        "importunknownsource": "Tipo nonconoseda de fonte de emporta",
+       "importnoprefix": "No prefisa intervical ia es furnida",
        "importcantopen": "Fix de emporta no ia pote es abrida",
        "importbadinterwiki": "Mal lia intervical",
        "importsuccess": "Emporta ia fini!",
        "pageinfo-category-subcats": "Cuantia de sucategorias",
        "pageinfo-category-files": "Cuantia de fixes",
        "pageinfo-user-id": "Numero de usor",
+       "pageinfo-file-hash": "Valua axida",
        "markaspatrolleddiff": "Marca como patruliada",
        "markaspatrolledtext": "Marca esta paje como patruliada",
        "markaspatrolledtext-file": "Marca esta varia de fix como patruliada",
        "autosumm-blank": "Paje vacuida",
        "autosumm-replace": "Contenida sustituida par \"$1\"",
        "autoredircomment": "Paje redirijeda a [[$1]]",
+       "autosumm-removed-redirect": "Sutrae de redirije a [[$1]]",
+       "autosumm-changed-redirect-target": "Gol de redirije cambiada de [[$1]] a [[$2]]",
        "autosumm-new": "Paje creada con \"$1\"",
        "autosumm-newblank": "Paje vacua creada",
        "lag-warn-normal": "Cambias plu resente ca $1 {{PLURAL:$1|secondo|secondos}} ante aora es cisa no mostrada en esta lista.",
        "watchlistedit-clear-titles": "Titulos:",
        "watchlistedit-clear-submit": "Vacui la lista (Esta es permanente!)",
        "watchlistedit-clear-done": "Tua lista de pajes monitorida es vacuida.",
+       "watchlistedit-clear-jobqueue": "Tua lista monitorida deveni vacuida. Esta pote ocupa alga tempo!",
        "watchlistedit-clear-removed": "{{PLURAL:$1|1 titulo|$1 titulos}} ia es sutraeda:",
        "watchlistedit-too-many": "La lista ave tro multe pajes per mostra asi.",
        "watchlisttools-clear": "Vacui la lista monitorida.",
        "version-poweredby-others": "otras",
        "version-poweredby-translators": "traduores a translatewiki.net",
        "version-credits-summary": "Nos desira reconose la persones seguente per sua contribuis a [[Special:Version|MediaWiki]].",
-       "version-license-info": "MediaWiki es un program libre; tu pote redistribui lo e/o altera lo su la restrinjes de la Lisensa Publica Jeneral GNU como publicida par la Funda de Programes Libre (Free Software Foundation); o revisa 2 de la Lisensa, o (si tu prefere) cualce revisa plu tarda.\n\nMediaWiki es distribuida con la espera ce lo va es usosa, ma CON NO GARANTIA; lo an no ave la garantia implicada de CONVENI PER VENDE (merchantability) or CONVENI PER UN INTENDE SPESIFADA (fitness for a particular purpose). Vide la Lisensa Publica Jeneral GNU per plu detalias.\n\nEsperable tu ia reseta [{{SERVER}}{{SCRIPTPATH}}/COPYING un copia de la Lisensa Publica Jeneral GNU] con esta program; si no, scrive a Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA o [//www.gnu.org/licenses/old-licenses/gpl-2.0.html leje lo enlinia].",
+       "version-license-info": "MediaWiki es un program libre; tu pote redistribui lo e/o altera lo su la restrinjes de la Lisensa Publica Jeneral GNU como publicida par la Funda de Programes Libre (Free Software Foundation); o revisa 2 de la Lisensa, o (si tu prefere) cualce revisa plu tarda. MediaWiki es distribuida con la espera ce lo va es usosa, ma <em>CON NO GARANTIA</em>; lo an no ave la garantia implicada de <strong>CONVENI PER VENDE</strong> (merchantability) or <strong>CONVENI PER UN INTENDE SPESIFADA</strong> (fitness for a particular purpose). Vide la Lisensa Publica Jeneral GNU per plu detalias. Esperable tu ia reseta [{{SERVER}}{{SCRIPTPATH}}/COPYING un copia de la Lisensa Publica Jeneral GNU] con esta program; si no, scrive a Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA o [//www.gnu.org/licenses/old-licenses/gpl-2.0.html leje lo enlinia].",
        "version-software": "Programes instalada",
        "version-software-product": "Produida",
        "version-software-version": "Revisa",
        "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|Eticeta|Eticetas}}]]: $2)",
        "tag-mw-contentmodelchange": "cambia de model de contenida",
        "tag-mw-contentmodelchange-description": "Editas cual [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:ChangeContentModel cambia la model de contenida] de un paje",
+       "tag-mw-new-redirect": "Redirije nova",
+       "tag-mw-new-redirect-description": "Editas cual crea un redirije nova o cambia un paje a un redirije",
+       "tag-mw-removed-redirect": "Redirije sutraeda",
+       "tag-mw-removed-redirect-description": "Editas cual cambia un redirije esistente a un nonredirije",
+       "tag-mw-changed-redirect-target": "Gol de redirije cambiada",
+       "tag-mw-changed-redirect-target-description": "Editas cual cambia la gol de un redirije",
+       "tag-mw-blank": "Vacui",
+       "tag-mw-blank-description": "Editas cual vacui un paje",
+       "tag-mw-replace": "Suprascriveda",
+       "tag-mw-replace-description": "Editas cual sutrae plu ca 90% de la contenida de un paje",
+       "tag-mw-rollback": "Reversa",
+       "tag-mw-rollback-description": "Editas cual reversa editas presedente par la lia \"reversa\"",
+       "tag-mw-undo": "Desfa",
+       "tag-mw-undo-description": "Editas cual desfa editas presedente par la lia \"desfa\"",
        "tags-title": "Eticetas",
        "tags-intro": "Esta paje lista la eticetas con cual la program pote marca un edita, e sua sinifias.",
        "tags-tag": "Nom de eticeta",
        "limitreport-expansiondepth": "Profondia la plu grande de despaci",
        "limitreport-expensivefunctioncount": "Cuantia de funsionas custosa de analisador sintatical",
        "expandtemplates": "Despaci stensiles",
-       "expand_templates_intro": "Esta paje spesial prende testo e despaci tota stensiles en lo, en modo recorsante.\nLo despaci ance funsionas suportada de analisador sintatical como\n<code><nowiki>{{</nowiki>#language:…}}</code> e variables como\n<code><nowiki>{{</nowiki>CURRENTDAY}}</code>.\nEn fato, lo despaci cuasi tota cosas entre brasetas risa duple.",
+       "expand_templates_intro": "Esta paje spesial prende vicitesto e despaci tota stensiles en lo, en modo recorsante. Lo despaci ance funsionas suportada de analisador sintatical como <code><nowiki>{{</nowiki>#language:…}}</code> e variables como <code><nowiki>{{</nowiki>CURRENTDAY}}</code>. En fato, lo despaci cuasi tota cosas entre brasetas risa duple.",
        "expand_templates_title": "Titulo de contesto, per {{FULLPAGENAME}}, etc.:",
-       "expand_templates_input": "Testo enflual:",
+       "expand_templates_input": "Vicitesto enflual:",
        "expand_templates_output": "Resulta",
        "expand_templates_xml_output": "Esflue XML",
        "expand_templates_html_output": "Esflue HTML cru",
        "expand_templates_preview": "Previde",
        "expand_templates_preview_fail_html": "<em>Car HTML cru es comutada en {{SITENAME}} e datos de sesion ia es perdeda, la previde es ascondeda per preveni atacas par JavaScript.</em>\n\n<strong>Si esta es un atenta valida de previde, atenta denova, per favore.</strong>\nSi lo ancora no funsiona, proba [[Special:UserLogout|desautentici]] e reautentici, e serti ce tua surfador permete cucis de esta pajeria.",
        "expand_templates_preview_fail_html_anon": "\n<em>Car HTML cru es comutada en {{SITENAME}} e tu no es autenticida, la previde es ascondeda per preveni atacas par JavaScript.</em>\n\n<strong>Si esta es un atenta valida de previde, [[Special:UserLogin|autentici]] e atenta denova, per favore.</strong>",
-       "expand_templates_input_missing": "Tu debe furni a la min alga testo enflual.",
+       "expand_templates_input_missing": "Tu debe furni a la min alga vicitesto enflual.",
        "pagelanguage": "Cambia lingua de paje",
        "pagelang-name": "Paje",
        "pagelang-language": "Lingua",
        "restrictionsfield-badip": "Adirije o estende IP nonvalida: $1",
        "restrictionsfield-label": "Estendes IP permeteda:",
        "restrictionsfield-help": "Un adirije IP o estende CIDR per linia: Per comuta tota, usa:<pre>0.0.0.0/0\n::/0</pre>",
+       "edit-error-short": "Era: $1",
+       "edit-error-long": "Eras:\n\n$1",
        "revid": "revisa $1",
        "pageid": "numero de paje $1",
        "rawhtml-notallowed": "Eticetas &lt;html&gt; no pote es usada estra pajes normal.",
index 243159c..d8f54b8 100644 (file)
        "userjspreview": "'''Jukira nti JavaScript gw'otegese omugezesamubugezesa oba omulozakobulozi.'''<br />\n'''Tannakazibwa.'''",
        "sitecsspreview": "'''Jukira nti CSS ono omulozakobulozi.'''<br />\n'''Tannakazibwa.'''",
        "sitejspreview": "'''Jukira nti JavaScript ono omulozakobulozi.'''<br />\n'''Tannakazibwa.'''",
-       "userinvalidcssjstitle": "'''Kulabula:''' Tewali endabika eyitibwa \"$1\".<br />\nEmpapula eza .css ne .js bamemba ze b'ekoledde, amannya ga zo ennukuta za mu<br />\nzonna ziteekwa okuba ntono, okugeza ''{{ns:user}}:Foo/vector.css'' so ssi ''{{ns:user}}:Foo/Vector.css''.",
+       "userinvalidconfigtitle": "'''Kulabula:''' Tewali endabika eyitibwa \"$1\".<br />\nEmpapula eza .css ne .js bamemba ze b'ekoledde, amannya ga zo ennukuta za mu<br />\nzonna ziteekwa okuba ntono, okugeza ''{{ns:user}}:Foo/vector.css'' so ssi ''{{ns:user}}:Foo/Vector.css''.",
        "updated": "(Ebituukanisidwa)",
        "note": "'''Okunnyonyola:'''",
        "previewnote": "'''Kuno kugezaamubugeza; by'okoze tebinnakazibwa!'''",
index 5797798..85e6a98 100644 (file)
        "userjspreview": "<strong>'''Lèt op: doe tes noe dien persuunlik JavaScript.\nDe pagina is neet opgesjlage!</strong>",
        "sitecsspreview": "'''Dit is allein 'n veurvertuin van de CSS.'''\n'''Deze is nog neet opgesjlage!'''",
        "sitejspreview": "<strong>Dit is allein 'n veurvertuin van de JavaScriptcode.\nDe code is nog neet opgesjlage!</strong>",
-       "userinvalidcssjstitle": "'''Waarsjoewing:''' d'r is gein skin \"$1\". \nLèt op: dien eige .css- en .js-pagina's beginne mèt  'ne klein lètter, beveurbeeld {{ns:user}}:Naam/vector.css in plaats van {{ns:user}}:Naam/Vector.css.",
+       "userinvalidconfigtitle": "'''Waarsjoewing:''' d'r is gein skin \"$1\". \nLèt op: dien eige .css- en .js-pagina's beginne mèt  'ne klein lètter, beveurbeeld {{ns:user}}:Naam/vector.css in plaats van {{ns:user}}:Naam/Vector.css.",
        "updated": "(Biegewèrk)",
        "note": "'''Opmirking:'''",
        "previewnote": "<strong>Lèt op: dit is 'n controlepazjena; dien teks is nog neet opgesjlage!</strong>",
        "prefs-files": "Bestenj",
        "prefs-custom-css": "Persoonlijke CSS",
        "prefs-custom-js": "Persoonlijke JS",
-       "prefs-common-css-js": "Gedeilde CSS/JS veur eder vormgaeving:",
+       "prefs-common-config": "Gedeilde CSS/JS veur eder vormgaeving:",
        "prefs-reset-intro": "Gebroek dees functie om dien veurkäöre te herstelle nao de standaardinstellinge.\nDees hanjeling kin neet ongedaon gemaak waere.",
        "prefs-emailconfirm-label": "E-mailbevestiging:",
        "youremail": "Dien e-mailadres",
index 58978b9..0b7b526 100644 (file)
        "userlogin-yourname-ph": "Inserisci o teu nómme uténte",
        "createacct-another-username-ph": "Scrivi o teu nomme utente",
        "yourpassword": "Pòula segretta:",
-       "userlogin-yourpassword": "Inserisci a teu ciâve",
+       "userlogin-yourpassword": "Ciâve",
        "userlogin-yourpassword-ph": "Scrivi a tu poula segretta.",
        "createacct-yourpassword-ph": "Scrivi 'na poula segretta.",
        "yourpasswordagain": "Riscrivi a pòula segrétta:",
        "userjspreview": "'''Questa a l'è solo un'anteprimma do proppio  JavaScript personâ. E modiffiche no son ancon stæte sarvæ!'''",
        "sitecsspreview": "'''Questa a l'è solo un'anteprimma do CSS. E modiffiche no son ancon stæte sarvæ!'''",
        "sitejspreview": "'''Questa a l'è solo un'anteprimma pe provâ o JavaScript; e modiffiche no son ancon stæte sarvæ!'''",
-       "userinvalidcssjstitle": "<strong>Attension:</strong> no existe arcun tema con nomme \"$1\". E paggine pe-i .css e .js personalizzæ g'han o tittolo minuscolo, presempio {{ns:user}}:Esempio/vector.css e no {{ns:user}}:Esempio/Vector.css.",
+       "userinvalidconfigtitle": "<strong>Attension:</strong> no existe arcun tema con nomme \"$1\". E paggine pe-i .css e .js personalizzæ g'han o tittolo minuscolo, presempio {{ns:user}}:Esempio/vector.css e no {{ns:user}}:Esempio/Vector.css.",
        "updated": "(Aggiornòu)",
        "note": "'''Notta:'''",
        "previewnote": "'''Questa chì a l'è solo 'n'anteprimma; i cangiamenti no son ancon stæti sarvæ!'''",
        "prefs-files": "File",
        "prefs-custom-css": "CSS personalizzou",
        "prefs-custom-js": "JavaScript personalizzou",
-       "prefs-common-css-js": "CSS/JavaScript condiviso pe tutte e pelle:",
+       "prefs-common-config": "CSS/JavaScript condiviso pe tutte e pelle:",
        "prefs-reset-intro": "L'è poscibile doeuviâ sta pagina pe rimpostâ e propie preferençe a quelle predefinie do scito.\nL'opiaçion a no poeu ese annullâ.",
        "prefs-emailconfirm-label": "Conferma de l'e-mail:",
        "youremail": "Indirìsso email:",
index a375bcc..368a641 100644 (file)
        "userjspreview": "'''به یاد داشته باشید که شما فقط دارید جاوااسکریپت کاربری‌تان را امتحان می‌کنید/پیش‌نمایش آن را می‌بینید.'''\n'''این جاوااسکریپت هنوز ذخیره نشده‌است!'''",
        "sitecsspreview": "'''به یاد داشته باشید که شما فقط دارید پیش‌نمایش این سی‌اس‌اس را می‌بینید.'''\n'''این سی‌اس‌اس هنوز ذخیره نشده‌است!'''",
        "sitejspreview": "'''فراموش مکنید که شما فقط دارید پیش‌نمایش سی‌اس‌اس کاربری‌تان را می‌بینید.'''\n'''این سی‌اس‌اس هنوز ذخیره نشده‌است!'''",
-       "userinvalidcssjstitle": "'''هشدار:''' پوسته‌ای به نام «$1» وجود ندارد.\nبه یاد داشته باشید که صفحه‌های شخصی ‎.css و ‎.js باید عنوانی با حروف کوچک داشته باشند؛ نمونه: {{ns:user}}:فو/vector.css در مقابل {{ns:user}}:فو/Vector.css.",
+       "userinvalidconfigtitle": "'''هشدار:''' پوسته‌ای به نام «$1» وجود ندارد.\nبه یاد داشته باشید که صفحه‌های شخصی ‎.css و ‎.js باید عنوانی با حروف کوچک داشته باشند؛ نمونه: {{ns:user}}:فو/vector.css در مقابل {{ns:user}}:فو/Vector.css.",
        "updated": "(تازة سازی بیة)",
        "note": "'''نکته:'''",
        "previewnote": "'''به یاد داشته باشید که این فقط پیش‌نمایش است.'''\nتغییرات شما هنوز ذخیره نشده‌است!",
        "prefs-files": "فایل",
        "prefs-custom-css": "سی‌اس‌اس شخصی",
        "prefs-custom-js": "جاوااسکریپت شخصی",
-       "prefs-common-css-js": "سی‌اس‌اس/جاوااسکریپت مشترک برای تمام پوسته‌ها:",
+       "prefs-common-config": "سی‌اس‌اس/جاوااسکریپت مشترک برای تمام پوسته‌ها:",
        "prefs-reset-intro": "شما می‌توانید از این صفحه برای بازگرداندن تنظیمات خود به پیش‌فرض تارنما استفاده کنید.\nاین کار بازگشت‌ناپذیر است.",
        "prefs-emailconfirm-label": "تأیید ایمیل:",
        "youremail": "ایمیل:",
index 6f924de..7762da1 100644 (file)
        "prefs-files": "Archivi",
        "prefs-custom-css": "CSS personalizàt",
        "prefs-custom-js": "JavaScript personalizat",
-       "prefs-common-css-js": "CSS/JavaScript cundivis per töte le skin:",
+       "prefs-common-config": "CSS/JavaScript cundivis per töte le skin:",
        "prefs-emailconfirm-label": "Cunferma de l'e-mail:",
        "youremail": "E-mail",
        "username": "{{GENDER:$1|Nom ütent}}:",
index dbd23a4..c77099f 100644 (file)
@@ -54,8 +54,8 @@
        "tog-norollbackdiff": "فأرخیا نە د یئ گئل ڤادئما رأتئن د بئین بوریت",
        "tog-useeditwarning": "د گاتی کئ آلئشتیا ئمایە نأبینە د بألگە ڤیرایئشت ڤئ جا مئ نئم مئنە ڤارئسیاری بأک",
        "tog-prefershttps": "هأمیشە د گاتی کئ مئ هام د ساموٙنە پئیڤأند أمن نە ڤئ کار بئیر",
-       "underline-always": "هأمیشە",
-       "underline-never": "هیژڤأخت",
+       "underline-always": "همیشٱ",
+       "underline-never": "هیژۋخت",
        "underline-default": "پوٙسە یا دوڤارتە نییأر پیش فأرض",
        "editfont-style": "راساگه فونت شیڤات نە ڤیرایئشت کو",
        "editfont-monospace": "فونت تأک بألگە یی",
        "newarticle": "تازە",
        "newarticletext": "شوما هایین ڤا دئما هوم پئیڤأندی کئ ڤوجوٙد نارە.\nسی رأڤأندیاری بألگە.شوروٙ بأکیت مینئ جأڤە هاری بأنیسیت (سی دوٙنئسئن بیشتئر سئیل [$1 ] بأکیت).\nأر شوما سی ئشتئڤا کئردئن هائیت ئیچئ، ری دوگمە ڤادئما رأتئن دوڤارتە نیأر بأپوٙرنیت.",
        "anontalkpagetext": "----",
-       "noarticletext": "د Ø¦Û\8cسئÙ\86Û\8cا Ø¯ Ø¦Û\8c Ø¨Ø£Ù\84گؤ Ù\86Û\8cسئسÛ\95 Û\8cÛ\8c Ú¤Ù\88جÙ\88Ù\99د Ù\86اشتÛ\95.\nØ´Ù\88Ù\85ا Ù\85Û\8c ØªÙ\88Ù\99Ù\86Û\8cت Ø¯[[Special:Search/{{PAGENAME}}|بگردÛ\8cد]] Ø¯ Ø¦Û\8c Ø¨Ø£Ù\84Ú¯Û\95 Û\8cا Ø¯ Ø¨Ø£Ù\84Ú¯Û\95 Ù\87Ø£Ù\86Û\8c Û\8cا <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} Ø¯ Ù\87Ø£Ù\86Û\8c Ù\86Û\95 Ù¾Ø¦Û\8c Ø¬Ù\88رÛ\8c Ø¨Ù\88Ù\99Û\95]</span>Ø\8c <span class=\"plainlinks\">[{{fullurl:{{FULLPAGENAME}}|action=edit}} Û\8cا Ø¦Û\8c Ø¨Ø£Ù\84Ú¯Û\95 Ù\86Û\95 Ú¤Û\8cراÛ\8cئشت Ø¨Ø£کیت]</span>.",
-       "noarticletext-nopermission": "د Ø¦Û\8cسئÙ\86Û\8cا Ø¯ Ø¦Û\8c Ø¨Ø£Ù\84گؤ Ù\86Û\8cسئسÛ\95 Û\8cÛ\8c Ú¤Ù\88جÙ\88Ù\99د Ù\86اشتÛ\95.\nØ´Ù\88Ù\85ا Ù\85Û\8c ØªÙ\88Ù\99Ù\86Û\8cت Ø¯[[Special:Search/{{PAGENAME}}|بگردÛ\8cد]] Ø¯ Ø¦Û\8c Ø¨Ø£Ù\84Ú¯Û\95 Û\8cا Ø¯ Ø¨Ø£Ù\84Ú¯Û\95 Ù\87Ø£Ù\86Û\8c Û\8cا <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} Ø¯ Ù\87Ø£Ù\86Û\8c Ù\86Û\95 Ù¾Ø¦Û\8c Ø¬Ù\88رÛ\8c Ø¨Ù\88Ù\99Û\95]</span>Ø\8c <span class=\"plainlinks\">[{{fullurl:{{FULLPAGENAME}}|action=edit}}</span>.ڤأÙ\84Û\8c Ø´Ù\88Ù\85ا ØµØ¦Ù\84ا Û\8cÛ\95 Ù\86Û\95 Ú©Ø¦ Ø¦Û\8c Ø¨Ø£Ù\84Ú¯Û\95 Ù\86Û\95 Ø±Ø§Ø³ Ø¨Ø£کیت ناریت.",
+       "noarticletext": "د Ø§Û\8cساÙ\9bÙ\86Û\8cا Ø§Û\8c Ø¨Ù\84Ú¯Ù± Ù\86Û\8cسسٱ Û\8bÙ\88جÛ\8aد Ù\86اشتٱ.\nØ´Ù\88Ù\85ا Ù\85Û\8c ØªÛ\8aÙ\86Û\8cت Ø¯[[Special:Search/{{PAGENAME}}|بگردÛ\8cد]] Ø¯ Ø§Û\8c Ø¨Ù\84Ú¯Ù± Ø§Û\8c Ø¯ Ø¨Ù\84Ú¯Ù± Ù\87Ù±Ù\86Û\8c Û\8cا <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} Ø¯ Ù\86Û\8f Ù¾Û\8c Ø¬Û\8aرÛ\8c Ø¨Û\8aÙ±]</span>Ø\8c <span class=\"plainlinks\">[{{fullurl:{{FULLPAGENAME}}|action=edit}} Û\8cا Ø§Û\8c Ø¨Ù\84Ú¯Ù± Ù\86اÙ\9b Û\8bÛ\8cراÛ\8cشت Ø¨Ù±کیت]</span>.",
+       "noarticletext-nopermission": "د Ø§Û\8cساÙ\9bÙ\86Û\8cا Ø§Û\8c Ø¨Ù\84Ú¯Ù± Ù\86Û\8cسسٱ Û\8cÛ\8c Û\8bÙ\88جÛ\8aد Ù\86اشتٱ.\nØ´Ù\88Ù\85ا Ù\85Û\8c ØªÛ\8aÙ\86Û\8cت Ø¯[[Special:Search/{{PAGENAME}}|بگردÛ\8cد]] Ø¯ Ø§Û\8c Ø¨Ù\84Ú¯Ù± Û\8cا Ø¯ Ø¨Ù\84Ú¯Ù± Ù\87Ù±Ù\86Û\8c Û\8cا <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} Ø¯ Ù\86Û\8f Ù¾Û\8c Ø¬Û\8aرÛ\8c Ø¨Û\8aÙ±]</span>Ø\8c <span class=\"plainlinks\">[{{fullurl:{{FULLPAGENAME}}|action=edit}}</span>.Û\8bÙ\84Û\8c Ø´Ù\88Ù\85ا Ø³Ø§Ù\9bÙ\84ا Û\8cÙ± Ù\86Ù± Ú©Ø§Ù\9b Ø§Û\8c  Ø¨Ù\84Ú¯Ù± Ù\86اÙ\9b Ø±Ø§Ø³ Ø¨Ù±کیت ناریت.",
        "missing-revision": "ڤانئیأری #$1 د بألگە یی کئ نومئش ڤئنە \"{{FULLPAGENAME}}\" ڤوجوٙد نارە.\n\nگاسی سی یئ گئل ڤیرگار ڤئ هئنگوم نأبییە کئ د یئ گئل بألگە پاکسا بییە هوم پئیڤأند بییە رأڤأندیاری بییە.\nگاسی جوزئیات د[{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} deletion log] دیاری بأکأن.",
        "userpage-userdoesnotexist": "حئساڤ کاریاری \"$1\" ثأڤت نأم نأبییە.\nأر میھایت ئی بألگە نئ بأسازیت یا ڤیرایئشت کاری بأکیت یئ گئل ڤارئسی أنجوم بئیتوٙ.",
        "userpage-userdoesnotexist-view": "حئساڤ کاریاری \"$1\" ثأڤت نأبییە.",
        "userjspreview": "<strong>د ڤیر داشتوٙئیت کئ شوما فأقأط می توٙنیت جاڤا ئسکئریپت کاریاری توٙنە پیش سئیل بأکیت. ڤئ ئیسئنی ئمایە نأبییە!</strong>",
        "sitecsspreview": "<strong>د ڤیر داشتوٙئیت کئ شوما فأقأط می توٙنیت ئی سی ئس ئس کاریاری توٙنە پیش سئیل بأکیت. ڤئ ئیسئنی ئمایە نأبییە!</strong>",
        "sitejspreview": "<strong>د ڤیر داشتوٙئیت کئ شوما فأقأط می توٙنیت ئی جاڤا ئسکئریپت کاریاری توٙنە پیش سئیل بأکیت. ڤئ ئیسئنی ئمایە نأبییە!</strong>",
-       "userinvalidcssjstitle": "<strong>زئنار:</strong> پوٙسە \"$1\" نیئش.\nسی ئس ئس جاڤأنە و بألگە یا جاڤا ئسکئریپت سأربألگ ڤا حأرفیا کوچئک نە ڤئ کار گئرئتە، ھأمچئنی {{ns:کاریار}}:فو/ڤئکتور.سی ئس ئس چی د ری ڤئ ری {{ns:کاریار}}:فو/ڤئکتور. سی ئس ئسە.",
+       "userinvalidconfigtitle": "<strong>زئنار:</strong> پوٙسە \"$1\" نیئش.\nسی ئس ئس جاڤأنە و بألگە یا جاڤا ئسکئریپت سأربألگ ڤا حأرفیا کوچئک نە ڤئ کار گئرئتە، ھأمچئنی {{ns:کاریار}}:فو/ڤئکتور.سی ئس ئس چی د ری ڤئ ری {{ns:کاریار}}:فو/ڤئکتور. سی ئس ئسە.",
        "updated": "(ڤئ هئنگوم سازی بییە)",
        "note": "'''نیسأنئن:'''",
        "previewnote": "فأقأط ئی پیش سئیل د ڤیرتوٙ با.\nآلئشت کاریاتوٙ ھأنی ئمایە نأبینە",
        "difference-title": "فرخ مینجا وانیریا \"$1\"",
        "difference-title-multipage": "فرخ مینجا بلگه یا \"$1\" و \"$2\"",
        "difference-multipage": "(فرخ مینجا بلگه یا)",
-       "lineno": "خط $1:",
+       "lineno": "خٱت $1:",
        "compareselectedversions": "دوبار دیئنیایی که انتخاو بینه مقایسه بکیتو",
        "showhideselectedversions": "شلک دیئن وانیریا انتخاو بیه نه آلشت بکید",
        "editundo": "ناانجومگر کردن",
        "searchprofile-advanced-tooltip": "نوم جايا نوم ديار بگرد",
        "search-result-size": "$1 ({{PLURAL:$2|1 کلیمه|$2 کلیمه یا}})",
        "search-result-category-size": "{{PLURAL:$1|1 أندوم|$1 أندومیا}} ({{PLURAL:$2|1 زیردأسە|$2 زیردأسە یا}}، {{PLURAL:$3|1 جانیا|$3 جانیایا}}",
-       "search-redirect": "(ورگشتن $1)",
+       "search-redirect": "(ۋورگشتن سی $1)",
        "search-section": "(بهرجا $1)",
        "search-category": "(دسه $1)",
        "search-file-match": "(یکی کردن مینونه جانیا)",
        "prefs-files": "جانیایا",
        "prefs-custom-css": "سی اس اس جاافتائه",
        "prefs-custom-js": "جاوا نیسسه جاافتائه",
-       "prefs-common-css-js": " سی اس اس/جاوا اسکریپت بهر بیه سی همه پوسه یا:",
+       "prefs-common-config": " سی اس اس/جاوا اسکریپت بهر بیه سی همه پوسه یا:",
        "prefs-reset-intro": "شما می تونیت ای بلگه سی د نو زنه کردن ترجیحات خوت وه شکل تیارگه پیش فرض وه کار بوونیت.\nیه ورئشت پذیر نئ.",
        "prefs-emailconfirm-label": "پش راست کردن انجومانامه:",
        "youremail": "أنجومانامە:",
        "rcshowhidecategorization": "جأرغە کاری بألگە $1",
        "rcshowhidecategorization-show": "نئشوٙ دأئن",
        "rcshowhidecategorization-hide": "قام کئردئن",
-       "rclinks": "آخرین آلشتیا $1 نشو بیه د اخرین روزیا $2",
+       "rclinks": "آخرین آلشتیا $1 د آخرین رۊزیا دیاری بٱک $2",
        "diff": "فأرخ",
        "hist": "ڤیرگار",
        "hide": "قام کئردئن",
        "tooltip-t-recentchangeslinked": "آلشتیا تازه باو مئن بلگيايي كه د ای بلگه هوم پیوند بيئنه",
        "tooltip-feed-rss": "هوال حون آر اس اس سی ای بلگه",
        "tooltip-feed-atom": "حوال هون اتمی سی ای بلگه",
-       "tooltip-t-contributions": "یه نوم گه د هومیاریا ای کارور",
+       "tooltip-t-contributions": "یاٛ گاٛل سیائٱ هومیاری سی {{GENDER:$1|ای کاریار}}",
        "tooltip-t-emailuser": "سی ای كارور ايميل كل كو",
        "tooltip-t-info": "دونسمنیا بیشتر دباره ای بلگه",
        "tooltip-t-upload": "سوارکردن جانیایا",
        "spam_reverting": "واگردونی وه آخری نسقه ای که هوم پیوندی وه $1 ناره.",
        "spam_blanking": "همه وانئریایی که مینونه دار هوم پیوند $1 هئن، دارن حالی بوئن",
        "spam_deleting": "همه وانئریایی که مینونه دار هوم پیوند $1 هئن، دارن پاکساگری بوئن",
-       "simpleantispam-label": "وارسی ضد اسپم.\nای \"جاگه\" نه پر نکیت!",
+       "simpleantispam-label": "ۋارسی ری ۋ ری کاری اٛسپم.\nای \"جاگٱ\" نٱ پور نٱکیت!",
        "pageinfo-title": "دونسمنیا سی \"$1\"",
        "pageinfo-not-current": "د بدبختی،نبوئه که ای دونسمنیا نه سی وانئریا دماتری نهااماییه بکیت.",
        "pageinfo-header-basic": "دونسمنیا پایه",
        "file-info-size": "$1 × $2 پیکسل, انازه فایل: $3, MIME نوع: $4",
        "file-info-size-pages": "$1 × $2 pixels, انازه جانیا: $3, MIME type: $4, $5 {{PLURAL:$5|بلگه|بلگه یا}}",
        "file-nohires": "عسك ون بالاتري دش ني",
-       "svg-long-desc": "اس Ù\88Û\8c Ø¬Û\8c Ø¬Ø§Ù\86Û\8cا.Ù\86Ù\88Ù\85Ù\86 $1 $2 Ù¾Ù\8aÙ\83سÙ\84 $1",
+       "svg-long-desc": "جاÙ\86Û\8cا Ø§Ù\9bس Û\8bÛ\8c Ø¬Û\8c, Ù\86Ù\88Ù\85Û\8c $1 Ã\97 $2 Ù¾Û\8cکسÙ\84, Ù±Ù\86ازٱ Ø¬Ø§Ù\86Û\8cا: $3",
        "svg-long-desc-animated": "جانیا جمشدار اس وی جی .نومنا $1 × $2 پيكسل،انازه جانیا:$3",
        "svg-long-error": "جانیا اس وی جی نامعتور:$1",
        "show-big-image": "جانیا اصلی",
index 873c63c..38488e2 100644 (file)
        "userjspreview": "'''Nepamirškite, kad jūs tik testuojat/peržiūrit savo naudotojo JavaScript, jis dar nebuvo išsaugotas!'''",
        "sitecsspreview": "'''Nepamirškite, kad jūs tik peržiūrit šio CSS .'''! N!''' Tai dar nebuvo išsaugotas!'''",
        "sitejspreview": "'''Nepamirškite, kad jūs tik peržiūrit šis JavaScript kodas .'''! N!''' Tai dar nebuvo išsaugotas!'''",
-       "userinvalidcssjstitle": "'''Dėmesio:''' Nėra jokios išvaizdos „$1“. Nepamirškite, kad savo .css ir .js puslapiai naudoja pavadinimą mažosiomis raidėmis, pvz., {{ns:user}}:Foo/vector.css, o ne {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "'''Dėmesio:''' Nėra jokios išvaizdos „$1“. Nepamirškite, kad savo .css ir .js puslapiai naudoja pavadinimą mažosiomis raidėmis, pvz., {{ns:user}}:Foo/vector.css, o ne {{ns:user}}:Foo/Vector.css.",
        "updated": "(Atnaujinta)",
        "note": "'''Pastaba:'''",
        "previewnote": "<strong>Nepamirškite, kad tai tik peržiūra, pakeitimai dar nėra išsaugoti!</strong>",
        "storedversion": "Išsaugota versija",
        "editingold": "<strong>Įspėjimas: Jūs keičiate ne naujausią puslapio versiją.</strong> Jei išsaugosite savo keitimus, po to daryti pakeitimai pradings.",
        "yourdiff": "Skirtumai",
-       "copyrightwarning": "Primename, kad viskas, kas patenka į {{SITENAME}}, yra laikoma paskelbtu pagal $2 (detaliau - $1). Jei nenorite, kad jūsų indėlis būtų be gailesčio redaguojamas ir platinamas, čia nerašykite.<br />\nJūs taip pat pasižadate, kad tai jūsų pačių rašytas turinys arba kopijuotas iš viešų ar panašių nemokamų šaltinių.\n'''NEKOPIJUOKITE AUTORINĖMIS TEISĖMIS APSAUGOTŲ DARBŲ BE LEIDIMO!'''",
-       "copyrightwarning2": "Primename, kad viskas, kas patenka į {{SITENAME}} gali būti redaguojama, perdaroma, ar pašalinama kitų naudotojų. Jei nenorite, kad jūsų indėlis būtų be gailesčio redaguojamas, čia nerašykite.<br />\nTaip pat jūs pasižadate, kad tai jūsų pačių rašytas tekstas arba kopijuotas\niš viešų ar panašių nemokamų šaltinių (detaliau - $1).\n'''NEKOPIJUOKITE AUTORINĖMIS TEISĖMIS APSAUGOTŲ DARBŲ BE LEIDIMO!'''",
+       "copyrightwarning": "Primename, kad viskas, kas patenka į {{SITENAME}}, yra skelbiama pagal $2 (plačiau – $1). Jei nenorite, kad jūsų indėlis būtų be gailesčio kaitaliojamas ir platinamas, nerašykite čia.<br />\nJūs taip pat pasižadate, kad tai jūsų pačių rašytas turinys arba kopijuotas iš viešų ar panašių nemokamų šaltinių.\n<strong>Nekopijuokite autorinėmis teisėmis apsaugotų darbų be leidimo!</strong>",
+       "copyrightwarning2": "Primename, kad viskas, kas patenka į {{SITENAME}} gali būti keičiama, perdaroma, ar pašalinama kitų naudotojų. Jei nenorite, kad jūsų indėlis būtų be gailesčio kaitaliojamas, nerašykite čia.<br />\nTaip pat jūs pasižadate, kad tai jūsų pačių rašytas tekstas arba kopijuotas iš viešų ar panašių nemokamų šaltinių (plačiau - $1).\n<strong>Nekopijuokite autorinėmis teisėmis apsaugotų darbų be leidimo!</strong>",
        "editpage-cannot-use-custom-model": "Šio puslapio turinio modelis negali būti pakeistas.",
        "longpageerror": "'''KLAIDA: Tekstas, kurį pateikėte, yra $1 {{PLURAL:$1|kilobaito|kilobaitų|kilobaitų}} ilgio, tai yra didesnis nei yra leistina. Yra leidžiami tiktai $2 {{PLURAL:$2|kilobaitas|kilobaitai|kilobaitų}}.''' Jis nebus išsaugotas.",
        "readonlywarning": "<strong>Įspėjimas: Duomenų bazė buvo užrakinta techninei profilaktikai, todėl šiuo metu negalėsite išsaugoti savo pakeitimų. Siūlome nusikopijuoti tekstą į tekstinį failą ir vėliau jį čia išsaugoti.</strong>\n\nJą užrakinusio sistemos administratoriaus paaiškinimas: $1",
        "revdelete-hide-text": "Versijos tekstas",
        "revdelete-hide-image": "Slėpti failo turinį",
        "revdelete-hide-name": "Slėpti veiksmą ir paskirtį",
-       "revdelete-hide-comment": "Keitimo komentaras",
+       "revdelete-hide-comment": "Keitimo aprašas",
        "revdelete-hide-user": "Redagavusiojo naudotojo vardas/IP adresas",
        "revdelete-hide-restricted": "Nuslėpti duomenis nuo adminstratorių kaip ir nuo kitų",
        "revdelete-radio-same": "(nekeisti)",
        "prefs-files": "Failai",
        "prefs-custom-css": "Asmeninis CSS",
        "prefs-custom-js": "Asmeninis JavaSript",
-       "prefs-common-css-js": "Bendras CSS/JS visoms išvaizdoms:",
+       "prefs-common-config": "Bendras CSS/JS visoms išvaizdoms:",
        "prefs-reset-intro": "Jūs galite pasinaudoti šiuo puslapiu, kad grąžintumėte savo nustatymus į svetainės numatytuosius.\nTai nebeatšaukiama.",
        "prefs-emailconfirm-label": "El. pašto patvirtinimas:",
        "youremail": "El. paštas:",
        "protect-othertime": "Kitas laikas:",
        "protect-othertime-op": "kitas laikas",
        "protect-existing-expiry": "Esamas galiojimo laikas: $3, $2",
-       "protect-existing-expiry-infinity": "Esamas galiojimo laikas: begalinis",
+       "protect-existing-expiry-infinity": "Esamas galiojimo laikas: neribotas",
        "protect-otherreason": "Kita/papildoma priežastis:",
        "protect-otherreason-op": "Kita priežastis",
        "protect-dropdown": "*Įprastos užrakinimo priežastys\n** Intensyvus vandalizmas\n** Nuolatinis nepageidautinų nuorodų dėliojimas\n** Beprasmis redagavimo karas\n** Didelės svarbos puslapis\n** Pakartotinis ištrinto puslapio atkūrinėjimas",
        "import-error-interwiki": "Puslapis \"$1\" nebuvo importuotas, nes jo pavadinimas yra rezervuotas išorinei nuorodai (interviki).",
        "import-error-special": "Puslapis „$1“ nebuvo importuotas, nes jis priklauso specialiai vardų sričiai, kurioje neleidžiami puslapiai.",
        "import-error-invalid": "Puslapis \"$1\" nebuvo įkeltas, kadangi jo vardas yra neteisingas.",
-       "import-error-unserialize": "Versija $2 puslapio „$1“ negali būti nepublikuota. Versija buvo pranešta dėl turinio modelio $3 naudojimo publikuoti kaip $4.",
+       "import-error-unserialize": "Puslapio „$1“ versija $2 negali būti struktūruota. Gautas pranešimas, kad šioje atmainoje naudojamas turinio pavyzdys yra $3, kuris struktūruojamas kaip $4.",
        "import-error-bad-location": "Versija $2 naudojanti turinio modelį $3 negali būti laikoma „$1“ šioje viki, nes šis modelis nėra palaikomas tame puslapyje.",
        "import-options-wrong": "Netinka {{PLURAL:$2|pasirinktis|pasirinktys}}: <nowiki>$1</nowiki>",
        "import-rootpage-invalid": "Duotas šaknų puslapis yra blogas pavadinimas.",
        "exif-morepermissionsurl": "Alternatyvi licencijavimo informacija",
        "exif-attributionurl": "Kai pakartotinai naudojate ši darbą, prašome nurodyti į",
        "exif-preferredattributionname": "Kai naudojate ši darbą prašome nurodyti",
-       "exif-pngfilecomment": "JPEG failo komentaras",
+       "exif-pngfilecomment": "Pastabos dėl PNG rinkmenos",
        "exif-disclaimer": "Atsakomybės apribojimas",
        "exif-contentwarning": "Turinio įspėjimas",
        "exif-giffilecomment": "GIF rinkmenos paaiškinimas",
index c68c667..8cfa6cf 100644 (file)
        "prefs-files": "Taksa",
        "prefs-custom-css": "CSS hman",
        "prefs-custom-js": "JavaScript hman",
-       "prefs-common-css-js": "CSS inţawm/vun zawng zawng tana JavaScript",
+       "prefs-common-config": "CSS inţawm/vun zawng zawng tana JavaScript",
        "prefs-reset-intro": "He phêk hi ränghmuna i duhthlansa tihdanglam nan i hmang thei.\nA sûtlêt theih loh.",
        "prefs-emailconfirm-label": "E-chenhmun tihchianna:",
        "youremail": "E-chenhmun:",
index 96c7c65..f56d350 100644 (file)
        "history": "hronoloģija",
        "history_short": "Vēsture",
        "history_small": "vēsture",
-       "updatedmarker": "atjaunināti kopš pēdējā apmeklējuma",
+       "updatedmarker": "atjaunināts kopš mana pēdējā apmeklējuma",
        "printableversion": "Drukājama versija",
        "permalink": "Pastāvīgā saite",
        "print": "Drukāt",
        "prefs-files": "Faili",
        "prefs-custom-css": "Personīgais CSS",
        "prefs-custom-js": "Personīgais JS",
-       "prefs-common-css-js": "Koplietojams CSS/JavaScript visās apdarēs:",
+       "prefs-common-config": "Koplietojams CSS/JavaScript visās apdarēs:",
        "prefs-emailconfirm-label": "E-pasta statuss:",
        "youremail": "Tava e-pasta adrese:",
        "username": "{{GENDER:$1|Lietotājvārds}}:",
index e121124..389dedd 100644 (file)
        "userjspreview": "'''預覽簿JavaScript。'''\n'''尚未儲焉!'''",
        "sitecsspreview": "'''預覽此CSS。'''\n'''尚未儲焉!'''",
        "sitejspreview": "'''預覽此JavaScript。'''\n'''尚未儲焉!'''",
-       "userinvalidcssjstitle": "'''警:'''\"$1\"無此面版。自製者,全名務小寫,如{{ns:user}}:Foo/vector.css 而非{{ns:user}}:Foo/Vector.css",
+       "userinvalidconfigtitle": "'''警:'''\"$1\"無此面版。自製者,全名務小寫,如{{ns:user}}:Foo/vector.css 而非{{ns:user}}:Foo/Vector.css",
        "updated": "(新)",
        "note": "'''註'''",
        "previewnote": "'''此乃預覽,尚未儲焉。'''",
        "prefs-files": "檔",
        "prefs-custom-css": "定之CSS",
        "prefs-custom-js": "定之JavaScript",
-       "prefs-common-css-js": "共CSS/JavaScript於面版:",
+       "prefs-common-config": "共CSS/JavaScript於面版:",
        "prefs-reset-intro": "爾用頁重設至預之設。無修之也。",
        "prefs-emailconfirm-label": "確郵:",
        "youremail": "郵:",
index f76bfc0..8cc626b 100644 (file)
        "userjspreview": "''' मोन राखू जे अहाँ मात्र अपन प्रयोक्ता  जावास्क्रिप्टक पूर्वदृश्य देख रहल छी।'''\n''' ई अखन धरि संरक्षित नै भऽ सकल!'''",
        "sitecsspreview": "''' मोन राखू जे अहाँ मात्र ऐ  सी.एस.एस. क पूर्वदृश्य देख रहल छी।'''\n''' ई अखन धरि संरक्षित नै भऽ सकल!'''",
        "sitejspreview": "''' मोन राखू जे अहाँ मात्र ऐ  जावास्क्रिप्टक पूर्वदृश्य देख रहल छी।'''\n''' ई अखन धरि संरक्षित नै भऽ सकल!'''",
-       "userinvalidcssjstitle": "'''चेतौनी:''' ऐ मे कोनो आवरण \"$1\" नै अछि।\nबनाएल .css आ .js पन्ना लघ्वक्षरक शीर्षकक प्रयोग करैत अछि, जेना {{ns:user}}:Foo/vector.css एकर विरुद्ध {{ns:user}}:Foo/Vector.css ।",
+       "userinvalidconfigtitle": "'''चेतौनी:''' ऐ मे कोनो आवरण \"$1\" नै अछि।\nबनाएल .css आ .js पन्ना लघ्वक्षरक शीर्षकक प्रयोग करैत अछि, जेना {{ns:user}}:Foo/vector.css एकर विरुद्ध {{ns:user}}:Foo/Vector.css ।",
        "updated": "(अद्यतन  कएल)",
        "note": "<strong>टिप्पणी:</strong>",
        "previewnote": "<strong>मोन राखू ई मात्र पूर्वावलोकन छी।</strong>\nअहाँक परिवर्तन अखन धरि सङ्ग्रह नै कएल गेल अछि!",
        "prefs-files": "सञ्चिकासभ",
        "prefs-custom-css": "खास सियसयस",
        "prefs-custom-js": "खास जावास्क्रिप्ट",
-       "prefs-common-css-js": "सभ रूप लेल साझी सी.एस.एस./ जावास्क्रिप्ट:",
+       "prefs-common-config": "सभ रूप लेल साझी सी.एस.एस./ जावास्क्रिप्ट:",
        "prefs-reset-intro": "अहाँ ऐ पन्नाक प्रयोग अपन विकल्पकेँ पूर्वनिविष्ट रूपेँ जाल पुनर्निधारित करबा लेल कऽ सकै छी।\nई बदलल नै जा सकैए।",
        "prefs-emailconfirm-label": "ई-पत्र पुष्टि:",
        "youremail": "ई-पत्र:",
index 72dcde6..7a3e818 100644 (file)
        "userjspreview": "'''Eling ya Rika kuwe mung lagi nampilna pratayang sekang JavaScript-e Rika.'''\n'''Pratayang kiye anu durung disimpen!'''",
        "sitecsspreview": "'''Eling ya Rika kuwe mung lagi nampilna pratayang sekang CSS kiye.'''\n'''Pratayang kiye anu durung disimpen!'''",
        "sitejspreview": "'''Eling ya Rika kuwe mung lagi nampilna pratayang sekang kode JavaScript kiye.'''\n'''Pratayang kiye anu durung disimpen!'''",
-       "userinvalidcssjstitle": "'''Pènget:''' Kulit \"$1\" ora ditemokna. Eling ya nek kaca .css lan .js kuwe nggunakna aksara cilik, conto {{ns:user}}:Foo/vector.css lan dudu {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "'''Pènget:''' Kulit \"$1\" ora ditemokna. Eling ya nek kaca .css lan .js kuwe nggunakna aksara cilik, conto {{ns:user}}:Foo/vector.css lan dudu {{ns:user}}:Foo/Vector.css.",
        "updated": "(Dianyari)",
        "note": "'''Cathetan:'''",
        "previewnote": "'''Eling ya kiye tembe pratayang thok.'''\nOwahane Rika durung disimpen!",
        "prefs-files": "Berkas",
        "prefs-custom-css": "CSS pribadi",
        "prefs-custom-js": "JavaScript pribadi",
-       "prefs-common-css-js": "Mbagekna CSS/JavaScript nggo kabeh kulit:",
+       "prefs-common-config": "Mbagekna CSS/JavaScript nggo kabeh kulit:",
        "prefs-reset-intro": "Rika teyeng nggunakna kaca kiye nggo mbalekna preferensi-ne Rika balik maring setelan baku situs.\nPambalikan kiye ora teyeng dibatalna.",
        "prefs-emailconfirm-label": "Konfirmasi imel:",
        "youremail": "Imel:",
index 33f6892..0195f74 100644 (file)
        "userjsyoucanpreview": "'''Мялень максома:''' Ванфтомада инголе нолдак тевс 'Васень няфтема' пунять тонь од CSS эли JS файлть варжаманкса.",
        "usercsspreview": "Киртть мяльсот тя аньцек тонь CSS файлцень васень няфтемац. Сон нинге изь ванфтов!'''",
        "userjspreview": "'''Киртть мяльсот тя аньцек тонь JavaScript файлть варжамась/васень няфтемась, сон нинге изь ванфтов!'''",
-       "userinvalidcssjstitle": "'''Инголе мярьгома:''' Аш тема файл \"$1\" мазопнеманкса. Киртть мяльсот .css эди .js лопас путови аньцек ёмла тяшкса коняксне, кепотьксонди {{ns:user}}:Foo/лем.css афи {{ns:user}}:Foo/Лем.css.",
+       "userinvalidconfigtitle": "'''Инголе мярьгома:''' Аш тема файл \"$1\" мазопнеманкса. Киртть мяльсот .css эди .js лопас путови аньцек ёмла тяшкса коняксне, кепотьксонди {{ns:user}}:Foo/лем.css афи {{ns:user}}:Foo/Лем.css.",
        "updated": "(Одонзаф)",
        "note": "'''Шарфтк мяльце:'''",
        "previewnote": "'''Киртть мяльсот, тя аньцек васень няфтемась.''' Тонь полафтоматне нинге исть ванфтов!",
index c775763..f626f0c 100644 (file)
        "userjspreview": "\n'''Tadidio fa manandrana/mijery tsipalotra ny fivoakan'ny JavaScript-nao fotsiny ihany ianao fa tsy mbola voatahiry akory izy io!'''",
        "sitecsspreview": "'''Fantaro fa manao topi-maso an'ity CSS ity fotsiny ianao'''.\n'''Mbola tsy voatahiry ilay izy !'''",
        "sitejspreview": "'''Tadidio fa manao topi-maso renifango JavaScript ianao.'''\n'''Mbola tsy notahirizina izy io !'''",
-       "userinvalidcssjstitle": "'''Tandremo''' : Tsy misy fampiankanjoana « $1 » izany.\nTadidio fa mampiasa soramadinika ny lohatenin'ny pejinao manan-tovana *.css sy *.js, ohatra {{ns:user}}:Foo/vector.css fa tsy {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "'''Tandremo''' : Tsy misy fampiankanjoana « $1 » izany.\nTadidio fa mampiasa soramadinika ny lohatenin'ny pejinao manan-tovana *.css sy *.js, ohatra {{ns:user}}:Foo/vector.css fa tsy {{ns:user}}:Foo/Vector.css.",
        "updated": "(Nohavaozina)",
        "note": "'''Fanamarihana:'''",
        "previewnote": "'''Fantaro fa topi-maso fotsiny ity.'''\nMbola tsy voatahiry ny fanovanao !",
        "prefs-files": "Rakitra",
        "prefs-custom-css": "CSS manokana",
        "prefs-custom-js": "Javascript manokana",
-       "prefs-common-css-js": "JavaScript ary CSS zaraina ho an'ny fiankanjoana rehetra:",
+       "prefs-common-config": "JavaScript ary CSS zaraina ho an'ny fiankanjoana rehetra:",
        "prefs-reset-intro": "Azonao ampiasaina ity pejy ity mba hamerina ny safidinao amin'izay safidy tsipalotr'ilay sehatra. Tsy azo averina io.",
        "prefs-emailconfirm-label": "Famarinana ny imailaka :",
        "youremail": "Imailaka:",
index d34ce7e..d525160 100644 (file)
        "userjspreview": "'''Ingeklah bahawa nan Sanak liek hanyolah pratayang JavaScript Sanak, dan bahawa pratayang tasabuik alun disimpan!'''",
        "sitecsspreview": "'''Ingeklah bahawa Sanak hanyo manampilan pratayang dari CSS iko.'''\n'''Parubahan alun disimpan!'''",
        "sitejspreview": "<strong>Ingek! Sanak hanyo manampilan pratonton kode JavaScript ko. Parubahan alun basimpan!</strong>",
-       "userinvalidcssjstitle": "'''Paringatan:''' Kulik \"$1\" indak ditamuan. Harap diingek bahawa laman .css dan .js manggunokan huruf kecil, contoh {{ns:user}}:Foo/vector.css dan bukannyo {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "'''Paringatan:''' Kulik \"$1\" indak ditamuan. Harap diingek bahawa laman .css dan .js manggunokan huruf kecil, contoh {{ns:user}}:Foo/vector.css dan bukannyo {{ns:user}}:Foo/Vector.css.",
        "updated": "(Dipabaharui)",
        "note": "'''Catatan:'''",
        "previewnote": "'''Ingek iko hanyo pratonton'''\nParubahan Sanak alun disimpan!",
        "prefs-files": "Berkas",
        "prefs-custom-css": "CSS paribadi",
        "prefs-custom-js": "JS paribadi",
-       "prefs-common-css-js": "CSS/JS untuak kasado kulik:",
+       "prefs-common-config": "CSS/JS untuak kasado kulik:",
        "prefs-reset-intro": "Angku dapek manggunokan laman ko untuak mangambalikan pangaturan ka setelan baku situs ko.\nPangambalian pangaturan indak dapek dibatalan.",
        "prefs-emailconfirm-label": "Surel konfirmasi:",
        "youremail": "Surel:",
index ae3fc10..a1f615a 100644 (file)
@@ -21,7 +21,8 @@
                        "Matma Rex",
                        "Kaldari",
                        "Xð",
-                       "Srdjan m"
+                       "Srdjan m",
+                       "Acamicamacaraca"
                ]
        },
        "tog-underline": "Потцртување на врски:",
        "userjspreview": "'''Запомнете дека ова е само преглед на вашиот JavaScript код, страницата сè уште не е зачувана!'''",
        "sitecsspreview": "'''Запомнете дека ова е само преглед на овој CSS-код.'''\n'''Сè уште не е зачуван!'''",
        "sitejspreview": "'''Запомнете дека ова е само преглед на овој JavaScript-код.'''\n'''Сè уште не е зачуван!'''",
-       "userinvalidcssjstitle": "'''Предупредување:''' Нема руво „$1“.\nЗапомнете дека сопствените .css и .js страници имаат имиња со мали букви, пр. {{ns:user}}:Некој/vector.css наместо {{ns:user}}:Некој/Vector.css.",
+       "userinvalidconfigtitle": "'''Предупредување:''' Нема руво „$1“.\nЗапомнете дека сопствените .css и .js страници имаат имиња со мали букви, пр. {{ns:user}}:Некој/vector.css наместо {{ns:user}}:Некој/Vector.css.",
        "updated": "(Подновено)",
        "note": "'''Напомена:'''",
        "previewnote": "'''Имајте предвид дека ова е само преглед.'''\nПромените сè уште не се зачувани!",
        "prefs-files": "Податотеки",
        "prefs-custom-css": "Посебно CSS",
        "prefs-custom-js": "Посебно JS",
-       "prefs-common-css-js": "Заеднички CSS/JS за сите изгледи:",
+       "prefs-common-config": "Заеднички CSS/JS за сите изгледи:",
        "prefs-reset-intro": "Може да ја користите оваа страница за враќање на вашите нагодувања на основно-зададените нагодувања на викито. Ова дејство е неповратно.",
        "prefs-emailconfirm-label": "Потврда на е-пошта:",
        "youremail": "Е-пошта:",
        "thumbnail_dest_directory": "Целниот именик не може да се создаде",
        "thumbnail_image-type": "Неподдржан тип на слика",
        "thumbnail_gd-library": "Нецелосни поставки на графичката библиотека: недостасува функцијата $1",
+       "thumbnail_image-size-zero": "Големината на сликата е нула.",
        "thumbnail_image-missing": "Изгледа дека податотеката недостасува: $1",
        "thumbnail_image-failure-limit": "Направив премногу обиди ($1 или повеќе) за да ја прикажам минијатурава. Обидете се подоцна.",
        "import": "Увезување на страници",
index 7f068cd..1706d9d 100644 (file)
        "userjspreview": "'''താങ്കൾ താങ്കളുടെ സ്വന്തം ജാവസ്ക്രിപ്റ്റ് പ്രിവ്യൂ ചെയ്യുക മാത്രമേ ചെയ്യുന്നുള്ളൂ എന്ന കാര്യം ഓർമ്മിക്കുക. ഇതു സേവ് ചെയ്തിട്ടില്ല!'''",
        "sitecsspreview": "'''താങ്കൾ ഈ സി.എസ്.എസ്.ന്റെ പ്രിവ്യൂ കാണുക മാത്രമേ ചെയ്യുന്നുള്ളൂ എന്ന കാര്യം ഓർമ്മിക്കുക.'''\n'''ഇതു സേവ് ചെയ്തിട്ടില്ല!'''",
        "sitejspreview": "'''താങ്കൾ ഈ ജാവസ്ക്രിപ്റ്റ് കോഡിന്റെ പ്രിവ്യൂ കാണുക മാത്രമേ ചെയ്യുന്നുള്ളൂ എന്ന കാര്യം ഓർമ്മിക്കുക.'''\n'''ഇതു സേവ് ചെയ്തിട്ടില്ല!'''",
-       "userinvalidcssjstitle": "'''മുന്നറിപ്പ്:''' \"$1\" എന്ന പേരിൽ ഒരു ദൃശ്യരൂപം ഇല്ല. '''.css''' ഉം '''.js''' ഉം താളുകൾ ഇംഗ്ലീഷ് ചെറിയക്ഷര തലക്കെട്ട് ആണ്‌ ഉപയോഗിക്കുന്നതെന്നു ദയവായി ഓർക്കുക. ഉദാ: {{ns:user}}:Foo/Vector.css എന്നതിനു പകരം {{ns:user}}:Foo/vector.css എന്നാണു ഉപയോഗിക്കേണ്ടത്.",
+       "userinvalidconfigtitle": "'''മുന്നറിപ്പ്:''' \"$1\" എന്ന പേരിൽ ഒരു ദൃശ്യരൂപം ഇല്ല. '''.css''' ഉം '''.js''' ഉം താളുകൾ ഇംഗ്ലീഷ് ചെറിയക്ഷര തലക്കെട്ട് ആണ്‌ ഉപയോഗിക്കുന്നതെന്നു ദയവായി ഓർക്കുക. ഉദാ: {{ns:user}}:Foo/Vector.css എന്നതിനു പകരം {{ns:user}}:Foo/vector.css എന്നാണു ഉപയോഗിക്കേണ്ടത്.",
        "updated": "(പുതുക്കിയിരിക്കുന്നു)",
        "note": "'''പ്രത്യേക ശ്രദ്ധയ്ക്ക്:'''",
        "previewnote": "'''ഇതൊരു പ്രിവ്യൂ മാത്രമാണെന്ന് ഓർക്കുക.'''\nതാങ്കൾ വരുത്തിയ മാറ്റങ്ങൾ ഇതുവരെ സേവ് ചെയ്തിട്ടില്ല!",
        "prefs-files": "പ്രമാണങ്ങൾ",
        "prefs-custom-css": "സ്വന്തം സി.എസ്.എസ്.",
        "prefs-custom-js": "സ്വന്തം ജെ.എസ്.",
-       "prefs-common-css-js": "എല്ലാ ദൃശ്യരൂപങ്ങൾക്കുമായി പങ്ക് വെയ്ക്കപ്പെട്ട സി.എസ്.എസ്./ജെ.എസ്.:",
+       "prefs-common-config": "എല്ലാ ദൃശ്യരൂപങ്ങൾക്കുമായി പങ്ക് വെയ്ക്കപ്പെട്ട സി.എസ്.എസ്./ജെ.എസ്.:",
        "prefs-reset-intro": "സൈറ്റിൽ സ്വതേയുണ്ടാവേണ്ട ക്രമീകരണങ്ങൾ പുനഃക്രമീകരിക്കാൻ താങ്കൾക്ക് ഈ താൾ ഉപയോഗിക്കാവുന്നതാണ്.\nഇത് തിരിച്ചു ചെയ്യാൻ സാദ്ധ്യമല്ല.",
        "prefs-emailconfirm-label": "ഇമെയിൽ സ്ഥിരീകരണം:",
        "youremail": "ഇമെയിൽ:",
index 78ffe43..25f3d25 100644 (file)
        "userjspreview": "'''Та өөрийн хэрэглэгчийн ЖаваСкриптийг зөвхөн урьдчилан харж байгаа бөгөөд энэ нь хараахан хадгалагдаагүй байгаа гэдгийг анхаарна уу!'''",
        "sitecsspreview": "'''Танд энэ CSS зөвхөн урьдчилан харагдаж гэдгээ санаарай.'''\n'''Хараахан хадгалагдаагүй байгаа гэдгийг анхаарна уу!'''",
        "sitejspreview": "'''Энэ JavaScript код танд л харагдах хэлбэрт байна'''\n'''Хараахан хадгалагдаагүй байгааг анзаарна уу!'''",
-       "userinvalidcssjstitle": "'''Анхаар:''' \"$1\" гэсэн арьс байхгүй байна.\nӨөрсдийн .css болон .js хуудсуудыг нэрлэхэд жижиг үсэг хэрэглэдэг болохыг сануулж байна. Жишээ нь: {{ns:user}}:Foo/vector.css гэж л хэрэглэх бөгөөд {{ns:user}}:Foo/Vector.css гэхгүй.",
+       "userinvalidconfigtitle": "'''Анхаар:''' \"$1\" гэсэн арьс байхгүй байна.\nӨөрсдийн .css болон .js хуудсуудыг нэрлэхэд жижиг үсэг хэрэглэдэг болохыг сануулж байна. Жишээ нь: {{ns:user}}:Foo/vector.css гэж л хэрэглэх бөгөөд {{ns:user}}:Foo/Vector.css гэхгүй.",
        "updated": "(Шинэчлэгдсэн)",
        "note": "'''Анхааруулга:'''",
        "previewnote": "'''Энэ бол зөвхөн урьдчилж харсан байдал.'''\nТаны хийсэн өөрчлөлтүүдийг одоохондоо хадгалаагүй байгаа!",
        "prefs-files": "Файлууд",
        "prefs-custom-css": "Өөрийн сонгосон CSS",
        "prefs-custom-js": "Өөрийн сонгосон JS",
-       "prefs-common-css-js": "Бүх скинд ашиглагдах CSS/ЖаваСкрипт:",
+       "prefs-common-config": "Бүх скинд ашиглагдах CSS/ЖаваСкрипт:",
        "prefs-reset-intro": "Та энэ хуудсыг ашиглан өөрийн тохиргоог сайтын анхны тохиргооо руу шилжүүлэх боломжтой.\nЭнэ үйлдлийг буцаах боломжгүй.",
        "prefs-emailconfirm-label": "Мэйлийн баталгаажуулалт:",
        "youremail": "Мэйл хаяг:",
index 7c7994d..a816be1 100644 (file)
@@ -50,7 +50,8 @@
                        "Nemo bis",
                        "Suyog",
                        "Matma Rex",
-                       "Tiven2240"
+                       "Tiven2240",
+                       "Sureshkhole"
                ]
        },
        "tog-underline": "दुव्यांचे अधोरेखन:",
        "tagline": "{{SITENAME}} कडून",
        "help": "साहाय्य",
        "search": "शोधा",
-       "search-ignored-headings": " #<!-- leave this line exactly as it is --> <pre>\n# Headings that will be ignored by search.\n# Changes to this take effect as soon as the page with the heading is indexed.\n# You can force page reindexing by doing a null edit.\n# The syntax is as follows:\n#   * Everything from a \"#\" character to the end of the line is a comment.\n#   * Every non-blank line is the exact title to ignore, case and everything.\nReferences\nExternal links\nSee also\n #</pre> <!-- leave this line exactly as it is -->",
+       "search-ignored-headings": " #<!-- हे असेच सोडा --> <pre>\n# शोधामधून ही शिर्षके वगळली जातील.\n# शिर्षकांमधील बदल अनुक्रमाणिकेत आल्याबरोबरच पुढील बदल आपसूकच होतील.\n# तुम्ही पानांमध्ये कसलाही बदल न करता पुन्हा साठवून ठेवल्यास पान आपसूकच अनुक्रमाणिकेत पुन्हा जोडले जाईल.\n# त्याचा सिन्टेक्स पुढीलप्रमाणे आहे:\n#   * \"#\" इथपासून ओळीच्या शेवटापर्यंतचे सर्वकाही मत म्हणून नोंदवलेले आहे.\n#   * प्रत्येक रिकामी नसलेली ओळ ही दुर्लक्षित करण्यासाठीचे शिर्षक आहे, पाने आणि सर्वकाही.\nसंदर्भयादी\nबाह्यदुवे\nहे ही पहा\n #</pre> <!-- हे असेच सोडा -->",
        "searchbutton": "शोधा",
        "go": "चला",
        "searcharticle": "जा",
        "accmailtext": "[[User talk:$1|$1]] यांसाठी अनियतक्रमाने निर्मित केलेला परवलीचा शब्द $2 यांना पाठवण्यात आला आहे.\n\nया नवीन खात्यासाठीचा परवलीचा शब्द,सनोंद-प्रवेश घेतल्यावर [[Special:ChangePassword|परवलीचा शब्द बदला]] येथे बदलता येईल.",
        "newarticle": "(नवीन लेख)",
        "newarticletext": "आपण सध्या अस्तित्त्वात नसलेल्या पानाच्या दुव्याचा मागोवा घेत आला आहात.\nहे पान नव्याने तयार करण्यासाठी खालील पेटीत टंकन करणे सुरु करा(अधिक माहितीसाठी [$1 साहाय्य पान] बघा).\n\nजर आपण येथे चुकून आला असाल तर ब्राउझरच्या  <strong>परत</strong>(बॅक) कळीवर टिचकी द्या.",
-       "anontalkpagetext": "---- ''हे चर्चापान अशा अज्ञात सदस्यासाठी आहे, ज्यांनी खाते तयार केलेले नाही किंवा त्याचा वापर करत नाहीत. त्यांच्या ओळखीसाठी आम्ही आंतरजाल अंकपत्ता वापरतो आहोत. असा अंकपत्ता बऱ्याच लोकांचा एकच असू शकतो. जर आपण अज्ञात सदस्य असाल आणि आपल्याला काही अप्रासंगिक संदेश मिळाला असेल तर कृपया [[Special:UserLogin| खाते तयार करा]] किंवा [[Special:CreateAccount|सनोंद-प्रवेश करा]] ज्यामुळे, पुढे असे गैरसमज होणार नाहीत.''",
+       "anontalkpagetext": "<em>हे चर्चापान अशा अज्ञात सदस्यासाठी आहे, ज्यांनी खाते तयार केलेले नाही किंवा त्याचा वापर करत नाहीत.</em> \nत्यांच्या ओळखीसाठी आम्ही आंतरजाल अंकपत्ता वापरतो आहोत. असा अंकपत्ता बऱ्याच लोकांचा एकच असू शकतो. \nजर आपण अज्ञात सदस्य असाल आणि आपल्याला काही अप्रासंगिक संदेश मिळाला असेल तर कृपया [[Special:CreateAccount| खाते तयार करा]] किंवा [[Special:CreateAccount|सनोंद-प्रवेश करा]] ज्यामुळे, पुढे असे गैरसमज होणार नाहीत.",
        "noarticletext": "या लेखात सध्या काहीही मजकूर नाही.\nतुम्ही विकिपीडियावरील इतर लेखांमध्ये या [[Special:Search/{{PAGENAME}}| मथळ्याचा शोध घेऊ शकता]], <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} इतर नोंदी शोधा],\nकिंवा हा लेख [{{fullurl:{{FULLPAGENAME}}|action=edit}}तयार करू शकता]</span>.",
        "noarticletext-nopermission": "सध्या या लेखात  काहीही मजकूर नाही.\nतुम्ही विकिपीडियावरील इतर लेखांमध्ये [[Special:Search/{{PAGENAME}}| या मथळ्याचा शोध घेऊ शकता]], <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAME}}}}आपण या लेखाच्या इतर नोंदी शोधा]</span>,परंतु, आपणास हा लेख लिहीण्याची परवानगी देण्यात येउ शकत नाही.",
        "missing-revision": "\"{{FULLPAGENAME}}\" या लेखाचे #$1 हे संस्करण अस्तित्वात नाही.वगळल्या गेलेल्या लेखपानाच्या जुन्या इतिहास-दुव्याचे अनुसरण केल्यामुळे असे होते.याबाबत विस्तृत माहिती  [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} वगळलेल्या नोंदी]येथे बघता येईल.",
        "userpage-userdoesnotexist": "\"<nowiki>$1</nowiki>\" सदस्य खात्याची नोंद नाही. कृपया हे पान तुम्ही संपादित किंवा नव्याने तयार करू इच्छिता काय याबद्दल विचार करा.",
        "userpage-userdoesnotexist-view": "सदस्यखाते \"$1\"  हे नोंदलेले नाही.",
        "blocked-notice-logextract": "हा सदस्य सध्या प्रतिबंधित आहे.\nखाली, सर्वांत नवीनतम प्रतिबंधन नोंदप्रविष्टी संदर्भासाठी दिली आहे:",
-       "clearyourcache": "'''सूचना:''' जतन केल्यावर बदल दिसण्यासाठी तुम्हाला कदाचित न्याहाळकाची सय टाळायला लागेल. असे करण्यासाठी - \n\n*'''फायरफॉक्स / सफारी:''' साठी ''Reload'' हे टिचकतांना ''Shift'' ही कळ दाबून ठेवा, किंवा ''Ctrl-F5'' अथवा ''Ctrl-R'' कळा एकत्रितपणे दाबा (मॅकसाठी ''⌘-R'').\n\n*'''गूगल क्रोम:''' साठी ''Ctrl-Shift-R'' कळा एकत्रितपणे दाबा (मॅकसाठी ''⌘-Shift-R'')\n\n*'''इंटरनेट एक्सप्लोअरर:''' ''Refresh'' करतांना ''Ctrl'' कळ दाबून ठेवा, किंवा त्याऐवजी ''Ctrl-F5'' दाबा.\n\n*'''कॉन्क्वरर:''' '''Reload''' दाबा किंवा ''F5'' दाबा\n\n*'''ऑपेरा:''' ''Tools → Preferences'' मधून सय रिकामी करा",
+       "clearyourcache": "<strong>नोंद:</strong> साठवून ठेवल्यानंतर बदल पहाण्यासाठी कदाचित तुमच्या ब्राऊजरच्या कॅचेला बायपास करावे लागेल.\n* <strong>फ़ायरफ़ॉक्स / सफ़ारी:</strong> धरुन ठेवा <em>Shift</em> टिचकी मारताना <em>Reload</em>, किंवा हे दाबताना <em>Ctrl-F5</em> किंवा <em>Ctrl-R</em> (<em>⌘-R</em> मॅकवर)\n* <strong>गुगल क्रोम:</strong> दाबा <em>Ctrl-Shift-R</em> (<em>⌘-Shift-R</em> मॅकसाठी)\n* <strong>ओपेरा:</strong> कडे जा <em>Menu → Settings</em> (<em>ओपेरा → पसंतीक्रम </em> on a Mac) आणि मग <em>गोपनियता आणि सुरक्षा → ब्राउजिंग डाटा काढून टाका → कॅचे छायाचित्रे आणि धारिणी</em>.",
        "usercssyoucanpreview": "'''टीप:'''तुमचे नवे सीएसएस जतन करण्यापूर्वी 'झलक पहा' कळ वापरा.",
        "userjsyoucanpreview": "'''टीप:''' तुमचा नवा जावास्क्रिप्ट जतन करण्यापूर्वी 'झलक पहा' कळ वापरा.",
        "usercsspreview": "'''तुम्ही तुमच्या सी.एस.एस.ची केवळ झलक पहात आहात, ती अजून जतन केलेली नाही हे लक्षात घ्या.'''",
        "userjspreview": "'''तुम्ही तुमची सदस्य जावास्क्रिप्ट तपासत आहात किंवा झलक पहात आहात ,ती अजून जतन केलेली नाही हे लक्षात घ्या!'''",
        "sitecsspreview": "'''तुम्ही तुमच्या सी.एस.एस.ची केवळ झलक पहात आहात, ती अजून जतन केलेली नाही हे लक्षात घ्या.'''",
        "sitejspreview": "'''तुम्ही तुमच्या जावास्क्रिप्टची केवळ झलक पहात आहात, ती अजून जतन केलेली नाही हे लक्षात घ्या.'''",
-       "userinvalidcssjstitle": "'''सावधान:''' \"$1\" अशी त्वचा नाही.custom .css आणि .js पाने lowercase title वापरतात हे लक्षात घ्या, उदा. {{ns:user}}:Foo/vector.css या विरुद्ध {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "'''सावधान:''' \"$1\" अशी त्वचा नाही.custom .css आणि .js पाने lowercase title वापरतात हे लक्षात घ्या, उदा. {{ns:user}}:Foo/vector.css या विरुद्ध {{ns:user}}:Foo/Vector.css.",
        "updated": "(अद्यतन केले)",
        "note": "'''सूचना:'''",
        "previewnote": "'''लक्षात ठेवा की ही फक्त झलक आहे''', बदल अजून जतन करण्यात आलेले नाहीत.",
        "revertmerge": "अविलीन करा",
        "mergelogpagetext": "एका पानाचा इतिहास इतर पानात टाकून अगदी अलीकडे एकत्रित केलेली एकत्रिकरणे निम्न्दर्शीत सूचीमध्ये आहेत.",
        "history-title": "\"$1\" चा संपादन इतिहास",
-       "difference-title": "\"$1\" à¤\9aà¥\8dया à¤µà¤¿à¤µà¤¿à¤§ à¤\89à¤\9cळण्यांमधील फरक",
+       "difference-title": "\"$1\" à¤\9aà¥\8dया à¤µà¤¿à¤µà¤¿à¤§ à¤\86वà¥\83त्यांमधील फरक",
        "difference-title-multipage": "\"$1\" व \"$2\" या पानांमधला फरक",
        "difference-multipage": "(पानांमधील फरक)",
        "lineno": "ओळ $1:",
        "searchprofile-articles-tooltip": "$1 मध्ये शोधा",
        "searchprofile-images-tooltip": "संचिकांसाठी शोधा",
        "searchprofile-everything-tooltip": "सर्व पाने शोधा (चर्चापानांसहित)",
-       "searchprofile-advanced-tooltip": "पारà¤\82परित(कस्टम) नामविश्वांमध्ये शोधा",
+       "searchprofile-advanced-tooltip": "बदलतà¥\8dया à¤¯à¥\87णà¥\8dयाà¤\9cà¥\8bà¤\97à¥\8dया(कस्टम) नामविश्वांमध्ये शोधा",
        "search-result-size": "$1 ({{PLURAL:$2|१ शब्द|$2 शब्द}})",
        "search-result-category-size": "{{PLURAL:$1|१ सदस्य|$1 सदस्य}} ({{PLURAL:$2|१ उपवर्ग|$2 उपवर्ग}}, {{PLURAL:$3|1 संचिका|$3 संचिका}})",
        "search-redirect": "($1 पासून पुनर्निर्देशन)",
        "prefs-files": "संचिका",
        "prefs-custom-css": "सीएसएस पद्धत बदला",
        "prefs-custom-js": "जावास्क्रिप्ट पद्धत बदला",
-       "prefs-common-css-js": "मिळून वापरलेले सर्व त्वचांसाठींचे सीएसएस / जावास्क्रिप्ट:",
+       "prefs-common-config": "मिळून वापरलेले सर्व त्वचांसाठींचे सीएसएस / जावास्क्रिप्ट:",
        "prefs-reset-intro": "आपण या पानाचा वापर, या संकेतस्थळच्या अविचलनुसार, आपला पसंतीक्रम पुनर्स्थापनेसाठी करू शकता.",
        "prefs-emailconfirm-label": "विपत्र निश्चितीकरण:",
        "youremail": "विपत्र:",
        "recentchanges-label-newpage": "या संपादनाने नवीन पान तयार झाले",
        "recentchanges-label-minor": "हे एक किरकोळ संपादन आहे",
        "recentchanges-label-bot": "हे संपादन एका सांगकाम्याकडून केले गेले आहे",
-       "recentchanges-label-unpatrolled": "हà¥\87 à¤¸à¤\82पादन à¤\85à¤\9cà¥\82न à¤¤à¤ªà¤¾à¤¸à¤²à¥\8dया गेले नाही",
+       "recentchanges-label-unpatrolled": "हà¥\87 à¤¸à¤\82पादन à¤\85à¤\9cà¥\82न à¤¤à¤ªà¤¾à¤¸à¤²à¥\87 गेले नाही",
        "recentchanges-label-plusminus": "या पानाचा आकार इतक्या बाइटस् ने बदलला",
        "recentchanges-legend-heading": "<strong>विवरण:</strong>",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} ([[Special:NewPages|नविन पानांची यादी]] हेही पाहा)",
        "undelete-show-file-submit": "होय",
        "namespace": "नामविश्व:",
        "invert": "निवडीचा क्रम उलटा करा",
-       "tooltip-invert": "निवडलà¥\87लà¥\8dया à¤¨à¤¾à¤®à¤µà¤¿à¤¶à¥\8dवातà¥\80ल (à¤\86णि à¤¤à¤¸à¥\87 à¤¨à¤¿à¤µà¤¡à¤²à¥\8dयास à¤¸à¤\82बà¤\82धित à¤¨à¤¾à¤®à¤µà¤¿à¤¶à¥\8dवातà¥\80ल)  à¤ªà¤¾à¤¨à¤¾à¤\82à¤\9aà¥\87 à¤¬à¤¦à¤²  à¤\85दà¥\83षà¥\8dय à¤\95रण्यासाठी टिचकी मारा",
+       "tooltip-invert": "निवडलà¥\87लà¥\8dया à¤¨à¤¾à¤®à¤µà¤¿à¤¶à¥\8dवातà¥\80ल (à¤\86णि à¤¤à¤¸à¥\87 à¤¨à¤¿à¤µà¤¡à¤²à¥\8dयास à¤¸à¤\82बà¤\82धित à¤¨à¤¾à¤®à¤µà¤¿à¤¶à¥\8dवातà¥\80ल)  à¤ªà¤¾à¤¨à¤¾à¤\82à¤\9aà¥\87 à¤¬à¤¦à¤²  à¤²à¤ªà¤µण्यासाठी टिचकी मारा",
        "namespace_association": "सहभागी नामविश्वे",
        "tooltip-namespace_association": "निवडलेल्या नामविश्वासंबधीत विषय अथवा चर्चा नामविश्वसुद्धा आंतर्भूत करण्याकरिता हा बॉक्स टिचकवून चिह्नित करा",
        "blanknamespace": "(मुख्य)",
        "whatlinkshere": "येथे काय जोडले आहे",
        "whatlinkshere-title": "\"$1\" ला जुळलेली पाने",
        "whatlinkshere-page": "पान:",
-       "linkshere": "खालील लेख '''[[:$1]]''' या पानाशी जोडले आहेत:",
+       "linkshere": "à¤\96ालà¥\80ल à¤²à¥\87à¤\96 '''[[:$1]]''' à¤¯à¤¾ à¤ªà¤¾à¤¨à¤¾à¤\82शà¥\80 à¤\9cà¥\8bडलà¥\87 à¤\86हà¥\87त:",
        "nolinkshere": "'''[[:$1]]''' येथे कोणत्याही पानांचे दुवे नाहीत.",
        "nolinkshere-ns": "निवडलेल्या नामविश्वातील कोणतीही पाने <strong>[[:$1]]</strong>ला दुवा देत नाहीत .",
        "isredirect": "पुनर्निर्देशित पान",
        "htmlform-user-not-valid": "<strong>$1</strong> हे वैध सदस्यनाम नाही.",
        "logentry-delete-delete": "$1 {{GENDER:$2|वगळलेले पान}} $3",
        "logentry-delete-delete_redir": "$1 ने $3 हे पुनर्निर्देशन उपरीलेखन (ओव्हररायटिंग) करून {{GENDER:$2|वगळले}}",
-       "logentry-delete-restore": "$1 {{GENDER:$2|पुनर्स्थापित पृष्ठ}} $3",
+       "logentry-delete-restore": "$1 {{GENDER:$2|पुनर्स्थापित पृष्ठ}} $3 ($4)",
        "logentry-delete-event": "$1 ने $3 वर{{PLURAL:$5|नोंद-प्रसंग|$5 नोंद प्रसंगांची}} दृष्यता{{GENDER:$2|बदलली}}:$4",
        "logentry-delete-revision": "$1 ने $3 पानावर{{PLURAL:$5|आवृत्ती|$5 आवृत्यांची}} दृष्यता{{GENDER:$2|बदलली}}:$4",
        "logentry-delete-event-legacy": "$1 ने $3 वर नोंद प्रसंगांची {{GENDER:$2|बदलली}}",
index 74271eb..334e350 100644 (file)
        "userjspreview": "'''Ingat bahawa anda hanya menguji/melihat pralihat JavaScript anda, ia belum lagi disimpan!'''",
        "sitecsspreview": "'''Ingat bahawa anda cuma melihat pralihat CSS ini.'''\n'''Ia belum lagi disimpan!'''",
        "sitejspreview": "'''Ingat bahawa anda cuma mempralihat kod JavaScript ini.'''\n'''Ia belum lagi disimpan!'''",
-       "userinvalidcssjstitle": "'''Amaran:''' Rupa \"$1\" tidak wujud. Ingat bahawa laman tempahan .css dan .js menggunakan tajuk berhuruf kecil, contohnya {{ns:user}}:Anu/vector.css tidak sama dengan {{ns:user}}:Anu/Vector.css.",
+       "userinvalidconfigtitle": "'''Amaran:''' Rupa \"$1\" tidak wujud. Ingat bahawa laman tempahan .css dan .js menggunakan tajuk berhuruf kecil, contohnya {{ns:user}}:Anu/vector.css tidak sama dengan {{ns:user}}:Anu/Vector.css.",
        "updated": "(Dikemas kini)",
        "note": "'''Catatan:'''",
        "previewnote": "'''Ingatlah bahawa ini hanya pralihat.'''\nPerubahan anda belum disimpan!",
        "prefs-files": "Fail",
        "prefs-custom-css": "CSS tempahan",
        "prefs-custom-js": "JS tempahan",
-       "prefs-common-css-js": "CSS/JavaScript kongsi untuk semua rupa:",
+       "prefs-common-config": "CSS/JavaScript kongsi untuk semua rupa:",
        "prefs-reset-intro": "Anda boleh menggunakan laman ini untuk menetapkan semula keutamaan anda kepada tetapan asali.\nTindakan ini tidak boleh dibatalkan.",
        "prefs-emailconfirm-label": "Pengesahan e-mel:",
        "youremail": "E-mel:",
index 55f3314..6f88ae3 100644 (file)
        "userjspreview": "'''Ftakar li inti qiegħed biss tipprova/tara dehra proviżorja tal-JavaScript personali; il-modifiki li għamilt għad iridu jiġu salvati!'''",
        "sitecsspreview": "'''Ftakar li din hija biss dehra proviżorja tas-CSS. Il-modifiki għadhom ma ġewx salvati!'''",
        "sitejspreview": "'''Ftakar li din hija biss dehra proviżorja tal-JavaScript. Il-modifiki għadhom ma ġewx salvati!'''",
-       "userinvalidcssjstitle": "'''Twissija:''' M'hemm l-ebda aspett bl-isem \"$1\".\nFtakar li l-paġni .css u .js personalizzati għandhom l-ewwel ittra tat-titlu żgħira, eż. {{ns:user}}:Foo/vector.css u mhux {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "'''Twissija:''' M'hemm l-ebda aspett bl-isem \"$1\".\nFtakar li l-paġni .css u .js personalizzati għandhom l-ewwel ittra tat-titlu żgħira, eż. {{ns:user}}:Foo/vector.css u mhux {{ns:user}}:Foo/Vector.css.",
        "updated": "(Aġġornata)",
        "note": "'''Nota:'''",
        "previewnote": "'''Ftakar li din hija biss dehra proviżorja.'''\nIt-tibdiliet tiegħek għadhom ma ġewx salvati!",
        "prefs-files": "Fajls",
        "prefs-custom-css": "CSS personalizzat",
        "prefs-custom-js": "JS personalizzat",
-       "prefs-common-css-js": "CSS/JS maqsum għal kull aspett grafiku:",
+       "prefs-common-config": "CSS/JS maqsum għal kull aspett grafiku:",
        "prefs-reset-intro": "Inti tista' tuża' din il-paġna sabiex terġa' tbiddel il-preferenzi tiegħek għal dawk li ngħatawlek fil-bidu. Din l-operazzjoni hija definittiva u ma tistax tiġi mħassra.",
        "prefs-emailconfirm-label": "Konferma tal-ittra-e:",
        "youremail": "E-mail:",
index 8571dbd..d7050d8 100644 (file)
        "prefs-files": "Fexeiros",
        "prefs-custom-css": "CSS personalizada",
        "prefs-custom-js": "JS personalizado",
-       "prefs-common-css-js": "CSS/JS partilhado por todas las maçcarilhas:",
+       "prefs-common-config": "CSS/JS partilhado por todas las maçcarilhas:",
        "prefs-emailconfirm-label": "Cunfirmaçon de l correio eiletrónico:",
        "youremail": "Morada de correio eiletrónico:",
        "username": "Nome de {{GENDER:$1|outelizador|outelizadora|outelizador(a)}}:",
index a13b456..ff1c0d7 100644 (file)
        "tog-hideminor": "Од полавтоматнесэ кекшемс вишинькине витевкстнэнь",
        "tog-hidepatrolled": "Кекшемс лувонь кирдиень витнеметнень-петнематнень чыяконь полавтнематнестэ",
        "tog-newpageshidepatrolled": "Кекшемс лувонь кирдиень ванстома лопатнень од лопань керьксэнть эйстэ",
-       "tog-extendwatchlist": "Келейгавтомс сёрмадовксонь мельга ваномань сёрмалевксэнть невтевест весе полавтнематне, аволь ансяк чыеньсетне.",
+       "tog-extendwatchlist": "Келейгавтомс сёрмадовкс мельга ваномань керьксэнть невтевест весе полавтнематне, аволь ансяк чиеньсетне.",
        "tog-usenewrc": "Пурнамс лиякстомтомат лопань коряс куронь-куронь чиень полавтнематнестэ-ванома лемрисьметнестэ",
        "tog-numberheadings": "Сёрмадовксконяксос кадык сынсь ловома валтнэ путовить",
        "tog-showtoolbar": "Невтемс кедьёнкслазнэнть сёрмадома шкасто",
        "tog-editondblclick": "Кавксть лепштязь совамс сёрмадовксонь витнеме-петнеме",
        "tog-editsectiononrightclick": "Витнемс секциятнень-пелькстнэнь, лепштямс сёрмадовксонть лемензэ лангс чеерень витьёнсе повнесэ",
-       "tog-watchcreations": "Совавтомс ванома лемрисьмезэнь монь теевть лопатнень ды сень, мезе йовкстан",
+       "tog-watchcreations": "Совавтомс ванома лемрисьмезэнь монь теевть лопатнень ды сень, мезе ёвкстан",
        "tog-watchdefault": "Совавтомс монь витевть лопатнень ванома лемрисьмезэнь",
        "tog-watchmoves": "Совавтомс монь одов лемдявт лопатнень-керьмазтнэнь ванома лемрисьмезэнь",
        "tog-watchdeletion": "Совавтомс монь нардавт лопатнень-керьмазтнэнь ванома лемрисьмезэнь",
@@ -39,9 +39,9 @@
        "tog-enotifminoredits": "Кучомс тень ёндол-сёрмине сестэяк, зярдо апокшкыне витнемат-петнемат теевить монь ванстевть лопатнесэ-керьмазтнэсэ",
        "tog-enotifrevealaddr": "Штавтомс е-сёрмань адресэм яволявтомань сёрмадовкстнэсэ",
        "tog-shownumberswatching": "Невтемс зяро теицятнеде, конат аравтызь лопанть эсест ванома лемрисьментень",
-       "tog-oldsig": "УликÑ\81 ÐºÐµÐ´Ñ\8cпÑ\83Ñ\82овкÑ\81оÑ\81Ñ\8c:",
+       "tog-oldsig": "Ð\9dеенÑ\8c ÐºÐµÐ´Ñ\8cпÑ\83Ñ\82овкÑ\81оÑ\82:",
        "tog-fancysig": "Лемпутовксось прок викитекст (сонсь теевиця сюлмавома певтеме)",
-       "tog-uselivepreview": "Ð\9cакÑ\81омÑ\81 Ñ\8dÑ\80иÑ\86Ñ\8f Ð²Ð°Ñ\81нÑ\8fнÑ\8c Ð½ÐµÐ²Ñ\82евкÑ\81",
+       "tog-uselivepreview": "Ð\9dевÑ\82емÑ\81 Ð²Ð°Ñ\81нÑ\8fнÑ\8c Ð½ÐµÐ²Ñ\82евкÑ\81 Ð»Ð¾Ð¿Ð°Ð½Ñ\8c Ð¾Ð´Ð¾Ð² Ð°Ð¿Ð°Ðº Ð¾Ð´ÐºÑ\81Ñ\82омÑ\82о",
        "tog-forceeditsummary": "Невтик монень, мезе сёрмадомс витнемадо-петнемадо ёвтамонь вальминентень",
        "tog-watchlisthideown": "Кекшить монь теевть витневкстнэнь ванома лемрисьменть эйстэ",
        "tog-watchlisthidebots": "Кекшить бот витневкстнэнь-петневкстнэнь ванома лемрисьсенть эйстэ",
        "newwindow": "(панжови од вальмасо)",
        "cancel": "Саемс мекев",
        "moredotdotdot": "Седе ламо...",
-       "morenotlisted": "Те лемрисьмесь апак прядо.",
+       "morenotlisted": "Те лемрисьмесь, кизды, апак прядо.",
        "mypage": "Монь лопам",
        "mytalk": "Кортнемам",
-       "anontalk": "Ð\9aоÑ\80Ñ\82амÑ\81 Ñ\82е IP-нÑ\82Ñ\8c Ð¼Ð°Ñ\80Ñ\82о",
+       "anontalk": "Ð\9aоÑ\80Ñ\82немам",
        "navigation": "Навигация",
        "and": "&#32;ды",
        "faq": "Сеедьстэ кепедень кевкстемат",
        "tagline": "{{SITENAME}} -нь пельде",
        "help": "Лезкс",
        "search": "Вешнемс",
-       "searchbutton": "Ð\92еÑ\88нек",
+       "searchbutton": "Ð\92еÑ\88немÑ\81",
        "go": "Адя",
        "searcharticle": "Адя",
        "history": "Лопань полавтнемат - витнемат",
        "print": "Нолдамс",
        "view": "Ванома потмо",
        "view-foreign": "Ваномс «$1» сайтасонть",
-       "edit": "Витнеме-петнеме",
+       "edit": "Витнемс-петнемс",
        "edit-local": "Витнемс-петнемс тескень ёвтнеманзо",
        "create": "Тейть-шкак",
        "create-local": "Поладомс тескень ёвтнеманзо",
        "newmessageslinkplural": "{{PLURAL:$1|од сёрма|999=од сёрмат}}",
        "newmessagesdifflinkplural": "меельце {{PLURAL:$1|полавтнемась|999=полавтнематне}}",
        "youhavenewmessagesmulti": "Од сёрминеть учить эйсэть $1-со",
-       "editsection": "витнеме-петнеме",
-       "editold": "витнеме-петнеме",
+       "editsection": "витнемс-петнемс",
+       "editold": "витнемс-петнемс",
        "viewsourceold": "ваномс лисьмапрянть",
-       "editlink": "витнеме-петнеме",
+       "editlink": "витнемс-петнемс",
        "viewsourcelink": "ваномс лисьмапрянзо",
-       "editsectionhint": "$1 секциянть-пельксэнть витнеме-петнеме",
+       "editsectionhint": "Витнемс-петнемс «$1» секциянть-пельксэнть",
        "toc": "Потмокс",
        "showtoc": "невтемс",
        "hidetoc": "кекшемс",
        "createaccountmail": "Тейть кодамо понгсь салававал, кучик сонзэ ало максозь е-сёрмапаргонтень",
        "createacct-reason": "Тувтал",
        "createacct-submit": "Шкик совамотаркат",
-       "createacct-another-submit": "Шкак Ð¾Ð´ совамотарка",
+       "createacct-another-submit": "ШкамÑ\81 совамотарка",
        "createacct-benefit-heading": "«{{SITENAME}}» сайтэнть теизь тонь кондямо ломанть.",
        "createacct-benefit-body1": "{{PLURAL:$1|витнема-петнема|витнемат-петнемат}}",
        "createacct-benefit-body2": "{{PLURAL:$1|лопа|лопат}}",
        "noname": "Зярс эзить максо кемекстазь теицянь лем.",
        "loginsuccesstitle": "Совавить",
        "loginsuccess": "'''Тон совить {{SITENAME}}-с кода \"$1\".'''",
-       "nosuchuser": "$1 лем марто теиця арась.\nТеиця лемтне явозь тень корясь вишка эли покш тештинесэ сёрмадозь. Ваннык видестэ - а видестэ сёрмадык, эли [[Special:CreateAccount|тейть-шкак од совамо тарка]].",
+       "nosuchuser": "$1 лем марто теиця арась.\nТеиця лемтне явозь тень корясь вишка эли покш тештинесэ сёрмадозь. Ваннык видестэ - а видестэ сёрмадык, эли [[Special:CreateAccount|тейть-шкак од совамотарка]].",
        "nosuchusershort": "\"$1\" лемсэ теиця арась. Варштака, кизды, аволь истя сёрмадозь.",
        "nouserspecified": "Теицянь лем эряви.",
        "login-userblocked": "Те теицясь аравтозь саймас. Совамонзо а мерить.",
-       "wrongpassword": "Аволь истя сёрмадык совамо валот. Варчыка одов.",
+       "wrongpassword": "Аволь истя сёрмадык совамовалот. Варчыка одов.",
        "wrongpasswordempty": "Салавань валот кадовсь апак сёрмадо. Сёрмадыка одов.",
        "passwordtooshort": "Салававалонть кувалмозо  {{PLURAL:$1|улезэ 1 тешкс| улезт $1 тешкст}}, аволь седе аламо.",
        "password-name-match": "Салава валонтень эряви явовомс теицянь леметь эйстэ.",
        "previewnote": "'''Кирдтяя мельсэ, те ансяк васнянь невтевкс.'''\nПолавтоматне зярс апак вансто!",
        "editing": "Витнят-петнят $1",
        "creating": "Шки-теи «$1»",
-       "editingsection": "Витнеме-петнеме $1 (секциянть)",
+       "editingsection": "Витнемс-петнемс $1 (секциянть)",
        "editingcomment": "Витнят-петнят $1 (од явкс)",
        "editconflict": "Витнемадо-петнемадо аладямо: $1",
        "yourtext": "Мезе сёрмадыть",
        "storedversion": "Ванстозь версия",
        "yourdiff": "Мейсэ явовить",
        "copyrightwarning": "Инескеть кирдть мельсэ {{SITENAME}}-сэ весе путовкстнэнь, лововить нолдазекс ало $2  конёвонть коряс (вант $1 педе пес). Арась мелеть витневтемс-петневтемс сёрмадовксот педе пес, иляк сестэ путо сонзэ тезэнь.<br />\nИстяжо тезэнь материалонь максомсот, кемекстат тон тонсь сёрмадык сонзэ, али саик сонзэ вейсэнь ёнксто али олячинь порталсто.\n'''ИЛЯ МАКСО ВАНСТОНЬ ВИДЕЧИСЭ ЛОМАНЕНЬ ВАЖОДЕМАНТЬ АПАК КЕВКСТНЕ!'''",
-       "titleprotectedwarning": "'''ВАНОК:  Те лопась сёлгозь, сонзэ шкамга-теемга [[Special:ListGroupRights|башка видечыть]] эрявить.'''\nЖурналонь меельсе сёрмадовксось максозь ало, эрявиндеряй сонзэ ваномс.",
+       "titleprotectedwarning": "'''ВАНОК:  Те лопась пекстазь, сонзэ шкамга-теемга эрявить [[Special:ListGroupRights|башка видечить]].'''\nЖурналонь меельце сёрмадовксось максозь ало, эрявиндеряй сонзэ ваномс.",
        "templatesused": "Те лопасонть тевс нолдазь {{PLURAL:$1|лопапарцун|лопапарцунт}}:",
        "templatesusedpreview": "Те икелькс вановкссонть тевс нолдазь  {{PLURAL:$1|лопа парцун|лопа парцунт}}:",
        "templatesusedsection": "Те пелькссэнть тевс нолдазь {{PLURAL:$1|лопа парцунось|лопа парцунтнэ}}:",
        "page_first": "васенце",
        "page_last": "меельсе",
        "histlegend": "Версиянь кочкамось: тешксты невтезь версиятнень,  али лепштик Enter повнэнть.<br />\nЧарькодевтемат: (молиц.) = редямось молиця версиястонть; (и. молиц.) = редямось икеле молиця версиястонть; '''а''' = аволь седе ламо лиякстомтома.",
-       "history-fieldset-title": "Ð\92аномÑ\81 Ð»Ð¾Ð¿Ð°Ð½Ñ\82Ñ\8c Ñ\8eÑ\80онзо-пÑ\83Ñ\82овкÑ\81онзо",
+       "history-fieldset-title": "Ð\92еÑ\88немÑ\81 Ð²ÐµÑ\80Ñ\81иÑ\8fÑ\82",
        "history-show-deleted": "Ансяк нардазь",
        "histfirst": "весемеде умонь",
        "histlast": "Меельце",
        "powersearch-ns": "Вешнемс не лемпотмотнестэ:",
        "powersearch-toggleall": "Весе",
        "powersearch-togglenone": "Арась мезе невтемс",
-       "search-external": "Ушо йондонь вешнема",
+       "search-external": "Ушо ёндонь вешнема",
        "preferences": "Аравтомат",
        "mypreferences": "Аравтомат",
        "prefs-edits": "Зяроксть витнезь-петнезь:",
        "timezoneregion-europe": "Эвропа",
        "timezoneregion-indian": "Индиянь иневедь",
        "timezoneregion-pacific": "Сэтьме иневедь",
-       "prefs-searchoptions": "Вешнем",
+       "prefs-searchoptions": "Вешнемс",
        "prefs-namespaces": "Лем потмот",
        "default": "зярдо лиякс апак ёвта",
        "prefs-files": "Файлат",
        "grouppage-sysop": "{{ns:project}}:Администраторт",
        "grouppage-bureaucrat": "{{ns:project}}:Бюрократт",
        "right-read": "Лопатнень ловномо",
-       "right-edit": "Ð\9bопаÑ\82ненÑ\8c Ð²Ð¸Ñ\82неме-пеÑ\82неме",
+       "right-edit": "Ð\92иÑ\82немÑ\81-пеÑ\82немÑ\81 Ð»Ð¾Ð¿Ð°Ñ\82",
        "right-createpage": "Теемс-шкамс лопат (аволь кортнема лопат)",
        "right-createtalk": "Теемс-шкамс кортнема лопат",
        "right-createaccount": "Теемс-шкамс совицянь од таркат",
        "right-writeapi": "Кода нолдамс тевс сёрмадома API-нть",
        "right-delete": "Нардамс лопатнень",
        "right-bigdelete": "Нардамс кувака икелькс ума марто лопатнень",
-       "right-browsearchive": "Ð\92еÑ\88немÑ\81 Ð½Ð°Ñ\80дань файлатнесэ",
+       "right-browsearchive": "Ð\92еÑ\88немÑ\81 Ð½Ð°Ñ\80дазь файлатнесэ",
        "right-undelete": "Вельмевтемс нардань лопа",
        "right-block": "Кардамс лия совийтнень-лисийтнень витнемадо-петнемадо",
        "right-blockemail": "Кардамс лия лисийтнень-совийтнень е-сёрмань кучомадо",
        "newuserlogpagetext": "Те теицянь шкавксто журнал",
        "rightslog": "Уськетеицянть видечинть кемекстома",
        "action-read": "те лопань ловномо",
-       "action-edit": "те лопанть витнеме-петнеме",
-       "action-createpage": "лопань тееме-шкамо",
-       "action-createtalk": "кортнема лопань тееме-шкамо",
+       "action-edit": "витнемс-петнемс те лопанть",
+       "action-createpage": "теик-шкик те лопанть",
+       "action-createtalk": "теик-шкик те кортнема лопанть",
        "action-createaccount": "Шкамс совицянь те тарканть",
        "action-minoredit": "Тешкстамс те витнеманть-петнеманть а покшкынекс",
        "action-move": "печтевтемс те лопанть",
        "action-upload_by_url": "ёвкстамс те файланть URL адресстэ",
        "action-writeapi": "нолдамс тевс сёрмадома API-нть",
        "action-delete": "нардамс те лопанть",
-       "action-deleterevision": "нардамс те лиякстомтоманть",
-       "action-browsearchive": "веÑ\88немÑ\81 Ð½Ð°Ñ\80дань лопатнестэ",
-       "action-undelete": "велÑ\8cмевÑ\82емÑ\81 Ð¼ÐµÐºÐµÐ² Ñ\82е Ð»Ð¾Ð¿Ð°Ð½Ñ\82Ñ\8c",
+       "action-deleterevision": "нардамс лиякстомтомат",
+       "action-browsearchive": "веÑ\88немÑ\81 Ð½Ð°Ñ\80дазь лопатнестэ",
+       "action-undelete": "велÑ\8cмевÑ\82емÑ\81 Ð»Ð¾Ð¿Ð°Ñ\82 Ð¼ÐµÐºÐµÐ²",
        "action-suppressionlog": "ваномс те теицянть тевнеде сёрмадовкс",
        "action-block": "кардамс те совицянть витнемадо-петнемадо",
        "action-import": "совавтомс тезэнь лопат лия Викистэ",
        "randomredirect-nopages": "\"$1\"  лем потмосонть лияв ютавтомат арасть.",
        "statistics": "Статистикат",
        "statistics-header-pages": "Лопань статистикат",
-       "statistics-header-edits": "СÑ\82аÑ\82иÑ\81Ñ\82иканÑ\8c Ð²Ð¸Ñ\82неме-пеÑ\82неме",
+       "statistics-header-edits": "Ð\92иÑ\82неманÑ\8c-пеÑ\82неманÑ\8c Ñ\81Ñ\82аÑ\82иÑ\81Ñ\82икаÑ\81Ñ\8c",
        "statistics-header-users": "Теицянь статистика",
        "statistics-header-hooks": "Лия статистика",
        "statistics-articles": "Потмо марто лопат",
        "doubleredirects": "Кавксть ютавтозь",
        "double-redirect-fixer": "Печтевтемс витнема-петнема пель",
        "brokenredirects": "Сезезь ёнксось",
-       "brokenredirects-edit": "витнеме-петнеме",
+       "brokenredirects-edit": "витнемс-петнемс",
        "brokenredirects-delete": "нардамс",
        "withoutinterwiki": "Лопат келеньютковань невтевкснень теме",
        "withoutinterwiki-summary": "Вана неть лопатне апак сюлма лия келень верзия марто:",
        "minimum-size": "Минимум кувалмозо",
        "maximum-size": "Покшолмань тёкш:",
        "pagesize": "(байтт)",
-       "restriction-edit": "Витнеме-петнеме",
+       "restriction-edit": "Витнемс-петнемс",
        "restriction-move": "Ютавтомс лия таркас",
        "restriction-create": "Шкамс-теемс",
        "restriction-upload": "Ёвкстамс",
        "whatlinkshere-hideredirs": "лияв-лияв ютавтовкстнэде $1",
        "whatlinkshere-hidetrans": "сюлмавозь пелькстнэде $1",
        "whatlinkshere-hidelinks": "сюлмавома петнеде $1",
-       "whatlinkshere-hideimages": "$1 файланть сюлмавомапензэ",
+       "whatlinkshere-hideimages": "файлань $1 сюлмавомапенеть",
        "whatlinkshere-filters": "Фильтрат",
        "block": "Аравтомс теицянть саймас",
        "unblock": "Нолдамс теицянть саймасто",
        "movepagetext": "Ало максозь лувонть тевс нолдазь, одс лемдят лопа, ве шкасто печтевтят од таркас сонзэ лиякстомтома юронзо-журналонзо.\nИкелень лемезэ тееви печтевтема лопакс, кона ютавты лисийть-совийть од лементень.\nНевтевкстнэ икелень лементь лангс а кармить лиякстомтовомо (инеськеть, вант улить - арасть [[Special:DoubleRedirects|кавтонь кирдань]] ды [[Special:BrokenRedirects|сезень печтевтемат]]).\nЭсеть лангсо вана невтевкстнэ невтест сев, ков эряви.\n\nМель явт, улиндеряй анок лопа од лементь таркасо, лопась  '''а печтевтеви'''. Печтевтеви ансяк сестэ, зярдо лопась чаво эли ашти певтевтема лопакс, конань арась витнемань-петнемань икелькс умазо. Лиякс меремга, маштови одов лемдемс лопа икелень лемсэнзэ, зярдо теят ильведевкс; уликс лия лопа а нардави.\n\n'''ВАНОК!'''\nОдс лемдямось тусы покш ды пек апак учонь полавтовомат лопатненень, конатнес ''весеменень пек содавикст''.\nИнеськеть, поладомадо икеле васня вант, чарькодят - чарькодят козонь те тевесь вети.",
        "movepagetalktext": "Поладозь кортавтома лопась, кодак истямось ули ютавтови автоматика вельде одс лемдязенть марто, '''а ютавтови, зярдо:'''<br />\n\n*Истямо лем марто кортавтома лопа, конась аволь чаво муеви\n*Эзить путо тешкст паксясонть ало.<br />\n\nЗярдо истят тевтне, сави тонстеть лопатнень кучомс-сюлмамс, кедьсэ.",
        "movenotallowed": "Арась меремат печтевтемс лопатнесэ.",
-       "newtitle": "Ð\9eд Ð»ÐµÐ¼ÐµÐ½Ñ\8cÑ\82э:",
+       "newtitle": "Ð\9eд Ð»ÐµÐ¼ÐµÐ·э:",
        "move-watch": "Ваномс лопанть",
        "movepagebtn": "Печтевтемс лопанть",
        "pagemovedsub": "Лопась печтевтевсь",
        "import-upload": "Ёвкстамс XML датанть",
        "importlogpage": "Импортонть журналось",
        "import-logentry-upload-detail": "Совавтозь $1 {{PLURAL:$1|лиякстомтома|лиякстомтомат}}",
-       "tooltip-pt-userpage": "Теицянь лопат",
-       "tooltip-pt-mytalk": "Кортнемалопам",
-       "tooltip-pt-preferences": "Мейсэ явован лиятнень эйстэ",
+       "tooltip-pt-userpage": "{{GENDER:|Теицянь лопат}}",
+       "tooltip-pt-mytalk": "{{GENDER:|Кортнемалопам}}",
+       "tooltip-pt-preferences": "{{GENDER:|Эсеть}} аравтнемат",
        "tooltip-pt-watchlist": "Лопатне, конатнень мельга ванстнят: лиякстомтовить а лиякстомтовить",
        "tooltip-pt-mycontris": "Мезесэ лездынь мезе путынь",
        "tooltip-pt-login": "Совавтовлить эсь прят тезэнь, арась мелеть, иля.",
        "watchlistedit-normal-title": "Витнемс-петнемс ванстома лопанть",
        "watchlistedit-normal-submit": "Нардамс конякстнэнь",
        "watchlistedit-normal-done": "{{PLURAL:$1|1 конякс нардазель|$1 конякст нардазельть}} ваномань лопастот:",
-       "watchlistedit-raw-title": "Витнеме-петнеме верек ваномалопанть",
-       "watchlistedit-raw-legend": "Витнеме-петнеме верек ваномалопанть",
+       "watchlistedit-raw-title": "Витнемс-петнемс верек ваномалопанть",
+       "watchlistedit-raw-legend": "Витнемс-петнемс верек ваномалопанть",
        "watchlistedit-raw-titles": "Конякст:",
        "watchlistedit-raw-submit": "Мезе мельга ванстнят, спискаст одкстомтомс",
        "watchlistedit-raw-added": "{{PLURAL:$1|1 конякс поладозель|$1 конякст поладозельть}}:",
        "watchlistedit-raw-removed": "{{PLURAL:$1|1 конякс нардазель|$1 конякст нардазельть}}:",
        "watchlisttools-view": "Лиякстоматьне лопатнесэ потмоксстонть",
        "watchlisttools-edit": "Ваномс ды витнемс-петнемс мезе мельга ванстнят",
-       "watchlisttools-raw": "Витнеме-петнеме верек ваномалопанть",
+       "watchlisttools-raw": "Витнемс-петнемс верек ваномалопанть",
        "duplicate-defaultsort": "'''Ванок!''' Рядс аравтомань те \"$2\" панжомась саеви те \"$1\" панжомадо икеле.",
        "version": "Версия",
        "version-skins": "Аравтозь лангт",
        "compare-rev1": "Версия 1",
        "compare-rev2": "Версия 2",
        "compare-submit": "Аравтомс карадо каршо",
-       "diff-form": "'''хорма'''",
+       "diff-form": "Мейсэ явовить",
        "htmlform-submit": "Максомс",
        "htmlform-reset": "Саемс мекев полавтоматнень",
        "htmlform-selectorother-other": "Лия",
        "special-characters-group-latin": "Латиница",
        "special-characters-group-symbols": "Тешкстт",
        "special-characters-group-cyrillic": "Кириллица",
-       "randomrootpage": "Кодама понгсь юрт лопа"
+       "randomrootpage": "Кодама понгсь юртлопа"
 }
index 4bec17e..f7fcfc6 100644 (file)
@@ -9,7 +9,8 @@
                        "Luuva",
                        "Macofe",
                        "進也",
-                       "Liuxinyu970226"
+                       "Liuxinyu970226",
+                       "Yoxem"
                ]
        },
        "tog-underline": "Liân-kiat oē té-sûn:",
        "pool-timeout": "Chhiau-koè só-tēng ê sî-kan",
        "pool-queuefull": "Tūi-lia̍t pâi moá ah",
        "pool-errorunknown": "M̄-chai siáⁿ chhò-gō͘",
-       "pool-servererror": "無提供系統服務總數的統計。",
+       "pool-servererror": "Bô thê-kiong hē-thóng ho̍k-bū chóng-sò͘--ê thóng-kè ($1).",
        "poolcounter-usage-error": "Ēng-hoat chhò-gō͘: $1",
        "aboutsite": "Koan-hē {{SITENAME}}",
        "aboutpage": "Project:Koan-hē",
        "collapsible-expand": "Hiàn",
        "confirmable-confirm": "{{GENDER:$1|你}}敢確定唅?",
        "confirmable-yes": "著啦",
-       "confirmable-no": "無啦!毋是!",
+       "confirmable-no": "Bô--lah!",
        "thisisdeleted": "Khoàⁿ a̍h-sī kiù $1?",
        "viewdeleted": "Beh khoàⁿ $1?",
        "restorelink": "{{PLURAL:$1|chi̍t ê thâi-tiàu ê pian-chi̍p|$1 thâi-tiàu ê pian-chi̍p}}",
        "formerror": "Chhò-gō·: bô-hoat-tō· kā pió sàng chhut khì.",
        "badarticleerror": "Bē-tàng tiàm chit ia̍h chip-hêng chit ê tōng-chok.",
        "cannotdelete": "Bô-hoat-tō· kā hit ê ia̍h a̍h-sī iáⁿ-siōng 「$1」 thâi tiāu. (Khó-lêng pa̍t-lâng í-keng kā thâi tiāu ah.)",
-       "cannotdelete-title": "無法度共\"$1\"這頁刣掉。",
+       "cannotdelete-title": "Bô-hoat-tō͘ kā \"$1\" chit ia̍h thâi-tiāu.",
        "delete-hook-aborted": "有設定阻擋刣掉的動作。\n毋閣無其他的解說。",
        "no-null-revision": "袂當予\"$1\"產生一个空的修訂本。",
        "badtitle": "M̄-chiâⁿ piau-tê",
        "namespaceprotected": "Lí bô khoân-lī kái '''$1'''  miâ-khong-kan ê ia̍h",
        "customcssprotected": "你無受權去改這个 CSS頁,因為這个頁有包括別个用者的個人設定。",
        "customjsprotected": "你無授權去改這个JavaScript頁,因為這个頁包括別个用者的個人設定。",
-       "mycustomcssprotected": "你無授權去改這个CSS頁。",
-       "mycustomjsprotected": "你無授權去改這个JavaScript頁。",
-       "myprivateinfoprotected": "你無授權改你家己的私人資訊。",
-       "mypreferencesprotected": "你無授權改你的家己的喜愛設定。",
+       "mycustomcssprotected": "Lí bô hí-khó lâi kái chit ê CSS ia̍h.",
+       "mycustomjsprotected": "Lí bô hí-khó lâi kái chit ê Javascript ia̍h.",
+       "myprivateinfoprotected": "Lí bô hí-khó lâi kái lí ka-kī--ê su-jîn chu-sìn.",
+       "mypreferencesprotected": "Lí bô hí-khó kái ka-kī--ê iōng-chiá siat-tēng.",
        "ns-specialprotected": "特殊頁袂得改。",
        "titleprotected": "這个標題已經予[[User:$1|$1]]保護起來,袂得提來用。\n原因是 <em>$2</em>。",
        "filereadonlyerror": "因為檔案庫這馬只會使看,所以袂得改 \"$1\"這个檔案。\n鎖檔案庫的管理員講是因為:\"$3\"。",
        "invalidtitle-knownnamespace": "佇名空間 \"$2\"佮文字\"$3\"的標題袂使得。",
        "invalidtitle-unknownnamespace": "名空間編號 $1(毋知名)的\"$2\"文字標題袂使用。",
        "exception-nologin": "Bô teng-ji̍p",
-       "exception-nologin-text": "請先登入,才有法度看這頁抑對這頁做動作。",
-       "exception-nologin-text-manual": "請先$1,才有法度看這頁抑對這頁做動作。",
+       "exception-nologin-text": "Chhiáⁿ seng teng-ji̍p, chiah ū hoat-tō͘ khoàⁿ chit ia̍h iah tùi chit ia̍h chò tōng-chok.",
+       "exception-nologin-text-manual": "Chhiáⁿ seng $1, chiah ū hoat-tō͘ khoàⁿ chit ia̍h iah tùi chit ia̍h chò tōng-chok.",
        "virus-badscanner": "設定毋著:你的病毒掃描程式阮毋知:<em>$1</em>",
-       "virus-scanfailed": "掃病毒無成功(代碼$1)",
+       "virus-scanfailed": "Sáu pēⁿ-to̍k bô sêng-kong (tāi-bé: $1)",
        "virus-unknownscanner": "M̄-chai siáⁿ pēⁿ-to̍k:",
        "logouttext": "'''Lí í-keng teng-chhut.'''\nChhiaⁿ chù-ì: ū-kóa ia̍h ū khó-lêng khoàⁿ-tio̍h bē-su lí iáu teng-ji̍p tī leh; che chi-iàu piàⁿ tiāu liû-lám-khì ê khoài-chhú-khu, tiō ē chèng-siông.",
        "welcomeuser": "Hoan-gêng, $1!",
        "badretype": "Lí su-ji̍p ê 2-cho· bi̍t-bé bô tùi.",
        "userexists": "Lí phah ê iōng-chiá miâ-chheng í-keng ū lâng iōng. Chhiáⁿ lí iōng pa̍t-ê miâ.",
        "loginerror": "Teng-ji̍p chhò-gō·",
-       "createacct-error": "開口座無成功",
+       "createacct-error": "Khui kháu-chō bô sêng-kong",
        "createaccounterror": "Bô hoat-tō͘ khui kháu-chō: $1",
        "nocookiesnew": "Lí--ê iōng-chiá kháu-chō í-keng khui hó--ah, m̄-koh lí iáu-boē teng-ji̍p.\n{{SITENAME}} ū sái Cookies choè kì-lio̍k teng-ji̍p--ê iōng-chiá.\nLí bô ín-chún iōng Cookies, chhiáⁿ lí seng kā chó͘-tòng the̍h tiāu, chiah koh iōng lí--ê iōng-chiá-miâ kap bi̍t-bé teng-ji̍p.",
        "nocookieslogin": "{{SITENAME}}有用cookies做記錄用者,毋過你無允準用cookies,等你改做會當了後,才閣試。",
-       "nocookiesfornew": "因為不明的原因,阮無法度建立用者的口座。\n請先確定你的cookie會使用,閣重進入這頁,閣試一擺。",
+       "nocookiesfornew": "In-ūi put-bêng--ê goân-in, gún bô-hoat-tō͘ kiàn-li̍p iōng-chiá--ê kháu-chō. Chhiáⁿ seng khak-tēng lí--ê cookie ē-tàng ēng, têng chìn-ji̍p chit ia̍h, koh chhì chi̍t pái.",
        "noname": "Lí phah--ê iōng-chiá-miâ boē-sái.",
        "loginsuccesstitle": "Teng-ji̍p sêng-kong",
        "loginsuccess": "Lí hiān-chhú-sî í-keng teng-ji̍p {{SITENAME}} chò \"$1\".",
        "anonpreviewwarning": "<em>Lí bô teng-ji̍p. Nā-sī lí beh pó-chûn, lí--ê IP ūi-chí ē kì-lo̍k tī i--ê siu-kái le̍k-sú lāi-bīn.</em>",
        "summary-preview": "Khài-iàu ê preview:",
        "subject-preview": "Ū-lám tê-bo̍k/piau-tê:",
-       "blockednoreason": "無寫理由",
+       "blockednoreason": "bô siá lí-iû",
        "whitelistedittext": "Lí ài $1 chiah ē-sái siu-kái.",
        "nosuchsectiontitle": "Chhoé bô toān-lo̍h",
        "loginreqtitle": "Su-iàu Teng-ji̍p",
        "newarticle": "(Sin)",
        "newarticletext": "Lí tòe 1 ê liân-kiat lâi kàu 1 bīn iáu-bōe chûn-chāi ê ia̍h. Beh khai-sí pian-chi̍p chit ia̍h, chhiáⁿ tī ē-kha ê bûn-jī keh-á lāi-té phah-jī. ([$1 Bo̍k-lio̍k] kà lí án-choáⁿ chìn-hêng.) Ká-sú lí bô-tiuⁿ-tî lâi kàu chia, ē-sai chhi̍h liû-lám-khì ê '''téng-1-ia̍h''' tńg--khì.",
        "anontalkpagetext": "----''Pún thó-lūn-ia̍h bô kò·-tēng ê kháu-chō/hō·-thâu, kan-na ū 1 ê IP chū-chí (chhin-chhiūⁿ 123.456.789.123). In-ūi bô kāng lâng tī bô kāng sî-chūn ū khó-lêng tú-hó kong-ke kāng-ê IP, lâu tī chia ê oē ū khó-lêng hō· bô kāng lâng ê! Beh pī-bián chit khoán būn-tê, ē-sái khì [[Special:UserLogin|khui 1 ê hō·-thâu a̍h-sī teng-ji̍p]].''",
-       "noarticletext": "這頁這馬無內容,你會使佇別頁[[Special:Search/{{PAGENAME}}|揣這頁標題]],\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} 揣相關日誌],\n抑[{{fullurl:{{FULLPAGENAME}}|action=edit}} 創造這頁]</span>。",
+       "noarticletext": "Chit ia̍h chit-má bô loē-iông, lí ē-sái tī pa̍t ia̍h [[Special:Search/{{PAGENAME}}|chhoē chit ia̍h piau-tê]], \n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} chhoē siong-koan ji̍t-chì], \niah [{{fullurl:{{FULLPAGENAME}}|action=edit}} chhòng-chò chit ia̍h]</span>。",
        "clearyourcache": "'''Chù-ì:''' Pó-chûn liáu-āu, tio̍h ē-kì leh kā liû-lám-khì ê cache piàⁿ tiāu chiah khoàⁿ-ē-tio̍h kái-piàn: *'''Firefox / Safari:''' chhi̍h tiâu \"Shift\" kâng-sî-chūn tiám-kik ''Reload/têng-sin chài-ji̍p'' a̍h-sī chhi̍h ''Ctrl-F5'' \"Ctrl-R\" kî-tiong chi̍t ê (''Command-R'' tī Mac) \n* '''Google Chrome:''' chhi̍h ''Ctrl-Shift-R'' (''Command-Shift-R'' tī Mac)\n'''Internet Explorer :'''chhi̍h tiâu \"Ctrl\" kâng-sî-chūn tiám-kek ''Refresh/têng-sin chài-ji̍p'' a̍h-sī chhi̍h \"Ctrl-F5\" \n* '''Konqueror:'''  tiám-kek ''Reload/têng-sin chài-ji̍p'' a̍h-sī chhi̍h ''F5''\n* '''Opera:''' piàⁿ-tiāu cache tī ''Tools(ke-si) → Preferences(siat-piān)''",
        "usercssyoucanpreview": "'''Phiat-pō·''': Pó-chûn chìn-chêng ē-sái chhi̍h 'Seng khoàⁿ-māi' kiám-cha sin ê CSS a̍h-sī JavaScript.",
        "userjsyoucanpreview": "'''Phiat-pō·''': Pó-chûn chìn-chêng ē-sái chhi̍h 'Seng khoàⁿ-māi' kiám-cha sin ê CSS a̍h-sī JavaScript.",
        "histfirst": "Tùi thâu-chêng",
        "histlast": "Tùi āu-piah",
        "history-feed-item-nocomment": "$1 tī $2",
-       "revdel-restore": "改會當看無",
+       "revdel-restore": "kái ē-tàng khoàⁿ--bô",
        "revertmerge": "取消合併",
        "history-title": "\"$1\"--ê le̍k-sú pán-pún",
        "difference-title": "\"$1\" pán-pún chi-kan bô-kāng--ê tē-hng",
        "prefs-files": "Tóng-àn",
        "prefs-custom-css": "Chū-siat CSS",
        "prefs-custom-js": "Chū-siat JavaScript",
-       "prefs-common-css-js": "Só͘-ū gōa-phôe kong-ke ê CSS/JavaScript",
+       "prefs-common-config": "Só͘-ū gōa-phôe kong-ke ê CSS/JavaScript",
        "prefs-reset-intro": "Lí ē-sài ēng pún ia̍h lâi chiong lí ê siat-tì têng-siat chò pún chām kì-tēng.\nChe bē hoat-tō͘ ho̍k-goân.",
        "prefs-emailconfirm-label": "Tiān-chú-phoe khak-tēng:",
        "youremail": "Lí ê email:",
        "recentchanges-label-newpage": "Chit ê siu-kái ē sán-seng sin ia̍h",
        "recentchanges-label-minor": "Che sī sió siu-kái",
        "recentchanges-label-bot": "Che sī ki-khì-lâng kái--ê",
-       "recentchanges-label-unpatrolled": "這个編輯抑無巡視過",
+       "recentchanges-label-unpatrolled": "Chit ê pian-chi̍p iah bô sûn--koè.",
        "recentchanges-label-plusminus": "Hit ia̍h kái liáu; cheng-chha ê ūi-goân-cho͘",
        "recentchanges-legend-heading": "<strong>Ké-soeh:</strong>",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (請參考[[Special:NewPages|新頁清單]])",
        "filehist-user": "Iōng-chiá",
        "filehist-dimensions": "寸尺",
        "filehist-comment": "soat-bêng",
-       "imagelinks": "tóng-àn sù-iōng ê chōng-hòng",
+       "imagelinks": "Tóng-àn sù-iōng ê chōng-hòng",
        "linkstoimage": "ē-kha {{PLURAL:$1|ê ia̍h}} ū iōng tio̍h chit ê iáⁿ-siōng:",
        "nolinkstoimage": "Bô poàⁿ ia̍h liân kàu chit tiuⁿ iáⁿ-siōng.",
        "sharedupload-desc-here": "Chit--ê $1--ê tóng-àn ū khó-lêng hō͘ kî-thaⁿ--ê choan-àn ēng tio̍h.\nChia sī chit--ê tóng-àn i--ê [$2 soat-bêng].",
index d8cd8f6..00975fd 100644 (file)
        "userjspreview": "'''Arricuordate ca chest'è sulamente n'anteprimma p' 'o JavaScript perzunale. 'E cagnamiente nun so' state ancora sarvate!'''",
        "sitecsspreview": "'''Arricuordate ca chest'è sulamente n'anteprimma p' 'o CSS. 'E cagnamiente nun so' state ancora sarvate!'''",
        "sitejspreview": "'''Arricuordate ca chest'è sulamente n'anteprimma p' 'o codece JavaScript. 'E cagnamiente nun so' state ancora sarvate!'''",
-       "userinvalidcssjstitle": "'''Attenziò:''' Nun esiste nisciuna skin c' 'o nomme \"$1\". Vide ch' 'e paggene .css e .js personalezzate teneno nu titolo n minucola, p'esempio {{ns:user}}:Esempio/vector.css e nun {{ns:user}}:Esempio/Vector.css.",
+       "userinvalidconfigtitle": "'''Attenziò:''' Nun esiste nisciuna skin c' 'o nomme \"$1\". Vide ch' 'e paggene .css e .js personalezzate teneno nu titolo n minucola, p'esempio {{ns:user}}:Esempio/vector.css e nun {{ns:user}}:Esempio/Vector.css.",
        "updated": "(Agghiurnato)",
        "note": "'''Nota:'''",
        "previewnote": "'''Chesta è sola n'anteprimma; 'e cagnamiénte â paggena nun songo ancora sarvate!'''",
        "prefs-files": "File",
        "prefs-custom-css": "CSS personalizzato",
        "prefs-custom-js": "JavaScript personalizzato",
-       "prefs-common-css-js": "CSS/JavaScript spartuto pe' tutt' 'e skin:",
+       "prefs-common-config": "CSS/JavaScript spartuto pe' tutt' 'e skin:",
        "prefs-reset-intro": "Putisse ausà sta paggena pe' rimpizzà 'e preferenze proprie comme chille predefinite d' 'o sito.\nL'operazione nun se può annullà.",
        "prefs-emailconfirm-label": "Cunferma 'e ll'e-mail:",
        "youremail": "E-mail:",
        "htmlform-user-not-exists": "<strong>$1</strong> nun esiste.",
        "htmlform-user-not-valid": "<strong>$1</strong> nun è nu nomme buono.",
        "logentry-delete-delete": "$1 {{GENDER:$2|scancellaje}} 'a paggena $3",
-       "logentry-delete-restore": "$1 {{GENDER:$2|arrepigliaje}} 'a paggena $3",
+       "logentry-delete-restore": "$1 {{GENDER:$2|arrepigliaje}} 'a paggena $3 ($4)",
        "logentry-delete-event": "$1 {{GENDER:$2|cagnaie}} 'a vesibbiletà 'e {{PLURAL:$5|n'azione d' 'o riggistro|$5 aziune d' 'o riggistro}} ncopp' 'a 'a $3: $4",
        "logentry-delete-revision": "$1 {{GENDER:$2|cagnaie}} 'a vesibbiletà 'e {{PLURAL:$5|na verziona|$5 verziune}} ncopp' 'a 'a $3: $4",
        "logentry-delete-event-legacy": "$1 {{GENDER:$2|cagnaie}} 'a vesibbiletà 'e l'aziune dint' 'o riggistro ncopp' 'a $3: $4",
index 276c072..fe826a2 100644 (file)
        "userjspreview": "'''Husk at dette bare er en test eller forhåndsvisning av ditt bruker-JavaScript, og det ikke er lagret!'''",
        "sitecsspreview": "'''Husk at du bare forhåndsviser denne CSS-en.'''\n'''Den har ikke blitt lagret ennå!'''",
        "sitejspreview": "'''Husk at du bare forhåndsviser denne JavaScript-koden.'''\n'''Den har ikke blitt lagret ennå!'''",
-       "userinvalidcssjstitle": "'''Advarsel:''' Det finnes ikke noe utseende ved navn «$1». Husk at .css- og .js-sider bruker titler i små bokstaver, for eksempel {{ns:user}}:Eksempel/vector.css, ikke {{ns:user}}:Eksempel/Vector.css",
+       "userinvalidconfigtitle": "'''Advarsel:''' Det finnes ikke noe utseende ved navn «$1». Husk at .css- og .js-sider bruker titler i små bokstaver, for eksempel {{ns:user}}:Eksempel/vector.css, ikke {{ns:user}}:Eksempel/Vector.css",
        "updated": "(Oppdatert)",
        "note": "'''Merk:'''",
        "previewnote": "'''Husk at dette bare er en forhåndsvisning.'''\nEndringene dine har ikke blitt lagret ennå!",
        "prefs-files": "Filer",
        "prefs-custom-css": "Personlig CSS",
        "prefs-custom-js": "Personlig Javascript",
-       "prefs-common-css-js": "Delt CSS/JS for alle drakter:",
+       "prefs-common-config": "Delt CSS/JS for alle drakter:",
        "prefs-reset-intro": "Du kan bruke denne siden til å tilbakestille innstillingene dine til standardinnstillingene.\nDette kan ikke tilbakestilles.",
        "prefs-emailconfirm-label": "E-postbekreftelse:",
        "youremail": "E-post:",
        "thumbnail_dest_directory": "Klarte ikke å opprette målmappe",
        "thumbnail_image-type": "Bildetypen støttes ikke",
        "thumbnail_gd-library": "Ufullstendig konfigurering av GD library: mangler funksjonen $1",
+       "thumbnail_image-size-zero": "Filstørrelsen til bildet ser ut til å være null.",
        "thumbnail_image-missing": "Filen ser ut til å mangle: $1",
        "thumbnail_image-failure-limit": "Det har vært for mange nylige forsøk ($1 eller flere) på å gjengi dette miniatyrbildet. Vennligst prøv igjen senere.",
        "import": "Importer sider",
index b621163..698a7b8 100644 (file)
        "youhavenewmessagesmulti": "Je hebben nieje berichten op $1",
        "editsection": "bewark",
        "editold": "bewark",
-       "viewsourceold": "brontekste bekieken",
+       "viewsourceold": "brontekste bekyken",
        "editlink": "bewark",
-       "viewsourcelink": "brontekste bekieken",
+       "viewsourcelink": "brontekste bekyken",
        "editsectionhint": "Bewarkingsveld: $1",
        "toc": "Inhold",
        "showtoc": "Bekieken",
        "perfcached": "Disse gegevens koemen uut t tussengeheugen en bin misschien niet aktueel. Der {{PLURAL:$1|is hooguut een resultaot|bin hooguut $1 resultaoten}} beschikbaor in t tussengeheugen.",
        "perfcachedts": "Disse gegevens koemen uut t tussengeheugen die veur t lest bie-ewörken is op $2 um $3. Der {{PLURAL:$4|is hooguut een resultaot|bin hooguut $4 resultaoten}} beschikbaor in t tussengeheugen.",
        "querypage-no-updates": "'''Disse zied wördt niet meer bie-ewörken.'''",
-       "viewsource": "Brontekste bekieken",
+       "viewsource": "Brontekste bekyken",
        "viewsource-title": "Bron bekieken van $1",
        "actionthrottled": "Haandeling tegenehöllen",
        "actionthrottledtext": "As maotregel tegen t plaotsen van alderhaande moek, is t antal keren da'j disse haandeling in n korte tied uutvoeren kunnen beteund. Je hebben de limiet overschrejen. Probeer t over n antal minuten weer.",
        "filereadonlyerror": "Kon t bestaand \"$1\" niet anpassen umdat de bestaandsmap \"$2\" op dit moment op allinnig-lezen steet.\n\nDe op-egeven reden is: \"$3\".",
        "invalidtitle-knownnamespace": "Ongeldige titel mit naamruumte \"$2\" en tekste \"$3\"",
        "invalidtitle-unknownnamespace": "Ongeldige titel mit onbekend naamruumtenummer $1 en tekste \"$2\"",
-       "exception-nologin": "Niet an-emeld",
+       "exception-nologin": "Nyt an-emeld",
        "exception-nologin-text": "Um disse zied te bekieken of disse haandeling uut te kunnen voeren mu'j [[Special:Userlogin|an-emeld]] ween bie disse wiki.",
        "virus-badscanner": "Slichte konfigurasie: onbekend antivirusprogramma: ''$1''",
        "virus-scanfailed": "inlezen is mislokt (kode $1)",
        "logouttext": "'''Je bin noen aofemeld.'''\n\nt Kan ween dat der wat ziejen bin die weeregeven wörden as of je an-emeld bin totda'j t tussengeheugen van joew webkieker leegmaken.",
        "welcomeuser": "Welkom, $1!",
        "welcomecreation-msg": "Joew gebruker is an-emaakt.\nVergeet niet joew [[Special:Preferences|veurkeuren veur {{SITENAME}}]] an te passen.",
-       "yourname": "Gebrukersnaam",
-       "userlogin-yourname": "Gebrukersnaam",
+       "yourname": "Gebrukersname",
+       "userlogin-yourname": "Gebrukersname",
        "userlogin-yourname-ph": "Geef joew gebrukersnaam op",
        "createacct-another-username-ph": "Vul de gebrukersnaam in",
        "yourpassword": "Wachtwoord",
        "yourpasswordagain": "Opniej invoeren",
        "createacct-yourpasswordagain": "Wachtwoord bevestigen",
        "createacct-yourpasswordagain-ph": "Geef t wachtwoord opniej op",
-       "userlogin-remembermypassword": "Vanzelf anmelden",
+       "userlogin-remembermypassword": "Vanselv anmelden",
        "userlogin-signwithsecure": "Beveiligde verbiending gebruken",
        "yourdomainname": "Joew domein",
        "password-change-forbidden": "Je kunnen joew wachtwoord niet wiezigen op disse wiki.",
        "externaldberror": "Der gung iets fout bie de externe authentisering, of je maggen je gebrukersprofiel niet bewarken.",
        "login": "Anmelden",
        "nav-login-createaccount": "Anmelden",
-       "logout": "Aofmelden",
+       "logout": "Ofmelden",
        "userlogout": "Aofmelden",
-       "notloggedin": "Niet an-emeld",
-       "userlogin-noaccount": "He'j nog gien gebrukersnaam?",
+       "notloggedin": "Nyt an-emeld",
+       "userlogin-noaccount": "Heb jy noch gyn gebrukersname?",
        "userlogin-joinproject": "Wörd lid van {{SITENAME}}",
-       "createaccount": "Inschrieven",
-       "userlogin-resetpassword-link": "Joew wachtwoord vergeten?",
-       "userlogin-helplink2": "Hulpe bie t anmelden",
-       "userlogin-loggedin": "Je bin al an-emeld as {{GENDER:$1|$1}}.\nGebruuk t onderstaonde formulier um an te melden as n aandere gebruker.",
-       "userlogin-createanother": "n Aandere gebrukerskonto anmaken",
+       "createaccount": "Inskryven",
+       "userlogin-resetpassword-link": "Juuw wachtwoord vergeaten?",
+       "userlogin-helplink2": "Hülpe by et anmelden",
+       "userlogin-loggedin": "Je binnen al an-emeld as {{GENDER:$1|$1}}.\nGebruuk et formulyr hyrunder üm an te melden as een andere gebruker.",
+       "userlogin-createanother": "Een andere gebrukerskonto anmaken",
        "createacct-emailrequired": "Netpostadres",
        "createacct-emailoptional": "Netpostadres (niet verplicht)",
        "createacct-email-ph": "Geef joew netpostadres op",
        "createaccount-text": "Der hef der ene n gebruker an-emaakt op {{SITENAME}} ($4), mit de naam $2 en t wachtwoord \"$3\". \nMeld je eigen noen an en wiezig t wachtwoord.\n\nNegeer dit bericht as disse gebruker zonder joew toestemming an-emaakt is.",
        "login-throttled": "Je hebben lestens te vake eprobeerd um an te melden mit n verkeerd wachtwoord.\nJe mutten effen $1 wachten veurda'j t opniej proberen.",
        "login-abort-generic": "Je bin niet an-emeld - Aofebreuken",
-       "loginlanguagelabel": "Taal: $1",
+       "loginlanguagelabel": "Taal / språke: $1",
        "suspicious-userlogout": "Joew verzeuk um of te melden is aofewezen umdat t dernaor uutziet dat t verstuurd is deur n kepotte webkieker of tussenopslagbuffer",
        "createacct-another-realname-tip": "Joew echte naam opgeven is niet verplicht.\nA'j t invullen, dan zu'w t gebruken um erkenning te geven veur joew warkzaamhejen.",
        "pt-login": "Anmelden",
        "pt-login-button": "Anmelden",
-       "pt-createaccount": "Inschrieven",
-       "pt-userlogout": "Aofmelden",
+       "pt-createaccount": "Inskryven",
+       "pt-userlogout": "Ofmelden",
        "php-mail-error-unknown": "Der was n onbekende fout mit de mail()-funksie van PHP",
        "user-mail-no-addy": "Eprobeerd n berichjen te versturen zonder n netpostadres",
        "user-mail-no-body": "Der is eprobeerd n netbreef zonder tekste of mit n biester korte tekste te versturen.",
        "userjspreview": "'''Denk deran da'j joew persoonlike JavaScript allinnig nog mer an t bekieken bin, t is nog niet op-esleugen!'''",
        "sitecsspreview": "'''Je bin allinnig mer de CSS an t naokieken.'''\n'''t Is nog niet op-esleugen!'''",
        "sitejspreview": "'''Je bin allinnig mer de JavaScript-kode an t naokieken.'''\n'''t Is nog niet op-esleugen!'''",
-       "userinvalidcssjstitle": "'''Waorschuwing:''' der is gien uutvoering mit de naam \"$1\". Vergeet niet dat joew eigen .css- en .js-ziejen beginnen mit n kleine letter, bv. \"{{ns:user}}:Naam/'''v'''ector\" in plaotse van \"{{ns:user}}:Naam/'''V'''ector.css\".",
+       "userinvalidconfigtitle": "'''Waorschuwing:''' der is gien uutvoering mit de naam \"$1\". Vergeet niet dat joew eigen .css- en .js-ziejen beginnen mit n kleine letter, bv. \"{{ns:user}}:Naam/'''v'''ector\" in plaotse van \"{{ns:user}}:Naam/'''V'''ector.css\".",
        "updated": "(Bewark)",
        "note": "'''Opmarking:'''",
        "previewnote": "'''Waort je: dit is n naokiekzied.'''\nJoew tekste is niet op-esleugen!",
        "prefs-files": "Bestaanden",
        "prefs-custom-css": "Persoonlike CSS",
        "prefs-custom-js": "Persoonlike JS",
-       "prefs-common-css-js": "Edeelden CSS/JS veur elke vormgeving:",
+       "prefs-common-config": "Edeelden CSS/JS veur elke vormgeving:",
        "prefs-reset-intro": "Je kunnen disse zied gebruken um joew veurkeuren naor de standardinstellingen weerumme te zetten.\nDisse haandeling kan niet ongedaonemaakt wörden.",
        "prefs-emailconfirm-label": "Netpostbevestiging:",
        "youremail": "Netpostadres (niet verplicht) *",
        "uploadbtn": "Bestaand opsturen",
        "reuploaddesc": "Weerumme naor de opstuurzied",
        "upload-tryagain": "Bestaandsbeschrieving biewarken",
-       "uploadnologin": "Niet an-emeld",
+       "uploadnologin": "Nyt an-emeld",
        "uploadnologintext": "Je mutten $1 ween um bestaanden op te kunnen sturen.",
        "upload_directory_missing": "De inlaojmap veur bestaanden ($1) is vort en kon niet an-emaakt wörden deur de webserver.",
        "upload_directory_read_only": "Op t moment ku'j gien bestaanden opsturen vanwegen techniese problemen ($1).",
        "categoriesfrom": "Laot kategorieën zien vanaof:",
        "deletedcontributions": "Vortedaone gebrukersbiedragen",
        "deletedcontributions-title": "Vortedaone gebrukersbiedragen",
-       "sp-deletedcontributions-contribs": "biedragen",
+       "sp-deletedcontributions-contribs": "bydragen",
        "linksearch": "Uutgaonde verwiezingen zeuken",
        "linksearch-pat": "Zeukpetroon:",
        "linksearch-ns": "Naamruumte:",
        "listgrouprights-addgroup-self-all": "Kan alle groepen bie de eigen gebruker doon",
        "listgrouprights-removegroup-self-all": "Kan alle groepen vortdoon van eigen gebruker",
        "trackingcategories": "Volgkategorieën",
-       "mailnologin": "Niet an-emeld.",
+       "mailnologin": "Nyt an-emeld.",
        "mailnologintext": "Je mutten [[Special:UserLogin|an-emeld]] ween en n geldig e-mailadres in \"[[Special:Preferences|mien veurkeuren]]\" invoeren um disse funksie te kunnen gebruken.",
        "emailuser": "n Bericht sturen",
        "emailuser-title-target": "Disse {{GENDER:$1|gebruker}} n bericht sturen",
        "watchlistfor2": "Veur $1 ($2)",
        "nowatchlist": "Gien artikels in volglieste.",
        "watchlistanontext": "$1 is verplicht um joew volglieste te bekieken of te wiezigen.",
-       "watchnologin": "Niet an-emeld",
+       "watchnologin": "Nyt an-emeld",
        "addwatch": "Op mien volglieste zetten",
        "addedwatchtext": "De zied \"[[:$1]]\" steet noen op joew [[Special:Watchlist|volglieste]].\nToekomstige wiezigingen op disse zied en de overlegzied zullen hier vermeld wörden.",
        "removewatch": "Van mien volglieste aofhaolen",
        "blanknamespace": "(Heufdnaamruumte)",
        "contributions": "{{GENDER:$1|Biedragen van disse gebruker}}",
        "contributions-title": "Biedragen van $1",
-       "mycontris": "Mien biedragen",
-       "anoncontribs": "Biedragen",
+       "mycontris": "Myn bydragen",
+       "anoncontribs": "Bydragen",
        "contribsub2": "Veur {{GENDER:$3|$1}} ($2)",
        "nocontribs": "Gien wiezigingen evunnen die an de estelde criteria voldoon.",
        "uctop": "(leste wieziging)",
        "blocklink": "blokkeren",
        "unblocklink": "deblokkeer",
        "change-blocklink": "blokkering wiezigen",
-       "contribslink": "biedragen",
+       "contribslink": "bydragen",
        "emaillink": "netpostbericht sturen",
        "autoblocker": "Vanzelf eblokkeerd umdat t IP-adres overenekömp mit t IP-adres van [[User:$1|$1]], die eblokkeerd is mit as reden: \"$2\"",
        "blocklogpage": "Blokkeerlogboek",
index e71366a..49a51cf 100644 (file)
        "userjsyoucanpreview": "'''Tipp:''' Bruuk den Vörschau-Knoop, üm dien nieg JS vör dat Spiekern to testen.",
        "usercsspreview": "'''Denk doran, dat du blots en Vörschau vun dien CSS ankickst, dat is noch nich spiekert!'''",
        "userjspreview": "'''Denk doran, dat du blots en Vörschau vun dien JS ankiekst, dat is noch nich spiekert!'''",
-       "userinvalidcssjstitle": "'''Wohrschau:''' Dat gifft keen Skin „$1“. Denk dor an, dat .css- un .js-Sieden  för Brukers mit en lütten Bookstaven anfangen mööt, to’n Bispeel ''{{ns:user}}:Brukernaam/vector.css'' un nich ''{{ns:user}}:Brukernaam/Vector.css''.",
+       "userinvalidconfigtitle": "'''Wohrschau:''' Dat gifft keen Skin „$1“. Denk dor an, dat .css- un .js-Sieden  för Brukers mit en lütten Bookstaven anfangen mööt, to’n Bispeel ''{{ns:user}}:Brukernaam/vector.css'' un nich ''{{ns:user}}:Brukernaam/Vector.css''.",
        "updated": "(Ännert)",
        "note": "'''Wohrschau:'''",
        "previewnote": "Dit is bloots en Vörschau, de Sied is noch nich spiekert!'''",
        "prefs-files": "Datein",
        "prefs-custom-css": "Anpasst CSS",
        "prefs-custom-js": "Anpasst JS",
-       "prefs-common-css-js": "Deelt CSS/JavaScript för all Skins:",
+       "prefs-common-config": "Deelt CSS/JavaScript för all Skins:",
        "prefs-reset-intro": "Du kannst disse Sied bruken, dien Instellungen al op de Standardinstellung trüchtosetten.\nDat kann nich wedder ungeschehn maakt warrn.",
        "prefs-emailconfirm-label": "E-Mail-Bestätigung:",
        "youremail": "Dien E-Mail (kene Plicht) *",
index 3a53657..cc780da 100644 (file)
@@ -25,7 +25,8 @@
                        "Suniltheblue",
                        "Irus",
                        "रमेश सिंह बोहरा",
-                       "Nirajan pant"
+                       "Nirajan pant",
+                       "Drjpoudel"
                ]
        },
        "tog-underline": "रेखाङ्कित लिङ्क:",
        "botpasswords-label-cancel": "रद्द गर्ने",
        "botpasswords-label-delete": "मेट्ने",
        "botpasswords-label-resetpassword": "प्रवेसशब्द पुनः तय गर्ने",
+       "botpasswords-label-grants-column": "प्रमाणित गरियो",
+       "botpasswords-bad-appid": "बोट नाम \"$1\" मान्य छैन।",
+       "botpasswords-insert-failed": "\"$1\" बोट नाम थप्न असफल भयो। के यो पहिले नै थपिएको थियो?",
+       "botpasswords-update-failed": "\"$1\" बोट नाम अद्यावधिक गर्न असफल भयो। के यो हटाइयो हो?",
+       "botpasswords-created-title": "बोट पासवर्ड सिर्जना गरियो",
        "resetpass_forbidden": "पासवर्ड परिवर्तन गर्न मिल्दैन",
        "resetpass-no-info": "यो पृष्ठ सिधै हेर्नको लागि तपाईंले प्रवेश गर्नुपर्छ ।",
        "resetpass-submit-loggedin": "प्रवेसशब्द परिवर्तन गर्ने",
        "userjspreview": "<strong>याद राख्नुहोस तपाईंले आफ्नो प्रयोगकर्ता जाभास्क्रिप्टको पूर्वावलोकन मात्र हेरिरहनु भएको छ।\nयसलाइ अहिले सम्म सङ्ग्रह गरिएको छैन!</strong>",
        "sitecsspreview": "<strong>याद राख्नुहोस् तपाईंले केवल विश्वव्यापी सियसयसको पूर्वावलोकन मात्र अवलोकन गर्नुभएको छ ।\nयसलाई अहिलेसम्म सङ्ग्रह गरिएको छैन!</strong>",
        "sitejspreview": "<strong>याद राख्नुहोस् तपाईंले केवल जाभास्क्रिप्ट कोडको पूर्वावलोकन मात्र हेरिरहनु भएको छ।\nयसलाई अहिले सम्म सङ्ग्रह गरिएको छैन!</strong>",
-       "userinvalidcssjstitle": "<strong>चेतावनी:</strong> यहाँ कुनैपनि \"$1\" नामको खोल छैन।\nप्रचलित .css तथा .js पृष्ठहरूले निम्नपद शीर्षक प्रयोग गर्छन्, जस्तै {{ns:user}}:Foo/Vector.css को सट्टामा {{ns:user}}:Foo/vector.css",
+       "userinvalidconfigtitle": "<strong>चेतावनी:</strong> यहाँ कुनैपनि \"$1\" नामको खोल छैन।\nप्रचलित .css तथा .js पृष्ठहरूले निम्नपद शीर्षक प्रयोग गर्छन्, जस्तै {{ns:user}}:Foo/Vector.css को सट्टामा {{ns:user}}:Foo/vector.css",
        "updated": "नवीन",
        "note": "'''सूचना:'''",
        "previewnote": "'''याद राख्नुहोस् यो केवल पूर्वावलोकन मात्र हो; तपाईंका परिवर्तनहरू संग्रहित भएका छैनन्!'''",
        "prefs-files": "फाइलहरू",
        "prefs-custom-css": "अनुकुलित CSS",
        "prefs-custom-js": "अनुकुलित JS",
-       "prefs-common-css-js": "साझा CSS/जाभा स्क्रिप्ट सबै त्वचा(स्किन)को लागि:",
+       "prefs-common-config": "साझा CSS/जाभा स्क्रिप्ट सबै त्वचा(स्किन)को लागि:",
        "prefs-reset-intro": "तपाईं यो पृष्ठलाई आफ्नो अभिरुचीहरू साइट पूर्वावस्थामा फर्काउन प्रयोग गर्न सक्नुहुन्छ । त्यस पछि यसलाई रद्द गर्न सक्नुहुन्न ।",
        "prefs-emailconfirm-label": "इ-मेल एकिन प्रक्रिया :",
        "youremail": "ईमेल",
        "userrights-changeable-col": "तपाईंले परिवर्तन गर्न सक्ने समूहहरू",
        "userrights-unchangeable-col": "तपाईंले परिवर्तन गर्न नसक्ने समूहहरू",
        "userrights-irreversible-marker": "$1*",
+       "userrights-expiry-none": "समाप्त हुँदैन",
+       "userrights-expiry": "म्याद सकिन्छ:",
+       "userrights-expiry-existing": "अवस्थित समाप्ति समय: $3, $2",
+       "userrights-expiry-othertime": "अन्य समय:",
+       "userrights-expiry-options": "१ दिन: १ दिन, १ हप्ता: १ हप्ता, १ महिना: १ महिना, ३ महिना: ३ महिना, ६ महिना: ६ महिना, १ वर्ष: १ वर्ष",
+       "userrights-invalid-expiry": "\"$1\" समूहको लागि समाप्ति समय अमान्य छ।",
+       "userrights-expiry-in-past": "\"$1\" समूहको अन्तिम समय बितेको छ।",
        "userrights-conflict": "प्रयोगकर्ताको अधिकार परिवर्तनमा मतभेद भयो ! कृपया तपाईंको परिवर्तन पुनरावलोकन तथा पुष्टि गर्नुहोस् ।",
        "group": "समूह :",
        "group-user": "प्रयोगकर्ताहरू",
        "right-managechangetags": "डाटाबेसबाट [[Special:Tags|tags]] बनाउने र हटाउने",
        "right-applychangetags": "एकको परिवर्तन सहित [[Special:Tags|tags]] लागु गर्ने",
        "right-changetags": "जोड्ने र हटाउने स्वतन्त्र [[Special:Tags|ट्याग]] व्यक्तिगत अवतरणहरू र लग इन्ट्रीहरूमा",
+       "right-deletechangetags": "डाटाबेसबाट  [[Special:Tags|tags]] हटाउनुहोस्",
+       "grant-generic": "\"$1\" अधिकार बन्डल",
+       "grant-group-page-interaction": "पृष्ठहरूसँग अन्तरक्रिया गर्नुहोस्",
+       "grant-group-file-interaction": "मिडियासँग अन्तरक्रिया गर्नुहोस्",
+       "grant-group-watchlist-interaction": "तपाईंको दृष्टिसूचीसँग अन्तरक्रिया गर्नुहोस्",
+       "grant-group-email": "इ-मेल पठाउनुहोस्",
        "grant-createeditmovepage": "पृष्ठहरूमा परिवर्तन गर्नुहोस्",
        "grant-editmycssjs": "तपाईंको प्रयोगकर्ता CSS/JavaScript सम्पादन गर्नुहोस्",
        "grant-editmyoptions": "तपाईंको प्रयोगकर्ता अभिरूचीहरूलाई सम्पादन गर्नुहोस्",
index a6d0fad..db558b3 100644 (file)
@@ -93,7 +93,7 @@
        "userjsyoucanpreview": "'''Tip:''' gebruik de knop \"{{int:showpreview}}\" om je nieuwe JavaScript te testen alvorens op te slaan.",
        "usercsspreview": "'''Dit is alleen een voorvertoning van je persoonlijke CSS.\nDeze is nog niet opgeslagen!'''",
        "userjspreview": "'''Let op: je test nu je persoonlijke JavaScript.'''\n'''De pagina is niet opgeslagen!'''",
-       "userinvalidcssjstitle": "'''Waarschuwing:''' er is geen uiterlijk \"$1\".\nJe eigen .css- en .js-pagina's beginnen met een kleine letter, bijvoorbeeld {{ns:user}}:Naam/vector.css in plaats van {{ns:user}}:Naam/Vector.css.",
+       "userinvalidconfigtitle": "'''Waarschuwing:''' er is geen uiterlijk \"$1\".\nJe eigen .css- en .js-pagina's beginnen met een kleine letter, bijvoorbeeld {{ns:user}}:Naam/vector.css in plaats van {{ns:user}}:Naam/Vector.css.",
        "previewnote": "'''Let op: dit is een controlepagina.'''\nJe tekst is niet opgeslagen!",
        "previewconflict": "Deze voorvertoning geeft aan hoe de tekst in het bovenste veld eruit ziet als je deze opslaat.",
        "session_fail_preview": "'''Je bewerking is niet verwerkt, omdat de sessiegegevens verloren zijn gegaan.\nProbeer het opnieuw.\nAls het dan nog niet lukt, [[Special:UserLogout|meld jezelf dan af]] en weer aan.'''",
index 48620c4..db1959d 100644 (file)
        "userjspreview": "'''Let op: u test nu uw persoonlijke JavaScript.'''\n'''De pagina is niet opgeslagen!'''",
        "sitecsspreview": "'''Dit is alleen een voorvertoning van de CSS.'''\n'''Deze is nog niet opgeslagen!'''",
        "sitejspreview": "'''Dit is alleen een voorvertoning van de JavaScriptcode.'''\n'''Deze is nog niet opgeslagen!'''",
-       "userinvalidcssjstitle": "'''Waarschuwing:''' er is geen uiterlijk \"$1\".\nUw eigen .css- en .js-pagina's beginnen met een kleine letter, bijvoorbeeld {{ns:user}}:Naam/vector.css in plaats van {{ns:user}}:Naam/Vector.css.",
+       "userinvalidconfigtitle": "'''Waarschuwing:''' er is geen uiterlijk \"$1\".\nUw eigen .css- en .js-pagina's beginnen met een kleine letter, bijvoorbeeld {{ns:user}}:Naam/vector.css in plaats van {{ns:user}}:Naam/Vector.css.",
        "updated": "(Bijgewerkt)",
        "note": "<strong>Opmerking:</strong>",
        "previewnote": "'''Let op: dit is een controlepagina.'''\nUw tekst is niet opgeslagen!",
        "prefs-files": "Bestanden",
        "prefs-custom-css": "Aangepaste CSS",
        "prefs-custom-js": "Aangepast JavaScript",
-       "prefs-common-css-js": "Gedeelde CSS/JavaScript voor elke vormgeving:",
+       "prefs-common-config": "Gedeelde CSS/JavaScript voor elke vormgeving:",
        "prefs-reset-intro": "Gebruik deze functie om uw voorkeuren te herstellen naar de standaardinstellingen.\nDeze handeling kan niet ongedaan gemaakt worden.",
        "prefs-emailconfirm-label": "E-mailbevestiging:",
        "youremail": "E-mailadres:",
        "thumbnail_dest_directory": "Niet in staat doelmap aan te maken",
        "thumbnail_image-type": "Dit bestandstype wordt niet ondersteund",
        "thumbnail_gd-library": "De instellingen voor de GD-bibliotheek zijn incompleet. De functie $1 ontbreekt",
+       "thumbnail_image-size-zero": "De afbeeldingsbestandsgrootte lijkt nul te zijn.",
        "thumbnail_image-missing": "Het bestand lijkt niet aanwezig te zijn: $1",
        "thumbnail_image-failure-limit": "Het maken van een miniatuurafbeelding is te vaak mislukt ($1 keer of vaker). Probeer het later nog eens.",
        "import": "Pagina's importeren",
index 17925d6..dbdefda 100644 (file)
        "userjspreview": "'''Hugs at du berre testar eller førehandsviser brukar-JavaScript-et ditt. Det har ikkje vorte lagra enno!'''",
        "sitecsspreview": "'''Hugs at du berre førehandsviser dette stilarket. '''\n'''Det er ikkje lagra enno!'''",
        "sitejspreview": "'''Hugs at du berre førehandsviser denne JavaScript-koden.'''\n'''Han er ikkje lagra enno!'''",
-       "userinvalidcssjstitle": "'''Åtvaring:''' Det finst ikkje noka sidedrakt som heiter «$1». Hugs på at vanlege .css- og .js-sider brukar titlar med små bokstavar, til dømes {{ns:user}}:Døme/vector.css, og ikkje {{ns:user}}:Døme/Vector.css.",
+       "userinvalidconfigtitle": "'''Åtvaring:''' Det finst ikkje noka sidedrakt som heiter «$1». Hugs på at vanlege .css- og .js-sider brukar titlar med små bokstavar, til dømes {{ns:user}}:Døme/vector.css, og ikkje {{ns:user}}:Døme/Vector.css.",
        "updated": "(Oppdatert)",
        "note": "'''Merk:'''",
        "previewnote": "'''Hugs at dette berre er ei førehandsvising.'''\nEndringane dine er ikkje lagra enno!",
        "revdelete-no-file": "Fila som vart synt til finst ikkje.",
        "revdelete-show-file-confirm": "Er du viss på at du ynskjer å sjå ein sletta versjon av fila \"<nowiki>$1</nowiki>\" frå $2 ved $3?",
        "revdelete-show-file-submit": "Ja",
+       "revdelete-selected-text": "{{PLURAL:$1|Vald versjon|Valde versjonar}} av [[:$2]]:",
        "logdelete-selected": "{{PLURAL:$1|Vald loggoppføring|Valde loggoppføringar}}:",
        "revdelete-confirm": "Stadfest at du ynskjer å gjera dette, at du skjønar konsekvensane, og at du gjer det i samsvar med [[{{MediaWiki:Policy-url}}|retningslinene]].",
        "revdelete-suppress-text": "Løyning av sideversjonar bør '''berre''' nyttast i desse tilfella:\n* Mogeleg ærekrenkjande informasjon\n* Upassanda personleg informasjon\n*: ''heimeadresser og -telefonnummer,  personnummer, osb.''",
-       "revdelete-legend": "Vel avgrensing for synlegdom",
+       "revdelete-legend": "Set avgrensing av synlegdom",
        "revdelete-hide-text": "Versjonstekst",
        "revdelete-hide-image": "Skjul filinnhald",
        "revdelete-hide-name": "Gøym handling og sidenamn",
        "titlematches": "Sidetitlar med treff på førespurnaden",
        "textmatches": "Sider med treff på førespurnaden",
        "notextmatches": "Ingen sider hadde treff på førespurnaden",
-       "prevn": "førre {{PLURAL:$1|$1}}",
-       "nextn": "neste {{PLURAL:$1|$1}}",
+       "prevn": "{{PLURAL:$1|førre|førre $1}}",
+       "nextn": "{{PLURAL:$1|neste|neste $1}}",
        "prev-page": "førre sida",
        "next-page": "neste side",
        "prevn-title": "Førre $1 {{PLURAL:$1|resultat|resultat}}",
        "prefs-files": "Filer",
        "prefs-custom-css": "Eigendefinert CSS",
        "prefs-custom-js": "Eigendefinert JavaScript",
-       "prefs-common-css-js": "Delt CSS/JavaScript for alle draktene:",
+       "prefs-common-config": "Delt CSS/JavaScript for alle draktene:",
        "prefs-reset-intro": "Du kan nytta denne sida til å tilbakestilla innstillingane dine til standardinnstillingane.\nDette kan ikkje tilbakestillast.",
        "prefs-emailconfirm-label": "Stadfesting av e-post:",
        "youremail": "E-post:",
        "right-unblockself": "Avblokkera seg sjølve",
        "right-protect": "Endra vernenivå og verna sider",
        "right-editprotected": "Endre verna sider",
+       "right-editcontentmodel": "Endra innhaldsmodellen til ei side",
        "right-editinterface": "Redigere brukargrensesnittet",
        "right-editusercss": "Endre andre brukarar sine CSS-filer",
        "right-edituserjs": "Endre andre brukarar sine JS-filer",
        "action-viewmywatchlist": "sjå overvakingslista di",
        "action-viewmyprivateinfo": "sjå den private informasjonen din",
        "action-editmyprivateinfo": "endra den private informasjonen din",
+       "action-editcontentmodel": "endra innhaldsmodellen til ei side",
        "nchanges": "{{PLURAL:$1|Éi endring|$1 endringar}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|sidan sist vitjing}}",
        "enhancedrc-history": "historikk",
        "rollback-success": "Rulla attende endringane av $1, attende til siste versjonen av $2.",
        "sessionfailure-title": "Feil med omgangen.",
        "sessionfailure": "Det ser ut til å vera eit problem med innloggingsøkta di. Handlinga er vorten avbroten for å vera føre var mot kidnapping av økta. Bruk attendeknappen i nettlesaren din og prøv om att.",
+       "changecontentmodel": "Endra innhaldsmodellen til ei side",
+       "changecontentmodel-model-label": "Ny innhaldsmodell",
+       "changecontentmodel-reason-label": "Årsak:",
+       "changecontentmodel-success-title": "Innhaldsmodellen vart endra",
+       "log-name-contentmodel": "Logg over endring av innhaldsmodell",
        "protectlogpage": "Vernelogg",
        "protectlogtext": "Nedanfor er ei liste over endringar i vern.\nSjå [[Special:ProtectedPages|lista over verna sider]] for lista over vern som nett no er verksame.",
        "protectedarticle": "verna «[[$1]]»",
        "pageinfo-article-id": "Side-ID",
        "pageinfo-language": "Sideinnhaldsspråk",
        "pageinfo-content-model": "Type sideinnhald",
-       "pageinfo-robot-policy": "Botindeksering",
+       "pageinfo-robot-policy": "Robotindeksering",
        "pageinfo-robot-index": "Tillate",
        "pageinfo-robot-noindex": "Ikkje tillate",
        "pageinfo-watchers": "Tal på overvakarar av sida",
index 83fe89c..368623e 100644 (file)
        "site-atom-feed": "Flux Atom de $1",
        "page-rss-feed": "Flux RSS de \"$1\"",
        "page-atom-feed": "Flux Atom de \"$1\"",
+       "feed-atom": "Atom",
        "red-link-title": "$1 (la pagina existís pas)",
        "sort-descending": "Botar en òrdre creissent",
        "sort-ascending": "Botar en òrdre descreissent",
        "nosuchusershort": "I a pas de contributor amb lo nom « $1 ». Verificatz l’ortografia.",
        "nouserspecified": "Vos cal especificar vòstre nom d'utilizaire.",
        "login-userblocked": "Aqueste utilizaire es blocat. Connexion pas autorizada.",
-       "wrongpassword": "Lo senhal es incorrècte. Ensajatz tornarmai.",
+       "wrongpassword": "Lo nom d'utilizaire o lo senhal es incorrècte.\nEnsajatz tornarmai.",
        "wrongpasswordempty": "Lo senhal picat èra void. Se vos plai, ensajatz tornarmai.",
        "passwordtooshort": "Vòstre senhal deu conténer al mens {{PLURAL:$1|1 caractèr|$1 caractèrs}}.",
        "password-name-match": "Vòstre senhal deu èsser diferent de vòstre nom d’utilizaire.",
        "botpasswords-bad-appid": "Lo nom del robòt «$1» es pas valid.",
        "botpasswords-insert-failed": "Fracàs de l’apondon del nom de robòt « $1 ». Es ja estat apondut ?",
        "botpasswords-created-title": "Senhal de robòts creat",
-       "botpasswords-created-body": "Lo senhal pel robòt « $1 » de l'utilizaire « $2 » es estat creat.",
+       "botpasswords-created-body": "Lo senhal pel robòt « $1 » de l'{{GENDER:$2|utilizaire|utilizaira}} « $2 » es estat creat.",
        "botpasswords-updated-title": "Senhal de robòts mes a jorn",
-       "botpasswords-updated-body": "Lo senhal pel robòt « $1 » de l'utilizaire « $2 » es estat mes a jorn.",
+       "botpasswords-updated-body": "Lo senhal pel robòt « $1 » de l'{{GENDER:$2|utilizaire|utilizaira}} « $2 » es estat mes a jorn.",
        "botpasswords-deleted-title": "Senhal de robòts suprimit",
-       "botpasswords-deleted-body": "Lo senhal pel robòt « $1 » de l'utilizaire « $2 » es estat suprimit.",
+       "botpasswords-deleted-body": "Lo senhal pel robòt « $1 » de l'{{GENDER:$2|utilizaire|utilizaira}} « $2 » es suprimit.",
        "botpasswords-no-provider": "BotPasswordsSessionProvider es pas disponible.",
        "resetpass_forbidden": "Los senhals pòdon pas èsser cambiats",
        "resetpass_forbidden-reason": "Los senhaus pòdon pas èsser cambiats : $1",
        "anoneditwarning": "<strong>Atencion :<strong> sètz pas connectat.\nVòstra adreça IP serà visibla per tot lo monde se fasètz de modificacions. Se <strong>[$1 vos connectatz]</strong> o <strong>[$2 creatz un compte]</strong>, vòstras modificacions seràn atribuidas a vòstre nom d’utilizaire, entre autres avantatges.",
        "anonpreviewwarning": "''Sètz pas identificat. Salvar enregistrarà vòstra adreça IP dins l’istoric de las modificacions de la pagina.''",
        "missingsummary": "'''Atencion :''' avètz pas modificat lo resumit de vòstra modificacion. Se clicatz tornarmai sul boton « Salvar », lo salvament serà fait sens avertiment mai.",
-       "missingcommenttext": "Mercé de metre un comentari çaijós.",
+       "missingcommenttext": "Mercé de metre un comentari.",
        "missingcommentheader": "<strong>Rapèl :</strong> Avètz pas provesit cap de subjècte per aqueste comentari.\nSe clicatz tornamai sus « {{int:Savearticle}} », vòstra modificacion serà enregistrada sens subjècte.",
        "summary-preview": "Apercebut del resumit de modificacion :",
        "subject-preview": "Apercebut del subjècte :",
        "userjspreview": "'''Remembratz-vos que sètz a visualizar o testar vòstre còdi JavaScript e qu’es pas encara estat enregistrat !'''",
        "sitecsspreview": "'''Remembratz-vos que sètz a previsualizar vòstre pròpri fuèlh CSS !'''\n'''Es pas estada encara enregistrada !'''",
        "sitejspreview": "'''Remembratz-vos que sètz a visualizar o testar vòstre còdi JavaScript e qu’es pas encara estat enregistrat !'''",
-       "userinvalidcssjstitle": "'''Atencion :''' existís pas d'estil « $1 ». Remembratz-vos que las paginas personalas amb extensions .css e .js utilizan de títols en minusculas, per exemple, {{ns:user}}:Foo/vector.css e non pas {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "'''Atencion :''' existís pas d'estil « $1 ». Remembratz-vos que las paginas personalas amb extensions .css e .js utilizan de títols en minusculas, per exemple, {{ns:user}}:Foo/vector.css e non pas {{ns:user}}:Foo/Vector.css.",
        "updated": "(Mes a jorn)",
        "note": "'''Nòta :'''",
        "previewnote": "'''Remembratz-vos qu'es pas qu'una previsualizacion.'''\nVòstras modificacions son pas encara estadas enregistradas !",
        "readonlywarning": "<strong>AVERTIMENT : La basa de donadas es estada verrolhada per d'operacions de mantenença. Doncas, poiretz pas publicar vòstras modificacions pel moment.</strong>\nL’administrator sistèma qu'an verrolhada la basa de donadas a donat l’explicacion seguenta : $1",
        "protectedpagewarning": "'''AVERTIMENT : Aquesta pagina es protegida. Sols los utilizaires qu'an l'estatut d'administrator la p�don modificar. ''' La darri�ra entrada del jornal es afichada �aij�s per refer�ncia :",
        "semiprotectedpagewarning": "'''N�ta:''' Aquesta pagina es estada protegida d'un tal biais que sols los contributors enregistrats la p�scan modificar. La darri�ra entrada del jornal es afichada �aij�s per refer�ncia :",
-       "cascadeprotectedwarning": "'''ATENCION :''' Aquesta pagina es estada protegida de biais que sols los administrators la pòscan editar perque es transclusa dins {{PLURAL:$1|la pagina protegida seguenta, qu'a|las paginas protegidas seguentas,  qu'an}} la « proteccion en cascada » activada :",
+       "cascadeprotectedwarning": "<strong>ATENCION :</strong> Aquesta pagina es estada protegida de biais que sols los utilizaires amb [[Special:ListGroupRights|de dreches especifics]] la pòscan modificar perque es inclusa dins {{PLURAL:$1|la pagina protegida seguenta, qu'a|las paginas protegidas seguentas,  qu'an}} la « proteccion en cascada » activada :",
        "titleprotectedwarning": "'''ATENCION : Aquesta pagina es estada protegida de tal biais que de [[Special:ListGroupRights|dreits especifics]] son requesits per la poder crear.''' La darrièra entrada del jornal es afichada çaijós per referéncia :",
        "templatesused": "{{PLURAL:$1|Modèl utilizat|Modèls utilizats}} sus aquesta pagina :",
        "templatesusedpreview": "{{PLURAL:$1|Modèl utilizat|Modèls utilizats}} dins aquesta previsualizacion :",
        "prefs-editwatchlist-clear": "Escafar la lista de seguiment",
        "prefs-watchlist-days": "Nombre de jorns d'afichar dins la lista de seguiment :",
        "prefs-watchlist-days-max": "(maximum $1 jorn{{PLURAL:$1||s}})",
-       "prefs-watchlist-edits": "Nombre de modificacions d'afichar dins la lista de seguiment espandida :",
+       "prefs-watchlist-edits": "Nombre maximal de modificacions d'afichar dins la lista de seguiment :",
        "prefs-watchlist-edits-max": "Nombre maximum : 1000",
        "prefs-watchlist-token": "Geton per la lista de seguiment :",
        "prefs-misc": "Preferéncias divèrsas",
        "recentchangesdays-max": "(maximum $1 {{PLURAL:$1|jorn|jorns}})",
        "recentchangescount": "Nombre de modificacions d'afichar per defaut :",
        "prefs-help-recentchangescount": "Aquò inclutz las modificacions recentas, las paginas d’istorics e los jornals.",
-       "prefs-help-watchlist-token2": "Aquí la clau secreta del flux Web de vòstra lista de seguiment.\nTota persona que la coneis poirà legir vòstra lista de seguiment, doncas, la comuniquetz pas.\n[[Special:ResetTokens|Clicatz aicí se la vos cal reïnicializar]].",
+       "prefs-help-watchlist-token2": "Aquí la clau secreta del flux Web de vòstra lista de seguiment.\nTota persona que la coneis poirà legir vòstra lista de seguiment, doncas, la comuniquetz pas.\nSe necessari, [[Special:ResetTokens|clicatz aicí per la reïnicializar]].",
        "savedprefs": "Las preferéncias son estadas salvadas.",
        "savedrights": "Los dreits d'utilizaire de {{GENDER:$1|$1}} son estats enregistrats.",
        "timezonelegend": "Fus orari :",
        "timezoneregion-europe": "Euròpa",
        "timezoneregion-indian": "Ocean Indian",
        "timezoneregion-pacific": "Ocean Pacific",
-       "allowemail": "Autorizar lo mandadís de corrièr electronic venent d’autres utilizaires",
+       "allowemail": "Autorizar los autres utilizaires a me mandar de corrièls",
        "prefs-searchoptions": "Recèrca",
        "prefs-namespaces": "Noms d’espacis",
        "default": "defaut",
        "prefs-files": "Fichièrs",
        "prefs-custom-css": "CSS personalizat",
        "prefs-custom-js": "JS personalizat",
-       "prefs-common-css-js": "JavaScript e CSS partejat per totes los abilhatges :",
+       "prefs-common-config": "JavaScript e CSS partejat per totes los abilhatges :",
        "prefs-reset-intro": "Podètz utilizar aquesta pagina per restablir vòstras preferéncias a las valors per defaut del site. Aquò pòt pas èsser desfait.",
        "prefs-emailconfirm-label": "Confirmacion del corrièr electronic :",
        "youremail": "Adreça de corrièr electronic :",
        "recentchanges-label-plusminus": "La talha de la pagina a cambiat d'aqueste nombre d’octets.",
        "recentchanges-legend-heading": "<strong>Legenda :</strong>",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (veire tanben la [[Special:NewPages|lista de las paginas novèlas]]).",
+       "recentchanges-legend-plusminus": "(''±123'')",
        "recentchanges-submit": "Afichar",
        "rcfilters-activefilters": "Filtres actius",
        "rcfilters-hours-title": "Darrièras oras",
        "sp-contributions-uploads": "impòrts",
        "sp-contributions-logs": "jornals",
        "sp-contributions-talk": "Discutir",
-       "sp-contributions-userrights": "gerir los dreits",
+       "sp-contributions-userrights": "gerir los dreits d’utilizair{{GENDER:$1|e|a}}",
        "sp-contributions-blocked-notice": "Aqueste utilizaire es actualament blocat. La darrièra entrada del jornal dels blocatges es indicada çaijós a títol d’informacion :",
        "sp-contributions-blocked-notice-anon": "Aquesta adreça IP es actualament blocada.\nLa darrièra intrada del jornal dels blocatges es indicada çaijós a títol d’informacion :",
        "sp-contributions-search": "Cercar las contribucions",
        "autoredircomment": "Redireccion cap a [[$1]]",
        "autosumm-new": "Creacion de la pagina amb « $1 »",
        "autosumm-newblank": "Pagina voida creada",
-       "size-bytes": "$1 o",
+       "size-bytes": "$1 {{PLURAL:$1|octet|octets}}",
        "size-kilobytes": "$1 Ko",
        "size-megabytes": "$1 Mo",
        "size-gigabytes": "$1 Go",
index 3fbf2c4..3d50f90 100644 (file)
        "userjspreview": "'''ଜାଣି ରଖନ୍ତୁ ଯେ ଆପଣ କେବଳ ନିଜର ସଭ୍ୟ ଜାଭାସ୍କ୍ରିପ୍ଟ (JavaScript) ଦେଖୁଅଛନ୍ତି ।'''\n'''ଏହା ଏଯାଏଁ ସାଇତା ଯାଇନାହିଁ!'''",
        "sitecsspreview": "'''ଜାଣି ରଖନ୍ତୁ ଯେ ଆପଣ କେବଳ ଏହି CSS ଦେଖୁଅଛନ୍ତି ।'''\n'''ଏହା ଏଯାଏଁ ସାଇତାଯାଇନାହିଁ!'''",
        "sitejspreview": "'''ଜାଣି ରଖନ୍ତୁ ଯେ ଆପଣ କେବଳ ଏହି ଜାଭାସ୍କ୍ରିପ୍ଟ (JavaScript) ଦେଖୁଅଛନ୍ତି ।'''\n'''ଏହା ଏଯାଏଁ ସାଇତା ଯାଇନାହିଁ!'''",
-       "userinvalidcssjstitle": "'''ଚେତାବନୀ:''' \"$1\" ନାମରେ କୌଣସି ବି ଆବରଣ ନାହିଁ ।\nମନମୁତାବକ .css ଓ .js ପୃଷ୍ଠା ଏକ ଛୋଟ ଇଂରାଜୀ ଅକ୍ଷର ଥିବା ନାମ ନେଇଥାନ୍ତି, ଯଥା: {{ns:user}}:Foo/Vector.css ବଦଳରେ {{ns:user}}:Foo/vector.css ର ବ୍ୟବହାର ।",
+       "userinvalidconfigtitle": "'''ଚେତାବନୀ:''' \"$1\" ନାମରେ କୌଣସି ବି ଆବରଣ ନାହିଁ ।\nମନମୁତାବକ .css ଓ .js ପୃଷ୍ଠା ଏକ ଛୋଟ ଇଂରାଜୀ ଅକ୍ଷର ଥିବା ନାମ ନେଇଥାନ୍ତି, ଯଥା: {{ns:user}}:Foo/Vector.css ବଦଳରେ {{ns:user}}:Foo/vector.css ର ବ୍ୟବହାର ।",
        "updated": "(ସତେଜ କରିଦିଆଗଲା)",
        "note": "'''ଟୀକା:'''",
        "previewnote": "'''ଜାଣିରଖନ୍ତୁ ଯେ, ଏହା କେବଳ ଏକ ଦେଖଣା ।'''\nଆପଣ କରିଥିବା ବଦଳସବୁ ଏଯାଏଁ ସାଇତା ଯାଇନାହିଁ!",
        "prefs-files": "ଫାଇଲ",
        "prefs-custom-css": "ମନମୁତାବକ CSS",
        "prefs-custom-js": "ମନମୁତାବକ JavaScript",
-       "prefs-common-css-js": "ସବୁ ଆବରଣ ପାଇଁ ବଣ୍ଟା ହୋଇଥିବା CSS/JavaScript:",
+       "prefs-common-config": "ସବୁ ଆବରଣ ପାଇଁ ବଣ୍ଟା ହୋଇଥିବା CSS/JavaScript:",
        "prefs-reset-intro": "ଆପଣ ଏହି ପୃଷ୍ଠାଟି ବ୍ୟବହାର କରି ଆପଣା ପସନ୍ଦସବୁକୁ ସାଇଟର ଆରମ୍ଭରେ ଥିବା ସଜାଣିକୁ ଲେଉଟାଇଦେଇପାରିବେ ।\nଏହାକୁ ପଛକୁ ଫେରାଯାଇପାରିବ ନାହିଁ",
        "prefs-emailconfirm-label": "ଇ-ମେଲ ସଜାଣି:",
        "youremail": "ଇ-ମେଲ:",
index f30e77c..554b2b4 100644 (file)
        "userjspreview": "'''Дæ зæрдыл дар уый, æмæ дæ JavaScript ды ныртæккæ фæлваргæ кæныс.'''\n'''Нырмæ æвæрд нæу!'''",
        "sitecsspreview": "'''Дæ зæрдыл дар уый, æмæ ацы CSS ды ныртæккæ фæлваргæ кæныс.'''\n'''Нырмæ æвæрд нæу!'''",
        "sitejspreview": "'''Дæ зæрдыл дар уый, æмæ ацы JavaScript ды ныртæккæ фæлваргæ кæныс.'''\n'''Нырмæ æвæрд нæу!'''",
-       "userinvalidcssjstitle": "'''Сындæг:''' \"$1\" царм нæй.\nХиæвæрд .css æмæ .js фæрстæ архайынц гыццыл дамгъæтимæ нæмттæй. Цæвиддон, {{ns:user}}:Foo/vector.cs, {{ns:user}}:Foo/Vector.css нæ фæлæ.",
+       "userinvalidconfigtitle": "'''Сындæг:''' \"$1\" царм нæй.\nХиæвæрд .css æмæ .js фæрстæ архайынц гыццыл дамгъæтимæ нæмттæй. Цæвиддон, {{ns:user}}:Foo/vector.cs, {{ns:user}}:Foo/Vector.css нæ фæлæ.",
        "updated": "(Ноггонд)",
        "note": "'''Фиппаинаг:'''",
        "previewnote": "'''Зон æй, æмæ ай у æрмæстдæр разбакаст.'''\nДæ ивдтытæ нырмæ æвæрд не рцыдысты!",
index 2460fdc..4ee5614 100644 (file)
        "prefs-files": "ਫ਼ਾਈਲਾਂ",
        "prefs-custom-css": "ਰਿਵਾਇਤੀ CSS",
        "prefs-custom-js": "ਰਿਵਾਇਤੀ ਜਾਵਾਸਕਰਿਪਟ",
-       "prefs-common-css-js": "ਸਾਰੀਆਂ ਸਕਿਨਾਂ ਲਈ ਸਾਂਝਾ CSS/ਜਾਵਾਸਕਰਿਪਟ:",
+       "prefs-common-config": "ਸਾਰੀਆਂ ਸਕਿਨਾਂ ਲਈ ਸਾਂਝਾ CSS/ਜਾਵਾਸਕਰਿਪਟ:",
        "prefs-emailconfirm-label": "ਈ-ਮੇਲ ਪੁਸ਼ਟੀ:",
        "youremail": "ਈ-ਮੇਲ:",
        "username": "{{GENDER:$1|ਵਰਤੋਂਕਾਰ ਦਾ ਨਾਂ}}:",
index 21f57d3..e8d4495 100644 (file)
        "userjsyoucanpreview": "'''Tip:''' Gamitan me ing button a 'Pakit ya ing preview' ('Show preview') ba yang subukan ing kekang bayung JS bayu ka mag-save.",
        "usercsspreview": "'''Tandanan mung pi-preview me mu ing kekang user CSS, e ya pa me-save!'''",
        "userjspreview": "'''Tandanan mung susubukan/pi-preview me pamu ing kekang user JavaScript, e ya pa me-save iti!'''",
-       "userinvalidcssjstitle": "'''Kapiadian:''' Alang pabalat (skin) a \"$1\".\nTandanan mung deng pasadiang bulung (custom pages) a .css ampong .js, gagamit lang bansag a mababang letra (lowercase), alm. (alimbawa), {{ns:user}}:Foo/vector.css, at e {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "'''Kapiadian:''' Alang pabalat (skin) a \"$1\".\nTandanan mung deng pasadiang bulung (custom pages) a .css ampong .js, gagamit lang bansag a mababang letra (lowercase), alm. (alimbawa), {{ns:user}}:Foo/vector.css, at e {{ns:user}}:Foo/Vector.css.",
        "updated": "(Mibayu)",
        "note": "'''Kapabaluan:'''",
        "previewnote": "'''Tandanan mu pasinag ya mu ini.\nDeng elilan mu ela pa misikap!'''",
index b6bff28..9170b8b 100644 (file)
        "userjspreview": "'''Pamiętaj, że to tylko podgląd Twojego kodu JavaScript – nic jeszcze nie zostało zapisane!'''",
        "sitecsspreview": "'''Pamiętaj, że to tylko podgląd arkusza stylów CSS.'''\n'''Zmiany nie zostały jeszcze zapisane!'''",
        "sitejspreview": "'''Pamiętaj, że to tylko podgląd kodu JavaScript.'''\n'''Zmiany nie zostały jeszcze zapisane!'''",
-       "userinvalidcssjstitle": "'''Uwaga:''' Brak skórki o nazwie „$1”.\nStrony użytkownika zawierające CSS i JavaScript powinny zaczynać się małą literą, np. {{ns:user}}:Foo/vector.css, w przeciwieństwie do nieprawidłowego {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "'''Uwaga:''' Brak skórki o nazwie „$1”.\nStrony użytkownika zawierające CSS i JavaScript powinny zaczynać się małą literą, np. {{ns:user}}:Foo/vector.css, w przeciwieństwie do nieprawidłowego {{ns:user}}:Foo/Vector.css.",
        "updated": "(Zmodyfikowano)",
        "note": "'''Uwaga:'''",
        "previewnote": "<strong>To jest tylko podgląd.</strong>\nZmiany nie zostały jeszcze zapisane!",
        "prefs-files": "Pliki",
        "prefs-custom-css": "własny CSS",
        "prefs-custom-js": "własny JavaScript",
-       "prefs-common-css-js": "Wspólny CSS/JS dla wszystkich skórek:",
+       "prefs-common-config": "Wspólny CSS/JS dla wszystkich skórek:",
        "prefs-reset-intro": "Na tej stronie można przywrócić domyślne ustawienia preferencji dla tej witryny.\nTej operacji nie można później cofnąć.",
        "prefs-emailconfirm-label": "Potwierdzenie adresu e‐mail:",
        "youremail": "Twój adres e‐mail:",
        "uploadstash-bad-path-invalid": "Ścieżka jest nieprawidłowa.",
        "uploadstash-bad-path-unknown-type": "Nieznany typ „$1”.",
        "uploadstash-bad-path-unrecognized-thumb-name": "Nierozpoznana nazwa miniaturki.",
-       "uploadstash-bad-path-no-handler": "Nie znaleziono obsługi dla typu mime 1 $ pliku $2.",
+       "uploadstash-bad-path-no-handler": "Nie znaleziono obsługi dla typu mime $1 pliku $2.",
        "uploadstash-bad-path-bad-format": "Klucz \"$1\" nie jest w odpowiednim formacie.",
        "uploadstash-file-not-found": "Klucz \"$1\" nie został znaleziony w schowku.",
        "uploadstash-file-not-found-no-thumb": "Nie można uzyskać miniaturki.",
index 9644648..3543a66 100644 (file)
        "userjspreview": "'''Che as visa che a l'é mach antramentre che as fa na preuva ëd sò còdes Javascript e che a l'é ancó pa stàit salvà!'''",
        "sitecsspreview": "'''Che a varda che a l'é mach an camin ch'a preuva cost CSS.'''\n'''A l'é pa ancora stàit salvà!'''",
        "sitejspreview": "'''Che a varda che a l'é mach an camin ch'a preuva cost còdes JavaScript.'''\n'''A l'é pa ancora stàit salvà!'''",
-       "userinvalidcssjstitle": "'''Atension:''' A-i é gnun-a pel «$1». Che as visa che le pàgine .css e .js che un as fa daspërchiel a deuvro tute minùscole për tìtol, pr'esempi {{ns:user}}:Scaramacaj/vector.css nopà che {{ns:user}}:Scaramacaj/Vector.css.",
+       "userinvalidconfigtitle": "'''Atension:''' A-i é gnun-a pel «$1». Che as visa che le pàgine .css e .js che un as fa daspërchiel a deuvro tute minùscole për tìtol, pr'esempi {{ns:user}}:Scaramacaj/vector.css nopà che {{ns:user}}:Scaramacaj/Vector.css.",
        "updated": "(Agiornà)",
        "note": "'''Nòta:'''",
        "previewnote": "'''Che a ten-a da ment che costa-sì a l'é mach na preuva.'''\nSoe modìfiche a son pa ancora stàite salvà!",
        "prefs-files": "Archivi",
        "prefs-custom-css": "CSS përsonaj",
        "prefs-custom-js": "JS përsonaj",
-       "prefs-common-css-js": "CSS e JS condividù për tute le pej:",
+       "prefs-common-config": "CSS e JS condividù për tute le pej:",
        "prefs-reset-intro": "A peul dovré costa pàgina për amposté torna ij sò gust a coj dë stàndard.\nSòn a peul pa esse anulà.",
        "prefs-emailconfirm-label": "Conferma dl'adrëssa ëd pòsta eletrònica:",
        "youremail": "Soa adrëssa ëd pòsta eletrònica:",
index ffcd741..26f15f2 100644 (file)
        "oct": "اکتوبر",
        "nov": "نومبر",
        "dec": "دسمبر",
-       "january-date": "جنوری $1",
+       "january-date": "جنوری",
        "february-date": "فروری $1",
        "march-date": "مارچ $1",
        "april-date": "اپریل $1",
        "userjspreview": "'''یاد رکھو بے  تسی صرف اپنی ورتن 'JavaScript چیک کررۓ او''\n'''اینوں ہجے بچایا نئیں گیا!'''",
        "sitecsspreview": "'''یادرکھو جے تسی اپنی ورتن  CSS دا کچا کم ویکھ رۓ او.'''\n'''Iاے ہلے بچائی نئیں گئ!'''",
        "sitejspreview": "'''یاد رکھو بے  تسی صرف ایہ 'JavaScript کوڈ چیک کررۓ او''\n'''اینوں ہجے بچایا نئیں گیا!'''",
-       "userinvalidcssjstitle": "'''خبردار:''' \"$1\" سکن نئیں اے۔\nCustom .css تے .js pages use a lowercase title, e.g. {{ns:user}}:Foo/vector.css as opposed to {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "'''خبردار:''' \"$1\" سکن نئیں اے۔\nCustom .css تے .js pages use a lowercase title, e.g. {{ns:user}}:Foo/vector.css as opposed to {{ns:user}}:Foo/Vector.css.",
        "updated": "(نواں کیتا گیا)",
        "note": "'''نوٹ:'''",
        "previewnote": "'''اے ہلے کچا کم اے؛ تبدیلیاں بچائیاں نہیں گئیاں'''",
        "history-title": "\"$1\" دا ریکارڈ",
        "difference-title": "\"$1\" دیاں دہرائیاں وچ وکھراپا",
        "difference-multipage": "(صفیاں چ فرق)",
-       "lineno": "لیک $1:",
+       "lineno": "لیک 1:",
        "compareselectedversions": "چنے صفحے آپنے سامنے کرو",
        "showhideselectedversions": "وکھاؤ/لکاؤ چنیاں دہرائیاں",
        "editundo": "واپس",
        "prefs-files": "فائلاں",
        "prefs-custom-css": "کسٹم سی ایس ایس",
        "prefs-custom-js": "کسٹم جاواسکرپٹ",
-       "prefs-common-css-js": "سی ایس ایس/جاواسکرپٹ شئیر کرو ہر وکھالے لئی:",
+       "prefs-common-config": "سی ایس ایس/جاواسکرپٹ شئیر کرو ہر وکھالے لئی:",
        "prefs-reset-intro": "تسیں ایس صفے نوں کسے سائٹ دی ڈیفالٹ دی چنوتیاں مرضی دیاں کرن ورت سکدے او۔\n\nاے واپس نئیں ہوسکدا۔",
        "prefs-emailconfirm-label": "ای-میل کنفرمیشن:",
        "youremail": "ای میل:",
index f1e170f..42256e0 100644 (file)
        "clearyourcache": "'''Nōda: Kitawīdinsnas pa enpeisāsnan stēisan nāunan ensadīnsenin mazzi ni būtwei widāminan. Prawerru skistīntun lasāltas rānkas minīsnan.'''\n*'''Mozilla, Firefox anga Safari - ''' zabāis \"Shift\" gnestan ne gnetteis \"Etnauninnais\" anga gnetteis \"Ctrl-F5\" anga \"Ctrl-R\" (\"Command-R\" en Macu);\n*'''Konqueror:''' gnetteis '''Etwārtai kraūneis\" anga \"F5\";\n'''Opera:''' skistinnais rānkas minīsnan en \"Ēnrankis-Pirminiskwas\";\n'''Internet Explorer:''' zabāis \"Ctrl\" gnetīntei \"Etnauninnais\" anga gnetteis \"Ctrl-F5\".",
        "usercsspreview": "'''Pamēnais, kāi sta ast tēr twāise CSS pirmādira - nika dabber ni pastāi enpeisātan!'''",
        "userjspreview": "'''Pamēnais, kāi sta ast tēr twāise JS kōdas pirmādira - nika dabber ni pastāi enpeisātan!'''",
-       "userinvalidcssjstitle": "'''Ēmpirsergīsenis:''' Ni ast prusna \"$1\".\nPamēnais kāi tērpautajas .css be .js pāusai turri pagaūtun si sen līkutan litteran, p. e.g. {{ns:user}}:Foo/vector.css, ni {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "'''Ēmpirsergīsenis:''' Ni ast prusna \"$1\".\nPamēnais kāi tērpautajas .css be .js pāusai turri pagaūtun si sen līkutan litteran, p. e.g. {{ns:user}}:Foo/vector.css, ni {{ns:user}}:Foo/Vector.css.",
        "updated": "(Ernaunīntan)",
        "note": "'''Endirīsenis:'''",
        "previewnote": "'''Sta ast tēr pirmādira.'''\nKitawīdinsnas dabber ni pastāi enpeisātan!\"",
index a6c4a99..e31095a 100644 (file)
        "userjspreview": "'''هېر مو نشي چې دا يوازې ستاسې د کارن د جاوا سکرېپټ آزمېيل/مخليدنه ده.'''\n'''تر اوسه پورې لا ستاسې بدلونونه نه دي خوندي شوي!'''",
        "sitecsspreview": "'''په پام کې دې وي چې دا يوازې ستاسې د CSS مخليدنه ده.'''\n'''تر اوسه پورې لا ستاسې بدلونونه نه دي خوندي شوي!'''",
        "sitejspreview": "'''په پام کې مو اوسه چې تاسې يوازې د دغه جاواسکرېپټ کوډ مخليدنه کوۍ.'''\n'''تر اوسه پورې دا نه دی خوندي شوی!'''",
-       "userinvalidcssjstitle": "<strong>خبرداری:</strong>دلته  هیڅ پوست نشته \"$1\".\nد ګمرکونو .ثي اس اس او .ج س مخونه  کوچني سرلیک استعمالوي، او داسې نور. {{ns:user}}:Foo/vector.css  که مخالف وي نو {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "<strong>خبرداری:</strong>دلته  هیڅ پوست نشته \"$1\".\nد ګمرکونو .ثي اس اس او .ج س مخونه  کوچني سرلیک استعمالوي، او داسې نور. {{ns:user}}:Foo/vector.css  که مخالف وي نو {{ns:user}}:Foo/Vector.css.",
        "updated": "(تازه)",
        "note": "'''يادونه:'''",
        "previewnote": "'''هېر مو نه شي چې دا يواځې يوه مخليدنه ده.'''\nستاسې لخوا ترسره شوي بدلونونه لا تر اوسه پورې نه دي خوندي شوي!!",
        "prefs-files": "دوتنې",
        "prefs-custom-css": "دوديزه سي اس اس",
        "prefs-custom-js": "ځاني جاواسکرېپټ",
-       "prefs-common-css-js": "د ټولو پوښونو لپاره د CSS/جاواسکرېپټ دوتنه:",
+       "prefs-common-config": "د ټولو پوښونو لپاره د CSS/جاواسکرېپټ دوتنه:",
        "prefs-emailconfirm-label": "د برېښليک باورتيا:",
        "youremail": "برېښليک *",
        "username": "{{GENDER:$1|کارن نوم}}:",
        "siteuser": "د {{SITENAME}} کارن $1",
        "anonuser": "د {{SITENAME}} ورکنومی کارن $1",
        "lastmodifiedatby": "دا مخ وروستی ځل $3 لخوا په $2، $1 بدلون موندلی.",
-       "othercontribs": "Ù\86Ù\88ر Ú©Ø§Ø± Ù¾Ø± Ø¨Ù\86سټ",
+       "othercontribs": "Ù\86Ù\88ر Ú©Ø§Ø± Ù¾Ø± Ø§Ø³Ø§Ø³ Ø¯ $1.",
        "others": "نور",
        "siteusers": "د {{SITENAME}} {{PLURAL:$2|کارن|کارنان}} $1",
        "anonusers": "د {{SITENAME}} {{PLURAL:$2|ورکنومی کارن|ورکنومي کارنان}} $1",
index 8069315..683ecf5 100644 (file)
        "userjspreview": "'''Lembre-se que está apenas testando/prevendo o seu JavaScript particular e que ele ainda não foi salvo!'''",
        "sitecsspreview": "'''Lembre-se de que você está apenas previsualizando este CSS.'''\n'''Ele ainda não foi salvo!'''",
        "sitejspreview": "'''Lembre-se de que você está apenas previsualizando este código JavaScript.'''\n'''Ele ainda não foi salvo!'''",
-       "userinvalidcssjstitle": "'''Aviso:''' Não existe um tema \"$1\". Lembre-se que as páginas .css e  .js utilizam um título em minúsculas, exemplo: {{ns:user}}:Alguém/vector.css aposto a {{ns:user}}:Alguém/Vector.css.",
+       "userinvalidconfigtitle": "'''Aviso:''' Não existe um tema \"$1\". Lembre-se que as páginas .css e  .js utilizam um título em minúsculas, exemplo: {{ns:user}}:Alguém/vector.css aposto a {{ns:user}}:Alguém/Vector.css.",
        "updated": "(Atualizado)",
        "note": "'''Nota:'''",
        "previewnote": "'''Lembre-se de que isto é apenas uma previsão.'''\nSuas alterações ainda não foram salvas!",
        "prefs-files": "Arquivos",
        "prefs-custom-css": "CSS personalizada",
        "prefs-custom-js": "JS personalizado",
-       "prefs-common-css-js": "CSS/JS compartilhado por todos os temas:",
+       "prefs-common-config": "CSS/JS compartilhado por todos os temas:",
        "prefs-reset-intro": "Você pode usar esta página para restaurar as suas preferências para os valores predefinidos do sítio.\nEsta ação não pode ser desfeita.",
        "prefs-emailconfirm-label": "Confirmação do e-mail:",
        "youremail": "Seu e-mail:",
        "rcfilters-liveupdates-button": "Atualizações instantâneas",
        "rcfilters-liveupdates-button-title-on": "Desativar as atualizações ao vivo",
        "rcfilters-liveupdates-button-title-off": "Exibir novas mudanças à medida que elas acontecem",
-       "rcfilters-watchlist-markseen-button": "Marque todas as mudanças como visto",
-       "rcfilters-watchlist-edit-watchlist-button": "Edite sua lista de páginas vigiadas",
+       "rcfilters-watchlist-markseen-button": "Marcar todas as mudanças como vistas",
+       "rcfilters-watchlist-edit-watchlist-button": "Editar lista de páginas vigiadas",
        "rcfilters-watchlist-showupdated": "As alterações nas páginas que você não visitou desde as mudanças ocorridas estão em <strong>negrito</strong>, com marcadores sólidos.",
        "rcfilters-preference-label": "Ocultar a versão melhorada das Mudanças Recentes",
        "rcfilters-preference-help": "Reverte o redesenho da interface de 2017 e todas as ferramentas adicionadas na altura e desde então.",
        "historywarning": "<strong>Aviso:</strong> A página que está prestes a eliminar tem um histórico com aproximadamente $1 {{PLURAL:$1|revisão|revisões}}:",
        "historyaction-submit": "Exibir",
        "confirmdeletetext": "Encontra-se prestes a eliminar uma página juntamente com todo o seu histórico.\nPor favor, confirme que possui a intenção de fazer isto, que compreende as consequências e que encontra-se a fazer isto de acordo com as [[{{MediaWiki:Policy-url}}|políticas]] do projeto.",
-       "actioncomplete": "Ação concluída",
+       "actioncomplete": "Ação efetuada com sucesso",
        "actionfailed": "Falha na ação",
        "deletedtext": "\"$1\" foi eliminada.\nConsulte $2 para um registro de eliminações recentes.",
        "dellogpage": "Registro de eliminações",
        "thumbnail_dest_directory": "Não foi possível criar o diretório de destino",
        "thumbnail_image-type": "Tipo de imagem não suportado",
        "thumbnail_gd-library": "Configuração da biblioteca GD incompleta: função $1 não encontrada",
+       "thumbnail_image-size-zero": "O tamanho do arquivo de imagem parece ser zero.",
        "thumbnail_image-missing": "Arquivo aparentemente inexistente: $1",
        "thumbnail_image-failure-limit": "Houveram muitas tentativas falhas recentemente ($1 ou mais) de criação desta miniatura. Por favor, tente novamente mais tarde.",
        "import": "Importar páginas",
index 1f15823..6340c76 100644 (file)
        "userjspreview": "<strong>Lembre-se de que está apenas a testar ou a antever o seu JavaScript particular.\nEste ainda não foi gravado!</strong>",
        "sitecsspreview": "<strong>Lembre-se de que está apenas a antever este CSS.\nEle ainda não foi gravado!</strong>",
        "sitejspreview": "<strong>Lembre-se de que está apenas a antever este código JavaScript.\nEle ainda não foi gravado!</strong>",
-       "userinvalidcssjstitle": "<strong>Aviso:</strong> Não existe um tema \"$1\".\nAs páginas personalizadas .css e .js têm um título em minúsculas, por exemplo: {{ns:user}}:Alguém/vector.css em vez de {{ns:user}}:Alguém/Vector.css.",
+       "userinvalidconfigtitle": "<strong>Aviso:</strong> Não existe um tema \"$1\".\nAs páginas personalizadas .css e .js têm um título em minúsculas, por exemplo: {{ns:user}}:Alguém/vector.css em vez de {{ns:user}}:Alguém/Vector.css.",
        "updated": "(Atualizado)",
        "note": "<strong>Nota:</strong>",
        "previewnote": "<strong>Lembre-se de que esta é apenas uma antevisão do resultado.</strong>\nAs modificações ainda não foram gravadas!",
        "templatesusedpreview": "{{PLURAL:$1|Predefinição utilizada|Predefinições utilizadas}} nesta antevisão:",
        "templatesusedsection": "{{PLURAL:$1|Predefinição utilizada|Predefinições utilizadas}} nesta secção:",
        "template-protected": "(protegida)",
-       "template-semiprotected": "(semi-protegida)",
+       "template-semiprotected": "(semiprotegida)",
        "hiddencategories": "Esta página pertence a {{PLURAL:$1|uma categoria oculta|$1 categorias ocultas}}:",
        "edittools": "<!-- O texto colocado aqui será mostrado abaixo dos formulários de edição e de envio de ficheiros. -->",
        "edittools-upload": "-",
        "prefs-files": "Ficheiros",
        "prefs-custom-css": "CSS personalizado",
        "prefs-custom-js": "JS personalizado",
-       "prefs-common-css-js": "CSS/JS partilhado por todos os temas:",
+       "prefs-common-config": "CSS/JS partilhado por todos os temas:",
        "prefs-reset-intro": "Pode usar esta página para repor as configurações padrão das preferências.\nAs suas preferências serão modificadas para os valores predefinidos do sítio.\nEsta operação não pode ser desfeita.",
        "prefs-emailconfirm-label": "Confirmação do correio eletrónico:",
        "youremail": "Correio eletrónico:",
        "restriction-create": "Criar",
        "restriction-upload": "Carregar",
        "restriction-level-sysop": "totalmente protegida",
-       "restriction-level-autoconfirmed": "semi-protegida",
+       "restriction-level-autoconfirmed": "semiprotegida",
        "restriction-level-all": "qualquer nível",
        "undelete": "Ver páginas eliminadas",
        "undeletepage": "Ver e restaurar páginas eliminadas",
        "thumbnail_dest_directory": "Não foi possível criar o diretório de destino",
        "thumbnail_image-type": "Tipo de imagem não suportado",
        "thumbnail_gd-library": "Configuração da biblioteca GD incompleta: função $1 em falta",
+       "thumbnail_image-size-zero": "O tamanho do ficheiro de imagem parece ser zero.",
        "thumbnail_image-missing": "Ficheiro em falta: $1",
        "thumbnail_image-failure-limit": "Ocorreram demasiadas tentativas recentes ($1 ou mais) de criação desta miniatura. Tente novamente mais tarde, por favor.",
        "import": "Importar páginas",
index 6a071ed..ec41d37 100644 (file)
                        "Wladek92",
                        "Gombang",
                        "Trizek (WMF)",
-                       "Acamicamacaraca"
+                       "Acamicamacaraca",
+                       "Avatar6"
                ]
        },
        "sidebar": "{{notranslate}}",
        "userjspreview": "Text displayed on preview of every user .js subpage",
        "sitecsspreview": "Text displayed on preview of .css pages in MediaWiki namespace.\n\nSee also:\n* {{msg-mw|Usercsspreview}}",
        "sitejspreview": "Text displayed on preview of .js pages in MediaWiki namespace",
-       "userinvalidcssjstitle": "Parameters:\n* $1 - skin name",
+       "userinvalidconfigtitle": "Parameters:\n* $1 - skin name",
        "updated": "{{Identical|Updated}}",
        "note": "{{Identical|Note}}",
        "previewnote": "Note displayed when clicking on Show preview",
        "prefs-files": "Title of a tab in [[Special:Preferences]].\n{{Identical|File}}",
        "prefs-custom-css": "visible on [[Special:Preferences]] -[Skins].\n{{Identical|Custom CSS}}",
        "prefs-custom-js": "visible on [[Special:Preferences]] -[Skins].\n{{Identical|Custom JavaScript}}",
-       "prefs-common-css-js": "Used as label in [[Special:Preferences#mw-prefsection-rendering|preferences]], tab \"Appearance\", section \"Skin\".\n\nSee also:\n* {{msg-mw|Globalcssjs-custom-css-js}}",
+       "prefs-common-config": "Used as label in [[Special:Preferences#mw-prefsection-rendering|preferences]], tab \"Appearance\", section \"Skin\".\n\nSee also:\n* {{msg-mw|Globalcssjs-custom-css-js}}",
        "prefs-reset-intro": "Used in [[Special:Preferences/reset]].",
        "prefs-emailconfirm-label": "Sub-heading in [[Special:Preferences]] > {{int:prefs-personal}} > {{int:email}}.",
        "youremail": "Label of the e-mail text box of the \"E-mail options\" section of [[Special:Preferences]].\nAlso used on create account form.\n\n{{Identical|E-mail}}",
        "thumbnail_dest_directory": "Used as thumbnail error message.\n\nSee also:\n* {{msg-mw|Thumbnail error}}\n* {{msg-mw|Thumbnail-temp-create}}\n* {{msg-mw|Thumbnail-dest-create}}\n* {{msg-mw|Thumbnail invalid params}}",
        "thumbnail_image-type": "This is the parameter 1 of the message {{msg-mw|thumbnail error}}",
        "thumbnail_gd-library": "This is the parameter 1 of the message {{msg-mw|thumbnail error}}.\n*$1 is a function name of the GD library",
+       "thumbnail_image-size-zero": "This is the parameter 1 of the message {{msg-mw|thumbnail error}}.\n*$1 is the path incl. filename of the image with zero size",
        "thumbnail_image-missing": "This is the parameter 1 of the message {{msg-mw|thumbnail error}}.\n*$1 is the path incl. filename of the missing image",
        "thumbnail_image-failure-limit": "Used as <code>$1</code> in {{msg-mw|Thumbnail error}}.\n\nParameters:\n* $1 - the maximum allowed number of failed attempts",
        "import": "The title of the special page [[Special:Import]];",
        "signature-anon": "{{notranslate}}\nUsed as signature for anonymous user. Parameters:\n* $1 - username (IP address?)\n* $2 - nickname (IP address?)\nSee also:\n* {{msg-mw|Signature}} - signature for registered user",
        "timezone-utc": "{{optional}}",
        "timezone-local": "Label to indicate that a time is in the user's local timezone.\n{{Identical|Local}}",
-       "duplicate-defaultsort": "See definition of [[w:Sorting|sort key]] on Wikipedia. Parameters:\n* $1 - old default sort key\n* $2 - new default sort key",
+       "duplicate-defaultsort": "<strong>Warning:</strong> Default sort key \"$2\" overrides earlier default sort key \"$1\".",
        "duplicate-displaytitle": "Warning shown when a page has its display title set multiple times. Parameters:\n* $1 - old display title\n* $2 - new display title",
        "restricted-displaytitle": "Warning shown when a display title is ignored because it is not equivalent to its actual title. Parameters:\n* $1 - the ignored display title",
        "invalid-indicator-name": "Warning shown when the [https://www.mediawiki.org/wiki/Help:Page_status_indicators &lt;indicator name=\"''unique-identifier''\">''content''&lt;/indicator>] parser tag is used incorrectly.",
index edf5b8f..8b3db5d 100644 (file)
        "userjspreview": "'''Yuyariy, qhawarillachkankim ruraqpa JavaScript-niykita, manaraqmi waqaychasqachu!'''",
        "sitecsspreview": "'''Yuyariy, qhawarillachkankim kay CSS-ta.'''\n'''Manaraqmi waqaychasqachu!'''",
        "sitejspreview": "'''Yuyariy, qhawarillachkankim kay JavaScript qillqata.'''\n'''Manaraqmi waqaychasqachu!'''",
-       "userinvalidcssjstitle": "'''Paqtataq:''' Manam kanchu \"$1\" qara. Yuyariy, kikinpa .css, .js p'anqankunaqa uchuy sanampa umalliyuqmi, ahinataq {{ns:user}}:Foo/vector.css manataq  {{ns:user}}:Foo/Vector.css nisqachu.",
+       "userinvalidconfigtitle": "'''Paqtataq:''' Manam kanchu \"$1\" qara. Yuyariy, kikinpa .css, .js p'anqankunaqa uchuy sanampa umalliyuqmi, ahinataq {{ns:user}}:Foo/vector.css manataq  {{ns:user}}:Foo/Vector.css nisqachu.",
        "updated": "(Musuqchasqa)",
        "note": "'''Musyay:'''",
        "previewnote": "'''Yuyaykuy: Kayqa qhawariyllam.'''\nLlamk'apusqaykiqa manaraqmi waqaychasqachu!",
        "prefs-files": "Willañiqikuna",
        "prefs-custom-css": "Munakusqa CSS",
        "prefs-custom-js": "Munakusqa JS",
-       "prefs-common-css-js": "Tukuy qarakunapaq rakinakusqa CSS/JS:",
+       "prefs-common-config": "Tukuy qarakunapaq rakinakusqa CSS/JS:",
        "prefs-reset-intro": "Kay p'anqataqa llamk'achiyta atinki allinkachinaykikunata kikinmanta kasqaman kutichinaykipaq.\nChaytataq manam kutichiyta atinkichu.",
        "prefs-emailconfirm-label": "E-chaskita takyachiy:",
        "youremail": "E-chaski imamaytayki",
index fa48180..b4a9057 100644 (file)
        "userjspreview": "'''Fa stim che quai è be ina prevista da tes JavaScript d'utilisader.'''\n'''El n'è anc betg memorisà.'''",
        "sitecsspreview": "'''Fa stim che quai è be ina prevista da quest CSS.'''\n'''El n'è anc betg memorisà.'''",
        "sitejspreview": "'''Fa stim che quai è be ina prevista da quest JavaScript.'''\n'''El n'è anc betg memorisà.'''",
-       "userinvalidcssjstitle": "'''Attenziun:''' I n'exista nagin skin \"$1\".\nFa stim che titels da paginas persunalisadas .css u .js vegnan scrits pitschen, p. ex. {{ns:user}}:Foo/vector.css e betg {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "'''Attenziun:''' I n'exista nagin skin \"$1\".\nFa stim che titels da paginas persunalisadas .css u .js vegnan scrits pitschen, p. ex. {{ns:user}}:Foo/vector.css e betg {{ns:user}}:Foo/Vector.css.",
        "updated": "(midà)",
        "note": "'''Remartga:'''",
        "previewnote": "'''Fa stim che quai è be ina prevista.'''\nTias midadas n'èn anc betg vegnidas memorisadas!",
        "prefs-files": "Datotecas",
        "prefs-custom-css": "CSS persunalisà",
        "prefs-custom-js": "JavaScript persunalisà",
-       "prefs-common-css-js": "CSS/JavaScript cundividì per tut ils skins:",
+       "prefs-common-config": "CSS/JavaScript cundividì per tut ils skins:",
        "prefs-reset-intro": "Ti pos utilisar questa pagina per restituir las valurs da standard da questa pagina per tias preferenzas. \nQuesta operaziun na po betg vegnir revocada.",
        "prefs-emailconfirm-label": "Confirmaziun per e-mail:",
        "youremail": "Adressa dad e-mail:",
index 2386dc4..2a06075 100644 (file)
@@ -71,6 +71,7 @@
        "tog-watchlisthideminor": "Ascunde modificările minore din lista de pagini urmărite",
        "tog-watchlisthideliu": "Ascunde modificările efectuate de utilizatori autentificați din lista de pagini urmărite",
        "tog-watchlistreloadautomatically": "Reîncarcă automat lista paginilor urmărite de fiecare dată când un filtru este modificat (necesită JavaScript)",
+       "tog-watchlistunwatchlinks": "Adaugă legături directe pentru urmărire/neurmărire inrărilor din lista de pagini urmărite (este nevoie de JavaScript pentru a activa funcționalitatea)",
        "tog-watchlisthideanons": "Ascunde modificările făcute de utilizatori anonimi din lista de pagini urmărite",
        "tog-watchlisthidepatrolled": "Ascunde paginile patrulate din lista de pagini urmărite",
        "tog-watchlisthidecategorization": "Ascunde categorisirea paginilor",
        "nosuchusershort": "Nu există niciun utilizator cu numele „$1”.\nVerificați ortografierea.",
        "nouserspecified": "Trebuie să specificați un nume de utilizator.",
        "login-userblocked": "Acest utilizator este blocat. Autentificarea nu este permisă.",
-       "wrongpassword": "Parola pe care ați introdus-o este incorectă. Vă rugăm să încercați din nou.",
+       "wrongpassword": "Utilizatorul sau parola pe care le-ați introdus sunt incorecte. Vă rugăm să încercați din nou.",
        "wrongpasswordempty": "Spațiul pentru introducerea parolei nu a fost completat. Vă rugăm să încercați din nou.",
        "passwordtooshort": "Parola trebuie să aibă cel puțin {{PLURAL:$1|1 caracter|$1 caractere|$1 de caractere}}.",
        "passwordtoolong": "Parolele nu pot fi mai lungi de {{PLURAL:$1|un caracter|$1 caractere|$1 de caractere}}.",
        "botpasswords-insert-failed": "Eroare adăugare nume bot \"$1\". A fost adăugat deja?",
        "botpasswords-update-failed": "Updatare nume bot $1 eșuat. A fost șters?",
        "botpasswords-created-title": "Parola de robot a fost creată",
-       "botpasswords-created-body": "Parola bot pentru bot nume \"$1\" a utilizatorului \"$2\" a fost creată.",
+       "botpasswords-created-body": "Parola de robot pentru robotul \"$1\" al {{GENDER:$2|utilizatorului|utilizatoarei}} \"$2\" a fost creată.",
        "botpasswords-updated-title": "Parola de robot a fost actualizată",
-       "botpasswords-updated-body": "Parola bot pentru bot nume \"$1\" a utilizatorului \"$2\" a fost actualizată.",
+       "botpasswords-updated-body": "Parola de robot pentru robotul \"$1\" al {{GENDER:$2|utilizatorului|utilizatoarei}} \"$2\" a fost actualizată.",
        "botpasswords-deleted-title": "Parola de bot a fost ștearsă",
-       "botpasswords-deleted-body": "Parola bot pentru bot nume \"$1\" a utilizatorului \"$2\" a fost stearsă.",
+       "botpasswords-deleted-body": "Parola de robot pentru robotul \"$1\" al {{GENDER:$2|utilizatorului|utilizatoarei}} \"$2\" a fost ștearsă.",
        "botpasswords-newpassword": "Noua parolă pentru autentificare cu <strong>$1</strong> este <strong>$2</strong>. <em>Salvați-l pentru utilizări ulterioare.</em><br>(Pentru roboți mai vechi, care necesită ca numele de autentificare să fie același cu numele de utilizator, puteți utiliza și <strong>$3</strong> ca nume de utilizator și <strong>$4</strong> ca parolă.)",
        "botpasswords-no-provider": "BotPasswordsSessionProvider nu este disponibil.",
        "botpasswords-restriction-failed": "Restricțiile privind parola botului împiedică această conectare.",
        "userjspreview": "'''Rețineți că vizualizați doar o previzualizare/versiune de testare a JavaScript-ului dumneavoastră de utilizator.'''\n'''Acesta nu a fost încă salvat!'''",
        "sitecsspreview": "'''Rețineți că doar previzualizați această foaie de stil.'''\n'''Ea nu a fost salvată încă!'''",
        "sitejspreview": "'''Rețineți că doar previzualizați acest cod JavaScript.'''\n'''El nu a fost salvat încă!'''",
-       "userinvalidcssjstitle": "'''Avertizare:''' Nu există aspectul „$1”.\nPaginile .css și .js specifice utilizatorilor au titluri care încep cu literă mică; de exemplu {{ns:user}}:Foo/vector.css în comparație cu {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "'''Avertizare:''' Nu există aspectul „$1”.\nPaginile .css și .js specifice utilizatorilor au titluri care încep cu literă mică; de exemplu {{ns:user}}:Foo/vector.css în comparație cu {{ns:user}}:Foo/Vector.css.",
        "updated": "(Actualizat)",
        "note": "'''Notă:'''",
        "previewnote": "'''Țineți cont că aceasta este doar o previzualizare.'''\nModificările dumneavoastră nu au fost încă salvate!",
        "yourtext": "Textul dumneavoastră",
        "storedversion": "Versiunea curentă",
        "editingold": "'''Atenție: Modificați o versiune veche a acestei pagini.'''\nDacă salvați pagina, toate modificările intermediare se vor pierde.",
+       "unicode-support-fail": "Se pare că browserul dumneavoastră nu știe Unicode. Acest lucru este necesar pentru a edita pagini, de aceea editarea dumneavoastră nu a fost salvată.",
        "yourdiff": "Diferențe",
        "copyrightwarning": "Reține că toate contribuțiile la {{SITENAME}} sunt distribuite sub licența $2 (vezi $1 pentru detalii).\nDacă nu doriți ca ceea ce scrieți să fie modificat fără milă și redistribuit în voie, atunci nu trimiteți materialele respective aici.<br />\nDe asemenea, ne asigurați că ceea ce ați scris a fost compoziție proprie sau copie dintr-o resursă publică sau liberă.\n'''Nu introduceți materiale aflate sub incidența drepturilor de autor fără a avea permisiune!'''",
        "copyrightwarning2": "Rețineți că toate contribuțiile la {{SITENAME}} pot fi modificate, alterate sau șterse de alți contribuitori.\nDacă nu doriți ca ceea ce scrieți să fie modificat fără milă și redistribuit în voie, atunci nu trimiteți materialele respective aici.<br />\nDe asemenea, ne asigurați că ceea ce ați scris a fost compoziție proprie sau copie dintr-o resursă publică sau liberă (vedeți $1 pentru detalii).\n'''Nu introduceți materiale aflate sub incidența drepturilor de autor fără a avea permisiune!'''",
        "postedit-confirmation-created": "Pagina a fost creată.",
        "postedit-confirmation-restored": "Pagina a fost restaurată.",
        "postedit-confirmation-saved": "Modificarea dumneavoastră a fost salvată.",
+       "postedit-confirmation-published": "Editarea dumneavoastră a fost publicată.",
        "edit-already-exists": "Pagina nouă nu a putut fi creată.\nEa există deja.",
        "defaultmessagetext": "Textul implicit",
        "content-failed-to-parse": "Nu s-a putut analiza conținutul de tip $2 pentru modelul $1: $3",
        "parser-template-loop-warning": "Buclă de formate detectată: [[$1]]",
        "template-loop-category": "Pagini cu bucle de formate",
        "template-loop-category-desc": "Pagina conține o buclă de șabloane, adică un șablon care se autoapelează recursiv.",
+       "template-loop-warning": "<strong>Atenție:</strong> Această pagină apelează [[:$1]] ceea ce cauzează un ciclu de formate (un apel recursiv infinit).",
        "parser-template-recursion-depth-warning": "Limită de adâncime a recursiei depășită ($1)",
        "language-converter-depth-warning": "Limita adâncimii convertorului de limbă a fost depășită ($1)",
        "node-count-exceeded-category": "Pagini unde numărul de noduri este depășit",
        "diff-multi-sameuser": "(Nu {{PLURAL:$1|s-a afișat o versiune intermediară efectuată|s-au afișat $1 versiuni intermediare efectuate|s-au afișat $1 de versiuni intermediare efectuate}} de același utilizator)",
        "diff-multi-otherusers": "(Nu {{PLURAL:$1|s-a afișat o versiune intermediară efectuată|s-au afișat $1 versiuni intermediare efectuate|s-au afișat $1 de versiuni intermediare efectuate}} de {{PLURAL:$2|un alt utilizator|alți $2 utilizatori|alți $2 de utilizatori}})",
        "diff-multi-manyusers": "({{PLURAL:$1|O versiune intermediară efectuată de|$1 (de) versiuni intermediare efectuate de peste}} $2 {{PLURAL:$2|utilizator|utilizatori}} {{PLURAL:$1|neafișată|neafișate}})",
+       "diff-paragraph-moved-tonew": "Paragraful a fost mutat. Clic pentru a ajunge la noua locație.",
+       "diff-paragraph-moved-toold": "Paragraful a fost mutat. Clic pentru a ajunge la vechea locație.",
        "difference-missing-revision": "{{PLURAL:$2|O versiune a|$2 versiuni ale|$2 de versiuni ale}} acestei diferențe ($1) nu {{PLURAL:$2|a fost găsită|au fost găsite}}.\n\nAcest lucru se întâmplă de obicei atunci când se accesează o legătură expirată către istoricul unei pagini șterse.\nDetalii se pot găsi în [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} jurnalul ștergerilor].",
        "searchresults": "Rezultatele căutării",
        "searchresults-title": "Rezultatele căutării pentru „$1”",
        "recentchangesdays-max": "(maxim {{PLURAL:$1|o zi|$1 zile}})",
        "recentchangescount": "Numărul modificărilor afișate implicit:",
        "prefs-help-recentchangescount": "Sunt incluse schimbările recente, istoricul paginilor și jurnalele.",
-       "prefs-help-watchlist-token2": "Aceasta este cheia secretă pentru fluxul web al listei dumneavoastră de pagini urmărite.\nOricine o cunoaște vă va putea citi lista de pagini urmărite, așa că n-o partajați cu nimeni.\n[[Special:ResetTokens|Faceți clic aici dacă doriți să o resetați]].",
+       "prefs-help-watchlist-token2": "Aceasta este cheia secretă pentru fluxul web al listei dumneavoastră de pagini urmărite.\nOricine o cunoaște vă va putea citi lista de pagini urmărite, așa că n-o partajați cu nimeni. Dacă doriți, \n[[Special:ResetTokens|o puteți reseta]].",
        "savedprefs": "Preferințele dumneavoastră au fost salvate.",
        "savedrights": "Grupurile utilizatorului {{GENDER:$1|$1}} au fost salvate.",
        "timezonelegend": "Fus orar:",
        "prefs-files": "Fișiere",
        "prefs-custom-css": "CSS personalizat",
        "prefs-custom-js": "JS personalizat",
-       "prefs-common-css-js": "Pagini CSS și JavaScript comune pentru toate interfețele:",
+       "prefs-common-config": "Pagini CSS și JavaScript comune pentru toate interfețele:",
        "prefs-reset-intro": "Poți folosi această pagină pentru a reseta preferințele la valorile implicite.\nAcțiunea nu este reversibilă.",
        "prefs-emailconfirm-label": "Confirmare e-mail:",
        "youremail": "Adresă de e-mail:",
        "prefs-editor": "Editor",
        "prefs-preview": "Previzualizare",
        "prefs-advancedrc": "Opțiuni avansate",
+       "prefs-opt-out": "Dezactivați îmbunătățirile",
        "prefs-advancedrendering": "Opțiuni avansate",
        "prefs-advancedsearchoptions": "Opțiuni avansate",
        "prefs-advancedwatchlist": "Opțiuni avansate",
        "recentchanges-legend": "Opțiuni schimbări recente",
        "recentchanges-summary": "Urmăriți cele mai recente modificări din wiki pe această pagină.",
        "recentchanges-noresult": "Nicio modificare din intervalul specificat nu corespunde acestor criterii.",
+       "recentchanges-timeout": "Căutarea a expirat. Puteți încerca parametri de căutare diferiți.",
+       "recentchanges-network": "Datorită unei erori tehnice nu au putut fi încărcate rezultate. Încercați să reîncărcați pagina.",
+       "recentchanges-notargetpage": "Introduceți numele paginii deasupra pentru a vedea schimbări legate de acea pagină.",
        "recentchanges-feed-description": "Urmărește cele mai recente schimbări folosind acest flux.",
        "recentchanges-label-newpage": "Această modificare a creat o pagină nouă",
        "recentchanges-label-minor": "Aceasta este o modificare minoră",
        "rcfilters-activefilters": "Filtre active",
        "rcfilters-advancedfilters": "Filtre avansate",
        "rcfilters-limit-title": "Schimbări de afișat",
+       "rcfilters-limit-and-date-label": "$1 {{PLURAL:$1|schimbare|schimbări|de schimbări}}, $2",
+       "rcfilters-date-popup-title": "Perioada căutării",
        "rcfilters-days-title": "Ultimele zile",
        "rcfilters-hours-title": "Ultimele ore",
        "rcfilters-days-show-days": "$1 {{PLURAL:$1|zi|zile|de zile}}",
        "rcfilters-days-show-hours": "$1 {{PLURAL:$1|oră|ore|de ore}}",
        "rcfilters-highlighted-filters-list": "FIltru: $1",
        "rcfilters-quickfilters": "Filtre salvate",
-       "rcfilters-quickfilters-placeholder-title": "Nicio legătură salvată încă",
+       "rcfilters-quickfilters-placeholder-title": "Niciun filtru salvat deocamdată",
        "rcfilters-quickfilters-placeholder-description": "Pentru a salva setările de filtrare și a le refolosi mai târziu, faceți clic pe iconița de marcaje în zona de Filtre active de mai jos.",
        "rcfilters-savedqueries-defaultlabel": "Filtre salvate",
        "rcfilters-savedqueries-rename": "Redenumește",
        "rcfilters-savedqueries-apply-and-setdefault-label": "Creați filtru implicit",
        "rcfilters-savedqueries-cancel-label": "Anulare",
        "rcfilters-savedqueries-add-new-title": "Salvați filtrele curente",
+       "rcfilters-savedqueries-already-saved": "Aceste filtre sunt deja salvate. Schimbați setările pentru a salva un nou filtru de căutare.",
        "rcfilters-restore-default-filters": "Restaurați filtrele prestabilite",
        "rcfilters-clear-all-filters": "Ștergeți toate filtrele",
        "rcfilters-show-new-changes": "Arată schimbările mai noi",
-       "rcfilters-search-placeholder": "Filtrați modificările recente (răsfoiți sau începeți să tastați)",
+       "rcfilters-search-placeholder": "Filtrați modificările recente (folosiți meniul sau căutați numele filtrului)",
        "rcfilters-invalid-filter": "Filtru invalid",
        "rcfilters-empty-filter": "Nu există filtre active. Toate contribuțiile sunt afișate.",
        "rcfilters-filterlist-title": "Filtre",
        "rcfilters-filter-user-experience-level-unregistered-label": "Neînregistrat",
        "rcfilters-filter-user-experience-level-unregistered-description": "Editorii care nu sunt conectați.",
        "rcfilters-filter-user-experience-level-newcomer-label": "Nou veniți",
-       "rcfilters-filter-user-experience-level-newcomer-description": "Editorii înregistrați care au mai puțin de 10 editări și 4 zile de activitate.",
+       "rcfilters-filter-user-experience-level-newcomer-description": "Editorii înregistrați care au mai puțin de 10 editări sau 4 zile de activitate.",
        "rcfilters-filter-user-experience-level-learner-label": "Cursanți",
        "rcfilters-filter-user-experience-level-learner-description": "Editorii înregistrați a căror experiență este între \"Noi veniți\" și \"Utilizatori experimentați\".",
        "rcfilters-filter-user-experience-level-experienced-label": "Utilizatori experimentați",
        "rcfilters-exclude-button-on": "Se exclud cele selectate",
        "rcfilters-view-tags": "Editări marcate",
        "rcfilters-view-namespaces-tooltip": "Filtrează rezultatele după spațiul de nume",
+       "rcfilters-view-tags-tooltip": "Filtrați rezultatele folosind etichete pentru editări",
+       "rcfilters-view-return-to-default-tooltip": "Întoarcere la meniul principal",
+       "rcfilters-view-tags-help-icon-tooltip": "Aflați mai multe despre etichetele de editare",
        "rcfilters-liveupdates-button": "Actualizări în timp real",
        "rcfilters-liveupdates-button-title-on": "Oprește actualizările",
        "rcfilters-liveupdates-button-title-off": "Arată noile schimbări când se întâmplă",
        "rcfilters-watchlist-markseen-button": "Marchează toate schimbările ca văzute",
        "rcfilters-watchlist-edit-watchlist-button": "Editați lista de pagini urmărite",
+       "rcfilters-watchlist-showupdated": "Paginile care au fost modificate după ultima dumneavoastră vizită sunt afișate <strong>îngroșat</strong>.",
        "rcfilters-preference-label": "Ascunde versiunea îmbunătățită a Schimbărilor Recente",
        "rcfilters-preference-help": "Ascunde interfața schimbată în 2017 și toate uneltele adăugate de atunci.",
+       "rcfilters-filter-showlinkedfrom-label": "Arată schimbările pe paginile către care există legături în",
+       "rcfilters-filter-showlinkedfrom-option-label": "<strong>Pages la care trimite</strong> pagina selectată",
        "rcnotefrom": "Dedesubt {{PLURAL:$5|se află o modificare|sunt modificările}} începând cu <b>$3, $4</b> (maximum <b>$1</b> afișate).",
        "rclistfromreset": "Resetați selectarea datei",
        "rclistfrom": "Afișează modificările începând cu $3, ora $2",
        "recentchangeslinked-feed": "Modificări corelate",
        "recentchangeslinked-toolbox": "Modificări corelate",
        "recentchangeslinked-title": "Modificări legate de „$1”",
-       "recentchangeslinked-summary": "Aceasta este o listă a schimbărilor efectuate recent asupra paginilor cu legături de la o anumită pagină (sau asupra membrilor unei anumite categorii).\nPaginile pe care le [[Special:Watchlist|urmăriți]] apar cu <strong>aldine</strong>.",
+       "recentchangeslinked-summary": "Introduceți titlul unei pagini pentru a vedea schimbările din paginile spre care trimite sau care trimit spre ea. (Pentru a vedea membrii unei categorii, scrieți Categorie:Numele categoriei).\nSchimbările din paginile pe care le [[Special:Watchlist|urmăriți]] apar cu <strong>aldine</strong>.",
        "recentchangeslinked-page": "Numele paginii:",
        "recentchangeslinked-to": "Arată în schimb modificările asupra paginilor care se leagă de pagina indicată",
        "recentchanges-page-added-to-category": "[[:$1]] a fost adăugată în categorii",
        "expandtemplates": "Expandare formate",
        "expand_templates_intro": "Această pagină specială servește la expandarea recursivă a tuturor formatelor dintr-un text. Ea acționează și asupra funcțiilor de analiză (''parser'') de tipul <nowiki>{{</nowiki>#if:...}}, a variabilelor precum <nowiki>{{</nowiki>CURRENTDAY}} și în general asupra oricăror coduri cuprinse între acolade duble.",
        "expand_templates_title": "Titlul contextului (de exemplu pentru {{PAGENAME}}):",
-       "expand_templates_input": "Introduceți textul aici:",
+       "expand_templates_input": "Introduceți wikitextul aici:",
        "expand_templates_output": "Rezultat",
        "expand_templates_xml_output": "Ieșire XML",
        "expand_templates_html_output": "Ieșire HTML brut",
index 640c040..03956b5 100644 (file)
        "userjspreview": "'''Arrecuerdete ca tu ste vide/teste sulamende in andeprime 'u JavaScript tue.'''\n'''Non g'à state angore reggistrete ninde!'''",
        "sitecsspreview": "'''Arrecuerdete ca tu ste vide sulamende in andeprime 'u CSS tune.'''\n'''Non g'à state angore reggistrate ninde!'''",
        "sitejspreview": "'''Arrecuerdete ca tu ste vide sulamende in andeprime 'u codece JavaScript tune.'''\n'''Non g'à state angore reggistrate ninde!'''",
-       "userinvalidcssjstitle": "'''Attenziò:''' Non ge stè 'nu skin \"$1\".\nArrecuerdete ca jndr'à le file personalizzete .css e .js s'ause scrivere le titele cu le lettere piccenne, pe esembie {{ns:user}}:Foo/vector.css è diverse da {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "'''Attenziò:''' Non ge stè 'nu skin \"$1\".\nArrecuerdete ca jndr'à le file personalizzete .css e .js s'ause scrivere le titele cu le lettere piccenne, pe esembie {{ns:user}}:Foo/vector.css è diverse da {{ns:user}}:Foo/Vector.css.",
        "updated": "(Cangiete)",
        "note": "'''Vide Bbuene:'''",
        "previewnote": "'''Arrecuerdete queste è sole 'n'andeprime.'''\nle cangiaminde non g'onne state angore reggistrate!",
        "prefs-files": "Fails",
        "prefs-custom-css": "CSS Personalizzete",
        "prefs-custom-js": "JS Personalizzete",
-       "prefs-common-css-js": "CSS/JS condivise pe tutte le sfonde:",
+       "prefs-common-config": "CSS/JS condivise pe tutte le sfonde:",
        "prefs-reset-intro": "Tu puè ausà sta pàgene pe azzerà le preferenze tue a quidde de default d'u site.\nQuiste non ge pò essere annullate.",
        "prefs-emailconfirm-label": "Conferme de l'e-mail:",
        "youremail": "Poste:",
index e4f7dd8..f7a6e44 100644 (file)
        "userjspreview": "'''Помните, что это только предварительный просмотр вашего javascript-файла, он ещё не сохранён!'''",
        "sitecsspreview": "'''Помните, что вы только предварительно просматриваете этот CSS.'''\n'''Он ещё не сохранён!'''",
        "sitejspreview": "'''Помните, что вы только предварительно просматриваете этот JavaScript-код.'''\n'''Он ещё не сохранён!'''",
-       "userinvalidcssjstitle": "'''Внимание:''' тема оформления «$1» не найдена. Помните, что пользовательские страницы .css и .js должны иметь название, состоящее только из строчных букв, например «{{ns:user}}:Некто/vector.css», а не «{{ns:user}}:Некто/Vector.css».",
+       "userinvalidconfigtitle": "'''Внимание:''' тема оформления «$1» не найдена. Помните, что пользовательские страницы .css и .js должны иметь название, состоящее только из строчных букв, например «{{ns:user}}:Некто/vector.css», а не «{{ns:user}}:Некто/Vector.css».",
        "updated": "(Обновлена)",
        "note": "'''Примечание:'''",
        "previewnote": "'''Помните, что это только предварительный просмотр.'''\nВаши изменения ещё не были сохранены!",
        "prefs-files": "Файлы",
        "prefs-custom-css": "Собственный CSS",
        "prefs-custom-js": "Собственный JS",
-       "prefs-common-css-js": "Общие CSS/JS для всех тем оформления:",
+       "prefs-common-config": "Общие CSS/JS для всех тем оформления:",
        "prefs-reset-intro": "Эта страница может быть использована для сброса ваших настроек на стандартные.\nУчтите, что это действие невозможно отменить.",
        "prefs-emailconfirm-label": "Подтверждение электронной почты:",
        "youremail": "Электронная почта:",
        "thumbnail_dest_directory": "Невозможно создать целевую директорию",
        "thumbnail_image-type": "Данный тип изображения не поддерживается",
        "thumbnail_gd-library": "Неполная конфигурация библиотеки GD, отсутствует функция $1",
+       "thumbnail_image-size-zero": "Размер файла изображения, кажется, равен нулю.",
        "thumbnail_image-missing": "По-видимому, отсутствует файл $1",
        "thumbnail_image-failure-limit": "Было сделано слишком много неудачных попыток ($1 или больше) формирования этого эскиза. Пожалуйста, повторите попытку позже.",
        "import": "Импортирование страниц",
index 2eb7f44..54bf4fc 100644 (file)
        "userjspreview": "'''Памятайте, же тестуєте а перезерате лем нагляд вашого хосновательского JavaScript-у, іщі не быв уложеный!'''",
        "sitecsspreview": "'''Памятайте, же собі перезерате лем нагляд того CSS.'''\n'''Іщі не было уложене!'''",
        "sitejspreview": "'''Памятайте, же собі перезерате лем нагляд того JavaScript-у.'''\n'''Іщі не быв уложеный!'''",
-       "userinvalidcssjstitle": "'''Увага:''' Тема взгляду „$1“ не екзістує. Не забудьте, же хосновательске .css і .js файлы хоснують малы писмена, наприклад {{ns:user}}:{{BASEPAGENAME}}/vector.css, а не {{ns:user}}:{{BASEPAGENAME}}/Vector.css.",
+       "userinvalidconfigtitle": "'''Увага:''' Тема взгляду „$1“ не екзістує. Не забудьте, же хосновательске .css і .js файлы хоснують малы писмена, наприклад {{ns:user}}:{{BASEPAGENAME}}/vector.css, а не {{ns:user}}:{{BASEPAGENAME}}/Vector.css.",
        "updated": "(Зміна уложена)",
        "note": "'''Позначка:'''&nbsp;",
        "previewnote": "'''Памятайте, же то лем нагляд.'''\nЗміны іщі не суть уложены!",
        "prefs-files": "Файлы",
        "prefs-custom-css": "Властный CSS",
        "prefs-custom-js": "Властный JS",
-       "prefs-common-css-js": "Сдїляне CSS/JS про вшыткы штілы:",
+       "prefs-common-config": "Сдїляне CSS/JS про вшыткы штілы:",
        "prefs-reset-intro": "Помочов той сторінкы можете вшыткы наставлїня вернути на імпліцітны годноты.\nТоту операцію не годен вернути назад.",
        "prefs-emailconfirm-label": "Потверджіня електронічной пошты:",
        "youremail": "Адреса електронічной пошты:",
index 0c2f265..c2805b2 100644 (file)
        "userjspreview": "<strong>एतत् केवलं सदस्यस्य JabaScript इत्यस्य प्राग्दृश्यं वर्तते इति स्मरतु । भवता/भवत्या कृतानि परिवर्तनानि एतवता न रक्षितानि !</strong>",
        "sitecsspreview": "<strong>एतत् केवलं CSS इत्यस्य प्राग्दृश्यं वर्तते इति स्मरतु । भवता/भवत्या कृतानि परिवर्तनानि एतवता न रक्षितानि !</strong>",
        "sitejspreview": "<strong>एतत् केवलं JavaScript इत्यस्य प्राग्दृश्यं वर्तते इति स्मरतु । भवता/भवत्या कृतानि परिवर्तनानि एतवता न रक्षितानि !</strong>",
-       "userinvalidcssjstitle": "'''पूर्वसूचना:'''  \"$1\" इति त्वक् न विद्यते।\nयोजकपरिवर्तिते .css, .js सञ्चिके लघु-आङ्ग्लवर्णमालायाः वर्णैः लिख्येते । उदा. {{ns:user}}:Foo/Vector.css एवं न लेखनीयम् । लघुवर्णैः {{ns:user}}:Foo/vector.css एवं लेखनीयम् ।",
+       "userinvalidconfigtitle": "'''पूर्वसूचना:'''  \"$1\" इति त्वक् न विद्यते।\nयोजकपरिवर्तिते .css, .js सञ्चिके लघु-आङ्ग्लवर्णमालायाः वर्णैः लिख्येते । उदा. {{ns:user}}:Foo/Vector.css एवं न लेखनीयम् । लघुवर्णैः {{ns:user}}:Foo/vector.css एवं लेखनीयम् ।",
        "updated": "(नवीकृतम् (updated))",
        "note": "'''सूचना:'''",
        "previewnote": "<strong>एतत् केवलं प्राग्दृश्यं वर्तते इति स्मरतु । भवता/भवत्या कृतानि परिवर्तनानि एतवता न रक्षितानि !</strong>",
        "prefs-files": "सञ्चिकाः",
        "prefs-custom-css": "स्वानुकूलसम्पादितं CSS",
        "prefs-custom-js": "स्वानुकूलसम्पादितं JavaScript",
-       "prefs-common-css-js": "सर्वासां त्वचां (of skins) कृते CSS/JavaScript:",
+       "prefs-common-config": "सर्वासां त्वचां (of skins) कृते CSS/JavaScript:",
        "prefs-reset-intro": "भवान्/भवती अस्य पृष्ठस्य साहाय्येन स्वस्य इष्टतमविकल्पान् मूलविकि-विकल्पसदृशं स्थापयितुं (कर्तुं) शक्नोति ।\nपरन्तु ततः भवान्/भवती पूर्ववत् स्थितिं प्राप्तुं न शक्ष्यति ।",
        "prefs-emailconfirm-label": "वि-पत्रं दृढीक्रियताम् :",
        "youremail": "वि-पत्रसङ्केतः :",
index 5820e6e..2af5131 100644 (file)
        "userjspreview": "'''Умнума: бу JavaScript тургутуутэ эрэ, уларыппытыҥ бигэргэтиллэ илик!'''",
        "sitecsspreview": "'''Бу CSS бигэргэппэккэ көрө олороргун умнума.'''\n'''Бигэргэтиллэ илик!'''",
        "sitejspreview": "'''Бу JavaScript-куодун бигэргэппэккэ көрө олороргун умнума.'''\n'''Бигэргэтиллэ илик!'''",
-       "userinvalidcssjstitle": "'''Болҕой:''' Бу тиэмэ «$1» суох. Кыттааччы .css и .js сирэйдэрэ кыра буукубаннан суруллуохтаахтар, холобур «{{ns:user}}:Ньургун/vector.css», маннык буолуо суохтаах «{{ns:user}}:Ньургун/Vector.css».",
+       "userinvalidconfigtitle": "'''Болҕой:''' Бу тиэмэ «$1» суох. Кыттааччы .css и .js сирэйдэрэ кыра буукубаннан суруллуохтаахтар, холобур «{{ns:user}}:Ньургун/vector.css», маннык буолуо суохтаах «{{ns:user}}:Ньургун/Vector.css».",
        "updated": "(Саҥардылынна)",
        "note": "'''Хос быһаарыы:'''",
        "previewnote": "'''Бу барыллаан көрүү эрэ.'''\nАтын уларытыы бигэргэтиллэ илик!",
        "prefs-files": "Билэлэр",
        "prefs-custom-css": "Бэйэ CSS",
        "prefs-custom-js": "Бэйэ JS",
-       "prefs-common-css-js": "Бары тиэмэлэргэ биир CSS/JS",
+       "prefs-common-config": "Бары тиэмэлэргэ биир CSS/JS",
        "prefs-reset-intro": "Бу сирэй көмөтүнэн туруорууларгын саҥаттан туруорар турукка төннөрүөххүн сөп.\nМаны бигэргэттэххинэ билигин баар туруоруулары дэбигис сөргүппэккин.",
        "prefs-emailconfirm-label": "Эл. почтаны бигэргэтии:",
        "youremail": "E-mail-ыҥ:",
index d5cb44a..54270ab 100644 (file)
                        "Ramjit Tudu"
                ]
        },
-       "tog-underline": "Joṛaoko latarre dag udugoḱma:",
-       "tog-hideminor": "Nitaḱ bodolaḱre huḍiṅ kạmi danaṅme",
+       "tog-underline": "ᱡᱚᱱᱚᱲ ᱞᱟᱛᱟᱨᱨᱮ ᱫᱟᱜᱽ ᱩᱫᱩᱜᱽᱢᱮ:",
+       "tog-hideminor": "ᱱᱤᱛᱚᱜ ᱵᱚᱫᱚᱞᱟᱜᱨᱮ ᱦᱩᱰᱤᱧ ᱥᱟᱯᱲᱟᱣ ᱩᱠᱩᱭᱢᱮ",
        "tog-hidepatrolled": "ᱡᱚᱲᱟᱣᱠᱟᱱ ᱥᱟᱯᱲᱟᱣᱠᱚ ᱱᱟᱣᱟ ᱵᱚᱫᱚᱞ ᱠᱷᱚᱱ ᱩᱠᱩᱭᱢᱮ",
        "tog-newpageshidepatrolled": "ᱱᱟᱣᱟ ᱥᱟᱦᱴᱟ ᱛᱟᱹᱞᱠᱟᱹ ᱠᱷᱚᱱ ᱵᱤᱰᱟᱹᱣᱠᱟᱱ ᱥᱟᱦᱴᱟᱠᱚ ᱩᱠᱩᱭᱢᱮ",
        "tog-hidecategorization": "ᱥᱟᱦᱴᱟ ᱨᱮᱱᱟᱜ ᱛᱷᱚᱠ ᱠᱚ ᱫᱟᱱᱟᱝ",
-       "tog-extendwatchlist": "Khạli nitoḱ bodolko do baṅ, joto bodolkodo ńeloḱ tạlikare phaylaomẽ.",
+       "tog-extendwatchlist": "ᱠᱷᱟᱹᱞᱤ ᱱᱮᱛᱚᱜ ᱵᱚᱫᱚᱞᱟᱜ ᱫᱚ ᱵᱟᱝ, ᱡᱚᱛᱚ ᱵᱟᱫᱚᱞᱟᱜ ᱜᱮ ᱧᱮᱞᱚᱜ ᱛᱟᱹᱞᱠᱟᱹᱨᱮ ᱯᱷᱟᱭᱞᱟᱣᱢᱮ",
        "tog-usenewrc": "Nahaḱ bodolakanaḱko ar nojor reaḱ pahaṭare bodolaḱko mit́são ńelńam",
-       "tog-numberheadings": "Mukhiạ kathako do actege piṛhipiṛhite sajaḱma",
+       "tog-numberheadings": "ᱢᱩᱬᱩᱛ ᱠᱟᱛᱷᱟᱠᱩ ᱫᱚ ᱟᱪᱛᱮᱜᱮ ᱯᱤᱲᱦᱤ ᱯᱤᱲᱦᱤᱛᱮ ᱥᱟᱡᱟᱜᱢᱟ",
        "tog-showtoolbar": "ᱦᱟᱹᱛᱤᱭᱟᱹᱨ ᱠᱟᱸᱛ ᱩᱫᱩᱜᱽ ᱢᱮ",
        "tog-editondblclick": "Bar dhao lin kate sakam torjomão reaḱ ạidari emogoḱma",
        "tog-editsectiononrightclick": "Pahaṭa reaḱ pahaṭa guṭkathare jojom seć lin hotete <br /> pahaṭa sompadon lạgitte ektiạr em hoyoḱma (JavaScript)",
-       "tog-watchcreations": "Ińaḱ tear sakam ar rakaṕ páelko ińaḱ ńelogoḱ tạlikare ńeloḱ ma",
-       "tog-watchdefault": "Ińaḱ purạoakanaḱ sakam ar phayelko do ińaḱ ńeloḱ tạlikare joṛaoḱma",
-       "tog-watchmoves": "Ińaḱ ocoḱ sakam ar phayelko inyaḱ nojor sakamre joṛaḱma",
-       "tog-watchdeletion": "Sakamko tońgeyme Ińaḱ ńeloḱ tạlika khon get́ giḍikam",
-       "tog-minordefault": "Etohoṕre sanam joṛao purạoanaḱko do bekor unuduḱ lekate cinhạkma",
-       "tog-previewontop": "Joṛao bakso purạo lahare unuduḱ hoyoḱma",
-       "tog-previewonfirst": "Pạhil joṛao purạore unuduḱ hoyoḱma",
+       "tog-watchcreations": "ᱥᱟᱦᱴᱟᱠᱩ ᱥᱮᱞᱮᱫᱢᱮ ᱤᱧᱟᱜ ᱛᱮᱭᱟᱨ ᱟᱨ ᱨᱮᱫᱠᱩ ᱤᱧᱟᱜ ᱧᱮᱞᱚᱜ ᱛᱟᱹᱞᱠᱟᱹᱨᱮ ᱨᱟᱠᱟᱵ ᱢᱮ",
+       "tog-watchdefault": "ᱤᱧᱟᱜ ᱥᱟᱯᱲᱟᱣ ᱧᱮᱞᱚᱜ ᱛᱟᱹᱞᱠᱟᱹᱨᱮ ᱥᱟᱦᱴᱟᱠᱩ ᱟᱨ ᱨᱮᱫᱠᱩ ᱥᱮᱞᱮᱫ ᱢᱮ",
+       "tog-watchmoves": "ᱤᱧᱟᱜ ᱥᱟᱯᱲᱟᱣ ᱧᱮᱞᱚᱜ ᱛᱟᱹᱞᱠᱟᱹᱨᱮ ᱥᱟᱦᱴᱟᱠᱩ ᱟᱨ ᱨᱮᱫᱠᱩ ᱚᱪᱟᱜᱽ ᱢᱮ",
+       "tog-watchdeletion": "ᱤᱧᱟᱜ ᱥᱟᱯᱲᱟᱣ ᱧᱮᱞᱚᱜ ᱛᱟᱹᱞᱠᱟᱹᱨᱮ ᱥᱟᱦᱴᱟᱠᱩ ᱟᱨ ᱨᱮᱫᱠᱩ ᱜᱮᱫ ᱜᱤᱰᱤᱭ ᱢᱮ",
+       "tog-minordefault": "ᱮᱛᱦᱚᱵᱨᱮ ᱥᱟᱱᱟᱢ ᱥᱟᱯᱲᱟᱣᱠᱩ ᱦᱩᱰᱤᱧ ᱞᱮᱠᱟᱛᱮ ᱪᱤᱱᱦᱟᱹ ᱠᱟᱜ ᱢᱮ",
+       "tog-previewontop": "ᱥᱟᱯᱲᱟᱣ ᱵᱟᱠᱥᱳ ᱞᱟᱦᱟᱨᱮ ᱩᱱᱩᱫᱩᱜ ᱩᱫᱩᱜᱽ ᱢᱮ",
+       "tog-previewonfirst": "ᱯᱟᱹᱦᱤᱞ ᱥᱟᱯᱲᱟᱣ ᱨᱮ ᱩᱱᱩᱫᱩᱜ ᱩᱫᱩᱜᱽ ᱢᱮ",
        "tog-enotifwatchlistpages": "E-mailạńme one tinre in̕aḱ n̕eloḱ tạlika do bodolok",
-       "tog-enotifusertalkpages": "E-mailạn̕me one tinre in̕aḱ roṛaḱ laṛcaṛ sakam do bodoloḱa",
+       "tog-enotifusertalkpages": "ᱤ-ᱢᱮᱞ ᱟᱹᱧᱢᱮ ᱛᱤᱱᱨᱮ ᱤᱧᱟᱜ ᱨᱚᱲ ᱥᱟᱦᱴᱟ ᱵᱚᱫᱚᱞᱜ-ᱟ",
        "tog-enotifminoredits": "E-mailạn̕me arhõ one tinre in̕aḱ sakamre huḍiń kạmi hoyoḱ",
-       "tog-enotifrevealaddr": "Dhạrwạk reaḱ sakamre ińaḱ e-mail ṭhikạna sodor hoyoḱma",
-       "tog-shownumberswatching": "Ńelok laṛcaṛkoaḱ songkha uduḱme",
+       "tog-enotifrevealaddr": "ᱰᱷᱟᱹᱨᱣᱟᱜ ᱥᱟᱦᱴᱟᱨᱮ ᱤᱧᱟᱜ e-mail ᱴᱷᱤᱠᱱᱟ ᱥᱚᱫᱚᱨ ᱦᱩᱭᱩᱜ ᱢᱟ",
+       "tog-shownumberswatching": "ᱧᱮᱞᱚᱜ ᱵᱮᱵᱟᱦᱟᱨᱤᱡ ᱠᱯᱣᱟᱜ ᱮᱞᱮᱞ ᱩᱫᱩᱜᱽ ᱢᱮ",
        "tog-oldsig": "ᱟᱢᱟᱜ ᱥᱩᱦᱤ:",
-       "tog-fancysig": "Signạcar do wikiṭesk hisạbte moneyemẽ (jahan acte hoyoḱ joṛao bạgikate)",
+       "tog-fancysig": "ᱥᱩᱦᱤ ᱫᱚ ᱣᱤᱠᱤ ᱚᱞ ᱦᱤᱥᱟᱹᱵᱛᱮ ᱢᱚᱱᱮᱭᱢᱮ (ᱟᱪᱛᱮ ᱦᱩᱭᱠᱟᱱ ᱡᱚᱱᱚᱲ ᱵᱟᱹᱜᱤᱠᱟᱛᱮ)",
        "tog-uselivepreview": "Jewet́ ńeloḱ beoharme (JavaScript jaruṛ menaḱa)",
-       "tog-forceeditsummary": "Khạli sompadon guṭkatha em oktere iń baḍae ocoyiń hoyoḱma",
-       "tog-watchlisthideown": "Ńeloḱ talikare ińaḱ joṛao kamiko danaṅme",
-       "tog-watchlisthidebots": "Boṭreaḱ sompadon kạmiko do ńeloḱ tạlika khon danaṅmẽ",
-       "tog-watchlisthideminor": "Ńeloḱ tạlikare ińak huḍiṅ joṛao kạmiko danaṅme",
-       "tog-watchlisthideliu": "Ńeloḱ tạlikareaḱ ekaunṭ bolok beoharkoaḱ sompadon danaṅ hoyoḱma",
-       "tog-watchlisthideanons": "Ńeloḱ tạlikare baṅ ńutamanić beoharićaḱ sompadonko danaṅ hoyoḱma",
-       "tog-watchlisthidepatrolled": "Biḍạen sompadonko do ńeloḱ sakamre danaṅmẽ",
+       "tog-forceeditsummary": "ᱠᱷᱟᱹᱞᱤ ᱥᱟᱯᱲᱟᱣ ᱜᱤᱴ ᱠᱟᱛᱷᱟ ᱮᱢ ᱚᱠᱛᱚᱨᱮ ᱤᱧ ᱵᱟᱰᱟᱭ ᱚᱪᱚᱭᱤᱧ ᱦᱩᱭᱩᱜᱢᱟ",
+       "tog-watchlisthideown": "ᱧᱮᱞᱚᱜ ᱛᱟᱹᱞᱠᱟᱹ ᱠᱷᱚᱱ ᱤᱧᱟᱜ ᱥᱟᱯᱲᱟᱣᱠᱩ ᱩᱠᱩᱭᱢᱮ",
+       "tog-watchlisthidebots": "ᱧᱮᱞᱚᱜ ᱛᱟᱹᱞᱠᱟᱹ ᱠᱷᱚᱱ ᱵᱚᱴ ᱥᱟᱯᱲᱟᱣᱠᱩ ᱩᱠᱩᱭᱢᱮ",
+       "tog-watchlisthideminor": "ᱧᱮᱞᱚᱜ ᱛᱟᱹᱞᱠᱟᱹ ᱠᱷᱚᱱ ᱦᱩᱰᱤᱧ ᱥᱟᱯᱲᱟᱣᱠᱩ ᱩᱠᱩᱭᱢᱮ",
+       "tog-watchlisthideliu": "ᱧᱮᱞᱚᱜ ᱛᱟᱹᱞᱠᱟᱹ ᱠᱷᱚᱱ ᱵᱚᱞᱚ ᱟᱠᱟᱱ ᱵᱮᱵᱦᱟᱨᱤᱭᱟᱜ ᱥᱟᱯᱲᱟᱣᱠᱩ ᱩᱠᱩᱭᱢᱮ",
+       "tog-watchlisthideanons": "ᱧᱮᱞᱚᱜ ᱛᱟᱹᱞᱠᱟᱹ ᱠᱷᱚᱱ ᱵᱟᱭ ᱵᱚᱞᱚ ᱟᱠᱟᱱ ᱵᱮᱵᱦᱟᱨᱤᱭᱟᱜ ᱥᱟᱯᱲᱟᱣᱠᱩ ᱩᱠᱩᱭᱢᱮ",
+       "tog-watchlisthidepatrolled": "ᱧᱮᱞᱚᱜ ᱛᱟᱹᱞᱠᱟᱹ ᱠᱷᱚᱱ ᱵᱤᱰᱟᱹᱣᱮᱱ ᱥᱟᱯᱲᱟᱣᱠᱩ ᱩᱠᱩᱭᱢᱮ",
        "tog-watchlisthidecategorization": "ᱥᱟᱦᱴᱟ ᱨᱮᱱᱟᱜ ᱛᱷᱚᱠ ᱠᱚ ᱫᱟᱱᱟᱝ",
-       "tog-ccmeonemails": "E-mail reaḱ kopy kulạńme Eṭaḱ laṛcaṛko kulakome",
-       "tog-diffonly": "Farak reaḱ latar sakamre babotko baṅ udugoḱma",
-       "tog-showhiddencats": "Danaṅ rokom sokomko uduḱmẽ",
+       "tog-ccmeonemails": "E-mail ᱠᱩ ᱨᱮᱭᱟᱜ ᱠᱚᱯᱤᱠᱩ ᱠᱩᱞᱢᱮ ᱡᱟᱸᱦᱟᱸ ᱤᱧ ᱮᱴᱟᱜ ᱵᱮᱵᱦᱟᱨᱤᱡᱠᱩ ᱴᱷᱮᱱᱤᱧ ᱠᱩᱞ ᱞᱮ",
+       "tog-diffonly": "ᱯᱷᱟᱨᱟᱠ ᱨᱮᱭᱟᱜ ᱞᱟᱛᱟᱨ ᱥᱟᱦᱴᱟᱨᱮ ᱵᱟᱵᱚᱛᱠᱩ ᱵᱟᱝ ᱩᱫᱩᱜᱚᱜ ᱢᱟ",
+       "tog-showhiddencats": "ᱩᱠᱩ ᱛᱷᱚᱠᱠᱩ ᱩᱫᱩᱜᱽᱢᱮ",
        "tog-norollbackdiff": "rollback tayomte farak alom uduga",
-       "underline-always": "Sanam okte",
-       "underline-never": "Tis hõ ban̕",
+       "underline-always": "ᱥᱟᱨᱟ ᱜᱷᱟᱹᱲᱤᱡ",
+       "underline-never": "ᱛᱤᱥ ᱦᱚᱸ ᱵᱟᱝ",
        "underline-default": "Browjarre cetlekate em hoy akana",
-       "editfont-style": "Sompadon ṭhại reaḱ fonṭ sṭayel:",
-       "editfont-monospace": "Monoespeć fonṭ",
-       "editfont-sansserif": "Sans-serif fon",
-       "editfont-serif": "Serif fon",
+       "editfont-style": "ᱥᱟᱯᱲᱟᱣ ᱡᱟᱜᱟ ᱨᱮᱱᱟᱜ ᱯᱷᱚᱱᱴ ᱮᱥᱴᱟᱭᱤᱞ:",
+       "editfont-monospace": "Monospaced font",
+       "editfont-sansserif": "Sans-serif font",
+       "editfont-serif": "Serif font",
        "sunday": "ᱥᱤᱸᱜᱮ ᱢᱟᱦᱟᱸ",
        "monday": "ᱚᱛᱮ ᱢᱟᱦᱟᱸ",
        "tuesday": "ᱵᱟᱞᱮ ᱢᱟᱦᱟᱸ",
        "december-date": "ᱰᱤᱥᱮᱢᱵᱚᱨ $1",
        "pagecategories": "{{PLURAL:$1|ᱛᱷᱚᱠ|ᱛᱷᱚᱠᱠᱩ}}",
        "category_header": "ᱛᱷᱚᱠ ᱨᱮᱱ ᱥᱟᱦᱴᱟᱞᱩ \"$1\"",
-       "subcategories": "Huḍiń rokom sokomko",
-       "category-media-header": "\"$1\" babot reaḱ rokom sokomte emen meḍiya rẽtko",
+       "subcategories": "ᱦᱩᱰᱤᱧ ᱛᱷᱚᱠᱠᱩ",
+       "category-media-header": "\"$1\" ᱵᱟᱵᱚᱛ ᱨᱮᱭᱟᱜ ᱢᱤᱰᱤᱭᱟ ᱛᱷᱚᱠ",
        "category-empty": "<em>ᱱᱚᱣᱟ ᱛᱷᱚᱠ ᱨᱮ ᱱᱮᱛᱚᱜ ᱩᱱᱩᱫᱩᱜ ᱥᱟᱦᱴᱟᱠᱚ ᱥᱮ ᱢᱤᱰᱤᱭᱟ ᱵᱟᱱᱩᱜ-ᱟ᱾</em>",
        "hidden-categories": "{{PLURAL:$1|ᱫᱟᱱᱟᱝ ᱛᱷᱚᱠ|ᱫᱟᱱᱟᱝ ᱛᱷᱚᱠᱠᱩ}}",
-       "hidden-category-category": "Uku akan rokom sokom ko",
-       "category-subcat-count": "{{PLURAL:$2| keṭagorire eken tayom hudińkeṭagori menaḱa. |Noa keṭagorire tayom menaḱa {{PLURAL:$1 hudińkeṭagoriko}}, jotokote $2}}",
-       "category-subcat-count-limited": "Noa rokom sokomre latar reaḱ {{PLURAL:$1 gan kạṭic rokom sokom $1gan kạtic rokom sokom menaḱa}}",
+       "hidden-category-category": "ᱩᱠᱩ ᱛᱷᱚᱠ ᱠᱩ",
+       "category-subcat-count": "{{PLURAL:$2| ᱱᱚᱣᱟ ᱛᱷᱚᱠ ᱨᱮ ᱱᱚᱣᱟᱠᱩ ᱦᱩᱰᱤᱧ ᱛᱷᱚᱠ ᱠᱩ ᱢᱮᱱᱟᱜ-ᱟ|ᱱᱚᱣᱟ ᱛᱷᱚᱠ ᱨᱮ ᱢᱮᱱᱟᱜ-ᱟ {{PLURAL:$1|ᱦᱩᱰᱤᱧ ᱛᱷᱚᱠᱠᱩ}}, ᱡᱚᱛᱚᱠᱚᱛᱮ $2}}",
+       "category-subcat-count-limited": "ᱱᱚᱣᱟ ᱛᱷᱚᱠ ᱨᱮᱫᱚ ᱱᱚᱣᱟᱠᱩ {{PLURAL:$1 ᱜᱟᱱ ᱦᱩᱰᱤᱧ ᱛᱷᱚᱠ $1 ᱜᱟᱱ ᱦᱩᱰᱤᱧ ᱛᱷᱚᱠᱠᱩ ᱢᱮᱱᱟᱜ-ᱟ}}",
        "category-article-count": "{{PLURAL:$2| ᱱᱚᱣᱟ ᱛᱷᱚᱠ ᱨᱮᱫᱚ ᱮᱠᱮᱱ ᱛᱟᱭᱚᱢᱛᱮᱱᱟᱜ ᱥᱟᱦᱴᱟ ᱢᱮᱱᱟᱜ-ᱟ᱾| ᱛᱟᱭᱚᱢ {{PLURAL:$2| ᱥᱟᱦᱴᱟ ᱫᱚ |$1 ᱥᱟᱦᱴᱟᱠᱚ ᱠᱟᱱᱟ}} ᱱᱤᱭᱟᱹ ᱛᱷᱚᱠᱨᱮ, ᱥᱟᱱᱟᱢᱠᱚᱛᱮ ᱦᱩᱭᱩᱜ ᱠᱟᱱᱟ $2 ᱾}}",
-       "category-article-count-limited": "Noa {{PLURAL:$1 sakam sakamko}} rokom sokomre menaḱa.",
+       "category-article-count-limited": "ᱱᱚᱣᱟ {{PLURAL:$1 ᱥᱟᱦᱴᱟ ᱥᱟᱦᱴᱟᱠᱩ}} ᱛᱷᱚᱠᱨᱮ ᱢᱮᱱᱟᱜ-ᱟ᱾",
        "category-file-count": "{{PLURAL:$2|ᱱᱚᱣᱟ ᱛᱷᱚᱠ ᱨᱮᱭᱟᱜ ᱩᱱᱩᱫᱩᱜ ᱫᱚ ᱮᱠᱮᱱ ᱯᱟᱸᱡᱟᱸ ᱨᱮᱫ ᱜᱮ᱾| ᱱᱚᱣᱟ ᱯᱟᱸᱡᱟᱸ \n{{PLURAL:$1|ᱨᱮᱫ ᱫᱚ|$1 ᱨᱮᱫ ᱫᱚᱠᱚ}} ᱱᱤᱭᱟᱹ ᱛᱷᱚᱠᱨᱮ $2 ᱡᱚᱛᱚᱜᱮ᱾}}",
-       "category-file-count-limited": "Latar reaḱ {{PLURAL:$1 rẽt rẽtko}} noa rokom sokomre menaḱa.",
-       "listingcontinuesabbrev": "Calaḱa",
+       "category-file-count-limited": "ᱱᱚᱣᱟ {{PLURAL:$1 ᱨᱮᱫ ᱨᱮᱫᱠᱩ }} ᱛᱷᱚᱠᱨᱮ ᱢᱮᱱᱟᱜ-ᱟ᱾",
+       "listingcontinuesabbrev": "ᱪᱟᱞᱟᱜ ᱠᱟᱱᱟ",
        "index-category": "ᱩᱱᱩᱫᱩᱜ-ᱟᱱ ᱥᱟᱦᱴᱟᱠᱚ",
        "noindex-category": "ᱩᱱᱩᱫᱩᱜ ᱵᱟᱹᱱᱩᱜ-ᱟᱱ ᱥᱟᱦᱴᱟᱠᱚ",
        "broken-file-category": "ᱨᱟᱹᱯᱩᱫ ᱨᱮᱫ ᱡᱚᱱᱚᱲᱠᱩ ᱥᱟᱞᱟᱫ ᱥᱟᱦᱴᱟᱠᱚ",
-       "about": "Lạgitte, Lạgti",
+       "about": "ᱞᱟᱹᱜᱤᱛ",
        "article": "ᱩᱱᱩᱫᱩᱜ ᱥᱟᱦᱴᱟ",
        "newwindow": "(ᱱᱟᱣᱟ ᱡᱟᱱᱞᱟᱨᱮ ᱡᱷᱤᱡ ᱢᱮ)",
        "cancel": "ᱵᱟᱫᱽ",
-       "moredotdotdot": "Aema",
+       "moredotdotdot": "ᱵᱟᱹᱲᱛᱤ...",
        "morenotlisted": "ᱱᱚᱣᱟ ᱛᱟᱹᱞᱠᱟᱹ ᱫᱚ ᱯᱟᱥᱮᱡ ᱟᱫᱷᱟ ᱜᱮᱭᱟ᱾",
        "mypage": "ᱥᱟᱦᱴᱟ",
        "mytalk": "ᱨᱚᱲ",
        "anontalk": "ᱨᱚᱲ",
        "navigation": "ᱟᱹᱪᱩᱨᱵᱟᱲᱟ",
        "and": "&#32;ᱟᱨ",
-       "faq": "Baḍae kupuliko",
-       "actions": "Kạmi",
+       "faq": "FAQ",
+       "actions": "ᱠᱟᱹᱢᱤᱠᱩ",
        "namespaces": "ᱧᱤᱛᱩᱢ ᱡᱟᱜᱟ",
        "variants": "ᱮᱴᱟᱜᱠᱳ",
        "navigation-heading": "ᱟᱪᱩᱨᱵᱟᱲᱟ ᱢᱮᱱᱩ",
-       "errorpagetitle": "vul",
-       "returnto": "$1 te ruar-rok' me",
+       "errorpagetitle": "ᱦᱩᱲᱟᱹᱜ",
+       "returnto": "$1 ᱛᱮ ᱨᱩᱭᱟᱹᱲᱚᱜ ᱢᱮ",
        "tagline": " {{SITENAME}} ᱠᱷᱚᱱ",
        "help": "ᱜᱚᱸᱲᱚᱸ",
        "search": "ᱥᱮᱸᱫᱽᱨᱟ",
        "searchbutton": "ᱥᱮᱸᱫᱽᱨᱟ",
-       "go": "Calaḱme",
+       "go": "ᱪᱟᱞᱟᱜ ᱢᱮ",
        "searcharticle": "ᱪᱟᱞᱟᱜ ᱢᱮ",
        "history": "ᱥᱟᱦᱴᱟ ᱱᱟᱜᱟᱢ",
        "history_short": "ᱱᱟᱜᱟᱢ",
        "history_small": "ᱱᱟᱜᱟᱢ",
-       "updatedmarker": "Ińaḱ mucạt hiripor khon nitaḱ halot",
+       "updatedmarker": "ᱤᱧᱟᱜ ᱢᱩᱪᱟᱹᱛ ᱦᱤᱨᱤᱯᱚᱨ ᱠᱷᱚᱱ ᱱᱤᱛᱚᱜ ᱦᱟᱞᱚᱛ",
        "printableversion": "ᱪᱷᱟᱯᱟ ᱜᱟᱱᱚᱜ ᱚᱰᱚᱫ",
        "permalink": "ᱛᱤᱨᱮᱡᱩᱜᱮ ᱡᱚᱱᱚᱲ",
-       "print": "Chapa",
+       "print": "ᱪᱷᱟᱯᱟ",
        "view": "ᱩᱰᱩᱜᱽᱢᱮ",
        "view-foreign": "$1 ᱨᱮ ᱧᱮᱞ ᱢᱮ",
        "edit": "ᱥᱟᱯᱲᱟᱣ",
        "create": "ᱛᱮᱭᱟᱨ",
        "create-local": "ᱢᱮᱥᱟᱭᱢᱮ ᱠᱟᱛᱷᱟ ᱠᱚ",
        "delete": "ᱜᱮᱫ ᱜᱤᱰᱤ",
-       "undelete_short": "Baṅ getgiḍilena {{PLURAL:$1 1ṭen joṛao $ joṛaoko}}",
-       "viewdeleted_short": "{{PLURAL:$1 gan ocoḱ sompadok $1 gan ocoḱ sompadon}} udugmẽ",
-       "protect": "banchao'",
-       "protect_change": "Judạ",
-       "unprotect": "Bodol ban̕cao",
+       "undelete_short": "ᱵᱟᱝ ᱜᱮᱫ ᱜᱤᱰᱤᱞᱮᱱᱟ {{PLURAL:$1 ᱢᱤᱫᱴᱮᱱ ᱡᱚᱱᱚᱲ $1 ᱡᱚᱱᱚᱲᱠᱩ}}",
+       "viewdeleted_short": "{{PLURAL:$1 ᱜᱟᱱ ᱜᱮᱫ ᱜᱤᱰᱤ ᱟᱠᱟᱱ ᱥᱟᱯᱲᱟᱣᱠᱩ $1 ᱜᱟᱱ ᱜᱮᱫ ᱜᱤᱰᱤ ᱟᱠᱟᱱ ᱥᱟᱯᱲᱟᱣᱠᱩ}} ᱩᱫᱩᱜᱽᱢᱮ",
+       "protect": "ᱨᱩᱠᱷᱤᱭᱟᱹ",
+       "protect_change": "ᱵᱚᱫᱚᱞ",
+       "unprotect": "ᱵᱚᱫᱚᱞ ᱨᱩᱠᱷᱤᱭᱟᱹ",
        "newpage": "ᱱᱟᱶᱟ ᱥᱟᱦᱴᱟ",
        "talkpagelinktext": "ᱨᱚᱲ",
        "specialpage": "ᱵᱤᱥᱮᱥ ᱥᱟᱦᱴᱟ",
        "mediawikipage": "ᱠᱷᱚᱵᱚᱨ ᱥᱟᱦᱴᱟ ᱧᱮᱞᱢᱮ",
        "templatepage": "ᱪᱷᱟᱸᱪ ᱥᱟᱦᱴᱟ ᱩᱫᱩᱜᱽ ᱢᱮ",
        "viewhelppage": "ᱜᱚᱸᱲᱚ ᱥᱟᱦᱴᱟ ᱧᱮᱞᱢᱮ",
-       "categorypage": "Babot reaḱ rokom sokom udugmẽ",
-       "viewtalkpage": "Galmarao ńelme",
+       "categorypage": "ᱛᱷᱚᱠ ᱥᱟᱦᱴᱟ ᱧᱮᱞᱢᱮ",
+       "viewtalkpage": "ᱜᱟᱞᱢᱟᱨᱟᱣ ᱧᱮᱞᱢᱮ",
        "otherlanguages": "ᱮᱴᱟᱜ ᱯᱟᱹᱨᱥᱤ ᱛᱮ",
        "redirectedfrom": "($1 ᱠᱷᱚᱱ ᱟᱹᱪᱩᱨ ᱦᱮᱡᱠᱟᱱᱟ)",
        "redirectpagesub": "ᱵᱟᱝ ᱥᱚᱡᱽᱦᱮ ᱥᱟᱦᱴᱟ",
-       "redirectto": "Ar hõ udugoḱakana:",
+       "redirectto": "ᱪᱟᱞᱟᱜ ᱠᱟᱱᱟ:",
        "lastmodifiedat": "ᱱᱚᱶᱟ ᱥᱟᱦᱴᱟ ᱢᱩᱪᱟᱹᱫ ᱫᱷᱟᱣ ᱵᱚᱫᱚᱞ ᱟᱠᱟᱱᱟ  $1 ᱢᱟᱹᱦᱤᱛ,  $2 ᱚᱠᱛᱚᱨᱮ",
-       "viewcount": "Noa sakamdo {{PLURAL:$1 dhom $1 dhom}} udug hoena.",
-       "protectedpage": "Rukhíạ sakamko",
+       "viewcount": "ᱱᱚᱣᱟ ᱥᱟᱦᱴᱟ ᱫᱚ {{PLURAL:$1 ᱫᱷᱟᱶ $1 ᱫᱷᱟᱶ}} ᱩᱫᱩᱜ ᱦᱩᱭᱮᱱᱟ᱾",
+       "protectedpage": "ᱨᱩᱠᱷᱤᱭᱟᱹ ᱥᱟᱦᱴᱟ",
        "jumpto": "ᱫᱚᱱᱢᱮ :",
        "jumptonavigation": "ᱟᱹᱪᱩᱨᱵᱟᱲᱟ",
        "jumptosearch": "ᱥᱮᱸᱫᱽᱨᱟ",
        "view-pool-error": "Ikạkańmẽ, sarvarre nitoḱ do aḍi cap menaḱa.\nẠḍi aema beoharko noa sakam ńel lạgit́ko kurumuṭueda.\nNãwate noa sakam ńel kurumuṭuy lạgit́te dayakate mit́ghạṛi tạṅgiymẽ.\n$1",
        "pool-timeout": "Somoy paromena cạbi lạgit́te tạṅgi hoyoḱkana",
        "pool-queuefull": "Pool queue is full",
-       "pool-errorunknown": "Bań baḍayaḱ bhul",
+       "pool-errorunknown": "ᱵᱟᱝ ᱵᱟᱰᱟᱭ ᱦᱩᱲᱟᱹᱜ",
        "aboutsite": "ᱵᱟᱵᱚᱛ {{SITENAME}}",
        "aboutpage": "Project: ᱵᱟᱵᱚᱛ",
        "copyright": "ᱩᱱᱩᱫᱩᱜ ᱫᱚ ᱧᱟᱢᱚᱜ-ᱟ $1 ᱞᱮᱠᱟᱛᱮ  ᱵᱟᱝᱠᱷᱟᱱ ᱚᱞ ᱛᱟᱦᱮᱱᱟ",
        "disclaimers": "ᱫᱟᱹᱵᱤ ᱵᱟᱱᱩᱜᱠᱳ",
        "disclaimerpage": "Project: ᱥᱟᱫᱷᱟᱨᱚᱱ ᱫᱟᱹᱵᱤ ᱵᱟᱱᱩᱜᱠᱩ",
        "edithelp": "ᱥᱟᱯᱲᱟᱣ ᱜᱚᱸᱲᱚᱸ",
-       "helppage-top-gethelp": "á±\9cá±\9aá±²á±\9a",
+       "helppage-top-gethelp": "á±\9cá±\9aᱸᱲá±\9aᱸ",
        "mainpage": "ᱢᱩᱬᱩᱛ ᱥᱟᱦᱴᱟ",
        "mainpage-description": "ᱢᱩᱬᱩᱛ ᱥᱟᱦᱴᱟ",
-       "policy-url": "Project:Ritiniti",
+       "policy-url": "Project:ᱨᱤᱛᱤᱱᱤᱛᱤ",
        "portal": "ᱜᱩᱥᱴᱤ ᱵᱚᱞᱚᱜ ᱫᱩᱭᱟᱹᱨ",
        "portal-url": "Project:ᱠᱷᱩᱴ ᱵᱚᱞᱚᱱ ᱦᱚᱨ",
        "privacy": "ᱩᱠᱩ ᱮᱠᱛᱤᱭᱟᱨ",
        "privacypage": "Project: ᱩᱠᱩ ᱮᱠᱛᱤᱭᱟᱨ",
-       "badaccess": "Ektiạr vul",
+       "badaccess": "ᱟᱹᱭᱫᱟᱹᱨᱤ ᱦᱩᱲᱟᱹᱜ",
        "badaccess-group0": "Am do oka kạmi lạgit́em aroj akat́, ona kạmi purạo lạgit́te ạidạri do bạnuḱa.",
        "badaccess-groups": "Am do oka kạmim menjoṅkan ona do khạli {{PLURAL:$2 rạsiạkore noa rạsiạreaḱ mit́ṭenre}} mitṭen beoharić sompadon daṛeyaḱa: $1.",
        "versionrequired": "ᱢᱤᱰᱤᱭᱟ ᱩᱭᱠᱤ ᱨᱮᱭᱟᱜ $1 ᱱᱟᱣᱟ ᱵᱷᱟᱨᱥᱚᱱ ᱡᱟᱹᱨᱩᱲᱟ",
        "versionrequiredtext": "Version $1 of MediaWiki is required to use this page.\nSee [[Special:Version|version page]].",
-       "ok": "Ṭhik gea",
+       "ok": "ᱴᱷᱤᱠ á±\9cᱮᱭá±\9f",
        "retrievedfrom": "\"$1\" ᱠᱷᱚᱱ ᱧᱟᱢ ᱟᱹᱜᱩᱭ",
-       "youhavenewmessages": "Amaḱ do $1 ($2) menaḱa",
+       "youhavenewmessages": "{{PLURAL:$3|ᱟᱢᱟᱜ ᱢᱮᱱᱟᱜ-ᱟ}} $1 ($2)᱾",
        "youhavenewmessagesfromusers": "{{PLURAL:$4|ᱟᱢ ᱫᱚ}} $1 ᱠᱷᱚᱱ {{PLURAL:$3|ᱟᱨᱢᱤᱫ ᱵᱮᱵᱷᱟᱨᱤᱭᱟᱹ|$3 ᱵᱷᱮᱵᱷᱟᱨᱩᱭᱟᱹ}} ($2) ᱾",
        "newmessageslinkplural": "{{PLURAL:$1|ᱢᱤᱫ ᱱᱟᱶᱟ ᱢᱮᱥᱮᱡᱽ|999=ᱱᱟᱶᱟ ᱢᱮᱥᱮᱡᱽᱠᱚ}}",
        "newmessagesdifflinkplural": "ᱢᱩᱪᱟᱹᱫ {{PLURAL:$1|ᱵᱚᱫᱚᱞ|999=ᱵᱚᱫᱚᱞᱠᱚ}}",
        "youhavenewmessagesmulti": "ᱟᱢᱟᱜ ᱱᱟᱣᱟ ᱠᱷᱚᱵᱟᱨᱠᱚ ᱫᱚ $1 ᱨᱮ ᱢᱮᱱᱟᱜ-ᱟ",
        "editsection": "ᱥᱟᱯᱲᱟᱣ",
        "editold": "ᱥᱟᱯᱲᱟᱣ",
-       "viewsourceold": "Ńamoḱ jayga",
+       "viewsourceold": "ᱯᱷᱮᱰᱟᱛ ᱧᱮᱞ",
        "editlink": "ᱥᱟᱯᱲᱟᱣ",
        "viewsourcelink": "ᱯᱷᱮᱰᱟᱛ ᱦᱚᱨ ᱧᱮᱞᱢᱮ",
        "editsectionhint": "ᱥᱟᱯᱲᱟᱣ ᱡᱟᱭᱜᱟ: $1",
        "toc": "ᱩᱱᱩᱫᱩᱜ",
-       "showtoc": "Uduḱme",
-       "hidetoc": "uku, Danaṅ",
-       "collapsible-collapse": "Murchạo caba",
-       "collapsible-expand": "Phaylao",
+       "showtoc": "ᱥᱚᱫᱚᱨ",
+       "hidetoc": "ᱫᱟᱱᱟᱝ",
+       "collapsible-collapse": "ᱢᱩᱨᱪᱷᱟᱹᱣ ᱪᱟᱵᱟ",
+       "collapsible-expand": "ᱯᱷᱟᱭᱞᱟᱣ",
        "confirmable-yes": "ᱦᱮᱸ",
        "confirmable-no": "ᱵᱟᱝ",
        "thisisdeleted": "ᱧᱮᱞ ᱥᱮ ᱨᱩᱭᱟᱹᱲ ᱫᱚᱲᱦᱟ $1?",
-       "viewdeleted": "$1 Ńelme",
+       "viewdeleted": "$1 ᱧᱮᱞᱢᱮ",
        "restorelink": "{{PLURAL:$1 mit́ṭen ocoḱgiḍi sompadon $1 gan udug giḍi sompadon}}",
-       "feedlinks": "Jom oco",
+       "feedlinks": "ᱟᱡᱚ:",
        "feed-invalid": "Garhak feed reaḱ rokom do ạnlekate baṅkana",
-       "feed-unavailable": "Sinḍikason feed do baṅ ńamoḱkana",
+       "feed-unavailable": "ᱥᱤᱱᱰᱤᱠᱮᱥᱚᱱ ᱟᱡᱚ ᱠᱩᱫᱚ ᱵᱟᱝ ᱧᱟᱢᱚᱜ ᱠᱟᱱᱟ",
        "site-rss-feed": "$1 RSS feed",
        "site-atom-feed": " $1 ᱡᱚᱢ ᱚᱪᱚ",
        "page-rss-feed": "\"$1\" RSS feed",
-       "page-atom-feed": "\"$1\" khon khudri jom",
+       "page-atom-feed": " $1 ᱡᱚᱢ ᱚᱪᱚ",
        "red-link-title": "$1 (ᱱᱤᱭᱟᱹ ᱥᱟᱦᱴᱟ ᱫᱚ ᱵᱟᱹᱱᱩᱜ-ᱟ)",
        "sort-descending": "Ulṭạo horop lekate sajao",
-       "sort-ascending": "Horop lekate sajao",
+       "sort-ascending": "ᱥᱟᱢᱴᱟᱣ ᱛᱮ",
        "nstab-main": "ᱥᱟᱦᱴᱟ",
        "nstab-user": "ᱵᱮᱵᱦᱟᱹᱨᱤᱭᱟᱹᱜ ᱥᱟᱦᱴᱟ",
        "nstab-media": "Media ᱥᱟᱦᱴᱟ",
        "nstab-special": "ᱚᱥᱚᱠᱟᱭᱛᱮᱭᱟᱜ ᱥᱟᱦᱴᱟ",
        "nstab-project": "ᱯᱨᱚᱡᱮᱠᱴ ᱥᱟᱦᱴᱟ",
        "nstab-image": "ᱨᱮᱫ",
-       "nstab-mediawiki": "Mesag",
+       "nstab-mediawiki": "ᱠᱷᱚᱵᱚᱨ",
        "nstab-template": "ᱪᱷᱟᱸᱪ",
        "nstab-help": "ᱜᱚᱸᱲᱚ ᱥᱟᱦᱴᱟ",
        "nstab-category": "ᱛᱷᱚᱠ",
        "mainpage-nstab": "ᱢᱩᱬᱩᱛ ᱥᱟᱦᱴᱟ",
-       "nosuchaction": "Noṅkanaḱ kạmi bạnuḱa",
+       "nosuchaction": "ᱱᱚᱝᱠᱟᱱ ᱠᱟᱹᱢᱤ ᱵᱟᱹᱱᱩᱜ-ᱟ",
        "nosuchactiontext": "Noa URL re goṭa akan kạmi do ạnlekate baṅkana.\nAm do paseć mit́ṭen vul joṛaoem emakada se URL oltem vul akada.\nNoa do noṅkanaḱ menkana je {{SITENAME}} sayeṭre beoharen sofṭower re mit́ṭen vul menaḱa.",
        "nosuchspecialpage": "ᱱᱚᱝᱠᱟᱱᱟ ᱟᱥᱚᱠᱟᱭ ᱥᱟᱦᱴᱟ ᱫᱚ ᱵᱟᱹᱱᱩᱜ-ᱟ",
        "nospecialpagetext": "<strong>ᱟᱢ ᱫᱚ ᱡᱟᱦᱟᱸ ᱥᱟᱦᱴᱟ ᱞᱟᱹᱜᱤᱫ ᱮᱢ ᱱᱮᱦᱚᱨ ᱟᱠᱟᱫᱟ ᱚᱱᱟᱫᱚ ᱵᱟᱹᱱᱩᱜ-ᱟ </strong>\nᱡᱟᱦᱟᱸ ᱥᱟᱦᱴᱟᱠᱩ ᱱᱚᱸᱰᱮ ᱢᱮᱱᱟᱜ-ᱟ ᱚᱱᱟᱨᱮᱱᱟᱜ ᱛᱟᱹᱞᱠᱟᱹ ᱱᱚᱸᱰᱮᱢ ᱧᱟᱢᱟ [[Special:SpecialPages|{{int:specialpages}}]]᱾",
-       "error": "bhul",
-       "databaseerror": "á¸\8caá¹­abase vul",
+       "error": "ᱦᱩᱲᱟᱹᱜ",
+       "databaseerror": "á±°á±\9fá±´á±\9fᱵᱮᱡᱽ á±¦á±©á±²á±\9fá±¹á±\9c",
        "databaseerror-error": "ᱦᱩᱲᱟᱹᱜ: $1",
        "laggedslavemode": "'''Sontoroḱme:''' sakamre do nahaḱ nãwãnaḱko paseć bạnuḱa.",
-       "readonly": "á¸\8caá¹­abes do talagea",
+       "readonly": "á±°á±\9fá±´á±\9fᱵᱮᱡᱽ á±\9bá±\9fá±\9eá±\9fá±\9cᱮᱭá±\9f",
        "enterlockreason": "Cạbie reaḱ karon do cet́kana ma lạimẽ, Saõte tinre tala cạbim jhija ona okte hõ lạimẽ",
        "readonlytext": "Nãwã hataen ar eṭagaḱ sompadon lạgit́te ḍaṭabes do nit bondo gea. Paseć ḍaṭabes rukhiyạre niyom lekate kạmi calaḱ kana. Thoṛa ghạrịić porte laha obosthare acur hạjuḱa.\nSasetić do noa kathae roṛ keda: $1",
        "missing-article": "\"$1\" $2 noa ńutumanaḱ sakhiyạ̣t sakamre olakanaḱ do bań ṅamoka.\nNoa hoy renaḱ karon do hoyoḱkana cabak tạrik pharak se noare joṛao sakam do get́ giḍi akana.\nJudi noa do karon bań hoylen khan, noa do am sopṭoyer re kạtićtem ńam daṛeyaḱa.\nDaya katet́ noa do nonde [[Special:ListUsers/sysop|administrator]],  ṭhen lạime, URL hotete.",
        "missingarticle-rev": "(ᱥᱩᱫᱷᱨᱟᱹᱣ#:$1)",
-       "missingarticle-diff": "(Pharak: $1, $2)",
+       "missingarticle-diff": "(ᱯᱷᱟᱨᱟᱠ: $1, $2)",
        "readonly_lag": "Ḍaṭabes do aćhote tege bondo hoe akana, je lekate udhin reaḱ ḍaṭabes sarvarkor mukhiạ ḍaṭabes sarvar lekate heć daṛeaḱ.",
-       "internalerror": "Bhitri reaḱ bhul",
-       "internalerror_info": "Bhitri reaḱ vul: $1",
-       "filecopyerror": "\"$1\" rẽt khon \"$2\" rẽt baṅ kopilena.",
-       "filerenameerror": "\"$1\" rẽt reaḱ ńutum bodol kate \"$2\" em baṅ hoyoḱ kana.",
+       "internalerror": "ᱵᱷᱤᱛᱨᱤ ᱦᱩᱲᱟᱹᱜ",
+       "internalerror_info": "ᱵᱷᱤᱛᱨᱤ ᱦᱩᱲᱟᱹᱜ: $1",
+       "filecopyerror": "\"$1\" ᱨᱮᱫ ᱠᱷᱚᱱ \"$2\" ᱨᱮᱫ ᱵᱟᱝ ᱠᱚᱯᱤᱞᱮᱱᱟ᱾",
+       "filerenameerror": "\"$1\" ᱨᱮᱫ ᱨᱮᱭᱟᱜ ᱧᱩᱛᱩᱢ ᱵᱚᱫᱚᱞ ᱠᱟᱛᱮ \"$2\" ᱮᱢ ᱵᱟᱝ ᱜᱟᱱᱚᱜ ᱠᱟᱱᱟ᱾",
        "filedeleteerror": "$1 ᱨᱮᱫ ᱫᱚ ᱵᱟᱝ ᱜᱮᱫᱽ ᱜᱤᱰᱤ ᱞᱮᱱᱟ",
-       "directorycreateerror": "\"$1\" dayrekṭori do baṅ tearlena.",
-       "filenotfound": "\"$1\" rẽt do baṅ sendra ńamoḱ kana.",
-       "unexpected": "Baṅ asakan mạn: \"$1\"=\"$2\".",
-       "formerror": "Vul: forom do baṅ jimạlena.",
-       "badarticleerror": "Noa sakamre kạmiko do baṅ puraolena.",
+       "directorycreateerror": "\"$1\" ᱰᱟᱭᱨᱮᱠᱴᱳᱨᱤ ᱫᱚ ᱵᱟᱝ ᱛᱮᱭᱟᱨᱞᱮᱱᱟ᱾",
+       "filenotfound": "\"$1\" ᱨᱮᱫ ᱫᱚ ᱵᱟᱝ ᱧᱟᱢᱞᱮᱱᱟ᱾",
+       "unexpected": "ᱵᱟᱝ ᱟᱥᱟᱜ ᱠᱟᱱ ᱢᱟᱹᱱ: \"$1\"=\"$2\".",
+       "formerror": "ᱦᱩᱲᱟᱹᱜ: ᱯᱷᱚᱨᱚᱢ ᱫᱚ ᱵᱟᱝ ᱡᱤᱢᱟᱹᱞᱮᱱᱟ᱾",
+       "badarticleerror": "ᱱᱚᱣᱟ ᱠᱟᱹᱢᱤ ᱫᱚ ᱱᱚᱣᱟ ᱥᱟᱦᱴᱟᱨᱮ ᱵᱟᱝ ᱦᱩᱭᱞᱮᱱᱟ᱾",
        "cannotdelete": "$1 sakam se rẽt do baṅ get giḍilena.\nPasec eṭaḱ hoṛ noa do lahareko get giḍi akada.",
-       "cannotdelete-title": "\"$1\" Sakam do baṅ get giḍiḱkana",
-       "badtitle": "barich' bishó́́́́y",
+       "cannotdelete-title": "\"$1\" ᱥᱟᱦᱴᱟ ᱫᱚ ᱵᱟᱝ ᱜᱮᱫ ᱜᱤᱰᱤ ᱟᱠᱟᱱᱟ",
+       "badtitle": "ᱵᱟᱹᱨᱤᱡ ᱴᱟᱭᱴᱮᱞ",
        "badtitletext": "ᱟᱢᱮᱢ ᱱᱮᱦᱚᱨᱟᱠᱟᱱ ᱥᱟᱦᱴᱟ ᱧᱤᱛᱩᱢ ᱫᱚ ᱵᱟᱝ ᱴᱷᱤᱠᱟ, ᱠᱷᱟᱹᱞᱤ ᱥᱮ ᱵᱷᱩᱞᱜᱮ ᱵᱷᱤᱛᱨᱤ ᱯᱟᱹᱨᱥᱤᱛᱮ ᱥᱮ ᱩᱭᱠᱤ ᱴᱟᱭᱴᱮᱞ ᱛᱮ ᱡᱚᱱᱚᱲ ᱜᱮᱭᱟ᱾\nᱱᱚᱣᱟᱨᱮ ᱫᱚ ᱢᱤᱫ ᱥᱮ ᱟᱭᱢᱟ ᱩᱱᱩᱫᱩᱜ ᱢᱮᱱᱟᱜ ᱚᱠᱟ ᱫᱚ ᱧᱤᱛᱩᱢᱨᱮ ᱵᱟᱝ ᱵᱮᱵᱦᱟᱨᱚᱜ᱾",
        "querypage-no-updates": "Noa sakam reaḱ nahaḱ halot bondo gea. Nonḍe doho akana ḍaṭako do baṅ saphaḱa.",
        "viewsource": "ᱯᱷᱮᱰᱟᱛ ᱧᱮᱞ",
-       "viewsource-title": "$1 renaḱ ńamoḱ ṭhại ńelmẽ",
-       "actionthrottled": "Kạmi reaḱ dhara bại",
+       "viewsource-title": "$1 ᱨᱮᱱᱟᱜ ᱯᱷᱮᱰᱟᱛ ᱧᱮᱞᱢᱮ",
+       "actionthrottled": "ᱠᱟᱹᱢᱤ ᱨᱮᱭᱟᱜ ᱫᱷᱟᱨᱟ ᱵᱟᱹᱭ",
        "protectedpagetext": "Noa sakam do ol toṅge lạgit́te do bańcao gea.",
        "viewsourcetext": "ᱱᱚᱭᱟ ᱥᱟᱦᱴᱟᱢ ᱧᱮᱞ ᱫᱟᱲᱮᱭᱟᱜ-ᱟ ᱟᱨᱮᱢ ᱠᱚᱯᱤ ᱫᱟᱲᱮᱭᱟᱜ-ᱟ᱾",
        "viewyourtext": "Am do '''Amaḱ sompadon''' noa sakam ńel arem kopi hatao daṛeaḱa:",
        "virus-unknownscanner": "Baṅ urum anṭvayras:",
        "welcomeuser": "ᱥᱟᱹᱜᱩᱱ ᱫᱟᱨᱟᱢ, $1!",
        "welcomecreation-msg": "Amaḱ ekaunṭ do̠ jhićena. Amaḱ pạsindko bodol alom hiṛińa.",
-       "yourname": "Beoboharicaḱ ńutum",
+       "yourname": "ᱵᱮᱵᱷᱟᱨᱤᱭᱟᱹ ᱧᱩᱛᱩᱢ:",
        "userlogin-yourname": "ᱵᱮᱵᱷᱟᱨᱤᱭᱟᱹ ᱧᱩᱛᱩᱢ",
-       "userlogin-yourname-ph": "á±\9fá±¢á±\9fá±\9c á±µá±®á±µá±¦á±\9fᱨᱤᱡ ᱧᱤᱛᱩᱢ ᱵᱚᱞᱚᱭ ᱢᱮ",
-       "createacct-another-username-ph": "ᱵᱮᱵᱦᱟᱨᱤᱭᱟᱜ ᱧᱤᱛᱩᱢ ᱵᱚᱞᱚᱭ ᱢᱮ",
+       "userlogin-yourname-ph": "á±\9fá±¢á±\9fá±\9c á±µá±®á±µá±¦á±\9fᱨᱤᱭá±\9fá±¹ ᱧᱤᱛᱩᱢ ᱵᱚᱞᱚᱭ ᱢᱮ",
+       "createacct-another-username-ph": "ᱵᱮᱵᱷᱟᱨᱤᱭᱟᱜ ᱧᱤᱛᱩᱢ ᱵᱚᱞᱚᱭ ᱢᱮ",
        "yourpassword": "ᱫᱟᱱᱟᱝ ᱥᱟᱵᱟᱫᱽ",
        "userlogin-yourpassword": "ᱫᱟᱱᱟᱝ ᱥᱟᱵᱟᱫᱽ",
        "userlogin-yourpassword-ph": "ᱫᱟᱱᱟᱝ ᱥᱟᱵᱟᱫᱽ ᱟᱫᱮᱨᱢᱮ",
        "createacct-yourpassword-ph": "ᱫᱟᱱᱟᱝ ᱥᱟᱵᱟᱫᱽ ᱟᱫᱮᱨᱢᱮ",
-       "yourpasswordagain": "Arhõ oku namber olme",
+       "yourpasswordagain": "ᱫᱚᱲᱦᱟᱛᱮ ᱫᱟᱱᱟᱝ ᱥᱟᱵᱟᱫᱽ ᱚᱞᱢᱮ",
        "createacct-yourpasswordagain": "ᱫᱟᱱᱟᱝ ᱥᱟᱵᱟᱫᱽ ᱴᱷᱟᱹᱣᱠᱟᱹᱭ ᱢᱮ",
        "createacct-yourpasswordagain-ph": "ᱫᱟᱱᱟᱝ ᱥᱟᱵᱟᱫᱽ ᱟᱫᱮᱨᱢᱮ ᱫᱚᱲᱦᱟ",
-       "userlogin-remembermypassword": "Bolo thirege dohokạńme",
-       "yourdomainname": "Amaḱ ḍomen:",
+       "userlogin-remembermypassword": "ᱵᱚᱞᱚ ᱛᱷᱤᱨᱜᱮ ᱫᱚᱦᱚᱠᱟᱹᱧᱢᱮ",
+       "yourdomainname": "ᱟᱢᱟᱜ ᱧᱩᱛᱩᱢ:",
        "externaldberror": "Hoe daṛeyaḱa jahan bahre reaḱ jacaeaḱ ḍaṭabes vul hoeakana se amaḱ bahre reaḱ ekaunṭ do nahaḱ halot aguire ạidạri bạnuḱa.",
        "login": "ᱵᱚᱞᱚᱜ ᱢᱮ",
-       "nav-login-createaccount": "Boloḱ́ duạr / ekaunt tearme",
-       "logout": "Bahre oḍoń",
-       "userlogout": "Bahre oḍoń",
-       "notloggedin": "Bhitri baṅ bolokana",
+       "nav-login-createaccount": "ᱵᱚᱞᱚᱜ ᱫᱩᱣᱟᱹᱨ / ᱦᱤᱥᱟᱹᱵ ᱠᱷᱟᱛᱟ ᱛᱮᱭᱟᱨᱢᱮ",
+       "logout": "ᱚᱰᱚᱠᱚᱜ ᱢᱮ",
+       "userlogout": "ᱚᱰᱚᱠᱚᱜ ᱢᱮ",
+       "notloggedin": "ᱵᱟᱢ ᱵᱚᱞᱚ ᱟᱠᱟᱱᱟ",
        "userlogin-noaccount": "ᱠᱷᱟᱛᱟ ᱵᱟᱹᱱᱩᱜ ᱛᱟᱢᱟ?",
        "userlogin-joinproject": "ᱠᱷᱚᱸᱡᱟ {{SITENAME}}",
-       "createaccount": "Ṭhai benaome",
+       "createaccount": "á± á±·á±\9fá±\9bá±\9f á±\9bᱮᱭá±\9fᱨᱢᱮ",
        "userlogin-resetpassword-link": "ᱟᱢᱟᱜ ᱫᱟᱱᱟᱝ ᱥᱟᱵᱟᱫᱽ ᱦᱤᱲᱤᱧ ᱠᱮᱫᱟᱢ?",
-       "userlogin-helplink2": "Bolon khạtir go̠ṛo̠",
+       "userlogin-helplink2": "ᱵᱚᱞᱚᱜ ᱠᱷᱟᱹᱛᱤᱨ ᱜᱚᱸᱲᱚᱸ",
        "userlogin-createanother": "ᱮᱴᱟᱜ ᱠᱷᱟᱛᱟ ᱛᱮᱭᱟᱨᱢᱮ",
        "createacct-emailrequired": "ᱤᱢᱮᱞ ᱵᱩᱴᱟᱹ",
        "createacct-emailoptional": "Email ᱴᱷᱤᱠᱱᱟ (ᱵᱟᱹᱲᱛᱤᱛᱮ)",
        "createacct-benefit-body3": "ᱱᱮᱛᱟᱨ {{PLURAL:$1|ᱮᱱᱮᱢᱤᱭᱟᱹ|ᱮᱱᱮᱢᱤᱭᱟᱹᱠᱚ}}",
        "badretype": "Am do okaṭaḱ oku nambarkom em keda ona do baṅ milạolena.",
        "userexists": "Laṛcaṛicaḱ ńutum em hoyena ona do beohar hoyakana.\nDayakatet́ eṭagaḱ ńutum bachaome.",
-       "loginerror": "Bhitri bolok do vulgea",
+       "loginerror": "ᱵᱷᱤᱛᱨᱤ ᱵᱚᱞᱚᱜ ᱦᱩᱲᱟᱹᱜ",
        "createacct-error": "ᱠᱷᱟᱛᱟ ᱛᱮᱭᱟᱨ ᱦᱩᱲᱟᱹᱜ",
-       "createaccounterror": "Ekaunṭ do baṅ tear lena: $1",
+       "createaccounterror": "ᱦᱤᱥᱟᱹᱵ ᱠᱷᱟᱛᱟ ᱵᱟᱝ ᱛᱮᱭᱟᱨᱠᱟᱱᱟ: $1",
        "nocookiesnew": "ᱵᱮᱵᱦᱟᱨᱤᱭᱟᱜ ᱦᱤᱥᱟᱹᱵ ᱛᱮᱭᱟᱨ ᱦᱩᱭ ᱞᱮᱱᱟ, ᱢᱮᱱᱠᱷᱟᱱ ᱟᱢᱫᱚ ᱱᱤᱛ ᱫᱷᱟᱹᱵᱤᱡ ᱵᱟᱢ ᱵᱚᱞᱚᱣᱠᱟᱱᱟ᱾ {{SITENAME}} ᱨᱮ ᱠᱩᱠᱤᱠᱚ ᱵᱮᱵᱦᱟᱨ ᱠᱟᱛᱮ ᱵᱮᱵᱦᱟᱨᱤᱭᱟᱜ ᱦᱤᱥᱟᱹᱵᱨᱮ ᱵᱚᱞᱚᱣᱟ᱾\nᱟᱢᱟᱜ ᱠᱩᱠᱤᱠᱚ ᱵᱚᱸᱫᱚ ᱦᱩᱭᱠᱟᱱᱟ᱾\nᱫᱟᱭᱟᱠᱟᱛᱮ ᱠᱩᱠᱤᱠᱚ ᱡᱷᱤᱡ ᱢᱮ, ᱚᱱᱟ ᱛᱟᱭᱚᱢ ᱟᱢᱟᱜ ᱱᱟᱣᱟ ᱵᱮᱵᱦᱟᱨᱤᱡ ᱧᱤᱛᱩᱢ ᱟᱨ ᱩᱠᱩ ᱮᱞᱥᱚᱝ ᱵᱮᱵᱦᱟᱨ ᱠᱟᱛᱮᱡ ᱱᱚᱣᱟ ᱦᱤᱥᱟᱹᱵᱨᱮ ᱵᱚᱞᱚᱜᱢᱮ᱾",
        "nocookieslogin": "{{SITENAME}} re kuki hotete beoharićaḱ bhitri boloḱ do hoyoḱa. Amaḱ sendrare kuki bondo menaḱa. Kuki cạlu kate arhõ kurumuṭuimẽ.",
        "nocookiesfornew": "Beoharićaḱ ekaunṭ do baṅ tear akana, Cedaḱ je noa ńamoḱ jaega babote ale do bale uruma.\nAle do baḍae ocolem amaḱ kuki doe kạmikana, sakam do arhõ rakaṕ lạgit́te kurumuṭuemẽ.",
        "passwordtooshort": "Uku nambar do {{PLURAL:$1 1 horop reaḱ $1 horop reaḱ}} mudre hoyoḱ jạruṛa.",
        "password-name-match": "Amaḱ oku nambar do amaḱ ńutum khon eṭaḱ hoyoḱ jạruṛtama.",
        "password-login-forbidden": "Noa laṛcaṛicaḱ ńutum ar oku nambar do ạnlekate baṅkana.",
-       "mailmypassword": "á±±á±\9fá±£á±\9fá±\9bá±® á±©á± á±© á±®á±\9eá±¥á±\9aá±\9d á±®á±¢á±¢á±®",
-       "passwordremindertitle": "á±±á±\9fá±£á±\9f á±±á±¤á±\9b á±\9eá±\9fá±¹á±\9cᱤá±\9b á±©á± á±© á±®á±\9eá±¥á±\9aá±\9d {{SITENAME}} ᱞᱟᱹᱜᱤᱛ ᱛᱮ",
+       "mailmypassword": "á±±á±\9fá±£á±\9fá±\9bá±® á±«á±\9fá±±á±\9fá±\9d á±¥á±\9fá±µá±\9fᱫᱽ á±®á±¢",
+       "passwordremindertitle": "á±±á±\9fá±£á±\9f á±±á±¤á±\9b á±\9eá±\9fá±¹á±\9cᱤá±\9b á±«á±\9fá±±á±\9fá±\9d á±¥á±\9fá±µá±\9fᱫᱽ {{SITENAME}} ᱞᱟᱹᱜᱤᱛ ᱛᱮ",
        "noemail": "\"$1\" beoharić lạgit́te do jahan e-mail ṭhikana rukhiyạ doho bạnuḱa.",
        "noemailcreate": "Am do mitṭen jewet e-mail ṭhikạna em jaruṛ menaḱtama.",
        "passwordsent": "\"$1\" ṭhikạnate resṭariyen e-mail lạgit́te mitṭen oku nambar em hoyena.\nDaya kate ńam porte arhõ bhitri boloḱme.",
        "invalidemailaddress": "Noa e-mail ṭhikạna do baṅ hataoa, karon noa formeṭ do pusṭạote baṅ em akana. Dayakate pusṭao formeṭte ṭhikạna emmẽ, se khet do khạliemẽ.",
        "cannotchangeemail": "Ekaunṭ e-mail ṭhikạnakodo noa wiki re baṅ bodoloḱ kana.",
        "emaildisabled": "Noa sayeṭre do e-mail em subita bạnuḱa.",
-       "accountcreated": "Ekaunṭ do teyarena",
+       "accountcreated": "ᱦᱤᱥᱟᱹᱵ ᱠᱷᱟᱛᱟ ᱛᱮᱭᱟᱨᱱᱟ",
        "accountcreatedtext": "$1 lạgit́te ekaunṭ do benaoena.",
-       "createaccount-title": "{{SITENAME}} lạgit́te ekaunṭ benao",
+       "createaccount-title": "{{SITENAME}} ᱞᱟᱹᱜᱤᱛᱛᱮ ᱦᱤᱥᱟᱹᱵ ᱠᱷᱟᱛᱟ ᱛᱮᱭᱟᱨ",
        "createaccount-text": "Okoe co am lạgit́te mitṭen ekaunṭko amaḱ e-mail ṭhikạna lạgit {{SITENAME}} re ($4) ńutum \"$2\", oku nambar \"$3\".\nAm do mesagem baṅ daṛeyaḱa, judi noa ekaunṭ do vulge benaolen khan.",
        "login-throttled": "Am do mitghạri lahare por por aema dhao boloḱem kurumuṭu keda.\nArhõ kurumuṭue lahare dayakate thoṛagan tạṅgiemẽ.",
        "login-abort-generic": "Amaḱ bhitri boloḱ do baṅ hoylena - batena.",
        "loginlanguagelabel": "ᱯᱟᱹᱨᱥᱤ: $1",
        "pt-login": "ᱵᱚᱞᱚᱜ ᱫᱩᱭᱟᱹᱨ",
-       "pt-login-button": "Bolon",
+       "pt-login-button": "ᱵᱚᱞᱚᱜ ᱢᱮ",
        "pt-createaccount": "ᱴᱷᱟᱭ ᱵᱮᱱᱟᱣᱢᱮ",
        "pt-userlogout": "ᱚᱰᱚᱠᱚᱜ ᱢᱮ",
        "user-mail-no-addy": "Jahan e-mail ṭhikana bạgi kate e-mail kul kurumuṭu hoena.",
-       "changepassword": "Uku nombor bodolme",
-       "resetpass_header": "Ekaunṭ oku namber bodol",
-       "oldpassword": "Mare uku nombor",
+       "changepassword": "ᱫᱟᱱᱟᱝ ᱥᱟᱵᱟᱫᱽ ᱵᱚᱫᱚᱞ",
+       "resetpass_header": "ᱦᱤᱥᱟᱹᱵ ᱠᱷᱟᱛᱟ ᱨᱮᱱᱟᱜ ᱫᱟᱱᱟᱝ ᱥᱟᱵᱟᱫᱽ ᱵᱚᱫᱚᱞ",
+       "oldpassword": "ᱢᱟᱨᱮᱭᱟᱜ ᱫᱟᱱᱟᱝ ᱥᱟᱵᱟᱫᱽ:",
        "newpassword": "ᱱᱟᱶᱟ ᱫᱟᱱᱟᱝᱥᱟᱵᱟᱫᱽᱺ",
-       "retypenew": "Doṛhate oku namber olme",
-       "resetpass_submit": "Oku namber joṛao ar bhitri bolok",
+       "retypenew": "ᱫᱚᱲᱦᱟᱛᱮ ᱫᱟᱱᱟᱝ ᱥᱟᱵᱟᱫᱽ ᱚᱞᱢᱮ:",
+       "resetpass_submit": "ᱫᱟᱱᱟᱝ ᱥᱟᱵᱟᱫᱽ ᱮᱢᱢᱮ ᱟᱨ ᱵᱚᱞᱚᱜ ᱢᱮ",
        "changepassword-success": "Amaḱ oku namber do napayte bodolena!\nNitoḱ do am bhitritem boloḱkana...",
        "botpasswords": "ᱵᱚᱴ ᱫᱟᱱᱟᱝ ᱥᱟᱵᱟᱫᱽ",
        "botpasswords-createnew": "ᱱᱟᱶᱟ ᱵᱚᱴ ᱫᱟᱱᱟᱝ ᱥᱟᱵᱟᱫᱽ ᱛᱮᱭᱟᱨᱢᱮ",
        "botpasswords-label-resetpassword": "ᱱᱟᱣᱟᱛᱮ ᱫᱟᱱᱟᱝᱥᱟᱵᱟᱫᱽ ᱮᱢ",
        "botpasswords-label-grants-column": "ᱦᱩᱭᱠᱟᱱ",
        "botpasswords-bad-appid": "ᱵᱚᱴ ᱧᱤᱛᱩᱢ \"$1\" ᱵᱟᱝ ᱴᱷᱤᱠᱟ᱾",
-       "botpasswords-created-title": "á±µá±\9aá±´ á±©á± á±© á±®á±\9eá±¥á±\9aá±\9d ᱛᱮᱭᱟᱨᱱᱟ",
+       "botpasswords-created-title": "á±µá±\9aá±´ á±©á± á±© á±«á±\9fá±±á±\9fá±\9d á±¥á±\9fá±µá±\9fᱫᱽ ᱛᱮᱭᱟᱨᱱᱟ",
        "botpasswords-updated-title": "ᱵᱚᱴ ᱫᱟᱱᱟᱝ ᱥᱟᱵᱟᱫᱽ ᱩᱛᱷᱱᱟᱹᱣ",
-       "botpasswords-deleted-title": "á±µá±\9aá±´ á±©á± á±© á±®á±\9eá±¥á±\9aá±\9d ᱢᱩᱪᱷᱟᱹᱣᱱᱟ",
-       "resetpass_forbidden": "Oku namber do baṅ bodoloklena",
-       "resetpass_forbidden-reason": "ᱩᱠᱩ á±®á±\9eá±¥á±\9aá±\9d ᱵᱟᱝ ᱵᱚᱫᱚᱞᱚᱜ-ᱟ: $1",
+       "botpasswords-deleted-title": "á±µá±\9aá±´ á±«á±\9fá±±á±\9fá±\9d á±¥á±\9fá±µá±\9fᱫᱽ ᱢᱩᱪᱷᱟᱹᱣᱱᱟ",
+       "resetpass_forbidden": "ᱫᱟᱱᱟᱝ ᱥᱟᱵᱟᱫᱽ ᱵᱟᱝ ᱵᱚᱫᱚᱞᱜ-ᱟ",
+       "resetpass_forbidden-reason": "ᱫá±\9fá±±á±\9fá±\9d á±¥á±\9fá±µá±\9fᱫᱽ ᱵᱟᱝ ᱵᱚᱫᱚᱞᱚᱜ-ᱟ: $1",
        "resetpass-no-info": "Noa sakam sojhete laṛcaṛ lạgit́te am do bhitri boloḱ hoyoḱtama.",
-       "resetpass-submit-loggedin": "Oku namber bodol",
-       "resetpass-submit-cancel": "Bạgi",
-       "resetpass-temp-password": "Nit lạgit uku nambar:",
-       "passwordreset": "á±±á±\9fá±£á±\9fá±\9bá±® á±©á± á±© á±®á±\9eá±¥á±\9aá±\9d á±®á±¢á±¢á±®",
+       "resetpass-submit-loggedin": "ᱫᱟᱱᱟᱝ ᱥᱟᱵᱟᱫᱽ ᱵᱚᱫᱚᱞ",
+       "resetpass-submit-cancel": "ᱵᱟᱫᱽ",
+       "resetpass-temp-password": "ᱱᱮᱛᱚᱜ ᱞᱟᱹᱜᱤᱛ ᱫᱟᱱᱟᱝ ᱥᱟᱵᱟᱫᱽ:",
+       "passwordreset": "á±±á±\9fá±£á±\9fá±\9bá±® á±«á±\9fá±±á±\9fá±\9d á±¥á±\9fá±µá±\9fᱫᱽ á±®á±¢",
        "passwordreset-disabled": "Noa wikire amaḱ uku nambar nãwãte em lạgit subita do bando gea.",
-       "passwordreset-username": "Beoharicaḱ ńutum:",
-       "passwordreset-domain": "á¸\8comen:",
-       "passwordreset-email": "E-mail ṭhikạna:",
+       "passwordreset-username": "ᱵᱮᱵᱦᱟᱨᱤᱡ ᱧᱩᱛᱩᱢ:",
+       "passwordreset-domain": "ᱧᱩá±\9bᱩᱢ:",
+       "passwordreset-email": "ᱤᱢᱮᱞ ᱵᱩᱴᱟᱹ:",
        "passwordreset-emailtitle": "{{SITENAME}} sayeṭre beoharićaḱ purạo thutiko",
-       "passwordreset-emailelement": "Beoharićaḱ ńutum: \n$1\n\nMit́ ghạṛi lạgit uku nambar: \n$2",
+       "passwordreset-emailelement": "ᱵᱮᱵᱦᱟᱨᱤᱭᱟᱜ ᱧᱩᱛᱩᱢ: \n$1\n\nᱢᱤᱫ ᱜᱷᱟᱹᱲᱤ ᱞᱟᱹᱜᱤᱛ ᱫᱟᱱᱟᱝ ᱱᱟᱵᱟᱫᱽ: \n$2",
        "passwordreset-emailsentemail": "Mitṭen disạ ruaṛ e-mail do kulena.",
-       "changeemail": "email ᱴᱷᱤᱠᱱᱟ ᱵᱚᱫᱚᱞ ᱢᱮ ᱥᱮ ᱚᱪᱚᱜᱽ ᱢᱮ",
+       "changeemail": "ᱤᱢᱮᱞ ᱴᱷᱤᱠᱱᱟ ᱵᱚᱫᱚᱞ ᱢᱮ ᱥᱮ ᱚᱪᱚᱜᱽ ᱢᱮ",
        "changeemail-header": "Ekaunṭ e-mail ṭhikạna do bodolme",
        "changeemail-no-info": "Noa sakam sojhete laṛcaṛ lạgit́te am do bhitri boloḱ hoyoḱtama.",
-       "changeemail-oldemail": "Nitaḱ e-mail ṭhikạna:",
+       "changeemail-oldemail": "ᱱᱮᱛᱚᱜ-ᱟᱜ ᱤᱢᱮᱞ ᱴᱷᱤᱠᱟᱹᱱᱟ",
        "changeemail-newemail": "ᱱᱟᱣᱟ ᱤᱢᱮᱞ ᱵᱩᱴᱟᱹ:",
-       "changeemail-none": "(Okaṭaḱ hõ baṅ)",
-       "changeemail-password": "á±\9fá±¢á±\9fá±\9c {{SITENAME}} á±©á± á±© á±®á±\9eá±¥á±\9aá±\9d:",
-       "changeemail-submit": "E-mail bodolme",
+       "changeemail-none": "(ᱪᱮᱫ ᱦᱚᱸ ᱵᱟᱹᱱᱩᱜ-ᱟ)",
+       "changeemail-password": "á±\9fá±¢á±\9fá±\9c {{SITENAME}} á±«á±\9fá±±á±\9fá±\9d á±¥á±\9fá±µá±\9fá±½:",
+       "changeemail-submit": "ᱤ-ᱢᱮᱞ ᱵᱚᱫᱚᱞᱢᱮ",
        "bold_sample": "ᱢᱚᱴᱟ ᱚᱞ",
        "bold_tip": "ᱢᱚᱴᱟ ᱚᱞ",
        "italic_sample": "ᱜᱷᱟᱸᱡᱮᱲ ᱚᱞ",
        "italic_tip": "ᱜᱷᱟᱸᱡᱮᱲ ᱚᱞ",
-       "link_sample": "Joṛaotet́ reaḱ bohoḱ",
+       "link_sample": "ᱡᱚᱱᱚᱲ ᱴᱟᱭᱴᱮᱞ",
        "link_tip": "ᱵᱷᱤᱛᱨᱤ ᱡᱚᱱᱚᱲ",
-       "extlink_sample": "http://www.nạmuna.makaṛgạṭi ạmạli",
+       "extlink_sample": "http://www.example.com ᱡᱚᱱᱚᱲ ᱴᱟᱭᱴᱮᱞ",
        "extlink_tip": "ᱵᱟᱨᱦᱮ ᱨᱮᱱᱟᱜ ᱡᱚᱱᱚᱲ (ᱫᱤᱥᱟᱹᱭᱢᱮ http://prefix)",
        "headline_sample": "ᱵᱚᱦᱚᱜ ᱨᱮᱱᱟᱜ ᱚᱞ",
-       "headline_tip": "level 2 guḍkatha",
-       "nowiki_sample": "Begor format olko bhoraome",
-       "nowiki_tip": "wiki formatting bạgiyaḱme",
+       "headline_tip": "ᱞᱮᱵᱷᱮᱞ ᱒ ᱦᱮᱰᱞᱟᱭᱤᱱ",
+       "nowiki_sample": "ᱵᱮᱜᱚᱨ ᱯᱷᱚᱨᱢᱮᱴ ᱚᱞᱠᱩ ᱵᱷᱚᱨᱟᱣᱢᱮ",
+       "nowiki_tip": "ᱣᱤᱠᱤ ᱯᱷᱚᱨᱢᱟᱴᱤᱝ ᱵᱟᱹᱜᱤᱭᱢᱮ",
        "image_tip": "ᱛᱚᱞᱟᱠᱟᱱ ᱨᱮᱫ",
        "media_tip": "ᱨᱮᱫ ᱡᱚᱱᱚᱲ",
-       "sig_tip": "Amaḱ suhi sãote okte",
-       "hr_tip": "Barabạri dag",
-       "summary": "Guṭ katha",
+       "sig_tip": "ᱟᱢᱟᱜ ᱥᱩᱦᱤ ᱥᱟᱶᱛᱮ ᱚᱠᱛᱚ ᱪᱷᱟᱯ",
+       "hr_tip": "ᱵᱟᱨᱟᱵᱟᱹᱨᱤ ᱫᱟᱜᱽ",
+       "summary": "ᱢᱩᱬᱩᱛ ᱠᱟᱛᱦᱟ:",
        "subject": "ᱥᱟᱛᱟᱢ:",
        "minoredit": "ᱱᱚᱣᱟ ᱫᱚ ᱦᱩᱰᱤᱧ ᱥᱟᱯᱲᱟᱣ ᱠᱟᱱᱟ",
        "watchthis": "ᱱᱚᱣᱟ ᱥᱟᱦᱴᱟ ᱧᱮᱞᱢᱮ",
        "savechanges": "ᱵᱚᱫᱚᱞᱠᱚ ᱨᱩᱠᱷᱤᱭᱟᱹᱭ ᱢᱮ",
        "publishpage": "ᱥᱟᱦᱴᱟ ᱯᱟᱨᱥᱟᱞ ᱢᱮ",
        "publishchanges": "ᱵᱚᱫᱚᱞᱠᱚ ᱯᱟᱨᱥᱟᱞ ᱢᱮ",
-       "preview": "Ńel, Unuduḱ",
-       "showpreview": "Unuduḱ",
+       "preview": "ᱧᱮᱞ ᱵᱤᱰᱟᱹᱣ",
+       "showpreview": "ᱧᱮᱞᱡᱚᱝ ᱩᱫᱩᱜᱽᱢᱮ",
        "showdiff": "ᱵᱚᱫᱚᱞᱠᱩ ᱩᱫᱩᱜᱽ ᱢᱮ",
        "anoneditwarning": "<strong>ᱦᱩᱥᱤᱭᱟᱹᱨ:</strong> ᱟᱢ ᱵᱟᱢ ᱵᱚᱞᱚᱣᱟᱠᱟᱱᱟ ᱾ ᱡᱩᱫᱤ ᱟᱢ ᱡᱟᱦᱟᱸᱱᱟᱜ ᱥᱟᱯᱲᱟᱣᱟᱢ ᱟᱢᱟᱜ IP ᱵᱩᱴᱟᱹ ᱥᱚᱫᱚᱨ ᱛᱟᱦᱮᱸᱱᱟ ᱾ ᱡᱩᱫᱤ ᱟᱢ [$1 ᱵᱚᱞᱚᱱᱟᱢ]</strong> ᱟᱨᱵᱟᱝ <strong>[$2 ᱠᱷᱟᱛᱟ ᱛᱮᱭᱟᱨᱟᱢ]</strong>, ᱟᱢᱟᱜ ᱥᱟᱯᱲᱟᱣ ᱫᱚ ᱩᱫᱩᱜᱚᱜ-ᱟ ᱟᱢᱟᱜ ᱵᱮᱵᱷᱟᱨᱤᱭᱟᱹ ᱧᱩᱛᱩᱢ, ᱥᱟᱶᱛᱮ ᱮᱴᱟᱜ ᱥᱩᱵᱤᱫᱷᱟ ᱠᱚ ᱾",
        "anonpreviewwarning": "\"Am do bhitri bam bolo akana. Noa sakamre amaḱ kạmiko jạre IP ṭhiikạ̣nare rukhiyạ ḱa.\"",
        "missingcommenttext": "ᱫᱟᱭᱟ ᱠᱟᱛᱮ ᱟᱢᱟᱜ ᱠᱟᱛᱷᱟ ᱵᱚᱞᱚᱭ ᱢᱮ᱾",
        "summary-preview": "Guṭ katha unuduḱ:",
        "subject-preview": "Babot/Guṭkatha unuduḱ:",
-       "blockedtitle": "Beoharić doe eset ocoakana.",
+       "blockedtitle": "ᱵᱮᱵᱷᱟᱨᱤᱡ ᱫᱚ ᱮᱥᱮᱫ ᱚᱪᱚᱣᱟᱠᱟᱱᱟᱭ",
        "blockedtext": "<strong>ᱟᱢᱟᱜ ᱵᱮᱵᱷᱟᱨᱤᱭᱟᱹ ᱧᱩᱛᱩᱢ ᱟᱨᱵᱟᱝ IP ᱵᱩᱴᱟᱹ ᱫᱚ ᱵᱚᱸᱫᱽ ᱟᱠᱟᱱᱟ ᱾ </strong>\n\nᱱᱚᱶᱟ ᱵᱚᱸᱫᱽ ᱫᱚ $1 ᱫᱟᱨᱟᱭᱛᱮ ᱦᱩᱭᱟᱠᱱᱟ ᱾\nᱱᱚᱶᱟ ᱨᱮᱱᱟᱜ ᱚᱡᱮ ᱫᱚ ᱮᱢᱮᱱᱟ <em>$2</em>.\n\n* ᱵᱚᱸᱫᱽ ᱮᱦᱚᱵ: $8\n* ᱵᱚᱸᱫᱽ ᱢᱩᱪᱟᱹᱫ: $6\n* ᱟᱥᱟᱦᱟᱱ ᱵᱚᱸᱫᱽᱠᱚ: $7\n\nᱟᱢ $1 ᱮᱢ ᱥᱟᱹᱜᱟᱹᱭ ᱫᱟᱲᱮᱭᱟᱭᱟ ᱵᱟᱝᱠᱷᱟᱱ ᱮᱴᱟᱜ [[{{MediaWiki:Grouppage-sysop}}|ᱟᱰᱢᱤᱱᱤᱥᱴᱨᱮᱴᱚᱨ]] ᱵᱚᱸᱫᱽ ᱵᱟᱵᱚᱫᱽ ᱛᱮ ᱜᱟᱞᱚᱪ ᱞᱟᱹᱜᱤᱫ ᱾\nᱟᱢ ᱵᱟᱢ ᱵᱮᱵᱷᱟᱨ ᱫᱟᱲᱮᱭᱟᱜ \"email this user\" ᱥᱩᱵᱤᱫᱷᱟ ᱡᱚᱛᱷᱟᱛ ᱤᱢᱮᱞ ᱵᱩᱴᱟᱹ ᱛᱟᱢ ᱵᱟᱝ ᱛᱟᱦᱮᱸᱱ ᱠᱷᱟᱱ ᱟᱨ ᱱᱚᱶᱟ ᱫᱚ ᱪᱤᱱᱦᱟᱹᱣ-ᱟ [[Special:Preferences|ᱠᱷᱟᱛᱟ ᱧᱮᱞᱚᱚᱜ]] ᱠᱷᱚᱱ ᱟᱨ ᱟᱢ ᱫᱚ ᱵᱟᱢ ᱵᱚᱸᱫᱽ ᱟᱠᱟᱱᱟ ᱱᱚᱶᱟ ᱵᱮᱵᱷᱟᱨ ᱠᱷᱚᱱ ᱾\nᱟᱢᱟᱜ ᱱᱤᱛᱚᱜᱟᱜ IP ᱵᱩᱴᱟᱹ ᱫᱚ $3, ᱟᱨ ᱵᱚᱸᱫᱽ ID ᱫᱚ #$5  \nᱫᱟᱭᱟᱠᱟᱛᱮ ᱥᱮᱞᱮᱫᱽ ᱢᱮ ᱪᱮᱛᱟᱱᱟᱜ ᱠᱟᱛᱷᱟᱠᱚ ᱡᱚᱛᱚ ᱞᱮᱠᱟᱱ ᱠᱩᱠᱞᱤ ᱨᱮ ᱾",
-       "blockednoreason": "jahan babot baṅ em akana",
-       "whitelistedittext": "Sakamre sompadon lạgit́te $1 em hoyoḱa.",
-       "nosuchsectiontitle": "Pahaṭa bȧn ńamlena",
-       "loginreqtitle": "Boloḱ jạruṛa",
+       "blockednoreason": "ᱡᱟᱸᱦᱟᱸᱱ ᱚᱡᱮ ᱵᱟᱝ ᱮᱢᱠᱟᱱᱟ",
+       "whitelistedittext": "ᱥᱟᱦᱴᱟ ᱥᱟᱯᱲᱟᱣ ᱞᱟᱹᱜᱤᱛ $1 ᱮᱢ ᱦᱩᱭᱩᱜ-ᱟ᱾",
+       "nosuchsectiontitle": "ᱛᱷᱚᱠ ᱵᱟᱝ ᱧᱟᱢᱞᱮᱱᱟ",
+       "loginreqtitle": "ᱵᱚᱞᱚᱜ ᱡᱟᱹᱨᱩᱲᱟ",
        "loginreqlink": "ᱵᱚᱞᱚᱜ ᱢᱮ",
        "loginreqpagetext": "Eṭagaḱ sakamko ńel lạgit́te do am $1 hoyoḱ jạruṛtama.",
-       "accmailtitle": "Uku nambar do kulena.",
+       "accmailtitle": "ᱫᱟᱱᱟᱝ ᱥᱟᱵᱟᱫᱽ ᱠᱩᱞ ᱦᱩᱭᱱᱟ",
        "accmailtext": "[[User talk:$1 $1]] lạgit́te aćte benaoen uku nambar do $2 kul hoena.\nBhitri bolo kateḱ noa nãwã ekaunṭ lạgit uku nambar \"[[Special:ChangePassword Change password]]\" sakam khonem bodol daṛyakya.",
-       "newarticle": "(Nãwa)",
+       "newarticle": "(ᱱᱟᱣᱟᱱᱟᱜ)",
        "newarticletext": "ᱟᱢ ᱚᱠᱟ ᱥᱟᱦᱴᱟ ᱨᱮᱱᱟᱜ ᱡᱚᱱᱟᱲᱮᱢ ᱯᱟᱸᱡᱟᱸ ᱟᱹᱜᱩᱭᱫᱟ ᱚᱱᱚ ᱫᱚ ᱵᱟᱱᱩᱜ-ᱟ᱾\nᱚᱱᱟ ᱥᱟᱦᱴᱟ ᱛᱮᱭᱟᱨ ᱞᱟᱹᱜᱤᱛ ᱛᱮ, ᱞᱟᱛᱟᱨ ᱵᱟᱠᱥᱚ ᱵᱷᱤᱛᱨᱤᱨᱮ ᱚᱞ ᱮᱦᱚᱵ ᱢᱮ (ᱟᱨᱦᱚᱸ ᱡᱟᱹᱥᱛᱤ ᱵᱟᱰᱟᱭ ᱞᱟᱹᱜᱤᱛᱴᱮ [$1 ᱜᱚᱸᱲᱚᱸ ᱥᱟᱦᱴᱟ] ᱯᱟᱸᱡᱚᱸᱭᱢᱮ)᱾\nᱟᱢ ᱵᱷᱩᱞᱛᱮ ᱱᱚᱸᱰᱮᱢ ᱦᱮᱡ ᱟᱠᱟᱱ ᱠᱷᱟᱡ, ᱟᱢᱟᱜ ᱵᱨᱟᱣᱡᱟᱨ ᱨᱮᱱᱟᱜ '''ᱛᱟᱭᱚᱢ''' ᱵᱟᱴᱚᱱ ᱞᱤᱱᱢᱮ᱾",
        "anontalkpagetext": "----\n\n<em>ᱱᱚᱶᱟ ᱫᱚ ᱜᱟᱞᱚᱪ ᱥᱟᱦᱴᱟ ᱠᱟᱱᱟ ᱩᱠᱩᱧᱩᱛᱩᱢ ᱵᱮᱵᱷᱟᱨᱤᱭᱟᱹ ᱠᱚᱣᱟᱜ ᱡᱟᱦᱟᱸᱭ ᱫᱚ ᱠᱷᱟᱛᱟ ᱵᱟᱭ ᱛᱮᱭᱟᱨ ᱟᱠᱟᱫᱟ ᱱᱤᱛ ᱦᱟᱹᱵᱤᱡ, ᱟᱨᱵᱟᱝ ᱡᱟᱦᱟᱸᱭ ᱵᱮᱵᱷᱟᱨ ᱟᱠᱟᱫᱟ ᱱᱚᱶᱟ ᱾</em>\nᱚᱱᱟᱛᱮ ᱟᱞᱮ ᱮᱞᱮᱞ IP ᱞᱮ ᱵᱮᱵᱷᱟᱨᱮᱜ-ᱟ ᱩᱱᱤ ᱪᱤᱱᱦᱟᱹᱣ ᱞᱟᱹᱜᱤᱫ ᱾\nᱚᱱᱠᱟᱱ IP ᱵᱩᱴᱟᱹ ᱫᱚ ᱦᱟᱹᱴᱤᱧ ᱫᱟᱲᱮᱭᱟᱜ-ᱟ ᱛᱤᱢᱤᱱ ᱵᱮᱵᱷᱟᱨᱤᱭᱟᱹ ᱫᱟᱨᱟᱭᱛᱮ ᱾\nᱡᱩᱫᱤ ᱟᱢ ᱩᱠᱩᱧᱩᱛᱩᱢ ᱵᱮᱵᱷᱟᱨᱤᱭᱟᱹ ᱠᱟᱱᱟᱢ ᱟᱨ ᱵᱷᱟᱹᱵᱤᱭᱮᱜ-ᱟᱢ ᱵᱟᱝ ᱡᱚᱲᱟᱣᱟᱱ ᱠᱟᱛᱷᱟ ᱟᱢᱮ ᱩᱫᱩᱜᱢᱮ ᱠᱟᱱᱟ, ᱮᱱᱠᱷᱟᱱ  [[Special:CreateAccount|ᱠᱷᱟᱛᱟ ᱛᱮᱭᱟᱨᱢᱮ]] ᱟᱨᱵᱟᱝ [[Special:UserLogin|ᱞᱚᱜᱤᱱ]] ᱢᱮ ᱫᱟᱨᱟᱭ ᱵᱷᱮᱣᱱᱟ ᱠᱚ ᱥᱟᱦᱟᱭ ᱞᱟᱹᱜᱤᱫ ᱮᱴᱟᱜ ᱩᱠᱩᱧᱩᱛᱩᱢ ᱵᱮᱵᱷᱟᱨᱤᱭᱟᱹ ᱠᱚ ᱥᱟᱶ ᱾",
        "noarticletext": "ᱱᱮᱛᱚᱜ ᱱᱚᱣᱟ ᱥᱟᱦᱴᱟᱨᱮ ᱪᱮᱫᱜᱮ ᱵᱟᱹᱱᱩᱜ-ᱟ᱾\nᱮᱴᱟᱜ ᱥᱟᱦᱴᱟᱨᱮᱢ [[Special:Search/{{PAGENAME}}|search for this page title]],\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} search the related logs],\nor [{{fullurl:{{FULLPAGENAME}}|action=edit}} edit this page]</span>.",
        "noarticletext-nopermission": "ᱱᱚᱣᱟ ᱥᱟᱦᱴᱟᱨᱮ ᱱᱤᱛᱚᱜ ᱪᱮᱫᱜᱮ ᱚᱞ ᱵᱟᱹᱱᱩᱜ-ᱟ᱾\n\nᱟᱢ [[Special:Search/{{PAGENAME}}|ᱱᱚᱭᱟ ᱥᱟᱦᱴᱟᱨᱮᱱᱟᱜ ᱧᱤᱛᱩᱢᱮᱢ ᱥᱮᱸᱫᱽᱨᱟ ᱫᱟᱲᱮᱭᱟᱜ-ᱟ]] ᱮᱴᱟᱜ ᱥᱟᱦᱴᱟ ᱠᱚᱨᱮᱦᱚᱸ,\nᱟᱨᱵᱟᱝ <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} search the related logs]</span>.",
        "userpage-userdoesnotexist": "\"<nowiki>$1</nowiki>\" ńutuman jahãe beoharićaḱ ekaunṭ do baṅ resṭri hoeakana. Daya kate biḍạo katet́ ńelmẽ noa sakam do benoa/sompadonem menet́ kana se baṅ.",
-       "userpage-userdoesnotexist-view": "Beoharićaḱ \"$1\" ekaunṭ do baṅ resṭire akana.",
-       "blocked-notice-logextract": "Nui beoharić do nitoḱe esetgea.\nRefarens lạgit́te nahaḱ boloḱ do latare em hoena:",
+       "userpage-userdoesnotexist-view": "ᱵᱮᱵᱦᱟᱨᱤᱭᱟᱜ \"$1\" ᱮᱠᱟᱣᱱᱴ ᱫᱚ ᱵᱟᱝ ᱨᱮᱥᱴᱨᱤ ᱟᱠᱟᱱᱟ᱾",
+       "blocked-notice-logextract": "ᱱᱩᱭ ᱵᱮᱵᱦᱟᱨᱤᱡ ᱫᱚ ᱱᱮᱛᱚᱜ ᱮ ᱥᱮᱥᱫᱜᱮᱭᱟ᱾\nᱨᱮᱯᱷᱟᱨᱮᱱᱥ ᱞᱟᱹᱜᱤᱛᱛᱮ ᱱᱟᱣᱟᱱᱟᱜ ᱵᱚᱞᱚᱜ ᱠᱩᱨᱩᱢᱩᱴᱩ ᱞᱟᱛᱟᱨᱨᱮ ᱮᱢ ᱦᱩᱭᱱᱟ:",
        "clearyourcache": "<strong>Note:</strong> After saving, you may have to bypass your browser's cache to see the changes.\n* <strong>Firefox / Safari:</strong> Hold <em>Shift</em> while clicking <em>Reload</em>, or press either <em>Ctrl-F5</em> or <em>Ctrl-R</em> (<em>⌘-R</em> on a Mac)\n* <strong>Google Chrome:</strong> Press <em>Ctrl-Shift-R</em> (<em>⌘-Shift-R</em> on a Mac)\n* <strong>Internet Explorer:</strong> Hold <em>Ctrl</em> while clicking <em>Refresh</em>, or press <em>Ctrl-F5</em>\n* <strong>Opera:</strong> Go to <em>Menu → Settings</em> (<em>Opera → Preferences</em> on a Mac) and then to <em>Privacy & security → Clear browsing data → Cached images and files</em>.",
-       "updated": "(Halot ruaṛ)",
-       "note": "'''Noṭ:'''",
-       "previewnote": "'''kheyalmẽ, noa do eken ńeloḱ lạgit.'''\nAmaḱ bodolaḱ kodo nit habićte bań rukhíạakana!",
+       "updated": "(ᱩᱛᱷᱱᱟᱣ ᱜᱮᱭᱟ)",
+       "note": "<strong>ᱱᱳᱴ:</strong>",
+       "previewnote": "'''ᱠᱷᱮᱭᱟᱞᱢᱮ, ᱱᱚᱣᱟ ᱫᱚ ᱮᱠᱮᱱ ᱧᱮᱞᱚᱜ ᱞᱟᱹᱜᱤᱛ᱾'''\nᱟᱢᱟᱜ ᱵᱚᱫᱚᱞ ᱠᱚᱫᱚ ᱱᱤᱛ ᱫᱷᱟᱹᱵᱤᱡ ᱵᱟᱝ ᱨᱩᱠᱷᱤᱭᱟᱹᱣᱠᱟᱱᱟ!",
        "continue-editing": "ᱥᱟᱯᱲᱟᱣ ᱡᱟᱜᱟ ᱪᱟᱞᱟᱜ ᱢᱮ",
-       "editing": "Joṛao do purạena: $1",
+       "editing": "ᱥᱟᱯᱲᱟᱣ ᱪᱟᱞᱟᱜᱠᱟᱱᱟ $1",
        "creating": "$1 ᱛᱮᱭᱟᱨᱚᱜᱠᱟᱱᱟ",
        "editingsection": "ᱥᱟᱯᱲᱟᱣᱢᱮ $1 (ᱦᱟᱹᱴᱤᱧ)",
-       "editingcomment": "Sompadon akadae $1 (Nãwa pahaṭa)",
-       "editconflict": "Sompadon reaḱ bene bạiri: $1",
-       "yourtext": "Amaḱ ol",
+       "editingcomment": "ᱥᱟᱯᱲᱟᱣᱢᱮ $1 (ᱱᱟᱣᱟ ᱦᱟᱹᱴᱤᱧ)",
+       "editconflict": "ᱥᱟᱯᱲᱟᱣ ᱡᱟᱞᱟ: $1",
+       "yourtext": "ᱟᱢᱟᱜ ᱚᱞ",
        "storedversion": "ᱡᱚᱜᱟᱣᱠᱟᱱ ᱥᱩᱫᱷᱨᱟᱹᱣ",
-       "yourdiff": "Farak",
+       "yourdiff": "ᱯᱷᱟᱨᱟᱠ",
        "templatesused": "ᱱᱚᱶᱟ ᱥᱟᱦᱴᱟᱨᱮ ᱵᱮᱵᱷᱟᱨᱟᱠᱟᱱ {{PLURAL:$1|ᱪᱷᱟᱸᱪ|ᱪᱷᱟᱸᱪᱠᱚ}} :",
        "templatesusedpreview": "{{PLURAL:$1|ᱪᱷᱟᱸᱪ|ᱪᱷᱟᱸᱪᱠᱚ}} ᱵᱮᱵᱷᱟᱨ ᱟᱠᱟᱱᱟ ᱱᱟᱶᱟ ᱧᱮᱱᱮᱞᱨᱮ:",
        "template-protected": "(ᱨᱩᱠᱷᱤᱭᱟᱹ)",
-       "template-semiprotected": "(Kạṭic-rukhiyạ)",
+       "template-semiprotected": "(ᱟᱫᱷᱟ ᱨᱩᱠᱷᱤᱭᱟᱹ)",
        "hiddencategories": "ᱱᱚᱣᱟ ᱥᱟᱦᱴᱟ ᱫᱚ {{PLURAL:$1 1 ᱩᱠᱩ ᱛᱷᱚᱠ|$1 ᱩᱠᱩ ᱛᱷᱚᱠᱠᱩ}} ᱜᱟᱶᱛᱟᱨᱮᱱᱜᱮ:",
        "nocreate-loggedin": "ᱱᱟᱣᱟ ᱥᱟᱦᱴᱟ ᱛᱮᱭᱟᱨ ᱞᱟᱹᱜᱤᱛᱛᱮ ᱟᱢᱟᱜ ᱟᱹᱭᱫᱟᱹᱨᱤ ᱫᱚ ᱵᱟᱱᱩᱜ-ᱟ᱾",
-       "sectioneditnotsupported-title": "Pahaṭa sompadona do bae hataoeda",
-       "sectioneditnotsupported-text": "Noa sompadona sakamre pahaṭa sompadona do bae hataoeda",
+       "sectioneditnotsupported-title": "ᱛᱷᱟᱠ ᱥᱟᱯᱲᱟᱣ ᱵᱟᱭ ᱦᱟᱛᱟᱣᱫᱟ",
+       "sectioneditnotsupported-text": "ᱱᱚᱣᱟ ᱥᱟᱦᱴᱟᱨᱮ ᱛᱷᱟᱠ ᱥᱟᱯᱲᱟᱣ ᱵᱟᱭ ᱦᱟᱛᱟᱣᱫᱟ᱾",
        "permissionserrors": "ᱟᱹᱭᱫᱟᱹᱨᱤ ᱦᱩᱲᱟᱹᱜ",
-       "permissionserrorstext": "Noa kạmi amaḱ ạidạri do banuḱa, {{PLURAL:$1 gan karon reaḱ gan karon reaḱ}} lạgit:",
-       "permissionserrorstext-withaction": "Amaḱ $2 kạmire ạydạri do bạnuḱa, Ona reaḱ {{PLURAL:$1 Karon/ Karonko}}:",
+       "permissionserrorstext": "ᱱᱚᱣᱟ ᱠᱟᱹᱢᱤᱨᱮ ᱟᱢᱟᱜ ᱟᱹᱭᱫᱟᱹᱨᱤ ᱵᱟᱹᱱᱩᱜ-ᱟ, {{PLURAL:$1 ᱱᱚᱣᱟ ᱞᱟᱹᱜᱤᱛ }}:",
+       "permissionserrorstext-withaction": "ᱟᱢᱟᱜ $2 ᱠᱟᱹᱢᱤᱨᱮ ᱟᱹᱭᱫᱟᱹᱨᱤ ᱫᱚ ᱵᱟᱱᱩᱜ-ᱟ, ᱚᱱᱟ ᱨᱮᱭᱟᱜ {{PLURAL:$1 ᱠᱟᱨᱚᱱ/ ᱠᱟᱨᱚᱱᱠᱚ}}:",
        "recreate-moveddeleted-warn": "'''ᱥᱚᱱᱛᱚᱨᱚᱜᱢᱮ: ᱟᱢ ᱫᱚ ᱟᱨᱦᱚᱸ ᱫᱚᱲᱦᱟᱛᱮ ᱥᱟᱦᱴᱟᱢ ᱛᱮᱭᱟᱨᱫᱟ ᱡᱟᱸᱦᱟ ᱫᱚ ᱞᱟᱦᱟᱨᱮᱜᱮ ᱜᱮᱫ ᱜᱤᱰᱤᱭᱟᱠᱟᱱᱟ᱾\nᱟᱢ ᱫᱚ ᱜᱩᱱᱟᱹᱱᱢᱮ ᱱᱚᱣᱟ ᱥᱟᱦᱴᱟ ᱥᱟᱯᱲᱟᱣ ᱠᱟᱹᱢᱤ ᱪᱟᱹᱞᱩ ᱜᱟᱱᱚᱜ-ᱟ ᱥᱮ ᱵᱟᱝ᱾\nᱱᱚᱣᱟ ᱜᱮᱫ ᱜᱤᱰᱤ ᱟᱨ ᱛᱟᱞᱟ ᱚᱪᱚᱜ ᱥᱟᱦᱴᱟ ᱱᱚᱸᱰᱮ ᱮᱢ ᱦᱩᱭᱱᱟ ᱫᱷᱚᱠ ᱞᱟᱹᱜᱤᱛᱛᱮ᱾",
        "moveddeleted-notice": "ᱱᱚᱭᱟ ᱥᱟᱦᱴᱟ ᱫᱚ ᱜᱮᱫ ᱜᱤᱰᱤ ᱦᱩᱭ ᱟᱠᱟᱱᱟ᱾\nᱜᱮᱫ ᱥᱮ ᱵᱟᱸᱪᱟᱣ ᱥᱮ ᱚᱪᱚᱜ ᱜᱤᱰᱤᱭᱟᱠᱟᱱ ᱥᱟᱦᱴᱟ ᱨᱮᱱᱟᱜ ᱥᱟᱹᱠᱷᱤ ᱞᱮᱠᱟᱛᱮ ᱞᱟᱛᱟᱨᱨᱮ ᱮᱢ ᱦᱩᱭᱱᱟ᱾",
-       "log-fulllog": "Joto cạbi udugmẽ",
-       "edit-hook-aborted": "Huk hotete joto sompadonko bạgi hoeakana.\nNoa reaḱ jahan katha do bạnuḱa.",
+       "log-fulllog": "ᱯᱩᱨᱟᱹ ᱪᱟᱹᱵᱤ ᱛᱟᱹᱞᱠᱟᱹ ᱩᱫᱩᱜᱽᱢᱮ",
+       "edit-hook-aborted": "ᱦᱩᱠ ᱦᱚᱛᱮᱛ ᱡᱚᱛᱚ ᱥᱟᱯᱲᱟᱣ ᱵᱟᱹᱜᱤ ᱦᱩᱭᱠᱟᱱᱟ᱾\nᱱᱚᱣᱟ ᱨᱮᱭᱟᱜ ᱡᱟᱸᱦᱟᱸ ᱠᱟᱛᱷᱟ ᱵᱟᱹᱱᱩᱜ-ᱟ᱾",
        "edit-gone-missing": "ᱥᱟᱦᱴᱟ ᱫᱚ ᱵᱟᱝ ᱦᱟᱞᱚᱛ ᱨᱩᱭᱟᱹᱲᱞᱮᱱᱟ᱾\nᱯᱟᱥᱮᱡᱽ ᱫᱚ ᱚᱪᱚᱜ ᱦᱩᱭ ᱟᱠᱟᱱᱟ᱾",
-       "edit-conflict": "Sompadon reṭepeṭe.",
-       "edit-no-change": "Amaḱ sompadon do baṅ hataolena, Cedaḱ je olre jahan bodol bạnuḱa.",
+       "edit-conflict": "ᱥᱟᱯᱲᱟᱣ ᱵᱟᱹᱭᱨᱤ᱾",
+       "edit-no-change": "ᱟᱢᱟᱜ ᱥᱟᱯᱲᱟᱣ ᱵᱟᱝ ᱦᱟᱛᱟᱣᱞᱮᱱᱟ ᱡᱮᱫᱟᱜ ᱡᱮ ᱚᱞᱨᱮ ᱡᱟᱸᱦᱟᱸᱱ ᱵᱚᱫᱚᱞ ᱵᱟᱹᱱᱩᱜ-ᱟ᱾",
        "postedit-confirmation-created": "ᱱᱚᱣᱟ ᱥᱟᱦᱴᱟ ᱫᱚ ᱛᱮᱭᱟᱨᱠᱟᱱᱟ᱾",
        "postedit-confirmation-restored": "ᱱᱚᱣᱟ ᱥᱟᱦᱴᱟ ᱫᱚ ᱟᱹᱜᱩ ᱨᱩᱣᱟᱹᱲ ᱦᱩᱭᱠᱟᱱᱟ᱾",
        "edit-already-exists": "ᱱᱟᱣᱟ ᱥᱟᱦᱴᱟ ᱵᱟᱝ ᱛᱮᱭᱟᱨ ᱞᱮᱱᱟ᱾\nᱱᱚᱣᱟ ᱫᱚ ᱞᱟᱦᱟ ᱠᱷᱚᱱ ᱢᱮᱱᱟᱜ ᱜᱮᱭᱟ᱾",
-       "defaultmessagetext": "Sedae olko",
+       "defaultmessagetext": "ᱯᱩᱭᱞᱩ ᱚᱞᱠᱟᱱ ᱠᱷᱚᱵᱚᱨ",
        "content-model-wikitext": "ᱣᱤᱠᱤ-ᱚᱞ",
        "content-model-text": "ᱥᱚᱢᱟᱱ ᱚᱞ",
        "post-expand-template-inclusion-warning": "\"Sontoroḱme\" Noa format do lạṭu geya.\nThoṛa format do noare banuḱana.",
        "post-expand-template-argument-category": "Pages containing omitted template arguments",
        "undo-failure": "ᱥᱟᱯᱲᱟᱣᱠᱚ ᱵᱟᱭ ᱟᱹᱪᱩᱨ ᱨᱩᱣᱟᱹᱲᱚᱜ-ᱟ ᱛᱟᱞᱟ-ᱢᱟᱞᱟ ᱥᱟᱯᱲᱟᱣ ᱵᱤᱨᱚᱫᱽ ᱤᱫᱤᱠᱟᱛᱮ |",
        "viewpagelogs": "ᱱᱚᱣᱟ ᱥᱟᱦᱴᱟ ᱨᱮᱭᱟᱜ ᱞᱚᱜᱽᱠᱚ ᱧᱮᱞᱢᱮ",
-       "nohistory": "Noa sakam re do jahan sompadon reaḱ jạṛ bạnuḱa.",
+       "nohistory": "ᱱᱚᱣᱟ ᱥᱟᱦᱴᱟ ᱨᱮᱱᱟᱜ ᱥᱟᱯᱲᱟᱣ ᱱᱟᱜᱟᱢ ᱵᱟᱹᱱᱩᱜ-ᱟ᱾",
        "currentrev": "ᱱᱮᱛᱟᱨ ᱧᱮᱞ",
        "currentrev-asof": "ᱱᱟᱣᱟ ᱧᱮᱞ ᱞᱮᱠᱟᱛᱮ $1",
        "revisionasof": "Revision as of $1",
        "revision-info": "Revision as of $1 by {{GENDER:$6|$2}}$7",
-       "previousrevision": "ᱯá±\9fᱹᱦᱤá±\9e á±¯á±\9fᱲᱦá±\9fá±£ á±¨á±©á±­á±\9fᱹᱣ",
+       "previousrevision": "ᱯá±\9fᱹᱦᱤá±\9e á±¯á±\9fᱲᱦá±\9fá±£ á±¨á±©á±£á±\9fᱹᱲ",
        "nextrevision": "ᱱᱟᱣᱟᱛᱮ ᱧᱮᱞ ᱨᱩᱣᱟᱹᱲ →",
        "currentrevisionlink": "ᱱᱮᱛᱟᱨ ᱧᱮᱞ",
        "cur": "ᱱᱮᱛᱚᱜ",
-       "next": "Laha seć",
+       "next": "ᱛᱟᱭᱚᱢ ᱨᱮᱱᱟᱜ",
        "last": "ᱞᱟᱦᱟ ᱛᱮᱱᱟᱜ",
-       "page_first": "Pahilaḱ",
-       "page_last": "Mucạt́aḱ",
-       "histlegend": "ᱮᱴᱟᱜ ᱵᱟᱪᱷᱟᱣ: ᱱᱟᱣᱟ ᱵᱚᱫᱚᱞᱠᱚ ᱛᱩᱞᱟᱹᱣ ᱢᱮᱱᱠᱷᱟᱱ, ᱨᱮᱰᱤᱭᱳ ᱵᱟᱠᱥᱚᱨᱮ ᱪᱤᱱ ᱮᱢ ᱠᱟᱛᱮ ᱵᱚᱞᱚᱜ ᱥᱮ ᱞᱟᱛᱟᱨ ᱨᱮᱱᱟᱜ ᱵᱟᱴᱚᱱ ᱞᱤᱱᱢᱮ᱾<br />\nᱩᱱᱩᱫᱩᱜ: '''({{int:cur}})''' = ᱱᱮᱛᱟᱨ ᱥᱩᱫᱷᱨᱟᱹᱣ ᱥᱟᱶᱛᱮ ᱥᱚᱝ, '''({{int:last}})''' = ᱞᱟᱦᱟ ᱨᱮᱭᱟᱜ ᱱᱟᱣᱟ ᱥᱩᱫᱷᱨᱟᱹᱣ ᱥᱟᱶᱛᱮ ᱥᱚᱝ, '''{{int:minoreditletter}}''' = ᱦᱩᱰᱤᱧ ᱥᱟᱯᱲᱟᱣ᱾",
+       "page_first": "ᱯᱟᱹᱦᱤᱞ",
+       "page_last": "ᱢᱩᱪᱟᱹᱫ",
+       "histlegend": "ᱮᱴᱟᱜ ᱵᱟᱪᱷᱟᱣ: ᱱᱟᱣᱟ ᱵᱚᱫᱚᱞᱠᱚ ᱛᱩᱞᱟᱹᱣ ᱢᱮᱱᱠᱷᱟᱱ, ᱨᱮᱰᱤᱭᱳ ᱵᱟᱠᱥᱚᱨᱮ ᱪᱤᱱ ᱮᱢ ᱠᱟᱛᱮ ᱵᱚᱞᱚᱜ ᱥᱮ ᱞᱟᱛᱟᱨ ᱨᱮᱱᱟᱜ ᱵᱟᱴᱚᱱ ᱞᱤᱱᱢᱮ᱾<br />\nᱩᱱᱩᱫᱩᱜ: <strong>({{int:cur}})</strong> = ᱱᱮᱛᱟᱨ ᱥᱩᱫᱷᱨᱟᱹᱣ ᱥᱟᱶᱛᱮ ᱥᱚᱝ, <strong>({{int:last}})</strong> = ᱞᱟᱦᱟ ᱨᱮᱭᱟᱜ ᱱᱟᱣᱟ ᱥᱩᱫᱷᱨᱟᱹᱣ ᱥᱟᱶᱛᱮ ᱥᱚᱝ, <strong>{{int:minoreditletter}}</strong> = ᱦᱩᱰᱤᱧ ᱥᱟᱯᱲᱟᱣ ᱾",
        "history-fieldset-title": "ᱧᱮᱞ ᱟᱹᱨᱩ ᱞᱟᱹᱜᱤᱫ ᱥᱮᱸᱫᱽᱨᱟ",
        "history-show-deleted": "khạli get giḍiyaḱ koge",
        "histfirst": "ᱢᱟᱨᱮᱱᱟᱜ",
        "histlast": "ᱱᱟᱣᱟᱱᱟᱜ",
-       "historysize": "({{PLURAL:$1 1 bayeṭ $1 bayeṭko}})",
-       "historyempty": "(banuḱa)",
+       "historysize": "({{PLURAL:$1 1 ᱵᱟᱭᱤᱴ $1 ᱵᱟᱭᱤᱴᱥ}})",
+       "historyempty": "(ᱠᱷᱟᱹᱞᱤ)",
        "history-feed-title": "ᱥᱩᱫᱷᱨᱟᱹᱣ ᱱᱟᱜᱟᱢ",
        "history-feed-description": "ᱩᱭᱠᱤᱨᱮ ᱱᱤᱭᱟᱹ ᱥᱟᱦᱴᱟ ᱵᱚᱫᱚᱞ ᱨᱮᱱᱟᱜ ᱱᱟᱜᱟᱢ",
-       "history-feed-item-nocomment": "re",
-       "rev-deleted-comment": "(Sompadon reaḱ guṭ katha do ocoǵ hoena)",
-       "rev-deleted-user": "(laṛcaṛić ńutum ocoḱena)",
+       "history-feed-item-nocomment": "$2 ᱨᱮ $1",
+       "rev-deleted-comment": "(ᱥᱟᱯᱲᱟᱣ ᱢᱩᱬᱩᱛ ᱠᱟᱛᱦᱟ ᱚᱪᱟᱜᱠᱟᱱᱟ)",
+       "rev-deleted-user": "(ᱵᱮᱵᱷᱟᱨᱤᱭᱟᱜ ᱧᱩᱛᱩᱢ ᱚᱪᱚᱜᱠᱟᱱᱟ)",
        "rev-deleted-event": "(Log kạmi do ocog hoena)",
-       "rev-deleted-user-contribs": "[Beoharićaḱ ńutum se IP ṭhikạna do ocog hoena - kạmi khon sompadon do uku hoe akana]",
-       "rev-delundel": "Juḍawaḱko ńel",
-       "rev-showdeleted": "Uduḱme",
+       "rev-deleted-user-contribs": "[ᱵᱮᱵᱷᱟᱨᱤᱡ ᱧᱩᱛᱩᱢ ᱥᱮ IP ᱴᱷᱤᱠᱟᱱᱟ ᱫᱚ ᱚᱪᱟᱜ ᱦᱩᱭᱱᱟ - ᱮᱱᱮᱢ ᱠᱷᱚᱱ ᱥᱟᱯᱲᱟᱣ ᱫᱚ ᱩᱠᱩ ᱟᱠᱟᱱᱟ]",
+       "rev-delundel": "ᱧᱮᱧᱮᱞᱟᱜ ᱵᱚᱫᱚᱞ",
+       "rev-showdeleted": "ᱥᱚᱫᱚᱨ",
        "revisiondelete": "ᱜᱮᱫ ᱜᱤᱰᱤ/ᱵᱟᱝ ᱜᱮᱫ ᱜᱤᱰᱤ ᱥᱩᱫᱷᱨᱟᱹᱣᱠᱚ",
-       "revdelete-show-file-submit": "Hẽ",
+       "revdelete-show-file-submit": "ᱦᱮᱸ",
        "revdelete-hide-text": "Nãwã aroe olko ukuemẽ",
-       "revdelete-hide-image": "Rẽt reaḱ babotko ukue mẽ",
+       "revdelete-hide-image": "ᱨᱮᱫ ᱥᱟᱛᱚᱢᱠᱩ ᱩᱠᱩᱭᱢᱮ",
        "revdelete-hide-name": "Kạmi ar bejha ukue mẽ",
        "revdelete-hide-comment": "Sompadon gut katha ukue mẽ",
        "revdelete-hide-user": "Sompadonićaḱ beohar ńutum/IP ṭhikạna ukuemẽ",
-       "revdelete-radio-same": "(alom bodola)",
+       "revdelete-radio-same": "(ᱟᱞᱚᱢ ᱵᱚᱫᱚᱞᱟ)",
        "revdelete-radio-set": "ᱩᱠᱩ",
        "revdelete-radio-unset": "ᱧᱮᱞ ᱜᱟᱱᱚᱜ",
-       "revdelete-log": "Babot:",
-       "revdel-restore": "Judạ lekate ńel",
+       "revdelete-log": "ᱚᱡᱮ:",
+       "revdel-restore": "ᱧᱮᱧᱮᱞᱟᱜ ᱵᱚᱫᱚᱞ",
        "pagehist": "ᱥᱟᱦᱴᱟ ᱱᱟᱜᱟᱢ",
-       "deletedhist": "Get giḍi jạṛ",
-       "revdelete-reasonotherlist": "Eṭaḱak karon",
+       "deletedhist": "ᱜᱮᱫ ᱜᱤᱰᱤᱭᱟᱠᱟᱱ ᱱᱟᱜᱟᱢ",
+       "revdelete-reasonotherlist": "ᱮᱴᱟᱜ ᱚᱡᱮ",
        "mergehistory-from": "ᱯᱷᱮᱰᱟᱛ ᱥᱟᱦᱴᱟᱺ",
        "mergehistory-reason": "ᱚᱡᱮ:",
        "mergelog": "ᱞᱚᱜᱽ ᱢᱮᱥᱟ",
-       "revertmerge": "bań mit́",
+       "revertmerge": "ᱵᱟᱝ ᱢᱤᱛ",
        "history-title": "\"$1\" ᱨᱮᱱᱟᱜ ᱥᱩᱫᱷᱨᱟᱹᱣ ᱱᱟᱜᱟᱢ",
        "difference-title": "\"$1\" ᱨᱮᱱᱟᱜ ᱫᱚᱦᱲᱟᱭᱮᱱ ᱛᱟᱞᱟᱨᱮ ᱯᱷᱟᱨᱟᱠ",
        "lineno": "ᱫᱷᱟᱹᱲ $1:",
        "diff-multi-sameuser": "({{PLURAL:$1|ᱢᱤᱫ ᱛᱟᱞᱟ-ᱢᱟᱞᱟ ᱫᱚᱦᱲᱟ|$1 ᱛᱟᱞᱟ-ᱢᱟᱞᱟ ᱫᱚᱦᱲᱟᱠᱚ}} ᱥᱚᱢᱟᱱ ᱵᱮᱵᱷᱟᱨᱤᱭᱟᱹ ᱫᱟᱨᱟᱭᱛᱮ ᱵᱟᱭ ᱧᱮᱞᱚᱜ-ᱟ)",
        "diff-multi-otherusers": "({{PLURAL:$1|ᱢᱤᱫ ᱛᱟᱞᱟ-ᱢᱟᱞᱟ ᱫᱚᱦᱲᱟ|$1 ᱛᱟᱞᱟ-ᱢᱟᱞᱟ ᱫᱚᱦᱲᱟᱠᱚ}} {{PLURAL:$2|ᱢᱤᱫ ᱮᱴᱟᱜ ᱵᱮᱵᱷᱟᱨᱤᱭᱟᱹ|$2 ᱵᱮᱵᱷᱟᱨᱤᱭᱟᱹᱠᱚ}} ᱵᱟᱠᱚ ᱧᱮᱞᱚᱜ-ᱟ)",
        "searchresults": "ᱥᱮᱸᱫᱽᱨᱟ ᱚᱨᱡᱚᱠᱳ",
-       "searchresults-title": "\"$1\"  á±¨á±®á±±á±\9fá±\9c á±¥á±®á±¸á±«á±½á±¨á±\9f á±¯á±·á±\9aá±\9e",
-       "prevn": "Laha reaḱ {{PLURAL:$1|$1}}",
-       "nextn": "Táyom teaḱ {{PLURAL:$1|$1}}",
+       "searchresults-title": "\"$1\"  á±¨á±®á±±á±\9fá±\9c á±¥á±®á±¸á±«á±½á±¨á±\9f á±\9aᱨᱡá±\9a",
+       "prevn": "ᱞᱟᱦᱟᱛᱮᱱᱟᱜ {{PLURAL:$1|$1}}",
+       "nextn": "ᱛᱟᱭᱚᱢᱛᱮᱱᱟᱜ {{PLURAL:$1|$1}}",
        "prev-page": "ᱯᱟᱪᱮ ᱥᱟᱦᱴᱟ",
        "next-page": "ᱫᱟᱨᱟᱭ ᱥᱟᱦᱴᱟ",
        "prevn-title": "ᱞᱟᱦᱟᱛᱮᱱᱟᱜ $1 {{PLURAL:$1|ᱚᱨᱡᱚ|ᱚᱨᱡᱚᱠᱚ}}",
-       "nextn-title": "Tayom $1 {{PLURAL:$1|result|results}}",
+       "nextn-title": "ᱛᱟᱭᱚᱢᱛᱮᱱᱟᱜ $1 {{PLURAL:$1|ᱚᱨᱡᱚ|ᱚᱨᱡᱚᱠᱚ}}",
        "shown-title": "ᱥᱟᱦᱴᱟ $1 {{PLURAL:$1|ᱚᱨᱡᱚ|ᱚᱨᱡᱚᱠᱳ}} ᱩᱰᱩᱜᱽᱢᱮ",
-       "viewprevnext": "Ńelme ($1 {{int:pipe-separator}} $2) ($3)",
+       "viewprevnext": "ᱧᱮᱞᱢᱮ ($1 {{int:pipe-separator}} $2) ($3)",
        "searchmenu-exists": "<strong>ᱥᱟᱦᱴᱟ ᱧᱤᱛᱩᱢ ᱫᱚ \"[[:$1]]\" ᱱᱤᱭᱟᱹ ᱩᱭᱠᱤᱨᱮ᱾</strong> {{PLURAL:$2|0=|ᱟᱨᱦᱚᱸ ᱧᱮᱞᱢᱮ ᱮᱴᱟᱜ ᱥᱮᱸᱫᱽᱨᱟ ᱟᱨᱡᱚ ᱠᱚᱨᱮ᱾}}",
        "searchmenu-new": "<strong>ᱥᱟᱦᱴᱟ ᱛᱮᱭᱟᱨ ᱢᱮ \"[[:$1]]\" ᱱᱚᱶᱟ ᱣᱤᱠᱤ ᱨᱮ!</strong> {{PLURAL:$2|0=|ᱟᱢᱟᱜ ᱥᱮᱸᱫᱽᱨᱟ ᱛᱮ ᱧᱟᱢᱮᱱ ᱥᱟᱦᱴᱟ ᱧᱮᱞᱢᱮ|ᱧᱟᱢᱮᱱ ᱥᱮᱸᱫᱽᱨᱟ ᱚᱨᱡᱚ ᱠᱚ ᱦᱚᱸ ᱧᱮᱞᱢᱮ}}",
        "searchprofile-articles": "ᱩᱱᱩᱫᱩᱜ ᱥᱟᱦᱴᱟᱠᱚ",
-       "searchprofile-images": "Multimedia",
+       "searchprofile-images": "ᱢᱟᱞᱴᱤᱢᱤᱰᱤᱭᱟ",
        "searchprofile-everything": "ᱡᱚᱛᱚᱜᱮ",
-       "searchprofile-advanced": "Sompadon",
+       "searchprofile-advanced": "ᱢᱟᱲᱟᱝ",
        "searchprofile-articles-tooltip": "$1 ᱨᱮ ᱥᱮᱸᱫᱽᱨᱟᱭᱢᱮ",
        "searchprofile-images-tooltip": "ᱨᱮᱫᱠᱩ ᱥᱮᱸᱫᱽᱨᱟ",
        "searchprofile-everything-tooltip": "ᱡᱚᱛᱚ ᱥᱟᱛᱚᱢ ᱥᱟᱦᱴᱟᱨᱮ ᱥᱮᱸᱫᱽᱨᱟᱭ ᱢᱮ (ᱨᱚᱲ ᱥᱟᱦᱴᱟ ᱠᱚᱦᱚᱸ)",
        "searchprofile-advanced-tooltip": "ᱵᱮᱱᱟᱣ ᱧᱤᱛᱩᱢ ᱛᱮ ᱥᱮᱸᱫᱽᱨᱟ",
        "search-result-size": "$1 ({{PLURAL:$2|1 ᱟᱹᱲᱟᱹ|$2 ᱟᱹᱲᱟᱹᱠᱳ}})",
-       "search-result-category-size": "{{PLURAL:$1 1 gãoren $1 gãota renko}} ({{PLURAL:$2 1 kạṭic rokom sokom $ 2 goṭen}}, {{PLURAL:$3 1 rẽt $3 rẽtko}})",
+       "search-result-category-size": "{{PLURAL:$1|1 ᱥᱚᱦᱮᱫ|$1 ᱥᱚᱦᱮᱫᱠᱩ}} ({{PLURAL:$2|1 ᱦᱩᱰᱤᱧ ᱛᱷᱚᱠ|$ 2 ᱦᱩᱰᱤᱧ ᱛᱷᱚᱠᱠᱩ}}, {{PLURAL:$3|1 ᱨᱮᱫ|$3 ᱨᱮᱫᱠᱩ}})",
        "search-redirect": "(ᱥᱚᱡᱷᱮ ᱦᱤᱡᱩᱜ-ᱟ $1 ᱠᱷᱚᱱ)",
-       "search-section": "(Pahaṭa $1)",
+       "search-section": "(ᱛᱷᱟᱠ $1)",
        "search-file-match": "(ᱢᱤᱫᱩᱜᱟᱜ ᱨᱮᱫ ᱩᱱᱩᱫᱩᱜ)",
        "search-suggest": "ᱪᱮᱫ ᱮᱢ ᱢᱮᱱ ᱚᱪᱚᱭᱮᱫᱟ: $1",
        "search-interwiki-caption": "ᱦᱚᱯᱚᱱ ᱯᱨᱚᱡᱮᱠᱴ ᱠᱷᱚᱱ ᱚᱨᱡᱚ",
        "search-interwiki-default": "$1 folko:",
-       "search-interwiki-more": "(Arhõ)",
+       "search-interwiki-more": "(ᱵᱟᱹᱲᱛᱤ)",
        "search-interwiki-more-results": "ᱵᱟᱹᱲᱛᱤ ᱚᱨᱡᱚᱠᱚ",
        "search-relatedarticle": "ᱥᱟᱹᱜᱟᱹᱭᱟᱱ",
-       "searchrelated": "songenko",
+       "searchrelated": "ᱥᱟᱹᱜᱟᱹᱭᱟᱱᱠᱩ",
        "searchall": "ᱡᱚᱛᱚ",
        "search-showingresults": "{{PLURAL:$4|ᱚᱨᱡᱚ <strong>$1</strong> ᱨᱮᱱᱟᱜ <strong>$3</strong>|ᱚᱨᱡᱚᱠᱚ <strong>$1 - $2</strong> ᱨᱮᱱᱟᱜ <strong>$3</strong>}}",
-       "search-nonefound": "Kupuli leka roṛruạṛ bạnuḱa",
+       "search-nonefound": "ᱠᱩᱠᱞᱤ ᱥᱟᱶᱛᱮ ᱯᱷᱚᱞ ᱵᱟᱝ ᱢᱤᱞᱟᱹᱮ ᱞᱮᱱᱟ᱾",
        "powersearch-ns": "ᱨᱟᱠᱷᱟ ᱧᱩᱛᱩᱢ ᱨᱮ ᱥᱮᱸᱫᱽᱨᱟ",
-       "powersearch-togglelabel": "Sendra",
-       "powersearch-toggleall": "Sanamaḱ",
-       "powersearch-togglenone": "Okaṭaḱ hõ baṅ",
+       "powersearch-togglelabel": "ᱠᱷᱚᱸᱡᱽ:",
+       "powersearch-toggleall": "ᱡᱚᱛᱚ",
+       "powersearch-togglenone": "ᱚᱠᱟᱴᱟᱜ ᱦᱚᱸ ᱵᱟᱝ",
        "preferences": "ᱠᱩᱥᱤᱠᱚ",
        "mypreferences": "ᱠᱩᱥᱤᱠᱚ",
        "prefs-edits": "ᱥᱟᱯᱲᱟᱣᱟᱜ ᱮᱞ:",
-       "prefs-skin": "Harta",
-       "skin-preview": "Ńel, Unuduḱ",
-       "datedefault": "Pạsind banuḱa",
+       "prefs-skin": "ᱦᱟᱨᱛᱟ",
+       "skin-preview": "ᱧᱮᱞ ᱵᱤᱰᱟᱹᱣ",
+       "datedefault": "ᱠᱩᱥᱤ ᱵᱟᱹᱱᱩᱜ-ᱟ",
        "prefs-user-pages": "ᱵᱮᱵᱦᱟᱨᱤᱭᱟᱜ ᱥᱟᱦᱴᱟᱠᱚ",
-       "prefs-resetpass": "Uku nombor bodolme",
+       "prefs-resetpass": "ᱫᱟᱱᱟᱝ ᱥᱟᱵᱟᱫᱽ ᱵᱚᱫᱚᱞ",
        "prefs-changeemail": "E-mail ṭhikạna bodolme",
-       "prefs-setemail": "E-mail ṭhikana benaome",
-       "saveprefs": "Rukhiyạymẽ",
-       "searchresultshead": "Sendra",
-       "timezoneregion-africa": "Aphrika",
-       "timezoneregion-america": "Amirika",
-       "timezoneregion-asia": "Esiya",
-       "timezoneregion-australia": "Ausṭralia",
+       "prefs-setemail": "ᱤᱢᱮᱞ ᱵᱩᱴᱟᱹ ᱡᱚᱲᱟᱣᱢᱮ",
+       "saveprefs": "ᱨᱩᱠᱷᱤᱭᱟᱹᱭᱢᱮ",
+       "searchresultshead": "ᱥᱮᱸᱫᱽᱨᱟ",
+       "timezoneregion-africa": "ᱟᱯᱷᱨᱤᱠᱟ",
+       "timezoneregion-america": "ᱟᱢᱮᱨᱤᱠᱟ",
+       "timezoneregion-asia": "ᱮᱥᱤᱭᱟ",
+       "timezoneregion-australia": "ᱚᱥᱴᱨᱮᱞᱤᱭᱟ",
        "prefs-searchoptions": "ᱥᱮᱸᱫᱽᱨᱟ",
-       "prefs-files": "Rẽtko",
-       "youremail": "E-mail:",
+       "prefs-files": "ᱨᱮᱫᱠᱚ",
+       "youremail": "Email:",
        "username": "Beoharićaḱ ńutum:",
-       "yourrealname": "Sạri ńutum",
+       "yourrealname": "ᱟᱥᱚᱞ ᱧᱩᱛᱩᱢ:",
        "yourlanguage": "ᱯᱟᱹᱨᱥᱤ:",
        "yournick": "ᱱᱟᱶᱟ ᱥᱩᱦᱤ:",
        "gender-male": "Baba hoṛ",
        "gender-female": "Gogo hoṛ, Kuṛi, Kuṛi gidrạ",
-       "email": "E-mail",
+       "email": "Email",
        "prefs-help-email": "E-mail ṭhikana do bạṛtitege, menkhan uku namber nãwãte benao jạruṛa, am do amaḱ uku nomborem hiṛiń keda.",
        "prefs-help-email-others": "Am são e-mail hotete jogajog dohoy lạgitte mitṭen joṛao se amaḱ katha roṛaḱ sakam bachao jońme.\nAmaḱ e-mail ṭhikạna do bań cabaḱa tinre onko do ko beohara",
        "prefs-signature": "ᱥᱩᱦᱤ",
        "prefs-preview": "ᱧᱮᱞ ᱵᱤᱰᱟᱹᱣ",
        "userrights": "Beoharićaḱ laṛcaṛ ektiạrko",
        "userrights-lookup-user": "Beoharkoaḱ gãotako laṛcaṛ",
-       "userrights-user-editname": "Beoharićaḱ ńutum emmẽ",
+       "userrights-user-editname": "ᱵᱮᱵᱦᱟᱨᱤᱭᱟᱜ ᱧᱤᱛᱩᱢ ᱵᱚᱞᱚᱭ ᱢᱮ:",
        "editusergroup": "Beoharićaḱ gãotako toṅgeymẽ",
        "userrights-editusergroup": "Beoharićaḱ gãotako toṅgeymẽ",
        "saveusergroups": "Beoharićaḱ gãotako rukhiyaymẽ",
        "group-sysop": "ᱟᱰᱢᱤᱱᱤᱥᱴᱨᱮᱴᱚᱨ",
        "grouppage-bot": "{{ns:project}}:ᱵᱚᱴᱠᱚ",
        "grouppage-sysop": "{{ns:project}}:ᱯᱟᱨᱜᱟᱱᱟᱠᱚ",
-       "right-read": "Sakamko paṛhaomẽ",
-       "right-edit": "Sakamko toṅge",
+       "right-read": "ᱥᱟᱦᱴᱟᱠᱩ ᱯᱟᱲᱦᱟᱣᱢᱮ",
+       "right-edit": "ᱥᱟᱦᱴᱟᱠᱩ ᱥᱟᱯᱲᱟᱣ",
        "right-createpage": "Sakamko benoamẽ (Okako do galmarao sakamko baṅkan)",
-       "right-createtalk": "Galmarao sakamko benaomẽ",
+       "right-createtalk": "ᱜᱟᱞᱢᱟᱨᱟᱣ ᱥᱟᱦᱴᱟᱠᱩ ᱵᱮᱱᱟᱣᱢᱮ",
        "right-createaccount": "ᱱᱟᱶᱟ ᱵᱮᱵᱷᱟᱨᱤᱭᱟᱹ ᱠᱷᱟᱛᱟ ᱛᱮᱭᱟᱨ",
-       "right-move": "Sakamko ocogmẽ",
+       "right-move": "ᱥᱟᱦᱴᱟᱠᱚ ᱥᱟᱦᱟᱭᱢᱮ",
        "right-move-subpages": "ᱥᱟᱦᱴᱟ ᱥᱟᱦᱟᱦᱟᱭᱢᱮ ᱥᱟᱶᱛᱮᱱ ᱥᱟᱦᱴᱟᱠᱚ ᱥᱟᱶ",
-       "right-movefile": "Rẽtko ocogmẽ",
-       "right-upload": "Rẽtko rakabmẽ",
+       "right-movefile": "ᱨᱮᱫᱠᱚ ᱚᱪᱚᱜᱽ ᱢᱮ",
+       "right-upload": "ᱨᱮᱫ ᱠᱚ ᱞᱟᱫᱮᱢᱮ",
        "right-writeapi": "ᱚᱞ API ᱨᱮᱱᱟᱜ ᱵᱮᱵᱷᱟᱨ",
-       "right-delete": "Sakamko get giḍiymẽ",
+       "right-delete": "ᱥᱟᱦᱴᱟᱠᱚ ᱜᱮᱫᱽ ᱢᱮ",
        "right-browsearchive": "ᱥᱮᱸᱫᱽᱨᱟᱭ ᱢᱮ ᱜᱮᱫ ᱟᱠᱟᱱᱟ ᱥᱟᱦᱴᱟᱠᱚ",
-       "newuserlogpage": "Laṛcaṛićaḱ tear cạbi",
+       "newuserlogpage": "ᱵᱮᱵᱦᱟᱨᱤᱭᱟᱜ ᱛᱮᱭᱟᱨ ᱪᱟᱹᱵᱤ",
        "rightslog": "ᱵᱮᱵᱷᱟᱨᱤᱭᱟᱹ ᱟᱹᱭᱫᱟᱹᱨ ᱞᱚᱜᱽ",
        "action-edit": "ᱱᱚᱭᱟ ᱥᱟᱦᱴᱟ ᱥᱟᱯᱲᱟᱣᱢᱮ",
        "action-createaccount": "ᱱᱚᱶᱟ ᱵᱮᱵᱷᱟᱨᱤᱭᱟᱹ ᱠᱷᱟᱛᱟ ᱵᱮᱱᱟᱣ",
        "action-browsearchive": "ᱥᱮᱸᱫᱽᱨᱟᱭ ᱢᱮ ᱜᱮᱫ ᱟᱠᱟᱱᱟ ᱥᱟᱦᱴᱟᱠᱚ",
        "action-undelete": "ᱵᱟᱝ ᱜᱮᱫᱟᱜ ᱥᱟᱦᱴᱟᱠᱚ",
        "action-suppressrevision": "ᱧᱮᱞᱟᱹᱨᱩ ᱟᱨ ᱛᱷᱟᱯᱚᱱᱟᱹᱨᱩ ᱫᱟᱱᱟᱝ ᱧᱮᱞᱟᱹᱨᱩᱠᱚ",
-       "nchanges": "$1 {{PLURAL:$1 bodol bodolko}}",
+       "nchanges": "$1 {{PLURAL:$1|ᱟᱹᱨᱩ|ᱟᱹᱨᱩᱠᱚ}}",
        "enhancedrc-history": "ᱱᱟᱜᱟᱢ",
        "recentchanges": "ᱨᱚᱠᱟ ᱵᱚᱫᱚᱞᱠᱚ",
-       "recentchanges-legend": "Nahaḱ bodol teaḱko",
+       "recentchanges-legend": "ᱱᱟᱣᱟᱱᱟ ᱵᱚᱫᱚᱞ ᱛᱮᱭᱟᱜᱠᱚ",
        "recentchanges-summary": "ᱱᱚᱣᱟ ᱥᱟᱦᱴᱟᱨᱮ ᱩᱭᱠᱤ ᱨᱮᱭᱟᱜ ᱡᱚᱛᱚ ᱠᱷᱚᱱ ᱱᱟᱣᱟ ᱵᱚᱫᱚᱞᱠᱚ ᱯᱟᱸᱡᱟᱸᱭᱢᱮ᱾",
        "recentchanges-noresult": "ᱮᱢᱞᱮᱱ ᱥᱚᱢᱚᱭ ᱵᱷᱤᱛᱤᱨ ᱨᱮ ᱵᱚᱫᱚᱞᱟᱜ ᱠᱚ ᱵᱟᱭ ᱢᱤᱫᱩᱜ ᱠᱟᱱᱟ ᱾",
        "recentchanges-feed-description": "ᱱᱚᱣᱟ feed ᱨᱮ ᱩᱭᱠᱤ ᱨᱮᱭᱟᱜ ᱡᱚᱛᱚ ᱠᱷᱚᱱ ᱱᱟᱣᱟ ᱵᱚᱫᱚᱞᱠᱚ ᱯᱟᱸᱡᱟᱸᱭᱢᱮ᱾",
        "recentchanges-label-newpage": "ᱱᱚᱣᱟ ᱥᱟᱯᱲᱟᱣ ᱢᱤᱫᱴᱮᱱ ᱱᱟᱣᱟ ᱥᱟᱦᱴᱟᱭ ᱛᱮᱭᱟᱨᱠᱮᱫᱟ",
        "recentchanges-label-minor": "ᱱᱚᱣᱟ ᱫᱚ ᱦᱩᱰᱤᱧ ᱥᱟᱯᱲᱟᱣ ᱠᱟᱱᱟ",
        "recentchanges-label-bot": "ᱱᱚᱣᱟ ᱥᱟᱯᱲᱟᱣ ᱫᱚ ᱵᱚᱴ ᱮ ᱠᱚᱨᱟᱣᱠᱟᱫᱟ",
-       "recentchanges-label-unpatrolled": "Noa sompadon do ńit́ hạbić baṅ ńel ńamakana",
+       "recentchanges-label-unpatrolled": "ᱱᱚᱣᱟ ᱥᱟᱯᱲᱟᱣ ᱱᱤᱛ ᱫᱷᱟᱹᱵᱤᱡ ᱵᱟᱝ ᱧᱮᱞ ᱧᱟᱢ ᱟᱠᱟᱱᱟ",
        "recentchanges-label-plusminus": "ᱥᱟᱦᱴᱟ ᱫᱚ  ᱵᱚᱫᱚᱞᱮᱱᱟ ᱱᱤᱱᱟᱹᱜ ᱮᱞ ᱵᱟᱭᱤᱴᱥ ᱛᱮ",
        "recentchanges-legend-heading": "<strong>ᱞᱤᱡᱮᱸᱰ:</strong>",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (ᱟᱨᱦᱚᱸ ᱧᱮᱞᱢᱮ [[Special:NewPages|ᱱᱟᱶᱟ ᱥᱟᱦᱴᱟ ᱞᱤᱥᱴᱤ]])",
        "rcshowhidebots": "ᱵᱚᱴᱠᱚ $1",
        "rcshowhidebots-show": "ᱧᱮᱞ",
        "rcshowhidebots-hide": "ᱫᱟᱱᱟᱝ",
-       "rcshowhideliu": "Regisṭari beoharićko $1",
+       "rcshowhideliu": "ᱵᱚᱞᱚᱣᱠᱟᱱ ᱵᱮᱵᱟᱦᱟᱨᱤᱠᱩ $1",
        "rcshowhideliu-show": "ᱧᱮᱞ",
        "rcshowhideliu-hide": "ᱫᱟᱱᱟᱝ",
        "rcshowhideanons": "$1 ᱧᱤᱛᱩᱢ ᱵᱟᱱᱩᱜ ᱵᱮᱵᱦᱟᱨᱤᱪ",
        "rcshowhideanons-show": "ᱧᱮᱞ",
        "rcshowhideanons-hide": "ᱫᱟᱱᱟᱝ",
-       "rcshowhidepatr": "$1 Biḍạen sompadonko",
+       "rcshowhidepatr": "$1 ᱵᱤᱰᱟᱭᱮᱱ ᱥᱟᱯᱲᱟᱣᱠᱩ",
        "rcshowhidepatr-show": "ᱥᱚᱫᱚᱨ",
        "rcshowhidepatr-hide": "ᱫᱟᱱᱟᱝ",
        "rcshowhidemine": "$1 ᱤᱧᱟᱜ ᱥᱟᱯᱲᱟᱣᱠᱩ",
        "rcshowhidecategorization": "$1 ᱥᱟᱦᱴᱟ ᱛᱷᱚᱠ",
        "rcshowhidecategorization-show": "ᱧᱮᱞ",
        "rcshowhidecategorization-hide": "ᱫᱟᱱᱟᱝ",
-       "rclinks": "$2 din lahare $1 bodol unuduḱme",
+       "rclinks": "$2 ᱫᱤᱱ ᱞᱟᱦᱟᱨᱮ $1 ᱵᱚᱫᱚᱞ ᱩᱫᱩᱜᱽᱢᱮ",
        "diff": "ᱡᱩᱫᱟᱹ",
        "hist": "ᱱᱟᱜᱟᱢ",
        "hide": "ᱫᱟᱱᱟᱝ",
        "boteditletter": "b",
        "rc-change-size-new": "$1 {{PLURAL:$1|ᱵᱟᱭᱤᱴ|ᱵᱟᱭᱤᱴᱥ}} ᱵᱚᱫᱚᱞ ᱛᱟᱭᱚᱢ",
        "rc-enhanced-expand": "Purạote uduḱ",
-       "rc-enhanced-hide": "Purạo cuku",
+       "rc-enhanced-hide": "ᱡᱚᱛᱚᱭᱟᱜ ᱩᱠᱩᱭᱢᱮ",
        "rc-old-title": "ᱚᱥᱚᱞᱨᱮ ᱛᱮᱭᱟᱨᱟᱠᱟᱱᱟ \"$1\" ᱞᱮᱠᱟᱛᱮ",
        "recentchangeslinked": "ᱥᱟᱶᱛᱮᱱᱟᱜ ᱵᱚᱫᱚᱞᱠᱚ",
        "recentchangeslinked-feed": "ᱥᱟᱹᱜᱟᱹᱭᱟᱱ ᱵᱚᱫᱚᱞᱠᱚ",
        "recentchangeslinked-toolbox": "ᱥᱟᱹᱜᱟᱹᱭᱟᱱ ᱵᱚᱫᱚᱞᱠᱚ",
-       "recentchangeslinked-title": "Bodolaḱko do \"$1\" sãote joṛao geya",
-       "recentchangeslinked-summary": "ᱱᱚᱣᱟ ᱫᱚ ᱚᱱᱟ ᱛᱟᱹᱞᱠᱟᱹ ᱠᱟᱱᱟ ᱚᱠᱟ ᱫᱟ ᱱᱮᱵᱮᱛᱟᱨᱜᱮ ᱵᱚᱫᱚᱞ ᱦᱩᱭ ᱟᱠᱟᱱᱟ ᱚᱠᱟ ᱫᱚ category ᱦᱟᱛᱟᱣ ᱟᱠᱟᱱ ᱥᱟᱠᱟᱢ ᱠᱷᱚᱱ᱾\n\n[[Special:Watchlist|your watchlist]] ᱨᱮᱭᱟᱜ ᱥᱟᱦᱴᱟ ᱫᱚ'''bold''' .",
+       "recentchangeslinked-title": "ᱵᱚᱫᱚᱞᱟᱜ ᱠᱚᱫᱚ \"$1\" ᱥᱟᱶᱛᱮ ᱡᱚᱲᱟᱣ ᱜᱮᱭᱟ",
+       "recentchangeslinked-summary": "ᱱᱚᱣᱟ ᱫᱚ ᱚᱱᱟ ᱛᱟᱹᱞᱠᱟᱹ ᱠᱟᱱᱟ ᱚᱠᱟ ᱫᱟ ᱱᱮᱵᱮᱛᱟᱨᱜᱮ ᱵᱚᱫᱚᱞ ᱦᱩᱭ ᱟᱠᱟᱱᱟ ᱚᱠᱟ ᱫᱚ category ᱦᱟᱛᱟᱣ ᱟᱠᱟᱱ ᱥᱟᱠᱟᱢ ᱠᱷᱚᱱ᱾\n\n[[Special:Watchlist|your watchlist]] ᱨᱮᱭᱟᱜ ᱥᱟᱦᱴᱟ ᱫᱚ '''bold''' .",
        "recentchangeslinked-page": "ᱥᱟᱦᱴᱟ ᱧᱤᱛᱩᱢ :",
        "recentchangeslinked-to": "ᱡᱚᱱᱚᱲ ᱥᱟᱦᱴᱟᱨᱮ ᱧᱮᱞ ᱚᱪᱚᱭ ᱢᱮ ᱮᱢᱟᱜ ᱥᱟᱦᱴᱟ ᱵᱟᱹᱜᱤ ᱠᱟᱛᱮ",
        "upload": "ᱨᱮᱫ ᱞᱟᱫᱮᱢᱮ",
-       "uploadbtn": "Rẽt rakabmẽ",
-       "uploadlogpage": "Chạbi do uthạome",
-       "filename": "Rẽt ńutum",
-       "filedesc": "Guṭ katha",
-       "fileuploadsummary": "Guṭ katha",
+       "uploadbtn": "ᱨᱮᱫ ᱞᱟᱫᱮ",
+       "uploadlogpage": "ᱨᱟᱠᱟᱵ ᱛᱟᱹᱞᱠᱟᱹ",
+       "filename": "ᱨᱮᱫ ᱧᱩᱛᱩᱢ",
+       "filedesc": "ᱢᱩᱬᱩᱛ ᱠᱟᱛᱦᱟ",
+       "fileuploadsummary": "ᱢᱩᱬᱩᱛ ᱠᱟᱛᱦᱟ:",
        "filestatus": "ᱟᱫᱚᱞ ᱚᱵᱚᱥᱛᱟ:",
-       "savefile": "Rẽt rukhiyaymẽ",
+       "savefile": "ᱨᱮᱫ ᱨᱩᱠᱷᱤᱭᱟᱹᱭᱢᱮ",
        "upload-source": "ᱯᱷᱮᱰᱟᱛ ᱨᱮᱫ",
        "sourcefilename": "ᱯᱷᱮᱰᱟᱛ ᱨᱮᱫᱧᱩᱢ:",
        "sourceurl": "ᱯᱷᱮᱰᱟᱛ URL:",
-       "upload-description": "Rẽt reaḱ jạṛ",
-       "watchthisupload": "Noa rẽt ńelmẽ",
-       "upload-file-error": "Bhitri reaḱ bhul",
+       "upload-description": "ᱨᱮᱫ ᱵᱤᱵᱚᱨᱚᱱᱤ",
+       "watchthisupload": "ᱱᱚᱣᱟ ᱨᱮᱫ ᱧᱮᱞᱢᱮ",
+       "upload-file-error": "ᱵᱷᱤᱛᱨᱤ ᱵᱷᱩᱞ",
        "upload-dialog-title": "ᱨᱮᱫ ᱞᱟᱫᱮ",
        "upload-dialog-button-cancel": "ᱵᱟᱫᱽ",
        "upload-dialog-button-back": "ᱛᱟᱭᱚᱢ",
        "upload-form-label-usage-filename": "ᱨᱮᱫ ᱧᱩᱛᱩᱢ",
        "upload-form-label-own-work": "ᱱᱚᱶᱟ ᱫᱚ ᱤᱧᱟᱜ ᱠᱟᱹᱢᱤᱭᱟᱜ",
        "upload-form-label-infoform-date": "ᱢᱟᱹᱦᱤᱛ",
-       "license": "Laisence benao",
-       "license-header": "Laisense benao",
+       "license": "ᱞᱟᱭᱥᱮᱱᱥ ᱛᱮᱭᱟᱨ:",
+       "license-header": "ᱞᱟᱭᱥᱮᱱᱥ ᱛᱮᱭᱟᱨ",
        "imgfile": "ᱨᱮᱫ",
        "listfiles": "ᱨᱮᱫ ᱛᱟᱹᱞᱠᱟᱹ",
-       "listfiles_date": "Tạrikh",
-       "listfiles_name": "Ńutum",
-       "listfiles_user": "Beoharić, Laṛcaṛic",
+       "listfiles_date": "ᱢᱟᱹᱦᱤᱛ",
+       "listfiles_name": "ᱧᱩᱛᱩᱢ",
+       "listfiles_user": "ᱵᱮᱵᱦᱟᱨᱤᱡ",
        "file-anchor-link": "ᱨᱮᱫ",
        "filehist": "ᱨᱮᱫ ᱨᱮᱭᱟᱜ ᱱᱟᱜᱟᱢ",
        "filehist-help": "ᱚᱠᱛᱚ ᱨᱮ ᱞᱤᱱ ᱢᱮ/ᱚᱠᱛᱚ ᱨᱮ ᱨᱮᱫ ᱧᱮᱞ ᱞᱟᱹᱜᱤᱛ ᱞᱤᱱ ᱢᱮ",
-       "filehist-deleteall": "Joto get giḍi",
-       "filehist-deleteone": "Get giḍi",
-       "filehist-revert": "Lahaleka",
+       "filehist-deleteall": "ᱡᱚᱛᱚ ᱜᱮᱫ ᱜᱤᱰᱤᱭᱢᱮ",
+       "filehist-deleteone": "ᱜᱮᱫ ᱜᱤᱰᱤ",
+       "filehist-revert": "ᱨᱩᱣᱟᱹᱲ ᱟᱹᱜᱩ",
        "filehist-current": "ᱱᱤᱛᱚᱜ",
        "filehist-datetime": "ᱛᱟᱹᱨᱤᱠᱷ/ᱚᱠᱛᱚ",
-       "filehist-thumb": "Ṭip",
+       "filehist-thumb": "ᱴᱤᱯ",
        "filehist-thumbtext": "Thumbnail for version as of $1",
        "filehist-nothumb": "ᱵᱟᱹᱱᱩᱜ-ᱟ ᱴᱤᱯ-ᱨᱟᱢᱟ",
        "filehist-user": "ᱵᱮᱵᱦᱟᱨᱤᱡᱽ",
        "upload-disallowed-here": "ᱟᱢᱫᱚ ᱱᱚᱣᱟ ᱨᱮᱫ ᱪᱮᱛᱟᱱ ᱵᱟᱢ ᱚᱞ ᱫᱟᱲᱮᱭᱟᱜ-ᱟ᱾",
        "mimesearch": "MIME ᱥᱮᱸᱫᱽᱨᱟ",
        "randompage": "ᱡᱚᱲᱟᱣ ᱥᱟᱦᱴᱟ",
-       "statistics": "Halot",
-       "statistics-pages": "Sakamko",
+       "statistics": "ᱦᱟᱞᱚᱛ",
+       "statistics-pages": "ᱥᱟᱦᱴᱟᱠᱚ",
        "double-redirect-fixer": "ᱢᱚᱸᱦᱟᱰᱟ ᱴᱷᱟᱹᱣᱠᱟᱹᱭᱤᱡ",
        "nbytes": "$1 {{PLURAL:$1|byte|bytes}}",
-       "nmembers": "$1 {{PLURAL:$1 Gaõtaren Gaõtarenko}}",
+       "nmembers": "$1 {{PLURAL:$1|ᱥᱚᱦᱮᱫ|ᱥᱚᱦᱮᱫᱠᱩ}}",
        "prefixindex": "ᱡᱚᱛᱚ ᱥᱟᱦᱴᱟᱠᱚ prefix ᱥᱟᱶ",
-       "shortpages": "Huḍiń sakamko",
-       "longpages": "Jiliń sakamko",
+       "shortpages": "ᱦᱩᱰᱤᱧ ᱥᱟᱦᱴᱟᱠᱚ",
+       "longpages": "ᱞᱟᱹᱴᱩ ᱥᱟᱦᱴᱟᱠᱚ",
        "listusers": "ᱵᱮᱵᱦᱟᱨᱤᱡ ᱛᱟᱹᱞᱠᱟᱹ",
        "listusers-creationsort": "ᱛᱮᱭᱟᱨᱟᱠᱟᱱ ᱢᱟᱹᱦᱤᱛ ᱞᱮᱠᱟᱛᱮ ᱯᱟᱱᱛᱮ",
-       "usercreated": "{{JẠT: $3 | benawakan}} $1 tarikre $2 okte",
+       "usercreated": "{{ᱡᱟᱱᱟᱝ:$3|ᱵᱮᱱᱟᱣᱠᱟᱱ}} $1 ᱢᱟᱹᱦᱤᱛᱨᱮ $2 ᱚᱠᱛᱚᱨᱮ",
        "newpages": "ᱱᱟᱶᱟ ᱥᱟᱦᱴᱟᱠᱳ",
-       "newpages-username": "Beoharićaḱ ńutum:",
-       "ancientpages": "Mare sakamko",
+       "newpages-username": "ᱵᱮᱵᱦᱟᱨᱤᱡ ᱧᱩᱛᱩᱢᱺ",
+       "ancientpages": "ᱢᱟᱨᱮ ᱥᱟᱦᱴᱟᱠᱚ",
        "move": "ᱚᱪᱚᱜ",
        "movethispage": "ᱱᱚᱶᱟ ᱥᱟᱦᱴᱟ ᱥᱟᱦᱟᱭᱢᱮ",
        "pager-newer-n": "{{PLURAL:$1|1 ᱱᱟᱣᱟᱱᱟᱜ | ᱱᱟᱣᱟᱱᱟᱜ $1}}",
-       "pager-older-n": "{{PLURAL:$1 arhõ mare 1ṭen arhõ mare $1ṭen}}",
+       "pager-older-n": "{{PLURAL:$1|ᱢᱟᱨᱮᱭᱟᱜ 1|ᱢᱟᱨᱮᱭᱟᱜ $1}}",
        "booksources": "ᱯᱚᱛᱚᱵ ᱯᱷᱮᱰᱟᱛ ᱦᱚᱨᱠᱟ",
        "booksources-search-legend": "ᱯᱚᱛᱚᱵ ᱨᱮᱭᱟᱜ ᱯᱷᱮᱰᱟᱛ ᱦᱚᱨ ᱞᱟᱹᱜᱤᱛ ᱥᱮᱸᱫᱽᱨᱟ",
        "booksources-search": "ᱥᱮᱸᱫᱽᱨᱟ",
        "specialloguserlabel": "ᱠᱟᱹᱢᱤᱭᱟᱹ:",
        "speciallogtitlelabel": "ᱡᱚᱥ (ᱧᱩᱛᱩᱢ ᱟᱨᱵᱟᱝ {{ns:user}}:ᱵᱮᱵᱷᱟᱨᱤᱭᱟᱹ ᱞᱟᱹᱜᱩᱫ ᱵᱮᱵᱷᱟᱨᱤᱭᱟᱹ ᱧᱩᱛᱩᱢ):",
-       "log": "Cạbiko",
+       "log": "ᱪᱟᱹᱵᱤᱠᱩ",
        "all-logs-page": "ᱡᱚᱛᱚ ᱫᱤᱥᱣᱟᱹ ᱞᱚᱜᱽ ᱠᱚ",
        "alllogstext": "ᱢᱮᱥᱟᱠᱟᱛᱮ ᱩᱫᱩᱜᱽᱢᱮ ᱡᱚᱛᱚ ᱢᱮᱱᱟᱜ {{SITENAME}} ᱞᱚᱜᱽᱠᱚ ᱾\nᱧᱮᱞᱚᱜ ᱠᱚᱢ ᱠᱟᱹᱡ ᱫᱟᱲᱮᱭᱟᱠᱚᱣᱟ ᱞᱚᱜᱽ ᱞᱮᱠᱟᱱ, ᱵᱮᱵᱷᱟᱨᱤᱭᱟᱹ ᱧᱩᱛᱩᱢ (case-sensitive), ᱟᱨᱵᱟᱝ ᱯᱨᱚᱵᱷᱟᱵᱟᱱ ᱥᱟᱦᱴᱟ (also case-sensitive) ᱠᱚ ᱵᱟᱪᱷᱚᱱ ᱠᱟᱛᱮ ᱾",
        "logempty": "ᱞᱚᱜᱽ ᱨᱮ ᱚᱱᱟᱞᱮᱠᱟᱱ ᱡᱤᱱᱤᱥ ᱵᱟᱹᱱᱩᱜ-ᱟ ᱾",
        "allpages-hide-redirects": "ᱢᱚᱦᱰᱟᱦᱟᱜᱠᱚ ᱫᱟᱱᱟᱝ",
        "categories": "ᱛᱷᱚᱠᱠᱚ",
        "linksearch-ok": "ᱥᱮᱸᱫᱽᱨᱟ",
-       "linksearch-line": "$2 khon $1 re joṛao hoeakana",
-       "listusers-submit": "Udugmẽ",
-       "listusers-blocked": "(Esetgea)",
-       "listgrouprights-group": "Gaõta",
-       "listgrouprights-rights": "Ạidạriko",
+       "linksearch-line": "$2 ᱠᱷᱚᱱ $1 ᱨᱮ ᱡᱚᱱᱚᱲ ᱟᱠᱟᱱ",
+       "listusers-submit": "ᱧᱮᱞ",
+       "listusers-blocked": "(ᱮᱥᱮᱫ ᱜᱮᱭᱟ)",
+       "listgrouprights-group": "ᱜᱟᱶᱛᱟ",
+       "listgrouprights-rights": "á±\9fᱹᱭᱫá±\9fᱹᱨᱤᱠá±\9a",
        "listgrouprights-helppage": "Goṛo:Gaõta ạidạri",
-       "listgrouprights-members": "Saõtenkoaḱ tạlika",
-       "listgrouprights-addgroup-all": "Joto gaõtare ko soṅgekom",
-       "listgrouprights-removegroup-all": "Joto gaõtaren ko ocoḱgiḍikom",
-       "emailuser": "Nui beoharić e-mail emayme",
-       "noemailtitle": "E-mail á¹­hikạna do banuḱa",
-       "emailusername": "Beoharićaḱ ńutum:",
-       "emailusernamesubmit": "Em",
-       "emailfrom": "Kulić:",
-       "emailto": "Ńamić:",
-       "emailmessage": "Mesag",
-       "emailsend": "Kulmẽ",
+       "listgrouprights-members": "(ᱥᱚᱦᱮᱫᱠᱩᱣᱟᱜ ᱛᱟᱹᱞᱠᱟᱹ)",
+       "listgrouprights-addgroup-all": "ᱡᱚᱛᱚ ᱜᱟᱶᱛᱟᱠᱩ ᱥᱮᱞᱮᱫ ᱠᱩ ᱢᱮ",
+       "listgrouprights-removegroup-all": "ᱡᱚᱛᱚ ᱜᱟᱶᱛᱟᱠᱩ ᱚᱪᱚᱜ ᱠᱩ ᱢᱮ",
+       "emailuser": "ᱱᱩᱭ ᱵᱮᱵᱦᱟᱨᱤᱡ e-mail ᱮᱢᱟᱭᱢᱮ",
+       "noemailtitle": "E-mail á±´á±·á±¤á± á±\9fᱹᱱá±\9f á±«á±\9a á±µá±\9fᱹᱱᱩá±\9c-á±\9f",
+       "emailusername": "ᱵᱮᱵᱦᱟᱨᱤᱡ ᱧᱩᱛᱩᱢᱺ",
+       "emailusernamesubmit": "ᱮᱢ",
+       "emailfrom": "ᱠᱩᱞᱤᱪ:",
+       "emailto": "ᱱᱟᱹᱢᱤᱪ:",
+       "emailmessage": "ᱠᱷᱚᱵᱚᱨ:",
+       "emailsend": "ᱠᱩᱞᱢᱮ",
        "usermessage-editor": "ᱥᱤᱥᱴᱚᱢ ᱨᱟᱭᱵᱟᱨ",
        "watchlist": "ᱧᱮᱞᱟᱜ ᱞᱤᱥᱴᱤ",
        "mywatchlist": "ᱧᱮᱞᱟᱜ ᱞᱤᱥᱴᱤ",
        "watchlistfor2": "$1 ($2) ᱞᱟᱹᱜᱤᱛ",
        "watch": "ᱧᱮᱞ",
-       "unwatch": "bang nelok' a",
+       "unwatch": "ᱵᱟᱱᱝ ᱧᱮᱞᱠᱟᱱ",
        "watchlist-details": "ᱵᱟᱝ ᱯᱩᱨᱟᱹᱣ ᱛᱟᱹᱞᱠᱟᱹᱨᱮ{{PLURAL:$1 ᱥᱟᱦᱴᱟ $1 ᱥᱟᱦᱴᱟ}} ᱢᱮᱱᱟᱜ-ᱟ (ᱨᱚᱲ ᱥᱟᱦᱴᱟ ᱠᱚᱦᱚᱸ)",
        "wlheader-showupdated": "ᱟᱢᱟᱜ ᱢᱩᱪᱟᱹᱫ ᱵᱚᱞᱚᱝᱨᱮ ᱡᱟᱸᱦᱟᱸ ᱥᱟᱦᱴᱟ ᱠᱚᱢ ᱵᱚᱫᱚᱞ ᱞᱮᱫᱟ ᱚᱱᱟᱠᱩ ᱧᱮᱞᱚᱜ-ᱟ <strong>bold</strong>.",
        "wlnote": "ᱞᱟᱛᱟᱨ ᱨᱮᱱᱟᱜ {{PLURAL:$1|ᱫᱚ ᱢᱩᱪᱟᱹᱫ ᱵᱚᱫᱚᱞ ᱠᱟᱱᱟ|ᱠᱚ ᱫᱚ ᱢᱩᱪᱟᱹᱫ <strong>$1</strong> ᱵᱚᱫᱚᱞᱠᱟᱱᱟ}} ᱢᱩᱪᱟᱹᱫ ᱨᱮ {{PLURAL:$2|ᱴᱟᱲᱟᱝ|<strong>$2</strong> ᱴᱟᱲᱟᱝ}},  $3, $4 ᱞᱮᱠᱟᱛᱮ ᱾",
        "wlshowlast": "ᱢᱩᱪᱟᱹᱛ ᱩᱫᱩᱜᱢᱮ $1 ᱴᱟᱲᱟᱝ $2 ᱢᱟᱦᱟᱸ",
-       "watchlist-options": "Ńelok tạlika reak sonketko",
-       "watching": "Ńeloḱ kana...",
+       "watchlist-options": "ᱧᱮᱞᱚᱜ ᱛᱟᱹᱞᱠᱟᱹ ᱨᱮᱭᱟᱜ ᱥᱚᱝᱠᱮᱛᱠᱩ",
+       "watching": "ᱧᱮᱞᱚᱜ ᱠᱟᱱᱟ...",
        "enotif_reset": "ᱱᱤᱱᱦᱟᱹᱭᱢᱮ ᱡᱚᱛᱚ ᱥᱟᱦᱴᱟ ᱦᱤᱨᱤᱭᱟᱠᱟᱱᱟ",
        "changed": "Bodolena",
        "deletepage": "ᱥᱟᱦᱴᱟ ᱜᱮᱫᱽ ᱢᱮ",
-       "delete-legend": "Get giḍi",
-       "actioncomplete": "kami Chabae-ena",
-       "actionfailed": "Kami bang hoe-lena",
-       "dellogpage": "Mãrao log",
+       "delete-legend": "ᱜᱮᱫ ᱜᱤᱰᱤ",
+       "actioncomplete": "ᱠᱟᱹᱢᱤ ᱪᱟᱵᱟᱭᱱᱟ",
+       "actionfailed": "ᱠᱟᱹᱢᱤ ᱵᱟᱝ ᱦᱩᱭᱞᱮᱱᱟ",
+       "dellogpage": "ᱢᱩᱪᱷᱟᱹᱣ ᱪᱟᱹᱵᱤ",
        "rollbacklink": "ᱜᱷᱩᱨᱞᱟᱹᱣ ᱟᱹᱪᱩᱨ",
        "rollbacklinkcount": "ᱜᱚᱰᱟᱣ ᱨᱩᱣᱟᱹᱲ $1 {{PLURAL:$1|ᱥᱟᱯᱲᱟᱣ|ᱥᱟᱯᱲᱟᱣᱠᱚ}}",
        "changecontentmodel-reason-label": "ᱚᱡᱮ:",
        "changecontentmodel-submit": "ᱵᱚᱫᱚᱞ",
-       "protectlogpage": "Rukhiyạy tala cạbi",
-       "protectedarticle": "Rukhiyạgeya \"[[$1]]\"",
+       "protectlogpage": "ᱨᱩᱠᱷᱤᱭᱟᱹᱭ ᱛᱟᱞᱟ ᱪᱟᱹᱵᱤ",
+       "protectedarticle": "ᱨᱩᱠᱷᱤᱭᱟᱹᱜᱮᱭᱟ \"[[$1]]\"",
        "modifiedarticleprotection": "\"[[$1]]\" ᱞᱟᱹᱜᱤᱫ ᱨᱩᱠᱷᱭᱟᱹ ᱟᱲᱮ ᱵᱚᱫᱚᱞᱮᱱᱟ",
-       "protectcomment": "karon",
-       "protectexpiry": "Cabaḱ",
+       "protectcomment": "ᱚᱡᱮ:",
+       "protectexpiry": "ᱢᱮᱭᱟᱫ ᱯᱟᱨᱚᱢ:",
        "protect-default": "ᱡᱚᱛᱚ ᱵᱮᱵᱷᱟᱨᱤᱭᱟᱹᱠᱚ ᱫᱟᱣ ᱮᱢ",
        "restriction-edit": "ᱥᱟᱯᱲᱟᱣ",
        "restriction-move": "ᱚᱪᱚᱜ",
-       "restriction-create": "Tearmẽ, Benaomẽ",
-       "undeletelink": "Ńel/doho ruạṛ",
-       "undeleteviewlink": "Ńel",
+       "restriction-create": "ᱛᱮᱭᱟᱨ",
+       "undeletelink": "ᱧᱮᱞ/ᱫᱚᱦᱚ ᱨᱩᱣᱟᱹᱲ",
+       "undeleteviewlink": "ᱧᱮᱞ",
        "undelete-search-submit": "ᱥᱮᱸᱫᱽᱨᱟ",
        "namespace": "ᱧᱤᱛᱩᱢ ᱡᱟᱜᱟ",
-       "invert": "Seć bachao",
+       "invert": "ᱥᱮᱪ ᱵᱟᱪᱷᱟᱣ",
        "tooltip-invert": "ᱱᱚᱶᱟ ᱵᱟᱠᱥᱟ ᱴᱤᱠ ᱢᱮ ᱥᱟᱦᱴᱟ ᱠᱷᱚᱱ ᱵᱚᱫᱚᱞᱟᱜᱠᱚ ᱫᱟᱱᱟᱝ ᱞᱟᱹᱜᱤᱫ  ᱵᱟᱛᱷᱚᱱ ᱨᱟᱠᱷᱟ ᱧᱩᱛᱩᱢ ᱥᱟᱶᱛᱮ (ᱟᱨ ᱡᱚᱯᱚᱲᱟᱣᱟᱱ ᱨᱟᱠᱷᱟ ᱧᱩᱛᱩᱢ ᱡᱩᱫᱤ ᱴᱤᱠ ᱟᱠᱟᱱᱟ)",
        "namespace_association": "ᱥᱚᱦᱚᱫᱤᱭᱟᱹ ᱨᱟᱠᱷᱟ ᱧᱩᱛᱩᱢ",
        "tooltip-namespace_association": "ᱱᱚᱶᱟ ᱵᱟᱠᱥᱟ ᱴᱤᱠ ᱢᱮ ᱨᱚᱯᱚᱲ ᱵᱟᱝᱠᱷᱟᱱ ᱥᱟᱛᱟᱢ ᱨᱟᱠᱷᱟ ᱧᱩᱛᱩᱢ ᱵᱟᱪᱷᱚᱱ ᱟᱠᱟᱱ ᱨᱟᱠᱷᱟ ᱧᱩᱛᱩᱢ ᱥᱟᱶ ᱡᱚᱯᱚᱲᱟᱣ ᱟᱠᱟᱱᱟᱜ",
        "blanknamespace": "(ᱢᱩᱬᱩᱛ)",
-       "contributions": "{{GENDER:$1|Beoharićaḱ }} Kạmiko",
-       "contributions-title": "$1 Beoharićaḱ kạmiko",
+       "contributions": "{{GENDER:$1|ᱵᱮᱵᱦᱟᱨᱤᱭᱟᱹ}} ᱮᱱᱮᱢᱠᱩ",
+       "contributions-title": "$1 ᱵᱮᱵᱦᱟᱨᱤᱭᱟᱜ ᱮᱱᱮᱢᱠᱩ",
        "mycontris": "ᱮᱱᱮᱢᱠᱩ",
        "anoncontribs": "ᱮᱱᱮᱢᱠᱩ",
        "contribsub2": "{{GENDER:$3|$1}} ($2) ᱞᱟᱹᱜᱤᱫ ᱛᱮ",
        "nocontribs": "ᱱᱚᱶᱟ ᱮᱢᱟᱜ ᱥᱟᱶ ᱡᱚᱲᱟᱣᱟᱱ ᱵᱚᱫᱚᱞᱠᱚ ᱵᱟᱭ ᱧᱟᱢᱞᱮᱱᱟ |",
        "uctop": "(ᱱᱤᱛᱚᱜ)",
-       "month": "Cando khon (ar etohopreaḱ)",
-       "year": "Nia serma reak' pahil khoch'",
+       "month": "ᱪᱟᱸᱫᱚ ᱠᱷᱚᱱ (ᱟᱨ ᱞᱟᱦᱟᱨᱮᱭᱟᱜ)",
+       "year": "ᱱᱚᱣᱟ ᱥᱮᱨᱢᱟ ᱠᱷᱚᱡ (ᱟᱨ ᱞᱟᱦᱟᱨᱮᱭᱟᱜ):",
        "sp-contributions-newbies": "ᱱᱟᱣᱟ ᱦᱤᱥᱟᱵ ᱨᱮᱱᱟᱜ ᱮᱱᱮᱢᱠᱚ ᱩᱫᱩᱜᱽ ᱢᱮ",
-       "sp-contributions-blocklog": "Tala eset",
-       "sp-contributions-uploads": "Rakaṕme",
-       "sp-contributions-logs": "Tala",
+       "sp-contributions-blocklog": "ᱠᱩᱞᱩᱯ ᱮᱥᱮᱫ",
+       "sp-contributions-uploads": "ᱞᱟᱫᱮᱠᱩ",
+       "sp-contributions-logs": "ᱛᱟᱞᱟᱠᱩ",
        "sp-contributions-talk": "ᱨᱚᱲ",
-       "sp-contributions-search": "Kạmiko emoḱ lạgitte sendrayme",
-       "sp-contributions-username": "IP á¹­hikạna se laá¹\9bcaá¹\9b\87aḱ nÌ\95utum",
+       "sp-contributions-search": "ᱮᱱᱮᱢᱠᱩ ᱞᱟᱹᱜᱤᱛ ᱥᱮᱸᱫᱨᱟ",
+       "sp-contributions-username": "IP á±´á±·á±¤á± á±\9fᱹᱱá±\9f á±¥á±® á±µá±®á±µá±¦á±\9fᱨᱤᱭá±\9fá±\9c á±§á±©á±\9bᱩᱢ",
        "sp-contributions-toponly": "ᱱᱮᱛᱟᱨ ᱥᱩᱫᱷᱨᱟᱹᱣ ᱠᱚᱨᱮᱭᱟᱜ ᱥᱟᱯᱲᱟᱣ ᱠᱟᱹᱢᱤᱠᱚ ᱩᱫᱩᱜᱽ ᱢᱮ",
        "sp-contributions-newonly": "ᱥᱩᱢᱩᱝ ᱟᱹᱨᱩᱠᱚ ᱥᱚᱫᱚᱨᱢᱮ ᱡᱟᱦᱟᱸ ᱥᱟᱦᱟᱴᱟ ᱫᱚ ᱥᱤᱨᱡᱟᱹᱣᱟᱜ ᱠᱟᱱᱟ",
        "sp-contributions-submit": "ᱥᱮᱸᱫᱽᱨᱟ",
        "linkshere": "ᱞᱟᱛᱟᱨ ᱨᱮᱭᱟᱜ ᱥᱟᱦᱴᱟᱠᱚ ᱫᱚ '''[[:$1]]''' ᱡᱚᱱᱚᱲ ᱢᱮᱱᱟᱜ-ᱟ :",
        "nolinkshere": "ᱥᱟᱦᱴᱟ ᱡᱚᱱᱚᱲ ᱵᱟᱱᱩᱜ-ᱟ ᱱᱤᱭᱟᱹ <strong>[[:$1]]</strong>.",
        "isredirect": "ᱵᱟᱝ ᱥᱚᱡᱽᱦᱮ ᱥᱟᱦᱴᱟ",
-       "istemplate": "Ar mit́ teć sãote joṛao",
+       "istemplate": "ᱥᱮᱞᱮᱫ",
        "isimage": "ᱨᱮᱫ ᱡᱚᱱᱚᱲ",
        "whatlinkshere-prev": "{{PLURAL:$1|ᱞᱟᱦᱟ ᱨᱮᱭᱟᱜ |ᱞᱟᱦᱟ ᱨᱮᱭᱟᱜ$1}}",
        "whatlinkshere-next": "{{PLURAL:$1 |ᱛᱟᱭᱚᱢ |ᱛᱟᱭᱚᱢ $1}}",
        "whatlinkshere-links": "← ᱡᱚᱱᱚᱲᱠᱚ",
-       "whatlinkshere-hideredirs": "$1 arhõ unuduḱ",
-       "whatlinkshere-hidetrans": "Selet́ $1",
+       "whatlinkshere-hideredirs": "$1 ᱟᱨᱦᱚᱸ ᱩᱱᱩᱫᱩᱜ",
+       "whatlinkshere-hidetrans": "ᱥᱮᱞᱮᱫ $1",
        "whatlinkshere-hidelinks": "$1 ᱡᱚᱱᱚᱲᱠᱚ",
        "whatlinkshere-hideimages": "$1 ᱨᱮᱫ ᱡᱳᱱᱳᱲᱠᱚ",
        "whatlinkshere-filters": "ᱪᱷᱟᱹᱱᱤᱠᱩ",
-       "block": "Beoharić esedem",
+       "block": "ᱵᱮᱵᱦᱟᱨᱤᱡ ᱮᱥᱮᱫᱮᱢ",
        "blockip": "Beoharić esedem",
-       "ipboptions": "2 Ghonṭa : 2 hours, 1 maha:1 day, 3 maha : 3 days,1 hapta :1 week, 2 hapta : 2 weeks, 1 cando :1 month, 3 cando : 3 months,6 cando :6 months,  1 serma :1 year,  Aemamaha : infinite",
+       "ipboptions": "᱒ ᱜᱷᱚᱱᱴᱟ : 2 hours, ᱑ ᱢᱟᱦᱟᱸ:1 day, ᱓ ᱢᱟᱦᱟᱸ : 3 days,᱑ ᱦᱟᱯᱛᱟ:1 week, ᱒ ᱦᱟᱯᱛᱟ : 2 weeks, ᱑ ᱪᱟᱸᱫᱚ :1 month, ᱓ ᱪᱟᱸᱫᱚ : 3 months, ᱖ ᱪᱟᱸᱫᱚ:6 months, ᱑ ᱥᱮᱨᱢᱟ:1 year, ᱥᱤᱢᱟᱹᱱᱟᱹ ᱪᱷᱟᱲᱟ : infinite",
        "autoblocklist-submit": "ᱥᱮᱸᱫᱽᱨᱟ",
-       "ipblocklist": "Beoharic esetgeyay",
-       "ipblocklist-submit": "Sendra",
+       "ipblocklist": "ᱵᱮᱵᱦᱟᱨᱤᱡ ᱮᱥᱮᱫᱜᱮᱭᱟᱭ",
+       "ipblocklist-submit": "ᱥᱮᱸᱫᱽᱨᱟ",
        "infiniteblock": "ᱚᱦᱤᱥᱟᱹᱵᱽ",
-       "emailblock": "E-mail do esetgea",
+       "emailblock": "E-mail ᱵᱚᱸᱫᱷ ᱜᱮᱭᱟ",
        "blocklink": "ᱮᱥᱮᱫᱽ",
-       "unblocklink": "bań block",
-       "change-blocklink": "block judạ",
+       "unblocklink": "ᱵᱟᱝ ᱮᱥᱮᱫ",
+       "change-blocklink": "block ᱵᱚᱫᱚᱞ",
        "contribslink": "ᱮᱱᱮᱢ",
-       "emaillink": "E-mail kulmẽ",
-       "blocklogpage": "Tala eset",
-       "blocklogentry": "Eset [[$1]] sãote cabaḱ okte oka do $2 $3",
+       "emaillink": "E-mail ᱠᱩᱞᱢᱮ",
+       "blocklogpage": "ᱠᱩᱞᱩᱯ ᱮᱥᱮᱫ",
+       "blocklogentry": "ᱮᱥᱮᱫ [[$1]] ᱥᱟᱶᱛᱮ ᱪᱟᱵᱟᱜ ᱚᱠᱛᱚ ᱚᱠᱟ ᱫᱚ $2 $3",
        "reblock-logentry": "ᱵᱚᱫᱚᱞᱮᱱᱟ ᱵᱚᱸᱫ ᱥᱟᱡᱟᱣᱠᱚ [[$1]] ᱞᱟᱹᱜᱤᱫ ᱪᱟᱵᱟᱜ ᱚᱠᱛᱚ $2 $3 ᱥᱟᱶ",
-       "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",
+       "block-log-flags-nocreate": "ᱮᱠᱟᱣᱱᱴ ᱵᱮᱱᱟᱣ ᱵᱚᱱᱫᱷ ᱜᱮᱭᱟ",
+       "block-log-flags-noemail": "E-mail ᱵᱚᱸᱫᱷ ᱜᱮᱭᱟ",
+       "block-log-flags-hiddenname": "ᱵᱮᱵᱦᱟᱨᱤᱭᱟᱜ ᱧᱩᱛᱩᱢ ᱩᱠᱩ ᱜᱮᱭᱟ",
        "proxyblocker": "ᱯᱨᱚᱠᱥᱤ ᱮᱥᱮᱫᱤᱡ",
        "movepagebtn": "ᱥᱟᱦᱴᱟ ᱥᱟᱦᱟᱭᱢᱮ",
-       "pagemovedsub": "Ocogoḱ do hoena",
-       "movelogpage": "Tala cạbi ocoḱme",
-       "revertmove": "ruạr agu",
+       "pagemovedsub": "ᱚᱪᱟᱜ ᱫᱚ ᱦᱩᱭᱱᱟ",
+       "movelogpage": "ᱛᱟᱞᱟ ᱪᱟᱹᱵᱤ ᱚᱪᱚᱜᱽᱢᱮ",
+       "revertmove": "ᱨᱩᱣᱟᱹᱲ ᱟᱹᱜᱩ",
        "export": "ᱟᱹᱜᱩᱭᱮᱱ ᱥᱟᱦᱴᱟᱠᱚ",
-       "export-addcat": "Joṛaomẽ",
-       "export-addns": "Joṛaomẽ",
-       "allmessagesname": "Ńutum",
+       "export-addcat": "ᱥᱮᱞᱮᱫ",
+       "export-addns": "ᱥᱮᱞᱮᱫ",
+       "allmessagesname": "ᱧᱩᱛᱩᱢ",
        "allmessagesdefault": "Bań bhul mesag ol",
-       "allmessages-filter-all": "Sanamaḱ",
-       "allmessages-filter-submit": "Calaḱmẽ",
+       "allmessages-filter-all": "ᱡᱚᱛᱚ",
+       "allmessages-filter-submit": "ᱪᱟᱞᱟᱜ ᱢᱮ",
        "thumbnail-more": "ᱞᱟᱹᱴᱩᱭ ᱢᱮ",
        "thumbnail_error": "Benawakan unuduḱ kạṭuṕ do baṅ ṭhika: $1",
-       "import-upload-filename": "Rẽt ńutum",
+       "import-upload-filename": "ᱨᱮᱫᱧᱩᱛᱩᱢᱺ",
        "importlogpage": "ᱞᱚᱜᱽ ᱟᱹᱜᱩ",
        "tooltip-pt-userpage": "{{GENDER:|ᱟᱢᱟᱜ ᱵᱮᱵᱦᱟᱨᱤᱭᱟᱹ}} ᱥᱟᱦᱴᱟ",
        "tooltip-pt-mytalk": "{{GENDER:|ᱟᱢᱟᱜ}} ᱨᱚᱲ ᱥᱟᱦᱴᱟ",
        "tooltip-pt-preferences": "{{GENDER:|ᱟᱢᱟᱜ}} ᱠᱩᱥᱤᱠᱚ",
        "tooltip-pt-watchlist": "ᱥᱟᱦᱴᱟ ᱛᱟᱹᱞᱠᱟᱹ ᱚᱠᱟᱛᱟᱜᱛᱮ ᱟᱢ ᱫᱚ ᱱᱟᱣᱟ ᱵᱚᱫᱚᱞ ᱞᱟᱹᱜᱤᱛ ᱛᱮ ᱧᱮᱞᱮᱜ ᱠᱟᱱ",
-       "tooltip-pt-mycontris": "Mit́ṭen lisṭ {{GENDER:|amaḱ}} kạmiko reaḱ",
+       "tooltip-pt-mycontris": "ᱛᱟᱹᱞᱠᱟᱹ {{GENDER:|ᱟᱢᱟᱜ}} ᱮᱱᱮᱢᱠᱚ ᱨᱮᱭᱟᱜ",
        "tooltip-pt-login": "ᱟᱢ ᱫᱚ ᱵᱚᱞᱟᱜ ᱞᱟᱹᱜᱤᱛ ᱩᱫᱽᱜᱟᱣᱤᱧ ᱮᱢᱟᱢᱠᱟᱱᱟ; ᱵᱚᱞᱚᱜ ᱞᱟᱜᱟᱜ-ᱟ ᱚᱝᱠᱟ ᱫᱚ ᱵᱟᱝ",
        "tooltip-pt-logout": "ᱚᱰᱚᱠᱚᱜ ᱢᱮ",
        "tooltip-pt-createaccount": "ᱟᱢ ᱫᱚ ᱢᱤᱫᱽᱴᱮᱱ ᱦᱤᱥᱟᱹᱵ ᱡᱷᱤᱪ ᱠᱟᱛᱮ ᱵᱚᱞᱚᱜ ᱞᱟᱹᱜᱤᱛᱤᱧ ᱩᱫᱽᱜᱟᱣᱮᱛ ᱢᱮᱭᱟ; ᱟᱫᱚ ᱡᱟᱹᱨᱩᱲ ᱵᱚᱞᱚᱜ ᱚᱝᱠᱟ ᱫᱚ ᱵᱟᱝ ᱠᱟᱱᱟ",
        "tooltip-ca-nstab-template": "ᱪᱷᱟᱸᱪ ᱧᱮᱞᱢᱮ",
        "tooltip-ca-nstab-help": "ᱜᱚᱸᱲᱚ ᱥᱟᱦᱴᱟ ᱧᱮᱞᱢᱮ",
        "tooltip-ca-nstab-category": "ᱛᱷᱚᱠ ᱥᱟᱦᱴᱟ ᱧᱮᱞᱢᱮ",
-       "tooltip-minoredit": "Noa do huḍiń joṛao lekate lekhay me",
-       "tooltip-save": "Bodolaḱko rukhiyayme",
-       "tooltip-preview": "Amaḱ bodolaḱ uduḱme, noa beoharme ạuri rukhiyayre",
-       "tooltip-diff": "Uduḱme okaṭaḱ onolem bodolakada",
+       "tooltip-minoredit": "ᱦᱩᱰᱤᱧ ᱥᱟᱯᱲᱟᱣ ᱦᱤᱥᱟᱹᱵᱛᱮ ᱞᱮKᱦᱟᱭᱢᱮ",
+       "tooltip-save": "ᱵᱚᱫᱚᱞᱠᱩ ᱨᱩᱠᱷᱤᱭᱟᱹᱭᱢᱮ",
+       "tooltip-preview": "ᱟᱢᱟᱜ ᱵᱚᱫᱚᱞᱠᱩ ᱩᱫᱩᱜᱽᱢᱮ, ᱨᱩᱠᱷᱤᱭᱟᱹ ᱞᱟᱦᱟᱨᱮ ᱱᱚᱣᱟ ᱵᱮᱵᱦᱟᱨ ᱢᱮ᱾",
+       "tooltip-diff": "ᱚᱠᱟᱴᱟᱜ ᱮᱢ ᱵᱚᱫᱚᱞᱠᱟᱫᱟ ᱚᱱᱟ ᱩᱫᱩᱜᱽᱢᱮ",
        "tooltip-compareselectedversions": "ᱱᱚᱣᱟ ᱥᱟᱦᱴᱟ ᱨᱮᱱᱟᱜ ᱵᱟᱨᱭᱟ ᱧᱮᱞᱟᱹᱨᱩ ᱵᱷᱮᱜᱮᱫ ᱧᱮᱞ ᱢᱮ",
        "tooltip-watch": "ᱱᱚᱶᱟ ᱥᱟᱦᱴᱟ ᱟᱢᱟᱜ ᱧᱮᱞᱚᱜ ᱛᱟᱹᱞᱠᱟᱹᱨᱮ ᱡᱚᱲᱟᱣᱢᱮ",
        "tooltip-rollback": "ᱫᱚᱲᱦᱟᱛᱮ ᱢᱤᱫ ᱫᱷᱟᱣ ᱞᱤᱱ ᱛᱮ contributor ᱟᱜ ᱢᱩᱪᱟᱹᱫ ᱥᱟᱯᱲᱟᱣ \"ᱜᱷᱩᱨᱞᱟᱹ ᱟᱹᱪᱩᱨ\" ᱢᱮ",
-       "tooltip-undo": "Noa joṛao kạmire ulṭao \"bạgiyaḱme\" ar ńeloḱ lekate noa joṛao jhicme. Noa do am guḍ karon joṛaoe ektiyariye emama.",
+       "tooltip-undo": "ᱱᱚᱣᱟ ᱥᱟᱯᱲᱟᱣ ᱠᱟᱹᱢᱤᱨᱮ \"ᱩᱞᱴᱟᱹᱣ ᱠᱟᱹᱢᱤ\" ᱟᱨ ᱧᱮᱞᱚᱜ ᱞᱮᱠᱟᱛᱮ ᱱᱚᱣᱟ ᱥᱟᱯᱲᱟᱣ ᱡᱷᱤᱡᱽᱢᱮ᱾ ᱱᱚᱣᱟ ᱫᱚ ᱟᱢ ᱢᱩᱬᱩᱛ ᱠᱟᱛᱷᱟᱨᱮ ᱥᱞᱮᱫᱚᱜ ᱨᱮᱱᱟᱜ ᱮᱠᱛᱤᱭᱟᱨ ᱮᱢᱟᱢᱟ᱾",
        "tooltip-preferences-save": "ᱠᱩᱥᱤᱠᱚ ᱨᱩᱠᱷᱤᱭᱟᱹᱭᱢᱮ",
-       "tooltip-summary": "Khaṭote guṭ katha bhoraome",
-       "others": "Eṭagaḱko",
-       "simpleantispam-label": "Enṭi espam ńel\nDo <strong>not</strong> noa purạome!",
+       "tooltip-summary": "ᱦᱩᱰᱤᱧᱛᱮ ᱢᱩᱬᱩᱛ ᱠᱟᱛᱷᱟ ᱵᱷᱚᱨᱟᱣᱢᱮ",
+       "others": "ᱮᱴᱟᱜᱠᱚ",
+       "simpleantispam-label": "ᱮᱱᱴᱤ ᱮᱥᱯᱮᱢ ᱧᱮᱞ᱾\nDo <strong>not</strong> ᱱᱚᱣᱟ ᱯᱩᱨᱟᱧᱢᱮ!",
        "pageinfo-title": "\"$1\" ᱞᱟᱹᱜᱤᱫ ᱥᱩᱪᱱᱟ",
        "pageinfo-header-basic": "ᱢᱩᱬ ᱥᱩᱪᱱᱟ",
        "pageinfo-header-edits": "ᱥᱟᱯᱲᱟᱣ ᱱᱟᱜᱟᱢ",
        "widthheightpage": "$1 × $2, $3 {{PLURAL:$3|ᱥᱟᱦᱴᱟ|ᱥᱟᱦᱴᱟᱠᱚ}}",
        "file-info-size": "$1 x $2 pixels, file size: $3, MIME type: $4",
        "file-info-size-pages": "$1 × $2 ᱯᱤᱠᱥᱮᱞ, ᱨᱮᱫ ᱥᱚᱝ: $3, MIME ᱞᱮᱠᱟᱱ: $4, $5 {{PLURAL:$5|ᱥᱟᱦᱴᱟ|ᱥᱟᱦᱴᱟᱠᱚ}}",
-       "file-nohires": "Aema resulation nondḍe banuḱa",
-       "svg-long-desc": "SVG rẽt, normalte $1 x $2 pixels, rẽt sayej: $3",
+       "file-nohires": "ᱟᱭᱢᱟ ᱨᱮᱡᱩᱞᱮᱥᱚᱱ ᱵᱟᱱᱩᱜ-ᱟ᱾",
+       "svg-long-desc": "SVG ᱨᱮᱫ, ᱱᱚᱨᱢᱟᱞᱛᱮ $1 x $2 pixels, ᱨᱮᱫ ᱡᱟᱜᱟ: $3",
        "show-big-image": "ᱟᱥᱚᱞ ᱨᱮᱫ",
-       "show-big-image-preview": "Noa ńeloḱ akar do:$1",
+       "show-big-image-preview": "ᱧᱮᱞᱡᱚᱝ ᱨᱮᱱᱟᱜ ᱟᱠᱟᱨ:$1",
        "show-big-image-other": "ᱮᱢᱟᱱ ᱛᱮᱭᱟᱜ {{PLURAL:$2|resolution|resolutions}}: $1",
        "show-big-image-size": "$1 X $2 Pixels",
-       "ilsubmit": "Sendra",
+       "ilsubmit": "ᱥᱮᱸᱫᱽᱨᱟ",
        "bydate": "ᱢᱟᱹᱦᱤᱛ ᱛᱮ",
        "monday-at": "ᱚᱛᱮᱢᱟᱦᱟᱸ $1 ᱨᱮ",
        "tuesday-at": "ᱵᱟᱞᱮᱢᱟᱦᱟᱸ $1 ᱨᱮ",
        "metadata": "ᱢᱮᱴᱟ ᱥᱟᱹᱠᱷᱭᱟᱹᱛ",
        "metadata-help": "ᱱᱚᱣᱟ ᱨᱮᱫ ᱨᱮᱫᱚ ᱵᱟᱹᱲᱛᱤ ᱠᱟᱛᱷᱟᱠᱚ ᱢᱮᱱᱟᱜ-ᱟ, ᱯᱟᱥᱮᱡ ᱱᱚᱣᱟ ᱫᱚ ᱰᱤᱡᱤᱴᱟᱞ ᱠᱮᱢᱨᱟ ᱥᱮ ᱮᱥᱠᱮᱱᱟᱨ ᱵᱮᱵᱦᱟᱨ ᱠᱟᱛᱮ ᱰᱤᱡᱤᱴᱟᱞ ᱟᱠᱟᱫᱟᱠᱚ᱾ ᱡᱩᱫᱤ ᱱᱚᱣᱟ ᱨᱮᱫ ᱨᱮᱭᱟᱜ ᱵᱩᱱᱤᱭᱟᱹᱫ ᱠᱷᱚᱱ ᱱᱟᱣᱟ ᱥᱩᱫᱷᱨᱟᱣ ᱞᱮᱱᱠᱷᱟᱱ, ᱯᱟᱥᱮᱡ ᱥᱟᱱᱟᱢᱠᱚ ᱛᱷᱚᱲᱟ ᱵᱟᱝᱠᱩ ᱥᱚᱫᱚᱨᱚᱜ-ᱟ ᱱᱚᱣᱟ ᱨᱮᱫ ᱨᱮᱫᱚ᱾",
        "metadata-fields": "Image metadata fields listed in this message will be included on image page display when the metadata table is collapsed.\nOthers will be hidden by default.\n* make\n* model\n* datetimeoriginal\n* exposuretime\n* fnumber\n* isospeedratings\n* focallength\n* artist\n* copyright\n* imagedescription\n* gpslatitude\n* gpslongitude\n* gpsaltitude",
-       "exif-imagewidth": "Ganḍe",
-       "exif-imagelength": "Usul",
+       "exif-imagewidth": "ᱜᱟᱸᱰᱮ",
+       "exif-imagelength": "ᱩᱥᱩᱞ",
        "exif-orientation": "ᱥᱟᱢᱟᱝ",
        "exif-xresolution": "ᱜᱤᱛᱤᱡ ᱛᱮ",
        "exif-yresolution": "ᱛᱤᱝᱜᱩ ᱛᱮ",
        "exif-datetime": "ᱨᱮᱫ ᱵᱚᱫᱚᱞ ᱛᱟᱹᱨᱤᱠᱷ ᱟᱨ ᱚᱠᱛᱚ",
-       "exif-make": "Kemera tearić",
-       "exif-model": "Kemera model",
-       "exif-software": "Beoharen Software",
-       "exif-artist": "Onoliạ",
-       "exif-exifversion": "Exif bharson",
-       "exif-colorspace": "Roṅcoṅ dhạrti",
+       "exif-make": "ᱠᱮᱢᱮᱨᱟ ᱵᱮᱱᱟᱣᱤᱪ",
+       "exif-model": "ᱠᱮᱢᱮᱨᱟ ᱢᱚᱰᱮᱞ",
+       "exif-software": "ᱵᱮᱵᱦᱟᱨᱟᱠᱟᱱ ᱥᱚᱯᱷᱴᱚᱭᱟᱨ",
+       "exif-artist": "ᱚᱱᱚᱞᱤᱭᱟᱹ",
+       "exif-exifversion": "Exif ᱵᱷᱟᱨᱥᱚᱱ",
+       "exif-colorspace": "ᱨᱚᱝᱪᱚᱝ ᱡᱟᱜᱟ",
        "exif-datetimeoriginal": "ᱰᱟᱴᱟ ᱛᱮᱭᱟᱨ ᱨᱮᱱᱟᱜ ᱢᱟᱹᱦᱤᱛ ᱟᱨ ᱚᱠᱛᱚ",
        "exif-datetimedigitized": "ᱰᱤᱡᱤᱴᱟᱭᱡᱤᱝᱟᱜ ᱢᱟᱹᱦᱤᱛ ᱟᱨ ᱚᱠᱛᱚ",
        "exif-subsectime": "ᱢᱟᱹᱦᱤᱛ ᱚᱠᱛᱚ ᱴᱤᱯᱤᱡ",
        "imgmultigo": "ᱥᱮᱱᱚᱜ!",
        "imgmultigoto": "ᱥᱮᱱᱚᱜ ᱢᱮ ᱥᱟᱦᱴᱟ $1",
        "watchlisttools-clear": "ᱠᱩᱥᱤᱭᱟᱜ ᱞᱤᱥᱴᱤ ᱥᱟᱯᱷᱟ",
-       "watchlisttools-view": "Jońgṛao bodolaḱko ńel",
-       "watchlisttools-edit": "Ńelok tạlika ńel ar joṛao",
-       "watchlisttools-raw": "Baṇ purạo akan ńelok tạlika purạomẽ",
-       "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|galmarao]])",
+       "watchlisttools-view": "ᱡᱚᱝᱲᱟᱣ ᱵᱚᱫᱚᱞᱠᱩ ᱧᱮᱞ",
+       "watchlisttools-edit": "ᱧᱮᱞᱢᱮ ᱟᱨ ᱧᱮᱞᱚᱜ ᱛᱟᱹᱞᱠᱟᱹ ᱥᱟᱯᱲᱟᱣᱢᱮ",
+       "watchlisttools-raw": "ᱵᱟᱝ ᱯᱩᱨᱟᱣ ᱟᱠᱟᱱ ᱧᱮᱞᱚᱜ ᱛᱟᱹᱞᱠᱟᱹ ᱥᱟᱯᱲᱟᱣᱢᱮ",
+       "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|ᱜᱟᱞᱢᱟᱨᱟᱣ]])",
        "duplicate-defaultsort": "'''Sontoroḱmẽ:''' ḍifolṭ sajao reaḱ cạbi: $2 lahare ḍifolṭ sajao reaḱ sakam: ''$1'' e bae luturaḱ kana.",
        "redirect": "ᱨᱮᱫ, ᱵᱮᱵᱷᱟᱨᱩᱭᱟᱹ, ᱥᱟᱦᱴᱟ, ᱧᱮᱞ-ᱟᱹᱨᱩ, ᱵᱟᱝᱠᱷᱟᱱ ᱞᱚᱜᱽ ID ᱫᱟᱨᱟᱭᱛᱮ ᱢᱚᱦᱰᱟ",
        "redirect-summary": "ᱱᱚᱶᱟ ᱥᱟᱦᱴᱟ ᱫᱚ ᱢᱚᱦᱰᱟ ᱟ ᱢᱤᱫ ᱨᱮᱫ (ᱮᱢᱟᱠᱟᱱ ᱨᱮᱫᱧᱩᱛᱩᱢ) ᱴᱷᱮᱱ, ᱢᱤᱫ ᱥᱟᱦᱴᱟ (ᱮᱢᱮᱱ ᱟᱹᱨᱩᱣᱟᱜ ID ᱟᱨᱵᱟᱝ ᱥᱟᱦᱴᱟ ID),  ᱢᱤᱫ ᱵᱮᱵᱷᱟᱨᱩᱭᱟᱹ ᱥᱟᱦᱴᱟ (ᱮᱢᱮᱱ ᱮᱞᱩᱠ ᱵᱮᱵᱷᱟᱨᱤᱭᱟᱹ ID ), ᱟᱨᱵᱟᱝ ᱢᱤᱫ ᱞᱚᱜᱽ ᱵᱚᱞᱚ (ᱮᱢᱮᱱ ᱞᱚᱜᱽ ID) ᱾ ᱵᱮᱵᱷᱟᱨᱟᱠᱟᱱ: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/page/64308]], [[{{#Special:Redirect}}/revision/328429]], [[{{#Special:Redirect}}/user/101]], ᱟᱨᱵᱟᱝ [[{{#Special:Redirect}}/logid/186]]",
        "fileduplicatesearch": "ᱥᱮᱸᱫᱽᱨᱟᱭ ᱢᱮ ᱵᱟᱹᱲᱤᱡ ᱨᱮᱫᱠᱚ",
        "fileduplicatesearch-submit": "ᱥᱮᱸᱫᱽᱨᱟ",
        "specialpages": "ᱵᱤᱥᱮᱥ ᱥᱟᱦᱴᱟᱠᱚ",
-       "external_image_whitelist": "#Noa sakam do cet leka menaḱa oṅkage dohoemẽ\n#Sanam okte re jạhiren kuṭrạ latar re (khạli hạtiń //talare) bạisạomẽ\n#Noako do bahre reaḱ (hotlinked) chubi reaḱ URL saõte milạo hoyoḱa\n#Okako milạḱa, onako do chubi lekate udugoḱa, baṅkhan do khali chubi joṛao udugoḱa\n#Noa layen reaḱ ehoṕre # menaḱa ona layenko menko hisapte beohar hoyoḱka\n#Noa do kas-baṅ rimjhạoaḱge\n#Noa dag cetanre regex kuṭrạ bạsạomẽ. Noa layen cetleka menaḱa oṅkage dohoemẽ</pre>",
-       "tag-filter": "[[Special:Tags|Tag]] saphay:",
+       "external_image_whitelist": "#ᱱᱚᱣᱟ ᱥᱟᱦᱴᱟ ᱫᱚ ᱪᱮᱛ ᱞᱮᱠᱟ ᱢᱮᱱᱟᱜ-ᱟ ᱚᱝᱠᱟᱜᱮ ᱫᱚᱦᱚᱭᱢᱮ\n#ᱡᱚᱛᱚ ᱚᱠᱛᱚ ᱨᱮ ᱡᱟᱹᱦᱤᱨᱮᱱ ᱠᱩᱴᱨᱟᱹ ᱞᱟᱛᱟᱨ ᱨᱮ (ᱠᱷᱟᱹᱞᱤ ᱦᱟᱹᱴᱤᱧ //ᱛᱟᱞᱟᱨᱮ) ᱵᱟᱹᱭᱥᱟᱹᱣᱢᱮ\n#ᱱᱚᱣᱟ ᱠᱚ ᱫᱚ ᱵᱟᱨᱦᱮ ᱨᱮᱭᱟᱜ (hotlinked) ᱪᱤᱛᱟᱹᱨ ᱨᱮᱭᱟᱜ URL ᱥᱟᱶᱛᱮ ᱢᱤᱞᱟᱹᱣ ᱦᱩᱭᱩᱜ-ᱟ\n#ᱚᱠᱟᱠᱩ ᱢᱤᱞᱟᱹᱜ-ᱟ, ᱚᱱᱟᱠᱩ ᱫᱚ ᱪᱤᱛᱟᱹᱨ ᱞᱮᱠᱟᱛᱮ ᱩᱫᱩᱜᱚᱜ-ᱟ, ᱵᱟᱝᱠᱷᱟᱱ ᱫᱚ ᱠᱷᱟᱹᱞᱤ ᱪᱤᱛᱟᱨ ᱡᱚᱱᱚᱲ ᱩᱫᱩᱜᱚᱜ-ᱟ\n#ᱱᱚᱣᱟ ᱞᱟᱭᱤᱱ ᱨᱮᱭᱟᱜ ᱮᱛᱦᱚᱵᱨᱮ # ᱢᱮᱱᱟᱜ-ᱟ ᱚᱱᱟ ᱞᱟᱭᱤᱱᱠᱚ ᱢᱮᱱᱠᱚ ᱦᱤᱥᱟᱹᱵᱛᱮ ᱵᱮᱵᱦᱟᱨ ᱦᱩᱭᱩᱜ-ᱟ\n#ᱱᱚᱣᱟ ᱫᱚ ᱨᱤᱢᱡᱷᱟᱹᱣᱜᱮ\n#ᱱᱚᱣᱟ ᱫᱟᱜᱽ ᱪᱮᱛᱟᱱᱨᱮ regex ᱠᱩᱴᱨᱟᱹ ᱵᱟᱹᱭᱥᱟᱹᱣᱢᱮ᱾ ᱱᱚᱣᱟ ᱞᱟᱭᱤᱱ ᱪᱮᱫᱞᱮᱠᱟ ᱢᱮᱱᱟᱜ-ᱟ ᱚᱝᱠᱟᱜᱮ ᱫᱚᱦᱚᱭᱢᱮ</pre>",
+       "tag-filter": "[[Special:Tags|Tag]] ᱪᱷᱟᱹᱠᱱᱤ:",
        "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|ᱥᱟᱛᱚᱢ|ᱥᱟᱛᱚᱢᱠᱩ}}]]: $2)",
        "tags-active-yes": "ᱦᱮᱸ",
        "tags-active-no": "ᱵᱟᱝ",
        "logentry-patrol-patrol-auto": "$1 ᱟᱡᱛᱮᱜᱮ {{GENDER:$2|ᱪᱤᱱᱦᱟᱹᱭᱮᱱᱟ}} $4 ᱧᱮᱞᱟᱹᱨᱩ $3 ᱥᱟᱦᱴᱟ ᱨᱮᱱᱟᱜ ᱾",
        "logentry-newusers-create": "ᱵᱮᱵᱦᱟᱨᱤᱭᱟᱜ ᱦᱤᱥᱟᱹᱵ $1 ᱫᱚ {{GENDER:$2|ᱛᱮᱭᱟᱨᱱᱟ}}",
        "logentry-newusers-autocreate": "ᱵᱮᱵᱷᱟᱨᱤᱭᱟᱹ ᱠᱷᱟᱛᱟ $1 ᱫᱚ {{GENDER:$2|ᱛᱮᱭᱟᱨᱮᱱᱟ}} ᱟᱡᱛᱮᱜᱮ",
-       "logentry-upload-upload": "$1 {{GENDER:$2|rakaṕ akadae}} $3",
+       "logentry-upload-upload": "$1 {{GENDER:$2|ᱨᱟᱠᱟᱵᱠᱟᱱ}} $3",
        "logentry-upload-overwrite": "$1 {{GENDER:$2|ᱞᱟᱫᱮᱭᱮᱱᱟ}} ᱢᱤᱫ ᱱᱟᱶᱟ ᱵᱷᱟᱨᱥᱚᱱ $3 ᱨᱮᱱᱟᱜ",
        "searchsuggest-search": "ᱥᱮᱸᱫᱽᱨᱟ {{SITENAME}}",
        "duration-days": "$1 {{PLURAL:$1|ᱢᱟᱦᱟᱸ|ᱢᱟᱸᱦᱟᱸ}}",
index d2c3b55..02fa5be 100644 (file)
        "userjspreview": "'''Arricorda ca stai sulu tistanno/vidennu 'n antiprima lu tò javascript pirsunali, nun hà statu ancora sarvatu!'''",
        "sitecsspreview": "<strong>Arricòrdati chi chista è sulu n'antiprima di stu CSS. Ancora nun fu sarvatu!</strong>",
        "sitejspreview": "<strong>Arricòrdati chi chista è sulu n'antiprima di stu còdici JavaScript. Ancora nun fu sarvatu!</strong>",
-       "userinvalidcssjstitle": "'''Accura:''' Nun esisti arcuna skin cu nomu \"$1\". S'arricorda ca li pàggini pi li .css e .js pirsunalizzati hannu la nizziali dû tìtulu minùscula, p'asempiu {{ns:user}}:Asempiu/vector.js e nun {{ns:user}}:Asempiu/Vector.css.",
+       "userinvalidconfigtitle": "'''Accura:''' Nun esisti arcuna skin cu nomu \"$1\". S'arricorda ca li pàggini pi li .css e .js pirsunalizzati hannu la nizziali dû tìtulu minùscula, p'asempiu {{ns:user}}:Asempiu/vector.js e nun {{ns:user}}:Asempiu/Vector.css.",
        "updated": "(Aggiurnatu)",
        "note": "<strong>Nota:</strong>",
        "previewnote": "<strong>Arricòrdati ca chista è sulu n'antiprima.</strong>\nLi tò canciamenti ancora nun foru sarvati!",
        "prefs-files": "File",
        "prefs-custom-css": "CSS pirsunalizzatu",
        "prefs-custom-js": "JavaScript pirsunalizzatu",
-       "prefs-common-css-js": "CSS/JavaScript cunnivisu tra tutti li peddi:",
+       "prefs-common-config": "CSS/JavaScript cunnivisu tra tutti li peddi:",
        "prefs-reset-intro": "Poi adupirari sta pàggina p'azzirari li tò prifirenzi a chiddi pridifinuti dû situ.\nSt'opirazzioni nun si pò annullari doppu ch'è fatta.",
        "prefs-emailconfirm-label": "Cunvàlida dâ posta elittrònica:",
        "youremail": "Nnirizzu di posta elittrònica:",
index c58040b..cd442a3 100644 (file)
        "userjspreview": "'''Mynd that ye're juist testin/previewin yer uiser JavaScript; it haesna been hained yet!'''",
        "sitecsspreview": "<strong>Mynd that ye'r yinly previewing this CSS.\nIt's no been hained yet!</strong>",
        "sitejspreview": "<strong>Mynd that ye'r yinly previewing this JavaScript code.\nIt's no been hained yet!</strong>",
-       "userinvalidcssjstitle": "<strong>Warnishmant</strong> Thaur's na ae skin \"$1\". Mynd that yer ain .css n .js pages uise ae lowercase teetle, e.g. {{ns:user}}:Foo/vector.css in steid o {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "<strong>Warnishmant</strong> Thaur's na ae skin \"$1\". Mynd that yer ain .css n .js pages uise ae lowercase teetle, e.g. {{ns:user}}:Foo/vector.css in steid o {{ns:user}}:Foo/Vector.css.",
        "updated": "(Updatit)",
        "note": "'''Mynd:'''",
        "previewnote": "<strong>Mynd that this is yinlie ae luikower.</strong>\nYer chynges hae na been hained yet!",
        "prefs-files": "Files",
        "prefs-custom-css": "Custom CSS",
        "prefs-custom-js": "Custom JS",
-       "prefs-common-css-js": "Shaired CSS/JavaScript fer aw skins:",
+       "prefs-common-config": "Shaired CSS/JavaScript fer aw skins:",
        "prefs-reset-intro": "Ye can uise this page tae reset yer preeferances til the steid defauts.\nThis canna be ondun.",
        "prefs-emailconfirm-label": "Wab-mail confirmation:",
        "youremail": "Yer email:",
index e23d82d..6b0dc67 100644 (file)
        "mytalk": "بحث",
        "anontalk": "بحث",
        "navigation": "رھنمائي",
-       "and": "&#32؛۽",
+       "and": "&#32;۽",
        "faq": "ڪپس",
        "actions": "ڪارگذاريون",
        "namespaces": "نانءُپولارَ",
index 93a1a40..06de033 100644 (file)
        "userjsyoucanpreview": "'''Suggerimentu:''' Usa lu buttoni '''Visuarizza antiprimma''' pa prubà li nobi JS primma di sàivvaddi.",
        "usercsspreview": "'''Ammitanti ch'è soru un'antiprimma di lu propriu CSS passunari; li mudìfigghi nò so ancora isthaddi sàivvaddi!'''",
        "userjspreview": "'''Ammitanti ch'è soru un'antiprimma pa prubà lu propriu JavaScript passunari; li mudìfigghi nò so ancora isthaddi sàivvaddi!'''",
-       "userinvalidcssjstitle": "'''Attinzioni:''' Nò isisthi nisciun aipettu gràficu \"$1\". Amminta chi li pàgini pa li .css e .js passunari àni lu primu caràtteri di lu tìturu minori, cumenti {{ns:user}}:Foo/vector.css e nò {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "'''Attinzioni:''' Nò isisthi nisciun aipettu gràficu \"$1\". Amminta chi li pàgini pa li .css e .js passunari àni lu primu caràtteri di lu tìturu minori, cumenti {{ns:user}}:Foo/vector.css e nò {{ns:user}}:Foo/Vector.css.",
        "updated": "(Aggiornaddu)",
        "note": "'''NOTA:'''",
        "previewnote": "'''Attinzioni: chistha è soru un'antiprimma. Li mudifigghi a la pagina NÒ so ancora isthaddi saivvaddi!'''",
index 1436537..64faa2e 100644 (file)
        "userjsyoucanpreview": "'''Tip:''' Usadad 'Cohuatlöx cuáxiit' testom me hun JS xuniim 'depre.",
        "usercsspreview": "'''He cuáxiit he CSSde caitom, zo necoccebj xuniim!'''",
        "userjspreview": "'''He cuáxiit/testom he JavaScriptde caitom, zo necoccebj xuniim!'''",
-       "userinvalidcssjstitle": "'''Attencion:''' Skin \"$1\" coccebj ne'dáár. Regardom .css ö .js páhinám usadad titlenam lowercase zo, yanuiíxzo mii yahöxde ti {{ns:user}}:Foo/vector.css opposadadde {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "'''Attencion:''' Skin \"$1\" coccebj ne'dáár. Regardom .css ö .js páhinám usadad titlenam lowercase zo, yanuiíxzo mii yahöxde ti {{ns:user}}:Foo/vector.css opposadadde {{ns:user}}:Foo/Vector.css.",
        "updated": "(varupdatenám)",
        "note": "'''Notificacion:'''",
        "previewnote": "'''Jan coccebj cuáxiit zo; quiíx necoccebj xuniim!'''",
index da31a63..de801f4 100644 (file)
        "userjspreview": "<strong>Honga kaŋ war goo ma šii/moofur de war JavaScript goykaa ga.\nA mana gaabundi jina!</strong>",
        "sitecsspreview": "<strong>Honga kaŋ war mma moofur de CSS woo ga.\n A mana gaabundi jina!</strong>",
        "sitejspreview": "<strong>Honga kaŋ war mma moofur de JavaScript ašariyaa woo ga.\nA mana gaabundi jina!</strong>",
-       "userinvalidcssjstitle": "<strong>Yaamar:</strong> \"$1\" kuuru kul šii.\nLaada .css nda .js moɲey ga goy nda karfu kayna maa, sanda {{ns:user}}:Foo/vector.css manti {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "<strong>Yaamar:</strong> \"$1\" kuuru kul šii.\nLaada .css nda .js moɲey ga goy nda karfu kayna maa, sanda {{ns:user}}:Foo/vector.css manti {{ns:user}}:Foo/Vector.css.",
        "updated": "(Taagante)",
        "note": "<strong>Laasaabu:</strong>",
        "previewnote": "<strong>Honga kaŋ war moofuryan de ti wo.</strong>\nWar barmawey mana gaabundi jina!",
        "prefs-files": "Tukey",
        "prefs-custom-css": "Laada CSS",
        "prefs-custom-js": "Laada JavaScript",
-       "prefs-common-css-js": "CSS/JavaScript kaŋ kuurey k'a may:",
+       "prefs-common-config": "CSS/JavaScript kaŋ kuurey k'a may:",
        "prefs-reset-intro": "War ga hin ka goy nda moɲoo woo ka war ibaayey yeeti nungoo tilasu alhaaley ga.\nWoo ši hin ka taafeeri.",
        "prefs-emailconfirm-label": "Bataga tabatandiyan:",
        "youremail": "Bataga:",
index 2ebb63a..fe79eb2 100644 (file)
        "userjsyoucanpreview": "'''Patarėms:''' Nauduokat „{{int:showpreview}}“ mīgtoka, kū ėšmiegintomiet sava naujaji JS prīš anou ėšsaugont.",
        "usercsspreview": "'''Napamirškėt, kū Tamsta tėk parveizėt sava nauduotoja CSS, ans da nabova ėšsauguots!'''",
        "userjspreview": "'''Nepamirškėt, kū Tamsta tėk testoujat/parvaizėt sava nauduotoja ''JavaScript'', ans da nabova ėšsauguots!'''",
-       "userinvalidcssjstitle": "'''Diemesė:''' Nė juokės ėšruodos „$1“. Napamirškėt, kū sava .css ėr .js poslapē nauduo pavadėnėma mažuosiomės raidiemis, pvz., Nauduotuos:Foo/vector.css, o ne Nauduotuos:Foo/Vector.css.",
+       "userinvalidconfigtitle": "'''Diemesė:''' Nė juokės ėšruodos „$1“. Napamirškėt, kū sava .css ėr .js poslapē nauduo pavadėnėma mažuosiomės raidiemis, pvz., Nauduotuos:Foo/vector.css, o ne Nauduotuos:Foo/Vector.css.",
        "updated": "(Atnaujėnta)",
        "note": "'''Žėniuo:'''",
        "previewnote": "'''Napamirškat, kū tas tėktās tier parvaiza, pakeitėmā da nier ėšsauguoti!'''",
        "prefs-files": "Abruozdielē",
        "prefs-custom-css": "Asabėšks CSS",
        "prefs-custom-js": "Asabėšks JavaScript",
-       "prefs-common-css-js": "Bendros CSS/JavaScript vėsuom aplinkuom:",
+       "prefs-common-config": "Bendros CSS/JavaScript vėsuom aplinkuom:",
        "prefs-reset-intro": "Čiuonās galat grōžintė vėsus sava nūstatīmus ont pradėniu (kap būn tiktās prisijongus).\nTas dalīks nab'atšaukiams.",
        "prefs-emailconfirm-label": "Tėkrā tuokis el. paštos?",
        "youremail": "El. paštos:",
index ca5597b..a9f8716 100644 (file)
        "userjspreview": "'''Zapamite da je ovo samo test/pretpregled Vaše JavaScript-e.'''\n'''Još uvijek nije snimljena!'''",
        "sitecsspreview": "'''Zapamtite ovo je samo izgled Vašeg CSS-a.'''\n'''Još uvijek nije snimljen!'''",
        "sitejspreview": "'''Zapamtite ovo je samo izgled ovog koda JavaScripte.'''\n'''Još uvijek nije snimljen!'''",
-       "userinvalidcssjstitle": "'''Upozorenje:''' Nema skina pod imenom \"$1\".\nUpamtite da korisničke .css i .js stranice koriste naslov s malim slovom, npr. {{ns:user}}:Foo/monobook.css umjesto {{ns:user}}:Foo/Monobook.css.",
+       "userinvalidconfigtitle": "'''Upozorenje:''' Nema skina pod imenom \"$1\".\nUpamtite da korisničke .css i .js stranice koriste naslov s malim slovom, npr. {{ns:user}}:Foo/monobook.css umjesto {{ns:user}}:Foo/Monobook.css.",
        "updated": "(Osvježeno)",
        "note": "'''Napomena:'''",
        "previewnote": "<strong>Ne zaboravite da je ovo samo pregled</strong>\nIzmjene stranice nisu još sačuvane!",
        "prefs-files": "Datoteke",
        "prefs-custom-css": "Prilagođeni CSS",
        "prefs-custom-js": "Prilagođeni JS",
-       "prefs-common-css-js": "Zajednički CSS/JS za sve izglede (skinove):",
+       "prefs-common-config": "Zajednički CSS/JS za sve izglede (skinove):",
        "prefs-reset-intro": "Možete koristiti ovu stranicu da poništite Vaše postavke na ovom sajtu na pretpostavljene vrijednosti.\nOvo se ne može vratiti unazad.",
        "prefs-emailconfirm-label": "E-mail potvrda:",
        "youremail": "Vaša e-pošta / Ваша е-пошта*",
index 7849d68..d886354 100644 (file)
        "prefs-files": "ၾၢႆႇ",
        "prefs-custom-css": "CSS ႁင်းတူဝ်",
        "prefs-custom-js": "JavaScript ႁင်းတူဝ်",
-       "prefs-common-css-js": "CSS/Javascript ၸိူဝ်းၽႄႈၸႂ်ႉဝႆႉ တႃႇ ၽိဝ်ၼင်တင်းသဵင်ႈ :",
+       "prefs-common-config": "CSS/Javascript ၸိူဝ်းၽႄႈၸႂ်ႉဝႆႉ တႃႇ ၽိဝ်ၼင်တင်းသဵင်ႈ :",
        "prefs-reset-intro": "ၸဝ်ႈၵဝ်ႇ တေၸၢင်ႈၸႂ်ႉၼႃႈလိၵ်ႈၼႆႉ တွၼ်ႈတႃႇ တင်ႈၶိုၼ်း လွင်ႈလႆႈၸႂ်ၸဝ်ႈၵဝ်ႇ ၸူးၵႃႈတီႈ သၢႆႉ ပိူင်ၵႅဝ်ႈ။ ၼႆႉမၼ်းတေဢမ်ႇၸၢင်ႈ ၶိုၼ်းမႄးၶိုင်ၶိုၼ်း။",
        "prefs-emailconfirm-label": "ၶေႃႈၼႄႉၼွၼ်း ဢီးမေးလ် :",
        "youremail": "ဢီးမေးလ် :",
index 0fb539b..41c87d4 100644 (file)
        "userjspreview": "'''ඔබ සිදුකරන්නේ ඔබගේ පරිශීලක ජාවාස්ක්‍රිප්ට් පරික්ෂා කිරීම/පෙර-දසුන පමණක් බව ධාරණය කරන්න.'''\n'''එය තවමත් සුරැකීමට ලක් කොට නොමැත!'''",
        "sitecsspreview": "'''ඔබ දකින්නේ මෙම CSS හි පෙරදසුනක් පමණක් බව සිහි තබාගන්න.'''\n'''එය තවමත් සුරැකීමට ලක් කොට නොමැත!'''",
        "sitejspreview": "'''ඔබ දකින්නේ මෙම JavaScript කේතයෙහි පෙරදසුනක් පමණක් බව සිහි තබාගන්න.'''\n'''එය තවමත් සුරැකීමට ලක් කොට නොමැත!'''",
-       "userinvalidcssjstitle": "'''අවවාදයයි:''' ඡවියක් නොමැත \"$1\".\nරීති ප්‍රකාරව .css හා .js පිටු විසින් ඉංග්‍රීසි කුඩා-අකුරු ශීර්ෂ භාවිතා කෙරෙන බව සිහි තබා ගන්න, නිදසුන. {{ns:user}}:Foo/vector.css මිස {{ns:user}}:Foo/Vector.css නොවන බව.",
+       "userinvalidconfigtitle": "'''අවවාදයයි:''' ඡවියක් නොමැත \"$1\".\nරීති ප්‍රකාරව .css හා .js පිටු විසින් ඉංග්‍රීසි කුඩා-අකුරු ශීර්ෂ භාවිතා කෙරෙන බව සිහි තබා ගන්න, නිදසුන. {{ns:user}}:Foo/vector.css මිස {{ns:user}}:Foo/Vector.css නොවන බව.",
        "updated": "(යාවත්කාලීන)",
        "note": "'''සටහන:'''",
        "previewnote": "'''මෙය පෙරදසුනක් පමණක් බව සිහිතබාගන්න.'''\nඔබගේ වෙනස්කිරීම් තවමත් සුරැකීමට ලක් කොට නොමැත!",
        "prefs-files": "ගොනු",
        "prefs-custom-css": "අභිරුචි CSS",
        "prefs-custom-js": " අභිරුචි JS",
-       "prefs-common-css-js": "සියළු සිවි සඳහා හවුලේ භාවිත  CSS/ජාවා ස්ක්‍රිප්ට්:",
+       "prefs-common-config": "සියළු සිවි සඳහා හවුලේ භාවිත  CSS/ජාවා ස්ක්‍රිප්ට්:",
        "prefs-reset-intro": "ඔබගේ අභිප්‍රේතයන්, අඩවි පෙරනිමි වෙතට යළි-පිහිටුවීම සඳහා, ඔබ හට මෙම පිටුව භාවිතා කල හැක.\nමෙය අහෝසි කල නොහැක.",
        "prefs-emailconfirm-label": "විද්‍යුත්-ලිපිනය තහවුරුකිරීම:",
        "youremail": "විද්‍යුත් තැපෑල:",
index e962b50..be54581 100644 (file)
        "userjspreview": "'''Nezabudnite, že iba testujete/náhľad vášho používateľského JavaScriptu, ešte nebol uložený!'''",
        "sitecsspreview": "'''Nezabudnite, že toto je iba náhľad tohto CSS.'''\n'''Zatiaľ nebolo uložené!'''",
        "sitejspreview": "'''Nezabudnite, že toto je iba náhľad tohto JavaScriptu.'''\n'''Zatiaľ nebol uložený!'''",
-       "userinvalidcssjstitle": "'''Upozornenie:''' Neexistuje vzhľad „$1“. Pamätajte, že vlastné .css a .js stránky používajú názov s malými písmenami, napr. {{ns:user}}:Foo/vector.css a nie {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "'''Upozornenie:''' Neexistuje vzhľad „$1“. Pamätajte, že vlastné .css a .js stránky používajú názov s malými písmenami, napr. {{ns:user}}:Foo/vector.css a nie {{ns:user}}:Foo/Vector.css.",
        "updated": "(Aktualizovaný)",
        "note": "'''Poznámka: '''",
        "previewnote": "'''Nezabudnite, toto je iba náhľad stránky, ktorú upravujete.\nZmeny ešte nie sú uložené!'''",
        "prefs-files": "Súbory",
        "prefs-custom-css": "Vlastný CSS",
        "prefs-custom-js": "Vlastný JS",
-       "prefs-common-css-js": "Spoločné CSS/JS pre všetky témy:",
+       "prefs-common-config": "Spoločné CSS/JS pre všetky témy:",
        "prefs-reset-intro": "Túto stránku môžete použiť na vrátenie predvolených hodnôt vašich nastavení.\nTúto operáciu nemožno vrátiť.",
        "prefs-emailconfirm-label": "Overenie e-mailu:",
        "youremail": "Váš e-mail²",
index f76e8dc..991e756 100644 (file)
        "loginreqlink": "لاگ ان",
        "accmailtitle": "پاس ورڈ بھیج ݙتے",
        "newarticle": "(نواں)",
+       "noarticletext": "ہݨ ایں ورقے تے کجھ کائنی لکھیا ہویا۔تساں ٻیاں ورقیاں وچ [[Special:Search/{{PAGENAME}}|ایں ورقے دے عنوان کوں ڳولھ سڳدے ہو]]، <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} متعلقہ لاڳ وچ ڳولھ سڳدے ہو]،\nیا [{{fullurl:{{FULLPAGENAME}}|action=edit}} ایں ورقے کوں مُنڈھوں بݨا سڳدے ہو]</span>۔",
        "userpage-userdoesnotexist-view": "صارف کھاتہ \"$1\" رجسٹرڈ کائنی۔",
        "updated": "(اپ ڈیٹ تھی ڳیا)",
        "note": "<strong>نوٹ:</strong>",
index b08b5cf..5c7b60f 100644 (file)
        "userjspreview": "'''Ne pozabite, da svoj uporabniški JavaScript le preizkušate/predogledujete.'''\n'''Ni še bil shranjen!'''",
        "sitecsspreview": "'''Ne pozabite, da ta CSS samo preizkušate.'''\n'''Ni še bil shranjen!'''",
        "sitejspreview": "'''Ne pozabite, da kodo tega JavaScripta samo preizkušate.'''\n'''Ni še bila shranjena!'''",
-       "userinvalidcssjstitle": "'''Opozorilo:''' Koža »$1« ne obstaja.\nVedite, da .css in .js strani po meri uporabljajo naslov z malo začetnico, npr. {{ns:user}}:Blabla/vector.css namesto {{ns:user}}:Blabla/Vector.css.",
+       "userinvalidconfigtitle": "'''Opozorilo:''' Koža »$1« ne obstaja.\nVedite, da .css in .js strani po meri uporabljajo naslov z malo začetnico, npr. {{ns:user}}:Blabla/vector.css namesto {{ns:user}}:Blabla/Vector.css.",
        "updated": "(Posodobljeno)",
        "note": "'''Opomba:'''",
        "previewnote": "'''Vedite, da stran le predogledujete.'''\nVaših sprememb še nismo shranili!",
        "prefs-files": "Datoteke",
        "prefs-custom-css": "CSS po meri",
        "prefs-custom-js": "JS po meri",
-       "prefs-common-css-js": "Skupni CSS/JS za vse kože:",
+       "prefs-common-config": "Skupni CSS/JS za vse kože:",
        "prefs-reset-intro": "To stran lahko uporabite za ponastavitev nastavitev na privzete za to spletišče.\nTega ni mogoče razveljaviti.",
        "prefs-emailconfirm-label": "Potrditev e-pošte:",
        "youremail": "E-poštni naslov:",
        "thumbnail_dest_directory": "Ne morem ustvariti ciljnega direktorija",
        "thumbnail_image-type": "Vrsta slike ni podprta",
        "thumbnail_gd-library": "Nepopolna konfiguracija knjižice GD: manjka funkcija $1",
+       "thumbnail_image-size-zero": "Zdi se, da je velikost datoteke slike enaka nič.",
        "thumbnail_image-missing": "Kaže, da datoteka manjka: $1",
        "thumbnail_image-failure-limit": "Nedavno je bilo preveč spodletelih poskusov ($1 ali več) izdelave sličice. Prosimo, poskusite znova pozneje.",
        "import": "Uvoz strani",
index 835e324..855fb7a 100644 (file)
        "userjsyoucanpreview": "'''Tipp:''' Benutze dann Vurschau-Button, im dei neues JS vur damm Speichern zu testa.",
        "usercsspreview": "== Vurschau Dennes Nutzer-CSS ==\n'''Beachte:''' Noach damm Speichern mußt du dennen Browser oaweisa, de neue Version zu loada: '''Mozilla/Firefox:''' ''Strg-Shift-R'', '''Internet Explorer:''' ''Strg-F5'', '''Opera:''' ''F5'', '''Safari:''' ''Cmd-Shift-R'', '''Konqueror:''' ''F5''.",
        "userjspreview": "== Vurschau Dennes Nutzer-JavaScript ==\n'''Beachte:''' Noach damm Speichern mußt du dennen Browser oaweisa, de neue Version zu loada: '''Mozilla/Firefox:''' ''Strg-Shift-R'', '''Internet Explorer:''' ''Strg-F5'', '''Opera:''' ''F5'', '''Safari:''' ''Cmd-Shift-R'', '''Konqueror:''' ''F5''.",
-       "userinvalidcssjstitle": "'''OCHTICHE:''' Skin „$1“ existiert ne. Bedenke, doß nutzerspezifische .css- und .js-Seyta miet a'm Kleenbuchstaba oafanga missa, olso beispielsweise ''{{ns:user}}:Mustermann/vector.css'' oa Stalle vu ''{{ns:user}}:Mustermoan/Vector.css''.",
+       "userinvalidconfigtitle": "'''OCHTICHE:''' Skin „$1“ existiert ne. Bedenke, doß nutzerspezifische .css- und .js-Seyta miet a'm Kleenbuchstaba oafanga missa, olso beispielsweise ''{{ns:user}}:Mustermann/vector.css'' oa Stalle vu ''{{ns:user}}:Mustermoan/Vector.css''.",
        "previewnote": "'''Dies ies ock anne Vurschau, de Seite wurde noo nee gespeichert!'''",
        "previewconflict": "Diese Vurschau gitt 'n Inhalt des obern Textfeldes wieder. Su werd de Seite aussahn, wenn du jitz speicherst.",
        "session_fail_preview": "'''Denne Beoarbeetung konnte ne gespeichert waan, do Sitzungsdaten verlorn geganga sein.\nBitte versiche is erneut, indem du under dar fulgenda Textvurschau noo amols uff „Seyte speichern“ klickst.\nSullte doas Problem bestiehn bleiba, [[Special:UserLogout|melde diech ob]] und danone wieder oa.'''",
index 0603a2e..c148b64 100644 (file)
        "nstab-project": "Projekti",
        "nstab-image": "Skedari",
        "nstab-mediawiki": "Mesazhi",
-       "nstab-template": "Shablloni",
+       "nstab-template": "Stampa",
        "nstab-help": "Ndihmë",
        "nstab-category": "Kategoria",
        "mainpage-nstab": "Faqja kryesore",
        "userjspreview": "'''Vini re se kjo është vetëm një provë ose parapamje e faqes tuaj JavaScript, ajo nuk është ruajtur akoma!'''",
        "sitecsspreview": "<strong>Vini re! Ju jeni duke inspektuar CSS-në!\nNuk është ruajtur ende!</strong>",
        "sitejspreview": "<strong>Vini re! Ju jeni duke inspektuar këtë kod JavaScript. \nNuk është ruajtur ende!</strong>",
-       "userinvalidcssjstitle": "'''Kujdes:''' Nuk ka pamje të quajtur \"$1\". Vini re se faqet .css dhe .js përdorin titull me gërma të vogla, p.sh. {{ns:user}}:Foo/vector.css, jo {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "'''Kujdes:''' Nuk ka pamje të quajtur \"$1\". Vini re se faqet .css dhe .js përdorin titull me gërma të vogla, p.sh. {{ns:user}}:Foo/vector.css, jo {{ns:user}}:Foo/Vector.css.",
        "updated": "(E ndryshuar)",
        "note": "'''Shënim:'''",
        "previewnote": "'''Mos harro që kjo është vetëm një parapamje.'''\nNdryshimet e tua nuk janë ruajtur ende!",
        "prefs-files": "Figura",
        "prefs-custom-css": "CSS i përpunuem",
        "prefs-custom-js": "JavaScripti i përpunuar",
-       "prefs-common-css-js": "CSS/Javascript të përbashkët për të gjitha pamjet:",
+       "prefs-common-config": "CSS/Javascript të përbashkët për të gjitha pamjet:",
        "prefs-reset-intro": "Mundeni me përdorë këtë faqe për me i kthy parapëlqimet tueja në ato të paracaktuemet e faqes.\nKjo nuk mundet me u zhbâ.",
        "prefs-emailconfirm-label": "Konfirmimi i emailit:",
        "youremail": "Adresa e email-it*",
index 247745b..2cc1fe0 100644 (file)
        "aboutpage": "Project:О нама",
        "copyright": "Садржај је доступан под лиценцом $1 осим ако је другачије наведено.",
        "copyrightpage": "{{ns:project}}:Ауторска права",
-       "currentevents": "Ð\9dовости",
+       "currentevents": "Ð\90кÑ\82Ñ\83елности",
        "currentevents-url": "Project:Новости",
        "disclaimers": "Одрицање одговорности",
        "disclaimerpage": "Project:Одрицање одговорности",
        "edithelp": "Помоћ при уређивању",
        "helppage-top-gethelp": "Помоћ",
-       "mainpage": "Ð\93лавна Ñ\81Ñ\82Ñ\80аниÑ\86а",
-       "mainpage-description": "Ð\93лавна Ñ\81Ñ\82Ñ\80аниÑ\86а",
+       "mainpage": "Главна страна",
+       "mainpage-description": "Главна страна",
        "policy-url": "Project:Правила",
        "portal": "Портал заједнице",
        "portal-url": "Project:Портал заједнице",
        "nstab-template": "Шаблон",
        "nstab-help": "Помоћ",
        "nstab-category": "Категорија",
-       "mainpage-nstab": "Ð\93лавна Ñ\81Ñ\82Ñ\80аниÑ\86а",
+       "mainpage-nstab": "Главна страна",
        "nosuchaction": "Нема такве радње",
        "nosuchactiontext": "Радња наведена у адреси није исправна.\nМожда сте погрешно написали адресу или сте пратили застарелу везу.\nМогуће је и да се ради о грешци у софтверу викија.",
        "nosuchspecialpage": "Нема такве посебне странице",
        "passwordreset-emailsentusername": "Ако сте навели имејл адресу приликом регистрације, биће послат имејл за ресетовање лозинке.",
        "passwordreset-nocaller": "Позивалац се мора навести",
        "passwordreset-nosuchcaller": "Позивалац не постоји: $1",
+       "passwordreset-ignored": "Ресетовање лозинке није успело. Можда послужилац није конфигурисан?",
        "passwordreset-invalidemail": "Неисправна имејл адреса",
        "passwordreset-nodata": "Корисничко име и адреса е-поште нису наведени",
        "changeemail": "Промени или уклони имејл адресу",
        "userjspreview": "<strong>Ово је само преглед јаваскрипта.\nСтраница још није сачувана!</strong>",
        "sitecsspreview": "<strong>Ово је само преглед CSS-а.\nСтраница још није сачувана!</strong>",
        "sitejspreview": "<strong>Ово је само преглед јаваскрипта.\nСтраница још није сачувана!</strong>",
-       "userinvalidcssjstitle": "<strong>Упозорење:</strong> не постоји тема „$1“.\nПрилагођене странице CSS и јаваскрипт почињу малим словом, нпр. {{ns:user}}:Foo/vector.css, а не {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "<strong>Упозорење:</strong> не постоји тема „$1“.\nПрилагођене странице CSS и јаваскрипт почињу малим словом, нпр. {{ns:user}}:Foo/vector.css, а не {{ns:user}}:Foo/Vector.css.",
        "updated": "(ажурирано)",
        "note": "<strong>Напомена:</strong>",
        "previewnote": "<strong>Не заборавите да је ово само претпреглед.</strong>\nВаше измене још нису сачуване!",
        "yourtext": "Ваш текст",
        "storedversion": "Ускладиштена измена",
        "editingold": "<strong>Упозорење: уређујете застарелу измену ове странице.</strong>\nАко је сачувате, све новије измене ће бити изгубљене.",
+       "unicode-support-fail": "Ваш прегледач не подржава Unicode. Он је неопоходан за уређивање страница, па зато не могу сачувати измену.",
        "yourdiff": "Разлике",
        "copyrightwarning": "Имајте на уму да се сви доприноси на овом викију сматрају као објављени под лиценцом $2 (више на $1).\nАко не желите да се ваши текстови мењају и размењују без ограничења, онда их не шаљите овде.<br />\nИсто тако обећавате да сте Ви аутор текста, или да сте га умножили с извора који је у јавном власништву.\n<strong>Не шаљите радове заштићене ауторским правима без дозволе!</strong>",
        "copyrightwarning2": "Имајте на уму да се сви доприноси на овом викију могу мењати, враћати или брисати од других корисника.\nАко не желите да се ваши текстови слободно мењају и расподељују, не шаљите их овде.<br />\nИсто тако обећавате да сте ви аутор текста, или да сте га умножили с извора који је у јавном власништву (више на $1).\n<strong>Не шаљите радове заштићене ауторским правима без дозволе!</strong>",
        "permissionserrors": "Грешка у дозволи",
        "permissionserrorstext": "Немате овлашћење за ову радњу из {{PLURAL:$1|1=следећег|следећих}} разлога:",
        "permissionserrorstext-withaction": "Немате дозволу за $2 из {{PLURAL:$1|следећег|следећих}} разлога:",
+       "contentmodelediterror": "Не можете уредити ову измену јер је њен модел садржаја <code>$1</code>, што се разликује од тренутног модела садржаја странице <code>$2</code>.",
        "recreate-moveddeleted-warn": "<strong>Упозорење: поново правите страницу која је претходно обрисана.</strong>\n\nРазмотрите да ли је прикладно да наставите с уређивањем ове странице.\nОвде је наведена историја брисања и премештања с образложењем:",
        "moveddeleted-notice": "Ова страница је обрисана.\nИсторија њеног брисања, заштите и премештања налази се испод:",
        "moveddeleted-notice-recent": "Жао нам је, ова страница је недавно обрисана (у последњих 24 сата).\nИсторија њеног брисања, заштите и премештања налази се испод:",
        "mergehistory-fail-bad-timestamp": "Временска ознака није исправна.",
        "mergehistory-fail-invalid-source": "Изворна страница није исправна.",
        "mergehistory-fail-invalid-dest": "Одредишна страница није исправна.",
+       "mergehistory-fail-no-change": "Спајање историје није спојило ниједну измену. Проверите параметре странице и времена.",
        "mergehistory-fail-permission": "Немате овлашћење за спајање историје.",
        "mergehistory-fail-self-merge": "Изворна и одредишна страница не могу бити исте.",
+       "mergehistory-fail-timestamps-overlap": "Изворне измене се преклапају или долазе након одредишних измена.",
        "mergehistory-fail-toobig": "Није могуће спојити историје јер више од $1 {{PLURAL:$1|измене ће бити премештене|измена ће бити премештено}}.",
        "mergehistory-no-source": "Изворна страница $1 не постоји.",
        "mergehistory-no-destination": "Одредишна страница $1 не постоји.",
        "diff-multi-sameuser": "({{PLURAL:$1|Једна међуизмена истог корисника није приказана|$1 међуизмене истог корисника нису приказане|$1 међуизмена истог корисника није приказано}})",
        "diff-multi-otherusers": "({{PLURAL:$1|Једна међуизмена|$1 међуизмене|$1 међуизмена}} од стране {{PLURAL:$2|још једног корисника није приказана|$2 корисника није приказано}})",
        "diff-multi-manyusers": "({{PLURAL:$1|Није приказана међуизмена|Нису приказане $1 међуизмене|Није приказано $1 међуизмена}} од више од $2 корисника)",
+       "diff-paragraph-moved-tonew": "Одломак је премештен. Кликните да пређете на његово ново место.",
+       "diff-paragraph-moved-toold": "Одломак је премештен. Кликните да пређете на његово старо место.",
        "difference-missing-revision": "Не могу да пронађем {{PLURAL:$2|једну измену|$2 измене|$2 измена}} од ове разлике ($1).\n\nОво се обично дешава када пратите застарелу везу до странице која је обрисана.\nВише информација можете пронаћи у [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} дневнику брисања].",
        "searchresults": "Резултати претраге",
        "searchresults-title": "Резултати претраге за „$1“",
        "powersearch-toggleall": "Све",
        "powersearch-togglenone": "Ништа",
        "powersearch-remember": "Запамти мој избор за будуће претраге",
-       "search-external": "СпоÑ\99на претрага",
+       "search-external": "СпоÑ\99аÑ\88Ñ\9aа претрага",
        "searchdisabled": "Претрага је онемогућена.\nУ међувремену можете тражити преко Гугла.\nУпамтите да његови пописи овог викија могу бити застарели.",
        "search-error": "Дошло је до грешке приликом претраге: $1",
        "search-warning": "Упозорење приликом претраге: $1",
        "prefs-files": "Датотеке",
        "prefs-custom-css": "Прилагођени CSS",
        "prefs-custom-js": "Прилагођени јаваскрипт",
-       "prefs-common-css-js": "Дељени CSS/Јаваскрипт за све теме:",
+       "prefs-common-config": "Дељени CSS/Јаваскрипт за све теме:",
        "prefs-reset-intro": "Можете користити ову страницу да поништите своја подешавања на подразумеване вредности.\nОва радња се не може вратити.",
        "prefs-emailconfirm-label": "Потврда имејла:",
        "youremail": "Имејл:",
        "userrights-expiry-current": "Истиче $1",
        "userrights-expiry-none": "Не истиче",
        "userrights-expiry": "Истиче:",
-       "userrights-expiry-existing": "Ð\9fоÑ\81Ñ\82оÑ\98еÑ\9bе Ð²Ñ\80иÑ\98еме Ð¸Ñ\81Ñ\82ека: $3, $2",
-       "userrights-expiry-othertime": "Ð\94Ñ\80Ñ\83го Ð²Ñ\80иÑ\98еме:",
+       "userrights-expiry-existing": "Постојеће време истека: $3, $2",
+       "userrights-expiry-othertime": "Друго време:",
        "userrights-expiry-options": "1 дан:1 day,1 недеља:1 week,1 месец:1 month,3 месеца:3 months,6 месеци:6 months,1 година:1 year",
+       "userrights-invalid-expiry": "Време истицања групе „$1“ није исправно.",
+       "userrights-expiry-in-past": "Време истицања групе „$1“ је прошло.",
        "userrights-cannot-shorten-expiry": "Не можете убрзати истек чланства у групи „$1”. Само корисници са дозволом да додају или уклоне ову групу могу да убрзају рок истека.",
        "userrights-conflict": "Сукоб промена корисничких права! Молимо проверите ваше измене.",
        "group": "Група:",
        "right-createaccount": "отварање нових корисничких налога",
        "right-autocreateaccount": "Пријавите се аутоматски са екстерним корисничким налогом",
        "right-minoredit": "означавање измена мањим",
-       "right-move": "Ð\9fремештање страница",
+       "right-move": "премештање страница",
        "right-move-subpages": "премештање страница с њиховим подстраницама",
        "right-move-rootuserpages": "премештање основних корисничких страница",
-       "right-move-categorypages": "Ð\9fремештање категорија",
-       "right-movefile": "Ð\9fремештање датотека",
+       "right-move-categorypages": "премештање категорија",
+       "right-movefile": "премештање датотека",
        "right-suppressredirect": "премештање страница без остављања преусмерења",
-       "right-upload": "Ð\9eтпремање датотека",
+       "right-upload": "отпремање датотека",
        "right-reupload": "замењивање постојећих датотека",
        "right-reupload-own": "замењивање сопствених датотека",
        "right-reupload-shared": "мењање датотека на дељеном складишту мултимедије",
        "right-upload_by_url": "Отпремање датотека са веб-адресе",
        "right-purge": "чишћење кеш меморије странице без потврде",
-       "right-autoconfirmed": "без ограничавања ставки за ИП адресе",
+       "right-autoconfirmed": "без ограничавања ставки за IP адресе",
        "right-bot": "сматрање измена као аутоматски процес",
-       "right-nominornewtalk": "непоседовање малих измена на страницама за разговор отвара прозор за нове поруке",
-       "right-apihighlimits": "коришћење виших граница за упите из АПИ-ја",
-       "right-writeapi": "писање АПИ-ја",
+       "right-nominornewtalk": "непоседовање мањих измена на страницама за разговор отвара прозор за нове поруке",
+       "right-apihighlimits": "коришћење виших граница за упите из API-ја",
+       "right-writeapi": "писање API-ја",
        "right-delete": "брисање страница",
        "right-bigdelete": "брисање страница с великом историјом",
        "right-deletelogentry": "брисање и враћање одређених ставки у дневнику",
        "right-viewsuppressed": "прегледање измена скривених од свих корисника",
        "right-suppressionlog": "прегледање приватних дневника",
        "right-block": "блокирање даљих измена других корисника",
-       "right-blockemail": "Ð\91локиÑ\80аÑ\98 ÐºÐ¾Ñ\80иÑ\81никÑ\83 Ñ\81лаÑ\9aе Ð¸Ð¼ÐµÑ\98ла",
+       "right-blockemail": "блокиÑ\80аÑ\9aе ÐºÐ¾Ñ\80иÑ\81ника Ð´Ð° Ñ\88аÑ\99Ñ\83 Ð¸Ð¼ÐµÑ\98л",
        "right-hideuser": "блокирање корисничког имена и његово сакривање од јавности",
        "right-ipblock-exempt": "заобилажење блокирања ИП адресе, аутоматска блокирања и блокирања опсега",
        "right-unblockself": "деблокирање самог себе",
        "right-edituserjs": "уређивање туђих JavaScript датотека",
        "right-editmyusercss": "уређивање сопствених CSS датотека",
        "right-editmyuserjs": "уређивање сопствених JavaScript датотека",
-       "right-viewmywatchlist": "види Ñ\81опÑ\81Ñ\82вени Ñ\81пиÑ\81ак надгледања",
+       "right-viewmywatchlist": "пÑ\80еглед Ñ\81опÑ\81Ñ\82веног Ñ\81пиÑ\81ка надгледања",
        "right-editmywatchlist": "уређивање сопственог списка надгледања; неке предузете радње ће свеједно додати странице на списак и без овог права",
-       "right-viewmyprivateinfo": "видиÑ\82е Ñ\81воÑ\98е Ð»Ð¸Ñ\87не Ð¿Ð¾Ð´Ð°Ñ\82ке (нпр. имејл адресу, право име)",
-       "right-editmyprivateinfo": "Ñ\83Ñ\80еÑ\92иваÑ\9aе Ñ\81опÑ\81Ñ\82вениÑ\85 Ð»Ð¸Ñ\87ниÑ\85 Ð¿Ð¾Ð´Ð°Ñ\82ака (нпÑ\80. Ð¸Ð¼ÐµÑ\98л Ð°Ð´Ñ\80еÑ\81а, Ð¿Ñ\80аво Ð¸Ð¼Ðµ)",
+       "right-viewmyprivateinfo": "пÑ\80еглед Ñ\81воÑ\98иÑ\85 Ð»Ð¸Ñ\87ниÑ\85 Ð¿Ð¾Ð´Ð°Ñ\82ака (нпр. имејл адресу, право име)",
+       "right-editmyprivateinfo": "Ñ\83Ñ\80еÑ\92иваÑ\9aе Ñ\81опÑ\81Ñ\82вениÑ\85 Ð»Ð¸Ñ\87ниÑ\85 Ð¿Ð¾Ð´Ð°Ñ\82ака (нпÑ\80. Ð¸Ð¼ÐµÑ\98л Ð°Ð´Ñ\80еÑ\81е, Ð¿Ñ\80авог Ð¸Ð¼ÐµÐ½Ð°)",
        "right-editmyoptions": "уређивање сопствених подешавања",
        "right-rollback": "брзо враћање измена последњег корисника који је мењао одређену страницу",
        "right-markbotedits": "означавање враћених измена као измене бота",
        "right-userrights-interwiki": "уређивање корисничких права на другим викијима",
        "right-siteadmin": "закључавање и откључавање базе података",
        "right-override-export-depth": "извоз страница укључујући и повазене странице до дубине од пет веза",
-       "right-sendemail": "Пошаљи имејл другим корисницима",
+       "right-sendemail": "слање имејла другим корисницима",
        "right-managechangetags": "прављење и (де)активирање [[Special:Tags|ознака]]",
        "right-applychangetags": "примењивање [[Special:Tags|ознака]] на нечије измене",
        "right-changetags": "додавање и уклањање разних [[Special:Tags|ознака]] на појединачним изменама и уносима у дневницима",
        "right-deletechangetags": "брисање [[Special:Tags|ознака]] из базе података",
+       "grant-generic": "Скуп права „$1“",
        "grant-group-page-interaction": "Уређивање страница",
        "grant-group-file-interaction": "Уређивање датотека",
        "grant-group-watchlist-interaction": "Уређивање вашег списка надгледања",
        "action-createpage": "прављење страница",
        "action-createtalk": "прављење страница за разговор",
        "action-createaccount": "отварање овог корисничког налога",
+       "action-autocreateaccount": "аутоматско прављење овог спољашњег корисничког налога",
        "action-history": "гледање историје ове странице",
        "action-minoredit": "означавање ове измене као мање",
        "action-move": "премештање ове странице",
        "action-reupload": "замењивање постојеће датотеке",
        "action-reupload-shared": "постављање ове датотеке на заједничко складиште",
        "action-upload_by_url": "отпремање ове датотеке преко веб-адресе",
-       "action-writeapi": "писање АПИ-ја",
+       "action-writeapi": "писање API-ја",
        "action-delete": "брисање ове странице",
        "action-deleterevision": "брисање измена",
        "action-deletelogentry": "бирсање уноса у дневницима",
        "recentchanges-submit": "Прикажи",
        "rcfilters-tag-remove": "Обриши $1",
        "rcfilters-legend-heading": "<strong>Списак скраћеница:</strong>",
-       "rcfilters-other-review-tools": "Ð\9eÑ\81Ñ\82али Ð°Ð»Ð°Ñ\82и за преглед",
+       "rcfilters-other-review-tools": "Ð\9eÑ\81Ñ\82але Ð°Ð»Ð°Ñ\82ке за преглед",
        "rcfilters-group-results-by-page": "Групиши резултате по страницама",
        "rcfilters-activefilters": "Активни филтери",
        "rcfilters-advancedfilters": "Напредни филтери",
        "rcfilters-state-message-subset": "Овај филтер нема ефекта јер су његови резултати укључени са онима {{PLURAL:$2|следећег, ширег филтера|следећих, ширих филтера}} (покушајте са означавањем да бисте их распознали): $1",
        "rcfilters-state-message-fullcoverage": "Одабир свих филтера у групи је исто као и одабир ниједног, тако да овај филтер нема ефекта. Група укључује: $1",
        "rcfilters-filtergroup-authorship": "Ауторство доприноса",
-       "rcfilters-filter-editsbyself-label": "Ваше измјене",
+       "rcfilters-filter-editsbyself-label": "Ваше измене",
        "rcfilters-filter-editsbyself-description": "Ваши доприноси.",
-       "rcfilters-filter-editsbyother-label": "Измјене других",
-       "rcfilters-filter-editsbyother-description": "Све измјене осим Ваших.",
+       "rcfilters-filter-editsbyother-label": "Измене других",
+       "rcfilters-filter-editsbyother-description": "Све измене осим Ваших.",
        "rcfilters-filtergroup-userExpLevel": "Корисничка регистрација и искуство",
        "rcfilters-filter-user-experience-level-registered-label": "Регистровани",
        "rcfilters-filter-user-experience-level-registered-description": "Пријављени уредници.",
        "rcfilters-filter-user-experience-level-experienced-description": "Регистровани уредници са више од 500 измена и 30 дана активности.",
        "rcfilters-filtergroup-automated": "Аутоматизовани доприноси",
        "rcfilters-filter-bots-label": "Бот",
-       "rcfilters-filter-bots-description": "Измјене направљене аутоматизованим алатима.",
+       "rcfilters-filter-bots-description": "Измене направљене аутоматизованим алатима.",
        "rcfilters-filter-humans-label": "Човек (није бот)",
-       "rcfilters-filter-humans-description": "Измјене које су направили људи-уредници.",
+       "rcfilters-filter-humans-description": "Измене које су направили људи-уредници.",
        "rcfilters-filtergroup-reviewstatus": "Патролираност",
        "rcfilters-filter-patrolled-label": "Патролирано",
-       "rcfilters-filter-patrolled-description": "Измјене означене као патролиране.",
+       "rcfilters-filter-patrolled-description": "Измене означене као патролиране.",
        "rcfilters-filter-unpatrolled-label": "Непатролирано",
-       "rcfilters-filter-unpatrolled-description": "Измјене које нису означене као патролиране.",
+       "rcfilters-filter-unpatrolled-description": "Измене које нису означене као патролиране.",
        "rcfilters-filtergroup-significance": "Значај",
        "rcfilters-filter-minor-label": "Мање измене",
-       "rcfilters-filter-minor-description": "Измјене које је аутор означио као мање.",
-       "rcfilters-filter-major-label": "Не-мање измјене",
-       "rcfilters-filter-major-description": "Измјене које нису означене као мање.",
+       "rcfilters-filter-minor-description": "Измене које је аутор означио као мање.",
+       "rcfilters-filter-major-label": "Не-мање измене",
+       "rcfilters-filter-major-description": "Измене које нису означене као мање.",
        "rcfilters-filtergroup-watchlist": "Странице на списку надгледања",
        "rcfilters-filter-watchlist-watched-label": "На списку надгледања",
-       "rcfilters-filter-watchlist-watched-description": "Измјене страница на Вашем списку надгледања.",
-       "rcfilters-filter-watchlist-watchednew-label": "Нове измјене на списку надгледања",
-       "rcfilters-filter-watchlist-watchednew-description": "Измјене страница на списку надгледања које нисте посјетили од када су направљене измјене.",
+       "rcfilters-filter-watchlist-watched-description": "Измене страница на Вашем списку надгледања.",
+       "rcfilters-filter-watchlist-watchednew-label": "Нове измене на списку надгледања",
+       "rcfilters-filter-watchlist-watchednew-description": "Измене страница на списку надгледања које нисте посетили од када су направљене измене.",
        "rcfilters-filter-watchlist-notwatched-label": "Није на списку надгледања",
-       "rcfilters-filter-watchlist-notwatched-description": "Све осим измјена страница на Вашем списку надгледања.",
+       "rcfilters-filter-watchlist-notwatched-description": "Све осим измена страница на Вашем списку надгледања.",
        "rcfilters-filtergroup-watchlistactivity": "Стање на списку надгледања",
        "rcfilters-filter-watchlistactivity-unseen-label": "Непогледане измене",
        "rcfilters-filter-watchlistactivity-unseen-description": "Измене страница које нисте посетили од када су направљене измене.",
        "rcfilters-filter-watchlistactivity-seen-label": "Погледане измене",
        "rcfilters-filter-watchlistactivity-seen-description": "Измене страница које сте посетили од када су направљене измене.",
-       "rcfilters-filtergroup-changetype": "Тип Ð¸Ð·Ð¼Ñ\98ене",
+       "rcfilters-filtergroup-changetype": "Ð\92Ñ\80Ñ\81Ñ\82а Ð¸Ð·Ð¼ене",
        "rcfilters-filter-pageedits-label": "Измене страница",
-       "rcfilters-filter-pageedits-description": "Измјене вики садржаја, расправа, описа категорија…",
+       "rcfilters-filter-pageedits-description": "Измене вики садржаја, расправа, описа категорија…",
        "rcfilters-filter-newpages-label": "Стварање страница",
        "rcfilters-filter-newpages-description": "Измене којима се стварају нове странице.",
-       "rcfilters-filter-categorization-label": "Измјене категорија",
+       "rcfilters-filter-categorization-label": "Измене категорија",
        "rcfilters-filter-categorization-description": "Записи о страницама додатим или уклоњеним из категорија.",
        "rcfilters-filter-logactions-label": "Радње забележене у дневницима",
        "rcfilters-filter-logactions-description": "Административне радње, стварање налога, брисање страница, отпремања…",
        "rcfilters-hideminor-conflicts-typeofchange-global": "Филтер за „мање” измене је у сукобу са једним или више филтера типа измена, зато што одређени типови измена не могу да се означе као „мање”. Сукобљени филтери су означени у подручју Активни филтери, изнад.",
        "rcfilters-hideminor-conflicts-typeofchange": "Одређени типови измена не могу да се означе као „мање”, тако да је овај филтер у сукобу са следећим филтерима типа измена: $1",
        "rcfilters-typeofchange-conflicts-hideminor": "Овај филтер типа измене је у сукобу са филтером за „мање” измене. Одређени типови измена не могу да се означе као „мање”.",
-       "rcfilters-filtergroup-lastRevision": "Посљедње измјене",
-       "rcfilters-filter-lastrevision-label": "Посљедња измјена",
+       "rcfilters-filtergroup-lastRevision": "Последње измене",
+       "rcfilters-filter-lastrevision-label": "Последња измена",
        "rcfilters-filter-lastrevision-description": "Само најновија измена на страници.",
-       "rcfilters-filter-previousrevision-label": "Није посљедња измјена",
-       "rcfilters-filter-previousrevision-description": "Све измјене које нису „посљедње измјене”.",
+       "rcfilters-filter-previousrevision-label": "Није последња измена",
+       "rcfilters-filter-previousrevision-description": "Све измене које нису „последње измене”.",
        "rcfilters-filter-excluded": "Изостављено",
        "rcfilters-tag-prefix-namespace-inverted": "<strong>:није</strong> $1",
        "rcfilters-exclude-button-off": "Изостави означено",
        "rcfilters-exclude-button-on": "Изостави одабрано",
-       "rcfilters-view-tags": "Означене измјене",
+       "rcfilters-view-tags": "Означене измене",
        "rcfilters-view-namespaces-tooltip": "Филтрирај резултате према именском простору",
-       "rcfilters-view-tags-tooltip": "Филтрирај резултате према ознаци измјене",
+       "rcfilters-view-tags-tooltip": "Филтрирај резултате према ознаци измене",
        "rcfilters-view-return-to-default-tooltip": "Повратак на главни мени",
-       "rcfilters-view-tags-help-icon-tooltip": "Сазнајте више о означеним измјенама",
+       "rcfilters-view-tags-help-icon-tooltip": "Сазнајте више о означеним изменама",
        "rcfilters-liveupdates-button": "Ажурирања уживо",
        "rcfilters-liveupdates-button-title-on": "Искључи ажурирања уживо",
        "rcfilters-liveupdates-button-title-off": "Прикажи нове измене уживо",
        "rcfilters-filter-showlinkedfrom-label": "Прикажи измене на страницама са којих долазе везе",
        "rcfilters-filter-showlinkedfrom-option-label": "<strong>Странице са којих долазе везе до</strong> изабране странице",
        "rcfilters-filter-showlinkedto-label": "Прикажи измене на страницама ка којима воде везе",
-       "rcfilters-filter-showlinkedto-option-label": "<strong>Странице ка којима воде везе са</strong> одабране странице",
+       "rcfilters-filter-showlinkedto-option-label": "<strong>Странице ка којима воде везе са</strong> изабране странице",
+       "rcfilters-target-page-placeholder": "Унесите име странице (или категорије)",
        "rcnotefrom": "Испод {{PLURAL:$5|је измена|су измене}} од <strong>$3, $4</strong> (до <strong>$1</strong> приказано).",
        "rclistfromreset": "Ресетуј одабир датума",
        "rclistfrom": "Прикажи нове измене почев од $2, $3",
        "uploadstash-errclear": "Чишћење датотека није успело.",
        "uploadstash-refresh": "Освежи списак датотека",
        "uploadstash-thumbnail": "погледај минијатуру",
+       "uploadstash-exception": "Не могу сачувати датотеку у складиште ($1): „$2“.",
        "uploadstash-bad-path": "Путања не постоји.",
        "uploadstash-bad-path-invalid": "Путања није исправна.",
        "uploadstash-bad-path-unknown-type": "Непознат тип „$1“.",
        "uploadstash-file-not-found-no-remote-thumb": "Добављање минијатуре није успело: $1\nАдреса = $2",
        "uploadstash-file-not-found-missing-content-type": "Недостаје заглавље за врсту садржаја.",
        "uploadstash-file-not-found-not-exists": "Не могу наћи путању или ово није обична датотека.",
+       "uploadstash-file-too-large": "Не могу послужити датотеку већу од $1 {{PLURAL:$1|бајта|бајтова}}",
+       "uploadstash-not-logged-in": "Нико није пријављен. Датотеке морају припадати корисницима.",
        "uploadstash-wrong-owner": "Ова датотека ($1) не припада тренутном кориснику.",
        "uploadstash-no-such-key": "Нема таквог кључа ($1). Не могу уклонити.",
        "uploadstash-no-extension": "Нема траженог додатка.",
        "apisandbox-request-url-label": "Адреса захтева:",
        "apisandbox-request-time": "Време за извршавање захтјева: {{PLURAL:$1|$1 милисекунда|$1 милисекунде|$1 милисекунди}}",
        "apisandbox-results-fixtoken": "Исправи жетон и пошаљи поново",
+       "apisandbox-results-fixtoken-fail": "Нисам успео добити жетон „$1“.",
        "apisandbox-alert-page": "Поља на страници су неисправна.",
        "apisandbox-alert-field": "Вредност овог поља је неисправна.",
        "apisandbox-continue": "Настави",
        "deletedcontributions": "Обрисани кориснички доприноси",
        "deletedcontributions-title": "Обрисани кориснички доприноси",
        "sp-deletedcontributions-contribs": "доприноси",
-       "linksearch": "Ð\9fÑ\80еÑ\82Ñ\80ага Ñ\81поÑ\99ниÑ\85 Ð²ÐµÐ·Ð°",
+       "linksearch": "Ð\9fÑ\80еÑ\82Ñ\80ажи Ñ\81поÑ\99аÑ\88Ñ\9aе Ð²ÐµÐ·Ðµ",
        "linksearch-pat": "Образац претраге:",
        "linksearch-ns": "Именски простор:",
        "linksearch-ok": "Претражи",
        "revertpage": "Враћене измене [[Special:Contribs/$2|$2]] ([[User talk:$2|разговор]]) на последњу измену корисника [[User:$1|$1]]",
        "revertpage-nouser": "Измене скривеног корисника су враћене на последњу измену {{GENDER:$1|корисника|кориснице}} [[User:$1|$1]]",
        "rollback-success": "Измене {{GENDER:$1|корисника|кориснице}} {{GENDER:$3|$1}} су враћене на последњу измену {{GENDER:$2|корисника|кориснице}} {{GENDER:$4|$2}}.",
+       "rollback-success-notify": "Враћене измене корисника $1;\nвраћено на последњу измену корисника $2. [$3 Прикажи измене]",
        "sessionfailure-title": "Сесија је окончана",
        "sessionfailure": "Изгледа да постоји проблем с вашом сесијом;\nова радња је отказана да би се избегла злоупотреба.\nМолимо, поново пошаљите образац.",
        "changecontentmodel": "Промени модел садржаја странице",
        "changecontentmodel-cannot-convert": "Модел садржаја странице [[:$1]] се не може претворити у врсту $2.",
        "changecontentmodel-nodirectediting": "Модел садржаја $1 не подржава изравно уређивање",
        "changecontentmodel-emptymodels-title": "Нема доступних модела садржаја",
+       "changecontentmodel-emptymodels-text": "Модел садржаја странице [[:$1]] се не може претворити ни у једну другу врсту.",
        "log-name-contentmodel": "Дневник промене модела садржаја",
        "log-description-contentmodel": "Ова страница приказује измене у моделима садржаја страница и странице које су направљене са моделом садржаја који се разликује од подразумеваног.",
+       "logentry-contentmodel-new": "$1 је {{GENDER:$2|направио|направила}} страницу $3 с нестандардним моделом садржаја „$5“",
        "logentry-contentmodel-change": "$1 је {{GENDER:$2|променио|променила}} модел садржаја странице $3 из „$4“ у „$5“",
        "logentry-contentmodel-change-revertlink": "врати",
        "logentry-contentmodel-change-revert": "врати",
        "undeletehistorynoadmin": "Ова страница је обрисана.\nРазлог за брисање се налази испод, заједно с детаљима о кориснику који је изменио ову страницу пре брисања.\nТекст обрисаних измена је доступан само администраторима.",
        "undelete-revision": "Обрисана измена странице $1 (дана $4; $5) од стране {{GENDER:$3|корисника|кориснице|корисника}} $3:",
        "undeleterevision-missing": "Неисправна или непостојећа измена.\nМожда сте унели погрешну везу, или је измена враћена или уклоњена из архиве.",
+       "undeleterevision-duplicate-revid": "Не могу вратити {{PLURAL:$1|измену|$1 измене|$1 измена}} јер се {{PLURAL:$1|њен|њихов}} <code>rev_id</code> већ користи.",
        "undelete-nodiff": "Претходне измене нису пронађене.",
        "undeletebtn": "Врати",
        "undeletelink": "погледај/врати",
        "namespace": "Именски простор:",
        "invert": "Обрни избор",
        "tooltip-invert": "Означите ову кућицу да бисте сакрили измене на страницама у одабраном именском простору (и повезаним именским просторима, ако је означено)",
+       "tooltip-whatlinkshere-invert": "Означите ову кутију за сакривање веза са страница у изабраном именском простору.",
        "namespace_association": "Повезани именски простор",
        "tooltip-namespace_association": "Означите ову кућицу да бисте укључили и разговор или именски простор теме која је повезана с одабраним именским простором",
        "blanknamespace": "(главни)",
        "sp-contributions-newbies-sub": "За нове кориснике",
        "sp-contributions-newbies-title": "Доприноси нових корисника",
        "sp-contributions-blocklog": "дневник блокирања",
+       "sp-contributions-suppresslog": "обрисани {{GENDER:$1|кориснички}} доприноси",
        "sp-contributions-deleted": "обрисани {{GENDER:$1|доприноси}}",
        "sp-contributions-uploads": "отпремања",
        "sp-contributions-logs": "дневници",
        "autosumm-blank": "Уклоњен целокупан садржај странице",
        "autosumm-replace": "Замењен садржај странице са „$1“",
        "autoredircomment": "Преусмерење на [[$1]]",
-       "autosumm-removed-redirect": "Уклоњено преусмјерење ка [[$1]]",
+       "autosumm-removed-redirect": "Уклоњено преусмерење ка [[$1]]",
        "autosumm-changed-redirect-target": "Промењена одредишна страница у преусмерењу из [[$1]] у [[$2]]",
        "autosumm-new": "Нова страница: $1",
        "autosumm-newblank": "Направљена празна страница",
        "redirect-file": "Назив датотеке",
        "redirect-logid": "ID дневника",
        "redirect-not-exists": "Вредност није пронађена",
-       "fileduplicatesearch": "Ð\9fÑ\80еÑ\82Ñ\80ага Ð´Ñ\83пликаÑ\82а",
+       "fileduplicatesearch": "Ð\9fÑ\80еÑ\82Ñ\80ажи Ð´Ñ\83пликаÑ\82е",
        "fileduplicatesearch-summary": "Претрага дуплираних датотека према хеш вредности.",
        "fileduplicatesearch-filename": "Назив датотеке:",
        "fileduplicatesearch-submit": "Претражи",
        "specialpages-group-highuse": "Најчешће коришћене странице",
        "specialpages-group-pages": "Спискови страница",
        "specialpages-group-pagetools": "Алатке",
-       "specialpages-group-wiki": "Ð\9fодаÑ\86и Ð¸ Ð°Ð»Ð°Ñ\82и",
+       "specialpages-group-wiki": "Ð\9fодаÑ\86и Ð¸ Ð°Ð»Ð°Ñ\82ке",
        "specialpages-group-redirects": "Преусмеравање посебних страница",
        "specialpages-group-spam": "Алатке против непожељних порука",
-       "specialpages-group-developer": "Ð\9fÑ\80огÑ\80амеÑ\80Ñ\81ки Ð°Ð»Ð°Ñ\82и",
+       "specialpages-group-developer": "Ð\9fÑ\80огÑ\80амеÑ\80Ñ\81ке Ð°Ð»Ð°Ñ\82ке",
        "blankpage": "Празна страница",
        "intentionallyblankpage": "Ова страница је намерно остављена празном.",
        "external_image_whitelist": " #Оставите овај ред онаквим какав јесте<pre>\n#Испод додајте одломке регуларних израза (само део који се налази између //)\n#Они ће бити упоређени с адресама спољашњих слика\n#Оне које се поклапају биће приказане као слике, а преостале као везе до слика\n#Редови који почињу с тарабом се сматрају коментарима\n#Сви уноси су осетљиви на мала и велика слова\n\n#Додајте све одломке регуларних израза изнад овог реда. Овај ред не дирајте</pre>",
        "tag-list-wrapper": "([[Special:Tags|$1 {{PLURAL:$1|ознака|ознаке|ознака}}]]: $2)",
        "tag-mw-contentmodelchange": "промена модела садржаја",
        "tag-mw-contentmodelchange-description": "Измене које мењају модел садржаја странице",
-       "tag-mw-new-redirect": "Ново преусмјерење",
+       "tag-mw-new-redirect": "Ново преусмерење",
        "tag-mw-new-redirect-description": "Измене којима је направљено ново преусмерење или је страница измењена да буде преусмерење",
-       "tag-mw-removed-redirect": "Уклоњено преусмјерење",
+       "tag-mw-removed-redirect": "Уклоњено преусмерење",
        "tag-mw-removed-redirect-description": "Измене које мењају постојеће преусмерење у страницу без преусмерења",
        "tag-mw-changed-redirect-target": "Промењено одредиште преусмерења",
        "tag-mw-changed-redirect-target-description": "Измене које мењају одредиште преусмерења",
        "tag-mw-replace-description": "Измене који уклањају више од 90% садржаја странице",
        "tag-mw-rollback": "Враћање",
        "tag-mw-rollback-description": "Измене које враћају страницу на претходне измене",
-       "tag-mw-undo": "Поништена измена",
+       "tag-mw-undo": "Поништена ранија измена",
        "tag-mw-undo-description": "Измене које поништавају претходне измене",
        "tags-title": "Ознаке",
        "tags-intro": "На овој страници је наведен списак ознака с којима програм може да означи измене и његово значење.",
        "tags-edit-chosen-no-results": "Одговарајуће ознаке нису пронађене",
        "tags-edit-reason": "Разлог:",
        "tags-edit-success": "Измене су примењене.",
+       "tags-edit-nooldid-title": "Неисправна одредишна измена",
+       "tags-edit-none-selected": "Изаберите бар једну ознаку коју треба додати или уклонити.",
        "comparepages": "Упоређивање страница",
        "compare-page1": "Страница 1",
        "compare-page2": "Страница 2",
        "compare-revision-not-exists": "Наведена измена не постоји.",
        "diff-form": "Разлике",
        "diff-form-oldid": "ID старе измене (необавезно)",
+       "diff-form-revid": "ID измене или разлике",
        "diff-form-submit": "Прикажи разлике",
        "permanentlink": "Стална веза",
        "permanentlink-revid": "ID измене",
        "htmlform-time-placeholder": "ЧЧ:ММ:СС",
        "htmlform-datetime-placeholder": "ГГГГ-ММ-ДД ЧЧ:ММ:СС",
        "htmlform-title-badnamespace": "[[:$1]] није у именском простору „{{ns:$2}}“.",
+       "htmlform-title-not-creatable": "Страница „$1“ се не може направити",
        "htmlform-title-not-exists": "$1 не постоји.",
        "htmlform-user-not-exists": "<strong>$1</strong> не постоји.",
        "htmlform-user-not-valid": "<strong>$1</strong> није исправно корисничко име.",
        "authpage-cannot-link": "Не могу започети спајање налога.",
        "cannotauth-not-allowed-title": "Приступ је одбијен",
        "cannotauth-not-allowed": "Није Вам дозвољено да користите ову страницу",
-       "changecredentials": "Промјена акредитива",
+       "changecredentials": "Промена акредитива",
        "changecredentials-submit": "Промени",
        "removecredentials": "Уклањање акредитива",
        "credentialsform-provider": "Врста акредитива:",
index fca6d7c..c35d8ba 100644 (file)
        "userjspreview": "'''Ovo je samo pregled javaskripta.'''\n'''Stranica još nije sačuvana!'''",
        "sitecsspreview": "'''Ovo je samo pregled CSS-a.'''\n'''Stranica još nije sačuvana!'''",
        "sitejspreview": "'''Ovo je samo pregled javaskripta.'''\n'''Stranica još nije sačuvana!'''",
-       "userinvalidcssjstitle": "<strong>Upozorenje:</strong> ne postoji tema „$1“.\nPrilagođene stranice CSS i javaskript počinju malim slovom, npr. {{ns:user}}:Foo/vector.css, a ne {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "<strong>Upozorenje:</strong> ne postoji tema „$1“.\nPrilagođene stranice CSS i javaskript počinju malim slovom, npr. {{ns:user}}:Foo/vector.css, a ne {{ns:user}}:Foo/Vector.css.",
        "updated": "(Ažurirano)",
        "note": "<strong>Napomena:</strong>",
        "previewnote": "<strong>Ne zaboravite da je ovo samo pretpregled.</strong>\nVaše izmene još nisu sačuvane!",
        "prefs-files": "Datoteke",
        "prefs-custom-css": "Prilagođeni CSS",
        "prefs-custom-js": "Prilagođeni javaskript",
-       "prefs-common-css-js": "Deljeni CSS/javaskript za sve teme:",
+       "prefs-common-config": "Deljeni CSS/javaskript za sve teme:",
        "prefs-reset-intro": "Možete koristiti ovu stranicu da poništite svoje postavke na podrazumevane vrednosti.\nOva radnja se ne može vratiti.",
        "prefs-emailconfirm-label": "Potvrda imejla:",
        "youremail": "Imejl:",
index 148cde9..cbf9593 100644 (file)
        "userjspreview": "== Foarskau fon dien Benutser-CSS ==\n'''Beoachtje:''' Ätter dät Spiekerjen moast du dien Browser kweede, ju näie Version tou leeden: '''Mozilla/Firefox:''' ''Strg-Shift-R'', '''Internet Explorer:''' ''Strg-F5'', '''Opera:''' ''F5'', '''Safari:''' ''Cmd-Shift-R'', '''Konqueror:''' ''F5''.",
        "sitecsspreview": "\"Beoachtje, dät du bloot ne Foarskau fon dit CSS bekiekest.\"\n\"Der wuud noch nit spiekerd!\"",
        "sitejspreview": "'''Beoachtje, dät du bloot n Foarbekiek fon dit JavaScript bekiekest.'''\n'''Dät is noch nit spiekerd!'''",
-       "userinvalidcssjstitle": "'''Woarskauenge:''' Deer existiert neen Skin \"$1\". Betoank jädden, dät benutserspezifiske .css- un .js-Sieden män n Littek-Bouksteeuwe anfange mouten, also t.B. ''{{ns:user}}:Mustermann/vector.css'', nit ''{{ns:user}}:Mustermann/Vector.css''.",
+       "userinvalidconfigtitle": "'''Woarskauenge:''' Deer existiert neen Skin \"$1\". Betoank jädden, dät benutserspezifiske .css- un .js-Sieden män n Littek-Bouksteeuwe anfange mouten, also t.B. ''{{ns:user}}:Mustermann/vector.css'', nit ''{{ns:user}}:Mustermann/Vector.css''.",
        "updated": "(Annerd)",
        "note": "'''Waiwiesenge:'''",
        "previewnote": "'''Dit is man ne Foarbekiek, ju Siede wuude noch nit spiekerd!'''",
        "prefs-files": "Doatäie",
        "prefs-custom-css": "Benutserdefinierde CSS",
        "prefs-custom-js": "Benutserdefinierd JS",
-       "prefs-common-css-js": "Gemeensoam CSS/JS foar aal Skins:",
+       "prefs-common-config": "Gemeensoam CSS/JS foar aal Skins:",
        "prefs-reset-intro": "Du koast disse Siede bruuke, uum do Ienstaalengen ap do Standoarde touräächtousätten.\nDät kon nit moor tourääch troald wäide.",
        "prefs-emailconfirm-label": "E-Mail-Bestäätigenge:",
        "youremail": "E-Mail-Adrässe:",
index fa6ae89..736a629 100644 (file)
        "userjspreview": "'''Inget yén anjeun ukur nguji/nyawang ''javascript'' pamaké anjeun, can disimpen!'''",
        "sitecsspreview": "'''Inget yén ieu CSS ukur pramidang.'''\n'''Can disimpen!'''",
        "sitejspreview": "'''Inget yén ieu kodeu JavaScript ukur pramidang.'''\n'''Can disimpen!'''",
-       "userinvalidcssjstitle": "'''Awas''': kulit \"$1\" mah teu aya. Sing émut yén kaca .css jeung .js mah migunakeun aksara leutik dina judulna, contona baé {{ns:user}}:Foo/vector.css lawan {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "'''Awas''': kulit \"$1\" mah teu aya. Sing émut yén kaca .css jeung .js mah migunakeun aksara leutik dina judulna, contona baé {{ns:user}}:Foo/vector.css lawan {{ns:user}}:Foo/Vector.css.",
        "updated": "(Geus diropéa)",
        "note": "'''Catetan:'''",
        "previewnote": "'''Inget yén ieu ukur pratayang, can disimpen.'''\nÉditan anjeun can disimpen!",
        "prefs-files": "Berkas",
        "prefs-custom-css": "CSS sakahayang",
        "prefs-custom-js": "JavaScript sakahayang",
-       "prefs-common-css-js": "CSS/JavaScript dipaké pikeun sakabéh kulit:",
+       "prefs-common-config": "CSS/JavaScript dipaké pikeun sakabéh kulit:",
        "prefs-reset-intro": "Anjeun bisa maké ieu kaca pikeun mulangkeun préferénsi anjeun ka nu baku.\nMun geus anggeus teu bisa dibolaykeun.",
        "prefs-emailconfirm-label": "Konfirmasi surélék:",
        "youremail": "Surélék:",
        "logentry-protect-unprotect": "$1 {{GENDER:$2|mupus}} panangtayungan ti $3",
        "logentry-protect-protect": "$1 {{GENDER:$2|ditangtayungan}} $3 $4",
        "logentry-upload-upload": "$1 {{GENDER:$2|ngamuat}} $3",
-       "logentry-upload-overwrite": "$1 {{GENDER:$2|ngunggah}} $3 vérsi anyar",
+       "logentry-upload-overwrite": "$1 {{GENDER:$2|ngunjal}} $3 vérsi anyar",
        "logentry-upload-revert": "$1 {{GENDER:$2|diunjal}} $3",
        "log-name-managetags": "Log pangokolaan tag",
        "logentry-managetags-create": "$1 {{GENDER:$2|nyieun}} tag \"$4\"",
index 3ab3adb..2729c41 100644 (file)
        "userjspreview": "'''Kom ihåg att du bara testar/förhandsgranskar ditt JavaScript.'''\n'''Det har inte sparats än!'''",
        "sitecsspreview": "'''Kom ihåg att du bara förhandsgranskar detta CSS.''' \n'''Det har ännu inte sparats!'''",
        "sitejspreview": "'''Kom ihåg att du bara förhandsgranskar denna JavaScript-kod.'''\n'''Det har ännu inte sparats!'''",
-       "userinvalidcssjstitle": "'''Varning:''' Utseendet \"$1\" finns inte. Kom ihåg att .css- och .js-sidor för enskilda användare börjar på liten bokstav. Exempel: {{ns:user}}:Foo/vector.css i stället för {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "'''Varning:''' Utseendet \"$1\" finns inte. Kom ihåg att .css- och .js-sidor för enskilda användare börjar på liten bokstav. Exempel: {{ns:user}}:Foo/vector.css i stället för {{ns:user}}:Foo/Vector.css.",
        "updated": "(Uppdaterad)",
        "note": "'''Obs!'''",
        "previewnote": "'''Kom ihåg att detta bara är en förhandsvisning.'''\nDina ändringar har ännu inte sparats!",
        "prefs-files": "Filer",
        "prefs-custom-css": "Personlig CSS",
        "prefs-custom-js": "Personligt JavaScript",
-       "prefs-common-css-js": "Delad CSS/JS för alla utseenden:",
+       "prefs-common-config": "Delad CSS/JS för alla utseenden:",
        "prefs-reset-intro": "Du kan använda den här sidan till att återställa dina inställningar till webbplatsens standardinställningar.\nDetta kan inte återställas.",
        "prefs-emailconfirm-label": "E-postbekräftelse:",
        "youremail": "E-post:",
        "rcfilters-invalid-filter": "Ogiltigt filter",
        "rcfilters-empty-filter": "Inga aktiva filter. Alla bidrag visas.",
        "rcfilters-filterlist-title": "Filter",
-       "rcfilters-filterlist-whatsthis": "Hur fungerar desse?",
+       "rcfilters-filterlist-whatsthis": "Hur fungerar dessa?",
        "rcfilters-filterlist-feedbacklink": "Berätta vad du tycker om dessa (nya) filtreringsverktyg",
        "rcfilters-highlightbutton-title": "Markera resultat",
        "rcfilters-highlightmenu-title": "Välj en färg",
        "thumbnail_dest_directory": "Kan inte skapa målkatalogen",
        "thumbnail_image-type": "Bildtypen stöds inte",
        "thumbnail_gd-library": "Inkomplett GD library konfigurering: saknar funktionen $1",
+       "thumbnail_image-size-zero": "Bildens filstorlek verkar vara noll.",
        "thumbnail_image-missing": "Fil verkar saknas: $1",
        "thumbnail_image-failure-limit": "Det har nyligen förekommit alltför många misslyckade försök ($1 eller fler) att skapa den här miniatyrbilden. Försök igen senare.",
        "import": "Importera sidor",
index 219a4f6..62318b8 100644 (file)
        "userjspreview": "'''Kumbuka kwamba unajaribu/kuhakiki mandhari ya ukurasa wako wa JavaScript tu.'''\n'''Haijahifadhiwa bado!'''",
        "sitecsspreview": "'''Kumbuka kwamba unahakiki tu mandhari ya CSS hii.'''\n'''Haijahifadhiwa bado!'''",
        "sitejspreview": "'''Kumbuka kwamba unahakiki tu mandhari ya JavaScript hii.'''\n'''Haijahifadhiwa bado!'''",
-       "userinvalidcssjstitle": "'''Onyo:''' Hakuna umbo \"$1\".\nKumbuka kwamba desturi ya kurasa za .css na .js hutumia herufi ndogo, yaani, {{ns:user}}:Foo/vector.css na si {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "'''Onyo:''' Hakuna umbo \"$1\".\nKumbuka kwamba desturi ya kurasa za .css na .js hutumia herufi ndogo, yaani, {{ns:user}}:Foo/vector.css na si {{ns:user}}:Foo/Vector.css.",
        "updated": "(Imesasishwa)",
        "note": "'''Taarifa:'''",
        "previewnote": "'''Hii ni hakikisho tu.''' \nMabadiliko hayajahifadhiwa bado!",
        "prefs-files": "Mafaili",
        "prefs-custom-css": "CSS niliyotunga mwenyewe",
        "prefs-custom-js": "JS niliyotunga mwenyewe",
-       "prefs-common-css-js": "CSS/JS inayoshirikishwa na maumbo yote:",
+       "prefs-common-config": "CSS/JS inayoshirikishwa na maumbo yote:",
        "prefs-reset-intro": "Unaweza kutumia ukurasa huu ili kurudisha mapendekezo yako kwenye yale ya msingi ya tovuti.\nHutaweza kulibatilisha tendo hili baadaye.",
        "prefs-emailconfirm-label": "Kuhakikisha barua pepe:",
        "youremail": "Barua pepe yako:",
index 0ba4ef5..2fa107f 100644 (file)
        "userjspreview": "'''Pamjyntej, aże to je no raźe ino podglůnd Twojego arkusza stylůw JavaScriptu.'''\n'''Ńic jeszcze ńy zostoło naszkryflane!'''",
        "sitecsspreview": "'''Pamjyntej, aże to je na raźe ino podglůnd Twojego arkusza stylůw CSS.'''\n'''Ńic jeszcze ńy zostoło naszkryflane!'''",
        "sitejspreview": "'''Pamjyntej, aże to je na raźe ino podglůnd Twojego JavaScriptu - nic jeszcze ńy zostoło naszkryflane!'''",
-       "userinvalidcssjstitle": "'''Pozůr:''' Ńy mo skůrki uo mjańe \"$1\". Pamjyntej, aże zajty użytkowńika zawjyrajůnce CSS i JavaScript powinny zaczynać śe małům buchsztabům, lb. {{ns:user}}:Foo/vector.css.",
+       "userinvalidconfigtitle": "'''Pozůr:''' Ńy mo skůrki uo mjańe \"$1\". Pamjyntej, aże zajty użytkowńika zawjyrajůnce CSS i JavaScript powinny zaczynać śe małům buchsztabům, lb. {{ns:user}}:Foo/vector.css.",
        "updated": "(Pomjyńano)",
        "note": "'''Pozůr:'''",
        "previewnote": "'''To je ino podglůnd - artikel jeszcze ńy je spamjyntany!'''",
index 979c3f2..655423a 100644 (file)
        "userjspreview": "'''நீர் உமது சாவா நிரலை சோதிக்கிறீர் அல்லது முன் தோற்றத்தை மட்டும் பார்க்கிறீர் என்பதை நினைவில் கொள்ளவும், இன்னமும் சேமிக்கப்படவில்லை!'''",
        "sitecsspreview": "'''நீங்கள் மட்டுமே இந்த CSS. இன் முன் தோற்றத்தை காண்கிறீர்கள் என்பதை நினைவில் கொள்ளவும்.'''\n'''இது இன்னமும் சேமிக்கப்படவில்லை!'''",
        "sitejspreview": "'''நீங்கள் மட்டுமே இந்த JavaScript code இன் முன் தோற்றத்தை காண்கிறீர்கள் என்பதை நினைவில் கொள்ளவும்.'''\n'''இது இன்னமும் சேமிக்கப்படவில்லை!'''",
-       "userinvalidcssjstitle": "'''எச்சரிக்கை:'''  \"$1\" என்றப் பெயரில் தோல்லொறுக் கிடையாது. சி.எஸ்.எஸ் மற்றும் ஜெ.எஸ். பக்கங்கள் ஆங்கில கீழ் வரிசைப் பெயர்களைக் கொண்டிருக்க வேண்டும் என்பதைக் கவனிக்கவும். எ+கா: {{ns:user}}:Foo/vector.css என்பது சரியான வடிவம் {{ns:user}}:Foo/Vector.css என்பது பிழையான வடிவம்.",
+       "userinvalidconfigtitle": "'''எச்சரிக்கை:'''  \"$1\" என்றப் பெயரில் தோல்லொறுக் கிடையாது. சி.எஸ்.எஸ் மற்றும் ஜெ.எஸ். பக்கங்கள் ஆங்கில கீழ் வரிசைப் பெயர்களைக் கொண்டிருக்க வேண்டும் என்பதைக் கவனிக்கவும். எ+கா: {{ns:user}}:Foo/vector.css என்பது சரியான வடிவம் {{ns:user}}:Foo/Vector.css என்பது பிழையான வடிவம்.",
        "updated": "(இற்றைப்படுத்தப்பட்டது)",
        "note": "'''குறிப்பு:'''",
        "previewnote": "'''இது ஒரு முன்தோற்றம் மட்டுமே''', உங்கள் மாற்றங்கள் இன்னும் சேமிக்கப்படவில்லை!",
        "prefs-files": "கோப்புகள்",
        "prefs-custom-css": "தனிப்பட்ட சி.எசு.எசு (CSS)",
        "prefs-custom-js": "தனிபயன் ஜாவாஸ்கிரிப்ட்",
-       "prefs-common-css-js": "எல்லா முகப்புறைகளுக்குமான(skins) பகிரப்பட்ட சி.எசு.எசு/சாவாகிறிப்டு (CSS/JavaScript):",
+       "prefs-common-config": "எல்லா முகப்புறைகளுக்குமான(skins) பகிரப்பட்ட சி.எசு.எசு/சாவாகிறிப்டு (CSS/JavaScript):",
        "prefs-reset-intro": " இந்த பக்கத்தை பயன்படுத்தி உங்கள் விருப்பங்களை தள இயல்புநிலைக்கு மீட்டமைக்கலாம்.\nஇது செய்யாமல் இருக்க இயலாது.",
        "prefs-emailconfirm-label": "மின்னஞ்சலை உறுதிசெய்தல்:",
        "youremail": "மின்னஞ்சல்:",
index 03054cd..08a3884 100644 (file)
        "userjspreview": "<strong>గుర్తుంచుకోండి, మీరింకా మీ వాడుకరి జావాస్క్రిప్ట్&zwnj;ను భద్రపరచలేదు, కేవలం పరీక్షిస్తున్నారు/సరిచూస్తున్నారు!</strong>",
        "sitecsspreview": "'''మీరు చూస్తున్నది ఈ CSS మునుజూపును మాత్రమేనని గుర్తుంచుకోండి.'''\n'''దీన్నింకా భద్రపరచలేదు!'''",
        "sitejspreview": "'''మీరు చూస్తున్నది ఈ JavaScript మునుజూపును మాత్రమేనని గుర్తుంచుకోండి.''' \n'''దీన్నింకా భద్రపరచలేదు!'''",
-       "userinvalidcssjstitle": "<strong>హెచ్చరిక:</strong> \"$1\" అనే రూపు లేదు.\nఅభిమత .css మరియు .js పుటల శీర్షికలు ఇంగ్లీషు చిన్నబడి అక్షరాల లోనే ఉండాలని గుర్తుంచుకోండి, ఉదాహరణకు ఇలా {{ns:user}}:Foo/vector.css అంతేగానీ, {{ns:user}}:Foo/Vector.css ఇలా కాదు.",
+       "userinvalidconfigtitle": "<strong>హెచ్చరిక:</strong> \"$1\" అనే రూపు లేదు.\nఅభిమత .css మరియు .js పుటల శీర్షికలు ఇంగ్లీషు చిన్నబడి అక్షరాల లోనే ఉండాలని గుర్తుంచుకోండి, ఉదాహరణకు ఇలా {{ns:user}}:Foo/vector.css అంతేగానీ, {{ns:user}}:Foo/Vector.css ఇలా కాదు.",
        "updated": "(నవీకరించబడింది)",
        "note": "<strong>గమనిక:</strong>",
        "previewnote": "<strong>ఇది మునుజూపు మాత్రమేనని గుర్తుంచుకోండి.</strong>\nమీ మార్పులు ఇంకా భద్రమవ్వలేదు!",
        "prefs-files": "ఫైళ్ళు",
        "prefs-custom-css": "ప్రత్యేక CSS",
        "prefs-custom-js": "ప్రత్యేక JS",
-       "prefs-common-css-js": "అన్ని రూపులలోనూ ఉన్న CSS/JS:",
+       "prefs-common-config": "అన్ని రూపులలోనూ ఉన్న CSS/JS:",
        "prefs-reset-intro": "ఈ పేజీలో, మీ అభిరుచులను సైటు డిఫాల్టు విలువలకు మార్చుకోవచ్చు. మళ్ళీ వెనక్కి తీసుకుపోలేరు.",
        "prefs-emailconfirm-label": "ఈ-మెయిల్ నిర్ధారణ:",
        "youremail": "ఈమెయిలు:",
index 85d3bf0..03ca342 100644 (file)
        "userjsyoucanpreview": "<strong>Эзоҳ:</strong> Тугмаи \"{{int:showpreview}}\" барои санҷиши парванди ҶаваСкрипт қабл аз захира истифода баред.",
        "usercsspreview": "<strong>Фаромӯш накунед, ки шумо фақат CSS корбариатонро пешнамоиш карда истодааед. Он ҳанӯз захира нашудааст!</strong>",
        "userjspreview": "'''Фаромӯш накунед, ки шумо фақат ҶаваСкрипти корбариатонро имтиҳон,пешнамоиш карда истодаед ва он ҳанӯз захира нашудааст!'''",
-       "userinvalidcssjstitle": "'''Ҳушдор:'''Пӯсте бо номи \"$1\" вуҷуд надорад. Таваҷҷӯҳ кунед ки саҳифаҳои .css ва .js бо ҳарфҳои хурд навишта мешаванд, Намуна. {{ns:user}}:Фу/vector.css дар муқобили корбар {{ns:user}}:Фу/Vector.css.",
+       "userinvalidconfigtitle": "'''Ҳушдор:'''Пӯсте бо номи \"$1\" вуҷуд надорад. Таваҷҷӯҳ кунед ки саҳифаҳои .css ва .js бо ҳарфҳои хурд навишта мешаванд, Намуна. {{ns:user}}:Фу/vector.css дар муқобили корбар {{ns:user}}:Фу/Vector.css.",
        "updated": "(Ба рӯз шуда)",
        "note": "'''Эзоҳ:'''",
        "previewnote": "'''Ба ёд дошта бошед, ки ин фақат пешнамоиш аст.'''\nТағийроти шумо ҳанӯз захира нашудааст!",
index 36e76c3..9171657 100644 (file)
        "usercssyoucanpreview": "'''Ezoh:''' Peş parvandai CSS jo JS xudro zaxira kuned, bo istifoda az tugmai \"Peşnamoiş\" metavoned onro ozmoiş kuned.",
        "userjsyoucanpreview": "'''Ezoh:''' Peş parvandai CSS jo JS xudro zaxira kuned, bo istifoda az tugmai \"Peşnamoiş\" metavoned onro ozmoiş kuned.",
        "userjspreview": "'''Faromūş nakuned, ki şumo faqat ÇavaSkripti korbariatonro imtihon,peşnamoiş karda istodaed va on hanūz zaxira naşudaast!'''",
-       "userinvalidcssjstitle": "'''Huşdor:'''Pūste bo nomi \"$1\" vuçud nadorad. Tavaççūh kuned ki sahifahoi .css va .js bo harfhoi xurd navişta meşavand, Namuna. {{ns:user}}:Fu/vector.css dar muqobili korbar {{ns:user}}:Fu/Vector.css.",
+       "userinvalidconfigtitle": "'''Huşdor:'''Pūste bo nomi \"$1\" vuçud nadorad. Tavaççūh kuned ki sahifahoi .css va .js bo harfhoi xurd navişta meşavand, Namuna. {{ns:user}}:Fu/vector.css dar muqobili korbar {{ns:user}}:Fu/Vector.css.",
        "updated": "(Ba rūz şuda)",
        "note": "'''Ezoh:'''",
        "previewnote": "'''In faqat peşnamoiş ast; digarguniho holo zaxira naşudaand!'''",
index a938047..fac7ea7 100644 (file)
        "userjspreview": "<strong>พึงระลึกว่าคุณกำลังทดสอบ/ดูตัวอย่างจาวาสคริปต์ผู้ใช้ของคุณ\nยังไม่ได้บันทึก!</strong>",
        "sitecsspreview": "<strong>พึงระลึกว่าคุณเพียงกำลังแสดงตัวอย่าง CSS นี้\nยังไม่ได้บันทึก!</strong>",
        "sitejspreview": "<strong>พึงระลึกว่าคุณเพียงกำลังแสดงตัวอย่างโค้ดจาวาสคริปต์นี้\nยังไม่ได้บันทึก!</strong>",
-       "userinvalidcssjstitle": "<strong>คำเตือน:</strong> ไม่มีหน้าตา \"$1\" หน้า .css และ .js ใช้ตัวเล็กทั้งหมด เช่น {{ns:user}}:Foo/vector.css มิใช่ {{ns:user}}:Foo/Vector.css",
+       "userinvalidconfigtitle": "<strong>คำเตือน:</strong> ไม่มีหน้าตา \"$1\" หน้า .css และ .js ใช้ตัวเล็กทั้งหมด เช่น {{ns:user}}:Foo/vector.css มิใช่ {{ns:user}}:Foo/Vector.css",
        "updated": "(ปรับแล้ว)",
        "note": "<strong>หมายเหตุ:</strong>",
        "previewnote": "<strong>พึงระลึกว่านี่เป็นเพียงการแสดงตัวอย่าง</strong>\nยังไม่ได้บันทึกการเปลี่ยนแปลงของคุณ!",
        "prefs-files": "ไฟล์",
        "prefs-custom-css": "สไตล์ชีตปรับแต่งเอง",
        "prefs-custom-js": "จาวาสคริปต์ปรับแต่งเอง",
-       "prefs-common-css-js": "CSS / จาวาสคริปต์รวมสำหรับทุกหน้าตา:",
+       "prefs-common-config": "CSS / จาวาสคริปต์รวมสำหรับทุกหน้าตา:",
        "prefs-reset-intro": "คุณสามารถใช้หน้านี้ตั้งการตั้งค่าของคุณเป็นค่าปริยายของเว็บใหม่\nไม่สามารถทำกลับได้",
        "prefs-emailconfirm-label": "การยืนยันอีเมล:",
        "youremail": "อีเมล:",
index 73e5cd2..0c76518 100644 (file)
        "userjsyoucanpreview": "'''Ümleme:''' Täze JavaScriptiňizi ýazdyrmankaňyz, synap görmek üçin \"{{int:showpreview}}\" düwmesine basyň.",
        "usercsspreview": "'''Bu ulanyjy CSS faýlyňyzyň ýöne bir deslapky syny.'''\n'''Ol heniz ýazdyrylan däldir!'''",
        "userjspreview": "'''Bu ulanyjy JavaScriptiňiziň ýöne bir barlagy/deslapky syny.'''\n'''Ol heniz ýazdyrylan däldir!'''",
-       "userinvalidcssjstitle": "''Duýduryş:''' \"$1\" atly bezeg ýok.\nHususy .css we .js sahypalarynyň setir harp bilen ýazylýandygyny ýatda saklaň, ýagny {{ns:user}}:Ulanyjy/Vector.css däl-de, eýsem {{ns:user}}:Ulanyjy/vector.css.",
+       "userinvalidconfigtitle": "''Duýduryş:''' \"$1\" atly bezeg ýok.\nHususy .css we .js sahypalarynyň setir harp bilen ýazylýandygyny ýatda saklaň, ýagny {{ns:user}}:Ulanyjy/Vector.css däl-de, eýsem {{ns:user}}:Ulanyjy/vector.css.",
        "updated": "(Täzelenen)",
        "note": "'''Bellik:'''",
        "previewnote": "'''Ýatda saklaň, bu bir ýöne deslapky syn.''' Üýtgeşmeleriňiz heniz ýazdyrylan däldir!",
        "prefs-files": "Faýllar",
        "prefs-custom-css": "Hususy CSS",
        "prefs-custom-js": "Hususy JS",
-       "prefs-common-css-js": "Ähli bezegler üçin paýlaşylýan CSS/JavaScript:",
+       "prefs-common-config": "Ähli bezegler üçin paýlaşylýan CSS/JavaScript:",
        "prefs-reset-intro": "Bu sahypada öz ileri tutmalaryňyzy saýtyň gaýybana ýagdaýyna getirip bilersiňiz. Yzyna dikeldip bolmaýar.",
        "prefs-emailconfirm-label": "E-poçta tassyklamasy:",
        "youremail": "E-poçta:",
index 9d822ad..bbaa125 100644 (file)
        "userjspreview": "'''Tandaang pagsubok/paunang tingin mo pa lang ito ng iyong JavaScript.'''\n'''Hindi pa ito nasasagip!'''",
        "sitecsspreview": "'''Tandaan mong paunang tingin pa lamang ito ng nasabing CSS.'''\n'''Hindi pa ito nasasagip!'''",
        "sitejspreview": "'''Tandaan mong paunang tingin pa lamang ito ng nasabing kodigong JavaScript.'''\n'''Hindi pa ito nasasagip!'''",
-       "userinvalidcssjstitle": "'''Babala:''' Walang pabalat na \"$1\".\nTandaang gumagamit ang pinasadyang mga pahinang .css at .js ng mga pamagat na may maliliit na mga titik, halimbawa na ang {{ns:user}}:Foo/vector.css na taliwas sa {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "'''Babala:''' Walang pabalat na \"$1\".\nTandaang gumagamit ang pinasadyang mga pahinang .css at .js ng mga pamagat na may maliliit na mga titik, halimbawa na ang {{ns:user}}:Foo/vector.css na taliwas sa {{ns:user}}:Foo/Vector.css.",
        "updated": "(Naisapanahon na)",
        "note": "'''Paunawa:'''",
        "previewnote": "'''Tandaan na isa lamang itong paunang tingin.'''\nHindi pa nasasagip ang mga binago mo!",
        "prefs-files": "Mga file",
        "prefs-custom-css": "Pasadyang CSS",
        "prefs-custom-js": "Pasadyang JS",
-       "prefs-common-css-js": "Naibahaging CSS/JS para sa lahat ng pabalat:",
+       "prefs-common-config": "Naibahaging CSS/JS para sa lahat ng pabalat:",
        "prefs-reset-intro": "Magagamit mo ang pahinang ito upang muling maitakda ang mga kagustuhan mo sa likas na pagtatakda ng sityo.\nHindi ito maibabalik sa dating gawi.",
        "prefs-emailconfirm-label": "Kumpirmasyon ng e-liham:",
        "youremail": "E-liham:",
index bf16795..bb4834c 100644 (file)
        "userjspreview": "'''Sadece test ediyorsun ya da önizleme görüyorsun - kullanıcı JavaScript'i henüz kaydolmadı.'''",
        "sitecsspreview": "'''Sadece kullanıcı CSS dosyanızın önizlemesini görüyorsunuz.''' \n'''Henüz kaydedilmedi!'''",
        "sitejspreview": "'''Sadece kullanıcı JavaScript kod dosyanızın önizlemesini görüyorsunuz.''' \n'''Henüz kaydedilmedi!'''",
-       "userinvalidcssjstitle": "'''Uyarı:''' \"$1\" adında bir tema yoktur. Özel .css ve .js sayfalarının adlarını küçük harf ile yazın, örneğin;  \"{{ns:user}}:Örnek/Vector.css\" yerine \"{{ns:user}}:Örnek/vector.css\" yazın.",
+       "userinvalidconfigtitle": "'''Uyarı:''' \"$1\" adında bir tema yoktur. Özel .css ve .js sayfalarının adlarını küçük harf ile yazın, örneğin;  \"{{ns:user}}:Örnek/Vector.css\" yerine \"{{ns:user}}:Örnek/vector.css\" yazın.",
        "updated": "(Güncellendi)",
        "note": "'''Not: '''",
        "previewnote": "'''Bunun yalnızca bir ön izleme olduğunu unutmayın.'''\nYaptığınız değişiklikler henüz kaydedilmedi!",
        "prefs-files": "Dosyalar",
        "prefs-custom-css": "Özel CSS",
        "prefs-custom-js": "Özel JS",
-       "prefs-common-css-js": "Tüm temalar için paylaşılan CSS/JS:",
+       "prefs-common-config": "Tüm temalar için paylaşılan CSS/JS:",
        "prefs-reset-intro": "Bu sayfayı tercihlerinizi site varsayılanına döndürmek için kullanabilirsiniz. Bu geri alınamaz.",
        "prefs-emailconfirm-label": "E-posta doğrulaması:",
        "youremail": "E-posta:",
        "recentchangeslinked-feed": "İlgili değişiklikler",
        "recentchangeslinked-toolbox": "İlgili değişiklikler",
        "recentchangeslinked-title": "\"$1\" ile ilişkili değişiklikler",
-       "recentchangeslinked-summary": "Aşağıdaki liste, belirtilen sayfaya (ya da belirtilen kategorinin üyelerine) bağlantı veren sayfalarda yapılan son değişikliklerin listesidir.\n[[Special:Watchlist|İzleme listenizdeki]] sayfalar <strong>kalın<strong> olarak belirtilmiştir.",
+       "recentchangeslinked-summary": "Aşağıdaki liste, belirtilen sayfaya (ya da belirtilen kategorinin üyelerine) bağlantı veren sayfalarda yapılan son değişikliklerin listesidir.\n[[Special:Watchlist|İzleme listenizdeki]] sayfalar <strong>kalın</strong> olarak belirtilmiştir.",
        "recentchangeslinked-page": "Sayfa adı:",
        "recentchangeslinked-to": "Belirtilen sayfadan verilenler yerine, sayfaya verilen bağlantıları göster.",
        "recentchanges-page-added-to-category": "[[:$1]] kategoriye eklendi",
index fe6c9bf..70ebecb 100644 (file)
        "userjspreview": "'''Бу бары тик JavaScript файлын алдан карау гына, ул әле сакланмаган!'''",
        "sitecsspreview": "'''онытмагыз, бу бары тик CSS-файлны алдан карау гына.'''\n'''Ул әле сакланмаган!'''",
        "sitejspreview": "'''Бу бары тик JavaScript файлын алдан карау гына.'''\n'''Ул әле сакланмаган!'''",
-       "userinvalidcssjstitle": "'''Игътибар:''' \"$1\" бизәү темасы табылмады. Кулланучының .css һәм .js битләре исемнәре бары тик кечкенә (юл) хәрефләрдән генә торырга тиеш икәнен онытмагыз. Мисалга: {{ns:user}}:Foo/vector.css, ә {{ns:user}}:Foo/Vector.css түгел!",
+       "userinvalidconfigtitle": "'''Игътибар:''' \"$1\" бизәү темасы табылмады. Кулланучының .css һәм .js битләре исемнәре бары тик кечкенә (юл) хәрефләрдән генә торырга тиеш икәнен онытмагыз. Мисалга: {{ns:user}}:Foo/vector.css, ә {{ns:user}}:Foo/Vector.css түгел!",
        "updated": "(Яңартылды)",
        "note": "'''Искәрмә:'''",
        "previewnote": "<strong>Исегездә тотыгыз, бу алдан карау гына.</strong>\nТәзәтмәләрегез әлегә сакланмаган!",
        "prefs-files": "Файллар",
        "prefs-custom-css": "Шәхси CSS",
        "prefs-custom-js": "Шәхси JS",
-       "prefs-common-css-js": "Барлык бизәлешләр өчен гомуми CSS/JS:",
+       "prefs-common-config": "Барлык бизәлешләр өчен гомуми CSS/JS:",
        "prefs-reset-intro": "Бу бит сезнең көйләнмәләрегезне бетерү өчен кулланыла. Бу эшне башкару нәтиҗәсендә сез яңадан үз көйләнмәләрне яңадан кайтара алмыйсыз.",
        "prefs-emailconfirm-label": "E-mail раслау",
        "youremail": "Электрон почта:",
index 91cf0b4..8b774ed 100644 (file)
        "userjsyoucanpreview": "'''Yärdäm:''' \"{{int:showpreview}}\" töymäsenä basıp, yaña JS-faylnı tikşerep bula.",
        "usercsspreview": "'''Bu barı tik CSS-faylnı aldan qaraw ğına, ul äle saqlanmağan!'''",
        "userjspreview": "'''Bu barı tik JavaScript faylın aldan qaraw ğına, ul äle saqlanmağan!'''",
-       "userinvalidcssjstitle": "'''İğtibar:''' \"$1\" bizäw teması tabılmadı. Qullanuçınıñ .css häm .js bitläre isemnäre barı tik keçkenä (yul) xäreflärdän genä torırğa tieş ikänen onıtmağız. Misalğa: {{ns:user}}:Foo/vector.css, ä {{ns:user}}:Foo/Vector.css tügel!",
+       "userinvalidconfigtitle": "'''İğtibar:''' \"$1\" bizäw teması tabılmadı. Qullanuçınıñ .css häm .js bitläre isemnäre barı tik keçkenä (yul) xäreflärdän genä torırğa tieş ikänen onıtmağız. Misalğa: {{ns:user}}:Foo/vector.css, ä {{ns:user}}:Foo/Vector.css tügel!",
        "updated": "(Yañartıldı)",
        "note": "'''İskärmä:'''",
        "previewnote": "'''Bu fäqät aldan qaraw ğına, üzgärtüläregez äle saqlanmağan!'''",
        "prefs-files": "Fayllar",
        "prefs-custom-css": "Üzemneñ CSS",
        "prefs-custom-js": "Üzemneñ JS",
-       "prefs-common-css-js": "Barlıq bizäleşlär öçen ğomumi CSS/JS:",
+       "prefs-common-config": "Barlıq bizäleşlär öçen ğomumi CSS/JS:",
        "prefs-reset-intro": "Bu bit sezneñ köylänmäläregezne beterü öçen qullanıla. Bu eşne başqaru näticäsendä sez yañadan üz köylänmälärne yañadan qaytara almıysız.",
        "prefs-emailconfirm-label": "E-mail raslaw",
        "youremail": "Elektron poçta:",
index 706c3a4..0a48ddc 100644 (file)
        "userjspreview": "'''دىققەت سىز پەقەت ئۆزىڭىزنىڭ شەخسىي JavaScript نى ئالدىن كۆزىتىۋاتىسىز/سىناۋاتىسىز.'''\n'''ئۇ تېخى ساقلانمىدى!'''",
        "sitecsspreview": "'''دىققەت سىز پەقەت بۇ CSS نى ئالدىن كۆزىتىۋاتىسىز.'''\n'''ئۇ تېخى ساقلانمىدى!'''",
        "sitejspreview": "'''دىققەت سىز پەقەت بۇ JavaScript كودنى ئالدىن كۆزىتىۋاتىسىز.'''\n'''ئۇ تېخى ساقلانمىدى!'''",
-       "userinvalidcssjstitle": "'''ئاگاھلاندۇرۇش:''' تېرە\\\"$1\" مەۋجۇد ئەمەس.\nئادەتلەنگەن .css ۋە .js تور بەت ماۋزۇسىغا كىچىك يېزىلىشتىكى ھەرپ ئىشلىتىلىدۇ، مەسىلەن، {{ns:user}}:Foo/vector.css بىلەن {{ns:user}}:Foo/Vector.css ئوخشاش ئەمەس.",
+       "userinvalidconfigtitle": "'''ئاگاھلاندۇرۇش:''' تېرە\\\"$1\" مەۋجۇد ئەمەس.\nئادەتلەنگەن .css ۋە .js تور بەت ماۋزۇسىغا كىچىك يېزىلىشتىكى ھەرپ ئىشلىتىلىدۇ، مەسىلەن، {{ns:user}}:Foo/vector.css بىلەن {{ns:user}}:Foo/Vector.css ئوخشاش ئەمەس.",
        "updated": "(يېڭىلاندى)",
        "note": "'''ئىزاھات:'''",
        "previewnote": "'''ئېسىڭىزدە بولسۇنكى بۇ پەقەتلا ئالدىن كۆزىتىش.'''\nئۆزگەرتكەن مەزمۇنىڭىز تېخى ساقلانمىدى!",
        "prefs-files": "ھۆججەتلەر",
        "prefs-custom-css": "ئىختىيارى CSS",
        "prefs-custom-js": "ئىختىيارى JS",
-       "prefs-common-css-js": "ھەممە تېرىدە ھەمبەھىرلەنگەن CSS/JS:",
+       "prefs-common-config": "ھەممە تېرىدە ھەمبەھىرلەنگەن CSS/JS:",
        "prefs-reset-intro": "سىز بۇ بەتنى ئىشلىتىپ تەڭشەكلىرىڭىزنى تور بېكەتنىڭ كۆڭۈلدىكى قىممىتىگە تەڭشىيەلەيسىز.\nبۇ مەشغۇلاتتىن يانغىلى بولمايدۇ.",
        "prefs-emailconfirm-label": "ئېلخەت جەزملەش:",
        "youremail": "ئېلخەت:",
index 10f2833..703fa0d 100644 (file)
        "databaseerror-function": "Функція: $1",
        "databaseerror-error": "Помилка: $1",
        "transaction-duration-limit-exceeded": "Щоб уникнути великого лагу при реплікації, ця транзакція була перервана, оскільки тривалість запису ($1) перевищила обмеження в $2 {{PLURAL:$2|секунду|секунд|секунди}}.\n\nЯкщо ви змінюєте декілька елементів за один раз, постарайтесь замість цього зробити декілька невеликих операцій.",
-       "laggedslavemode": "Увага: сторінка може не містити останніх редагувань.",
+       "laggedslavemode": "<strong>Увага:</strong> сторінка може не містити останніх редагувань.",
        "readonly": "Запис до бази даних заблокований",
        "enterlockreason": "Зазначте причину і приблизний термін блокування",
        "readonlytext": "Додавання нових статей та інші зміни бази даних у даний момент заблоковані: ймовірно, через планове сервісне обслуговування бази даних, після закінчення якого буде відновлено нормальний стан.\n\nАдміністратор, що заблокував базу, дав таке пояснення: $1",
        "userjspreview": "'''Пам'ятайте, що це тільки попередній перегляд вашого JavaScript-файлу і він поки-що не збережений!'''",
        "sitecsspreview": "'''Пам'ятайте, що це тільки попередній перегляд цього CSS.'''\n'''Його ще не збережено!'''",
        "sitejspreview": "'''Пам'ятайте, що це лише попередній перегляд вашого JavaScript-коду.'''\n'''Його ще не збережено!'''",
-       "userinvalidcssjstitle": "'''Увага:''' тема оформлення «$1» не знайдена.\nПам'ятайте, що користувацькі .css та .js сторінки повинні мати назву, що складається лише з малих літер, наприклад «{{ns:user}}:Хтось/vector.css», а не «{{ns:user}}:Хтось/Vector.css».",
+       "userinvalidconfigtitle": "'''Увага:''' тема оформлення «$1» не знайдена.\nПам'ятайте, що користувацькі .css та .js сторінки повинні мати назву, що складається лише з малих літер, наприклад «{{ns:user}}:Хтось/vector.css», а не «{{ns:user}}:Хтось/Vector.css».",
        "updated": "(Оновлена)",
        "note": "'''Зауваження:'''",
        "previewnote": "'''Це лише попередній перегляд.'''\nВаші зміни ще не збережено!",
        "postedit-confirmation-created": "Створено сторінку.",
        "postedit-confirmation-restored": "Сторінка була відновлена.",
        "postedit-confirmation-saved": "Ваше редагування збережено",
+       "postedit-confirmation-published": "Ваше редагування опубліковано.",
        "edit-already-exists": "Неможливо створити нову сторінку.\nВона вже існує.",
        "defaultmessagetext": "Стандартний текст повідомлення",
        "content-failed-to-parse": "Не вдалось ідентифікувати $2 як тип $1 через: $3",
        "prefs-files": "Файли",
        "prefs-custom-css": "Власний CSS",
        "prefs-custom-js": "Власний JS",
-       "prefs-common-css-js": "CSS/JS спільні для всіх тем оформлення:",
+       "prefs-common-config": "CSS/JS спільні для всіх тем оформлення:",
        "prefs-reset-intro": "Ця сторінка може бути використана для зміни ваших налаштувань на стандартні.\nПісля виконання цієї дії ви не зможете відкотити зміни.",
        "prefs-emailconfirm-label": "Підтвердження адреси:",
        "youremail": "Адреса електронної пошти:",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|обговорення]])",
        "timezone-utc": "UTC",
        "timezone-local": "Місцеві",
-       "duplicate-defaultsort": "Увага. Ключ сортування «$2» перекриває попередній ключ сортування «$1».",
+       "duplicate-defaultsort": "{{#if:{{REVISIONID}}|<span class=\"t-test test\" style=\"display:none;>|\n}}<strong>Увага!</strong> Ключ сортування «$2» перекриває попередній ключ сортування «$1».{{\n#if:{{REVISIONID}}|</span>}}",
        "duplicate-displaytitle": "<strong>Увага:</strong> Відображений заголовок \"$2\" заміщує раніше відображений заголовок \"$1\".",
        "restricted-displaytitle": "<strong>Увага:</strong> Відображувану назву «$1» було проігноровано, оскільки вона не відповідає чинній назві сторінки.",
        "invalid-indicator-name": "<strong>Помилка:</strong> Сторінка індикатора стану <code>name</code> атрибута не може бути пуста.",
index e33c545..d10cba2 100644 (file)
        "userjspreview": "<strong>یاد رہے کہ اس وقت آپ اپنی جاوا اسکرپٹ کی محض نمائش دیکھ/جانچ رہے ہیں، یہ اب تک محفوظ نہیں ہوئی ہے!</strong>",
        "sitecsspreview": "<strong>یاد رہے کہ اس وقت آپ اس سی ایس کی محض نمائش دیکھ رہے ہیں، یہ اب تک محفوظ نہیں ہوئی ہے!</strong>",
        "sitejspreview": "<strong>یاد رہے کہ اس وقت آپ اس جاوا اسکرپٹ کوڈ کی محض نمائش دیکھ رہے ہیں، یہ اب تک محفوظ نہیں ہوئی ہے!</strong>",
-       "userinvalidcssjstitle": "<strong>انتباہ:</strong> یہاں «$1» نام سے کوئی پوشاک موجود نہیں۔ شخصی .css اور .js کے صفحات اپنے عنوان میں چھوٹے حروف استعمال کرتے ہیں، مثلاً {{ns:user}}:Foo/Vector.css کی بجائے {{ns:user}}:Foo/vector.css",
+       "userinvalidconfigtitle": "<strong>انتباہ:</strong> یہاں «$1» نام سے کوئی پوشاک موجود نہیں۔ شخصی .css اور .js کے صفحات اپنے عنوان میں چھوٹے حروف استعمال کرتے ہیں، مثلاً {{ns:user}}:Foo/Vector.css کی بجائے {{ns:user}}:Foo/vector.css",
        "updated": "(اپ ڈیٹڈ)",
        "note": "'''نوٹ:'''",
        "previewnote": "'''یاد رکھیں، یہ صرف نمائش ہے ۔آپ کی ترامیم ابھی محفوظ نہیں کی گئیں۔'''",
        "templatesusedsection": "اِس قطعہ میں مستعمل {{PLURAL:$1|سانچہ|سانچے}}:",
        "template-protected": "(محفوظ شدہ)",
        "template-semiprotected": "(نیم محفوظ)",
-       "hiddencategories": "یہ صفحہ {{PLURAL:$1|1 چُھپے زمرے|$1 چُھپے زمرہ جات}} میں شامل ہے:",
+       "hiddencategories": "اس صفحہ میں {{PLURAL:$1|1 پوشیدہ زمرے|$1 پوشیدہ زمرہ جات}} شامل ہیں:",
        "nocreatetext": "{{SITENAME}} نے نئے صفحات تخلیق کرنے پر پابندی لگا رکھی ہے۔\nتاہم آپ پہلے سے موجود صفحات میں ترمیم کر سکتے ہیں یا [[Special:UserLogin|اپنے کھاتے ميں داخل ہوں یا کھاتہ بنائیں]]۔",
        "nocreate-loggedin": "آپ کو نئے صفحات تخلیق کرنے کی اجازت نہیں ہے.",
        "sectioneditnotsupported-title": "قطعہ کی تدوین حمایت شدہ نہیں ہے",
        "prefs-files": "فائلیں",
        "prefs-custom-css": "شخصی سی ایس ایس",
        "prefs-custom-js": "شخصی جاوا اسکرپٹ",
-       "prefs-common-css-js": "جملہ پوشاکوں کے لیے مشترکہ سی ایس ایس/جاوا اسکرپٹ:",
+       "prefs-common-config": "جملہ پوشاکوں کے لیے مشترکہ سی ایس ایس/جاوا اسکرپٹ:",
        "prefs-reset-intro": "آپ اس صفحہ کے ذریعہ اپنی موجودہ ترجیحات کو سائٹ کی ابتدائی ترتیبات کے مطابق ڈھال سکتے ہیں۔\nلیکن اسے واپس نہیں پھیرا جا سکتا۔",
        "prefs-emailconfirm-label": "برقی خط کی تصدیق:",
        "youremail": "برقی خط:",
index 7481a98..a5f3d41 100644 (file)
        "prefs-files": "Fayllar",
        "prefs-custom-css": "Shaxsiy CSS",
        "prefs-custom-js": "Shaxsiy JavaScript",
-       "prefs-common-css-js": "Umumiy CSS/JavaScript (barcha tashqi koʻrinishlar uchun):",
+       "prefs-common-config": "Umumiy CSS/JavaScript (barcha tashqi koʻrinishlar uchun):",
        "prefs-emailconfirm-label": "Elektron pochta manzilini tasdiqlash:",
        "youremail": "E-mail:",
        "username": "Foydalanuvchi nomi",
index b4b772b..f18227e 100644 (file)
        "userjspreview": "'''Sta qua la xe solo n'anteprima par proar el proprio JavaScript personal; le modifiche no le xe gnancora stà salvà!'''",
        "sitecsspreview": "'''Sta qua la xe solo n'anteprima del proprio CSS personal. Le modifiche no le xe gnancora stà salvà!'''",
        "sitejspreview": "'''Sta qua la xe solo n'anteprima par proar el proprio JavaScript personal; le modifiche no le xe gnancora stà salvà!'''",
-       "userinvalidcssjstitle": "'''Ocio:'''  No ghe xe nissuna skin con nome \"$1\". Nota che le pagine par i .css e .js personalizà le gà l'iniziale del titolo minuscola, par esenpio {{ns:user}}:Esenpio/vector.css e no {{ns:user}}:Esenpio/Vector.css.",
+       "userinvalidconfigtitle": "'''Ocio:'''  No ghe xe nissuna skin con nome \"$1\". Nota che le pagine par i .css e .js personalizà le gà l'iniziale del titolo minuscola, par esenpio {{ns:user}}:Esenpio/vector.css e no {{ns:user}}:Esenpio/Vector.css.",
        "updated": "(Agiornà)",
        "note": "'''Nota:'''",
        "previewnote": "'''Tiente in mente che sta qua la xe solo n'anteprima.'''\nI to canbiamenti NO i xe gnancora stà salvài!",
        "prefs-files": "File",
        "prefs-custom-css": "CSS personalixà",
        "prefs-custom-js": "JS personalixà",
-       "prefs-common-css-js": "CSS/JS condiviso par tute łe skin:",
+       "prefs-common-config": "CSS/JS condiviso par tute łe skin:",
        "prefs-reset-intro": "Te pol doparar sta pagina par riportar le to preferense a quele predefinìe.\nSta operassion no la pol èssar anulà.",
        "prefs-emailconfirm-label": "Conferma de l'e-mail:",
        "youremail": "La to e-mail",
index 61c6728..bcb21b1 100644 (file)
        "prefs-files": "Failad",
        "prefs-custom-css": "Ičeze CSS",
        "prefs-custom-js": "Ičeze JS",
-       "prefs-common-css-js": "Ühthižed CSS/JavaScript kaikiden temiden täht:",
+       "prefs-common-config": "Ühthižed CSS/JavaScript kaikiden temiden täht:",
        "prefs-reset-intro": "Tö sat kävutada nece lehtpol', miše pördutada teiden järgendused saitan ezijärgendusidennoks.\nNecidä tegendad ei sa toižetada.",
        "prefs-emailconfirm-label": "E-počtan vahvištand:",
        "youremail": "E-počt:",
index 84c0a47..bb064d8 100644 (file)
        "anonpreviewwarning": "''Bạn chưa đăng nhập. Khi lưu trang này, địa chỉ IP của bạn sẽ được ghi vào lịch sử trang.''",
        "missingsummary": "'''Nhắc nhở:''' Bạn đã không ghi lại tóm lược sửa đổi. Nếu bạn nhấn Lưu trang một lần nữa, sửa đổi của bạn sẽ được lưu mà không có tóm lược.",
        "selfredirect": "<strong>Cảnh báo:</strong> Bạn sắp đổi hướng trang này đến chính trang này.\nCó lẽ bạn đã định rõ mục tiêu sai hoặc bạn đang sửa trang sai.\nNếu bạn bấm “$1” lần nữa, trang đổi hướng sẽ được tạo ra.",
-       "missingcommenttext": "Xin hãy gõ vào lời bàn luận ở dưới.",
+       "missingcommenttext": "Xin hãy nhập bình luận vào đây.",
        "missingcommentheader": "<strong>Nhắc nhở:</strong> Bạn chưa ghi chủ đề/tiêu đề cho bàn luận này.\nNếu bạn nhấn nút “$1” lần nữa, sửa đổi của bạn sẽ được lưu mà không có đề mục.",
        "summary-preview": "Xem trước dòng tóm lược sửa đổi:",
        "subject-preview": "Xem trước đề mục:",
        "userjspreview": "'''Nhớ rằng bạn chỉ đang kiểm thử/xem trước trang JavaScript, nó chưa được lưu!'''",
        "sitecsspreview": "'''Nhớ rằng bạn chỉ đang xem trước bản CSS này.'''\n'''Nó chưa được lưu!'''",
        "sitejspreview": "'''Nhớ rằng bạn chỉ đang xem trước bản JavaScript này.\n'''Nó chưa được lưu!'''",
-       "userinvalidcssjstitle": "'''Cảnh báo:''' Không có skin “$1”. Hãy nhớ rằng các trang .css và .js tùy chỉnh sử dụng tiêu đề chữ thường, như {{ns:user}}:Ví&nbsp;dụ/vector.css chứ không phải {{ns:user}}:Ví&nbsp;dụ/Vector.css.",
+       "userinvalidconfigtitle": "'''Cảnh báo:''' Không có skin “$1”. Hãy nhớ rằng các trang .css và .js tùy chỉnh sử dụng tiêu đề chữ thường, như {{ns:user}}:Ví&nbsp;dụ/vector.css chứ không phải {{ns:user}}:Ví&nbsp;dụ/Vector.css.",
        "updated": "(Cập nhật)",
        "note": "'''Ghi chú:'''",
        "previewnote": "'''Đây chỉ mới là bản xem trước.'''\nCác thay đổi của bạn vẫn chưa được lưu!",
        "yourtext": "Nội dung bạn nhập",
        "storedversion": "Phiên bản lưu",
        "editingold": "'''Chú ý: bạn đang sửa một phiên bản cũ. Nếu bạn lưu, các sửa đổi trên các phiên bản mới hơn sẽ bị mất.'''",
+       "unicode-support-fail": "Trình duyệt của bạn không hỗ trợ Unicode. Đây là yêu cầu bắt buộc nếu bạn muốn sửa đổi trang tại đây, do đó thay đổi của bạn không được lưu.",
        "yourdiff": "Khác",
        "copyrightwarning": "Xin chú ý rằng tất cả các đóng góp của bạn tại {{SITENAME}} được xem là sẽ phát hành theo giấy phép $2 (xem $1 để biết thêm chi tiết). Nếu bạn không muốn những gì mình viết ra bị sửa đổi không thương tiếc và không sẵn lòng cho phép phát hành lại, xin đừng nhấn nút \"Lưu trang\".<br />\nBạn phải đảm bảo với chúng tôi rằng chính bạn là tác giả của những gì mình viết ra, hoặc chép nó từ một nguồn thuộc phạm vi công cộng hoặc tự do tương đương.<br />\n<strong>ĐỪNG ĐĂNG NỘI DUNG CÓ BẢN QUYỀN MÀ CHƯA XIN PHÉP!</strong>",
        "copyrightwarning2": "Xin chú ý rằng tất cả các đóng góp của bạn tại {{SITENAME}} có thể được sửa đổi, thay thế, hoặc xóa bỏ bởi các thành viên khác. Nếu bạn không muốn trang của bạn bị sửa đổi không thương tiếc, đừng đăng trang ở đây.<br />\nBạn phải đảm bảo với chúng tôi rằng chính bạn là người viết nên, hoặc chép nó từ một nguồn thuộc phạm vi công cộng hoặc tự do tương đương (xem $1 để biết thêm chi tiết).\n'''Đừng đăng nội dung có bản quyền mà không xin phép!'''",
        "postedit-confirmation-created": "Trang đã được tạo ra.",
        "postedit-confirmation-restored": "Trang đã được phục hồi.",
        "postedit-confirmation-saved": "Sửa đổi của bạn đã được lưu.",
+       "postedit-confirmation-published": "Thay đổi của bạn đã được xuất bản.",
        "edit-already-exists": "Không thể tạo trang mới.\nNó đã tồn tại.",
        "defaultmessagetext": "Nội dung mặc định",
        "content-failed-to-parse": "Thất bại phân tích nội dung $2 cho kiểu $1: $3",
        "parser-template-loop-warning": "Phát hiện bản mẫu lặp vòng: [[$1]]",
        "template-loop-category": "Trang có bản mẫu lặp vòng",
        "template-loop-category-desc": "Trang chứa một hoặc nhiều bản mẫu lặp vòng, tức là những bản mẫu tự gọi đệ quy chính nó.",
+       "template-loop-warning": "<strong>Cảnh báo:</strong> Trang này gọi [[:$1]] tạo ra vòng lặp bản mẫu (vòng gọi đệ quy vô hạn).",
        "parser-template-recursion-depth-warning": "Bản mẫu đã vượt quá giới hạn về độ sâu đệ quy ($1)",
        "language-converter-depth-warning": "Đã vượt quá giới hạn độ sâu của bộ chuyển đổi ngôn ngữ ($1)",
        "node-count-exceeded-category": "Trang có số nốt vượt quá giới hạn cho phép",
        "recentchangesdays-max": "(tối đa $1 ngày)",
        "recentchangescount": "Số sửa đổi hiển thị mặc định:",
        "prefs-help-recentchangescount": "Số này bao gồm các thay đổi gần đây, lịch sử trang, và nhật trình.",
-       "prefs-help-watchlist-token2": "Đây là chìa khóa bí mật cho nguồn cấp dữ liệu danh sách theo dõi của bạn.\nBất cứ ai biết nó sẽ có thể để đọc danh sách theo dõi của bạn, vì vậy đừng chia sẻ nó.\n[[Special:ResetTokens|Nhấn chuột vào đây nếu bạn cần phải thiết lập lại nó]].",
+       "prefs-help-watchlist-token2": "Đây là chìa khóa bí mật cho nguồn cấp dữ liệu danh sách theo dõi của bạn.\nBất cứ ai biết nó sẽ có thể để đọc danh sách theo dõi của bạn, vì vậy đừng chia sẻ nó.\nNếu cần, [[Special:ResetTokens|bạn có thể thiết lập lại nó]].",
        "savedprefs": "Đã lưu các tùy chọn cá nhân.",
        "savedrights": "Đã lưu các nhóm người dùng của {{GENDER:$1}}$1.",
        "timezonelegend": "Múi giờ:",
        "timezoneregion-europe": "Châu Âu",
        "timezoneregion-indian": "Ấn Độ Dương",
        "timezoneregion-pacific": "Thái Bình Dương",
-       "allowemail": "Nhận thư điện tử từ các thành viên khác",
+       "allowemail": "Cho phép các thành viên khác gửi thư điện tử cho tôi",
+       "email-allow-new-users-label": "Nhận thư điện tử từ các thành viên mới",
+       "email-blacklist-label": "Cấm các thành viên sau gửi thư điện tử cho tôi:",
        "prefs-searchoptions": "Tìm kiếm",
        "prefs-namespaces": "Không gian tên",
        "default": "mặc định",
        "prefs-files": "Tập tin",
        "prefs-custom-css": "sửa CSS",
        "prefs-custom-js": "sửa JS",
-       "prefs-common-css-js": "CSS/JavaScript chung cho mọi giao diện:",
+       "prefs-common-config": "CSS/JavaScript chung cho mọi giao diện:",
        "prefs-reset-intro": "Có thể mặc định lại toàn bộ tùy chọn dùng trang này. Điều này không thể hoàn tác.",
        "prefs-emailconfirm-label": "Xác nhận thư điện tử:",
        "youremail": "Thư điện tử:",
        "recentchanges-legend": "Tùy chọn thay đổi gần đây",
        "recentchanges-summary": "Xem các thay đổi gần đây nhất trên wiki này tại đây.",
        "recentchanges-noresult": "Không có thay đổi trong khoảng thời gian phù hợp với các tiêu chí này.",
+       "recentchanges-timeout": "Yêu cầu tìm kiếm này đã bị quá hạn. Bạn có thể thử sử dụng các tham số tìm kiếm khác.",
+       "recentchanges-network": "Không thể tải kết quả do lỗi kĩ thuật. Xin hãy làm mới lại trang.",
+       "recentchanges-notargetpage": "Nhập tên trang vào ô trên để xem các thay đổi có liên quan tới trang đó.",
        "recentchanges-feed-description": "Theo dõi các thay đổi gần đây nhất của wiki dùng nguồn cấp dữ liệu này.",
        "recentchanges-label-newpage": "Bản sửa này tạo ra trang mới",
        "recentchanges-label-minor": "Đây là một sửa đổi nhỏ",
        "rcfilters-group-results-by-page": "Nhóm kết quả theo trang",
        "rcfilters-activefilters": "Bộ lọc hiện hành",
        "rcfilters-advancedfilters": "Bộ lọc nâng cao",
-       "rcfilters-limit-title": "Số kết quả để hiển thị",
+       "rcfilters-limit-title": "Số kết quả hiển thị",
+       "rcfilters-limit-and-date-label": "$1 thay đổi, $2",
        "rcfilters-days-title": "Những ngày gần đây",
        "rcfilters-hours-title": "Số giờ gần đây",
        "rcfilters-days-show-days": "$1 ngày",
        "rcfilters-days-show-hours": "$1 giờ",
        "rcfilters-highlighted-filters-list": "Tô màu: $1",
        "rcfilters-quickfilters": "Bộ lọc đã lưu",
-       "rcfilters-quickfilters-placeholder-title": "Chưa lưu liên kết",
+       "rcfilters-quickfilters-placeholder-title": "Chưa lưu các bộ lọc",
        "rcfilters-quickfilters-placeholder-description": "Để lưu thiết lập bộ lọc để dùng lại sau, bấm hình dấu trang trong hộp “Bộ lọc hiện hành” bên dưới.",
        "rcfilters-savedqueries-defaultlabel": "Bộ lọc đã lưu",
        "rcfilters-savedqueries-rename": "Đổi tên",
        "rcfilters-view-namespaces-tooltip": "Lọc kết quả theo không gian tên",
        "rcfilters-view-tags-tooltip": "Lọc kết quả theo thẻ đánh dấu",
        "rcfilters-view-return-to-default-tooltip": "Quay lại trình đơn bộ lọc chính",
+       "rcfilters-view-tags-help-icon-tooltip": "Tìm hiểu thêm về các sửa đổi bị đánh dấu",
        "rcfilters-liveupdates-button": "Cập nhật trực tiếp",
        "rcfilters-liveupdates-button-title-on": "Tắt cập nhật trực tiếp",
        "rcfilters-liveupdates-button-title-off": "Hiển thị các thay đổi mới lúc khi xảy ra",
        "rcfilters-watchlist-markseen-button": "Đánh dấu tất cả thay đổi là đã xem",
        "rcfilters-watchlist-edit-watchlist-button": "Sửa danh sách trang theo dõi",
        "rcfilters-watchlist-showupdated": "Thay đổi mới trên các trang kể lần cuối bạn xem trang được in <strong>đậm</strong> và có dấu tô màu.",
+       "rcfilters-preference-label": "Ẩn phiên bản cải tiến của trang Thay đổi Gần đây",
+       "rcfilters-target-page-placeholder": "Nhập tên trang (hoặc thể loại)",
        "rcnotefrom": "Dưới đây là {{PLURAL:$5|thay đổi duy nhất|các thay đổi}} từ <strong>$3 $4</strong> (hiển thị tối đa <strong>$1</strong> thay đổi).",
        "rclistfromreset": "Đặt lại lựa chọn ngày",
        "rclistfrom": "Xem các thay đổi từ $2 $3 trở về sau",
        "file-deleted-duplicate-notitle": "Một tập tin giống hệt như tập tin này đã từng bị xóa và tên bị xóa hẳn trước đây.\nBạn nên xin một người có quyền xem dữ liệu tập tin bị xóa hẳn xem lại trường hợp này trước khi tiếp tục tải nó lên lại.",
        "uploadwarning": "Cảnh báo!",
        "uploadwarning-text": "Xin hãy chỉnh sửa miêu tả tập tin ở dưới và thử lại.",
+       "uploadwarning-text-nostash": "Xin hãy tải lại tập tin lên, sửa đổi phần mô tả và thử lại.",
        "savefile": "Lưu tập tin",
        "uploaddisabled": "Chức năng tải lên đã bị khóa.",
        "copyuploaddisabled": "Chức năng tải lên từ địa chỉ URL đã bị tắt.",
        "uploadstash-refresh": "Làm mới danh sách tập tin",
        "uploadstash-thumbnail": "xem hình thu nhỏ",
        "uploadstash-exception": "Không thể lưu tập tin vào hàng đợi tải lên ($1): “$2”.",
+       "uploadstash-bad-path": "Đường dẫn không tồn tại.",
+       "uploadstash-bad-path-invalid": "Đường dẫn không hợp lệ.",
+       "uploadstash-bad-path-unknown-type": "Loại không xác định \"$1\".",
+       "uploadstash-bad-path-unrecognized-thumb-name": "Tên hình thu nhỏ không nhận dạng được.",
+       "uploadstash-file-not-found-no-thumb": "Không thể tải hình thu nhỏ.",
+       "uploadstash-file-not-found-no-object": "Không tạo được đối tượng tập tin cục bộ cho hình thu nhỏ.",
+       "uploadstash-file-not-found-no-remote-thumb": "Nạp hình thu nhỏ thất bại: $1\nURL = $2",
+       "uploadstash-not-logged-in": "Người dùng chưa đăng nhập, các tập tin phải do người dùng đã đăng nhập tải lên.",
+       "uploadstash-no-such-key": "Khoá không tồn tại ($1), không thể xoá.",
+       "uploadstash-zero-length": "Tập tin có dung lượng bằng không.",
        "invalid-chunk-offset": "Khúc lệch (chunk offset) không hợp lệ",
        "img-auth-accessdenied": "Không cho phép truy cập",
        "img-auth-nopathinfo": "Thiếu PATH_INFO.\nMáy chủ của bạn không được thiết lập để truyền thông tin này.\nCó thể do nó dựa trên CGI và không hỗ trợ img_auth.\nXem [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization hướng dẫn điều khiển truy cập hình ảnh].",
        "thumbnail_dest_directory": "Không thể tạo thư mục đích",
        "thumbnail_image-type": "Không hỗ trợ kiểu hình này",
        "thumbnail_gd-library": "Cấu hình thư viện GD chưa hoàn thành: thiếu hàm $1",
+       "thumbnail_image-size-zero": "Dung lượng tập tin ảnh bằng không.",
        "thumbnail_image-missing": "Hình như tập tin mất tích: $1",
        "thumbnail_image-failure-limit": "Việc tạo ra hình thu nhỏ này đã bị thất bại nhiều lần quá gần đây ($1 lần trở lên). Xin vui lòng thử lại sau.",
        "import": "Nhập các trang",
        "import-mapping-namespace": "Nhập vào một không gian tên:",
        "import-mapping-subpage": "Nhập thành các trang con của trang sau:",
        "import-upload-filename": "Tên tập tin:",
+       "import-upload-username-prefix": "Tiền tố liên wiki:",
+       "import-assign-known-users": "Gán sửa đổi cho thành viên cục bộ nếu tồn tại thành viên có tên tương tự",
        "import-comment": "Lý do:",
        "importtext": "Xin hãy xuất tập tin từ wiki nguồn dùng [[Special:Export|công cụ xuất]].\nLưu nó vào máy tính của bạn rồi tải nó lên đây.",
        "importstart": "Đang nhập các trang…",
        "imported-log-entries": "Đã nhập {{PLURAL:$1|mục nhật trình|$1 mục nhật trình}}.",
        "importfailed": "Không nhập được: $1",
        "importunknownsource": "Không hiểu nguồn trang để nhập vào",
+       "importnoprefix": "Chưa điền tiền tố liên wiki",
        "importcantopen": "Không thể mở tập tin để nhập vào",
        "importbadinterwiki": "Liên kết liên wiki sai",
        "importsuccess": "Nhập thành công!",
        "pageinfo-category-subcats": "Số thể loại con",
        "pageinfo-category-files": "Số tập tin",
        "pageinfo-user-id": "ID người dùng",
+       "pageinfo-file-hash": "Giá trị băm",
        "markaspatrolleddiff": "Đánh dấu tuần tra",
        "markaspatrolledtext": "Đánh dấu tuần tra trang này",
        "markaspatrolledtext-file": "Đánh dấu đã tuần tra phiên bản file này",
        "autosumm-blank": "Đã tẩy trống trang",
        "autosumm-replace": "Đã thay thế cả nội dung bằng “$1”",
        "autoredircomment": "Đổi hướng đến [[$1]]",
+       "autosumm-removed-redirect": "Xoá đổi hướng đến trang [[$1]]",
+       "autosumm-changed-redirect-target": "Thay đổi trang đích của đổi hướng từ [[$1]] sang [[S2]]",
        "autosumm-new": "Tạo trang mới với nội dung “$1”",
        "autosumm-newblank": "Đã tạo trang trống",
        "size-bytes": "$1 byte",
        "watchlistedit-clear-titles": "Các tiêu đề:",
        "watchlistedit-clear-submit": "Xóa sạch danh sách theo dõi (không thể lùi lại!)",
        "watchlistedit-clear-done": "Đã xóa sạch danh sách theo dõi của bạn.",
+       "watchlistedit-clear-jobqueue": "Danh sách theo dõi của bạn đang bị xoá. Quá trình này có thể tốn một khoảng thời gian!",
        "watchlistedit-clear-removed": "$1 tựa đề đã được xóa khỏi danh sách:",
        "watchlistedit-too-many": "Danh sách có quá nhiều trang để hiển thị.",
        "watchlisttools-clear": "Xóa sạch danh sách theo dõi",
        "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1}}Thẻ]]: $2)",
        "tag-mw-contentmodelchange": "thay đổi kiểu nội dung",
        "tag-mw-contentmodelchange-description": "Sửa đổi [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:ChangeContentModel thay đổi kiểu nội dung] của trang",
+       "tag-mw-new-redirect": "Trang đổi hướng mới",
+       "tag-mw-new-redirect-description": "Các sửa đổi tạo ra trang đổi hướng mới hoặc biến một trang thành trang đổi hướng.",
+       "tag-mw-removed-redirect": "Xoá đổi hướng",
+       "tag-mw-removed-redirect-description": "Các thay đổi biến một trang đổi hướng thành trang không đổi hướng",
+       "tag-mw-changed-redirect-target": "Thay đổi trang đích của đổi hướng",
+       "tag-mw-changed-redirect-target-description": "Các thay đổi làm thay đổi trang đích của một trang đổi hướng",
+       "tag-mw-blank": "Tẩy trống trang",
+       "tag-mw-blank-description": "Các sửa đổi tẩy trống (xoá trắng) một trang",
+       "tag-mw-replace": "Thay thế nội dung",
+       "tag-mw-replace-description": "Các sửa đổi thay đổi trên 90% nội dung của một trang",
+       "tag-mw-rollback": "Lùi tất cả",
+       "tag-mw-rollback-description": "Các thay đổi cho phép lùi hàng loạt thay đổi của một người dùng trước đó thông qua liên kết lùi tất cả",
+       "tag-mw-undo": "Lùi sửa",
+       "tag-mw-undo-description": "Các thay đổi lùi sửa (hoàn tác) những thay đổi trước đó thông qua liên kết lùi lại",
        "tags-title": "Thẻ đánh dấu",
        "tags-intro": "Trang này liệt kê các thẻ đánh dấu mà phần mềm dùng nó để đánh dấu một sửa đổi, và ý nghĩa của nó.",
        "tags-tag": "Tên thẻ",
index 478a77f..33ec285 100644 (file)
        "userjsyoucanpreview": "'''Mob:''' Välolös eli „{{int:showpreview}}“ ad blufön eli JS nulik olik bü dakip.",
        "usercsspreview": "'''Memolös, das anu te büologol eli CSS olik.'''\n'''No nog pedakipon!'''",
        "userjspreview": "'''Memolös, das anu te blufol/büologol eli JavaScript olik, no nog pedakipon!'''",
-       "userinvalidcssjstitle": "'''Nuned:''' No dabinon fomät: \"$1\".\nMemolös, das pads: .css e .js mutons labön tiädi minudik: {{ns:user}}:Foo/vector.css, no {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "'''Nuned:''' No dabinon fomät: \"$1\".\nMemolös, das pads: .css e .js mutons labön tiädi minudik: {{ns:user}}:Foo/vector.css, no {{ns:user}}:Foo/Vector.css.",
        "updated": "(peatimükon)",
        "note": "'''Penet:'''",
        "previewnote": "'''Memolös, das is pajonon te büologed.'''\nVotükams olik no nog pedakipons!",
index 5caa187..4e53f27 100644 (file)
        "userjsyoucanpreview": "'''Nõvvoannõq:''' Pruugiq nuppi 'Näütäq proovikaehust' uma vahtsõ CCS-i vai JavaScripti ülekaemisõs, inne ku taa ärq pästät.",
        "usercsspreview": "'''Seo um CSS-i proovikaehus. Määntsitki muutuisi olõ-i viil pästet.'''",
        "userjspreview": "'''Unõhtagu-i, et seo kujo su umast javascriptist om viil pästmäldäq!'''",
-       "userinvalidcssjstitle": "'''Miildetulõtus:''' Olõ-i stiili nimega \"$1\". Piäq meelen, et pruukja säedüq .css- and .js-leheq piät nakkama väiku algustähega.",
+       "userinvalidconfigtitle": "'''Miildetulõtus:''' Olõ-i stiili nimega \"$1\". Piäq meelen, et pruukja säedüq .css- and .js-leheq piät nakkama väiku algustähega.",
        "updated": "(Värskis tett)",
        "note": "'''Miildetulõtus:'''",
        "previewnote": "'''Seo om õnnõ proovikaehus!''' \nSuq tettüq muutmisõq olõ-õi viil pästedüq!",
index c84e1e2..501d375 100644 (file)
        "userjsyoucanpreview": "'''Racsegne:''' eployîz l' boton «{{int:showpreview}}» po sayî vosse novea JavaScript divant del schaper.",
        "usercsspreview": "Èn rovyîz nén ki c' est djusse on prévoeyaedje di vosse stîle CSS d' uzeu.'''\n'''I n' a nén co stî schapé!'''",
        "userjspreview": "'''Èn rovyîz nén ki c' est djusse on prévoeyaedje/saye di vosse JavaScript d' uzeu, i n' a nén co stî schapé!'''",
-       "userinvalidcssjstitle": "'''Asteme:''' I n' a pont d' pea lomêye «$1». Tuzez ki les pådjes .css eyet .js des uzeus eployèt des tite e ptitès letes, metans {{ns:user}}:Toto/vector.css et nén {{ns:user}}:Toto/Vector.css.",
+       "userinvalidconfigtitle": "'''Asteme:''' I n' a pont d' pea lomêye «$1». Tuzez ki les pådjes .css eyet .js des uzeus eployèt des tite e ptitès letes, metans {{ns:user}}:Toto/vector.css et nén {{ns:user}}:Toto/Vector.css.",
        "updated": "(Ramidré)",
        "note": "'''Note :'''",
        "previewnote": "'''Èn rovyîz nén ki c' est djusse on prévoeyaedje.'''\n'''Les candmints n' ont nén co stî schapés!'''",
        "prefs-files": "Fitchîs",
        "prefs-custom-css": "CSS a vosse môde",
        "prefs-custom-js": "JavaScript a vosse môde",
-       "prefs-common-css-js": "CSS/JavaScript pårtaedjî po totes les peas:",
+       "prefs-common-config": "CSS/JavaScript pårtaedjî po totes les peas:",
        "prefs-reset-intro": "Vos ploz eployî ç' boton chal po rmete totes vos preferinces åzès prémetowès valixhances del waibe.\nÇoul n' pôrè nén esse disfwait.",
        "prefs-emailconfirm-label": "Acertinaedje di l' emile:",
        "youremail": "Vost emile:",
index 9c35d91..0cf2055 100644 (file)
        "prefs-files": "Mga paypay",
        "prefs-custom-css": "Custom CSS",
        "prefs-custom-js": "Custom JavaScript",
-       "prefs-common-css-js": "Saro nga CSS/JavaScript para han ngatanan nga mga panit:",
+       "prefs-common-config": "Saro nga CSS/JavaScript para han ngatanan nga mga panit:",
        "prefs-reset-intro": "Puydi nimo ini gamiton nga pakli para makareset han imo mga preperensya nga ginbutang nga daan han sityo. Diri ini puydi mapawaray-buhat.",
        "prefs-emailconfirm-label": "Kompirmasyon han email:",
        "youremail": "E-mail:",
index a82560f..9724fa7 100644 (file)
        "userjsyoucanpreview": "'''Xelal :''' di la digël nga cuq ci «Wonendi» ngir gis say xobi CSS walla JavaScript yu bees laata nga leen di denc.",
        "usercsspreview": "Bul fatte ne lii wonendib sa CSS rekk la; dencagoo say coppite!'''",
        "userjspreview": "'''Bul fatte ne lii ab wonendib sa yoonu javaScript rekk la; dencagoo say coppite!'''",
-       "userinvalidcssjstitle": "'''Moytul :''' amul genn col gu tudd « $1 ». Bul fatte ne xët yiy jeexee .css ak .js seeni koj ay araf yu tuut ñoo ciy tegu/.<br />ci misaal, {{ns:user}}:Foo/'''v'''ector.css moo baax, waaye bii du baax {{ns:user}}:Foo/'''V'''ector.css .",
+       "userinvalidconfigtitle": "'''Moytul :''' amul genn col gu tudd « $1 ». Bul fatte ne xët yiy jeexee .css ak .js seeni koj ay araf yu tuut ñoo ciy tegu/.<br />ci misaal, {{ns:user}}:Foo/'''v'''ector.css moo baax, waaye bii du baax {{ns:user}}:Foo/'''V'''ector.css .",
        "updated": "(bees na)",
        "note": "'''Karmat :'''",
        "previewnote": "'''Lii ab wonendi rekk la; coppite yi ci xët wi dencagoo leen!'''",
index b1040c4..a7f8844 100644 (file)
        "userjspreview": "'''注意侬只是垃许测试/预览侬个 JavaScript。'''\n'''还弗曾保存!'''",
        "sitecsspreview": "<strong>注意侬现在只是来上预览该CSS,还弗曾保存!</strong>",
        "sitejspreview": "<strong>注意侬现在只是来上预览该JavaScript代码,还弗曾保存!</strong>",
-       "userinvalidcssjstitle": "'''警告:''' 弗存在皮肤\"$1\"。注意自定义个 .css 搭 .js 页要使用小写标题,譬如,{{ns:user}}:Foo/vector.css 弗同于 {{ns:user}}:Foo/Vector.css。",
+       "userinvalidconfigtitle": "'''警告:''' 弗存在皮肤\"$1\"。注意自定义个 .css 搭 .js 页要使用小写标题,譬如,{{ns:user}}:Foo/vector.css 弗同于 {{ns:user}}:Foo/Vector.css。",
        "updated": "(已更新)",
        "note": "'''注意:'''",
        "previewnote": "<strong>该个还只是预览。</strong>倷个修改还朆保存!",
        "prefs-files": "文件",
        "prefs-custom-css": "自定义CSS",
        "prefs-custom-js": "自定义JavaScript",
-       "prefs-common-css-js": "所有皮肤一道用个CSS/JavaScript:",
+       "prefs-common-config": "所有皮肤一道用个CSS/JavaScript:",
        "prefs-emailconfirm-label": "电子邮件确认:",
        "youremail": "电子信箱:",
        "username": "{{GENDER:$1|用户名}}:",
index 47bb2a7..59410a6 100644 (file)
        "userjsyoucanpreview": "'''Селвг:''' тана шин JS боомг шүүҗ хадһлар, «{{int:showpreview}}» товч олзлтн.",
        "usercsspreview": "'''Тана CSS боомгин мел хәләвр бәәдг тускар тодлтн, тер ода чигн хадһлсн уга!'''",
        "userjspreview": "'''Тана JavaScript боомгин мел хәләвр бәәдг тускар тодлтн. Тана сольлһн ода чигн хадһлсн уга!'''",
-       "userinvalidcssjstitle": "'''Оньг өгтн:''' «$1» гидг нерәдлһтә хувцнь олҗ биш. Күүнә .css болн .js халхс һанцхн бичкн үзгүдтә бичсн кергтә, үлгүрнь «{{ns:user}}:Болвчн/vector.css»; «{{ns:user}}:Болвчн/Vector.css» - буру.",
+       "userinvalidconfigtitle": "'''Оньг өгтн:''' «$1» гидг нерәдлһтә хувцнь олҗ биш. Күүнә .css болн .js халхс һанцхн бичкн үзгүдтә бичсн кергтә, үлгүрнь «{{ns:user}}:Болвчн/vector.css»; «{{ns:user}}:Болвчн/Vector.css» - буру.",
        "updated": "(Шинрүлсн)",
        "note": "'''Аҗгллһн:'''",
        "previewnote": "'''Эн мел хәләвр бәәдг тускар тодлтн.'''\nТана сольлһн ода чигн хадһлсн уга!",
index eda5374..b296c6c 100644 (file)
        "prefs-files": "ფაილები",
        "prefs-custom-css": "მომხმარებლის CSS",
        "prefs-custom-js": "მომხმარებლის JS",
-       "prefs-common-css-js": "ზოგადი CSS/JS ყველა თემისთვის:",
+       "prefs-common-config": "ზოგადი CSS/JS ყველა თემისთვის:",
        "prefs-reset-intro": "ეს გვერდი შეიძლება გამოყენებული იქნეს თქვენი კონფიგურაციის შესაცვლელად საწყის კონფიგურაციაზე. ამ მოქმედების დადასტურების შემთხვევაში, თქვენ ვეღარ შეძლებთ მის გაუქმებას.",
        "prefs-emailconfirm-label": "ელ-ფოსტის დადასტურება:",
        "youremail": "ელ-ფოშტა:",
index d0d7069..429bd9a 100644 (file)
        "userjspreview": "'''געדענקט אַז איר טוט בלויז אויספרואוון\\פֿאראויסזען אייער באַניצער JavaScript.'''\n'''עס איז דערווײַל נאכנישט אָפגעהיטן!'''",
        "sitecsspreview": "'''געדענקט אַז איר טוט בלויז פֿאראויסזען דעם דאָזיקן CSS קאד.'''\n'''ער איז דערווײַל נאכנישט אויפֿגעהיטן!'''",
        "sitejspreview": "'''געדענקט אַז איר טוט בלויז פֿאראויסזען דעם דאָזיקן JavaScript קאד.'''\n'''ער איז דערווײַל נאכנישט אויפֿגעהיטן!'''",
-       "userinvalidcssjstitle": "'''ווארענונג:''' סאיז נישטא קיין סקין \"$1\". גדענקט אז קאסטעם .css און .js בלעטער נוצען לאוער קעיס טיטול, e.g. {{ns:user}}:Foo/vector.css ווי אנדערשט צו {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "'''ווארענונג:''' סאיז נישטא קיין סקין \"$1\". גדענקט אז קאסטעם .css און .js בלעטער נוצען לאוער קעיס טיטול, e.g. {{ns:user}}:Foo/vector.css ווי אנדערשט צו {{ns:user}}:Foo/Vector.css.",
        "updated": "(דערהיינטיגט)",
        "note": "'''באמערקונג:'''",
        "previewnote": "'''געדענקט אַז דאָס איז נאָר אַ פאָרויסקוק.'''\nאייערע ענדערונגען זענען נאָך נישט געהיט!",
        "prefs-files": "טעקעס",
        "prefs-custom-css": "באַניצער דעפֿינירט CSS",
        "prefs-custom-js": "באַניצער דעפֿינירט JS",
-       "prefs-common-css-js": "שותפֿותדיקער CSS/JS פֿאַר אַלע אויספֿארמירונגען:",
+       "prefs-common-config": "שותפֿותדיקער CSS/JS פֿאַר אַלע אויספֿארמירונגען:",
        "prefs-reset-intro": "איר קענט ניצן דעם בלאַט צוריקצושטעלן אײַערע פרעפֿערענצן גרונטלעך פֿאַרן ארט.\nמען קען דאָס נישט אַנולירן.",
        "prefs-emailconfirm-label": "ע-פאסט באַשטעטיקונג:",
        "youremail": "ע-פאסט:",
index 5183213..7dc88b6 100644 (file)
        "userjspreview": "''''Ẹ mọ́ gbàgbé pé àdánwò/àkọ́yẹ̀wò JavaScript oníṣe yín nìyí.'''\n'''Kò tíì jẹ́ mímúpamọ́!'''",
        "sitecsspreview": "'''Ẹ rántí pé àkọ́yẹ̀wò CSS nìyí.'''\n'''Kò tíì jẹ́ mímúpamọ!'''",
        "sitejspreview": "'''Ẹ rántí pé àkọ́yẹ̀wò àmìọ̀rọ̀ JavaScript nìyí.'''\n'''Kò tíì jẹ́ mímúpamọ!'''",
-       "userinvalidcssjstitle": "'''Ìkìlọ̀:''' Kò sí awọ-ìbojú \"$1\".\nẸ rántí pé àwọn ojúewé àkànṣe .css àti .js únlo àkọlé onílẹ́tà kékeré, f.a. {{ns:user}}:Foo/vector.css yàtò sí {{ns:user}}:Foo/Vector.css.",
+       "userinvalidconfigtitle": "'''Ìkìlọ̀:''' Kò sí awọ-ìbojú \"$1\".\nẸ rántí pé àwọn ojúewé àkànṣe .css àti .js únlo àkọlé onílẹ́tà kékeré, f.a. {{ns:user}}:Foo/vector.css yàtò sí {{ns:user}}:Foo/Vector.css.",
        "updated": "(Sísọdọ̀tun)",
        "note": "'''Àkíyèsí:'''",
        "previewnote": "'''Ẹ rántí pé àyẹ̀wò lásán nì yí.'''\nÀwọn àtúnṣe yín kò tíì jẹ́ kìkópamọ́!",
        "prefs-files": "Àwọn faili",
        "prefs-custom-css": "CSS àkànṣe",
        "prefs-custom-js": "JavaScript àkànṣe",
-       "prefs-common-css-js": "CSS/JavaScript àpínlò fún gbogbo àwọn awọ:",
+       "prefs-common-config": "CSS/JavaScript àpínlò fún gbogbo àwọn awọ:",
        "prefs-reset-intro": "Ẹ le lo ojúewé yìí láti ṣàtùntò àwọn ìfẹ́ràn yín sí àkọ́kọ́ṣe ibiìtakùn yìí.\nKò ní ṣeé dápadà mọ́.",
        "prefs-emailconfirm-label": "E-mail ìmúdájú:",
        "youremail": "E-mail:",
index 4a1f11d..0201c42 100644 (file)
        "lastmodifiedat": "呢一頁嘅最後修改係響$1 $2。",
        "viewcount": "呢一頁已經有$1人次睇過。",
        "protectedpage": "受保護頁",
-       "jumpto": "跳去:",
+       "jumpto": "跳去",
        "jumptonavigation": "定向",
        "jumptosearch": "搵嘢",
        "view-pool-error": "對唔住,個伺服器響呢段時間超出咗負荷。\n太多用戶試過去睇呢一版。\n響再睇呢一版之前請等多一陣。\n\n$1",
        "userjspreview": "'''記住你而家只係測試/預覽緊你定義嘅JavaScript。'''\n'''佢嘅內容重未儲存!'''",
        "sitecsspreview": "'''記住你而家只係預覽呢段 CSS。'''\n'''佢嘅內容重未儲存!'''",
        "sitejspreview": "'''記住你而家只係預覽呢段 JavaScript 代碼。'''\n'''佢嘅內容重未儲存!'''",
-       "userinvalidcssjstitle": "'''警告:''' 無叫做 \"$1\" 嘅畫面。請記住自訂介面的 .css 和 .js 頁面時應使用細楷,例如:{{ns:user}}:Foo/vector.css 而唔係 {{ns:user}}:Foo/Vector.css 。",
+       "userinvalidconfigtitle": "'''警告:''' 無叫做 \"$1\" 嘅畫面。請記住自訂介面的 .css 和 .js 頁面時應使用細楷,例如:{{ns:user}}:Foo/vector.css 而唔係 {{ns:user}}:Foo/Vector.css 。",
        "updated": "(己更新)",
        "note": "'''留意:'''",
        "previewnote": "'''請記住呢個只係預覽。'''\n更改嘅内容重未儲存!",
        "prefs-files": "檔案",
        "prefs-custom-css": "自定 CSS",
        "prefs-custom-js": "自定 JavaScript",
-       "prefs-common-css-js": "共有嘅CSS同埋JavaScript畀所有畫面用:",
+       "prefs-common-config": "共有嘅CSS同埋JavaScript畀所有畫面用:",
        "prefs-reset-intro": "你可以用呢版去重設你嘅喜好設定到網站預設值。呢個動作無得番轉頭。",
        "prefs-emailconfirm-label": "電郵確認:",
        "youremail": "電郵:",
        "rcfilters-activefilters": "用緊嘅篩選條件",
        "rcfilters-advancedfilters": "進階嘅篩選條件",
        "rcfilters-limit-title": "顯示幾多結果",
+       "rcfilters-limit-and-date-label": "$1次{{PLURAL:$1|改動}},$2",
        "rcfilters-days-title": "最近幾多日",
        "rcfilters-hours-title": "最近幾多個鐘頭",
        "rcfilters-days-show-days": "$1 {{PLURAL:$1|日}}",
        "recentchangeslinked-feed": "連結頁嘅更改",
        "recentchangeslinked-toolbox": "連結頁嘅更改",
        "recentchangeslinked-title": "對「$1」有關嘅更改",
-       "recentchangeslinked-summary": "呢一個特別頁列示咗''由''所畀到嘅一版連結到頁嘅最近更改(或者係指定分類嘅成員)。\n響[[Special:Watchlist|你張監視清單]]嘅版會以'''粗體'''顯示。",
+       "recentchangeslinked-summary": "輸入一個頁名來睇下嗰版連出去或者連入去嗰版嘅頁面嘅相關更改(想睇一個類入面嘅版,請輸入Category:分類名)。喺[[Special:Watchlist|你張監視清單]]嘅版會以<strong>粗體</strong>顯示。",
        "recentchangeslinked-page": "頁名:",
        "recentchangeslinked-to": "顯示連到所畀到嘅版",
        "recentchanges-page-added-to-category": "[[:$1]] 加咗落分類",
index 01a162d..f8c6f24 100644 (file)
        "userjsyoucanpreview": "'''Tip:''' Gebruuk de knoppe 'Naekieken' om je nieuwe JS te tessen voe da je opsli.",
        "usercsspreview": "'''Dit is alleên een voeôvertonieng van je persoônlijke CSS, dezen is nog nie opeslogen!'''",
        "userjspreview": "'''Let op: je test noe je persoônlijke JavaScript. De pagina is nie opeslogen!'''",
-       "userinvalidcssjstitle": "'''Waerschuwieng:''' der is hin skin \"$1\". Let op: jen eihen .css- en .js-pagina's behunnen mie een kleine letter, buvobbeld {{ns:user}}:Naem/vector.css in plekke van {{ns:user}}:Naem/Vector.css.",
+       "userinvalidconfigtitle": "'''Waerschuwieng:''' der is hin skin \"$1\". Let op: jen eihen .css- en .js-pagina's behunnen mie een kleine letter, buvobbeld {{ns:user}}:Naem/vector.css in plekke van {{ns:user}}:Naem/Vector.css.",
        "updated": "(Biehewerkt)",
        "note": "'''Opmerkieng:'''",
        "previewnote": "'''Let op: dit is een controlepagina; je tekst is nie opeslogen!'''",
diff --git a/languages/i18n/zgh.json b/languages/i18n/zgh.json
new file mode 100644 (file)
index 0000000..3be0439
--- /dev/null
@@ -0,0 +1,534 @@
+{
+       "@metadata": {
+               "authors": [
+                       "Amara-Amaziɣ",
+                       "Aslmad mohamed belarhzali",
+                       "Gagnabil",
+                       "Mdb897",
+                       "YesIn",
+                       "ⵕⴰⵊⵉ"
+               ]
+       },
+       "sunday": "ⴰⵙⴰⵎⴰⵙ",
+       "monday": "ⴰⵢⵏⴰⵙ",
+       "tuesday": "ⴰⵙⵉⵏⴰⵙ",
+       "wednesday": "ⴰⴽⵔⴰⵙ",
+       "thursday": "ⴰⴽⵡⴰⵙ",
+       "friday": "ⴰⵙⵉⵎⵡⴰⵙ",
+       "saturday": "ⴰⵙⵉⴹⵢⴰⵙ",
+       "sun": "ⴰⵙⴰ",
+       "mon": "ⴰⵢⵏ",
+       "tue": "ⴰⵙⵏ",
+       "wed": "ⴰⴽⵕ",
+       "thu": "ⴰⴽⵡ",
+       "fri": "ⴰⵙⵎ",
+       "sat": "ⴰⵙⴹ",
+       "january": "ⵉⵏⵏⴰⵢⵔ",
+       "february": "ⴱⵕⴰⵢⵕ",
+       "march": "ⵎⴰⵕⵚ",
+       "april": "ⵉⴱⵔⵉⵔ",
+       "may_long": "ⵎⴰⵢⵢⵓ",
+       "june": "ⵢⵓⵏⵢⵓ",
+       "july": "ⵢⵓⵍⵢⵓⵣ",
+       "august": "ⵖⵓⵛⵜ",
+       "september": "ⵛⵓⵜⴰⵏⴱⵉⵔ",
+       "october": "ⴽⵜⵓⴱⵔ",
+       "november": "ⵏⵓⵡⴰⵏⴱⵉⵔ",
+       "december": "ⴷⵓⵊⴰⵏⴱⵉⵔ",
+       "january-gen": "ⵉⵏⵏⴰⵢⵔ",
+       "february-gen": "ⴱⵕⴰⵢⵕ",
+       "march-gen": "ⵎⴰⵕⵚ",
+       "april-gen": "ⵉⴱⵔⵉⵔ",
+       "may-gen": "ⵎⴰⵢⵢⵓ",
+       "june-gen": "ⵢⵓⵏⵢⵓ",
+       "july-gen": "ⵢⵓⵍⵢⵓⵣ",
+       "august-gen": "ⵖⵓⵛⵜ",
+       "september-gen": "ⵛⵓⵜⴰⵏⴱⵉⵔ",
+       "october-gen": "ⴽⵜⵓⴱⵔ",
+       "november-gen": "ⵏⵓⵡⴰⵏⴱⵉⵔ",
+       "december-gen": "ⴷⵓⵊⴰⵏⴱⵉⵔ",
+       "jan": "ⵉⵏⵏ",
+       "feb": "ⴱⵕⴰ",
+       "mar": "ⵎⴰⵕ",
+       "apr": "ⵉⴱⵔ",
+       "may": "ⵎⴰⵢ",
+       "jun": "ⵢⵓⵏ",
+       "jul": "ⵢⵓⵍ",
+       "aug": "ⵖⵓⵛ",
+       "sep": "ⵛⵓⵜ",
+       "oct": "ⴽⵟⵓ",
+       "nov": "ⵏⵓⵡ",
+       "dec": "ⴷⵓⵊ",
+       "january-date": "$1 ⵉⵏⵏⴰⵢⵏ",
+       "february-date": "$1 ⴱⵕⴰⵢⵕ",
+       "march-date": "$1 ⵎⴰⵕⵚ",
+       "april-date": "$1 ⵉⴱⵔⵉⵔ",
+       "may-date": "$1 ⵎⴰⵢⵢⵓ",
+       "june-date": "$1 ⵢⵓⵏⵢⵓ",
+       "july-date": "$1 ⵢⵓⵍⵢⵓⵣ",
+       "august-date": "$1 ⵖⵓⵛⵜ",
+       "september-date": "$1 ⵛⵓⵜⴰⵏⴱⵉⵔ",
+       "november-date": "$1 ⵏⵓⵡⴰⵏⴱⵉⵔ",
+       "december-date": "$1 ⴷⵓⵊⴰⵏⴱⵉⵔ",
+       "period-am": "ⴼⵡ",
+       "period-pm": "ⵣⵡ",
+       "pagecategories": "{{PLURAL:$1|ⴰⵙⵎⵉⵍ|ⵉⵙⵎⵉⵍⵏ}}",
+       "category_header": "Tasniwin g usmil \"$1\"",
+       "subcategories": "ⵉⴷⵓⵙⵎⵉⵍⵏ",
+       "category-media-header": "ⵎⵉⴷⵢⴰ ⴳ ⵓⵙⵎⵉⵍ \"$1\"",
+       "hidden-categories": "{{PLURAL:$1|ⴰⵙⵎⵉⵍ ⵉⵎⵎⵏⵜⵍⵏ|ⵉⵙⵎⵉⵍⵏ ⵎⵎⵏⵜⵍⵏⵉⵏ}}",
+       "about": "ⵅⴼ",
+       "article": "ⵜⴰⵙⵏⴰ ⵏ ⵜⵓⵎⴰⵢⵜ",
+       "newwindow": "(ⴰⴷ ⵉⵏⵏⵓⵔⵥⵎ ⴳ ⵓⵙⴽⵙⵍ ⴰⵎⴰⵢⵏⵓ)",
+       "cancel": "ⵙⵔ",
+       "moredotdotdot": "ⵓⴳⴳⴰⵔ...",
+       "mypage": "ⵜⴰⵙⵏⴰ",
+       "mytalk": "ⵎⵙⴳⴷⴰⵍ",
+       "anontalk": "ⵎⵙⴰⵡⴰⵍ",
+       "navigation": "ⴰⵙⵜⴰⵔⴰ",
+       "and": "&#32;ⴷ",
+       "namespaces": "ⵜⵉⵔⵉⵡⵉⵏ ⵏ ⵉⵙⵎⴰⵡⵏ",
+       "variants": "ⵜⵉⵎⵣⴰⵔⴰⵢⵉⵏ",
+       "navigation-heading": "ⵓⵎⵓⵖ ⵏ ⵓⵙⵙⴰⵔⴰ",
+       "errorpagetitle": "ⵜⴰⵣⴳⵍⵜ",
+       "returnto": "ⴰⵖⵓⵍ ⵖⵔ $1.",
+       "tagline": "ⵙⴳ {{SITENAME}}",
+       "help": "ⵜⵉⵡⵉⵙⵉ",
+       "search": "ⵔⵣⵓ",
+       "searchbutton": "ⵔⵣⵓ",
+       "go": "ⵔⵣⵓ",
+       "searcharticle": "ⴷⴷⵓ",
+       "history": "ⴰⵎⵣⵔⴰⵢ ⵏ ⵜⴰⵙⵏⴰ",
+       "history_short": "ⴰⵎⵣⵔⵓⵢ",
+       "history_small": "ⴰⵎⵣⵔⵓⵢ",
+       "printableversion": "ⵜⵓⵏⵖⵉⵍⵜ ⵉⵜⵜⵡⴰⵙⵉⴳⴳⵣⵏ",
+       "permalink": "ⴰⵙⵖⵏ ⴰⵎⵖⵍⴰⵍ",
+       "view": "ⵙⴽⵏ",
+       "view-foreign": "ⵙⴽⵏ ⴳ $1",
+       "edit": "ⵙⵏⴼⵍ",
+       "create": "ⵙⵏⵓⵍⴼⵓ",
+       "delete": "ⴽⴽⵙ",
+       "protect_change": "ⵙⵏⴼⵍ",
+       "newpage": "ⵜⴰⵙⵏⴰ ⵜⴰⵎⴰⵢⵏⵓⵜ",
+       "talkpagelinktext": "ⵎⵙⴰⵡⴰⵍ",
+       "specialpage": "ⵜⴰⵙⵏⵉⵡⵉⵏ ⵥⵍⵉⵏⵉⵏ",
+       "personaltools": "ⵉⵎⴰⵙⵙⵏ ⵓⴷⵎⴰⵡⴰⵏⵏ",
+       "talk": "ⴰⵎⵙⴰⵡⴰⵍ",
+       "views": "ⵜⴰⵏⵏⴰⵢⵉⵏ",
+       "toolbox": "ⵉⵎⴰⵙⵙⵏ",
+       "imagepage": "ⵥⵕ ⵜⴰⵙⵏⴰ ⵏ ⵓⴼⴰⵢⵍⵓ",
+       "mediawikipage": "ⵥⵕ ⵜⴰⵙⵏⴰ ⵏ ⵜⵓⵣⵓⵏⵜ",
+       "viewhelppage": "ⵥⵕ ⵜⴰⵙⵏⴰ ⵏ ⵜⵡⵉⵙⵉ",
+       "otherlanguages": "ⵙ ⵜⵓⵜⵍⴰⵢⵉⵏ ⵢⴰⴹⵏⵉⵏ",
+       "redirectedfrom": "(ⵓⵖⵓⵍ ⵙⴳ $1)",
+       "redirectto": "ⵙⵏⵉⵍ ⵖⵔ:",
+       "lastmodifiedat": "ⵜⵏⵏⴼⵍ ⵜⴰⵙⵏⴰ ⴰ ⵉ ⵜⵉⴽⴽⵍⵜ ⵜⴰⵎⴳⴳⴰⵔⵓⵜ ⴳ $1, ⴳ $2.",
+       "jumpto": "ⵏⴹⵓ ⵖⵔ:",
+       "jumptonavigation": "ⴰⵙⵜⴰⵔⴰ",
+       "jumptosearch": "ⵔⵣⵓ",
+       "aboutsite": "ⵖⴼ {{SITENAME}}",
+       "aboutpage": "Project:ⵅⴼ",
+       "copyrightpage": "{{ns:project}}:ⵉⵣⵔⴼⴰⵏ ⵏ ⵓⵙⵏⵖⵍ",
+       "currentevents": "ⵜⵉⵎⵙⴰⵔⵉⵏ ⵜⵉⵎⵉⵔⴰⵏⵉⵏ",
+       "currentevents-url": "Project:ⵜⵉⵎⵙⴰⵔⵉⵏ ⵜⵉⵎⵉⵔⴰⵏⵉⵏ",
+       "disclaimers": "ⴰⵙⵍⵉⴳⵍ",
+       "disclaimerpage": "Project:ⴰⵙⵎⵉⴳⵍ ⴰⵎⴰⵜⴰⵢ",
+       "edithelp": "ⵜⵉⵡⵉⵙⵉ ⵏ ⵓⵙⵏⴼⵍ",
+       "helppage-top-gethelp": "ⵜⵉⵡⵉⵙⵉ",
+       "mainpage": "ⵜⴰⵙⵏⴰ ⵏ ⵓⵙⵏⵓⴱⴳ",
+       "mainpage-description": "ⵜⴰⵙⵏⴰ ⵏ ⵓⵙⵏⵓⴱⴳ",
+       "policy-url": "Project:ⵜⴰⵙⵔⵜⵉⵜ",
+       "portal": "ⴰⵡⵡⵓⵔ ⵏ ⵜⴳⵔⴰⵡⵜ",
+       "portal-url": "Project:ⴰⵡⵡⵓⵔ ⵏ ⵜⴳⵔⴰⵡⵜ",
+       "privacy": "ⵜⴰⵙⵔⵜⵉⵜ ⵏ ⵜⵉⵏⵏⵓⵜⵍⴰ",
+       "privacypage": "Project:ⵜⴰⵙⵔⵜⵉⵜ ⵏ ⵜⵉⵏⵏⵓⵜⵍⴰ",
+       "ok": "ⵡⴰⵅⵅⴰ",
+       "retrievedfrom": "ⵉⵜⵜⵓⵙⴰⵖⵓⵍ ⵙⴳ $1",
+       "newmessageslinkplural": "{{PLURAL:$1|ⵜⵓⵣⵉⵏⵜ ⵜⴰⵎⴰⵢⵏⵓⵜ|999=ⵜⵓⵣⵉⵏⵉⵏ ⵜⵉⵎⴰⵢⵏⵓⵜⵉⵏ}}",
+       "youhavenewmessagesmulti": "ⵍⵍⴰⵏ ⵖⵓⵔⴽ ⵜⵓⵣⵉⵏⵉⵏ ⵜⵉⵎⴰⵢⵏⵓⵜⵉⵏ ⴳ $1",
+       "editsection": "ⵙⵏⴼⵍ",
+       "editold": "ⵙⵏⴼⵍ",
+       "viewsourceold": "ⵙⴽⵏ ⴰⵙⴰⴳⵎ",
+       "editlink": "ⵙⵏⴼⵍ",
+       "viewsourcelink": "ⵙⴽⵏ ⴰⵙⴰⴳⵎ",
+       "editsectionhint": "ⵙⵏⴼⵍ ⵜⵉⴳⵣⵎⵉ: $1",
+       "toc": "ⵜⵓⵎⴰⵢⵉⵏ",
+       "showtoc": "ⵙⴽⵏ",
+       "hidetoc": "ⵙⵙⵏⵜⵍ",
+       "confirmable-yes": "ⵢⴰⵀ",
+       "confirmable-no": "ⵓⵀⵓ",
+       "viewdeleted": "ⵥⵕ $1?",
+       "site-atom-feed": "ⴰⵏⴳⵉ ⵏ ⴰⵜⵓⵎ ⵏ $1",
+       "red-link-title": "$1 (ⵜⴰⵙⵏⴰ ⵓⵔ ⵜⵍⵍⵉ)",
+       "nstab-main": "ⵜⴰⵙⵏⴰ",
+       "nstab-user": "ⵜⴰⵙⵏⴰ ⵏ ⵓⵙⵎⵔⴰⵙ",
+       "nstab-special": "ⵜⴰⵙⵏⴰ ⵉⵥⵍⵉⵏ",
+       "nstab-project": "ⵜⴰⵙⵏⴰ ⵏ ⵓⵙⵏⵜⵉ",
+       "nstab-image": "ⴰⴼⴰⵢⵍⵓ",
+       "nstab-mediawiki": "ⵜⵓⵣⵉⵏⵜ",
+       "nstab-template": "ⵜⴰⵙⴽⴽⴰ",
+       "nstab-help": "ⵜⴰⵙⵏⴰ ⵏ ⵜⵡⵉⵙⵉ",
+       "nstab-category": "ⴰⵙⵎⵉⵍ",
+       "mainpage-nstab": "ⵜⴰⵙⵏⴰ ⵏ ⵓⵙⵏⵓⴱⴳ",
+       "error": "ⵜⴰⵣⴳⵍⵜ",
+       "databaseerror-error": "ⵜⴰⵣⴳⵍⵜ: $1",
+       "viewsource": "ⵙⴽⵏ ⴰⵙⴰⴳⵎ",
+       "userlogin-yourname": "ⵉⵙⵎ ⵏ ⵓⵙⵎⵔⴰⵙ",
+       "userlogin-yourname-ph": "ⵙⵙⴽⵛⵎ ⵉⵙⵎ ⵏ ⵓⵏⵙⵙⵎⵔⵙ ⵏⵏⵎ/ⴽ",
+       "yourpassword": "ⵜⴰⴳⵓⵔⵉ ⵏ ⵓⵣⵔⴰⵢ",
+       "userlogin-yourpassword": "ⵜⴰⴳⵓⵔⵉ ⵏ ⵓⵣⵔⴰⵢ",
+       "userlogin-yourpassword-ph": "ⵙⵙⴽⵛⵎ ⵜⴰⴳⵓⵔⵉ ⵏ ⵓⵣⵔⴰⵢ ⵏⵏⴽ",
+       "createacct-yourpassword-ph": "ⵙⵙⴽⵛⵎ ⴽⵔⴰ ⵏ ⵜⴳⵓⵔⵉ ⵏ ⵓⵣⴰⵔⵢ",
+       "createacct-yourpasswordagain": "ⵙⵙⵍⴽⵏ ⵜⴰⴳⵓⵔⵉ ⵏ ⵓⵣⵔⴰⵢ",
+       "createacct-yourpasswordagain-ph": "ⵙⵙⴽⵛⵎ ⴷⴰⵖ ⵜⴰⴳⵓⵔⵉ ⵏ ⵓⵣⵔⴰⵢ",
+       "login": "ⴽⵛⵎ",
+       "login-security": "ⵙⵙⵉⴷⴻⴷ ⵜⴰⵎⴰⴳⵉⵜ ⵏⵏⴽ",
+       "logout": "ⴼⴼⵖ",
+       "userlogout": "ⴼⴼⵖ",
+       "userlogin-noaccount": "ⵓⵔ ⵖⵓⵔⴽ ⵉⵍⵍⵉ ⵓⵎⵉⴹⴰⵏ?",
+       "createaccount": "ⵔⵥⵎ ⴽⵔⴰ ⵏ ⵓⵎⵉⴹⴰⵏ",
+       "userlogin-resetpassword-link": "ⵜⴻⵜⵜⵓⴷ ⵜⴰⴳⵓⵔⵉ ⵏ ⵓⵣⵔⴰⵢ ⵏⵏⵎ/ⴽ?",
+       "createacct-emailoptional": "ⵉⵎⴰⵢⵍ (ⴰⵔⵓⵛⵛⵉⵍ)",
+       "createacct-email-ph": "ⵙⵙⴽⵛⵎ ⴰⵏⵙⴰ ⵉⵎⴰⵢⵍ ⵏⵏⴽ",
+       "createacct-reason": "ⵜⴰⵎⵏⵜⵉⵍⵜ",
+       "createacct-submit": "ⵔⵥⵎ ⴰⵎⵉⴹⴰⵏ {{GENDER:|ⵏⵏⴽ|ⵏⵏⵎ}}",
+       "createacct-benefit-heading": "{{SITENAME}} ⵜⴻⵜⵜⵓⴽ ⵙⴳ ⵎⵉⴷⴷⵏ ⴰⵎ ⴽⵢⵢⵉⵏ",
+       "createacct-benefit-body1": "{{PLURAL:$1|ⴰⵙⵏⴼⵍ|ⵉⵙⵏⴼⴰⵍ}}",
+       "createacct-benefit-body2": "{{PLURAL:$1|ⵜⴰⵙⵏⴰ|ⵜⴰⵙⵏⵉⵡⵉⵏ}}",
+       "createacct-benefit-body3": "{{PLURAL:$1|ⴰⵏⴰⵎⵓ|ⵉⵏⴰⵎⵓⵜⵏ}} {{PLURAL:$1|ⴰⵎⴳⴳⴰⵔⵓ|ⵉⵎⴳⴳⵓⵔⴰ}}",
+       "loginlanguagelabel": "ⵜⵓⵜⵍⴰⵢⵜ: $1",
+       "pt-login": "ⴽⵛⵎ",
+       "pt-login-button": "ⴽⵛⵎ",
+       "pt-createaccount": "ⵙⵏⴼⵍⵓⵍ ⴰⵎⵉⴹⴰⵏ",
+       "pt-userlogout": "ⴼⴼⵖ",
+       "botpasswords-label-create": "ⵙⵏⵓⵍⴼⵓ",
+       "botpasswords-label-delete": "ⴽⴽⵙ",
+       "passwordreset": "ⵔⴰⵔ ⴷ ⵜⴰⴳⵓⵔⵉ ⵏ ⵓⵣⵔⴰⵢ",
+       "bold_sample": "ⴰⴹⵔⵉⵙ ⴰⵣⵓⵔⴰⵔ",
+       "bold_tip": "ⴰⴹⵔⵉⵙ ⴰⵣⵓⵔⴰⵔ",
+       "italic_sample": "ⴰⴹⵔⵉⵙ ⵓⵣⵍⵉⴳ",
+       "italic_tip": "ⴰⴹⵔⵉⵙ ⵓⵣⵍⵉⴳ",
+       "link_sample": "ⴰⵣⵡⵍ ⵏ ⵓⵙⵖⵓⵏ",
+       "link_tip": "ⴰⵙⵖⵓⵏ ⴰⴳⵯⵏⵙⴰⵏ",
+       "extlink_sample": "http://www.example.com ⴰⵣⵡⵍ ⵏ ⵓⵙⵖⵓⵏ",
+       "extlink_tip": "ⴰⵙⵖⵓⵏ ⴰⴱⵕⵕⴰⵏⵉ (ⴽⵜⵢ ⴰⵣⵡⵉⵔ http://)",
+       "image_tip": "ⴰⴼⴰⵢⵍⵓ ⵉⵜⵜⵓⵙⵉⴷⴼⵏ",
+       "media_tip": "ⴰⵙⵖⵓⵏ ⵏ ⵓⴼⴰⵢⵍⵓ",
+       "hr_tip": "ⵉⵣⵔⵉⵔⵉ ⴰⴳⵍⴰⵡⴰⵏ (ⵓⵔ ⵜⵄⵢⵢⵇ)",
+       "summary": "ⴰⵙⴳⵣⵍ:",
+       "minoredit": "ⵡⴰ ⴷ ⴰⵙⵏⴼⵍ ⵓⵎⵥⵉⵢ",
+       "watchthis": "ⵎⵎⴰⵜⵔ ⵜⴰⵙⵏⴰ ⴰⴷ",
+       "savearticle": "ⵃⴹⵓ ⵜⴰⵙⵏⴰ",
+       "showdiff": "ⵙⵎⴰⵍ ⵉⵙⵏⴼⵍⵏ",
+       "loginreqlink": "ⴽⵛⵎ",
+       "newarticle": "(ⴰⵎⴰⵢⵏⵓ)",
+       "continue-editing": "ⴷⴷⵓ ⵙ ⴰⵏⵙⴰ ⵏ ⵓⵙⵏⴼⵍ",
+       "editing": "ⴰⵙⵏⴼⵍ ⵏ $1",
+       "creating": "ⴰⵙⵏⵓⵍⴼⵓ ⵏ $1",
+       "editingsection": "ⵙⵏⴼⵍ ⴰⴳⵣⵣⵓⵎ $1",
+       "templatesused": "{{PLURAL:$1|ⵜⴰⵙⴽⴽⴰ|ⵜⴰⵙⴽⴽⵉⵡⵉⵏ}} {{PLURAL:$1|ⵉⵜⵜⵓⵙⵎⵔⵙⵏ|ⵜⵜⵓⵙⵎⵔⵙⵏⵉⵏ}} ⴳ ⵜⴰⵙⵏⴰ ⴰⴷ:",
+       "template-protected": "(ⵉⵜⵜⵢⴰⵔⴰⵢ)",
+       "hiddencategories": "ⵜⴰⵙⵏⴰ ⴰ ⴷ ⴰⴳⵎⴰⵎ ⵏ {{PLURAL:$1|1 ⵏⵜⵍ ⴰⵙⵎⵉⵍ|$1 ⵏⵜⵍ ⵉⵙⵎⵉⵍⵏ}}:",
+       "content-model-wikitext": "wikitext",
+       "revisionasof": "ⵜⵓⵏⵖⵉⵍⵜ ⵏ $1",
+       "previousrevision": "ⵜⵓⵏⵖⵉⵍⵜ ⵜⴰⵇⴱⵓⵔⵜ",
+       "cur": "ⵎⵔⵏ",
+       "histfirst": "ⴰⵇⴱⵓⵔ",
+       "histlast": "ⴰⵎⴰⵢⵏⵓ",
+       "revdelete-show-file-submit": "ⵢⴰⵀ",
+       "revdelete-log": "ⵜⴰⵎⵏⵜⵉⵍⵜ:",
+       "mergehistory-reason": "ⵜⴰⵎⵏⵜⵉⵍⵜ:",
+       "lineno": "ⵉⵣⵔⵉⵔⵉ $1:",
+       "editundo": "ⵙⵔ",
+       "searchresults": "ⵜⵉⵢⴰⴼⵓⵜⵉⵏ ⵏ ⵓⵔⵣⵣⵓ",
+       "searchresults-title": "ⵜⵉⵢⴰⴼⵓⵜⵉⵏ ⵏ ⵓⵔⵣⵣⵓ ⵖⴼ \"$1\"",
+       "nextn": "{{PLURAL:$1|$1}} ⴰⵎⴹⴼⵉⵔ",
+       "shown-title": "ⵙⵎⴰⵍ $1 {{PLURAL:$1|ⵜⵢⴰⴼⵓⵜ|ⵜⵢⴰⴼⵓⵜⵉⵏ}} ⵉ ⵜⴰⵙⵏⴰ",
+       "viewprevnext": "ⵥⵕ ($1 {{int:pipe-separator}} $2) ($3)",
+       "searchprofile-articles": "ⵜⴰⵙⵏⵉⵡⵉⵏ ⵏ ⵜⵓⵎⴰⵢⵜ",
+       "searchprofile-images": "ⵎⵓⵍⵜⵉⵎⵉⴷⵢⴰ",
+       "searchprofile-everything": "ⴽⵓ ⵜⴰⵖⴰⵡⵙⴰ",
+       "searchprofile-advanced": "ⵓⵏⵣⵉⵢ",
+       "searchprofile-articles-tooltip": "ⵔⵣⵓ ⴳ $1",
+       "searchprofile-images-tooltip": "ⵔⵣⵓ ⵖⴼ ⵉⴼⵓⵢⵍⴰ",
+       "searchprofile-everything-tooltip": "ⵔⵣⵓ ⴳ ⵜⵓⵎⴰⵢⵜ ⴰⴽⴽⵯ (ⵓⵍⴰ ⴳ ⵜⴰⵙⵏⵉⵡⵉⵏ ⵏ ⵓⵎⵙⴰⵡⴰⵍ)",
+       "searchprofile-advanced-tooltip": "ⵔⵣⵓ ⴳ ⵜⵉⵔⵉⵡⵉⵏ ⵏ ⵉⵙⵎⴰⵡⵏ ⵉⵜⵡⴰⵏⵉⵎⴰⵏ",
+       "search-result-size": "$1 ({{PLURAL:$2|1 ⵜⴳⵓⵔⵉ|$2 ⵜⴳⵓⵔⵉⵡⵉⵏ}})",
+       "search-suggest": "ⵉⵙ ⵜⵅⵙⴷ ⴰⴷ ⵜⵉⵏⵉⴷ: $1",
+       "search-interwiki-more": "(ⵓⴳⴳⴰⵔ)",
+       "searchall": "ⴰⴽⴽ",
+       "mypreferences": "ⵉⵙⵎⵏⵢⵉⴼⵏ",
+       "prefs-files": "ⵉⴼⴰⵢⵍⵓⵜⵏ",
+       "youremail": "ⵉⵎⴰⵢⵍ:",
+       "yourlanguage": "ⵜⵓⵜⵍⴰⵢⵜ:",
+       "yournick": "ⴰⵙⴳⵎⴹ ⴰⵎⴰⵢⵏⵓ:",
+       "email": "ⵉⵎⴰⵢⵍ",
+       "prefs-signature": "ⴰⵙⴳⵎⴹ",
+       "userrights-reason": "ⵜⴰⵎⵏⵜⵉⵍⵜ:",
+       "right-upload": "ⵙⴽⵜⵔ ⵉⴼⴰⵢⵍⵓⵜⵏ",
+       "right-upload_by_url": "ⵙⴽⵜⵔ ⴰⴼⴰⵢⵍⵓ ⵙⴳ URL",
+       "right-writeapi": "ⴰⵙⵙⵎⵔⵙ ⵏ API ⵉ ⵜⵉⵔⵔⴰ",
+       "right-delete": "ⴽⴽⵙ ⵜⴰⵙⵏⵉⵡⵉⵏ",
+       "newuserlogpage": "ⴰⵔⵔⴰ ⵏ ⵉⵙⵏⴼⴰⵍⵏ ⵏ ⵉⵎⵉⴹⴰⵏⴻⵏ ⵏ ⵉⵙⵙⵎⵔⴰⵙⵏ",
+       "action-edit": "ⵙⵏⴼⵍ ⵜⴰⵙⵏⴰ ⴰ",
+       "action-createpage": "ⵙⵏⵓⵍⴼⵓ ⵜⴰⵙⵏⴰ ⴰ",
+       "enhancedrc-history": "ⴰⵎⵣⵔⴰⵢ",
+       "recentchanges": "ⵉⵙⵏⴼⵍⵏ ⵉⵎⴳⴳⵓⵔⴰ",
+       "recentchanges-legend": "ⵜⵉⴷⵖⵔⵉⵏ ⵏ ⵉⵙⵏⴼⵍⵏ ⵉⵎⴳⴳⵓⵔⴰ",
+       "recentchanges-label-newpage": "ⵉⵙⵏⴼⵍⵓⵍ ⵓⵙⵏⴼⵍ ⴰ ⵢⴰⵜ ⵜⴰⵙⵏⴰ ⵜⴰⵎⴰⵢⵏⵓⵜ",
+       "recentchanges-label-minor": "ⵡⴰ ⴷ ⴰⵙⵏⴼⵍ ⵓⵎⵥⵉⵢ",
+       "recentchanges-label-bot": "ⴰⵙⵏⴼⵍ ⴰⴷ ⵉⵜⵡⴰⵙⴽⴰⵔ ⵙ ⵓⴱⵓⵜ",
+       "rclistfrom": "ⵙⴽⵏ ⵉⵙⵏⴼⵍⵏ ⵉⵎⴰⵢⵏⵓⵜⵏ ⵙⴳ $2,$3",
+       "rcshowhideminor": "$1 ⵉⵙⵏⴼⴰⵍ ⵓⵎⵥⵉⵢⵏ",
+       "rcshowhideminor-show": "ⵙⴽⵏ",
+       "rcshowhideminor-hide": "ⵙⵙⵏⵜⵍ",
+       "rcshowhidebots": "$1 {{PLURAL:$1|ⴱⵓⵜ|ⵉⴷ ⴱⵓⵜ}}",
+       "rcshowhidebots-show": "ⵙⵎⴰⵍ",
+       "rcshowhidebots-hide": "ⵙⵙⵏⵜⵍ",
+       "rcshowhideliu-show": "ⵙⴽⵏ",
+       "rcshowhideliu-hide": "ⵙⵙⵏⵜⵍ",
+       "rcshowhideanons": "$1 ⵉⵏⵙⵙⵎⵔⵙⵏ ⵉⵔⵓⵙⵙⵉⵏⵏ",
+       "rcshowhideanons-show": "ⵙⴽⵏ",
+       "rcshowhideanons-hide": "ⵙⵙⵏⵜⵍ",
+       "rcshowhidemine": "$1 ⵉⵙⵏⴼⴰⵍ ⵉⵏⵓ",
+       "rcshowhidemine-show": "ⵙⴽⵏ",
+       "rcshowhidemine-hide": "ⵙⵙⵏⵜⵍ",
+       "rclinks": "ⵙⴽⵏ ⵉⵙⵏⴼⵉⵍⵏ $1 ⵉⵎⴳⴳⵓⵔⴰ ⴳ ⵓⵙⵙⴰⵏ $2 ⵉⵎⴳⴳⵓⵔⴰ",
+       "diff": "ⵎⵣⵔⵢ",
+       "hist": "ⵎⵣⵔⵢ",
+       "hide": "ⵙⵙⵏⵜⵍ",
+       "show": "ⵙⴽⵏ",
+       "minoreditletter": "ⵎ",
+       "newpageletter": "ⵎⵢⵏ",
+       "boteditletter": "ⴱⵓⵜ",
+       "rc-change-size-new": "$1 {{PLURAL:$1|ⴱⴰⵢⵜ|ⵉⴷ ⴱⴰⵢⵜ}} ⴷⴼⴼⵉⵔ ⵏ ⵓⵙⵏⴼⵍ",
+       "recentchangeslinked": "ⵉⵙⵏⴼⴰⵍ ⵏⵏⴰ ⵖⵓⵔ ⴰⵙⵙⴰⵖ",
+       "recentchangeslinked-toolbox": "ⵉⵙⵏⴼⵍⵏ ⵇⵇⵏⵏⵉⵏ",
+       "recentchangeslinked-title": "ⵉⵙⵏⴼⵍⵏ ⵇⵇⵏⵏⵉⵏ ⵙ $1",
+       "recentchangeslinked-summary": "ⵙⴽⵛⵎ ⵉⵙⵎ ⵏ ⵜⴰⵙⵏⴰ ⵃⵎⴰ ⴰⴷ ⵜⵣⵔⴷ ⵉⵙⵏⴼⴰⵍⵏ ⵉⵎⴰⵢⵏⵓⵜⵏ ⴳ ⵜⴰⵙⵏⵉⵡⵉⵏ ⵙⴳ ⵏⵖ ⵖⵔ ⵜⴰⵙⵏⴰ ⴰⴷ (ⵃⵎⴰ ⴰⴷ ⵜⵣⵔⴷ ⵉⴳⵎⴰⵎⵏ ⵏ ⴽⵔⴰ ⵏ ⵓⵙⵎⵉⵍ, ⵙⴽⵛⵎ ⴰⵙⵎⵉⵍ: ⵉⵙⵎ ⵏ ⵓⵙⵎⵉⵍ). ⵉⵙⵏⴼⵍⵏ ⵏ ⵜⴰⵙⵏⵉⵡⵉⵏ ⵉⵍⵍⴰⵏ ⴳ [[Special:Watchlist|ⵜⴰⵍⴳⴰⵎⵜ ⵏ ⵓⴹⴼⴼⵓⵔ ⵏⴽ]] ⵔⴰⴷ ⵜⵢⴰⵔⴰⵏ ⵙ <strong>ⵓⵣⵓⵔⴰⵔ</strong>",
+       "recentchangeslinked-page": "ⵉⵙⵎ ⵏ ⵜⴰⵙⵏⴰ:",
+       "upload": "ⵙⴽⵜⵔ ⴰⴼⴰⵢⵍⵓ",
+       "uploadbtn": "ⵙⴽⵜⵔ ⴰⴼⴰⵢⵍⵓ",
+       "filedesc": "ⴰⵙⴳⵣⵍ",
+       "upload-form-label-infoform-name": "ⵉⵙⵎ",
+       "upload-form-label-usage-filename": "ⵉⵙⵎ ⵏ ⵓⴼⴰⵢⵍⵓ",
+       "upload-form-label-infoform-date": "ⴰⵙⴰⴽⵓⴷ",
+       "license-header": "ⵜⵓⵔⴰⴳⵜ",
+       "listfiles-delete": "ⴽⴽⵙ",
+       "imgfile": "ⴰⴼⴰⵢⵍⵓ",
+       "listfiles": "ⵜⴰⵍⴳⴰⵎⵜ ⵏ ⵓⴼⴰⵢⵍⵓ",
+       "listfiles_date": "ⴰⵙⴰⴽⵓⴷ",
+       "listfiles_name": "ⵉⵙⵎ",
+       "listfiles-latestversion-yes": "ⵢⴰⵀ",
+       "listfiles-latestversion-no": "ⵓⵀⵓ",
+       "file-anchor-link": "ⴰⴼⴰⵢⵍⵓ",
+       "filehist": "ⴰⵎⵣⵔⵓⵢ ⵏ ⵓⴼⴰⵢⵍⵓ",
+       "filehist-help": "ⴰⴷⵔ ⵖⴼ ⵓⵙⴰⴽⵓⴷ/ⴰⴽⵓⴷ ⵃⵎⴰ ⴰⴷ ⵜⵥⵔⴷ ⴰⵙⴷⴰⵡ ⵎⴰⵎⴽ ⵢⴰⴷⵍⵍⵉ ⵉⴳⴰ ⴰⵇⵓⴷ ⴰⵏ.",
+       "filehist-deleteone": "ⴽⴽⵙ",
+       "filehist-current": "ⴰⵎⵉⵔⴰⵏ",
+       "filehist-datetime": "ⴰⵙⴰⴽⵓⴷ/ⴰⴽⵓⴷ",
+       "filehist-thumb": "ⵜⴰⵛⵏⵢⴰⵍⵜ",
+       "filehist-thumbtext": "ⵜⴰⵛⵏⵢⴰⵍⵜ ⵏ ⵜⵓⵏⵖⵉⵍⵜ ⴳ $1",
+       "filehist-user": "ⴰⵙⵎⵔⴰⵙ",
+       "filehist-dimensions": "ⵉⵎⵏⴰⴷⵏ",
+       "filehist-comment": "ⴰⵖⴼⴰⵡⴰⵍ",
+       "imagelinks": "ⴰⵙⵎⵔⵙ ⵏ ⵓⴼⴰⵢⵍⵓ",
+       "linkstoimage": "{{PLURAL:$1|ⵉⵣⴷⴰⵢⵏ ⵏ ⵜⵙⵏⴰ|$1 ⴰⵣⴷⴰⵢ ⵏ ⵜⵙⵏⴰ}} ⵖⵔ ⵓⴼⴰⵢⵍⵓ ⴰⴷ:",
+       "sharedupload-desc-here": "ⴰⵙⴷⴰⵡ ⴰⴷ ⵙⴳ $1 ⵉⵥⴹⴰⵔ ⴰ ⵉⵜⵜⵡⴰⵙⵎⵔⵙ ⴳ ⵉⵙⵏⵜⴰⵢⵏ ⵢⴰⴹⵏ.\nⴰⵙⵏⵓⵎⵎⵍ ⵏⵙ ⴳ [$2 ⵜⴰⵙⵏⴰ ⵏⵙ ⵏ ⵓⵙⵏⵓⵎⵎⵍ] ⵜⵡⴰⵙⵎⴰⵍ ⵙⴰⴷⵓ.",
+       "filepage-nofile": "ⵓⵔ ⵓⴼⴰⵢⵍⵓ ⵙ ⵢⵉⵙⵎ ⴰ.",
+       "filerevert-comment": "ⵜⴰⵎⵏⵜⵉⵍⵜ:",
+       "filedelete": "ⴽⴽⵙ $1",
+       "filedelete-legend": "ⴽⴽⵙ ⴰⴼⴰⵢⵍⵓ",
+       "filedelete-comment": "ⵜⴰⵎⵏⵜⵉⵍⵜ:",
+       "filedelete-submit": "ⴽⴽⵙ",
+       "download": "ⴰⴳⵎ",
+       "randompage": "ⵜⴰⵙⵏⴰ ⵜⴰⴷⵀⵎⴰⵙⵜ",
+       "statistics": "ⴰⵙⵏⵎⴽⵜⴰ",
+       "statistics-pages": "ⵜⴰⵙⵏⵉⵡⵉⵏ",
+       "brokenredirects-delete": "ⴽⴽⵙ",
+       "withoutinterwiki-legend": "ⴰⵣⵡⵉⵔ",
+       "nbytes": "$1 {{PLURAL:$1|ⴱⴰⵢⵜ|ⵉⴷ ⴱⴰⵢⵜ}}",
+       "nmembers": "$1 {{PLURAL:$1|ⵓⴳⵎⴰⵎ|ⵉⴳⵎⴰⵎⵏ}}",
+       "prefixindex": "ⵎⴰⵕⵕⴰ ⵜⴰⵙⵏⵉⵡⵉⵏ ⴷ ⵓⵣⵡⵉⵔ",
+       "protectedpages-page": "ⵜⴰⵙⵏⴰ",
+       "protectedpages-reason": "ⵜⴰⵎⵏⵜⵉⵍⵜ",
+       "listusers": "ⵓⵎⵓⵖ ⵏ ⵓⵏⵙⵙⵎⵔⵙ",
+       "newpages": "ⵜⴰⵙⵏⵉⵡⵉⵏ ⵜⵉⵎⴰⵢⵏⵓⵜⵉⵏ",
+       "move": "ⵙⵎⵓⵜⵜⵉ",
+       "apisandbox-examples": "ⵉⵎⴷⵢⴰⵜⵏ",
+       "booksources": "ⵉⵙⵓⴳⴰⵎ ⵏ ⵓⴷⵍⵉⵙ",
+       "booksources-search-legend": "ⵔⵣⵓ ⵅⴼ ⵉⴷⵍⵉⵙⵏ ⵏ ⵓⵙⴰⴳⵎ",
+       "booksources-search": "ⵔⵣⵓ",
+       "allpages": "ⵎⴰⵕⵕⴰ ⵜⴰⵙⵏⵉⵡⵉⵏ",
+       "allarticles": "ⵜⴰⵙⵏⵉⵡⵉⵏ ⴰⴽⴽ",
+       "allpagessubmit": "ⴷⴷⵓ",
+       "categories": "ⵉⵙⵎⵉⵍⵏ",
+       "sp-deletedcontributions-contribs": "ⵜⵓⵎⵓⵜⵉⵏ",
+       "listgrouprights-members": "ⵜⴰⵍⴳⴰⵎⵜ ⵏ ⵉⴳⵎⴰⵎⵏ",
+       "watchlist": "ⵜⴰⵍⴳⴰⵎⵜ ⵏ ⵓⴹⴼⴼⵓⵔ",
+       "mywatchlist": "ⵜⴰⵍⴳⴰⵎⵜ ⵏ ⵓⴹⴼⴼⵓⵔ",
+       "watchlistfor2": "ⵉ $1 $2",
+       "watch": "ⵥⵕ",
+       "wlshowlast": "ⵙⴽⵏ $1 ⵜⴰⵙⵔⴰⴳⵉⵏ $2 ⵓⵙⵙⴰⵏ ⵉⵎⴳⴳⵓⵔⴰ",
+       "watchlist-options": "ⵜⵉⴷⵖⵔⵉⵏ ⵏ ⵜⵍⴳⴰⵎⵜ ⵏ ⵓⴹⴼⴼⵓⵔ",
+       "deletepage": "ⴽⴽⵙ ⵜⴰⵙⵏⴰ",
+       "delete-confirm": "ⴽⴽⵙ \"$1\"",
+       "delete-legend": "ⴽⴽⵙ",
+       "deletecomment": "ⵜⴰⵎⵏⵜⵉⵍⵜ:",
+       "rollbacklink": "ⵔⴰⵔ",
+       "changecontentmodel-reason-label": "ⵜⴰⵎⵏⵜⵉⵍⵜ:",
+       "protectedarticle": "ⵉⵎⵃⴹⵉ \"[[$1]]\"",
+       "protectcomment": "ⵜⴰⵎⵏⵜⵉⵍⵜ:",
+       "restriction-edit": "ⵙⵏⴼⵍ",
+       "restriction-move": "ⵙⵎⵓⵜⵜⵉ",
+       "undeletecomment": "ⵜⴰⵎⵏⵜⵉⵍⵜ:",
+       "undelete-show-file-submit": "ⵢⴰⵀ",
+       "namespace": "ⵜⵉⵔⵉⵡⵉⵏ ⵏ ⵉⵙⵎⴰⵡⵏ:",
+       "invert": "ⵙⵏⵅⴰⵍⴼ ⴰⵙⵜⵉ",
+       "blanknamespace": "(ⴰⴷⵙⵍⴰⵏ)",
+       "contributions": "ⵜⵓⵎⵓⵜⵉⵏ ⵏ {{GENDER:$1|ⵓⵏⵙⵙⵎⵔⵙ}}",
+       "mycontris": "ⵜⵓⵎⵓⵜⵉⵏ",
+       "anoncontribs": "ⵜⵓⵎⵓⵜⵉⵏ",
+       "contribsub2": "ⵉ {{GENDER:$3|$1}} ($2)",
+       "sp-contributions-talk": "ⵎⵙⴰⵡⴰⵍ",
+       "sp-contributions-submit": "ⵔⵣⵓ",
+       "whatlinkshere": "ⵎⴰ ⴰⵢⴷ ⵉⵇⵇⵏⵏ ⵙ ⴷⴰ",
+       "whatlinkshere-title": "ⵜⴰⵙⵏⵉⵡⵉⵏ ⵉⵣⴷⵉⵏ ⵖⵔ $1",
+       "whatlinkshere-page": "ⵜⴰⵙⵏⴰ:",
+       "linkshere": "ⵜⴰⵙⵏⵉⵡⵉⵏ ⴰⴷ ⵣⴷⵉⵏ ⵖⵔ <strong>[[:$1]]</strong>:",
+       "nolinkshere": "ⵓⵔ ⵍⵍⵉⵏ ⵜⴰⵙⵏⵉⵡⵉⵏ ⵉⵣⴷⵉⵏ ⵖⵔ <strong>[[:$1]]</strong>",
+       "isimage": "ⴰⵙⵖⵓⵏ ⵏ ⵓⴼⴰⵢⵍⵓ",
+       "whatlinkshere-links": "← ⵉⵙⵖⵓⵏⴻⵏ",
+       "whatlinkshere-hidelinks": "$1 ⵉⵙⵖⵓⵏⴻⵏ",
+       "whatlinkshere-hideimages": "$1 ⵉⵣⴷⴰⵢⵏ ⵖⵔ ⵓⴼⵉⵍⵢⵓ",
+       "whatlinkshere-filters": "ⵜⵉⵙⵜⵜⴰⵢⵉⵏ",
+       "ipbreason": "ⵜⴰⵎⵏⵜⵉⵍⵜ:",
+       "blocklist-reason": "ⵜⴰⵎⵏⵜⵉⵍⵜ",
+       "blocklink": "ⴳⴷⵍ",
+       "contribslink": "ⵜⵓⵎⵓⵜⵉⵏ",
+       "movereason": "ⵜⴰⵎⵏⵜⵉⵍⵜ:",
+       "delete_and_move_confirm": "ⵢⴰⵀ, ⴽⴽⵙ ⵜⴰⵙⵏⴰ",
+       "allmessagesname": "ⵉⵙⵎ",
+       "allmessages-language": "ⵜⵓⵜⵍⴰⵢⵜ:",
+       "allmessages-filter-translate": "ⵙⵙⵓⵖⵍ",
+       "thumbnail-more": "ⵙⵙⵉⵎⵖⵓⵔ",
+       "tooltip-pt-userpage": "ⵜⴰⵙⵏⴰ ⵏ ⵓⵙⵎⵔⴰⵙ {{GENDER:|ⵏⵏⴽ|ⵏⵏⵎ}}",
+       "tooltip-pt-mytalk": "ⵜⴰⵙⵏⴰ {{GENDER:|ⵏⵏⴽ|ⵏⵏⵎ}} ⵏ ⵓⵎⵙⴰⵡⴰⵍ",
+       "tooltip-pt-preferences": "ⵉⵙⵎⵏⵢⵉⴼⵏ {{GENDER:|ⵏⵏⴽ|ⵏⵏⵎ}}",
+       "tooltip-pt-mycontris": "ⵢⴰⵜ ⵜⵍⴳⴰⵎⵜ ⵏ ⵜⵓⵎⵓⵜⵉⵏ {{GENDER:|ⵏⵏⴽ|ⵏⵏⵎ}}",
+       "tooltip-pt-login": "ⴰⵔⴽ ⵏⵙⵙⵔⵇⴰⴱ ⴰⵜⴽⵛⵎⵜ; ⵎⴰⵛ ⵓⵔ ⵉⴳⵉ ⵓⵣⵓⵛⵍⵍ",
+       "tooltip-pt-logout": "ⴼⴼⵖ",
+       "tooltip-pt-createaccount": "ⴰⵔⴽ ⵏⵙⵙⵔⵇⴰⴱ ⴰ ⵜⵣⵎⵣⵣⵓⵜ ⴰⵎⵉⴹⴰⵏ ⴷ ⴰ ⵜⴽⵛⵎⵜ;  ⵎⴰⵛ ⵓⵔ ⵉⴳⵉ ⵓⵣⵓⵛⵍⵍ",
+       "tooltip-ca-talk": "ⴰⵎⵙⴰⵡⴰⵍ ⵖⴼ ⵜⵓⵎⴰⵢⵜ ⵏ ⵜⴰⵙⵏⴰ",
+       "tooltip-ca-edit": "ⵙⵏⴼⵍ ⵜⴰⵙⵏⴰ ⴰ",
+       "tooltip-ca-addsection": "ⵙⵏⵜⵉ ⵢⴰⵜ ⵜⴳⵣⵎⵉ ⵜⴰⵎⴰⵢⵏⵓⵜ",
+       "tooltip-ca-viewsource": "ⵜⴰⵙⵏⴰ ⴰ ⵜⴻⵜⵜⵢⴰⵔⴰⵢ.\nⵜⵣⵎⵔⴷ ⴰ ⵜⵥⵕⴷ ⴰⵙⴰⴳⴰⵎ",
+       "tooltip-ca-history": "ⴰⵎⵣⵔⴰⵢ ⵏ ⵜⵙⵏⵖⴰⵍ ⵏ ⵜⴰⵙⵏⴰ ⴰⴷ",
+       "tooltip-ca-protect": "ⵢⴰⵔⴰⵢ ⵜⴰⵙⵏⴰ ⴰ",
+       "tooltip-ca-delete": "ⴽⴽⵙ ⵜⴰⵙⵏⴰ ⴰⴷ",
+       "tooltip-ca-move": "ⵙⵎⵓⵜⵜⵉ ⵜⴰⵙⵏⴰ ⴰ",
+       "tooltip-ca-watch": "ⵔⵏⵓ ⵜⴰⵙⵏⴰ ⴰ ⵉ ⵜⵍⴳⴰⵎⵜ ⵏ ⵓⴹⴼⴼⵓⵔ {{GENDER:|ⵏⵏⴽ|ⵏⵏⵎ}}",
+       "tooltip-ca-unwatch": "ⴽⴽⵙ ⵜⴰⵙⵏⴰ ⴰ ⵙⴳ ⵓⵎⵓⵖ ⵏ ⵓⵙⵎⵓⵇⵇⵍ  {{GENDER:|ⵏⵏⴽ|ⵏⵏⵎ}}",
+       "tooltip-search": "ⵔⵣⵓ ⴳ {{SITENAME}}",
+       "tooltip-search-go": "ⴷⴷⵓ ⵙ ⵜⴰⵙⵏⴰ ⵎⵎ ⵢⵉⵙⵎ ⴰ ⵎⴽ ⵜⵍⵍⴰ",
+       "tooltip-search-fulltext": "ⵔⵣⵓ ⵜⴰⵙⵏⵉⵡⵉⵏ ⴳ ⵉⵍⵍⴰ ⵓⴹⵕⵉⵚ ⴰ",
+       "tooltip-p-logo": "ⴷⴷⵓ ⵖⵔ ⵓⵙⵏⵓⴱⴳ",
+       "tooltip-n-mainpage": "ⴽⴽ ⴷ ⵜⴰⵙⵏⴰ ⵏ ⵓⵙⵏⵓⴱⴳ",
+       "tooltip-n-mainpage-description": "ⴽⴽ ⴷ ⵜⴰⵙⵏⴰ ⵏ ⵓⵙⵏⵓⴱⴳ",
+       "tooltip-n-portal": "ⵖⴼ ⵓⵙⵏⴼⴰⵔ, ⵎⴰ ⴰⵢⴷ ⵜⵣⵎⵔⴷ ⴰⴷ ⵜ ⵜⴳⴷ, ⵎⴰⵏⵉ ⴳ ⵔⴰⴷ ⵜⴰⴼⴷ ⵜⵉⵖⴰⵡⵙⵉⵡⵉⵏ",
+       "tooltip-n-currentevents": "ⴰⴼ ⵓⴳⴳⴰⵔ ⵏ ⵉⵏⵖⵎⵉⵙⵏ ⵖⴼ ⵜⵉⵎⵙⴰⵔⵉⵏ ⵜⵉⵎⵉⵔⴰⵏⵉⵏ",
+       "tooltip-n-recentchanges": "ⵢⴰⵜ ⵜⵍⴳⴰⵎⵜ ⵏ ⵉⵙⵏⴼⵍⵏ ⵉⵎⴳⴳⵓⵔⴰ ⴳ ⵡⵉⴽⵉ",
+       "tooltip-n-randompage": "ⵣⴷⵎ ⵜⴰⵙⵏⴰ ⵜⴰⴷⵀⵎⴰⵙⵜ",
+       "tooltip-n-help": "The place to find out",
+       "tooltip-t-whatlinkshere": "ⵓⵎⵓⵖ ⵏ ⵎⴰⵕⵕ ⵜⴰⵙⵏⵉⵡⵉⵏ ⵏ ⵡⵉⴽⵉ ⵉⵣⴷⵉⵏ ⵉ ⴷⴰ",
+       "tooltip-t-recentchangeslinked": "ⵉⵙⵏⴼⵍⵏ ⵉⵏⴳⴳⵓⵔⴰ ⴳ ⵜⴰⵙⵏⵉⵡⵉⵏ ⵉⵣⴷⵉⵏ ⵖⵔ ⵜⴰⵙⵏⴰ ⴰ",
+       "tooltip-t-contributions": "ⵢⴰⵜ ⵜⵍⴳⴰⵎⵜ ⵏ ⵜⵓⵎⵓⵜⵉⵏ ⵏ {{GENDER:$1|ⵓⵙⵎⵔⴰⵙ ⴰ}}",
+       "tooltip-t-emailuser": "ⴰⵣⵏ ⵢⴰⵏ ⵉⵎⴰⵢⵍ ⵉ {{GENDER:$1|ⴰⵏⵙⵙⵎⵔⵙ ⴰ|ⵜⴰⵏⵙⵙⵎⵔⵙⵜ ⴰ}}",
+       "tooltip-t-upload": "ⵙⴽⵜⵔ ⵉⴼⵓⵢⵍⴰ",
+       "tooltip-t-specialpages": "ⵢⴰⵜ ⵜⵍⴳⴰⵎⵜ ⵏ ⵜⴰⵙⵏⵉⵡⵉⵏ ⴰⴽⴽ ⵥⵍⵉⵏⵉⵏ",
+       "tooltip-t-print": "ⵜⵓⵏⵖⵉⵍⵜ ⵉⵜⵜⵡⴰⵙⵉⴳⴳⵣⵏ ⵏ ⵜⴰⵙⵏⴰ ⴰⴷ",
+       "tooltip-t-permalink": "ⴰⵙⵖⵓⵏ ⴰⵎⵖⵍⴰⵍ ⵏ ⵜⵓⵏⵖⵉⵍⵜ ⴰⴷ ⵏ ⵜⴰⵙⵏⴰ",
+       "tooltip-ca-nstab-main": "ⵙⴽⵏ ⵜⵓⵎⴰⵢⵜ ⵏ ⵜⴰⵙⵏⴰ",
+       "tooltip-ca-nstab-user": "ⵥⵔ ⵜⴰⵙⵏⴰ ⵏ ⵓⵙⵎⵔⴰⵙ",
+       "tooltip-ca-nstab-special": "ⵜⴰⴷ ⵜⴳⴰ ⵢⴰⵜ ⵜⴰⵙⵏⴰ ⵉⵣⵍⵉⵏ, ⵓⵔ ⵢⴰⵍⵍⴼⵓⵙ ⴰⴷ ⵜⴻⵜⵜⵓⵙⵏⴼⵍ",
+       "tooltip-ca-nstab-project": "ⵥⵕ ⵜⴰⵙⵏⴰ ⵏ ⵓⵙⵏⵜⵉ",
+       "tooltip-ca-nstab-image": "ⵙⴽⵏ ⵜⴰⵙⵏⴰ ⵏ ⵓⴼⴰⵢⵍⵓ",
+       "tooltip-ca-nstab-template": "ⵥⵔ ⵜⴰⵙⴽⴽⴰ",
+       "tooltip-ca-nstab-category": "ⵙⴽⵏ ⴰⵏⴰⵡ ⵏ ⵜⴰⵙⵏⴰ",
+       "tooltip-save": "ⵃⴹⵓ ⵉⵙⵏⴼⴰⵍ ⵏⵏⴽ",
+       "tooltip-diff": "ⵙⴽⵏ ⵎⴰⵏ ⵉⵙⵏⴼⴰⵍ ⵜⴳⴳⵉⴷ ⵉ ⵓⴹⵔⵉⵙ",
+       "tooltip-watch": "ⵔⵏⵓ ⵜⴰⵙⵏⴰ ⴰ ⵉ ⵜⵍⴳⴰⵎⵜ ⵏ ⵓⴹⴼⴼⵓⵔ {{GENDER:|ⵏⵏⴽ|ⵏⵏⵎ}}",
+       "tooltip-rollback": "\"ⵔⴰⵔ\" ⵙⵙⵔ ⴰⵙⵏⴼⵍ ⵏⵖ ⵉⵙⵏⴼⴰⵍⵏ ⵏ ⵓⵎⴰⴷⵔⴰⵡ ⴰⵎⴳⴳⴰⵔⵓ ⴳ ⵜⴰⵙⵏⴰ ⴷ ⵙ ⵢⴰⵏ ⵓⴽⵍⵉⴽ",
+       "pageinfo-header-edits": "ⵙⵏⴼⵍ ⴰⵎⵣⵔⵓⵢ",
+       "pageinfo-display-title": "ⵙⴽⵏ ⴰⵣⵡⵍ",
+       "pageinfo-language": "ⵜⵓⵜⵍⴰⵢⵜ ⵏ ⵜⵙⵏⴰ",
+       "pageinfo-watchers": "ⵓⵟⵟⵓⵏ ⵏ ⵉⵎⵥⵕⴰⵢⵏ ⵏ ⵜⴰⵙⵏⴰ",
+       "pageinfo-firsttime": "ⴰⵙⴰⴽⵓⴷ ⵏ ⵓⵙⵏⴼⵍⵓⵍ ⵏ ⵜⴰⵙⵏⴰ",
+       "pageinfo-lastuser": "ⴰⵎⵙⵏⴼⵍ ⴰⵎⴳⴳⴰⵔⵓ",
+       "pageinfo-lasttime": "ⴰⵙⴰⴽⵓⴷ ⵏ ⵓⵙⵏⴼⵍ ⴰⵎⴳⴳⴰⵔⵓ",
+       "pageinfo-edits": "ⵎⴰⵕⵕⴰ ⵓⵟⵟⵓⵏ ⵏ ⵉⵙⵏⴼⴰⵍⵏ",
+       "pageinfo-hidden-categories": "ⵏⵜⵍ {{PLURAL:$1|ⴰⵙⵎⵉⵍ|ⵉⵙⵎⵉⵍⵏ}}($1)",
+       "pageinfo-toolboxlink": "ⴰⵏⵖⵎⵉⵙ ⵖⴼ ⵜⴰⵙⵏⴰ",
+       "pageinfo-contentpage-yes": "ⵢⴰⵀ",
+       "pageinfo-protect-cascading-yes": "ⵢⴰⵀ",
+       "previousdiff": "ⴰⵙⵏⴼⵍ ⴰⵎⴳⴳⴰⵔⵓ",
+       "nextdiff": "ⴰⵙⵏⴼⵍ ⴰⵎⴰⵢⵏⵓ",
+       "file-info-size": "$1 × $2 ⵉⴷ ⴱⵉⴽⵙⵍ, ⵜⵉⴷⴷⵉ ⵏ ⵓⴼⴰⵢⵍⵓ: $3, ⴰⵏⴰⵡ MIME: $4",
+       "svg-long-desc": "ⴰⴼⴰⵢⵍⵓ SVG, ⵙ $1 × $2 ⵉⴷ ⴱⵉⴽⵙⵍ, ⵜⵉⴷⴷⵉ ⵏ ⵓⴼⴰⵢⵍⵓ: $3",
+       "show-big-image": "ⴰⴼⴰⵢⵍⵓ ⴰⵙⴰⵍⴰⵏ",
+       "show-big-image-preview": "ⵜⴰⵙⵎⴽⵜⴰ ⵏ ⵓⵣⵔⵉⵣⵡⴰⵔ ⴰⴷ: $1.",
+       "show-big-image-other": "{{PLURAL:$2|ⵜⴰⴼⵙⴰⵢⵜ|ⵜⵉⴼⵙⴰⵢⵉⵏ}}: ⵢⴰⴹⵏ $1.",
+       "show-big-image-size": "$1 × $2 ⵉⴷ ⴱⵉⴽⵙⵍ",
+       "metadata": "ⵎⵉⵜⴰⴷⴰⵜⴰ",
+       "metadata-fields": "ⵉⴳⵔⴰⵏ ⵏ ⵎⵉⵜⴰⵉⵙⴼⴽⴰ ⵏ ⵜⵉⵡⵍⴰⴼⵉⵏ ⵏⵏⴰ ⵉⴼⵙⵔⵏ ⴳ ⵜⴱⵔⴰⵜ ⴰⴷ ⵔⴰⴷ ⵉⵍⵉⵏ ⴳ ⵜⴰⵙⵏⴰ ⵏ ⵓⵙⵏⵓⵎⵎⵍ ⵏ ⵜⴰⵡⵍⴰⴼⵜ ⴰⴽⵓⴷ ⵏⵏⴰ ⵉⵎⵓⵏ ⵓⵙⴽⵜⵓⵔ. ⵉⴳⵔⴰⵏ ⵢⴰⴹⵏ ⵔⴰⴷ ⴼⴼⵔⵏ ⵙ ⵓⵎⵕⴰⴹ.\n* make\n* model\n* datetimeoriginal\n* exposuretime\n* fnumber\n* isospeedratings\n* focallength\n* artist\n* copyright\n* imagedescription\n* gpslatitude\n* gpslongitude\n* gpsaltitude",
+       "exif-orientation": "ⴰⵙⵡⴰⵍⴰ",
+       "exif-datetime": "ⴰⵙⴰⴽⵓⴷ ⴷ ⵡⴰⴽⵓⴷ ⵏ ⵓⵙⵏⴼⵍ ⵏ ⵓⴼⴰⵢⵍⵓ",
+       "exif-languagecode": "ⵜⵓⵜⵍⴰⵢⵜ",
+       "exif-dc-contributor": "ⵉⵏⴰⵎⵓⵜⵏ",
+       "exif-iimcategory-edu": "ⴰⵙⴳⵎⵉ",
+       "exif-iimcategory-hth": "ⵜⴰⴷⵓⵙⵉ",
+       "namespacesall": "ⴰⴽⴽⵯ",
+       "monthsall": "ⵎⴰⵕⵕⴰ",
+       "confirm_purge_button": "ⵡⴰⵅⵅⴰ",
+       "confirm-watch-button": "ⵡⴰⵅⵅⴰ",
+       "confirm-unwatch-button": "ⵡⴰⵅⵅⴰ",
+       "confirm-rollback-button": "ⵡⴰⵅⵅⴰ",
+       "imgmultigo": "ⴷⴷⵓ!",
+       "imgmultigoto": "ⴷⴷⵓ ⵖⵔ ⵜⴰⵙⵏⴰ ⴰⴷ $1",
+       "img-lang-default": "(ⵜⵓⵜⵍⴰⵢⵜ ⵙ ⵓⵡⵏⵓⵍ)",
+       "watchlisttools-clear": "ⵙⴼⴹ ⵜⴰⵍⴳⴰⵎⵜ ⵏ ⵓⴹⴼⴼⵓⵔ",
+       "watchlisttools-edit": "ⵥⵕ ⴷ ⵜⵙⵏⴼⵍⴷ ⵜⴰⵍⴳⴰⵎⵜ ⵏ ⵓⴹⴼⴼⵓⵔ",
+       "redirect-submit": "ⴷⴷⵓ",
+       "redirect-file": "ⵉⵙⵎ ⵏ ⵓⴼⴰⵍⵢⵓ",
+       "specialpages": "ⵜⴰⵙⵏⵉⵡⵉⵏ ⵥⵍⵉⵏⵉⵏ",
+       "specialpages-group-pagetools": "ⵉⵎⴰⵙⵙⵏ ⵏ ⵜⴰⵙⵏⴰ",
+       "tag-filter": "ⵙⵜⵉ [[Special:Tags|ⵉⵔⵛⵓⵎⵏ]]:",
+       "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|ⴰⵔⵛⵓⵎ|ⵉⵔⵛⵓⵎⵏ}}]]: $2)",
+       "tags-active-yes": "ⵢⴰⵀ",
+       "tags-active-no": "ⵓⵀⵓ",
+       "tags-delete": "ⴽⴽⵙ",
+       "tags-create-reason": "ⵜⴰⵎⵏⵜⵉⵍⵜ:",
+       "tags-delete-reason": "ⵜⴰⵎⵏⵜⵉⵍⵜ:",
+       "tags-activate-reason": "ⵜⴰⵎⵏⵜⵉⵍⵜ:",
+       "tags-deactivate-reason": "ⵜⴰⵎⵏⵜⵉⵍⵜ:",
+       "tags-edit-reason": "ⵜⴰⵎⵏⵜⵉⵍⵜ:",
+       "compare-page1": "ⵜⴰⵙⵏⴰ 1",
+       "compare-page2": "ⵜⴰⵙⵏⴰ 2",
+       "htmlform-no": "ⵓⵀⵓ",
+       "htmlform-yes": "ⵢⴰⵀ",
+       "logentry-delete-delete": "$1 {{GENDER:$2|ⵉⴽⴽⵙ|ⵜⴽⴽⵙ}} ⵜⴰⵙⵏⴰ $3",
+       "logentry-move-move": "$1 {{GENDER:$2|ⵉⵙⵎⵓⵜⵜⵉ|ⵜⵙⵎⵓⵜⵜⵉ}} ⵜⴰⵙⵏⴰ ⵙⴳ $3 ⵖⵔ $4",
+       "logentry-newusers-create": "{{GENDER:$2|ⵉⵙⵏⴼⵍ ⵓⵏⵙⵙⵎⵔⵙ|ⵜⵙⵏⴼⵍ ⵜⵏⵙⵙⵎⵔⵙⵜ}} $1 ⴰⵎⵉⴹⴰⵏ ⵏⵙ",
+       "logentry-upload-upload": "{{GENDER:$2|ⵉⵙⴽⵜⵔ|ⵜⵙⴽⵜⵔ}} $1 $3",
+       "feedback-thanks-title": "ⵜⴰⵏⵎⵎⵉⵔⵜ!",
+       "searchsuggest-search": "ⵔⵣⵓ ⴳ {{SITENAME}}",
+       "expand_templates_ok": "ⵡⴰⵅⵅⴰ",
+       "pagelanguage": "ⵙⵏⴼⵍ ⵜⵓⵜⵍⴰⵢⵜ ⵏ ⵜⴰⵙⵏⴰ",
+       "pagelang-name": "ⵜⴰⵙⵏⴰ",
+       "pagelang-language": "ⵜⵓⵜⵍⴰⵢⵜ",
+       "right-pagelang": "ⵙⵏⴼⵍ ⵜⵓⵜⵍⴰⵢⵜ ⵏ ⵜⴰⵙⵏⴰ",
+       "authmanager-email-label": "ⵉⵎⴰⵢⵍ",
+       "authmanager-realname-label": "ⵉⵙⵎ ⴰⵎⴷⴷⴰⵜ",
+       "authmanager-realname-help": "ⵉⵙⵎ ⴰⵎⴷⴷⴰⵜ ⵏ ⵓⵏⵙⵙⵔⵎⵙ",
+       "credentialsform-account": "ⵉⵙⵎ ⵏ ⵓⵎⵉⴹⴰⵏ:"
+}
index f97c234..96b9d91 100644 (file)
        "userjspreview": "<strong>请记住您现在只是在测试/预览您的用户JavaScript。它尚未保存!</strong>",
        "sitecsspreview": "<strong>请记住您现在只是在预览该CSS。它尚未保存!</strong>",
        "sitejspreview": "<strong>请记住您现在只是在预览该JavaScript代码。它尚未保存!</strong>",
-       "userinvalidcssjstitle": "<strong>警告:</strong>不存在皮肤“$1”。注意自定义的 .css 和 .js 页要使用小写标题,例如,{{ns:user}}:Foo/vector.css 不同于 {{ns:user}}:Foo/Vector.css。",
+       "userinvalidconfigtitle": "<strong>警告:</strong>不存在皮肤“$1”。注意自定义的 .css 和 .js 页要使用小写标题,例如,{{ns:user}}:Foo/vector.css 不同于 {{ns:user}}:Foo/Vector.css。",
        "updated": "(已更新)",
        "note": "<strong>注意:</strong>",
        "previewnote": "<strong>请记住这只是预览。</strong>您的更改尚未保存!",
        "prefs-files": "文件",
        "prefs-custom-css": "自定义CSS",
        "prefs-custom-js": "自定义JavaScript",
-       "prefs-common-css-js": "所有皮肤共用的CSS/JavaScript:",
+       "prefs-common-config": "所有皮肤共用的CSS/JavaScript:",
        "prefs-reset-intro": "可以通过本页面将系统设置重置为网站默认值。该操作无法撤销。",
        "prefs-emailconfirm-label": "电子邮件确认:",
        "youremail": "电子邮件:",
        "thumbnail_dest_directory": "无法建立目标目录",
        "thumbnail_image-type": "图像类型不支持",
        "thumbnail_gd-library": "未完成的GD设置:功能遗失 $1",
+       "thumbnail_image-size-zero": "图片文件大小看似为0。",
        "thumbnail_image-missing": "文件可能丢失:$1",
        "thumbnail_image-failure-limit": "近期尝试生成此缩略图失败太多次($1次或更多)。请稍后再试。",
        "import": "导入页面",
index 212487e..340d5c2 100644 (file)
        "thu": "四",
        "fri": "五",
        "sat": "六",
-       "january": "月",
+       "january": "1月",
        "february": "二月",
        "march": "三月",
        "april": "四月",
        "september": "九月",
        "october": "十月",
        "november": "十一月",
-       "december": "十二月",
+       "december": "12月",
        "january-gen": "一月",
        "february-gen": "二月",
        "march-gen": "三月",
        "october-gen": "十月",
        "november-gen": "十一月",
        "december-gen": "十二月",
-       "jan": "1 月",
-       "feb": "2 月",
-       "mar": "3 月",
-       "apr": "4 月",
-       "may": "5 月",
-       "jun": "6 月",
-       "jul": "7 月",
-       "aug": "8 月",
-       "sep": "9 月",
-       "oct": "10 月",
-       "nov": "11 月",
-       "dec": "12 月",
+       "jan": "1月",
+       "feb": "2月",
+       "mar": "3月",
+       "apr": "4月",
+       "may": "5月",
+       "jun": "6月",
+       "jul": "7月",
+       "aug": "8月",
+       "sep": "9月",
+       "oct": "10月",
+       "nov": "11月",
+       "dec": "12月",
        "january-date": "1月$1日",
        "february-date": "2月$1日",
        "march-date": "3月$1日",
        "pool-errorunknown": "不明錯誤",
        "pool-servererror": "無法使用程序計數服務 ($1)。",
        "poolcounter-usage-error": "用法錯誤:$1",
-       "aboutsite": "關於 {{SITENAME}}",
+       "aboutsite": "關於{{SITENAME}}",
        "aboutpage": "Project:關於",
        "copyright": "除非另有註明,否則所有內容皆以 $1 條款授權。",
        "copyrightpage": "{{ns:project}}:版權",
        "site-atom-feed": "$1 的 Atom 來源",
        "page-rss-feed": "\"$1\" 的 RSS 來源",
        "page-atom-feed": "\"$1\" 的 Atom 來源",
-       "red-link-title": "$1 (頁面不存在)",
+       "red-link-title": "$1(頁面不存在)",
        "sort-descending": "降冪排序",
        "sort-ascending": "昇冪排序",
        "nstab-main": "頁面",
        "userjspreview": "<strong>您目前正預覽您的使用者 JavaScript,JavaScript 還尚未儲存!</strong>",
        "sitecsspreview": "<strong>您目前正預覽此 CSS,CSS 還尚未儲存!</strong>",
        "sitejspreview": "<strong>您目前正預覽此 JavaScript,JavaScript 還尚未儲存!</strong>",
-       "userinvalidcssjstitle": "<strong>警告:</strong> 無此外觀樣式 \"$1\"。\n自訂的 .css 和 .js 頁面要使用小寫標題,例如:{{ns:user}}:Foo/vector.css 與 {{ns:user}}:Foo/Vector.css 是不同的。",
+       "userinvalidconfigtitle": "<strong>警告:</strong> 無此外觀樣式 \"$1\"。\n自訂的 .css 和 .js 頁面要使用小寫標題,例如:{{ns:user}}:Foo/vector.css 與 {{ns:user}}:Foo/Vector.css 是不同的。",
        "updated": "(已更新)",
        "note": "<strong>注意:</strong>",
        "previewnote": "<strong>您目前正在預覽,您的變更還尚未儲存!</strong>",
        "prevn-title": "前 $1 筆結果",
        "nextn-title": "後 $1 筆結果",
        "shown-title": "每頁顯示 $1 筆結果",
-       "viewprevnext": "檢視 ($1 {{int:pipe-separator}} $2) ($3)",
+       "viewprevnext": "檢視($1{{int:pipe-separator}} $2)($3)",
        "searchmenu-exists": "<strong>此 Wiki 已有名稱為 \"[[:$1]]\" 的頁面。</strong> {{PLURAL:$2|0=|或請參考其他搜尋結果。}}",
        "searchmenu-new": "<strong>於此 Wiki 建立頁面 \"[[:$1]]\"!</strong>{{PLURAL:$2|0=|或請參考您輸入的條件找到的搜尋結果。|或請參考其他搜尋結果。}}",
        "searchprofile-articles": "內容頁面",
        "prefs-files": "檔案",
        "prefs-custom-css": "自訂 CSS",
        "prefs-custom-js": "自訂 JavaScript",
-       "prefs-common-css-js": "所有外觀共用的 CSS/JavaScript:",
+       "prefs-common-config": "所有外觀共用的 CSS/JavaScript:",
        "prefs-reset-intro": "您可以使用此頁面重設您的偏好設定為網站預設值。\n這個動作將無法復原。",
        "prefs-emailconfirm-label": "電子郵件確認:",
        "youremail": "Email:",
        "newpageletter": "新",
        "boteditletter": "機",
        "number_of_watching_users_pageview": "[$1 位正在監視的使用者]",
-       "rc-change-size-new": "變更後為 $1 位元組",
+       "rc-change-size-new": "變更後為$1位元組",
        "newsectionsummary": "/* $1 */ 新章節",
        "rc-enhanced-expand": "顯示詳細資料",
        "rc-enhanced-hide": "隱藏詳細資料",
        "thumbnail_dest_directory": "無法建立目標目錄",
        "thumbnail_image-type": "不支援的圖片類型",
        "thumbnail_gd-library": "未完成 GD 設定:缺少函數 $1",
+       "thumbnail_image-size-zero": "圖片檔案大小似乎為零。",
        "thumbnail_image-missing": "檔案遺失:$1",
        "thumbnail_image-failure-limit": "最近顯示此縮圖已發生太多次失敗 ($1 次或更多),請稍後再試。",
        "import": "匯入頁面",
        "revdelete-uname-unhid": "取消隱藏使用者名稱",
        "revdelete-restricted": "已套用對管理員的限制",
        "revdelete-unrestricted": "已移除對管理員的限制",
-       "logentry-block-block": "$1 {{GENDER:$2|已封鎖}} {{GENDER:$4|$3}} 期限為 $5 $6",
+       "logentry-block-block": "$1{{GENDER:$2|已封鎖}}{{GENDER:$4|$3}}期限為$5$6",
        "logentry-block-unblock": "$1 {{GENDER:$2|已解除封鎖}} {{GENDER:$4|$3}}",
-       "logentry-block-reblock": "$1 {{GENDER:$2|已變更}} {{GENDER:$4|$3}} 的封鎖設定期限為 $5 $6",
+       "logentry-block-reblock": "$1{{GENDER:$2|已變更}}{{GENDER:$4|$3}}的封鎖設定期限為$5$6",
        "logentry-suppress-block": "$1 {{GENDER:$2|已封鎖}} {{GENDER:$4|$3}} 期限為 $5 $6",
        "logentry-suppress-reblock": "$1 {{GENDER:$2|已變更}} {{GENDER:$4|$3}} 的封鎖設定期限為 $5 $6",
        "logentry-import-upload": "$1 已由檔案上傳{{GENDER:$2|匯入}} $3",
index 94b0a34..affcc83 100644 (file)
@@ -160,6 +160,7 @@ $specialPageAliases = [
        'Withoutinterwiki'          => [ 'Bez_interwiki', 'Stránky_bez_interwiki_odkazů' ],
 ];
 
+// TODO: unify "Strana" with "Stránka"
 $magicWords = [
        'redirect'                  => [ '0', '#PŘESMĚRUJ', '#REDIRECT' ],
        'notoc'                     => [ '0', '__BEZOBSAHU__', '__NOTOC__' ],
@@ -199,6 +200,7 @@ $magicWords = [
        'pagenamee'                 => [ '1', 'NÁZEVSTRANYE', 'PAGENAMEE' ],
        'namespace'                 => [ '1', 'JMENNÝPROSTOR', 'NAMESPACE' ],
        'namespacee'                => [ '1', 'JMENNÝPROSTORE', 'NAMESPACEE' ],
+       'namespacenumber'           => [ '1', 'ČÍSLOJMENNÉHOPROSTORU', 'NAMESPACENUMBER' ],
        'talkspace'                 => [ '1', 'DISKUSNÍPROSTOR', 'TALKSPACE' ],
        'talkspacee'                => [ '1', 'DISKUSNÍPROSTORE', 'TALKSPACEE' ],
        'subjectspace'              => [ '1', 'ČLÁNEKPROSTOR', 'SUBJECTSPACE', 'ARTICLESPACE' ],
@@ -207,6 +209,8 @@ $magicWords = [
        'fullpagenamee'             => [ '1', 'PLNÝNÁZEVSTRANYE', 'FULLPAGENAMEE' ],
        'subpagename'               => [ '1', 'NÁZEVPODSTRANY', 'SUBPAGENAME' ],
        'subpagenamee'              => [ '1', 'NÁZEVPODSTRANYE', 'SUBPAGENAMEE' ],
+       'rootpagename'              => [ '1', 'NÁZEVKOŘENOVÉSTRANY', 'ROOTPAGENAME' ],
+       'rootpagenamee'             => [ '1', 'NÁZEVKOŘENOVÉSTRANYE', 'ROOTPAGENAMEE' ],
        'basepagename'              => [ '1', 'NÁZEVNADSTRANY', 'BASEPAGENAME' ],
        'basepagenamee'             => [ '1', 'NÁZEVNADSTRANYE', 'BASEPAGENAMEE' ],
        'talkpagename'              => [ '1', 'NÁZEVDISKUSE', 'TALKPAGENAME' ],
@@ -227,12 +231,22 @@ $magicWords = [
        'img_lang'                  => [ '1', 'jazyk=$1', 'lang=$1' ],
        'img_page'                  => [ '1', 'strana=$1', 'strana_$1', 'page=$1', 'page $1' ],
        'img_border'                => [ '1', 'okraj', 'border' ],
+       'img_link'                  => [ '1', 'odkaz=$1', 'link=$1' ],
+       'img_class'                 => [ '1', 'třída=$1', 'class=$1' ],
+       'int'                       => [ '0', 'HLÁŠENÍ:', 'INT:' ],
        'sitename'                  => [ '1', 'NÁZEVWEBU', 'SITENAME' ],
        'ns'                        => [ '0', 'JMENNÝPROSTOR:', 'NS:' ],
+       'nse'                       => [ '0', 'JMENNÝPROSTORE:', 'NSE:' ],
        'localurl'                  => [ '0', 'MÍSTNÍURL:', 'LOCALURL:' ],
        'localurle'                 => [ '0', 'MÍSTNÍURLE:', 'LOCALURLE:' ],
+       'articlepath'               => [ '0', 'CESTAKČLÁNKU', 'ARTICLEPATH' ],
+       'pageid'                    => [ '0', 'IDSTRÁNKY', 'PAGEID' ],
        'servername'                => [ '0', 'NÁZEVSERVERU', 'SERVERNAME' ],
+       'scriptpath'                => [ '0', 'CESTAKESKRIPTŮM', 'SCRIPTPATH' ],
+       'stylepath'                 => [ '0', 'CESTAKESTYLŮM', 'STYLEPATH' ],
        'grammar'                   => [ '0', 'SKLOŇUJ:', 'GRAMMAR:' ],
+       'gender'                    => [ '0', 'POHLAVÍ:', 'GENDER:' ],
+       'bidi'                      => [ '0', 'OBASMĚRY:', 'BIDI:' ],
        'notitleconvert'            => [ '0', '__BEZKONVERZENADPISU__', '__NOTITLECONVERT__', '__NOTC__' ],
        'nocontentconvert'          => [ '0', '__BEZKONVERZEOBSAHU__', '__NOCONTENTCONVERT__', '__NOCC__' ],
        'currentweek'               => [ '1', 'AKTUÁLNÍTÝDEN', 'CURRENTWEEK' ],
@@ -243,24 +257,32 @@ $magicWords = [
        'revisionday'               => [ '1', 'DENREVIZE', 'REVISIONDAY' ],
        'revisionday2'              => [ '1', 'DENREVIZE2', 'REVISIONDAY2' ],
        'revisionmonth'             => [ '1', 'MĚSÍCREVIZE', 'REVISIONMONTH' ],
+       'revisionmonth1'            => [ '1', 'MĚSÍCREVIZE1', 'REVISIONMONTH1' ],
        'revisionyear'              => [ '1', 'ROKREVIZE', 'REVISIONYEAR' ],
        'revisiontimestamp'         => [ '1', 'KÓDČASUREVIZE', 'REVISIONTIMESTAMP' ],
+       'revisionuser'              => [ '1', 'AUTORREVIZE', 'REVISIONUSER' ],
+       'revisionsize'              => [ '1', 'VELIKOSTREVIZE', 'REVISIONSIZE' ],
        'plural'                    => [ '0', 'PLURÁL:', 'PLURAL:' ],
        'fullurl'                   => [ '0', 'PLNÉURL:', 'FULLURL:' ],
        'fullurle'                  => [ '0', 'PLNÉURLE:', 'FULLURLE:' ],
+       'canonicalurl'              => [ '0', 'KANONICKÉURL:', 'CANONICALURL:' ],
+       'canonicalurle'             => [ '0', 'KANONICKÉURLE:', 'CANONICALURLE:' ],
        'lcfirst'                   => [ '0', 'PRVNÍMALÉ:', 'LCFIRST:' ],
        'ucfirst'                   => [ '0', 'PRVNÍVELKÉ:', 'UCFIRST:' ],
        'lc'                        => [ '0', 'MALÁ:', 'LC:' ],
        'uc'                        => [ '0', 'VELKÁ:', 'UC:' ],
        'displaytitle'              => [ '1', 'ZOBRAZOVANÝNADPIS', 'DISPLAYTITLE' ],
        'newsectionlink'            => [ '1', '__LINKPŘIDATKOMENTÁŘ__', '__NEWSECTIONLINK__' ],
+       'nonewsectionlink'          => [ '1', '__BEZLINKUPŘIDATKOMENTÁŘ__', '__NONEWSECTIONLINK__' ],
        'currentversion'            => [ '1', 'VERZESOFTWARE', 'CURRENTVERSION' ],
        'urlencode'                 => [ '0', 'ENKÓDOVATURL:', 'URLENCODE:' ],
        'anchorencode'              => [ '0', 'ENKÓDOVATNADPIS', 'ANCHORENCODE' ],
        'currenttimestamp'          => [ '1', 'AKTUÁLNÍKÓDČASU', 'CURRENTTIMESTAMP' ],
        'localtimestamp'            => [ '1', 'MÍSTNÍKÓDČASU', 'LOCALTIMESTAMP' ],
+       'directionmark'             => [ '1', 'ZNAKSMĚRU', 'DIRECTIONMARK', 'DIRMARK' ],
        'language'                  => [ '0', '#JAZYK:', '#LANGUAGE:' ],
        'contentlanguage'           => [ '1', 'JAZYKOBSAHU', 'CONTENTLANGUAGE', 'CONTENTLANG' ],
+       'pagelanguage'              => [ '1', 'JAZYKSTRÁNKY', 'PAGELANGUAGE' ],
        'pagesinnamespace'          => [ '1', 'STRÁNEKVEJMENNÉMPROSTORU:', 'PAGESINNAMESPACE:', 'PAGESINNS:' ],
        'numberofadmins'            => [ '1', 'POČETSPRÁVCŮ', 'NUMBEROFADMINS' ],
        'formatnum'                 => [ '0', 'FORMÁTUJČÍSLO', 'FORMATNUM' ],
@@ -268,7 +290,7 @@ $magicWords = [
        'padright'                  => [ '0', 'ZAROVNATVPRAVO', 'PADRIGHT' ],
        'special'                   => [ '0', 'speciální', 'special' ],
        'defaultsort'               => [ '1', 'KLÍČŘAZENÍ:', 'DEFAULTSORT:', 'DEFAULTSORTKEY:', 'DEFAULTCATEGORYSORT:' ],
-       'filepath'                  => [ '0', 'CESTAKSOUBORU', 'FILEPATH:' ],
+       'filepath'                  => [ '0', 'CESTAKSOUBORU:', 'FILEPATH:' ],
        'tag'                       => [ '0', 'značka', 'tag' ],
        'hiddencat'                 => [ '1', '__SKRÝTKAT__', '__HIDDENCAT__' ],
        'pagesincategory'           => [ '1', 'STRÁNEKVKATEGORII', 'STRÁNEKVKAT', 'PAGESINCATEGORY', 'PAGESINCAT' ],
@@ -276,7 +298,13 @@ $magicWords = [
        'index'                     => [ '1', '__INDEXOVAT__', '__INDEX__' ],
        'noindex'                   => [ '1', '__NEINDEXOVAT__', '__NOINDEX__' ],
        'staticredirect'            => [ '1', '__STATICKÉPŘESMĚROVÁNÍ__', '__STATICREDIRECT__' ],
+       'numberingroup'             => [ '1', 'POČETVESKUPINĚ', 'NUMBERINGROUP', 'NUMINGROUP' ],
        'protectionlevel'           => [ '1', 'ÚROVEŇZAMČENÍ', 'PROTECTIONLEVEL' ],
+       'protectionexpiry'          => [ '1', 'VYPRŠENÍZAMČENÍ', 'PROTECTIONEXPIRY' ],
+       'formatdate'                => [ '0', 'formátujdatum', 'formatdate', 'dateformat' ],
+       'pagesincategory_all'       => [ '0', 'vše', 'all' ],
+       'pagesincategory_pages'     => [ '0', 'stránky', 'pages' ],
+       'pagesincategory_subcats'   => [ '0', 'kategorie', 'subcats' ],
        'pagesincategory_files'     => [ '0', 'soubory', 'files' ],
 ];
 
diff --git a/languages/messages/MessagesEs_formal.php b/languages/messages/MessagesEs_formal.php
new file mode 100644 (file)
index 0000000..2c5d727
--- /dev/null
@@ -0,0 +1,11 @@
+<?php
+/** Spanish (formal) (español (formal))
+ *
+ * To improve a translation please visit https://translatewiki.net
+ *
+ * @ingroup Language
+ * @file
+ *
+ */
+
+$fallback = 'es';
index 7e4bf7c..1778a79 100644 (file)
@@ -1011,7 +1011,7 @@ abstract class Maintenance {
 
                // ... append parameters ...
                if ( $this->mParams ) {
-                       $output .= " [--" . implode( array_keys( $this->mParams ), "|--" ) . "]";
+                       $output .= " [--" . implode( "|--", array_keys( $this->mParams ) ) . "]";
                }
 
                // ... and append arguments.
@@ -1294,7 +1294,7 @@ abstract class Maintenance {
         * This function has the same parameters as wfGetDB()
         *
         * @param int $db DB index (DB_REPLICA/DB_MASTER)
-        * @param array $groups default: empty array
+        * @param string|string[] $groups default: empty array
         * @param string|bool $wiki default: current wiki
         * @return IMaintainableDatabase
         */
diff --git a/maintenance/archives/patch-actor-table.sql b/maintenance/archives/patch-actor-table.sql
new file mode 100644 (file)
index 0000000..fdd95e8
--- /dev/null
@@ -0,0 +1,57 @@
+--
+-- patch-actor-table.sql
+--
+-- T167246. Add an `actor` table and various columns (and temporary tables) to reference it.
+
+CREATE TABLE /*_*/actor (
+  actor_id bigint unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
+  actor_user int unsigned,
+  actor_name varchar(255) binary NOT NULL
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/actor_user ON /*_*/actor (actor_user);
+CREATE UNIQUE INDEX /*i*/actor_name ON /*_*/actor (actor_name);
+
+CREATE TABLE /*_*/revision_actor_temp (
+  revactor_rev int unsigned NOT NULL,
+  revactor_actor bigint unsigned NOT NULL,
+  revactor_timestamp binary(14) NOT NULL default '',
+  revactor_page int unsigned NOT NULL,
+  PRIMARY KEY (revactor_rev, revactor_actor)
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/revactor_rev ON /*_*/revision_actor_temp (revactor_rev);
+CREATE INDEX /*i*/actor_timestamp ON /*_*/revision_actor_temp (revactor_actor,revactor_timestamp);
+CREATE INDEX /*i*/page_actor_timestamp ON /*_*/revision_actor_temp (revactor_page,revactor_actor,revactor_timestamp);
+
+ALTER TABLE /*_*/archive
+  ALTER COLUMN ar_user_text SET DEFAULT '',
+  ADD COLUMN ar_actor bigint unsigned NOT NULL DEFAULT 0 AFTER ar_user_text;
+CREATE INDEX /*i*/ar_actor_timestamp ON /*_*/archive (ar_actor,ar_timestamp);
+
+ALTER TABLE /*_*/ipblocks
+  ADD COLUMN ipb_by_actor bigint unsigned NOT NULL DEFAULT 0 AFTER ipb_by_text;
+
+ALTER TABLE /*_*/image
+  ALTER COLUMN img_user_text SET DEFAULT '',
+  ADD COLUMN img_actor bigint unsigned NOT NULL DEFAULT 0 AFTER img_user_text;
+CREATE INDEX /*i*/img_actor_timestamp ON /*_*/image (img_actor, img_timestamp);
+
+ALTER TABLE /*_*/oldimage
+  ALTER COLUMN oi_user_text SET DEFAULT '',
+  ADD COLUMN oi_actor bigint unsigned NOT NULL DEFAULT 0 AFTER oi_user_text;
+CREATE INDEX /*i*/oi_actor_timestamp ON /*_*/oldimage (oi_actor,oi_timestamp);
+
+ALTER TABLE /*_*/filearchive
+  ALTER COLUMN fa_user_text SET DEFAULT '',
+  ADD COLUMN fa_actor bigint unsigned NOT NULL DEFAULT 0 AFTER fa_user_text;
+CREATE INDEX /*i*/fa_actor_timestamp ON /*_*/filearchive (fa_actor,fa_timestamp);
+
+ALTER TABLE /*_*/recentchanges
+  ALTER COLUMN rc_user_text SET DEFAULT '',
+  ADD COLUMN rc_actor bigint unsigned NOT NULL DEFAULT 0 AFTER rc_user_text;
+CREATE INDEX /*i*/rc_ns_actor ON /*_*/recentchanges (rc_namespace, rc_actor);
+CREATE INDEX /*i*/rc_actor ON /*_*/recentchanges (rc_actor, rc_timestamp);
+
+ALTER TABLE /*_*/logging
+  ADD COLUMN log_actor bigint unsigned NOT NULL DEFAULT 0 AFTER log_user_text;
+CREATE INDEX /*i*/actor_time ON /*_*/logging (log_actor, log_timestamp);
+CREATE INDEX /*i*/log_actor_type_time ON /*_*/logging (log_actor, log_type, log_timestamp);
index f001236..55ffcb8 100644 (file)
@@ -43,7 +43,7 @@ class CheckLess extends Maintenance {
                self::requireTestsAutoloader();
 
                // If phpunit isn't available by autoloader try pulling it in
-               if ( !class_exists( 'PHPUnit_Framework_TestCase' ) ) {
+               if ( !class_exists( 'PHPUnit\\Framework\\TestCase' ) ) {
                        require_once 'PHPUnit/Autoload.php';
                }
 
index 3763d3b..b2fdf2f 100644 (file)
@@ -110,7 +110,7 @@ class CleanupUsersWithNoId extends LoggedUpdateMaintenance {
                                $next = "$field > $value OR $field = $value AND ($next)";
                        }
                }
-               $display = join( ' ', array_reverse( $display ) );
+               $display = implode( ' ', array_reverse( $display ) );
                return [ $next, $display ];
        }
 
index c52013b..326073f 100644 (file)
@@ -43,13 +43,19 @@ class DeleteDefaultMessages extends Maintenance {
 
                $this->output( "Checking existence of old default messages..." );
                $dbr = $this->getDB( DB_REPLICA );
-               $res = $dbr->select( [ 'page', 'revision' ],
+
+               $actorQuery = ActorMigration::newMigration()
+                       ->getWhere( $dbr, 'rev_user', User::newFromName( 'MediaWiki default' ) );
+               $res = $dbr->select(
+                       [ 'page', 'revision' ] + $actorQuery['tables'],
                        [ 'page_namespace', 'page_title' ],
                        [
                                'page_namespace' => NS_MEDIAWIKI,
-                               'page_latest=rev_id',
-                               'rev_user_text' => 'MediaWiki default',
-                       ]
+                               $actorQuery['conds'],
+                       ],
+                       __METHOD__,
+                       [],
+                       [ 'revision' => [ 'JOIN', 'page_latest=rev_id' ] ] + $actorQuery['joins']
                );
 
                if ( $dbr->numRows( $res ) == 0 ) {
index 20d5c2f..9849dc5 100644 (file)
@@ -41,7 +41,6 @@ class DeleteSelfExternals extends Maintenance {
                $this->output( "Deleting self externals from $wgServer\n" );
                $db = $this->getDB( DB_MASTER );
                while ( 1 ) {
-                       wfWaitForSlaves();
                        $this->commitTransaction( $db, __METHOD__ );
                        $q = $db->limitResult( "DELETE /* deleteSelfExternals */ FROM externallinks WHERE el_to"
                                . $db->buildLike( $wgServer . '/', $db->anyString() ), $this->getBatchSize() );
index 57fd91b..e1b1682 100644 (file)
@@ -59,11 +59,15 @@ class FixUserRegistration extends Maintenance {
                                $id = $row->user_id;
                                $lastId = $id;
                                // Get first edit time
+                               $actorQuery = ActorMigration::newMigration()
+                                       ->getWhere( $dbw, 'rev_user', User::newFromId( $id ) );
                                $timestamp = $dbw->selectField(
-                                       'revision',
+                                       [ 'revision' ] + $actorQuery['tables'],
                                        'MIN(rev_timestamp)',
-                                       [ 'rev_user' => $id ],
-                                       __METHOD__
+                                       $actorQuery['conds'],
+                                       __METHOD__,
+                                       [],
+                                       $actorQuery['joins']
                                );
                                // Update
                                if ( $timestamp !== null ) {
index 3c4336f..f7ef7a2 100644 (file)
@@ -38,9 +38,9 @@ in the load balancer, usually indicating a replication environment.' );
        }
 
        public function execute() {
+               global $wgActorTableSchemaMigrationStage;
+
                $dbw = $this->getDB( DB_MASTER );
-               $user = $dbw->tableName( 'user' );
-               $revision = $dbw->tableName( 'revision' );
 
                // Autodetect mode...
                if ( $this->hasOption( 'background' ) ) {
@@ -51,6 +51,17 @@ in the load balancer, usually indicating a replication environment.' );
                        $backgroundMode = wfGetLB()->getServerCount() > 1;
                }
 
+               $actorQuery = ActorMigration::newMigration()->getJoin( 'rev_user' );
+
+               $needSpecialQuery = ( $wgActorTableSchemaMigrationStage !== MIGRATION_OLD &&
+                       $wgActorTableSchemaMigrationStage !== MIGRATION_NEW );
+               if ( $needSpecialQuery ) {
+                       foreach ( $actorQuery['joins'] as &$j ) {
+                               $j[0] = 'JOIN'; // replace LEFT JOIN
+                       }
+                       unset( $j );
+               }
+
                if ( $backgroundMode ) {
                        $this->output( "Using replication-friendly background mode...\n" );
 
@@ -62,15 +73,55 @@ in the load balancer, usually indicating a replication environment.' );
                        $migrated = 0;
                        for ( $min = 0; $min <= $lastUser; $min += $chunkSize ) {
                                $max = $min + $chunkSize;
-                               $result = $dbr->query(
-                                       "SELECT
-                                               user_id,
-                                               COUNT(rev_user) AS user_editcount
-                                       FROM $user
-                                       LEFT OUTER JOIN $revision ON user_id=rev_user
-                                       WHERE user_id > $min AND user_id <= $max
-                                       GROUP BY user_id",
-                                       __METHOD__ );
+
+                               if ( $needSpecialQuery ) {
+                                       // Use separate subqueries to collect counts with the old
+                                       // and new schemas, to avoid having to do whole-table scans.
+                                       $result = $dbr->select(
+                                               [
+                                                       'user',
+                                                       'rev1' => '('
+                                                               . $dbr->selectSQLText(
+                                                                       [ 'revision', 'revision_actor_temp' ],
+                                                                       [ 'rev_user', 'ct' => 'COUNT(*)' ],
+                                                                       [
+                                                                               "rev_user > $min AND rev_user <= $max",
+                                                                               'revactor_rev' => null,
+                                                                       ],
+                                                                       __METHOD__,
+                                                                       [ 'GROUP BY' => 'rev_user' ],
+                                                                       [ 'revision_actor_temp' => [ 'LEFT JOIN', 'revactor_rev = rev_id' ] ]
+                                                               ) . ')',
+                                                       'rev2' => '('
+                                                               . $dbr->selectSQLText(
+                                                                       [ 'revision' ] + $actorQuery['tables'],
+                                                                       [ 'actor_user', 'ct' => 'COUNT(*)' ],
+                                                                       "actor_user > $min AND actor_user <= $max",
+                                                                       __METHOD__,
+                                                                       [ 'GROUP BY' => 'actor_user' ],
+                                                                       $actorQuery['joins']
+                                                               ) . ')',
+                                               ],
+                                               [ 'user_id', 'user_editcount' => 'COALESCE(rev1.ct,0) + COALESCE(rev2.ct,0)' ],
+                                               "user_id > $min AND user_id <= $max",
+                                               __METHOD__,
+                                               [],
+                                               [
+                                                       'rev1' => [ 'LEFT JOIN', 'user_id = rev_user' ],
+                                                       'rev2' => [ 'LEFT JOIN', 'user_id = actor_user' ],
+                                               ]
+                                       );
+                               } else {
+                                       $revUser = $actorQuery['fields']['rev_user'];
+                                       $result = $dbr->select(
+                                               [ 'user', 'rev' => [ 'revision' ] + $actorQuery['tables'] ],
+                                               [ 'user_id', 'user_editcount' => "COUNT($revUser)" ],
+                                               "user_id > $min AND user_id <= $max",
+                                               __METHOD__,
+                                               [ 'GROUP BY' => 'user_id' ],
+                                               [ 'rev' => [ 'LEFT JOIN', "user_id = $revUser" ] ] + $actorQuery['joins']
+                                       );
+                               }
 
                                foreach ( $result as $row ) {
                                        $dbw->update( 'user',
@@ -93,8 +144,43 @@ in the load balancer, usually indicating a replication environment.' );
                        }
                } else {
                        $this->output( "Using single-query mode...\n" );
-                       $sql = "UPDATE $user SET user_editcount=(SELECT COUNT(*) FROM $revision WHERE rev_user=user_id)";
-                       $dbw->query( $sql );
+
+                       $user = $dbw->tableName( 'user' );
+                       if ( $needSpecialQuery ) {
+                               $subquery1 = $dbw->selectSQLText(
+                                       [ 'revision', 'revision_actor_temp' ],
+                                       [ 'COUNT(*)' ],
+                                       [
+                                               'user_id = rev_user',
+                                               'revactor_rev' => null,
+                                       ],
+                                       __METHOD__,
+                                       [],
+                                       [ 'revision_actor_temp' => [ 'LEFT JOIN', 'revactor_rev = rev_id' ] ]
+                               );
+                               $subquery2 = $dbw->selectSQLText(
+                                       [ 'revision' ] + $actorQuery['tables'],
+                                       [ 'COUNT(*)' ],
+                                       'user_id = actor_user',
+                                       __METHOD__,
+                                       [],
+                                       $actorQuery['joins']
+                               );
+                               $dbw->query(
+                                       "UPDATE $user SET user_editcount=($subquery1) + ($subquery2)",
+                                       __METHOD__
+                               );
+                       } else {
+                               $subquery = $dbw->selectSQLText(
+                                       [ 'revision' ] + $actorQuery['tables'],
+                                       [ 'COUNT(*)' ],
+                                       [ 'user_id = ' . $actorQuery['fields']['rev_user'] ],
+                                       __METHOD__,
+                                       [],
+                                       $actorQuery['joins']
+                               );
+                               $dbw->query( "UPDATE $user SET user_editcount=($subquery)", __METHOD__ );
+                       }
                }
 
                $this->output( "Done!\n" );
index 66e8d01..bebee85 100644 (file)
@@ -23,6 +23,7 @@
                                        "mw.Title",
                                        "mw.Uri",
                                        "mw.RegExp",
+                                       "mw.String",
                                        "mw.messagePoster.*",
                                        "mw.notification",
                                        "mw.Notification_",
index ad80af5..c8fb629 100644 (file)
@@ -558,7 +558,7 @@ class Languages {
                        }
 
                        if ( isset( $messages[$key] ) ) {
-                               $messages[$key] = implode( $messages[$key], ", " );
+                               $messages[$key] = implode( ", ", $messages[$key] );
                        }
                }
 
index e762494..e81eec0 100644 (file)
@@ -1,3 +1,5 @@
+U+04724䜤|U+09FC1鿁|
+U+04CA4䲤|U+09FD0鿐|
 U+04E07万|U+0842C萬|U+04E07万|
 U+04E0E与|U+08207與|U+04E0E与|
 U+04E11丑|U+04E11丑|U+0919C醜|
@@ -139,8 +141,8 @@ U+06E16渖|U+0700B瀋|
 U+06E38游|U+06E38游|U+0904A遊|
 U+06EAF溯|U+06EAF溯|U+06CDD泝|
 U+06F13漓|U+06F13漓|U+07055灕|
-U+070BC炼|U+07149煉|U+0934A鍊|
 U+07096炖|U+071C9燉|
+U+070BC炼|U+07149煉|U+0934A鍊|
 U+0753B画|U+0756B畫|U+07575畵|
 U+075C7症|U+075C7症|U+07665癥|
 U+07618瘘|U+0763A瘺|U+0763B瘻|
@@ -166,6 +168,7 @@ U+07EFF绿|U+07DA0綠|U+07DD1緑|
 U+07F10缐|U+07DDA線|
 U+07F30缰|U+097C1韁|U+07E6E繮|
 U+07FA1羡|U+07FA8羨|
+U+080C4胄|U+080C4胄|U+05191冑|
 U+080DC胜|U+052DD勝|U+080DC胜|
 U+080E1胡|U+080E1胡|U+09B0D鬍|U+0885A衚|
 U+0810F脏|U+09AD2髒|U+081DF臟|
@@ -213,10 +216,10 @@ U+09528锨|U+06774杴|U+09341鍁|
 U+0954B镋|U+09482钂|U+093B2鎲|
 U+0954C镌|U+0942B鐫|U+093B8鎸|
 U+09562镢|U+09481钁|U+0941D鐝|
+U+095F2闲|U+09592閒|U+09591閑|
 U+09605阅|U+095B1閱|U+095B2閲|
 U+096C7雇|U+096C7雇|U+050F1僱|
 U+096D5雕|U+096D5雕|U+09D70鵰|
-U+095F2闲|U+09592閒|U+09591閑|
 U+09709霉|U+09709霉|U+09EF4黴|
 U+09762面|U+09762面|U+09EB5麵|U+09EAA麪|U+09EAB麫|
 U+0987B须|U+09808須|U+09B1A鬚|
@@ -226,11 +229,185 @@ U+09965饥|U+098E2飢|U+09951饑|
 U+09980馀|U+09918餘|
 U+09986馆|U+09928館|U+08218舘|
 U+09A82骂|U+07F75罵|U+099E1駡|
-U+09CC1鳁|U+09C2E鰮|
 U+09C87鲇|U+09BF0鯰|U+09B8E鮎|
 U+09C9E鲞|U+09BD7鯗|U+09B9D鮝|
+U+09CC1鳁|U+09C2E鰮|
 U+09CC4鳄|U+09C77鱷|U+09C10鰐|
 U+09E21鸡|U+096DE雞|U+09DC4鷄|
 U+09E5A鹚|U+09DBF鶿|U+09DC0鷀|
 U+09EB9麹|U+09EB4麴|
-U+080C4胄|U+080C4胄|U+05191冑|
+U+09FCE鿎|U+040EE䃮|
+U+09FCF鿏|U+04951䥑|
+U+09FD2鿒|U+09FD3鿓|
+U+09FD4鿔|U+093B6鎶|
+U+235CB𣗋|U+06B13欓|
+U+23C97𣲗|U+06E4B湋|
+U+23C98𣲘|U+06F55潕|
+U+23E23𣸣|U+06FC6濆|
+U+24A7D𤩽|U+074DB瓛|
+U+26221𦈡|U+07E7B繻|
+U+2677C𦝼|U+081A2膢|
+U+28408𨐈|U+08F04輄|
+U+28C47𨱇|U+092B6銶|
+U+28C4F𨱏|U+0939D鎝|
+U+28C51𨱑|U+09404鐄|
+U+28C54𨱔|U+0940F鐏|
+U+29F7E𩽾|U+09B9F鮟|
+U+29F83𩾃|U+09BB8鮸|
+U+29F8C𩾌|U+09C47鱇|
+U+2A7DD𪟝|U+052E3勣|
+U+2A8FB𪣻|U+0587F塿|
+U+2AA36𪨶|U+08F0B輋|
+U+2AA58𪩘|U+05DD8巘|
+U+2AFA2𪾢|U+0774D睍|
+U+2B127𫄧|U+07D96綖|
+U+2B128𫄨|U+07D7A絺|
+U+2B137𫄷|U+07E76繶|
+U+2B138𫄸|U+07E81纁|
+U+2B1ED𫇭|U+0853F蔿|
+U+2B300𫌀|U+08940襀|
+U+2B363𫍣|U+08A77詷|
+U+2B36F𫍯|U+08AF4諴|
+U+2B372𫍲|U+08B0F謏|
+U+2B37D𫍽|U+08B5E譞|
+U+2B404𫐄|U+08ECF軏|
+U+2B410𫐐|U+08F17輗|
+U+2B413𫐓|U+08F2E輮|
+U+2B461𫑡|U+09133鄳|
+U+2B4E7𫓧|U+09207鈇|
+U+2B4EF𫓯|U+09288銈|
+U+2B4F6𫓶|U+092D7鋗|
+U+2B4F9𫓹|U+09324錤|
+U+2B50D𫔍|U+09407鐇|
+U+2B50E𫔎|U+0940D鐍|
+U+2B536𫔶|U+095D1闑|
+U+2B5AE𫖮|U+09857顗|
+U+2B5AF𫖯|U+0982B頫|
+U+2B5B3𫖳|U+09835頵|
+U+2B5E7𫗧|U+09917餗|
+U+2B5F4𫗴|U+09958饘|
+U+2B61C𫘜|U+099BC馼|
+U+2B61D𫘝|U+099C3駃|
+U+2B626𫘦|U+09A0A騊|
+U+2B627𫘧|U+09A04騄|
+U+2B628𫘨|U+09A20騠|
+U+2B62A𫘪|U+09A35騵|
+U+2B62C𫘬|U+09A31騱|
+U+2B695𫚕|U+09C24鰤|
+U+2B696𫚖|U+09B86鮆|
+U+2B6AD𫚭|U+09C72鱲|
+U+2B6ED𫛭|U+09D5F鵟|
+U+2B7A9𫞩|U+0748A璊|
+U+2B7C5𫟅|U+07DA1綡|
+U+2B7E6𫟦|U+04875䡵|
+U+2B7F9𫟹|U+09277鉷|
+U+2B7FC𫟼|U+0943D鐽|
+U+2B806𫠆|U+0980D頍|
+U+2B80A𫠊|U+04B84䮄|
+U+2B81C𫠜|U+09F6F齯|
+U+2B8B8𫢸|U+050E4僤|
+U+2BAC7𫫇|U+05641噁|
+U+2BB5F𫭟|U+05878塸|
+U+2BB62𫭢|U+057E8埨|
+U+2BB7C𫭼|U+2144D𡑍|
+U+2BB83𫮃|U+058A0墠|
+U+2BC1B𫰛|U+05A19娙|
+U+2BD77𫵷|U+03823㠣|
+U+2BD87𫶇|U+05D7D嵽|
+U+2BDF7𫷷|U+05EDE廞|
+U+2BE29𫸩|U+05F44彄|
+U+2C029𬀩|U+06690暐|
+U+2C02A𬀪|U+0665B晛|
+U+2C0A9𬂩|U+0689C梜|
+U+2C0CA𬃊|U+06ACD櫍|
+U+2C1D5𬇕|U+06FAB澫|
+U+2C1D9𬇙|U+06D7F浿|
+U+2C1F9𬇹|U+06F0D漍|
+U+2C27C𬉼|U+071B0熰|
+U+2C288𬊈|U+071D6燖|
+U+2C2A4𬊤|U+071C0燀|
+U+2C35B𬍛|U+074C5瓅|
+U+2C361𬍡|U+07497璗|
+U+2C364𬍤|U+07495璕|
+U+2C488𬒈|U+07910礐|
+U+2C497𬒗|U+255FD𥗽|
+U+2C542𬕂|U+07BE2篢|
+U+2C613𬘓|U+07D03紃|
+U+2C618𬘘|U+07D1E紞|
+U+2C621𬘡|U+07D6A絪|
+U+2C629𬘩|U+07D8E綎|
+U+2C62B𬘫|U+07D84綄|
+U+2C62C𬘬|U+07DAA綪|
+U+2C62D𬘭|U+07D9D綝|
+U+2C62F𬘯|U+07DA7綧|
+U+2C642𬙂|U+07E2F縯|
+U+2C64A𬙊|U+07E86纆|
+U+2C64B𬙋|U+07E95纕|
+U+2C72C𬜬|U+08504蔄|
+U+2C72F𬜯|U+044E3䓣|
+U+2C79F𬞟|U+0860B蘋|
+U+2C7C1𬟁|U+08649虉|
+U+2C7FD𬟽|U+08740蝀|
+U+2C8D9𬣙|U+08A0F訏|
+U+2C8DE𬣞|U+08A5D詝|
+U+2C8E1𬣡|U+08AD3諓|
+U+2C8F3𬣳|U+08A6A詪|
+U+2C907𬤇|U+08AF2諲|
+U+2C90A𬤊|U+08ADF諟|
+U+2C91D𬤝|U+08B53譓|
+U+2CA02𬨂|U+08EDD軝|
+U+2CA0E𬨎|U+08F36輶|
+U+2CA7D𬩽|U+09129鄩|
+U+2CAA9𬪩|U+091B2醲|
+U+2CB29𬬩|U+091F4釴|
+U+2CB2D𬬭|U+09300錀|
+U+2CB2E𬬮|U+092F9鋹|
+U+2CB31𬬱|U+091FF釿|
+U+2CB38𬬸|U+09265鉥|
+U+2CB39𬬹|U+0926E鉮|
+U+2CB3B𬬻|U+0946A鑪|
+U+2CB3F𬬿|U+0924A鉊|
+U+2CB41𬭁|U+09267鉧|
+U+2CB4A𬭊|U+289C0𨧀|
+U+2CB4E𬭎|U+092D0鋐|
+U+2CB5A𬭚|U+0931E錞|
+U+2CB5B𬭛|U+28A0F𨨏|
+U+2CB64𬭤|U+0936D鍭|
+U+2CB69𬭩|U+09393鎓|
+U+2CB6C𬭬|U+093CF鏏|
+U+2CB6F𬭯|U+04955䥕|
+U+2CB73𬭳|U+28B4E𨭎|
+U+2CB76𬭶|U+28B46𨭆|
+U+2CB78𬭸|U+093FB鏻|
+U+2CB7C𬭼|U+09429鐩|
+U+2CBB1𬮱|U+095C9闉|
+U+2CBBF𬮿|U+09691隑|
+U+2CBC0𬯀|U+096AE隮|
+U+2CBCE𬯎|U+096A4隤|
+U+2CC56𬱖|U+09814頔|
+U+2CC5F𬱟|U+09820頠|
+U+2CCF5𬳵|U+099D3駓|
+U+2CCF6𬳶|U+099C9駉|
+U+2CCFD𬳽|U+099EA駪|
+U+2CCFF𬳿|U+099FC駼|
+U+2CD02𬴂|U+09A11騑|
+U+2CD03𬴃|U+09A1E騞|
+U+2CD0A𬴊|U+09A4E驎|
+U+2CD8B𬶋|U+09B88鮈|
+U+2CD8D𬶍|U+09B80鮀|
+U+2CD8F𬶏|U+09BA0鮠|
+U+2CD90𬶐|U+09BA1鮡|
+U+2CD9F𬶟|U+09BFB鯻|
+U+2CDA0𬶠|U+09C0A鰊|
+U+2CDA8𬶨|U+09C40鱀|
+U+2CDAD𬶭|U+09C36鰶|
+U+2CDAE𬶮|U+09C5A鱚|
+U+2CDD5𬷕|U+09D4F鵏|
+U+2CE18𬸘|U+09DA0鶠|
+U+2CE1A𬸚|U+09E11鸑|
+U+2CE23𬸣|U+09DB1鶱|
+U+2CE26𬸦|U+09DDF鷟|
+U+2CE2A𬸪|U+09DED鷭|
+U+2CE7C𬹼|U+09F58齘|
+U+2CE88𬺈|U+09F6E齮|
+U+2CE93𬺓|U+09F7C齼|
index b0aa131..2453123 100644 (file)
@@ -75,6 +75,7 @@
 逕寄 径寄
 逕啟 径启
 逕迎 径迎
+逕流 径流
 徵狀 症状
 報帳 报账
 本帳 本账
 促著 促着
 咬著 咬着
 埋著 埋着
+憑著 凭着
+憑著名      凭著名
+憑著作      凭著作
+憑著者      凭著者
 三十六著   三十六着
 走為上著   走为上着
 記憶體      内存
@@ -2673,6 +2678,8 @@ A型肝炎        甲型肝炎
 庫德人      库尔德人
 希拉蕊      希拉里
 希拉莉      希拉里
+文翠珊      特蕾莎·梅
+德蕾莎·梅伊      特蕾莎·梅
 麻薩諸塞   马萨诸塞
 東南亞國家協會  东南亚国家联盟
 獨立國協   独联体
index 59f8c9e..e85a512 100644 (file)
 收錄著      收錄着
 咬著 咬着
 埋著 埋着
+憑著 憑着
+憑著名      憑著名
+憑著作      憑著作
+憑著者      憑著者
 三十六著   三十六着
 走為上著   走為上着
 鬧著 鬧着
@@ -3030,6 +3034,8 @@ IP地址  IP位址
 程序员      程式設計師
 昂山素季   昂山素姬
 翁山蘇姬   昂山素姬
+德蕾莎·梅伊      文翠珊
+特蕾莎·梅 文翠珊
 西洋棋      國際象棋
 隐私 私隱
 隱私 私隱
index 2124eba..2a7f0ac 100644 (file)
 鐵鍊 铁链
 金鍊 金链
 銀鍊 银链
+雪鍊 雪链
 鍊錘 链锤
 洗鍊 洗练
 手鍊 手链
 鍊表 链表
+鍊狀 链状
 反覆 反复
 回覆 回复
 答覆 答复
index c907640..1798437 100644 (file)
@@ -776,6 +776,8 @@ IP地址    IP位址
 連結他      連結他
 昂山素季   翁山蘇姬
 昂山素姬   翁山蘇姬
+特蕾莎·梅 德蕾莎·梅伊
+文翠珊      德蕾莎·梅伊
 国际象棋   西洋棋
 國際象棋   西洋棋
 私隱 隱私
index d4e621d..c2fcb16 100644 (file)
@@ -19,6 +19,7 @@
 陳杰 陳杰
 黃杰 黃杰
 謝杰 謝杰
+博杰普爾   博杰普爾
 寶曆 寶曆
 涂謹申      涂謹申
 涂鴻欽      涂鴻欽
 葉陽后      葉陽后
 后庄 后庄
 後庄 後庄
+龜山庄      龜山庄
+寶山庄      寶山庄
+員山庄      員山庄
+舊庄 舊庄
+庄內 庄內
+庄内地方   庄內地方
 后蒼 后蒼
 馬格里布   馬格里布
 佳里鎮      佳里鎮
 苹果 蘋果
 苹果干      蘋果乾
 苹婆 蘋婆
-龜山庄      龜山庄
-寶山庄      寶山庄
-員山庄      員山庄
 昵称 暱稱
 單于 單于
 鮮于 鮮于
 陈升 陳昇
 尔冬升      爾冬陞
 南宮适      南宮适
-舊庄 舊庄
 拿破仑      拿破崙
 冗余 冗餘
 课余 課餘
index 89719ff..9a57047 100644 (file)
@@ -8,6 +8,7 @@ U+0362D㘭|U+05773坳|
 U+0375B㝛|U+05BBF宿|
 U+03760㝠|U+051A5冥|
 U+03800㠀|U+05C9B岛|
+U+03823㠣|U+2BD77𫵷|
 U+0382F㠯|U+04EE5以|
 U+03836㠶|U+05E06帆|
 U+0384C㡌|U+05E3D帽|
@@ -31,8 +32,10 @@ U+03F1D㼝|U+07897碗|
 U+03F5E㽞|U+07559留|
 U+03FDC㿜|U+0762A瘪|
 U+04039䀹|U+25174𥅴|
+U+040EE䃮|U+09FCE鿎|
 U+04230䈰|U+07B72筲|
 U+04280䊀|U+07CCA糊|
+U+044E3䓣|U+2C72F𬜯|
 U+045EC䗬|U+08702蜂|
 U+0460F䘏|U+06064恤|
 U+04611䘑|U+08109脉|
@@ -42,9 +45,13 @@ U+046E1䛡|U+08BDD话|
 U+04754䝔|U+0737E獾|
 U+04800䠀|U+08E5A蹚|
 U+04836䠶|U+05C04射|
+U+04875䡵|U+2B7E6𫟦|
+U+04951䥑|U+09FCF鿏|
+U+04955䥕|U+2CB6F𬭯|
 U+04965䥥|U+09570镰|
 U+04B03䬃|U+098D2飒|
 U+04B7E䭾|U+09A6E驮|
+U+04B84䮄|U+2B80A𫠊|
 U+04C1F䰟|U+09B42魂|
 U+04CD8䳘|U+09E45鹅|
 U+04D8A䶊|U+08844衄|
@@ -68,6 +75,7 @@ U+0509A傚|U+06548效|
 U+050A2傢|U+05BB6家|
 U+050CA僊|U+04ED9仙|
 U+050CD働|U+052A8动|
+U+050E4僤|U+2B8B8𫢸|
 U+050F1僱|U+096C7雇|
 U+0510C儌|U+04FA5侥|
 U+05138儸|U+03469㑩|U+07F57罗|
@@ -95,6 +103,7 @@ U+052B9効|U+06548效|
 U+052C5勅|U+06555敕|
 U+052CC勌|U+05026倦|
 U+052D1勑|U+06555敕|
+U+052E3勣|U+2A7DD𪟝|
 U+052E6勦|U+0527F剿|
 U+052F3勳|U+052CB勋|
 U+0531F匟|U+07095炕|
@@ -125,6 +134,7 @@ U+05605嘅|U+06168慨|
 U+05611嘑|U+0547C呼|
 U+05620嘠|U+0560E嘎|
 U+05637嘷|U+055E5嗥|
+U+05641噁|U+2BAC7𫫇|
 U+05649噉|U+05556啖|
 U+05690嚐|U+05C1D尝|
 U+056A5嚥|U+054BD咽|
@@ -136,10 +146,14 @@ U+05705圅|U+051FD函|
 U+0577F坿|U+09644附|
 U+0579C垜|U+0579B垛|
 U+057BB垻|U+0575D坝|
+U+057E8埨|U+2BB62𫭢|
 U+0585A塚|U+051A2冢|
 U+0585F塟|U+0846C葬|
 U+05872塲|U+0573A场|
+U+05878塸|U+2BB5F𫭟|
+U+0587F塿|U+2A8FB𪣻|
 U+05896墖|U+05854塔|
+U+058A0墠|U+2BB83𫮃|
 U+058B0墰|U+0575B坛|
 U+058BB墻|U+05899墙|
 U+058CE壎|U+057D9埙|
@@ -155,6 +169,7 @@ U+059C9姉|U+059CA姊|
 U+059D9姙|U+0598A妊|
 U+059EA姪|U+04F84侄|
 U+059F8姸|U+0598D妍|
+U+05A19娙|U+2BC1B𫰛|
 U+05A63婣|U+059FB姻|
 U+05A6C婬|U+06DEB淫|
 U+05A8D媍|U+05987妇|
@@ -185,10 +200,12 @@ U+05CDD峝|U+05CD2峒|
 U+05D11崑|U+06606昆|
 U+05D19崙|U+04ED1仑|
 U+05D57嵗|U+05C81岁|
+U+05D7D嵽|U+2BD87𫶇|
 U+05D83嶃|U+05D2D崭|
 U+05DBD嶽|U+05CB3岳|
 U+05DD6巖|U+05CA9岩|
 U+05DD7巗|U+05CA9岩|
+U+05DD8巘|U+2AA58𪩘|
 U+05DF5巵|U+0536E卮|
 U+05E00帀|U+0531D匝|
 U+05E0B帋|U+07EB8纸|
@@ -201,10 +218,12 @@ U+05EBB庻|U+05EB6庶|
 U+05EBD庽|U+05BD3寓|
 U+05ED0廐|U+053A9厩|
 U+05ED5廕|U+0836B荫|
+U+05EDE廞|U+2BDF7𫷷|
 U+05EF5廵|U+05DE1巡|
 U+05EF9廹|U+08FEB迫|
 U+05EFB廻|U+056DE回|
 U+05F14弔|U+0540A吊|
+U+05F44彄|U+2BE29𫸩|
 U+05F46彆|U+0522B别|
 U+05F6B彫|U+096D5雕|
 U+05F83徃|U+05F80往|
@@ -268,6 +287,8 @@ U+065F9旹|U+065F6时|
 U+065FE旾|U+06625春|
 U+06607昇|U+06607昇|U+05347升|
 U+0662C昬|U+0660F昏|
+U+0665B晛|U+2C02A𬀪|
+U+06690暐|U+2C029𬀩|
 U+066B1暱|U+06635昵|
 U+066E1曡|U+053E0叠|
 U+0671E朞|U+0671F期|
@@ -284,6 +305,7 @@ U+06830栰|U+07B4F筏|
 U+06852桒|U+06851桑|
 U+0686E桮|U+0676F杯|
 U+0687A桺|U+067F3柳|
+U+0689C梜|U+2C0A9𬂩|
 U+068CA棊|U+068CB棋|
 U+06917椗|U+07887碇|
 U+06936椶|U+068D5棕|
@@ -296,7 +318,9 @@ U+069D5槕|U+0684C桌|
 U+06A11樑|U+06881梁|
 U+06A5C橜|U+06A5B橛|
 U+06AC8櫈|U+051F3凳|
+U+06ACD櫍|U+2C0CA𬃊|
 U+06B05欅|U+06989榉|
+U+06B13欓|U+235CB𣗋|
 U+06B1D欝|U+090C1郁|
 U+06B35欵|U+06B3E款|
 U+06B4E歎|U+053F9叹|
@@ -314,23 +338,29 @@ U+06C5A汚|U+06C61污|
 U+06C88瀋|U+06C88沈|U+0700B渖|
 U+06CDD泝|U+06EAF溯|
 U+06D29洩|U+06CC4泄|
+U+06D7F浿|U+2C1D9𬇙|
 U+06D96涖|U+08385莅|
 U+06DD2淒|U+051C4凄|
 U+06DDB淛|U+06D59浙|
 U+06DE8淨|U+051C0净|
 U+06DE9淩|U+051CC凌|
+U+06E4B湋|U+23C97𣲗|
 U+06E67湧|U+06D8C涌|
 U+06E7C湼|U+06D85涅|
 U+06EBC溼|U+06E7F湿|
 U+06ED9滙|U+06C47汇|
 U+06EDB滛|U+06DEB淫|
 U+06EF7滷|U+05364卤|
+U+06F0D漍|U+2C1F9𬇹|
 U+06F44潄|U+06F31漱|
 U+06F55潕|U+23C98𣲘|
+U+06F55潕|U+23C98𣲘|
 U+06F59潙|U+06CA9沩|
 U+06F81澁|U+06DA9涩|
 U+06F90澐|U+06C84沄|
+U+06FAB澫|U+2C1D5𬇕|
 U+06FBE澾|U+03CE0㳠|
+U+06FC6濆|U+23E23𣸣|
 U+06FC7濇|U+06DA9涩|
 U+06FDB濛|U+06FDB濛|U+08499蒙|
 U+06FF6濶|U+09614阔|
@@ -340,8 +370,11 @@ U+070D6烖|U+0707E灾|
 U+07151煑|U+0716E煮|
 U+07157煗|U+06696暖|
 U+07188熈|U+07199熙|
+U+071B0熰|U+2C27C𬉼|
+U+071C0燀|U+2C2A4𬊤|
 U+071C4燄|U+07130焰|
 U+071C9燉|U+07096炖|U+071C9燉|
+U+071D6燖|U+2C288𬊈|
 U+071EC燬|U+06BC1毁|
 U+071FB燻|U+0718F熏|
 U+07217爗|U+070E8烨|
@@ -365,7 +398,12 @@ U+07416琖|U+076CF盏|
 U+07431琱|U+096D5雕|
 U+07447瑇|U+073B3玳|
 U+0746F瑯|U+07405琅|
+U+0748A璊|U+2B7A9𫞩|
+U+07495璕|U+2C364𬍤|
+U+07497璗|U+2C361𬍡|
 U+074A2璢|U+07460瑠|
+U+074C5瓅|U+2C35B𬍛|
+U+074DB瓛|U+24A7D𤩽|
 U+0750E甎|U+07816砖|
 U+07515甕|U+074EE瓮|
 U+07516甖|U+07F42罂|
@@ -404,6 +442,7 @@ U+076CC盌|U+07897碗|
 U+0770E眎|U+089C6视|
 U+0771E眞|U+0771F真|
 U+07721眡|U+089C6视|
+U+0774D睍|U+2AFA2𪾢|
 U+07760睠|U+07737眷|
 U+0776A睪|U+0777E睾|
 U+07787瞇|U+0772F眯|
@@ -417,6 +456,7 @@ U+07881碁|U+068CB棋|
 U+078AA碪|U+07827砧|
 U+078DF磟|U+0788C碌|
 U+07906礆|U+078B1碱|
+U+07910礐|U+2C488𬒈|
 U+0792E礮|U+070AE炮|
 U+07955祕|U+079D8秘|
 U+07958祘|U+07B97算|
@@ -446,6 +486,7 @@ U+07B87箇|U+04E2A个|
 U+07B92箒|U+05E1A帚|
 U+07BA0箠|U+068F0棰|
 U+07BDB篛|U+07BAC箬|
+U+07BE2篢|U+2C542𬕂|
 U+07C11簑|U+084D1蓑|
 U+07C12簒|U+07BE1篡|
 U+07C2E簮|U+07C2A簪|
@@ -457,13 +498,24 @@ U+07C83粃|U+079D5秕|
 U+07CA7粧|U+05986妆|
 U+07CC9糉|U+07CBD粽|
 U+07CF0糰|U+056E2团|
+U+07D03紃|U+2C613𬘓|
+U+07D1E紞|U+2C618𬘘|
 U+07D25紥|U+0624E扎|
 U+07D2E紮|U+0624E扎|
 U+07D43絃|U+05F26弦|
 U+07D4F絏|U+07EC1绁|
+U+07D6A絪|U+2C621𬘡|
 U+07D76絶|U+07EDD绝|
+U+07D7A絺|U+2B128𫄨|
+U+07D84綄|U+2C62B𬘫|
 U+07D89綉|U+07EE3绣|
+U+07D8E綎|U+2C629𬘩|
 U+07D91綑|U+06346捆|
+U+07D96綖|U+2B127𫄧|
+U+07D9D綝|U+2C62D𬘭|
+U+07DA1綡|U+2B7C5𫟅|
+U+07DA7綧|U+2C62F𬘯|
+U+07DAA綪|U+2C62C𬘬|
 U+07DAB綫|U+07EBF线|
 U+07DB5綵|U+05F69彩|U+0433D䌽|
 U+07DD0緐|U+07E41繁|
@@ -474,13 +526,19 @@ U+07DDC緜|U+07EF5绵|
 U+07DE5緥|U+08913褓|
 U+07DFC緼|U+07F0A缊|
 U+07E27縧|U+07EE6绦|
+U+07E2F縯|U+2C642𬙂|
 U+07E34縴|U+07EA4纤|
 U+07E50繐|U+07A57穗|
 U+07E56繖|U+04F1E伞|
 U+07E59繙|U+07FFB翻|
 U+07E66繦|U+08941襁|
 U+07E6E繮|U+07F30缰|
+U+07E76繶|U+2B137𫄷|
+U+07E7B繻|U+26221𦈡|
+U+07E81纁|U+2B138𫄸|
+U+07E86纆|U+2C64A𬙊|
 U+07E94纔|U+0624D才|
+U+07E95纕|U+2C64B𬙋|
 U+07F47罇|U+06A3D樽|
 U+07F4B罋|U+074EE瓮|
 U+07F4E罎|U+0575B坛|
@@ -504,6 +562,7 @@ U+08117脗|U+0543B吻|
 U+08123脣|U+05507唇|
 U+08141腁|U+080FC胼|
 U+08193膓|U+080A0肠|
+U+081A2膢|U+2677C𦝼|
 U+081C8臈|U+0814A腊|
 U+081CB臋|U+081C0臀|
 U+081D5臕|U+08198膘|
@@ -529,9 +588,11 @@ U+08493蒓|U+083BC莼|
 U+084C6蓆|U+05E2D席|
 U+084E1蓡|U+053C2参|
 U+084F4蓴|U+083BC莼|
+U+08504蔄|U+2C72C𬜬|
 U+08514蔔|U+0535C卜|
 U+08515蔕|U+08482蒂|
 U+08518蔘|U+053C2参|
+U+0853F蔿|U+2B1ED𫇭|
 U+0855A蕚|U+0843C萼|
 U+0857F蕿|U+08431萱|
 U+08591薑|U+059DC姜|
@@ -539,14 +600,17 @@ U+085C9藉|U+085C9藉|U+0501F借|
 U+085F4藴|U+08574蕴|
 U+085F7藷|U+085AF薯|
 U+085FC藼|U+08431萱|
+U+0860B蘋|U+2C79F𬞟|
 U+08610蘐|U+08431萱|
 U+08613蘓|U+082CF苏|
 U+08624蘤|U+082B1花|
+U+08649虉|U+2C7C1𬟁|
 U+08698蚘|U+086D4蛔|
 U+086D5蛕|U+086D4蛔|
 U+0870B蜋|U+08782螂|
 U+08716蜖|U+086D4蛔|
 U+08728蜨|U+08776蝶|
+U+08740蝀|U+2C7FD𬟽|
 U+08768蝨|U+08671虱|
 U+0876F蝯|U+0733F猿|
 U+08771蝱|U+0867B虻|
@@ -571,6 +635,7 @@ U+088CC裌|U+088B7袷|
 U+088CF裏|U+091CC里|
 U+088E0裠|U+088D9裙|
 U+0892D褭|U+08885袅|
+U+08940襀|U+2B300𫌀|
 U+08943襃|U+08912褒|
 U+0894D襍|U+06742杂|
 U+08986覆|U+08986覆|U+0590D复|
@@ -580,19 +645,30 @@ U+0898A覊|U+07F81羁|
 U+08994覔|U+089C5觅|
 U+089A9覩|U+07779睹|
 U+089DD觝|U+062B5抵|
+U+08A0F訏|U+2C8D9𬣙|
 U+08A17託|U+06258托|U+08BAC讬|
 U+08A3C証|U+08BC1证|
+U+08A5D詝|U+2C8DE𬣞|
+U+08A6A詪|U+2C8F3𬣳|
 U+08A76詶|U+0916C酬|
+U+08A77詷|U+2B363𫍣|
 U+08A96誖|U+06096悖|
 U+08AAC説|U+08BF4说|
+U+08AD3諓|U+2C8E1𬣡|
+U+08ADF諟|U+2C90A𬤊|
 U+08AEE諮|U+08C18谘|U+054A8咨|
+U+08AF2諲|U+2C907𬤇|
+U+08AF4諴|U+2B36F𫍯|
 U+08B0C謌|U+06B4C歌|
+U+08B0F謏|U+2B372𫍲|
 U+08B21謡|U+08C23谣|
 U+08B2D謭|U+08C2B谫|
 U+08B41譁|U+054D7哗|
 U+08B46譆|U+0563B嘻|
 U+08B4C譌|U+08BB9讹|
+U+08B53譓|U+2C91D𬤝|
 U+08B54譔|U+064B0撰|
+U+08B5E譞|U+2B37D𫍽|
 U+08B5F譟|U+0566A噪|
 U+08B6D譭|U+06BC1毁|
 U+08B81讁|U+08C2A谪|
@@ -626,8 +702,15 @@ U+08E98躘|U+28001𨀁|
 U+08EAD躭|U+0803D耽|
 U+08EB3躳|U+08EAC躬|
 U+08EB6躶|U+088F8裸|
+U+08ECF軏|U+2B404𫐄|
+U+08EDD軝|U+2CA02𬨂|
+U+08F04輄|U+28408𨐈|
+U+08F0B輋|U+2AA36𪨶|
+U+08F17輗|U+2B410𫐐|
 U+08F19輙|U+08F84辄|
 U+08F2D輭|U+08F6F软|
+U+08F2E輮|U+2B413𫐓|
+U+08F36輶|U+2CA0E𬨎|
 U+08F3C輼|U+08F92辒|
 U+08FA0辠|U+07F6A罪|
 U+08FA2辢|U+08FA3辣|
@@ -644,6 +727,8 @@ U+09049遉|U+04FA6侦|
 U+0904A遊|U+06E38游|
 U+09061遡|U+06EAF溯|
 U+0906F遯|U+09041遁|
+U+09129鄩|U+2CA7D𬩽|
+U+09133鄳|U+2B461𫑡|
 U+09156酖|U+09E29鸩|
 U+09167酧|U+0916C酬|
 U+09183醃|U+0814C腌|
@@ -651,36 +736,66 @@ U+09186醆|U+076CF盏|
 U+09195醕|U+09187醇|
 U+091A3醣|U+07CD6糖|
 U+091AF醯|U+09170酰|
+U+091B2醲|U+2CAA9𬪩|
 U+091BB醻|U+0916C酬|
 U+091BC醼|U+05BB4宴|
 U+091E6釦|U+06263扣|
 U+091EC釬|U+0710A焊|
+U+091F4釴|U+2CB29𬬩|
+U+091FF釿|U+2CB31𬬱|
 U+09205鈅|U+094A5钥|
+U+09207鈇|U+2B4E7𫓧|
 U+0920E鈎|U+094A9钩|
 U+09244鉄|U+094C1铁|
 U+09246鉆|U+094BB钻|
+U+0924A鉊|U+2CB3F𬬿|
 U+09262鉢|U+094B5钵|
+U+09265鉥|U+2CB38𬬸|
+U+09267鉧|U+2CB41𬭁|
+U+0926E鉮|U+2CB39𬬹|
+U+09277鉷|U+2B7F9𫟹|
+U+09288銈|U+2B4EF𫓯|
 U+092B2銲|U+0710A焊|
+U+092B6銶|U+28C47𨱇|
+U+092D0鋐|U+2CB4E𬭎|
+U+092D7鋗|U+2B4F6𫓶|
 U+092ED鋭|U+09510锐|
+U+092F9鋹|U+2CB2E𬬮|
+U+09300錀|U+2CB2D𬬭|
+U+0931E錞|U+2CB5A𬭚|
+U+09324錤|U+2B4F9𫓹|
 U+09332録|U+05F55录|
 U+09341鍁|U+09528锨|
 U+0934A鍊|U+070BC炼|U+094FE链|
 U+0936B鍫|U+09539锹|
+U+0936D鍭|U+2CB64𬭤|
 U+09373鍳|U+09274鉴|
 U+0937E鍾|U+0953A锺|U+0949F钟|
 U+0938C鎌|U+09570镰|
+U+09393鎓|U+2CB69𬭩|
 U+09397鎗|U+067AA枪|
 U+0939A鎚|U+09524锤|
+U+0939D鎝|U+28C4F𨱏|
 U+093AD鎭|U+093AE镇|
 U+093AD鎭|U+09547镇|
+U+093B6鎶|U+09FD4鿔|
 U+093B8鎸|U+0954C镌|
 U+093BB鎻|U+09501锁|
+U+093CF鏏|U+2CB6C𬭬|
 U+093DA鏚|U+0621A戚|
+U+093FB鏻|U+2CB78𬭸|
+U+09404鐄|U+28C51𨱑|
+U+09407鐇|U+2B50D𫔍|
+U+0940D鐍|U+2B50E𫔎|
+U+0940F鐏|U+28C54𨱔|
 U+0941D鐝|U+09562镢|
+U+09429鐩|U+2CB7C𬭼|
+U+0943D鐽|U+2B7FC𫟼|
 U+09451鑑|U+09274鉴|
 U+0945A鑚|U+094BB钻|
 U+0945B鑛|U+077FF矿|
 U+09464鑤|U+05228刨|
+U+0946A鑪|U+2CB3B𬬻|
 U+09475鑵|U+07F50罐|
 U+09482钂|U+0954B镋|
 U+09592閒|U+095F2闲|
@@ -689,6 +804,8 @@ U+095A4閤|U+09601阁|U+05408合|
 U+095A7閧|U+054C4哄|
 U+095B2閲|U+09605阅|
 U+095C7闇|U+06697暗|
+U+095C9闉|U+2CBB1𬮱|
+U+095D1闑|U+2B536𫔶|
 U+095DA闚|U+07AA5窥|
 U+095E2闢|U+08F9F辟|
 U+09628阨|U+05384厄|
@@ -701,8 +818,11 @@ U+0967B陻|U+05819堙|
 U+0967F陿|U+072ED狭|
 U+09682隂|U+09634阴|
 U+09684隄|U+05824堤|
+U+09691隑|U+2CBBF𬮿|
 U+09696隖|U+0575E坞|
 U+096A3隣|U+090BB邻|
+U+096A4隤|U+2CBCE𬯎|
+U+096AE隮|U+2CBC0𬯀|
 U+096B7隷|U+096B6隶|
 U+0976D靭|U+097E7韧|
 U+09771靱|U+097E7韧|
@@ -713,12 +833,18 @@ U+097C6韆|U+05343千|
 U+097C8韈|U+0889C袜|
 U+097E4韤|U+0889C袜|
 U+097EE韮|U+097ED韭|
+U+0980D頍|U+2B806𫠆|
+U+09814頔|U+2CC56𬱖|
 U+0981F頟|U+0989D额|
+U+09820頠|U+2CC5F𬱟|
+U+0982B頫|U+2B5AF𫖯|
+U+09835頵|U+2B5B3𫖳|
 U+0983C頼|U+08D56赖|
 U+0983D頽|U+09893颓|
 U+09847顇|U+060B4悴|
 U+0984B顋|U+0816E腮|
 U+09854顔|U+0989C颜|
+U+09857顗|U+2B5AE𫖮|
 U+09858願|U+0613F愿|
 U+09866顦|U+06194憔|
 U+098C3飃|U+098D8飘|
@@ -727,6 +853,7 @@ U+098E4飤|U+09972饲|
 U+098F1飱|U+098E7飧|
 U+09901餁|U+0996A饪|
 U+09908餈|U+07CCD糍|
+U+09917餗|U+2B5E7𫗧|
 U+09918餘|U+09980馀|U+04F59余|
 U+09935餵|U+05582喂|
 U+09939餹|U+07CD6糖|
@@ -734,11 +861,26 @@ U+0993B餻|U+07CD5糕|
 U+0993D餽|U+09988馈|
 U+0994D饍|U+081B3膳|
 U+09951饑|U+09965饥|
+U+09958饘|U+2B5F4𫗴|
 U+0995D饝|U+0998D馍|
+U+099BC馼|U+2B61C𫘜|
+U+099C3駃|U+2B61D𫘝|
 U+099C8駈|U+09A71驱|
+U+099C9駉|U+2CCF6𬳶|
+U+099D3駓|U+2CCF5𬳵|
 U+099E1駡|U+09A82骂|
+U+099EA駪|U+2CCFD𬳽|
+U+099FC駼|U+2CCFF𬳿|
+U+09A04騄|U+2B627𫘧|
+U+09A0A騊|U+2B626𫘦|
 U+09A10騐|U+09A8C验|
+U+09A11騑|U+2CD02𬴂|
+U+09A1E騞|U+2CD03𬴃|
+U+09A20騠|U+2B628𫘨|
 U+09A23騣|U+09B03鬃|
+U+09A31騱|U+2B62C𫘬|
+U+09A35騵|U+2B62A𫘪|
+U+09A4E驎|U+2CD0A𬴊|
 U+09A58驘|U+09AA1骡|
 U+09ABD骽|U+0817F腿|
 U+09ABE骾|U+09CA0鲠|
@@ -750,22 +892,44 @@ U+09B26鬦|U+06597斗|
 U+09B28鬨|U+054C4哄|
 U+09B2A鬪|U+06597斗|
 U+09B30鬰|U+090C1郁|
+U+09B80鮀|U+2CD8D𬶍|
+U+09B86鮆|U+2B696𫚖|
+U+09B88鮈|U+2CD8B𬶋|
 U+09B8E鮎|U+09C87鲇|
 U+09B9D鮝|U+09C9E鲞|
+U+09B9F鮟|U+29F7E𩽾|
+U+09BA0鮠|U+2CD8F𬶏|
+U+09BA1鮡|U+2CD90𬶐|
+U+09BB8鮸|U+29F83𩾃|
 U+09BF0鯰|U+09CB6鲶|U+09C87鲇|
+U+09BFB鯻|U+2CD9F𬶟|
+U+09C0A鰊|U+2CDA0𬶠|
 U+09C10鰐|U+09CC4鳄|
 U+09C1B鰛|U+09CC1鳁|
+U+09C24鰤|U+2B695𫚕|
 U+09C2E鰮|U+09CC1鳁|
+U+09C36鰶|U+2CDAD𬶭|
+U+09C40鱀|U+2CDA8𬶨|
+U+09C47鱇|U+29F8C𩾌|
+U+09C5A鱚|U+2CDAE𬶮|
+U+09C72鱲|U+2B6AD𫚭|
 U+09CEC鳬|U+051EB凫|
 U+09D08鴈|U+096C1雁|
+U+09D4F鵏|U+2CDD5𬷕|
 U+09D5E鵞|U+09E45鹅|
+U+09D5F鵟|U+2B6ED𫛭|
 U+09D70鵰|U+096D5雕|U+05F6B彫|
 U+09D76鵶|U+09E26鸦|
+U+09DA0鶠|U+2CE18𬸘|
+U+09DB1鶱|U+2CE23𬸣|
 U+09DC0鷀|U+09E5A鹚|
 U+09DC4鷄|U+09E21鸡|
+U+09DDF鷟|U+2CE26𬸦|
+U+09DED鷭|U+2CE2A𬸪|
 U+09DF0鷰|U+071D5燕|
 U+09DF4鷴|U+09E47鹇|
 U+09E0E鸎|U+083BA莺|
+U+09E11鸑|U+2CE1A𬸚|
 U+09E7B鹻|U+078B1碱|
 U+09E7C鹼|U+078B1碱|U+07877硷|
 U+09EAA麪|U+09762面|
@@ -778,12 +942,20 @@ U+09F03鼃|U+086D9蛙|
 U+09F07鼇|U+09CCC鳌|
 U+09F08鼈|U+09CD6鳖|
 U+09F15鼕|U+0549A咚|
+U+09F58齘|U+2CE7C𬹼|
 U+09F63齣|U+051FA出|
 U+09F67齧|U+0556E啮|
 U+09F69齩|U+054AC咬|
+U+09F6E齮|U+2CE88𬺈|
+U+09F6F齯|U+2B81C𫠜|
+U+09F7C齼|U+2CE93𬺓|
+U+09FC1鿁|U+04724䜤|
+U+09FD0鿐|U+04CA4䲤|
+U+09FD3鿓|U+09FD2鿒|
 U+20542𠕂|U+0518D再|
 U+20545𠕅|U+0518D再|
 U+207B0𠞰|U+0527F剿|
+U+2144D𡑍|U+2BB7C𫭼|
 U+21681𡚁|U+05F0A弊|
 U+21A25𡨥|U+05BC7寇|
 U+21ED5𡻕|U+05C81岁|
@@ -792,10 +964,15 @@ U+242EE𤋮|U+07199熙|
 U+24A0F𤨏|U+07410琐|
 U+24C48𤱈|U+04EA9亩|
 U+24EA5𤺥|U+07629瘩|
+U+255FD𥗽|U+2C497𬒗|
 U+262B1𦊱|U+06302挂|
 U+26351𦍑|U+07F8C羌|
 U+26548𦕈|U+07707眇|
 U+26D4F𦵏|U+0846C葬|
+U+289C0𨧀|U+2CB4A𬭊|
+U+28A0F𨨏|U+2CB5B𬭛|
+U+28B46𨭆|U+2CB76𬭶|
+U+28B4E𨭎|U+2CB73𬭳|
 U+28F7B𨽻|U+096B6隶|
 U+294D0𩓐|U+08116脖|
 U+295D7𩗗|U+098D3飓|
index 3e0ef0e..d153930 100644 (file)
 乾哭
 乾噦
 乾咽
-乾和
 幹吏
 乾號
-乾颱
 乾卦
 乾剝剝
 乾刻版
 髮絲
 斷髮
 不斷發
+中斷發
 判斷發
 評斷發
 買斷發
 賣斷發
 打斷發
+假發票
 披頭散髮
 髮禁
 世界盃
 蓬鬆鬆
 輕鬆鬆
 鬆鬆地
+鬆耦合
 囉囉囌囌
 囉囌
 骨罈
 田庄英雄
 本庄
 庄司
+街庄
 厂部
 衝量
 衝車
 謝杰
 正杰
 柳斌杰
+修杰楷
+修杰麟
+熊杰
+博杰普爾
 稜鏡
 稜角
 稜台
 注釋
 月面
 路面
-修杰楷
-修杰麟
 學裡
 獄裡
 館裡
 于楓
 于熙珍
 邱于庭
-熊杰
 卜云吉
 黎吉雲
 代表
diff --git a/maintenance/migrateActors.php b/maintenance/migrateActors.php
new file mode 100644 (file)
index 0000000..5b144fc
--- /dev/null
@@ -0,0 +1,550 @@
+<?php
+/**
+ * Migrate actors from pre-1.31 columns to the 'actor' table
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup Maintenance
+ */
+
+use Wikimedia\Rdbms\IDatabase;
+
+require_once __DIR__ . '/Maintenance.php';
+
+/**
+ * Maintenance script that migrates actors from pre-1.31 columns to the
+ * 'actor' table
+ *
+ * @ingroup Maintenance
+ */
+class MigrateActors extends LoggedUpdateMaintenance {
+       public function __construct() {
+               parent::__construct();
+               $this->addDescription( 'Migrates actors from pre-1.31 columns to the \'actor\' table' );
+               $this->setBatchSize( 100 );
+       }
+
+       protected function getUpdateKey() {
+               return __CLASS__;
+       }
+
+       protected function doDBUpdates() {
+               global $wgActorTableSchemaMigrationStage;
+
+               if ( $wgActorTableSchemaMigrationStage < MIGRATION_WRITE_NEW ) {
+                       $this->output(
+                               "...cannot update while \$wgActorTableSchemaMigrationStage < MIGRATION_WRITE_NEW\n"
+                       );
+                       return false;
+               }
+
+               $this->output( "Creating actor entries for all registered users\n" );
+               $end = 0;
+               $dbw = $this->getDB( DB_MASTER );
+               $max = $dbw->selectField( 'user', 'MAX(user_id)', false, __METHOD__ );
+               $count = 0;
+               while ( $end < $max ) {
+                       $start = $end + 1;
+                       $end = min( $start + $this->mBatchSize, $max );
+                       $this->output( "... $start - $end\n" );
+                       $dbw->insertSelect(
+                               'actor',
+                               'user',
+                               [ 'actor_user' => 'user_id', 'actor_name' => 'user_name' ],
+                               [ "user_id >= $start", "user_id <= $end" ],
+                               __METHOD__,
+                               [ 'IGNORE' ],
+                               [ 'ORDER BY' => [ 'user_id' ] ]
+                       );
+                       $count += $dbw->affectedRows();
+                       wfWaitForSlaves();
+               }
+               $this->output( "Completed actor creation, added $count new actor(s)\n" );
+
+               $errors = 0;
+               $errors += $this->migrateToTemp(
+                       'revision', 'rev_id', [ 'revactor_timestamp' => 'rev_timestamp', 'revactor_page' => 'rev_page' ],
+                       'rev_user', 'rev_user_text', 'revactor_rev', 'revactor_actor'
+               );
+               $errors += $this->migrate( 'archive', 'ar_id', 'ar_user', 'ar_user_text', 'ar_actor' );
+               $errors += $this->migrate( 'ipblocks', 'ipb_id', 'ipb_by', 'ipb_by_text', 'ipb_by_actor' );
+               $errors += $this->migrate( 'image', 'img_name', 'img_user', 'img_user_text', 'img_actor' );
+               $errors += $this->migrate(
+                       'oldimage', [ 'oi_name', 'oi_timestamp' ], 'oi_user', 'oi_user_text', 'oi_actor'
+               );
+               $errors += $this->migrate( 'filearchive', 'fa_id', 'fa_user', 'fa_user_text', 'fa_actor' );
+               $errors += $this->migrate( 'recentchanges', 'rc_id', 'rc_user', 'rc_user_text', 'rc_actor' );
+               $errors += $this->migrate( 'logging', 'log_id', 'log_user', 'log_user_text', 'log_actor' );
+
+               $errors += $this->migrateLogSearch();
+
+               return $errors === 0;
+       }
+
+       /**
+        * Calculate a "next" condition and a display string
+        * @param IDatabase $dbw
+        * @param string[] $primaryKey Primary key of the table.
+        * @param object $row Database row
+        * @return array [ string $next, string $display ]
+        */
+       private function makeNextCond( $dbw, $primaryKey, $row ) {
+               $next = '';
+               $display = [];
+               for ( $i = count( $primaryKey ) - 1; $i >= 0; $i-- ) {
+                       $field = $primaryKey[$i];
+                       $display[] = $field . '=' . $row->$field;
+                       $value = $dbw->addQuotes( $row->$field );
+                       if ( $next === '' ) {
+                               $next = "$field > $value";
+                       } else {
+                               $next = "$field > $value OR $field = $value AND ($next)";
+                       }
+               }
+               $display = implode( ' ', array_reverse( $display ) );
+               return [ $next, $display ];
+       }
+
+       /**
+        * Add actors for anons in a set of rows
+        * @param IDatabase $dbw
+        * @param string $nameField
+        * @param object[] &$rows
+        * @param array &$complainedAboutUsers
+        * @param int &$countErrors
+        * @return int Count of actors inserted
+        */
+       private function addActorsForRows(
+               IDatabase $dbw, $nameField, array &$rows, array &$complainedAboutUsers, &$countErrors
+       ) {
+               $needActors = [];
+               $countActors = 0;
+
+               $keep = [];
+               foreach ( $rows as $index => $row ) {
+                       $keep[$index] = true;
+                       if ( $row->actor_id === null ) {
+                               // All registered users should have an actor_id already. So
+                               // if we have a usable name here, it means they didn't run
+                               // maintenance/cleanupUsersWithNoId.php
+                               $name = $row->$nameField;
+                               if ( User::isUsableName( $name ) ) {
+                                       if ( !isset( $complainedAboutUsers[$name] ) ) {
+                                               $complainedAboutUsers[$name] = true;
+                                               $this->error(
+                                                       "User name \"$name\" is usable, cannot create an anonymous actor for it."
+                                                       . " Run maintenance/cleanupUsersWithNoId.php to fix this situation.\n"
+                                               );
+                                       }
+                                       unset( $keep[$index] );
+                                       $countErrors++;
+                               } else {
+                                       $needActors[$name] = 0;
+                               }
+                       }
+               }
+               $rows = array_intersect_key( $rows, $keep );
+
+               if ( $needActors ) {
+                       $dbw->insert(
+                               'actor',
+                               array_map( function ( $v ) {
+                                       return [
+                                               'actor_name' => $v,
+                                       ];
+                               }, array_keys( $needActors ) ),
+                               __METHOD__
+                       );
+                       $countActors += $dbw->affectedRows();
+
+                       $res = $dbw->select(
+                               'actor',
+                               [ 'actor_id', 'actor_name' ],
+                               [ 'actor_name' => array_keys( $needActors ) ],
+                               __METHOD__
+                       );
+                       foreach ( $res as $row ) {
+                               $needActors[$row->actor_name] = $row->actor_id;
+                       }
+                       foreach ( $rows as $row ) {
+                               if ( $row->actor_id === null ) {
+                                       $row->actor_id = $needActors[$row->$nameField];
+                               }
+                       }
+               }
+
+               return $countActors;
+       }
+
+       /**
+        * Migrate actors in a table.
+        *
+        * Assumes any row with the actor field non-zero have already been migrated.
+        * Blanks the name field when migrating.
+        *
+        * @param string $table Table to migrate
+        * @param string|string[] $primaryKey Primary key of the table.
+        * @param string $userField User ID field name
+        * @param string $nameField User name field name
+        * @param string $actorField Actor field name
+        * @return int Number of errors
+        */
+       protected function migrate( $table, $primaryKey, $userField, $nameField, $actorField ) {
+               $complainedAboutUsers = [];
+
+               $primaryKey = (array)$primaryKey;
+               $pkFilter = array_flip( $primaryKey );
+               $this->output(
+                       "Beginning migration of $table.$userField and $table.$nameField to $table.$actorField\n"
+               );
+               wfWaitForSlaves();
+
+               $dbw = $this->getDB( DB_MASTER );
+               $next = '1=1';
+               $countUpdated = 0;
+               $countActors = 0;
+               $countErrors = 0;
+               while ( true ) {
+                       // Fetch the rows needing update
+                       $res = $dbw->select(
+                               [ $table, 'actor' ],
+                               array_merge( $primaryKey, [ $userField, $nameField, 'actor_id' ] ),
+                               [
+                                       $actorField => 0,
+                                       $next,
+                               ],
+                               __METHOD__,
+                               [
+                                       'ORDER BY' => $primaryKey,
+                                       'LIMIT' => $this->mBatchSize,
+                               ],
+                               [
+                                       'actor' => [
+                                               'LEFT JOIN',
+                                               "$userField != 0 AND actor_user = $userField OR "
+                                               . "($userField = 0 OR $userField IS NULL) AND actor_name = $nameField"
+                                       ]
+                               ]
+                       );
+                       if ( !$res->numRows() ) {
+                               break;
+                       }
+
+                       // Insert new actors for rows that need one
+                       $rows = iterator_to_array( $res );
+                       $lastRow = end( $rows );
+                       $countActors += $this->addActorsForRows(
+                               $dbw, $nameField, $rows, $complainedAboutUsers, $countErrors
+                       );
+
+                       // Update the existing rows
+                       foreach ( $rows as $row ) {
+                               if ( !$row->actor_id ) {
+                                       list( , $display ) = $this->makeNextCond( $dbw, $primaryKey, $row );
+                                       $this->error(
+                                               "Could not make actor for row with $display "
+                                               . "$userField={$row->$userField} $nameField={$row->$nameField}\n"
+                                       );
+                                       $countErrors++;
+                                       continue;
+                               }
+                               $dbw->update(
+                                       $table,
+                                       [
+                                               $actorField => $row->actor_id,
+                                               $nameField => '',
+                                       ],
+                                       array_intersect_key( (array)$row, $pkFilter ) + [
+                                               $actorField => 0
+                                       ],
+                                       __METHOD__
+                               );
+                               $countUpdated += $dbw->affectedRows();
+                       }
+
+                       list( $next, $display ) = $this->makeNextCond( $dbw, $primaryKey, $lastRow );
+                       $this->output( "... $display\n" );
+                       wfWaitForSlaves();
+               }
+
+               $this->output(
+                       "Completed migration, updated $countUpdated row(s) with $countActors new actor(s), "
+                       . "$countErrors error(s)\n"
+               );
+               return $countErrors;
+       }
+
+       /**
+        * Migrate actors in a table to a temporary table.
+        *
+        * Assumes the new table is named "{$table}_actor_temp", and it has two
+        * columns, in order, being the primary key of the original table and the
+        * actor ID field.
+        * Blanks the name field when migrating.
+        *
+        * @param string $table Table to migrate
+        * @param string $primaryKey Primary key of the table.
+        * @param array $extra Extra fields to copy
+        * @param string $userField User ID field name
+        * @param string $nameField User name field name
+        * @param string $newPrimaryKey Primary key of the new table.
+        * @param string $actorField Actor field name
+        */
+       protected function migrateToTemp(
+               $table, $primaryKey, $extra, $userField, $nameField, $newPrimaryKey, $actorField
+       ) {
+               $complainedAboutUsers = [];
+
+               $newTable = $table . '_actor_temp';
+               $this->output(
+                       "Beginning migration of $table.$userField and $table.$nameField to $newTable.$actorField\n"
+               );
+               wfWaitForSlaves();
+
+               $dbw = $this->getDB( DB_MASTER );
+               $next = [];
+               $countUpdated = 0;
+               $countActors = 0;
+               $countErrors = 0;
+               while ( true ) {
+                       // Fetch the rows needing update
+                       $res = $dbw->select(
+                               [ $table, $newTable, 'actor' ],
+                               [ $primaryKey, $userField, $nameField, 'actor_id' ] + $extra,
+                               [ $newPrimaryKey => null ] + $next,
+                               __METHOD__,
+                               [
+                                       'ORDER BY' => $primaryKey,
+                                       'LIMIT' => $this->mBatchSize,
+                               ],
+                               [
+                                       $newTable => [ 'LEFT JOIN', "{$primaryKey}={$newPrimaryKey}" ],
+                                       'actor' => [
+                                               'LEFT JOIN',
+                                               "$userField != 0 AND actor_user = $userField OR "
+                                               . "($userField = 0 OR $userField IS NULL) AND actor_name = $nameField"
+                                       ]
+                               ]
+                       );
+                       if ( !$res->numRows() ) {
+                               break;
+                       }
+
+                       // Insert new actors for rows that need one
+                       $rows = iterator_to_array( $res );
+                       $lastRow = end( $rows );
+                       $countActors += $this->addActorsForRows(
+                               $dbw, $nameField, $rows, $complainedAboutUsers, $countErrors
+                       );
+
+                       // Update rows
+                       if ( $rows ) {
+                               $inserts = [];
+                               $updates = [];
+                               foreach ( $rows as $row ) {
+                                       if ( !$row->actor_id ) {
+                                               list( , $display ) = $this->makeNextCond( $dbw, [ $primaryKey ], $row );
+                                               $this->error(
+                                                       "Could not make actor for row with $display "
+                                                       . "$userField={$row->$userField} $nameField={$row->$nameField}\n"
+                                               );
+                                               $countErrors++;
+                                               continue;
+                                       }
+                                       $ins = [
+                                               $newPrimaryKey => $row->$primaryKey,
+                                               $actorField => $row->actor_id,
+                                       ];
+                                       foreach ( $extra as $to => $from ) {
+                                               $ins[$to] = $row->$to; // It's aliased
+                                       }
+                                       $inserts[] = $ins;
+                                       $updates[] = $row->$primaryKey;
+                               }
+                               $this->beginTransaction( $dbw, __METHOD__ );
+                               $dbw->insert( $newTable, $inserts, __METHOD__ );
+                               $dbw->update( $table, [ $nameField => '' ], [ $primaryKey => $updates ], __METHOD__ );
+                               $countUpdated += $dbw->affectedRows();
+                               $this->commitTransaction( $dbw, __METHOD__ );
+                       }
+
+                       // Calculate the "next" condition
+                       list( $n, $display ) = $this->makeNextCond( $dbw, [ $primaryKey ], $lastRow );
+                       $next = [ $n ];
+                       $this->output( "... $display\n" );
+               }
+
+               $this->output(
+                       "Completed migration, updated $countUpdated row(s) with $countActors new actor(s), "
+                       . "$countErrors error(s)\n"
+               );
+               return $countErrors;
+       }
+
+       /**
+        * Migrate actors in the log_search table.
+        * @return int Number of errors
+        */
+       protected function migrateLogSearch() {
+               $complainedAboutUsers = [];
+
+               $primaryKey = [ 'ls_field', 'ls_value' ];
+               $pkFilter = array_flip( $primaryKey );
+               $this->output( "Beginning migration of log_search\n" );
+               wfWaitForSlaves();
+
+               $dbw = $this->getDB( DB_MASTER );
+               $countUpdated = 0;
+               $countActors = 0;
+               $countErrors = 0;
+
+               $next = '1=1';
+               while ( true ) {
+                       // Fetch the rows needing update
+                       $res = $dbw->select(
+                               [ 'log_search', 'actor' ],
+                               [ 'ls_field', 'ls_value', 'actor_id' ],
+                               [
+                                       'ls_field' => 'target_author_id',
+                                       $next,
+                               ],
+                               __METHOD__,
+                               [
+                                       'DISTINCT',
+                                       'ORDER BY' => [ 'ls_value' ],
+                                       'LIMIT' => $this->mBatchSize,
+                               ],
+                               [ 'actor' => [ 'LEFT JOIN', 'ls_value = ' . $dbw->buildStringCast( 'actor_user' ) ] ]
+                       );
+                       if ( !$res->numRows() ) {
+                               break;
+                       }
+
+                       // Update the rows
+                       $del = [];
+                       foreach ( $res as $row ) {
+                               $lastRow = $row;
+                               if ( !$row->actor_id ) {
+                                       list( , $display ) = $this->makeNextCond( $dbw, $primaryKey, $row );
+                                       $this->error( "No actor for row with $display\n" );
+                                       $countErrors++;
+                                       continue;
+                               }
+                               $dbw->update(
+                                       'log_search',
+                                       [
+                                               'ls_field' => 'target_author_actor',
+                                               'ls_value' => $row->actor_id,
+                                       ],
+                                       [
+                                               'ls_field' => $row->ls_field,
+                                               'ls_value' => $row->ls_value,
+                                       ],
+                                       __METHOD__,
+                                       [ 'IGNORE' ]
+                               );
+                               $countUpdated += $dbw->affectedRows();
+                               $del[] = $row->ls_value;
+                       }
+                       if ( $del ) {
+                               $dbw->delete(
+                                       'log_search', [ 'ls_field' => 'target_author_id', 'ls_value' => $del ], __METHOD__
+                               );
+                               $countUpdated += $dbw->affectedRows();
+                       }
+
+                       list( $next, $display ) = $this->makeNextCond( $dbw, $primaryKey, $lastRow );
+                       $this->output( "... $display\n" );
+                       wfWaitForSlaves();
+               }
+
+               $next = '1=1';
+               while ( true ) {
+                       // Fetch the rows needing update
+                       $res = $dbw->select(
+                               [ 'log_search', 'actor' ],
+                               [ 'ls_field', 'ls_value', 'actor_id' ],
+                               [
+                                       'ls_field' => 'target_author_ip',
+                                       $next,
+                               ],
+                               __METHOD__,
+                               [
+                                       'DISTINCT',
+                                       'ORDER BY' => [ 'ls_value' ],
+                                       'LIMIT' => $this->mBatchSize,
+                               ],
+                               [ 'actor' => [ 'LEFT JOIN', 'ls_value = actor_name' ] ]
+                       );
+                       if ( !$res->numRows() ) {
+                               break;
+                       }
+
+                       // Insert new actors for rows that need one
+                       $rows = iterator_to_array( $res );
+                       $lastRow = end( $rows );
+                       $countActors += $this->addActorsForRows(
+                               $dbw, 'ls_value', $rows, $complainedAboutUsers, $countErrors
+                       );
+
+                       // Update the rows
+                       $del = [];
+                       foreach ( $rows as $row ) {
+                               if ( !$row->actor_id ) {
+                                       list( , $display ) = $this->makeNextCond( $dbw, $primaryKey, $row );
+                                       $this->error( "Could not make actor for row with $display\n" );
+                                       $countErrors++;
+                                       continue;
+                               }
+                               $dbw->update(
+                                       'log_search',
+                                       [
+                                               'ls_field' => 'target_author_actor',
+                                               'ls_value' => $row->actor_id,
+                                       ],
+                                       [
+                                               'ls_field' => $row->ls_field,
+                                               'ls_value' => $row->ls_value,
+                                       ],
+                                       __METHOD__,
+                                       [ 'IGNORE' ]
+                               );
+                               $countUpdated += $dbw->affectedRows();
+                               $del[] = $row->ls_value;
+                       }
+                       if ( $del ) {
+                               $dbw->delete(
+                                       'log_search', [ 'ls_field' => 'target_author_ip', 'ls_value' => $del ], __METHOD__
+                               );
+                               $countUpdated += $dbw->affectedRows();
+                       }
+
+                       list( $next, $display ) = $this->makeNextCond( $dbw, $primaryKey, $lastRow );
+                       $this->output( "... $display\n" );
+                       wfWaitForSlaves();
+               }
+
+               $this->output(
+                       "Completed migration, updated $countUpdated row(s) with $countActors new actor(s), "
+                       . "$countErrors error(s)\n"
+               );
+               return $countErrors;
+       }
+}
+
+$maintClass = "MigrateActors";
+require_once RUN_MAINTENANCE_IF_MAIN;
index c35315d..cdecab0 100644 (file)
@@ -205,7 +205,7 @@ class MigrateComments extends LoggedUpdateMaintenance {
                                        $next = "$field > $value OR $field = $value AND ($next)";
                                }
                        }
-                       $prompt = join( ' ', array_reverse( $prompt ) );
+                       $prompt = implode( ' ', array_reverse( $prompt ) );
                        $this->output( "... $prompt\n" );
                        wfWaitForSlaves();
                }
@@ -282,7 +282,6 @@ class MigrateComments extends LoggedUpdateMaintenance {
                        // Calculate the "next" condition
                        $next = [ $primaryKey . ' > ' . $dbw->addQuotes( $row->$primaryKey ) ];
                        $this->output( "... {$row->$primaryKey}\n" );
-                       wfWaitForSlaves();
                }
 
                $this->output(
index eeaddba..bf8d071 100644 (file)
@@ -100,7 +100,6 @@ class MigrateUserGroup extends Maintenance {
                        $count += $affected;
                        $blockStart += $batchSize;
                        $blockEnd += $batchSize;
-                       wfWaitForSlaves();
                }
                $this->output( "Done! $count users in group '$oldGroup' are now in '$newGroup' instead.\n" );
        }
index 090c3d4..6d14f8a 100644 (file)
@@ -117,7 +117,6 @@ class MoveBatch extends Maintenance {
                        if ( $interval ) {
                                sleep( $interval );
                        }
-                       wfWaitForSlaves();
                }
        }
 }
diff --git a/maintenance/mssql/archives/patch-actor-table.sql b/maintenance/mssql/archives/patch-actor-table.sql
new file mode 100644 (file)
index 0000000..b26ad44
--- /dev/null
@@ -0,0 +1,53 @@
+--
+-- patch-actor-table.sql
+--
+-- T167246. Add an `actor` table and various columns (and temporary tables) to reference it.
+
+CREATE TABLE /*_*/actor (
+  actor_id bigint unsigned NOT NULL CONSTRAINT PK_actor PRIMARY KEY IDENTITY(0,1),
+  actor_user int unsigned,
+  actor_name nvarchar(255) NOT NULL
+);
+CREATE UNIQUE INDEX /*i*/actor_user ON /*_*/actor (actor_user);
+CREATE UNIQUE INDEX /*i*/actor_name ON /*_*/actor (actor_name);
+
+-- Dummy
+INSERT INTO /*_*/actor (actor_name) VALUES ('##Anonymous##');
+
+CREATE TABLE /*_*/revision_actor_temp (
+  revactor_rev int unsigned NOT NULL CONSTRAINT FK_revactor_rev FOREIGN KEY REFERENCES /*_*/revision(rev_id) ON DELETE CASCADE,
+  revactor_actor bigint unsigned NOT NULL,
+  revactor_timestamp varchar(14) NOT NULL CONSTRAINT DF_revactor_timestamp DEFAULT '',
+  revactor_page int unsigned NOT NULL,
+  CONSTRAINT PK_revision_actor_temp PRIMARY KEY (revactor_rev, revactor_actor)
+);
+CREATE UNIQUE INDEX /*i*/revactor_rev ON /*_*/revision_actor_temp (revactor_rev);
+CREATE INDEX /*i*/actor_timestamp ON /*_*/revision_actor_temp (revactor_actor,revactor_timestamp);
+CREATE INDEX /*i*/page_actor_timestamp ON /*_*/revision_actor_temp (revactor_page,revactor_actor,revactor_timestamp);
+
+ALTER TABLE /*_*/archive ADD CONSTRAINT DF_ar_user_text DEFAULT '' FOR ar_user_text;
+ALTER TABLE /*_*/archive ADD ar_actor bigint unsigned NOT NULL CONSTRAINT DF_ar_actor DEFAULT 0;
+CREATE INDEX /*i*/ar_actor_timestamp ON /*_*/archive (ar_actor,ar_timestamp);
+
+ALTER TABLE /*_*/ipblocks ADD ipb_by_actor bigint unsigned NOT NULL CONSTRAINT DF_ipb_by_actor DEFAULT 0;
+
+ALTER TABLE /*_*/image ADD CONSTRAINT DF_img_user_text DEFAULT '' FOR img_user_text;
+ALTER TABLE /*_*/image ADD img_actor bigint unsigned NOT NULL CONSTRAINT DF_img_actor DEFAULT 0;
+CREATE INDEX /*i*/img_actor_timestamp ON /*_*/image (img_actor, img_timestamp);
+
+ALTER TABLE /*_*/oldimage ADD CONSTRAINT DF_oi_user_text DEFAULT '' FOR oi_user_text;
+ALTER TABLE /*_*/oldimage ADD oi_actor bigint unsigned NOT NULL CONSTRAINT DF_oi_actor DEFAULT 0;
+CREATE INDEX /*i*/oi_actor_timestamp ON /*_*/oldimage (oi_actor,oi_timestamp);
+
+ALTER TABLE /*_*/filearchive ADD CONSTRAINT DF_fa_user_text DEFAULT '' FOR fa_user_text;
+ALTER TABLE /*_*/filearchive ADD fa_actor bigint unsigned NOT NULL CONSTRAINT DF_fa_actor DEFAULT 0;
+CREATE INDEX /*i*/fa_actor_timestamp ON /*_*/filearchive (fa_actor,fa_timestamp);
+
+ALTER TABLE /*_*/recentchanges ADD CONSTRAINT DF_rc_user_text DEFAULT '' FOR rc_user_text;
+ALTER TABLE /*_*/recentchanges ADD rc_actor bigint unsigned NOT NULL CONSTRAINT DF_rc_actor DEFAULT 0;
+CREATE INDEX /*i*/rc_ns_actor ON /*_*/recentchanges (rc_namespace, rc_actor);
+CREATE INDEX /*i*/rc_actor ON /*_*/recentchanges (rc_actor, rc_timestamp);
+
+ALTER TABLE /*_*/logging ADD log_actor bigint unsigned NOT NULL CONSTRAINT DF_log_actor DEFAULT 0;
+CREATE INDEX /*i*/actor_time ON /*_*/logging (log_actor, log_timestamp);
+CREATE INDEX /*i*/log_actor_type_time ON /*_*/logging (log_actor, log_type, log_timestamp);
index 4673264..5348c47 100644 (file)
@@ -54,6 +54,23 @@ CREATE INDEX /*i*/user_email ON /*_*/mwuser (user_email);
 -- Insert a dummy user to represent anons
 INSERT INTO /*_*/mwuser (user_name) VALUES ('##Anonymous##');
 
+--
+-- The "actor" table associates user names or IP addresses with integers for
+-- the benefit of other tables that need to refer to either logged-in or
+-- logged-out users. If something can only ever be done by logged-in users, it
+-- can refer to the user table directly.
+--
+CREATE TABLE /*_*/actor (
+  actor_id bigint unsigned NOT NULL CONSTRAINT PK_actor PRIMARY KEY IDENTITY(0,1),
+  actor_user int unsigned,
+  actor_name nvarchar(255) NOT NULL
+);
+CREATE UNIQUE INDEX /*i*/actor_user ON /*_*/actor (actor_user);
+CREATE UNIQUE INDEX /*i*/actor_name ON /*_*/actor (actor_name);
+
+-- Insert a dummy actor to represent no actor
+INSERT INTO /*_*/actor (actor_name) VALUES ('##Anonymous##');
+
 --
 -- User permissions have been broken out to a separate table;
 -- this allows sites with a shared user table to have different
@@ -200,7 +217,7 @@ INSERT INTO /*_*/revision (rev_page,rev_text_id,rev_comment,rev_user,rev_len) VA
 ALTER TABLE /*_*/page ADD CONSTRAINT FK_page_latest_page_id FOREIGN KEY (page_latest) REFERENCES /*_*/revision(rev_id);
 
 --
--- Temporary table to avoid blocking on an alter of revision.
+-- Temporary tables to avoid blocking on an alter of revision.
 --
 -- On large wikis like the English Wikipedia, altering the revision table is a
 -- months-long process. This table is being created to avoid such an alter, and
@@ -213,6 +230,17 @@ CREATE TABLE /*_*/revision_comment_temp (
 );
 CREATE UNIQUE INDEX /*i*/revcomment_rev ON /*_*/revision_comment_temp (revcomment_rev);
 
+CREATE TABLE /*_*/revision_actor_temp (
+  revactor_rev int unsigned NOT NULL CONSTRAINT FK_revactor_rev FOREIGN KEY REFERENCES /*_*/revision(rev_id) ON DELETE CASCADE,
+  revactor_actor bigint unsigned NOT NULL,
+  revactor_timestamp varchar(14) NOT NULL CONSTRAINT DF_revactor_timestamp DEFAULT '',
+  revactor_page int unsigned NOT NULL,
+  CONSTRAINT PK_revision_actor_temp PRIMARY KEY (revactor_rev, revactor_actor)
+);
+CREATE UNIQUE INDEX /*i*/revactor_rev ON /*_*/revision_actor_temp (revactor_rev);
+CREATE INDEX /*i*/actor_timestamp ON /*_*/revision_actor_temp (revactor_actor,revactor_timestamp);
+CREATE INDEX /*i*/page_actor_timestamp ON /*_*/revision_actor_temp (revactor_page,revactor_actor,revactor_timestamp);
+
 --
 -- Holds TEXT of individual page revisions.
 --
@@ -246,7 +274,8 @@ CREATE TABLE /*_*/archive (
    ar_comment NVARCHAR(255) NOT NULL CONSTRAINT DF_ar_comment DEFAULT '',
    ar_comment_id bigint unsigned NOT NULL CONSTRAINT DF_ar_comment_id DEFAULT 0 CONSTRAINT FK_ar_comment_id FOREIGN KEY REFERENCES /*_*/comment(comment_id),
    ar_user INT CONSTRAINT ar_user__user_id__fk FOREIGN KEY REFERENCES /*_*/mwuser(user_id),
-   ar_user_text NVARCHAR(255) NOT NULL,
+   ar_user_text NVARCHAR(255) NOT NULL CONSTRAINT DF_ar_user_text DEFAULT '',
+   ar_actor bigint unsigned NOT NULL CONSTRAINT DF_ar_actor DEFAULT 0,
    ar_timestamp varchar(14) NOT NULL default '',
    ar_minor_edit BIT NOT NULL DEFAULT 0,
    ar_flags NVARCHAR(255) NOT NULL,
@@ -262,6 +291,7 @@ CREATE TABLE /*_*/archive (
 );
 CREATE INDEX /*i*/name_title_timestamp ON /*_*/archive (ar_namespace,ar_title,ar_timestamp);
 CREATE INDEX /*i*/ar_usertext_timestamp ON /*_*/archive (ar_user_text,ar_timestamp);
+CREATE INDEX /*i*/ar_actor_timestamp ON /*_*/archive (ar_actor,ar_timestamp);
 CREATE INDEX /*i*/ar_revid ON /*_*/archive (ar_rev_id);
 
 
@@ -600,6 +630,9 @@ CREATE TABLE /*_*/ipblocks (
   -- User ID who made the block.
   ipb_by int REFERENCES /*_*/mwuser(user_id) ON DELETE CASCADE,
 
+  -- Actor ID who made the block.
+  ipb_by_actor bigint unsigned NOT NULL CONSTRAINT DF_ipb_by_actor DEFAULT 0,
+
   -- User name of blocker
   ipb_by_text nvarchar(255) NOT NULL default '',
 
@@ -709,7 +742,8 @@ CREATE TABLE /*_*/image (
 
   -- user_id and user_name of uploader.
   img_user int REFERENCES /*_*/mwuser(user_id) ON DELETE SET NULL,
-  img_user_text nvarchar(255) NOT NULL,
+  img_user_text nvarchar(255) NOT NULL CONSTRAINT DF_img_user_text DEFAULT '',
+  img_actor bigint unsigned NOT NULL CONSTRAINT DF_img_actor DEFAULT 0,
 
   -- Time of the upload.
   img_timestamp nvarchar(14) NOT NULL default '',
@@ -722,6 +756,7 @@ CREATE TABLE /*_*/image (
 );
 
 CREATE INDEX /*i*/img_usertext_timestamp ON /*_*/image (img_user_text,img_timestamp);
+CREATE INDEX /*i*/img_actor_timestamp ON /*_*/image (img_actor, img_timestamp);
 -- Used by Special:ListFiles for sort-by-size
 CREATE INDEX /*i*/img_size ON /*_*/image (img_size);
 -- Used by Special:Newimages and Special:ListFiles
@@ -768,7 +803,8 @@ CREATE TABLE /*_*/oldimage (
   oi_description nvarchar(255) NOT NULL CONSTRAINT DF_oi_description DEFAULT '',
   oi_description_id bigint unsigned NOT NULL CONSTRAINT DF_oi_description_id DEFAULT 0 CONSTRAINT FK_oi_description_id FOREIGN KEY REFERENCES /*_*/comment(comment_id),
   oi_user int REFERENCES /*_*/mwuser(user_id),
-  oi_user_text nvarchar(255) NOT NULL,
+  oi_user_text nvarchar(255) NOT NULL CONSTRAINT DF_oi_user_text DEFAULT '',
+  oi_actor bigint unsigned NOT NULL CONSTRAINT DF_oi_actor DEFAULT 0,
   oi_timestamp varchar(14) NOT NULL default '',
 
   oi_metadata varbinary(max) NOT NULL,
@@ -783,6 +819,7 @@ CREATE TABLE /*_*/oldimage (
 );
 
 CREATE INDEX /*i*/oi_usertext_timestamp ON /*_*/oldimage (oi_user_text,oi_timestamp);
+CREATE INDEX /*i*/oi_actor_timestamp ON /*_*/oldimage (oi_actor,oi_timestamp);
 CREATE INDEX /*i*/oi_name_timestamp ON /*_*/oldimage (oi_name,oi_timestamp);
 CREATE INDEX /*i*/oi_sha1 ON /*_*/oldimage (oi_sha1);
 
@@ -830,7 +867,8 @@ CREATE TABLE /*_*/filearchive (
   fa_description nvarchar(255) CONSTRAINT DF_fa_description DEFAULT '',
   fa_description_id bigint unsigned NOT NULL CONSTRAINT DF_fa_description DEFAULT 0 CONSTRAINT FK_fa_description FOREIGN KEY REFERENCES /*_*/comment(comment_id),
   fa_user int default 0 REFERENCES /*_*/mwuser(user_id) ON DELETE SET NULL,
-  fa_user_text nvarchar(255),
+  fa_user_text nvarchar(255) CONSTRAINT DF_fa_user_text DEFAULT '',
+  fa_actor bigint unsigned NOT NULL CONSTRAINT DF_fa_actor DEFAULT 0,
   fa_timestamp varchar(14) default '',
 
   -- Visibility of deleted revisions, bitfield
@@ -851,6 +889,7 @@ CREATE INDEX /*i*/fa_storage_group ON /*_*/filearchive (fa_storage_group, fa_sto
 CREATE INDEX /*i*/fa_deleted_timestamp ON /*_*/filearchive (fa_deleted_timestamp);
 -- sort by uploader
 CREATE INDEX /*i*/fa_user_timestamp ON /*_*/filearchive (fa_user_text,fa_timestamp);
+CREATE INDEX /*i*/fa_actor_timestamp ON /*_*/filearchive (fa_actor,fa_timestamp);
 -- find file by sha1, 10 bytes will be enough for hashes to be indexed
 CREATE INDEX /*i*/fa_sha1 ON /*_*/filearchive (fa_sha1);
 
@@ -923,7 +962,8 @@ CREATE TABLE /*_*/recentchanges (
 
   -- As in revision
   rc_user int NOT NULL default 0 CONSTRAINT rc_user__user_id__fk FOREIGN KEY REFERENCES /*_*/mwuser(user_id),
-  rc_user_text nvarchar(255) NOT NULL,
+  rc_user_text nvarchar(255) NOT NULL CONSTRAINT DF_rc_user_text DEFAULT '',
+  rc_actor bigint unsigned NOT NULL CONSTRAINT DF_rc_actor DEFAULT 0,
 
   -- When pages are renamed, their RC entries do _not_ change.
   rc_namespace int NOT NULL default 0,
@@ -995,6 +1035,8 @@ CREATE INDEX /*i*/new_name_timestamp ON /*_*/recentchanges (rc_new,rc_namespace,
 CREATE INDEX /*i*/rc_ip ON /*_*/recentchanges (rc_ip);
 CREATE INDEX /*i*/rc_ns_usertext ON /*_*/recentchanges (rc_namespace, rc_user_text);
 CREATE INDEX /*i*/rc_user_text ON /*_*/recentchanges (rc_user_text, rc_timestamp);
+CREATE INDEX /*i*/rc_ns_actor ON /*_*/recentchanges (rc_namespace, rc_actor);
+CREATE INDEX /*i*/rc_actor ON /*_*/recentchanges (rc_actor, rc_timestamp);
 CREATE INDEX /*i*/rc_name_type_patrolled_timestamp ON /*_*/recentchanges (rc_namespace, rc_type, rc_patrolled, rc_timestamp);
 
 
@@ -1126,6 +1168,9 @@ CREATE TABLE /*_*/logging (
   -- Name of the user who performed this action
   log_user_text nvarchar(255) NOT NULL default '',
 
+  -- The actor who performed this action
+  log_actor bigint unsigned NOT NULL CONSTRAINT DF_log_actor DEFAULT 0,
+
   -- Key to the page affected. Where a user is the target,
   -- this will point to the user page.
   log_namespace int NOT NULL default 0,
@@ -1156,6 +1201,8 @@ CREATE INDEX /*i*/log_page_id_time ON /*_*/logging (log_page,log_timestamp);
 CREATE INDEX /*i*/type_action ON /*_*/logging (log_type, log_action, log_timestamp);
 CREATE INDEX /*i*/log_user_text_type_time ON /*_*/logging (log_user_text, log_type, log_timestamp);
 CREATE INDEX /*i*/log_user_text_time ON /*_*/logging (log_user_text, log_timestamp);
+CREATE INDEX /*i*/actor_time ON /*_*/logging (log_actor, log_timestamp);
+CREATE INDEX /*i*/log_actor_type_time ON /*_*/logging (log_actor, log_type, log_timestamp);
 
 INSERT INTO /*_*/logging (log_user,log_page,log_params) VALUES(0,0,'');
 
diff --git a/maintenance/oracle/archives/patch-actor-table.sql b/maintenance/oracle/archives/patch-actor-table.sql
new file mode 100644 (file)
index 0000000..93c7531
--- /dev/null
@@ -0,0 +1,64 @@
+--
+-- patch-actor-table.sql
+--
+-- T167246. Add an `actor` table and various columns (and temporary tables) to reference it.
+
+define mw_prefix='{$wgDBprefix}';
+
+CREATE SEQUENCE actor_actor_id_seq;
+CREATE TABLE &mw_prefix.actor (
+  actor_id NUMBER NOT NULL,
+  actor_user NUMBER,
+  actor_name VARCHAR2(255) NOT NULL
+);
+
+ALTER TABLE &mw_prefix.actor ADD CONSTRAINT &mw_prefix.actor_pk PRIMARY KEY (actor_id);
+
+/*$mw$*/
+CREATE TRIGGER &mw_prefix.actor_seq_trg BEFORE INSERT ON &mw_prefix.actor
+       FOR EACH ROW WHEN (new.actor_id IS NULL)
+BEGIN
+       &mw_prefix.lastval_pkg.setLastval(actor_actor_id_seq.nextval, :new.actor_id);
+END;
+/*$mw$*/
+
+-- Create a dummy actor to satisfy fk contraints
+INSERT INTO &mw_prefix.actor (actor_id, actor_name) VALUES (0,'##Anonymous##');
+
+CREATE TABLE &mw_prefix.revision_actor_temp (
+  revactor_rev NUMBER NOT NULL,
+  revactor_actor NUMBER NOT NULL,
+  revactor_timestamp TIMESTAMP(6) WITH TIME ZONE NOT NULL,
+  revactor_page NUMBER NOT NULL
+);
+ALTER TABLE &mw_prefix.revision_actor_temp ADD CONSTRAINT &mw_prefix.revision_actor_temp_pk PRIMARY KEY (revactor_rev, revactor_actor);
+CREATE UNIQUE INDEX &mw_prefix.revactor_rev ON &mw_prefix.revision_actor_temp (revactor_rev);
+CREATE INDEX &mw_prefix.actor_timestamp ON &mw_prefix.revision_actor_temp (revactor_actor,revactor_timestamp);
+CREATE INDEX &mw_prefix.page_actor_timestamp ON &mw_prefix.revision_actor_temp (revactor_page,revactor_actor,revactor_timestamp);
+
+ALTER TABLE &mw_prefix.archive ALTER COLUMN ar_user_text VARCHAR2(255) NULL;
+ALTER TABLE &mw_prefix.archive ADD COLUMN ar_actor NUMBER DEFAULT 0 NOT NULL;
+CREATE INDEX &mw_prefix.ar_actor_timestamp ON &mw_prefix.archive (ar_actor,ar_timestamp);
+
+ALTER TABLE &mw_prefix.ipblocks ADD COLUMN ipb_by_actor NUMBER DEFUALT 0 NOT NULL;
+
+ALTER TABLE &mw_prefix.image ALTER COLUMN img_user_text VARCHAR2(255) NULL;
+ALTER TABLE &mw_prefix.image ADD COLUMN img_actor NUMBER DEFAULT 0 NOT NULL;
+CREATE INDEX &mw_prefix.img_actor_timestamp ON &mw_prefix.image (img_actor, img_timestamp);
+
+ALTER TABLE &mw_prefix.oldimage ALTER COLUMN oi_user_text VARCHAR2(255) NULL;
+ALTER TABLE &mw_prefix.oldimage ADD COLUMN oi_actor NUMBER DEFAULT 0 NOT NULL;
+CREATE INDEX &mw_prefix.oi_actor_timestamp ON &mw_prefix.oldimage (oi_actor,oi_timestamp);
+
+ALTER TABLE &mw_prefix.filearchive ALTER COLUMN fa_user_text VARCHAR2(255) NULL;
+ALTER TABLE &mw_prefix.filearchive ADD COLUMN fa_actor NUMBER DEFAULT 0 NOT NULL;
+CREATE INDEX &mw_prefix.fa_actor_timestamp ON &mw_prefix.filearchive (fa_actor,fa_timestamp);
+
+ALTER TABLE &mw_prefix.recentchanges ALTER COLUMN rc_user_text VARCHAR2(255) NULL;
+ALTER TABLE &mw_prefix.recentchanges ADD COLUMN rc_actor NUMBER DEFAULT 0 NOT NULL;
+CREATE INDEX &mw_prefix.rc_ns_actor ON &mw_prefix.recentchanges (rc_namespace, rc_actor);
+CREATE INDEX &mw_prefix.rc_actor ON &mw_prefix.recentchanges (rc_actor, rc_timestamp);
+
+ALTER TABLE &mw_prefix.logging ADD COLUMN log_actor NUMBER DEFAULT 0 NOT NULL;
+CREATE INDEX &mw_prefix.actor_time ON &mw_prefix.logging (log_actor, log_timestamp);
+CREATE INDEX &mw_prefix.log_actor_type_time ON &mw_prefix.logging (log_actor, log_type, log_timestamp);
index 7195a5e..110188d 100644 (file)
@@ -60,6 +60,26 @@ INSERT INTO &mw_prefix.mwuser
   (user_id, user_name, user_options, user_touched, user_registration, user_editcount)
   VALUES (0,'Anonymous','', current_timestamp, current_timestamp,0);
 
+CREATE SEQUENCE actor_actor_id_seq;
+CREATE TABLE &mw_prefix.actor (
+  actor_id NUMBER NOT NULL,
+  actor_user NUMBER,
+  actor_name VARCHAR2(255) NOT NULL
+);
+
+ALTER TABLE &mw_prefix.actor ADD CONSTRAINT &mw_prefix.actor_pk PRIMARY KEY (actor_id);
+
+/*$mw$*/
+CREATE TRIGGER &mw_prefix.actor_seq_trg BEFORE INSERT ON &mw_prefix.actor
+       FOR EACH ROW WHEN (new.actor_id IS NULL)
+BEGIN
+       &mw_prefix.lastval_pkg.setLastval(actor_actor_id_seq.nextval, :new.actor_id);
+END;
+/*$mw$*/
+
+-- Create a dummy actor to satisfy fk contraints
+INSERT INTO &mw_prefix.actor (actor_id, actor_name) VALUES (0,'##Anonymous##');
+
 CREATE TABLE &mw_prefix.user_groups (
   ug_user   NUMBER      DEFAULT 0 NOT NULL,
   ug_group  VARCHAR2(255)     NOT NULL,
@@ -197,6 +217,17 @@ ALTER TABLE &mw_prefix.revision_comment_temp ADD CONSTRAINT &mw_prefix.revision_
 ALTER TABLE &mw_prefix.revision_comment_temp ADD CONSTRAINT &mw_prefix.revision_comment_temp_fk2 FOREIGN KEY (revcomment_comment_id) REFERENCES &mw_prefix."COMMENT"(comment_id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED;
 CREATE UNIQUE INDEX &mw_prefix.revcomment_rev ON &mw_prefix.revision_comment_temp (revcomment_rev);
 
+CREATE TABLE &mw_prefix.revision_actor_temp (
+  revactor_rev NUMBER NOT NULL,
+  revactor_actor NUMBER NOT NULL,
+  revactor_timestamp TIMESTAMP(6) WITH TIME ZONE NOT NULL,
+  revactor_page NUMBER NOT NULL
+);
+ALTER TABLE &mw_prefix.revision_actor_temp ADD CONSTRAINT &mw_prefix.revision_actor_temp_pk PRIMARY KEY (revactor_rev, revactor_actor);
+CREATE UNIQUE INDEX &mw_prefix.revactor_rev ON &mw_prefix.revision_actor_temp (revactor_rev);
+CREATE INDEX &mw_prefix.actor_timestamp ON &mw_prefix.revision_actor_temp (revactor_actor,revactor_timestamp);
+CREATE INDEX &mw_prefix.page_actor_timestamp ON &mw_prefix.revision_actor_temp (revactor_page,revactor_actor,revactor_timestamp);
+
 CREATE SEQUENCE text_old_id_seq;
 CREATE TABLE &mw_prefix.pagecontent ( -- replaces reserved word 'text'
   old_id     NUMBER  NOT NULL,
@@ -221,7 +252,8 @@ CREATE TABLE &mw_prefix.archive (
   ar_comment     VARCHAR2(255),
   ar_comment_id  NUMBER DEFAULT 0 NOT NULL,
   ar_user        NUMBER          DEFAULT 0 NOT NULL,
-  ar_user_text   VARCHAR2(255)         NOT NULL,
+  ar_user_text   VARCHAR2(255)         NULL,
+  ar_actor       NUMBER          DEFAULT 0 NOT NULL,
   ar_timestamp   TIMESTAMP(6) WITH TIME ZONE  NOT NULL,
   ar_minor_edit  CHAR(1)         DEFAULT '0' NOT NULL,
   ar_flags       VARCHAR2(255),
@@ -240,6 +272,7 @@ ALTER TABLE &mw_prefix.archive ADD CONSTRAINT &mw_prefix.archive_fk1 FOREIGN KEY
 ALTER TABLE &mw_prefix.archive ADD CONSTRAINT &mw_prefix.archive_fk2 FOREIGN KEY (ar_comment_id) REFERENCES &mw_prefix."COMMENT"(comment_id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED;
 CREATE INDEX &mw_prefix.archive_i01 ON &mw_prefix.archive (ar_namespace,ar_title,ar_timestamp);
 CREATE INDEX &mw_prefix.archive_i02 ON &mw_prefix.archive (ar_user_text,ar_timestamp);
+CREATE INDEX &mw_prefix.ar_actor_timestamp ON &mw_prefix.archive (ar_actor,ar_timestamp);
 CREATE INDEX &mw_prefix.archive_i03 ON &mw_prefix.archive (ar_rev_id);
 /*$mw$*/
 CREATE TRIGGER &mw_prefix.archive_seq_trg BEFORE INSERT ON &mw_prefix.archive
@@ -439,6 +472,7 @@ CREATE TABLE &mw_prefix.ipblocks (
   ipb_user              NUMBER      DEFAULT 0 NOT  NULL,
   ipb_by                NUMBER      DEFAULT 0 NOT NULL,
   ipb_by_text           VARCHAR2(255)      NULL,
+  ipb_by_actor          NUMBER      DEFUALT 0 NOT NULL,
   ipb_reason            VARCHAR2(255)      NULL,
   ipb_reason_id         NUMBER DEFAULT 0 NOT NULL,
   ipb_timestamp         TIMESTAMP(6) WITH TIME ZONE  NOT NULL,
@@ -484,7 +518,8 @@ CREATE TABLE &mw_prefix.image (
   img_minor_mime   VARCHAR2(100) DEFAULT 'unknown',
   img_description  VARCHAR2(255),
   img_user         NUMBER       DEFAULT 0 NOT NULL,
-  img_user_text    VARCHAR2(255)      NOT NULL,
+  img_user_text    VARCHAR2(255)      NULL,
+  img_actor        NUMBER       DEFAULT 0 NOT NULL,
   img_timestamp    TIMESTAMP(6) WITH TIME ZONE,
   img_sha1         VARCHAR2(32)
 );
@@ -494,6 +529,7 @@ CREATE INDEX &mw_prefix.image_i01 ON &mw_prefix.image (img_user_text,img_timesta
 CREATE INDEX &mw_prefix.image_i02 ON &mw_prefix.image (img_size);
 CREATE INDEX &mw_prefix.image_i03 ON &mw_prefix.image (img_timestamp);
 CREATE INDEX &mw_prefix.image_i04 ON &mw_prefix.image (img_sha1);
+CREATE INDEX &mw_prefix.img_actor_timestamp ON &mw_prefix.image (img_actor, img_timestamp);
 
 CREATE TABLE &mw_prefix.image_comment_temp (
   imgcomment_name VARCHAR2(255) NOT NULL,
@@ -515,7 +551,8 @@ CREATE TABLE &mw_prefix.oldimage (
   oi_description   VARCHAR2(255),
   oi_description_id  NUMBER DEFAULT 0 NOT NULL,
   oi_user          NUMBER          DEFAULT 0 NOT NULL,
-  oi_user_text     VARCHAR2(255)         NOT NULL,
+  oi_user_text     VARCHAR2(255)         NULL,
+  oi_actor         NUMBER          DEFAULT 0 NOT NULL,
   oi_timestamp     TIMESTAMP(6) WITH TIME ZONE  NOT NULL,
   oi_metadata      CLOB,
   oi_media_type    VARCHAR2(32) DEFAULT NULL,
@@ -528,6 +565,7 @@ ALTER TABLE &mw_prefix.oldimage ADD CONSTRAINT &mw_prefix.oldimage_fk1 FOREIGN K
 ALTER TABLE &mw_prefix.oldimage ADD CONSTRAINT &mw_prefix.oldimage_fk2 FOREIGN KEY (oi_user) REFERENCES &mw_prefix.mwuser(user_id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED;
 ALTER TABLE &mw_prefix.oldimage ADD CONSTRAINT &mw_prefix.oldimage_fk3 FOREIGN KEY (oi_description_id) REFERENCES &mw_prefix."COMMENT"(comment_id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED;
 CREATE INDEX &mw_prefix.oldimage_i01 ON &mw_prefix.oldimage (oi_user_text,oi_timestamp);
+CREATE INDEX &mw_prefix.oi_actor_timestamp ON &mw_prefix.oldimage (oi_actor,oi_timestamp);
 CREATE INDEX &mw_prefix.oldimage_i02 ON &mw_prefix.oldimage (oi_name,oi_timestamp);
 CREATE INDEX &mw_prefix.oldimage_i03 ON &mw_prefix.oldimage (oi_name,oi_archive_name);
 CREATE INDEX &mw_prefix.oldimage_i04 ON &mw_prefix.oldimage (oi_sha1);
@@ -555,7 +593,8 @@ CREATE TABLE &mw_prefix.filearchive (
   fa_description        VARCHAR2(255),
   fa_description_id     NUMBER DEFAULT 0 NOT NULL,
   fa_user               NUMBER          DEFAULT 0 NOT NULL,
-  fa_user_text          VARCHAR2(255)         NOT NULL,
+  fa_user_text          VARCHAR2(255)         NULL,
+  fa_actor              NUMBER          DEFAULT 0 NOT NULL,
   fa_timestamp          TIMESTAMP(6) WITH TIME ZONE,
   fa_deleted            NUMBER      DEFAULT 0 NOT NULL,
   fa_sha1              VARCHAR2(32)
@@ -569,6 +608,7 @@ CREATE INDEX &mw_prefix.filearchive_i01 ON &mw_prefix.filearchive (fa_name, fa_t
 CREATE INDEX &mw_prefix.filearchive_i02 ON &mw_prefix.filearchive (fa_storage_group, fa_storage_key);
 CREATE INDEX &mw_prefix.filearchive_i03 ON &mw_prefix.filearchive (fa_deleted_timestamp);
 CREATE INDEX &mw_prefix.filearchive_i04 ON &mw_prefix.filearchive (fa_user_text,fa_timestamp);
+CREATE INDEX &mw_prefix.fa_actor_timestamp ON &mw_prefix.filearchive (fa_actor,fa_timestamp);
 CREATE INDEX &mw_prefix.filearchive_i05 ON &mw_prefix.filearchive (fa_sha1);
 /*$mw$*/
 CREATE TRIGGER &mw_prefix.filearchive_seq_trg BEFORE INSERT ON &mw_prefix.filearchive
@@ -617,7 +657,8 @@ CREATE TABLE &mw_prefix.recentchanges (
   rc_timestamp       TIMESTAMP(6) WITH TIME ZONE  NOT NULL,
   rc_cur_time        TIMESTAMP(6) WITH TIME ZONE,
   rc_user            NUMBER          DEFAULT 0 NOT NULL,
-  rc_user_text       VARCHAR2(255)         NOT NULL,
+  rc_user_text       VARCHAR2(255)         NULL,
+  rc_actor           NUMBER          DEFAULT 0 NOT NULL,
   rc_namespace       NUMBER     DEFAULT 0 NOT NULL,
   rc_title           VARCHAR2(255)         NOT NULL,
   rc_comment         VARCHAR2(255),
@@ -651,6 +692,8 @@ CREATE INDEX &mw_prefix.recentchanges_i04 ON &mw_prefix.recentchanges (rc_new,rc
 CREATE INDEX &mw_prefix.recentchanges_i05 ON &mw_prefix.recentchanges (rc_ip);
 CREATE INDEX &mw_prefix.recentchanges_i06 ON &mw_prefix.recentchanges (rc_namespace, rc_user_text);
 CREATE INDEX &mw_prefix.recentchanges_i07 ON &mw_prefix.recentchanges (rc_user_text, rc_timestamp);
+CREATE INDEX &mw_prefix.rc_ns_actor ON &mw_prefix.recentchanges (rc_namespace, rc_actor);
+CREATE INDEX &mw_prefix.rc_actor ON &mw_prefix.recentchanges (rc_actor, rc_timestamp);
 CREATE INDEX &mw_prefix.recentchanges_i08 ON &mw_prefix.recentchanges (rc_namespace, rc_type, rc_patrolled, rc_timestamp);
 /*$mw$*/
 CREATE TRIGGER &mw_prefix.recentchanges_seq_trg BEFORE INSERT ON &mw_prefix.recentchanges
@@ -721,6 +764,7 @@ CREATE TABLE &mw_prefix.logging (
   log_timestamp   TIMESTAMP(6) WITH TIME ZONE  NOT NULL,
   log_user        NUMBER                DEFAULT 0 NOT NULL,
   log_user_text        VARCHAR2(255),
+  log_actor       NUMBER                DEFAULT 0 NOT NULL,
   log_namespace   NUMBER     DEFAULT 0 NOT NULL,
   log_title       VARCHAR2(255)         NOT NULL,
   log_page                             NUMBER,
@@ -739,6 +783,8 @@ CREATE INDEX &mw_prefix.logging_i04 ON &mw_prefix.logging (log_timestamp);
 CREATE INDEX &mw_prefix.logging_i05 ON &mw_prefix.logging (log_type, log_action, log_timestamp);
 CREATE INDEX &mw_prefix.logging_i06 ON &mw_prefix.logging (log_user_text, log_type, log_timestamp);
 CREATE INDEX &mw_prefix.logging_i07 ON &mw_prefix.logging (log_user_text, log_timestamp);
+CREATE INDEX &mw_prefix.actor_time ON &mw_prefix.logging (log_actor, log_timestamp);
+CREATE INDEX &mw_prefix.log_actor_type_time ON &mw_prefix.logging (log_actor, log_type, log_timestamp);
 /*$mw$*/
 CREATE TRIGGER &mw_prefix.logging_seq_trg BEFORE INSERT ON &mw_prefix.logging
        FOR EACH ROW WHEN (new.log_id IS NULL)
index 945df32..7acf6d8 100644 (file)
@@ -82,16 +82,18 @@ class Orphans extends Maintenance {
                }
 
                $commentQuery = $commentStore->getJoin( 'rev_comment' );
+               $actorQuery = ActorMigration::newMigration()->getJoin( 'rev_user' );
 
                $this->output( "Checking for orphan revision table entries... "
                        . "(this may take a while on a large wiki)\n" );
                $result = $dbw->select(
-                       [ 'revision', 'page' ] + $commentQuery['tables'],
-                       [ 'rev_id', 'rev_page', 'rev_timestamp', 'rev_user_text' ] + $commentQuery['fields'],
+                       [ 'revision', 'page' ] + $commentQuery['tables'] + $actorQuery['tables'],
+                       [ 'rev_id', 'rev_page', 'rev_timestamp' ] + $commentQuery['fields'] + $actorQuery['fields'],
                        [ 'page_id' => null ],
                        __METHOD__,
                        [],
                        [ 'page' => [ 'LEFT JOIN', [ 'rev_page=page_id' ] ] ] + $commentQuery['joins']
+                               + $actorQuery['joins']
                );
                $orphans = $result->numRows();
                if ( $orphans > 0 ) {
index fbe16de..7bb1605 100644 (file)
@@ -76,19 +76,31 @@ TEXT
                $end = $maxRevId > 0
                        ? $maxRevId
                        : $dbw->selectField( 'revision', 'MAX(rev_id)', false, __METHOD__ );
+
+               if ( empty( $end ) ) {
+                       $this->output( "No revisions found, aborting.\n" );
+                       return true;
+               }
+
                $blockStart = $start;
                $attempted = 0;
                $inserted = 0;
 
                $this->output( "Copying IP revisions to ip_changes, from rev_id $start to rev_id $end\n" );
 
+               $actorMigration = ActorMigration::newMigration();
+               $actorQuery = $actorMigration->getJoin( 'rev_user' );
+               $revUserIsAnon = $actorMigration->isAnon( $actorQuery['fields']['rev_user'] );
+
                while ( $blockStart <= $end ) {
                        $blockEnd = min( $blockStart + $this->getBatchSize(), $end );
                        $rows = $dbr->select(
-                               'revision',
-                               [ 'rev_id', 'rev_timestamp', 'rev_user_text' ],
-                               [ "rev_id BETWEEN " . (int)$blockStart . " AND " . (int)$blockEnd, 'rev_user' => 0 ],
-                               __METHOD__
+                               [ 'revision' ] + $actorQuery['tables'],
+                               [ 'rev_id', 'rev_timestamp', 'rev_user_text' => $actorQuery['fields']['rev_user_text'] ],
+                               [ "rev_id BETWEEN " . (int)$blockStart . " AND " . (int)$blockEnd, $revUserIsAnon ],
+                               __METHOD__,
+                               [],
+                               $actorQuery['joins']
                        );
 
                        $numRows = $rows->numRows();
index f960753..332d7c5 100644 (file)
@@ -53,6 +53,8 @@ class PopulateLogSearch extends LoggedUpdateMaintenance {
        }
 
        protected function doDBUpdates() {
+               global $wgActorTableSchemaMigrationStage;
+
                $batchSize = $this->getBatchSize();
                $db = $this->getDB( DB_MASTER );
                if ( !$db->tableExists( 'log_search' ) ) {
@@ -81,8 +83,8 @@ class PopulateLogSearch extends LoggedUpdateMaintenance {
                                'logging', [ 'log_id', 'log_type', 'log_action', 'log_params' ], $cond, __FUNCTION__
                        );
                        foreach ( $res as $row ) {
-                               // RevisionDelete logs - revisions
                                if ( LogEventsList::typeAction( $row, $delTypes, 'revision' ) ) {
+                                       // RevisionDelete logs - revisions
                                        $params = LogPage::extractParams( $row->log_params );
                                        // Param format: <urlparam> <item CSV> [<ofield> <nfield>]
                                        if ( count( $params ) < 2 ) {
@@ -107,30 +109,31 @@ class PopulateLogSearch extends LoggedUpdateMaintenance {
                                        $log = new LogPage( $row->log_type );
                                        // Add item relations...
                                        $log->addRelations( $field, $items, $row->log_id );
-                                       // Determine what table to query...
+                                       // Query item author relations...
                                        $prefix = substr( $field, 0, strpos( $field, '_' ) ); // db prefix
                                        if ( !isset( self::$tableMap[$prefix] ) ) {
                                                continue; // bad row?
                                        }
-                                       $table = self::$tableMap[$prefix];
-                                       $userField = $prefix . '_user';
-                                       $userTextField = $prefix . '_user_text';
-                                       // Add item author relations...
-                                       $userIds = $userIPs = [];
-                                       $sres = $db->select( $table,
-                                               [ $userField, $userTextField ],
-                                               [ $field => $items ]
-                                       );
-                                       foreach ( $sres as $srow ) {
-                                               if ( $srow->$userField > 0 ) {
-                                                       $userIds[] = intval( $srow->$userField );
-                                               } elseif ( $srow->$userTextField != '' ) {
-                                                       $userIPs[] = $srow->$userTextField;
+                                       $tables = [ self::$tableMap[$prefix] ];
+                                       $fields = [];
+                                       $joins = [];
+                                       if ( $wgActorTableSchemaMigrationStage < MIGRATION_NEW ) {
+                                               $fields['userid'] = $prefix . '_user';
+                                               $fields['username'] = $prefix . '_user_text';
+                                       }
+                                       if ( $wgActorTableSchemaMigrationStage > MIGRATION_OLD ) {
+                                               if ( $prefix === 'rev' ) {
+                                                       $tables[] = 'revision_actor_temp';
+                                                       $joins['revision_actor_temp'] = [
+                                                               $wgActorTableSchemaMigrationStage === MIGRATION_NEW ? 'JOIN' : 'LEFT JOIN',
+                                                               'rev_id = revactor_rev',
+                                                       ];
+                                                       $fields['actorid'] = 'revactor_actor';
+                                               } else {
+                                                       $fields['actorid'] = $prefix . '_actor';
                                                }
                                        }
-                                       // Add item author relations...
-                                       $log->addRelations( 'target_author_id', $userIds, $row->log_id );
-                                       $log->addRelations( 'target_author_ip', $userIPs, $row->log_id );
+                                       $sres = $db->select( $tables, $fields, [ $field => $items ], __METHOD__, [], $joins );
                                } elseif ( LogEventsList::typeAction( $row, $delTypes, 'event' ) ) {
                                        // RevisionDelete logs - log events
                                        $params = LogPage::extractParams( $row->log_params );
@@ -142,22 +145,49 @@ class PopulateLogSearch extends LoggedUpdateMaintenance {
                                        $log = new LogPage( $row->log_type );
                                        // Add item relations...
                                        $log->addRelations( 'log_id', $items, $row->log_id );
-                                       // Add item author relations...
-                                       $userIds = $userIPs = [];
-                                       $sres = $db->select( 'logging',
-                                               [ 'log_user', 'log_user_text' ],
-                                               [ 'log_id' => $items ]
-                                       );
-                                       foreach ( $sres as $srow ) {
-                                               if ( $srow->log_user > 0 ) {
-                                                       $userIds[] = intval( $srow->log_user );
-                                               } elseif ( IP::isIPAddress( $srow->log_user_text ) ) {
-                                                       $userIPs[] = $srow->log_user_text;
+                                       // Query item author relations...
+                                       $fields = [];
+                                       if ( $wgActorTableSchemaMigrationStage < MIGRATION_NEW ) {
+                                               $fields['userid'] = 'log_user';
+                                               $fields['username'] = 'log_user_text';
+                                       }
+                                       if ( $wgActorTableSchemaMigrationStage > MIGRATION_OLD ) {
+                                               $fields['actorid'] = 'log_actor';
+                                       }
+
+                                       $sres = $db->select( 'logging', $fields, [ 'log_id' => $items ], __METHOD__ );
+                               } else {
+                                       continue;
+                               }
+
+                               // Add item author relations...
+                               $userIds = $userIPs = $userActors = [];
+                               foreach ( $sres as $srow ) {
+                                       if ( $wgActorTableSchemaMigrationStage < MIGRATION_NEW ) {
+                                               if ( $srow->userid > 0 ) {
+                                                       $userIds[] = intval( $srow->userid );
+                                               } elseif ( $srow->username != '' ) {
+                                                       $userIPs[] = $srow->username;
                                                }
                                        }
+                                       if ( $wgActorTableSchemaMigrationStage > MIGRATION_OLD ) {
+                                               if ( $srow->actorid ) {
+                                                       $userActors[] = intval( $srow->actorid );
+                                               } elseif ( $srow->userid > 0 ) {
+                                                       $userActors[] = User::newFromId( $srow->userid )->getActorId( $db );
+                                               } else {
+                                                       $userActors[] = User::newFromName( $srow->username, false )->getActorId( $db );
+                                               }
+                                       }
+                               }
+                               // Add item author relations...
+                               if ( $wgActorTableSchemaMigrationStage <= MIGRATION_WRITE_BOTH ) {
                                        $log->addRelations( 'target_author_id', $userIds, $row->log_id );
                                        $log->addRelations( 'target_author_ip', $userIPs, $row->log_id );
                                }
+                               if ( $wgActorTableSchemaMigrationStage >= MIGRATION_WRITE_BOTH ) {
+                                       $log->addRelations( 'target_author_actor', $userActors, $row->log_id );
+                               }
                        }
                        $blockStart += $batchSize;
                        $blockEnd += $batchSize;
index 2fe7ea6..cacd067 100644 (file)
@@ -58,6 +58,14 @@ class PopulateLogUsertext extends LoggedUpdateMaintenance {
                }
                $end = $db->selectField( 'logging', 'MAX(log_id)', false, __METHOD__ );
 
+               // If this is being run during an upgrade from 1.16 or earlier, this
+               // will be run before the actor table change and should continue. But
+               // if it's being run on a new installation, the field won't exist to be populated.
+               if ( !$db->fieldInfo( 'logging', 'log_user_text' ) ) {
+                       $this->output( "No log_user_text field, nothing to do.\n" );
+                       return true;
+               }
+
                # Do remaining chunk
                $end += $batchSize - 1;
                $blockStart = $start;
@@ -77,7 +85,6 @@ class PopulateLogUsertext extends LoggedUpdateMaintenance {
                        $this->commitTransaction( $db, __METHOD__ );
                        $blockStart += $batchSize;
                        $blockEnd += $batchSize;
-                       wfWaitForSlaves();
                }
                $this->output( "Done populating log_user_text field.\n" );
 
index f83be9c..bcc4999 100644 (file)
@@ -117,7 +117,6 @@ class PopulateRevisionLength extends LoggedUpdateMaintenance {
 
                        $blockStart += $batchSize;
                        $blockEnd += $batchSize;
-                       wfWaitForSlaves();
                }
 
                return $count;
index 54937ab..d2372a9 100644 (file)
@@ -109,7 +109,6 @@ class PopulateRevisionSha1 extends LoggedUpdateMaintenance {
 
                        $blockStart += $batchSize;
                        $blockEnd += $batchSize;
-                       wfWaitForSlaves();
                }
 
                return $count;
@@ -135,7 +134,6 @@ class PopulateRevisionSha1 extends LoggedUpdateMaintenance {
                                $updateSize = 0;
                                $this->commitTransaction( $db, __METHOD__ );
                                $this->output( "Commited row with ar_timestamp={$row->ar_timestamp}\n" );
-                               wfWaitForSlaves();
                                $this->beginTransaction( $db, __METHOD__ );
                        }
                }
diff --git a/maintenance/postgres/archives/patch-actor-table.sql b/maintenance/postgres/archives/patch-actor-table.sql
new file mode 100644 (file)
index 0000000..68e5d26
--- /dev/null
@@ -0,0 +1,24 @@
+--
+-- patch-actor-table.sql
+--
+-- T167246. Add an `actor` table and various columns (and temporary tables) to reference it.
+
+CREATE SEQUENCE actor_actor_id_seq;
+CREATE TABLE actor (
+  actor_id INTEGER NOT NULL PRIMARY KEY DEFAULT nextval('actor_actor_id_seq'),
+  actor_user INTEGER,
+  actor_name TEXT NOT NULL
+);
+CREATE UNIQUE INDEX actor_user ON actor (actor_user);
+CREATE UNIQUE INDEX actor_name ON actor (actor_name);
+
+CREATE TABLE revision_actor_temp (
+  revactor_rev INTEGER NOT NULL,
+  revactor_actor INTEGER NOT NULL,
+  revactor_timestamp TIMESTAMPTZ  NOT NULL,
+  revactor_page INTEGER          NULL  REFERENCES page (page_id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
+  PRIMARY KEY (revactor_rev, revactor_actor)
+);
+CREATE UNIQUE INDEX revactor_rev ON revision_actor_temp (revactor_rev);
+CREATE INDEX rev_actor_timestamp ON revision_actor_temp (revactor_actor,revactor_timestamp);
+CREATE INDEX rev_page_actor_timestamp ON revision_actor_temp (revactor_page,revactor_actor,revactor_timestamp);
index 34de2cb..01177d8 100644 (file)
@@ -10,6 +10,7 @@ BEGIN;
 SET client_min_messages = 'ERROR';
 
 DROP SEQUENCE IF EXISTS user_user_id_seq CASCADE;
+DROP SEQUENCE IF EXISTS actor_actor_id_seq CASCADE;
 DROP SEQUENCE IF EXISTS page_page_id_seq CASCADE;
 DROP SEQUENCE IF EXISTS revision_rev_id_seq CASCADE;
 DROP SEQUENCE IF EXISTS comment_comment_id_seq CASCADE;
@@ -58,6 +59,15 @@ CREATE INDEX user_email_token_idx ON mwuser (user_email_token);
 INSERT INTO mwuser
   VALUES (DEFAULT,'Anonymous','',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,now(),now());
 
+CREATE SEQUENCE actor_actor_id_seq;
+CREATE TABLE actor (
+  actor_id      INTEGER  NOT NULL  PRIMARY KEY DEFAULT nextval('actor_actor_id_seq'),
+  actor_user INTEGER,
+  actor_name    TEXT     NOT NULL
+);
+CREATE UNIQUE INDEX actor_user ON actor (actor_user);
+CREATE UNIQUE INDEX actor_name ON actor (actor_name);
+
 CREATE TABLE user_groups (
   ug_user    INTEGER          NULL  REFERENCES mwuser(user_id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
   ug_group   TEXT         NOT NULL,
@@ -134,8 +144,8 @@ CREATE TABLE revision (
   rev_page           INTEGER          NULL  REFERENCES page (page_id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
   rev_text_id        INTEGER          NULL, -- FK
   rev_comment        TEXT         NOT NULL DEFAULT '',
-  rev_user           INTEGER      NOT NULL  REFERENCES mwuser(user_id) ON DELETE RESTRICT DEFERRABLE INITIALLY DEFERRED,
-  rev_user_text      TEXT         NOT NULL,
+  rev_user           INTEGER      NOT NULL DEFAULT 0 REFERENCES mwuser(user_id) ON DELETE RESTRICT DEFERRABLE INITIALLY DEFERRED,
+  rev_user_text      TEXT         NOT NULL DEFAULT '',
   rev_timestamp      TIMESTAMPTZ  NOT NULL,
   rev_minor_edit     SMALLINT     NOT NULL  DEFAULT 0,
   rev_deleted        SMALLINT     NOT NULL  DEFAULT 0,
@@ -158,6 +168,17 @@ CREATE TABLE revision_comment_temp (
 );
 CREATE UNIQUE INDEX revcomment_rev ON revision_comment_temp (revcomment_rev);
 
+CREATE TABLE revision_actor_temp (
+  revactor_rev       INTEGER NOT NULL,
+  revactor_actor     INTEGER NOT NULL,
+  revactor_timestamp TIMESTAMPTZ  NOT NULL,
+  revactor_page      INTEGER          NULL  REFERENCES page (page_id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
+  PRIMARY KEY (revactor_rev, revactor_actor)
+);
+CREATE UNIQUE INDEX revactor_rev ON revision_actor_temp (revactor_rev);
+CREATE INDEX rev_actor_timestamp ON revision_actor_temp (revactor_actor,revactor_timestamp);
+CREATE INDEX rev_page_actor_timestamp ON revision_actor_temp (revactor_page,revactor_actor,revactor_timestamp);
+
 CREATE SEQUENCE ip_changes_ipc_rev_id_seq;
 
 CREATE TABLE ip_changes (
@@ -221,8 +242,9 @@ CREATE TABLE archive (
   ar_sha1           TEXT         NOT NULL DEFAULT '',
   ar_comment        TEXT         NOT NULL DEFAULT '',
   ar_comment_id     INTEGER      NOT NULL DEFAULT 0,
-  ar_user           INTEGER          NULL  REFERENCES mwuser(user_id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
-  ar_user_text      TEXT         NOT NULL,
+  ar_user           INTEGER      NOT NULL DEFAULT 0 REFERENCES mwuser(user_id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
+  ar_user_text      TEXT         NOT NULL DEFAULT '',
+  ar_actor          INTEGER      NOT NULL DEFAULT 0,
   ar_timestamp      TIMESTAMPTZ  NOT NULL,
   ar_minor_edit     SMALLINT     NOT NULL  DEFAULT 0,
   ar_flags          TEXT,
@@ -235,6 +257,7 @@ CREATE TABLE archive (
 );
 CREATE INDEX archive_name_title_timestamp ON archive (ar_namespace,ar_title,ar_timestamp);
 CREATE INDEX archive_user_text            ON archive (ar_user_text);
+CREATE INDEX archive_actor                ON archive (ar_actor);
 
 
 CREATE TABLE slots (
@@ -362,8 +385,9 @@ CREATE TABLE ipblocks (
   ipb_id                INTEGER      NOT NULL  PRIMARY KEY DEFAULT nextval('ipblocks_ipb_id_seq'),
   ipb_address           TEXT             NULL,
   ipb_user              INTEGER          NULL  REFERENCES mwuser(user_id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
-  ipb_by                INTEGER      NOT NULL  REFERENCES mwuser(user_id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
+  ipb_by                INTEGER      NOT NULL  DEFAULT 0 REFERENCES mwuser(user_id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
   ipb_by_text           TEXT         NOT NULL  DEFAULT '',
+  ipb_by_actor          INTEGER      NOT NULL  DEFAULT 0,
   ipb_reason            TEXT         NOT NULL  DEFAULT '',
   ipb_reason_id         INTEGER      NOT NULL  DEFAULT 0,
   ipb_timestamp         TIMESTAMPTZ  NOT NULL,
@@ -397,8 +421,9 @@ CREATE TABLE image (
   img_major_mime   TEXT                DEFAULT 'unknown',
   img_minor_mime   TEXT                DEFAULT 'unknown',
   img_description  TEXT      NOT NULL  DEFAULT '',
-  img_user         INTEGER       NULL  REFERENCES mwuser(user_id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
-  img_user_text    TEXT      NOT NULL,
+  img_user         INTEGER   NOT NULL  DEFAULT 0 REFERENCES mwuser(user_id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
+  img_user_text    TEXT      NOT NULL  DEFAULT '',
+  img_actor        INTEGER   NOT NULL  DEFAULT 0,
   img_timestamp    TIMESTAMPTZ,
   img_sha1         TEXT      NOT NULL  DEFAULT ''
 );
@@ -422,8 +447,9 @@ CREATE TABLE oldimage (
   oi_bits          SMALLINT         NULL,
   oi_description   TEXT         NOT NULL DEFAULT '',
   oi_description_id INTEGER     NOT NULL DEFAULT 0,
-  oi_user          INTEGER          NULL  REFERENCES mwuser(user_id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
-  oi_user_text     TEXT         NOT NULL,
+  oi_user          INTEGER      NOT NULL DEFAULT 0  REFERENCES mwuser(user_id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
+  oi_user_text     TEXT         NOT NULL DEFAULT '',
+  oi_actor         INTEGER      NOT NULL DEFAULT 0,
   oi_timestamp     TIMESTAMPTZ      NULL,
   oi_metadata      BYTEA        NOT NULL DEFAULT '',
   oi_media_type    TEXT             NULL,
@@ -459,8 +485,9 @@ CREATE TABLE filearchive (
   fa_minor_mime         TEXT                   DEFAULT 'unknown',
   fa_description        TEXT         NOT NULL DEFAULT '',
   fa_description_id     INTEGER      NOT NULL DEFAULT 0,
-  fa_user               INTEGER          NULL  REFERENCES mwuser(user_id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
-  fa_user_text          TEXT         NOT NULL,
+  fa_user               INTEGER      NOT NULL DEFAULT 0 REFERENCES mwuser(user_id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
+  fa_user_text          TEXT         NOT NULL DEFAULT '',
+  fa_actor              INTEGER      NOT NULL DEFAULT 0,
   fa_timestamp          TIMESTAMPTZ,
   fa_deleted            SMALLINT     NOT NULL DEFAULT 0,
   fa_sha1               TEXT         NOT NULL DEFAULT ''
@@ -504,8 +531,9 @@ CREATE TABLE recentchanges (
   rc_id              INTEGER      NOT NULL  PRIMARY KEY DEFAULT nextval('recentchanges_rc_id_seq'),
   rc_timestamp       TIMESTAMPTZ  NOT NULL,
   rc_cur_time        TIMESTAMPTZ      NULL,
-  rc_user            INTEGER          NULL  REFERENCES mwuser(user_id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
-  rc_user_text       TEXT         NOT NULL,
+  rc_user            INTEGER      NOT NULL  DEFAULT 0 REFERENCES mwuser(user_id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
+  rc_user_text       TEXT         NOT NULL  DEFAULT '',
+  rc_actor           INTEGER      NOT NULL  DEFAULT 0,
   rc_namespace       SMALLINT     NOT NULL,
   rc_title           TEXT         NOT NULL,
   rc_comment         TEXT         NOT NULL  DEFAULT '',
@@ -605,7 +633,8 @@ CREATE TABLE logging (
   log_type        TEXT         NOT NULL,
   log_action      TEXT         NOT NULL,
   log_timestamp   TIMESTAMPTZ  NOT NULL,
-  log_user        INTEGER                REFERENCES mwuser(user_id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
+  log_user        INTEGER      NOT NULL DEFAULT 0 REFERENCES mwuser(user_id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
+  log_actor       INTEGER      NOT NULL DEFAULT 0,
   log_namespace   SMALLINT     NOT NULL,
   log_title       TEXT         NOT NULL,
   log_comment     TEXT         NOT NULL DEFAULT '',
@@ -617,12 +646,15 @@ CREATE TABLE logging (
 );
 CREATE INDEX logging_type_name ON logging (log_type, log_timestamp);
 CREATE INDEX logging_user_time ON logging (log_timestamp, log_user);
+CREATE INDEX logging_actor_time_backwards ON logging (log_timestamp, log_actor);
 CREATE INDEX logging_page_time ON logging (log_namespace, log_title, log_timestamp);
 CREATE INDEX logging_times ON logging (log_timestamp);
 CREATE INDEX logging_user_type_time ON logging (log_user, log_type, log_timestamp);
+CREATE INDEX logging_actor_type_time ON logging (log_actor, log_type, log_timestamp);
 CREATE INDEX logging_page_id_time ON logging (log_page, log_timestamp);
 CREATE INDEX logging_user_text_type_time ON logging (log_user_text, log_type, log_timestamp);
 CREATE INDEX logging_user_text_time ON logging (log_user_text, log_timestamp);
+CREATE INDEX logging_actor_time ON logging (log_actor, log_timestamp);
 
 CREATE TABLE log_search (
   ls_field   TEXT     NOT NULL,
index 6f88f3d..c91d4ed 100644 (file)
@@ -23,6 +23,8 @@
  * @licence GNU General Public Licence 2.0 or later
  */
 
+use Wikimedia\Rdbms\IDatabase;
+
 require_once __DIR__ . '/Maintenance.php';
 
 /**
@@ -74,27 +76,35 @@ class ReassignEdits extends Maintenance {
         * @return int Number of entries changed, or that would be changed
         */
        private function doReassignEdits( &$from, &$to, $rc = false, $report = false ) {
+               global $wgActorTableSchemaMigrationStage;
+
                $dbw = $this->getDB( DB_MASTER );
                $this->beginTransaction( $dbw, __METHOD__ );
 
                # Count things
                $this->output( "Checking current edits..." );
+               $revQueryInfo = ActorMigration::newMigration()->getWhere( $dbw, 'rev_user', $from );
                $res = $dbw->select(
-                       'revision',
+                       [ 'revision' ] + $revQueryInfo['tables'],
                        'COUNT(*) AS count',
-                       $this->userConditions( $from, 'rev_user', 'rev_user_text' ),
-                       __METHOD__
+                       $revQueryInfo['conds'],
+                       __METHOD__,
+                       [],
+                       $revQueryInfo['joins']
                );
                $row = $dbw->fetchObject( $res );
                $cur = $row->count;
                $this->output( "found {$cur}.\n" );
 
                $this->output( "Checking deleted edits..." );
+               $arQueryInfo = ActorMigration::newMigration()->getWhere( $dbw, 'ar_user', $from, false );
                $res = $dbw->select(
-                       'archive',
+                       [ 'archive' ] + $arQueryInfo['tables'],
                        'COUNT(*) AS count',
-                       $this->userConditions( $from, 'ar_user', 'ar_user_text' ),
-                       __METHOD__
+                       $arQueryInfo['conds'],
+                       __METHOD__,
+                       [],
+                       $arQueryInfo['joins']
                );
                $row = $dbw->fetchObject( $res );
                $del = $row->count;
@@ -103,11 +113,14 @@ class ReassignEdits extends Maintenance {
                # Don't count recent changes if we're not supposed to
                if ( $rc ) {
                        $this->output( "Checking recent changes..." );
+                       $rcQueryInfo = ActorMigration::newMigration()->getWhere( $dbw, 'rc_user', $from, false );
                        $res = $dbw->select(
-                               'recentchanges',
+                               [ 'recentchanges' ] + $rcQueryInfo['tables'],
                                'COUNT(*) AS count',
-                               $this->userConditions( $from, 'rc_user', 'rc_user_text' ),
-                               __METHOD__
+                               $rcQueryInfo['conds'],
+                               __METHOD__,
+                               [],
+                               $rcQueryInfo['joins']
                        );
                        $row = $dbw->fetchObject( $res );
                        $rec = $row->count;
@@ -123,17 +136,38 @@ class ReassignEdits extends Maintenance {
                        if ( $total ) {
                                # Reassign edits
                                $this->output( "\nReassigning current edits..." );
-                               $dbw->update( 'revision', $this->userSpecification( $to, 'rev_user', 'rev_user_text' ),
-                                       $this->userConditions( $from, 'rev_user', 'rev_user_text' ), __METHOD__ );
+                               if ( $wgActorTableSchemaMigrationStage < MIGRATION_NEW ) {
+                                       $dbw->update(
+                                               'revision',
+                                               [
+                                                       'rev_user' => $to->getId(),
+                                                       'rev_user_text' =>
+                                                               $wgActorTableSchemaMigrationStage <= MIGRATION_WRITE_BOTH ? $to->getName() : ''
+                                               ],
+                                               $from->isLoggedIn()
+                                                       ? [ 'rev_user' => $from->getId() ] : [ 'rev_user_text' => $from->getName() ],
+                                               __METHOD__
+                                       );
+                               }
+                               if ( $wgActorTableSchemaMigrationStage > MIGRATION_OLD ) {
+                                       $dbw->update(
+                                               'revision_actor_temp',
+                                               [ 'revactor_actor' => $to->getActorId( $dbw ) ],
+                                               [ 'revactor_actor' => $from->getActorId() ],
+                                               __METHOD__
+                                       );
+                               }
                                $this->output( "done.\nReassigning deleted edits..." );
-                               $dbw->update( 'archive', $this->userSpecification( $to, 'ar_user', 'ar_user_text' ),
-                                       $this->userConditions( $from, 'ar_user', 'ar_user_text' ), __METHOD__ );
+                               $dbw->update( 'archive',
+                                       $this->userSpecification( $dbw, $to, 'ar_user', 'ar_user_text', 'ar_actor' ),
+                                       [ $arQueryInfo['conds'] ], __METHOD__ );
                                $this->output( "done.\n" );
                                # Update recent changes if required
                                if ( $rc ) {
                                        $this->output( "Updating recent changes..." );
-                                       $dbw->update( 'recentchanges', $this->userSpecification( $to, 'rc_user', 'rc_user_text' ),
-                                               $this->userConditions( $from, 'rc_user', 'rc_user_text' ), __METHOD__ );
+                                       $dbw->update( 'recentchanges',
+                                               $this->userSpecification( $dbw, $to, 'rc_user', 'rc_user_text', 'rc_actor' ),
+                                               [ $rcQueryInfo['conds'] ], __METHOD__ );
                                        $this->output( "done.\n" );
                                }
                        }
@@ -144,32 +178,31 @@ class ReassignEdits extends Maintenance {
                return (int)$total;
        }
 
-       /**
-        * Return the most efficient set of user conditions
-        * i.e. a user => id mapping, or a user_text => text mapping
-        *
-        * @param User $user User for the condition
-        * @param string $idfield Field name containing the identifier
-        * @param string $utfield Field name containing the user text
-        * @return array
-        */
-       private function userConditions( &$user, $idfield, $utfield ) {
-               return $user->getId()
-                       ? [ $idfield => $user->getId() ]
-                       : [ $utfield => $user->getName() ];
-       }
-
        /**
         * Return user specifications
         * i.e. user => id, user_text => text
         *
+        * @param IDatabase $dbw Database handle
         * @param User $user User for the spec
         * @param string $idfield Field name containing the identifier
         * @param string $utfield Field name containing the user text
+        * @param string $acfield Field name containing the actor ID
         * @return array
         */
-       private function userSpecification( &$user, $idfield, $utfield ) {
-               return [ $idfield => $user->getId(), $utfield => $user->getName() ];
+       private function userSpecification( IDatabase $dbw, &$user, $idfield, $utfield, $acfield ) {
+               global $wgActorTableSchemaMigrationStage;
+
+               $ret = [];
+               if ( $wgActorTableSchemaMigrationStage < MIGRATION_NEW ) {
+                       $ret += [
+                               $idfield => $user->getId(),
+                               $utfield => $wgActorTableSchemaMigrationStage <= MIGRATION_WRITE_BOTH ? $user->getName() : '',
+                       ];
+               }
+               if ( $wgActorTableSchemaMigrationStage > MIGRATION_OLD ) {
+                       $ret += [ $acfield => $user->getActorId( $dbw ) ];
+               }
+               return $ret;
        }
 
        /**
index 672bae8..6c7dbff 100644 (file)
@@ -116,12 +116,11 @@ class RebuildRecentchanges extends Maintenance {
                $this->output( "Loading from page and revision tables...\n" );
 
                $commentQuery = $commentStore->getJoin( 'rev_comment' );
+               $actorQuery = ActorMigration::newMigration()->getJoin( 'rev_user' );
                $res = $dbw->select(
-                       [ 'revision', 'page' ] + $commentQuery['tables'],
+                       [ 'revision', 'page' ] + $commentQuery['tables'] + $actorQuery['tables'],
                        [
                                'rev_timestamp',
-                               'rev_user',
-                               'rev_user_text',
                                'rev_minor_edit',
                                'rev_id',
                                'rev_deleted',
@@ -129,7 +128,7 @@ class RebuildRecentchanges extends Maintenance {
                                'page_title',
                                'page_is_new',
                                'page_id'
-                       ] + $commentQuery['fields'],
+                       ] + $commentQuery['fields'] + $actorQuery['fields'],
                        [
                                'rev_timestamp > ' . $dbw->addQuotes( $dbw->timestamp( $this->cutoffFrom ) ),
                                'rev_timestamp < ' . $dbw->addQuotes( $dbw->timestamp( $this->cutoffTo ) )
@@ -138,19 +137,19 @@ class RebuildRecentchanges extends Maintenance {
                        [ 'ORDER BY' => 'rev_timestamp DESC' ],
                        [
                                'page' => [ 'JOIN', 'rev_page=page_id' ],
-                       ] + $commentQuery['joins']
+                       ] + $commentQuery['joins'] + $actorQuery['joins']
                );
 
                $this->output( "Inserting from page and revision tables...\n" );
                $inserted = 0;
+               $actorMigration = ActorMigration::newMigration();
                foreach ( $res as $row ) {
                        $comment = $commentStore->getComment( 'rev_comment', $row );
+                       $user = User::newFromAnyId( $row->rev_user, $row->rev_user_text, $row->rev_actor );
                        $dbw->insert(
                                'recentchanges',
                                [
                                        'rc_timestamp' => $row->rev_timestamp,
-                                       'rc_user' => $row->rev_user,
-                                       'rc_user_text' => $row->rev_user_text,
                                        'rc_namespace' => $row->page_namespace,
                                        'rc_title' => $row->page_title,
                                        'rc_minor' => $row->rev_minor_edit,
@@ -162,7 +161,8 @@ class RebuildRecentchanges extends Maintenance {
                                        'rc_type' => $row->page_is_new ? RC_NEW : RC_EDIT,
                                        'rc_source' => $row->page_is_new ? RecentChange::SRC_NEW : RecentChange::SRC_EDIT,
                                        'rc_deleted' => $row->rev_deleted
-                               ] + $commentStore->insert( $dbw, 'rc_comment', $comment ),
+                               ] + $commentStore->insert( $dbw, 'rc_comment', $comment )
+                                       + $actorMigration->getInsertValues( $dbw, 'rc_user', $user ),
                                __METHOD__
                        );
                        if ( ( ++$inserted % $this->getBatchSize() ) == 0 ) {
@@ -274,12 +274,11 @@ class RebuildRecentchanges extends Maintenance {
                $this->output( "Loading from user, page, and logging tables...\n" );
 
                $commentQuery = $commentStore->getJoin( 'log_comment' );
+               $actorQuery = ActorMigration::newMigration()->getJoin( 'log_user' );
                $res = $dbw->select(
-                       [ 'user', 'logging', 'page' ] + $commentQuery['tables'],
+                       [ 'logging', 'page' ] + $commentQuery['tables'] + $actorQuery['tables'],
                        [
                                'log_timestamp',
-                               'log_user',
-                               'user_name',
                                'log_namespace',
                                'log_title',
                                'page_id',
@@ -288,11 +287,10 @@ class RebuildRecentchanges extends Maintenance {
                                'log_id',
                                'log_params',
                                'log_deleted'
-                       ] + $commentQuery['fields'],
+                       ] + $commentQuery['fields'] + $actorQuery['fields'],
                        [
                                'log_timestamp > ' . $dbw->addQuotes( $dbw->timestamp( $this->cutoffFrom ) ),
                                'log_timestamp < ' . $dbw->addQuotes( $dbw->timestamp( $this->cutoffTo ) ),
-                               'log_user=user_id',
                                // Some logs don't go in RC since they are private.
                                // @FIXME: core/extensions also have spammy logs that don't go in RC.
                                'log_type' => array_diff( $wgLogTypes, array_keys( $wgLogRestrictions ) ),
@@ -302,20 +300,20 @@ class RebuildRecentchanges extends Maintenance {
                        [
                                'page' =>
                                        [ 'LEFT JOIN', [ 'log_namespace=page_namespace', 'log_title=page_title' ] ]
-                       ] + $commentQuery['joins']
+                       ] + $commentQuery['joins'] + $actorQuery['joins']
                );
 
                $field = $dbw->fieldInfo( 'recentchanges', 'rc_cur_id' );
 
                $inserted = 0;
+               $actorMigration = ActorMigration::newMigration();
                foreach ( $res as $row ) {
                        $comment = $commentStore->getComment( 'log_comment', $row );
+                       $user = User::newFromAnyId( $row->log_user, $row->log_user_text, $row->log_actor );
                        $dbw->insert(
                                'recentchanges',
                                [
                                        'rc_timestamp' => $row->log_timestamp,
-                                       'rc_user' => $row->log_user,
-                                       'rc_user_text' => $row->user_name,
                                        'rc_namespace' => $row->log_namespace,
                                        'rc_title' => $row->log_title,
                                        'rc_minor' => 0,
@@ -334,7 +332,8 @@ class RebuildRecentchanges extends Maintenance {
                                        'rc_logid' => $row->log_id,
                                        'rc_params' => $row->log_params,
                                        'rc_deleted' => $row->log_deleted
-                               ] + $commentStore->insert( $dbw, 'rc_comment', $comment ),
+                               ] + $commentStore->insert( $dbw, 'rc_comment', $comment )
+                                       + $actorMigration->getInsertValues( $dbw, 'rc_user', $user ),
                                __METHOD__
                        );
 
@@ -352,8 +351,7 @@ class RebuildRecentchanges extends Maintenance {
 
                $dbw = $this->getDB( DB_MASTER );
 
-               list( $recentchanges, $usergroups, $user ) =
-                       $dbw->tableNamesN( 'recentchanges', 'user_groups', 'user' );
+               $userQuery = User::getQueryInfo();
 
                # @FIXME: recognize other bot account groups (not the same as users with 'bot' rights)
                # @NOTE: users with 'bot' rights choose when edits are bot edits or not. That information
@@ -363,32 +361,42 @@ class RebuildRecentchanges extends Maintenance {
 
                # Flag our recent bot edits
                if ( $botgroups ) {
-                       $botwhere = $dbw->makeList( $botgroups );
-
                        $this->output( "Flagging bot account edits...\n" );
 
                        # Find all users that are bots
-                       $sql = "SELECT DISTINCT user_name FROM $usergroups, $user " .
-                               "WHERE ug_group IN($botwhere) AND user_id = ug_user";
-                       $res = $dbw->query( $sql, __METHOD__ );
+                       $res = $dbw->select(
+                               array_merge( [ 'user_groups' ], $userQuery['tables'] ),
+                               $userQuery['fields'],
+                               [ 'ug_group' => $botgroups ],
+                               __METHOD__,
+                               [ 'DISTINCT' ],
+                               [ 'user_group' => [ 'JOIN', 'user_id = ug_user' ] ] + $userQuery['joins']
+                       );
 
                        $botusers = [];
                        foreach ( $res as $obj ) {
-                               $botusers[] = $obj->user_name;
+                               $botusers[] = User::newFromRow( $obj );
                        }
 
                        # Fill in the rc_bot field
                        if ( $botusers ) {
-                               $rcids = $dbw->selectFieldValues(
-                                       'recentchanges',
-                                       'rc_id',
-                                       [
-                                               'rc_user_text' => $botusers,
-                                               "rc_timestamp > " . $dbw->addQuotes( $dbw->timestamp( $this->cutoffFrom ) ),
-                                               "rc_timestamp < " . $dbw->addQuotes( $dbw->timestamp( $this->cutoffTo ) )
-                                       ],
-                                       __METHOD__
-                               );
+                               $actorQuery = ActorMigration::newMigration()->getWhere( $dbw, 'rc_user', $botusers, false );
+                               $rcids = [];
+                               foreach ( $actorQuery['orconds'] as $cond ) {
+                                       $rcids = array_merge( $rcids, $dbw->selectFieldValues(
+                                               [ 'recentchanges' ] + $actorQuery['tables'],
+                                               'rc_id',
+                                               [
+                                                       "rc_timestamp > " . $dbw->addQuotes( $dbw->timestamp( $this->cutoffFrom ) ),
+                                                       "rc_timestamp < " . $dbw->addQuotes( $dbw->timestamp( $this->cutoffTo ) ),
+                                                       $cond,
+                                               ],
+                                               __METHOD__,
+                                               [],
+                                               $actorQuery['joins']
+                                       ) );
+                               }
+                               $rcids = array_values( array_unique( $rcids ) );
 
                                foreach ( array_chunk( $rcids, $this->getBatchSize() ) as $rcidBatch ) {
                                        $dbw->update(
@@ -404,28 +412,40 @@ class RebuildRecentchanges extends Maintenance {
 
                # Flag our recent autopatrolled edits
                if ( !$wgMiserMode && $autopatrolgroups ) {
-                       $patrolwhere = $dbw->makeList( $autopatrolgroups );
                        $patrolusers = [];
 
                        $this->output( "Flagging auto-patrolled edits...\n" );
 
                        # Find all users in RC with autopatrol rights
-                       $sql = "SELECT DISTINCT user_name FROM $usergroups, $user " .
-                               "WHERE ug_group IN($patrolwhere) AND user_id = ug_user";
-                       $res = $dbw->query( $sql, __METHOD__ );
+                       $res = $dbw->select(
+                               array_merge( [ 'user_groups' ], $userQuery['tables'] ),
+                               $userQuery['fields'],
+                               [ 'ug_group' => $autopatrolgroups ],
+                               __METHOD__,
+                               [ 'DISTINCT' ],
+                               [ 'user_group' => [ 'JOIN', 'user_id = ug_user' ] ] + $userQuery['joins']
+                       );
 
                        foreach ( $res as $obj ) {
-                               $patrolusers[] = $dbw->addQuotes( $obj->user_name );
+                               $patrolusers[] = User::newFromRow( $obj );
                        }
 
                        # Fill in the rc_patrolled field
                        if ( $patrolusers ) {
-                               $patrolwhere = implode( ',', $patrolusers );
-                               $sql2 = "UPDATE $recentchanges SET rc_patrolled=1 " .
-                                       "WHERE rc_user_text IN($patrolwhere) " .
-                                       "AND rc_timestamp > " . $dbw->addQuotes( $dbw->timestamp( $this->cutoffFrom ) ) . ' ' .
-                                       "AND rc_timestamp < " . $dbw->addQuotes( $dbw->timestamp( $this->cutoffTo ) );
-                               $dbw->query( $sql2 );
+                               $actorQuery = ActorMigration::newMigration()->getWhere( $dbw, 'rc_user', $patrolusers, false );
+                               foreach ( $actorQuery['orconds'] as $cond ) {
+                                       $dbw->update(
+                                               'recentchanges',
+                                               [ 'rc_patrolled' => 1 ],
+                                               [
+                                                       $cond,
+                                                       'rc_timestamp > ' . $dbw->addQuotes( $dbw->timestamp( $this->cutoffFrom ) ),
+                                                       'rc_timestamp < ' . $dbw->addQuotes( $dbw->timestamp( $this->cutoffTo ) ),
+                                               ],
+                                               __METHOD__
+                                       );
+                                       wfGetLBFactory()->waitForReplication();
+                               }
                        }
                }
        }
index 0910c9c..e066300 100644 (file)
@@ -39,13 +39,27 @@ class RemoveUnusedAccounts extends Maintenance {
        }
 
        public function execute() {
+               global $wgActorTableSchemaMigrationStage;
+
                $this->output( "Remove unused accounts\n\n" );
 
                # Do an initial scan for inactive accounts and report the result
                $this->output( "Checking for unused user accounts...\n" );
-               $del = [];
+               $delUser = [];
+               $delActor = [];
                $dbr = $this->getDB( DB_REPLICA );
-               $res = $dbr->select( 'user', [ 'user_id', 'user_name', 'user_touched' ], '', __METHOD__ );
+               if ( $wgActorTableSchemaMigrationStage > MIGRATION_OLD ) {
+                       $res = $dbr->select(
+                               [ 'user', 'actor' ],
+                               [ 'user_id', 'user_name', 'user_touched', 'actor_id' ],
+                               '',
+                               __METHOD__,
+                               [],
+                               [ 'actor' => [ 'LEFT JOIN', 'user_id = actor_user' ] ]
+                       );
+               } else {
+                       $res = $dbr->select( 'user', [ 'user_id', 'user_name', 'user_touched' ], '', __METHOD__ );
+               }
                if ( $this->hasOption( 'ignore-groups' ) ) {
                        $excludedGroups = explode( ',', $this->getOption( 'ignore-groups' ) );
                } else {
@@ -61,27 +75,49 @@ class RemoveUnusedAccounts extends Maintenance {
                        # group or if it's touched within the $touchedSeconds seconds.
                        $instance = User::newFromId( $row->user_id );
                        if ( count( array_intersect( $instance->getEffectiveGroups(), $excludedGroups ) ) == 0
-                               && $this->isInactiveAccount( $row->user_id, true )
+                               && $this->isInactiveAccount( $row->user_id, $row->actor_id, true )
                                && wfTimestamp( TS_UNIX, $row->user_touched ) < wfTimestamp( TS_UNIX, time() - $touchedSeconds )
                        ) {
                                # Inactive; print out the name and flag it
-                               $del[] = $row->user_id;
+                               $delUser[] = $row->user_id;
+                               if ( $row->actor_id ) {
+                                       $delActor[] = $row->actor_id;
+                               }
                                $this->output( $row->user_name . "\n" );
                        }
                }
-               $count = count( $del );
+               $count = count( $delUser );
                $this->output( "...found {$count}.\n" );
 
                # If required, go back and delete each marked account
                if ( $count > 0 && $this->hasOption( 'delete' ) ) {
                        $this->output( "\nDeleting unused accounts..." );
                        $dbw = $this->getDB( DB_MASTER );
-                       $dbw->delete( 'user', [ 'user_id' => $del ], __METHOD__ );
-                       $dbw->delete( 'user_groups', [ 'ug_user' => $del ], __METHOD__ );
-                       $dbw->delete( 'user_former_groups', [ 'ufg_user' => $del ], __METHOD__ );
-                       $dbw->delete( 'user_properties', [ 'up_user' => $del ], __METHOD__ );
-                       $dbw->delete( 'logging', [ 'log_user' => $del ], __METHOD__ );
-                       $dbw->delete( 'recentchanges', [ 'rc_user' => $del ], __METHOD__ );
+                       $dbw->delete( 'user', [ 'user_id' => $delUser ], __METHOD__ );
+                       if ( $wgActorTableSchemaMigrationStage > MIGRATION_OLD ) {
+                               # Keep actor rows referenced from ipblocks
+                               $keep = $dbw->selectFieldValues(
+                                       'ipblocks', 'ipb_by_actor', [ 'ipb_by_actor' => $delActor ], __METHOD__
+                               );
+                               $del = array_diff( $delActor, $keep );
+                               if ( $del ) {
+                                       $dbw->delete( 'actor', [ 'actor_id' => $del ], __METHOD__ );
+                               }
+                               if ( $keep ) {
+                                       $dbw->update( 'actor', [ 'actor_user' => 0 ], [ 'actor_id' => $keep ], __METHOD__ );
+                               }
+                       }
+                       $dbw->delete( 'user_groups', [ 'ug_user' => $delUser ], __METHOD__ );
+                       $dbw->delete( 'user_former_groups', [ 'ufg_user' => $delUser ], __METHOD__ );
+                       $dbw->delete( 'user_properties', [ 'up_user' => $delUser ], __METHOD__ );
+                       if ( $wgActorTableSchemaMigrationStage > MIGRATION_OLD ) {
+                               $dbw->delete( 'logging', [ 'log_actor' => $delActor ], __METHOD__ );
+                               $dbw->delete( 'recentchanges', [ 'rc_actor' => $delActor ], __METHOD__ );
+                       }
+                       if ( $wgActorTableSchemaMigrationStage < MIGRATION_NEW ) {
+                               $dbw->delete( 'logging', [ 'log_user' => $delUser ], __METHOD__ );
+                               $dbw->delete( 'recentchanges', [ 'rc_user' => $delUser ], __METHOD__ );
+                       }
                        $this->output( "done.\n" );
                        # Update the site_stats.ss_users field
                        $users = $dbw->selectField( 'user', 'COUNT(*)', [], __METHOD__ );
@@ -102,10 +138,11 @@ class RemoveUnusedAccounts extends Maintenance {
         * (No edits, no deleted edits, no log entries, no current/old uploads)
         *
         * @param int $id User's ID
+        * @param int $actor User's actor ID
         * @param bool $master Perform checking on the master
         * @return bool
         */
-       private function isInactiveAccount( $id, $master = false ) {
+       private function isInactiveAccount( $id, $actor, $master = false ) {
                $dbo = $this->getDB( $master ? DB_MASTER : DB_REPLICA );
                $checks = [
                        'revision' => 'rev',
@@ -116,14 +153,37 @@ class RemoveUnusedAccounts extends Maintenance {
                ];
                $count = 0;
 
+               $migration = ActorMigration::newMigration();
+
+               $user = User::newFromAnyId( $id, null, $actor );
+
                $this->beginTransaction( $dbo, __METHOD__ );
-               foreach ( $checks as $table => $fprefix ) {
-                       $conds = [ $fprefix . '_user' => $id ];
-                       $count += (int)$dbo->selectField( $table, 'COUNT(*)', $conds, __METHOD__ );
+               foreach ( $checks as $table => $prefix ) {
+                       $actorQuery = $migration->getWhere(
+                               $dbo, $prefix . '_user', $user, $prefix !== 'oi' && $prefix !== 'fa'
+                       );
+                       $count += (int)$dbo->selectField(
+                               [ $table ] + $actorQuery['tables'],
+                               'COUNT(*)',
+                               $actorQuery['conds'],
+                               __METHOD__,
+                               [],
+                               $actorQuery['joins']
+                       );
                }
 
-               $conds = [ 'log_user' => $id, 'log_type != ' . $dbo->addQuotes( 'newusers' ) ];
-               $count += (int)$dbo->selectField( 'logging', 'COUNT(*)', $conds, __METHOD__ );
+               $actorQuery = $migration->getWhere( $dbo, 'log_user', $user, false );
+               $count += (int)$dbo->selectField(
+                       [ 'logging' ] + $actorQuery['tables'],
+                       'COUNT(*)',
+                       [
+                               $actorQuery['conds'],
+                               'log_type != ' . $dbo->addQuotes( 'newusers' )
+                       ],
+                       __METHOD__,
+                       [],
+                       $actorQuery['joins']
+               );
 
                $this->commitTransaction( $dbo, __METHOD__ );
 
index 34bc62b..878eb9b 100644 (file)
@@ -91,20 +91,26 @@ class RollbackEdits extends Maintenance {
 
        /**
         * Get all pages that should be rolled back for a given user
-        * @param string $user A name to check against rev_user_text
+        * @param string $user A name to check against
         * @return array
         */
        private function getRollbackTitles( $user ) {
                $dbr = $this->getDB( DB_REPLICA );
                $titles = [];
-               $results = $dbr->select(
-                       [ 'page', 'revision' ],
-                       [ 'page_namespace', 'page_title' ],
-                       [ 'page_latest = rev_id', 'rev_user_text' => $user ],
-                       __METHOD__
-               );
-               foreach ( $results as $row ) {
-                       $titles[] = Title::makeTitle( $row->page_namespace, $row->page_title );
+               $actorQuery = ActorMigration::newMigration()
+                       ->getWhere( $dbr, 'rev_user', User::newFromName( $user, false ) );
+               foreach ( $actorQuery['orconds'] as $cond ) {
+                       $results = $dbr->select(
+                               [ 'page', 'revision' ] + $actorQuery['tables'],
+                               [ 'page_namespace', 'page_title' ],
+                               [ $cond ],
+                               __METHOD__,
+                               [],
+                               [ 'revision' => [ 'JOIN', 'page_latest = rev_id' ] ] + $actorQuery['joins']
+                       );
+                       foreach ( $results as $row ) {
+                               $titles[] = Title::makeTitle( $row->page_namespace, $row->page_title );
+                       }
                }
 
                return $titles;
index dd05bbe..2c8bdb6 100644 (file)
@@ -40,6 +40,7 @@ class MwSql extends Maintenance {
                        'Takes a file name containing SQL as argument or runs interactively.' );
                $this->addOption( 'query',
                        'Run a single query instead of running interactively', false, true );
+               $this->addOption( 'json', 'Output the results as JSON instead of PHP objects' );
                $this->addOption( 'cluster', 'Use an external cluster by name', false, true );
                $this->addOption( 'wikidb',
                        'The database wiki ID to use if not the current one', false, true );
@@ -175,9 +176,15 @@ class MwSql extends Maintenance {
                        // Do nothing
                        return;
                } elseif ( is_object( $res ) && $res->numRows() ) {
+                       $out = '';
                        foreach ( $res as $row ) {
-                               $this->output( print_r( $row, true ) );
+                               $out .= print_r( $row, true );
+                               $rows[] = $row;
                        }
+                       if ( $this->hasOption( 'json' ) ) {
+                               $out = json_encode( $rows, JSON_PRETTY_PRINT );
+                       }
+                       $this->output( $out . "\n" );
                } else {
                        $affected = $db->affectedRows();
                        $this->output( "Query OK, $affected row(s) affected\n" );
diff --git a/maintenance/sqlite/archives/patch-actor-table.sql b/maintenance/sqlite/archives/patch-actor-table.sql
new file mode 100644 (file)
index 0000000..bf15a04
--- /dev/null
@@ -0,0 +1,371 @@
+--
+-- patch-actor-table.sql
+--
+-- T167246. Add an `actor` table and various columns (and temporary tables) to reference it.
+-- Sigh, sqlite, such trouble just to change the default value of a column.
+
+CREATE TABLE /*_*/actor (
+  actor_id bigint unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
+  actor_user int unsigned,
+  actor_name varchar(255) binary NOT NULL
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/actor_user ON /*_*/actor (actor_user);
+CREATE UNIQUE INDEX /*i*/actor_name ON /*_*/actor (actor_name);
+
+CREATE TABLE /*_*/revision_actor_temp (
+  revactor_rev int unsigned NOT NULL,
+  revactor_actor bigint unsigned NOT NULL,
+  revactor_timestamp binary(14) NOT NULL default '',
+  revactor_page int unsigned NOT NULL,
+  PRIMARY KEY (revactor_rev, revactor_actor)
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/revactor_rev ON /*_*/revision_actor_temp (revactor_rev);
+CREATE INDEX /*i*/actor_timestamp ON /*_*/revision_actor_temp (revactor_actor,revactor_timestamp);
+CREATE INDEX /*i*/page_actor_timestamp ON /*_*/revision_actor_temp (revactor_page,revactor_actor,revactor_timestamp);
+
+BEGIN;
+
+DROP TABLE IF EXISTS /*_*/archive_tmp;
+CREATE TABLE /*_*/archive_tmp (
+  ar_id int unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
+  ar_namespace int NOT NULL default 0,
+  ar_title varchar(255) binary NOT NULL default '',
+  ar_text mediumblob NOT NULL,
+  ar_comment varbinary(767) NOT NULL default '',
+  ar_comment_id bigint unsigned NOT NULL DEFAULT 0,
+  ar_user int unsigned NOT NULL default 0,
+  ar_user_text varchar(255) binary NOT NULL DEFAULT '',
+  ar_actor bigint unsigned NOT NULL DEFAULT 0,
+  ar_timestamp binary(14) NOT NULL default '',
+  ar_minor_edit tinyint NOT NULL default 0,
+  ar_flags tinyblob NOT NULL,
+  ar_rev_id int unsigned,
+  ar_text_id int unsigned,
+  ar_deleted tinyint unsigned NOT NULL default 0,
+  ar_len int unsigned,
+  ar_page_id int unsigned,
+  ar_parent_id int unsigned default NULL,
+  ar_sha1 varbinary(32) NOT NULL default '',
+  ar_content_model varbinary(32) DEFAULT NULL,
+  ar_content_format varbinary(64) DEFAULT NULL
+) /*$wgDBTableOptions*/;
+
+INSERT OR IGNORE INTO /*_*/archive_tmp (
+       ar_id, ar_namespace, ar_title, ar_text, ar_comment, ar_user, ar_user_text,
+       ar_timestamp, ar_minor_edit, ar_flags, ar_rev_id, ar_text_id, ar_deleted,
+       ar_len, ar_page_id, ar_parent_id, ar_sha1, ar_content_model,
+       ar_content_format)
+  SELECT
+       ar_id, ar_namespace, ar_title, ar_text, ar_comment, ar_user, ar_user_text,
+       ar_timestamp, ar_minor_edit, ar_flags, ar_rev_id, ar_text_id, ar_deleted,
+       ar_len, ar_page_id, ar_parent_id, ar_sha1, ar_content_model,
+       ar_content_format
+  FROM /*_*/archive;
+
+DROP TABLE /*_*/archive;
+ALTER TABLE /*_*/archive_tmp RENAME TO /*_*/archive;
+CREATE INDEX /*i*/name_title_timestamp ON /*_*/archive (ar_namespace,ar_title,ar_timestamp);
+CREATE INDEX /*i*/ar_usertext_timestamp ON /*_*/archive (ar_user_text,ar_timestamp);
+CREATE INDEX /*i*/ar_revid ON /*_*/archive (ar_rev_id);
+CREATE INDEX /*i*/ar_actor_timestamp ON /*_*/archive (ar_actor,ar_timestamp);
+
+COMMIT;
+
+BEGIN;
+
+DROP TABLE IF EXISTS ipblocks_tmp;
+CREATE TABLE /*_*/ipblocks_tmp (
+  ipb_id int NOT NULL PRIMARY KEY AUTO_INCREMENT,
+  ipb_address tinyblob NOT NULL,
+  ipb_user int unsigned NOT NULL default 0,
+  ipb_by int unsigned NOT NULL default 0,
+  ipb_by_text varchar(255) binary NOT NULL default '',
+  ipb_by_actor bigint unsigned NOT NULL DEFAULT 0,
+  ipb_reason varbinary(767) NOT NULL default '',
+  ipb_reason_id bigint unsigned NOT NULL DEFAULT 0,
+  ipb_timestamp binary(14) NOT NULL default '',
+  ipb_auto bool NOT NULL default 0,
+  ipb_anon_only bool NOT NULL default 0,
+  ipb_create_account bool NOT NULL default 1,
+  ipb_enable_autoblock bool NOT NULL default '1',
+  ipb_expiry varbinary(14) NOT NULL default '',
+  ipb_range_start tinyblob NOT NULL,
+  ipb_range_end tinyblob NOT NULL,
+  ipb_deleted bool NOT NULL default 0,
+  ipb_block_email bool NOT NULL default 0,
+  ipb_allow_usertalk bool NOT NULL default 0,
+  ipb_parent_block_id int default NULL
+) /*$wgDBTableOptions*/;
+
+INSERT OR IGNORE INTO /*_*/ipblocks_tmp (
+       ipb_id, ipb_address, ipb_user, ipb_by, ipb_by_text, ipb_reason,
+       ipb_timestamp, ipb_auto, ipb_anon_only, ipb_create_account,
+       ipb_enable_autoblock, ipb_expiry, ipb_range_start, ipb_range_end,
+       ipb_deleted, ipb_block_email, ipb_allow_usertalk, ipb_parent_block_id)
+  SELECT
+       ipb_id, ipb_address, ipb_user, ipb_by, ipb_by_text, ipb_reason,
+       ipb_timestamp, ipb_auto, ipb_anon_only, ipb_create_account,
+       ipb_enable_autoblock, ipb_expiry, ipb_range_start, ipb_range_end,
+       ipb_deleted, ipb_block_email, ipb_allow_usertalk, ipb_parent_block_id
+  FROM /*_*/ipblocks;
+
+DROP TABLE /*_*/ipblocks;
+ALTER TABLE /*_*/ipblocks_tmp RENAME TO /*_*/ipblocks;
+CREATE UNIQUE INDEX /*i*/ipb_address ON /*_*/ipblocks (ipb_address(255), ipb_user, ipb_auto, ipb_anon_only);
+CREATE INDEX /*i*/ipb_user ON /*_*/ipblocks (ipb_user);
+CREATE INDEX /*i*/ipb_range ON /*_*/ipblocks (ipb_range_start(8), ipb_range_end(8));
+CREATE INDEX /*i*/ipb_timestamp ON /*_*/ipblocks (ipb_timestamp);
+CREATE INDEX /*i*/ipb_expiry ON /*_*/ipblocks (ipb_expiry);
+CREATE INDEX /*i*/ipb_parent_block_id ON /*_*/ipblocks (ipb_parent_block_id);
+
+COMMIT;
+
+BEGIN;
+
+DROP TABLE IF EXISTS /*_*/image_tmp;
+CREATE TABLE /*_*/image_tmp (
+  img_name varchar(255) binary NOT NULL default '' PRIMARY KEY,
+  img_size int unsigned NOT NULL default 0,
+  img_width int NOT NULL default 0,
+  img_height int NOT NULL default 0,
+  img_metadata mediumblob NOT NULL,
+  img_bits int NOT NULL default 0,
+  img_media_type ENUM("UNKNOWN", "BITMAP", "DRAWING", "AUDIO", "VIDEO", "MULTIMEDIA", "OFFICE", "TEXT", "EXECUTABLE", "ARCHIVE") default NULL,
+  img_major_mime ENUM("unknown", "application", "audio", "image", "text", "video", "message", "model", "multipart", "chemical") NOT NULL default "unknown",
+  img_minor_mime varbinary(100) NOT NULL default "unknown",
+  img_description varbinary(767) NOT NULL default '',
+  img_user int unsigned NOT NULL default 0,
+  img_user_text varchar(255) binary NOT NULL DEFAULT '',
+  img_actor bigint unsigned NOT NULL DEFAULT 0,
+  img_timestamp varbinary(14) NOT NULL default '',
+  img_sha1 varbinary(32) NOT NULL default ''
+) /*$wgDBTableOptions*/;
+
+INSERT OR IGNORE INTO /*_*/image_tmp (
+       img_name, img_size, img_width, img_height, img_metadata, img_bits,
+       img_media_type, img_major_mime, img_minor_mime, img_description, img_user,
+       img_user_text, img_timestamp, img_sha1)
+  SELECT
+       img_name, img_size, img_width, img_height, img_metadata, img_bits,
+       img_media_type, img_major_mime, img_minor_mime, img_description, img_user,
+       img_user_text, img_timestamp, img_sha1
+  FROM /*_*/image;
+
+DROP TABLE /*_*/image;
+ALTER TABLE /*_*/image_tmp RENAME TO /*_*/image;
+CREATE INDEX /*i*/img_user_timestamp ON /*_*/image (img_user,img_timestamp);
+CREATE INDEX /*i*/img_usertext_timestamp ON /*_*/image (img_user_text,img_timestamp);
+CREATE INDEX /*i*/img_actor_timestamp ON /*_*/image (img_actor,img_timestamp);
+CREATE INDEX /*i*/img_size ON /*_*/image (img_size);
+CREATE INDEX /*i*/img_timestamp ON /*_*/image (img_timestamp);
+CREATE INDEX /*i*/img_sha1 ON /*_*/image (img_sha1(10));
+CREATE INDEX /*i*/img_media_mime ON /*_*/image (img_media_type,img_major_mime,img_minor_mime);
+
+COMMIT;
+
+BEGIN;
+
+DROP TABLE IF EXISTS /*_*/oldimage_tmp;
+CREATE TABLE /*_*/oldimage_tmp (
+  oi_name varchar(255) binary NOT NULL default '',
+  oi_archive_name varchar(255) binary NOT NULL default '',
+  oi_size int unsigned NOT NULL default 0,
+  oi_width int NOT NULL default 0,
+  oi_height int NOT NULL default 0,
+  oi_bits int NOT NULL default 0,
+  oi_description varbinary(767) NOT NULL default '',
+  oi_description_id bigint unsigned NOT NULL DEFAULT 0,
+  oi_user int unsigned NOT NULL default 0,
+  oi_user_text varchar(255) binary NOT NULL DEFAULT '',
+  oi_actor bigint unsigned NOT NULL DEFAULT 0,
+  oi_timestamp binary(14) NOT NULL default '',
+  oi_metadata mediumblob NOT NULL,
+  oi_media_type ENUM("UNKNOWN", "BITMAP", "DRAWING", "AUDIO", "VIDEO", "MULTIMEDIA", "OFFICE", "TEXT", "EXECUTABLE", "ARCHIVE") default NULL,
+  oi_major_mime ENUM("unknown", "application", "audio", "image", "text", "video", "message", "model", "multipart", "chemical") NOT NULL default "unknown",
+  oi_minor_mime varbinary(100) NOT NULL default "unknown",
+  oi_deleted tinyint unsigned NOT NULL default 0,
+  oi_sha1 varbinary(32) NOT NULL default ''
+) /*$wgDBTableOptions*/;
+
+INSERT OR IGNORE INTO /*_*/oldimage_tmp (
+       oi_name, oi_archive_name, oi_size, oi_width, oi_height, oi_bits,
+       oi_description, oi_user, oi_user_text, oi_timestamp, oi_metadata,
+       oi_media_type, oi_major_mime, oi_minor_mime, oi_deleted, oi_sha1)
+  SELECT
+       oi_name, oi_archive_name, oi_size, oi_width, oi_height, oi_bits,
+       oi_description, oi_user, oi_user_text, oi_timestamp, oi_metadata,
+       oi_media_type, oi_major_mime, oi_minor_mime, oi_deleted, oi_sha1
+  FROM /*_*/oldimage;
+
+DROP TABLE /*_*/oldimage;
+ALTER TABLE /*_*/oldimage_tmp RENAME TO /*_*/oldimage;
+CREATE INDEX /*i*/oi_usertext_timestamp ON /*_*/oldimage (oi_user_text,oi_timestamp);
+CREATE INDEX /*i*/oi_name_timestamp ON /*_*/oldimage (oi_name,oi_timestamp);
+CREATE INDEX /*i*/oi_name_archive_name ON /*_*/oldimage (oi_name,oi_archive_name(14));
+CREATE INDEX /*i*/oi_sha1 ON /*_*/oldimage (oi_sha1(10));
+CREATE INDEX /*i*/oi_actor_timestamp ON /*_*/oldimage (oi_actor,oi_timestamp);
+
+COMMIT;
+
+BEGIN;
+
+DROP TABLE IF EXISTS /*_*/filearchive_tmp;
+CREATE TABLE /*_*/filearchive_tmp (
+  fa_id int NOT NULL PRIMARY KEY AUTO_INCREMENT,
+  fa_name varchar(255) binary NOT NULL default '',
+  fa_archive_name varchar(255) binary default '',
+  fa_storage_group varbinary(16),
+  fa_storage_key varbinary(64) default '',
+  fa_deleted_user int,
+  fa_deleted_timestamp binary(14) default '',
+  fa_deleted_reason varbinary(767) default '',
+  fa_deleted_reason_id bigint unsigned NOT NULL DEFAULT 0,
+  fa_size int unsigned default 0,
+  fa_width int default 0,
+  fa_height int default 0,
+  fa_metadata mediumblob,
+  fa_bits int default 0,
+  fa_media_type ENUM("UNKNOWN", "BITMAP", "DRAWING", "AUDIO", "VIDEO", "MULTIMEDIA", "OFFICE", "TEXT", "EXECUTABLE", "ARCHIVE") default NULL,
+  fa_major_mime ENUM("unknown", "application", "audio", "image", "text", "video", "message", "model", "multipart", "chemical") default "unknown",
+  fa_minor_mime varbinary(100) default "unknown",
+  fa_description varbinary(767) default '',
+  fa_description_id bigint unsigned NOT NULL DEFAULT 0,
+  fa_user int unsigned default 0,
+  fa_user_text varchar(255) binary DEFAULT '',
+  fa_actor bigint unsigned NOT NULL DEFAULT 0,
+  fa_timestamp binary(14) default '',
+  fa_deleted tinyint unsigned NOT NULL default 0,
+  fa_sha1 varbinary(32) NOT NULL default ''
+) /*$wgDBTableOptions*/;
+
+INSERT OR IGNORE INTO /*_*/filearchive_tmp (
+       fa_id, fa_name, fa_archive_name, fa_storage_group, fa_storage_key,
+       fa_deleted_user, fa_deleted_timestamp, fa_deleted_reason, fa_size,
+       fa_width, fa_height, fa_metadata, fa_bits, fa_media_type, fa_major_mime,
+       fa_minor_mime, fa_description, fa_user, fa_user_text, fa_timestamp,
+       fa_deleted, fa_sha1)
+  SELECT
+       fa_id, fa_name, fa_archive_name, fa_storage_group, fa_storage_key,
+       fa_deleted_user, fa_deleted_timestamp, fa_deleted_reason, fa_size,
+       fa_width, fa_height, fa_metadata, fa_bits, fa_media_type, fa_major_mime,
+       fa_minor_mime, fa_description, fa_user, fa_user_text, fa_timestamp,
+       fa_deleted, fa_sha1
+  FROM /*_*/filearchive;
+
+DROP TABLE /*_*/filearchive;
+ALTER TABLE /*_*/filearchive_tmp RENAME TO /*_*/filearchive;
+CREATE INDEX /*i*/fa_name ON /*_*/filearchive (fa_name, fa_timestamp);
+CREATE INDEX /*i*/fa_storage_group ON /*_*/filearchive (fa_storage_group, fa_storage_key);
+CREATE INDEX /*i*/fa_deleted_timestamp ON /*_*/filearchive (fa_deleted_timestamp);
+CREATE INDEX /*i*/fa_user_timestamp ON /*_*/filearchive (fa_user_text,fa_timestamp);
+CREATE INDEX /*i*/fa_sha1 ON /*_*/filearchive (fa_sha1(10));
+CREATE INDEX /*i*/fa_actor_timestamp ON /*_*/filearchive (fa_actor,fa_timestamp);
+
+COMMIT;
+
+BEGIN;
+
+DROP TABLE IF EXISTS /*_*/logging_tmp;
+CREATE TABLE /*_*/logging_tmp (
+  log_id int unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
+  log_type varbinary(32) NOT NULL default '',
+  log_action varbinary(32) NOT NULL default '',
+  log_timestamp binary(14) NOT NULL default '19700101000000',
+  log_user int unsigned NOT NULL default 0,
+  log_user_text varchar(255) binary NOT NULL default '',
+  log_actor bigint unsigned NOT NULL DEFAULT 0,
+  log_namespace int NOT NULL default 0,
+  log_title varchar(255) binary NOT NULL default '',
+  log_page int unsigned NULL,
+  log_comment varbinary(767) NOT NULL default '',
+  log_comment_id bigint unsigned NOT NULL DEFAULT 0,
+  log_params blob NOT NULL,
+  log_deleted tinyint unsigned NOT NULL default 0
+) /*$wgDBTableOptions*/;
+
+INSERT OR IGNORE INTO /*_*/logging_tmp (
+       log_id, log_type, log_action, log_timestamp, log_user, log_user_text,
+       log_namespace, log_title, log_page, log_comment, log_comment_id,
+       log_params, log_deleted)
+  SELECT
+       log_id, log_type, log_action, log_timestamp, log_user, log_user_text,
+       log_namespace, log_title, log_page, log_comment, log_comment_id,
+       log_params, log_deleted
+  FROM /*_*/logging;
+
+DROP TABLE /*_*/logging;
+ALTER TABLE /*_*/logging_tmp RENAME TO /*_*/logging;
+CREATE INDEX /*i*/type_time ON /*_*/logging (log_type, log_timestamp);
+CREATE INDEX /*i*/user_time ON /*_*/logging (log_user, log_timestamp);
+CREATE INDEX /*i*/actor_time ON /*_*/logging (log_actor, log_timestamp);
+CREATE INDEX /*i*/page_time ON /*_*/logging (log_namespace, log_title, log_timestamp);
+CREATE INDEX /*i*/times ON /*_*/logging (log_timestamp);
+CREATE INDEX /*i*/log_user_type_time ON /*_*/logging (log_user, log_type, log_timestamp);
+CREATE INDEX /*i*/log_actor_type_time ON /*_*/logging (log_actor, log_type, log_timestamp);
+CREATE INDEX /*i*/log_page_id_time ON /*_*/logging (log_page,log_timestamp);
+CREATE INDEX /*i*/type_action ON /*_*/logging (log_type, log_action, log_timestamp);
+CREATE INDEX /*i*/log_user_text_type_time ON /*_*/logging (log_user_text, log_type, log_timestamp);
+CREATE INDEX /*i*/log_user_text_time ON /*_*/logging (log_user_text, log_timestamp);
+
+COMMIT;
+
+BEGIN;
+
+DROP TABLE IF EXISTS /*_*/recentchanges_tmp;
+CREATE TABLE /*_*/recentchanges_tmp (
+  rc_id int NOT NULL PRIMARY KEY AUTO_INCREMENT,
+  rc_timestamp varbinary(14) NOT NULL default '',
+  rc_user int unsigned NOT NULL default 0,
+  rc_user_text varchar(255) binary NOT NULL DEFAULT '',
+  rc_actor bigint unsigned NOT NULL DEFAULT 0,
+  rc_namespace int NOT NULL default 0,
+  rc_title varchar(255) binary NOT NULL default '',
+  rc_comment varbinary(767) NOT NULL default '',
+  rc_comment_id bigint unsigned NOT NULL DEFAULT 0,
+  rc_minor tinyint unsigned NOT NULL default 0,
+  rc_bot tinyint unsigned NOT NULL default 0,
+  rc_new tinyint unsigned NOT NULL default 0,
+  rc_cur_id int unsigned NOT NULL default 0,
+  rc_this_oldid int unsigned NOT NULL default 0,
+  rc_last_oldid int unsigned NOT NULL default 0,
+  rc_type tinyint unsigned NOT NULL default 0,
+  rc_source varchar(16) binary not null default '',
+  rc_patrolled tinyint unsigned NOT NULL default 0,
+  rc_ip varbinary(40) NOT NULL default '',
+  rc_old_len int,
+  rc_new_len int,
+  rc_deleted tinyint unsigned NOT NULL default 0,
+  rc_logid int unsigned NOT NULL default 0,
+  rc_log_type varbinary(255) NULL default NULL,
+  rc_log_action varbinary(255) NULL default NULL,
+  rc_params blob NULL
+) /*$wgDBTableOptions*/;
+
+INSERT OR IGNORE INTO /*_*/recentchanges_tmp (
+       rc_id, rc_timestamp, rc_user, rc_user_text, rc_namespace, rc_title,
+       rc_comment, rc_comment_id, rc_minor, rc_bot, rc_new, rc_cur_id,
+       rc_this_oldid, rc_last_oldid, rc_type, rc_source, rc_patrolled, rc_ip,
+       rc_old_len, rc_new_len, rc_deleted, rc_logid, rc_log_type, rc_log_action,
+       rc_params)
+  SELECT
+       rc_id, rc_timestamp, rc_user, rc_user_text, rc_namespace, rc_title,
+       rc_comment, rc_comment_id, rc_minor, rc_bot, rc_new, rc_cur_id,
+       rc_this_oldid, rc_last_oldid, rc_type, rc_source, rc_patrolled, rc_ip,
+       rc_old_len, rc_new_len, rc_deleted, rc_logid, rc_log_type, rc_log_action,
+       rc_params
+  FROM /*_*/recentchanges;
+
+DROP TABLE /*_*/recentchanges;
+ALTER TABLE /*_*/recentchanges_tmp RENAME TO /*_*/recentchanges;
+CREATE INDEX /*i*/rc_timestamp ON /*_*/recentchanges (rc_timestamp);
+CREATE INDEX /*i*/rc_namespace_title ON /*_*/recentchanges (rc_namespace, rc_title);
+CREATE INDEX /*i*/rc_cur_id ON /*_*/recentchanges (rc_cur_id);
+CREATE INDEX /*i*/new_name_timestamp ON /*_*/recentchanges (rc_new,rc_namespace,rc_timestamp);
+CREATE INDEX /*i*/rc_ip ON /*_*/recentchanges (rc_ip);
+CREATE INDEX /*i*/rc_ns_usertext ON /*_*/recentchanges (rc_namespace, rc_user_text);
+CREATE INDEX /*i*/rc_ns_actor ON /*_*/recentchanges (rc_namespace, rc_actor);
+CREATE INDEX /*i*/rc_user_text ON /*_*/recentchanges (rc_user_text, rc_timestamp);
+CREATE INDEX /*i*/rc_actor ON /*_*/recentchanges (rc_actor, rc_timestamp);
+CREATE INDEX /*i*/rc_name_type_patrolled_timestamp ON /*_*/recentchanges (rc_namespace, rc_type, rc_patrolled, rc_timestamp);
+
+COMMIT;
index 4414c5b..a67e261 100644 (file)
@@ -462,7 +462,6 @@ class CompressOld extends Maintenance {
                                $this->output( "/" );
                                $this->commitTransaction( $dbw, __METHOD__ );
                                $i += $thisChunkSize;
-                               wfWaitForSlaves();
                        }
                        $this->output( "\n" );
                }
index b7ae691..da3ada7 100644 (file)
@@ -240,7 +240,6 @@ class FixT22757 extends Maintenance {
                                                __METHOD__
                                        );
                                        $this->commitTransaction( $dbw, __METHOD__ );
-                                       $this->waitForSlaves();
                                }
 
                                print "$primaryId: resolved to $url\n";
@@ -254,15 +253,6 @@ class FixT22757 extends Maintenance {
                print "Good stubs: $numGood\n";
        }
 
-       function waitForSlaves() {
-               static $iteration = 0;
-               ++$iteration;
-               if ( ++$iteration > 50 == 0 ) {
-                       wfWaitForSlaves();
-                       $iteration = 0;
-               }
-       }
-
        function findTextIdInPage( $pageId, $textId ) {
                $ids = $this->getRevTextMap( $pageId );
                if ( !isset( $ids[$textId] ) ) {
index b881d7e..d633a9c 100644 (file)
@@ -140,6 +140,28 @@ CREATE INDEX /*i*/user_email_token ON /*_*/user (user_email_token);
 CREATE INDEX /*i*/user_email ON /*_*/user (user_email(50));
 
 
+--
+-- The "actor" table associates user names or IP addresses with integers for
+-- the benefit of other tables that need to refer to either logged-in or
+-- logged-out users. If something can only ever be done by logged-in users, it
+-- can refer to the user table directly.
+--
+CREATE TABLE /*_*/actor (
+  -- Unique ID to identify each actor
+  actor_id bigint unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
+
+  -- Key to user.user_id, or NULL for anonymous edits.
+  actor_user int unsigned,
+
+  -- Text username or IP address
+  actor_name varchar(255) binary NOT NULL
+) /*$wgDBTableOptions*/;
+
+-- User IDs and names must be unique.
+CREATE UNIQUE INDEX /*i*/actor_user ON /*_*/actor (actor_user);
+CREATE UNIQUE INDEX /*i*/actor_name ON /*_*/actor (actor_name);
+
+
 --
 -- User permissions have been broken out to a separate table;
 -- this allows sites with a shared user table to have different
@@ -351,9 +373,11 @@ CREATE TABLE /*_*/revision (
 
   -- Key to user.user_id of the user who made this edit.
   -- Stores 0 for anonymous edits and for some mass imports.
+  -- Deprecated in favor of revision_actor_temp.revactor_actor.
   rev_user int unsigned NOT NULL default 0,
 
   -- Text username or IP address of the editor.
+  -- Deprecated in favor of revision_actor_temp.revactor_actor.
   rev_user_text varchar(255) binary NOT NULL default '',
 
   -- Timestamp of when revision was created
@@ -425,6 +449,29 @@ CREATE TABLE /*_*/revision_comment_temp (
 -- Ensure uniqueness
 CREATE UNIQUE INDEX /*i*/revcomment_rev ON /*_*/revision_comment_temp (revcomment_rev);
 
+--
+-- Temporary table to avoid blocking on an alter of revision.
+--
+-- On large wikis like the English Wikipedia, altering the revision table is a
+-- months-long process. This table is being created to avoid such an alter, and
+-- will be merged back into revision in the future.
+--
+CREATE TABLE /*_*/revision_actor_temp (
+  -- Key to rev_id
+  revactor_rev int unsigned NOT NULL,
+  -- Key to actor_id
+  revactor_actor bigint unsigned NOT NULL,
+  -- Copy fields from revision for indexes
+  revactor_timestamp binary(14) NOT NULL default '',
+  revactor_page int unsigned NOT NULL,
+  PRIMARY KEY (revactor_rev, revactor_actor)
+) /*$wgDBTableOptions*/;
+-- Ensure uniqueness
+CREATE UNIQUE INDEX /*i*/revactor_rev ON /*_*/revision_actor_temp (revactor_rev);
+-- Match future indexes on revision
+CREATE INDEX /*i*/actor_timestamp ON /*_*/revision_actor_temp (revactor_actor,revactor_timestamp);
+CREATE INDEX /*i*/page_actor_timestamp ON /*_*/revision_actor_temp (revactor_page,revactor_actor,revactor_timestamp);
+
 --
 -- Every time an edit by a logged out user is saved,
 -- a row is created in ip_changes. This stores
@@ -547,8 +594,9 @@ CREATE TABLE /*_*/archive (
   -- Basic revision stuff...
   ar_comment varbinary(767) NOT NULL default '', -- Deprecated in favor of ar_comment_id
   ar_comment_id bigint unsigned NOT NULL DEFAULT 0, -- ("DEFAULT 0" is temporary, signaling that ar_comment should be used)
-  ar_user int unsigned NOT NULL default 0,
-  ar_user_text varchar(255) binary NOT NULL,
+  ar_user int unsigned NOT NULL default 0, -- Deprecated in favor of ar_actor
+  ar_user_text varchar(255) binary NOT NULL DEFAULT '', -- Deprecated in favor of ar_actor
+  ar_actor bigint unsigned NOT NULL DEFAULT 0, -- ("DEFAULT 0" is temporary, signaling that ar_user/ar_user_text should be used)
   ar_timestamp binary(14) NOT NULL default '',
   ar_minor_edit tinyint NOT NULL default 0,
 
@@ -606,6 +654,7 @@ CREATE INDEX /*i*/name_title_timestamp ON /*_*/archive (ar_namespace,ar_title,ar
 
 -- Index for Special:DeletedContributions
 CREATE INDEX /*i*/ar_usertext_timestamp ON /*_*/archive (ar_user_text,ar_timestamp);
+CREATE INDEX /*i*/ar_actor_timestamp ON /*_*/archive (ar_actor,ar_timestamp);
 
 -- Index for linking archive rows with tables that normally link with revision
 -- rows, such as change_tag.
@@ -975,10 +1024,13 @@ CREATE TABLE /*_*/ipblocks (
   ipb_user int unsigned NOT NULL default 0,
 
   -- User ID who made the block.
-  ipb_by int unsigned NOT NULL default 0,
+  ipb_by int unsigned NOT NULL default 0, -- Deprecated in favor of ipb_by_actor
 
   -- User name of blocker
-  ipb_by_text varchar(255) binary NOT NULL default '',
+  ipb_by_text varchar(255) binary NOT NULL default '', -- Deprecated in favor of ipb_by_actor
+
+  -- Actor who made the block.
+  ipb_by_actor bigint unsigned NOT NULL DEFAULT 0, -- ("DEFAULT 0" is temporary, signaling that ipb_by/ipb_by_text should be used)
 
   -- Text comment made by blocker. Deprecated in favor of ipb_reason_id
   ipb_reason varbinary(767) NOT NULL default '',
@@ -1096,8 +1148,13 @@ CREATE TABLE /*_*/image (
   img_description varbinary(767) NOT NULL default '',
 
   -- user_id and user_name of uploader.
+  -- Deprecated in favor of img_actor.
   img_user int unsigned NOT NULL default 0,
-  img_user_text varchar(255) binary NOT NULL,
+  img_user_text varchar(255) binary NOT NULL DEFAULT '',
+
+  -- actor_id of the uploader.
+  -- ("DEFAULT 0" is temporary, signaling that img_user/img_user_text should be used)
+  img_actor bigint unsigned NOT NULL DEFAULT 0,
 
   -- Time of the upload.
   img_timestamp varbinary(14) NOT NULL default '',
@@ -1109,6 +1166,7 @@ CREATE TABLE /*_*/image (
 -- Used by Special:Newimages and ApiQueryAllImages
 CREATE INDEX /*i*/img_user_timestamp ON /*_*/image (img_user,img_timestamp);
 CREATE INDEX /*i*/img_usertext_timestamp ON /*_*/image (img_user_text,img_timestamp);
+CREATE INDEX /*i*/img_actor_timestamp ON /*_*/image (img_actor,img_timestamp);
 -- Used by Special:ListFiles for sort-by-size
 CREATE INDEX /*i*/img_size ON /*_*/image (img_size);
 -- Used by Special:Newimages and Special:ListFiles
@@ -1156,8 +1214,9 @@ CREATE TABLE /*_*/oldimage (
   oi_bits int NOT NULL default 0,
   oi_description varbinary(767) NOT NULL default '', -- Deprecated.
   oi_description_id bigint unsigned NOT NULL DEFAULT 0, -- ("DEFAULT 0" is temporary, signaling that oi_description should be used)
-  oi_user int unsigned NOT NULL default 0,
-  oi_user_text varchar(255) binary NOT NULL,
+  oi_user int unsigned NOT NULL default 0, -- Deprecated in favor of oi_actor
+  oi_user_text varchar(255) binary NOT NULL DEFAULT '', -- Deprecated in favor of oi_actor
+  oi_actor bigint unsigned NOT NULL DEFAULT 0, -- ("DEFAULT 0" is temporary, signaling that oi_user/oi_user_text should be used)
   oi_timestamp binary(14) NOT NULL default '',
 
   oi_metadata mediumblob NOT NULL,
@@ -1169,6 +1228,7 @@ CREATE TABLE /*_*/oldimage (
 ) /*$wgDBTableOptions*/;
 
 CREATE INDEX /*i*/oi_usertext_timestamp ON /*_*/oldimage (oi_user_text,oi_timestamp);
+CREATE INDEX /*i*/oi_actor_timestamp ON /*_*/oldimage (oi_actor,oi_timestamp);
 CREATE INDEX /*i*/oi_name_timestamp ON /*_*/oldimage (oi_name,oi_timestamp);
 -- oi_archive_name truncated to 14 to avoid key length overflow
 CREATE INDEX /*i*/oi_name_archive_name ON /*_*/oldimage (oi_name,oi_archive_name(14));
@@ -1217,8 +1277,9 @@ CREATE TABLE /*_*/filearchive (
   fa_minor_mime varbinary(100) default "unknown",
   fa_description varbinary(767) default '', -- Deprecated
   fa_description_id bigint unsigned NOT NULL DEFAULT 0, -- ("DEFAULT 0" is temporary, signaling that fa_description should be used)
-  fa_user int unsigned default 0,
-  fa_user_text varchar(255) binary,
+  fa_user int unsigned default 0, -- Deprecated in favor of fa_actor
+  fa_user_text varchar(255) binary DEFAULT '', -- Deprecated in favor of fa_actor
+  fa_actor bigint unsigned NOT NULL DEFAULT 0, -- ("DEFAULT 0" is temporary, signaling that fa_user/fa_user_text should be used)
   fa_timestamp binary(14) default '',
 
   -- Visibility of deleted revisions, bitfield
@@ -1236,6 +1297,7 @@ CREATE INDEX /*i*/fa_storage_group ON /*_*/filearchive (fa_storage_group, fa_sto
 CREATE INDEX /*i*/fa_deleted_timestamp ON /*_*/filearchive (fa_deleted_timestamp);
 -- sort by uploader
 CREATE INDEX /*i*/fa_user_timestamp ON /*_*/filearchive (fa_user_text,fa_timestamp);
+CREATE INDEX /*i*/fa_actor_timestamp ON /*_*/filearchive (fa_actor,fa_timestamp);
 -- find file by sha1, 10 bytes will be enough for hashes to be indexed
 CREATE INDEX /*i*/fa_sha1 ON /*_*/filearchive (fa_sha1(10));
 
@@ -1306,8 +1368,9 @@ CREATE TABLE /*_*/recentchanges (
   rc_timestamp varbinary(14) NOT NULL default '',
 
   -- As in revision
-  rc_user int unsigned NOT NULL default 0,
-  rc_user_text varchar(255) binary NOT NULL,
+  rc_user int unsigned NOT NULL default 0, -- Deprecated in favor of rc_actor
+  rc_user_text varchar(255) binary NOT NULL DEFAULT '', -- Deprecated in favor of rc_actor
+  rc_actor bigint unsigned NOT NULL DEFAULT 0, -- ("DEFAULT 0" is temporary, signaling that rc_user/rc_user_text should be used)
 
   -- When pages are renamed, their RC entries do _not_ change.
   rc_namespace int NOT NULL default 0,
@@ -1390,9 +1453,11 @@ CREATE INDEX /*i*/rc_ip ON /*_*/recentchanges (rc_ip);
 
 -- Probably intended for Special:NewPages namespace filter
 CREATE INDEX /*i*/rc_ns_usertext ON /*_*/recentchanges (rc_namespace, rc_user_text);
+CREATE INDEX /*i*/rc_ns_actor ON /*_*/recentchanges (rc_namespace, rc_actor);
 
 -- SiteStats active user count, Special:ActiveUsers, Special:NewPages user filter
 CREATE INDEX /*i*/rc_user_text ON /*_*/recentchanges (rc_user_text, rc_timestamp);
+CREATE INDEX /*i*/rc_actor ON /*_*/recentchanges (rc_actor, rc_timestamp);
 
 -- ApiQueryRecentChanges (T140108)
 CREATE INDEX /*i*/rc_name_type_patrolled_timestamp ON /*_*/recentchanges (rc_namespace, rc_type, rc_patrolled, rc_timestamp);
@@ -1532,10 +1597,13 @@ CREATE TABLE /*_*/logging (
   log_timestamp binary(14) NOT NULL default '19700101000000',
 
   -- The user who performed this action; key to user_id
-  log_user int unsigned NOT NULL default 0,
+  log_user int unsigned NOT NULL default 0, -- Deprecated in favor of log_actor
 
   -- Name of the user who performed this action
-  log_user_text varchar(255) binary NOT NULL default '',
+  log_user_text varchar(255) binary NOT NULL default '', -- Deprecated in favor of log_actor
+
+  -- The actor who performed this action
+  log_actor bigint unsigned NOT NULL DEFAULT 0, -- ("DEFAULT 0" is temporary, signaling that log_user/log_user_text should be used)
 
   -- Key to the page affected. Where a user is the target,
   -- this will point to the user page.
@@ -1564,6 +1632,7 @@ CREATE INDEX /*i*/type_time ON /*_*/logging (log_type, log_timestamp);
 
 -- Special:Log performer filter
 CREATE INDEX /*i*/user_time ON /*_*/logging (log_user, log_timestamp);
+CREATE INDEX /*i*/actor_time ON /*_*/logging (log_actor, log_timestamp);
 
 -- Special:Log title filter, log extract
 CREATE INDEX /*i*/page_time ON /*_*/logging (log_namespace, log_title, log_timestamp);
@@ -1573,6 +1642,7 @@ CREATE INDEX /*i*/times ON /*_*/logging (log_timestamp);
 
 -- Special:Log filter by performer and type
 CREATE INDEX /*i*/log_user_type_time ON /*_*/logging (log_user, log_type, log_timestamp);
+CREATE INDEX /*i*/log_actor_type_time ON /*_*/logging (log_actor, log_type, log_timestamp);
 
 -- Apparently just used for a few maintenance pages (findMissingFiles.php, Flow).
 -- Could be removed?
index 63176cb..d88d5e9 100644 (file)
@@ -200,7 +200,11 @@ TEXT
                                        $this->updateSortKeySizeHistogram( $newSortKey );
                                }
 
-                               if ( !$dryRun ) {
+                               if ( $dryRun ) {
+                                       // Add 1 to the count if the sortkey was changed. (Note that this doesn't count changes in
+                                       // other fields, if any, those usually only happen when upgrading old MediaWikis.)
+                                       $count += ( $row->cl_sortkey !== $newSortKey );
+                               } else {
                                        $dbw->update(
                                                'categorylinks',
                                                [
@@ -213,6 +217,7 @@ TEXT
                                                [ 'cl_from' => $row->cl_from, 'cl_to' => $row->cl_to ],
                                                __METHOD__
                                        );
+                                       $count++;
                                }
                                if ( $row ) {
                                        $batchConds = [ $this->getBatchCondition( $row, $dbw ) ];
@@ -222,17 +227,16 @@ TEXT
                                $this->commitTransaction( $dbw, __METHOD__ );
                        }
 
-                       $count += $res->numRows();
-                       $this->output( "$count done.\n" );
-
-                       if ( !$dryRun && ++$batchCount % self::SYNC_INTERVAL == 0 ) {
-                               $this->output( "Waiting for replica DBs ... " );
-                               wfWaitForSlaves();
-                               $this->output( "done\n" );
+                       if ( $dryRun ) {
+                               $this->output( "$count rows would be updated so far.\n" );
+                       } else {
+                               $this->output( "$count done.\n" );
                        }
                } while ( $res->numRows() == self::BATCH_SIZE );
 
-               $this->output( "$count rows processed\n" );
+               if ( !$dryRun ) {
+                       $this->output( "$count rows processed\n" );
+               }
 
                if ( $verboseStats ) {
                        $this->output( "\n" );
index e139c3a..c1d3426 100644 (file)
@@ -156,11 +156,13 @@ return [
        ],
        'jquery.byteLength' => [
                'scripts' => 'resources/src/jquery/jquery.byteLength.js',
+               'deprecated' => 'Use "mediawiki.String" instead.',
+               'dependencies' => 'mediawiki.String',
                'targets' => [ 'desktop', 'mobile' ],
        ],
        'jquery.byteLimit' => [
-               'scripts' => 'resources/src/jquery/jquery.byteLimit.js',
-               'dependencies' => 'jquery.byteLength',
+               'dependencies' => 'jquery.lengthLimit',
+               'deprecated' => 'Use "jquery.lengthLimit" instead.',
                'targets' => [ 'desktop', 'mobile' ],
        ],
        'jquery.checkboxShiftClick' => [
@@ -266,6 +268,11 @@ return [
                ],
                'targets' => [ 'desktop', 'mobile' ],
        ],
+       'jquery.lengthLimit' => [
+               'scripts' => 'resources/src/jquery/jquery.lengthLimit.js',
+               'dependencies' => 'mediawiki.String',
+               'targets' => [ 'desktop', 'mobile' ],
+       ],
        'jquery.localize' => [
                'scripts' => 'resources/src/jquery/jquery.localize.js',
        ],
@@ -1063,7 +1070,7 @@ return [
                ],
                'dependencies' => [
                        'mediawiki.RegExp',
-                       'jquery.byteLimit',
+                       'jquery.lengthLimit',
                ],
                'messages' => [
                        'htmlform-chosen-placeholder',
@@ -1105,7 +1112,7 @@ return [
        'mediawiki.inspect' => [
                'scripts' => 'resources/src/mediawiki/mediawiki.inspect.js',
                'dependencies' => [
-                       'jquery.byteLength',
+                       'mediawiki.String',
                        'mediawiki.RegExp',
                ],
                'targets' => [ 'desktop', 'mobile' ],
@@ -1169,6 +1176,10 @@ return [
                'scripts' => 'resources/src/mediawiki/mediawiki.RegExp.js',
                'targets' => [ 'desktop', 'mobile' ],
        ],
+       'mediawiki.String' => [
+               'scripts' => 'resources/src/mediawiki/mediawiki.String.js',
+               'targets' => [ 'desktop', 'mobile' ],
+       ],
        'mediawiki.pager.tablePager' => [
                'styles' => 'resources/src/mediawiki/mediawiki.pager.tablePager.less',
        ],
@@ -1201,7 +1212,7 @@ return [
                        'resources/src/mediawiki/mediawiki.Title.phpCharToUpper.js',
                ],
                'dependencies' => [
-                       'jquery.byteLength',
+                       'mediawiki.String',
                        'mediawiki.util',
                ],
                'targets' => [ 'desktop', 'mobile' ],
@@ -1410,6 +1421,27 @@ return [
 
        /* MediaWiki Action */
 
+       'mediawiki.action.delete' => [
+               'scripts' => 'resources/src/mediawiki.action/mediawiki.action.delete.js',
+               'dependencies' => [
+                       'oojs-ui-core',
+                       'jquery.lengthLimit',
+               ],
+               'messages' => [
+                       // @todo Load this message in content language
+                       'colon-separator',
+               ],
+       ],
+       'mediawiki.action.delete.file' => [
+               'scripts' => 'resources/src/mediawiki.action/mediawiki.action.delete.file.js',
+               'dependencies' => [
+                       'jquery.lengthLimit',
+               ],
+               'messages' => [
+                       // @todo Load this message in content language
+                       'colon-separator',
+               ],
+       ],
        'mediawiki.action.edit' => [
                'scripts' => [
                        'resources/src/mediawiki.action/mediawiki.action.edit.js',
@@ -1421,7 +1453,7 @@ return [
                        'mediawiki.editfont.styles',
                        'jquery.textSelection',
                        'oojs-ui-core',
-                       'mediawiki.widgets.visibleByteLimit',
+                       'mediawiki.widgets.visibleLengthLimit',
                        'mediawiki.api',
                ],
        ],
@@ -1761,7 +1793,7 @@ return [
                        'resources/src/mediawiki.rcfilters/mw.rcfilters.UriProcessor.js',
                ],
                'dependencies' => [
-                       'jquery.byteLength',
+                       'mediawiki.String',
                        'oojs',
                        'mediawiki.api',
                        'mediawiki.api.options',
@@ -1923,6 +1955,7 @@ return [
                        'recentchanges-timeout',
                        'recentchanges-network',
                        'recentchanges-notargetpage',
+                       'allpagesbadtitle',
                        'quotation-marks',
                ],
                'dependencies' => [
@@ -2079,6 +2112,7 @@ return [
                'scripts' => 'resources/src/mediawiki.special/mediawiki.special.edittags.js',
                'dependencies' => [
                        'jquery.chosen',
+                       'jquery.lengthLimit',
                ],
                'messages' => [
                        'tags-edit-chosen-placeholder',
@@ -2094,7 +2128,7 @@ return [
        'mediawiki.special.movePage' => [
                'scripts' => 'resources/src/mediawiki.special/mediawiki.special.movePage.js',
                'dependencies' => [
-                       'mediawiki.widgets.visibleByteLimit',
+                       'mediawiki.widgets.visibleLengthLimit',
                        'mediawiki.widgets',
                ],
        ],
@@ -2139,6 +2173,17 @@ return [
                'scripts' => 'resources/src/mediawiki.special/mediawiki.special.recentchanges.js',
                'targets' => [ 'desktop', 'mobile' ],
        ],
+       'mediawiki.special.revisionDelete' => [
+               'scripts' => 'resources/src/mediawiki.special/mediawiki.special.revisionDelete.js',
+               'messages' => [
+                       // @todo Load this message in content language
+                       'colon-separator',
+               ],
+               'dependencies' => [
+                       'jquery.lengthLimit',
+               ],
+               'targets' => [ 'desktop', 'mobile' ],
+       ],
        'mediawiki.special.search' => [
                'scripts' => 'resources/src/mediawiki.special/mediawiki.special.search.js',
                'styles' => 'resources/src/mediawiki.special/mediawiki.special.search.css',
@@ -2173,6 +2218,10 @@ return [
        ],
        'mediawiki.special.undelete' => [
                'scripts' => 'resources/src/mediawiki.special/mediawiki.special.undelete.js',
+               'dependencies' => [
+                       'mediawiki.widgets.visibleLengthLimit',
+                       'mediawiki.widgets',
+               ],
        ],
        'mediawiki.special.unwatchedPages' => [
                'scripts' => 'resources/src/mediawiki.special/mediawiki.special.unwatchedPages.js',
@@ -2260,6 +2309,7 @@ return [
                'scripts' => 'resources/src/mediawiki.special/mediawiki.special.userrights.js',
                'dependencies' => [
                        'mediawiki.notification.convertmessagebox',
+                       'jquery.lengthLimit',
                ],
        ],
        'mediawiki.special.watchlist' => [
@@ -2308,7 +2358,7 @@ return [
        ],
        'mediawiki.legacy.protect' => [
                'scripts' => 'resources/src/mediawiki.legacy/protect.js',
-               'dependencies' => 'jquery.byteLimit',
+               'dependencies' => 'jquery.lengthLimit',
                'messages' => [ 'protect-unchain-permissions' ]
        ],
        // Used in the web installer. Test it after modifying this definition!
@@ -2420,7 +2470,7 @@ return [
                        // TitleInputWidget
                        'mediawiki.Title',
                        'mediawiki.api',
-                       'jquery.byteLimit',
+                       'mediawiki.String',
                ],
                'messages' => [
                        // NamespaceInputWidget
@@ -2474,12 +2524,18 @@ return [
                'targets' => [ 'desktop', 'mobile' ],
        ],
        'mediawiki.widgets.visibleByteLimit' => [
+               'dependencies' => 'mediawiki.widgets.visibleLengthLimit',
+               'deprecated' => 'Use "mediawiki.widgets.visibleLengthLimit" instead.',
+               'targets' => [ 'desktop', 'mobile' ]
+       ],
+       'mediawiki.widgets.visibleLengthLimit' => [
                'scripts' => [
-                       'resources/src/mediawiki.widgets.visibleByteLimit/mediawiki.widgets.visibleByteLimit.js'
+                       'resources/src/mediawiki.widgets.visibleLengthLimit/mediawiki.widgets.visibleLengthLimit.js'
                ],
                'dependencies' => [
                        'oojs-ui-core',
-                       'jquery.byteLimit'
+                       'jquery.lengthLimit',
+                       'mediawiki.String',
                ],
                'targets' => [ 'desktop', 'mobile' ]
        ],
index 222f14a..5764ae9 100644 (file)
@@ -1,38 +1,19 @@
 /**
  * @class jQuery.plugin.byteLength
- * @author Jan Paul Posma, 2011
- * @author Timo Tijhof, 2012
- * @author David Chan, 2013
  */
 
 /**
  * Calculate the byte length of a string (accounting for UTF-8).
  *
+ * @method byteLength
+ * @deprecated Use `require( 'mediawiki.String' ).byteLength` instead.
  * @static
  * @inheritable
  * @param {string} str
  * @return {number}
  */
-jQuery.byteLength = function ( str ) {
-       // This basically figures out how many bytes a UTF-16 string (which is what js sees)
-       // will take in UTF-8 by replacing a 2 byte character with 2 *'s, etc, and counting that.
-       // Note, surrogate (\uD800-\uDFFF) characters are counted as 2 bytes, since there's two of them
-       // and the actual character takes 4 bytes in UTF-8 (2*2=4). Might not work perfectly in
-       // edge cases such as illegal sequences, but that should never happen.
-
-       // https://en.wikipedia.org/wiki/UTF-8#Description
-       // The mapping from UTF-16 code units to UTF-8 bytes is as follows:
-       // > Range 0000-007F: codepoints that become 1 byte of UTF-8
-       // > Range 0080-07FF: codepoints that become 2 bytes of UTF-8
-       // > Range 0800-D7FF: codepoints that become 3 bytes of UTF-8
-       // > Range D800-DFFF: Surrogates (each pair becomes 4 bytes of UTF-8)
-       // > Range E000-FFFF: codepoints that become 3 bytes of UTF-8 (continued)
-
-       return str
-               .replace( /[\u0080-\u07FF\uD800-\uDFFF]/g, '**' )
-               .replace( /[\u0800-\uD7FF\uE000-\uFFFF]/g, '***' )
-               .length;
-};
+mediaWiki.log.deprecate( jQuery, 'byteLength', require( 'mediawiki.String' ).byteLength,
+       'Use require( \'mediawiki.String\' ).byteLength instead.', '$.byteLength' );
 
 /**
  * @class jQuery
diff --git a/resources/src/jquery/jquery.byteLimit.js b/resources/src/jquery/jquery.byteLimit.js
deleted file mode 100644 (file)
index c75246c..0000000
+++ /dev/null
@@ -1,238 +0,0 @@
-/**
- * @class jQuery.plugin.byteLimit
- */
-( function ( $ ) {
-
-       var eventKeys = [
-               'keyup.byteLimit',
-               'keydown.byteLimit',
-               'change.byteLimit',
-               'mouseup.byteLimit',
-               'cut.byteLimit',
-               'paste.byteLimit',
-               'focus.byteLimit',
-               'blur.byteLimit'
-       ].join( ' ' );
-
-       /**
-        * Utility function to trim down a string, based on byteLimit
-        * and given a safe start position. It supports insertion anywhere
-        * in the string, so "foo" to "fobaro" if limit is 4 will result in
-        * "fobo", not "foba". Basically emulating the native maxlength by
-        * reconstructing where the insertion occurred.
-        *
-        * @static
-        * @param {string} safeVal Known value that was previously returned by this
-        * function, if none, pass empty string.
-        * @param {string} newVal New value that may have to be trimmed down.
-        * @param {number} byteLimit Number of bytes the value may be in size.
-        * @param {Function} [fn] See jQuery#byteLimit.
-        * @return {Object}
-        * @return {string} return.newVal
-        * @return {boolean} return.trimmed
-        */
-       $.trimByteLength = function ( safeVal, newVal, byteLimit, fn ) {
-               var startMatches, endMatches, matchesLen, inpParts,
-                       oldVal = safeVal;
-
-               // Run the hook if one was provided, but only on the length
-               // assessment. The value itself is not to be affected by the hook.
-               if ( $.byteLength( fn ? fn( newVal ) : newVal ) <= byteLimit ) {
-                       // Limit was not reached, just remember the new value
-                       // and let the user continue.
-                       return {
-                               newVal: newVal,
-                               trimmed: false
-                       };
-               }
-
-               // Current input is longer than the active limit.
-               // Figure out what was added and limit the addition.
-               startMatches = 0;
-               endMatches = 0;
-
-               // It is important that we keep the search within the range of
-               // the shortest string's length.
-               // Imagine a user adds text that matches the end of the old value
-               // (e.g. "foo" -> "foofoo"). startMatches would be 3, but without
-               // limiting both searches to the shortest length, endMatches would
-               // also be 3.
-               matchesLen = Math.min( newVal.length, oldVal.length );
-
-               // Count same characters from the left, first.
-               // (if "foo" -> "foofoo", assume addition was at the end).
-               while (
-                       startMatches < matchesLen &&
-                       oldVal.charAt( startMatches ) === newVal.charAt( startMatches )
-               ) {
-                       startMatches += 1;
-               }
-
-               while (
-                       endMatches < ( matchesLen - startMatches ) &&
-                       oldVal.charAt( oldVal.length - 1 - endMatches ) === newVal.charAt( newVal.length - 1 - endMatches )
-               ) {
-                       endMatches += 1;
-               }
-
-               inpParts = [
-                       // Same start
-                       newVal.slice( 0, startMatches ),
-                       // Inserted content
-                       newVal.slice( startMatches, newVal.length - endMatches ),
-                       // Same end
-                       newVal.slice( newVal.length - endMatches )
-               ];
-
-               // Chop off characters from the end of the "inserted content" string
-               // until the limit is statisfied.
-               if ( fn ) {
-                       // stop, when there is nothing to slice - T43450
-                       while ( $.byteLength( fn( inpParts.join( '' ) ) ) > byteLimit && inpParts[ 1 ].length > 0 ) {
-                               inpParts[ 1 ] = inpParts[ 1 ].slice( 0, -1 );
-                       }
-               } else {
-                       while ( $.byteLength( inpParts.join( '' ) ) > byteLimit ) {
-                               inpParts[ 1 ] = inpParts[ 1 ].slice( 0, -1 );
-                       }
-               }
-
-               return {
-                       newVal: inpParts.join( '' ),
-                       // For pathological fn() that always returns a value longer than the limit, we might have
-                       // ended up not trimming - check for this case to avoid infinite loops
-                       trimmed: newVal !== inpParts.join( '' )
-               };
-       };
-
-       /**
-        * Enforces a byte limit on an input field, so that UTF-8 entries are counted as well,
-        * when, for example, a database field has a byte limit rather than a character limit.
-        * Plugin rationale: Browser has native maxlength for number of characters, this plugin
-        * exists to limit number of bytes instead.
-        *
-        * Can be called with a custom limit (to use that limit instead of the maxlength attribute
-        * value), a filter function (in case the limit should apply to something other than the
-        * exact input value), or both. Order of parameters is important!
-        *
-        * @param {number} [limit] Limit to enforce, fallsback to maxLength-attribute,
-        *  called with fetched value as argument.
-        * @param {Function} [fn] Function to call on the string before assessing the length.
-        * @return {jQuery}
-        * @chainable
-        */
-       $.fn.byteLimit = function ( limit, fn ) {
-               // If the first argument is the function,
-               // set fn to the first argument's value and ignore the second argument.
-               if ( $.isFunction( limit ) ) {
-                       fn = limit;
-                       limit = undefined;
-               // Either way, verify it is a function so we don't have to call
-               // isFunction again after this.
-               } else if ( !fn || !$.isFunction( fn ) ) {
-                       fn = undefined;
-               }
-
-               // The following is specific to each element in the collection.
-               return this.each( function ( i, el ) {
-                       var $el, elLimit, prevSafeVal;
-
-                       $el = $( el );
-
-                       // If no limit was passed to byteLimit(), use the maxlength value.
-                       // Can't re-use 'limit' variable because it's in the higher scope
-                       // that would affect the next each() iteration as well.
-                       // Note that we use attribute to read the value instead of property,
-                       // because in Chrome the maxLength property by default returns the
-                       // highest supported value (no indication that it is being enforced
-                       // by choice). We don't want to bind all of this for some ridiculously
-                       // high default number, unless it was explicitly set in the HTML.
-                       // Also cast to a (primitive) number (most commonly because the maxlength
-                       // attribute contains a string, but theoretically the limit parameter
-                       // could be something else as well).
-                       elLimit = Number( limit === undefined ? $el.attr( 'maxlength' ) : limit );
-
-                       // If there is no (valid) limit passed or found in the property,
-                       // skip this. The < 0 check is required for Firefox, which returns
-                       // -1  (instead of undefined) for maxLength if it is not set.
-                       if ( !elLimit || elLimit < 0 ) {
-                               return;
-                       }
-
-                       if ( fn ) {
-                               // Save function for reference
-                               $el.data( 'byteLimit.callback', fn );
-                       }
-
-                       // Remove old event handlers (if there are any)
-                       $el.off( '.byteLimit' );
-
-                       if ( fn ) {
-                               // Disable the native maxLength (if there is any), because it interferes
-                               // with the (differently calculated) byte limit.
-                               // Aside from being differently calculated (average chars with byteLimit
-                               // is lower), we also support a callback which can make it to allow longer
-                               // values (e.g. count "Foo" from "User:Foo").
-                               // maxLength is a strange property. Removing or setting the property to
-                               // undefined directly doesn't work. Instead, it can only be unset internally
-                               // by the browser when removing the associated attribute (Firefox/Chrome).
-                               // https://bugs.chromium.org/p/chromium/issues/detail?id=136004
-                               $el.removeAttr( 'maxlength' );
-
-                       } else {
-                               // If we don't have a callback the bytelimit can only be lower than the charlimit
-                               // (that is, there are no characters less than 1 byte in size). So lets (re-)enforce
-                               // the native limit for efficiency when possible (it will make the while-loop below
-                               // faster by there being less left to interate over).
-                               $el.attr( 'maxlength', elLimit );
-                       }
-
-                       // Safe base value, used to determine the path between the previous state
-                       // and the state that triggered the event handler below - and enforce the
-                       // limit approppiately (e.g. don't chop from the end if text was inserted
-                       // at the beginning of the string).
-                       prevSafeVal = '';
-
-                       // We need to listen to after the change has already happened because we've
-                       // learned that trying to guess the new value and canceling the event
-                       // accordingly doesn't work because the new value is not always as simple as:
-                       // oldValue + String.fromCharCode( e.which ); because of cut, paste, select-drag
-                       // replacements, and custom input methods and what not.
-                       // Even though we only trim input after it was changed (never prevent it), we do
-                       // listen on events that input text, because there are cases where the text has
-                       // changed while text is being entered and keyup/change will not be fired yet
-                       // (such as holding down a single key, fires keydown, and after each keydown,
-                       // we can trim the previous one).
-                       // See https://www.w3.org/TR/DOM-Level-3-Events/#events-keyboard-event-order for
-                       // the order and characteristics of the key events.
-                       $el.on( eventKeys, function () {
-                               var res = $.trimByteLength(
-                                       prevSafeVal,
-                                       this.value,
-                                       elLimit,
-                                       fn
-                               );
-
-                               // Only set value property if it was trimmed, because whenever the
-                               // value property is set, the browser needs to re-initiate the text context,
-                               // which moves the cursor at the end the input, moving it away from wherever it was.
-                               // This is a side-effect of limiting after the fact.
-                               if ( res.trimmed === true ) {
-                                       this.value = res.newVal;
-                                       // Trigger a 'change' event to let other scripts attached to this node know that the value
-                                       // was changed. This will also call ourselves again, but that's okay, it'll be a no-op.
-                                       $el.trigger( 'change' );
-                               }
-                               // Always adjust prevSafeVal to reflect the input value. Not doing this could cause
-                               // trimByteLength to compare the new value to an empty string instead of the
-                               // old value, resulting in trimming always from the end (T42850).
-                               prevSafeVal = res.newVal;
-                       } );
-               } );
-       };
-
-       /**
-        * @class jQuery
-        * @mixins jQuery.plugin.byteLimit
-        */
-}( jQuery ) );
diff --git a/resources/src/jquery/jquery.lengthLimit.js b/resources/src/jquery/jquery.lengthLimit.js
new file mode 100644 (file)
index 0000000..2738d1a
--- /dev/null
@@ -0,0 +1,204 @@
+/**
+ * @class jQuery.plugin.lengthLimit
+ */
+( function ( $, mw ) {
+
+       var
+               eventKeys = [
+                       'keyup.lengthLimit',
+                       'keydown.lengthLimit',
+                       'change.lengthLimit',
+                       'mouseup.lengthLimit',
+                       'cut.lengthLimit',
+                       'paste.lengthLimit',
+                       'focus.lengthLimit',
+                       'blur.lengthLimit'
+               ].join( ' ' ),
+               trimByteLength = require( 'mediawiki.String' ).trimByteLength,
+               trimCodePointLength = require( 'mediawiki.String' ).trimCodePointLength;
+
+       /**
+        * Utility function to trim down a string, based on byteLimit
+        * and given a safe start position. It supports insertion anywhere
+        * in the string, so "foo" to "fobaro" if limit is 4 will result in
+        * "fobo", not "foba". Basically emulating the native maxlength by
+        * reconstructing where the insertion occurred.
+        *
+        * @method trimByteLength
+        * @deprecated Use `require( 'mediawiki.String' ).trimByteLength` instead.
+        * @static
+        * @param {string} safeVal Known value that was previously returned by this
+        * function, if none, pass empty string.
+        * @param {string} newVal New value that may have to be trimmed down.
+        * @param {number} byteLimit Number of bytes the value may be in size.
+        * @param {Function} [filterFn] See jQuery#byteLimit.
+        * @return {Object}
+        * @return {string} return.newVal
+        * @return {boolean} return.trimmed
+        */
+       mw.log.deprecate( $, 'trimByteLength', trimByteLength,
+               'Use require( \'mediawiki.String\' ).trimByteLength instead.', '$.trimByteLength' );
+
+       function lengthLimit( trimFn, limit, filterFn ) {
+               var allowNativeMaxlength = trimFn === trimByteLength;
+
+               // If the first argument is the function,
+               // set filterFn to the first argument's value and ignore the second argument.
+               if ( $.isFunction( limit ) ) {
+                       filterFn = limit;
+                       limit = undefined;
+               // Either way, verify it is a function so we don't have to call
+               // isFunction again after this.
+               } else if ( !filterFn || !$.isFunction( filterFn ) ) {
+                       filterFn = undefined;
+               }
+
+               // The following is specific to each element in the collection.
+               return this.each( function ( i, el ) {
+                       var $el, elLimit, prevSafeVal;
+
+                       $el = $( el );
+
+                       // If no limit was passed to lengthLimit(), use the maxlength value.
+                       // Can't re-use 'limit' variable because it's in the higher scope
+                       // that would affect the next each() iteration as well.
+                       // Note that we use attribute to read the value instead of property,
+                       // because in Chrome the maxLength property by default returns the
+                       // highest supported value (no indication that it is being enforced
+                       // by choice). We don't want to bind all of this for some ridiculously
+                       // high default number, unless it was explicitly set in the HTML.
+                       // Also cast to a (primitive) number (most commonly because the maxlength
+                       // attribute contains a string, but theoretically the limit parameter
+                       // could be something else as well).
+                       elLimit = Number( limit === undefined ? $el.attr( 'maxlength' ) : limit );
+
+                       // If there is no (valid) limit passed or found in the property,
+                       // skip this. The < 0 check is required for Firefox, which returns
+                       // -1  (instead of undefined) for maxLength if it is not set.
+                       if ( !elLimit || elLimit < 0 ) {
+                               return;
+                       }
+
+                       if ( filterFn ) {
+                               // Save function for reference
+                               $el.data( 'lengthLimit.callback', filterFn );
+                       }
+
+                       // Remove old event handlers (if there are any)
+                       $el.off( '.lengthLimit' );
+
+                       if ( filterFn || !allowNativeMaxlength ) {
+                               // Disable the native maxLength (if there is any), because it interferes
+                               // with the (differently calculated) character/byte limit.
+                               // Aside from being differently calculated,
+                               // we also support a callback which can make it to allow longer
+                               // values (e.g. count "Foo" from "User:Foo").
+                               // maxLength is a strange property. Removing or setting the property to
+                               // undefined directly doesn't work. Instead, it can only be unset internally
+                               // by the browser when removing the associated attribute (Firefox/Chrome).
+                               // https://bugs.chromium.org/p/chromium/issues/detail?id=136004
+                               $el.removeAttr( 'maxlength' );
+
+                       } else {
+                               // For $.byteLimit only, if we don't have a callback,
+                               // the byteLimit can only be lower than the native maxLength limit
+                               // (that is, there are no characters less than 1 byte in size). So lets (re-)enforce
+                               // the native limit for efficiency when possible (it will make the while-loop below
+                               // faster by there being less left to interate over). This does not work for $.codePointLimit
+                               // (code units for surrogates represent half a character each).
+                               $el.attr( 'maxlength', elLimit );
+                       }
+
+                       // Safe base value, used to determine the path between the previous state
+                       // and the state that triggered the event handler below - and enforce the
+                       // limit approppiately (e.g. don't chop from the end if text was inserted
+                       // at the beginning of the string).
+                       prevSafeVal = '';
+
+                       // We need to listen to after the change has already happened because we've
+                       // learned that trying to guess the new value and canceling the event
+                       // accordingly doesn't work because the new value is not always as simple as:
+                       // oldValue + String.fromCharCode( e.which ); because of cut, paste, select-drag
+                       // replacements, and custom input methods and what not.
+                       // Even though we only trim input after it was changed (never prevent it), we do
+                       // listen on events that input text, because there are cases where the text has
+                       // changed while text is being entered and keyup/change will not be fired yet
+                       // (such as holding down a single key, fires keydown, and after each keydown,
+                       // we can trim the previous one).
+                       // See https://www.w3.org/TR/DOM-Level-3-Events/#events-keyboard-event-order for
+                       // the order and characteristics of the key events.
+                       $el.on( eventKeys, function () {
+                               var res = trimFn(
+                                       prevSafeVal,
+                                       this.value,
+                                       elLimit,
+                                       filterFn
+                               );
+
+                               // Only set value property if it was trimmed, because whenever the
+                               // value property is set, the browser needs to re-initiate the text context,
+                               // which moves the cursor at the end the input, moving it away from wherever it was.
+                               // This is a side-effect of limiting after the fact.
+                               if ( res.trimmed === true ) {
+                                       this.value = res.newVal;
+                                       // Trigger a 'change' event to let other scripts attached to this node know that the value
+                                       // was changed. This will also call ourselves again, but that's okay, it'll be a no-op.
+                                       $el.trigger( 'change' );
+                               }
+                               // Always adjust prevSafeVal to reflect the input value. Not doing this could cause
+                               // trimFn to compare the new value to an empty string instead of the
+                               // old value, resulting in trimming always from the end (T42850).
+                               prevSafeVal = res.newVal;
+                       } );
+               } );
+       }
+
+       /**
+        * Enforces a byte limit on an input field, assuming UTF-8 encoding, for situations
+        * when, for example, a database field has a byte limit rather than a character limit.
+        * Plugin rationale: Browser has native maxlength for number of characters (technically,
+        * UTF-16 code units), this plugin exists to limit number of bytes instead.
+        *
+        * Can be called with a custom limit (to use that limit instead of the maxlength attribute
+        * value), a filter function (in case the limit should apply to something other than the
+        * exact input value), or both. Order of parameters is important!
+        *
+        * @param {number} [limit] Limit to enforce, fallsback to maxLength-attribute,
+        *  called with fetched value as argument.
+        * @param {Function} [filterFn] Function to call on the string before assessing the length.
+        * @return {jQuery}
+        * @chainable
+        */
+       $.fn.byteLimit = function ( limit, filterFn ) {
+               return lengthLimit.call( this, trimByteLength, limit, filterFn );
+       };
+
+       /**
+        * Enforces a codepoint (character) limit on an input field.
+        *
+        * For unfortunate historical reasons, browsers' native maxlength counts [the number of UTF-16
+        * code units rather than Unicode codepoints] [1], which means that codepoints outside the Basic
+        * Multilingual Plane (e.g. many emojis) count as 2 characters each. This plugin exists to
+        * correct this.
+        *
+        * [1]: https://www.w3.org/TR/html5/sec-forms.html#limiting-user-input-length-the-maxlength-attribute
+        *
+        * Can be called with a custom limit (to use that limit instead of the maxlength attribute
+        * value), a filter function (in case the limit should apply to something other than the
+        * exact input value), or both. Order of parameters is important!
+        *
+        * @param {number} [limit] Limit to enforce, fallsback to maxLength-attribute,
+        *  called with fetched value as argument.
+        * @param {Function} [filterFn] Function to call on the string before assessing the length.
+        * @return {jQuery}
+        * @chainable
+        */
+       $.fn.codePointLimit = function ( limit, filterFn ) {
+               return lengthLimit.call( this, trimCodePointLength, limit, filterFn );
+       };
+
+       /**
+        * @class jQuery
+        * @mixins jQuery.plugin.lengthLimit
+        */
+}( jQuery, mediaWiki ) );
diff --git a/resources/src/mediawiki.action/mediawiki.action.delete.file.js b/resources/src/mediawiki.action/mediawiki.action.delete.file.js
new file mode 100644 (file)
index 0000000..d6e6796
--- /dev/null
@@ -0,0 +1,31 @@
+/*!
+ * JavaScript for Special:RevisionDelete
+ */
+( function ( mw, $ ) {
+       $( function () {
+               var colonSeparator = mw.message( 'colon-separator' ).text(),
+                       summaryCodePointLimit = mw.config.get( 'wgCommentCodePointLimit' ),
+                       summaryByteLimit = mw.config.get( 'wgCommentByteLimit' ),
+                       $wpDeleteReasonList = $( '#wpDeleteReasonList' ),
+                       $wpReason = $( '#wpReason' ),
+                       filterFn = function ( input ) {
+                               // Should be built the same as in SpecialRevisionDelete::submit()
+                               var comment = $wpDeleteReasonList.val();
+                               if ( comment === 'other' ) {
+                                       comment = input;
+                               } else if ( input !== '' ) {
+                                       // Entry from drop down menu + additional comment
+                                       comment += colonSeparator + input;
+                               }
+                               return comment;
+                       };
+
+               // Limit to bytes or UTF-8 codepoints, depending on MediaWiki's configuration
+               if ( summaryCodePointLimit ) {
+                       $wpReason.codePointLimit( summaryCodePointLimit, filterFn );
+               } else if ( summaryByteLimit ) {
+                       $wpReason.bytePointLimit( summaryByteLimit, filterFn );
+               }
+       } );
+
+}( mediaWiki, jQuery ) );
diff --git a/resources/src/mediawiki.action/mediawiki.action.delete.js b/resources/src/mediawiki.action/mediawiki.action.delete.js
new file mode 100644 (file)
index 0000000..c353a48
--- /dev/null
@@ -0,0 +1,30 @@
+/*!
+ * Scripts for action=delete at domready
+ */
+( function ( mw, $ ) {
+       $( function () {
+               var colonSeparator = mw.message( 'colon-separator' ).text(),
+                       summaryCodePointLimit = mw.config.get( 'wgCommentCodePointLimit' ),
+                       summaryByteLimit = mw.config.get( 'wgCommentByteLimit' ),
+                       reasonList = OO.ui.infuse( $( '#wpDeleteReasonList' ).closest( '.oo-ui-widget' ) ),
+                       reason = OO.ui.infuse( $( '#wpReason' ).closest( '.oo-ui-widget' ) ),
+                       filterFn = function ( input ) {
+                               // Should be built the same as in Article::delete()
+                               var comment = reasonList.getValue();
+                               if ( comment === 'other' ) {
+                                       comment = input;
+                               } else if ( input !== '' ) {
+                                       // Entry from drop down menu + additional comment
+                                       comment += colonSeparator + input;
+                               }
+                               return comment;
+                       };
+
+               // Limit to bytes or UTF-8 codepoints, depending on MediaWiki's configuration
+               if ( summaryCodePointLimit ) {
+                       reason.$input.codePointLimit( summaryCodePointLimit, filterFn );
+               } else if ( summaryByteLimit ) {
+                       reason.$input.bytePointLimit( summaryByteLimit, filterFn );
+               }
+       } );
+}( mediaWiki, jQuery ) );
index 087c5bc..a85e740 100644 (file)
 
        $( function () {
                var editBox, scrollTop, $editForm,
-                       // TODO T6714: Once this can be adjusted, read this from config.
-                       summaryByteLimit = 255,
+                       summaryCodePointLimit = mw.config.get( 'wgCommentCodePointLimit' ),
+                       summaryByteLimit = mw.config.get( 'wgCommentByteLimit' ),
                        wpSummary = OO.ui.infuse( $( '#wpSummaryWidget' ) );
 
                // Show a byte-counter to users with how many bytes are left for their edit summary.
                // TODO: This looks a bit weird, as there is no unit in the UI, just numbers; showing
                // 'bytes' confused users in testing, and showing 'chars' would be a lie. See T42035.
-               mw.widgets.visibleByteLimit( wpSummary, summaryByteLimit );
+               // (Showing 'chars' is still confusing with the code point limit, since it's not obvious
+               // that e.g. combining diacritics or zero-width punctuation count as characters.)
+               if ( summaryCodePointLimit ) {
+                       mw.widgets.visibleCodePointLimit( wpSummary, summaryCodePointLimit );
+               } else if ( summaryByteLimit ) {
+                       mw.widgets.visibleByteLimit( wpSummary, summaryByteLimit );
+               }
 
                // Restore the edit box scroll state following a preview operation,
                // and set up a form submission handler to remember this state.
index ca4b6fb..a13f059 100644 (file)
@@ -19,9 +19,9 @@
                 * @return {number} plural form index
                 */
                getPluralForm: function ( number, pluralRules ) {
-                       var i;
+                       var i, pluralRuleParser = require( 'mediawiki.libs.pluralruleparser' );
                        for ( i = 0; i < pluralRules.length; i++ ) {
-                               if ( mw.libs.pluralRuleParser( pluralRules[ i ], number ) ) {
+                               if ( pluralRuleParser( pluralRules[ i ], number ) ) {
                                        break;
                                }
                        }
index aa49ae1..b96bebc 100644 (file)
@@ -1,6 +1,9 @@
 ( function ( mw, $ ) {
+       var ProtectionForm,
+               reasonCodePointLimit = mw.config.get( 'wgCommentCodePointLimit' ),
+               reasonByteLimit = mw.config.get( 'wgCommentByteLimit' );
 
-       var ProtectionForm = window.ProtectionForm = {
+       ProtectionForm = window.ProtectionForm = {
                /**
                 * Set up the protection chaining interface (i.e. "unlock move permissions" checkbox)
                 * on the protection form
                                this.toggleUnchainedInputs( !this.areAllTypesMatching() );
                        }
 
-                       $( '#mwProtect-reason' ).byteLimit( 180 );
+                       // Arbitrary 75 to leave some space for the autogenerated null edit's summary
+                       if ( reasonCodePointLimit ) {
+                               $( '#mwProtect-reason' ).codePointLimit( reasonCodePointLimit - 75 );
+                       } else if ( reasonByteLimit ) {
+                               $( '#mwProtect-reason' ).byteLimit( reasonByteLimit - 75 );
+                       }
 
                        this.updateCascadeCheckbox();
                        return true;
index 58f6dc2..1218644 100644 (file)
        list-style-image: e( '/* @embed */' ) url( @fallback ) e( '\9' );
 }
 
+.hyphens( @value: auto ) {
+       & when ( @value = auto ){
+               // Legacy `word-wrap`; IE 6-11, Edge 12+, Firefox 3.5+, Chrome 4+, Safari 3.1+,
+               //   Opera 11.5+, iOS 3.2+, Android 2.1+
+               // `overflow-wrap` is W3 standard, but it doesn't seem as if browser vendors
+               //   will abandon `word-wrap` (it has wider support), therefore no duplication.
+               word-wrap: break-word;
+       }
+       & when ( @value = none ) {
+               word-wrap: normal;
+       }
+
+       // CSS3 hyphenation
+       -webkit-hyphens: @value; // Safari 5.1+, iOS 4.3+
+       -moz-hyphens: @value;    // Firefox 6-42
+       -ms-hyphens: @value;     // IE 10-11, Edge 12+
+       hyphens: @value;         // Firefox 43+, Chrome 55+, Android 62+, UC Browser 11.8+, Samsung 6.2+
+}
+
 .transform( @value ) {
        -webkit-transform: @value; // Safari 3.1-8.0, iOS 3.2-8.4, Android 2.1-4.4.4
        -moz-transform: @value; // Firefox 3.5-15
index 4b78175..16f58ee 100644 (file)
@@ -1,4 +1,7 @@
 ( function ( mw, $ ) {
+
+       var byteLength = require( 'mediawiki.String' ).byteLength;
+
        /* eslint no-underscore-dangle: "off" */
        /**
         * Controller for the filters in Recent Changes
                                info.noResultsDetails = 'NO_RESULTS_TIMEOUT';
                        } else if ( $root.find( '.mw-changeslist-notargetpage' ).length ) {
                                info.noResultsDetails = 'NO_RESULTS_NO_TARGET_PAGE';
+                       } else if ( $root.find( '.mw-changeslist-invalidtargetpage' ).length ) {
+                               info.noResultsDetails = 'NO_RESULTS_INVALID_TARGET_PAGE';
                        } else {
                                info.noResultsDetails = 'NO_RESULTS_NORMAL';
                        }
                // Stringify state
                stringified = JSON.stringify( state );
 
-               if ( $.byteLength( stringified ) > 65535 ) {
+               if ( byteLength( stringified ) > 65535 ) {
                        // Sanity check, since the preference can only hold that.
                        return;
                }
index 1f72484..d181532 100644 (file)
                                {
                                        $topSection: $topSection,
                                        $filtersContainer: $( '.rcfilters-container' ),
-                                       $changesListContainer: $( [
-                                               '.mw-changeslist',
-                                               '.mw-changeslist-empty',
-                                               '.mw-changeslist-timeout',
-                                               '.mw-changeslist-notargetpage'
-                                       ].join( ', ' ) ),
+                                       $changesListContainer: $( '.mw-changeslist, .mw-changeslist-empty' ),
                                        $formContainer: $initialFieldset
                                }
                        );
index 413d45b..ca9b252 100644 (file)
                }
        }
 
-       // Temporarily hide any specific 'no result' message while we load rcfilters.
-       .mw-changeslist-empty,
-       .mw-changeslist-timeout,
-       .mw-changeslist-notargetpage {
+       // Temporarily hide the empty results section while we load rcfilters.
+       .mw-changeslist-empty {
+               display: none;
+       }
+
+       .errorbox {
                display: none;
        }
 
index e2092dc..b49a1cb 100644 (file)
                                                        .text( mw.msg( this.getMsgKeyForNoResults( noResultsDetails ) ) )
                                        );
 
-                               this.$element.removeClass( 'mw-changeslist-timeout' );
-                               this.$element.removeClass( 'mw-changeslist-notargetpage' );
+                               // remove all classes matching mw-changeslist-*
+                               this.$element.removeClass( function ( elementIndex, allClasses ) {
+                                       return allClasses
+                                               .split( ' ' )
+                                               .filter( function ( className ) {
+                                                       return className.indexOf( 'mw-changeslist-' ) === 0;
+                                               } )
+                                               .join( ' ' );
+                               } );
                        }
 
                        this.$element.append( $message );
                        NO_RESULTS_NORMAL: 'recentchanges-noresult',
                        NO_RESULTS_TIMEOUT: 'recentchanges-timeout',
                        NO_RESULTS_NETWORK_ERROR: 'recentchanges-network',
-                       NO_RESULTS_NO_TARGET_PAGE: 'recentchanges-notargetpage'
+                       NO_RESULTS_NO_TARGET_PAGE: 'recentchanges-notargetpage',
+                       NO_RESULTS_INVALID_TARGET_PAGE: 'allpagesbadtitle'
                };
                return reasonMsgKeyMap[ reason ];
        };
index cbb4350..d968f0c 100644 (file)
                this.footers = [];
 
                // Parent
-               mw.rcfilters.ui.MenuSelectWidget.parent.call( this, $.extend( {
+               mw.rcfilters.ui.MenuSelectWidget.parent.call( this, $.extend( config, {
                        $autoCloseIgnore: this.$overlay,
                        width: 650,
                        // Our filtering is done through the model
                        filterFromInput: false
-               }, config ) );
+               } ) );
                this.setGroupElement(
                        $( '<div>' )
                                .addClass( 'mw-rcfilters-ui-menuSelectWidget-group' )
index 0df51f4..39252dd 100644 (file)
@@ -98,7 +98,7 @@
 
                dropdownWidget: {
                        getApiValue: function () {
-                               var item = this.getMenu().getSelectedItem();
+                               var item = this.getMenu().findSelectedItem();
                                return item === null ? undefined : item.getData();
                        },
                        setApiValue: function ( v ) {
                        var i,
                                menu = formatDropdown.getMenu(),
                                items = menu.getItems(),
-                               selectedField = menu.getSelectedItem() ? menu.getSelectedItem().getData() : null;
+                               selectedField = menu.findSelectedItem() ? menu.findSelectedItem().getData() : null;
 
                        for ( i = 0; i < items.length; i++ ) {
                                items[ i ].getData().toggle( items[ i ].getData() === selectedField );
                                }
 
                                menu = formatDropdown.getMenu();
-                               selectedLabel = menu.getSelectedItem() ? menu.getSelectedItem().getLabel() : '';
+                               selectedLabel = menu.findSelectedItem() ? menu.findSelectedItem().getLabel() : '';
                                if ( typeof selectedLabel !== 'string' ) {
                                        selectedLabel = selectedLabel.text();
                                }
index 3e6e684..45c3cf9 100644 (file)
@@ -3,7 +3,11 @@
  */
 ( function ( mw, $ ) {
        $( function () {
-               var $tagList = $( '#mw-edittags-tag-list' );
+               var summaryCodePointLimit = mw.config.get( 'wgCommentCodePointLimit' ),
+                       summaryByteLimit = mw.config.get( 'wgCommentByteLimit' ),
+                       $wpReason = $( '#wpReason' ),
+                       $tagList = $( '#mw-edittags-tag-list' );
+
                if ( $tagList.length ) {
                        $tagList.chosen( {
                                /* eslint-disable camelcase */
                                $( '#mw-edittags-remove-all' ).prop( 'checked', false );
                        }
                } );
+
+               // Limit to bytes or UTF-8 codepoints, depending on MediaWiki's configuration
+               // use maxLength because it's leaving room for log entry text.
+               if ( summaryCodePointLimit ) {
+                       $wpReason.codePointLimit();
+               } else if ( summaryByteLimit ) {
+                       $wpReason.bytePointLimit();
+               }
        } );
+
 }( mediaWiki, jQuery ) );
index 2e980ac..d828396 100644 (file)
@@ -3,10 +3,18 @@
  */
 ( function ( mw, $ ) {
        $( function () {
+               var summaryCodePointLimit = mw.config.get( 'wgCommentCodePointLimit' ),
+                       summaryByteLimit = mw.config.get( 'wgCommentByteLimit' ),
+                       wpReason = OO.ui.infuse( $( '#wpReason' ) );
+
                // Infuse for pretty dropdown
                OO.ui.infuse( $( '#wpNewTitle' ) );
-               // Limit to bytes, not characters
-               mw.widgets.visibleByteLimit( OO.ui.infuse( $( '#wpReason' ) ) );
+               // Limit to bytes or UTF-8 codepoints, depending on MediaWiki's configuration
+               if ( summaryCodePointLimit ) {
+                       mw.widgets.visibleCodePointLimit( wpReason, summaryCodePointLimit );
+               } else if ( summaryByteLimit ) {
+                       mw.widgets.visibleByteLimit( wpReason, summaryByteLimit );
+               }
                // Infuse for nicer "help" popup
                if ( $( '#wpMovetalk-field' ).length ) {
                        OO.ui.infuse( $( '#wpMovetalk-field' ) );
diff --git a/resources/src/mediawiki.special/mediawiki.special.revisionDelete.js b/resources/src/mediawiki.special/mediawiki.special.revisionDelete.js
new file mode 100644 (file)
index 0000000..c6d44fa
--- /dev/null
@@ -0,0 +1,29 @@
+/*!
+ * JavaScript for Special:RevisionDelete
+ */
+( function ( mw, $ ) {
+       var colonSeparator = mw.message( 'colon-separator' ).text(),
+               summaryCodePointLimit = mw.config.get( 'wgCommentCodePointLimit' ),
+               summaryByteLimit = mw.config.get( 'wgCommentByteLimit' ),
+               $wpRevDeleteReasonList = $( '#wpRevDeleteReasonList' ),
+               $wpReason = $( '#wpReason' ),
+               filterFn = function ( input ) {
+                       // Should be built the same as in SpecialRevisionDelete::submit()
+                       var comment = $wpRevDeleteReasonList.val();
+                       if ( comment === 'other' ) {
+                               comment = input;
+                       } else if ( input !== '' ) {
+                               // Entry from drop down menu + additional comment
+                               comment += colonSeparator + input;
+                       }
+                       return comment;
+               };
+
+       // Limit to bytes or UTF-8 codepoints, depending on MediaWiki's configuration
+       if ( summaryCodePointLimit ) {
+               $wpReason.codePointLimit( summaryCodePointLimit, filterFn );
+       } else if ( summaryByteLimit ) {
+               $wpReason.bytePointLimit( summaryByteLimit, filterFn );
+       }
+
+}( mediaWiki, jQuery ) );
index 4629d57..e3cf598 100644 (file)
@@ -1,10 +1,23 @@
 /*!
  * JavaScript for Special:Undelete
  */
-jQuery( function ( $ ) {
-       $( '#mw-undelete-invert' ).click( function () {
-               $( '.mw-undelete-revlist input[type="checkbox"]' ).prop( 'checked', function ( i, val ) {
-                       return !val;
+( function ( mw, $ ) {
+       $( function () {
+               var summaryCodePointLimit = mw.config.get( 'wgCommentCodePointLimit' ),
+                       summaryByteLimit = mw.config.get( 'wgCommentByteLimit' ),
+                       wpComment = OO.ui.infuse( $( '#wpComment' ).closest( '.oo-ui-widget' ) );
+
+               $( '#mw-undelete-invert' ).click( function () {
+                       $( '.mw-undelete-revlist input[type="checkbox"]' ).prop( 'checked', function ( i, val ) {
+                               return !val;
+                       } );
                } );
+
+               // Limit to bytes or UTF-8 codepoints, depending on MediaWiki's configuration
+               if ( summaryCodePointLimit ) {
+                       mw.widgets.visibleCodePointLimit( wpComment, summaryCodePointLimit );
+               } else if ( summaryByteLimit ) {
+                       mw.widgets.visibleByteLimit( wpComment, summaryByteLimit );
+               }
        } );
-} );
+}( mediaWiki, jQuery ) );
index 054f45f..69fec08 100644 (file)
@@ -4,6 +4,4 @@
 
 .mw-watch-link-disabled {
        pointer-events: none;
-       /* Fallback for older browsers not supporting pointer-events: none */
-       cursor: default;
 }
index 34c8bbd..0886f8c 100644 (file)
                                title = mw.util.getParamValue( 'title', $link.attr( 'href' ) );
                        // nice format
                        title = mw.Title.newFromText( title ).toText();
-                       // Disable link whilst we're busy to avoid double handling
-                       if ( $link.data( 'mwDisabled' ) ) {
-                               // mw-watch-link-disabled disables pointer-events which prevents the click event
-                               // from happening in the first place. In older browsers we kill the event here.
-                               return false;
-                       }
-                       $link.data( 'mwDisabled', true ).addClass( 'mw-watch-link-disabled' );
+                       $link.addClass( 'mw-watch-link-disabled' );
 
                        // Preload the notification module for mw.notify
                        mw.loader.load( 'mediawiki.notification' );
@@ -46,7 +40,7 @@
                        }
 
                        promise.always( function () {
-                               $link.data( 'mwDisabled', false ).removeClass( 'mw-watch-link-disabled' );
+                               $link.removeClass( 'mw-watch-link-disabled' );
                        } );
 
                        e.preventDefault();
index 57578a6..de5ab87 100644 (file)
                                };
                                reader.readAsArrayBuffer( file );
                        } else if ( 'URL' in window && 'createObjectURL' in window.URL ) {
-                               // Supported in Firefox 4.0 and above <https://developer.mozilla.org/en/DOM/window.URL.createObjectURL>
+                               // Supported in Firefox 4.0 and above <https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL>
                                // WebKit has it in a namespace for now but that's ok. ;)
                                //
                                // Lifetime of this URL is until document close, which is fine
index d3494f7..981344d 100644 (file)
@@ -1,8 +1,12 @@
 /*!
  * JavaScript for Special:UserRights
  */
-( function ( $ ) {
-       var convertmessagebox = require( 'mediawiki.notification.convertmessagebox' );
+( function ( mw, $ ) {
+       var convertmessagebox = require( 'mediawiki.notification.convertmessagebox' ),
+               summaryCodePointLimit = mw.config.get( 'wgCommentCodePointLimit' ),
+               summaryByteLimit = mw.config.get( 'wgCommentByteLimit' ),
+               $wpReason = $( '#wpReason' );
+
        // Replace successbox with notifications
        convertmessagebox();
 
        $( '.mw-userrights-nested select' ).on( 'change', function ( e ) {
                $( e.target.parentNode ).find( 'input' ).toggle( $( e.target ).val() === 'other' );
        } );
-}( jQuery ) );
+
+       // Limit to bytes or UTF-8 codepoints, depending on MediaWiki's configuration
+       if ( summaryCodePointLimit ) {
+               $wpReason.codePointLimit( summaryCodePointLimit );
+       } else if ( summaryByteLimit ) {
+               $wpReason.bytePointLimit( summaryByteLimit );
+       }
+
+}( mediaWiki, jQuery ) );
diff --git a/resources/src/mediawiki.widgets.visibleByteLimit/mediawiki.widgets.visibleByteLimit.js b/resources/src/mediawiki.widgets.visibleByteLimit/mediawiki.widgets.visibleByteLimit.js
deleted file mode 100644 (file)
index a810c98..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-/**
- * @class mw.widgets
- */
-
-/**
- * Add a visible byte limit label to a TextInputWidget.
- *
- * Uses jQuery#byteLimit to enforce the limit.
- *
- * @param {OO.ui.TextInputWidget} textInputWidget Text input widget
- * @param {number} [limit] Byte limit, defaults to $input's maxlength
- */
-mediaWiki.widgets.visibleByteLimit = function ( textInputWidget, limit ) {
-       limit = limit || +textInputWidget.$input.attr( 'maxlength' );
-
-       function updateCount() {
-               textInputWidget.setLabel( ( limit - $.byteLength( textInputWidget.getValue() ) ).toString() );
-       }
-       textInputWidget.on( 'change', updateCount );
-       // Initialise value
-       updateCount();
-
-       // Actually enforce limit
-       textInputWidget.$input.byteLimit( limit );
-};
diff --git a/resources/src/mediawiki.widgets.visibleLengthLimit/mediawiki.widgets.visibleLengthLimit.js b/resources/src/mediawiki.widgets.visibleLengthLimit/mediawiki.widgets.visibleLengthLimit.js
new file mode 100644 (file)
index 0000000..52ebe74
--- /dev/null
@@ -0,0 +1,54 @@
+( function ( mw ) {
+
+       var byteLength = require( 'mediawiki.String' ).byteLength,
+               codePointLength = require( 'mediawiki.String' ).codePointLength;
+
+       /**
+        * @class mw.widgets
+        */
+
+       /**
+        * Add a visible byte limit label to a TextInputWidget.
+        *
+        * Uses jQuery#byteLimit to enforce the limit.
+        *
+        * @param {OO.ui.TextInputWidget} textInputWidget Text input widget
+        * @param {number} [limit] Byte limit, defaults to $input's maxlength
+        */
+       mw.widgets.visibleByteLimit = function ( textInputWidget, limit ) {
+               limit = limit || +textInputWidget.$input.attr( 'maxlength' );
+
+               function updateCount() {
+                       textInputWidget.setLabel( ( limit - byteLength( textInputWidget.getValue() ) ).toString() );
+               }
+               textInputWidget.on( 'change', updateCount );
+               // Initialise value
+               updateCount();
+
+               // Actually enforce limit
+               textInputWidget.$input.byteLimit( limit );
+       };
+
+       /**
+        * Add a visible codepoint (character) limit label to a TextInputWidget.
+        *
+        * Uses jQuery#codePointLimit to enforce the limit.
+        *
+        * @param {OO.ui.TextInputWidget} textInputWidget Text input widget
+        * @param {number} [limit] Byte limit, defaults to $input's maxlength
+        */
+       mw.widgets.visibleCodePointLimit = function ( textInputWidget, limit ) {
+               limit = limit || +textInputWidget.$input.attr( 'maxlength' );
+
+               function updateCount() {
+                       textInputWidget.setLabel( ( limit - codePointLength( textInputWidget.getValue() ) ).toString() );
+               }
+               textInputWidget.on( 'change', updateCount );
+               // Initialise value
+               updateCount();
+
+               // Actually enforce limit
+               textInputWidget.$input.codePointLimit( limit );
+       };
+
+}( mediaWiki ) );
index 98d07f3..4b1109b 100644 (file)
@@ -6,6 +6,8 @@
  */
 ( function ( $, mw ) {
 
+       var trimByteLength = require( 'mediawiki.String' ).trimByteLength;
+
        /**
         * Creates an mw.widgets.TitleInputWidget object.
         *
                // Parent method
                value = mw.widgets.TitleInputWidget.parent.prototype.cleanUpValue.call( this, value );
 
-               return $.trimByteLength( this.value, value, this.maxLength, function ( value ) {
+               return trimByteLength( this.value, value, this.maxLength, function ( value ) {
                        var title = widget.getMWTitle( value );
                        return title ? title.getMain() : value;
                } ).newVal;
index 95227d0..fda6742 100644 (file)
                $root
                        .find( '.mw-htmlform-select-and-other-field' )
                        .each( function () {
-                               var $this = $( this ),
+                               var $reasonList, currentValReasonList, maxlengthUnit, lengthLimiter, widget,
+                                       $this = $( this ),
+                                       $widget = $this.closest( '.oo-ui-widget[data-ooui]' );
+
+                               if ( $widget ) {
+                                       mw.loader.using( 'mediawiki.widgets.SelectWithInputWidget', function () {
+                                               widget = OO.ui.Widget.static.infuse( $widget );
+                                               maxlengthUnit = widget.getData().maxlengthUnit;
+                                               lengthLimiter = maxlengthUnit === 'codepoints' ? 'codePointLimit' : 'byteLimit';
+                                               widget.textinput.$input[ lengthLimiter ]( function ( input ) {
+                                                       // Should be built the same as in HTMLSelectAndOtherField::loadDataFromRequest
+                                                       var comment = widget.dropdowninput.getValue();
+                                                       if ( comment === 'other' ) {
+                                                               comment = input;
+                                                       } else if ( input !== '' ) {
+                                                               // Entry from drop down menu + additional comment
+                                                               comment += colonSeparator + input;
+                                                       }
+                                                       return comment;
+                                               } );
+                                       } );
+                               } else {
                                        // find the reason list
-                                       $reasonList = $root.find( '#' + $this.data( 'id-select' ) ),
+                                       $reasonList = $root.find( '#' + $this.data( 'id-select' ) );
                                        // cache the current selection to avoid expensive lookup
                                        currentValReasonList = $reasonList.val();
 
-                               $reasonList.change( function () {
-                                       currentValReasonList = $reasonList.val();
-                               } );
+                                       $reasonList.change( function () {
+                                               currentValReasonList = $reasonList.val();
+                                       } );
 
-                               $this.byteLimit( function ( input ) {
-                                       // Should be built the same as in HTMLSelectAndOtherField::loadDataFromRequest
-                                       var comment = currentValReasonList;
-                                       if ( comment === 'other' ) {
-                                               comment = input;
-                                       } else if ( input !== '' ) {
-                                               // Entry from drop down menu + additional comment
-                                               comment += colonSeparator + input;
-                                       }
-                                       return comment;
-                               } );
+                                       // Select the function for the length limit
+                                       maxlengthUnit = $this.data( 'mw-maxlength-unit' );
+                                       lengthLimiter = maxlengthUnit === 'codepoints' ? 'codePointLimit' : 'byteLimit';
+                                       $this[ lengthLimiter ]( function ( input ) {
+                                               // Should be built the same as in HTMLSelectAndOtherField::loadDataFromRequest
+                                               var comment = currentValReasonList;
+                                               if ( comment === 'other' ) {
+                                                       comment = input;
+                                               } else if ( input !== '' ) {
+                                                       // Entry from drop down menu + additional comment
+                                                       comment += colonSeparator + input;
+                                               }
+                                               return comment;
+                                       } );
+                               }
                        } );
        } );
 
diff --git a/resources/src/mediawiki/mediawiki.String.js b/resources/src/mediawiki/mediawiki.String.js
new file mode 100644 (file)
index 0000000..5d9bef0
--- /dev/null
@@ -0,0 +1,205 @@
+( function () {
+
+       /**
+        * @class mw.String
+        * @singleton
+        */
+
+       /**
+        * Calculate the byte length of a string (accounting for UTF-8).
+        *
+        * @author Jan Paul Posma, 2011
+        * @author Timo Tijhof, 2012
+        * @author David Chan, 2013
+        *
+        * @param {string} str
+        * @return {number}
+        */
+       function byteLength( str ) {
+               // This basically figures out how many bytes a UTF-16 string (which is what js sees)
+               // will take in UTF-8 by replacing a 2 byte character with 2 *'s, etc, and counting that.
+               // Note, surrogate (\uD800-\uDFFF) characters are counted as 2 bytes, since there's two of them
+               // and the actual character takes 4 bytes in UTF-8 (2*2=4). Might not work perfectly in
+               // edge cases such as illegal sequences, but that should never happen.
+
+               // https://en.wikipedia.org/wiki/UTF-8#Description
+               // The mapping from UTF-16 code units to UTF-8 bytes is as follows:
+               // > Range 0000-007F: codepoints that become 1 byte of UTF-8
+               // > Range 0080-07FF: codepoints that become 2 bytes of UTF-8
+               // > Range 0800-D7FF: codepoints that become 3 bytes of UTF-8
+               // > Range D800-DFFF: Surrogates (each pair becomes 4 bytes of UTF-8)
+               // > Range E000-FFFF: codepoints that become 3 bytes of UTF-8 (continued)
+
+               return str
+                       .replace( /[\u0080-\u07FF\uD800-\uDFFF]/g, '**' )
+                       .replace( /[\u0800-\uD7FF\uE000-\uFFFF]/g, '***' )
+                       .length;
+       }
+
+       /**
+        * Calculate the character length of a string (accounting for UTF-16 surrogates).
+        *
+        * @param {string} str
+        * @return {number}
+        */
+       function codePointLength( str ) {
+               return str
+                       // Low surrogate + high surrogate pairs represent one character (codepoint) each
+                       .replace( /[\uD800-\uDBFF][\uDC00-\uDFFF]/g, '*' )
+                       .length;
+       }
+
+       // Like String#charAt, but return the pair of UTF-16 surrogates for characters outside of BMP.
+       function codePointAt( string, offset, backwards ) {
+               // We don't need to check for offsets at the beginning or end of string,
+               // String#slice will simply return a shorter (or empty) substring.
+               var maybePair = backwards ?
+                       string.slice( offset - 1, offset + 1 ) :
+                       string.slice( offset, offset + 2 );
+               if ( /^[\uD800-\uDBFF][\uDC00-\uDFFF]$/.test( maybePair ) ) {
+                       return maybePair;
+               } else {
+                       return string.charAt( offset );
+               }
+       }
+
+       function trimLength( safeVal, newVal, length, lengthFn ) {
+               var startMatches, endMatches, matchesLen, inpParts, chopOff, oldChar, newChar,
+                       oldVal = safeVal;
+
+               // Run the hook if one was provided, but only on the length
+               // assessment. The value itself is not to be affected by the hook.
+               if ( lengthFn( newVal ) <= length ) {
+                       // Limit was not reached, just remember the new value
+                       // and let the user continue.
+                       return {
+                               newVal: newVal,
+                               trimmed: false
+                       };
+               }
+
+               // Current input is longer than the active limit.
+               // Figure out what was added and limit the addition.
+               startMatches = 0;
+               endMatches = 0;
+
+               // It is important that we keep the search within the range of
+               // the shortest string's length.
+               // Imagine a user adds text that matches the end of the old value
+               // (e.g. "foo" -> "foofoo"). startMatches would be 3, but without
+               // limiting both searches to the shortest length, endMatches would
+               // also be 3.
+               matchesLen = Math.min( newVal.length, oldVal.length );
+
+               // Count same characters from the left, first.
+               // (if "foo" -> "foofoo", assume addition was at the end).
+               while ( startMatches < matchesLen ) {
+                       oldChar = codePointAt( oldVal, startMatches, false );
+                       newChar = codePointAt( newVal, startMatches, false );
+                       if ( oldChar !== newChar ) {
+                               break;
+                       }
+                       startMatches += oldChar.length;
+               }
+
+               while ( endMatches < ( matchesLen - startMatches ) ) {
+                       oldChar = codePointAt( oldVal, oldVal.length - 1 - endMatches, true );
+                       newChar = codePointAt( newVal, newVal.length - 1 - endMatches, true );
+                       if ( oldChar !== newChar ) {
+                               break;
+                       }
+                       endMatches += oldChar.length;
+               }
+
+               inpParts = [
+                       // Same start
+                       newVal.slice( 0, startMatches ),
+                       // Inserted content
+                       newVal.slice( startMatches, newVal.length - endMatches ),
+                       // Same end
+                       newVal.slice( newVal.length - endMatches )
+               ];
+
+               // Chop off characters from the end of the "inserted content" string
+               // until the limit is statisfied.
+               // Make sure to stop when there is nothing to slice (T43450).
+               while ( lengthFn( inpParts.join( '' ) ) > length && inpParts[ 1 ].length > 0 ) {
+                       // Do not chop off halves of surrogate pairs
+                       chopOff = /[\uD800-\uDBFF][\uDC00-\uDFFF]$/.test( inpParts[ 1 ] ) ? 2 : 1;
+                       inpParts[ 1 ] = inpParts[ 1 ].slice( 0, -chopOff );
+               }
+
+               return {
+                       newVal: inpParts.join( '' ),
+                       // For pathological lengthFn() that always returns a length greater than the limit, we might have
+                       // ended up not trimming - check for this case to avoid infinite loops
+                       trimmed: newVal !== inpParts.join( '' )
+               };
+       }
+
+       /**
+        * Utility function to trim down a string, based on byteLimit
+        * and given a safe start position. It supports insertion anywhere
+        * in the string, so "foo" to "fobaro" if limit is 4 will result in
+        * "fobo", not "foba". Basically emulating the native maxlength by
+        * reconstructing where the insertion occurred.
+        *
+        * @param {string} safeVal Known value that was previously returned by this
+        * function, if none, pass empty string.
+        * @param {string} newVal New value that may have to be trimmed down.
+        * @param {number} byteLimit Number of bytes the value may be in size.
+        * @param {Function} [filterFn] Function to call on the string before assessing the length.
+        * @return {Object}
+        * @return {string} return.newVal
+        * @return {boolean} return.trimmed
+        */
+       function trimByteLength( safeVal, newVal, byteLimit, filterFn ) {
+               var lengthFn;
+               if ( filterFn ) {
+                       lengthFn = function ( val ) {
+                               return byteLength( filterFn( val ) );
+                       };
+               } else {
+                       lengthFn = byteLength;
+               }
+
+               return trimLength( safeVal, newVal, byteLimit, lengthFn );
+       }
+
+       /**
+        * Utility function to trim down a string, based on codePointLimit
+        * and given a safe start position. It supports insertion anywhere
+        * in the string, so "foo" to "fobaro" if limit is 4 will result in
+        * "fobo", not "foba". Basically emulating the native maxlength by
+        * reconstructing where the insertion occurred.
+        *
+        * @param {string} safeVal Known value that was previously returned by this
+        * function, if none, pass empty string.
+        * @param {string} newVal New value that may have to be trimmed down.
+        * @param {number} codePointLimit Number of characters the value may be in size.
+        * @param {Function} [filterFn] Function to call on the string before assessing the length.
+        * @return {Object}
+        * @return {string} return.newVal
+        * @return {boolean} return.trimmed
+        */
+       function trimCodePointLength( safeVal, newVal, codePointLimit, filterFn ) {
+               var lengthFn;
+               if ( filterFn ) {
+                       lengthFn = function ( val ) {
+                               return codePointLength( filterFn( val ) );
+                       };
+               } else {
+                       lengthFn = codePointLength;
+               }
+
+               return trimLength( safeVal, newVal, codePointLimit, lengthFn );
+       }
+
+       module.exports = {
+               byteLength: byteLength,
+               codePointLength: codePointLength,
+               trimByteLength: trimByteLength,
+               trimCodePointLength: trimCodePointLength
+       };
+
+}() );
index 851f06c..2b76187 100644 (file)
@@ -32,6 +32,8 @@
        /* Private members */
 
        var
+               mwString = require( 'mediawiki.String' ),
+
                namespaceIds = mw.config.get( 'wgNamespaceIds' ),
 
                /**
                        // Except for special pages, e.g. [[Special:Block/Long name]]
                        // Note: The PHP implementation also asserts that even in NS_SPECIAL, the title should
                        // be less than 512 bytes.
-                       if ( namespace !== NS_SPECIAL && $.byteLength( title ) > TITLE_MAX_BYTES ) {
+                       if ( namespace !== NS_SPECIAL && mwString.byteLength( title ) > TITLE_MAX_BYTES ) {
                                return false;
                        }
 
                 * @return {string}
                 */
                trimToByteLength = function ( s, length ) {
-                       var byteLength, chopOffChars, chopOffBytes;
-
-                       // bytelength is always greater or equal to the length in characters
-                       s = s.substr( 0, length );
-                       while ( ( byteLength = $.byteLength( s ) ) > length ) {
-                               // Calculate how many characters can be safely removed
-                               // First, we need to know how many bytes the string exceeds the threshold
-                               chopOffBytes = byteLength - length;
-                               // A character in UTF-8 is at most 4 bytes
-                               // One character must be removed in any case because the
-                               // string is too long
-                               chopOffChars = Math.max( 1, Math.floor( chopOffBytes / 4 ) );
-                               s = s.substr( 0, s.length - chopOffChars );
-                       }
-                       return s;
+                       return mwString.trimByteLength( '', s, length ).newVal;
                },
 
                /**
index f91ffbb..6478fd9 100644 (file)
@@ -10,6 +10,7 @@
 ( function ( mw, $ ) {
 
        var inspect,
+               byteLength = require( 'mediawiki.String' ).byteLength,
                hasOwn = Object.prototype.hasOwnProperty;
 
        function sortByProperty( array, prop, descending ) {
                        size = 0;
                        for ( i = 0; i < args.length; i++ ) {
                                if ( typeof args[ i ] === 'function' ) {
-                                       size += $.byteLength( getFunctionBody( args[ i ] ) );
+                                       size += byteLength( getFunctionBody( args[ i ] ) );
                                } else {
-                                       size += $.byteLength( JSON.stringify( args[ i ] ) );
+                                       size += byteLength( JSON.stringify( args[ i ] ) );
                                }
                        }
 
                                        $.extend( stats, mw.loader.store.stats );
                                        try {
                                                raw = localStorage.getItem( mw.loader.store.getStoreKey() );
-                                               stats.totalSizeInBytes = $.byteLength( raw );
-                                               stats.totalSize = humanSize( $.byteLength( raw ) );
+                                               stats.totalSizeInBytes = byteLength( raw );
+                                               stats.totalSize = humanSize( byteLength( raw ) );
                                        } catch ( e ) {}
                                }
                                return [ stats ];
index 104f699..a2e071e 100644 (file)
@@ -53,7 +53,7 @@
        }
 
        function defineFallbacks() {
-               // <https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Set>
+               // <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set>
                StringSet = window.Set || ( function () {
                        /**
                         * @private
                                 *         OO.compare( [ 1 ], [ 1 ] );
                                 *     } );
                                 *
+                                * Example of inline dependency obtained via `require()`:
+                                *
+                                *     mw.loader.using( [ 'mediawiki.util' ], function ( require ) {
+                                *         var util = require( 'mediawiki.util' );
+                                *     } );
+                                *
                                 * Since MediaWiki 1.23 this also returns a promise.
                                 *
                                 * Since MediaWiki 1.28 the promise is resolved with a `require` function.
                                /**
                                 * Get the exported value of a module.
                                 *
-                                * Modules may provide this via their local `module.exports`.
+                                * This static method is publicly exposed for debugging purposes
+                                * only and must not be used in production code. In production code,
+                                * please use the dynamically provided `require()` function instead.
                                 *
-                                * @protected
+                                * In case of lazy-loaded modules via mw.loader#using(), the returned
+                                * Promise provides the function, see #using() for examples.
+                                *
+                                * @private
                                 * @since 1.27
                                 * @param {string} moduleName Module name
                                 * @return {Mixed} Exported value
index fe0b029..cc313c7 100644 (file)
@@ -30,7 +30,7 @@ window.mwNow = ( function () {
  *
  * Browsers we support in our modern run-time (Grade A):
  * - Chrome 13+
- * - IE 10+
+ * - IE 11+
  * - Firefox 4+
  * - Safari 5+
  * - Opera 15+
@@ -86,7 +86,7 @@ window.isCompatible = function ( str ) {
                // support in the modern run-time.
                // Note: Please extend the regex instead of adding new ones
                !(
-                       ua.match( /webOS\/1\.[0-4]|SymbianOS|Series60|NetFront|Opera Mini|S40OviBrowser|MeeGo|Android.+Glass|^Mozilla\/5\.0 .+ Gecko\/$|googleweblight/ ) ||
+                       ua.match( /MSIE 10|webOS\/1\.[0-4]|SymbianOS|Series60|NetFront|Opera Mini|S40OviBrowser|MeeGo|Android.+Glass|^Mozilla\/5\.0 .+ Gecko\/$|googleweblight/ ) ||
                        ua.match( /PlayStation/i )
                )
        );
index cc769d7..262eb35 100644 (file)
@@ -2,7 +2,7 @@
 
 use Wikimedia\TestingAccessWrapper;
 
-abstract class MWHttpRequestTestCase extends PHPUnit_Framework_TestCase {
+abstract class MWHttpRequestTestCase extends PHPUnit\Framework\TestCase {
        protected static $httpEngine;
        protected $oldHttpEngine;
 
index 598c715..c69fa73 100644 (file)
@@ -9,7 +9,7 @@ use MediaWiki\Shell\Shell;
  * as long as firejail and sudo has similar config.
  * @group Shell
  */
-class FirejailCommandIntegrationTest extends PHPUnit_Framework_TestCase {
+class FirejailCommandIntegrationTest extends PHPUnit\Framework\TestCase {
 
        public function setUp() {
                parent::setUp();
index 4dd4bc6..d4b1f91 100644 (file)
@@ -889,7 +889,7 @@ class ParserTestRunner {
                if ( isset( $output ) && isset( $opts['showflags'] ) ) {
                        $actualFlags = array_keys( TestingAccessWrapper::newFromObject( $output )->mFlags );
                        sort( $actualFlags );
-                       $out .= "\nflags=" . join( ', ', $actualFlags );
+                       $out .= "\nflags=" . implode( ', ', $actualFlags );
                }
 
                ScopedCallback::consume( $teardownGuard );
@@ -1148,7 +1148,7 @@ class ParserTestRunner {
         * @return array
         */
        private function listTables() {
-               global $wgCommentTableSchemaMigrationStage;
+               global $wgCommentTableSchemaMigrationStage, $wgActorTableSchemaMigrationStage;
 
                $tables = [ 'user', 'user_properties', 'user_former_groups', 'page', 'page_restrictions',
                        'protected_titles', 'revision', 'ip_changes', 'text', 'pagelinks', 'imagelinks',
@@ -1166,6 +1166,12 @@ class ParserTestRunner {
                        $tables[] = 'image_comment_temp';
                }
 
+               if ( $wgActorTableSchemaMigrationStage >= MIGRATION_WRITE_BOTH ) {
+                       // The new tables for actors are in use
+                       $tables[] = 'actor';
+                       $tables[] = 'revision_actor_temp';
+               }
+
                if ( in_array( $this->db->getType(), [ 'mysql', 'sqlite', 'oracle' ] ) ) {
                        array_push( $tables, 'searchindex' );
                }
index 2f82ca7..1a2cfc9 100644 (file)
@@ -3,7 +3,7 @@
 class PhpunitTestRecorder extends TestRecorder {
        private $testCase;
 
-       public function setTestCase( PHPUnit_Framework_TestCase $testCase ) {
+       public function setTestCase( PHPUnit\Framework\TestCase $testCase ) {
                $this->testCase = $testCase;
        }
 
index dd606d8..0a162a2 100644 (file)
@@ -11,7 +11,7 @@ class MediaWikiPHPUnitTestListener
        protected function getTestName( PHPUnit_Framework_Test $test ) {
                $name = get_class( $test );
 
-               if ( $test instanceof PHPUnit_Framework_TestCase ) {
+               if ( $test instanceof PHPUnit\Framework\TestCase ) {
                        $name .= '::' . $test->getName( true );
                }
 
index 817f161..92c0714 100644 (file)
@@ -14,7 +14,7 @@ use Wikimedia\TestingAccessWrapper;
 /**
  * @since 1.18
  */
-abstract class MediaWikiTestCase extends PHPUnit_Framework_TestCase {
+abstract class MediaWikiTestCase extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
@@ -1435,8 +1435,9 @@ abstract class MediaWikiTestCase extends PHPUnit_Framework_TestCase {
         */
        private function resetDB( $db, $tablesUsed ) {
                if ( $db ) {
-                       $userTables = [ 'user', 'user_groups', 'user_properties' ];
-                       $pageTables = [ 'page', 'revision', 'ip_changes', 'revision_comment_temp', 'comment' ];
+                       $userTables = [ 'user', 'user_groups', 'user_properties', 'actor' ];
+                       $pageTables = [ 'page', 'revision', 'ip_changes', 'revision_comment_temp',
+                               'revision_actor_temp', 'comment' ];
                        $coreDBDataTables = array_merge( $userTables, $pageTables );
 
                        // If any of the user or page tables were marked as used, we should clear all of them.
diff --git a/tests/phpunit/data/media/jpeg-segment-loop1.jpg b/tests/phpunit/data/media/jpeg-segment-loop1.jpg
new file mode 100644 (file)
index 0000000..962f3fe
Binary files /dev/null and b/tests/phpunit/data/media/jpeg-segment-loop1.jpg differ
diff --git a/tests/phpunit/data/media/jpeg-segment-loop2.jpg b/tests/phpunit/data/media/jpeg-segment-loop2.jpg
new file mode 100644 (file)
index 0000000..e3a7505
Binary files /dev/null and b/tests/phpunit/data/media/jpeg-segment-loop2.jpg differ
diff --git a/tests/phpunit/includes/ActorMigrationTest.php b/tests/phpunit/includes/ActorMigrationTest.php
new file mode 100644 (file)
index 0000000..1b0c848
--- /dev/null
@@ -0,0 +1,695 @@
+<?php
+
+use MediaWiki\User\UserIdentity;
+use Wikimedia\TestingAccessWrapper;
+
+/**
+ * @group Database
+ * @covers ActorMigration
+ */
+class ActorMigrationTest extends MediaWikiLangTestCase {
+
+       protected $tablesUsed = [
+               'revision',
+               'revision_actor_temp',
+               'ipblocks',
+               'recentchanges',
+               'actor',
+       ];
+
+       /**
+        * Create an ActorMigration for a particular stage
+        * @param int $stage
+        * @return ActorMigration
+        */
+       protected function makeMigration( $stage ) {
+               return new ActorMigration( $stage );
+       }
+
+       /**
+        * @dataProvider provideGetJoin
+        * @param int $stage
+        * @param string $key
+        * @param array $expect
+        */
+       public function testGetJoin( $stage, $key, $expect ) {
+               $m = $this->makeMigration( $stage );
+               $result = $m->getJoin( $key );
+               $this->assertEquals( $expect, $result );
+       }
+
+       public static function provideGetJoin() {
+               return [
+                       'Simple table, old' => [
+                               MIGRATION_OLD, 'rc_user', [
+                                       'tables' => [],
+                                       'fields' => [
+                                               'rc_user' => 'rc_user',
+                                               'rc_user_text' => 'rc_user_text',
+                                               'rc_actor' => 'NULL',
+                                       ],
+                                       'joins' => [],
+                               ],
+                       ],
+                       'Simple table, write-both' => [
+                               MIGRATION_WRITE_BOTH, 'rc_user', [
+                                       'tables' => [ 'actor_rc_user' => 'actor' ],
+                                       'fields' => [
+                                               'rc_user' => 'COALESCE( actor_rc_user.actor_user, rc_user )',
+                                               'rc_user_text' => 'COALESCE( actor_rc_user.actor_name, rc_user_text )',
+                                               'rc_actor' => 'rc_actor',
+                                       ],
+                                       'joins' => [
+                                               'actor_rc_user' => [ 'LEFT JOIN', 'actor_rc_user.actor_id = rc_actor' ],
+                                       ],
+                               ],
+                       ],
+                       'Simple table, write-new' => [
+                               MIGRATION_WRITE_NEW, 'rc_user', [
+                                       'tables' => [ 'actor_rc_user' => 'actor' ],
+                                       'fields' => [
+                                               'rc_user' => 'COALESCE( actor_rc_user.actor_user, rc_user )',
+                                               'rc_user_text' => 'COALESCE( actor_rc_user.actor_name, rc_user_text )',
+                                               'rc_actor' => 'rc_actor',
+                                       ],
+                                       'joins' => [
+                                               'actor_rc_user' => [ 'LEFT JOIN', 'actor_rc_user.actor_id = rc_actor' ],
+                                       ],
+                               ],
+                       ],
+                       'Simple table, new' => [
+                               MIGRATION_NEW, 'rc_user', [
+                                       'tables' => [ 'actor_rc_user' => 'actor' ],
+                                       'fields' => [
+                                               'rc_user' => 'actor_rc_user.actor_user',
+                                               'rc_user_text' => 'actor_rc_user.actor_name',
+                                               'rc_actor' => 'rc_actor',
+                                       ],
+                                       'joins' => [
+                                               'actor_rc_user' => [ 'JOIN', 'actor_rc_user.actor_id = rc_actor' ],
+                                       ],
+                               ],
+                       ],
+
+                       'ipblocks, old' => [
+                               MIGRATION_OLD, 'ipb_by', [
+                                       'tables' => [],
+                                       'fields' => [
+                                               'ipb_by' => 'ipb_by',
+                                               'ipb_by_text' => 'ipb_by_text',
+                                               'ipb_by_actor' => 'NULL',
+                                       ],
+                                       'joins' => [],
+                               ],
+                       ],
+                       'ipblocks, write-both' => [
+                               MIGRATION_WRITE_BOTH, 'ipb_by', [
+                                       'tables' => [ 'actor_ipb_by' => 'actor' ],
+                                       'fields' => [
+                                               'ipb_by' => 'COALESCE( actor_ipb_by.actor_user, ipb_by )',
+                                               'ipb_by_text' => 'COALESCE( actor_ipb_by.actor_name, ipb_by_text )',
+                                               'ipb_by_actor' => 'ipb_by_actor',
+                                       ],
+                                       'joins' => [
+                                               'actor_ipb_by' => [ 'LEFT JOIN', 'actor_ipb_by.actor_id = ipb_by_actor' ],
+                                       ],
+                               ],
+                       ],
+                       'ipblocks, write-new' => [
+                               MIGRATION_WRITE_NEW, 'ipb_by', [
+                                       'tables' => [ 'actor_ipb_by' => 'actor' ],
+                                       'fields' => [
+                                               'ipb_by' => 'COALESCE( actor_ipb_by.actor_user, ipb_by )',
+                                               'ipb_by_text' => 'COALESCE( actor_ipb_by.actor_name, ipb_by_text )',
+                                               'ipb_by_actor' => 'ipb_by_actor',
+                                       ],
+                                       'joins' => [
+                                               'actor_ipb_by' => [ 'LEFT JOIN', 'actor_ipb_by.actor_id = ipb_by_actor' ],
+                                       ],
+                               ],
+                       ],
+                       'ipblocks, new' => [
+                               MIGRATION_NEW, 'ipb_by', [
+                                       'tables' => [ 'actor_ipb_by' => 'actor' ],
+                                       'fields' => [
+                                               'ipb_by' => 'actor_ipb_by.actor_user',
+                                               'ipb_by_text' => 'actor_ipb_by.actor_name',
+                                               'ipb_by_actor' => 'ipb_by_actor',
+                                       ],
+                                       'joins' => [
+                                               'actor_ipb_by' => [ 'JOIN', 'actor_ipb_by.actor_id = ipb_by_actor' ],
+                                       ],
+                               ],
+                       ],
+
+                       'Revision, old' => [
+                               MIGRATION_OLD, 'rev_user', [
+                                       'tables' => [],
+                                       'fields' => [
+                                               'rev_user' => 'rev_user',
+                                               'rev_user_text' => 'rev_user_text',
+                                               'rev_actor' => 'NULL',
+                                       ],
+                                       'joins' => [],
+                               ],
+                       ],
+                       'Revision, write-both' => [
+                               MIGRATION_WRITE_BOTH, 'rev_user', [
+                                       'tables' => [
+                                               'temp_rev_user' => 'revision_actor_temp',
+                                               'actor_rev_user' => 'actor',
+                                       ],
+                                       'fields' => [
+                                               'rev_user' => 'COALESCE( actor_rev_user.actor_user, rev_user )',
+                                               'rev_user_text' => 'COALESCE( actor_rev_user.actor_name, rev_user_text )',
+                                               'rev_actor' => 'temp_rev_user.revactor_actor',
+                                       ],
+                                       'joins' => [
+                                               'temp_rev_user' => [ 'LEFT JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
+                                               'actor_rev_user' => [ 'LEFT JOIN', 'actor_rev_user.actor_id = temp_rev_user.revactor_actor' ],
+                                       ],
+                               ],
+                       ],
+                       'Revision, write-new' => [
+                               MIGRATION_WRITE_NEW, 'rev_user', [
+                                       'tables' => [
+                                               'temp_rev_user' => 'revision_actor_temp',
+                                               'actor_rev_user' => 'actor',
+                                       ],
+                                       'fields' => [
+                                               'rev_user' => 'COALESCE( actor_rev_user.actor_user, rev_user )',
+                                               'rev_user_text' => 'COALESCE( actor_rev_user.actor_name, rev_user_text )',
+                                               'rev_actor' => 'temp_rev_user.revactor_actor',
+                                       ],
+                                       'joins' => [
+                                               'temp_rev_user' => [ 'LEFT JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
+                                               'actor_rev_user' => [ 'LEFT JOIN', 'actor_rev_user.actor_id = temp_rev_user.revactor_actor' ],
+                                       ],
+                               ],
+                       ],
+                       'Revision, new' => [
+                               MIGRATION_NEW, 'rev_user', [
+                                       'tables' => [
+                                               'temp_rev_user' => 'revision_actor_temp',
+                                               'actor_rev_user' => 'actor',
+                                       ],
+                                       'fields' => [
+                                               'rev_user' => 'actor_rev_user.actor_user',
+                                               'rev_user_text' => 'actor_rev_user.actor_name',
+                                               'rev_actor' => 'temp_rev_user.revactor_actor',
+                                       ],
+                                       'joins' => [
+                                               'temp_rev_user' => [ 'JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
+                                               'actor_rev_user' => [ 'JOIN', 'actor_rev_user.actor_id = temp_rev_user.revactor_actor' ],
+                                       ],
+                               ],
+                       ],
+               ];
+       }
+
+       /**
+        * @dataProvider provideGetWhere
+        * @param int $stage
+        * @param string $key
+        * @param UserIdentity[] $users
+        * @param bool $useId
+        * @param array $expect
+        */
+       public function testGetWhere( $stage, $key, $users, $useId, $expect ) {
+               $expect['conds'] = '(' . implode( ') OR (', $expect['orconds'] ) . ')';
+
+               if ( count( $users ) === 1 ) {
+                       $users = reset( $users );
+               }
+
+               $m = $this->makeMigration( $stage );
+               $result = $m->getWhere( $this->db, $key, $users, $useId );
+               $this->assertEquals( $expect, $result );
+       }
+
+       public function provideGetWhere() {
+               $makeUserIdentity = function ( $id, $name, $actor ) {
+                       $u = $this->getMock( UserIdentity::class );
+                       $u->method( 'getId' )->willReturn( $id );
+                       $u->method( 'getName' )->willReturn( $name );
+                       $u->method( 'getActorId' )->willReturn( $actor );
+                       return $u;
+               };
+
+               $genericUser = [ $makeUserIdentity( 1, 'User1', 11 ) ];
+               $complicatedUsers = [
+                       $makeUserIdentity( 1, 'User1', 11 ),
+                       $makeUserIdentity( 2, 'User2', 12 ),
+                       $makeUserIdentity( 3, 'User3', 0 ),
+                       $makeUserIdentity( 0, '192.168.12.34', 34 ),
+                       $makeUserIdentity( 0, '192.168.12.35', 0 ),
+               ];
+
+               return [
+                       'Simple table, old' => [
+                               MIGRATION_OLD, 'rc_user', $genericUser, true, [
+                                       'tables' => [],
+                                       'orconds' => [ 'userid' => "rc_user = '1'" ],
+                                       'joins' => [],
+                               ],
+                       ],
+                       'Simple table, write-both' => [
+                               MIGRATION_WRITE_BOTH, 'rc_user', $genericUser, true, [
+                                       'tables' => [],
+                                       'orconds' => [
+                                               'actor' => "rc_actor = '11'",
+                                               'userid' => "rc_actor = '0' AND rc_user = '1'"
+                                       ],
+                                       'joins' => [],
+                               ],
+                       ],
+                       'Simple table, write-new' => [
+                               MIGRATION_WRITE_NEW, 'rc_user', $genericUser, true, [
+                                       'tables' => [],
+                                       'orconds' => [
+                                               'actor' => "rc_actor = '11'",
+                                               'userid' => "rc_actor = '0' AND rc_user = '1'"
+                                       ],
+                                       'joins' => [],
+                               ],
+                       ],
+                       'Simple table, new' => [
+                               MIGRATION_NEW, 'rc_user', $genericUser, true, [
+                                       'tables' => [],
+                                       'orconds' => [ 'actor' => "rc_actor = '11'" ],
+                                       'joins' => [],
+                               ],
+                       ],
+
+                       'ipblocks, old' => [
+                               MIGRATION_OLD, 'ipb_by', $genericUser, true, [
+                                       'tables' => [],
+                                       'orconds' => [ 'userid' => "ipb_by = '1'" ],
+                                       'joins' => [],
+                               ],
+                       ],
+                       'ipblocks, write-both' => [
+                               MIGRATION_WRITE_BOTH, 'ipb_by', $genericUser, true, [
+                                       'tables' => [],
+                                       'orconds' => [
+                                               'actor' => "ipb_by_actor = '11'",
+                                               'userid' => "ipb_by_actor = '0' AND ipb_by = '1'"
+                                       ],
+                                       'joins' => [],
+                               ],
+                       ],
+                       'ipblocks, write-new' => [
+                               MIGRATION_WRITE_NEW, 'ipb_by', $genericUser, true, [
+                                       'tables' => [],
+                                       'orconds' => [
+                                               'actor' => "ipb_by_actor = '11'",
+                                               'userid' => "ipb_by_actor = '0' AND ipb_by = '1'"
+                                       ],
+                                       'joins' => [],
+                               ],
+                       ],
+                       'ipblocks, new' => [
+                               MIGRATION_NEW, 'ipb_by', $genericUser, true, [
+                                       'tables' => [],
+                                       'orconds' => [ 'actor' => "ipb_by_actor = '11'" ],
+                                       'joins' => [],
+                               ],
+                       ],
+
+                       'Revision, old' => [
+                               MIGRATION_OLD, 'rev_user', $genericUser, true, [
+                                       'tables' => [],
+                                       'orconds' => [ 'userid' => "rev_user = '1'" ],
+                                       'joins' => [],
+                               ],
+                       ],
+                       'Revision, write-both' => [
+                               MIGRATION_WRITE_BOTH, 'rev_user', $genericUser, true, [
+                                       'tables' => [
+                                               'temp_rev_user' => 'revision_actor_temp',
+                                       ],
+                                       'orconds' => [
+                                               'actor' =>
+                                                       "(temp_rev_user.revactor_actor IS NOT NULL) AND temp_rev_user.revactor_actor = '11'",
+                                               'userid' => "temp_rev_user.revactor_actor IS NULL AND rev_user = '1'"
+                                       ],
+                                       'joins' => [
+                                               'temp_rev_user' => [ 'LEFT JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
+                                       ],
+                               ],
+                       ],
+                       'Revision, write-new' => [
+                               MIGRATION_WRITE_NEW, 'rev_user', $genericUser, true, [
+                                       'tables' => [
+                                               'temp_rev_user' => 'revision_actor_temp',
+                                       ],
+                                       'orconds' => [
+                                               'actor' =>
+                                                       "(temp_rev_user.revactor_actor IS NOT NULL) AND temp_rev_user.revactor_actor = '11'",
+                                               'userid' => "temp_rev_user.revactor_actor IS NULL AND rev_user = '1'"
+                                       ],
+                                       'joins' => [
+                                               'temp_rev_user' => [ 'LEFT JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
+                                       ],
+                               ],
+                       ],
+                       'Revision, new' => [
+                               MIGRATION_NEW, 'rev_user', $genericUser, true, [
+                                       'tables' => [
+                                               'temp_rev_user' => 'revision_actor_temp',
+                                       ],
+                                       'orconds' => [ 'actor' => "temp_rev_user.revactor_actor = '11'" ],
+                                       'joins' => [
+                                               'temp_rev_user' => [ 'JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
+                                       ],
+                               ],
+                       ],
+
+                       'Multiple users, old' => [
+                               MIGRATION_OLD, 'rc_user', $complicatedUsers, true, [
+                                       'tables' => [],
+                                       'orconds' => [
+                                               'userid' => "rc_user IN ('1','2','3') ",
+                                               'username' => "rc_user_text IN ('192.168.12.34','192.168.12.35') "
+                                       ],
+                                       'joins' => [],
+                               ],
+                       ],
+                       'Multiple users, write-both' => [
+                               MIGRATION_WRITE_BOTH, 'rc_user', $complicatedUsers, true, [
+                                       'tables' => [],
+                                       'orconds' => [
+                                               'actor' => "rc_actor IN ('11','12','34') ",
+                                               'userid' => "rc_actor = '0' AND rc_user IN ('1','2','3') ",
+                                               'username' => "rc_actor = '0' AND rc_user_text IN ('192.168.12.34','192.168.12.35') "
+                                       ],
+                                       'joins' => [],
+                               ],
+                       ],
+                       'Multiple users, write-new' => [
+                               MIGRATION_WRITE_NEW, 'rc_user', $complicatedUsers, true, [
+                                       'tables' => [],
+                                       'orconds' => [
+                                               'actor' => "rc_actor IN ('11','12','34') ",
+                                               'userid' => "rc_actor = '0' AND rc_user IN ('1','2','3') ",
+                                               'username' => "rc_actor = '0' AND rc_user_text IN ('192.168.12.34','192.168.12.35') "
+                                       ],
+                                       'joins' => [],
+                               ],
+                       ],
+                       'Multiple users, new' => [
+                               MIGRATION_NEW, 'rc_user', $complicatedUsers, true, [
+                                       'tables' => [],
+                                       'orconds' => [ 'actor' => "rc_actor IN ('11','12','34') " ],
+                                       'joins' => [],
+                               ],
+                       ],
+
+                       'Multiple users, no use ID, old' => [
+                               MIGRATION_OLD, 'rc_user', $complicatedUsers, false, [
+                                       'tables' => [],
+                                       'orconds' => [
+                                               'username' => "rc_user_text IN ('User1','User2','User3','192.168.12.34','192.168.12.35') "
+                                       ],
+                                       'joins' => [],
+                               ],
+                       ],
+                       'Multiple users, write-both' => [
+                               MIGRATION_WRITE_BOTH, 'rc_user', $complicatedUsers, false, [
+                                       'tables' => [],
+                                       'orconds' => [
+                                               'actor' => "rc_actor IN ('11','12','34') ",
+                                               'username' => "rc_actor = '0' AND "
+                                               . "rc_user_text IN ('User1','User2','User3','192.168.12.34','192.168.12.35') "
+                                       ],
+                                       'joins' => [],
+                               ],
+                       ],
+                       'Multiple users, write-new' => [
+                               MIGRATION_WRITE_NEW, 'rc_user', $complicatedUsers, false, [
+                                       'tables' => [],
+                                       'orconds' => [
+                                               'actor' => "rc_actor IN ('11','12','34') ",
+                                               'username' => "rc_actor = '0' AND "
+                                               . "rc_user_text IN ('User1','User2','User3','192.168.12.34','192.168.12.35') "
+                                       ],
+                                       'joins' => [],
+                               ],
+                       ],
+                       'Multiple users, new' => [
+                               MIGRATION_NEW, 'rc_user', $complicatedUsers, false, [
+                                       'tables' => [],
+                                       'orconds' => [ 'actor' => "rc_actor IN ('11','12','34') " ],
+                                       'joins' => [],
+                               ],
+                       ],
+               ];
+       }
+
+       /**
+        * @dataProvider provideInsertRoundTrip
+        * @param string $table
+        * @param string $key
+        * @param string $pk
+        * @param array $extraFields
+        */
+       public function testInsertRoundTrip( $table, $key, $pk, $extraFields ) {
+               $u = $this->getTestUser()->getUser();
+               $user = $this->getMock( UserIdentity::class );
+               $user->method( 'getId' )->willReturn( $u->getId() );
+               $user->method( 'getName' )->willReturn( $u->getName() );
+               if ( $u->getActorId( $this->db ) ) {
+                       $user->method( 'getActorId' )->willReturn( $u->getActorId() );
+               } else {
+                       $this->db->insert(
+                               'actor',
+                               [ 'actor_user' => $u->getId(), 'actor_name' => $u->getName() ],
+                               __METHOD__
+                       );
+                       $user->method( 'getActorId' )->willReturn( $this->db->insertId() );
+               }
+
+               $stages = [
+                       MIGRATION_OLD => [ MIGRATION_OLD, MIGRATION_WRITE_NEW ],
+                       MIGRATION_WRITE_BOTH => [ MIGRATION_OLD, MIGRATION_NEW ],
+                       MIGRATION_WRITE_NEW => [ MIGRATION_WRITE_BOTH, MIGRATION_NEW ],
+                       MIGRATION_NEW => [ MIGRATION_WRITE_BOTH, MIGRATION_NEW ],
+               ];
+
+               $nameKey = $key . '_text';
+               $actorKey = $key === 'ipb_by' ? 'ipb_by_actor' : substr( $key, 0, -5 ) . '_actor';
+
+               foreach ( $stages as $writeStage => $readRange ) {
+                       if ( $key === 'ipb_by' ) {
+                               $extraFields['ipb_address'] = __CLASS__ . "#$writeStage";
+                       }
+
+                       $w = $this->makeMigration( $writeStage );
+                       $usesTemp = $key === 'rev_user';
+
+                       if ( $usesTemp ) {
+                               list( $fields, $callback ) = $w->getInsertValuesWithTempTable( $this->db, $key, $user );
+                       } else {
+                               $fields = $w->getInsertValues( $this->db, $key, $user );
+                       }
+
+                       if ( $writeStage <= MIGRATION_WRITE_BOTH ) {
+                               $this->assertSame( $user->getId(), $fields[$key], "old field, stage=$writeStage" );
+                               $this->assertSame( $user->getName(), $fields[$nameKey], "old field, stage=$writeStage" );
+                       } else {
+                               $this->assertArrayNotHasKey( $key, $fields, "old field, stage=$writeStage" );
+                               $this->assertArrayNotHasKey( $nameKey, $fields, "old field, stage=$writeStage" );
+                       }
+                       if ( $writeStage >= MIGRATION_WRITE_BOTH && !$usesTemp ) {
+                               $this->assertSame( $user->getActorId(), $fields[$actorKey], "new field, stage=$writeStage" );
+                       } else {
+                               $this->assertArrayNotHasKey( $actorKey, $fields, "new field, stage=$writeStage" );
+                       }
+
+                       $this->db->insert( $table, $extraFields + $fields, __METHOD__ );
+                       $id = $this->db->insertId();
+                       if ( $usesTemp ) {
+                               $callback( $id, $extraFields );
+                       }
+
+                       for ( $readStage = $readRange[0]; $readStage <= $readRange[1]; $readStage++ ) {
+                               $r = $this->makeMigration( $readStage );
+
+                               $queryInfo = $r->getJoin( $key );
+                               $row = $this->db->selectRow(
+                                       [ $table ] + $queryInfo['tables'],
+                                       $queryInfo['fields'],
+                                       [ $pk => $id ],
+                                       __METHOD__,
+                                       [],
+                                       $queryInfo['joins']
+                               );
+
+                               $this->assertSame( $user->getId(), (int)$row->$key, "w=$writeStage, r=$readStage, id" );
+                               $this->assertSame( $user->getName(), $row->$nameKey, "w=$writeStage, r=$readStage, name" );
+                               $this->assertSame(
+                                       $readStage === MIGRATION_OLD || $writeStage === MIGRATION_OLD ? 0 : $user->getActorId(),
+                                       (int)$row->$actorKey,
+                                       "w=$writeStage, r=$readStage, actor"
+                               );
+                       }
+               }
+       }
+
+       public static function provideInsertRoundTrip() {
+               $db = wfGetDB( DB_REPLICA ); // for timestamps
+
+               $ipbfields = [
+               ];
+               $revfields = [
+               ];
+
+               return [
+                       'recentchanges' => [ 'recentchanges', 'rc_user', 'rc_id', [
+                               'rc_timestamp' => $db->timestamp(),
+                               'rc_namespace' => 0,
+                               'rc_title' => 'Test',
+                               'rc_this_oldid' => 42,
+                               'rc_last_oldid' => 41,
+                               'rc_source' => 'test',
+                       ] ],
+                       'ipblocks' => [ 'ipblocks', 'ipb_by', 'ipb_id', [
+                               'ipb_range_start' => '',
+                               'ipb_range_end' => '',
+                               'ipb_timestamp' => $db->timestamp(),
+                               'ipb_expiry' => $db->getInfinity(),
+                       ] ],
+                       'revision' => [ 'revision', 'rev_user', 'rev_id', [
+                               'rev_page' => 42,
+                               'rev_text_id' => 42,
+                               'rev_len' => 0,
+                               'rev_timestamp' => $db->timestamp(),
+                       ] ],
+               ];
+       }
+
+       public static function provideStages() {
+               return [
+                       'MIGRATION_OLD' => [ MIGRATION_OLD ],
+                       'MIGRATION_WRITE_BOTH' => [ MIGRATION_WRITE_BOTH ],
+                       'MIGRATION_WRITE_NEW' => [ MIGRATION_WRITE_NEW ],
+                       'MIGRATION_NEW' => [ MIGRATION_NEW ],
+               ];
+       }
+
+       /**
+        * @dataProvider provideStages
+        * @param int $stage
+        * @expectedException InvalidArgumentException
+        * @expectedExceptionMessage Must use getInsertValuesWithTempTable() for rev_user
+        */
+       public function testInsertWrong( $stage ) {
+               $m = $this->makeMigration( $stage );
+               $m->getInsertValues( $this->db, 'rev_user', $this->getTestUser()->getUser() );
+       }
+
+       /**
+        * @dataProvider provideStages
+        * @param int $stage
+        * @expectedException InvalidArgumentException
+        * @expectedExceptionMessage Must use getInsertValues() for rc_user
+        */
+       public function testInsertWithTempTableWrong( $stage ) {
+               $m = $this->makeMigration( $stage );
+               $m->getInsertValuesWithTempTable( $this->db, 'rc_user', $this->getTestUser()->getUser() );
+       }
+
+       /**
+        * @dataProvider provideStages
+        * @param int $stage
+        */
+       public function testInsertWithTempTableDeprecated( $stage ) {
+               $wrap = TestingAccessWrapper::newFromClass( ActorMigration::class );
+               $wrap->formerTempTables += [ 'rc_user' => '1.30' ];
+
+               $this->hideDeprecated( 'ActorMigration::getInsertValuesWithTempTable for rc_user' );
+               $m = $this->makeMigration( $stage );
+               list( $fields, $callback )
+                       = $m->getInsertValuesWithTempTable( $this->db, 'rc_user', $this->getTestUser()->getUser() );
+               $this->assertTrue( is_callable( $callback ) );
+       }
+
+       /**
+        * @dataProvider provideStages
+        * @param int $stage
+        * @expectedException InvalidArgumentException
+        * @expectedExceptionMessage $extra[rev_timestamp] is not provided
+        */
+       public function testInsertWithTempTableCallbackMissingFields( $stage ) {
+               $m = $this->makeMigration( $stage );
+               list( $fields, $callback )
+                       = $m->getInsertValuesWithTempTable( $this->db, 'rev_user', $this->getTestUser()->getUser() );
+               $callback( 1, [] );
+       }
+
+       public function testInsertUserIdentity() {
+               $user = $this->getTestUser()->getUser();
+               $userIdentity = $this->getMock( UserIdentity::class );
+               $userIdentity->method( 'getId' )->willReturn( $user->getId() );
+               $userIdentity->method( 'getName' )->willReturn( $user->getName() );
+               $userIdentity->method( 'getActorId' )->willReturn( 0 );
+
+               list( $cFields, $cCallback ) = CommentStore::newKey( 'rev_comment' )
+                       ->insertWithTempTable( $this->db, '' );
+               $m = $this->makeMigration( MIGRATION_WRITE_BOTH );
+               list( $fields, $callback ) =
+                       $m->getInsertValuesWithTempTable( $this->db, 'rev_user', $userIdentity );
+               $extraFields = [
+                       'rev_page' => 42,
+                       'rev_text_id' => 42,
+                       'rev_len' => 0,
+                       'rev_timestamp' => $this->db->timestamp(),
+               ] + $cFields;
+               $this->db->insert( 'revision', $extraFields + $fields, __METHOD__ );
+               $id = $this->db->insertId();
+               $callback( $id, $extraFields );
+               $cCallback( $id );
+
+               $qi = Revision::getQueryInfo();
+               $row = $this->db->selectRow(
+                       $qi['tables'], $qi['fields'], [ 'rev_id' => $id ], __METHOD__, [], $qi['joins']
+               );
+               $this->assertSame( $user->getId(), (int)$row->rev_user );
+               $this->assertSame( $user->getName(), $row->rev_user_text );
+               $this->assertSame( $user->getActorId(), (int)$row->rev_actor );
+
+               $m = $this->makeMigration( MIGRATION_WRITE_BOTH );
+               $fields = $m->getInsertValues( $this->db, 'dummy_user', $userIdentity );
+               $this->assertSame( $user->getId(), $fields['dummy_user'] );
+               $this->assertSame( $user->getName(), $fields['dummy_user_text'] );
+               $this->assertSame( $user->getActorId(), $fields['dummy_actor'] );
+       }
+
+       public function testConstructor() {
+               $m = ActorMigration::newMigration();
+               $this->assertInstanceOf( ActorMigration::class, $m );
+               $this->assertSame( $m, ActorMigration::newMigration() );
+       }
+
+       /**
+        * @dataProvider provideIsAnon
+        * @param int $stage
+        * @param string $isAnon
+        * @param string $isNotAnon
+        */
+       public function testIsAnon( $stage, $isAnon, $isNotAnon ) {
+               $m = $this->makeMigration( $stage );
+               $this->assertSame( $isAnon, $m->isAnon( 'foo' ) );
+               $this->assertSame( $isNotAnon, $m->isNotAnon( 'foo' ) );
+       }
+
+       public static function provideIsAnon() {
+               return [
+                       'MIGRATION_OLD' => [ MIGRATION_OLD, 'foo = 0', 'foo != 0' ],
+                       'MIGRATION_WRITE_BOTH' => [ MIGRATION_WRITE_BOTH, 'foo = 0', 'foo != 0' ],
+                       'MIGRATION_WRITE_NEW' => [ MIGRATION_WRITE_NEW, 'foo = 0', 'foo != 0' ],
+                       'MIGRATION_NEW' => [ MIGRATION_NEW, 'foo IS NULL', 'foo IS NOT NULL' ],
+               ];
+       }
+
+}
index 1e46555..19780a6 100644 (file)
@@ -35,6 +35,7 @@ class BlockTest extends MediaWikiLangTestCase {
                $blockOptions = [
                        'address' => $user->getName(),
                        'user' => $user->getId(),
+                       'by' => $this->getTestSysop()->getUser()->getId(),
                        'reason' => 'Parce que',
                        'expiry' => time() + 100500,
                ];
@@ -393,7 +394,7 @@ class BlockTest extends MediaWikiLangTestCase {
                $block = new Block(
                        /* address */ $username,
                        /* user */ 0,
-                       /* by */ 0,
+                       /* by */ $this->getTestSysop()->getUser()->getId(),
                        /* reason */ $reason,
                        /* timestamp */ 0,
                        /* auto */ false,
index e332cdd..a510897 100644 (file)
@@ -542,7 +542,6 @@ class CommentStoreTest extends MediaWikiLangTestCase {
                $ipbfields = [
                        'ipb_range_start' => '',
                        'ipb_range_end' => '',
-                       'ipb_by' => 0,
                        'ipb_timestamp' => $db->timestamp(),
                        'ipb_expiry' => $db->getInfinity(),
                ];
@@ -550,8 +549,6 @@ class CommentStoreTest extends MediaWikiLangTestCase {
                        'rev_page' => 42,
                        'rev_text_id' => 42,
                        'rev_len' => 0,
-                       'rev_user' => 0,
-                       'rev_user_text' => '',
                        'rev_timestamp' => $db->timestamp(),
                ];
                $comStoreComment = new CommentStoreComment(
index f6ccbc7..9f8bb38 100644 (file)
@@ -2,7 +2,7 @@
 
 use MediaWiki\Session\SessionManager;
 
-class FauxRequestTest extends PHPUnit_Framework_TestCase {
+class FauxRequestTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
index 8adb55f..1011a37 100644 (file)
@@ -5,7 +5,7 @@
  * @covers ::wfArrayFilter
  * @covers ::wfArrayFilterByKey
  */
-class WfArrayFilterTest extends \PHPUnit_Framework_TestCase {
+class WfArrayFilterTest extends \PHPUnit\Framework\TestCase {
        public function testWfArrayFilter() {
                $arr = [ 'a' => 1, 'b' => 2, 'c' => 3 ];
                $filtered = wfArrayFilter( $arr, function ( $val, $key ) {
index 6aa9849..87a7dff 100644 (file)
@@ -10,7 +10,7 @@
  *
  * @author Jeroen De Dauw < jeroendedauw@gmail.com >
  */
-class MediaWikiVersionFetcherTest extends PHPUnit_Framework_TestCase {
+class MediaWikiVersionFetcherTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
index 6fbe053..7fdc3ed 100644 (file)
@@ -50,6 +50,10 @@ class PageArchiveTest extends MediaWikiTestCase {
        protected function setUp() {
                parent::setUp();
 
+               $this->setMwGlobals( 'wgCommentTableSchemaMigrationStage', MIGRATION_OLD );
+               $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', MIGRATION_OLD );
+               $this->overrideMwServices();
+
                // First create our dummy page
                $page = Title::newFromText( 'PageArchiveTest_thePage' );
                $page = new WikiPage( $page );
@@ -84,28 +88,44 @@ class PageArchiveTest extends MediaWikiTestCase {
        public function testUndeleteRevisions() {
                // First make sure old revisions are archived
                $dbr = wfGetDB( DB_REPLICA );
-               $res = $dbr->select( 'archive', '*', [ 'ar_rev_id' => $this->ipRevId ] );
+               $arQuery = Revision::getArchiveQueryInfo();
+               $res = $dbr->select(
+                       $arQuery['tables'],
+                       $arQuery['fields'],
+                       [ 'ar_rev_id' => $this->ipRevId ],
+                       __METHOD__,
+                       [],
+                       $arQuery['joins']
+               );
                $row = $res->fetchObject();
                $this->assertEquals( $this->ipEditor, $row->ar_user_text );
 
                // Should not be in revision
-               $res = $dbr->select( 'revision', '*', [ 'rev_id' => $this->ipRevId ] );
+               $res = $dbr->select( 'revision', '1', [ 'rev_id' => $this->ipRevId ] );
                $this->assertFalse( $res->fetchObject() );
 
                // Should not be in ip_changes
-               $res = $dbr->select( 'ip_changes', '*', [ 'ipc_rev_id' => $this->ipRevId ] );
+               $res = $dbr->select( 'ip_changes', '1', [ 'ipc_rev_id' => $this->ipRevId ] );
                $this->assertFalse( $res->fetchObject() );
 
                // Restore the page
                $this->archivedPage->undelete( [] );
 
                // Should be back in revision
-               $res = $dbr->select( 'revision', '*', [ 'rev_id' => $this->ipRevId ] );
+               $revQuery = Revision::getQueryInfo();
+               $res = $dbr->select(
+                       $revQuery['tables'],
+                       $revQuery['fields'],
+                       [ 'rev_id' => $this->ipRevId ],
+                       __METHOD__,
+                       [],
+                       $revQuery['joins']
+               );
                $row = $res->fetchObject();
                $this->assertEquals( $this->ipEditor, $row->rev_user_text );
 
                // Should be back in ip_changes
-               $res = $dbr->select( 'ip_changes', '*', [ 'ipc_rev_id' => $this->ipRevId ] );
+               $res = $dbr->select( 'ip_changes', [ 'ipc_hex' ], [ 'ipc_rev_id' => $this->ipRevId ] );
                $row = $res->fetchObject();
                $this->assertEquals( IP::toHex( $this->ipEditor ), $row->ipc_hex );
        }
@@ -134,6 +154,7 @@ class PageArchiveTest extends MediaWikiTestCase {
                                'ar_minor_edit' => '0',
                                'ar_user' => '0',
                                'ar_user_text' => '2600:387:ed7:947e:8c16:a1ad:dd34:1dd7',
+                               'ar_actor' => null,
                                'ar_len' => '11',
                                'ar_deleted' => '0',
                                'ar_rev_id' => '3',
@@ -159,6 +180,7 @@ class PageArchiveTest extends MediaWikiTestCase {
                                'ar_minor_edit' => '0',
                                'ar_user' => '0',
                                'ar_user_text' => '127.0.0.1',
+                               'ar_actor' => null,
                                'ar_len' => '7',
                                'ar_deleted' => '0',
                                'ar_rev_id' => '2',
index b05a742..61f1802 100644 (file)
@@ -108,7 +108,9 @@ abstract class RevisionDbTestBase extends MediaWikiTestCase {
                }
 
                if ( !isset( $props['user_text'] ) ) {
-                       $props['user_text'] = 'Tester';
+                       $user = $this->getTestUser()->getUser();
+                       $props['user_text'] = $user->getName();
+                       $props['user'] = $user->getId();
                }
 
                if ( !isset( $props['user'] ) ) {
@@ -243,7 +245,6 @@ abstract class RevisionDbTestBase extends MediaWikiTestCase {
                                'rev_id',
                                'rev_page',
                                'rev_text_id',
-                               'rev_user',
                                'rev_minor_edit',
                                'rev_deleted',
                                'rev_len',
@@ -257,7 +258,6 @@ abstract class RevisionDbTestBase extends MediaWikiTestCase {
                                strval( $textId ),
                                '0',
                                '0',
-                               '0',
                                '13',
                                strval( $parentId ),
                                's0ngbdoxagreuf2vjtuxzwdz64n29xm',
@@ -397,7 +397,8 @@ abstract class RevisionDbTestBase extends MediaWikiTestCase {
                        $services->getDBLoadBalancer(),
                        $services->getService( '_SqlBlobStore' ),
                        $services->getMainWANObjectCache(),
-                       $services->getCommentStore()
+                       $services->getCommentStore(),
+                       $services->getActorMigration()
                );
 
                $store->setContentHandlerUseDB( $this->getContentHandlerUseDB() );
@@ -745,15 +746,17 @@ abstract class RevisionDbTestBase extends MediaWikiTestCase {
                // test it ---------------------------------
                $since = $revisions[$sinceIdx]->getTimestamp();
 
+               $revQuery = Revision::getQueryInfo();
                $allRows = iterator_to_array( $dbw->select(
-                       'revision',
-                       [ 'rev_id', 'rev_timestamp', 'rev_user' ],
+                       $revQuery['tables'],
+                       [ 'rev_id', 'rev_timestamp', 'rev_user' => $revQuery['fields']['rev_user'] ],
                        [
                                'rev_page' => $page->getId(),
                                //'rev_timestamp > ' . $dbw->addQuotes( $dbw->timestamp( $since ) )
                        ],
                        __METHOD__,
-                       [ 'ORDER BY' => 'rev_timestamp ASC', 'LIMIT' => 50 ]
+                       [ 'ORDER BY' => 'rev_timestamp ASC', 'LIMIT' => 50 ],
+                       $revQuery['joins']
                ) );
 
                $wasLast = Revision::userWasLastToEdit( $dbw, $page->getId(), $userA->getId(), $since );
index 8eac064..8b644c5 100644 (file)
@@ -157,13 +157,6 @@ class RevisionTest extends MediaWikiTestCase {
                        new MWException( "Text already stored in external store (id someid), " .
                                "can't serialize content object" )
                ];
-               yield 'unknown user id and no user name' => [
-                       [
-                               'content' => new JavaScriptContent( 'hello world.' ),
-                               'user' => 9989,
-                       ],
-                       new MWException( 'user_text not given, and unknown user ID 9989' )
-               ];
                yield 'with bad content object (class)' => [
                        [ 'content' => new stdClass() ],
                        new MWException( 'content field must contain a Content object.' )
@@ -494,7 +487,8 @@ class RevisionTest extends MediaWikiTestCase {
                        $lb,
                        $this->getBlobStore(),
                        $cache,
-                       MediaWikiServices::getInstance()->getCommentStore()
+                       MediaWikiServices::getInstance()->getCommentStore(),
+                       MediaWikiServices::getInstance()->getActorMigration()
                );
                return $blobStore;
        }
@@ -617,6 +611,7 @@ class RevisionTest extends MediaWikiTestCase {
         */
        public function testLoadFromTitle() {
                $this->setMwGlobals( 'wgCommentTableSchemaMigrationStage', MIGRATION_OLD );
+               $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', MIGRATION_OLD );
                $this->overrideMwServices();
                $title = $this->getMockTitle();
 
@@ -875,6 +870,8 @@ class RevisionTest extends MediaWikiTestCase {
         */
        public function testUserJoinCond() {
                $this->hideDeprecated( 'Revision::userJoinCond' );
+               $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', MIGRATION_OLD );
+               $this->overrideMwServices();
                $this->assertEquals(
                        [ 'LEFT JOIN', [ 'rev_user != 0', 'user_id = rev_user' ] ],
                        Revision::userJoinCond()
@@ -892,7 +889,7 @@ class RevisionTest extends MediaWikiTestCase {
                );
        }
 
-       private function overrideCommentStore() {
+       private function overrideCommentStoreAndActorMigration() {
                $mockStore = $this->getMockBuilder( CommentStore::class )
                        ->disableOriginalConstructor()
                        ->getMock();
@@ -906,8 +903,26 @@ class RevisionTest extends MediaWikiTestCase {
                                'fields' => [ 'commentstore' => 'field' ],
                                'joins' => [ 'commentstore' => 'join' ],
                        ] );
-
                $this->setService( 'CommentStore', $mockStore );
+
+               $mockStore = $this->getMockBuilder( ActorMigration::class )
+                       ->disableOriginalConstructor()
+                       ->getMock();
+               $mockStore->expects( $this->any() )
+                       ->method( 'getJoin' )
+                       ->willReturnCallback( function ( $key ) {
+                               $p = strtok( $key, '_' );
+                               return [
+                                       'tables' => [ 'actormigration' => 'table' ],
+                                       'fields' => [
+                                               $p . '_user' => 'actormigration_user',
+                                               $p . '_user_text' => 'actormigration_user_text',
+                                               $p . '_actor' => 'actormigration_actor',
+                                       ],
+                                       'joins' => [ 'actormigration' => 'join' ],
+                               ];
+                       } );
+               $this->setService( 'ActorMigration', $mockStore );
        }
 
        public function provideSelectFields() {
@@ -920,6 +935,7 @@ class RevisionTest extends MediaWikiTestCase {
                                'rev_timestamp',
                                'rev_user_text',
                                'rev_user',
+                               'rev_actor' => 'NULL',
                                'rev_minor_edit',
                                'rev_deleted',
                                'rev_len',
@@ -939,6 +955,7 @@ class RevisionTest extends MediaWikiTestCase {
                                'rev_timestamp',
                                'rev_user_text',
                                'rev_user',
+                               'rev_actor' => 'NULL',
                                'rev_minor_edit',
                                'rev_deleted',
                                'rev_len',
@@ -956,7 +973,8 @@ class RevisionTest extends MediaWikiTestCase {
        public function testSelectFields( $contentHandlerUseDB, $expected ) {
                $this->hideDeprecated( 'Revision::selectFields' );
                $this->setMwGlobals( 'wgContentHandlerUseDB', $contentHandlerUseDB );
-               $this->overrideCommentStore();
+               $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', MIGRATION_OLD );
+               $this->overrideCommentStoreAndActorMigration();
                $this->assertEquals( $expected, Revision::selectFields() );
        }
 
@@ -972,6 +990,7 @@ class RevisionTest extends MediaWikiTestCase {
                                'ar_timestamp',
                                'ar_user_text',
                                'ar_user',
+                               'ar_actor' => 'NULL',
                                'ar_minor_edit',
                                'ar_deleted',
                                'ar_len',
@@ -993,6 +1012,7 @@ class RevisionTest extends MediaWikiTestCase {
                                'ar_timestamp',
                                'ar_user_text',
                                'ar_user',
+                               'ar_actor' => 'NULL',
                                'ar_minor_edit',
                                'ar_deleted',
                                'ar_len',
@@ -1010,7 +1030,8 @@ class RevisionTest extends MediaWikiTestCase {
        public function testSelectArchiveFields( $contentHandlerUseDB, $expected ) {
                $this->hideDeprecated( 'Revision::selectArchiveFields' );
                $this->setMwGlobals( 'wgContentHandlerUseDB', $contentHandlerUseDB );
-               $this->overrideCommentStore();
+               $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', MIGRATION_OLD );
+               $this->overrideCommentStoreAndActorMigration();
                $this->assertEquals( $expected, Revision::selectArchiveFields() );
        }
 
@@ -1068,6 +1089,7 @@ class RevisionTest extends MediaWikiTestCase {
                                'tables' => [
                                        'archive',
                                        'commentstore' => 'table',
+                                       'actormigration' => 'table',
                                ],
                                'fields' => [
                                        'ar_id',
@@ -1078,16 +1100,17 @@ class RevisionTest extends MediaWikiTestCase {
                                        'ar_text',
                                        'ar_text_id',
                                        'ar_timestamp',
-                                       'ar_user_text',
-                                       'ar_user',
                                        'ar_minor_edit',
                                        'ar_deleted',
                                        'ar_len',
                                        'ar_parent_id',
                                        'ar_sha1',
-                                       'commentstore' => 'field'
+                                       'commentstore' => 'field',
+                                       'ar_user' => 'actormigration_user',
+                                       'ar_user_text' => 'actormigration_user_text',
+                                       'ar_actor' => 'actormigration_actor',
                                ],
-                               'joins' => [ 'commentstore' => 'join' ],
+                               'joins' => [ 'commentstore' => 'join', 'actormigration' => 'join' ],
                        ]
                ];
                yield 'wgContentHandlerUseDB true' => [
@@ -1098,6 +1121,7 @@ class RevisionTest extends MediaWikiTestCase {
                                'tables' => [
                                        'archive',
                                        'commentstore' => 'table',
+                                       'actormigration' => 'table',
                                ],
                                'fields' => [
                                        'ar_id',
@@ -1108,18 +1132,19 @@ class RevisionTest extends MediaWikiTestCase {
                                        'ar_text',
                                        'ar_text_id',
                                        'ar_timestamp',
-                                       'ar_user_text',
-                                       'ar_user',
                                        'ar_minor_edit',
                                        'ar_deleted',
                                        'ar_len',
                                        'ar_parent_id',
                                        'ar_sha1',
                                        'commentstore' => 'field',
+                                       'ar_user' => 'actormigration_user',
+                                       'ar_user_text' => 'actormigration_user_text',
+                                       'ar_actor' => 'actormigration_actor',
                                        'ar_content_format',
                                        'ar_content_model',
                                ],
-                               'joins' => [ 'commentstore' => 'join' ],
+                               'joins' => [ 'commentstore' => 'join', 'actormigration' => 'join' ],
                        ]
                ];
        }
@@ -1130,7 +1155,7 @@ class RevisionTest extends MediaWikiTestCase {
         */
        public function testGetArchiveQueryInfo( $globals, $expected ) {
                $this->setMwGlobals( $globals );
-               $this->overrideCommentStore();
+               $this->overrideCommentStoreAndActorMigration();
 
                $revisionStore = $this->getRevisionStore();
                $revisionStore->setContentHandlerUseDB( $globals['wgContentHandlerUseDB'] );
@@ -1148,22 +1173,23 @@ class RevisionTest extends MediaWikiTestCase {
                        ],
                        [],
                        [
-                               'tables' => [ 'revision', 'commentstore' => 'table' ],
+                               'tables' => [ 'revision', 'commentstore' => 'table', 'actormigration' => 'table' ],
                                'fields' => [
                                        'rev_id',
                                        'rev_page',
                                        'rev_text_id',
                                        'rev_timestamp',
-                                       'rev_user_text',
-                                       'rev_user',
                                        'rev_minor_edit',
                                        'rev_deleted',
                                        'rev_len',
                                        'rev_parent_id',
                                        'rev_sha1',
                                        'commentstore' => 'field',
+                                       'rev_user' => 'actormigration_user',
+                                       'rev_user_text' => 'actormigration_user_text',
+                                       'rev_actor' => 'actormigration_actor',
                                ],
-                               'joins' => [ 'commentstore' => 'join' ],
+                               'joins' => [ 'commentstore' => 'join', 'actormigration' => 'join' ],
                        ],
                ];
                yield 'wgContentHandlerUseDB false, opts page' => [
@@ -1172,20 +1198,21 @@ class RevisionTest extends MediaWikiTestCase {
                        ],
                        [ 'page' ],
                        [
-                               'tables' => [ 'revision', 'commentstore' => 'table', 'page' ],
+                               'tables' => [ 'revision', 'commentstore' => 'table', 'actormigration' => 'table', 'page' ],
                                'fields' => [
                                        'rev_id',
                                        'rev_page',
                                        'rev_text_id',
                                        'rev_timestamp',
-                                       'rev_user_text',
-                                       'rev_user',
                                        'rev_minor_edit',
                                        'rev_deleted',
                                        'rev_len',
                                        'rev_parent_id',
                                        'rev_sha1',
                                        'commentstore' => 'field',
+                                       'rev_user' => 'actormigration_user',
+                                       'rev_user_text' => 'actormigration_user_text',
+                                       'rev_actor' => 'actormigration_actor',
                                        'page_namespace',
                                        'page_title',
                                        'page_id',
@@ -1199,6 +1226,7 @@ class RevisionTest extends MediaWikiTestCase {
                                                [ 'page_id = rev_page' ],
                                        ],
                                        'commentstore' => 'join',
+                                       'actormigration' => 'join',
                                ],
                        ],
                ];
@@ -1208,31 +1236,33 @@ class RevisionTest extends MediaWikiTestCase {
                        ],
                        [ 'user' ],
                        [
-                               'tables' => [ 'revision', 'commentstore' => 'table', 'user' ],
+                               'tables' => [ 'revision', 'commentstore' => 'table', 'actormigration' => 'table', 'user' ],
                                'fields' => [
                                        'rev_id',
                                        'rev_page',
                                        'rev_text_id',
                                        'rev_timestamp',
-                                       'rev_user_text',
-                                       'rev_user',
                                        'rev_minor_edit',
                                        'rev_deleted',
                                        'rev_len',
                                        'rev_parent_id',
                                        'rev_sha1',
                                        'commentstore' => 'field',
+                                       'rev_user' => 'actormigration_user',
+                                       'rev_user_text' => 'actormigration_user_text',
+                                       'rev_actor' => 'actormigration_actor',
                                        'user_name',
                                ],
                                'joins' => [
                                        'user' => [
                                                'LEFT JOIN',
                                                [
-                                                       'rev_user != 0',
-                                                       'user_id = rev_user',
+                                                       'actormigration_user != 0',
+                                                       'user_id = actormigration_user',
                                                ],
                                        ],
                                        'commentstore' => 'join',
+                                       'actormigration' => 'join',
                                ],
                        ],
                ];
@@ -1242,20 +1272,21 @@ class RevisionTest extends MediaWikiTestCase {
                        ],
                        [ 'text' ],
                        [
-                               'tables' => [ 'revision', 'commentstore' => 'table', 'text' ],
+                               'tables' => [ 'revision', 'commentstore' => 'table', 'actormigration' => 'table', 'text' ],
                                'fields' => [
                                        'rev_id',
                                        'rev_page',
                                        'rev_text_id',
                                        'rev_timestamp',
-                                       'rev_user_text',
-                                       'rev_user',
                                        'rev_minor_edit',
                                        'rev_deleted',
                                        'rev_len',
                                        'rev_parent_id',
                                        'rev_sha1',
                                        'commentstore' => 'field',
+                                       'rev_user' => 'actormigration_user',
+                                       'rev_user_text' => 'actormigration_user_text',
+                                       'rev_actor' => 'actormigration_actor',
                                        'old_text',
                                        'old_flags',
                                ],
@@ -1265,6 +1296,7 @@ class RevisionTest extends MediaWikiTestCase {
                                                [ 'rev_text_id=old_id' ],
                                        ],
                                        'commentstore' => 'join',
+                                       'actormigration' => 'join',
                                ],
                        ],
                ];
@@ -1274,20 +1306,23 @@ class RevisionTest extends MediaWikiTestCase {
                        ],
                        [ 'text', 'page', 'user' ],
                        [
-                               'tables' => [ 'revision', 'commentstore' => 'table', 'page', 'user', 'text' ],
+                               'tables' => [
+                                       'revision', 'commentstore' => 'table', 'actormigration' => 'table', 'page', 'user', 'text'
+                               ],
                                'fields' => [
                                        'rev_id',
                                        'rev_page',
                                        'rev_text_id',
                                        'rev_timestamp',
-                                       'rev_user_text',
-                                       'rev_user',
                                        'rev_minor_edit',
                                        'rev_deleted',
                                        'rev_len',
                                        'rev_parent_id',
                                        'rev_sha1',
                                        'commentstore' => 'field',
+                                       'rev_user' => 'actormigration_user',
+                                       'rev_user_text' => 'actormigration_user_text',
+                                       'rev_actor' => 'actormigration_actor',
                                        'page_namespace',
                                        'page_title',
                                        'page_id',
@@ -1306,8 +1341,8 @@ class RevisionTest extends MediaWikiTestCase {
                                        'user' => [
                                                'LEFT JOIN',
                                                [
-                                                       'rev_user != 0',
-                                                       'user_id = rev_user',
+                                                       'actormigration_user != 0',
+                                                       'user_id = actormigration_user',
                                                ],
                                        ],
                                        'text' => [
@@ -1315,6 +1350,7 @@ class RevisionTest extends MediaWikiTestCase {
                                                [ 'rev_text_id=old_id' ],
                                        ],
                                        'commentstore' => 'join',
+                                       'actormigration' => 'join',
                                ],
                        ],
                ];
@@ -1324,24 +1360,25 @@ class RevisionTest extends MediaWikiTestCase {
                        ],
                        [],
                        [
-                               'tables' => [ 'revision', 'commentstore' => 'table' ],
+                               'tables' => [ 'revision', 'commentstore' => 'table', 'actormigration' => 'table' ],
                                'fields' => [
                                        'rev_id',
                                        'rev_page',
                                        'rev_text_id',
                                        'rev_timestamp',
-                                       'rev_user_text',
-                                       'rev_user',
                                        'rev_minor_edit',
                                        'rev_deleted',
                                        'rev_len',
                                        'rev_parent_id',
                                        'rev_sha1',
                                        'commentstore' => 'field',
+                                       'rev_user' => 'actormigration_user',
+                                       'rev_user_text' => 'actormigration_user_text',
+                                       'rev_actor' => 'actormigration_actor',
                                        'rev_content_format',
                                        'rev_content_model',
                                ],
-                               'joins' => [ 'commentstore' => 'join' ],
+                               'joins' => [ 'commentstore' => 'join', 'actormigration' => 'join' ],
                        ],
                ];
        }
@@ -1352,7 +1389,7 @@ class RevisionTest extends MediaWikiTestCase {
         */
        public function testGetQueryInfo( $globals, $options, $expected ) {
                $this->setMwGlobals( $globals );
-               $this->overrideCommentStore();
+               $this->overrideCommentStoreAndActorMigration();
 
                $revisionStore = $this->getRevisionStore();
                $revisionStore->setContentHandlerUseDB( $globals['wgContentHandlerUseDB'] );
index c7e15ea..c4e4308 100644 (file)
@@ -5,7 +5,7 @@
  * @todo all test methods in this class should be refactored and...
  *    use a single test method and a single data provider...
  */
-class SanitizerValidateEmailTest extends PHPUnit_Framework_TestCase {
+class SanitizerValidateEmailTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
index d31ca5c..e81f0af 100644 (file)
@@ -134,6 +134,7 @@ class RevisionStoreDbTest extends MediaWikiTestCase {
                        $blobStore,
                        new WANObjectCache( [ 'cache' => new HashBagOStuff() ] ),
                        MediaWikiServices::getInstance()->getCommentStore(),
+                       MediaWikiServices::getInstance()->getActorMigration(),
                        $wikiId
                );
 
@@ -621,12 +622,15 @@ class RevisionStoreDbTest extends MediaWikiTestCase {
         * @covers \MediaWiki\Storage\RevisionStore::newRevisionFromRow_1_29
         */
        public function testNewRevisionFromRow_anonEdit() {
+               $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', MIGRATION_WRITE_BOTH );
+               $this->overrideMwServices();
+
                $page = WikiPage::factory( Title::newFromText( 'UTPage' ) );
                $text = __METHOD__ . 'a-ä';
                /** @var Revision $rev */
                $rev = $page->doEditContent(
                        new WikitextContent( $text ),
-                       __METHOD__. 'a'
+                       __METHOD__ . 'a'
                )->value['revision'];
 
                $store = MediaWikiServices::getInstance()->getRevisionStore();
@@ -669,6 +673,9 @@ class RevisionStoreDbTest extends MediaWikiTestCase {
         * @covers \MediaWiki\Storage\RevisionStore::newRevisionFromRow_1_29
         */
        public function testNewRevisionFromRow_userEdit() {
+               $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', MIGRATION_WRITE_BOTH );
+               $this->overrideMwServices();
+
                $page = WikiPage::factory( Title::newFromText( 'UTPage' ) );
                $text = __METHOD__ . 'b-ä';
                /** @var Revision $rev */
@@ -1048,7 +1055,7 @@ class RevisionStoreDbTest extends MediaWikiTestCase {
                $page = WikiPage::factory( Title::newFromText( 'UTPage' ) );
                /** @var Revision $rev */
                $rev = $page->doEditContent(
-                       new WikitextContent( __METHOD__. 'b' ),
+                       new WikitextContent( __METHOD__ . 'b' ),
                        __METHOD__ . 'b',
                        0,
                        false,
index aa59a5b..43784b6 100644 (file)
@@ -30,7 +30,7 @@ class RevisionStoreRecordTest extends MediaWikiTestCase {
                $title = Title::newFromText( 'Dummy' );
                $title->resetArticleID( 17 );
 
-               $user = new UserIdentityValue( 11, 'Tester' );
+               $user = new UserIdentityValue( 11, 'Tester', 0 );
                $comment = CommentStoreComment::newUnsavedComment( 'Hello World' );
 
                $main = SlotRecord::newUnsaved( 'main', new TextContent( 'Lorem Ipsum' ) );
@@ -58,7 +58,7 @@ class RevisionStoreRecordTest extends MediaWikiTestCase {
                $title = Title::newFromText( 'Dummy' );
                $title->resetArticleID( 17 );
 
-               $user = new UserIdentityValue( 11, 'Tester' );
+               $user = new UserIdentityValue( 11, 'Tester', 0 );
                $comment = CommentStoreComment::newUnsavedComment( 'Hello World' );
 
                $main = SlotRecord::newUnsaved( 'main', new TextContent( 'Lorem Ipsum' ) );
@@ -213,7 +213,7 @@ class RevisionStoreRecordTest extends MediaWikiTestCase {
                $title = Title::newFromText( 'Dummy' );
                $title->resetArticleID( 17 );
 
-               $user = new UserIdentityValue( 11, 'Tester' );
+               $user = new UserIdentityValue( 11, 'Tester', 0 );
 
                $comment = CommentStoreComment::newUnsavedComment( 'Hello World' );
 
@@ -688,7 +688,7 @@ class RevisionStoreRecordTest extends MediaWikiTestCase {
 
                        return new RevisionStoreRecord(
                                $title,
-                               new UserIdentityValue( 11, __METHOD__ ),
+                               new UserIdentityValue( 11, __METHOD__, 0 ),
                                CommentStoreComment::newUnsavedComment( __METHOD__ ),
                                (object)[
                                        'rev_id' => strval( $revId ),
index 8e8de6e..8498947 100644 (file)
@@ -32,7 +32,8 @@ class RevisionStoreTest extends MediaWikiTestCase {
                        $loadBalancer ? $loadBalancer : $this->getMockLoadBalancer(),
                        $blobStore ? $blobStore : $this->getMockSqlBlobStore(),
                        $WANObjectCache ? $WANObjectCache : $this->getHashWANObjectCache(),
-                       MediaWikiServices::getInstance()->getCommentStore()
+                       MediaWikiServices::getInstance()->getCommentStore(),
+                       MediaWikiServices::getInstance()->getActorMigration()
                );
        }
 
@@ -83,8 +84,6 @@ class RevisionStoreTest extends MediaWikiTestCase {
                        'rev_page',
                        'rev_text_id',
                        'rev_timestamp',
-                       'rev_user_text',
-                       'rev_user',
                        'rev_minor_edit',
                        'rev_deleted',
                        'rev_len',
@@ -101,6 +100,14 @@ class RevisionStoreTest extends MediaWikiTestCase {
                ];
        }
 
+       private function getActorQueryFields() {
+               return [
+                       'rev_user' => 'rev_user',
+                       'rev_user_text' => 'rev_user_text',
+                       'rev_actor' => 'NULL',
+               ];
+       }
+
        private function getContentHandlerQueryFields() {
                return [
                        'rev_content_format',
@@ -117,6 +124,7 @@ class RevisionStoreTest extends MediaWikiTestCase {
                                'fields' => array_merge(
                                        $this->getDefaultQueryFields(),
                                        $this->getCommentQueryFields(),
+                                       $this->getActorQueryFields(),
                                        $this->getContentHandlerQueryFields()
                                ),
                                'joins' => [],
@@ -129,7 +137,8 @@ class RevisionStoreTest extends MediaWikiTestCase {
                                'tables' => [ 'revision' ],
                                'fields' => array_merge(
                                        $this->getDefaultQueryFields(),
-                                       $this->getCommentQueryFields()
+                                       $this->getCommentQueryFields(),
+                                       $this->getActorQueryFields()
                                ),
                                'joins' => [],
                        ]
@@ -142,6 +151,7 @@ class RevisionStoreTest extends MediaWikiTestCase {
                                'fields' => array_merge(
                                        $this->getDefaultQueryFields(),
                                        $this->getCommentQueryFields(),
+                                       $this->getActorQueryFields(),
                                        [
                                                'page_namespace',
                                                'page_title',
@@ -164,6 +174,7 @@ class RevisionStoreTest extends MediaWikiTestCase {
                                'fields' => array_merge(
                                        $this->getDefaultQueryFields(),
                                        $this->getCommentQueryFields(),
+                                       $this->getActorQueryFields(),
                                        [
                                                'user_name',
                                        ]
@@ -181,6 +192,7 @@ class RevisionStoreTest extends MediaWikiTestCase {
                                'fields' => array_merge(
                                        $this->getDefaultQueryFields(),
                                        $this->getCommentQueryFields(),
+                                       $this->getActorQueryFields(),
                                        [
                                                'old_text',
                                                'old_flags',
@@ -199,6 +211,7 @@ class RevisionStoreTest extends MediaWikiTestCase {
                                'fields' => array_merge(
                                        $this->getDefaultQueryFields(),
                                        $this->getCommentQueryFields(),
+                                       $this->getActorQueryFields(),
                                        $this->getContentHandlerQueryFields(),
                                        [
                                                'page_namespace',
@@ -227,6 +240,7 @@ class RevisionStoreTest extends MediaWikiTestCase {
         */
        public function testGetQueryInfo( $contentHandlerUseDb, $options, $expected ) {
                $this->setMwGlobals( 'wgCommentTableSchemaMigrationStage', MIGRATION_OLD );
+               $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', MIGRATION_OLD );
                $this->overrideMwServices();
                $store = $this->getRevisionStore();
                $store->setContentHandlerUseDB( $contentHandlerUseDb );
@@ -243,8 +257,6 @@ class RevisionStoreTest extends MediaWikiTestCase {
                        'ar_text',
                        'ar_text_id',
                        'ar_timestamp',
-                       'ar_user_text',
-                       'ar_user',
                        'ar_minor_edit',
                        'ar_deleted',
                        'ar_len',
@@ -258,6 +270,7 @@ class RevisionStoreTest extends MediaWikiTestCase {
         */
        public function testGetArchiveQueryInfo_contentHandlerDb() {
                $this->setMwGlobals( 'wgCommentTableSchemaMigrationStage', MIGRATION_OLD );
+               $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', MIGRATION_OLD );
                $this->overrideMwServices();
                $store = $this->getRevisionStore();
                $store->setContentHandlerUseDB( true );
@@ -272,6 +285,9 @@ class RevisionStoreTest extends MediaWikiTestCase {
                                                'ar_comment_text' => 'ar_comment',
                                                'ar_comment_data' => 'NULL',
                                                'ar_comment_cid' => 'NULL',
+                                               'ar_user_text' => 'ar_user_text',
+                                               'ar_user' => 'ar_user',
+                                               'ar_actor' => 'NULL',
                                                'ar_content_format',
                                                'ar_content_model',
                                        ]
@@ -287,6 +303,7 @@ class RevisionStoreTest extends MediaWikiTestCase {
         */
        public function testGetArchiveQueryInfo_noContentHandlerDb() {
                $this->setMwGlobals( 'wgCommentTableSchemaMigrationStage', MIGRATION_OLD );
+               $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', MIGRATION_OLD );
                $this->overrideMwServices();
                $store = $this->getRevisionStore();
                $store->setContentHandlerUseDB( false );
@@ -301,6 +318,9 @@ class RevisionStoreTest extends MediaWikiTestCase {
                                                'ar_comment_text' => 'ar_comment',
                                                'ar_comment_data' => 'NULL',
                                                'ar_comment_cid' => 'NULL',
+                                               'ar_user_text' => 'ar_user_text',
+                                               'ar_user' => 'ar_user',
+                                               'ar_actor' => 'NULL',
                                        ]
                                ),
                                'joins' => [],
index 63956c1..af49ecf 100644 (file)
@@ -4,7 +4,7 @@
  * @author Addshore
  * @covers TitleArrayFromResult
  */
-class TitleArrayFromResultTest extends PHPUnit_Framework_TestCase {
+class TitleArrayFromResultTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
index f4eb6bf..9ae84d9 100644 (file)
@@ -164,7 +164,7 @@ class TitleMethodsTest extends MediaWikiLangTestCase {
                $this->assertTrue( $title->hasContentModel( $expectedModelId ) );
        }
 
-       public static function provideIsCssOrJsPage() {
+       public static function provideIsSiteConfigPage() {
                return [
                        [ 'Help:Foo', false ],
                        [ 'Help:Foo.js', false ],
@@ -173,6 +173,8 @@ class TitleMethodsTest extends MediaWikiLangTestCase {
                        [ 'User:Foo.js', false ],
                        [ 'User:Foo/bar.js', false ],
                        [ 'User:Foo/bar.css', false ],
+                       [ 'User:Foo/bar.JS', false ],
+                       [ 'User:Foo/bar.CSS', false ],
                        [ 'User talk:Foo/bar.css', false ],
                        [ 'User:Foo/bar.js.xxx', false ],
                        [ 'User:Foo/bar.xxx', false ],
@@ -180,6 +182,7 @@ class TitleMethodsTest extends MediaWikiLangTestCase {
                        [ 'MediaWiki:Foo.css', true ],
                        [ 'MediaWiki:Foo.JS', false ],
                        [ 'MediaWiki:Foo.CSS', false ],
+                       [ 'MediaWiki:Foo/bar.css', true ],
                        [ 'MediaWiki:Foo.css.xxx', false ],
                        [ 'TEST-JS:Foo', false ],
                        [ 'TEST-JS:Foo.js', false ],
@@ -187,15 +190,15 @@ class TitleMethodsTest extends MediaWikiLangTestCase {
        }
 
        /**
-        * @dataProvider provideIsCssOrJsPage
-        * @covers Title::isCssOrJsPage
+        * @dataProvider provideIsSiteConfigPage
+        * @covers Title::isSiteConfigPage
         */
-       public function testIsCssOrJsPage( $title, $expectedBool ) {
+       public function testSiteConfigPage( $title, $expectedBool ) {
                $title = Title::newFromText( $title );
-               $this->assertEquals( $expectedBool, $title->isCssOrJsPage() );
+               $this->assertEquals( $expectedBool, $title->isSiteConfigPage() );
        }
 
-       public static function provideIsCssJsSubpage() {
+       public static function provideIsUserConfigPage() {
                return [
                        [ 'Help:Foo', false ],
                        [ 'Help:Foo.js', false ],
@@ -203,28 +206,32 @@ class TitleMethodsTest extends MediaWikiLangTestCase {
                        [ 'User:Foo', false ],
                        [ 'User:Foo.js', false ],
                        [ 'User:Foo/bar.js', true ],
+                       [ 'User:Foo/bar.JS', false ],
                        [ 'User:Foo/bar.css', true ],
+                       [ 'User:Foo/bar.CSS', false ],
                        [ 'User talk:Foo/bar.css', false ],
                        [ 'User:Foo/bar.js.xxx', false ],
                        [ 'User:Foo/bar.xxx', false ],
                        [ 'MediaWiki:Foo.js', false ],
-                       [ 'User:Foo/bar.JS', false ],
-                       [ 'User:Foo/bar.CSS', false ],
+                       [ 'MediaWiki:Foo.css', false ],
+                       [ 'MediaWiki:Foo.JS', false ],
+                       [ 'MediaWiki:Foo.CSS', false ],
+                       [ 'MediaWiki:Foo.css.xxx', false ],
                        [ 'TEST-JS:Foo', false ],
                        [ 'TEST-JS:Foo.js', false ],
                ];
        }
 
        /**
-        * @dataProvider provideIsCssJsSubpage
-        * @covers Title::isCssJsSubpage
+        * @dataProvider provideIsUserConfigPage
+        * @covers Title::isUserConfigPage
         */
-       public function testIsCssJsSubpage( $title, $expectedBool ) {
+       public function testIsUserConfigPage( $title, $expectedBool ) {
                $title = Title::newFromText( $title );
-               $this->assertEquals( $expectedBool, $title->isCssJsSubpage() );
+               $this->assertEquals( $expectedBool, $title->isUserConfigPage() );
        }
 
-       public static function provideIsCssSubpage() {
+       public static function provideIsUserCssConfigPage() {
                return [
                        [ 'Help:Foo', false ],
                        [ 'Help:Foo.css', false ],
@@ -237,33 +244,35 @@ class TitleMethodsTest extends MediaWikiLangTestCase {
        }
 
        /**
-        * @dataProvider provideIsCssSubpage
-        * @covers Title::isCssSubpage
+        * @dataProvider provideIsUserCssConfigPage
+        * @covers Title::isUserCssConfigPage
         */
-       public function testIsCssSubpage( $title, $expectedBool ) {
+       public function testIsUserCssConfigPage( $title, $expectedBool ) {
                $title = Title::newFromText( $title );
-               $this->assertEquals( $expectedBool, $title->isCssSubpage() );
+               $this->assertEquals( $expectedBool, $title->isUserCssConfigPage() );
        }
 
-       public static function provideIsJsSubpage() {
+       public static function provideIsUserJsConfigPage() {
                return [
                        [ 'Help:Foo', false ],
                        [ 'Help:Foo.css', false ],
                        [ 'User:Foo', false ],
                        [ 'User:Foo.js', false ],
+                       [ 'User:Foo.json', false ],
                        [ 'User:Foo.css', false ],
                        [ 'User:Foo/bar.js', true ],
+                       [ 'User:Foo/bar.json', false ],
                        [ 'User:Foo/bar.css', false ],
                ];
        }
 
        /**
-        * @dataProvider provideIsJsSubpage
-        * @covers Title::isJsSubpage
+        * @dataProvider provideIsUserJsConfigPage
+        * @covers Title::isUserJsConfigPage
         */
-       public function testIsJsSubpage( $title, $expectedBool ) {
+       public function testIsUserJsConfigPage( $title, $expectedBool ) {
                $title = Title::newFromText( $title );
-               $this->assertEquals( $expectedBool, $title->isJsSubpage() );
+               $this->assertEquals( $expectedBool, $title->isUserJsConfigPage() );
        }
 
        public static function provideIsWikitextPage() {
@@ -279,13 +288,14 @@ class TitleMethodsTest extends MediaWikiLangTestCase {
                        [ 'User:Foo/bar.js.xxx', true ],
                        [ 'User:Foo/bar.xxx', true ],
                        [ 'MediaWiki:Foo.js', false ],
-                       [ 'MediaWiki:Foo.css', false ],
-                       [ 'MediaWiki:Foo/bar.css', false ],
                        [ 'User:Foo/bar.JS', true ],
                        [ 'User:Foo/bar.CSS', true ],
+                       [ 'MediaWiki:Foo.css', false ],
+                       [ 'MediaWiki:Foo.JS', true ],
+                       [ 'MediaWiki:Foo.CSS', true ],
+                       [ 'MediaWiki:Foo.css.xxx', true ],
                        [ 'TEST-JS:Foo', false ],
                        [ 'TEST-JS:Foo.js', false ],
-                       [ 'TEST-JS_TALK:Foo.js', true ],
                ];
        }
 
index e20cc7b..7dfb735 100644 (file)
@@ -96,6 +96,7 @@ class TitlePermissionTest extends MediaWikiLangTestCase {
        /**
         * @todo This test method should be split up into separate test methods and
         * data providers
+        * @covers Title::checkQuickPermissions
         */
        public function testQuickPermissions() {
                global $wgContLang;
@@ -386,6 +387,7 @@ class TitlePermissionTest extends MediaWikiLangTestCase {
        /**
         * @todo This test method should be split up into separate test methods and
         * data providers
+        * @covers Title::checkSpecialsAndNSPermissions
         */
        public function testSpecialsAndNSPermissions() {
                global $wgNamespaceProtection;
@@ -442,91 +444,139 @@ class TitlePermissionTest extends MediaWikiLangTestCase {
        /**
         * @todo This test method should be split up into separate test methods and
         * data providers
+        * @covers Title::checkUserConfigPermissions
         */
-       public function testCssAndJavascriptPermissions() {
+       public function testJsConfigEditPermissions() {
                $this->setUser( $this->userName );
 
                $this->setTitle( NS_USER, $this->userName . '/test.js' );
-               $this->runCSSandJSPermissions(
+               $this->runConfigEditPermissions(
                        [ [ 'badaccess-group0' ], [ 'mycustomjsprotected', 'bogus' ] ],
+
                        [ [ 'badaccess-group0' ], [ 'mycustomjsprotected', 'bogus' ] ],
                        [ [ 'badaccess-group0' ] ],
+
                        [ [ 'badaccess-group0' ], [ 'mycustomjsprotected', 'bogus' ] ],
                        [ [ 'badaccess-group0' ] ]
                );
+       }
+
+       /**
+        * @todo This test method should be split up into separate test methods and
+        * data providers
+        * @covers Title::checkUserConfigPermissions
+        */
+       public function testCssConfigEditPermissions() {
+               $this->setUser( $this->userName );
 
                $this->setTitle( NS_USER, $this->userName . '/test.css' );
-               $this->runCSSandJSPermissions(
+               $this->runConfigEditPermissions(
                        [ [ 'badaccess-group0' ], [ 'mycustomcssprotected', 'bogus' ] ],
+
                        [ [ 'badaccess-group0' ] ],
                        [ [ 'badaccess-group0' ], [ 'mycustomcssprotected', 'bogus' ] ],
+
                        [ [ 'badaccess-group0' ] ],
                        [ [ 'badaccess-group0' ], [ 'mycustomcssprotected', 'bogus' ] ]
                );
+       }
+
+       /**
+        * @todo This test method should be split up into separate test methods and
+        * data providers
+        * @covers Title::checkUserConfigPermissions
+        */
+       public function testOtherJsConfigEditPermissions() {
+               $this->setUser( $this->userName );
 
                $this->setTitle( NS_USER, $this->altUserName . '/test.js' );
-               $this->runCSSandJSPermissions(
+               $this->runConfigEditPermissions(
                        [ [ 'badaccess-group0' ], [ 'customjsprotected', 'bogus' ] ],
+
                        [ [ 'badaccess-group0' ], [ 'customjsprotected', 'bogus' ] ],
                        [ [ 'badaccess-group0' ], [ 'customjsprotected', 'bogus' ] ],
+
                        [ [ 'badaccess-group0' ], [ 'customjsprotected', 'bogus' ] ],
                        [ [ 'badaccess-group0' ] ]
                );
+       }
+
+       /**
+        * @todo This test method should be split up into separate test methods and
+        * data providers
+        * @covers Title::checkUserConfigPermissions
+        */
+       public function testOtherCssConfigEditPermissions() {
+               $this->setUser( $this->userName );
 
                $this->setTitle( NS_USER, $this->altUserName . '/test.css' );
-               $this->runCSSandJSPermissions(
+               $this->runConfigEditPermissions(
                        [ [ 'badaccess-group0' ], [ 'customcssprotected', 'bogus' ] ],
+
                        [ [ 'badaccess-group0' ], [ 'customcssprotected', 'bogus' ] ],
                        [ [ 'badaccess-group0' ], [ 'customcssprotected', 'bogus' ] ],
+
                        [ [ 'badaccess-group0' ] ],
                        [ [ 'badaccess-group0' ], [ 'customcssprotected', 'bogus' ] ]
                );
+       }
+
+       /**
+        * @todo This test method should be split up into separate test methods and
+        * data providers
+        * @covers Title::checkUserConfigPermissions
+        */
+       public function testOtherNonConfigEditPermissions() {
+               $this->setUser( $this->userName );
 
                $this->setTitle( NS_USER, $this->altUserName . '/tempo' );
-               $this->runCSSandJSPermissions(
+               $this->runConfigEditPermissions(
                        [ [ 'badaccess-group0' ] ],
+
                        [ [ 'badaccess-group0' ] ],
                        [ [ 'badaccess-group0' ] ],
+
                        [ [ 'badaccess-group0' ] ],
                        [ [ 'badaccess-group0' ] ]
                );
        }
 
-       protected function runCSSandJSPermissions( $result0, $result1, $result2, $result3, $result4 ) {
+       protected function runConfigEditPermissions(
+               $resultNone,
+               $resultMyCss,
+               $resultMyJs,
+               $resultUserCss,
+               $resultUserJs
+       ) {
                $this->setUserPerm( '' );
-               $this->assertEquals( $result0,
-                       $this->title->getUserPermissionsErrors( 'bogus',
-                               $this->user ) );
+               $result = $this->title->getUserPermissionsErrors( 'bogus', $this->user );
+               $this->assertEquals( $resultNone, $result );
 
                $this->setUserPerm( 'editmyusercss' );
-               $this->assertEquals( $result1,
-                       $this->title->getUserPermissionsErrors( 'bogus',
-                               $this->user ) );
+               $result = $this->title->getUserPermissionsErrors( 'bogus', $this->user );
+               $this->assertEquals( $resultMyCss, $result );
 
                $this->setUserPerm( 'editmyuserjs' );
-               $this->assertEquals( $result2,
-                       $this->title->getUserPermissionsErrors( 'bogus',
-                               $this->user ) );
+               $result = $this->title->getUserPermissionsErrors( 'bogus', $this->user );
+               $this->assertEquals( $resultMyJs, $result );
 
                $this->setUserPerm( 'editusercss' );
-               $this->assertEquals( $result3,
-                       $this->title->getUserPermissionsErrors( 'bogus',
-                               $this->user ) );
+               $result = $this->title->getUserPermissionsErrors( 'bogus', $this->user );
+               $this->assertEquals( $resultUserCss, $result );
 
                $this->setUserPerm( 'edituserjs' );
-               $this->assertEquals( $result4,
-                       $this->title->getUserPermissionsErrors( 'bogus',
-                               $this->user ) );
+               $result = $this->title->getUserPermissionsErrors( 'bogus', $this->user );
+               $this->assertEquals( $resultUserJs, $result );
 
                $this->setUserPerm( [ 'edituserjs', 'editusercss' ] );
-               $this->assertEquals( [ [ 'badaccess-group0' ] ],
-                       $this->title->getUserPermissionsErrors( 'bogus',
-                               $this->user ) );
+               $result = $this->title->getUserPermissionsErrors( 'bogus', $this->user );
+               $this->assertEquals( [ [ 'badaccess-group0' ] ], $result );
        }
 
        /**
         * @todo This test method should be split up into separate test methods and
         * data providers
+        * @covers Title::checkPageRestrictions
         */
        public function testPageRestrictions() {
                global $wgContLang;
@@ -619,6 +669,9 @@ class TitlePermissionTest extends MediaWikiLangTestCase {
                                $this->user ) );
        }
 
+       /**
+        * @covers Title::checkCascadingSourcesRestrictions
+        */
        public function testCascadingSourcesRestrictions() {
                $this->setTitle( NS_MAIN, "test page" );
                $this->setUserPerm( [ "edit", "bogus" ] );
@@ -648,6 +701,7 @@ class TitlePermissionTest extends MediaWikiLangTestCase {
        /**
         * @todo This test method should be split up into separate test methods and
         * data providers
+        * @covers Title::checkActionPermissions
         */
        public function testActionPermissions() {
                $this->setUserPerm( [ "createpage" ] );
@@ -720,6 +774,9 @@ class TitlePermissionTest extends MediaWikiLangTestCase {
                        $this->title->userCan( 'move-target', $this->user ) );
        }
 
+       /**
+        * @covers Title::checkUserBlock
+        */
        public function testUserBlock() {
                global $wgEmailConfirmToEdit, $wgEmailAuthentication;
                $wgEmailConfirmToEdit = true;
index dc2e9ae..e4b21ce 100644 (file)
@@ -3,7 +3,7 @@
 /**
  * @covers WikiReference
  */
-class WikiReferenceTest extends PHPUnit_Framework_TestCase {
+class WikiReferenceTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
index dec7bf3..c7975ef 100644 (file)
@@ -3,7 +3,7 @@
 /**
  * @group Xml
  */
-class XmlJsTest extends PHPUnit_Framework_TestCase {
+class XmlJsTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
index 575f0c9..3f6cac9 100644 (file)
@@ -157,6 +157,7 @@ class ApiBaseTest extends ApiTestCase {
                $block = new \Block( [
                        'address' => $user->getName(),
                        'user' => $user->getID(),
+                       'by' => $this->getTestSysop()->getUser()->getId(),
                        'reason' => __METHOD__,
                        'expiry' => time() + 100500,
                ] );
index 10bdfa7..b9e4645 100644 (file)
@@ -108,7 +108,7 @@ class ApiPageSetTest extends ApiTestCase {
                $userName = $user->getName();
                $userDbkey = str_replace( ' ', '_', $userName );
                $request = new FauxRequest( [
-                       'titles' => join( '|', [
+                       'titles' => implode( '|', [
                                'Special:MyContributions',
                                'Special:MyPage',
                                'Special:MyTalk/subpage',
index 19f66fa..24b7500 100644 (file)
@@ -589,7 +589,7 @@ class ApiQueryRecentChangesIntegrationTest extends ApiTestCase {
                        'rc_minor' => 0,
                        'rc_cur_id' => $title->getArticleID(),
                        'rc_user' => 0,
-                       'rc_user_text' => 'External User',
+                       'rc_user_text' => 'm>External User',
                        'rc_comment' => '',
                        'rc_comment_text' => '',
                        'rc_comment_data' => null,
index fdbeded..8919c5e 100644 (file)
@@ -1072,7 +1072,7 @@ class ApiQueryWatchlistIntegrationTest extends ApiTestCase {
                        'rc_minor' => 0,
                        'rc_cur_id' => $title->getArticleID(),
                        'rc_user' => 0,
-                       'rc_user_text' => 'External User',
+                       'rc_user_text' => 'ext>External User',
                        'rc_comment' => '',
                        'rc_comment_text' => '',
                        'rc_comment_data' => null,
index 2371eea..d2bdb49 100644 (file)
@@ -157,7 +157,7 @@ abstract class ApiQueryContinueTestBase extends ApiQueryTestBase {
 
        /**
         * Recursively merge the new result returned from the query to the previous results.
-        * @param mixed $results
+        * @param mixed &$results
         * @param mixed $newResult
         * @param bool $numericIds If true, treat keys as ids to be merged instead of appending
         */
diff --git a/tests/phpunit/includes/api/query/ApiQueryUserContributionsTest.php b/tests/phpunit/includes/api/query/ApiQueryUserContributionsTest.php
new file mode 100644 (file)
index 0000000..6e8bc0b
--- /dev/null
@@ -0,0 +1,149 @@
+<?php
+
+/**
+ * @group API
+ * @group Database
+ * @group medium
+ * @covers ApiQueryContributions
+ */
+class ApiQueryContributionsTest extends ApiTestCase {
+       public function addDBDataOnce() {
+               global $wgActorTableSchemaMigrationStage;
+
+               $reset = new \Wikimedia\ScopedCallback( function ( $v ) {
+                       global $wgActorTableSchemaMigrationStage;
+                       $wgActorTableSchemaMigrationStage = $v;
+                       $this->overrideMwServices();
+               }, [ $wgActorTableSchemaMigrationStage ] );
+               $wgActorTableSchemaMigrationStage = MIGRATION_WRITE_BOTH;
+               $this->overrideMwServices();
+
+               $users = [
+                       User::newFromName( '192.168.2.2', false ),
+                       User::newFromName( '192.168.2.1', false ),
+                       User::newFromName( '192.168.2.3', false ),
+                       User::createNew( __CLASS__ . ' B' ),
+                       User::createNew( __CLASS__ . ' A' ),
+                       User::createNew( __CLASS__ . ' C' ),
+               ];
+
+               $title = Title::newFromText( __CLASS__ );
+               $page = WikiPage::factory( $title );
+               for ( $i = 0; $i < 3; $i++ ) {
+                       foreach ( array_reverse( $users ) as $user ) {
+                               $status = $page->doEditContent(
+                                       ContentHandler::makeContent( "Test revision $user #$i", $title ), 'Test edit', 0, false, $user
+                               );
+                               if ( !$status->isOK() ) {
+                                       $this->fail( "Failed to edit $title: " . $status->getWikiText( false, false, 'en' ) );
+                               }
+                       }
+               }
+       }
+
+       /**
+        * @dataProvider provideSorting
+        * @param int $stage One of the MIGRATION_* constants for $wgActorTableSchemaMigrationStage
+        * @param array $params Extra parameters for the query
+        * @param bool $reverse Reverse order?
+        * @param int $revs Number of revisions to expect
+        */
+       public function testSorting( $stage, $params, $reverse, $revs ) {
+               if ( isset( $params['ucuserprefix'] ) &&
+                       ( $stage === MIGRATION_WRITE_BOTH || $stage === MIGRATION_WRITE_NEW ) &&
+                       $this->db->getType() === 'mysql' && $this->usesTemporaryTables()
+               ) {
+                       // https://bugs.mysql.com/bug.php?id=10327
+                       $this->markTestSkipped( 'MySQL bug 10327 - can\'t reopen temporary tables' );
+               }
+
+               $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', $stage );
+               $this->overrideMwServices();
+
+               if ( isset( $params['ucuserids'] ) ) {
+                       $params['ucuserids'] = implode( '|', array_map( 'User::idFromName', $params['ucuserids'] ) );
+               }
+               if ( isset( $params['ucuser'] ) ) {
+                       $params['ucuser'] = implode( '|', $params['ucuser'] );
+               }
+
+               $sort = 'rsort';
+               if ( $reverse ) {
+                       $params['ucdir'] = 'newer';
+                       $sort = 'sort';
+               }
+
+               $params += [
+                       'action' => 'query',
+                       'list' => 'usercontribs',
+                       'ucprop' => 'ids',
+               ];
+
+               $apiResult = $this->doApiRequest( $params + [ 'uclimit' => 500 ] );
+               $this->assertArrayNotHasKey( 'continue', $apiResult[0] );
+               $this->assertArrayHasKey( 'query', $apiResult[0] );
+               $this->assertArrayHasKey( 'usercontribs', $apiResult[0]['query'] );
+
+               $count = 0;
+               $ids = [];
+               foreach ( $apiResult[0]['query']['usercontribs'] as $page ) {
+                       $count++;
+                       $ids[$page['user']][] = $page['revid'];
+               }
+               $this->assertSame( $revs, $count, 'Expected number of revisions' );
+               foreach ( $ids as $user => $revids ) {
+                       $sorted = $revids;
+                       call_user_func_array( $sort, [ &$sorted ] );
+                       $this->assertSame( $sorted, $revids, "IDs for $user are sorted" );
+               }
+
+               for ( $limit = 1; $limit < $revs; $limit++ ) {
+                       $continue = [];
+                       $count = 0;
+                       $batchedIds = [];
+                       while ( $continue !== null ) {
+                               $apiResult = $this->doApiRequest( $params + [ 'uclimit' => $limit ] + $continue );
+                               $this->assertArrayHasKey( 'query', $apiResult[0], "Batching with limit $limit" );
+                               $this->assertArrayHasKey( 'usercontribs', $apiResult[0]['query'],
+                                       "Batching with limit $limit" );
+                               $continue = isset( $apiResult[0]['continue'] ) ? $apiResult[0]['continue'] : null;
+                               foreach ( $apiResult[0]['query']['usercontribs'] as $page ) {
+                                       $count++;
+                                       $batchedIds[$page['user']][] = $page['revid'];
+                               }
+                               $this->assertLessThanOrEqual( $revs, $count, "Batching with limit $limit" );
+                       }
+                       $this->assertSame( $ids, $batchedIds, "Result set is the same when batching with limit $limit" );
+               }
+       }
+
+       public static function provideSorting() {
+               $users = [ __CLASS__ . ' A', __CLASS__ . ' B', __CLASS__ . ' C' ];
+               $users2 = [ __CLASS__ . ' A', __CLASS__ . ' B', __CLASS__ . ' D' ];
+               $ips = [ '192.168.2.1', '192.168.2.2', '192.168.2.3', '192.168.2.4' ];
+
+               foreach (
+                       [
+                               'old' => MIGRATION_OLD,
+                               'write both' => MIGRATION_WRITE_BOTH,
+                               'write new' => MIGRATION_WRITE_NEW,
+                               'new' => MIGRATION_NEW,
+                       ] as $stageName => $stage
+               ) {
+                       foreach ( [ false, true ] as $reverse ) {
+                               $name = $stageName . ( $reverse ? ', reverse' : '' );
+                               yield "Named users, $name" => [ $stage, [ 'ucuser' => $users ], $reverse, 9 ];
+                               yield "Named users including a no-edit user, $name" => [
+                                       $stage, [ 'ucuser' => $users2 ], $reverse, 6
+                               ];
+                               yield "IP users, $name" => [ $stage, [ 'ucuser' => $ips ], $reverse, 9 ];
+                               yield "All users, $name" => [
+                                       $stage, [ 'ucuser' => array_merge( $users, $ips ) ], $reverse, 18
+                               ];
+                               yield "User IDs, $name" => [ $stage, [ 'ucuserids' => $users ], $reverse, 9 ];
+                               yield "Users by prefix, $name" => [ $stage, [ 'ucuserprefix' => __CLASS__ ], $reverse, 9 ];
+                               yield "IPs by prefix, $name" => [ $stage, [ 'ucuserprefix' => '192.168.2.' ], $reverse, 9 ];
+                       }
+               }
+       }
+}
index e4056ee..211eba0 100644 (file)
@@ -1436,6 +1436,7 @@ class AuthManagerTest extends \MediaWikiTestCase {
                $blockOptions = [
                        'address' => 'UTBlockee',
                        'user' => $user->getID(),
+                       'by' => $this->getTestSysop()->getUser()->getId(),
                        'reason' => __METHOD__,
                        'expiry' => time() + 100500,
                        'createAccount' => true,
@@ -1448,6 +1449,7 @@ class AuthManagerTest extends \MediaWikiTestCase {
 
                $blockOptions = [
                        'address' => '127.0.0.0/24',
+                       'by' => $this->getTestSysop()->getUser()->getId(),
                        'reason' => __METHOD__,
                        'expiry' => time() + 100500,
                        'createAccount' => true,
index 4e44547..81cdc9d 100644 (file)
@@ -76,6 +76,7 @@ class CheckBlocksSecondaryAuthenticationProviderTest extends \MediaWikiTestCase
                $blockOptions = [
                        'address' => 'UTBlockee',
                        'user' => $user->getID(),
+                       'by' => $this->getTestSysop()->getUser()->getId(),
                        'reason' => __METHOD__,
                        'expiry' => time() + 100500,
                        'createAccount' => true,
@@ -149,6 +150,7 @@ class CheckBlocksSecondaryAuthenticationProviderTest extends \MediaWikiTestCase
                $blockOptions = [
                        'address' => '127.0.0.0/24',
                        'reason' => __METHOD__,
+                       'by' => $this->getTestSysop()->getUser()->getId(),
                        'expiry' => time() + 100500,
                        'createAccount' => true,
                ];
index dd02793..1a7ed12 100644 (file)
@@ -5,7 +5,7 @@ namespace MediaWiki\Auth;
 use Psr\Log\LoggerInterface;
 use Wikimedia\TestingAccessWrapper;
 
-class EmailNotificationSecondaryAuthenticationProviderTest extends \PHPUnit_Framework_TestCase {
+class EmailNotificationSecondaryAuthenticationProviderTest extends \PHPUnit\Framework\TestCase {
        public function testConstructor() {
                $config = new \HashConfig( [
                        'EnableEmail' => true,
index ab42696..333eb28 100644 (file)
@@ -27,12 +27,16 @@ class RecentChangeTest extends MediaWikiTestCase {
         * @covers RecentChange::loadFromRow
         */
        public function testNewFromRow() {
+               $user = $this->getTestUser()->getUser();
+               $actorId = $user->getActorId();
+
                $row = new stdClass();
                $row->rc_foo = 'AAA';
                $row->rc_timestamp = '20150921134808';
                $row->rc_deleted = 'bar';
                $row->rc_comment_text = 'comment';
                $row->rc_comment_data = null;
+               $row->rc_user = $user->getId();
 
                $rc = RecentChange::newFromRow( $row );
 
@@ -43,6 +47,9 @@ class RecentChangeTest extends MediaWikiTestCase {
                        'rc_comment' => 'comment',
                        'rc_comment_text' => 'comment',
                        'rc_comment_data' => null,
+                       'rc_user' => $user->getId(),
+                       'rc_user_text' => $user->getName(),
+                       'rc_actor' => $actorId,
                ];
                $this->assertEquals( $expected, $rc->getAttributes() );
 
@@ -51,6 +58,7 @@ class RecentChangeTest extends MediaWikiTestCase {
                $row->rc_timestamp = '20150921134808';
                $row->rc_deleted = 'bar';
                $row->rc_comment = 'comment';
+               $row->rc_user = $user->getId();
 
                Wikimedia\suppressWarnings();
                $rc = RecentChange::newFromRow( $row );
@@ -63,6 +71,9 @@ class RecentChangeTest extends MediaWikiTestCase {
                        'rc_comment' => 'comment',
                        'rc_comment_text' => 'comment',
                        'rc_comment_data' => null,
+                       'rc_user' => $user->getId(),
+                       'rc_user_text' => $user->getName(),
+                       'rc_actor' => $actorId,
                ];
                $this->assertEquals( $expected, $rc->getAttributes() );
        }
index 9ade892..c607ae5 100644 (file)
@@ -7,7 +7,7 @@
  *
  * @author Jeroen De Dauw < jeroendedauw@gmail.com >
  */
-class ComposerVersionNormalizerTest extends PHPUnit_Framework_TestCase {
+class ComposerVersionNormalizerTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
index 379eebd..c833934 100644 (file)
@@ -2,7 +2,7 @@
 
 use Wikimedia\TestingAccessWrapper;
 
-class EtcdConfigTest extends PHPUnit_Framework_TestCase {
+class EtcdConfigTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
index 8086b4b..1ee188e 100644 (file)
@@ -2,7 +2,7 @@
 
 namespace MediaWiki\Logger\Monolog;
 
-class LogstashFormatterTest extends \PHPUnit_Framework_TestCase {
+class LogstashFormatterTest extends \PHPUnit\Framework\TestCase {
        /**
         * @dataProvider provideV1
         * @param array $record The input record.
index 6977aef..3ab9b56 100644 (file)
@@ -3,7 +3,7 @@
 /**
  * @covers MWCallableUpdate
  */
-class MWCallableUpdateTest extends PHPUnit_Framework_TestCase {
+class MWCallableUpdateTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
index 1261c24..693897e 100644 (file)
@@ -3,7 +3,7 @@
 /**
  * @covers TransactionRoundDefiningUpdate
  */
-class TransactionRoundDefiningUpdateTest extends PHPUnit_Framework_TestCase {
+class TransactionRoundDefiningUpdateTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
index d43e04a..f762693 100644 (file)
@@ -3,7 +3,7 @@
 /**
  * @covers ExternalStoreFactory
  */
-class ExternalStoreFactoryTest extends PHPUnit_Framework_TestCase {
+class ExternalStoreFactoryTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
index 75c93be..c4290e1 100644 (file)
@@ -3,7 +3,7 @@
 /**
  * @covers HTMLRestrictionsField
  */
-class HTMLRestrictionsFieldTest extends PHPUnit_Framework_TestCase {
+class HTMLRestrictionsFieldTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
index 7bb03db..3b91f5b 100644 (file)
@@ -290,12 +290,15 @@ EOF
                $importer->doImport();
 
                $db = wfGetDB( DB_MASTER );
+               $revQuery = Revision::getQueryInfo();
 
                $row = $db->selectRow(
-                       'revision',
-                       [ 'rev_user', 'rev_user_text' ],
+                       $revQuery['tables'],
+                       $revQuery['fields'],
                        [ 'rev_timestamp' => $db->timestamp( "201601010{$n}0000" ) ],
-                       __METHOD__
+                       __METHOD__,
+                       [],
+                       $revQuery['joins']
                );
                $this->assertSame(
                        $assign && $create ? 'UserDoesNotExist' : 'Xxx>UserDoesNotExist',
@@ -304,10 +307,12 @@ EOF
                $this->assertSame( $assign && $create ? $hookId : 0, (int)$row->rev_user );
 
                $row = $db->selectRow(
-                       'revision',
-                       [ 'rev_user', 'rev_user_text' ],
+                       $revQuery['tables'],
+                       $revQuery['fields'],
                        [ 'rev_timestamp' => $db->timestamp( "201601010{$n}0001" ) ],
-                       __METHOD__
+                       __METHOD__,
+                       [],
+                       $revQuery['joins']
                );
                $this->assertSame( ( $assign ? '' : 'Xxx>' ) . $user->getName(), $row->rev_user_text );
                $this->assertSame( $assign ? $user->getId() : 0, (int)$row->rev_user );
index d5a267e..b5d6144 100644 (file)
@@ -8,7 +8,7 @@
  * @licence GNU GPL v2+
  * @author Thiemo Kreuz
  */
-class JobQueueMemoryTest extends PHPUnit_Framework_TestCase {
+class JobQueueMemoryTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
index 8831265..4fbbc99 100644 (file)
@@ -4,7 +4,7 @@
  *
  * @group Database
  */
-class ArrayUtilsTest extends PHPUnit_Framework_TestCase {
+class ArrayUtilsTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
index 89cf68f..667eb0a 100644 (file)
@@ -149,6 +149,12 @@ class CSSMinTest extends MediaWikiTestCase {
                        [ "foo { content: '\"'; }", "foo{content:'\"'}" ],
                        // - Whitespace in string values
                        [ 'foo { content: " "; }', 'foo{content:" "}' ],
+
+                       // Whitespaces after opening and before closing parentheses and brackets
+                       [ 'a:not( [ href ] ) { prop: url( foobar.png ); }', 'a:not([href]){prop:url(foobar.png)}' ],
+
+                       // Ensure that the invalid "url (" will not become the valid "url(" by minification
+                       [ 'foo { prop: url ( foobar.png ); }', 'foo{prop:url (foobar.png)}' ],
                ];
        }
 
index c995840..c9cdf58 100644 (file)
@@ -3,7 +3,7 @@
 /**
  * @covers DeferredStringifier
  */
-class DeferredStringifierTest extends PHPUnit_Framework_TestCase {
+class DeferredStringifierTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
index 264cc00..1b3397c 100644 (file)
@@ -3,7 +3,7 @@
 /**
  * @covers DnsSrvDiscoverer
  */
-class DnsSrvDiscovererTest extends PHPUnit_Framework_TestCase {
+class DnsSrvDiscovererTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
index 3e8aaeb..a7cf755 100644 (file)
@@ -26,7 +26,7 @@
  *
  * @author Jeroen De Dauw < jeroendedauw@gmail.com >
  */
-abstract class GenericArrayObjectTest extends PHPUnit_Framework_TestCase {
+abstract class GenericArrayObjectTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
index 040bebf..ba28828 100644 (file)
@@ -3,7 +3,7 @@
 /**
  * @group HashRing
  */
-class HashRingTest extends PHPUnit_Framework_TestCase {
+class HashRingTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
index eb601e7..c5e87e4 100644 (file)
@@ -3,7 +3,7 @@
 /**
  * @covers HtmlArmor
  */
-class HtmlArmorTest extends PHPUnit_Framework_TestCase {
+class HtmlArmorTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
index 8192f01..03c7b0c 100644 (file)
@@ -5,7 +5,7 @@
  * @todo tests below for findIE6Extension should be split into...
  *    ...a dataprovider and test method.
  */
-class IEUrlExtensionTest extends PHPUnit_Framework_TestCase {
+class IEUrlExtensionTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
index a36b259..6a75181 100644 (file)
@@ -8,7 +8,7 @@
  * @todo Test methods in this call should be split into a method and a
  * dataprovider.
  */
-class IPTest extends PHPUnit_Framework_TestCase {
+class IPTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
index 98494c4..6105678 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 
-class JavaScriptMinifierTest extends PHPUnit_Framework_TestCase {
+class JavaScriptMinifierTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
index 57c9831..695a734 100644 (file)
@@ -3,7 +3,7 @@
  * PHP Unit tests for MWMessagePack
  * @covers MWMessagePack
  */
-class MWMessagePackTest extends PHPUnit_Framework_TestCase {
+class MWMessagePackTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
index 363b081..2a962b7 100644 (file)
@@ -2,7 +2,7 @@
 /**
  * @group Cache
  */
-class MapCacheLRUTest extends PHPUnit_Framework_TestCase {
+class MapCacheLRUTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
index d09171c..d64ba3d 100644 (file)
@@ -24,7 +24,7 @@ class ArrayBackedMemoizedCallable extends MemoizedCallable {
  * PHP Unit tests for MemoizedCallable class.
  * @covers MemoizedCallable
  */
-class MemoizedCallableTest extends PHPUnit_Framework_TestCase {
+class MemoizedCallableTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
index ddb7bfa..c8940e5 100644 (file)
@@ -9,7 +9,7 @@
  *
  * @group Cache
  */
-class ProcessCacheLRUTest extends PHPUnit_Framework_TestCase {
+class ProcessCacheLRUTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
index d0e404b..85b8c62 100644 (file)
@@ -6,7 +6,7 @@ use Liuggio\StatsdClient\Sender\SenderInterface;
 /**
  * @covers SamplingStatsdClient
  */
-class SamplingStatsdClientTest extends PHPUnit_Framework_TestCase {
+class SamplingStatsdClientTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
index 3e5ccac..fcfa53e 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 
-class StringUtilsTest extends PHPUnit_Framework_TestCase {
+class StringUtilsTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
index 7e166f8..581a518 100644 (file)
@@ -19,7 +19,7 @@
  * @author Ori Livneh <ori@wikimedia.org>
  */
 
-class TimingTest extends PHPUnit_Framework_TestCase {
+class TimingTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
index d226227..1cbd86f 100644 (file)
@@ -24,7 +24,7 @@
  * @copyright © 2014 Wikimedia Foundation and contributors
  * @since 1.25
  */
-class XhprofDataTest extends PHPUnit_Framework_TestCase {
+class XhprofDataTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
index e2723b9..0ea1328 100644 (file)
@@ -18,7 +18,7 @@
  * @file
  */
 
-class XhprofTest extends PHPUnit_Framework_TestCase {
+class XhprofTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
index 8a11d87..8616b41 100644 (file)
@@ -5,7 +5,7 @@
  * @group Xml
  * @covers XMLTypeCheck
  */
-class XmlTypeCheckTest extends PHPUnit_Framework_TestCase {
+class XmlTypeCheckTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
index 10dca7d..02eac11 100644 (file)
@@ -7,7 +7,7 @@ use Wikimedia\Http\HttpAcceptNegotiator;
  *
  * @author Daniel Kinzler
  */
-class HttpAcceptNegotiatorTest extends \PHPUnit_Framework_TestCase {
+class HttpAcceptNegotiatorTest extends \PHPUnit\Framework\TestCase {
 
        public function provideGetFirstSupportedValue() {
                return [
index 788c297..e4b47b4 100644 (file)
@@ -7,7 +7,7 @@ use Wikimedia\Http\HttpAcceptParser;
  *
  * @author Daniel Kinzler
  */
-class HttpAcceptParserTest extends \PHPUnit_Framework_TestCase {
+class HttpAcceptParserTest extends \PHPUnit\Framework\TestCase {
 
        public function provideParseWeights() {
                return [
index 1f3cae5..fbe5a2b 100644 (file)
@@ -3,7 +3,7 @@
  * @group Media
  * @covers MimeAnalyzer
  */
-class MimeAnalyzerTest extends PHPUnit_Framework_TestCase {
+class MimeAnalyzerTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
index d71b16c..d0360a9 100644 (file)
@@ -5,7 +5,7 @@ use Wikimedia\TestingAccessWrapper;
 /**
  * @group BagOStuff
  */
-class CachedBagOStuffTest extends PHPUnit_Framework_TestCase {
+class CachedBagOStuffTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
index 9eb3409..332e23b 100644 (file)
@@ -5,7 +5,7 @@ use Wikimedia\TestingAccessWrapper;
 /**
  * @group BagOStuff
  */
-class HashBagOStuffTest extends PHPUnit_Framework_TestCase {
+class HashBagOStuffTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
index ca78f65..77198f9 100644 (file)
@@ -16,7 +16,7 @@ use Wikimedia\TestingAccessWrapper;
  * @covers WANObjectCache::getInterimValue
  * @covers WANObjectCache::setInterimValue
  */
-class WANObjectCacheTest extends PHPUnit_Framework_TestCase {
+class WANObjectCacheTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
index 25613fe..538d625 100644 (file)
@@ -6,7 +6,7 @@ use Psr\Log\LoggerInterface;
 /**
  * @covers \Wikimedia\Rdbms\TransactionProfiler
  */
-class TransactionProfilerTest extends PHPUnit_Framework_TestCase {
+class TransactionProfilerTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
index 2a707d3..dd86a73 100644 (file)
@@ -12,7 +12,7 @@ use Wikimedia\Rdbms\ConnectionManager;
  *
  * @author Daniel Kinzler
  */
-class ConnectionManagerTest extends \PHPUnit_Framework_TestCase {
+class ConnectionManagerTest extends \PHPUnit\Framework\TestCase {
 
        /**
         * @return IDatabase|PHPUnit_Framework_MockObject_MockObject
index 20ce095..8d7d104 100644 (file)
@@ -12,7 +12,7 @@ use Wikimedia\Rdbms\SessionConsistentConnectionManager;
  *
  * @author Daniel Kinzler
  */
-class SessionConsistentConnectionManagerTest extends \PHPUnit_Framework_TestCase {
+class SessionConsistentConnectionManagerTest extends \PHPUnit\Framework\TestCase {
 
        /**
         * @return IDatabase|PHPUnit_Framework_MockObject_MockObject
index e660265..d1f961a 100644 (file)
@@ -10,7 +10,7 @@ use Wikimedia\Rdbms\ResultWrapper;
 /**
  * @covers Wikimedia\Rdbms\DBConnRef
  */
-class DBConnRefTest extends PHPUnit_Framework_TestCase {
+class DBConnRefTest extends PHPUnit\Framework\TestCase {
 
        /**
         * @return ILoadBalancer
index 4e5f735..b13e8fc 100644 (file)
@@ -5,7 +5,7 @@ use Wikimedia\Rdbms\DatabaseDomain;
 /**
  * @covers Wikimedia\Rdbms\DatabaseDomain
  */
-class DatabaseDomainTest extends PHPUnit_Framework_TestCase {
+class DatabaseDomainTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
index 54c7d52..5fcca1a 100644 (file)
@@ -109,7 +109,7 @@ class FakeDatabaseMysqlBase extends DatabaseMysqlBase {
        }
 }
 
-class DatabaseMysqlBaseTest extends PHPUnit_Framework_TestCase {
+class DatabaseMysqlBaseTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
@@ -495,4 +495,19 @@ class DatabaseMysqlBaseTest extends PHPUnit_Framework_TestCase {
 
                $db->clearFlag( Database::DBO_IGNORE );
        }
+
+       /**
+        * @covers Wikimedia\Rdbms\MySQLMasterPos
+        */
+       public function testSerialize() {
+               $pos = new MySQLMasterPos( '3E11FA47-71CA-11E1-9E33-C80AA9429562:99', 53636363 );
+               $roundtripPos = unserialize( serialize( $pos ) );
+
+               $this->assertEquals( $pos, $roundtripPos );
+
+               $pos = new MySQLMasterPos( '255-11-23', 53636363 );
+               $roundtripPos = unserialize( serialize( $pos ) );
+
+               $this->assertEquals( $pos, $roundtripPos );
+       }
 }
index d8ebeff..ebf6e45 100644 (file)
@@ -6,7 +6,7 @@ use Wikimedia\Rdbms\LikeMatch;
  * Test the parts of the Database abstract class that deal
  * with creating SQL text.
  */
-class DatabaseSQLTest extends PHPUnit_Framework_TestCase {
+class DatabaseSQLTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
@@ -559,11 +559,11 @@ class DatabaseSQLTest extends PHPUnit_Framework_TestCase {
                                        'uniqueIndexes' => [ 'field' ],
                                        'rows' => [ 'field' => 'text', 'field2' => 'text2' ],
                                ],
-                               "DELETE FROM replace_table " .
+                               "BEGIN; DELETE FROM replace_table " .
                                        "WHERE (field = 'text'); " .
                                        "INSERT INTO replace_table " .
                                        "(field,field2) " .
-                                       "VALUES ('text','text2')"
+                                       "VALUES ('text','text2'); COMMIT"
                        ],
                        [
                                [
@@ -575,11 +575,11 @@ class DatabaseSQLTest extends PHPUnit_Framework_TestCase {
                                                'md_deps' => 'deps',
                                        ],
                                ],
-                               "DELETE FROM module_deps " .
+                               "BEGIN; DELETE FROM module_deps " .
                                        "WHERE (md_module = 'module' AND md_skin = 'skin'); " .
                                        "INSERT INTO module_deps " .
                                        "(md_module,md_skin,md_deps) " .
-                                       "VALUES ('module','skin','deps')"
+                                       "VALUES ('module','skin','deps'); COMMIT"
                        ],
                        [
                                [
@@ -597,7 +597,7 @@ class DatabaseSQLTest extends PHPUnit_Framework_TestCase {
                                                ],
                                        ],
                                ],
-                               "DELETE FROM module_deps " .
+                               "BEGIN; DELETE FROM module_deps " .
                                        "WHERE (md_module = 'module' AND md_skin = 'skin'); " .
                                        "INSERT INTO module_deps " .
                                        "(md_module,md_skin,md_deps) " .
@@ -606,7 +606,7 @@ class DatabaseSQLTest extends PHPUnit_Framework_TestCase {
                                        "WHERE (md_module = 'module2' AND md_skin = 'skin2'); " .
                                        "INSERT INTO module_deps " .
                                        "(md_module,md_skin,md_deps) " .
-                                       "VALUES ('module2','skin2','deps2')"
+                                       "VALUES ('module2','skin2','deps2'); COMMIT"
                        ],
                        [
                                [
@@ -624,7 +624,7 @@ class DatabaseSQLTest extends PHPUnit_Framework_TestCase {
                                                ],
                                        ],
                                ],
-                               "DELETE FROM module_deps " .
+                               "BEGIN; DELETE FROM module_deps " .
                                        "WHERE (md_module = 'module') OR (md_skin = 'skin'); " .
                                        "INSERT INTO module_deps " .
                                        "(md_module,md_skin,md_deps) " .
@@ -633,7 +633,7 @@ class DatabaseSQLTest extends PHPUnit_Framework_TestCase {
                                        "WHERE (md_module = 'module2') OR (md_skin = 'skin2'); " .
                                        "INSERT INTO module_deps " .
                                        "(md_module,md_skin,md_deps) " .
-                                       "VALUES ('module2','skin2','deps2')"
+                                       "VALUES ('module2','skin2','deps2'); COMMIT"
                        ],
                        [
                                [
@@ -645,9 +645,9 @@ class DatabaseSQLTest extends PHPUnit_Framework_TestCase {
                                                'md_deps' => 'deps',
                                        ],
                                ],
-                               "INSERT INTO module_deps " .
+                               "BEGIN; INSERT INTO module_deps " .
                                        "(md_module,md_skin,md_deps) " .
-                                       "VALUES ('module','skin','deps')"
+                                       "VALUES ('module','skin','deps'); COMMIT"
                        ],
                ];
        }
index b42a367..542470d 100644 (file)
@@ -5,7 +5,7 @@ use Wikimedia\Rdbms\LBFactorySingle;
 use Wikimedia\Rdbms\TransactionProfiler;
 use Wikimedia\TestingAccessWrapper;
 
-class DatabaseTest extends PHPUnit_Framework_TestCase {
+class DatabaseTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
index 7becad2..73fd471 100644 (file)
@@ -4,7 +4,7 @@
  * @group Media
  * @covers XMPReader
  */
-class XMPTest extends PHPUnit_Framework_TestCase {
+class XMPTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
index 0785920..746f68a 100644 (file)
@@ -5,7 +5,7 @@ use Psr\Log\NullLogger;
 /**
  * @group Media
  */
-class XMPValidateTest extends PHPUnit_Framework_TestCase {
+class XMPValidateTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
index 2dc9a2c..786d761 100644 (file)
@@ -36,6 +36,7 @@ abstract class LogFormatterTestCase extends MediaWikiLangTestCase {
                        'log_timestamp' => isset( $data['timestamp'] ) ? $data['timestamp'] : wfTimestampNow(),
                        'log_user' => isset( $data['user'] ) ? $data['user'] : 0,
                        'log_user_text' => isset( $data['user_text'] ) ? $data['user_text'] : 'User',
+                       'log_actor' => isset( $data['actor'] ) ? $data['actor'] : 0,
                        'log_namespace' => isset( $data['namespace'] ) ? $data['namespace'] : NS_MAIN,
                        'log_title' => isset( $data['title'] ) ? $data['title'] : 'Main_Page',
                        'log_page' => isset( $data['page'] ) ? $data['page'] : 0,
index 0991254..c943cef 100644 (file)
@@ -108,4 +108,21 @@ class JpegMetadataExtractorTest extends MediaWikiTestCase {
                $expected = 'BE';
                $this->assertEquals( $expected, $res['byteOrder'] );
        }
+
+       public function testInfiniteRead() {
+               // test file truncated right after a segment, which previously
+               // caused an infinite loop looking for the next segment byte.
+               // Should get past infinite loop and throw in wfUnpack()
+               $this->setExpectedException( 'MWException' );
+               $res = JpegMetadataExtractor::segmentSplitter( $this->filePath . 'jpeg-segment-loop1.jpg' );
+       }
+
+       public function testInfiniteRead2() {
+               // test file truncated after a segment's marker and size, which
+               // would cause a seek past end of file. Seek past end of file
+               // doesn't actually fail, but prevents further reading and was
+               // devolving into the previous case (testInfiniteRead).
+               $this->setExpectedException( 'MWException' );
+               $res = JpegMetadataExtractor::segmentSplitter( $this->filePath . 'jpeg-segment-loop2.jpg' );
+       }
 }
index 6e8c9ce..df5614d 100644 (file)
@@ -5,7 +5,7 @@ use Wikimedia\TestingAccessWrapper;
 /**
  * @group BagOStuff
  */
-class RedisBagOStuffTest extends PHPUnit_Framework_TestCase {
+class RedisBagOStuffTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
index 6b680e5..7adc43b 100644 (file)
@@ -1784,12 +1784,13 @@ more stuff
 
                // Make sure the log entry looks good
                // log_params is not checked here
+               $actorQuery = ActorMigration::newMigration()->getJoin( 'log_user' );
                $this->assertSelect(
-                       'logging',
+                       [ 'logging' ] + $actorQuery['tables'],
                        [
                                'log_comment',
-                               'log_user',
-                               'log_user_text',
+                               'log_user' => $actorQuery['fields']['log_user'],
+                               'log_user_text' => $actorQuery['fields']['log_user_text'],
                                'log_namespace',
                                'log_title',
                        ],
@@ -1800,7 +1801,9 @@ more stuff
                                $user->getName(),
                                (string)$page->getTitle()->getNamespace(),
                                $page->getTitle()->getDBkey(),
-                       ] ]
+                       ] ],
+                       [],
+                       $actorQuery['joins']
                );
        }
 
index 28430ad..0e1605a 100644 (file)
@@ -26,7 +26,7 @@ use Wikimedia\ScopedCallback;
  * @covers ParserOptions
  * @covers ParserOutput
  */
-class ParserIntegrationTest extends PHPUnit_Framework_TestCase {
+class ParserIntegrationTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
index 0839cfb..78175fa 100644 (file)
@@ -26,6 +26,8 @@
  */
 class UserPasswordPolicyTest extends MediaWikiTestCase {
 
+       protected $tablesUsed = [ 'user', 'user_groups' ];
+
        protected $policies = [
                'checkuser' => [
                        'MinimalPasswordLength' => 10,
index 4a2810b..929ff0f 100644 (file)
@@ -3,7 +3,7 @@
 /**
  * @covers VersionChecker
  */
-class VersionCheckerTest extends PHPUnit_Framework_TestCase {
+class VersionCheckerTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
index db1fb64..e4f58eb 100644 (file)
@@ -4,7 +4,7 @@
  * @group ResourceLoader
  * @covers DerivativeResourceLoaderContext
  */
-class DerivativeResourceLoaderContextTest extends PHPUnit_Framework_TestCase {
+class DerivativeResourceLoaderContextTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
index c00432e..fa4d804 100644 (file)
@@ -6,7 +6,7 @@ use Wikimedia\TestingAccessWrapper;
  * @group Cache
  * @covers MessageBlobStore
  */
-class MessageBlobStoreTest extends PHPUnit_Framework_TestCase {
+class MessageBlobStoreTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
index e51d0d6..7bfd769 100644 (file)
@@ -5,7 +5,7 @@ use Wikimedia\TestingAccessWrapper;
 /**
  * @group ResourceLoader
  */
-class ResourceLoaderClientHtmlTest extends PHPUnit_Framework_TestCase {
+class ResourceLoaderClientHtmlTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
index f848556..b226ee1 100644 (file)
@@ -8,7 +8,7 @@
  * @group Cache
  * @covers ResourceLoaderContext
  */
-class ResourceLoaderContextTest extends PHPUnit_Framework_TestCase {
+class ResourceLoaderContextTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
index 0718af6..a1b1422 100644 (file)
@@ -3,7 +3,7 @@
 /**
  * @group ResourceLoader
  */
-class ResourceLoaderSkinModuleTest extends PHPUnit_Framework_TestCase {
+class ResourceLoaderSkinModuleTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
index 28c69fa..54533a7 100644 (file)
@@ -19,7 +19,7 @@
  * http://www.gnu.org/copyleft/gpl.html
  */
 
-class SearchSuggestionSetTest extends \PHPUnit_Framework_TestCase {
+class SearchSuggestionSetTest extends \PHPUnit\Framework\TestCase {
        /**
         * Test that adding a new suggestion at the end
         * will keep proper score ordering
index 768ed91..1e8175e 100644 (file)
@@ -6,7 +6,7 @@ use MediaWiki\Services\ServiceContainer;
  *
  * @group MediaWiki
  */
-class ServiceContainerTest extends PHPUnit_Framework_TestCase {
+class ServiceContainerTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
index cb2669f..b031431 100644 (file)
@@ -9,7 +9,7 @@ use Wikimedia\TestingAccessWrapper;
 /**
  * @group Shell
  */
-class CommandFactoryTest extends PHPUnit_Framework_TestCase {
+class CommandFactoryTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
index 2ba7bdc..2e03163 100644 (file)
@@ -7,7 +7,7 @@ use Wikimedia\TestingAccessWrapper;
  * @covers \MediaWiki\Shell\Command
  * @group Shell
  */
-class CommandTest extends PHPUnit_Framework_TestCase {
+class CommandTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
@@ -170,5 +170,12 @@ class CommandTest extends PHPUnit_Framework_TestCase {
                $command->input( str_repeat( '!', 1000000 ) );
                $result = $command->execute();
                $this->assertSame( 1000000, strlen( $result->getStdout() ) );
+
+               // And try it with empty input
+               $command = new Command();
+               $command->params( 'cat' );
+               $command->input( '' );
+               $result = $command->execute();
+               $this->assertSame( '', $result->getStdout() );
        }
 }
index 1585375..681c3dc 100644 (file)
@@ -23,7 +23,7 @@ use MediaWiki\Shell\FirejailCommand;
 use MediaWiki\Shell\Shell;
 use Wikimedia\TestingAccessWrapper;
 
-class FirejailCommandTest extends PHPUnit_Framework_TestCase {
+class FirejailCommandTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
@@ -34,7 +34,7 @@ class FirejailCommandTest extends PHPUnit_Framework_TestCase {
                $limit = "/bin/bash '$IP/includes/shell/limit.sh'";
                $profile = "--profile=$IP/includes/shell/firejail.profile";
                $blacklist = '--blacklist=' . realpath( MW_CONFIG_FILE );
-               $default = "$blacklist --noroot --seccomp=@default --private-dev";
+               $default = "$blacklist --noroot --seccomp --private-dev";
                return [
                        [
                                'No restrictions',
@@ -58,12 +58,12 @@ class FirejailCommandTest extends PHPUnit_Framework_TestCase {
                        [
                                'seccomp',
                                'ls', Shell::SECCOMP,
-                               "$limit 'firejail --quiet $profile --seccomp=@default -- '\''ls'\''' $env"
+                               "$limit 'firejail --quiet $profile --seccomp -- '\''ls'\''' $env"
                        ],
                        [
                                'seccomp & no execve',
                                'ls', Shell::SECCOMP | Shell::NO_EXECVE,
-                               "$limit 'firejail --quiet $profile --shell=none --seccomp=@default,execve -- '\''ls'\''' $env"
+                               "$limit 'firejail --quiet $profile --shell=none --seccomp=execve -- '\''ls'\''' $env"
                        ],
                ];
        }
index 986a665..8162bc0 100644 (file)
@@ -6,7 +6,7 @@ use MediaWiki\Shell\Shell;
  * @covers \MediaWiki\Shell\Shell
  * @group Shell
  */
-class ShellTest extends PHPUnit_Framework_TestCase {
+class ShellTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
index 05aa6d3..69e0e38 100644 (file)
@@ -27,7 +27,7 @@
  *
  * @author Katie Filbert < aude.wiki@gmail.com >
  */
-class FileBasedSiteLookupTest extends PHPUnit_Framework_TestCase {
+class FileBasedSiteLookupTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
index d230550..2ac2714 100644 (file)
@@ -27,7 +27,7 @@ use MediaWiki\Site\MediaWikiPageNameNormalizer;
  *
  * @author Marius Hoch
  */
-class MediaWikiPageNameNormalizerTest extends PHPUnit_Framework_TestCase {
+class MediaWikiPageNameNormalizerTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
index 3a41b5f..279bbb2 100644 (file)
@@ -29,7 +29,7 @@
  *
  * @author Daniel Kinzler
  */
-class SiteExporterTest extends PHPUnit_Framework_TestCase {
+class SiteExporterTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
index 373dcc2..7c949ac 100644 (file)
@@ -29,7 +29,7 @@
  *
  * @author Daniel Kinzler
  */
-class SiteImporterTest extends PHPUnit_Framework_TestCase {
+class SiteImporterTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
index c411ed8..8c84ce5 100644 (file)
@@ -27,7 +27,7 @@
  *
  * @author Katie Filbert < aude.wiki@gmail.com >
  */
-class SitesCacheFileBuilderTest extends PHPUnit_Framework_TestCase {
+class SitesCacheFileBuilderTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
index e07e425..cfaf49b 100644 (file)
@@ -4,12 +4,11 @@ namespace MediaWiki\Sparql;
 use Http;
 use MediaWiki\Http\HttpRequestFactory;
 use MWHttpRequest;
-use PHPUnit_Framework_TestCase;
 
 /**
  * @covers \MediaWiki\Sparql\SparqlClient
  */
-class SparqlClientTest extends PHPUnit_Framework_TestCase {
+class SparqlClientTest extends \PHPUnit\Framework\TestCase {
 
        private function getRequestFactory( $request ) {
                $requestFactory = $this->getMock( HttpRequestFactory::class );
index 9b81d6d..aac25d8 100644 (file)
@@ -199,10 +199,15 @@ class ChangesListSpecialPageTest extends AbstractChangesListSpecialPageTestCase
        }
 
        public function testRcHidemyselfFilter() {
+               $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', MIGRATION_WRITE_BOTH );
+               $this->overrideMwServices();
+
                $user = $this->getTestUser()->getUser();
+               $user->getActorId( wfGetDB( DB_MASTER ) );
                $this->assertConditions(
                        [ # expected
-                               "rc_user_text != '{$user->getName()}'",
+                               "NOT((rc_actor = '{$user->getActorId()}') OR "
+                                       . "(rc_actor = '0' AND rc_user = '{$user->getId()}'))",
                        ],
                        [
                                'hidemyself' => 1,
@@ -212,9 +217,10 @@ class ChangesListSpecialPageTest extends AbstractChangesListSpecialPageTestCase
                );
 
                $user = User::newFromName( '10.11.12.13', false );
+               $id = $user->getActorId( wfGetDB( DB_MASTER ) );
                $this->assertConditions(
                        [ # expected
-                               "rc_user_text != '10.11.12.13'",
+                               "NOT((rc_actor = '$id') OR (rc_actor = '0' AND rc_user_text = '10.11.12.13'))",
                        ],
                        [
                                'hidemyself' => 1,
@@ -225,10 +231,15 @@ class ChangesListSpecialPageTest extends AbstractChangesListSpecialPageTestCase
        }
 
        public function testRcHidebyothersFilter() {
+               $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', MIGRATION_WRITE_BOTH );
+               $this->overrideMwServices();
+
                $user = $this->getTestUser()->getUser();
+               $user->getActorId( wfGetDB( DB_MASTER ) );
                $this->assertConditions(
                        [ # expected
-                               "rc_user_text = '{$user->getName()}'",
+                               "(rc_actor = '{$user->getActorId()}') OR "
+                               . "(rc_actor = '0' AND rc_user_text = '{$user->getName()}')",
                        ],
                        [
                                'hidebyothers' => 1,
@@ -238,9 +249,10 @@ class ChangesListSpecialPageTest extends AbstractChangesListSpecialPageTestCase
                );
 
                $user = User::newFromName( '10.11.12.13', false );
+               $id = $user->getActorId( wfGetDB( DB_MASTER ) );
                $this->assertConditions(
                        [ # expected
-                               "rc_user_text = '10.11.12.13'",
+                               "(rc_actor = '$id') OR (rc_actor = '0' AND rc_user_text = '10.11.12.13')",
                        ],
                        [
                                'hidebyothers' => 1,
@@ -428,10 +440,13 @@ class ChangesListSpecialPageTest extends AbstractChangesListSpecialPageTestCase
        }
 
        public function testFilterUserExpLevelAllExperienceLevels() {
+               $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', MIGRATION_WRITE_BOTH );
+               $this->overrideMwServices();
+
                $this->assertConditions(
                        [
                                # expected
-                               'rc_user != 0',
+                               'COALESCE( actor_rc_user.actor_user, rc_user ) != 0',
                        ],
                        [
                                'userExpLevel' => 'newcomer;learner;experienced',
@@ -441,10 +456,13 @@ class ChangesListSpecialPageTest extends AbstractChangesListSpecialPageTestCase
        }
 
        public function testFilterUserExpLevelRegistrered() {
+               $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', MIGRATION_WRITE_BOTH );
+               $this->overrideMwServices();
+
                $this->assertConditions(
                        [
                                # expected
-                               'rc_user != 0',
+                               'COALESCE( actor_rc_user.actor_user, rc_user ) != 0',
                        ],
                        [
                                'userExpLevel' => 'registered',
@@ -454,10 +472,13 @@ class ChangesListSpecialPageTest extends AbstractChangesListSpecialPageTestCase
        }
 
        public function testFilterUserExpLevelUnregistrered() {
+               $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', MIGRATION_WRITE_BOTH );
+               $this->overrideMwServices();
+
                $this->assertConditions(
                        [
                                # expected
-                               'rc_user' => 0,
+                               'COALESCE( actor_rc_user.actor_user, rc_user ) = 0',
                        ],
                        [
                                'userExpLevel' => 'unregistered',
@@ -467,10 +488,13 @@ class ChangesListSpecialPageTest extends AbstractChangesListSpecialPageTestCase
        }
 
        public function testFilterUserExpLevelRegistreredOrLearner() {
+               $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', MIGRATION_WRITE_BOTH );
+               $this->overrideMwServices();
+
                $this->assertConditions(
                        [
                                # expected
-                               'rc_user != 0',
+                               'COALESCE( actor_rc_user.actor_user, rc_user ) != 0',
                        ],
                        [
                                'userExpLevel' => 'registered;learner',
@@ -480,10 +504,14 @@ class ChangesListSpecialPageTest extends AbstractChangesListSpecialPageTestCase
        }
 
        public function testFilterUserExpLevelUnregistreredOrExperienced() {
+               $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', MIGRATION_WRITE_BOTH );
+               $this->overrideMwServices();
+
                $conds = $this->buildQuery( [ 'userExpLevel' => 'unregistered;experienced' ] );
 
                $this->assertRegExp(
-                       '/\(rc_user = 0\) OR \(\(user_editcount >= 500\) AND \(user_registration <= \'[^\']+\'\)\)/',
+                       '/\(COALESCE\( actor_rc_user.actor_user, rc_user \) = 0\) OR '
+                               . '\(\(user_editcount >= 500\) AND \(user_registration <= \'[^\']+\'\)\)/',
                        reset( $conds ),
                        "rc conditions: userExpLevel=unregistered;experienced"
                );
@@ -595,8 +623,10 @@ class ChangesListSpecialPageTest extends AbstractChangesListSpecialPageTestCase
                        ]
                );
 
+               // @todo: This is not at all safe or sane. It just blindly assumes
+               // nothing in $conds depends on any other tables.
                $result = wfGetDB( DB_MASTER )->select(
-                       $tables,
+                       'user',
                        'user_name',
                        array_filter( $conds ) + [ 'user_email' => 'ut' ]
                );
index f95e387..4862747 100644 (file)
@@ -4,6 +4,9 @@
  * @group Database
  */
 class UserGroupMembershipTest extends MediaWikiTestCase {
+
+       protected $tablesUsed = [ 'user', 'user_groups' ];
+
        /**
         * @var User Belongs to no groups
         */
index 4c1a5fd..c225ba5 100644 (file)
@@ -21,7 +21,9 @@ class UserTest extends MediaWikiTestCase {
                $this->setMwGlobals( [
                        'wgGroupPermissions' => [],
                        'wgRevokePermissions' => [],
+                       'wgActorTableSchemaMigrationStage' => MIGRATION_WRITE_BOTH,
                ] );
+               $this->overrideMwServices();
 
                $this->setUpPermissionGlobals();
 
@@ -617,6 +619,7 @@ class UserTest extends MediaWikiTestCase {
                        'enableAutoblock' => true,
                        'expiry' => wfTimestamp( TS_MW, $expiryFiveHours ),
                ] );
+               $block->setBlocker( $this->getTestSysop()->getUser() );
                $block->setTarget( $user1tmp );
                $block->setBlocker( $userBlocker );
                $res = $block->insert();
@@ -694,6 +697,7 @@ class UserTest extends MediaWikiTestCase {
                $request1 = new FauxRequest();
                $request1->getSession()->setUser( $testUser );
                $block = new Block( [ 'enableAutoblock' => true ] );
+               $block->setBlocker( $this->getTestSysop()->getUser() );
                $block->setTarget( $testUser );
                $block->setBlocker( $userBlocker );
                $res = $block->insert();
@@ -739,6 +743,7 @@ class UserTest extends MediaWikiTestCase {
                $request1 = new FauxRequest();
                $request1->getSession()->setUser( $user1Tmp );
                $block = new Block( [ 'enableAutoblock' => true, 'expiry' => 'infinity' ] );
+               $block->setBlocker( $this->getTestSysop()->getUser() );
                $block->setTarget( $user1Tmp );
                $block->setBlocker( $userBlocker );
                $res = $block->insert();
@@ -834,6 +839,7 @@ class UserTest extends MediaWikiTestCase {
                $request1 = new FauxRequest();
                $request1->getSession()->setUser( $user1tmp );
                $block = new Block( [ 'enableAutoblock' => true ] );
+               $block->setBlocker( $this->getTestSysop()->getUser() );
                $block->setTarget( $user1tmp );
                $block->setBlocker( $userBlocker );
                $res = $block->insert();
@@ -879,6 +885,7 @@ class UserTest extends MediaWikiTestCase {
                $request1 = new FauxRequest();
                $request1->getSession()->setUser( $user1tmp );
                $block = new Block( [ 'enableAutoblock' => true ] );
+               $block->setBlocker( $this->getTestSysop()->getUser() );
                $block->setTarget( $user1tmp );
                $block->setBlocker( $userBlocker );
                $res = $block->insert();
@@ -956,20 +963,18 @@ class UserTest extends MediaWikiTestCase {
                ] );
 
                $db = wfGetDB( DB_MASTER );
-
-               $data = new stdClass();
-               $data->user_id = 1;
-               $data->user_name = 'name';
-               $data->user_real_name = 'Real Name';
-               $data->user_touched = 1;
-               $data->user_token = 'token';
-               $data->user_email = 'a@a.a';
-               $data->user_email_authenticated = null;
-               $data->user_email_token = 'token';
-               $data->user_email_token_expires = null;
-               $data->user_editcount = $editCount;
-               $data->user_registration = $db->timestamp( time() - $memberSince * 86400 );
-               $user = User::newFromRow( $data );
+               $userQuery = User::getQueryInfo();
+               $row = $db->selectRow(
+                       $userQuery['tables'],
+                       $userQuery['fields'],
+                       [ 'user_id' => $this->getTestUser()->getUser()->getId() ],
+                       __METHOD__,
+                       [],
+                       $userQuery['joins']
+               );
+               $row->user_editcount = $editCount;
+               $row->user_registration = $db->timestamp( time() - $memberSince * 86400 );
+               $user = User::newFromRow( $row );
 
                $this->assertEquals( $expLevel, $user->getExperienceLevel() );
        }
@@ -1028,4 +1033,113 @@ class UserTest extends MediaWikiTestCase {
                );
                $this->assertTrue( User::isLocallyBlockedProxy( $ip ) );
        }
+
+       public function testActorId() {
+               $this->hideDeprecated( 'User::selectFields' );
+
+               // Newly-created user has an actor ID
+               $user = User::createNew( 'UserTestActorId1' );
+               $id = $user->getId();
+               $this->assertTrue( $user->getActorId() > 0, 'User::createNew sets an actor ID' );
+
+               $user = User::newFromName( 'UserTestActorId2' );
+               $user->addToDatabase();
+               $this->assertTrue( $user->getActorId() > 0, 'User::addToDatabase sets an actor ID' );
+
+               $user = User::newFromName( 'UserTestActorId1' );
+               $this->assertTrue( $user->getActorId() > 0, 'Actor ID can be retrieved for user loaded by name' );
+
+               $user = User::newFromId( $id );
+               $this->assertTrue( $user->getActorId() > 0, 'Actor ID can be retrieved for user loaded by ID' );
+
+               $user2 = User::newFromActorId( $user->getActorId() );
+               $this->assertEquals( $user->getId(), $user2->getId(),
+                       'User::newFromActorId works for an existing user' );
+
+               $row = $this->db->selectRow( 'user', User::selectFields(), [ 'user_id' => $id ], __METHOD__ );
+               $user = User::newFromRow( $row );
+               $this->assertTrue( $user->getActorId() > 0,
+                       'Actor ID can be retrieved for user loaded with User::selectFields()' );
+
+               $this->db->delete( 'actor', [ 'actor_user' => $id ], __METHOD__ );
+               User::purge( wfWikiId(), $id );
+               // Because WANObjectCache->delete() stupidly doesn't delete from the process cache.
+               ObjectCache::getMainWANInstance()->clearProcessCache();
+
+               $user = User::newFromId( $id );
+               $this->assertFalse( $user->getActorId() > 0, 'No Actor ID by default if none in database' );
+               $this->assertTrue( $user->getActorId( $this->db ) > 0, 'Actor ID can be created if none in db' );
+
+               $user->setName( 'UserTestActorId4-renamed' );
+               $user->saveSettings();
+               $this->assertEquals(
+                       $user->getName(),
+                       $this->db->selectField(
+                               'actor', 'actor_name', [ 'actor_id' => $user->getActorId() ], __METHOD__
+                       ),
+                       'User::saveSettings updates actor table for name change'
+               );
+
+               // For sanity
+               $ip = '192.168.12.34';
+               $this->db->delete( 'actor', [ 'actor_name' => $ip ], __METHOD__ );
+
+               $user = User::newFromName( $ip, false );
+               $this->assertFalse( $user->getActorId() > 0, 'Anonymous user has no actor ID by default' );
+               $this->assertTrue( $user->getActorId( $this->db ) > 0,
+                       'Actor ID can be created for an anonymous user' );
+
+               $user = User::newFromName( $ip, false );
+               $this->assertTrue( $user->getActorId() > 0, 'Actor ID can be loaded for an anonymous user' );
+               $user2 = User::newFromActorId( $user->getActorId() );
+               $this->assertEquals( $user->getName(), $user2->getName(),
+                       'User::newFromActorId works for an anonymous user' );
+       }
+
+       public function testNewFromAnyId() {
+               // Registered user
+               $user = $this->getTestUser()->getUser();
+               for ( $i = 1; $i <= 7; $i++ ) {
+                       $test = User::newFromAnyId(
+                               ( $i & 1 ) ? $user->getId() : null,
+                               ( $i & 2 ) ? $user->getName() : null,
+                               ( $i & 4 ) ? $user->getActorId() : null
+                       );
+                       $this->assertSame( $user->getId(), $test->getId() );
+                       $this->assertSame( $user->getName(), $test->getName() );
+                       $this->assertSame( $user->getActorId(), $test->getActorId() );
+               }
+
+               // Anon user. Can't load by only user ID when that's 0.
+               $user = User::newFromName( '192.168.12.34', false );
+               $user->getActorId( $this->db ); // Make sure an actor ID exists
+
+               $test = User::newFromAnyId( null, '192.168.12.34', null );
+               $this->assertSame( $user->getId(), $test->getId() );
+               $this->assertSame( $user->getName(), $test->getName() );
+               $this->assertSame( $user->getActorId(), $test->getActorId() );
+               $test = User::newFromAnyId( null, null, $user->getActorId() );
+               $this->assertSame( $user->getId(), $test->getId() );
+               $this->assertSame( $user->getName(), $test->getName() );
+               $this->assertSame( $user->getActorId(), $test->getActorId() );
+
+               // Bogus data should still "work" as long as nothing triggers a ->load(),
+               // and accessing the specified data shouldn't do that.
+               $test = User::newFromAnyId( 123456, 'Bogus', 654321 );
+               $this->assertSame( 123456, $test->getId() );
+               $this->assertSame( 'Bogus', $test->getName() );
+               $this->assertSame( 654321, $test->getActorId() );
+
+               // Exceptional cases
+               try {
+                       User::newFromAnyId( null, null, null );
+                       $this->fail( 'Expected exception not thrown' );
+               } catch ( InvalidArgumentException $ex ) {
+               }
+               try {
+                       User::newFromAnyId( 0, null, 0 );
+                       $this->fail( 'Expected exception not thrown' );
+               } catch ( InvalidArgumentException $ex ) {
+               }
+       }
 }
index caeeaab..12d2574 100644 (file)
@@ -12,7 +12,7 @@
 /**
  * @covers AvroValidator
  */
-class AvroValidatorTest extends PHPUnit_Framework_TestCase {
+class AvroValidatorTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
index 796d459..9e5163f 100644 (file)
@@ -3,7 +3,7 @@
 /**
  * @covers ClassCollector
  */
-class ClassCollectorTest extends PHPUnit_Framework_TestCase {
+class ClassCollectorTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
index b3885bd..316d9f4 100644 (file)
@@ -3,7 +3,7 @@
 /**
  * @covers FileContentsHasherTest
  */
-class FileContentsHasherTest extends PHPUnit_Framework_TestCase {
+class FileContentsHasherTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
index 66c8d00..94705bf 100644 (file)
@@ -5,7 +5,7 @@
  *
  * @covers MWCryptHash
  */
-class MWCryptHashTest extends PHPUnit_Framework_TestCase {
+class MWCryptHashTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
index c411e53..abdfbb1 100644 (file)
@@ -1,5 +1,5 @@
 <?php
-class MWRestrictionsTest extends PHPUnit_Framework_TestCase {
+class MWRestrictionsTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
index c6b8bdd..d335a93 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 
-class UIDGeneratorTest extends PHPUnit_Framework_TestCase {
+class UIDGeneratorTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
index 09a0676..9f18e5a 100644 (file)
@@ -4,7 +4,7 @@
  * @covers ZipDirectoryReader
  * NOTE: this test is more like an integration test than a unit test
  */
-class ZipDirectoryReaderTest extends PHPUnit_Framework_TestCase {
+class ZipDirectoryReaderTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
index 5e36e94..be51626 100644 (file)
@@ -30,6 +30,40 @@ class WatchedItemQueryServiceUnitTest extends MediaWikiTestCase {
                return $mockStore;
        }
 
+       /**
+        * @return PHPUnit_Framework_MockObject_MockObject|ActorMigration
+        */
+       private function getMockActorMigration() {
+               $mockStore = $this->getMockBuilder( ActorMigration::class )
+                       ->disableOriginalConstructor()
+                       ->getMock();
+               $mockStore->expects( $this->any() )
+                       ->method( 'getJoin' )
+                       ->willReturn( [
+                               'tables' => [ 'actormigration' => 'table' ],
+                               'fields' => [
+                                       'rc_user' => 'actormigration_user',
+                                       'rc_user_text' => 'actormigration_user_text',
+                                       'rc_actor' => 'actormigration_actor',
+                               ],
+                               'joins' => [ 'actormigration' => 'join' ],
+                       ] );
+               $mockStore->expects( $this->any() )
+                       ->method( 'getWhere' )
+                       ->willReturn( [
+                               'tables' => [ 'actormigration' => 'table' ],
+                               'conds' => 'actormigration_conds',
+                               'joins' => [ 'actormigration' => 'join' ],
+                       ] );
+               $mockStore->expects( $this->any() )
+                       ->method( 'isAnon' )
+                       ->willReturn( 'actormigration is anon' );
+               $mockStore->expects( $this->any() )
+                       ->method( 'isNotAnon' )
+                       ->willReturn( 'actormigration is not anon' );
+               return $mockStore;
+       }
+
        /**
         * @param PHPUnit_Framework_MockObject_MockObject|Database $mockDb
         * @return WatchedItemQueryService
@@ -37,7 +71,8 @@ class WatchedItemQueryServiceUnitTest extends MediaWikiTestCase {
        private function newService( $mockDb ) {
                return new WatchedItemQueryService(
                        $this->getMockLoadBalancer( $mockDb ),
-                       $this->getMockCommentStore()
+                       $this->getMockCommentStore(),
+                       $this->getMockActorMigration()
                );
        }
 
@@ -57,10 +92,17 @@ class WatchedItemQueryServiceUnitTest extends MediaWikiTestCase {
                        )
                        ->will( $this->returnCallback( function ( $a, $conj ) {
                                $sqlConj = $conj === LIST_AND ? ' AND ' : ' OR ';
-                               return join( $sqlConj, array_map( function ( $s ) {
-                                       return '(' . $s . ')';
-                               }, $a
-                               ) );
+                               $conds = [];
+                               foreach ( $a as $k => $v ) {
+                                       if ( is_int( $k ) ) {
+                                               $conds[] = "($v)";
+                                       } elseif ( is_array( $v ) ) {
+                                               $conds[] = "($k IN ('" . implode( "','", $v ) . "'))";
+                                       } else {
+                                               $conds[] = "($k = '$v')";
+                                       }
+                               }
+                               return implode( $sqlConj, $conds );
                        } ) );
 
                $mock->expects( $this->any() )
@@ -490,20 +532,20 @@ class WatchedItemQueryServiceUnitTest extends MediaWikiTestCase {
                        [
                                [ 'includeFields' => [ WatchedItemQueryService::INCLUDE_USER ] ],
                                null,
-                               [],
-                               [ 'rc_user_text' ],
-                               [],
+                               [ 'actormigration' => 'table' ],
+                               [ 'rc_user_text' => 'actormigration_user_text' ],
                                [],
                                [],
+                               [ 'actormigration' => 'join' ],
                        ],
                        [
                                [ 'includeFields' => [ WatchedItemQueryService::INCLUDE_USER_ID ] ],
                                null,
-                               [],
-                               [ 'rc_user' ],
-                               [],
+                               [ 'actormigration' => 'table' ],
+                               [ 'rc_user' => 'actormigration_user' ],
                                [],
                                [],
+                               [ 'actormigration' => 'join' ],
                        ],
                        [
                                [ 'includeFields' => [ WatchedItemQueryService::INCLUDE_COMMENT ] ],
@@ -705,20 +747,20 @@ class WatchedItemQueryServiceUnitTest extends MediaWikiTestCase {
                        [
                                [ 'filters' => [ WatchedItemQueryService::FILTER_ANON ] ],
                                null,
+                               [ 'actormigration' => 'table' ],
                                [],
+                               [ 'actormigration is anon' ],
                                [],
-                               [ 'rc_user = 0' ],
-                               [],
-                               [],
+                               [ 'actormigration' => 'join' ],
                        ],
                        [
                                [ 'filters' => [ WatchedItemQueryService::FILTER_NOT_ANON ] ],
                                null,
+                               [ 'actormigration' => 'table' ],
                                [],
+                               [ 'actormigration is not anon' ],
                                [],
-                               [ 'rc_user != 0' ],
-                               [],
-                               [],
+                               [ 'actormigration' => 'join' ],
                        ],
                        [
                                [ 'filters' => [ WatchedItemQueryService::FILTER_PATROLLED ] ],
@@ -759,20 +801,20 @@ class WatchedItemQueryServiceUnitTest extends MediaWikiTestCase {
                        [
                                [ 'onlyByUser' => 'SomeOtherUser' ],
                                null,
+                               [ 'actormigration' => 'table' ],
                                [],
+                               [ 'actormigration_conds' ],
                                [],
-                               [ 'rc_user_text' => 'SomeOtherUser' ],
-                               [],
-                               [],
+                               [ 'actormigration' => 'join' ],
                        ],
                        [
                                [ 'notByUser' => 'SomeOtherUser' ],
                                null,
+                               [ 'actormigration' => 'table' ],
                                [],
+                               [ 'NOT(actormigration_conds)' ],
                                [],
-                               [ "rc_user_text != 'SomeOtherUser'" ],
-                               [],
-                               [],
+                               [ 'actormigration' => 'join' ],
                        ],
                        [
                                [ 'dir' => WatchedItemQueryService::DIR_OLDER ],
@@ -984,62 +1026,74 @@ class WatchedItemQueryServiceUnitTest extends MediaWikiTestCase {
                        [
                                [],
                                'deletedhistory',
+                               [],
                                [
                                        '(rc_type != ' . RC_LOG . ') OR ((rc_deleted & ' . LogPage::DELETED_ACTION . ') != ' .
                                                LogPage::DELETED_ACTION . ')'
                                ],
+                               [],
                        ],
                        [
                                [],
                                'suppressrevision',
+                               [],
                                [
                                        '(rc_type != ' . RC_LOG . ') OR (' .
                                                '(rc_deleted & ' . ( LogPage::DELETED_ACTION | LogPage::DELETED_RESTRICTED ) . ') != ' .
                                                ( LogPage::DELETED_ACTION | LogPage::DELETED_RESTRICTED ) . ')'
                                ],
+                               [],
                        ],
                        [
                                [],
                                'viewsuppressed',
+                               [],
                                [
                                        '(rc_type != ' . RC_LOG . ') OR (' .
                                                '(rc_deleted & ' . ( LogPage::DELETED_ACTION | LogPage::DELETED_RESTRICTED ) . ') != ' .
                                                ( LogPage::DELETED_ACTION | LogPage::DELETED_RESTRICTED ) . ')'
                                ],
+                               [],
                        ],
                        [
                                [ 'onlyByUser' => 'SomeOtherUser' ],
                                'deletedhistory',
+                               [ 'actormigration' => 'table' ],
                                [
-                                       'rc_user_text' => 'SomeOtherUser',
+                                       'actormigration_conds',
                                        '(rc_deleted & ' . Revision::DELETED_USER . ') != ' . Revision::DELETED_USER,
                                        '(rc_type != ' . RC_LOG . ') OR ((rc_deleted & ' . LogPage::DELETED_ACTION . ') != ' .
                                                LogPage::DELETED_ACTION . ')'
                                ],
+                               [ 'actormigration' => 'join' ],
                        ],
                        [
                                [ 'onlyByUser' => 'SomeOtherUser' ],
                                'suppressrevision',
+                               [ 'actormigration' => 'table' ],
                                [
-                                       'rc_user_text' => 'SomeOtherUser',
+                                       'actormigration_conds',
                                        '(rc_deleted & ' . ( Revision::DELETED_USER | Revision::DELETED_RESTRICTED ) . ') != ' .
                                                ( Revision::DELETED_USER | Revision::DELETED_RESTRICTED ),
                                        '(rc_type != ' . RC_LOG . ') OR (' .
                                                '(rc_deleted & ' . ( LogPage::DELETED_ACTION | LogPage::DELETED_RESTRICTED ) . ') != ' .
                                                ( LogPage::DELETED_ACTION | LogPage::DELETED_RESTRICTED ) . ')'
                                ],
+                               [ 'actormigration' => 'join' ],
                        ],
                        [
                                [ 'onlyByUser' => 'SomeOtherUser' ],
                                'viewsuppressed',
+                               [ 'actormigration' => 'table' ],
                                [
-                                       'rc_user_text' => 'SomeOtherUser',
+                                       'actormigration_conds',
                                        '(rc_deleted & ' . ( Revision::DELETED_USER | Revision::DELETED_RESTRICTED ) . ') != ' .
                                                ( Revision::DELETED_USER | Revision::DELETED_RESTRICTED ),
                                        '(rc_type != ' . RC_LOG . ') OR (' .
                                                '(rc_deleted & ' . ( LogPage::DELETED_ACTION | LogPage::DELETED_RESTRICTED ) . ') != ' .
                                                ( LogPage::DELETED_ACTION | LogPage::DELETED_RESTRICTED ) . ')'
                                ],
+                               [ 'actormigration' => 'join' ],
                        ],
                ];
        }
@@ -1050,7 +1104,9 @@ class WatchedItemQueryServiceUnitTest extends MediaWikiTestCase {
        public function testGetWatchedItemsWithRecentChangeInfo_userPermissionRelatedExtraChecks(
                array $options,
                $notAllowedAction,
-               array $expectedExtraConds
+               array $expectedExtraTables,
+               array $expectedExtraConds,
+               array $expectedExtraJoins
        ) {
                $commonConds = [ 'wl_user' => 1, '(rc_this_oldid=page_latest) OR (rc_type=3)' ];
                $conds = array_merge( $commonConds, $expectedExtraConds );
@@ -1059,12 +1115,15 @@ class WatchedItemQueryServiceUnitTest extends MediaWikiTestCase {
                $mockDb->expects( $this->once() )
                        ->method( 'select' )
                        ->with(
-                               [ 'recentchanges', 'watchlist', 'page' ],
+                               array_merge( [ 'recentchanges', 'watchlist', 'page' ], $expectedExtraTables ),
                                $this->isType( 'array' ),
                                $conds,
                                $this->isType( 'string' ),
                                $this->isType( 'array' ),
-                               $this->isType( 'array' )
+                               array_merge( [
+                                       'watchlist' => [ 'INNER JOIN', [ 'wl_namespace=rc_namespace', 'wl_title=rc_title' ] ],
+                                       'page' => [ 'LEFT JOIN', 'rc_cur_id=page_id' ],
+                               ], $expectedExtraJoins )
                        )
                        ->will( $this->returnValue( [] ) );
 
@@ -1572,7 +1631,7 @@ class WatchedItemQueryServiceUnitTest extends MediaWikiTestCase {
                        )
                        ->will( $this->returnCallback( function ( $a, $conj ) {
                                $sqlConj = $conj === LIST_AND ? ' AND ' : ' OR ';
-                               return join( $sqlConj, array_map( function ( $s ) {
+                               return implode( $sqlConj, array_map( function ( $s ) {
                                        return '(' . $s . ')';
                                }, $a
                                ) );
index 52e653c..9485170 100644 (file)
@@ -443,7 +443,7 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
                        )
                        ->will( $this->returnCallback( function ( $a, $conj ) {
                                $sqlConj = $conj === LIST_AND ? ' AND ' : ' OR ';
-                               return join( $sqlConj, array_map( function ( $s ) {
+                               return implode( $sqlConj, array_map( function ( $s ) {
                                        return '(' . $s . ')';
                                }, $a
                                ) );
@@ -540,7 +540,7 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
                        )
                        ->will( $this->returnCallback( function ( $a, $conj ) {
                                $sqlConj = $conj === LIST_AND ? ' AND ' : ' OR ';
-                               return join( $sqlConj, array_map( function ( $s ) {
+                               return implode( $sqlConj, array_map( function ( $s ) {
                                        return '(' . $s . ')';
                                }, $a
                                ) );
index d77291a..544a063 100644 (file)
@@ -6,7 +6,7 @@
  *
  * @author Thiemo Kreuz
  */
-class LanguageCodeTest extends PHPUnit_Framework_TestCase {
+class LanguageCodeTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
index 5cb5602..050ed83 100644 (file)
@@ -209,70 +209,104 @@ class LanguageTest extends LanguageClassesTestCase {
        }
 
        /**
-        * @covers Language::truncate
+        * @covers Language::truncateForDatabase
+        * @covers Language::truncateInternal
         */
-       public function testTruncate() {
+       public function testTruncateForDatabase() {
                $this->assertEquals(
                        "XXX",
-                       $this->getLang()->truncate( "1234567890", 0, 'XXX' ),
+                       $this->getLang()->truncateForDatabase( "1234567890", 0, 'XXX' ),
                        'truncate prefix, len 0, small ellipsis'
                );
 
                $this->assertEquals(
                        "12345XXX",
-                       $this->getLang()->truncate( "1234567890", 8, 'XXX' ),
+                       $this->getLang()->truncateForDatabase( "1234567890", 8, 'XXX' ),
                        'truncate prefix, small ellipsis'
                );
 
                $this->assertEquals(
                        "123456789",
-                       $this->getLang()->truncate( "123456789", 5, 'XXXXXXXXXXXXXXX' ),
+                       $this->getLang()->truncateForDatabase( "123456789", 5, 'XXXXXXXXXXXXXXX' ),
                        'truncate prefix, large ellipsis'
                );
 
                $this->assertEquals(
                        "XXX67890",
-                       $this->getLang()->truncate( "1234567890", -8, 'XXX' ),
+                       $this->getLang()->truncateForDatabase( "1234567890", -8, 'XXX' ),
                        'truncate suffix, small ellipsis'
                );
 
                $this->assertEquals(
                        "123456789",
-                       $this->getLang()->truncate( "123456789", -5, 'XXXXXXXXXXXXXXX' ),
+                       $this->getLang()->truncateForDatabase( "123456789", -5, 'XXXXXXXXXXXXXXX' ),
                        'truncate suffix, large ellipsis'
                );
                $this->assertEquals(
                        "123XXX",
-                       $this->getLang()->truncate( "123                ", 9, 'XXX' ),
+                       $this->getLang()->truncateForDatabase( "123                ", 9, 'XXX' ),
                        'truncate prefix, with spaces'
                );
                $this->assertEquals(
                        "12345XXX",
-                       $this->getLang()->truncate( "12345            8", 11, 'XXX' ),
+                       $this->getLang()->truncateForDatabase( "12345            8", 11, 'XXX' ),
                        'truncate prefix, with spaces and non-space ending'
                );
                $this->assertEquals(
                        "XXX234",
-                       $this->getLang()->truncate( "1              234", -8, 'XXX' ),
+                       $this->getLang()->truncateForDatabase( "1              234", -8, 'XXX' ),
                        'truncate suffix, with spaces'
                );
                $this->assertEquals(
                        "12345XXX",
-                       $this->getLang()->truncate( "1234567890", 5, 'XXX', false ),
+                       $this->getLang()->truncateForDatabase( "1234567890", 5, 'XXX', false ),
                        'truncate without adjustment'
                );
                $this->assertEquals(
                        "泰乐菌...",
-                       $this->getLang()->truncate( "泰乐菌素123456789", 11, '...', false ),
+                       $this->getLang()->truncateForDatabase( "泰乐菌素123456789", 11, '...', false ),
                        'truncate does not chop Unicode characters in half'
                );
                $this->assertEquals(
                        "\n泰乐菌...",
-                       $this->getLang()->truncate( "\n泰乐菌素123456789", 12, '...', false ),
+                       $this->getLang()->truncateForDatabase( "\n泰乐菌素123456789", 12, '...', false ),
                        'truncate does not chop Unicode characters in half if there is a preceding newline'
                );
        }
 
+       /**
+        * @dataProvider provideTruncateData
+        * @covers Language::truncateForVisual
+        * @covers Language::truncateInternal
+        */
+       public function testTruncateForVisual(
+               $expected, $string, $length, $ellipsis = '...', $adjustLength = true
+       ) {
+               $this->assertEquals(
+                       $expected,
+                       $this->getLang()->truncateForVisual( $string, $length, $ellipsis, $adjustLength )
+               );
+       }
+
+       /**
+        * @return array Format is ($expected, $string, $length, $ellipsis, $adjustLength)
+        */
+       public static function provideTruncateData() {
+               return [
+                       [ "XXX", "тестирам да ли ради", 0, "XXX" ],
+                       [ "testnXXX", "testni scenarij", 8, "XXX" ],
+                       [ "حالة اختبار", "حالة اختبار", 5, "XXXXXXXXXXXXXXX" ],
+                       [ "XXXедент", "прецедент", -8, "XXX" ],
+                       [ "XXപിൾ", "ആപ്പിൾ", -5, "XX" ],
+                       [ "神秘XXX", "神秘                ", 9, "XXX" ],
+                       [ "ΔημιουργXXX", "Δημιουργία           Σύμπαντος", 11, "XXX" ],
+                       [ "XXXの家です", "地球は私たちの唯               の家です", -8, "XXX" ],
+                       [ "زندگیXXX", "زندگی زیباست", 6, "XXX", false ],
+                       [ "ცხოვრება...", "ცხოვრება არის საოცარი", 8, "...", false ],
+                       [ "\nທ່ານ...", "\nທ່ານບໍ່ຮູ້ຫນັງສື", 5, "...", false ],
+               ];
+       }
+
        /**
         * @dataProvider provideHTMLTruncateData
         * @covers Language::truncateHTML
index 4a7fed2..0bb6a4d 100644 (file)
@@ -25,7 +25,7 @@ class SpecialPageAliasTest extends MediaWikiTestCase {
        }
 
        public function validSpecialPageAliasesProvider() {
-               $codes = array_keys( Language::fetchLanguageNames( 'mwfile' ) );
+               $codes = array_keys( Language::fetchLanguageNames( null, 'mwfile' ) );
 
                $data = [];
 
index 0b8d0d9..c15d789 100644 (file)
@@ -4,13 +4,12 @@ namespace MediaWiki\Tests\Maintenance;
 
 use Benchmarker;
 use MediaWikiCoversValidator;
-use PHPUnit_Framework_TestCase;
 use Wikimedia\TestingAccessWrapper;
 
 /**
  * @covers Benchmarker
  */
-class BenchmarkerTest extends PHPUnit_Framework_TestCase {
+class BenchmarkerTest extends \PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
index 14dfb44..97e0c88 100644 (file)
@@ -215,7 +215,7 @@ class FetchTextTest extends MediaWikiTestCase {
 
        function testExistingSeveral() {
                $this->assertFilter(
-                       join( "\n", [
+                       implode( "\n", [
                                self::$textId1,
                                self::$textId5,
                                self::$textId3,
index a78bd82..fa249b2 100755 (executable)
@@ -113,7 +113,7 @@ class PHPUnitMaintClass extends Maintenance {
                        }
                }
 
-               if ( !class_exists( 'PHPUnit_Framework_TestCase' ) ) {
+               if ( !class_exists( 'PHPUnit\\Framework\\TestCase' ) ) {
                        echo "PHPUnit not found. Please install it and other dev dependencies by
                running `composer install` in MediaWiki root directory.\n";
                        exit( 1 );
index 4ab0c2c..6c2ff02 100644 (file)
@@ -6,7 +6,7 @@
  *
  * @author Marius Hoch < hoo@online.de >
  */
-class AvailableRightsTest extends PHPUnit_Framework_TestCase {
+class AvailableRightsTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
index 0cf4659..60c97cc 100644 (file)
@@ -20,7 +20,7 @@
  * Validates all loaded extensions and skins using the ExtensionRegistry
  * against the extension.json schema in the docs/ folder.
  */
-class ExtensionJsonValidationTest extends PHPUnit_Framework_TestCase {
+class ExtensionJsonValidationTest extends PHPUnit\Framework\TestCase {
 
        use MediaWikiCoversValidator;
 
index 9016cb7..4df791e 100644 (file)
@@ -25,6 +25,8 @@ class StructureTest extends MediaWikiTestCase {
                        'MediaWikiTestCase',
                        'ResourceLoaderTestCase',
                        'PHPUnit_Framework_TestCase',
+                       '\\?PHPUnit\\Framework\\TestCase',
+                       'TestCase', // \PHPUnit\Framework\TestCase with appropriate use statement
                        'DumpTestCase',
                ] );
                $testClassRegex = "^class .* extends ($testClassRegex)";
index 8390ab3..785e114 100644 (file)
@@ -45,13 +45,12 @@ return [
                'scripts' => [
                        'tests/qunit/suites/resources/startup.test.js',
                        'tests/qunit/suites/resources/jquery/jquery.accessKeyLabel.test.js',
-                       'tests/qunit/suites/resources/jquery/jquery.byteLength.test.js',
-                       'tests/qunit/suites/resources/jquery/jquery.byteLimit.test.js',
                        'tests/qunit/suites/resources/jquery/jquery.color.test.js',
                        'tests/qunit/suites/resources/jquery/jquery.colorUtil.test.js',
                        'tests/qunit/suites/resources/jquery/jquery.getAttrs.test.js',
                        'tests/qunit/suites/resources/jquery/jquery.hidpi.test.js',
                        'tests/qunit/suites/resources/jquery/jquery.highlightText.test.js',
+                       'tests/qunit/suites/resources/jquery/jquery.lengthLimit.test.js',
                        'tests/qunit/suites/resources/jquery/jquery.localize.test.js',
                        'tests/qunit/suites/resources/jquery/jquery.makeCollapsible.test.js',
                        'tests/qunit/suites/resources/jquery/jquery.tabIndex.test.js',
@@ -65,6 +64,8 @@ return [
                        'tests/qunit/suites/resources/mediawiki/mediawiki.jscompat.test.js',
                        'tests/qunit/suites/resources/mediawiki/mediawiki.messagePoster.factory.test.js',
                        'tests/qunit/suites/resources/mediawiki/mediawiki.RegExp.test.js',
+                       'tests/qunit/suites/resources/mediawiki/mediawiki.String.byteLength.test.js',
+                       'tests/qunit/suites/resources/mediawiki/mediawiki.String.trimByteLength.test.js',
                        'tests/qunit/suites/resources/mediawiki/mediawiki.storage.test.js',
                        'tests/qunit/suites/resources/mediawiki/mediawiki.template.test.js',
                        'tests/qunit/suites/resources/mediawiki/mediawiki.template.mustache.test.js',
@@ -102,13 +103,12 @@ return [
                ],
                'dependencies' => [
                        'jquery.accessKeyLabel',
-                       'jquery.byteLength',
-                       'jquery.byteLimit',
                        'jquery.color',
                        'jquery.colorUtil',
                        'jquery.getAttrs',
                        'jquery.hidpi',
                        'jquery.highlightText',
+                       'jquery.lengthLimit',
                        'jquery.localize',
                        'jquery.makeCollapsible',
                        'jquery.tabIndex',
@@ -125,6 +125,7 @@ return [
                        'mediawiki.jqueryMsg',
                        'mediawiki.messagePoster',
                        'mediawiki.RegExp',
+                       'mediawiki.String',
                        'mediawiki.storage',
                        'mediawiki.Title',
                        'mediawiki.toc',
diff --git a/tests/qunit/suites/resources/jquery/jquery.byteLength.test.js b/tests/qunit/suites/resources/jquery/jquery.byteLength.test.js
deleted file mode 100644 (file)
index 558e641..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-( function ( $ ) {
-       QUnit.module( 'jquery.byteLength', QUnit.newMwEnvironment() );
-
-       QUnit.test( 'Simple text', function ( assert ) {
-               var azLc = 'abcdefghijklmnopqrstuvwxyz',
-                       azUc = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
-                       num = '0123456789',
-                       x = '*',
-                       space = '   ';
-
-               assert.equal( $.byteLength( azLc ), 26, 'Lowercase a-z' );
-               assert.equal( $.byteLength( azUc ), 26, 'Uppercase A-Z' );
-               assert.equal( $.byteLength( num ), 10, 'Numbers 0-9' );
-               assert.equal( $.byteLength( x ), 1, 'An asterisk' );
-               assert.equal( $.byteLength( space ), 3, '3 spaces' );
-
-       } );
-
-       QUnit.test( 'Special text', function ( assert ) {
-               // https://en.wikipedia.org/wiki/UTF-8
-               var u0024 = '$',
-                       // Cent symbol
-                       u00A2 = '\u00A2',
-                       // Euro symbol
-                       u20AC = '\u20AC',
-                       // Character \U00024B62 (Han script) can't be represented in javascript as a single
-                       // code point, instead it is composed as a surrogate pair of two separate code units.
-                       // http://codepoints.net/U+24B62
-                       // http://www.fileformat.info/info/unicode/char/24B62/index.htm
-                       u024B62 = '\uD852\uDF62';
-
-               assert.strictEqual( $.byteLength( u0024 ), 1, 'U+0024' );
-               assert.strictEqual( $.byteLength( u00A2 ), 2, 'U+00A2' );
-               assert.strictEqual( $.byteLength( u20AC ), 3, 'U+20AC' );
-               assert.strictEqual( $.byteLength( u024B62 ), 4, 'U+024B62 (surrogate pair: \\uD852\\uDF62)' );
-       } );
-}( jQuery ) );
diff --git a/tests/qunit/suites/resources/jquery/jquery.byteLimit.test.js b/tests/qunit/suites/resources/jquery/jquery.byteLimit.test.js
deleted file mode 100644 (file)
index 8555a7e..0000000
+++ /dev/null
@@ -1,248 +0,0 @@
-( function ( $, mw ) {
-       var simpleSample, U_20AC, mbSample;
-
-       QUnit.module( 'jquery.byteLimit', QUnit.newMwEnvironment() );
-
-       // Simple sample (20 chars, 20 bytes)
-       simpleSample = '12345678901234567890';
-
-       // 3 bytes (euro-symbol)
-       U_20AC = '\u20AC';
-
-       // Multi-byte sample (22 chars, 26 bytes)
-       mbSample = '1234567890' + U_20AC + '1234567890' + U_20AC;
-
-       // Basic sendkey-implementation
-       function addChars( $input, charstr ) {
-               var c, len;
-
-               function x( $input, i ) {
-                       // Add character to the value
-                       return $input.val() + charstr.charAt( i );
-               }
-
-               for ( c = 0, len = charstr.length; c < len; c += 1 ) {
-                       $input
-                               .val( x( $input, c ) )
-                               .trigger( 'change' );
-               }
-       }
-
-       /**
-        * Test factory for $.fn.byteLimit
-        *
-        * @param {Object} options
-        * @param {string} options.description Test name
-        * @param {jQuery} options.$input jQuery object in an input element
-        * @param {string} options.sample Sequence of characters to simulate being
-        *  added one by one
-        * @param {string} options.expected Expected final value of `$input`
-        */
-       function byteLimitTest( options ) {
-               var opt = $.extend( {
-                       description: '',
-                       $input: null,
-                       sample: '',
-                       expected: ''
-               }, options );
-
-               QUnit.test( opt.description, function ( assert ) {
-                       opt.$input.appendTo( '#qunit-fixture' );
-
-                       // Simulate pressing keys for each of the sample characters
-                       addChars( opt.$input, opt.sample );
-
-                       assert.equal(
-                               opt.$input.val(),
-                               opt.expected,
-                               'New value matches the expected string'
-                       );
-               } );
-       }
-
-       byteLimitTest( {
-               description: 'Plain text input',
-               $input: $( '<input>' ).attr( 'type', 'text' ),
-               sample: simpleSample,
-               expected: simpleSample
-       } );
-
-       byteLimitTest( {
-               description: 'Plain text input. Calling byteLimit with no parameters and no maxlength attribute (T38310)',
-               $input: $( '<input>' ).attr( 'type', 'text' )
-                       .byteLimit(),
-               sample: simpleSample,
-               expected: simpleSample
-       } );
-
-       byteLimitTest( {
-               description: 'Limit using the maxlength attribute',
-               $input: $( '<input>' ).attr( 'type', 'text' )
-                       .attr( 'maxlength', '10' )
-                       .byteLimit(),
-               sample: simpleSample,
-               expected: '1234567890'
-       } );
-
-       byteLimitTest( {
-               description: 'Limit using a custom value',
-               $input: $( '<input>' ).attr( 'type', 'text' )
-                       .byteLimit( 10 ),
-               sample: simpleSample,
-               expected: '1234567890'
-       } );
-
-       byteLimitTest( {
-               description: 'Limit using a custom value, overriding maxlength attribute',
-               $input: $( '<input>' ).attr( 'type', 'text' )
-                       .attr( 'maxlength', '10' )
-                       .byteLimit( 15 ),
-               sample: simpleSample,
-               expected: '123456789012345'
-       } );
-
-       byteLimitTest( {
-               description: 'Limit using a custom value (multibyte)',
-               $input: $( '<input>' ).attr( 'type', 'text' )
-                       .byteLimit( 14 ),
-               sample: mbSample,
-               expected: '1234567890' + U_20AC + '1'
-       } );
-
-       byteLimitTest( {
-               description: 'Limit using a custom value (multibyte) overlapping a byte',
-               $input: $( '<input>' ).attr( 'type', 'text' )
-                       .byteLimit( 12 ),
-               sample: mbSample,
-               expected: '123456789012'
-       } );
-
-       byteLimitTest( {
-               description: 'Pass the limit and a callback as input filter',
-               $input: $( '<input>' ).attr( 'type', 'text' )
-                       .byteLimit( 6, function ( val ) {
-                               var title = mw.Title.newFromText( String( val ) );
-                               // Return without namespace prefix
-                               return title ? title.getMain() : '';
-                       } ),
-               sample: 'User:Sample',
-               expected: 'User:Sample'
-       } );
-
-       byteLimitTest( {
-               description: 'Limit using the maxlength attribute and pass a callback as input filter',
-               $input: $( '<input>' ).attr( 'type', 'text' )
-                       .attr( 'maxlength', '6' )
-                       .byteLimit( function ( val ) {
-                               var title = mw.Title.newFromText( String( val ) );
-                               // Return without namespace prefix
-                               return title ? title.getMain() : '';
-                       } ),
-               sample: 'User:Sample',
-               expected: 'User:Sample'
-       } );
-
-       byteLimitTest( {
-               description: 'Pass the limit and a callback as input filter',
-               $input: $( '<input>' ).attr( 'type', 'text' )
-                       .byteLimit( 6, function ( val ) {
-                               var title = mw.Title.newFromText( String( val ) );
-                               // Return without namespace prefix
-                               return title ? title.getMain() : '';
-                       } ),
-               sample: 'User:Example',
-               // The callback alters the value to be used to calculeate
-               // the length. The altered value is "Exampl" which has
-               // a length of 6, the "e" would exceed the limit.
-               expected: 'User:Exampl'
-       } );
-
-       byteLimitTest( {
-               description: 'Input filter that increases the length',
-               $input: $( '<input>' ).attr( 'type', 'text' )
-                       .byteLimit( 10, function ( text ) {
-                               return 'prefix' + text;
-                       } ),
-               sample: simpleSample,
-               // Prefix adds 6 characters, limit is reached after 4
-               expected: '1234'
-       } );
-
-       // Regression tests for T43450
-       byteLimitTest( {
-               description: 'Input filter of which the base exceeds the limit',
-               $input: $( '<input>' ).attr( 'type', 'text' )
-                       .byteLimit( 3, function ( text ) {
-                               return 'prefix' + text;
-                       } ),
-               sample: simpleSample,
-               hasLimit: true,
-               limit: 6, // 'prefix' length
-               expected: ''
-       } );
-
-       QUnit.test( 'Confirm properties and attributes set', function ( assert ) {
-               var $el;
-
-               $el = $( '<input>' ).attr( 'type', 'text' )
-                       .attr( 'maxlength', '7' )
-                       .appendTo( '#qunit-fixture' )
-                       .byteLimit();
-
-               assert.strictEqual( $el.attr( 'maxlength' ), '7', 'maxlength attribute unchanged for simple limit' );
-
-               $el = $( '<input>' ).attr( 'type', 'text' )
-                       .attr( 'maxlength', '7' )
-                       .appendTo( '#qunit-fixture' )
-                       .byteLimit( 12 );
-
-               assert.strictEqual( $el.attr( 'maxlength' ), '12', 'maxlength attribute updated for custom limit' );
-
-               $el = $( '<input>' ).attr( 'type', 'text' )
-                       .attr( 'maxlength', '7' )
-                       .appendTo( '#qunit-fixture' )
-                       .byteLimit( 12, function ( val ) {
-                               return val;
-                       } );
-
-               assert.strictEqual( $el.attr( 'maxlength' ), undefined, 'maxlength attribute removed for limit with callback' );
-
-               $( '<input>' ).attr( 'type', 'text' )
-                       .addClass( 'mw-test-byteLimit-foo' )
-                       .attr( 'maxlength', '7' )
-                       .appendTo( '#qunit-fixture' );
-
-               $( '<input>' ).attr( 'type', 'text' )
-                       .addClass( 'mw-test-byteLimit-foo' )
-                       .attr( 'maxlength', '12' )
-                       .appendTo( '#qunit-fixture' );
-
-               $el = $( '.mw-test-byteLimit-foo' );
-
-               assert.strictEqual( $el.length, 2, 'Verify that there are no other elements clashing with this test suite' );
-
-               $el.byteLimit();
-       } );
-
-       QUnit.test( 'Trim from insertion when limit exceeded', function ( assert ) {
-               var $el;
-
-               // Use a new <input> because the bug only occurs on the first time
-               // the limit it reached (T42850)
-               $el = $( '<input>' ).attr( 'type', 'text' )
-                       .appendTo( '#qunit-fixture' )
-                       .byteLimit( 3 )
-                       .val( 'abc' ).trigger( 'change' )
-                       .val( 'zabc' ).trigger( 'change' );
-
-               assert.strictEqual( $el.val(), 'abc', 'Trim from the insertion point (at 0), not the end' );
-
-               $el = $( '<input>' ).attr( 'type', 'text' )
-                       .appendTo( '#qunit-fixture' )
-                       .byteLimit( 3 )
-                       .val( 'abc' ).trigger( 'change' )
-                       .val( 'azbc' ).trigger( 'change' );
-
-               assert.strictEqual( $el.val(), 'abc', 'Trim from the insertion point (at 1), not the end' );
-       } );
-}( jQuery, mediaWiki ) );
diff --git a/tests/qunit/suites/resources/jquery/jquery.lengthLimit.test.js b/tests/qunit/suites/resources/jquery/jquery.lengthLimit.test.js
new file mode 100644 (file)
index 0000000..7117d1f
--- /dev/null
@@ -0,0 +1,286 @@
+( function ( $, mw ) {
+       var simpleSample, U_20AC, poop, mbSample;
+
+       QUnit.module( 'jquery.lengthLimit', QUnit.newMwEnvironment() );
+
+       // Simple sample (20 chars, 20 bytes)
+       simpleSample = '12345678901234567890';
+
+       // 3 bytes (euro-symbol)
+       U_20AC = '\u20AC';
+
+       // Outside of the BMP (pile of poo emoji)
+       poop = '\uD83D\uDCA9'; // "💩"
+
+       // Multi-byte sample (22 chars, 26 bytes)
+       mbSample = '1234567890' + U_20AC + '1234567890' + U_20AC;
+
+       // Basic sendkey-implementation
+       function addChars( $input, charstr ) {
+               var c, len;
+
+               function x( $input, i ) {
+                       // Add character to the value
+                       return $input.val() + charstr.charAt( i );
+               }
+
+               for ( c = 0, len = charstr.length; c < len; c += 1 ) {
+                       $input
+                               .val( x( $input, c ) )
+                               .trigger( 'change' );
+               }
+       }
+
+       /**
+        * Test factory for $.fn.byteLimit
+        *
+        * @param {Object} options
+        * @param {string} options.description Test name
+        * @param {jQuery} options.$input jQuery object in an input element
+        * @param {string} options.sample Sequence of characters to simulate being
+        *  added one by one
+        * @param {string} options.expected Expected final value of `$input`
+        */
+       function byteLimitTest( options ) {
+               var opt = $.extend( {
+                       description: '',
+                       $input: null,
+                       sample: '',
+                       expected: ''
+               }, options );
+
+               QUnit.test( opt.description, function ( assert ) {
+                       opt.$input.appendTo( '#qunit-fixture' );
+
+                       // Simulate pressing keys for each of the sample characters
+                       addChars( opt.$input, opt.sample );
+
+                       assert.equal(
+                               opt.$input.val(),
+                               opt.expected,
+                               'New value matches the expected string'
+                       );
+               } );
+       }
+
+       byteLimitTest( {
+               description: 'Plain text input',
+               $input: $( '<input>' ).attr( 'type', 'text' ),
+               sample: simpleSample,
+               expected: simpleSample
+       } );
+
+       byteLimitTest( {
+               description: 'Plain text input. Calling byteLimit with no parameters and no maxlength attribute (T38310)',
+               $input: $( '<input>' ).attr( 'type', 'text' )
+                       .byteLimit(),
+               sample: simpleSample,
+               expected: simpleSample
+       } );
+
+       byteLimitTest( {
+               description: 'Limit using the maxlength attribute',
+               $input: $( '<input>' ).attr( 'type', 'text' )
+                       .attr( 'maxlength', '10' )
+                       .byteLimit(),
+               sample: simpleSample,
+               expected: '1234567890'
+       } );
+
+       byteLimitTest( {
+               description: 'Limit using a custom value',
+               $input: $( '<input>' ).attr( 'type', 'text' )
+                       .byteLimit( 10 ),
+               sample: simpleSample,
+               expected: '1234567890'
+       } );
+
+       byteLimitTest( {
+               description: 'Limit using a custom value, overriding maxlength attribute',
+               $input: $( '<input>' ).attr( 'type', 'text' )
+                       .attr( 'maxlength', '10' )
+                       .byteLimit( 15 ),
+               sample: simpleSample,
+               expected: '123456789012345'
+       } );
+
+       byteLimitTest( {
+               description: 'Limit using a custom value (multibyte)',
+               $input: $( '<input>' ).attr( 'type', 'text' )
+                       .byteLimit( 14 ),
+               sample: mbSample,
+               expected: '1234567890' + U_20AC + '1'
+       } );
+
+       byteLimitTest( {
+               description: 'Limit using a custom value (multibyte, outside BMP)',
+               $input: $( '<input>' ).attr( 'type', 'text' )
+                       .byteLimit( 3 ),
+               sample: poop,
+               expected: ''
+       } );
+
+       byteLimitTest( {
+               description: 'Limit using a custom value (multibyte) overlapping a byte',
+               $input: $( '<input>' ).attr( 'type', 'text' )
+                       .byteLimit( 12 ),
+               sample: mbSample,
+               expected: '123456789012'
+       } );
+
+       byteLimitTest( {
+               description: 'Pass the limit and a callback as input filter',
+               $input: $( '<input>' ).attr( 'type', 'text' )
+                       .byteLimit( 6, function ( val ) {
+                               var title = mw.Title.newFromText( String( val ) );
+                               // Return without namespace prefix
+                               return title ? title.getMain() : '';
+                       } ),
+               sample: 'User:Sample',
+               expected: 'User:Sample'
+       } );
+
+       byteLimitTest( {
+               description: 'Limit using the maxlength attribute and pass a callback as input filter',
+               $input: $( '<input>' ).attr( 'type', 'text' )
+                       .attr( 'maxlength', '6' )
+                       .byteLimit( function ( val ) {
+                               var title = mw.Title.newFromText( String( val ) );
+                               // Return without namespace prefix
+                               return title ? title.getMain() : '';
+                       } ),
+               sample: 'User:Sample',
+               expected: 'User:Sample'
+       } );
+
+       byteLimitTest( {
+               description: 'Pass the limit and a callback as input filter',
+               $input: $( '<input>' ).attr( 'type', 'text' )
+                       .byteLimit( 6, function ( val ) {
+                               var title = mw.Title.newFromText( String( val ) );
+                               // Return without namespace prefix
+                               return title ? title.getMain() : '';
+                       } ),
+               sample: 'User:Example',
+               // The callback alters the value to be used to calculeate
+               // the length. The altered value is "Exampl" which has
+               // a length of 6, the "e" would exceed the limit.
+               expected: 'User:Exampl'
+       } );
+
+       byteLimitTest( {
+               description: 'Input filter that increases the length',
+               $input: $( '<input>' ).attr( 'type', 'text' )
+                       .byteLimit( 10, function ( text ) {
+                               return 'prefix' + text;
+                       } ),
+               sample: simpleSample,
+               // Prefix adds 6 characters, limit is reached after 4
+               expected: '1234'
+       } );
+
+       // Regression tests for T43450
+       byteLimitTest( {
+               description: 'Input filter of which the base exceeds the limit',
+               $input: $( '<input>' ).attr( 'type', 'text' )
+                       .byteLimit( 3, function ( text ) {
+                               return 'prefix' + text;
+                       } ),
+               sample: simpleSample,
+               expected: ''
+       } );
+
+       QUnit.test( 'Confirm properties and attributes set', function ( assert ) {
+               var $el;
+
+               $el = $( '<input>' ).attr( 'type', 'text' )
+                       .attr( 'maxlength', '7' )
+                       .appendTo( '#qunit-fixture' )
+                       .byteLimit();
+
+               assert.strictEqual( $el.attr( 'maxlength' ), '7', 'maxlength attribute unchanged for simple limit' );
+
+               $el = $( '<input>' ).attr( 'type', 'text' )
+                       .attr( 'maxlength', '7' )
+                       .appendTo( '#qunit-fixture' )
+                       .byteLimit( 12 );
+
+               assert.strictEqual( $el.attr( 'maxlength' ), '12', 'maxlength attribute updated for custom limit' );
+
+               $el = $( '<input>' ).attr( 'type', 'text' )
+                       .attr( 'maxlength', '7' )
+                       .appendTo( '#qunit-fixture' )
+                       .byteLimit( 12, function ( val ) {
+                               return val;
+                       } );
+
+               assert.strictEqual( $el.attr( 'maxlength' ), undefined, 'maxlength attribute removed for limit with callback' );
+
+               $( '<input>' ).attr( 'type', 'text' )
+                       .addClass( 'mw-test-byteLimit-foo' )
+                       .attr( 'maxlength', '7' )
+                       .appendTo( '#qunit-fixture' );
+
+               $( '<input>' ).attr( 'type', 'text' )
+                       .addClass( 'mw-test-byteLimit-foo' )
+                       .attr( 'maxlength', '12' )
+                       .appendTo( '#qunit-fixture' );
+
+               $el = $( '.mw-test-byteLimit-foo' );
+
+               assert.strictEqual( $el.length, 2, 'Verify that there are no other elements clashing with this test suite' );
+
+               $el.byteLimit();
+       } );
+
+       QUnit.test( 'Trim from insertion when limit exceeded', function ( assert ) {
+               var $el;
+
+               // Use a new <input> because the bug only occurs on the first time
+               // the limit it reached (T42850)
+               $el = $( '<input>' ).attr( 'type', 'text' )
+                       .appendTo( '#qunit-fixture' )
+                       .byteLimit( 3 )
+                       .val( 'abc' ).trigger( 'change' )
+                       .val( 'zabc' ).trigger( 'change' );
+
+               assert.strictEqual( $el.val(), 'abc', 'Trim from the insertion point (at 0), not the end' );
+
+               $el = $( '<input>' ).attr( 'type', 'text' )
+                       .appendTo( '#qunit-fixture' )
+                       .byteLimit( 3 )
+                       .val( 'abc' ).trigger( 'change' )
+                       .val( 'azbc' ).trigger( 'change' );
+
+               assert.strictEqual( $el.val(), 'abc', 'Trim from the insertion point (at 1), not the end' );
+       } );
+
+       QUnit.test( 'Do not cut up false matching substrings in emoji insertions', function ( assert ) {
+               var $el,
+                       oldVal = '\uD83D\uDCA9\uD83D\uDCA9', // "💩💩"
+                       newVal = '\uD83D\uDCA9\uD83D\uDCB9\uD83E\uDCA9\uD83D\uDCA9', // "💩💹🢩💩"
+                       expected = '\uD83D\uDCA9\uD83D\uDCB9\uD83D\uDCA9'; // "💩💹💩"
+
+               // Possible bad results:
+               // * With no surrogate support:
+               //   '\uD83D\uDCA9\uD83D\uDCB9\uD83E\uDCA9' "💩💹🢩"
+               // * With correct trimming but bad detection of inserted text:
+               //   '\uD83D\uDCA9\uD83D\uDCB9\uDCA9' "💩💹�"
+
+               $el = $( '<input>' ).attr( 'type', 'text' )
+                       .appendTo( '#qunit-fixture' )
+                       .byteLimit( 12 )
+                       .val( oldVal ).trigger( 'change' )
+                       .val( newVal ).trigger( 'change' );
+
+               assert.strictEqual( $el.val(), expected, 'Pasted emoji correctly trimmed at the end' );
+       } );
+
+       byteLimitTest( {
+               description: 'Unpaired surrogates do not crash',
+               $input: $( '<input>' ).attr( 'type', 'text' ).byteLimit( 4 ),
+               sample: '\uD800\uD800\uDFFF',
+               expected: '\uD800'
+       } );
+
+}( jQuery, mediaWiki ) );
diff --git a/tests/qunit/suites/resources/mediawiki/mediawiki.String.byteLength.test.js b/tests/qunit/suites/resources/mediawiki/mediawiki.String.byteLength.test.js
new file mode 100644 (file)
index 0000000..ae3ebbf
--- /dev/null
@@ -0,0 +1,39 @@
+( function () {
+       var byteLength = require( 'mediawiki.String' ).byteLength;
+
+       QUnit.module( 'mediawiki.String.byteLength', QUnit.newMwEnvironment() );
+
+       QUnit.test( 'Simple text', function ( assert ) {
+               var azLc = 'abcdefghijklmnopqrstuvwxyz',
+                       azUc = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
+                       num = '0123456789',
+                       x = '*',
+                       space = '   ';
+
+               assert.equal( byteLength( azLc ), 26, 'Lowercase a-z' );
+               assert.equal( byteLength( azUc ), 26, 'Uppercase A-Z' );
+               assert.equal( byteLength( num ), 10, 'Numbers 0-9' );
+               assert.equal( byteLength( x ), 1, 'An asterisk' );
+               assert.equal( byteLength( space ), 3, '3 spaces' );
+
+       } );
+
+       QUnit.test( 'Special text', function ( assert ) {
+               // https://en.wikipedia.org/wiki/UTF-8
+               var u0024 = '$',
+                       // Cent symbol
+                       u00A2 = '\u00A2',
+                       // Euro symbol
+                       u20AC = '\u20AC',
+                       // Character \U00024B62 (Han script) can't be represented in javascript as a single
+                       // code point, instead it is composed as a surrogate pair of two separate code units.
+                       // http://codepoints.net/U+24B62
+                       // http://www.fileformat.info/info/unicode/char/24B62/index.htm
+                       u024B62 = '\uD852\uDF62';
+
+               assert.strictEqual( byteLength( u0024 ), 1, 'U+0024' );
+               assert.strictEqual( byteLength( u00A2 ), 2, 'U+00A2' );
+               assert.strictEqual( byteLength( u20AC ), 3, 'U+20AC' );
+               assert.strictEqual( byteLength( u024B62 ), 4, 'U+024B62 (surrogate pair: \\uD852\\uDF62)' );
+       } );
+}() );
diff --git a/tests/qunit/suites/resources/mediawiki/mediawiki.String.trimByteLength.test.js b/tests/qunit/suites/resources/mediawiki/mediawiki.String.trimByteLength.test.js
new file mode 100644 (file)
index 0000000..e2eea94
--- /dev/null
@@ -0,0 +1,150 @@
+( function ( $, mw ) {
+       var simpleSample, U_20AC, poop, mbSample,
+               trimByteLength = require( 'mediawiki.String' ).trimByteLength;
+
+       QUnit.module( 'mediawiki.String.trimByteLength', QUnit.newMwEnvironment() );
+
+       // Simple sample (20 chars, 20 bytes)
+       simpleSample = '12345678901234567890';
+
+       // 3 bytes (euro-symbol)
+       U_20AC = '\u20AC';
+
+       // Outside of the BMP (pile of poo emoji)
+       poop = '\uD83D\uDCA9'; // "💩"
+
+       // Multi-byte sample (22 chars, 26 bytes)
+       mbSample = '1234567890' + U_20AC + '1234567890' + U_20AC;
+
+       /**
+        * Test factory for mw.String#trimByteLength
+        *
+        * @param {Object} options
+        * @param {string} options.description Test name
+        * @param {string} options.sample Sequence of characters to trim
+        * @param {string} [options.initial] Previous value of the sequence of characters, if any
+        * @param {Number} options.limit Length to trim to
+        * @param {Function} [options.fn] Filter function
+        * @param {string} options.expected Expected final value
+        */
+       function byteLimitTest( options ) {
+               var opt = $.extend( {
+                       description: '',
+                       sample: '',
+                       initial: '',
+                       limit: 0,
+                       fn: function ( a ) { return a; },
+                       expected: ''
+               }, options );
+
+               QUnit.test( opt.description, function ( assert ) {
+                       var res = trimByteLength( opt.initial, opt.sample, opt.limit, opt.fn );
+
+                       assert.equal(
+                               res.newVal,
+                               opt.expected,
+                               'New value matches the expected string'
+                       );
+               } );
+       }
+
+       byteLimitTest( {
+               description: 'Limit using the maxlength attribute',
+               limit: 10,
+               sample: simpleSample,
+               expected: '1234567890'
+       } );
+
+       byteLimitTest( {
+               description: 'Limit using a custom value (multibyte)',
+               limit: 14,
+               sample: mbSample,
+               expected: '1234567890' + U_20AC + '1'
+       } );
+
+       byteLimitTest( {
+               description: 'Limit using a custom value (multibyte, outside BMP)',
+               limit: 3,
+               sample: poop,
+               expected: ''
+       } );
+
+       byteLimitTest( {
+               description: 'Limit using a custom value (multibyte) overlapping a byte',
+               limit: 12,
+               sample: mbSample,
+               expected: '1234567890'
+       } );
+
+       byteLimitTest( {
+               description: 'Pass the limit and a callback as input filter',
+               limit: 6,
+               fn: function ( val ) {
+                       var title = mw.Title.newFromText( String( val ) );
+                       // Return without namespace prefix
+                       return title ? title.getMain() : '';
+               },
+               sample: 'User:Sample',
+               expected: 'User:Sample'
+       } );
+
+       byteLimitTest( {
+               description: 'Pass the limit and a callback as input filter',
+               limit: 6,
+               fn: function ( val ) {
+                       var title = mw.Title.newFromText( String( val ) );
+                       // Return without namespace prefix
+                       return title ? title.getMain() : '';
+               },
+               sample: 'User:Example',
+               // The callback alters the value to be used to calculeate
+               // the length. The altered value is "Exampl" which has
+               // a length of 6, the "e" would exceed the limit.
+               expected: 'User:Exampl'
+       } );
+
+       byteLimitTest( {
+               description: 'Input filter that increases the length',
+               limit: 10,
+               fn: function ( text ) {
+                       return 'prefix' + text;
+               },
+               sample: simpleSample,
+               // Prefix adds 6 characters, limit is reached after 4
+               expected: '1234'
+       } );
+
+       byteLimitTest( {
+               description: 'Trim from insertion when limit exceeded',
+               limit: 3,
+               initial: 'abc',
+               sample: 'zabc',
+               // Trim from the insertion point (at 0), not the end
+               expected: 'abc'
+       } );
+
+       byteLimitTest( {
+               description: 'Trim from insertion when limit exceeded',
+               limit: 3,
+               initial: 'abc',
+               sample: 'azbc',
+               // Trim from the insertion point (at 1), not the end
+               expected: 'abc'
+       } );
+
+       byteLimitTest( {
+               description: 'Do not cut up false matching substrings in emoji insertions',
+               limit: 12,
+               initial: '\uD83D\uDCA9\uD83D\uDCA9', // "💩💩"
+               sample: '\uD83D\uDCA9\uD83D\uDCB9\uD83E\uDCA9\uD83D\uDCA9', // "💩💹🢩💩"
+               expected: '\uD83D\uDCA9\uD83D\uDCB9\uD83D\uDCA9' // "💩💹💩"
+       } );
+
+       byteLimitTest( {
+               description: 'Unpaired surrogates do not crash',
+               limit: 4,
+               sample: '\uD800\uD800\uDFFF',
+               expected: '\uD800'
+       } );
+
+}( jQuery, mediaWiki ) );
index 0866b9e..6a704b5 100644 (file)
                        'Mozilla/5.0 (Windows NT 6.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.95 Safari/537.36 OPR/15.0.1147.153',
                        'Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.57 Safari/537.36 OPR/16.0.1196.62',
                        'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.125 Safari/537.36 OPR/23.0.1522.75',
-                       // Internet Explorer 10+
-                       'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; WOW64; Trident/6.0)',
+                       // Internet Explorer 11
                        'Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko',
-                       // IE Mobile
-                       'Mozilla/5.0 (compatible; MSIE 9.0; Windows Phone OS 7.5; Trident/5.0; IEMobile/9.0; NOKIA; Lumia 800)',
                        // Edge
                        'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.246',
                        // Edge Mobile
                blacklisted: [
                        /* Grade C */
 
+                       // Internet Explorer 10
+                       'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; WOW64; Trident/6.0)',
+                       // IE Mobile 10
+                       'Mozilla/5.0 (compatible; MSIE 10.0; Windows Phone 8.0; Trident/6.0; IEMobile/10.0; ARM; Touch; HTC; Windows Phone 8X by HTC)',
                        // PlayStation
                        'Mozilla/5.0 (PLAYSTATION 3; 1.10)',
                        'Mozilla/5.0 (PLAYSTATION 3; 3.55)',
index 1628377..2dbf271 100644 (file)
@@ -37,14 +37,14 @@ To run only one test (name contains string 'preferences'):
 The runner reads the config file `wdio.conf.js` and runs the spec listed in
 `page.js`.
 
-The defaults in the configuration files aim are targetting  a MediaWiki-Vagrant
-installation on installation on http://127.0.0.1:8080 with a user Admin and
-password 'vagrant'.  Those settings can be overriden using environment
+The defaults in the configuration files aim are targeting a MediaWiki-Vagrant
+installation on http://127.0.0.1:8080 with a user Admin and
+password 'vagrant'.  Those settings can be overridden using environment
 variables:
 
 `MW_SERVER`: to be set to the value of your $wgServer
-`MW_SCRIPT_PATH`: ditto with  $wgScriptPath
-`MEDIAWIKI_USER`: username of an account that can create users on the wiki.
+`MW_SCRIPT_PATH`: ditto with $wgScriptPath
+`MEDIAWIKI_USER`: username of an account that can create users on the wiki
 `MEDIAWIKI_PASSWORD`: password for above user
 
 Example: