Merge "Use RemexHtml as the tidy implementation for parser tests"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Tue, 6 Mar 2018 22:27:42 +0000 (22:27 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Tue, 6 Mar 2018 22:27:42 +0000 (22:27 +0000)
555 files changed:
.gitignore
RELEASE-NOTES-1.31
autoload.php
includes/ActorMigration.php [new file with mode: 0644]
includes/Block.php
includes/DefaultSettings.php
includes/EditPage.php
includes/FileDeleteForm.php
includes/GlobalFunctions.php
includes/Linker.php
includes/MediaWikiServices.php
includes/OutputPage.php
includes/ProtectionForm.php
includes/Revision.php
includes/RevisionList.php
includes/ServiceWiring.php
includes/SiteStatsInit.php
includes/Storage/BlobStore.php
includes/Storage/NameTableAccessException.php [new file with mode: 0644]
includes/Storage/NameTableStore.php [new file with mode: 0644]
includes/Storage/RevisionRecord.php
includes/Storage/RevisionSlots.php
includes/Storage/RevisionStore.php
includes/Storage/SqlBlobStore.php
includes/Title.php
includes/actions/InfoAction.php
includes/api/ApiCSPReport.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/de.json
includes/api/i18n/en.json
includes/api/i18n/fr.json
includes/api/i18n/it.json
includes/api/i18n/ja.json
includes/api/i18n/lt.json
includes/api/i18n/pt-br.json
includes/api/i18n/pt.json
includes/api/i18n/ru.json
includes/api/i18n/zh-hans.json
includes/auth/AuthManager.php
includes/cache/UserCache.php
includes/changes/ChangesList.php
includes/changes/RecentChange.php
includes/changetags/ChangeTags.php
includes/changetags/ChangeTagsLogItem.php
includes/config/EtcdConfig.php
includes/content/AbstractContent.php
includes/content/ContentHandler.php
includes/content/WikitextContent.php
includes/db/DatabaseOracle.php
includes/deferred/SiteStatsUpdate.php
includes/diff/DifferenceEngine.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/OracleInstaller.php
includes/installer/OracleUpdater.php
includes/installer/PostgresUpdater.php
includes/installer/SqliteUpdater.php
includes/installer/i18n/bg.json
includes/installer/i18n/el.json
includes/installer/i18n/es.json
includes/installer/i18n/fa.json
includes/installer/i18n/he.json
includes/installer/i18n/ja.json
includes/installer/i18n/sr-ec.json
includes/jobqueue/JobQueueSecondTestQueue.php
includes/jobqueue/jobs/RecentChangesUpdateJob.php
includes/libs/CSSMin.php
includes/libs/filebackend/SwiftFileBackend.php
includes/libs/objectcache/WANObjectCache.php
includes/libs/rdbms/ChronologyProtector.php
includes/libs/rdbms/database/Database.php
includes/libs/rdbms/database/DatabaseMssql.php
includes/libs/rdbms/database/DatabaseMysqlBase.php
includes/libs/rdbms/database/DatabaseMysqli.php
includes/libs/rdbms/database/DatabasePostgres.php
includes/libs/rdbms/database/DatabaseSqlite.php
includes/libs/rdbms/database/IDatabase.php
includes/libs/rdbms/database/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/logging/LogEntry.php
includes/logging/LogPage.php
includes/logging/LogPager.php
includes/logging/WikitextLogFormatter.php [new file with mode: 0644]
includes/media/JpegMetadataExtractor.php
includes/page/Article.php
includes/page/WikiPage.php
includes/parser/BlockLevelPass.php
includes/parser/CoreParserFunctions.php
includes/parser/Parser.php
includes/parser/StripState.php
includes/preferences/DefaultPreferencesFactory.php
includes/profiler/Profiler.php
includes/resourceloader/ResourceLoader.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/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/SpecialRecentchanges.php
includes/specials/SpecialRecentchangeslinked.php
includes/specials/SpecialRedirect.php
includes/specials/SpecialRevisiondelete.php
includes/specials/SpecialUndelete.php
includes/specials/SpecialUpload.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/User.php
includes/user/UserIdentity.php
includes/user/UserIdentityValue.php
includes/watcheditem/WatchedItemQueryService.php
includes/widget/SizeFilterWidget.php [new file with mode: 0644]
jsduck.json
languages/Language.php
languages/classes/LanguageCrh.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.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/gor.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/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/MessagesSd.php
maintenance/Maintenance.php
maintenance/archives/patch-actor-table.sql [new file with mode: 0644]
maintenance/archives/upgradeLogging.php
maintenance/clearInterwikiCache.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/populateBacklinkNamespace.php
maintenance/populateFilearchiveSha1.php
maintenance/populateIpChanges.php
maintenance/populateLogSearch.php
maintenance/populateLogUsertext.php
maintenance/populateParentId.php
maintenance/populateRecentChangesSource.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/rebuildFileCache.php
maintenance/rebuildrecentchanges.php
maintenance/refreshLinks.php
maintenance/removeUnusedAccounts.php
maintenance/rollbackEdits.php
maintenance/sql.php
maintenance/sqlite/archives/patch-actor-table.sql [new file with mode: 0644]
maintenance/storage/checkStorage.php
maintenance/storage/compressOld.php
maintenance/storage/fixT22757.php
maintenance/storage/moveToExternal.php
maintenance/storage/orphanStats.php
maintenance/storage/resolveStubs.php
maintenance/storage/storageTypeStats.php
maintenance/storage/trackBlobs.php
maintenance/tables.sql
maintenance/updateCollation.php
maintenance/updateRestrictions.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.action/mediawiki.action.edit.styles.less
resources/src/mediawiki.legacy/protect.js
resources/src/mediawiki.less/mediawiki.mixins.less
resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FilterGroup.js
resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FilterItem.js
resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FiltersViewModel.js
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.ChangesLimitAndDateButtonWidget.js
resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.ChangesListWrapperWidget.js
resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.MenuSelectWidget.js
resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.ValuePickerWidget.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.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.SizeFilterWidget.base.css [new file with mode: 0644]
resources/src/mediawiki.widgets/mw.widgets.SizeFilterWidget.js [new file with mode: 0644]
resources/src/mediawiki.widgets/mw.widgets.TitleInputWidget.js
resources/src/mediawiki/htmlform/multiselect.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.diff.styles.css
resources/src/mediawiki/mediawiki.inspect.js
resources/src/startup.js
tests/common/TestsAutoLoader.php
tests/parser/ParserTestRunner.php
tests/parser/parserTests.txt
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/PageArchiveTest.php
tests/phpunit/includes/RevisionDbTestBase.php
tests/phpunit/includes/RevisionTest.php
tests/phpunit/includes/Storage/MutableRevisionRecordTest.php
tests/phpunit/includes/Storage/MutableRevisionSlotsTest.php
tests/phpunit/includes/Storage/NameTableStoreTest.php [new file with mode: 0644]
tests/phpunit/includes/Storage/RevisionSlotsTest.php
tests/phpunit/includes/Storage/RevisionStoreDbTest.php
tests/phpunit/includes/Storage/RevisionStoreRecordTest.php
tests/phpunit/includes/Storage/RevisionStoreTest.php
tests/phpunit/includes/TitleMethodsTest.php
tests/phpunit/includes/TitlePermissionTest.php
tests/phpunit/includes/api/ApiBaseTest.php
tests/phpunit/includes/api/ApiQueryRecentChangesIntegrationTest.php
tests/phpunit/includes/api/ApiQueryWatchlistIntegrationTest.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/changes/RecentChangeTest.php
tests/phpunit/includes/config/EtcdConfigTest.php
tests/phpunit/includes/content/JavaScriptContentTest.php
tests/phpunit/includes/content/TextContentTest.php
tests/phpunit/includes/content/WikitextContentTest.php
tests/phpunit/includes/db/DatabaseSqliteTest.php
tests/phpunit/includes/db/LoadBalancerTest.php
tests/phpunit/includes/deferred/SiteStatsUpdateTest.php [new file with mode: 0644]
tests/phpunit/includes/import/ImportTest.php
tests/phpunit/includes/libs/CSSMinTest.php
tests/phpunit/includes/libs/rdbms/database/DatabaseMysqlBaseTest.php
tests/phpunit/includes/libs/rdbms/database/DatabaseSQLTest.php
tests/phpunit/includes/logging/DatabaseLogEntryTest.php [new file with mode: 0644]
tests/phpunit/includes/logging/LogFormatterTestCase.php
tests/phpunit/includes/media/JpegMetadataExtractorTest.php
tests/phpunit/includes/page/WikiPageDbTestBase.php
tests/phpunit/includes/parser/StripStateTest.php [new file with mode: 0644]
tests/phpunit/includes/password/UserPasswordPolicyTest.php
tests/phpunit/includes/resourceloader/ResourceLoaderClientHtmlTest.php
tests/phpunit/includes/shell/CommandTest.php
tests/phpunit/includes/shell/FirejailCommandTest.php
tests/phpunit/includes/specialpage/ChangesListSpecialPageTest.php
tests/phpunit/includes/specials/SpecialUploadTest.php [new file with mode: 0644]
tests/phpunit/includes/user/UserGroupMembershipTest.php
tests/phpunit/includes/user/UserTest.php
tests/phpunit/includes/watcheditem/WatchedItemQueryServiceUnitTest.php
tests/phpunit/languages/LanguageTest.php
tests/phpunit/languages/SpecialPageAliasTest.php
tests/phpunit/languages/classes/LanguageCrhTest.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/jquery/jquery.tablesorter.test.js
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 2c071b5..83669e6 100644 (file)
@@ -28,6 +28,9 @@ production.
   as upstream is inactive and has no plans to move to PHP 7.
 * The old CategorizedRecentChanges feature, including its related configuration
   option $wgAllowCategorizedRecentChanges, has been removed.
+* (T188472) The 'comma' value for $wgArticleCountMethod is no longer supported for
+  performance reasons, and installations with this setting will now work as if it
+  was configured with 'any'.
 
 === New features in 1.31 ===
 * Wikimedia\Rdbms\IDatabase->select() and similar methods now support
@@ -51,6 +54,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 ===
 
@@ -106,6 +121,7 @@ changes to languages because of Phabricator reports.
 * (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
@@ -237,10 +253,38 @@ 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 methods in EditPage, deprecated in MediaWiki 1.30, were removed:
+  * $isCssJsSubpage — use ::isUserConfigPage()
+  * $isCssSubpage — use ::isUserCssConfigPage()
+  * $isJsSubpage — use ::isUserJsConfigPage()
+  * $isWrongCaseCssJsPage – use ::isWrongCaseUserConfigPage()
+  * ::getSummaryInput() – use ::getSummaryInputWidget()
+  * ::getSummaryInputOOUI() – use ::getSummaryInputWidget()
+  * ::getCheckboxes() – use ::getCheckboxesWidget() or ::getCheckboxesDefinition()
+  * ::getCheckboxesOOUI() – use ::getCheckboxesWidget() or ::getCheckboxesDefinition()
 * 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.
+* In User, the cookie-related methods which were wrappers for the functions on the response
+  object, and were deprecated in 1.27, have been removed:
+  * ::setCookie()
+  * ::clearCookie()
+  * ::setExtendedLoginCookie()
+  Note that User::setCookies() remains, and is not deprecated.
+* The global functions wfProfileIn and wfProfileOut, deprecated in 1.25, have been removed.
+* The following methods related to caching of half-parsed HTML were deprecated:
+  * Parser::serializeHalfParsedText()
+  * Parser::unserializeHalfParsedText()
+  * Parser::isValidHalfParsedText()
+  * StripState::getSubState()
+  * StripState::merge()
 
 == Compatibility ==
 MediaWiki 1.31 requires PHP 5.5.9 or later. Although HHVM 3.18.5 or later is supported,
index 9042f7b..b5f3e4a 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',
@@ -947,6 +953,8 @@ $wgAutoloadLocalClasses = [
        'MediaWiki\\Storage\\IncompleteRevisionException' => __DIR__ . '/includes/Storage/IncompleteRevisionException.php',
        'MediaWiki\\Storage\\MutableRevisionRecord' => __DIR__ . '/includes/Storage/MutableRevisionRecord.php',
        'MediaWiki\\Storage\\MutableRevisionSlots' => __DIR__ . '/includes/Storage/MutableRevisionSlots.php',
+       'MediaWiki\\Storage\\NameTableAccessException' => __DIR__ . '/includes/Storage/NameTableAccessException.php',
+       'MediaWiki\\Storage\\NameTableStore' => __DIR__ . '/includes/Storage/NameTableStore.php',
        'MediaWiki\\Storage\\RevisionAccessException' => __DIR__ . '/includes/Storage/RevisionAccessException.php',
        'MediaWiki\\Storage\\RevisionArchiveRecord' => __DIR__ . '/includes/Storage/RevisionArchiveRecord.php',
        'MediaWiki\\Storage\\RevisionFactory' => __DIR__ . '/includes/Storage/RevisionFactory.php',
@@ -995,6 +1003,7 @@ $wgAutoloadLocalClasses = [
        'MediaWiki\\Widget\\Search\\SimpleSearchResultSetWidget' => __DIR__ . '/includes/widget/search/SimpleSearchResultSetWidget.php',
        'MediaWiki\\Widget\\Search\\SimpleSearchResultWidget' => __DIR__ . '/includes/widget/search/SimpleSearchResultWidget.php',
        'MediaWiki\\Widget\\SelectWithInputWidget' => __DIR__ . '/includes/widget/SelectWithInputWidget.php',
+       'MediaWiki\\Widget\\SizeFilterWidget' => __DIR__ . '/includes/widget/SizeFilterWidget.php',
        'MediaWiki\\Widget\\TitleInputWidget' => __DIR__ . '/includes/widget/TitleInputWidget.php',
        'MediaWiki\\Widget\\UserInputWidget' => __DIR__ . '/includes/widget/UserInputWidget.php',
        'MediaWiki\\Widget\\UsersMultiselectWidget' => __DIR__ . '/includes/widget/UsersMultiselectWidget.php',
@@ -1016,6 +1025,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 +1085,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 +1581,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 +1730,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',
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..fad49e4 100644 (file)
@@ -3311,9 +3311,10 @@ $wgAllowUserJs = false;
 $wgAllowUserCss = false;
 
 /**
- * Allow user-preferences implemented in CSS?
- * This allows users to customise the site appearance to a greater
- * degree; disabling it will improve page load times.
+ * Allow style-related user-preferences?
+ *
+ * This controls whether the `editfont` and `underline` preferences
+ * are availabe to users.
  */
 $wgAllowUserCssPrefs = true;
 
@@ -4438,7 +4439,6 @@ $wgEnableMagicLinks = [
  *
  * This variable can have the following values:
  * - 'any': all pages as considered as valid articles
- * - 'comma': the page must contain a comma to be considered valid
  * - 'link': the page must contain a [[wiki link]] to be considered valid
  *
  * See also See https://www.mediawiki.org/wiki/Manual:Article_count
@@ -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 f9c7fb2..ad5f75d 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,73 +3138,21 @@ 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',
                ];
        }
 
-       /**
-        * Standard summary input and label (wgSummary), abstracted so EditPage
-        * subclasses may reorganize the form.
-        * Note that you do not need to worry about the label's for=, it will be
-        * inferred by the id given to the input. You can remove them both by
-        * passing [ 'id' => false ] to $userInputAttrs.
-        *
-        * @deprecated since 1.30 Use getSummaryInputWidget() instead
-        * @param string $summary The value of the summary input
-        * @param string $labelText The html to place inside the label
-        * @param array $inputAttrs Array of attrs to use on the input
-        * @param array $spanLabelAttrs Array of attrs to use on the span inside the label
-        * @return array An array in the format [ $label, $input ]
-        */
-       public function getSummaryInput( $summary = "", $labelText = null,
-               $inputAttrs = null, $spanLabelAttrs = null
-       ) {
-               wfDeprecated( __METHOD__, '1.30' );
-               $inputAttrs = $this->getSummaryInputAttributes( $inputAttrs );
-               $inputAttrs += Linker::tooltipAndAccesskeyAttribs( 'summary' );
-
-               $spanLabelAttrs = ( is_array( $spanLabelAttrs ) ? $spanLabelAttrs : [] ) + [
-                       'class' => $this->missingSummary ? 'mw-summarymissed' : 'mw-summary',
-                       'id' => "wpSummaryLabel"
-               ];
-
-               $label = null;
-               if ( $labelText ) {
-                       $label = Xml::tags(
-                               'label',
-                               $inputAttrs['id'] ? [ 'for' => $inputAttrs['id'] ] : null,
-                               $labelText
-                       );
-                       $label = Xml::tags( 'span', $spanLabelAttrs, $label );
-               }
-
-               $input = Html::input( 'wpSummary', $summary, 'text', $inputAttrs );
-
-               return [ $label, $input ];
-       }
-
-       /**
-        * Builds a standard summary input with a label.
-        *
-        * @deprecated since 1.30 Use getSummaryInputWidget() instead
-        * @param string $summary The value of the summary input
-        * @param string $labelText The html to place inside the label
-        * @param array $inputAttrs Array of attrs to use on the input
-        *
-        * @return OOUI\FieldLayout OOUI FieldLayout with Label and Input
-        */
-       function getSummaryInputOOUI( $summary = "", $labelText = null, $inputAttrs = null ) {
-               wfDeprecated( __METHOD__, '1.30' );
-               return $this->getSummaryInputWidget( $summary, $labelText, $inputAttrs );
-       }
-
        /**
         * Builds a standard summary input with a label.
         *
@@ -3702,7 +3623,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 +3733,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 +3834,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;
@@ -4248,76 +4169,6 @@ ERROR;
                return $checkboxes;
        }
 
-       /**
-        * Returns an array of html code of the following checkboxes old style:
-        * minor and watch
-        *
-        * @deprecated since 1.30 Use getCheckboxesWidget() or getCheckboxesDefinition() instead
-        * @param int &$tabindex Current tabindex
-        * @param array $checked See getCheckboxesDefinition()
-        * @return array
-        */
-       public function getCheckboxes( &$tabindex, $checked ) {
-               wfDeprecated( __METHOD__, '1.30' );
-               $checkboxes = [];
-               $checkboxesDef = $this->getCheckboxesDefinition( $checked );
-
-               // Backwards-compatibility for the EditPageBeforeEditChecks hook
-               if ( !$this->isNew ) {
-                       $checkboxes['minor'] = '';
-               }
-               $checkboxes['watch'] = '';
-
-               foreach ( $checkboxesDef as $name => $options ) {
-                       $legacyName = isset( $options['legacy-name'] ) ? $options['legacy-name'] : $name;
-                       $label = $this->context->msg( $options['label-message'] )->parse();
-                       $attribs = [
-                               'tabindex' => ++$tabindex,
-                               'id' => $options['id'],
-                       ];
-                       $labelAttribs = [
-                               'for' => $options['id'],
-                       ];
-                       if ( isset( $options['tooltip'] ) ) {
-                               $attribs['accesskey'] = $this->context->msg( "accesskey-{$options['tooltip']}" )->text();
-                               $labelAttribs['title'] = Linker::titleAttrib( $options['tooltip'], 'withaccess' );
-                       }
-                       if ( isset( $options['title-message'] ) ) {
-                               $labelAttribs['title'] = $this->context->msg( $options['title-message'] )->text();
-                       }
-                       if ( isset( $options['label-id'] ) ) {
-                               $labelAttribs['id'] = $options['label-id'];
-                       }
-                       $checkboxHtml =
-                               Xml::check( $name, $options['default'], $attribs ) .
-                               '&#160;' .
-                               Xml::tags( 'label', $labelAttribs, $label );
-
-                       $checkboxes[ $legacyName ] = $checkboxHtml;
-               }
-
-               // Avoid PHP 7.1 warning of passing $this by reference
-               $editPage = $this;
-               Hooks::run( 'EditPageBeforeEditChecks', [ &$editPage, &$checkboxes, &$tabindex ], '1.29' );
-               return $checkboxes;
-       }
-
-       /**
-        * Returns an array of checkboxes for the edit form, including 'minor' and 'watch' checkboxes and
-        * any other added by extensions.
-        *
-        * @deprecated since 1.30 Use getCheckboxesWidget() or getCheckboxesDefinition() instead
-        * @param int &$tabindex Current tabindex
-        * @param array $checked Array of checkbox => bool, where bool indicates the checked
-        *                 status of the checkbox
-        *
-        * @return array Associative array of string keys to OOUI\FieldLayout instances
-        */
-       public function getCheckboxesOOUI( &$tabindex, $checked ) {
-               wfDeprecated( __METHOD__, '1.30' );
-               return $this->getCheckboxesWidget( $tabindex, $checked );
-       }
-
        /**
         * Returns an array of checkboxes for the edit form, including 'minor' and 'watch' checkboxes and
         * any other added by extensions.
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..8bb0a40 100644 (file)
@@ -16,6 +16,7 @@ use MediaWiki\Preferences\PreferencesFactory;
 use MediaWiki\Shell\CommandFactory;
 use MediaWiki\Storage\BlobStore;
 use MediaWiki\Storage\BlobStoreFactory;
+use MediaWiki\Storage\NameTableStore;
 use MediaWiki\Storage\RevisionFactory;
 use MediaWiki\Storage\RevisionLookup;
 use MediaWiki\Storage\RevisionStore;
@@ -690,6 +691,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
@@ -746,6 +771,22 @@ class MediaWikiServices extends ServiceContainer {
                return $this->getService( 'RevisionFactory' );
        }
 
+       /**
+        * @since 1.31
+        * @return NameTableStore
+        */
+       public function getContentModelStore() {
+               return $this->getService( 'ContentModelStore' );
+       }
+
+       /**
+        * @since 1.31
+        * @return NameTableStore
+        */
+       public function getSlotRoleStore() {
+               return $this->getService( 'SlotRoleStore' );
+       }
+
        /**
         * @since 1.31
         * @return PreferencesFactory
@@ -770,6 +811,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 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..22eb115 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' );
@@ -906,11 +938,6 @@ class Revision implements IDBAccessObject {
                        return $this->mRecord->getContent( 'main', $audience, $user );
                }
                catch ( RevisionAccessException $e ) {
-                       wfDebugLog(
-                               'T184670',
-                               __METHOD__ . ": Cannot get content: " . $e->getMessage() .
-                               "\n" . $e->getTraceAsString()
-                       );
                        return null;
                }
        }
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..08d343b 100644 (file)
@@ -45,6 +45,7 @@ use MediaWiki\MediaWikiServices;
 use MediaWiki\Preferences\DefaultPreferencesFactory;
 use MediaWiki\Shell\CommandFactory;
 use MediaWiki\Storage\BlobStoreFactory;
+use MediaWiki\Storage\NameTableStore;
 use MediaWiki\Storage\RevisionStore;
 use MediaWiki\Storage\SqlBlobStore;
 use Wikimedia\ObjectFactory;
@@ -179,7 +180,8 @@ return [
        'WatchedItemQueryService' => function ( MediaWikiServices $services ) {
                return new WatchedItemQueryService(
                        $services->getDBLoadBalancer(),
-                       $services->getCommentStore()
+                       $services->getCommentStore(),
+                       $services->getActorMigration()
                );
        },
 
@@ -442,6 +444,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 +502,8 @@ return [
                        $services->getDBLoadBalancer(),
                        $blobStore,
                        $services->getMainWANObjectCache(),
-                       $services->getCommentStore()
+                       $services->getCommentStore(),
+                       $services->getActorMigration()
                );
 
                $store->setLogger( LoggerFactory::getInstance( 'RevisionStore' ) );
@@ -514,6 +540,35 @@ return [
                return $services->getBlobStoreFactory()->newSqlBlobStore();
        },
 
+       'ContentModelStore' => function ( MediaWikiServices $services ) {
+               return new NameTableStore(
+                       $services->getDBLoadBalancer(),
+                       $services->getMainWANObjectCache(),
+                       LoggerFactory::getInstance( 'NameTableSqlStore' ),
+                       'content_models',
+                       'model_id',
+                       'model_name'
+                       /**
+                        * No strtolower normalization is added to the service as there are examples of
+                        * extensions that do not stick to this assumption.
+                        * - extensions/examples/DataPages define( 'CONTENT_MODEL_XML_DATA','XML_DATA' );
+                        * - extensions/Scribunto define( 'CONTENT_MODEL_SCRIBUNTO', 'Scribunto' );
+                        */
+               );
+       },
+
+       'SlotRoleStore' => function ( MediaWikiServices $services ) {
+               return new NameTableStore(
+                       $services->getDBLoadBalancer(),
+                       $services->getMainWANObjectCache(),
+                       LoggerFactory::getInstance( 'NameTableSqlStore' ),
+                       'slot_roles',
+                       'role_id',
+                       'role_name',
+                       'strtolower'
+               );
+       },
+
        'PreferencesFactory' => function ( MediaWikiServices $services ) {
                global $wgContLang;
                $authManager = AuthManager::singleton();
@@ -532,7 +587,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 f527cb2..8adb218 100644 (file)
@@ -79,13 +79,6 @@ class SiteStatsInit {
                if ( $config->get( 'ArticleCountMethod' ) == 'link' ) {
                        $tables[] = 'pagelinks';
                        $conds[] = 'pl_from=page_id';
-               } elseif ( $config->get( 'ArticleCountMethod' ) == 'comma' ) {
-                       // To make a correct check for this, we would need, for each page,
-                       // to load the text, maybe uncompress it, maybe decode it and then
-                       // check if there's one comma.
-                       // But one thing we are sure is that if the page is empty, it can't
-                       // contain a comma :)
-                       $conds[] = 'page_len > 0';
                }
 
                $this->articles = $this->dbr->selectField(
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();
 }
diff --git a/includes/Storage/NameTableAccessException.php b/includes/Storage/NameTableAccessException.php
new file mode 100644 (file)
index 0000000..393cb1f
--- /dev/null
@@ -0,0 +1,45 @@
+<?php
+/**
+ * Exception representing a failure to look up a row from a name 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
+ */
+
+namespace MediaWiki\Storage;
+
+use RuntimeException;
+
+/**
+ * Exception representing a failure to look up a row from a name table.
+ *
+ * @since 1.31
+ */
+class NameTableAccessException extends RuntimeException {
+
+       /**
+        * @param string $tableName
+        * @param string $accessType
+        * @param string|int $accessValue
+        * @return NameTableAccessException
+        */
+       public static function newFromDetails( $tableName, $accessType, $accessValue ) {
+               $message = "Failed to access name from ${tableName} using ${accessType} = ${accessValue}";
+               return new self( $message );
+       }
+
+}
diff --git a/includes/Storage/NameTableStore.php b/includes/Storage/NameTableStore.php
new file mode 100644 (file)
index 0000000..a1eba74
--- /dev/null
@@ -0,0 +1,365 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
+
+namespace MediaWiki\Storage;
+
+use IExpiringStore;
+use Psr\Log\LoggerInterface;
+use WANObjectCache;
+use Wikimedia\Assert\Assert;
+use Wikimedia\Rdbms\Database;
+use Wikimedia\Rdbms\IDatabase;
+use Wikimedia\Rdbms\LoadBalancer;
+
+/**
+ * @author Addshore
+ * @since 1.31
+ */
+class NameTableStore {
+
+       /** @var LoadBalancer */
+       private $loadBalancer;
+
+       /** @var WANObjectCache */
+       private $cache;
+
+       /** @var LoggerInterface */
+       private $logger;
+
+       /** @var string[] */
+       private $tableCache = null;
+
+       /** @var bool|string */
+       private $wikiId = false;
+
+       /** @var int */
+       private $cacheTTL;
+
+       /** @var string */
+       private $table;
+       /** @var string */
+       private $idField;
+       /** @var string */
+       private $nameField;
+       /** @var null|callable */
+       private $normalizationCallback = null;
+
+       /**
+        * @param LoadBalancer $dbLoadBalancer A load balancer for acquiring database connections
+        * @param WANObjectCache $cache A cache manager for caching data
+        * @param LoggerInterface $logger
+        * @param string $table
+        * @param string $idField
+        * @param string $nameField
+        * @param callable $normalizationCallback Normalization to be applied to names before being
+        * saved or queried. This should be a callback that accepts and returns a single string.
+        * @param bool|string $wikiId The ID of the target wiki database. Use false for the local wiki.
+        */
+       public function __construct(
+               LoadBalancer $dbLoadBalancer,
+               WANObjectCache $cache,
+               LoggerInterface $logger,
+               $table,
+               $idField,
+               $nameField,
+               callable $normalizationCallback = null,
+               $wikiId = false
+       ) {
+               $this->loadBalancer = $dbLoadBalancer;
+               $this->cache = $cache;
+               $this->logger = $logger;
+               $this->table = $table;
+               $this->idField = $idField;
+               $this->nameField = $nameField;
+               $this->normalizationCallback = $normalizationCallback;
+               $this->wikiId = $wikiId;
+               $this->cacheTTL = IExpiringStore::TTL_MONTH;
+       }
+
+       /**
+        * @param int $index A database index, like DB_MASTER or DB_REPLICA
+        * @param int $flags Database connection flags
+        *
+        * @return IDatabase
+        */
+       private function getDBConnection( $index, $flags = 0 ) {
+               return $this->loadBalancer->getConnection( $index, [], $this->wikiId, $flags );
+       }
+
+       private function getCacheKey() {
+               return $this->cache->makeKey( 'NameTableSqlStore', $this->table, $this->wikiId );
+       }
+
+       /**
+        * @param string $name
+        * @return string
+        */
+       private function normalizeName( $name ) {
+               if ( $this->normalizationCallback === null ) {
+                       return $name;
+               }
+               return call_user_func( $this->normalizationCallback, $name );
+       }
+
+       /**
+        * Acquire the id of the given name.
+        * This creates a row in the table if it doesn't already exist.
+        *
+        * @param string $name
+        * @throws NameTableAccessException
+        * @return int
+        */
+       public function acquireId( $name ) {
+               Assert::parameterType( 'string', $name, '$name' );
+               $name = $this->normalizeName( $name );
+
+               $table = $this->getTableFromCachesOrReplica();
+               $searchResult = array_search( $name, $table, true );
+               if ( $searchResult === false ) {
+                       $id = $this->store( $name );
+                       if ( $id === null ) {
+                               // RACE: $name was already in the db, probably just inserted, so load from master
+                               // Use DBO_TRX to avoid missing inserts due to other threads or REPEATABLE-READs
+                               $table = $this->loadTable(
+                                       $this->getDBConnection( DB_MASTER, LoadBalancer::CONN_TRX_AUTO )
+                               );
+                               $searchResult = array_search( $name, $table, true );
+                               if ( $searchResult === false ) {
+                                       // Insert failed due to IGNORE flag, but DB_MASTER didn't give us the data
+                                       $m = "No insert possible but master didn't give us a record for " .
+                                               "'{$name}' in '{$this->table}'";
+                                       $this->logger->error( $m );
+                                       throw new NameTableAccessException( $m );
+                               }
+                               $this->purgeWANCache(
+                                       function () {
+                                               $this->cache->reap( $this->getCacheKey(), INF );
+                                       }
+                               );
+                       } else {
+                               $table[$id] = $name;
+                               $searchResult = $id;
+                               // As store returned an ID we know we inserted so delete from WAN cache
+                               $this->purgeWANCache(
+                                       function () {
+                                               $this->cache->delete( $this->getCacheKey() );
+                                       }
+                               );
+                       }
+                       $this->tableCache = $table;
+               }
+
+               return $searchResult;
+       }
+
+       /**
+        * Get the id of the given name.
+        * If the name doesn't exist this will throw.
+        * This should be used in cases where we believe the name already exists or want to check for
+        * existence.
+        *
+        * @param string $name
+        * @throws NameTableAccessException The name does not exist
+        * @return int Id
+        */
+       public function getId( $name ) {
+               Assert::parameterType( 'string', $name, '$name' );
+               $name = $this->normalizeName( $name );
+
+               $table = $this->getTableFromCachesOrReplica();
+               $searchResult = array_search( $name, $table, true );
+
+               if ( $searchResult !== false ) {
+                       return $searchResult;
+               }
+
+               throw NameTableAccessException::newFromDetails( $this->table, 'name', $name );
+       }
+
+       /**
+        * Get the name of the given id.
+        * If the id doesn't exist this will throw.
+        * This should be used in cases where we believe the id already exists.
+        *
+        * Note: Calls to this method will result in a master select for non existing IDs.
+        *
+        * @param int $id
+        * @throws NameTableAccessException The id does not exist
+        * @return string name
+        */
+       public function getName( $id ) {
+               Assert::parameterType( 'integer', $id, '$id' );
+
+               $table = $this->getTableFromCachesOrReplica();
+               if ( array_key_exists( $id, $table ) ) {
+                       return $table[$id];
+               }
+
+               $table = $this->cache->getWithSetCallback(
+                       $this->getCacheKey(),
+                       $this->cacheTTL,
+                       function ( $oldValue, &$ttl, &$setOpts ) use ( $id ) {
+                               // Check if cached value is up-to-date enough to have $id
+                               if ( is_array( $oldValue ) && array_key_exists( $id, $oldValue ) ) {
+                                       // Completely leave the cache key alone
+                                       $ttl = WANObjectCache::TTL_UNCACHEABLE;
+                                       // Use the old value
+                                       return $oldValue;
+                               }
+                               // Regenerate from replica DB, and master DB if needed
+                               foreach ( [ DB_REPLICA, DB_MASTER ] as $source ) {
+                                       // Log a fallback to master
+                                       if ( $source === DB_MASTER ) {
+                                               $this->logger->info(
+                                                       __METHOD__ . 'falling back to master select from ' .
+                                                       $this->table . ' with id ' . $id
+                                               );
+                                       }
+                                       $db = $this->getDBConnection( $source );
+                                       $cacheSetOpts = Database::getCacheSetOptions( $db );
+                                       $table = $this->loadTable( $db );
+                                       if ( array_key_exists( $id, $table ) ) {
+                                               break; // found it
+                                       }
+                               }
+                               // Use the value from last source checked
+                               $setOpts += $cacheSetOpts;
+
+                               return $table;
+                       },
+                       [ 'minAsOf' => INF ] // force callback run
+               );
+
+               $this->tableCache = $table;
+
+               if ( array_key_exists( $id, $table ) ) {
+                       return $table[$id];
+               }
+
+               throw NameTableAccessException::newFromDetails( $this->table, 'id', $id );
+       }
+
+       /**
+        * Get the whole table, in no particular order as a map of ids to names.
+        * This method could be subject to DB or cache lag.
+        *
+        * @return string[] keys are the name ids, values are the names themselves
+        *  Example: [ 1 => 'foo', 3 => 'bar' ]
+        */
+       public function getMap() {
+               return $this->getTableFromCachesOrReplica();
+       }
+
+       /**
+        * @return string[]
+        */
+       private function getTableFromCachesOrReplica() {
+               if ( $this->tableCache !== null ) {
+                       return $this->tableCache;
+               }
+
+               $table = $this->cache->getWithSetCallback(
+                       $this->getCacheKey(),
+                       $this->cacheTTL,
+                       function ( $oldValue, &$ttl, &$setOpts ) {
+                               $dbr = $this->getDBConnection( DB_REPLICA );
+                               $setOpts += Database::getCacheSetOptions( $dbr );
+                               return $this->loadTable( $dbr );
+                       }
+               );
+
+               $this->tableCache = $table;
+
+               return $table;
+       }
+
+       /**
+        * Reap the WANCache entry for this table.
+        *
+        * @param callable $purgeCallback callback to 'purge' the WAN cache
+        */
+       private function purgeWANCache( $purgeCallback ) {
+               // If the LB has no DB changes don't both with onTransactionPreCommitOrIdle
+               if ( !$this->loadBalancer->hasOrMadeRecentMasterChanges() ) {
+                       $purgeCallback();
+                       return;
+               }
+
+               $this->getDBConnection( DB_MASTER )
+                       ->onTransactionPreCommitOrIdle( $purgeCallback, __METHOD__ );
+       }
+
+       /**
+        * Gets the table from the db
+        *
+        * @param IDatabase $db
+        *
+        * @return string[]
+        */
+       private function loadTable( IDatabase $db ) {
+               $result = $db->select(
+                       $this->table,
+                       [
+                               'id' => $this->idField,
+                               'name' => $this->nameField
+                       ],
+                       [],
+                       __METHOD__
+               );
+
+               $assocArray = [];
+               foreach ( $result as $row ) {
+                       $assocArray[$row->id] = $row->name;
+               }
+
+               return $assocArray;
+       }
+
+       /**
+        * Stores the given name in the DB, returning the ID when an insert occurs.
+        *
+        * @param string $name
+        * @return int|null int if we know the ID, null if we don't
+        */
+       private function store( $name ) {
+               Assert::parameterType( 'string', $name, '$name' );
+               Assert::parameter( $name !== '', '$name', 'should not be an empty string' );
+               // Note: this is only called internally so normalization of $name has already occurred.
+
+               $dbw = $this->getDBConnection( DB_MASTER );
+
+               $dbw->insert(
+                       $this->table,
+                       [ $this->nameField => $name ],
+                       __METHOD__,
+                       [ 'IGNORE' ]
+               );
+
+               if ( $dbw->affectedRows() === 0 ) {
+                       $this->logger->info(
+                               'Tried to insert name into table ' . $this->table . ', but value already existed.'
+                       );
+                       return null;
+               }
+
+               return $dbw->insertId();
+       }
+
+}
index 8734f48..6d83e1c 100644 (file)
@@ -196,6 +196,17 @@ abstract class RevisionRecord {
                return $slot;
        }
 
+       /**
+        * Returns whether the given slot is defined in this revision.
+        *
+        * @param string $role The role name of the desired slot
+        *
+        * @return bool
+        */
+       public function hasSlot( $role ) {
+               return $this->mSlots->hasSlot( $role );
+       }
+
        /**
         * Returns the slot names (roles) of all slots present in this revision.
         * getContent() will succeed only for the names returned by this method.
index 8d3d7e3..7fa5431 100644 (file)
@@ -110,6 +110,19 @@ class RevisionSlots {
                }
        }
 
+       /**
+        * Returns whether the given slot is set.
+        *
+        * @param string $role The role name of the desired slot
+        *
+        * @return bool
+        */
+       public function hasSlot( $role ) {
+               $slots = $this->getSlots();
+
+               return isset( $slots[$role] );
+       }
+
        /**
         * Returns the slot names (roles) of all slots present in this revision.
         * getContent() will succeed only for the names returned by this method.
index e7c9060..e00deef 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,
@@ -591,7 +613,7 @@ class RevisionStore
 
                        $fields['title'] = Title::makeTitle( $current->page_namespace, $current->page_title );
 
-                       $mainSlot = $this->emulateMainSlot_1_29( $fields, 0, $title );
+                       $mainSlot = $this->emulateMainSlot_1_29( $fields, self::READ_LATEST, $title );
                        $revision = new MutableRevisionRecord( $title, $this->wikiId );
                        $this->initializeMutableRevisionFromArray( $revision, $fields );
                        $revision->setSlot( $mainSlot );
@@ -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..66aadeb 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 ::isUserJsConfigPage()
+        * @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 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 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..d07df5a 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=' . $revQuery['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..84e12d7 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,12 @@ 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.
+                       $q = $actorMigration->getWhere(
+                               $db, 'log_user', User::newFromName( $params['user'], 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 bbee4fa..b2cb6f7 100644 (file)
@@ -22,7 +22,7 @@
                        "Tacsipacsi"
                ]
        },
-       "apihelp-main-extended-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:Special:MyLanguage/API:Main_page|Dokumentation]]\n* [[mw:Special:MyLanguage/API:FAQ|Häufig gestellte Fragen]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api Mailingliste]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce API-Ankündigungen]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Fehlerberichte und Anfragen]\n</div>\n<strong>Status:</strong> Alle auf dieser Seite gezeigten Funktionen sollten funktionieren, allerdings ist die API in aktiver Entwicklung und kann sich zu jeder Zeit ändern. Abonniere die [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ MediaWiki-API-Ankündigungs-Mailingliste], um über Aktualisierungen informiert zu werden.\n\n<strong>Fehlerhafte Anfragen:</strong> Wenn fehlerhafte Anfragen an die API gesendet werden, wird ein HTTP-Header mit dem Schlüssel „MediaWiki-API-Error“ gesendet. Der Wert des Headers und der Fehlercode werden auf den gleichen Wert gesetzt. Für weitere Informationen siehe [[mw:Special:MyLanguage/API:Errors_and_warnings|API: Fehler und Warnungen]].\n\n<p class=\"mw-apisandbox-link\"><strong>Testen:</strong> Zum einfachen Testen von API-Anfragen, siehe [[Special:ApiSandbox]].</p>",
+       "apihelp-main-extended-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:Special:MyLanguage/API:Main_page|Dokumentation]]\n* [[mw:Special:MyLanguage/API:FAQ|Häufig gestellte Fragen]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api Mailingliste]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce API-Ankündigungen]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Fehlerberichte und Anfragen]\n</div>\n<strong>Status:</strong> Die MediaWiki-API ist eine ausgereifte und stabile Schnittstelle, die aktiv unterstützt und verbessert wird. Während wir versuchen, dies zu vermeiden, können wir gelegentlich Breaking Changes erforderlich machen. Abonniere die [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ MediaWiki-API-Ankündigungs-Mailingliste] für Mitteilungen zu Aktualisierungen.\n\n<strong>Fehlerhafte Anfragen:</strong> Wenn fehlerhafte Anfragen an die API gesendet werden, wird ein HTTP-Header mit dem Schlüssel „MediaWiki-API-Error“ gesendet. Der Wert des Headers und der Fehlercode werden auf den gleichen Wert gesetzt. Für weitere Informationen siehe [[mw:Special:MyLanguage/API:Errors_and_warnings|API: Fehler und Warnungen]].\n\n<p class=\"mw-apisandbox-link\"><strong>Testen:</strong> Zum einfachen Testen von API-Anfragen, siehe [[Special:ApiSandbox]].</p>",
        "apihelp-main-param-action": "Auszuführende Aktion.",
        "apihelp-main-param-format": "Format der Ausgabe.",
        "apihelp-main-param-maxlag": "maxlag kann verwendet werden, wenn MediaWiki auf einem datenbankreplizierten Cluster installiert ist. Um weitere Replikationsrückstände zu verhindern, lässt dieser Parameter den Client warten, bis der Replikationsrückstand kleiner als der angegebene Wert (in Sekunden) ist. Bei einem größerem Rückstand wird der Fehlercode <samp>maxlag</samp> zurückgegeben mit einer Nachricht wie <samp>Waiting for $host: $lag seconds lagged</samp>.<br />Siehe [[mw:Special:MyLanguage/Manual:Maxlag_parameter|Handbuch: Maxlag parameter]] für weitere Informationen.",
index 8d7a61c..3bbe399 100644 (file)
@@ -7,7 +7,7 @@
        },
 
        "apihelp-main-summary": "",
-       "apihelp-main-extended-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:Special:MyLanguage/API:Main_page|Documentation]]\n* [[mw:Special:MyLanguage/API:FAQ|FAQ]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api Mailing list]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce API Announcements]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Bugs & requests]\n</div>\n<strong>Status:</strong> All features shown on this page should be working, but the API is still in active development, and may change at any time. Subscribe to [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ the mediawiki-api-announce mailing list] for notice of updates.\n\n<strong>Erroneous requests:</strong> When erroneous requests are sent to the API, an HTTP header will be sent with the key \"MediaWiki-API-Error\" and then both the value of the header and the error code sent back will be set to the same value. For more information see [[mw:Special:MyLanguage/API:Errors_and_warnings|API: Errors and warnings]].\n\n<p class=\"mw-apisandbox-link\"><strong>Testing:</strong> For ease of testing API requests, see [[Special:ApiSandbox]].</p>",
+       "apihelp-main-extended-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:Special:MyLanguage/API:Main_page|Documentation]]\n* [[mw:Special:MyLanguage/API:FAQ|FAQ]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api Mailing list]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce API Announcements]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Bugs & requests]\n</div>\n<strong>Status:</strong> The MediaWiki API is a mature and stable interface that is actively supported and improved. While we try to avoid it, we may ocassionally need to make breaking changes; subscribe to [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ the mediawiki-api-announce mailing list] for notice of updates.\n\n<strong>Erroneous requests:</strong> When erroneous requests are sent to the API, an HTTP header will be sent with the key \"MediaWiki-API-Error\" and then both the value of the header and the error code sent back will be set to the same value. For more information see [[mw:Special:MyLanguage/API:Errors_and_warnings|API: Errors and warnings]].\n\n<p class=\"mw-apisandbox-link\"><strong>Testing:</strong> For ease of testing API requests, see [[Special:ApiSandbox]].</p>",
        "apihelp-main-param-action": "Which action to perform.",
        "apihelp-main-param-format": "The format of the output.",
        "apihelp-main-param-maxlag": "Maximum lag can be used when MediaWiki is installed on a database replicated cluster. To save actions causing any more site replication lag, this parameter can make the client wait until the replication lag is less than the specified value. In case of excessive lag, error code <samp>maxlag</samp> is returned with a message like <samp>Waiting for $host: $lag seconds lagged</samp>.<br />See [[mw:Special:MyLanguage/Manual:Maxlag_parameter|Manual: Maxlag parameter]] for more information.",
index 4c3b74d..daa88d5 100644 (file)
@@ -33,7 +33,7 @@
                        "Kenjiraw"
                ]
        },
-       "apihelp-main-extended-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:Special:MyLanguage/API:Main_page|Documentation]]\n* [[mw:Special:MyLanguage/API:FAQ|FAQ]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api Liste de diffusion]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce Annonces de l’API]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Bogues et demandes]\n</div>\n<strong>État :</strong> Toutes les fonctionnalités affichées sur cette page devraient fonctionner, mais l’API est encore en cours de développement et peut changer à tout moment. Inscrivez-vous à [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ la liste de diffusion mediawiki-api-announce] pour être informé des mises à jour.\n\n<strong>Requêtes erronées :</strong> Si des requêtes erronées sont envoyées à l’API, un entête HTTP sera renvoyé avec la clé « MediaWiki-API-Error ». La valeur de cet entête et le code d’erreur renvoyé prendront la même valeur. Pour plus d’information, voyez [[mw:Special:MyLanguage/API:Errors_and_warnings|API: Errors and warnings]].\n\n<p class=\"mw-apisandbox-link\"><strong>Test :</strong> Pour faciliter le test des requêtes de l’API, voyez [[Special:ApiSandbox]].</p>",
+       "apihelp-main-extended-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:Special:MyLanguage/API:Main_page|Documentation]]\n* [[mw:Special:MyLanguage/API:FAQ|FAQ]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api Liste de diffusion]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce Annonces de l’API]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Bogues et demandes]\n</div>\n<strong>État :</strong>L’API MédiaWiki est une interface stable et mature qui est supportée et améliorée de façon active. Bien que nous essayions de l’éviter, nous pouvons avoir parfois besoin de faire des modifications impactantes ; inscrivez-vous à [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ la liste de diffusion mediawiki-api-announce] pour être informé des mises à jour.\n\n<strong>Requêtes erronées :</strong> Si des requêtes erronées sont envoyées à l’API, un entête HTTP sera renvoyé avec la clé « MediaWiki-API-Error ». La valeur de cet entête et le code d’erreur renvoyé prendront la même valeur. Pour plus d’information, voyez [[mw:Special:MyLanguage/API:Errors_and_warnings|API: Errors and warnings]].\n\n<p class=\"mw-apisandbox-link\"><strong>Test :</strong> Pour faciliter le test des requêtes de l’API, voyez [[Special:ApiSandbox]].</p>",
        "apihelp-main-param-action": "Quelle action effectuer.",
        "apihelp-main-param-format": "Le format de sortie.",
        "apihelp-main-param-maxlag": "La latence maximale peut être utilisée quand MédiaWiki est installé sur un cluster de base de données répliqué. Pour éviter des actions provoquant un supplément de latence de réplication de site, ce paramètre peut faire attendre le client jusqu’à ce que la latence de réplication soit inférieure à une valeur spécifiée. En cas de latence excessive, le code d’erreur <samp>maxlag</samp> est renvoyé avec un message tel que <samp>Attente de $host : $lag secondes de délai</samp>.<br />Voyez [[mw:Special:MyLanguage/Manual:Maxlag_parameter|Manuel: Maxlag parameter]] pour plus d’information.",
index 66b0942..38d2901 100644 (file)
@@ -19,7 +19,7 @@
                        "Margherita.mignanelli"
                ]
        },
-       "apihelp-main-extended-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:Special:MyLanguage/API:Main_page|Documentazione]] (in inglese)\n* [[mw:Special:MyLanguage/API:FAQ|FAQ]] (in inglese)\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api Mailing list]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce Annunci sull'API]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Bug & richieste]\n</div>\n<strong>Stato:</strong> tutte le funzioni e caratteristiche mostrate su questa pagina dovrebbero funzionare, ma le API sono ancora in fase attiva di sviluppo, e potrebbero cambiare in qualsiasi momento. Iscriviti alla [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ la mailing list sugli annunci delle API MediaWiki] per essere informato sugli aggiornamenti.\n\n<strong>Istruzioni sbagliate:</strong> quando vengono impartite alle API delle istruzioni sbagliate, un'intestazione HTTP verrà inviata col messaggio \"MediaWiki-API-Error\" e, sia il valore dell'intestazione, sia il codice d'errore, verranno impostati con lo stesso valore. Per maggiori informazioni leggi [[mw:Special:MyLanguage/API:Errors_and_warnings|API:Errori ed avvertimenti]] (in inglese).\n\n<p class=\"mw-apisandbox-link\"><strong>Test:</strong> per testare facilmente le richieste API, vedi [[Special:ApiSandbox]].</p>",
+       "apihelp-main-extended-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:Special:MyLanguage/API:Main_page|Documentazione]] (in inglese)\n* [[mw:Special:MyLanguage/API:FAQ|FAQ]] (in inglese)\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api Mailing list]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce Annunci sull'API]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Bug & richieste]\n</div>\n<strong>Stato:</strong> l'API MediaWiki è un'interfaccia matura e stabile che è attivamente supportata e migliorata. Anche se cerchiamo di evitarlo, potremmo dover fare delle modifiche che causano malfunzionamenti; iscriviti alla [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ mailing list sugli annunci delle API MediaWiki] per essere informato sugli aggiornamenti.\n\n<strong>Istruzioni sbagliate:</strong> quando vengono impartite alle API delle istruzioni sbagliate, un'intestazione HTTP verrà inviata col messaggio \"MediaWiki-API-Error\" e, sia il valore dell'intestazione, sia il codice d'errore, verranno impostati con lo stesso valore. Per maggiori informazioni leggi [[mw:Special:MyLanguage/API:Errors_and_warnings|API:Errori ed avvertimenti]] (in inglese).\n\n<p class=\"mw-apisandbox-link\"><strong>Test:</strong> per testare facilmente le richieste API, vedi [[Special:ApiSandbox]].</p>",
        "apihelp-main-param-action": "Azione da compiere.",
        "apihelp-main-param-format": "Formato dell'output.",
        "apihelp-main-param-assert": "Verifica che l'utente abbia effettuato l'accesso se si è impostato <kbd>user</kbd>, o che abbia i permessi di bot se si è impostato <kbd>bot</kbd>.",
index f51b03f..80f161b 100644 (file)
@@ -12,7 +12,8 @@
                        "Suchichi02",
                        "Kkairri",
                        "ネイ",
-                       "Omotecho"
+                       "Omotecho",
+                       "Yusuke1109"
                ]
        },
        "apihelp-main-extended-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:Special:MyLanguage/API:Main_page|説明文書]]\n* [[mw:Special:MyLanguage/API:FAQ|よくある質問]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api メーリングリスト]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce API 告知]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R バグの報告とリクエスト]\n</div>\n<strong>状態:</strong> このページに表示されている機能は全て動作するはずですが、この API は未だ活発に開発されており、変更される可能性があります。アップデートの通知を受け取るには、[https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ the mediawiki-api-announce メーリングリスト]に参加してください。\n\n<strong>誤ったリクエスト:</strong> 誤ったリクエストが API に送られた場合、\"MediaWiki-API-Error\" HTTP ヘッダーが送信され、そのヘッダーの値と送り返されるエラーコードは同じ値にセットされます。より詳しい情報は [[mw:Special:MyLanguage/API:Errors_and_warnings|API: Errors and warnings]] を参照してください。\n\n<p class=\"mw-apisandbox-link\"><strong>テスト:</strong> API のリクエストのテストは、[[Special:ApiSandbox]]で簡単に行えます。</p>",
@@ -35,7 +36,7 @@
        "apihelp-block-param-autoblock": "その利用者が最後に使用したIPアドレスと、ブロック後に編集を試みた際のIPアドレスを自動的にブロックします。",
        "apihelp-block-param-noemail": "Wikiを通して電子メールを送信することを禁止します。(<code>blockemail</code> 権限が必要です)",
        "apihelp-block-param-hidename": "ブロック記録から利用者名を秘匿します。(<code>hideuser</code> 権限が必要です)",
-       "apihelp-block-param-allowusertalk": "自身のトークページの編集を許可する (<var>[[mw:Manual:$wgBlockAllowsUTEdit|$wgBlockAllowsUTEdit]]</var> に依存)。",
+       "apihelp-block-param-allowusertalk": "自身のトークページの編集を許可する (<var>[[mw:Special:MyLanguage/Manual:$wgBlockAllowsUTEdit|$wgBlockAllowsUTEdit]]</var> に依存)。",
        "apihelp-block-param-reblock": "その利用者がすでにブロックされている場合、ブロックを上書きします。",
        "apihelp-block-param-watchuser": "その利用者またはIPアドレスの利用者ページとトークページをウォッチします。",
        "apihelp-block-example-ip-simple": "IPアドレス <kbd>192.0.2.5</kbd> を <kbd>First strike<kbd> という理由で3日ブロックする",
        "apihelp-parse-paramvalue-prop-displaytitle": "構文解析されたウィキテキストのタイトルを追加します。",
        "apihelp-parse-paramvalue-prop-headitems": "ページの <code>&lt;head&gt;</code> の中に入れてアイテムを提供します。",
        "apihelp-parse-paramvalue-prop-headhtml": "ページの解析された <code>&lt;head&gt;</code> を与える。",
-       "apihelp-parse-paramvalue-prop-jsconfigvars": "ページに固有のJavaScriptの設定変数を提供します。",
+       "apihelp-parse-paramvalue-prop-jsconfigvars": "ページに固有のJavaScriptの設定変数を提供します。適用するには、<code>mw.config.set()</code>を使用します。",
        "apihelp-parse-paramvalue-prop-encodedjsconfigvars": "JSON文字列としてページに固有のJavaScriptの設定変数を提供します。",
        "apihelp-parse-paramvalue-prop-indicators": "ページ上で使用されるページのステータスインジケータのHTMLを提供します。",
        "apihelp-parse-paramvalue-prop-iwlinks": "構文解析されたウィキテキスト内でウィキ間リンクを提供します。",
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 fec224e..a2a0e79 100644 (file)
@@ -17,7 +17,7 @@
                        "Hamilton Abreu"
                ]
        },
-       "apihelp-main-extended-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:Special:MyLanguage/API:Main_page|Documentação]]\n* [[mw:Special:MyLanguage/API:FAQ|FAQ]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api Lista de discussão]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce Anúncios da API]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Erros e pedidos]\n</div>\n<strong>Estado:</strong> Todas as funcionalidades mostradas nesta página devem ter o comportamento documentado, mas a API ainda está em desenvolvimento ativo e pode ser alterada a qualquer momento. Inscreva-se na [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ lista de discussão mediawiki-api-announce] para ser informado acerca das atualizações.\n\n<strong>Pedidos incorretos:</strong> Quando são enviados pedidos incorretos à API, será devolvido um cabeçalho HTTP com a chave \"MediaWiki-API-Error\" e depois tanto o valor desse cabeçalho como o código de erro devolvido serão definidos com o mesmo valor. Para mais informação, consulte [[mw:Special:MyLanguage/API:Errors_and_warnings|API:Erros e avisos]].\n\n<p class=\"mw-apisandbox-link\">\n<strong>Testes:</strong> Para testar facilmente pedidos à API, visite [[Special:ApiSandbox|Testes da API]].\n</p>",
+       "apihelp-main-extended-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:Special:MyLanguage/API:Main_page|Documentação]]\n* [[mw:Special:MyLanguage/API:FAQ|FAQ]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api Lista de discussão]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce Anúncios da API]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Erros e pedidos]\n</div>\n<strong>Estado:</strong> A API MediaWiki é uma interface madura e estável que é ativamente suportada e aprimorada. Enquanto tentamos evitá-lo, talvez ocortamente precisemos fazer mudanças de ruptura; se inscrever [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ lista de discussão mediawiki-api-announce] para ser informado acerca das atualizações.\n\n<strong>Pedidos incorretos:</strong> Quando são enviados pedidos incorretos à API, será devolvido um cabeçalho HTTP com a chave \"MediaWiki-API-Error\" e depois tanto o valor desse cabeçalho como o código de erro devolvido serão definidos com o mesmo valor. Para mais informação, consulte [[mw:Special:MyLanguage/API:Errors_and_warnings|API:Erros e avisos]].\n\n<p class=\"mw-apisandbox-link\">\n<strong>Testes:</strong> Para testar facilmente pedidos à API, visite [[Special:ApiSandbox|Testes da API]].\n</p>",
        "apihelp-main-param-action": "Qual ação executar.",
        "apihelp-main-param-format": "O formato da saída.",
        "apihelp-main-param-maxlag": "O atraso máximo pode ser usado quando o MediaWiki está instalado em um cluster replicado no banco de dados. Para salvar as ações que causam mais atraso na replicação do site, esse parâmetro pode fazer o cliente aguardar até que o atraso da replicação seja menor do que o valor especificado. Em caso de atraso excessivo, o código de erro <samp>maxlag</samp> é retornado com uma mensagem como <samp>Waiting for $host: $lag seconds lagged</samp>.<br />Veja [[mw:Special:MyLanguage/Manual:Maxlag_parameter|Manual: Maxlag parameter]] para mais informações.",
index c18160a..14cf5aa 100644 (file)
@@ -12,7 +12,7 @@
                        "Waldir"
                ]
        },
-       "apihelp-main-extended-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:Special:MyLanguage/API:Main_page|Documentação]]\n* [[mw:Special:MyLanguage/API:FAQ|FAQ]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api Lista de discussão]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce Anúncios da API]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Erros e pedidos]\n</div>\n<strong>Estado:</strong> Todas as funcionalidades mostradas nesta página devem ter o comportamento documentado, mas a API ainda está em desenvolvimento ativo e pode ser alterada a qualquer momento. Inscreva-se na [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ lista de discussão mediawiki-api-announce] para ser informado acerca das atualizações.\n\n<strong>Pedidos incorretos:</strong> Quando são enviados pedidos incorretos à API, será devolvido um cabeçalho HTTP com a chave \"MediaWiki-API-Error\" e depois tanto o valor desse cabeçalho como o código de erro devolvido serão definidos com o mesmo valor. Para mais informação, consulte [[mw:Special:MyLanguage/API:Errors_and_warnings|API:Erros e avisos]].\n\n<p class=\"mw-apisandbox-link\">\n<strong>Testes:</strong> Para testar facilmente pedidos à API, visite [[Special:ApiSandbox|Testes da API]].\n</p>",
+       "apihelp-main-extended-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:Special:MyLanguage/API:Main_page|Documentação]]\n* [[mw:Special:MyLanguage/API:FAQ|FAQ]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api Lista de divulgação]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce Anúncios da API]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Defeitos e pedidos]\n</div>\n<strong>Estado:</strong> A API do MediaWiki é uma interface consolidada e estável que é constantemente suportada e melhorada. Embora tentemos evitá-lo, podemos ocasionalmente realizar alterações disruptivas. Inscreva-se na [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ lista de distribuição mediawiki-api-announce] para receber notificações das atualizações.\n\n<strong>Pedidos incorretos:</strong> Quando são enviados pedidos incorretos à API, será devolvido um cabeçalho HTTP com a chave \"MediaWiki-API-Error\" e depois tanto o valor desse cabeçalho como o código de erro devolvido serão definidos com o mesmo valor. Para mais informação, consulte [[mw:Special:MyLanguage/API:Errors_and_warnings|API:Erros e avisos]].\n\n<p class=\"mw-apisandbox-link\"><strong>Testes:</strong> Para testar facilmente pedidos à API, visite [[Special:ApiSandbox|Testes da API]].</p>",
        "apihelp-main-param-action": "A operação a ser realizada.",
        "apihelp-main-param-format": "O formato do resultado.",
        "apihelp-main-param-maxlag": "O atraso máximo pode ser usado quando o MediaWiki é instalado num ''cluster'' de bases de dados replicadas. Para impedir que as operações causem ainda mais atrasos de replicação do sítio, este parâmetro pode fazer o cliente aguardar até que o atraso de replicação seja inferior ao valor especificado. Caso o atraso atual exceda esse valor, o código de erro <samp>maxlag</samp> é devolvido com uma mensagem como <samp>À espera do servidor $host: $lag segundos de atraso</samp>.<br />Consulte [[mw:Special:MyLanguage/Manual:Maxlag_parameter|Manual: Parâmetro maxlag]] para mais informações.",
        "apihelp-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 5eec830..461757d 100644 (file)
@@ -26,7 +26,7 @@
                        "NeverBehave"
                ]
        },
-       "apihelp-main-extended-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:Special:MyLanguage/API:Main_page|文档]]\n* [[mw:Special:MyLanguage/API:FAQ|常见问题]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api 邮件列表]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce API公告]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R 程序错误与功能请求]\n</div>\n<strong>状态信息:</strong>本页所展示的所有特性都应正常工作,但是API仍在开发当中,将会随时变化。请订阅[https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ mediawiki-api-announce 邮件列表]以便获得更新通知。\n\n<strong>错误请求:</strong>当API收到错误请求时,HTTP header将会返回一个包含\"MediaWiki-API-Error\"的值,随后header的值与error code将会送回并设置为相同的值。详细信息请参阅[[mw:Special:MyLanguage/API:Errors_and_warnings|API:错误与警告]]。\n\n<p class=\"mw-apisandbox-link\"><strong>测试中:</strong>测试API请求的易用性,请参见[[Special:ApiSandbox]]。</p>",
+       "apihelp-main-extended-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:Special:MyLanguage/API:Main_page|文档]]\n* [[mw:Special:MyLanguage/API:FAQ|常见问题]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api 邮件列表]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce API公告]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R 程序错误与功能请求]\n</div>\n<strong>状态信息:</strong>MediaWiki API是一个成熟稳定的,不断受到支持和改进的界面。尽管我们尽力避免,但偶尔也需要作出重大更新;请订阅[https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ mediawiki-api-announce 邮件列表]以便获得更新通知。\n\n<strong>错误请求:</strong>当API收到错误请求时,HTTP header将会返回一个包含\"MediaWiki-API-Error\"的值,随后header的值与error code将会送回并设置为相同的值。详细信息请参阅[[mw:Special:MyLanguage/API:Errors_and_warnings|API:错误与警告]]。\n\n<p class=\"mw-apisandbox-link\"><strong>测试中:</strong>测试API请求的易用性,请参见[[Special:ApiSandbox]]。</p>",
        "apihelp-main-param-action": "要执行的操作。",
        "apihelp-main-param-format": "输出的格式。",
        "apihelp-main-param-maxlag": "最大延迟可被用于MediaWiki安装于数据库复制集中。要保存导致更多网站复制延迟的操作,此参数可使客户端等待直到复制延迟少于指定值时。万一发生过多延迟,错误代码<samp>maxlag</samp>会返回消息,例如<samp>等待$host中:延迟$lag秒</samp>。<br />参见[[mw:Special:MyLanguage/Manual:Maxlag_parameter|手册:Maxlag参数]]以获取更多信息。",
index 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 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 3811da3..7020159 100644 (file)
@@ -119,6 +119,11 @@ class EtcdConfig implements Config, LoggerAwareInterface {
                return $this->procCache['config'][$name];
        }
 
+       public function getModifiedIndex() {
+               $this->load();
+               return $this->procCache['modifiedIndex'];
+       }
+
        /**
         * @throws ConfigException
         */
@@ -151,13 +156,17 @@ class EtcdConfig implements Config, LoggerAwareInterface {
                                // refresh the cache from etcd, using a mutex to reduce stampedes...
                                if ( $this->srvCache->lock( $key, 0, $this->baseCacheTTL ) ) {
                                        try {
-                                               list( $config, $error, $retry ) = $this->fetchAllFromEtcd();
-                                               if ( is_array( $config ) ) {
+                                               $etcdResponse = $this->fetchAllFromEtcd();
+                                               $error = $etcdResponse['error'];
+                                               if ( is_array( $etcdResponse['config'] ) ) {
                                                        // Avoid having all servers expire cache keys at the same time
                                                        $expiry = microtime( true ) + $this->baseCacheTTL;
                                                        $expiry += mt_rand( 0, 1e6 ) / 1e6 * $this->skewCacheTTL;
-
-                                                       $data = [ 'config' => $config, 'expires' => $expiry ];
+                                                       $data = [
+                                                               'config' => $etcdResponse['config'],
+                                                               'expires' => $expiry,
+                                                               'modifiedIndex' => $etcdResponse['modifiedIndex']
+                                                       ];
                                                        $this->srvCache->set( $key, $data, BagOStuff::TTL_INDEFINITE );
 
                                                        $this->logger->info( "Refreshed stale etcd configuration cache." );
@@ -165,7 +174,7 @@ class EtcdConfig implements Config, LoggerAwareInterface {
                                                        return WaitConditionLoop::CONDITION_REACHED;
                                                } else {
                                                        $this->logger->error( "Failed to fetch configuration: $error" );
-                                                       if ( !$retry ) {
+                                                       if ( !$etcdResponse['retry'] ) {
                                                                // Fail fast since the error is likely to keep happening
                                                                return WaitConditionLoop::CONDITION_FAILED;
                                                        }
@@ -195,9 +204,10 @@ class EtcdConfig implements Config, LoggerAwareInterface {
        }
 
        /**
-        * @return array (config array or null, error string, allow retries)
+        * @return array (containing the keys config, error, retry, modifiedIndex)
         */
        public function fetchAllFromEtcd() {
+               // TODO: inject DnsSrvDiscoverer in order to be able to test this method
                $dsd = new DnsSrvDiscoverer( $this->host );
                $servers = $dsd->getServers();
                if ( !$servers ) {
@@ -209,8 +219,8 @@ class EtcdConfig implements Config, LoggerAwareInterface {
                        $server = $dsd->pickServer( $servers );
                        $host = IP::combineHostAndPort( $server['target'], $server['port'] );
                        // Try to load the config from this particular server
-                       list( $config, $error, $retry ) = $this->fetchAllFromEtcdServer( $host );
-                       if ( is_array( $config ) || !$retry ) {
+                       $response = $this->fetchAllFromEtcdServer( $host );
+                       if ( is_array( $response['config'] ) || $response['retry'] ) {
                                break;
                        }
 
@@ -218,12 +228,12 @@ class EtcdConfig implements Config, LoggerAwareInterface {
                        $servers = $dsd->removeServer( $server, $servers );
                } while ( $servers );
 
-               return [ $config, $error, $retry ];
+               return $response;
        }
 
        /**
         * @param string $address Host and port
-        * @return array (config array or null, error string, whether to allow retries)
+        * @return array (containing the keys config, error, retry, modifiedIndex)
         */
        protected function fetchAllFromEtcdServer( $address ) {
                // Retrieve all the values under the MediaWiki config directory
@@ -233,19 +243,21 @@ class EtcdConfig implements Config, LoggerAwareInterface {
                        'headers' => [ 'content-type' => 'application/json' ]
                ] );
 
+               $response = [ 'config' => null, 'error' => null, 'retry' => false, 'modifiedIndex' => 0 ];
+
                static $terminalCodes = [ 404 => true ];
                if ( $rcode < 200 || $rcode > 399 ) {
-                       return [
-                               null,
-                               strlen( $rerr ) ? $rerr : "HTTP $rcode ($rdesc)",
-                               empty( $terminalCodes[$rcode] )
-                       ];
+                       $response['error'] = strlen( $rerr ) ? $rerr : "HTTP $rcode ($rdesc)";
+                       $response['retry'] = empty( $terminalCodes[$rcode] );
+                       return $response;
                }
+
                try {
-                       return [ $this->parseResponse( $rbody ), null, false ];
+                       $parsedResponse = $this->parseResponse( $rbody );
                } catch ( EtcdConfigParseError $e ) {
-                       return [ null, $e->getMessage(), false ];
+                       $parsedResponse = [ 'error' => $e->getMessage() ];
                }
+               return array_merge( $response, $parsedResponse );
        }
 
        /**
@@ -264,8 +276,8 @@ class EtcdConfig implements Config, LoggerAwareInterface {
                                "Unexpected JSON response: Missing or invalid node at top level." );
                }
                $config = [];
-               $this->parseDirectory( '', $info['node'], $config );
-               return $config;
+               $lastModifiedIndex = $this->parseDirectory( '', $info['node'], $config );
+               return [ 'modifiedIndex' => $lastModifiedIndex, 'config' => $config ];
        }
 
        /**
@@ -275,8 +287,10 @@ class EtcdConfig implements Config, LoggerAwareInterface {
         * @param string $dirName The relative directory name
         * @param array $dirNode The decoded directory node
         * @param array &$config The output array
+        * @return int lastModifiedIndex The maximum last modified index across all keys in the directory
         */
        protected function parseDirectory( $dirName, $dirNode, &$config ) {
+               $lastModifiedIndex = 0;
                if ( !isset( $dirNode['nodes'] ) ) {
                        throw new EtcdConfigParseError(
                                "Unexpected JSON response in dir '$dirName'; missing 'nodes' list." );
@@ -290,16 +304,19 @@ class EtcdConfig implements Config, LoggerAwareInterface {
                        $baseName = basename( $node['key'] );
                        $fullName = $dirName === '' ? $baseName : "$dirName/$baseName";
                        if ( !empty( $node['dir'] ) ) {
-                               $this->parseDirectory( $fullName, $node, $config );
+                               $lastModifiedIndex = max(
+                                       $this->parseDirectory( $fullName, $node, $config ),
+                                       $lastModifiedIndex );
                        } else {
                                $value = $this->unserialize( $node['value'] );
                                if ( !is_array( $value ) || !array_key_exists( 'val', $value ) ) {
                                        throw new EtcdConfigParseError( "Failed to parse value for '$fullName'." );
                                }
-
+                               $lastModifiedIndex = max( $node['modifiedIndex'], $lastModifiedIndex );
                                $config[$fullName] = $value['val'];
                        }
                }
+               return $lastModifiedIndex;
        }
 
        /**
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 bc20aa0..5beef31 100644 (file)
@@ -270,28 +270,22 @@ class WikitextContent extends TextContent {
                        return false;
                }
 
-               switch ( $wgArticleCountMethod ) {
-                       case 'any':
-                               return true;
-                       case 'comma':
-                               $text = $this->getNativeData();
-                               return strpos( $text, ',' ) !== false;
-                       case 'link':
-                               if ( $hasLinks === null ) { # not known, find out
-                                       if ( !$title ) {
-                                               $context = RequestContext::getMain();
-                                               $title = $context->getTitle();
-                                       }
-
-                                       $po = $this->getParserOutput( $title, null, null, false );
-                                       $links = $po->getLinks();
-                                       $hasLinks = !empty( $links );
+               if ( $wgArticleCountMethod === 'link' ) {
+                       if ( $hasLinks === null ) { # not known, find out
+                               if ( !$title ) {
+                                       $context = RequestContext::getMain();
+                                       $title = $context->getTitle();
                                }
 
-                               return $hasLinks;
+                               $po = $this->getParserOutput( $title, null, null, false );
+                               $links = $po->getLinks();
+                               $hasLinks = !empty( $links );
+                       }
+
+                       return $hasLinks;
                }
 
-               return false;
+               return true;
        }
 
        /**
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..7cb2950 100644 (file)
@@ -66,6 +66,12 @@ class SiteStatsUpdate implements DeferrableUpdate, MergeableUpdate {
        public static function factory( array $deltas ) {
                $update = new self( 0, 0, 0 );
 
+               foreach ( $deltas as $name => $unused ) {
+                       if ( !in_array( $name, self::$counters ) ) { // T187585
+                               throw new UnexpectedValueException( __METHOD__ . ": no field called '$name'" );
+                       }
+               }
+
                foreach ( self::$counters as $field ) {
                        if ( isset( $deltas[$field] ) && $deltas[$field] ) {
                                $update->$field = $deltas[$field];
@@ -148,18 +154,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',
index fa30d68..037a80f 100644 (file)
@@ -1294,7 +1294,7 @@ class DifferenceEngine extends ContextSource {
 
                if ( !$diff && !$otitle ) {
                        $header .= "
-                       <tr style=\"vertical-align: top;\" lang=\"{$userLang}\">
+                       <tr class=\"diff-title\" lang=\"{$userLang}\">
                        <td class=\"diff-ntitle\">{$ntitle}</td>
                        </tr>";
                        $multiColspan = 1;
@@ -1313,7 +1313,7 @@ class DifferenceEngine extends ContextSource {
                        }
                        if ( $otitle || $ntitle ) {
                                $header .= "
-                               <tr style=\"vertical-align: top;\" lang=\"{$userLang}\">
+                               <tr class=\"diff-title\" lang=\"{$userLang}\">
                                <td colspan=\"$colspan\" class=\"diff-otitle\">{$otitle}</td>
                                <td colspan=\"$colspan\" class=\"diff-ntitle\">{$ntitle}</td>
                                </tr>";
@@ -1321,12 +1321,12 @@ class DifferenceEngine extends ContextSource {
                }
 
                if ( $multi != '' ) {
-                       $header .= "<tr><td colspan=\"{$multiColspan}\" style=\"text-align: center;\" " .
+                       $header .= "<tr><td colspan=\"{$multiColspan}\" " .
                                "class=\"diff-multi\" lang=\"{$userLang}\">{$multi}</td></tr>";
                }
                if ( $notice != '' ) {
-                       $header .= "<tr><td colspan=\"{$multiColspan}\" style=\"text-align: center;\" " .
-                               "lang=\"{$userLang}\">{$notice}</td></tr>";
+                       $header .= "<tr><td colspan=\"{$multiColspan}\" " .
+                               "class=\"diff-notice\" lang=\"{$userLang}\">{$notice}</td></tr>";
                }
 
                return $header . $diff . "</table>";
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..0d55454 100644 (file)
@@ -1047,7 +1047,7 @@ abstract class DatabaseUpdater {
         * Sets the number of active users in the site_stats table
         */
        protected function doActiveUsersInit() {
-               $activeUsers = $this->db->selectField( 'site_stats', 'ss_active_users', false, __METHOD__ );
+               $activeUsers = $this->db->selectField( 'site_stats', 'ss_active_users', '', __METHOD__ );
                if ( $activeUsers == -1 ) {
                        $activeUsers = $this->db->selectField( 'recentchanges',
                                'COUNT( DISTINCT rc_user_text )',
@@ -1227,8 +1227,28 @@ abstract class DatabaseUpdater {
                                "maintenance/migrateComments.php.\n"
                        );
                        $task = $this->maintenance->runChild( MigrateComments::class, 'migrateComments.php' );
-                       $task->execute();
-                       $this->output( "done.\n" );
+                       $ok = $task->execute();
+                       $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..bce4690 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' ],
                ];
        }
 
@@ -472,6 +474,17 @@ class MysqlUpdater extends DatabaseUpdater {
                        return;
                }
 
+               $insertOpts = [ 'IGNORE' ];
+               $selectOpts = [];
+
+               // If wl_id exists, make insertSelect() more replication-safe by
+               // ordering on that column. If not, hint that it should be safe anyway.
+               if ( $this->db->fieldExists( 'watchlist', 'wl_id', __METHOD__ ) ) {
+                       $selectOpts['ORDER BY'] = 'wl_id';
+               } else {
+                       $insertOpts[] = 'NO_AUTO_COLUMNS';
+               }
+
                $this->output( "Adding missing watchlist talk page rows... " );
                $this->db->insertSelect( 'watchlist', 'watchlist',
                        [
@@ -479,7 +492,7 @@ class MysqlUpdater extends DatabaseUpdater {
                                'wl_namespace' => 'wl_namespace | 1',
                                'wl_title' => 'wl_title',
                                'wl_notificationtimestamp' => 'wl_notificationtimestamp'
-                       ], [ 'NOT (wl_namespace & 1)' ], __METHOD__, 'IGNORE' );
+                       ], [ 'NOT (wl_namespace & 1)' ], __METHOD__, $insertOpts, $selectOpts );
                $this->output( "done.\n" );
 
                $this->output( "Adding missing watchlist subject page rows... " );
@@ -489,7 +502,7 @@ class MysqlUpdater extends DatabaseUpdater {
                                'wl_namespace' => 'wl_namespace & ~1',
                                'wl_title' => 'wl_title',
                                'wl_notificationtimestamp' => 'wl_notificationtimestamp'
-                       ], [ 'wl_namespace & 1' ], __METHOD__, 'IGNORE' );
+                       ], [ 'wl_namespace & 1' ], __METHOD__, $insertOpts, $selectOpts );
                $this->output( "done.\n" );
        }
 
@@ -901,7 +914,8 @@ class MysqlUpdater extends DatabaseUpdater {
                                        'tl_title' => 'pl_title'
                                ], [
                                        'pl_namespace' => 10
-                               ], __METHOD__
+                               ], __METHOD__,
+                               [ 'NO_AUTO_COLUMNS' ] // There's no "tl_id" auto-increment field
                        );
                }
                $this->output( "Done. Please run maintenance/refreshLinks.php for a more " .
index e5418e4..659a1d7 100644 (file)
@@ -165,35 +165,30 @@ class OracleInstaller extends DatabaseInstaller {
        }
 
        public function openConnection() {
-               $status = Status::newGood();
-               try {
-                       $db = new DatabaseOracle(
-                               $this->getVar( 'wgDBserver' ),
-                               $this->getVar( '_InstallUser' ),
-                               $this->getVar( '_InstallPassword' ),
-                               $this->getVar( '_InstallDBname' ),
-                               0,
-                               $this->getVar( 'wgDBprefix' )
-                       );
-                       $status->value = $db;
-               } catch ( DBConnectionError $e ) {
-                       $this->connError = $e->db->lastErrno();
-                       $status->fatal( 'config-connection-error', $e->getMessage() );
-               }
-
-               return $status;
+               return $this->doOpenConnection();
        }
 
        public function openSYSDBAConnection() {
+               return $this->doOpenConnection( DatabaseOracle::DBO_SYSDBA );
+       }
+
+       /**
+        * @param int $flags
+        * @return Status Status with DatabaseOracle or null as the value
+        */
+       private function doOpenConnection( $flags = 0 ) {
                $status = Status::newGood();
                try {
-                       $db = new DatabaseOracle(
-                               $this->getVar( 'wgDBserver' ),
-                               $this->getVar( '_InstallUser' ),
-                               $this->getVar( '_InstallPassword' ),
-                               $this->getVar( '_InstallDBname' ),
-                               DBO_SYSDBA,
-                               $this->getVar( 'wgDBprefix' )
+                       $db = Database::factory(
+                               'oracle',
+                               [
+                                       'host' => $this->getVar( 'wgDBserver' ),
+                                       'user' => $this->getVar( '_InstallUser' ),
+                                       'password' => $this->getVar( '_InstallPassword' ),
+                                       'dbname' => $this->getVar( '_InstallDBname' ),
+                                       'tablePrefix' => $this->getVar( 'wgDBprefix' ),
+                                       'flags' => $flags
+                               ]
                        );
                        $status->value = $db;
                } catch ( DBConnectionError $e ) {
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 0c93b3a..3a08647 100644 (file)
        "config-page-existingwiki": "Υπάρχον wiki",
        "config-help-restart": "Θέλετε να καταργήσετε όλα τα αποθηκευμένα δεδομένα που έχετε εισαγάγει και να επανεκκινήσετε τη διαδικασία εγκατάστασης;",
        "config-restart": "Ναι, επανεκκίνηση",
-       "config-welcome": "=== Î ÎµÏ\81ιβαλλονÏ\84ικοί Î­Î»ÎµÎ³Ï\87οι ===\nΤÏ\8eÏ\81α Î¸Î± Î³Î¯Î½Î¿Ï\85ν Î²Î±Ï\83ικοί Î­Î»ÎµÎ³Ï\87οι Î³Î¹Î± Î½Î± Î´Î¿Ï\8dμε Î±Î½ Î±Ï\85Ï\84Ï\8c Ï\84ο Ï\80εÏ\81ιβάλλον ÎµÎ¯Î½Î±Î¹ ÎºÎ±Ï\84άλληλο Î³Î¹Î± Ï\84ην ÎµÎ³ÎºÎ±Ï\84άÏ\83Ï\84αÏ\83η Ï\84οÏ\85 MediaWiki.\nÎ\98Ï\85μηθείÏ\84ε Î½Î± Ï\83Ï\85μÏ\80εÏ\81ιλάβεÏ\84ε Î±Ï\85Ï\84έÏ\82 Ï\84ιÏ\82 Ï\80ληÏ\81οÏ\86οÏ\81ίεÏ\82 ÎµÎ¬Î½ Î±Î½Î±Î¶Î·Ï\84ήÏ\83εÏ\84ε Ï\85Ï\80οÏ\83Ï\84ήÏ\81ιξη Î³Î¹Î± Ï\84ο Ï\80Ï\8eÏ\82 να ολοκληρώσετε την εγκατάσταση.",
-       "config-copyright": "=== Πνευματικά δικαιώματα και Όροι ===\n\n$1\n\nΑυτό το πρόγραμμα είναι ελεύθερο λογισμικό• μπορείτε να το αναδιανείμετε ή και να το τροποποιήσετε υπό τους όρους της Γενικής Άδειας Δημόσιας Χρήσης GNU, όπως αυτή δημοσιεύεται από το Ίδρυμα Ελεύθερου Λογισμικού• είτε της έκδοσης 2 της Άδειας, είτε (κατά την επιλογή σας) οποιασδήποτε μεταγενέστερης έκδοσης.\n\nΑυτό το πρόγραμμα διανέμεται με την ελπίδα ότι θα είναι χρήσιμο, αλλά <strong>χωρίς καμία εγγύηση</strong>• χωρίς καν την υπονοούμενη εγγύηση της <strong>εμπορευσιμότητας</strong> ή της <strong>καταλληλοτότητας για συγκεκριμένο σκοπό</strong>.\nΔείτε την Γενική Άδεια Δημόσιας Χρήσης GNU για περισσότερες λεπτομέρειες.\n\nΘα πρέπει να έχετε λάβει <doclink href=\"Copying\">ένα αντίγραφο της Γενικής Άδειας Δημόσιας Χρήσης GNU</doclink> μαζί με αυτό το πρόγραμμα• αν όχι, γράψτε στο Free Software Foundation,\n51 Franklin Street, Fifth Floor,\nBoston, MA 02110-1335\nUSA ή [https://www.gnu.org/copyleft/gpl.html διαβάστε online].",
+       "config-welcome": "=== Î\88λεγÏ\87οι Ï\84οÏ\85 Ï\80εÏ\81ιβάλλονÏ\84οÏ\82 ===\nΤÏ\8eÏ\81α Î¸Î± Î³Î¯Î½Î¿Ï\85ν Î²Î±Ï\83ικοί Î­Î»ÎµÎ³Ï\87οι Î³Î¹Î± Î½Î± Î´Î¿Ï\8dμε Î±Î½ Î±Ï\85Ï\84Ï\8c Ï\84ο Ï\80εÏ\81ιβάλλον ÎµÎ¯Î½Î±Î¹ ÎºÎ±Ï\84άλληλο Î³Î¹Î± Ï\84ην ÎµÎ³ÎºÎ±Ï\84άÏ\83Ï\84αÏ\83η Ï\84οÏ\85 MediaWiki.\nÎ\98Ï\85μηθείÏ\84ε Î½Î± Ï\83Ï\85μÏ\80εÏ\81ιλάβεÏ\84ε Î±Ï\85Ï\84έÏ\82 Ï\84ιÏ\82 Ï\80ληÏ\81οÏ\86οÏ\81ίεÏ\82 ÎµÎ¬Î½ Î±Î½Î±Î¶Î·Ï\84ήÏ\83εÏ\84ε Ï\85Ï\80οÏ\83Ï\84ήÏ\81ιξη Î³Î¹Î± να ολοκληρώσετε την εγκατάσταση.",
+       "config-copyright": "=== Πνευματικά δικαιώματα και όροι ===\n\n$1\n\nΑυτό το πρόγραμμα είναι ελεύθερο λογισμικό· μπορείτε να το αναδιανείμετε ή/και να το τροποποιήσετε υπό τους όρους της Γενικής Άδειας Δημόσιας Χρήσης GNU, όπως αυτή δημοσιεύεται από το Ίδρυμα Ελεύθερου Λογισμικού· είτε της έκδοσης 2 της Άδειας, είτε (κατ' επιλογήν σας) οποιασδήποτε μεταγενέστερης έκδοσης.\n\nΑυτό το πρόγραμμα διανέμεται με την ελπίδα ότι θα είναι χρήσιμο, αλλά <strong>χωρίς καμία εγγύηση</strong>· χωρίς καν τη σιωπηρή εγγύηση της <strong>εμπορευσιμότητας</strong> ή <strong>καταλληλότητας για συγκεκριμένο σκοπό</strong>.\nΔείτε τη Γενική Άδεια Δημόσιας Χρήσης GNU για περισσότερες λεπτομέρειες.\n\nΘα πρέπει να έχετε παραλάβει <doclink href=\"Copying\">ένα αντίγραφο της Γενικής Άδειας Δημόσιας Χρήσης GNU</doclink> μαζί με αυτό το πρόγραμμα· αν όχι, στείλτε ένα γράμμα στο Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA, ή [https://www.gnu.org/copyleft/gpl.html διαβάστε το διαδικτυακά].",
        "config-sidebar": "* [https://www.mediawiki.org Αρχική MediaWiki]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents Οδηγός Χρήστη]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents Οδηγός Διαχειριστή]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ Συχνές ερωτήσεις]\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-php": "H PHP $1 είναι εγκατεστημένη.",
        "config-env-hhvm": "Το HHVM $1 είναι εγκατεστημένο.",
-       "config-unicode-using-intl": "Χρησιμοποιώντας την [https://pecl.php.net/intl επέκταση intl PECL] για κανονικοποίηση Unicode.",
+       "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Εάν λειτουργείτε έναν ιστότοπο υψηλής επισκεψιμότητας, θα πρέπει να ρίξετε μια ματιά στην [https://www.mediawiki.org/wiki/Special:MyLanguage/Unicode_normalization_considerations κανονικοποίηση Unicode].",
        "config-no-db": "Δεν βρέθηκε κάποιο κατάλληλο πρόγραμμα οδήγησης βάσης δεδομένων! Θα πρέπει να εγκαταστήσετε ένα πρόγραμμα οδήγησης βάσης δεδομένων για  PHP.\nΟ παρακάτω {{PLURAL:$2|τύπος βάσης δεδομένων|τύποι βάσεων δεδομένων}} υποστηρίζονται: $1.\n\nΑν κάνετε compile την PHP μόνοι σας, ρυθμίστε ξανά τις παραμέτρους με κάποιον ενεργοποιημένο εξυπηρετητή βάσεων  δεδομένων, για παράδειγμα, χρησιμοποιώντας την εντολή <code>./configure --with-mysqli</code>.\nΕάν έχετε εγκαταστήσει την PHP από κάποιο πακέτο στο Debian ή στο Ubuntu, τότε θα πρέπει να εγκαταστήσετε επίσης, για παράδειγμα, το πακέτο <code>php5-mysql</code>.",
        "config-outdated-sqlite": "<strong>Προειδοποίηση:</strong> έχετε την SQLite έκδοση $1, που είναι χαμηλότερη από την ελάχιστη απαιτούμενη έκδοση $2. Η SQLite δεν θα είναι διαθέσιμη.",
        "config-apc": "Το [http://www.php.net/apc APC] είναι εγκατεστημένο",
        "config-wincache": "[https://www.iis.net/download/WinCacheForPhp Το WinCache] είναι εγκατεστημένο",
        "config-diff3-bad": "Το GNU diff3 δεν βρέθηκε.",
-       "config-git": "Βρέθηκε η Git έκδοση λογισμικού ελέγχου: <code>$1</code>.",
-       "config-git-bad": "Î\97 Git Î­ÎºÎ´Î¿Ï\83η Ï\84οÏ\85 Î»Î¿Î³Î¹Ï\83μικοÏ\8d ÎµÎ»Î­Î³Ï\87οÏ\85 δεν βρέθηκε.",
+       "config-git": "Βρέθηκε το λογισμικό ελέγχου εκδόσεων Git: <code>$1</code>.",
+       "config-git-bad": "Το Î»Î¿Î³Î¹Ï\83μικÏ\8c ÎµÎ»Î­Î³Ï\87οÏ\85 ÎµÎºÎ´Ï\8cÏ\83εÏ\89ν Git δεν βρέθηκε.",
        "config-no-uri": "<strong>Σφάλμα:</strong> Δεν ήταν δυνατό να καθοριστεί το τρέχον URI.\nΗ εγκατάσταση ματαιώθηκε.",
-       "config-using-server": "Χρησιμοποιείται το όνομα διακομιστή \"<nowiki>$1</nowiki>\".",
-       "config-using-uri": "Χρησιμοποιώντας την διεύθυνση URL του διακομιστή \"<nowiki>$1$2</nowiki>\".",
+       "config-using-server": "Χρησιμοποιείται το όνομα διακομιστή «<nowiki>$1</nowiki>».",
+       "config-using-uri": "Χρησιμοποιείται η διεύθυνση URL του διακομιστή «<nowiki>$1$2</nowiki>».",
+       "config-uploads-not-safe": "<strong>Προειδοποίηση:</strong> Ο προεπιλεγμένος σας κατάλογος για ανέβασμα <code>$1</code> είναι ευάλωτος σε εκτέλεση αυθαίρετων σεναρίων ενεργειών.\nΑν και το MediaWiki ελέγχει για απειλές ασφαλείας όλα τα αρχεία που ανεβαίνουν, συνιστάται ιδιαίτερα να [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Security#Upload_security κλείσετε αυτό το κενό ασφαλείας] πριν ενεργοποιήσετε το ανέβασμα αρχείων.",
+       "config-no-cli-uploads-check": "<strong>Προειδοποίηση:</strong> Ο προεπιλεγμένος σας κατάλογος για ανέβασμα (<code>$1</code>) δεν έχει ελεγχθεί για ευπάθεια σε εκτέλεση αυθαίρετων σεναρίων ενεργειών κατά τη διάρκεια της εγκατάστασης μέσω γραμμής εντολών.",
        "config-brokenlibxml": "Το σύστημά σας έχει έναν συνδυασμό εκδόσεων της PHP και της libxml2 που είναι προβληματικός και μπορεί να προκαλέσει καταστροφή κρυμμένων στοιχείων στο MediaWiki και σε άλλες εφαρμογές ιστού.\nΑναβαθμίστε σε libxml2 2.7.3 ή μεταγενέστερη έκδοση ([https://bugs.php.net/bug.php?id=45996 bug που έχει καταχωριστεί για την PHP]).\nΗ εγκατάσταση ματαιώθηκε.",
        "config-db-type": "Τύπος βάσης δεδομένων:",
        "config-db-host": "Φιλοξενία βάσης δεδομένων:",
+       "config-db-host-help": "Εάν ο διακομιστής βάσης δεδομένων σας βρίσκεται σε διαφορετικό διακομιστή, εισαγάγετε εδώ το όνομα του κεντρικού υπολογιστή ή τη διεύθυνση IP.\n\nΕάν χρησιμοποιείτε μοιραζόμενη φιλοξενία του ιστοτόπου σας, ο πάροχος φιλοξενίας σας θα πρέπει να σας δίνει το σωστό όνομα κεντρικού υπολογιστή στην τεκμηρίωση του.\n\nΕάν εγκαθιστάτε σε διακομιστή Windows και χρησιμοποιείτε MySQL, το «localhost» μπορεί να μην λειτουργεί ως όνομα διακομιστή. Εάν δεν λειτουργεί, δοκιμάστε «127.0.0.1» ως τοπική διεύθυνση IP.\n\nΕάν χρησιμοποιείτε PostgreSQL, αφήστε αυτό το πεδίο κενό για να συνδεθείτε μέσω υποδοχής Unix.",
        "config-db-host-oracle": "Βάση δεδομένων TNS:",
        "config-db-wiki-settings": "Αναγνώριση αυτού του wiki",
        "config-db-name": "Όνομα βάσης δεδομένων:",
-       "config-db-name-help": "Επιλέξτε ένα όνομα που ταιριάζει στο  wiki σας. Δεν πρέπει να περιέχει κενά διαστήματα.\n\nΕάν χρησιμοποιείτε κοινόχρηστο web hosting, ο πάροχος φιλοξενίας είτε θα σας δώσει ένα συγκεκριμένο όνομα βάσης δεδομένων για να χρησιμοποιήσετε ή θα σας δώσει τη δυνατότητα να δημιουργήσετε βάσεις δεδομένων μέσω ενός πίνακα ελέγχου.",
+       "config-db-name-help": "Επιλέξτε όνομα που να χαρακτηρίζει το wiki σας. Δεν πρέπει να περιέχει κενά διαστήματα.\n\nΕάν χρησιμοποιείτε μοιραζόμενη φιλοξενία του ιστοτόπου σας, ο πάροχος φιλοξενίας σας είτε θα σας δίνει να χρησιμοποιήσετε ένα συγκεκριμένο όνομα βάσης δεδομένων ή θα σας δίνει τη δυνατότητα να δημιουργείτε βάσεις δεδομένων μέσω κάποιου πίνακα ελέγχου.",
        "config-db-name-oracle": "Σχήμα βάσης δεδομένων:",
        "config-db-install-account": "Λογαριασμός χρήστη για την εγκατάσταση",
        "config-db-username": "Όνομα χρήστη βάσης δεδομένων:",
-       "config-db-password": "Κωδικός πρόσβασης βάσης δεδομένων:",
+       "config-db-password": "Συνθηματικό βάσης δεδομένων:",
+       "config-db-install-username": "Εισαγάγετε το όνομα χρήστη που θα χρησιμοποιηθεί για τη σύνδεση στη βάση δεδομένων κατά τη διάρκεια της διαδικασίας εγκατάστασης.\nΑυτό δεν είναι το όνομα χρήστη του λογαριασμού του MediaWiki· αυτό είναι το όνομα χρήστη για τη βάση δεδομένων σας.",
+       "config-db-install-password": "Εισαγάγετε το συνθηματικό που θα χρησιμοποιηθεί για τη σύνδεση στη βάση δεδομένων κατά τη διάρκεια της διαδικασίας εγκατάστασης.\nΑυτό δεν είναι το συνθηματικό του λογαριασμού του MediaWiki· αυτό είναι το συνθηματικό για τη βάση δεδομένων σας.",
        "config-db-install-help": "Εισαγάγετε το όνομα χρήστη και τον κωδικό πρόσβασης που θα χρησιμοποιηθεί για τη σύνδεση με τη βάση δεδομένων κατά τη διάρκεια της διαδικασίας εγκατάστασης.",
        "config-db-account-lock": "Χρησιμοποιήστε το ίδιο όνομα χρήστη και συνθηματικό κατά τη διάρκεια της κανονικής λειτουργίας",
        "config-db-wiki-account": "Λογαριασμός χρήστη για κανονική λειτουργία",
        "config-db-wiki-help": "Πληκτρολογήστε το όνομα χρήστη και τον κωδικό πρόσβασης που θα χρησιμοποιηθεί για τη σύνδεση με τη βάση δεδομένων κατά τη διάρκεια της κανονικής λειτουργίας του wiki.\nΕάν ο λογαριασμός δεν υπάρχει και o λογαριασμός εγκατάστασης  έχει επαρκή δικαιώματα, αυτός ο λογαριασμός χρήστη θα δημιουργηθεί με τα ελάχιστα δικαιώματα που απαιτούνται για τη λειτουργία του wiki.",
        "config-db-prefix": "Πρόθεμα πίνακα βάσης δεδομένων:",
-       "config-db-prefix-help": "Εάν χρειάζεται να μοιραστείτε μία βάση δεδομένων μεταξύ πολλαπλών wikis, ή μεταξύ του MediaWiki και μιας άλλης web εφαρμογής, μπορείτε να προσθέσετε ένα πρόθεμα σε όλα τα ονόματα πίνακα για να αποφεύγονται οι διενέξεις.\nΜην χρησιμοποιείτε κενά διαστήματα.\n\nΑυτό το πεδίο αφήνεται συνήθως κενό.",
+       "config-db-prefix-help": "Εάν χρειάζεται να μοιραστείτε μία βάση δεδομένων μεταξύ πολλαπλών wiki, ή μεταξύ του MediaWiki και μιας άλλης εφαρμογής Ιστού, μπορείτε να προσθέσετε ένα πρόθεμα σε όλα τα ονόματα πινάκων για να αποφεύγονται οι διενέξεις.\nΜην χρησιμοποιείτε κενά διαστήματα.\n\nΑυτό το πεδίο αφήνεται συνήθως κενό.",
        "config-mysql-old": "Απαιτείται Microsoft SQL Server $1 ή νεότερο. Εσείς έχετε $2.",
        "config-db-port": "Θύρα βάσης δεδομένων:",
        "config-db-schema": "Σχήμα για MediaWiki:",
        "config-type-oracle": "Oracle",
        "config-type-mssql": "Microsoft SQL Server",
        "config-support-info": "To MediaWiki υποστηρίζει τα ακόλουθα συστήματα βάσεων δεδομένων:\n\n$1\n\nΑν δεν εμφανίζεται παρακάτω το σύστημα βάσης δεδομένων που θέλετε να χρησιμοποιήσετε, τότε ακολουθήστε τις οδηγίες στον παραπάνω σύνδεσμο για να ενεργοποιήσετε την υποστήριξη.",
+       "config-dbsupport-mysql": "* Η [{{int:version-db-mysql-url}} MySQL] είναι ο πρωταρχικός στόχος για το MediaWiki και υποστηρίζεται καλύτερα. Το MediaWiki συνεργάζεται επίσης με τη [{{int:version-db-mariadb-url}} MariaDB] και το [{{int:version-db-percona-url}} διακομιστή Percona], που είναι όλα συμβατά με MySQL. ([http://www.php.net/manual/en/mysqli.installation.php Πώς να μεταγλωττίσετε την PHP με υποστήριξη MySQL])",
+       "config-dbsupport-postgres": "* Η [{{int:version-db-postgres-url}} PostgreSQL] είναι δημοφιλές σύστημα βάσης δεδομένων ανοικτού κώδικα ως εναλλακτική της MySQL. ([http://www.php.net/manual/en/pgsql.installation.php Πώς να μεταγλωττίσετε την PHP με υποστήριξη PostgreSQL])",
+       "config-dbsupport-sqlite": "* Η [{{int:version-db-sqlite-url}} SQLite] είναι ένα ελαφρύ σύστημα βάσης δεδομένων που υποστηρίζεται πολύ καλά. ([http://www.php.net/manual/en/pdo.installation.php Πώς να μεταγλωττίσετε την PHP με υποστήριξη SQLite], χρησιμοποιεί PDO)",
+       "config-dbsupport-oracle": "* Η [{{int:version-db-oracle-url}} Oracle] είναι εμπορική βάση δεδομένων για επιχειρήσεις. ([http://www.php.net/manual/en/oci8.installation.php Πώς να μεταγλωττίσετε την PHP με υποστήριξη OCI8])",
+       "config-dbsupport-mssql": "* Ο [{{int:version-db-mssql-url}} Microsoft SQL Server] είναι εμπορική βάση δεδομένων για επιχειρήσεις που λειτουργεί σε Windows. ([http://www.php.net/manual/en/sqlsrv.installation.php Πώς να μεταγλωττίσετε την PHP με υποστήριξη SQLSRV])",
        "config-header-mysql": "Ρυθμίσεις MySQL",
        "config-header-postgres": "Ρυθμίσεις PostgreSQL",
        "config-header-sqlite": "Ρυθμίσεις SQLite",
        "config-sqlite-cant-create-db": "Δεν ήταν δυνατή η δημιουργία του αρχείου βάσης δεδομένων <code>$1</code>.",
        "config-upgrade-done-no-regenerate": "Η αναβάθμιση ολοκληρώθηκε.\n\nΜπορείτε τώρα να [$1 ξεκινήσετε να χρησιμοποιείτε το wiki σας].",
        "config-regenerate": "Αναδημιουργία LocalSettings.php →",
-       "config-db-web-account": "Î\9bογαÏ\81ιαÏ\83μÏ\8cÏ\82 Î²Î¬Ï\83ηÏ\82 Î´ÎµÎ´Î¿Î¼Î­Î½Ï\89ν Î³Î¹Î± Ï\80Ï\81Ï\8cÏ\83βαÏ\83η Î¹στού",
+       "config-db-web-account": "Î\9bογαÏ\81ιαÏ\83μÏ\8cÏ\82 Î²Î¬Ï\83ηÏ\82 Î´ÎµÎ´Î¿Î¼Î­Î½Ï\89ν Î³Î¹Î± Ï\80Ï\81Ï\8cÏ\83βαÏ\83η Î\99στού",
        "config-db-web-account-same": "Χρήση του ίδιου λογαριασμού για την εγκατάσταση",
        "config-mysql-engine": "Μηχανή αποθήκευσης:",
        "config-mysql-innodb": "InnoDB",
        "config-mysql-myisam": "MyISAM",
+       "config-mysql-engine-help": "Το <strong>InnoDB</strong> είναι σχεδόν πάντα η καλύτερη επιλογή, αφού έχει καλή υποστήριξη ταυτόχρονης λειτουργίας.\n\nΤο <strong>MyISAM</strong> μπορεί να είναι ταχύτερο σε εγκαταστάσεις του ενός χρήστη ή μόνο ανάγνωσης. \nΟι βάσεις δεδομένων MyISAM τείνουν να φθείρονται συχνότερα από τις βάσεις δεδομένων InnoDB.",
        "config-mysql-charset": "Σύνολο χαρακτήρων βάσης δεδομένων:",
        "config-mysql-binary": "Δυαδικό",
        "config-mysql-utf8": "UTF-8",
+       "config-mysql-charset-help": "Σε <strong>λειτουργία δυαδικού</strong>, το MediaWiki αποθηκεύει κείμενο UTF-8 στη βάση δεδομένων σε δυαδικά πεδία.\nΑυτό είναι πιο αποτελεσματικό από τη λειτουργία UTF-8 της MySQL και σας επιτρέπει να χρησιμοποιείτε ολόκληρο το φάσμα των χαρακτήρων Unicode.\n\nΣε <strong>λειτουργία UTF-8</strong>, η MySQL θα γνωρίζει σε ποιο σύνολο χαρακτήρων ανήκουν τα δεδομένα σας και θα μπορεί να τα παρουσιάζει και να τα μετατρέπει κατάλληλα, αλλά δεν θα σας επιτρέπει να αποθηκεύετε χαρακτήρες πάνω από το [https://en.wikipedia.org/wiki/Mapping_of_Unicode_character_planes βασικό πολύγλωσσο επίπεδο].",
        "config-mssql-auth": "Τύπος ελέγχου ταυτότητας:",
        "config-mssql-sqlauth": "Έλεγχος ταυτότητας του SQL Server",
        "config-mssql-windowsauth": "Έλεγχος ταυτότητας των Windows",
        "config-ns-site-name": "Ίδιο με το όνομα του wiki: $1",
        "config-ns-other": "Άλλο (προσδιορίστε)",
        "config-ns-other-default": "ΤοWikiμου",
+       "config-project-namespace-help": "Ακολουθώντας το παράδειγμα της Βικιπαίδειας, πολλά wiki διατηρούν τις σελίδες πολιτικής τους χωριστά από τις σελίδες περιεχομένου τους, σε έναν '''ονοματοχώρο έργου'''.\nΌλοι οι τίτλοι σελίδων σε αυτόν τον ονοματοχώρο ξεκινούν με συγκεκριμένο πρόθεμα, το οποίο μπορείτε να ορίσετε εδώ.\nΣυνήθως, αυτό το πρόθεμα παράγεται από το όνομα του wiki, αλλά δεν μπορεί να περιέχει χαρακτήρες στίξης όπως «#» ή «:».",
        "config-admin-box": "Λογαριασμός διαχειριστή",
        "config-admin-name": "Το όνομα χρήστη σας:",
        "config-admin-password": "Κωδικός πρόσβασης:",
        "config-admin-password-confirm": "Επανάληψη κωδικού πρόσβασης:",
+       "config-admin-help": "Εισαγάγετε το προτιμώμενο όνομα χρήστη εδώ, για παράδειγμα «Γιάννης Ιστολόγιος». \nΑυτό είναι το όνομα που θα χρησιμοποιείτε για να συνδέεστε στο wiki.",
        "config-admin-name-blank": "Εισαγάγετε όνομα χρήστη διαχειριστή.",
        "config-admin-name-invalid": "Το συγκεκριμένο όνομα χρήστη  \"<nowiki>$1</nowiki>\" δεν είναι έγκυρο. Δώστε ένα διαφορετικό όνομα χρήστη.",
        "config-admin-password-blank": "Εισαγάγετε κωδικό για το λογαριασμό διαχειριστή.",
        "config-admin-password-mismatch": "Οι δύο κωδικοί πρόσβασης που εισηγάγατε δεν ταιριάζουν.",
        "config-admin-email": "Διεύθυνση ηλεκτρονικού ταχυδρομείου:",
+       "config-admin-email-help": "Εισαγάγετε μια διεύθυνση ηλεκτρονικού ταχυδρομείου εδώ για να μπορείτε να λαμβάνετε μηνύματα ηλεκτρονικού ταχυδρομείου από άλλους χρήστες στο wiki, να επαναφέρετε το συνθηματικό σας και να ενημερώνεστε για τις αλλαγές σε σελίδες που βρίσκονται στη λίστα παρακολούθησής σας. Μπορείτε να αφήσετε αυτό το πεδίο κενό.",
        "config-admin-error-bademail": "Έχετε εισαγάγει μη έγκυρη διεύθυνση ηλεκτρονικού ταχυδρομείου.",
+       "config-subscribe": "Εγγραφή στη [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce λίστα αλληλογραφίας ανακοινώσεων σχετικά με νέες κυκλοφορίες].",
+       "config-subscribe-help": "Πρόκειται για μια λίστα αλληλογραφίας χαμηλού όγκου που χρησιμοποιείται για ανακοινώσεις σχετικά με νέες κυκλοφορίες, συμπεριλαμβανομένων σημαντικών ανακοινώσεων ασφαλείας. \nΚαλό θα ήταν να εγγραφείτε σε αυτήν και να ενημερώνετε την εγκατάσταση του MediaWiki όταν βγαίνουν νέες εκδόσεις.",
+       "config-pingback": "Διαμοιρασμός δεδομένων σχετικά με αυτήν την εγκατάσταση με τους προγραμματιστές του MediaWiki.",
+       "config-pingback-help": "Αν επιλέξετε αυτή την επιλογή, το MediaWiki θα στέλνει περιοδικά στο https://www.mediawiki.org βασικά δεδομένα σχετικά με αυτήν την υλοποίηση του MediaWiki. Αυτά τα δεδομένα περιλαμβάνουν, για παράδειγμα, τον τύπο του συστήματος, την έκδοση PHP και την επιλεγμένη βάση δεδομένων. Το Ίδρυμα Wikimedia μοιράζεται αυτά τα δεδομένα με τους προγραμματιστές του MediaWiki για να καθοδηγήσει τις μελλοντικές προσπάθειες ανάπτυξης. θα αποσταλούν τα ακόλουθα δεδομένα σχετικά με το σύστημά σας:\n<pre>$1</pre>",
+       "config-almost-done": "Έχετε σχεδόν τελειώσει!\nΤώρα μπορείτε να παραλείψετε την υπόλοιπη διαμόρφωση και να εγκαταστήσετε το wiki αμέσως τώρα.",
        "config-optional-continue": "Να ερωτηθώ περισσότερες ερωτήσεις.",
-       "config-optional-skip": "Î\92αÏ\81έθηκα Î®Î´Î·, Î±Ï\80λά ÎµÎ³ÎºÎ±Ï\84αÏ\83Ï\84ήÏ\83Ï\84ε το wiki.",
+       "config-optional-skip": "Î\92αÏ\81έθηκα Î®Î´Î·, Î½Î± ÎµÎ³ÎºÎ±Ï\84αÏ\83Ï\84αθεί Î±Ï\80λά το wiki.",
        "config-profile": "Προφίλ δικαιωμάτων χρήστη:",
-       "config-profile-wiki": "Ανοικτό wiki",
-       "config-profile-no-anon": "Απαιτείται η δημιουργία λογαριασμού",
-       "config-profile-fishbowl": "Εξουσιοδοτημένοι συντάκτες μόνο",
-       "config-profile-private": "Ιδιωτικό wiki",
+       "config-profile-wiki": "ανοικτό wiki",
+       "config-profile-no-anon": "απαιτούμενη δημιουργία λογαριασμού",
+       "config-profile-fishbowl": "εξουσιοδοτημένοι συντάκτες μόνο",
+       "config-profile-private": "ιδιωτικό wiki",
+       "config-profile-help": "Τα wiki λειτουργούν καλύτερα όταν αφήνετε να τα επεξεργαστούν όσο το δυνατόν περισσότεροι άνθρωποι.\nΣτο MediaWiki, είναι εύκολο να ελέγξετε τις πρόσφατες αλλαγές και να επαναφέρετε τυχόν ζημιές που προκαλούνται από αρχάριους ή κακόβουλους χρήστες.\n\nΩστόσο, πολλοί έχουν βρει το MediaWiki χρήσιμο σε ένα ευρύ φάσμα ρόλων και μερικές φορές δεν είναι εύκολο να πείσουμε τους πάντες για τα οφέλη του τρόπου του wiki.\nΥπάρχει λοιπόν επιλογή.\n\nΤο μοντέλο <strong>{{int:config-profile-wiki}}</strong> επιτρέπει σε οποιονδήποτε να επεξεργαστεί, χωρίς καν να συνδεθεί. Ένα wiki με <strong>{{int:config-profile-no-anon}}</strong> παρέχει μεγαλύτερη λογοδοσία, αλλά μπορεί να αποτρέψει τους περιστασιακούς συνεισφέροντες.\n\nΤο σενάριο <strong>{{int:config-profile-fishbowl}}</strong> επιτρέπει στους εγκεκριμένους χρήστες να επεξεργαστούν, αλλά το κοινό μπορεί να δει τις σελίδες, συμπεριλαμβανομένου του ιστορικού. \nΈνα <strong>{{int:config-profile-private}}</strong> επιτρέπει μόνο στους εγκεκριμένους χρήστες να προβάλλουν σελίδες, και μόνο στους ίδιους να επεξεργαστούν.\n\nΠιο πολύπλοκες ρυθμίσεις δικαιωμάτων χρήστη είναι διαθέσιμες μετά την εγκατάσταση, δείτε τη [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:User_rights σχετική μη αυτόματη καταχώριση].",
        "config-license": "Πνευματικά δικαιώματα και άδεια χρήσης:",
        "config-license-none": "Χωρίς άδεια χρήσης στο υποσέλιδο",
        "config-license-cc-by-sa": "Creative Commons Αναφορά Δημιουργού-Παρόμοια Διανομή",
        "config-license-gfdl": "Αδειοδότηση Ελεύθερης Τεκμηρίωσης GNU 1.3 ή μεταγενέστερη",
        "config-license-pd": "Κοινό Κτήμα",
        "config-license-cc-choose": "Επιλέξτε μια προσαρμοσμένη άδεια Creative Commons",
+       "config-license-help": "Πολλά δημόσια wiki βάζουν όλες τις συνεισφορές υπό [https://freedomdefined.org/Definition ελεύθερη άδεια].\nΑυτό βοηθά να δημιουργηθεί μια αίσθηση κοινοτικής ιδιοκτησίας και ενθαρρύνει τη μακροχρόνια συνεισφορά.\nΔεν είναι γενικά απαραίτητο για ένα ιδιωτικό ή εταιρικό wiki.\n\nΑν θέλετε να μπορείτε να χρησιμοποιείτε κείμενο από τη Βικιπαίδεια και αν θέλετε να μπορεί η Βικιπαίδεια να δεχτεί κείμενο που αντιγράφεται από το wiki σας, θα πρέπει να επιλέξετε <strong>{{int:config-license-cc-by-sa}}</strong>.\n\nΗ Βικιπαίδεια χρησιμοποιούσε παλιά την Άδεια Ελεύθερης Τεκμηρίωσης GNU (GFDL).\nΗ GFDL είναι μια έγκυρη άδεια, αλλά είναι δύσκολο να κατανοηθεί.\nΕίναι επίσης δύσκολο να επαναχρησιμοποιηθεί το περιεχόμενο που έχει χορηγηθεί βάσει της GFDL.",
        "config-email-settings": "Ρυθμίσεις ηλεκτρονικού ταχυδρομείου",
+       "config-enable-email": "Ενεργοποίηση εξερχόμενου ηλεκτρονικού ταχυδρομείου",
+       "config-enable-email-help": "Αν θέλετε να λειτουργήσει το ηλεκτρονικό ταχυδρομείο, οι [http://www.php.net/manual/en/mail.configuration.php ρυθμίσεις αλληλογραφίας της PHP] πρέπει να ρυθμιστούν σωστά.\nΑν δεν θέλετε καθόλου λειτουργίες ηλεκτρονικού ταχυδρομείου, μπορείτε να τις απενεργοποιήσετε εδώ.",
        "config-email-user": "Ενεργοποίηση ηλεκτρονικού ταχυδρομείου από χρήστη σε χρήστη",
+       "config-email-user-help": "Να επιτρέπεται σε όλους τους χρήστες να στέλνουν ο ένας στον άλλον μηνύματα ηλεκτρονικού ταχυδρομείου εάν το έχουν ενεργοποιήσει στις προτιμήσεις τους.",
        "config-email-usertalk": "Ενεργοποίηση ειδοποίησης σελίδας συζήτησης χρήστη",
+       "config-email-usertalk-help": "Να επιτρέπεται στους χρήστες να λαμβάνουν ειδοποιήσεις σχετικά με τις αλλαγές στις σελίδες συζήτησης χρήστη, εάν το έχουν ενεργοποιήσει στις προτιμήσεις τους.",
        "config-email-watchlist": "Ενεργοποίηση ειδοποίησης λίστας παρακολούθησης",
-       "config-email-watchlist-help": "Î\95Ï\80ιÏ\84Ï\81έÏ\88Ï\84ε στους χρήστες να λαμβάνουν ειδοποιήσεις για τις σελίδες που παρακολουθούν αν το έχουν ενεργοποιήσει στις προτιμήσεις τους.",
+       "config-email-watchlist-help": "Î\9dα ÎµÏ\80ιÏ\84Ï\81έÏ\80εÏ\84αι στους χρήστες να λαμβάνουν ειδοποιήσεις για τις σελίδες που παρακολουθούν αν το έχουν ενεργοποιήσει στις προτιμήσεις τους.",
        "config-email-auth": "Ενεργοποίηση ταυτοποίησης μέσω ηλεκτρονικού ταχυδρομείου",
+       "config-email-auth-help": "Εάν αυτή η επιλογή είναι ενεργοποιημένη, οι χρήστες πρέπει να επιβεβαιώσουν τη διεύθυνση ηλεκτρονικού ταχυδρομείου τους χρησιμοποιώντας ένα σύνδεσμο που τους αποστέλλεται όποτε την καθορίζουν ή την αλλάζουν.\nΜόνο οι ταυτοποιημένες διευθύνσεις ηλεκτρονικού ταχυδρομείου μπορούν να λαμβάνουν μηνύματα ηλεκτρονικού ταχυδρομείου από άλλους χρήστες ή να αλλάζουν μηνύματα ειδοποίησης.\nΟ καθορισμός αυτής της επιλογής <strong>συνιστάται</strong> για δημόσια wiki λόγω πιθανής κατάχρησης των λειτουργιών του ηλεκτρονικού ταχυδρομείου.",
        "config-email-sender": "Διεύθυνση ηλεκτρονικού ταχυδρομείου επιστροφής:",
+       "config-email-sender-help": "Εισαγάγετε τη διεύθυνση ηλεκτρονικού ταχυδρομείου που θα χρησιμοποιηθεί ως διεύθυνση επιστροφής στο εξερχόμενο ηλεκτρονικό ταχυδρομείο.\nΕκεί είναι που θα στέλνονται οι επιστροφές.\nΠολλοί διακομιστές αλληλογραφίας απαιτούν τουλάχιστον το τμήμα ονόματος τομέα να είναι έγκυρο.",
        "config-upload-settings": "Ανέβασμα εικόνων και άλλων αρχείων",
-       "config-upload-enable": "Î\95νεÏ\81γοÏ\80οιήÏ\83Ï\84ε Ï\84ο Î±Î½Î­Î²Î±Ï\83μα αρχείων",
-       "config-upload-help": "Το ανέβασμα αρχείων εκθέτει πιθανώς το διακομιστή σας σε κινδύνους ασφαλείας.\nΓια περισσότερες πληροφορίες, διαβάστε την [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Security ενότητα περί ασφάλειας] στο εγχειρίδιο.\n\nΓια να ενεργοποιήσετε το ανέβασμα αρχείων, αλλάξτε την κατάσταση του υποκαταλόγου <code>εικόνες</code> που βρίσκεται κάτω από τον ριζικό κατάλογο του MediaWiki έτσι ώστε ο διακομιστής ιστού να μπορεί να γράψει σε αυτόν.\nΣτη συνέχεια ενεργοποιήσετε αυτή την επιλογή.",
+       "config-upload-enable": "Î\95νεÏ\81γοÏ\80οίηÏ\83η Î±Î½ÎµÎ²Î¬Ï\83μαÏ\84οÏ\82 αρχείων",
+       "config-upload-help": "Το ανέβασμα αρχείων εκθέτει πιθανώς το διακομιστή σας σε κινδύνους ασφαλείας.\nΓια περισσότερες πληροφορίες, διαβάστε την [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Security ενότητα περί ασφάλειας] στο εγχειρίδιο.\n\nΓια να ενεργοποιήσετε το ανέβασμα αρχείων, αλλάξτε την κατάσταση του υποκαταλόγου <code>images</code> που βρίσκεται κάτω από τον ριζικό κατάλογο του MediaWiki έτσι ώστε ο διακομιστής Ιστού να μπορεί να γράψει σε αυτόν.\nΣτη συνέχεια ενεργοποιήσετε αυτή την επιλογή.",
        "config-upload-deleted": "Καταλόγος για διαγραφέντα αρχεία:",
+       "config-upload-deleted-help": "Επιλέξτε έναν κατάλογο στον οποίο να αρχειοθετείτε τα διαγραμμένα αρχεία.\nΙδανικά, αυτό δεν θα πρέπει να είναι προσβάσιμο από τον Ιστό.",
        "config-logo": "Διεύθυνση URL λογότυπου:",
+       "config-logo-help": "Το προεπιλεγμένο θέμα εμφάνισης του MediaWiki περιλαμβάνει χώρο για λογότυπο 135x160 εικονοστοιχείων πάνω από το μενού της πλαϊνής μπάρας. Ανεβάστε μια εικόνα με το κατάλληλο μέγεθος και εισαγάγετε τη διεύθυνση URL εδώ.\n\nΜπορείτε να χρησιμοποιήσετε τα <code>$wgStylePath</code> ή <code>$wgScriptPath</code> εάν το λογότυπό σας είναι σχετικό με αυτές τις διαδρομές.\n\nΕάν δεν θέλετε λογότυπο, αφήστε αυτό το πλαίσιο κενό.",
        "config-instantcommons": "Ενεργοποίηση Instant Commons",
+       "config-instantcommons-help": "Τα [https://www.mediawiki.org/wiki/InstantCommons Instant Commons] είναι ένα χαρακτηριστικό που επιτρέπει στα wiki να χρησιμοποιούν εικόνες, ήχους και άλλα μέσα που βρίσκονται στον ιστότοπο των Wikimedia Commons. Για να γίνει αυτό, το MediaWiki απαιτεί πρόσβαση στο Διαδίκτυο.\n\nΓια περισσότερες πληροφορίες σχετικά με αυτήν τη δυνατότητα, συμπεριλαμβανομένων των οδηγιών για τον τρόπο ρύθμισης της για άλλα wiki από τα Wikimedia Commons, συμβουλευτείτε [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:$wgForeignFileRepos το εγχειρίδιο].",
        "config-cc-error": "Ο επιλογέας αδειών  Creative Commons επιλογέα δεν έδωσε κανένα αποτέλεσμα.\nΕισάγετε το όνομα της άδειας χειροκίνητα.",
        "config-cc-again": "Επιλέξτε ξανά...",
        "config-cc-not-chosen": "Επιλέξτε την  άδεια Creative Commons που θέλετε και κάντε κλικ στο κουμπί \"proceed\".",
        "config-advanced-settings": "Προηγμένες ρυθμίσεις παραμέτρων",
        "config-cache-options": "Ρυθμίσεις για την προσωρινή αποθήκευση αντικειμένου:",
+       "config-cache-help": "Η προσωρινή αποθήκευση αντικειμένων χρησιμοποιείται για τη βελτίωση της ταχύτητας του MediaWiki με προσωρινή αποθήκευση των δεδομένων που χρησιμοποιούνται συχνά.\nΜεσαίοι και μεγάλοι ιστότοποι ενθαρρύνονται ιδιαίτερα να το ενεργοποιήσουν αυτό και μικροί ιστότοποι θα δουν οφέλη επίσης.",
+       "config-cache-none": "Χωρίς προσωρινή αποθήκευση (δεν καταργείται καμία λειτουργία, αλλά η ταχύτητα μπορεί να επηρεαστεί σε μεγαλύτερους ιστότοπους wiki)",
+       "config-cache-memcached": "Χρήση Memcached (απαιτεί πρόσθετη ρύθμιση και διαμόρφωση)",
        "config-memcache-badip": "Έχετε εισάγει μια μη έγκυρη διεύθυνση IP για το Memcached: $1.",
        "config-memcache-noport": "Δεν καθορίσατε μια θύρα για να χρησιμοποιήσετε για το Memcached server: $1.\nΑν δεν ξέρετε τη θύρα, η προεπιλογή είναι 11211.",
        "config-memcache-badport": "Οι Memcached αριθμοί θύρας θα πρέπει να είναι μεταξύ $1 και $2.",
        "config-install-mainpage-exists": "Κύρια σελίδα ήδη υπάρχει, παρακάμπτεται",
        "config-install-extension-tables": "Γίνεται δημιουργία πινάκων για τις εγκατεστημένες επεκτάσεις",
        "config-install-mainpage-failed": "Δεν ήταν δυνατή η εισαγωγή της αρχικής σελίδας: $1",
-       "config-install-done": "<strong>Συγχαρητήρια!</strong>\nΈχετε εγκαταστήσει με επιτυχία το MediaWiki.\n\nΤο πρόγραμμα εγκατάστασης έχει δημιουργήσει το  αρχείο   <code>LocalSettings.php</code>.\nΠεριέχει όλες τις ρυθμίσεις παραμέτρων σας.\n\nΘα πρέπει να το κατεβάσετε και να το βάλετε στη βάση της εγκατάστασης του  wiki σας (στον ίδιο κατάλογο όπως το  index.php). Η λήψη θα αρχίσει αυτόματα.\n\nΑν η λήψη δεν προσφέφθηκε, ή αν την ακυρώσατε, μπορείτε να επανεκκινήσετε τη λήψη κάνοντας κλικ στο παρακάτω link:\n\n$3\n\n<strong>Σημείωση:</strong> Εάν δεν το κάνετε αυτό τώρα, αυτό το  αρχείο ρύθμισης παραμέτρων δεν θα είναι διαθέσιμο για σας αργότερα, αν βγείτε από την εγκατάσταση, χωρίς να το κατεβάσετε!\n\nΌταν γίνει αυτό, μπορείτε να <strong>[$2 μπείτε στο wiki σας]</strong>.",
+       "config-install-done": "<strong>Συγχαρητήρια!</strong>\nΈχετε εγκαταστήσει το MediaWiki.\n\nΤο πρόγραμμα εγκατάστασης έχει δημιουργήσει το αρχείο <code>LocalSettings.php</code>.\nΠεριέχει όλες τις ρυθμίσεις παραμέτρων σας.\n\nΘα πρέπει να το κατεβάσετε και να το βάλετε στη βάση της εγκατάστασης του wiki σας (στον ίδιο κατάλογο με το index.php). Η λήψη θα πρέπει να έχει ξεκινήσει αυτόματα.\n\nΕάν δεν σας προτάθηκε λήψη, ή αν την ακυρώσατε, μπορείτε να επανεκκινήσετε τη λήψη κάνοντας κλικ στο σύνδεσμο ακριβώς από κάτω:\n\n$3\n\n<strong>Σημείωση:</strong> Εάν δεν το κάνετε αυτό τώρα, αυτό το αρχείο ρύθμισης παραμέτρων δεν θα είναι διαθέσιμο για σας αργότερα αν βγείτε από την εγκατάσταση χωρίς να το κατεβάσετε!\n\nΌταν θα έχει γίνει αυτό, μπορείτε να <strong>[$2 μπείτε στο wiki σας]</strong>.",
        "config-install-success": " Το σύστημα της MediaWiki έχει εγκατασταθεί με επιτυχία. Μπορείτε τώρα να επισκεφθείτε το \n <$1$2> για να δείτε το wiki σας.\nΑν έχετε ερωτήσεις, ελέγξετε την λίστα με τις πιο συχνές ερωτήσεις:\n<https://www.mediawiki.org/wiki/Manual:FAQ> ή χρησιμοποιήστε ένα από τα φόρουμ υποστήριξης που είναι συνδεδεμένα σε αυτήν την σελίδα.",
        "config-download-localsettings": "Λήψη του <code>LocalSettings.php</code>",
        "config-help": "βοήθεια",
        "config-help-tooltip": "κλικ για ανάπτυξη",
        "config-nofile": "Το αρχείο «$1» δεν μπορεί να βρεθεί. Μήπως έχει διαγραφεί;",
-       "config-extension-link": "Γνωρίζατε ότι το wiki σας υποστηρίζει [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Extensions επεκτάσεις];\n\nΜπορείτε να περιηγηθείτε [https://www.mediawiki.org/wiki/Special:MyLanguage/Category:Extensions_by_category επεκτάσεις ανά κατηγορία] ή το [https://www.mediawiki.org/wiki/Extension_Matrix Extension Matrix] για να δείτε την πλήρη λίστα των επεκτάσεων.",
+       "config-extension-link": "Γνωρίζατε ότι το wiki σας υποστηρίζει [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Extensions επεκτάσεις];\n\nΜπορείτε να περιηγηθείτε στις [https://www.mediawiki.org/wiki/Special:MyLanguage/Category:Extensions_by_category επεκτάσεις ανά κατηγορία] ή στον [https://www.mediawiki.org/wiki/Extension_Matrix πίνακα επεκτάσεων] για να δείτε την πλήρη λίστα των επεκτάσεων.",
        "config-skins-screenshots": "$1 (στιγμιότυπα: $2)",
        "config-screenshot": "στιγμιότυπο",
        "mainpagetext": "<strong>To MediaWiki εγκαταστάθηκε.</strong>",
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 aff2fd3..d75b063 100644 (file)
@@ -74,7 +74,7 @@
        "config-apc": "[http://www.php.net/apc APC] نصب شده‌است.",
        "config-apcu": "[http://www.php.net/apcu APCu] نصب شده‌است",
        "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] یافت نشد. ذخیره شی فعال نیست.",
+       "config-no-cache-apcu": "<strong>هشدار:</strong> پیوند [http://www.php.net/apcu APCu] یا [http://www.iis.net/download/WinCacheForPhp WinCache] یافت نشد. ذخیره شی فعال نیست.",
        "config-mod-security": "'''هشدار:''' وب سرور شما [https://modsecurity.org/ mod_security] فعال است.اگر اشتباه پیکربندی شده‌‌ باشد،می تواند باعث ایجاد مشکلاتی برای مدیاویکی یا دیگر نرم‌افزاری شود که به کاربران اجازه می‌دهد پیام دلخواه ارسال کنند.\nبه [https://modsecurity.org/documentation/ mod_security documentation] مراجعه کنید یا اگر با خطاهای اتفاقی مواجه شدید با پشتیبانی میزبان خود در تماس باشید.",
        "config-diff3-bad": "جی‌ان‌یو دیف۳ پیدا نشد.",
        "config-git": "کنترل نسخهٔ نرم‌افزار گیت پیدا شد: <code>$1</code>.",
        "config-cache-options": "تنظیمات برای ذخیره شی:",
        "config-cache-help": "کش شی برای بهتر شدن سرعت مدیا ویکی، از طریق کش کردن داده‌های با بیشترین استفاده انجام می‌گیرد.\nوبگاه‌های متوسط تا بزرگ به انجام این کار شدیدا توصیه می‌شوند، در عین حال وبگاه‌های کوچک نیز می‌توانند از مزایای ایم مورد بهره ببرند.",
        "config-cache-none": "بدون ذخیره (هیچ کارآمدی پاک نشده‌است، اما ممکن است سرعت در سایت‌های بزرگتر ویکی تأثیر داشته باشد)",
-       "config-cache-accel": "کاشهٔ اشیای پی‌اچ‌پی (APC یا APCu یا XCache یا WinCahe)",
+       "config-cache-accel": "کاشهٔ اشیای پی‌اچ‌پی (APC یا APCu یا WinCahe)",
        "config-cache-memcached": "از ممکچد (که نیازمند تنظیمات اضافی و پیکربندی) استفاده کنید",
        "config-memcached-servers": "سرورهای دریافت حافظه:",
        "config-memcached-help": "فهرست آدرس‌های آی‌پی برای استفاده از ممکچد.\nباید هر یک خط را تعیین کند و درگاه مورد استفاده را تعیین کند.برای مثال: \n۱۲۷.۰.۰.۱:۱۱۲۱۱\n۱۹۲.۱۶۸.۱.۲۵:۱۲۳۴",
index ec3c782..d388a59 100644 (file)
@@ -69,7 +69,7 @@
        "config-apc": "[http://www.php.net/apc APC] מותקן",
        "config-apcu": "[http://www.php.net/apcu APCu] מותקן",
        "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": "'''אזהרה''': בשרת הווב שלך מופעל [https://modsecurity.org/ mod_security]. אם הוא לא מוגדר טוב, זה יכול לגרום לבעיות במדיה־ויקי ובתכנה אחרת שמאפשרת למשתמשים לשלוח תוכן שרירותי.\nיש לקרוא את [https://modsecurity.org/documentation/ התיעוד של mod_security] או ליצור קשר עם אנשי התמיכה של שירותי האירוח שלכם אם מופיעות לך שגיאות אקראיות.",
        "config-diff3-bad": "GNU diff3 לא נמצא.",
        "config-git": "נמצאה Git, תכנת בקרת התצורה: <code dir=\"ltr\">$1</code>.",
        "config-cache-options": "הגדרות למטמון עצמים (object caching):",
        "config-cache-help": "מטמון עצמים משמש לשיפור המהירות של מדיה־ויקי על־ידי שמירה של נתונים שהשימוש בהם נפוץ במטמון.\nלאתרים בינוניים וגדולים כדאי מאוד להפעיל את זה, וגם אתרים קטנים ייהנו מזה.",
        "config-cache-none": "ללא מטמון (שום יכולת אינה מוּסרת, אבל הביצועים באתרים גדולים ייפגעו)",
-       "config-cache-accel": "מטמון עצמים (object caching) של PHP&rlm; (APC,&rlm; APCu,&rlm; XCache או WinCache)",
+       "config-cache-accel": "מטמון עצמים (object caching) של PHP&rlm; (APC,&rlm; APCu,&rlm; או WinCache)",
        "config-cache-memcached": "להשתמש ב־Memcached (דורש התקנות והגדרות נוספות)",
        "config-memcached-servers": "שרתי Memcached:",
        "config-memcached-help": "רשימת כתובות IP ש־Memcached ישתמש בהן.\nיש לרשום כתובת אחת בכל שורה ולציין את הפִּתְחָה (port), למשל:\n 127.0.0.1:11211\n 192.168.1.25:1234",
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 335bc6a..abf60b2 100644 (file)
@@ -13,7 +13,9 @@
        "config-desc": "Инсталација за Медијавики",
        "config-title": "Инсталација Медијавикија $1",
        "config-information": "Информација",
+       "config-localsettings-upgrade": "Откривена је датотека <code>LocalSettings.php</code>.\nДа бисте надоградили инсталацију, унесите вредности од <code>$wgUpgradeKey</code> у оквиру испод.\nНаћи ћете га у <code>LocalSettings.php</code>.",
        "config-localsettings-key": "Кључ за уградњу:",
+       "config-localsettings-badkey": "Наведени кључ за надоградњу је неисправан.",
        "config-session-error": "Грешка при започињању сесије: $1",
        "config-session-expired": "Ваши подаци о сесији су истекли.\nСесије су подешене да трају $1.\nЊихов рок можете повећати постављањем <code>session.gc_maxlifetime</code> у php.ini.\nПоново покрените инсталацију.",
        "config-no-session": "Ваши подаци о сесији су изгубљени!\nПроверите Ваш php.ini и обезбедите да је <code>session.save_path</code> постављен на одговарајући директоријум.",
@@ -40,6 +42,8 @@
        "config-page-existingwiki": "Постојећи вики",
        "config-help-restart": "Желите ли да обришете све сачуване податке које сте унели и поново покренете инсталацију?",
        "config-restart": "Да, покрени поново",
+       "config-env-good": "Окружење је проверено.\nМожете инсталирати Медијавики.",
+       "config-env-bad": "Окружење је проверено.\nНе можете инсталирати Медијавики.",
        "config-env-php": "PHP $1 је инсталиран.",
        "config-env-hhvm": "HHVM $1 је инсталиран.",
        "config-apc": "[http://www.php.net/apc APC] је инсталиран",
        "config-mssql-sqlauth": "Провера идентитета SQL Server-а",
        "config-mssql-windowsauth": "Провера идентитета Виндоуса",
        "config-site-name": "Име викија:",
+       "config-site-name-blank": "Унесите име сајта.",
+       "config-project-namespace": "Именски простор пројекта:",
+       "config-ns-generic": "Пројекат",
+       "config-ns-site-name": "Исти као име викија: $1",
+       "config-ns-other": "Друго (наведите)",
+       "config-ns-other-default": "MyWiki",
+       "config-admin-box": "Налог администратора",
        "config-admin-name": "Ваше корисничко име:",
        "config-admin-password": "Лозинка:",
+       "config-admin-password-confirm": "Поново унесите лозинку:",
+       "config-admin-help": "Овде упишите жељено корисничко име; на пример, „Јован Крстић“.\nОво име ћете користити за пријаву на вики.",
+       "config-admin-name-blank": "Унесите корисничко име администратора.",
+       "config-admin-password-blank": "Унесите лозинку за налог администратора.",
+       "config-admin-password-mismatch": "Лозинке које сте унели се не поклапају.",
        "config-admin-email": "Имејл адреса:",
+       "config-admin-error-bademail": "Унели сте неисправну имејл адресу.",
        "config-optional-skip": "Досадно ми је, само инсталирај вики.",
+       "config-profile-wiki": "Отворен вики",
        "config-profile-no-anon": "Неопходно је отворити налог",
        "config-profile-fishbowl": "Само овлашћени корисници",
        "config-profile-private": "Приватна вики",
        "config-license-gfdl": "ГНУ-ова лиценца за слободну документацију издање 1.3 или новије",
        "config-license-pd": "Јавно власништво",
        "config-email-settings": "Подешавања имејла",
+       "config-enable-email": "Омогући одлазни имејл",
+       "config-upload-settings": "Отпремања слика и датотека",
+       "config-upload-enable": "Омогући отпремање датотека",
+       "config-upload-deleted": "Фасцикла за обрисане датотеке:",
+       "config-logo": "URL логотипа:",
+       "config-instantcommons": "Омогући Instant Commons",
+       "config-cc-again": "Изаберите поново...",
        "config-cc-not-chosen": "Одаберите која Кријејтив комонс лиценца вам одговара и потврдите.",
+       "config-advanced-settings": "Напредна конфигурација",
+       "config-memcached-servers": "Memcached сервери:",
+       "config-extensions": "Екстензије",
        "config-skins": "Теме",
+       "config-skins-use-as-default": "Користи ову тему као подразумевану",
+       "config-skins-must-enable-some": "Морате изабрати барем једну тему.",
        "config-install-step-done": "готово",
        "config-install-step-failed": "није успело",
        "config-install-extensions": "Обухвата екстензије",
        "config-install-schema": "Прављење шеме",
        "config-install-tables": "Прављење табела",
+       "config-install-stats": "Покрећем статистику",
        "config-install-keys": "Генеришем тајне кључеве",
+       "config-install-sysop": "Правим кориснички налог администратора",
+       "config-install-subscribe-fail": "Не могу да Вас претплатим на mediawiki-announce: $1",
+       "config-install-mainpage": "Правим главну страну са стандардним садржајем",
        "config-install-mainpage-exists": "Главна страна већ постоји, прескакање",
        "config-install-mainpage-failed": "Не могу да убацим главну страну: „$1”",
        "config-download-localsettings": "Преузми <code>LocalSettings.php</code>",
        "config-help-tooltip": "кликните да проширите",
        "config-nofile": "Не могу да пронађем датотеку „$1”. Није ли она била избрисана?",
        "config-skins-screenshots": "„$1” (снимци екрана: $2)",
+       "config-skins-screenshot": "$1 ($2)",
        "config-screenshot": "снимак екрана",
        "mainpagetext": "<strong>Медијавики је успешно инсталиран.</strong>",
        "mainpagedocfooter": "Погледајте [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents кориснички водич] за коришћење програма.\n\n== Увод ==\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Помоћ у вези са подешавањима]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ Често постављена питања]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce Дописни списак о издањима Медијавикија]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam Научите како да се борите против спама на Вашој вики]"
index 4e3409a..01f467f 100644 (file)
@@ -27,6 +27,11 @@ class JobQueueSecondTestQueue extends JobQueue {
         */
        private $debugQueue;
 
+       /**
+        * @var bool
+        */
+       private $onlyWriteToDebugQueue;
+
        protected function __construct( array $params ) {
                if ( !isset( $params['mainqueue'] ) ) {
                        throw new MWException( "mainqueue parameter must be provided to the debug queue" );
@@ -39,6 +44,7 @@ class JobQueueSecondTestQueue extends JobQueue {
                $conf = [ 'wiki' => $params['wiki'], 'type' => $params['type'] ];
                $this->mainQueue = JobQueue::factory( $params['mainqueue'] + $conf );
                $this->debugQueue = JobQueue::factory( $params['debugqueue'] + $conf );
+               $this->onlyWriteToDebugQueue = isset( $params['readonly'] ) ? $params['readonly'] : false;
 
                // We need to construct parent after creating the main and debug queue
                // because super constructor calls some methods we delegate to the main queue.
@@ -118,7 +124,9 @@ class JobQueueSecondTestQueue extends JobQueue {
         * @param int $flags
         */
        protected function doBatchPush( array $jobs, $flags ) {
-               $this->mainQueue->doBatchPush( $jobs, $flags );
+               if ( !$this->onlyWriteToDebugQueue ) {
+                       $this->mainQueue->doBatchPush( $jobs, $flags );
+               }
 
                try {
                        $this->debugQueue->doBatchPush( $jobs, $flags );
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 bce8334..5695d82 100644 (file)
@@ -50,10 +50,14 @@ class SwiftFileBackend extends FileBackendStore {
        protected $rgwS3AccessKey;
        /** @var string S3 authentication key (RADOS Gateway) */
        protected $rgwS3SecretKey;
-       /** @var array Additional users (account:user) to open read permissions for */
+       /** @var array Additional users (account:user) with read permissions on public containers */
        protected $readUsers;
-       /** @var array Additional users (account:user) to open write permissions for */
+       /** @var array Additional users (account:user) with write permissions on public containers */
        protected $writeUsers;
+       /** @var array Additional users (account:user) with read permissions on private containers */
+       protected $secureReadUsers;
+       /** @var array Additional users (account:user) with write permissions on private containers */
+       protected $secureWriteUsers;
 
        /** @var BagOStuff */
        protected $srvCache;
@@ -100,8 +104,10 @@ class SwiftFileBackend extends FileBackendStore {
         *                          This is used for generating expiring pre-authenticated URLs.
         *                          Only use this when using rgw and to work around
         *                          http://tracker.newdream.net/issues/3454.
-        *   - readUsers           : Swift users that should have read access (account:username)
-        *   - writeUsers          : Swift users that should have write access (account:username)
+        *   - readUsers           : Swift users with read access to public containers (account:username)
+        *   - writeUsers          : Swift users with write access to public containers (account:username)
+        *   - secureReadUsers     : Swift users with read access to private containers (account:username)
+        *   - secureWriteUsers    : Swift users with write access to private containers (account:username)
         */
        public function __construct( array $config ) {
                parent::__construct( $config );
@@ -148,6 +154,12 @@ class SwiftFileBackend extends FileBackendStore {
                $this->writeUsers = isset( $config['writeUsers'] )
                        ? $config['writeUsers']
                        : [];
+               $this->secureReadUsers = isset( $config['secureReadUsers'] )
+                       ? $config['secureReadUsers']
+                       : [];
+               $this->secureWriteUsers = isset( $config['secureWriteUsers'] )
+                       ? $config['secureWriteUsers']
+                       : [];
        }
 
        public function getFeatures() {
@@ -625,8 +637,8 @@ class SwiftFileBackend extends FileBackendStore {
 
                $stat = $this->getContainerStat( $fullCont );
                if ( is_array( $stat ) ) {
-                       $readUsers = array_merge( $this->readUsers, [ $this->swiftUser ] );
-                       $writeUsers = array_merge( $this->writeUsers, [ $this->swiftUser ] );
+                       $readUsers = array_merge( $this->secureReadUsers, [ $this->swiftUser ] );
+                       $writeUsers = array_merge( $this->secureWriteUsers, [ $this->swiftUser ] );
                        // Make container private to end-users...
                        $status->merge( $this->setContainerAccess(
                                $fullCont,
@@ -1463,13 +1475,15 @@ class SwiftFileBackend extends FileBackendStore {
 
                // @see SwiftFileBackend::setContainerAccess()
                if ( empty( $params['noAccess'] ) ) {
-                       $readUsers = array_merge( $this->readUsers, [ '.r:*', $this->swiftUser ] ); // public
+                       // public
+                       $readUsers = array_merge( $this->readUsers, [ '.r:*', $this->swiftUser ] );
+                       $writeUsers = array_merge( $this->writeUsers, [ $this->swiftUser ] );
                } else {
-                       $readUsers = array_merge( $this->readUsers, [ $this->swiftUser ] ); // private
+                       // private
+                       $readUsers = array_merge( $this->secureReadUsers, [ $this->swiftUser ] );
+                       $writeUsers = array_merge( $this->secureWriteUsers, [ $this->swiftUser ] );
                }
 
-               $writeUsers = array_merge( $this->writeUsers, [ $this->swiftUser ] ); // sanity
-
                list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $this->http->run( [
                        'method' => 'PUT',
                        'url' => $this->storageUrl( $auth, $container ),
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..8ccccc3 100644 (file)
@@ -27,6 +27,7 @@ namespace Wikimedia\Rdbms;
 
 use Psr\Log\LoggerAwareInterface;
 use Psr\Log\LoggerInterface;
+use Psr\Log\NullLogger;
 use Wikimedia\ScopedCallback;
 use Wikimedia\Timestamp\ConvertibleTimestamp;
 use Wikimedia;
@@ -58,6 +59,9 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
        const SLOW_WRITE_SEC = 0.500;
        const SMALL_WRITE_ROWS = 100;
 
+       /** @var string Whether lock granularity is on the level of the entire database */
+       const ATTR_DB_LEVEL_LOCKING = 'db-level-locking';
+
        /** @var string SQL query */
        protected $lastQuery = '';
        /** @var float|bool UNIX timestamp of last write query */
@@ -236,6 +240,9 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
        /** @var TransactionProfiler */
        protected $trxProfiler;
 
+       /** @var int */
+       protected $nonNativeInsertSelectBatchSize = 10000;
+
        /**
         * Constructor and database handle and attempt to connect to the DB server
         *
@@ -278,6 +285,10 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                $this->queryLogger = $params['queryLogger'];
                $this->errorLogger = $params['errorLogger'];
 
+               if ( isset( $params['nonNativeInsertSelectBatchSize'] ) ) {
+                       $this->nonNativeInsertSelectBatchSize = $params['nonNativeInsertSelectBatchSize'];
+               }
+
                // Set initial dummy domain until open() sets the final DB/prefix
                $this->currentDomain = DatabaseDomain::newUnspecified();
 
@@ -299,7 +310,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
         *
         * This also connects to the database immediately upon object construction
         *
-        * @param string $dbType A possible DB type (sqlite, mysql, postgres)
+        * @param string $dbType A possible DB type (sqlite, mysql, postgres,...)
         * @param array $p Parameter map with keys:
         *   - host : The hostname of the DB server
         *   - user : The name of the database user the client operates under
@@ -331,11 +342,74 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
         *   - cliMode: Whether to consider the execution context that of a CLI script.
         *   - agent: Optional name used to identify the end-user in query profiling/logging.
         *   - srvCache: Optional BagOStuff instance to an APC-style cache.
+        *   - nonNativeInsertSelectBatchSize: Optional batch size for non-native INSERT SELECT emulation.
         * @return Database|null If the database driver or extension cannot be found
         * @throws InvalidArgumentException If the database driver or extension cannot be found
         * @since 1.18
         */
        final public static function factory( $dbType, $p = [] ) {
+               $class = self::getClass( $dbType, isset( $p['driver'] ) ? $p['driver'] : null );
+
+               if ( class_exists( $class ) && is_subclass_of( $class, IDatabase::class ) ) {
+                       // Resolve some defaults for b/c
+                       $p['host'] = isset( $p['host'] ) ? $p['host'] : false;
+                       $p['user'] = isset( $p['user'] ) ? $p['user'] : false;
+                       $p['password'] = isset( $p['password'] ) ? $p['password'] : false;
+                       $p['dbname'] = isset( $p['dbname'] ) ? $p['dbname'] : false;
+                       $p['flags'] = isset( $p['flags'] ) ? $p['flags'] : 0;
+                       $p['variables'] = isset( $p['variables'] ) ? $p['variables'] : [];
+                       $p['tablePrefix'] = isset( $p['tablePrefix'] ) ? $p['tablePrefix'] : '';
+                       $p['schema'] = isset( $p['schema'] ) ? $p['schema'] : '';
+                       $p['cliMode'] = isset( $p['cliMode'] )
+                               ? $p['cliMode']
+                               : ( PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg' );
+                       $p['agent'] = isset( $p['agent'] ) ? $p['agent'] : '';
+                       if ( !isset( $p['connLogger'] ) ) {
+                               $p['connLogger'] = new NullLogger();
+                       }
+                       if ( !isset( $p['queryLogger'] ) ) {
+                               $p['queryLogger'] = new NullLogger();
+                       }
+                       $p['profiler'] = isset( $p['profiler'] ) ? $p['profiler'] : null;
+                       if ( !isset( $p['trxProfiler'] ) ) {
+                               $p['trxProfiler'] = new TransactionProfiler();
+                       }
+                       if ( !isset( $p['errorLogger'] ) ) {
+                               $p['errorLogger'] = function ( Exception $e ) {
+                                       trigger_error( get_class( $e ) . ': ' . $e->getMessage(), E_USER_WARNING );
+                               };
+                       }
+
+                       $conn = new $class( $p );
+               } else {
+                       $conn = null;
+               }
+
+               return $conn;
+       }
+
+       /**
+        * @param string $dbType A possible DB type (sqlite, mysql, postgres,...)
+        * @param string|null $driver Optional name of a specific DB client driver
+        * @return array Map of (Database::ATTRIBUTE_* constant => value) for all such constants
+        * @throws InvalidArgumentException
+        * @since 1.31
+        */
+       final public static function attributesFromType( $dbType, $driver = null ) {
+               static $defaults = [ self::ATTR_DB_LEVEL_LOCKING => false ];
+
+               $class = self::getClass( $dbType, $driver );
+
+               return call_user_func( [ $class, 'getAttributes' ] ) + $defaults;
+       }
+
+       /**
+        * @param string $dbType A possible DB type (sqlite, mysql, postgres,...)
+        * @param string|null $driver Optional name of a specific DB client driver
+        * @return string Database subclass name to use
+        * @throws InvalidArgumentException
+        */
+       private static function getClass( $dbType, $driver = null ) {
                // For database types with built-in support, the below maps type to IDatabase
                // implementations. For types with multipe driver implementations (PHP extensions),
                // an array can be used, keyed by extension name. In case of an array, the
@@ -351,17 +425,18 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
 
                $dbType = strtolower( $dbType );
                $class = false;
+
                if ( isset( $builtinTypes[$dbType] ) ) {
                        $possibleDrivers = $builtinTypes[$dbType];
                        if ( is_string( $possibleDrivers ) ) {
                                $class = $possibleDrivers;
                        } else {
-                               if ( !empty( $p['driver'] ) ) {
-                                       if ( !isset( $possibleDrivers[$p['driver']] ) ) {
+                               if ( (string)$driver !== '' ) {
+                                       if ( !isset( $possibleDrivers[$driver] ) ) {
                                                throw new InvalidArgumentException( __METHOD__ .
-                                                       " type '$dbType' does not support driver '{$p['driver']}'" );
+                                                       " type '$dbType' does not support driver '{$driver}'" );
                                        } else {
-                                               $class = $possibleDrivers[$p['driver']];
+                                               $class = $possibleDrivers[$driver];
                                        }
                                } else {
                                        foreach ( $possibleDrivers as $posDriver => $possibleClass ) {
@@ -381,42 +456,15 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                                " no viable database extension found for type '$dbType'" );
                }
 
-               if ( class_exists( $class ) && is_subclass_of( $class, IDatabase::class ) ) {
-                       // Resolve some defaults for b/c
-                       $p['host'] = isset( $p['host'] ) ? $p['host'] : false;
-                       $p['user'] = isset( $p['user'] ) ? $p['user'] : false;
-                       $p['password'] = isset( $p['password'] ) ? $p['password'] : false;
-                       $p['dbname'] = isset( $p['dbname'] ) ? $p['dbname'] : false;
-                       $p['flags'] = isset( $p['flags'] ) ? $p['flags'] : 0;
-                       $p['variables'] = isset( $p['variables'] ) ? $p['variables'] : [];
-                       $p['tablePrefix'] = isset( $p['tablePrefix'] ) ? $p['tablePrefix'] : '';
-                       $p['schema'] = isset( $p['schema'] ) ? $p['schema'] : '';
-                       $p['cliMode'] = isset( $p['cliMode'] )
-                               ? $p['cliMode']
-                               : ( PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg' );
-                       $p['agent'] = isset( $p['agent'] ) ? $p['agent'] : '';
-                       if ( !isset( $p['connLogger'] ) ) {
-                               $p['connLogger'] = new \Psr\Log\NullLogger();
-                       }
-                       if ( !isset( $p['queryLogger'] ) ) {
-                               $p['queryLogger'] = new \Psr\Log\NullLogger();
-                       }
-                       $p['profiler'] = isset( $p['profiler'] ) ? $p['profiler'] : null;
-                       if ( !isset( $p['trxProfiler'] ) ) {
-                               $p['trxProfiler'] = new TransactionProfiler();
-                       }
-                       if ( !isset( $p['errorLogger'] ) ) {
-                               $p['errorLogger'] = function ( Exception $e ) {
-                                       trigger_error( get_class( $e ) . ': ' . $e->getMessage(), E_USER_WARNING );
-                               };
-                       }
-
-                       $conn = new $class( $p );
-               } else {
-                       $conn = null;
-               }
+               return $class;
+       }
 
-               return $conn;
+       /**
+        * @return array Map of (Database::ATTRIBUTE_* constant => value
+        * @since 1.31
+        */
+       protected static function getAttributes() {
+               return [];
        }
 
        /**
@@ -1413,14 +1461,27 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                list( $startOpts, $useIndex, $preLimitTail, $postLimitTail, $ignoreIndex ) =
                        $this->makeSelectOptions( $options );
 
-               if ( !empty( $conds ) ) {
-                       if ( is_array( $conds ) ) {
-                               $conds = $this->makeList( $conds, self::LIST_AND );
-                       }
+               if ( is_array( $conds ) ) {
+                       $conds = $this->makeList( $conds, self::LIST_AND );
+               }
+
+               if ( $conds === null || $conds === false ) {
+                       $this->queryLogger->warning(
+                               __METHOD__
+                               . ' called from '
+                               . $fname
+                               . ' with incorrect parameters: $conds must be a string or an array'
+                       );
+                       $conds = '';
+               }
+
+               if ( $conds === '' ) {
+                       $sql = "SELECT $startOpts $vars $from $useIndex $ignoreIndex $preLimitTail";
+               } elseif ( is_string( $conds ) ) {
                        $sql = "SELECT $startOpts $vars $from $useIndex $ignoreIndex " .
                                "WHERE $conds $preLimitTail";
                } else {
-                       $sql = "SELECT $startOpts $vars $from $useIndex $ignoreIndex $preLimitTail";
+                       throw new DBUnexpectedError( $this, __METHOD__ . ' called with incorrect parameters' );
                }
 
                if ( isset( $options['LIMIT'] ) ) {
@@ -2097,8 +2158,8 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                }
 
                // We can't separate explicit JOIN clauses with ',', use ' ' for those
-               $implicitJoins = !empty( $ret ) ? implode( ',', $ret ) : "";
-               $explicitJoins = !empty( $retJOIN ) ? implode( ' ', $retJOIN ) : "";
+               $implicitJoins = $ret ? implode( ',', $ret ) : "";
+               $explicitJoins = $retJOIN ? implode( ' ', $retJOIN ) : "";
 
                // Compile our final table clause
                return implode( ' ', [ $implicitJoins, $explicitJoins ] );
@@ -2244,40 +2305,46 @@ 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 (' .
+               try {
+                       $this->startAtomic( $fname );
+                       $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 );
+                               }
+
+                               if ( $indexWhereClauses ) {
+                                       $this->delete( $table, $this->makeList( $indexWhereClauses, LIST_OR ), $fname );
+                                       $affectedRowCount += $this->affectedRows();
                                }
-                               $indexWhereClauses[] = $this->makeList( $indexRowValues, LIST_AND );
-                       }
 
-                       if ( $indexWhereClauses ) {
-                               $this->delete( $table, $this->makeList( $indexWhereClauses, LIST_OR ), $fname );
+                               // Now insert the row
+                               $this->insert( $table, $row, $fname );
                                $affectedRowCount += $this->affectedRows();
                        }
-
-                       // Now insert the row
-                       $this->insert( $table, $row, $fname );
-                       $affectedRowCount += $this->affectedRows();
+                       $this->endAtomic( $fname );
+                       $this->affectedRowCount = $affectedRowCount;
+               } catch ( Exception $e ) {
+                       $this->rollback( $fname, self::FLUSHING_INTERNAL );
+                       throw $e;
                }
-
-               $this->affectedRowCount = $affectedRowCount;
        }
 
        /**
@@ -2343,11 +2410,8 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                }
 
                $affectedRowCount = 0;
-               $useTrx = !$this->trxLevel;
-               if ( $useTrx ) {
-                       $this->begin( $fname, self::TRANSACTION_INTERNAL );
-               }
                try {
+                       $this->startAtomic( $fname );
                        # Update any existing conflicting row(s)
                        if ( $where !== false ) {
                                $ok = $this->update( $table, $set, $where, $fname );
@@ -2358,16 +2422,12 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                        # Now insert any non-conflicting row(s)
                        $ok = $this->insert( $table, $rows, $fname, [ 'IGNORE' ] ) && $ok;
                        $affectedRowCount += $this->affectedRows();
+                       $this->endAtomic( $fname );
+                       $this->affectedRowCount = $affectedRowCount;
                } catch ( Exception $e ) {
-                       if ( $useTrx ) {
-                               $this->rollback( $fname, self::FLUSHING_INTERNAL );
-                       }
+                       $this->rollback( $fname, self::FLUSHING_INTERNAL );
                        throw $e;
                }
-               if ( $useTrx ) {
-                       $this->commit( $fname, self::FLUSHING_INTERNAL );
-               }
-               $this->affectedRowCount = $affectedRowCount;
 
                return $ok;
        }
@@ -2425,11 +2485,16 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                return $this->query( $sql, $fname );
        }
 
-       public function insertSelect(
+       final public function insertSelect(
                $destTable, $srcTable, $varMap, $conds,
                $fname = __METHOD__, $insertOptions = [], $selectOptions = [], $selectJoinConds = []
        ) {
-               if ( $this->cliMode ) {
+               static $hints = [ 'NO_AUTO_COLUMNS' ];
+
+               $insertOptions = (array)$insertOptions;
+               $selectOptions = (array)$selectOptions;
+
+               if ( $this->cliMode && $this->isInsertSelectSafe( $insertOptions, $selectOptions ) ) {
                        // For massive migrations with downtime, we don't want to select everything
                        // into memory and OOM, so do all this native on the server side if possible.
                        return $this->nativeInsertSelect(
@@ -2438,7 +2503,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                                $varMap,
                                $conds,
                                $fname,
-                               $insertOptions,
+                               array_diff( $insertOptions, $hints ),
                                $selectOptions,
                                $selectJoinConds
                        );
@@ -2450,12 +2515,22 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                        $varMap,
                        $conds,
                        $fname,
-                       $insertOptions,
+                       array_diff( $insertOptions, $hints ),
                        $selectOptions,
                        $selectJoinConds
                );
        }
 
+       /**
+        * @param array $insertOptions INSERT options
+        * @param array $selectOptions SELECT options
+        * @return bool Whether an INSERT SELECT with these options will be replication safe
+        * @since 1.31
+        */
+       protected function isInsertSelectSafe( array $insertOptions, array $selectOptions ) {
+               return true;
+       }
+
        /**
         * Implementation of insertSelect() based on select() and insert()
         *
@@ -2490,12 +2565,41 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                        return false;
                }
 
-               $rows = [];
-               foreach ( $res as $row ) {
-                       $rows[] = (array)$row;
+               try {
+                       $affectedRowCount = 0;
+                       $this->startAtomic( $fname );
+                       $rows = [];
+                       $ok = true;
+                       foreach ( $res as $row ) {
+                               $rows[] = (array)$row;
+
+                               // Avoid inserts that are too huge
+                               if ( count( $rows ) >= $this->nonNativeInsertSelectBatchSize ) {
+                                       $ok = $this->insert( $destTable, $rows, $fname, $insertOptions );
+                                       if ( !$ok ) {
+                                               break;
+                                       }
+                                       $affectedRowCount += $this->affectedRows();
+                                       $rows = [];
+                               }
+                       }
+                       if ( $rows && $ok ) {
+                               $ok = $this->insert( $destTable, $rows, $fname, $insertOptions );
+                               if ( $ok ) {
+                                       $affectedRowCount += $this->affectedRows();
+                               }
+                       }
+                       if ( $ok ) {
+                               $this->endAtomic( $fname );
+                               $this->affectedRowCount = $affectedRowCount;
+                       } else {
+                               $this->rollback( $fname, self::FLUSHING_INTERNAL );
+                       }
+                       return $ok;
+               } catch ( Exception $e ) {
+                       $this->rollback( $fname, self::FLUSHING_INTERNAL );
+                       throw $e;
                }
-
-               return $this->insert( $destTable, $rows, $fname, $insertOptions );
        }
 
        /**
@@ -3124,6 +3228,8 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                } catch ( Exception $e ) {
                        // already logged; let LoadBalancer move on during mass-rollback
                }
+
+               $this->affectedRowCount = 0; // for the sake of consistency
        }
 
        /**
index b1c8909..771e2e5 100644 (file)
@@ -569,7 +569,7 @@ class DatabaseMssql extends Database {
                        }
                }
 
-               return empty( $result ) ? false : $result;
+               return $result ?: false;
        }
 
        /**
index 57b7544..a5220b9 100644 (file)
@@ -67,6 +67,8 @@ abstract class DatabaseMysqlBase extends Database {
        private $serverVersion = null;
        /** @var bool|null */
        private $insertSelectIsSafe = null;
+       /** @var stdClass|null */
+       private $replicationInfoRow = null;
 
        /**
         * Additional $params include:
@@ -508,19 +510,35 @@ abstract class DatabaseMysqlBase extends Database {
                return $this->nativeReplace( $table, $rows, $fname );
        }
 
-       protected function nativeInsertSelect(
-               $destTable, $srcTable, $varMap, $conds,
-               $fname = __METHOD__, $insertOptions = [], $selectOptions = [], $selectJoinConds = []
-       ) {
-               if ( $this->insertSelectIsSafe === null ) {
-                       // In MySQL, an INSERT SELECT is only replication safe with row-based
-                       // replication or if innodb_autoinc_lock_mode is 0. When those
-                       // conditions aren't met, use non-native mode.
-                       // While we could try to determine if the insert is safe anyway by
-                       // checking if the target table has an auto-increment column that
-                       // isn't set in $varMap, that seems unlikely to be worth the extra
-                       // complexity.
-                       $row = $this->selectRow(
+       protected function isInsertSelectSafe( array $insertOptions, array $selectOptions ) {
+               $row = $this->getReplicationSafetyInfo();
+               // For row-based-replication, the resulting changes will be relayed, not the query
+               if ( $row->binlog_format === 'ROW' ) {
+                       return true;
+               }
+               // LIMIT requires ORDER BY on a unique key or it is non-deterministic
+               if ( isset( $selectOptions['LIMIT'] ) ) {
+                       return false;
+               }
+               // In MySQL, an INSERT SELECT is only replication safe with row-based
+               // replication or if innodb_autoinc_lock_mode is 0. When those
+               // conditions aren't met, use non-native mode.
+               // While we could try to determine if the insert is safe anyway by
+               // checking if the target table has an auto-increment column that
+               // isn't set in $varMap, that seems unlikely to be worth the extra
+               // complexity.
+               return (
+                       in_array( 'NO_AUTO_COLUMNS', $insertOptions ) ||
+                       (int)$row->innodb_autoinc_lock_mode === 0
+               );
+       }
+
+       /**
+        * @return stdClass Process cached row
+        */
+       protected function getReplicationSafetyInfo() {
+               if ( $this->replicationInfoRow === null ) {
+                       $this->replicationInfoRow = $this->selectRow(
                                false,
                                [
                                        'innodb_autoinc_lock_mode' => '@@innodb_autoinc_lock_mode',
@@ -529,33 +547,9 @@ abstract class DatabaseMysqlBase extends Database {
                                [],
                                __METHOD__
                        );
-                       $this->insertSelectIsSafe = $row->binlog_format === 'ROW' ||
-                               (int)$row->innodb_autoinc_lock_mode === 0;
-               }
-
-               if ( !$this->insertSelectIsSafe ) {
-                       return $this->nonNativeInsertSelect(
-                               $destTable,
-                               $srcTable,
-                               $varMap,
-                               $conds,
-                               $fname,
-                               $insertOptions,
-                               $selectOptions,
-                               $selectJoinConds
-                       );
-               } else {
-                       return parent::nativeInsertSelect(
-                               $destTable,
-                               $srcTable,
-                               $varMap,
-                               $conds,
-                               $fname,
-                               $insertOptions,
-                               $selectOptions,
-                               $selectJoinConds
-                       );
                }
+
+               return $this->replicationInfoRow;
        }
 
        /**
@@ -677,7 +671,7 @@ abstract class DatabaseMysqlBase extends Database {
                        }
                }
 
-               return empty( $result ) ? false : $result;
+               return $result ?: false;
        }
 
        /**
index 9152d1e..984e1c0 100644 (file)
@@ -25,6 +25,7 @@ namespace Wikimedia\Rdbms;
 use mysqli;
 use mysqli_result;
 use IP;
+use stdClass;
 
 /**
  * Database abstraction object for PHP extension mysqli.
@@ -34,8 +35,6 @@ use IP;
  * @see Database
  */
 class DatabaseMysqli extends DatabaseMysqlBase {
-       /** @var mysqli $mConn */
-
        /**
         * @param string $sql
         * @return resource
@@ -162,7 +161,7 @@ class DatabaseMysqli extends DatabaseMysqlBase {
         * @return int
         */
        function lastErrno() {
-               if ( $this->conn ) {
+               if ( $this->conn instanceof mysqli ) {
                        return $this->conn->errno;
                } else {
                        return mysqli_connect_errno();
@@ -202,7 +201,7 @@ class DatabaseMysqli extends DatabaseMysqlBase {
 
        /**
         * @param mysqli_result $res
-        * @return bool
+        * @return stdClass|bool
         */
        protected function mysqlFetchObject( $res ) {
                $object = $res->fetch_object();
@@ -235,7 +234,7 @@ class DatabaseMysqli extends DatabaseMysqlBase {
        }
 
        /**
-        * @param mysqli $res
+        * @param mysqli_result $res
         * @return mixed
         */
        protected function mysqlNumFields( $res ) {
@@ -243,7 +242,7 @@ class DatabaseMysqli extends DatabaseMysqlBase {
        }
 
        /**
-        * @param mysqli $res
+        * @param mysqli_result $res
         * @param int $n
         * @return mixed
         */
@@ -266,7 +265,7 @@ class DatabaseMysqli extends DatabaseMysqlBase {
        }
 
        /**
-        * @param mysqli $res
+        * @param mysqli_result $res
         * @param int $n
         * @return mixed
         */
@@ -277,7 +276,7 @@ class DatabaseMysqli extends DatabaseMysqlBase {
        }
 
        /**
-        * @param mysqli $res
+        * @param mysqli_result $res
         * @param int $n
         * @return mixed
         */
index 7d34641..38cc4ae 100644 (file)
@@ -394,7 +394,7 @@ class DatabasePostgres extends Database {
                        // Forced result for simulated queries
                        return $this->lastAffectedRowCount;
                }
-               if ( empty( $this->lastResultHandle ) ) {
+               if ( !$this->lastResultHandle ) {
                        return 0;
                }
 
index 58b6ef9..3d6cee3 100644 (file)
@@ -103,6 +103,10 @@ class DatabaseSqlite extends Database {
                ] );
        }
 
+       protected static function getAttributes() {
+               return [ self::ATTR_DB_LEVEL_LOCKING => true ];
+       }
+
        /**
         * @param string $filename
         * @param array $p Options map; supports:
index d59bee3..9ad78a7 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:
@@ -1276,7 +1276,9 @@ interface IDatabase {
         * @param string $fname The function name of the caller, from __METHOD__
         *
         * @param array $insertOptions Options for the INSERT part of the query, see
-        *    IDatabase::insert() for details.
+        *    IDatabase::insert() for details. Also, one additional option is
+        *    available: pass 'NO_AUTO_COLUMNS' to hint that the query does not use
+        *    an auto-increment or sequence to determine any column values.
         * @param array $selectOptions Options for the SELECT part of the query, see
         *    IDatabase::select() for details.
         * @param array $selectJoinConds Join conditions for the SELECT part of the query, see
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..8210507 100644 (file)
@@ -172,7 +172,9 @@ interface ILoadBalancer {
        /**
         * Get a connection handle by server index
         *
-        * Avoid using CONN_TRX_AUTO with sqlite (e.g. check getServerType() first)
+        * The CONN_TRX_AUTO flag is ignored for databases with ATTR_DB_LEVEL_LOCKING
+        * (e.g. sqlite) in order to avoid deadlocks. ILoadBalancer::getServerAttributes()
+        * can be used to check such flags beforehand.
         *
         * If the caller uses $domain or sets CONN_TRX_AUTO in $flags, then it must also
         * call ILoadBalancer::reuseConnection() on the handle when finished using it.
@@ -199,14 +201,16 @@ interface ILoadBalancer {
         * @param IDatabase $conn
         * @throws InvalidArgumentException
         */
-       public function reuseConnection( $conn );
+       public function reuseConnection( IDatabase $conn );
 
        /**
         * Get a database connection handle reference
         *
         * The handle's methods simply wrap those of a Database handle
         *
-        * Avoid using CONN_TRX_AUTO with sqlite (e.g. check getServerType() first)
+        * The CONN_TRX_AUTO flag is ignored for databases with ATTR_DB_LEVEL_LOCKING
+        * (e.g. sqlite) in order to avoid deadlocks. ILoadBalancer::getServerAttributes()
+        * can be used to check such flags beforehand.
         *
         * @see ILoadBalancer::getConnection() for parameter information
         *
@@ -223,7 +227,9 @@ interface ILoadBalancer {
         *
         * The handle's methods simply wrap those of a Database handle
         *
-        * Avoid using CONN_TRX_AUTO with sqlite (e.g. check getServerType() first)
+        * The CONN_TRX_AUTO flag is ignored for databases with ATTR_DB_LEVEL_LOCKING
+        * (e.g. sqlite) in order to avoid deadlocks. ILoadBalancer::getServerAttributes()
+        * can be used to check such flags beforehand.
         *
         * @see ILoadBalancer::getConnection() for parameter information
         *
@@ -240,7 +246,9 @@ interface ILoadBalancer {
         *
         * The handle's methods simply wrap those of a Database handle
         *
-        * Avoid using CONN_TRX_AUTO with sqlite (e.g. check getServerType() first)
+        * The CONN_TRX_AUTO flag is ignored for databases with ATTR_DB_LEVEL_LOCKING
+        * (e.g. sqlite) in order to avoid deadlocks. ILoadBalancer::getServerAttributes()
+        * can be used to check such flags beforehand.
         *
         * @see ILoadBalancer::getConnection() for parameter information
         *
@@ -258,7 +266,9 @@ interface ILoadBalancer {
         * The index must be an actual index into the array. If a connection to the server is
         * already open and not considered an "in use" foreign connection, this simply returns it.
         *
-        * Avoid using CONN_TRX_AUTO with sqlite (e.g. check getServerType() first)
+        * Avoid using CONN_TRX_AUTO for databases with ATTR_DB_LEVEL_LOCKING (e.g. sqlite) in
+        * order to avoid deadlocks. ILoadBalancer::getServerAttributes() can be used to check
+        * such flags beforehand.
         *
         * If the caller uses $domain or sets CONN_TRX_AUTO in $flags, then it must also
         * call ILoadBalancer::reuseConnection() on the handle when finished using it.
@@ -319,6 +329,13 @@ interface ILoadBalancer {
         */
        public function getServerType( $i );
 
+       /**
+        * @param int $i Server index
+        * @return array (Database::ATTRIBUTE_* constant => value) for all such constants
+        * @since 1.31
+        */
+       public function getServerAttributes( $i );
+
        /**
         * Get the current master position for chronology control purposes
         * @return DBMasterPos|bool Returns false if not applicable
index ccdb48e..99a24c2 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 );
@@ -677,6 +680,22 @@ class LoadBalancer implements ILoadBalancer {
                        $domain = false; // local connection requested
                }
 
+               if ( ( $flags & self::CONN_TRX_AUTO ) === self::CONN_TRX_AUTO ) {
+                       // Assuming all servers are of the same type (or similar), which is overwhelmingly
+                       // the case, use the master server information to get the attributes. The information
+                       // for $i cannot be used since it might be DB_REPLICA, which might require connection
+                       // attempts in order to be resolved into a real server index.
+                       $attributes = $this->getServerAttributes( $this->getWriterIndex() );
+                       if ( $attributes[Database::ATTR_DB_LEVEL_LOCKING] ) {
+                               // Callers sometimes want to (a) escape REPEATABLE-READ stateness without locking
+                               // rows (e.g. FOR UPDATE) or (b) make small commits during a larger transactions
+                               // to reduce lock contention. None of these apply for sqlite and using separate
+                               // connections just causes self-deadlocks.
+                               $flags &= ~self::CONN_TRX_AUTO;
+                               $this->connLogger->info( __METHOD__ . ': ignoring CONN_TRX_AUTO to avoid deadlocks.' );
+                       }
+               }
+
                $groups = ( $groups === false || $groups === [] )
                        ? [ false ] // check one "group": the generic pool
                        : (array)$groups;
@@ -736,7 +755,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 +773,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 +875,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;
                                }
@@ -975,6 +997,13 @@ class LoadBalancer implements ILoadBalancer {
                return $conn;
        }
 
+       public function getServerAttributes( $i ) {
+               return Database::attributesFromType(
+                       $this->getServerType( $i ),
+                       isset( $this->servers[$i]['driver'] ) ? $this->servers[$i]['driver'] : null
+               );
+       }
+
        /**
         * Test if the specified index represents an open connection
         *
@@ -1085,7 +1114,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 +1123,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 +1192,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 +1218,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 +1715,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 +1723,7 @@ class LoadBalancer implements ILoadBalancer {
                                ] );
                                $ok = false;
                        } else {
-                               $this->replLogger->info( __METHOD__ . ': Done' );
+                               $this->replLogger->debug( __METHOD__ . ': done waiting' );
                                $ok = true;
                        }
                } else {
index 35502c7..80a138d 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,
@@ -211,6 +213,30 @@ class DatabaseLogEntry extends LogEntryBase {
                }
        }
 
+       /**
+        * Loads a LogEntry with the given id from database
+        *
+        * @param int $id
+        * @param IDatabase $db
+        * @return DatabaseLogEntry|null
+        */
+       public static function newFromId( $id, IDatabase $db ) {
+               $queryInfo = self::getSelectQueryData();
+               $queryInfo['conds'] += [ 'log_id' => $id ];
+               $row = $db->selectRow(
+                       $queryInfo['tables'],
+                       $queryInfo['fields'],
+                       $queryInfo['conds'],
+                       __METHOD__,
+                       $queryInfo['options'],
+                       $queryInfo['join_conds']
+               );
+               if ( !$row ) {
+                       return null;
+               }
+               return self::newFromRow( $row );
+       }
+
        /** @var stdClass Database result row. */
        protected $row;
 
@@ -293,11 +319,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 +385,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 +625,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 +639,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 +675,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 +684,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..24fdfb0 100644 (file)
@@ -176,13 +176,12 @@ 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
+               $this->mConds[] = ActorMigration::newMigration()->getWhere(
+                       wfGetDB( DB_REPLICA ), 'log_user', User::newFromName( $name, false )
+               )['conds'];
+
                $this->enforcePerformerRestrictions();
 
                $this->performer = $name;
@@ -423,9 +422,9 @@ class LogPager extends ReverseChronologicalPager {
                $this->actionRestrictionsEnforced = true;
                $user = $this->getUser();
                if ( !$user->isAllowed( 'deletedhistory' ) ) {
-                       $this->mConds[] = $this->mDb->bitAnd( 'log_deleted', LogPage::DELETED_USER ) . ' = 0';
+                       $this->mConds[] = $this->mDb->bitAnd( 'log_deleted', LogPage::DELETED_ACTION ) . ' = 0';
                } elseif ( !$user->isAllowedAny( 'suppressrevision', 'viewsuppressed' ) ) {
-                       $this->mConds[] = $this->mDb->bitAnd( 'log_deleted', LogPage::SUPPRESSED_USER ) .
+                       $this->mConds[] = $this->mDb->bitAnd( 'log_deleted', LogPage::SUPPRESSED_ACTION ) .
                                ' != ' . LogPage::SUPPRESSED_USER;
                }
        }
@@ -441,9 +440,9 @@ class LogPager extends ReverseChronologicalPager {
                $this->performerRestrictionsEnforced = true;
                $user = $this->getUser();
                if ( !$user->isAllowed( 'deletedhistory' ) ) {
-                       $this->mConds[] = $this->mDb->bitAnd( 'log_deleted', LogPage::DELETED_ACTION ) . ' = 0';
+                       $this->mConds[] = $this->mDb->bitAnd( 'log_deleted', LogPage::DELETED_USER ) . ' = 0';
                } elseif ( !$user->isAllowedAny( 'suppressrevision', 'viewsuppressed' ) ) {
-                       $this->mConds[] = $this->mDb->bitAnd( 'log_deleted', LogPage::SUPPRESSED_ACTION ) .
+                       $this->mConds[] = $this->mDb->bitAnd( 'log_deleted', LogPage::SUPPRESSED_USER ) .
                                ' != ' . LogPage::SUPPRESSED_ACTION;
                }
        }
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 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 544d23d..ca7d747 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',
                ];
 
@@ -2772,7 +2774,7 @@ class WikiPage implements Page, IDBAccessObject {
         * @param int $u1 Unused
         * @param bool $u2 Unused
         * @param array|string &$error Array of errors to append to
-        * @param User $user The deleting user
+        * @param User $deleter The deleting user
         * @param array $tags Tags to apply to the deletion action
         * @param string $logsubtype
         * @return Status Status object; if successful, $status->value is the log_id of the
@@ -2780,10 +2782,11 @@ class WikiPage implements Page, IDBAccessObject {
         *   found, $status is a non-fatal 'cannotdelete' error
         */
        public function doDeleteArticleReal(
-               $reason, $suppress = false, $u1 = null, $u2 = null, &$error = '', User $user = null,
+               $reason, $suppress = false, $u1 = null, $u2 = null, &$error = '', User $deleter = null,
                $tags = [], $logsubtype = 'delete'
        ) {
-               global $wgUser, $wgContentHandlerUseDB, $wgCommentTableSchemaMigrationStage;
+               global $wgUser, $wgContentHandlerUseDB, $wgCommentTableSchemaMigrationStage,
+                       $wgActorTableSchemaMigrationStage;
 
                wfDebug( __METHOD__ . "\n" );
 
@@ -2798,9 +2801,9 @@ class WikiPage implements Page, IDBAccessObject {
                // Avoid PHP 7.1 warning of passing $this by reference
                $wikiPage = $this;
 
-               $user = is_null( $user ) ? $wgUser : $user;
+               $deleter = is_null( $deleter ) ? $wgUser : $deleter;
                if ( !Hooks::run( 'ArticleDelete',
-                       [ &$wikiPage, &$user, &$reason, &$error, &$status, $suppress ]
+                       [ &$wikiPage, &$deleter, &$reason, &$error, &$status, $suppress ]
                ) ) {
                        if ( $status->isOK() ) {
                                // Hook aborted but didn't set a fatal status
@@ -2848,6 +2851,7 @@ class WikiPage implements Page, IDBAccessObject {
                }
 
                $commentStore = CommentStore::getStore();
+               $actorMigration = ActorMigration::newMigration();
 
                $revQuery = Revision::getQueryInfo();
                $bitfield = false;
@@ -2884,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,
@@ -2900,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;
@@ -2930,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 ) {
@@ -2940,7 +2947,7 @@ class WikiPage implements Page, IDBAccessObject {
                $logtype = $suppress ? 'suppress' : 'delete';
 
                $logEntry = new ManualLogEntry( $logtype, $logsubtype );
-               $logEntry->setPerformer( $user );
+               $logEntry->setPerformer( $deleter );
                $logEntry->setTarget( $logTitle );
                $logEntry->setComment( $reason );
                $logEntry->setTags( $tags );
@@ -2956,11 +2963,11 @@ class WikiPage implements Page, IDBAccessObject {
 
                $dbw->endAtomic( __METHOD__ );
 
-               $this->doDeleteUpdates( $id, $content, $revision, $user );
+               $this->doDeleteUpdates( $id, $content, $revision, $deleter );
 
                Hooks::run( 'ArticleDeleteComplete', [
                        &$wikiPageBeforeDelete,
-                       &$user,
+                       &$deleter,
                        $reason,
                        $id,
                        $content,
@@ -3161,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' ] ];
@@ -3249,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 8cf8f85..7f78912 100644 (file)
@@ -329,6 +329,14 @@ class BlockLevelPass {
                                                        $this->lastSection = 'pre';
                                                }
                                                $t = substr( $t, 1 );
+                                       } elseif ( preg_match( '/^(?:<style\\b[^>]*>.*?<\\/style>\s*|<link\\b[^>]*>\s*)+$/iS', $t ) ) {
+                                               # T186965: <style> or <link> by itself on a line shouldn't open or close paragraphs.
+                                               # But it should clear $pendingPTag.
+                                               if ( $pendingPTag ) {
+                                                       $output .= $this->closeParagraph();
+                                                       $pendingPTag = false;
+                                                       $this->lastSection = '';
+                                               }
                                        } else {
                                                # paragraph
                                                if ( trim( $t ) === '' ) {
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 2adfd0a..8e5dcbd 100644 (file)
@@ -358,7 +358,7 @@ class Parser {
                $this->mLangLinkLanguages = [];
                $this->currentRevisionCache = null;
 
-               $this->mStripState = new StripState;
+               $this->mStripState = new StripState( $this );
 
                # Clear these on every parse, T6549
                $this->mTplRedirCache = $this->mTplDomCache = [];
@@ -543,6 +543,11 @@ class Parser {
                $this->mOutput->setLimitReportData( 'limitreport-expensivefunctioncount',
                        [ $this->mExpensiveFunctionCount, $this->mOptions->getExpensiveParserFunctionLimit() ]
                );
+
+               foreach ( $this->mStripState->getLimitReport() as list( $key, $value ) ) {
+                       $this->mOutput->setLimitReportData( $key, $value );
+               }
+
                Hooks::run( 'ParserLimitReportPrepare', [ $this, $this->mOutput ] );
 
                $limitReport = "NewPP limit report\n";
@@ -2215,8 +2220,14 @@ class Parser {
                                $link = $origLink;
                        }
 
-                       $unstrip = $this->mStripState->unstripNoWiki( $link );
-                       $nt = is_string( $unstrip ) ? Title::newFromText( $unstrip ) : null;
+                       // \x7f isn't a default legal title char, so most likely strip
+                       // markers will force us into the "invalid form" path above.  But,
+                       // just in case, let's assert that xmlish tags aren't valid in
+                       // the title position.
+                       $unstrip = $this->mStripState->killMarkers( $link );
+                       $noMarkers = ( $unstrip === $link );
+
+                       $nt = $noMarkers ? Title::newFromText( $link ) : null;
                        if ( $nt === null ) {
                                $s .= $prefix . '[[' . $line;
                                continue;
@@ -5987,11 +5998,13 @@ class Parser {
         * unserializeHalfParsedText(). The text can then be safely incorporated into
         * the return value of a parser hook.
         *
+        * @deprecated since 1.31
         * @param string $text
         *
         * @return array
         */
        public function serializeHalfParsedText( $text ) {
+               wfDeprecated( __METHOD__, '1.31' );
                $data = [
                        'text' => $text,
                        'version' => self::HALF_PARSED_VERSION,
@@ -6012,11 +6025,13 @@ class Parser {
         * If the $data array has been stored persistently, the caller should first
         * check whether it is still valid, by calling isValidHalfParsedText().
         *
+        * @deprecated since 1.31
         * @param array $data Serialized data
         * @throws MWException
         * @return string
         */
        public function unserializeHalfParsedText( $data ) {
+               wfDeprecated( __METHOD__, '1.31' );
                if ( !isset( $data['version'] ) || $data['version'] != self::HALF_PARSED_VERSION ) {
                        throw new MWException( __METHOD__ . ': invalid version' );
                }
@@ -6037,11 +6052,13 @@ class Parser {
         * serializeHalfParsedText(), is compatible with the current version of the
         * parser.
         *
+        * @deprecated since 1.31
         * @param array $data
         *
         * @return bool
         */
        public function isValidHalfParsedText( $data ) {
+               wfDeprecated( __METHOD__, '1.31' );
                return isset( $data['version'] ) && $data['version'] == self::HALF_PARSED_VERSION;
        }
 
index 298aad3..d329f69 100644 (file)
  * @ingroup Parser
  */
 class StripState {
-       protected $prefix;
        protected $data;
        protected $regex;
 
-       protected $tempType, $tempMergePrefix;
+       protected $parser;
+
        protected $circularRefGuard;
-       protected $recursionLevel = 0;
+       protected $depth = 0;
+       protected $highestDepth = 0;
+       protected $expandSize = 0;
 
-       const UNSTRIP_RECURSION_LIMIT = 20;
+       protected $depthLimit = 20;
+       protected $sizeLimit = 5000000;
 
        /**
-        * @param string|null $prefix
-        * @since 1.26 The prefix argument should be omitted, as the strip marker
-        *  prefix string is now a constant.
+        * @param Parser|null $parser
+        * @param array $options
         */
-       public function __construct( $prefix = null ) {
-               if ( $prefix !== null ) {
-                       wfDeprecated( __METHOD__ . ' with called with $prefix argument' .
-                               ' (call with no arguments instead)', '1.26' );
-               }
+       public function __construct( Parser $parser = null, $options = [] ) {
                $this->data = [
                        'nowiki' => [],
                        'general' => []
                ];
                $this->regex = '/' . Parser::MARKER_PREFIX . "([^\x7f<>&'\"]+)" . Parser::MARKER_SUFFIX . '/';
                $this->circularRefGuard = [];
+               $this->parser = $parser;
+
+               if ( isset( $options['depthLimit'] ) ) {
+                       $this->depthLimit = $options['depthLimit'];
+               }
+               if ( isset( $options['sizeLimit'] ) ) {
+                       $this->sizeLimit = $options['sizeLimit'];
+               }
        }
 
        /**
@@ -122,56 +128,109 @@ class StripState {
                        return $text;
                }
 
-               $oldType = $this->tempType;
-               $this->tempType = $type;
-               $text = preg_replace_callback( $this->regex, [ $this, 'unstripCallback' ], $text );
-               $this->tempType = $oldType;
+               $callback = function ( $m ) use ( $type ) {
+                       $marker = $m[1];
+                       if ( isset( $this->data[$type][$marker] ) ) {
+                               if ( isset( $this->circularRefGuard[$marker] ) ) {
+                                       return $this->getWarning( 'parser-unstrip-loop-warning' );
+                               }
+
+                               if ( $this->depth > $this->highestDepth ) {
+                                       $this->highestDepth = $this->depth;
+                               }
+                               if ( $this->depth >= $this->depthLimit ) {
+                                       return $this->getLimitationWarning( 'unstrip-depth', $this->depthLimit );
+                               }
+
+                               $value = $this->data[$type][$marker];
+                               if ( $value instanceof Closure ) {
+                                       $value = $value();
+                               }
+
+                               $this->expandSize += strlen( $value );
+                               if ( $this->expandSize > $this->sizeLimit ) {
+                                       return $this->getLimitationWarning( 'unstrip-size', $this->sizeLimit );
+                               }
+
+                               $this->circularRefGuard[$marker] = true;
+                               $this->depth++;
+                               $ret = $this->unstripType( $type, $value );
+                               $this->depth--;
+                               unset( $this->circularRefGuard[$marker] );
+
+                               return $ret;
+                       } else {
+                               return $m[0];
+                       }
+               };
+
+               $text = preg_replace_callback( $this->regex, $callback, $text );
                return $text;
        }
 
        /**
-        * @param array $m
-        * @return array
+        * Get warning HTML and register a limitation warning with the parser
+        *
+        * @param string $type
+        * @param int $max
+        * @return string
         */
-       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>';
-                       }
-                       $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];
+       private function getLimitationWarning( $type, $max = '' ) {
+               if ( $this->parser ) {
+                       $this->parser->limitationWarn( $type, $max );
                }
+               return $this->getWarning( "$type-warning", $max );
+       }
+
+       /**
+        * Get warning HTML
+        *
+        * @param string $message
+        * @param int $max
+        * @return string
+        */
+       private function getWarning( $message, $max = '' ) {
+               return '<span class="error">' .
+                       wfMessage( $message )
+                               ->numParams( $max )->inContentLanguage()->text() .
+                       '</span>';
+       }
+
+       /**
+        * Get an array of parameters to pass to ParserOutput::setLimitReportData()
+        *
+        * @unstable Should only be called by Parser
+        * @return array
+        */
+       public function getLimitReport() {
+               return [
+                       [ 'limitreport-unstrip-depth',
+                               [
+                                       $this->highestDepth,
+                                       $this->depthLimit
+                               ],
+                       ],
+                       [ 'limitreport-unstrip-size',
+                               [
+                                       $this->expandSize,
+                                       $this->sizeLimit
+                               ],
+                       ]
+               ];
        }
 
        /**
         * Get a StripState object which is sufficient to unstrip the given text.
         * It will contain the minimum subset of strip items necessary.
         *
+        * @deprecated since 1.31
         * @param string $text
-        *
         * @return StripState
         */
        public function getSubState( $text ) {
-               $subState = new StripState();
+               wfDeprecated( __METHOD__, '1.31' );
+
+               $subState = new StripState;
                $pos = 0;
                while ( true ) {
                        $startPos = strpos( $text, Parser::MARKER_PREFIX, $pos );
@@ -202,11 +261,14 @@ class StripState {
         * will not be preserved. The strings in the $texts array will have their
         * strip markers rewritten, the resulting array of strings will be returned.
         *
+        * @deprecated since 1.31
         * @param StripState $otherState
         * @param array $texts
         * @return array
         */
        public function merge( $otherState, $texts ) {
+               wfDeprecated( __METHOD__, '1.31' );
+
                $mergePrefix = wfRandomString( 16 );
 
                foreach ( $otherState->data as $type => $items ) {
@@ -215,21 +277,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 830dbb4..f9b03c7 100644 (file)
@@ -1486,10 +1486,8 @@ MESSAGE;
        }
 
        /**
-        * Returns JS code which runs given JS code if the client-side framework is
-        * present.
+        * Wraps JavaScript code to run after startup and base modules.
         *
-        * @deprecated since 1.25; use makeInlineScript instead
         * @param string $script JavaScript code
         * @return string JavaScript code
         */
@@ -1499,10 +1497,10 @@ MESSAGE;
        }
 
        /**
-        * Construct an inline script tag with given JS code.
+        * Returns an HTML script tag that runs given JS code after startup and base modules.
         *
-        * The code will be wrapped in a closure, and it will be executed by ResourceLoader
-        * only if the client has adequate support for MediaWiki JavaScript code.
+        * The code will be wrapped in a closure, and it will be executed by ResourceLoader's
+        * startup module if the client has adequate support for MediaWiki JavaScript code.
         *
         * @param string $script JavaScript code
         * @return WrappedString HTML
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 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 4abdebf..d6d4c27 100644 (file)
@@ -686,7 +686,7 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
         */
        public function checkLastModified() {
                $dbr = $this->getDB();
-               $lastmod = $dbr->selectField( 'recentchanges', 'MAX(rc_timestamp)', false, __METHOD__ );
+               $lastmod = $dbr->selectField( 'recentchanges', 'MAX(rc_timestamp)', '', __METHOD__ );
 
                return $lastmod;
        }
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 53b7a2f..f7cb654 100644 (file)
@@ -617,7 +617,13 @@ class SpecialUpload extends SpecialPage {
                        $licenseText = '== ' . $msg['license-header'] . " ==\n{{" . $license . "}}\n";
                }
 
-               $pageText = $comment == '' ? '' : '== ' . $msg['filedesc'] . " ==\n" . $comment . "\n";
+               $pageText = $comment . "\n";
+               $headerText = '== ' . $msg['filedesc'] . ' ==';
+               if ( $comment !== '' && strpos( $comment, $headerText ) === false ) {
+                       // prepend header to page text unless it's already there (or there is no content)
+                       $pageText = $headerText . "\n" . $pageText;
+               }
+
                if ( $config->get( 'UseCopyrightUpload' ) ) {
                        $pageText .= '== ' . $msg['filestatus'] . " ==\n" . $copyStatus . "\n";
                        $pageText .= $licenseText;
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..cd04995 100644 (file)
@@ -185,8 +185,8 @@ 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 );
+                       $max = $this->mDb->selectField( 'user', 'max(user_id)', '', __METHOD__ );
+                       $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..e764e8b 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 );
+                       $max = $dbr->selectField( 'user', 'max(user_id)', '', __METHOD__ );
+                       $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..efdc75a 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'];
-               $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'];
+               $tables = array_merge( $rcQuery['tables'], [ 'page' ] );
+               $fields = array_merge( $rcQuery['fields'], [
+                       'length' => 'page_len', 'rev_id' => 'page_latest', 'page_namespace', 'page_title'
+               ] );
+               $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 eeade49..ab791b4 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.
@@ -3856,76 +4052,6 @@ class User implements IDBAccessObject, UserIdentity {
                }
        }
 
-       /**
-        * Set a cookie on the user's client. Wrapper for
-        * WebResponse::setCookie
-        * @deprecated since 1.27
-        * @param string $name Name of the cookie to set
-        * @param string $value Value to set
-        * @param int $exp Expiration time, as a UNIX time value;
-        *                   if 0 or not specified, use the default $wgCookieExpiration
-        * @param bool $secure
-        *  true: Force setting the secure attribute when setting the cookie
-        *  false: Force NOT setting the secure attribute when setting the cookie
-        *  null (default): Use the default ($wgCookieSecure) to set the secure attribute
-        * @param array $params Array of options sent passed to WebResponse::setcookie()
-        * @param WebRequest|null $request WebRequest object to use; $wgRequest will be used if null
-        *        is passed.
-        */
-       protected function setCookie(
-               $name, $value, $exp = 0, $secure = null, $params = [], $request = null
-       ) {
-               wfDeprecated( __METHOD__, '1.27' );
-               if ( $request === null ) {
-                       $request = $this->getRequest();
-               }
-               $params['secure'] = $secure;
-               $request->response()->setCookie( $name, $value, $exp, $params );
-       }
-
-       /**
-        * Clear a cookie on the user's client
-        * @deprecated since 1.27
-        * @param string $name Name of the cookie to clear
-        * @param bool $secure
-        *  true: Force setting the secure attribute when setting the cookie
-        *  false: Force NOT setting the secure attribute when setting the cookie
-        *  null (default): Use the default ($wgCookieSecure) to set the secure attribute
-        * @param array $params Array of options sent passed to WebResponse::setcookie()
-        */
-       protected function clearCookie( $name, $secure = null, $params = [] ) {
-               wfDeprecated( __METHOD__, '1.27' );
-               $this->setCookie( $name, '', time() - 86400, $secure, $params );
-       }
-
-       /**
-        * Set an extended login cookie on the user's client. The expiry of the cookie
-        * is controlled by the $wgExtendedLoginCookieExpiration configuration
-        * variable.
-        *
-        * @see User::setCookie
-        *
-        * @deprecated since 1.27
-        * @param string $name Name of the cookie to set
-        * @param string $value Value to set
-        * @param bool $secure
-        *  true: Force setting the secure attribute when setting the cookie
-        *  false: Force NOT setting the secure attribute when setting the cookie
-        *  null (default): Use the default ($wgCookieSecure) to set the secure attribute
-        */
-       protected function setExtendedLoginCookie( $name, $value, $secure ) {
-               global $wgExtendedLoginCookieExpiration, $wgCookieExpiration;
-
-               wfDeprecated( __METHOD__, '1.27' );
-
-               $exp = time();
-               $exp += $wgExtendedLoginCookieExpiration !== null
-                       ? $wgExtendedLoginCookieExpiration
-                       : $wgCookieExpiration;
-
-               $this->setCookie( $name, $value, $exp, $secure );
-       }
-
        /**
         * Persist this user's session (e.g. set cookies)
         *
@@ -4040,31 +4166,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 +4288,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 +4341,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 +4902,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 +5357,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 +5713,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 +5732,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;
        }
 
diff --git a/includes/widget/SizeFilterWidget.php b/includes/widget/SizeFilterWidget.php
new file mode 100644 (file)
index 0000000..c4d1dfc
--- /dev/null
@@ -0,0 +1,75 @@
+<?php
+
+namespace MediaWiki\Widget;
+
+use \OOUI\RadioSelectInputWidget;
+use \OOUI\TextInputWidget;
+use \OOUI\LabelWidget;
+
+/**
+ * Select and input widget.
+ *
+ * @copyright 2011-2018 MediaWiki Widgets Team and others; see AUTHORS.txt
+ * @license The MIT License (MIT); see LICENSE.txt
+ */
+class SizeFilterWidget extends \OOUI\Widget {
+
+       protected $radioselectinput = null;
+       protected $textinput = null;
+
+       /**
+        * RadioSelectInputWidget and a TextInputWidget to set minimum or maximum byte size
+        *
+        * @param array $config Configuration options
+        *   - array $config['textinput'] Configuration for the TextInputWidget
+        *   - array $config['radioselectinput'] Configuration for the RadioSelectWidget
+        *   - bool $congif['selectMin'] Whether to select 'min', false would select 'max'
+        */
+       public function __construct( array $config = [] ) {
+               // Configuration initialization
+               $config = array_merge( [
+                       'selectMin' => true,
+                       'textinput' => [],
+                       'radioselectinput' => []
+               ], $config );
+               $config['textinput'] = array_merge( [
+                       'type' => 'number'
+               ], $config['textinput'] );
+               $config['radioselectinput'] = array_merge( [ 'options' => [
+                       [
+                               'data' => 'min',
+                               'label' => wfMessage( 'minimum-size' )->text()
+                       ],
+                       [
+                               'data' => 'max',
+                               'label' => wfMessage( 'maximum-size' )->text()
+                       ]
+               ] ], $config['radioselectinput'] );
+
+               // Parent constructor
+               parent::__construct( $config );
+
+               // Properties
+               $this->config = $config;
+               $this->radioselectinput = new RadioSelectInputWidget( $config[ 'radioselectinput'] );
+               $this->textinput = new TextInputWidget( $config[ 'textinput' ] );
+               $this->label = new LabelWidget( [ 'label' => wfMessage( 'pagesize' )->text() ] );
+
+               // Initialization
+               $this->radioselectinput->setValue( $config[ 'selectMin' ] ? 'min' : 'max' );
+               $this
+                       ->addClasses( [ 'mw-widget-sizeFilterWidget' ] )
+                       ->appendContent( $this->radioselectinput, $this->textinput, $this->label );
+       }
+
+       protected function getJavaScriptClassName() {
+               return 'mw.widgets.SizeFilterWidget';
+       }
+
+       public function getConfig( &$config ) {
+               $config['textinput'] = $this->config['textinput'];
+               $config['radioselectinput'] = $this->config['radioselectinput'];
+               $config['selectMin'] = $this->config['selectMin'];
+               return parent::getConfig( $config );
+       }
+}
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 f384471..d5418b9 100644 (file)
@@ -58,6 +58,26 @@ class CrhConverter extends LanguageConverter {
        const L_F_UC = 'EİÖÜ'; # Crimean Tatar Latin uppercase front vowels
        const L_F = 'eiöüEİÖÜ'; # Crimean Tatar Latin front vowels
 
+       /**
+        * @param Language $langobj
+        * @param string $maincode
+        * @param array $variants
+        * @param array $variantfallbacks
+        * @param array $flags
+        */
+       function __construct( $langobj, $maincode,
+                                                               $variants = [],
+                                                               $variantfallbacks = [],
+                                                               $flags = [] ) {
+               parent::__construct( $langobj, $maincode,
+                       $variants, $variantfallbacks, $flags );
+
+               // No point delaying this since they're in code.
+               // Waiting until loadDefaultTables() means they never get loaded
+               // when the tables themselves are loaded from cache.
+               $this->loadExceptions();
+       }
+
        public $mCyrillicToLatin = [
 
                ## these are independent of location in the word, but have
@@ -106,9 +126,8 @@ class CrhConverter extends LanguageConverter {
 
                // hack, hack, hack
                'A' => 'А', 'a' => 'а', 'E' => 'Е', 'e' => 'е',
-               'Ö' => 'О', 'ö' => 'о', 'U' => 'У', 'u' => 'у',
-               'Ü' => 'У', 'ü' => 'у', 'Y' => 'Й', 'y' => 'й',
-
+               'Ö' => 'Ё', 'ö' => 'ё', 'U' => 'У', 'u' => 'у',
+               'Ü' => 'Ю', 'ü' => 'ю', 'Y' => 'Й', 'y' => 'й',
                'C' => 'Дж', 'c' => 'дж', 'Ğ' => 'Гъ', 'ğ' => 'гъ',
                'Ñ' => 'Нъ', 'ñ' => 'нъ', 'Q' => 'Къ', 'q' => 'къ',
 
@@ -129,10 +148,6 @@ class CrhConverter extends LanguageConverter {
                ];
        }
 
-       function postLoadTables() {
-               $this->loadExceptions();
-       }
-
        function loadExceptions() {
                if ( $this->mExceptionsLoaded ) {
                        return;
index 2239875..2252645 100644 (file)
@@ -261,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..2d9c892 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لم تحفظ تغييراتك إلى الآن!",
        "expansion-depth-exceeded-category": "الصفحات التي حدث فيها تجاوز عمق التوسيع",
        "expansion-depth-exceeded-warning": "الصفحة تجاوزت عمق التوسيع",
        "parser-unstrip-loop-warning": "حلقة معراة تم الكشف عنها",
-       "parser-unstrip-recursion-limit": "تعدى حد العودية Unstrip  ($1)",
+       "unstrip-depth-warning": "تعدى حد العودية Unstrip  ($1)",
        "undo-success": "يمكن استرجاع التعديل.\nمن فضلك تحقق من المقارنة بالأسفل للتأكد من أن هذا هو ما تريد أن تفعله، وبعد ذلك احفظ التغييرات بالأسفل للانتهاء من استرجاع التعديل.",
        "undo-failure": "لم يمكن استرجاع التعديل بسبب تعديلات متعارضة تمت على الصفحة.",
        "undo-norev": "فشل في الرجوع عن التعديل حيث أنه غير موجود أو تم حذفه.",
index c467a1e..f635f9a 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!",
        "expansion-depth-exceeded-category": "Bladsye waar die uitbreidingsdiepte oorskry is",
        "expansion-depth-exceeded-warning": "Die bladsy bevat te veel sjablone",
        "parser-unstrip-loop-warning": "'n \"Unstrip\"-lus is bespreur.",
-       "parser-unstrip-recursion-limit": "Die rekursielimiet ($1) vir \"unstrip\" is oorskry",
+       "unstrip-depth-warning": "Die rekursielimiet ($1) vir \"unstrip\" is oorskry",
        "converter-manual-rule-error": "'n Fout is in 'n handmatig toegevoegde taalomskalelingsreël gevind.",
        "undo-success": "Die wysiging kan ongedaan gemaak word.\nKontroleer die vergelyking hieronder om seker te maak dis wat u wil doen, en stoor dan om die terugrol te voltooi.",
        "undo-failure": "Die wysiging kan nie ongedaan gemaak word nie omdat dit met intermediêre wysigings bots.",
        "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..5109460 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>",
        "expansion-depth-exceeded-category-desc": "mangsiw micuwat kasabelih nu ilabuay a kelec.",
        "expansion-depth-exceeded-warning": "kasabelih mangsiw micuwat ilabuay a kelec",
        "parser-unstrip-loop-warning": "masedap tu Unstrip masaliyut.",
-       "parser-unstrip-recursion-limit": "Unstrip musaliyuliyud mangasiw tu kelec ($1)",
+       "unstrip-depth-warning": "Unstrip musaliyuliyud mangasiw tu kelec ($1)",
        "converter-manual-rule-error": "imahini palima-saungay kamu miliyaw likec masedap tu mungangaw",
        "undo-success": "tina mikawaway-kalumyiti kapah tu mapatiku.\nkapikinsa tu zikuz sasutili’ay sapat nu sulit, malucekay tu ukakaiyan kaidihan isu patiku, satu suped tu zikuz misumad tu pahezek mikawaway-kalumyiti patiku.",
        "undo-failure": "nay mikawaway-kalumyiti a sumad izaw sasula’cus,tina mikawaway-kalumyiti caay patiku.",
        "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 0062d99..8d6c35e 100644 (file)
@@ -73,7 +73,8 @@
                        "Mr. Ibrahem",
                        "Aboulouei1",
                        "سامي الرحيلي",
-                       "Azouz.anis"
+                       "Azouz.anis",
+                       "Elbasyouny"
                ]
        },
        "tog-underline": "سطر تحت الوصلات:",
        "changepassword-success": "تم تغيير كلمة السر !",
        "changepassword-throttled": "لديك محاولات تسجيل دخول كثيرة حديثة. من فضلك انتظر $1 قبل المحاولة ثانية.",
        "botpasswords": "كلمات مرور البوت",
-       "botpasswords-summary": "<em>كلمات سر البوت</em> يسمح بالوصول لحساب مستخدم من خلال API بدون استخدام اعتمادات تسجيل الدخول الرئيسية للحساب. صلاحيات المستخدم المتوفرة عند تسجيل الدخول باستخدام كلمة سر بوت ربما تكون مقيدة.\n\nلو أنك لا تعرف لماذا تريد فعل هذا، فأنت ينبغي على الأرجح ألا تففعله. لا أحد ينبغي أن يسألك أبدا أن تولد واحدة من هذه وإعطاؤهم إياها.",
+       "botpasswords-summary": "'''كلمات سر البوت''' تسمح بالوصول لحساب مستخدم من خلال API بدون استخدام اعتمادات تسجيل الدخول الرئيسية للحساب. صلاحيات المستخدم المتوفرة عند تسجيل الدخول باستخدام كلمة سر بوت ربما تكون مقيدة.\n\nإن لم تكن تعلم لماذا تريد فعل هذا، فينبغي عليك على الأرجح ألا تفعله. لا ينبغي أن يسألك أحد أبدًا أن تولد واحدة من هذه وتعطيها له.",
        "botpasswords-disabled": "كلمات السر الخاصة بالبوت معطلة.",
        "botpasswords-no-central-id": "لاستخدام كلمة السر الخاصة بالبوت، يجب أن تقوم بتسجيل الدخول من خلال حساب موحد.",
        "botpasswords-existing": "كلمات مرور البوت الموجودة",
        "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لم تحفظ تغييراتك إلى الآن!",
        "sectioneditnotsupported-text": "تعديل الأقسام غير مدعوم في هذه الصفحة",
        "permissionserrors": "خطأ في السماح",
        "permissionserrorstext": "لا تمتلك الصلاحية لفعل هذا، {{PLURAL:$1||للسبب التالي|للسببين التاليين|للأسباب التالية}}:",
-       "permissionserrorstext-withaction": "ليست لك الصلاحية ل$2، لل{{PLURAL:$1||سبب التالي|سببين التاليين|أسباب التالية}}:",
+       "permissionserrorstext-withaction": "ليست لك الصلاحية ل$2; لل{{PLURAL:$1||سبب التالي|سببين التاليين|أسباب التالية}}:",
        "contentmodelediterror": "أنت لا يمكنك تعديل هذه المراجعة لأن موديل محتواها هو  <code>$1</code>، والذي يختلف عن موديل المحتوى الحالي للصفحة  <code>$2</code>.",
        "recreate-moveddeleted-warn": "'''تحذير: أنت تعيد إنشاء صفحة سبق حذفها.'''\n\nيجب عليك التيقن من أن الاستمرار بتحرير هذه الصفحة ملائم.\nسجلا الحذف والنقل لهذه الصفحة معروضان هنا للتيسير:",
        "moveddeleted-notice": "هذه الصفحة تم حذفها.\nسجلا حذف ونقل الصفحة معروضان بالأسفل كمرجع.",
        "expansion-depth-exceeded-category-desc": "الصفحة تجاوزت الحد الأقصى لعمق التوسيع.",
        "expansion-depth-exceeded-warning": "الصفحة تجاوزت عمق التوسيع",
        "parser-unstrip-loop-warning": "حلقة معراة تم الكشف عنها",
-       "parser-unstrip-recursion-limit": "تعدى حد العودية Unstrip  ($1)",
+       "unstrip-depth-warning": "تعدى حد العودية Unstrip  ($1)",
+       "unstrip-depth-category": "الصفحات التي يتم تجاوز حد عمق أونستريب فيها",
+       "unstrip-size-warning": "تم تجاوز حد الحجم Unstrip ($1)",
+       "unstrip-size-category": "الصفحات التي يتم تجاوز الحد الأقصى لحجم أونستريب فيها",
        "converter-manual-rule-error": "خطأ تم اكتشافه في قاعدة تحويل اللغة اليدوية",
        "undo-success": "يمكن استرجاع التعديل.\nتحقق من المقارنة بالأسفل للتأكد من أن هذا هو ما تريد أن تفعله، ثم احفظ التغييرات بالأسفل للانتهاء من استرجاع التعديل.",
        "undo-failure": "لم يمكن استرجاع التعديل بسبب تعديلات متعارضة تمت على الصفحة.",
        "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": "البريد:",
        "emailuserfooter": "هذا البريد الإلكتروني {{GENDER:$1|تم إرساله}} بواسطة $1 إلى {{GENDER:$2|$2}} بواسطة وظيفة \"{{int:emailuser}}\" في {{SITENAME}}. لو {{GENDER:$2|أنك}} رددت على هذا البريد، فعنوان البريد الخاص {{GENDER:$2|بك}} سيتم إرساله مباشرة {{GENDER:$1|للمرسل الأصلي|للمرسلة الأصلية}}، مما يكشف عنوان البريد الإلكتروني الخاص {{GENDER:$2|بك}} {{GENDER:$1|لهم}}.",
        "usermessage-summary": "ترك رسالة نظام.",
        "usermessage-editor": "مراسل النظام",
-       "watchlist": "قائمة مراقبتي",
-       "mywatchlist": "قائمة مراقبتي",
+       "watchlist": "قائمة المراقبة",
+       "mywatchlist": "قائمة المراقبة",
        "watchlistfor2": "ل$1 $2",
        "nowatchlist": "لا توجد مدخلات في قائمة مراقبتك.",
        "watchlistanontext": "الرجاء تسجيل الدخول لعرض أو تعديل الصفحات في قائمة مراقبتك.",
        "limitreport-templateargumentsize-value": "$1/$2 {{PLURAL:$2|بايت}}",
        "limitreport-expansiondepth": "أعمق التوسيعات",
        "limitreport-expensivefunctioncount": "دوال محللة مكلفة",
+       "limitreport-unstrip-depth": "عمق الاستدعاء الذاتي لأونستريب",
+       "limitreport-unstrip-depth-value": "$1/$2",
+       "limitreport-unstrip-size": "حجم أونستريب ما بعد التوسع",
+       "limitreport-unstrip-size-value": "$1/$2 {{PLURAL:$2|بايت}}",
        "expandtemplates": "توسعة قالب",
        "expand_templates_intro": "تتعامل هذه الصفحة الخاصة مع نصوص الويكي وتقوم بفرد كل القوالب الموجودة به.\nوتقوم أيضا بفرد دوال القوالب مثل\n<code><nowiki>{{</nowiki>#language:…}}</code> and variables like <code><nowiki>{{</nowiki>CURRENTDAY}}</code>. حقيقةً، تقوم التعامل مع كل ما بين الأقواس المزدوجة.",
        "expand_templates_title": "عنوان صفحة هذا النص، لأجل معالجة {{FULLPAGENAME}} إلخ.:",
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 b7d65d2..36e3e8a 100644 (file)
        "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ولسه ما تسييفتش! ،",
index 43e7591..cb72b81 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আপোনাৰ সালসলনিসমূহ এতিয়াও সংৰক্ষণ কৰা হোৱা নাই!",
        "expansion-depth-exceeded-category": "বিস্তৃতি গভীৰতা বেছি হোৱা পৃষ্ঠা",
        "expansion-depth-exceeded-warning": "পৃষ্ঠাটোৰ বিস্তৃতি গভীৰতা বেছি হৈছে",
        "parser-unstrip-loop-warning": "আন্‌ষ্ট্ৰীপ লুপ চিনাক্ত কৰা হৈছে",
-       "parser-unstrip-recursion-limit": "আন্‌ষ্ট্ৰিপ ৰিকাৰ্ছন সীমা অতিক্ৰম হৈছে ($1)",
+       "unstrip-depth-warning": "আন্‌ষ্ট্ৰিপ ৰিকাৰ্ছন সীমা অতিক্ৰম হৈছে ($1)",
        "converter-manual-rule-error": "মেনুৱেল ভাষা পৰিৱৰ্তন নিয়মত ত্ৰুটি পোৱা গৈছে",
        "undo-success": "এই সম্পাদনা পূৰ্ববৎ কৰিব পাৰি ।\nঅনুগ্ৰহ কৰি তলৰ তুলনাটি পৰীক্ষা কৰি ঠাৱৰ কৰক যে আপুনি এনে কৰিব বিচাৰিছে, আৰু তলত সালসলনিসমূহ সাঁচি এই কাৰ্য্য সম্পন্ন কৰক ।",
        "undo-failure": "এই সম্পাদনা মধ্যৱৰ্তী সম্পাদনাসমূহৰ দ্বন্দৰ কাৰণে পূৰ্ববৎ কৰা নহ'ব ।",
        "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..2c93bc8 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!",
        "expansion-depth-exceeded-category-desc": "La páxina superó la fondura máxima d'espansión.",
        "expansion-depth-exceeded-warning": "La páxina pasó la fondura d'espansión",
        "parser-unstrip-loop-warning": "Deteutóse un bucle \"unstrip\"",
-       "parser-unstrip-recursion-limit": "Pasóse la llende de recursividá d'unstrip ($1)",
+       "unstrip-depth-warning": "Pasóse la llende de recursividá d'unstrip ($1)",
        "converter-manual-rule-error": "Detectóse un error na regla de conversión manual de llingua",
        "undo-success": "La edición se pue esfacer.\nPor favor comprueba la comparanza d'abaxo pa confirmar que ye eso lo que quies facer, y depués guarda los cambios p'acabar d'esfacer la edición.",
        "undo-failure": "Nun pudo esfacese la edición por aciu d'ediciones intermedies conflictives.",
        "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..5ce0f45 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आप कय बदलाव अभीन तक नाई सहेजा है!",
        "expansion-depth-exceeded-category-desc": "ई पन्ना विस्तार गहराई पार करत है।",
        "expansion-depth-exceeded-warning": "पन्ना में विस्तार गहराई पार कई गा है",
        "parser-unstrip-loop-warning": "Unstrip लूप मिला",
-       "parser-unstrip-recursion-limit": "Unstrip पुनरावर्तन सीमा पार कई गय ($1)",
+       "unstrip-depth-warning": "Unstrip पुनरावर्तन सीमा पार कई गय ($1)",
        "converter-manual-rule-error": "मैन्यूअल भाषा परिवर्तन नियम में त्रुटि",
        "undo-success": "ई संपादन पहिले जैसन रहा वईसय कई सका जात है ।\nअईसन करेक लिये कृपया निचे लिखल पाठ कय ध्यान से दखीकय बदलाव सहेजा जाय।",
        "undo-failure": "एकरे बीचे अउर बदलाव होएक कारण ई संपादन पहिले जैसन करब संभव नाई है।",
        "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..ee3523f 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سیزین دییشیکلرینیز هله ذخیره اولونماییب!",
        "expansion-depth-exceeded-category": "گئنیشلمه درین‌لیگی آشیلان صحیفه‌لر",
        "expansion-depth-exceeded-warning": "صحیفه گئنیشلنمه درین‌لیگی آشیلدی",
        "parser-unstrip-loop-warning": "تکرارلاما دونمه سی قبول ائدیلدی",
-       "parser-unstrip-recursion-limit": "حداکثر ارجاع دستور دا unstrip تجاوز گئچیب دیر ($1)",
+       "unstrip-depth-warning": "حداکثر ارجاع دستور دا unstrip تجاوز گئچیب دیر ($1)",
        "converter-manual-rule-error": "خطا ال مبدیلی قانون لاریندا",
        "undo-success": "بو دَییشیک‌لیک گئری آلینا بیلر. لطفاً آشاغی‌داکی موقاییسه ائتمیی نظارت ائدین، حقیقتن بو دییشیک‌لیگی ائتمک ایستدیگینیزدن امین اولون و صحیفنی یازا‌راق بیر اوولکی دییشیک‌لیگی گئرییه آلین.",
        "undo-failure": "دییشیک‌لیک‌لرین توققوشماسی نتیجه‌سینده گئرییه قایتارما ایشی اوغورسوز اولدو.",
        "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-days-title": "سوْن گۆنلر",
+       "rcfilters-hours-title": "سوْن ساعاتلار",
        "rcfilters-savedqueries-defaultlabel": "ذخیره اوْلونموش فیلترلر",
+       "rcfilters-filterlist-title": "فیلترلر",
+       "rcfilters-filtergroup-authorship": "دییشدیرن",
+       "rcfilters-filtergroup-automated": "اوْتوماتیک دییشدیرمه‌لر",
+       "rcfilters-filter-humans-label": "اینسان (غئیر روْبات)",
+       "rcfilters-filtergroup-changetype": "دَییشیکلیک نوعو",
+       "rcfilters-filter-pageedits-label": "صفحه دییشدیرمه‌لری",
+       "rcfilters-filter-newpages-label": "صفحه یاراتما",
+       "rcfilters-filtergroup-lastRevision": "سوْن نوسخه‌لر",
        "rcnotefrom": "آشاغی داکی دَییشیک لرده <strong>$3, $4</strong> (دن <strong>$1</strong> {{PLURAL:$5|چان گوستریلیب|چان گوستریلیب دیر}}).",
        "rclistfrom": "$3 $2 واختیندان باشلایاراق یئنی دییشیکلری گؤستر",
        "rcshowhideminor": "کیچیک دَییشیکلری $1",
        "whatlinkshere-hidetrans": "$1 علاوه‌لری",
        "whatlinkshere-hidelinks": "$1 باغلانتیلاری",
        "whatlinkshere-hideimages": "$1 فایل باغلانتی‌سی",
-       "whatlinkshere-filters": "سۆزگَجلر",
+       "whatlinkshere-filters": "فیلترلر",
        "whatlinkshere-submit": "گئت",
        "autoblockid": "اوتوماتیک باغلانما #$1",
        "block": "ایستیفادچینی باغلاما",
index a44a7c0..db55a9a 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Һеҙҙең үҙгәртеүҙәр әле яҙылмаған!",
        "expansion-depth-exceeded-category-desc": "Биттең асылыу тәрәнлеге мөмкин булған юғары сикте уҙған.",
        "expansion-depth-exceeded-warning": "Биттә һалыныу тәрәнлеге сиге үтеп кителгән",
        "parser-unstrip-loop-warning": "Ябылмаған pre табылды",
-       "parser-unstrip-recursion-limit": "($1) рекурсия сиге үтеп кителгән",
+       "unstrip-depth-warning": "($1) рекурсия сиге үтеп кителгән",
        "converter-manual-rule-error": "Тел әйлендереү ҡағиҙәһендә хата табылды",
        "undo-success": "Был үҙгәртеүҙе кире алып була. Үҙгәртеүҙәр нәҡ һеҙ теләгәнсә булһа, версияларҙы сағыштырып ҡарағыҙ, ғәмәлгә индерер өсөн, «Битте һаҡларға» төймәһенә баҫығыҙ.",
        "undo-failure": "Ара үҙгәртеүҙәр тура килмәү сәбәпле төҙәтеүҙе кире алып булмай.",
        "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": "Электрон почта :",
        "recentchangeslinked-feed": "Бәйле үҙгәртеүҙәр",
        "recentchangeslinked-toolbox": "Бәйле үҙгәртеүҙәр",
        "recentchangeslinked-title": "\"$1\" битенә бәйле үҙгәртеүҙәр",
-       "recentchangeslinked-summary": "Ð\91Ñ\8bл ÐºÒ¯Ñ\80Ò»Ó\99Ñ\82елгÓ\99н Ð±Ð¸Ñ\82 - Ò»Ñ\8bлÑ\82анма Ñ\8fһаÒ\93ан (йÓ\99ки ÐºÒ¯Ñ\80Ò»Ó\99Ñ\82елгÓ\99н ÐºÐ°Ñ\82егоÑ\80иÑ\8fÒ\93а ÐºÐµÑ\80гÓ\99н) Ò»Ñ\83Ò£Ò\93Ñ\8b Ò¯Ò\99гÓ\99Ñ\80Ñ\82еүÒ\99Ó\99Ñ\80 Ð¸Ñ\81емлеге.\n[[Special:Watchlist|Ð\9aÒ¯Ò\99Ó\99Ñ\82еү Ð¸Ñ\81емлегегеÒ\99гÓ\99]] ÐºÐµÑ\80Ó\99 Ñ\82оÑ\80Ò\93ан Ð±Ð¸Ñ\82Ñ\82әр '''ҡалын''' итеп күрһәтелгән.",
+       "recentchangeslinked-summary": "Ð\91Ñ\8bл Ð±Ð¸Ñ\82кÓ\99 Ð¹Ó\99ки Ð±Ñ\8bл Ð±Ð¸Ñ\82Ñ\82Ó\99н Ò»Ñ\8bлÑ\82анÒ\93ан Ð±Ð¸Ñ\82Ñ\82Ó\99Ñ\80Ò\99Ó\99 Ò¯Ò\99гÓ\99Ñ\80Ñ\82еүÒ\99Ó\99Ñ\80Ò\99е ÐºÒ¯Ñ\80еү Ó©Ñ\81өн Ð±Ð¸Ñ\82 Ð¸Ñ\81емен Ñ\8fÒ\99Ñ\8bÒ\93Ñ\8bÒ\99 (Ð\9aаÑ\82егоÑ\80иÑ\8fлаÑ\80 Ó©Ñ\81өн \"Ð\9aаÑ\82егоÑ\80иÑ\8f:каÑ\82еÑ\80огиÑ\8f Ð¸Ñ\81еме\"н Ñ\8fÒ\99Ñ\8bÒ\93Ñ\8bÒ\99).\n[[Special:Watchlist|Ð\9aÒ¯Ò\99Ó\99Ñ\82еү Ð¸Ñ\81емлегегеÒ\99гÓ\99]] Ð¸Ð½Ð´ÐµÑ\80елгÓ\99н Ð±Ð¸Ñ\82Ñ\82Ó\99Ñ\80Ò\99Ó\99 Ò¯Ò\99гÓ\99Ñ\80Ñ\82еүÒ\99әр '''ҡалын''' итеп күрһәтелгән.",
        "recentchangeslinked-page": "Бит исеме:",
        "recentchangeslinked-to": "Киреһенсә, был биткә һылтанма яһаған биттәрҙәге үҙгәртеүҙәрҙе күрһәтергә",
        "recentchanges-page-added-to-category": "[[:$1]] категорияға өҫтәлгән",
        "unwatchthispage": "Күҙәтеүҙе туҡтатырға",
        "notanarticle": "Мәҡәлә түгел",
        "notvisiblerev": "Башҡа ҡатнашыусы тарафынан керетелгән һуңғы өлгө юйылған",
-       "watchlist-details": "Һеҙҙең күҙәтеү исемлегегеҙҙә, фекерләшеү биттәрен һанамағанда, {{PLURAL:$1|$1 бит}} бар.",
+       "watchlist-details": "Һеҙҙең $1 күҙәтеү исемлегегеҙҙә, фекерләшеү биттәре менән бергә, {{PLURAL:$1|$1 бит}} бар.",
        "wlheader-enotif": "Электрон почта аша белдереү индерелгән.",
        "wlheader-showupdated": "Һеҙҙең аҙаҡҡы кереүегеҙҙән һуң үҙгәргән биттәр '''ҡалын''' шрифт менән күрһәтелгән.",
        "wlnote": "Түбәндә $3 $4 ваҡытына тиклем аҙаҡҡы {{PLURAL:$2|1=сәғәт|'''$2''' сәғәт}} эсендә эшләнгән {{PLURAL:$1|1=үҙгәртеү|'''$1''' үҙгәртеү}} күрһәтелгән.",
index 22f8bbb..e1037ac 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شمئی ٹگلان انگتء ذخیرگ نبیتگ انت!",
        "expansion-depth-exceeded-category-desc": "کتگوری په دیمان که آوانی تا پرش بوتینی  جولی  گیشتیر شُته گنت.",
        "expansion-depth-exceeded-warning": "پیج چه مکسیمم تچک کتنء امک(عمق) گیش شُت",
        "parser-unstrip-loop-warning": "حلقهء unstrip ودی بوت",
-       "parser-unstrip-recursion-limit": "چه مکسیمم ارجاع مان دستور unstrip گیش شُت ات($1)",
+       "unstrip-depth-warning": "چه مکسیمم ارجاع مان دستور unstrip گیش شُت ات($1)",
        "converter-manual-rule-error": "ارور مان قوانین مبدل دستی زبان",
        "undo-success": "اصلاح برگشت نه بیت. لطفا مقایسه جهلگینء کنترل کنیت په تایید شی که شی هما انت که شما لوٹیت، و بعدا تغغیرات جهلی په تمام کتن بر نگردگ اصلاح ذخیره کنیت.",
        "undo-failure": "اصلاح بر نرگردیت په خاطر تضاد میان اصلاحاتی",
        "prefs-files": "فایلان",
        "prefs-custom-css": "رسمی سی‌اس‌اس",
        "prefs-custom-js": "رسمی جی‌اس",
-       "prefs-common-css-js": "یک پیمین سی‌اس‌اس/جاوااسکریپٹ پر پهکین اسکین:",
+       "prefs-common-config": "یک پیمین سی‌اس‌اس/جاوااسکریپٹ پر پهکین اسکین:",
        "prefs-reset-intro": "شما توانت چه ای  صفحه په واترینگ تنظیمات وت په پیش‌فرض استفاده کنیت. ای کار بازگشت‌ناپذیر انت.",
        "prefs-emailconfirm-label": "تایید کتن ایمیل:",
        "youremail": "ایمیل:",
index 2208f5b..427ccdb 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!",
        "expansion-depth-exceeded-category": "Mga pahina kun saen an panrarom na kalakbangan nagsobra",
        "expansion-depth-exceeded-warning": "An pahina nagsobra sa panrarom na kalakbangan",
        "parser-unstrip-loop-warning": "Panul-ot na kaluktusan namansayan",
-       "parser-unstrip-recursion-limit": "Panul-ot na rekusyong kasagkodan nagsobra ($1)",
+       "unstrip-depth-warning": "Panul-ot na rekusyong kasagkodan nagsobra ($1)",
        "converter-manual-rule-error": "Kasalaan detektado sa manwal na konbersyon kan pinapasunod sa lengguwahe",
        "undo-success": "Pwedeng bawion an paghirá. Sosogon tabì an pagkakaiba sa babâ tangarig maberipikár kun ini an boot mong gibohon, dangan itagama an mga pagbabàgo sa babâ tangarig tapuson an pagbawì sa paghirá.",
        "undo-failure": "Dai napogol an paghirá ta igwa pang ibang paghirá sa tahaw na nagkokomplikto.",
        "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 c428513..7d6c6fa 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Вашыя зьмены яшчэ не былі захаваныя!",
        "expansion-depth-exceeded-category-desc": "Старонка перавышае максымальную глыбіню раскрыцьця.",
        "expansion-depth-exceeded-warning": "Старонка перавысіла дазволеную глыбіню разгортваньня",
        "parser-unstrip-loop-warning": "Вызначаная пятля unstrip",
-       "parser-unstrip-recursion-limit": "Перавышаны ліміт рэкурсіі unstrip ($1)",
+       "unstrip-depth-warning": "Перавышаны ліміт глыбіні функцыі unstrip ($1)",
        "converter-manual-rule-error": "Знойдзеная памылка ў ручным правіле моўнага канвэртару",
        "undo-success": "Рэдагаваньне можа быць адмененае. Калі ласка, параўнайце адрозьненьні паміж вэрсіямі, каб упэўніцца, што гэта адпаведныя зьмены, а потым запішыце зьмены для сканчэньня рэдагаваньня.",
        "undo-failure": "Рэдагаваньне ня можа быць скасаванае праз канфлікт паміж папярэднімі рэдагаваньнямі.",
        "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-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-editprotected": "рэдагаваньне старонак, абароненых у рэжыме «{{int:protect-level-sysop}}»",
-       "right-editsemiprotected": "рэдагаваньне старонак, абароненых у рэжыме «{{int:protect-level-autoconfirmed}}»",
+       "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": "рэдагаваньне мадэлі зьместу старонкі",
-       "right-editinterface": "рэдагаваньне інтэрфэйса карыстальніка",
-       "right-editusercss": "рэдагаваньне CSS файлаў іншых удзельнікаў",
-       "right-edituserjs": "рэдагаваньне JS файлаў іншых удзельнікаў",
+       "right-editinterface": "Рэдагаваньне інтэрфэйсу карыстальніка",
+       "right-editusercss": "Рэдагаваньне CSS-файлаў іншых удзельнікаў",
+       "right-edituserjs": "Рэдагаваньне JavaScript-файлаў іншых удзельнікаў",
        "right-editmyusercss": "рэдагаваць уласныя карыстальніцкія CSS-файлы",
        "right-editmyuserjs": "рэдагаваць уласныя карыстальніцкія JavaScript-файлы",
        "right-viewmywatchlist": "праглядаць уласны сьпіс назіраньня",
        "limitreport-templateargumentsize-value": "$1/$2 {{PLURAL:$2|байт}}",
        "limitreport-expansiondepth": "Найбольшая глыбіня разгортваньня",
        "limitreport-expensivefunctioncount": "Колькасьць дарагіх функцыяў парсэра",
+       "limitreport-unstrip-depth-value": "$1/$2",
        "expandtemplates": "Разгортваньне шаблёнаў",
        "expand_templates_intro": "Гэтая спэцыяльная старонка бярэ вікітэкст і разгортвае ўсе шаблёны рэкурсіўна.\nАдначасова разгортваюцца функцыі парсэру накшталт\n<code><nowiki>{{</nowiki>#language:…}}</code>, і зьменныя накшталт\n<code><nowiki>{{</nowiki>CURRENTDAY}}</code>\nФактычна, гэтая старонка разгортвае амаль усё ўнутры падвойных фігурных дужак.",
        "expand_templates_title": "Загаловак старонкі, для {{FULLPAGENAME}} і г.д.:",
index 795ab33..fa1e7a0 100644 (file)
@@ -32,7 +32,8 @@
                        "Mechanizatar",
                        "Artsiom91",
                        "Andrus",
-                       "Da voli"
+                       "Da voli",
+                       "OlegCinema"
                ]
        },
        "tog-underline": "Падкрэсліваць спасылкі:",
        "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> Праўкі яшчэ не замацаваныя!",
        "expansion-depth-exceeded-category-desc": "На старонцы перасягнута максімальная глыбіня разгортвання.",
        "expansion-depth-exceeded-warning": "Старонка перасягнула глыбіню разгортвання",
        "parser-unstrip-loop-warning": "Выяўлены закальцаваны unstrip",
-       "parser-unstrip-recursion-limit": "Перавышаны ліміт рэкурсіі unstrip ($1)",
+       "unstrip-depth-warning": "Перавышаны ліміт рэкурсіі unstrip ($1)",
        "converter-manual-rule-error": "Выяўлена памылка ў ручным правіле моўнага канвертара",
        "undo-success": "Праўку можна адкаціць, але праверце папярэдні паказ, што ніжэй, каб упэўніцца ў адпаведнасці будучых змяненняў, і толькі тады запішыце іх, каб завершыць адкат праўкі.",
        "undo-failure": "Немагчыма адкаціць праўку, таму што перашкаджаюць праўкі, што былі перад ёй.",
        "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": "Эл.пошта *",
        "rcfilters-savedqueries-remove": "Сцерці",
        "rcfilters-savedqueries-new-name-label": "Назва",
        "rcfilters-savedqueries-apply-label": "Запісаць настройкі",
+       "rcfilters-savedqueries-apply-and-setdefault-label": "Стварыць фільтр па ўмаўчанню",
        "rcfilters-savedqueries-cancel-label": "Скасаваць",
        "rcfilters-savedqueries-add-new-title": "Запісаць цяперашнія настройкі фільтра",
        "rcfilters-restore-default-filters": "Аднавіць фільтры па змоўчанні",
        "uploadstash-errclear": "Ачыстка файлаў не ўдалася",
        "uploadstash-refresh": "Абнавіць спіс файлаў",
        "uploadstash-thumbnail": "паказаць мініяцюру",
+       "uploadstash-file-not-found-no-local-path": "Не знойдзена лакальнага шляху да отмасштабированному файлу.",
+       "uploadstash-file-not-found-no-object": "Не атрымалася стварыць аб'ект лакальнага файла для мініяцюры.",
+       "uploadstash-file-not-found-missing-content-type": "Отсутствует загаловак content-type.",
+       "uploadstash-file-not-found-not-exists": "Шлях не знойдзены, альбо файл незразумелы.",
+       "uploadstash-no-extension": "Пустое расшырэнне.",
        "invalid-chunk-offset": "Недапушчальнае зрушэнне фрагмента",
        "img-auth-accessdenied": "Доступ забаронены",
        "img-auth-nopathinfo": "Адсутнічае PATH_INFO.\nВаш сервер не настроены на перадачу гэтых звестак.\nМагчыма, ён на аснове CGI і не падтрымлівае img_auth.\nГл. https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization.",
        "apisandbox-results": "Вынікі",
        "apisandbox-sending-request": "Адпраўка API-запыту…",
        "apisandbox-loading-results": "Атрымліваем API-вынікі…",
+       "apisandbox-request-format-url-label": "Радок URL-запыту",
        "apisandbox-request-url-label": "URL-адрас запыту:",
+       "apisandbox-request-json-label": "Запытаць JSON:",
        "apisandbox-request-time": "Час запыту: {{PLURAL:$1|$1 мс}}",
        "apisandbox-results-fixtoken": "Папраўце токен і паўтарыце адпраўку",
        "apisandbox-alert-page": "Палі на гэтай старонцы недапушчальныя.",
        "trackingcategories-msg": "Катэгорыя для асочвання",
        "trackingcategories-name": "Назва паведамлення",
        "trackingcategories-desc": "Крытэрый уключэння ў катэгорыю",
+       "restricted-displaytitle-ignored": "Старонкі з игнорируемыми отображаемыми назвамі",
        "restricted-displaytitle-ignored-desc": "Старонка змяшчае <code><nowiki>{{DISPLAYTITLE}}</nowiki></code>, які ігнаруецца з-за несупадзення з актуальнай назвай старонкі",
        "noindex-category-desc": "Старонка не індэксуецца пошукавымі робатамі, таму што на ёй ёсць магічнае слова <code><nowiki>__NOINDEX__</nowiki></code> і яна знаходзіцца ў прасторы назваў, дзе гэты сцяг дазволены.",
        "index-category-desc": "На старонцы ёсць магічнае слова <code><nowiki>__INDEX__</nowiki></code> (і старонка знаходзіцца ў прасторы назваў, дзе гэты сцяг дазволены), таму яна індэксуецца пошукавымі робатамі у выпадках, калі гэтага звычайна не адбываецца.",
        "enotif_lastdiff": "Гл. $1 каб бачыць гэтую мену.",
        "enotif_anon_editor": "ананімны ўдзельнік $1",
        "enotif_body": "Паважаны(ая) $WATCHINGUSERNAME,\n\n$PAGEINTRO $NEWPAGE\n\nКароткае апісанне змены: $PAGESUMMARY $PAGEMINOREDIT\n\nЗвярнуцца да змяніўшага:\nэл. пошта: $PAGEEDITOR_EMAIL\nвікі: $PAGEEDITOR_WIKI\n\nКалі вы не наведаеце гэтую старонку, прадставіўшыся сістэме, у выпадку далейшай актыўнасці апавяшчэнняў больш не будзе. Вы можаце таксама адключыць опцыю паведамлення для ўсіх старонак у вашым спісе назірання.\n\nСістэма апавяшчэння {{grammar:genitive|{{SITENAME}}}}\n\n--\nЗмена настроек апавяшчэння\n{{canonicalurl:{{#special:Preferences}}}}\n\nЗмена наладаў вашага спісу назірання\n{{canonicalurl:{{#special:EditWatchlist}}}}\n\nВыдаліць старонку з вашага спісу назірання\n$UNWATCHURL\n\nЗваротная сувязь і дапамога\n$HELPPAGE",
+       "enotif_minoredit": "Гэта малая праўка",
        "created": "створана",
        "changed": "зменена",
        "deletepage": "Сцерці старонку",
        "undelete-search-title": "Шукаць у сцёртых старонках",
        "undelete-search-box": "Знайсці ў сцёртых старонках",
        "undelete-search-prefix": "Паказаць старонкі, пачынаючы з:",
+       "undelete-search-full": "Паказаць назвы старонак, якія змяшчаюць:",
        "undelete-search-submit": "Шукаць",
        "undelete-no-results": "Не знойдзены адпаведныя старонкі ў архіве сціранняў.",
        "undelete-filename-mismatch": "Немагчыма аднавіць версію файла з адзнакай часу $1: не супадаюць назвы файлаў",
        "unblocked-id": "Блок $1 быў зняты",
        "unblocked-ip": "[[Special:Contributions/$1|$1]] быў разблакіраваны.",
        "blocklist": "Заблакаваныя ўдзельнікі",
+       "autoblocklist": "Аўтаблакіроўкі",
        "autoblocklist-submit": "Шукаць",
+       "autoblocklist-legend": "Спіс аўтаблакіровак",
+       "autoblocklist-empty": "Спіс аўтаблакіровак пусты.",
        "ipblocklist": "Заблакаваныя ўдзельнікі",
        "ipblocklist-legend": "Знайсці заблакаванага ўдзельніка",
        "blocklist-userblocks": "Схаваць блакіроўкі ўліковых запісаў",
        "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-namespace": "Імпартаваць у прастору назваў:",
        "import-mapping-subpage": "Імпартаваць як падстаронкі наступнай старонкі:",
        "import-upload-filename": "Назва файла:",
+       "import-upload-username-prefix": "Прэфіксы интервики:",
+       "import-assign-known-users": "Связать праўкі з лакальнымі ўдзельнікамі, калі ўдзельнікі з такімі імямі существуют.",
        "import-comment": "Каментарый:",
        "importtext": "Калі ласка, экспартуйце файл з крынічнай вікі з дапамогай [[Special:Export|прылады экспарту]].\nЗахавайце яго на свой камп'ютар, а потым загрузіце сюды.",
        "importstart": "Імпартаванне старонак...",
        "imported-log-entries": "{{PLURAL:$1|Імпартаваны $1 запіс журнала|Імпартавана $1 запісы журнала|Імпартавана $1 запісаў журнала}}.",
        "importfailed": "Не ўдалося імпартаваць: $1",
        "importunknownsource": "Невядомы тып імпартаванай крыніцы",
+       "importnoprefix": "Не пазначаны прэфікс інтэрвікі",
        "importcantopen": "Немагчыма адкрыць файл імпарту",
        "importbadinterwiki": "Кепская спасылка interwiki",
        "importsuccess": "Імпартаванне скончана!",
        "tooltip-pt-mycontris": "Пералік {{GENDER:|вашага}} ўкладу",
        "tooltip-pt-anoncontribs": "Спіс правак, зробленых з гэтага IP-адраса",
        "tooltip-pt-login": "Вам прапануецца ўвайсці ў сістэму, але гэта неабавязкова.",
+       "tooltip-pt-login-private": "Вы павінны ўвайсці ў, каб выкарыстоўваць гэтую вікі",
        "tooltip-pt-logout": "Выйсці з сістэмы",
        "tooltip-pt-createaccount": "Вам прапануецца стварыць уліковы запіс і ўвайсці ў сістэму, але гэта не абавязкова",
        "tooltip-ca-talk": "Размовы пра змест гэтай старонкі",
        "newimages-summary": "Тут паказаныя нядаўна ўкладзеныя файлы.",
        "newimages-legend": "Фільтр",
        "newimages-label": "Назва файла (або яе частка):",
+       "newimages-user": "Адрас IP або імя ўдзельніка",
+       "newimages-newbies": "Паказаць толькі ўклады з новых рахункаў",
        "newimages-showbots": "Паказваць укладанні ботамі",
        "newimages-hidepatrolled": "Без паказу ўхваленых ўкладанняў",
+       "newimages-mediatype": "Тып медиафайла:",
        "noimages": "Тут нічога няма.",
+       "gallery-slideshow-toggle": "Переключить мініяцюры",
        "ilsubmit": "Знайсці",
        "bydate": "п. датаў",
        "sp-newimages-showfrom": "Паказаць новыя файлы, пачынаючы з $2, $1",
        "confirmrecreate-noreason": "{{GENDER:$1|Удзельнік|Удзельніца}} [[User:$1|$1]] ([[User talk:$1|размовы]]) {{GENDER:$1|сцёр|сцёрла}} гэту старонку пасля таго, як вы пачалі яе рэдагаваць.\nКалі ласка, пацвердзіце, што вы сапраўды жадаеце аднавіць гэтую старонку.",
        "recreate": "Аднавіць",
        "unit-pixel": "крпк",
+       "confirm-purge-title": "Ачысціць кэш гэтай старонкі",
        "confirm_purge_button": "ОК",
        "confirm-purge-top": "Ці ачысціць кэш для гэтай старонкі?",
        "confirm-purge-bottom": "Асвяжэнне старонкі ачышчае копію ў кэшы і вымушае паказ самай новай версіі.",
        "autosumm-blank": "Выдаленне ўсяго зместу старонкі",
        "autosumm-replace": "Замена старонкі на '$1'",
        "autoredircomment": "Перасылае да [[$1]]",
+       "autosumm-removed-redirect": "Выдаленае перенаправление на [[$1]]",
        "autosumm-new": "Новая старонка: '$1'",
        "autosumm-newblank": "Створана пустая старонка",
        "lag-warn-normal": "Змены, зробленыя менш за $1 {{PLURAL:$1|сек.|сек.}} таму назад, могуць не трапіць у гэты спіс.",
        "watchlistedit-clear-titles": "Складнікі:",
        "watchlistedit-clear-submit": "Ачысціць спіс назірання (адкат будзе немагчымы!)",
        "watchlistedit-clear-done": "Ваш спіс назірання ачышчаны.",
+       "watchlistedit-clear-jobqueue": "Лік назіраных чысціцца. Гэта можа заняць некаторы час.",
        "watchlistedit-clear-removed": "{{PLURAL:$1|1 складнік выняты|$1 складнікі выняты|$1 складнікаў вынята}}:",
        "watchlistedit-too-many": "Занадта шмат старонак, каб паказаць тут.",
        "watchlisttools-clear": "Ачысціць спіс назірання",
        "tag-filter-submit": "Фільтр",
        "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|Тэг|Тэгі}}]]: $2)",
        "tag-mw-contentmodelchange": "змена мадэлі змесціва",
+       "tag-mw-new-redirect": "Новае перенаправление",
+       "tag-mw-removed-redirect": "Выдаленае перанакіраванне",
+       "tag-mw-changed-redirect-target": "Змяненне мэты перенаправления",
+       "tag-mw-blank": "ачыстка",
+       "tag-mw-blank-description": "Праўкі, ачышчальныя гэтую старонку",
+       "tag-mw-replace": "Заменена",
+       "tag-mw-rollback": "адкат",
+       "tag-mw-undo": "адмена",
        "tags-title": "Біркі",
        "tags-intro": "Тут пералічаныя біркі, якімі праграмы могуць пазначыць праўку, а таксама іх значэнні.",
        "tags-tag": "Назва біркі",
        "compare-title-not-exists": "Не існуе названай вамі назвы.",
        "compare-revision-not-exists": "Паказанай вамі версіі не існуе.",
        "diff-form": "'''фармуляр'''",
+       "diff-form-oldid": "Стары ідэнтыфікатар версіі (неабавязкова)",
+       "diff-form-submit": "Паказаць адрозненні",
+       "permanentlink": "Пастаянная спасылка",
+       "permanentlink-revid": "ідэнтыфікатар праўкі",
+       "permanentlink-submit": "Перайсці да версіі",
        "dberr-problems": "Прабачце, на пляцоўцы здарыліся тэхнічныя цяжкасці.",
        "dberr-again": "Паспрабуйце перачытаць праз некалькі хвілін.",
        "dberr-info": "(Немагчыма звязацца з базай даных: $1)",
        "htmlform-cloner-create": "Дадаць яшчэ",
        "htmlform-cloner-delete": "Сцерці",
        "htmlform-cloner-required": "Неабходна хаця б адно значэнне.",
+       "htmlform-date-placeholder": "ГГГГ-ММ-ДД",
+       "htmlform-time-placeholder": "ЧЧ:ММ:СС",
+       "htmlform-datetime-placeholder": "ГГГГ-ММ-ДД ЧЧ:ММ:СС",
+       "htmlform-date-invalid": "Указанае вамі значэнне не похоже на дату. Паспрабуйце выкарыстоўваць фармат ГГГГ-ММ-ДД.",
+       "htmlform-datetime-invalid": "Вамі выбрана значэнне не падобна на дату і час. Паспрабуйце выкарыстоўваць фармат ГГГГ-ММ-ДД ГГ-ММ-СС.",
        "htmlform-title-badnamespace": "[[:$1]] не ў прасторы назваў \"{{ns:$2}}\".",
        "htmlform-title-not-creatable": "\"$1\" - немагчымы загаловак для старонкі",
        "htmlform-title-not-exists": "$1 не існуе.",
        "feedback-bugornote": "Калі вы гатовыя падрабязна апісаць тэхнічную праблему, калі ласка, [$1 паведаміце пра памылку].\nУ адваротным выпадку вы можаце выкарыстоўваць гэтую простую форму. Ваш каментар будзе дададзены на старонку «[$3 $2]» разам з вашым імем удзельніка і выкарыстоўваемым браўзерам.",
        "feedback-cancel": "Адмена",
        "feedback-close": "Зроблена.",
+       "feedback-external-bug-report-button": "адправіць тэхнічнае заданне",
        "feedback-dialog-title": "Даслаць водгук",
        "feedback-error1": "Памылка. Невядомы вынік з API",
        "feedback-error2": "Памылка. Збой праўкі",
        "feedback-error3": "Памылка. Няма адказу ад API",
+       "feedback-error4": "Памылка: Немагчыма размясціць запіс з паказаным загалоўкам зваротнай сувязі",
        "feedback-message": "Паведамленне",
        "feedback-subject": "Тэма:",
        "feedback-submit": "Даслаць",
        "feedback-thanks": "Дзякуй! Ваш водгук размешчаны на старонцы «[$2 $1]».",
        "feedback-thanks-title": "Дзякуем!",
+       "feedback-useragent": "Браўзэр:",
        "searchsuggest-search": "Шукаць у {{GRAMMAR:месны|{{SITENAME}}}}",
        "searchsuggest-containing": "змяшчае...",
        "api-error-badtoken": "Унутраная памылка: няслушны ключ.",
        "pagelang-language": "Мова",
        "pagelang-use-default": "Прадвызначаная мова",
        "pagelang-select-lang": "Выберыце мову",
+       "pagelang-reason": "Прычына",
        "pagelang-submit": "Адправіць",
        "right-pagelang": "Змяняць мову старонкі",
        "action-pagelang": "змяняць мову старонкі",
        "default-skin-not-found-no-skins": "Упс! Прадвызначаная вокладка для вашай вікі (<code>$wgDefaultSkin</code>), <code>$1</code>, недаступна.\n\nВы не ўстанавілі вокладкі.\n\n; Калі вы толькі што ўстанавілі ці абнавілі MediaWiki:\n: Магчыма, вы ўстанавілі з git, ці наўпрост з зыходнага коду, выкарыстаўшы іншы метад. Гэта нармальна. MediaWiki 1.24 і навейшыя не ўключаюць вокладкі ў асноўнае сховішча. Паспрабуйце ўстанавіць некалькі вокладак з [https://www.mediawiki.org/wiki/Category:All_skins каталога вокладак mediawiki.org], такім чынам:\n:* Узяўшы [https://www.mediawiki.org/wiki/Download tarball-інсталятар], які ўтрымлівае некалькі вокладак і прыставак. Вы можаце скапіяваць і ўставіць каталог <code>skins/</code> адтуль.\n:* Зрабіўшы клон аднаго з сховішчаў <code>mediawiki/skins/*</code> праз git у каталог <code>skins/</code> вашай інсталяцыі MediaWiki.\n: Калі вы распрацоўшчык MediaWiki, гэта не павінна адбіцца на вашым git-сховішчы. Гл. [https://www.mediawiki.org/wiki/Manual:Skin_configuration Інструкцыя: Настройка вокладак] дзеля інфармацыі па ўключэнні вокладак і выбары прадвызначэння.",
        "default-skin-not-found-row-enabled": "* <code>$1</code> / $2 (уключана)",
        "default-skin-not-found-row-disabled": "* <code>$1</code> / $2 (<strong>выключана</strong>)",
+       "mediastatistics": "статыстыка медыя",
+       "mediastatistics-summary": "Статыстычная звесткі пра тыпы запампаваных файлаў. Статыстыка ўключае звесткі толькі пра апошнія версіі файлаўю Старыя або выдаленыя версіі файлаў не ўлічваюцца.",
+       "mediastatistics-bytespertype": "Агульны памер файлаў у гэтай секцыі: $1 {{PLURAL:$1|байт|байта|байт}} ($2; $3%).",
+       "mediastatistics-table-mimetype": "MIME-тып",
        "mediastatistics-table-extensions": "Магчымыя прыстаўкі",
        "mediastatistics-table-count": "Колькасць файлаў",
        "mediastatistics-table-totalbytes": "Агульны памер",
        "mediastatistics-header-unknown": "Невядомыя",
        "mediastatistics-header-bitmap": "Растравыя выявы",
-       "mediastatistics-header-drawing": "Рысункі (вектарныя выявы)",
+       "mediastatistics-header-drawing": "Малюнкі (вектарныя выявы)",
+       "mediastatistics-header-audio": "Аўдыё",
+       "mediastatistics-header-video": "Відэа",
+       "mediastatistics-header-multimedia": "Мультымедыя",
+       "mediastatistics-header-office": "Офісныя",
        "mediastatistics-header-text": "Тэкст",
+       "mediastatistics-header-executable": "Выкананыя",
        "mediastatistics-header-archive": "Сціснутыя фарматы",
        "mediastatistics-header-total": "Усе файлы",
        "json-error-state-mismatch": "Недапушчальны або некарэктны JSON",
index 2b07773..7b3628f 100644 (file)
        "index-category": "Индексирани страници",
        "noindex-category": "Неиндексирани страници",
        "broken-file-category": "Страници с неработещи препратки към файлове",
+       "categoryviewer-pagedlinks": "($1) ($2)",
+       "category-header-numerals": "$1–$2",
        "about": "За {{SITENAME}}",
        "article": "Страница",
        "newwindow": "(отваря се в нов прозорец)",
        "versionrequiredtext": "Използването на тази страница изисква версия $1 на софтуера МедияУики. Вижте [[Special:Version|текущата версия]].",
        "ok": "Добре",
        "pagetitle": "$1 – {{SITENAME}}",
+       "pagetitle-view-mainpage": "{{SITENAME}}",
+       "backlinksubtitle": "← $1",
        "retrievedfrom": "Взето от „$1“.",
        "youhavenewmessages": "{{PLURAL:$3|Имате}} $1 ($2).",
        "youhavenewmessagesfromusers": "Имате $1 от {{PLURAL:$3|друг потребител|$3 потребители}} ($2).",
        "nocookiesnew": "Потребителската сметка беше създадена, но все още не сте влезли.\n{{SITENAME}} използва бисквитки при влизането на потребителите.\nРазрешете бисквитките в браузъра си, тъй като те са забранени, а след това влезте с потребителското си име и парола.",
        "nocookieslogin": "{{SITENAME}} използва бисквитки (cookies) за запис на влизанията.\nРазрешете бисквитките в браузъра си, тъй като те са забранени, и опитайте отново.",
        "nocookiesfornew": "Потребителската сметка не беше създадена, тъй като не беше възможно да се потвърди източникът ѝ.\nУверете се, че бисквитките са позволени от браузъра, презаредете страницата и опитайте отново.",
+       "nocookiesforlogin": "{{int:nocookieslogin}}",
        "noname": "Не указахте валидно потребителско име.",
        "loginsuccesstitle": "Успешно влизане",
        "loginsuccess": "<strong>Влязохте в {{SITENAME}} като „$1“.</strong>",
        "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“.\nНеобходимо е да се знае, че имената на потребителските ви страници за CSS и JavaScript трябва да се състоят от малки букви, например: „{{ns:user}}:Иван/vector.css“ (а не „{{ns:user}}:Иван/Vector.css“).",
        "updated": "(обновена)",
        "note": "<strong>Забележка:</strong>",
        "previewnote": "<strong>Обърнете внимание, че това е само предварителен преглед.</strong>\nПромените все още не са съхранени!",
        "template-semiprotected": "(полузащитен)",
        "hiddencategories": "Тази страница е включена в {{PLURAL:$1|Една скрита категория|$1 скрити категории}}:",
        "edittools": "<!-- Евентуален текст тук ще бъде показван под формулярите за редактиране и качване. -->",
+       "edittools-upload": "-",
        "nocreatetext": "Създаването на нови страници в {{SITENAME}} е ограничено. Можете да се върнете назад и да редактирате някоя от съществуващите страници, [[Special:UserLogin|да се регистрирате или да създадете нова потребителска сметка]].",
        "nocreate-loggedin": "Нямате необходимите права да създавате нови страници.",
        "sectioneditnotsupported-title": "Не се поддържа редактиране на раздели",
        "postedit-confirmation-created": "Страницата е създадена.",
        "postedit-confirmation-restored": "Страницата е възстановена.",
        "postedit-confirmation-saved": "Редакцията Ви беше съхранена.",
+       "postedit-confirmation-published": "Вашата редакция е публикувана.",
        "edit-already-exists": "Не можа да се създаде нова страница.\nТакава вече съществува.",
        "defaultmessagetext": "Текст на съобщението по подразбиране",
        "content-failed-to-parse": "Неуспех при анализиране на съдържанието от тип $2 за модела $1: $3",
        "content-model-text": "обикновен текст",
        "content-model-javascript": "JavaScript",
        "content-model-css": "CSS",
+       "content-model-json": "JSON",
        "content-json-empty-object": "Празен обект",
        "content-json-empty-array": "Празен масив",
        "deprecated-self-close-category": "Страници, използващи невалидни самозатварящи се HTML тагове",
        "expansion-depth-exceeded-category-desc": "Страницата превишава максимално допустимата дълбочина на разгръщане.",
        "expansion-depth-exceeded-warning": "Страницата е превишила разрешената дълбочина на разгръщане",
        "parser-unstrip-loop-warning": "Открито е ''unstrip'' зацикляне",
-       "parser-unstrip-recursion-limit": "''Unstrip'' лимита на рекурсия превишава ($1)",
+       "unstrip-depth-warning": "''Unstrip'' лимита на рекурсия превишава ($1)",
        "undo-success": "Редакцията може да бъде върната.\nПрегледайте долното сравнение и се уверете, че наистина искате да го направите. След това съхранете страницата, за да извършите връщането.",
        "undo-failure": "Редакцията не може да бъде върната поради конфликтни междинни редакции.",
        "undo-norev": "Редакцията не може да бъде върната, тъй като не съществува или е била изтрита.",
        "mergehistory-comment": "Слята [[:$1]] в [[:$2]]: $3",
        "mergehistory-same-destination": "Изходната и целевата страница не могат да съвпадат",
        "mergehistory-reason": "Причина:",
+       "mergehistory-revisionrow": "$1 ($2) $3 . . $4 $5 $6",
        "mergelog": "Дневник на сливанията",
        "revertmerge": "Разделяне",
        "mergelogpagetext": "Страницата съдържа списък с последните сливания на редакционни истории.",
        "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": "Е-поща:",
        "username": "{{GENDER:$1|Потребителско име}}:",
        "prefs-memberingroups": "{{GENDER:$2|Член}} на {{PLURAL:$1|група|групи}}:",
+       "prefs-memberingroups-type": "$1",
        "group-membership-link-with-expiry": "$1 (до $2)",
        "prefs-registration": "Регистрация:",
+       "prefs-registration-date-time": "$1",
        "yourrealname": "Истинско име:",
        "yourlanguage": "Език:",
        "yourvariant": "Езиков вариант на съдържанието:",
        "saveusergroups": "Съхраняване на {{GENDER:$1|потребителските}} групи",
        "userrights-groupsmember": "Член на:",
        "userrights-groupsmember-auto": "Член по подразбиране на:",
+       "userrights-groupsmember-type": "$1",
        "userrights-groups-help": "Може да променяте групите, в които е потребителят:\n* Поставена отметка означава, че потребителят е член на групата.\n* Поле без отметка означава, че потребителят не е член на групата.\n* Знакът * показва, че не можете да премахнете групата, след като е вече добавена (или обратно).\n* Знакът # показва, че можете да удължите само срокът на членството; не може да го върнете на по-ранна дата.",
        "userrights-reason": "Причина:",
        "userrights-no-interwiki": "Нямате права да редактирате потребителските групи на други уикита.",
        "userrights-nodatabase": "Базата данни $1 не съществува или не е на локалния сървър.",
        "userrights-changeable-col": "Групи, които можете да променяте",
        "userrights-unchangeable-col": "Групи, които не можете да променяте",
+       "userrights-irreversible-marker": "$1*",
+       "userrights-no-shorten-expiry-marker": "$1#",
        "userrights-expiry-current": "Изтича на $1",
        "userrights-expiry-none": "Не изтича",
        "userrights-expiry": "Изтича на:",
        "action-deletechangetags": "изтриване на етикети от базата от данни",
        "action-purge": "почисти кеша на тази страница",
        "nchanges": "$1 {{PLURAL:$1|промяна|промени}}",
+       "ntimes": "$1×",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|от последното посещение}}",
        "enhancedrc-history": "история",
        "recentchanges": "Последни промени",
        "recentchanges-label-plusminus": "Размерът на страницата е променен с този брой байтове",
        "recentchanges-legend-heading": "<strong>Легенда:</strong>",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (вижте също [[Special:NewPages|списъка с нови страници]])",
+       "recentchanges-legend-plusminus": "(<em>±123</em>)",
        "recentchanges-submit": "Показване",
        "rcfilters-tag-remove": "Премахване на '$1'",
        "rcfilters-legend-heading": "<strong>Списък на съкращенията:</strong>",
        "minoreditletter": "м",
        "newpageletter": "Н",
        "boteditletter": "б",
+       "unpatrolledletter": "!",
        "number_of_watching_users_pageview": "[$1 {{PLURAL:$1|наблюдаващ потребител|наблюдаващи потребители}}]",
+       "rc-change-size": "$1",
        "rc-change-size-new": "$1 {{PLURAL:$1|байт|байта}} след редакцията",
        "newsectionsummary": "Нова тема /* $1 */",
        "rc-enhanced-expand": "Показване на детайли",
        "mimesearch": "MIME-търсене",
        "mimesearch-summary": "На тази страница можете да филтрирате файловете по техния MIME-тип.\nВход: медиен тип/подтип или медиен тип/*, напр. <code>image/jpeg</code>.",
        "mimetype": "MIME-тип:",
-       "download": "сваляне",
+       "download": "изтегляне",
        "unwatchedpages": "Ненаблюдавани страници",
        "listredirects": "Списък на пренасочванията",
        "listduplicatedfiles": "Списък на повтарящи се файлове",
        "apisandbox-sending-request": "Изпращане на API заявка...",
        "apisandbox-loading-results": "Получаване на API резултати...",
        "apisandbox-request-url-label": "URL-адрес на заявката:",
+       "apisandbox-request-format-json-label": "JSON",
        "apisandbox-request-json-label": "JSON заявка:",
        "apisandbox-request-time": "Време на заявката: {{PLURAL:$1|$1 ms}}",
        "apisandbox-alert-page": "Полета на тази страница не са валидни.",
        "apisandbox-multivalue-all-values": "$1 (Всички стойности)",
        "booksources": "Източници на книги",
        "booksources-search-legend": "Търсене на информация за книга",
+       "booksources-isbn": "ISBN:",
        "booksources-search": "Търсене",
        "booksources-text": "По-долу е списъкът от връзки към други сайтове, продаващи нови и използвани книги или имащи повече информация за книгите, които търсите:",
        "booksources-invalid-isbn": "Предоставеният ISBN изглежда е невалиден; проверете за грешки и копирайте от оригиналния източник.",
        "listgrants": "Разрешения",
        "listgrants-grant": "Разрешение",
        "listgrants-rights": "Права",
+       "listgrants-grant-display": "$1 <code>($2)</code>",
        "trackingcategories": "Категории за проследяване",
        "trackingcategories-summary": "Тази страница съдържа списък на категории за проследяване, които се попълват автоматично от софтуера на МедияУики. Имената им могат да се променят чрез съответните системни съобщения в именното пространство {{ns:8}}.",
        "trackingcategories-msg": "Категория за проследяване",
        "confirmdeletetext": "На път сте да изтриете страница заедно с цялата ѝ редакционна история.\nПотвърдете, че искате това, разбирате последствията и правите това в съответствие с [[{{MediaWiki:Policy-url}}|политиката]].",
        "actioncomplete": "Действието беше изпълнено",
        "actionfailed": "Действието не сполучи",
-       "deletedtext": "Страницата „$1“ беше изтрита. Вижте $2 за запис на последните изтривания.",
+       "deletedtext": "Страница „$1“ беше изтрита.\nВижте $2 за да видите списък на последните изтривания.",
        "dellogpage": "Дневник на изтриванията",
        "dellogpagetext": "Списък на последните изтривания.",
        "deletionlog": "дневник на изтриванията",
        "deletecomment": "Причина:",
        "deleteotherreason": "Друга/допълнителна причина:",
        "deletereasonotherlist": "Друга причина",
-       "deletereason-dropdown": "*Стандартни причини за изтриване\n** Спам\n** Вандализъм\n** Нарушение на авторски права\n** По молба на автора\n** Грешно пренасочване",
+       "deletereason-dropdown": "* Стандартни причини за изтриване\n** Спам\n** Вандализъм\n** Нарушение на авторски права\n** По молба на автора\n** Грешно пренасочване",
        "delete-edit-reasonlist": "Редактиране на причините за изтриване",
        "delete-toobig": "Тази страница има голяма редакционна история с над $1 {{PLURAL:$1|версия|версии}}. Изтриването на такива страници е ограничено, за да се предотвратят евентуални поражения на {{SITENAME}}.",
        "delete-warning-toobig": "Тази страница има голяма редакционна история с над $1 {{PLURAL:$1|версия|версии}}. Възможно е изтриването да наруши някои операции в базата данни на {{SITENAME}}; необходимо е особено внимание при продължаване на действието.",
        "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": "Заглавие на страницата",
        "protect-fallback": "Позволяване само за потребители с права на „$1“",
        "protect-level-autoconfirmed": "Позволено само за автоматично одобрени потребители",
        "protect-level-sysop": "Позволено само за администратори",
+       "protect-summary-desc": "[$1=$2] ($3)",
        "protect-summary-cascade": "каскадно",
        "protect-expiring": "изтича на $1 (UTC)",
        "protect-expiring-local": "срок на изтичане $1",
        "undelete-error-long": "Възникнаха грешки при възстановяването на изтрития файл:\n\n$1",
        "undelete-show-file-confirm": "Сигурни ли сте, че искате да прегледате изтритата версия на файла „<nowiki>$1</nowiki>“ от $2 в $3?",
        "undelete-show-file-submit": "Да",
+       "undelete-revision-row2": "$1 ($2) $3 . . $4 $5 $6 $7 $8",
        "namespace": "Именно пространство:",
        "invert": "Обръщане на избора",
        "tooltip-invert": "Поставянето на отметка ще скрие всички промени в страниците от избраното именно пространство (и свързаните именни пространства)",
        "ipb_expiry_old": "Срокът на изтичане е минал.",
        "ipb_expiry_temp": "Скритите потребителски имена трябва да се блокират безсрочно.",
        "ipb_hide_invalid": "Тази потребителска сметка не може да бъде прикрита; с нея са направени повече от {{PLURAL:$1|една редакция|$1 редакции}}.",
-       "ipb_already_blocked": "„$1“ е вече блокиран",
+       "ipb_already_blocked": "„$1“ е вече блокиран.",
        "ipb-needreblock": "$1 е вече блокиран. Желаете ли да промените настройките?",
        "ipb-otherblocks-header": "{{PLURAL:$1|Друго блокиране|Други блокирания}}",
        "unblock-hideuser": "Не можете да отблокирате този потребител, тъй като потребителското му име е скрито.",
        "ip_range_exceeded": "IP диапазонът превишава максималния диапазон. Позволен диапазон: /$1.",
        "proxyblocker": "Блокировач на проксита",
        "proxyblockreason": "IP-адресът ви беше блокиран, тъй като е анонимно достъпен междинен сървър. Свържете се с доставчика ви на интернет и го информирайте за този сериозен проблем в сигурността.",
+       "sorbs": "DNSBL",
        "sorbsreason": "IP-адресът ви е записан като анонимно достъпен междинен сървър в DNSBL на {{SITENAME}}.",
        "sorbs_create_account_reason": "IP-адресът ви е записан като анонимно достъпен междинен сървър в DNSBL на {{SITENAME}}. Не може да създадете сметка.",
        "cant-see-hidden-user": "Потребителят, който опитвате да блокирате, вече е блокиран и скрит. Тъй като нямате права да скривате потребители, не можете да видите или редактирате блокирането на потребителя.",
        "tooltip-undo": "Препратката „връщане“ премахва тази редакция и отваря страницата за редактиране в режим на предварителен преглед.\nВ полето за резюме може да се впише причина за връщането.",
        "tooltip-preferences-save": "Съхраняване на предпочитанията",
        "tooltip-summary": "Въведете кратко резюме",
+       "interlanguage-link-title": "$1 – $2",
+       "interlanguage-link-title-nonlang": "$1 – $2",
        "common.css": "/* Чрез редактиране на този файл ще промените всички облици */",
        "common.js": "/* Този файл съдържа код на Джаваскрипт и се зарежда при всички потребители. */",
        "anonymous": "{{PLURAL:$1|Анонимен потребител|Анонимни потребители}}на {{SITENAME}}",
        "pageinfo-visiting-watchers": "Брой наблюдаващи страницата, които са посетили последните промени",
        "pageinfo-few-watchers": "Под $1 {{PLURAL:$1|наблюдаващ|наблюдаващи}}",
        "pageinfo-redirects-name": "Брой пренасочвания към тази страница",
+       "pageinfo-redirects-value": "$1",
        "pageinfo-subpages-name": "Подстраници на тази страница",
        "pageinfo-subpages-value": "$1 ($2 {{PLURAL:$2|пренасочваща|пренасочващи}}; $3 {{PLURAL:$3|непренасочваща|непренасочващи}})",
        "pageinfo-firstuser": "Създател на страницата",
        "mediawarning": "<strong>Внимание:</strong> Възможно е файлът да съдържа злонамерен програмен код. Неговото изпълнение може да доведе до повреди в системата Ви.",
        "imagemaxsize": "Ограничение на размера на картинките:<br /><em>(само за описателните страници)</em>",
        "thumbsize": "Размер на миникартинките:",
+       "widthheight": "$1 × $2",
        "widthheightpage": "$1 × $2, $3 {{PLURAL:$3|страница|страници}}",
        "file-info": "големина на файла: $1, MIME-тип: $2",
        "file-info-size": "$1 × $2 пиксела, големина на файла: $3, MIME-тип: $4",
        "ilsubmit": "Търсене",
        "bydate": "по дата",
        "sp-newimages-showfrom": "Показване на новите файлове, като се започне от $2, $1",
+       "video-dims": "$1, $2 × $3",
        "seconds": "{{PLURAL:$1|$1 секунда|$1 секунди}}",
        "minutes": "{{PLURAL:$1|$1 минута|$1 минути}}",
        "hours": "{{PLURAL:$1|$1 час|$1 часа}}",
        "metadata-expand": "Показване на допълнителните данни",
        "metadata-collapse": "Скриване на допълнителните данни",
        "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",
+       "metadata-langitem": "<strong>$2:</strong> $1",
+       "metadata-langitem-default": "$1",
        "exif-imagewidth": "Ширина",
        "exif-imagelength": "Височина",
        "exif-bitspersample": "Дълбочина на цвета (битове)",
        "exif-exposuretime": "Време на експонация",
        "exif-exposuretime-format": "$1 сек ($2)",
        "exif-fnumber": "F (бленда)",
+       "exif-fnumber-format": "f/$1",
        "exif-exposureprogram": "Програма на експонацията",
        "exif-spectralsensitivity": "Спектрална чувствителност",
        "exif-isospeedratings": "Светлочувствителност ISO",
        "exif-originalimageheight": "Височина на изображението преди намаляването",
        "exif-originalimagewidth": "Ширина на изображението преди намаляването",
        "exif-compression-1": "Некомпресиран",
+       "exif-compression-5": "LZW",
+       "exif-compression-6": "JPEG (стар)",
+       "exif-compression-7": "JPEG",
        "exif-copyrighted-true": "Заштитено с авторски права",
        "exif-copyrighted-false": "Статутът на авторските права не е указан",
+       "exif-photometricinterpretation-2": "RGB",
        "exif-unknowndate": "Неизвестна дата",
        "exif-orientation-1": "Нормално",
        "exif-orientation-2": "Отражение по хоризонталата",
        "confirmrecreate": "Потребител [[User:$1|$1]] ([[User talk:$1|беседа]]) е {{GENDER:$1|изтрил}} страницата, след като сте започнали да я редактирате, като е посочил следното обяснение:\n: <em>$2</em>\nПотвърдете, че наистина желаете да създадете страницата отново.",
        "confirmrecreate-noreason": "Потребител [[User:$1|$1]] ([[User talk:$1|беседа]]) {{GENDER:$1|изтри}} тази страница след като сте започнали да я редактирате. Необходимо е потвърждение, че наистина желаете да създадете страницата отново.",
        "recreate": "Повторно създаване",
+       "unit-pixel": "п",
        "confirm-purge-title": "Изчистване на страницата",
        "confirm_purge_button": "Добре",
        "confirm-purge-top": "Изчистване на складираното копие на страницата?",
        "confirm-unwatch-top": "Премахване на страницата от списъка Ви за наблюдение?",
        "confirm-rollback-button": "OK",
        "confirm-rollback-top": "Отменяне на редакции по тази страница?",
+       "semicolon-separator": ";&#32;",
+       "comma-separator": ",&#32;",
+       "colon-separator": ":&#32;",
+       "pipe-separator": "&#32;|&#32;",
+       "word-separator": "&#32;",
+       "ellipsis": "...",
+       "percent": "$1%",
+       "parentheses": "($1)",
+       "brackets": "[$1]",
        "quotation-marks": "„$1“",
        "imgmultipageprev": "← предишна страница",
        "imgmultipagenext": "следваща страница →",
        "imgmultigo": "Отваряне",
        "imgmultigoto": "Отиване на страница $1",
+       "img-lang-opt": "$2 ($1)",
        "img-lang-default": "(език по подразбиране)",
        "img-lang-go": "Отваряне",
        "ascending_abbrev": "възх",
        "watchlisttools-edit": "Преглед и редактиране на списъка за наблюдение",
        "watchlisttools-raw": "Редактиране на необработения списък за наблюдение",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|беседа]])",
+       "timezone-utc": "UTC",
        "timezone-local": "Местно",
        "duplicate-defaultsort": "<strong>Внимание:</strong> Ключът за сортиране по подразбиране „$2“ отменя по-ранния ключ „$1“.",
        "version": "Версия",
        "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": "Версия",
        "rotate-comment": "Изображението е завъртяно на $1 {{PLURAL:$1|градус|градуса}} по часовниковата стрелка",
        "limitreport-cputime-value": "$1 {{PLURAL:$1|секунда|секунди}}",
        "limitreport-walltime-value": "$1 {{PLURAL:$1|секунда|секунди}}",
+       "limitreport-ppvisitednodes-value": "$1/$2",
+       "limitreport-ppgeneratednodes-value": "$1/$2",
        "limitreport-postexpandincludesize-value": "$1/$2 {{PLURAL:$2|байт|байта}}",
        "limitreport-templateargumentsize-value": "$1/$2 {{PLURAL:$2|байт|байта}}",
+       "limitreport-expansiondepth-value": "$1/$2",
+       "limitreport-expensivefunctioncount-value": "$1/$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": "Резултат",
        "default-skin-not-found-row-disabled": "* <code>$1</code> / $2 (<strong>изключено</strong>)",
        "mediastatistics": "Статистика за файлове",
        "mediastatistics-summary": "Статистика за видовете качени файлове. Тя включва само последните версии на файловете, без старите или изтритите версии.",
+       "mediastatistics-nfiles": "$1 ($2%)",
        "mediastatistics-nbytes": "{{PLURAL:$1|$1 байт|$1 байта}} ($2; $3%)",
        "mediastatistics-bytespertype": "Пълен размер на файловете за този раздел: {{PLURAL:$1|$1 байт|$1 байта}} ($2; $3%).",
        "mediastatistics-allbytes": "Общ размер на всички файлове: {{PLURAL:$1|$1 байт|$1 байта}} ($2).",
        "authmanager-realname-label": "Истинско име",
        "authmanager-realname-help": "Истинско име на потребителя",
        "authmanager-provider-temporarypassword": "Временна парола",
+       "authprovider-confirmlink-option": "$1 ($2)",
+       "authprovider-confirmlink-failed-line": "$1: $2",
        "authprovider-resetpass-skip-label": "Пропускане",
        "authform-newtoken": "Липсва маркер. $1",
        "authform-notoken": "Липсва маркер",
        "restrictionsfield-badip": "Невалиден IP-адрес или интервал от адреси: $1",
        "edit-error-short": "Грешка: $1",
        "edit-error-long": "Грешки:\n\n$1",
-       "revid": "версия $1"
+       "revid": "версия $1",
+       "pagedata-bad-title": "Невалидно заглавие: $1."
 }
index c25ab3e..0157021 100644 (file)
        "expansion-depth-exceeded-category-desc": "تاکدیم که که آیی پراخئ جُهلئ شه اندازه گ ئا گیشتیر بوته.",
        "expansion-depth-exceeded-warning": "تاکدیم شه حداکثر بسط داتین عمق ئا تجاوز کورت",
        "parser-unstrip-loop-warning": "حلقه بی دستور unstrip ئی تا ودی نه بوت",
-       "parser-unstrip-recursion-limit": "شه حداکثر ارجاع بئ دستور unstrip ئی تا تجاوز بوت ($1)",
+       "unstrip-depth-warning": "شه حداکثر ارجاع بئ دستور unstrip ئی تا تجاوز بوت ($1)",
        "converter-manual-rule-error": "خطا بئ زانئ دستی ئین بدل کورتینی قوانین ئی تا",
        "undo-success": "ای ایڈیٹ ئا توانیت شه بین بَریت.\nمهربانی بکنیت جهلگی فرق ئا بگنیدیت و قبول بکنیت که آ چیزی است که شما ئه لوٹیت انجام دهیت،پدا جهلگئ تغیراتانا ذخیره بکنیت تا که ایڈیٹ ئی شه بین بورتینئ کار الاس بکنیت.",
        "undo-failure": "ایڈیٹ ئا نتوانتیت شه بین بَریت، بی خاتیرئ بعضی تعرض ئان یا میانئ ئین ایڈیٹان.",
        "prefs-files": "فایل ئان",
        "prefs-custom-css": "سی‌اس‌اس شخصی",
        "prefs-custom-js": "شخصی ئین جاوااسکریپت",
-       "prefs-common-css-js": "سی‌اس‌اس/جاوااسکریپت مشترک په موچین پوسته‌ئان:",
+       "prefs-common-config": "سی‌اس‌اس/جاوااسکریپت مشترک په موچین پوسته‌ئان:",
        "prefs-emailconfirm-label": "ایمیلئ تائید کورتین:",
        "youremail": "ایمیل:",
        "username": "{{GENDER:$1|کار زوروکئ نام}}:",
index bda0e06..93f1abc 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राउर बदलाव अभी तक सुरक्षित नईखे करल गईल!",
        "expansion-depth-exceeded-category-desc": "पन्ना अधिकतम बिस्तार गहिराई के पार क गइल बा।",
        "expansion-depth-exceeded-warning": "पन्ना अधिकतम बिस्तार गहिराई के पार क गइल",
        "parser-unstrip-loop-warning": "अनस्ट्रिप लूप पकड़ में आइल बा",
-       "parser-unstrip-recursion-limit": "अनस्ट्रिप रिकर्शन सीमा पार हो गइल ($1)",
+       "unstrip-depth-warning": "अनस्ट्रिप रिकर्शन सीमा पार हो गइल ($1)",
        "converter-manual-rule-error": "मैनुअल भाषा परिवर्तन नियम में खराबी पकड़ल गइल",
        "undo-success": "संपादन वापस कइल जा सकत बा।\nनीचे दिहल तुलना के चेक करीं आ पुष्टी करीं की आप इहे कइल चाहत बाड़ीं, ओकरा बाद बदलाव सहेज के संपादन वापसी के पूरा करीं।",
        "undo-failure": "बीच में अउरी संपादन होखला की कारण ई संपादन वापस नइखे लिहल जा सकत।",
        "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..45df08b 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আপনার পরিবর্তন এখনও সংরক্ষণ করা হয়নি!",
        "expansion-depth-exceeded-category-desc": "পাতাটি সর্বোচ্চ এক্সপশন সীমানা অতিক্রম করেছে।",
        "expansion-depth-exceeded-warning": "পাতাটি এক্সেপশন সীমানা অতিক্রম করেছে",
        "parser-unstrip-loop-warning": "ত্রুটিপূর্ণ লুপ খুঁজে পাওয়া গিয়েছে",
-       "parser-unstrip-recursion-limit": "লুপ রিকারশন সীমানা অতিক্রম করেছে ($1)",
+       "unstrip-depth-warning": "লুপ রিকারশন সীমানা অতিক্রম করেছে ($1)",
        "converter-manual-rule-error": "ম্যানুয়াল ভাষা রূপান্তর নিয়মে ত্রুটি পাওয়া গিয়েছে",
        "undo-success": "সম্পাদনাটি বাতিল করা যাবে। অনুগ্রহ করে নিচের তুলনাটি পরীক্ষা করে দেখুন ও নিশ্চিত করুন যে এটাই আপনি করতে চান, এবং তারপর নিচের সম্পাদনাগুলি সংরক্ষণ করে সম্পাদনাটির বাতিল প্রক্রিয়া সমাপ্ত করুন।",
        "undo-failure": "এ সম্পাদনা মধ্যবর্তী সম্পাদনাসমূহের কারণে পূর্বাবস্থায় ফিরিয়ে নেওয়া যাবে না।",
        "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": "ইমেইল *",
        "thumbnail_dest_directory": "গন্তব্য ডিরেক্টরি তৈরি করা যায়নি",
        "thumbnail_image-type": "চিত্রের ধরন সমর্থন করে না",
        "thumbnail_gd-library": "অসম্পূর্ণ জিডি লাইব্রেরী কনফিগারেশন: $1 ফাংশন নেই",
+       "thumbnail_image-size-zero": "চিত্রের ফাইলের আকার শূন্য বলে মনে হচ্ছে।",
        "thumbnail_image-missing": "ফাইলটি খুজে পাওয়া যাচ্ছে না: $1",
        "thumbnail_image-failure-limit": "সাম্প্রতিককালে এই ক্ষুদ্র প্রাকপ্রদর্শনমূলক চিত্রটিকে (thumbnail) রেন্ডার করার চেষ্টা অত্যধিকবার ($1 বা তার বেশিবার) ব্যর্থ হয়েছে। অনুগ্রহ করে কিছুক্ষণ পরে আবার চেষ্টা করুন।",
        "import": "পাতা আমদানি",
        "import-mapping-subpage": "নিচের পাতার উপপাতাসমূহ হিসাবে আমদানি করুন:",
        "import-upload-filename": "ফাইলনাম:",
        "import-upload-username-prefix": "আন্তঃউইকি উপসর্গ:",
+       "import-assign-known-users": "ব্যবহারকারী স্থানীয়ভাবে থাকলে তার নামে সম্পাদনা অর্পণ করুন",
        "import-comment": "মন্তব্য:",
        "importtext": "অনুগ্রহ করে ফাইলটি উৎস উইকি থেকে [[Special:Export|রপ্তানি ইউটিলিটি]] ব্যবহার করে রপ্তানি করুন।\nফাইলটি আপনার কম্পিউটারে সংরক্ষণ করুন এবং এখানে আপলোড করুন।",
        "importstart": "পাতা আমদানি করা হচ্ছে...",
        "watchlistedit-clear-titles": "শিরোনামসমূহ:",
        "watchlistedit-clear-submit": "নজরতালিকা পরিষ্কার করুন (এটি স্থায়ী!)",
        "watchlistedit-clear-done": "আপনার নজরতালিকা পরিষ্কার করা হয়েছে।",
+       "watchlistedit-clear-jobqueue": "আপনার নজরতালিকা পরিষ্কার করা হচ্ছে। এটি কিছু সময় নিতে পারে!",
        "watchlistedit-clear-removed": "{{PLURAL:$1|১টি শিরোনাম|$1টি শিরোনাম}} সরিয়ে ফেলা হয়েছে:",
        "watchlistedit-too-many": "এখানে প্রদর্শনের জন্য অনেক পাতা রয়েছে।",
        "watchlisttools-clear": "নজরতালিকা পরিষ্কার করুন",
        "tag-mw-rollback": "পুনর্বহাল",
        "tag-mw-rollback-description": "সম্পাদনাগুলি যা রোলব্যাক সংযোগ ব্যবহার করে পূর্বের সম্পাদনায় ফেরত নেয়",
        "tag-mw-undo": "পূর্বাবস্থায় ফেরত",
+       "tag-mw-undo-description": "সম্পাদনা পূর্বাবস্থায় আনার লিঙ্ক ব্যবহার করে পূর্বাবস্থায় আনা সম্পাদনাগুলি",
        "tags-title": "ট্যাগসমূহ",
        "tags-intro": "এই পাতায় সফটওয়্যারটি একটি সম্পাদনা চিহ্নিত করার জন্য যে সকল ট্যাগ ব্যবহার করে তার তালিকা ও বর্ণনা রয়েছে।",
        "tags-tag": "ট্যাগ নাম",
        "limitreport-templateargumentsize-value": "$1/$2 {{PLURAL:$2|বাইট}}",
        "limitreport-expansiondepth": "সর্বোচ্চ গভীরতা বিস্তার",
        "limitreport-expensivefunctioncount": "ব্যয়বহুল পার্সার ফাংশন গণনা",
+       "limitreport-unstrip-size-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_title": "প্রাতিবেশিক শিরোনাম, {{FULLPAGENAME}}, ইত্যাদির জন্য:",
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..736ee00 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 !",
        "expansion-depth-exceeded-category-desc": "En tu all d'an donder astenn brasañ emañ ar bajenn.",
        "expansion-depth-exceeded-warning": "Pajenn a ya dreist d'an donder astenn",
        "parser-unstrip-loop-warning": "Detektet ez eus bet ul lagadenn n'haller ket divontañ",
-       "parser-unstrip-recursion-limit": "Aet dreist d'ar vevenn rekurziñ n'haller ket divontañ : $1",
+       "unstrip-depth-warning": "Aet dreist d'ar vevenn rekurziñ n'haller ket divontañ : $1",
        "converter-manual-rule-error": "Fazi dinodet  er reolenn cheñch yezh dre zorn",
        "undo-success": "Gallout a reer dizober ar c'hemmoù-mañ. Gwiriit, mar plij, gant ar geñveriadenn a-is evit bezañ sur eo an dra-se a fell deoc'h ober; goude-se enrollit ar c'hemmoù a-is a-benn echuiñ disteurel ar c'hemmoù.",
        "undo-failure": "N'eus ket bet tu da zisteuler ar c'hemm-mañ abalamour d'un tabut gant kemmoù degaset e-keit-se.",
        "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..fad50b2 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!",
        "expansion-depth-exceeded-category-desc": "Stranica je prešla najveću dubinu proširenja.",
        "expansion-depth-exceeded-warning": "Stranice koje su prekoračile dubinu proširenja",
        "parser-unstrip-loop-warning": "Pronađena petlja",
-       "parser-unstrip-recursion-limit": "Prekoračeno ograničenje rekurzije ($1)",
+       "unstrip-depth-warning": "Prekoračeno ograničenje rekurzije ($1)",
        "converter-manual-rule-error": "Pronađena je greška u pravilu za ručno pretvaranje jezika",
        "undo-success": "Izmjenu je moguće poništiti.\nMolimo da ispod uporedite razlike u verzijama kako biste bili sigurni da ovo zaista želite uraditi, te sačuvajte stranicu i izmjene će biti poništene.",
        "undo-failure": "Ova izmjena se ne može poništiti jer se međuverzije sukobe.",
        "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..0e578c3 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!",
        "expansion-depth-exceeded-category-desc": "La pàgina ha excedit la profunditat màxima d'expansió.",
        "expansion-depth-exceeded-warning": "La pàgina ha excedit la profunditat d'expansió",
        "parser-unstrip-loop-warning": "S'ha detectat un bucle no desmuntable",
-       "parser-unstrip-recursion-limit": "S'ha excedit el límit ($1) de recursivitat no desmuntable",
+       "unstrip-depth-warning": "S'ha excedit el límit ($1) de recursivitat no desmuntable",
        "converter-manual-rule-error": "Error detectat a la norma de conversió de llengua manual",
        "undo-success": "Pot desfer-se la modificació. Si us plau, reviseu la comparació de sota per a assegurar-vos que és el que voleu fer; llavors deseu els canvis per a finalitzar la desfeta de l'edició.",
        "undo-failure": "No pot desfer-se la modificació perquè hi ha edicions intermèdies en conflicte.",
        "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:",
        "allpages": "Totes les pàgines",
        "nextpage": "Pàgina següent ($1)",
        "prevpage": "Pàgina anterior ($1)",
-       "allpagesfrom": "Mostra les pàgines que comencin per:",
-       "allpagesto": "Mostra pàgines que acabin en:",
+       "allpagesfrom": "Mostra les pàgines a partir de:",
+       "allpagesto": "Mostra les pàgines fins a:",
        "allarticles": "Totes les pàgines",
        "allinnamespace": "Totes les pàgines (de l’espai de noms $1)",
        "allpagessubmit": "Vés-hi",
index 66aa5b2..9616cbe 100644 (file)
@@ -57,8 +57,8 @@
        "tog-showhiddencats": "Гайта къайлаха йолу категореш",
        "tog-norollbackdiff": "Юха яьккхиначул тӀаьхьа ма гайта версийн башхо",
        "tog-useeditwarning": "Хаамбе бина хийцамаш дӀаязцабеш аса болх дӀатосучу хенахь",
-       "tog-prefershttps": "СиÑ\81Ñ\82емин Ð´Ð¾Ð²Ð·Ð¸Ð¹Ñ\82ина Ñ\87Ñ\83л Ñ\82Ó\80еÑ\85Ñ\8cа Ð´Ð°Ð¸Ð¼Ð°Ð½ Ð»ÐµÐ»Ð°Ð´Ðµ Ð»Ð°Ñ\80дина Ð²Ð¾Ð²Ñ\88аÑ\85Ñ\82аÑ\81ар",
-       "underline-always": "Ð\94аимна",
+       "tog-prefershttps": "Ð\93Ñ\83Ñ\82Ñ\82аÑ\80а Ð»ÐµÐ»Ð°Ð´Ðµ ÐºÑ\85еÑ\80амза Ñ\82аÑ\81далар",
+       "underline-always": "Ð\93Ñ\83Ñ\82Ñ\82аÑ\80",
        "underline-never": "Цкъа а",
        "underline-default": "Лелае браузеран нисярца",
        "editfont-style": "Тадар чохь долу шрифт:",
        "history_short": "Истори",
        "history_small": "истори",
        "updatedmarker": "Керла яккхина сона гинчултӀаьхьа",
-       "printableversion": "Зорба туху верси",
-       "permalink": "Ð\94аиман Ð¹Ð¾Ð»Ñ\83 хьажорг",
+       "printableversion": "Зорбанан верси",
+       "permalink": "ЦаÑ\85ийÑ\86алÑ\83н хьажорг",
        "print": "Зорба тоха",
        "view": "Хьажа",
        "view-foreign": "Сайтехь $1 хьажа",
        "noemailcreate": "Ахьа нийса электронан поштан адрес дӀаяздан деза",
        "passwordsent": "Керла пароль декъашхочун $1 электронан адрес тӀе дӀахьажина. Дехар до, керла пароль еъча юху системин чугӀо.",
        "blocked-mailpassword": "Хьан IP-адрес ца тадарш дан магийна дац, цуьндела пароль меттахӀотош йолу функци блоктоьхна ю.",
-       "eauthentsent": "Ð\94Ó\80аÑ\8fздинÑ\87Ñ\83 Ñ\8dлекÑ\82Ñ\80онан Ð°Ð´Ñ\80еÑ\81 Ñ\82Ó\80е Ñ\85аам Ð±Ð°Ð¸Ð¹Ñ\82ина.\nÐ\94аиман хаамаш баийта хааман чохь де бохург дан деза адрес хьай хилар бакъдеш.",
+       "eauthentsent": "Ð\94Ó\80аÑ\8fздинÑ\87Ñ\83 Ñ\8dлекÑ\82Ñ\80онан Ð°Ð´Ñ\80еÑ\81 Ñ\82Ó\80е Ñ\85аам Ð±Ð°Ð¸Ð¹Ñ\82ина.\nÐ\93Ñ\83Ñ\82Ñ\82аÑ\80а хаамаш баийта хааман чохь де бохург дан деза адрес хьай хилар бакъдеш.",
        "throttled-mailpassword": "Пароль дага йоуьйту функци {{PLURAL:$1|тӀаьххьара $1 сахьтехь}} лелина.\nЗулам цахилийта $1 {{PLURAL:$1|сахьтан чохь}} цӀа бен функци лело йиш яц.",
        "mailerror": "Кехат дохьуьйтуш гӀалат ду: $1",
        "acct_creation_throttle_hit": "Де-буьйса хена чохь хьа IP-адрес тӀера {{PLURAL:$1|кхоьллина $1 декъашхочун дӀаяздар|кхоьллина $1 декъашхочун дӀаяздарш}} оьцу хана юкъахь кхин дукха дӀаяздарш кхолла магийна дац.\nЦундела и IP-адрес лелочарна кхин керла дӀаяздарш кхолла цало.",
        "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": "Электронан пошт:",
        "rcfilters-liveupdates-button-title-off": "Керла хийцамаш ма-бинехь гайта",
        "rcfilters-preference-label": "Керла хийцамийн дика кечйина верси къайлаяккха",
        "rcfilters-preference-help": "2017 шеран интерфейсан редизайн а, оцу хенахь дуьйна тӀетоьхна гӀирсаш а къайлайоху.",
+       "rcfilters-filter-showlinkedfrom-label": "Хьажоргаш йолучу агӀонашна тӀехь нисдарш гайта",
+       "rcfilters-filter-showlinkedfrom-option-label": "<strong>Хаьржиначунна хьажоргаш йолу агӀонаш</strong>",
+       "rcfilters-filter-showlinkedto-label": "Хьажоргаш йолучу агӀонашна тӀехь нисдарш гайта",
        "rcnotefrom": "Лахахь гайтина тӀера <strong>$2</strong> (хийцамаш <strong>$1</strong> кӀезиг).",
        "rclistfromreset": "Терахь харжар дӀадаккха",
        "rclistfrom": "Гайта хийцам {{CURRENTYEAR}} шеран {{CURRENTDAY}} {{CURRENTMONTHNAMEGEN}} {{CURRENTTIME}} бина болу",
        "rc-enhanced-expand": "Гайта мадарра",
        "rc-enhanced-hide": "Ма дарра дерг къайладаккха",
        "rc-old-title": "дуьххьара кхоьллина яра «$1» цӀарца",
-       "recentchangeslinked": "Ð\9aÑ\85Ñ\83Ñ\8cнÑ\86а Ð´Ð¾Ð»Ñ\83 нисдарш",
-       "recentchangeslinked-feed": "Ð\9aÑ\85Ñ\83Ñ\8cнÑ\86а Ð´Ð¾Ð»Ñ\83 нисдарш",
-       "recentchangeslinked-toolbox": "Ð\9aÑ\85Ñ\83Ñ\8cнÑ\86а Ð´Ð¾Ð»Ñ\83 нисдарш",
-       "recentchangeslinked-title": "Ð\9aÑ\85Ñ\83Ñ\8cнÑ\86а Ð´Ð¾Ð»Ñ\83 Ð½Ð¸Ñ\81даÑ\80Ñ\88 $1",
+       "recentchangeslinked": "Ð\94иÑ\85кина нисдарш",
+       "recentchangeslinked-feed": "Ð\94иÑ\85кина нисдарш",
+       "recentchangeslinked-toolbox": "Ð\94иÑ\85кина нисдарш",
+       "recentchangeslinked-title": "Ð\94иÑ\85кина Ð½Ð¸Ñ\81даÑ\80Ñ\88 Â«$1»",
        "recentchangeslinked-summary": "ХӀара хийцам биначу агӀонийн могӀам бу, тӀетовжар долуш хьагучу агӀон (я хьагойтуш йолучу категорена).\nАгӀонаш юькъа йогӀуш йолу хьан [[Special:Watchlist|тергаме могӀам чохь]] '''къастийна ю'''.",
        "recentchangeslinked-page": "АгӀон цӀе:",
        "recentchangeslinked-to": "Кхечу агӀор, гайта хийцамаш агӀонашца, хӀоттийначу агӀонтӀе хьажорг йолуш",
        "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": "Массо агӀонаш экспорт ян",
        "tooltip-t-emailuser": "ДӀабахьийта хаам оцу декъашхона",
        "tooltip-t-upload": "Чуйаха файлаш",
        "tooltip-t-specialpages": "Белхан агӀонанийн могӀам",
-       "tooltip-t-print": "Ð¥Ó\80окÑ\85Ñ\83 Ð°Ð³Ó\80онна Ð·Ð¾Ñ\80ба Ñ\82Ñ\83Ñ\85Ñ\83 Ð±Ð°Ñ\88Ñ\85о",
-       "tooltip-t-permalink": "Ð\94аима Ð¹Ð¾Ð»Ñ\83 Ñ\85Ñ\8cажоÑ\80г Ñ\85Ó\80окÑ\85Ñ\83 Ð±Ð°Ñ\88Ñ\85а Ð°Ð³Ó\80онна",
+       "tooltip-t-print": "Ð¥Ó\80окÑ\85Ñ\83 Ð°Ð³Ó\80она Ð·Ð¾Ñ\80банан Ð²ÐµÑ\80Ñ\81и",
+       "tooltip-t-permalink": "Ð¥Ó\80окÑ\85Ñ\83 Ð°Ð³Ó\80она Ð²ÐµÑ\80Ñ\81ин Ñ\82Ó\80е Ñ\86аÑ\85ийÑ\86алÑ\83н Ñ\85Ñ\8cажоÑ\80г",
        "tooltip-ca-nstab-main": "Коьрта яззаман чулацам",
        "tooltip-ca-nstab-user": "ХӀора декъашхочун долахь йолу агӀо ю",
        "tooltip-ca-nstab-media": "Медиа-файл",
        "pageinfo-hidden-categories": "{{PLURAL:$1|Къайла категори|Къайла категореш}} ($1)",
        "pageinfo-templates": "{{PLURAL:$1|1=Кеп|Кепаш}} ($1)",
        "pageinfo-transclusions": "{{PLURAL:$1|1=Юкъатохало агӀо|Юкъатохало агӀонаш}} ($1)",
-       "pageinfo-toolboxlink": "Агlонах болу бовзам",
+       "pageinfo-toolboxlink": "АгӀонан хаам",
        "pageinfo-redirectsto": "ДӀасахьажорг",
        "pageinfo-redirectsto-info": "Хаам",
        "pageinfo-contentpage": "Лорурго чулацаме гойту агӀо",
        "yesterday-at": "селхана $1 даьлча",
        "bad_image_list": "Барам хила беза иштта:\n\nЛораш хира ю могӀамяхь йолу элементнаш (могӀийн, йола луш йолу символ тӀира *).\nДуьххьаралера хьажорг магӀанийн хила беза хьажорг кху цамагдо сурт дуьлаче.\nТӀехьа йогӀуш йолу хьажорг оцу могӀарехь хира ю магóш, билгалла аьлча яззамаш долуче, сурт хьаллаточехь.",
        "metadata": "Метахаамаш",
-       "metadata-help": "Ð¥Ó\80окÑ\85Ñ\83 Ñ\84айлаÑ\86а ÐºÑ\85ин Ñ\82Ó\80е Ñ\85аам Ð±Ñ\83, Ð´Ð°Ð¸Ð¼Ð°Ð½ чуйоккхуш йолу терахьца чоьнашца йа тӀейоккхучуьнца. Нагахь файлан тӀаьхьа хийцам биняхь, тӀаккха цӀхьаболу барам цӀхьаьна ца ба мега хӀинцалера суьртаца.",
+       "metadata-help": "Ð¥Ó\80окÑ\85Ñ\83 Ñ\84айлаÑ\86а ÐºÑ\85ин Ñ\82Ó\80е Ñ\85аам Ð±Ñ\83, Ð³Ñ\83Ñ\82Ñ\82аÑ\80а чуйоккхуш йолу терахьца чоьнашца йа тӀейоккхучуьнца. Нагахь файлан тӀаьхьа хийцам биняхь, тӀаккха цӀхьаболу барам цӀхьаьна ца ба мега хӀинцалера суьртаца.",
        "metadata-expand": "Гайта кхин тlе болу хаам",
        "metadata-collapse": "Къайла баккха кхин тlе болу хаам",
        "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",
        "autosumm-replace": "АгӀона чуьраниг хийцина → «$1»",
        "autoredircomment": "[[$1]] тӀе хьажийна",
        "autosumm-removed-redirect": "ДӀаяьккхина дӀасхьажог [[$1]]",
+       "autosumm-changed-redirect-target": "ДӀасахьажорг хийцина [[$1]] → [[$2]]",
        "autosumm-new": "Керла агӀо: «$1»",
        "autosumm-newblank": "Кхоьллина еса агӀо",
        "lag-warn-normal": "{{PLURAL:$1|$1 Секунд}} хьалха бина хийцамаш хӀокху могӀамехь гуш ца хилла мега.",
        "tag-mw-contentmodelchange": "модулан чулацаман хийцам",
        "tag-mw-new-redirect": "Керла дӀасахьажорг",
        "tag-mw-removed-redirect": "дӀаяьккхина дӀасхьажорг",
+       "tag-mw-changed-redirect-target": "хийцаран бахьна ду дӀасахьажорг",
        "tag-mw-rollback": "Юхаяккха",
        "tag-mw-undo": "цаоьшу",
        "tags-title": "Билгалонаш",
        "diff-form-oldid": "Версин шира идентификатор (тӀехь дац)",
        "diff-form-revid": "Башхаллаш йолу версин идентификатор",
        "diff-form-submit": "Схьагайта башхаллаш",
+       "permanentlink": "Цахийцалун хьажорг",
+       "permanentlink-revid": "Нисдаран ID",
+       "permanentlink-submit": "Хьажа версега",
        "dberr-problems": "Бехк ма бил! ХӀокху сайтехь техникан халонаш хила.",
        "dberr-again": "Хьажа карла йаккха агlо массех минот йаьлча.",
        "dberr-info": "(аьтто ца хили зӀе хӀотта серверца бухара хаамашца: $1)",
        "htmlform-user-not-exists": "<strong>$1</strong> яц.",
        "htmlform-user-not-valid": "<strong>$1</strong> — декъашхочун магийна йоцу цӀе.",
        "logentry-delete-delete": "$1 {{GENDER:$2|дӀаяьккхина}} агӀо $3",
-       "logentry-delete-delete_redir": "$1 {{GENDER:$2|дӀаяьккхина}} $3 дӀасахьажорг гӀоьнца",
+       "logentry-delete-delete_redir": "$1 {{GENDER:$2|дӀаяьккхина}} $3 дӀасахьажорг тӀехула дӀаязйина",
        "logentry-delete-restore": "$1 {{GENDER:$2|меттахӀоттайина|меттахӀоттайина}} агӀо $3",
        "logentry-delete-event": "$1 {{GENDER:$2|хийцина}} гуш хилар {{PLURAL:$5|1=$5 дӀаяздаран|$5 дӀаяздаршан}} тептаран → $3: $4",
        "logentry-delete-revision": "$1 {{GENDER:$2|хийцина}} {{PLURAL:$5|1=$5 верси|$5 версеш}} гуш хилар $3: $4 агӀорахь",
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 55083ff..d023d8e 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!",
        "expansion-depth-exceeded-category-desc": "Stránka překročila maximální hloubku expanze.",
        "expansion-depth-exceeded-warning": "Stránka překročila hloubku expanze",
        "parser-unstrip-loop-warning": "Detekováno zacyklení unstrip",
-       "parser-unstrip-recursion-limit": "Překročen limit rekurze unstrip ($1)",
+       "unstrip-depth-warning": "Překročen limit rekurze unstrip ($1)",
        "converter-manual-rule-error": "Detekována chyba v pravidlech manuální jazykové konverze",
        "undo-success": "Editace může být zrušena. Zkontrolujte a pak potvrďte změny zobrazené níže.",
        "undo-failure": "Editace nemohla být zrušena kvůli konfliktu mezilehlých editací.",
        "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.",
        "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..2ecc67a 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",
        "expansion-depth-exceeded-category-desc": "Mae'r dudalen yn rhy fawr!",
        "expansion-depth-exceeded-warning": "Mae dyfnder ehangu'r dudalen y tu hwnt i'r terfyn",
        "parser-unstrip-loop-warning": "Wedi darganfod dolen dad-blicio (unstrip loop)",
-       "parser-unstrip-recursion-limit": "Wedi mynd dros ben y terfyn ar ddychweliad dad-blicio (unstrip recursion) ($1)",
+       "unstrip-depth-warning": "Wedi mynd dros ben y terfyn ar ddychweliad dad-blicio (unstrip recursion) ($1)",
        "converter-manual-rule-error": "Cafwyd hyd i wall yn y rheol trosi iaith â llaw",
        "undo-success": "Gellir dadwneud y golygiad. Byddwch gystal â gwirio'r gymhariaeth isod i sicrhau mai dyma sydd arnoch eisiau gwneud, ac yna rhowch y newidiadau ar gadw i gwblhau'r gwaith o ddadwneud y golygiad.",
        "undo-failure": "Methwyd a dadwneud y golygiad oherwydd gwrthdaro â golygiadau cyfamserol.",
        "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..68769ef 100644 (file)
        "botpasswords-label-delete": "Slet",
        "botpasswords-label-resetpassword": "Nulstil adgangskode",
        "botpasswords-label-grants": "Tilgængelige bevillinger:",
+       "botpasswords-help-grants": "Tildelinger giver tilgang til rettighederne som allerede indehaves af din brugerkonto. Aktivering af en rettighed her giver ikke adgang til alle rettigheder din brugerkonto ikke ellers ville have. Se [[Special:ListGrants|tildelingstabellen]] for yderligere information.",
        "botpasswords-label-grants-column": "Tildelt",
        "botpasswords-bad-appid": "Robotnavnet »$1« er ikke gyldigt.",
        "botpasswords-insert-failed": "Kunne ikke tilføje robotnavnet »$1«. Var det allerede tilføjet?",
        "botpasswords-update-failed": "Kunne ikke tilføje robotnavnet »$1«. Er det slettet?",
        "botpasswords-created-title": "Botkodeord oprettet",
-       "botpasswords-created-body": "Robottens adgangskode for robotnavn »$1« for bruger »$2« blev oprettet.",
+       "botpasswords-created-body": "Robottens adgangskode for robotnavn »$1« for {{GENDER:$2|brugeren}} »$2« blev oprettet.",
        "botpasswords-updated-title": "Bot kodeord opdateret",
-       "botpasswords-updated-body": "Robottens adgangskode for robotnavn »$1« for brugeren »$2« blev opdateret.",
+       "botpasswords-updated-body": "Robottens adgangskode for robotnavn »$1« for {{GENDER:$2|brugeren}} »$2« blev opdateret.",
        "botpasswords-deleted-title": "Bot kodeord slettet",
-       "botpasswords-deleted-body": "Robottens adgangskode for robotnavn »$1« for brugeren »$2« blev slettet.",
+       "botpasswords-deleted-body": "Robottens adgangskode for robotnavn »$1« for {{GENDER:$2|brugeren}} »$2« blev slettet.",
        "botpasswords-newpassword": "Den nye adgangskode at logge ind med for <strong>$1</strong> er <strong>$2</strong>. <em>Gem denne oplysning for fremtidig reference.</em> <br> (For gamle robotter som kræver at logindnavnet er det samme som det eventuelle brugernavn, så kan du også bruge <strong>$3</strong> som brugernavn og <strong>$4</strong> som adgangskode).",
        "botpasswords-no-provider": "BotPasswordsSessionProvider er ikke tilgængelig.",
        "botpasswords-restriction-failed": "Begrænsninger for robotadgangskode forhindrer dette logind.",
        "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!",
        "expansion-depth-exceeded-category-desc": "Siden overskrider den maksimale udvidelsesdybde.",
        "expansion-depth-exceeded-warning": "Siden overskred ekspansionsdybden",
        "parser-unstrip-loop-warning": "Unstrip-loop opdaget",
-       "parser-unstrip-recursion-limit": "Unstrip rekursionsgrænse er nået ($1)",
+       "unstrip-depth-warning": "Unstrip rekursionsgrænse er nået ($1)",
        "converter-manual-rule-error": "Fejl opdaget i manuel sprogkonvertingsregel",
        "undo-success": "Redigeringen kan fjernes.\nKontroller venligst sammenligningen herunder for at bekræfte at det er hvad du ønsker at gøre, og gem så ændringerne for at fuldføre fjernelsen.",
        "undo-failure": "Redigeringen kunne ikke fjernes på grund af konflikter med efterfølgende redigeringer.",
        "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 b343b68..b78710a 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!",
        "expansion-depth-exceeded-category-desc": "Die Seite überschreitet die maximale Expandierungstiefe.",
        "expansion-depth-exceeded-warning": "Die Seite hat die Expansionstiefe überschritten.",
        "parser-unstrip-loop-warning": "Zirkelbezug festgestellt",
-       "parser-unstrip-recursion-limit": "Rekursionsgrenze beim Auflösen überschritten ($1)",
+       "unstrip-depth-warning": "Rekursionsgrenze beim Auflösen überschritten ($1)",
+       "unstrip-depth-category": "Seiten, auf denen die Unstrip-Tiefengrenze überschritten ist.",
+       "unstrip-size-warning": "Unstrip-Größengrenze überschritten ($1)",
+       "unstrip-size-category": "Seiten, auf denen die Unstrip-Größengrenze überschritten ist",
        "converter-manual-rule-error": "Bei der manuellen Sprachkonvertierungsregel wurde ein Fehler entdeckt.",
        "undo-success": "Die Bearbeitung kann rückgängig gemacht werden.\nBitte prüfe den Vergleich unten, um sicherzustellen, dass du dies tun möchtest, und speichere dann unten deine Änderungen, um die Bearbeitung rückgängig zu machen.",
        "undo-failure": "Die Änderung konnte nicht rückgängig gemacht werden, da der betroffene Abschnitt zwischenzeitlich verändert wurde.",
        "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.",
        "limitreport-expansiondepth": "Höchste Expansionstiefe",
        "limitreport-expansiondepth-value": "$1/$2",
        "limitreport-expensivefunctioncount": "Anzahl aufwändiger Parserfunktionen",
+       "limitreport-unstrip-depth": "Unstrip-Rekursionstiefe",
+       "limitreport-unstrip-depth-value": "$1 von $2",
+       "limitreport-unstrip-size": "Unstrip-Größe nach dem Expandieren",
+       "limitreport-unstrip-size-value": "$1 von $2 {{PLURAL:$2|Byte|Bytes}}",
        "expandtemplates": "Vorlagen expandieren",
        "expand_templates_intro": "Auf dieser Spezialseite kann Wikitext eingegeben werden. Alle enthaltenen Vorlagen werden dabei rekursiv expandiert.\nAuch Parserfunktionen wie\n<code><nowiki>{{</nowiki>#language:…}}</code> und Variablen wie\n<code><nowiki>{{</nowiki>CURRENTDAY}}</code> werden ausgewertet –\nfaktisch alles, was in doppelten geschweiften Klammern enthalten ist.",
        "expand_templates_title": "Kontexttitel für beispielsweise {{FULLPAGENAME}}:",
index de56748..0f58aaf 100644 (file)
        "history_small": "tarix",
        "updatedmarker": "cı kewtena mına peyêne ra dıme biyo rocane",
        "printableversion": "Versiyonê çapkerdışi",
-       "permalink": "Gıreyo vınderde",
+       "permalink": "Gıreyo daimi",
        "print": "Bınuşne",
        "view": "Bıvin",
        "view-foreign": "$1 de bıvin",
        "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ê!",
        "node-count-exceeded-warning": "Pela amora gozgıra ravêr da",
        "expansion-depth-exceeded-category": "Perrê ke xoriyiya herabiyayışi tede ravêrdeya.",
        "expansion-depth-exceeded-warning": "Ravêriya pela xori herayêna",
-       "parser-unstrip-loop-warning": "Unstrip lete vineya",
-       "parser-unstrip-recursion-limit": "Sinorê limit dê qayış dê ($1) ravêrya",
+       "parser-unstrip-loop-warning": "Çerxê newekerdışi vêniya",
+       "unstrip-depth-warning": "Sinorê newekerdışi ($1) viyarna ra",
        "converter-manual-rule-error": "Rehberê zıwan açarnayışi dı xırabin tesbit biya",
        "undo-success": "No vurnayiş tepeye geryeno. pêverronayişêyê cêrıni kontrol bıkeri.",
        "undo-failure": "Poxta pëverameyişa vurnayişan ra  peyd grotışë kari në bı",
        "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) *:",
        "compare-title-not-exists": "Sernameyo ke şımayê vanê mewcud niyo.",
        "compare-revision-not-exists": "Revizyono ke şımaye vanê mewcud niyo.",
        "diff-form": "yew '''form'''",
+       "permanentlink": "Gıreyo daimi",
        "dberr-problems": "Mayê muxulêm! Ena sita dı newke xırabiya teknik esta.",
        "dberr-again": "Dı-rê deqiqeyi vınde û heni bar ke.",
        "dberr-info": "(Erzmelumati ra xızmetkari nêreseno: $1)",
index 2919aed..1e89bbf 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!",
        "expansion-depth-exceeded-category": "Boki, źož ekspansiska dłymokosć jo pśekšocona",
        "expansion-depth-exceeded-warning": "Bok jo ekspansisku dłymokosć pśekšocył",
        "parser-unstrip-loop-warning": "Njeskóńcna kokula namakana",
-       "parser-unstrip-recursion-limit": "Rekursiska granica pśekšocona ($1)",
+       "unstrip-depth-warning": "Rekursiska granica pśekšocona ($1)",
        "converter-manual-rule-error": "Zmólka w manuelnem pšawidle rěcnego konwertěrowanja namakana",
        "undo-success": "Wobźěłanje móžo se wótpóraś. Pšosym pśeglěduj dołojcne pśirownowanje aby se wěsty był, až to wót wěrnosći coš, a pón składuj změny, aby se wobźěłanje doskóńcnje wótpórało.",
        "undo-failure": "Změna njejo se mógała wótpóraś, dokulaž jo něchten pótrjefjony wótrězk mjaztym změnił.",
        "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..df4faa6 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तमले अर्‍या फेरबदेली आँजि सङ्ग्रहित भया: आथिन!",
        "expansion-depth-exceeded-category-desc": "ये पानाले उच्चतम विस्तार सिमा नाघ्या छ ।",
        "expansion-depth-exceeded-warning": "पानाले सल्दिले सिमालाई नाघ्या छ",
        "parser-unstrip-loop-warning": "अनस्ट्रिप लुप धेखिन्छ",
-       "parser-unstrip-recursion-limit": "अन्स्ट्रिप पुनरावर्तन सिमा पार गरियो ($1)",
+       "unstrip-depth-warning": "अन्स्ट्रिप पुनरावर्तन सिमा पार गरियो ($1)",
        "converter-manual-rule-error": "म्यानुअल भाषा अनुवाद सिध्दान्तमी समस्या धेखियो",
        "viewpagelogs": "यै पानाका लगहरू हेर",
        "nohistory": "ये पृष्ठका लागी कोइ सम्पादन इतिहास छैन।",
        "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..1202ecc 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.",
        "expansion-depth-exceeded-category-desc": "La pàgina la và d'ed là dal fònd mâsim de slarghêda.",
        "expansion-depth-exceeded-warning": "Cla pàgina ché la pasê al fònd de şlargamèint",
        "parser-unstrip-loop-warning": "Catê sèria 'd Unistrip",
-       "parser-unstrip-recursion-limit": "A s'é pasê i lémit 'd arciâm 'd Unstrip ($1)",
+       "unstrip-depth-warning": "A s'é pasê i lémit 'd arciâm 'd Unstrip ($1)",
        "converter-manual-rule-error": "È stê catê un erōr int la règola a mân ed cambiamèint ed la léngua",
        "undo-success": "Cla mudéfica ché la pō, èser scanşlêda., Cuntròla che sòta al diferèinsi che gh'é tr' al dō versiòun per èser sicûr che còl che gh'é scrét al cumbîna cun còl ch'ét vō, e dòunca salvêr al mudéfici per finîr la scanşladûra.",
        "undo-failure": "Imposébil scanşlêr la mudéfica a câşva 'd un cuntrâst cun dal mudéfichi fâti dôp.",
        "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..d9f1efd 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Οι αλλαγές σας δεν έχουν ακόμη αποθηκευτεί!",
        "expansion-depth-exceeded-category-desc": "Η σελίδα υπερβαίνει το μέγιστο βάθος επέκτασης.",
        "expansion-depth-exceeded-warning": "Η σελίδα υπερέβη το βάθος επέκτασης",
        "parser-unstrip-loop-warning": "εντοπίστηκε ένας βρόχος unstrip",
-       "parser-unstrip-recursion-limit": "Υπέρβαση του ορίου αναδρομής Unstrip ($1)",
+       "unstrip-depth-warning": "Υπέρβαση του ορίου αναδρομής Unstrip ($1)",
        "converter-manual-rule-error": "Εντοπίστηκε σφάλμα σε μη αυτόματο κανόνα μετατροπής γλώσσας",
        "undo-success": "Η επεξεργασία μπορεί να αναιρεθεί.\nΠαρακαλούμε ελέγξτε την σύγκριση παρακάτω για να επιβεβαιώσετε ότι είναι αυτό το οποίο θέλετε να κάνετε και έπειτα αποθηκεύστε τις αλλαγές παρακάτω για να ολοκληρώσετε την αναίρεση της επεξεργασίας.",
        "undo-failure": "Η επεξεργασία δεν μπορούσε να αναστραφεί λόγω αντικρουόμενων ενδιάμεσων επεξεργασιών.",
        "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-filter-watchlist-watched-label": "Στην λίστα παρακολούθησης",
        "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-filtergroup-watchlistactivity": "Δραστηριότητα λίστας παρακολούθησης",
        "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-namespaces-tooltip": "Φιλτράρισμα αποτελεσμάτων κατά ονοματοχώρο",
        "rcfilters-liveupdates-button": "Ζωντανή ανανέωση",
        "apisandbox": "Αμμοδοχείο API",
        "apisandbox-api-disabled": "Η Διεπαφή Προγραμματισμού Εφαρμογών (API) είναι απενεργοποιημένη σε αυτήν την τοποθεσία.",
        "apisandbox-intro": "Χρησιμοποιήστε αυτήν τη σελίδα για να πειραματιστείτε με το <strong>API της υπηρεσίας ιστού του MediaWiki</strong>.\nΑνατρέξτε στην [[mw:API:Main page|τεκμηρίωση του API]] για περισσότερες πληροφορίες πάνω στη χρήση του API. \nΠαράδειγμα: [https://www.mediawiki.org/wiki/API#A_simple_example λήψη του περιεχομένου της Αρχικής Σελίδας]. Επιλέξτε κάποια ενέργεια για να δείτε περισσότερα παραδείγματα.\n\nΝα σημειωθεί ότι, παρόλο που αυτό εδώ είναι αμμοδοχείο, οι ενέργειες που εκτελείτε σε αυτήν τη σελίδα μπορούν να τροποποιήσουν το wiki.",
+       "apisandbox-fullscreen": "Επέκταση πίνακα",
        "apisandbox-unfullscreen": "Εμφάνιση της σελίδας",
        "apisandbox-submit": "Υποβολή του αιτήματος",
        "apisandbox-reset": "Εκκαθάριση",
        "editcomment": "Το σχόλιο της επεξεργασίας ήταν: <em>$1</em>.",
        "revertpage": "Ανάκληση των αλλαγών [[Special:Contributions/$2|$2]] ([[User talk:$2|συζήτηση]]) επιστροφή στην προηγούμενη αναθεώρηση [[User:$1|$1]]",
        "revertpage-nouser": "Αναστράφηκαν οι επεξεργασίες από τον (όνομα χρήστη αφαιρέθηκε) στη τελευταία έκδοση από τον/την {{GENDER:$1|[[User:$1|$1]]}}φ",
-       "rollback-success": "Î\91νεÏ\83Ï\84Ï\81αμμένεÏ\82 ÎµÎºÎ´Ï\8cÏ\83ειÏ\82 Î±Ï\80Ï\8c $1, Î±Î»Î»Î¬Ï\87θηκαν Ï\83Ï\84ην Ï\80Ï\81οηγοÏ\8dμενη Î­ÎºÎ´Î¿Ï\83η Î±Ï\80Ï\8c $2.",
+       "rollback-success": "Î\91ναÏ\83Ï\84Ï\81οÏ\86ή ÎµÏ\80εξεÏ\81γαÏ\83ιÏ\8eν Î±Ï\80Ï\8c {{GENDER:$3|Ï\84ον|Ï\84ην}} $1, ÎµÏ\80ιÏ\83Ï\84Ï\81οÏ\86ή Ï\83Ï\84ην Ï\80Ï\81οηγοÏ\8dμενη Î­ÎºÎ´Î¿Ï\83η Î±Ï\80Ï\8c {{GENDER:$4|Ï\84ον|Ï\84ην}} $2.",
        "rollback-success-notify": "Αναίρεση επεξεργασιών από $1; επιστροφή στην τελευταία αναθεώρηση από $2.[$3 Εμφάνιση αλλαγών]",
        "sessionfailure-title": "Η συνεδρία απέτυχε",
        "sessionfailure": "Υπάρχει πρόβλημα με τη σύνδεσή σας -η ενέργεια αυτή ακυρώθηκε προληπτικά για την αντιμετώπιση τυχόν πειρατείας συνόδου (session hijacking). Παρακαλoύμε πατήστε \"Επιστροφή\", ξαναφορτώστε τη σελίδα από την οποία φθάσατε εδώ και προσπαθήστε ξανά.",
        "sp-contributions-uploads": "ανεβάσματα αρχείων",
        "sp-contributions-logs": "καταγραφές",
        "sp-contributions-talk": "συζήτηση",
-       "sp-contributions-userrights": "διαχείριση δικαιωμάτων χρηστών",
+       "sp-contributions-userrights": "διαχείριση δικαιωμάτων {{GENDER:$1|χρήστη|χρήστριας|χρηστών}}",
        "sp-contributions-blocked-notice": "{{GENDER:$1|Αυτός ο χρήστης|Αυτή η χρήστης}} έχει αποκλειστεί επί του παρόντος.\nΗ πιο πρόσφατη καταχώρηση του αρχείου καταγραφής φραγών παρέχεται παρακάτω για αναφορά:",
        "sp-contributions-blocked-notice-anon": "Αυτή η διεύθυνση IP υπόκειται επί του παρόντος σε φραγή",
        "sp-contributions-search": "Αναζήτηση για συνεισφορές",
        "unblocked-id": "Η φραγή του $1 έχει τερματιστεί",
        "unblocked-ip": "Έχει αρθεί η φραγή της [[Special:Contributions/$1|$1]].",
        "blocklist": "Αποκλεισμένοι χρήστες",
+       "autoblocklist": "Αυτόματες φραγές",
        "autoblocklist-submit": "Αναζήτηση",
        "ipblocklist": "Αποκλεισμένοι χρήστες",
        "ipblocklist-legend": "Εύρεση ενός χρήστη που έχει υποστεί φραγή",
        "cant-move-to-user-page": "Δεν έχετε άδεια για να μετακινήσετε μια σελίδα σε σελίδα χρήστη (παρά μόνο σε υποσελίδα χρήστη).",
        "cant-move-category-page": "Δεν έχετε άδεια να μετακινείτε σελίδες κατηγοριών.",
        "cant-move-to-category-page": "Δεν έχετε άδεια να μετακινήσετε μια σελίδα σε σελίδα της κατηγορίας.",
+       "cant-move-subpages": "Δεν έχετε άδεια να μετακινείτε υποσελίδες.",
+       "namespace-nosubpages": "Ο ονοματοχώρος \"$1\" δεν επιτρέπει υποσελίδες.",
        "newtitle": "Νέος τίτλος:",
        "move-watch": "Παρακολούθησε αυτή τη σελίδα",
        "movepagebtn": "Μετακίνηση σελίδας",
        "import-nonewrevisions": "Καμία αναθεώρηση δεν εισήχθει (όλες είτε ήταν ήδη παρούσες, ή παραλήφθηκαν λόγω σφαλμάτων).",
        "xml-error-string": "$1 στη γραμμή $2, στήλη $3 (byte $4): $5",
        "import-upload": "Ανέβασμα δεδομένων XML",
-       "import-token-mismatch": "Απώλεια δεδομένων περιόδου λειτουργίας.\n\nΜπορεί να έχετε αποσυνδεθεί. <strong>Παρακαλούμε επιβεβαιώστε ότι βρίσκεστε ακόμα σε σύνδεση και προσπαθήστε ξανά</strong>.\nΑν εξακολουθεί να μην λειτουργεί, δοκιμάστε να [[Special:UserLogout|αποσυνδεθείτε]] και να συνδεθείτε ξανά, και βεβαιωθείτε ότι το πρόγραμμα περιήγησής σας επιτρέπει cookies από αυτόν τον ιστότοπο.",
+       "import-token-mismatch": "Απώλεια δεδομένων περιόδου λειτουργίας.\n\nΜπορεί να έχετε αποσυνδεθεί. '''Παρακαλούμε επιβεβαιώστε ότι βρίσκεστε ακόμα σε σύνδεση και προσπαθήστε ξανά'''.\nΑν εξακολουθεί να μην λειτουργεί, δοκιμάστε να [[Special:UserLogout|αποσυνδεθείτε]] και να συνδεθείτε ξανά, και βεβαιωθείτε ότι το πρόγραμμα περιήγησής σας επιτρέπει cookies από αυτόν τον ιστότοπο.",
        "import-invalid-interwiki": "Δεν είναι δυνατή η εισαγωγή από το καθορισμένο wiki.",
        "import-error-edit": "Η σελίδα «$1» δεν εισήχθη επειδή δεν σας επιτρέπεται να την επεξεργαστείτε.",
        "import-error-create": "Η σελίδα «$1» δεν εισήχθη επειδή δεν σας επιτρέπεται να την δημιουργήσετε.",
        "tooltip-pt-mycontris": "Κατάλογος των συνεισφορών {{GENDER:|σας}}",
        "tooltip-pt-anoncontribs": "Μια λίστα με τις επεξεργασίες που έγιναν από αυτή τη διεύθυνση IP",
        "tooltip-pt-login": "Σας ενθαρρύνουμε να συνδεθείτε· ωστόσο, δεν είναι υποχρεωτικό",
+       "tooltip-pt-login-private": "Πρέπει να συνδεθείτε για να χρησιμοποιήσετε αυτό το βίκι",
        "tooltip-pt-logout": "Έξοδος",
        "tooltip-pt-createaccount": "Σας ενθαρρύνουμε να δημιουργήσετε ένα λογαριασμό και να συνδεθείτε· ωστόσο, δεν είναι υποχρεωτικό",
        "tooltip-ca-talk": "Συζήτηση για τη σελίδα περιεχομένου",
        "newimages-legend": "Φίλτρο",
        "newimages-label": "Όνομα αρχείου (ή μέρος αυτού):",
        "newimages-user": "Διεύθυνση IP ή όνομα χρήστη",
+       "newimages-newbies": "Εμφάνιση των συνεισφορών των νέων λογαριασμών μόνο",
        "newimages-showbots": "Εμφάνιση αρχείων ανεβασμένων από ρομπότ",
        "newimages-hidepatrolled": "Απόκρυψη ελεγμένων αρχείων.",
        "newimages-mediatype": "Τύπος μέσου:",
        "autosumm-blank": "Διαγραφή του περιεχομένου της σελίδας",
        "autosumm-replace": "Αντικατάσταση σελίδας με '$1'",
        "autoredircomment": "Ανακατεύθυνση στη σελίδα [[$1]]",
+       "autosumm-removed-redirect": "Αφαιρέθηκε ανακατεύθυνση προς το [[$1]]",
+       "autosumm-changed-redirect-target": "Άλλαξε στόχος ανακατεύθυνσης από το [[$1]] στο [[$2]]",
        "autosumm-new": "Νέα σελίδα με '$1'",
        "autosumm-newblank": "Δημιουργήθηκε κενή σελίδα",
        "lag-warn-normal": "Αλλαγές νεότερες από {{PLURAL:$1|δευτερόλεπτο|δευτερόλεπτα}} μπορεί να μην φαίνονται σε αυτή τη λίστα.",
        "watchlistedit-clear-titles": "Τίτλοι:",
        "watchlistedit-clear-submit": "Εκκαθάριση της λίστας παρακολούθησης (Αυτό είναι οριστικό!)",
        "watchlistedit-clear-done": "Η λίστα παρακολούθησής σας έχει καθαριστεί.",
+       "watchlistedit-clear-jobqueue": "Η λίστα παρακολούθησής σας καθαρίζεται. Αυτό ίσως πάρει λίγο χρόνο!",
        "watchlistedit-clear-removed": "{{PLURAL:$1|1 τίτλος αφαιρέθηκε|$1 τίτλοι αφαιρέθηκαν}}:",
        "watchlistedit-too-many": "Υπάρχουν υπερβολικά πολλές σελίδες και δεν μπορούν να εμφανιστούν εδώ.",
        "watchlisttools-clear": "Εκκαθάριση της λίστας παρακολούθησης",
        "version-poweredby-others": "άλλοι",
        "version-poweredby-translators": "translatewiki.net μεταφραστές",
        "version-credits-summary": "Θα θέλαμε να αναγνωρίσουμε τη συμβολή των παρακάτω προσώπων στο [[Special:Version|MediaWiki]].",
-       "version-license-info": "Το MediaWiki ÎµÎ¯Î½Î±Î¹ ÎµÎ»ÎµÏ\8dθεÏ\81ο Î»Î¿Î³Î¹Ï\83μικÏ\8c. Î\9cÏ\80οÏ\81είÏ\84ε Î½Î± Ï\84ο Î±Î½Î±Î´Î¹Î±Î½ÎµÎ¯Î¼ÎµÏ\84ε Î®/και Î½Î± Ï\84ο Ï\84Ï\81οÏ\80οÏ\80οιήÏ\83εÏ\84ε Ï\85Ï\80Ï\8c Ï\84οÏ\85Ï\82 Ï\8cÏ\81οÏ\85Ï\82 Ï\84ηÏ\82 Î¬Î´ÎµÎ¹Î±Ï\82 GNU General Public License Ï\8cÏ\80Ï\89Ï\82 Î±Ï\85Ï\84ή ÎµÎºÎ´Ï\8cθηκε Î±Ï\80Ï\8c Ï\84ο Free Software Foundation· ÎµÎ¯Ï\84ε Ï\84ηÏ\82 Î´ÎµÏ\8dÏ\84εÏ\81ηÏ\82 Î­ÎºÎ´Î¿Ï\83ηÏ\82 Ï\84ηÏ\82 Î¬Î´ÎµÎ¹Î±Ï\82, ÎµÎ¯Ï\84ε (καÏ\84' ÎµÏ\80ιλογή Ï\83αÏ\82) Î¿Ï\80οιαÏ\83δήÏ\80οÏ\84ε ÎµÏ\80Ï\8cμενηÏ\82 Î­ÎºÎ´Î¿Ï\83ηÏ\82.\n\nΤο MediaWiki Î´Î¹Î±Î½Î­Î¼ÎµÏ\84αι Î¼Îµ Ï\84ην ÎµÎ»Ï\80ίδα Ï\8cÏ\84ι Î¸Î± ÎµÎ¯Î½Î±Î¹ Ï\87Ï\81ήÏ\83ιμο, Î±Î»Î»Î¬ Î§Î©Î¡Î\99Σ Î\9aÎ\91Î\9cÎ\99Î\91 Î\95Î\93Î\93Î¥Î\97ΣÎ\97· Î¿Ï\8dÏ\84ε ÎºÎ±Î½ Ï\84ην Ï\83ιÏ\89Ï\80ηÏ\81ή ÎµÎ³Î³Ï\8dηÏ\83η Î\95Î\9cΠÎ\9fΡÎ\95ΥΣÎ\99Î\9cÎ\9fΤÎ\97ΤÎ\91Σ Î® Î\9aÎ\91ΤÎ\91Î\9bÎ\9bÎ\97Î\9bÎ\9fΤÎ\97ΤÎ\91Σ Î\93Î\99Î\91 Î\95Î\9dÎ\91 Î£Î¥Î\93Î\9aÎ\95Î\9aΡÎ\99Î\9cÎ\95Î\9dÎ\9f Î£Î\9aÎ\9fΠÎ\9f. Î\92λ. GNU General Public License Î³Î¹Î± Ï\80εÏ\81ιÏ\83Ï\83Ï\8cÏ\84εÏ\81εÏ\82 Î»ÎµÏ\80Ï\84ομέÏ\81ειεÏ\82.\n\nÎ\98α Ï\80Ï\81έÏ\80ει Î½Î± Î­Ï\87εÏ\84ε Î»Î¬Î²ÎµÎ¹ [{{SERVER}}{{SCRIPTPATH}}/COPYING Î­Î½Î± Î±Î½Ï\84ίγÏ\81αÏ\86ο Ï\84ηÏ\82 GNU General Public License] μαζί με αυτό το πρόγραμμα· αν όχι, στείλτε ένα γράμμα στο Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA ή [//www.gnu.org/licenses/old-licenses/gpl-2.0.html διαβάστε το διαδικτυακά].",
+       "version-license-info": "Το MediaWiki ÎµÎ¯Î½Î±Î¹ ÎµÎ»ÎµÏ\8dθεÏ\81ο Î»Î¿Î³Î¹Ï\83μικÏ\8c. Î\9cÏ\80οÏ\81είÏ\84ε Î½Î± Ï\84ο Î±Î½Î±Î´Î¹Î±Î½ÎµÎ¯Î¼ÎµÏ\84ε Î®/και Î½Î± Ï\84ο Ï\84Ï\81οÏ\80οÏ\80οιήÏ\83εÏ\84ε Ï\85Ï\80Ï\8c Ï\84οÏ\85Ï\82 Ï\8cÏ\81οÏ\85Ï\82 Ï\84ηÏ\82 Î\93ενικήÏ\82 Î\86δειαÏ\82 Î\94ημÏ\8cÏ\83ιαÏ\82 Î§Ï\81ήÏ\83ηÏ\82 GNU Ï\8cÏ\80Ï\89Ï\82 Î±Ï\85Ï\84ή Î´Î·Î¼Î¿Ï\83ιεÏ\8dεÏ\84αι Î±Ï\80Ï\8c Ï\84ο Î\8aδÏ\81Ï\85μα Î\95λεÏ\8dθεÏ\81οÏ\85 Î\9bογιÏ\83μικοÏ\8d· ÎµÎ¯Ï\84ε Ï\84ηÏ\82 Î­ÎºÎ´Î¿Ï\83ηÏ\82 2 Ï\84ηÏ\82 Î\86δειαÏ\82, ÎµÎ¯Ï\84ε (καÏ\84' ÎµÏ\80ιλογήν Ï\83αÏ\82) Î¿Ï\80οιαÏ\83δήÏ\80οÏ\84ε Î¼ÎµÏ\84αγενέÏ\83Ï\84εÏ\81ηÏ\82 Î­ÎºÎ´Î¿Ï\83ηÏ\82.\n\nΤο MediaWiki Î´Î¹Î±Î½Î­Î¼ÎµÏ\84αι Î¼Îµ Ï\84ην ÎµÎ»Ï\80ίδα Ï\8cÏ\84ι Î¸Î± ÎµÎ¯Î½Î±Î¹ Ï\87Ï\81ήÏ\83ιμο, Î±Î»Î»Î¬ <em>ΧΩΡÎ\99Σ Î\9aÎ\91Î\9cÎ\99Î\91 Î\95Î\93Î\93Î¥Î\97ΣÎ\97</em>· Ï\87Ï\89Ï\81ίÏ\82 ÎºÎ±Î½ Ï\84η Ï\83ιÏ\89Ï\80ηÏ\81ή ÎµÎ³Î³Ï\8dηÏ\83η <strong>Î\95Î\9cΠÎ\9fΡÎ\95ΥΣÎ\99Î\9cÎ\9fΤÎ\97ΤÎ\91Σ</strong> Î® <strong>Î\9aÎ\91ΤÎ\91Î\9bÎ\9bÎ\97Î\9bÎ\9fΤÎ\97ΤÎ\91Σ Î\93Î\99Î\91 Î£Î¥Î\93Î\9aÎ\95Î\9aΡÎ\99Î\9cÎ\95Î\9dÎ\9f Î£Î\9aÎ\9fΠÎ\9f</strong>. Î\94είÏ\84ε Ï\84η Î\93ενική Î\86δεια Î\94ημÏ\8cÏ\83ιαÏ\82 Î§Ï\81ήÏ\83ηÏ\82 GNU Î³Î¹Î± Ï\80εÏ\81ιÏ\83Ï\83Ï\8cÏ\84εÏ\81εÏ\82 Î»ÎµÏ\80Ï\84ομέÏ\81ειεÏ\82.\n\nÎ\98α Ï\80Ï\81έÏ\80ει Î½Î± Î­Ï\87εÏ\84ε Ï\80αÏ\81αλάβει [{{SERVER}}{{SCRIPTPATH}}/COPYING Î­Î½Î± Î±Î½Ï\84ίγÏ\81αÏ\86ο Ï\84ηÏ\82 Î\93ενικήÏ\82 Î\86δειαÏ\82 Î\94ημÏ\8cÏ\83ιαÏ\82 Î§Ï\81ήÏ\83ηÏ\82 GNU] μαζί με αυτό το πρόγραμμα· αν όχι, στείλτε ένα γράμμα στο 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": "Έκδοση",
        "tag-filter-submit": "Φίλτρο",
        "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|Ετικέτα|Ετικέτες}}]]: $2)",
        "tag-mw-new-redirect": "Νέα ανακατεύθυνση",
+       "tag-mw-removed-redirect": "Αφαιρέθηκε ανακατεύθυνση",
+       "tag-mw-changed-redirect-target": "Στόχος ανακατεύθυνσης άλλαξε",
        "tag-mw-blank": "Άδειασμα περιεχομένου σελίδας",
        "tag-mw-blank-description": "Επεξεργασίες που σβήνουν όλο το περιεχόμενο σελίδας",
        "tag-mw-replace": "Αντικαταστάθηκε",
        "tag-mw-replace-description": "Επεξεργασίες που αφαιρούν πάνω από 90% του περιεχομένου της σελίδας",
        "tag-mw-rollback": "Επαναφορά",
+       "tag-mw-undo": "Αναιρέθηκε",
        "tags-title": "Ετικέτες",
        "tags-intro": "Η σελίδα καταγράφει τις ετικέτες, καθώς και το τι σημαίνει η κάθε μία, με τις οποίες το λογισμικό μπορεί να μαρκάρει μία επεξεργασία.",
        "tags-tag": "Όνομα ετικέτας",
        "tags-create-reason": "Αιτία:",
        "tags-create-submit": "Δημιουργία",
        "tags-create-no-name": "Πρέπει να καθορίσετε όνομα ετικέτας.",
-       "tags-create-invalid-chars": "Τα ονόματα ετικετών δεν πρέπει να περιέχουν κόμματα (<code>,</code>) ή καθέτους (<code>/</code>).",
+       "tags-create-invalid-chars": "Τα ονόματα ετικετών δεν πρέπει να περιέχουν κόμματα (<code>,</code>), κατακόρυφες καθέτους (<code>|</code>), ή καθέτους (<code>/</code>).",
        "tags-create-invalid-title-chars": "Τα ονόματα ετικετών δεν πρέπει να περιέχουν χαρακτήρες που δεν είναι δυνατό να χρησιμοποιηθούν σε τίτλους σελίδων.",
        "tags-create-already-exists": "Η ετικέτα «$1» υπάρχει ήδη.",
        "tags-create-warnings-above": "{{PLURAL:$2|Αντιμετωπίστηκε η ακόλουθη προειδοποίηση|Αντιμετωπίστηκαν οι ακόλουθες προειδοποιήσεις}} όταν έγινε προσπάθεια για τη δημιουργία της ετικέτας «$1»:",
        "compare-invalid-title": "Ο τίτλος που καθορίσατε δεν είναι έγκυρος.",
        "compare-title-not-exists": "Ο τίτλος που καθορίσατε δεν υπάρχει.",
        "compare-revision-not-exists": "Η αναθεώρηση που καθορίσατε δεν υπάρχει.",
-       "diff-form": "μια '''φόρμα'''",
+       "diff-form": "Διαφορές",
+       "diff-form-submit": "Εμφάνιση διαφορών",
        "permanentlink": "Σταθερός σύνδεσμος",
        "permanentlink-revid": "Αναγνωριστικό αναθεώρησης",
+       "permanentlink-submit": "Πήγαινε στην αναθεώρηση",
        "dberr-problems": "Λυπούμαστε! Αυτός ο ιστότοπος αντιμετωπίζει τεχνικές δυσκολίες.",
        "dberr-again": "Δοκιμάστε να περιμενένετε λίγα λεπτά και να ανανεώσετε.",
        "dberr-info": "(Δεν είναι δυνατή η πρόσβαση στη βάση δεδομένων: $1)",
        "limitreport-templateargumentsize-value": "$1 από $2 {{PLURAL:$2|byte|bytes}}",
        "limitreport-expansiondepth": "Μεγαλύτερο βάθος ανάπτυξης",
        "limitreport-expensivefunctioncount": "Πλήθος ακριβών συναρτήσεων συντακτικού αναλυτή",
+       "limitreport-unstrip-depth-value": "$1/$2",
+       "limitreport-unstrip-size-value": "$1/$2 {{PLURAL:$2|byte|bytes}}",
        "expandtemplates": "Επέκταση προτύπων",
-       "expand_templates_intro": "Αυτή η ειδική σελίδα παίρνει κείμενο και αναπτύσσει όλα τα πρότυπα σε αυτό αναδρομικά. \nΕπίσης αναπτύσσει συναρτήσεις συντακτικού αναλυτή όπως η\n<nowiki>{{</nowiki>#language:…}}, και μεταβλητές όπως η\n<nowiki>{{</nowiki>CURRENTDAY}}.\nΟυσιαστικά επεκτείνει οτιδήποτε βρίσκεται σε διπλές αγκύλες.",
+       "expand_templates_intro": "Αυτή η ειδική σελίδα παίρνει κώδικα wiki και αναπτύσσει όλα τα πρότυπα σε αυτό αναδρομικά. \nΕπίσης αναπτύσσει συναρτήσεις συντακτικού αναλυτή όπως η\n<nowiki>{{</nowiki>#language:…}}, και μεταβλητές όπως η\n<nowiki>{{</nowiki>CURRENTDAY}}.\nΟυσιαστικά επεκτείνει οτιδήποτε βρίσκεται σε διπλές αγκύλες.",
        "expand_templates_title": "Τίτλων συμφραζόμενων, για την {{FULLPAGENAME}} κ.τ.λ.:",
-       "expand_templates_input": "Î\9aείμενο εισόδου:",
+       "expand_templates_input": "Î\92ικικείμενο εισόδου:",
        "expand_templates_output": "Αποτέλεσμα",
        "expand_templates_xml_output": "Έξοδος XML",
        "expand_templates_html_output": "Ανεπεξέργαστη έξοδος HTML",
        "special-characters-group-thai": "ταϊλανδικά",
        "special-characters-group-lao": "λαοτινά",
        "special-characters-group-khmer": "καμποτζιανά",
+       "special-characters-group-canadianaboriginal": "Καναδικό Αβοριγινικό",
        "special-characters-title-endash": "παύλα",
        "special-characters-title-emdash": "διπλή παύλα",
        "special-characters-title-minus": "σύμβολο πλην",
        "log-action-filter-contentmodel-change": "Αλλαγή του μοντέλου περιεχομένου",
        "log-action-filter-contentmodel-new": "Δημιουργία σελίδας με μη προεπιλεγμένο μοντέλο περιεχομένου",
        "log-action-filter-delete-delete": "Διαγραφή σελίδας",
+       "log-action-filter-delete-restore": "Ξεδιαγραφή σελίδας",
+       "log-action-filter-import-interwiki": "Εισαγωγή Transwiki",
        "log-action-filter-import-upload": "Εισαγωγή μέσω ανεβάσματος XML",
        "log-action-filter-managetags-create": "Δημιουργία ετικέτας",
        "log-action-filter-managetags-delete": "Διαγραφή ετικέττας",
        "log-action-filter-newusers-autocreate": "Αυτόματη δημιουργία",
+       "log-action-filter-patrol-patrol": "Χειροκίνητη περιπολία",
+       "log-action-filter-patrol-autopatrol": "Αυτόματη περιπολία",
        "log-action-filter-protect-protect": "Προστασία",
        "log-action-filter-protect-modify": "Τροποποίηση προστασίας",
        "log-action-filter-protect-unprotect": "Άρση προστασίας",
        "log-action-filter-protect-move_prot": "Μετακίνηση προστασίας",
        "log-action-filter-rights-rights": "Χειροκίνητη αλλαγή",
        "log-action-filter-rights-autopromote": "Αυτόματη αλλαγή",
+       "log-action-filter-upload-upload": "Νέα μεταφόρτωση",
+       "log-action-filter-upload-overwrite": "Επαναμεταφόρτωση",
        "authmanager-create-disabled": "Η δημιουργία λογαριασμού έχει απενεργοποιηθεί.",
        "authmanager-authplugin-setpass-failed-title": "Η αλλαγή κωδικού πρόσβασης απέτυχε",
        "authmanager-email-label": "Διεύθυνση ηλ. ταχυδρομείου",
+       "authmanager-email-help": "Διεύθυνση ηλεκτρονικού ταχυδρομείου:",
        "authmanager-realname-label": "Πραγματικό όνομα",
        "authmanager-realname-help": "Πραγματικό όνομα του χρήστη",
        "authmanager-provider-temporarypassword": "Προσωρινός κωδικός",
        "authprovider-resetpass-skip-label": "Παράβλεψη",
        "authprovider-resetpass-skip-help": "Παράβλεψη της επαναφοράς του κωδικού πρόσβασης.",
        "specialpage-securitylevel-not-allowed-title": "Δεν επιτρέπεται",
+       "authpage-cannot-login": "Αποτυχία στο ξεκίνημα σύνδεσης.",
+       "authpage-cannot-create": "Αποτυχία στην αρχή δημιουργίας λογαριασμού.",
        "cannotauth-not-allowed-title": "Δεν έχετε δικαίωμα πρόσβασης.",
        "cannotauth-not-allowed": "Δεν επιτρέπεται να χρησιμοποιήσετε αυτή τη σελίδα",
+       "changecredentials": "Αλλαγή πιστοποιητικών",
+       "changecredentials-submit": "Αλλαγή πιστοποιητικών",
+       "removecredentials": "Αφαίρεση πιστοποιητικών",
+       "removecredentials-submit": "Αφαίρεση πιστοποιητικών",
        "credentialsform-account": "Όνομα λογαριασμού:",
+       "cannotlink-no-provider-title": "Δεν υπάρχουν συνδέσιμοι λογαριασμοί",
+       "cannotlink-no-provider": "Δεν υπάρχουν συνδέσιμοι λογαριασμοί.",
        "linkaccounts": "Σύνδεση λογαριασμών",
        "linkaccounts-success-text": "Ο λογαριασμός συνδέθηκε",
        "linkaccounts-submit": "Σύνδεση λογαριασμών",
+       "unlinkaccounts": "Ξεσύνδεση λογαριασμών",
+       "unlinkaccounts-success": "Ο λογαριασμός ξεσυνδέθηκε.",
        "userjsispublic": "Σημείωση: Οι υποσελίδες JavaScript δεν πρέπει να περιέχουν εμπιστευτικά δεδομένα καθώς είναι ορατά από άλλους χρήστες.",
        "usercssispublic": "Σημείωση: Οι υποσελίδες CSS δεν πρέπει να περιέχουν εμπιστευτικά δεδομένα καθώς είναι ορατά από άλλους χρήστες.",
+       "edit-error-short": "Σφάλμα: $1",
+       "edit-error-long": "Σφάλματα: \n\n$1",
        "revid": "αναθεώρηση $1",
+       "pagedata-title": "Δεδομένα σελίδας",
        "pagedata-bad-title": "Μη έγκυρος τίτλος: $1."
 }
index bcf0ec5..aaaf785 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!",
        "expansion-depth-exceeded-category-desc": "The page exceeds the maximum expansion depth.",
        "expansion-depth-exceeded-warning": "Page exceeded the expansion depth",
        "parser-unstrip-loop-warning": "Unstrip loop detected",
-       "parser-unstrip-recursion-limit": "Unstrip recursion limit exceeded ($1)",
+       "unstrip-depth-warning": "Unstrip depth limit exceeded ($1)",
+       "unstrip-depth-category": "Pages where the unstrip depth limit is exceeded",
+       "unstrip-size-warning": "Unstrip size limit exceeded ($1)",
+       "unstrip-size-category": "Pages where the unstrip size limit is exceeded",
        "converter-manual-rule-error": "Error detected in manual language conversion rule",
        "undo-success": "The edit can be undone.\nPlease check the comparison below to verify that this is what you want to do, and then save the changes below to finish undoing the edit.",
        "undo-failure": "The edit could not be undone due to conflicting intermediate edits.",
        "stub-threshold-disabled": "Disabled",
        "recentchangesdays": "Days to show in recent changes:",
        "recentchangesdays-max": "Maximum $1 {{PLURAL:$1|day|days}}",
-       "recentchangescount": "Number of edits to show by default:",
-       "prefs-help-recentchangescount": "This includes recent changes, page histories, and logs.",
+       "recentchangescount": "Number of edits to show in recent changes, page histories, and in logs, by default:",
+       "prefs-help-recentchangescount": "Maximum number: 1000",
        "prefs-help-watchlist-token2": "This is the secret key to the web feed of your watchlist.\nAnyone who knows it will be able to read your watchlist, so do not share it.\nIf you need to, [[Special:ResetTokens|you can reset it]].",
        "savedprefs": "Your preferences have been saved.",
        "savedrights": "The user groups of {{GENDER:$1|$1}} have 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:",
        "limitreport-expansiondepth-value": "$1/$2",
        "limitreport-expensivefunctioncount": "Expensive parser function count",
        "limitreport-expensivefunctioncount-value": "$1/$2",
+       "limitreport-unstrip-depth": "Unstrip recursion depth",
+       "limitreport-unstrip-depth-value": "$1/$2",
+       "limitreport-unstrip-size": "Unstrip post-expand size",
+       "limitreport-unstrip-size-value": "$1/$2 {{PLURAL:$2|byte|bytes}}",
        "expandtemplates": "Expand templates",
        "expand_templates_intro": "This special page takes wikitext and expands all templates in it recursively.\nIt also expands supported parser functions like\n<code><nowiki>{{</nowiki>#language:…}}</code> and variables like\n<code><nowiki>{{</nowiki>CURRENTDAY}}</code>.\nIn fact, it expands pretty much everything in double-braces.",
        "expand_templates_title": "Context title, for {{FULLPAGENAME}}, etc.:",
index 1ebc823..035abcb 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!",
        "expansion-depth-exceeded-category-desc": "La paĝo superis maksimuman profundecon de ekspando.",
        "expansion-depth-exceeded-warning": "Paĝo preterpasis la ekpansiprofundon.",
        "parser-unstrip-loop-warning": "Cirkloreferencon detektis",
-       "parser-unstrip-recursion-limit": "Rikurlimiton de analizopoj ($1) superis",
+       "unstrip-depth-warning": "Rikurlimiton de analizopoj ($1) superis",
        "converter-manual-rule-error": "Eraron detektis en mana lingvokonverta regulo",
        "undo-success": "La redakto estas malfarebla.\nBonvolu kontroli la jenan komparaĵon por certiĝi ĉu tio estas tio, kion vi volas, kaj konservi la ŝanĝojn malsupre por fine malfari la redakton.",
        "undo-failure": "Ne povis nuligi redakton pro konfliktaj intermezaj redaktoj.",
        "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 d1989ac..9cb3299 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.",
        "expansion-depth-exceeded-category-desc": "Esta página sobrepasa la profundidad de expansión máxima.",
        "expansion-depth-exceeded-warning": "La página ha sobrepasado el límite de profundidad de expansión",
        "parser-unstrip-loop-warning": "Se ha detectado un bucle en la función \"unstrip\"",
-       "parser-unstrip-recursion-limit": "Se ha superado el límite de recursividad de la función \"unstrip\" ($1)",
+       "unstrip-depth-warning": "Se ha superado el límite de recursividad de la función \"unstrip\" ($1)",
        "converter-manual-rule-error": "Se ha detectado un error en una regla manual de conversión de idioma",
        "undo-success": "Puedes deshacer la edición. Antes de deshacer la edición, comprueba la siguiente comparación para verificar que realmente es lo que quieres hacer, y entonces guarda los cambios para así efectuar la reversión.",
        "undo-failure": "No se ha podido deshacer la edición ya que otro usuario ha realizado una edición intermedia.",
        "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..6206b0b 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!",
        "expansion-depth-exceeded-category-desc": "Lehekülg ületab hõrendussügavuse ülemmäära.",
        "expansion-depth-exceeded-warning": "Lehekülg ületas hõrendussügavuse.",
        "parser-unstrip-loop-warning": "''Unstrip''-funktsiooni silmus tuvastatud",
-       "parser-unstrip-recursion-limit": "''Unstrip''-funktsioonis rekursiooni piirmäär ületatud ($1)",
+       "unstrip-depth-warning": "''Unstrip''-funktsioonis rekursiooni piirmäär ületatud ($1)",
        "converter-manual-rule-error": "Tõrge keelevariandi käsivahetusreeglis",
        "undo-success": "Selle redaktsiooni käigus tehtud muudatusi saab eemaldada. Palun kontrolli allolevat võrdlust veendumaks, et tahad need muudatused tõepoolest eemaldada. Seejärel saad lehekülje salvestada.",
        "undo-failure": "Muudatust ei saa vahapeal tehtud redigeerimiste tõttu tühistada.",
        "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:",
        "rollback-success": "Tühistati muudatused, mille tegi {{GENDER:$3|$1}};\npöörduti tagasi viimasele muudatusele, mille tegi {{GENDER:$4|$2}}.",
        "rollback-success-notify": "Tühistatud kasutaja $1 tehtud muudatused;\npöördutud tagasi kasutaja $2 viimase redaktsiooni juurde. [$3 Näita muudatusi]",
        "sessionfailure-title": "Seansiviga",
-       "sessionfailure": "Sinu sisselogimisseansiga näib probleem olevat.\nSee toiming on seansiärandamise vastase ettevaatusabinõuna tühistatud.\nMine tagasi eelmisele leheküljele ja taaslaadi see, seejärel proovi uuesti.",
+       "sessionfailure": "Näib, et sinu sisselogimisseanss on probleemne.\nSeansiärandamise vastase ettevaatusabinõuna on see toiming tühistatud.\nPalun saada vorm uuesti.",
        "changecontentmodel": "Lehekülje sisumudeli muutmine",
        "changecontentmodel-legend": "Sisumudeli muutmine",
        "changecontentmodel-title-label": "Lehekülje pealkiri",
        "thumbnail_dest_directory": "Sihtkataloogi loomine ebaõnnestus.",
        "thumbnail_image-type": "Selline pildi tüüp ei ole toetatav",
        "thumbnail_gd-library": "GD teegi häälestus on poolik: funktsioon $1 puudub",
+       "thumbnail_image-size-zero": "Paistab, et pildifail on nullsuurusega.",
        "thumbnail_image-missing": "Fail näib puuduvat: $1",
        "thumbnail_image-failure-limit": "Selle pisipildi viimistlemine on hiljuti liiga palju kordi ($1 või rohkem) ebaõnnestunud. Palun proovi hiljem uuesti.",
        "import": "Lehekülgede import",
        "watchlistedit-clear-titles": "Pealkirjad:",
        "watchlistedit-clear-submit": "Tühjenda jälgimisloend (jäädavalt!)",
        "watchlistedit-clear-done": "Sinu jälgimisloend on tühjendatud.",
+       "watchlistedit-clear-jobqueue": "Jälgimisloend on tühjendamisel. Selleks võib kuluda mõni aeg!",
        "watchlistedit-clear-removed": "{{PLURAL:$1|Üks pealkiri|$1 pealkirja}} eemaldati:",
        "watchlistedit-too-many": "Pealkirju on siin kuvamiseks liiga palju.",
        "watchlisttools-clear": "tühjenda jälgimisloend",
index 3100c46..2b1f1af 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!",
        "expansion-depth-exceeded-category-desc": "Orriak zabalkunde sakonera maximoa gainditzen du.",
        "expansion-depth-exceeded-warning": "Espantsio sakonera gainditu duten orrialdeak",
        "parser-unstrip-loop-warning": "Loop unstrip bat aurkitu da",
-       "parser-unstrip-recursion-limit": "Unstrip errekurtsio limitea gainditu da ($1)",
+       "unstrip-depth-warning": "Unstrip errekurtsio limitea gainditu da ($1)",
        "converter-manual-rule-error": "Akatsa aurkitu da hizkuntzen eskuszko konbertsio arauan",
        "undo-success": "Aldaketa desegin daiteke.\nMesedez beheko alderaketa egiaztatu, egin nahi duzuna hori dela frogatzeko, eta ondoren azpiko aldaketak gorde, aldaketa desegiten amaitzeko.",
        "undo-failure": "Ezin izan da aldaketa desegin tarteko aldaketekin gatazkak direla-eta.",
        "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..9086795 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تغییرات شما هنوز ذخیره نشده‌است!",
        "expansion-depth-exceeded-category-desc": "رده برای صفحاتی که در آنها از عمق گسترش فراتر رفته است.",
        "expansion-depth-exceeded-warning": "صفحه حداکثر عمق بسط دادن تجاوز کرد",
        "parser-unstrip-loop-warning": "حلقه در دستور unstrip پیدا شد",
-       "parser-unstrip-recursion-limit": "از حداکثر ارجاع در دستور unstrip تجاوز شد ($1)",
+       "unstrip-depth-warning": "از حداکثر ارجاع در دستور unstrip تجاوز شد ($1)",
        "converter-manual-rule-error": "خطا در قوانین مبدل دستی زبان",
        "undo-success": "این ویرایش را می‌توان خنثی کرد.\nلطفاً تفاوت زیر را بررسی کنید تا تأیید کنید که این چیزی است که می‌خواهید انجام دهید، سپس تغییرات زیر را ذخیره کنید تا خنثی‌سازی ویرایش را به پایان ببرید.",
        "undo-failure": "به علت تعارض با ویرایش‌های میانی، این ویرایش را نمی‌توان خنثی کرد.",
        "prefs-files": "پرونده‌ها",
        "prefs-custom-css": "سی‌اس‌اس شخصی",
        "prefs-custom-js": "جاوااسکریپت شخصی",
-       "prefs-common-css-js": "سی‌اس‌اس/جاوااسکریپت مشترک برای تمام پوسته‌ها:",
+       "prefs-common-config": "سی‌اس‌اس/جاوااسکریپت مشترک برای تمام پوسته‌ها:",
        "prefs-reset-intro": "شما می‌توانید از این صفحه برای بازگرداندن تنظیمات خود به پیش‌فرض تارنما استفاده کنید.\nاین کار بازگشت‌ناپذیر است.",
        "prefs-emailconfirm-label": "تأیید ایمیل:",
        "youremail": "ایمیل:",
        "recentchanges-summary": "آخرین تغییرات ویکی را در این صفحه پی‌گیری کنید.",
        "recentchanges-noresult": "در فاصله زمانی ارائه شده هیچ تغییری با این معیارهای صورت نگرفته است",
        "recentchanges-timeout": "این جستجو زمانش تمام شد. اگر مایلید کلیدواژه‌های دیگری را جستجو کنید.",
-       "recentchanges-network": "به دلیل خطای فنی، نتیجه‌ای بدست نیامد. لطفاً برای تازه‌سازی صفحخ مجدداُ تلاش کنید.",
+       "recentchanges-network": "به دلیل خطای فنی، نتیجه‌ای بدست نیامد. لطفاً برای تازه‌سازی صفحه مجدداً تلاش کنید.",
        "recentchanges-notargetpage": "نام صفحه را در بالا وارد کنید تا تغییرات مرتبط با آن صفحه را مشاهده کنید.",
        "recentchanges-feed-description": "آخرین تغییرات ویکی را در این خوراک پی‌گیری کنید.",
        "recentchanges-label-newpage": "این ویرایش صفحه‌ای تازه ایجاد کرد",
        "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 71c593e..47208a7 100644 (file)
        "passwordreset-emailelement": "Käyttäjätunnus: \n$1\n\nVäliaikainen salasana: \n$2",
        "passwordreset-emailsentemail": "Jos tämä on sinun tunnuksellesi rekisteröity sähköpostiosoite, salasanan uudistamisesta kertova viesti lähetetään.",
        "passwordreset-emailsentusername": "Jos on olemassa vastaava rekisteröity sähköpostiosoite, salasanan uudistamisesta kertova viesti lähetetään.",
+       "passwordreset-nosuchcaller": "Kutsuvaa funktiota ei ole olemassa: $1",
        "passwordreset-ignored": "Salasanan palauttamista ei käsitelty. Ehkä tarjoajaa ei ollut määritetty?",
        "passwordreset-invalidemail": "Virheellinen sähköpostiosoite",
        "passwordreset-nodata": "Käyttäjätunnusta ja salasanaa ei annettu",
        "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",
        "expansion-depth-exceeded-category-desc": "Tämä sivu ylittää suurimman sallitun laajentamissyvyyden (expansion depth).",
        "expansion-depth-exceeded-warning": "Sivu ylitti laajentamissyvyyden.",
        "parser-unstrip-loop-warning": "Unstrip-silmukka havaittiin",
-       "parser-unstrip-recursion-limit": "Unstrip-rekursion enimmäissyvyys ($1) ylitettiin",
+       "unstrip-depth-warning": "Unstrip-rekursion enimmäissyvyys ($1) ylitettiin",
        "converter-manual-rule-error": "Kielivarianttisäännössä on virhe",
        "undo-success": "Kumoaminen voidaan suorittaa.\nVarmista alla olevasta vertailusta, että haluat saada aikaan tämän lopputuloksen, ja sen jälkeen tallenna alla näkyvät muutokset.",
        "undo-failure": "Muokkausta ei voi kumota välissä olevien ristiriitaisten muutosten vuoksi.",
        "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",
        "rcfilters-state-message-subset": "Tällä suodattimella ei ole vaikutusta, koska sen tulokset sisältyvät {{PLURAL:$2|seuraavaan laajempaan suodattimeen|seuraaviin laajempiin suodattimiin}} (kokeile korostusta sen erottamiseksi): $1",
        "rcfilters-state-message-fullcoverage": "Tässä ryhmässä kaikkien suodattimien valitseminen on sama, kuin ei valitse mitään, joten tällä suodattimella ei ole vaikutusta. Ryhmään sisältyy: $1",
        "rcfilters-filtergroup-authorship": "Muutoksen tekijä",
-       "rcfilters-filter-editsbyself-label": "Sinun tekemät muutokset",
+       "rcfilters-filter-editsbyself-label": "Sinun muutoksesi",
        "rcfilters-filter-editsbyself-description": "Tekemäsi muutokset.",
        "rcfilters-filter-editsbyother-label": "Muiden muutokset",
        "rcfilters-filter-editsbyother-description": "Muiden käyttäjien tekemät muutokset.",
        "rcfilters-filter-bots-description": "Automaattisilla työkaluilla tehdyt muokkaukset.",
        "rcfilters-filter-humans-label": "Ihminen (ei botti)",
        "rcfilters-filter-humans-description": "Ihmisten tekemät muokkaukset.",
-       "rcfilters-filtergroup-reviewstatus": "Tarkistuksen tila",
-       "rcfilters-filter-patrolled-label": "Tarkastetut",
-       "rcfilters-filter-patrolled-description": "Tarkastetut muokkaukset",
-       "rcfilters-filter-unpatrolled-label": "Ei tarkastetut",
-       "rcfilters-filter-unpatrolled-description": "Muutokset, joita ei ole tarkastettu",
+       "rcfilters-filtergroup-reviewstatus": "Sivun partioinnin status",
+       "rcfilters-filter-patrolled-label": "Partioidut",
+       "rcfilters-filter-patrolled-description": "Muokkaukset, jotka on merkitty partioiduiksi.",
+       "rcfilters-filter-unpatrolled-label": "Ei ole partioitu",
+       "rcfilters-filter-unpatrolled-description": "Muutokset, joita ei ole merkitty partioiduiksi.",
        "rcfilters-filtergroup-significance": "Merkitys",
        "rcfilters-filter-minor-label": "Pienet muutokset",
        "rcfilters-filter-minor-description": "Muokkaukset, jotka on merkitty pieniksi.",
        "rcfilters-typeofchange-conflicts-hideminor": "\"Muutoksen tyyppi\" on ristiriidassa \"Pienet muutokset\" -suodattimen kanssa. Joitain muutostyyppejä ei voida merkitä \"pieniksi\".",
        "rcfilters-filtergroup-lastRevision": "Viimeisimmät versiot",
        "rcfilters-filter-lastrevision-label": "Viimeisin versio",
-       "rcfilters-filter-lastrevision-description": "Vain viimeisin muutos sivulle.",
+       "rcfilters-filter-lastrevision-description": "Vain viimeisin muutos sivuun.",
        "rcfilters-filter-previousrevision-label": "Ei viimeisin muutos",
        "rcfilters-filter-previousrevision-description": "Kaikki muutokset, jotka eivät ole viimeisin versio.",
        "rcfilters-filter-excluded": "Poissuljettu",
        "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.",
+       "uploadnologintext": "Ole hyvä ja $1, jotta voit tallentaa tiedostoja.",
        "upload_directory_missing": "Tallennushakemisto $1 puuttuu, eikä palvelin pysty luomaan sitä.",
        "upload_directory_read_only": "Palvelimella ei ole kirjoitusoikeuksia tallennushakemistoon $1.",
        "uploaderror": "Tallennusvirhe",
        "uploadstash-bad-path": "\nPolkua ei ole.",
        "uploadstash-bad-path-invalid": "Polku ei kelpaa.",
        "uploadstash-bad-path-unknown-type": "Tuntematon tyyppi \"$1\".",
+       "uploadstash-bad-path-bad-format": "Avain \"$1\" ei ole sopivassa muodossa.",
+       "uploadstash-file-not-found": "Avainta \"$1\" ei löytynyt kätköstä.",
+       "uploadstash-file-not-found-no-object": "Ei voitu luoda paikallista tiedostokohdetta pienoiskuvalle.",
+       "uploadstash-file-not-found-missing-content-type": "Puuttuva sisältötyypin ylätunniste",
        "uploadstash-not-logged-in": "Käyttäjää ei ole kirjautunut sisään, tiedostojen on kuuluttava käyttäjille.",
+       "uploadstash-wrong-owner": "Tämä tiedosto ($1) ei kuulu nykyiselle käyttäjälle.",
        "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.",
        "apisandbox-results-error": "Tapahtui virhe ladattaessa API-kyselyn vastausta: $1",
        "apisandbox-request-format-url-label": "URL-kyselymerkkijono",
        "apisandbox-request-url-label": "Pyynnön URL",
+       "apisandbox-request-json-label": "Pyydetty JSON:",
        "apisandbox-request-time": "Pyyntöön kulunut aika: {{PLURAL:$1|$1 ms}}",
        "apisandbox-results-fixtoken": "Korjaa \"token\" ja lähetä uudelleen",
        "apisandbox-alert-page": "Tällä sivulla olevat kentät eivät ole kelvollisia.",
        "booksources-text": "Alla linkkejä ulkopuolisiin sivustoihin, joilla myydään uusia ja käytettyjä kirjoja. Sivuilla voi myös olla lisätietoa kirjoista.",
        "booksources-invalid-isbn": "Annettu ISBN-numero ei ole kelvollinen. Tarkista alkuperäisestä lähteestä kirjoitusvirheiden varalta.",
        "magiclink-tracking-rfc": "Sivut, jotka käyttävät RFC-taikalinkkejä",
+       "magiclink-tracking-rfc-desc": "Tämä sivu käyttää RFC-taikalinkkejä. Katso sivulta [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Magic_links mediawiki.org] miten siirtyä käyttämään.",
        "magiclink-tracking-pmid": "Sivut, jotka käyttävät PMID-taikalinkkejä",
+       "magiclink-tracking-pmid-desc": "Tämä sivu käyttää PMID-taikalinkkejä. Katso sivulta\n[https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Magic_links mediawiki.org] miten siirtyä käyttämään.",
        "magiclink-tracking-isbn": "Sivut, jotka käyttävät ISBN-taikalinkkejä",
+       "magiclink-tracking-isbn-desc": "Tämä sivu käyttää ISBN-taikalinkkejä. Katso sivulta [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Magic_links mediawiki.org] miten siirtyä käyttämään.",
        "specialloguserlabel": "Suorittaja:",
        "speciallogtitlelabel": "Kohde (sivu tai {{ns:user}}:käyttäjänimi):",
        "log": "Lokit",
        "allinnamespace": "Kaikki sivut nimiavaruudessa $1",
        "allpagessubmit": "Hae",
        "allpagesprefix": "Sivut, jotka alkavat etuliitteellä:",
-       "allpagesbadtitle": "Annettu otsikko oli kelvoton tai siinä oli wikien välinen etuliite.",
+       "allpagesbadtitle": "Annettu sivun nimi oli kelvoton tai siinä oli etuliite, joka tarkoittaa toista wikiprojektia tai toisenkielistä wikiä.\nSivun nimessä on voinut olla yksi tai useampia merkkejä, jotka eivät ole sallittuja sivun nimessä.",
        "allpages-bad-ns": "{{GRAMMAR:inessive|{{SITENAME}}}} ei ole nimiavaruutta ”$1”.",
        "allpages-hide-redirects": "Piilota ohjaukset",
        "cachedspecial-viewing-cached-ttl": "Tarkastelet arkistoitua versiota tästä sivusta, joka voi olla jopa $1 vanha.",
        "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.",
        "rollback-success": "Käyttäjän {{GENDER:$3|$1}} tekemät muokkaukset kumottiin ja sivu palautettiin käyttäjän {{GENDER:$4|$2}} versioon.",
        "rollback-success-notify": "Kumottiin käyttäjän $1 muokkaukset; palautettiin viimeiseen käyttäjän $2 versioon. [$3 Näytä muutokset]",
        "sessionfailure-title": "Istuntovirhe",
-       "sessionfailure": "Näyttää siltä, että tämänhetkisessä istunnossasi on jokin ongelma. \nTämä toiminto on peruutettu varotoimena, jotta estetään istunnon kaappaaminen.\nMene aikaisemmalle sivulle ja päivitä se. Yritä sitten uudelleen.",
+       "sessionfailure": "Näyttää siltä, että tämänhetkisessä istunnossasi on jokin ongelma; \ntämä toiminto on peruutettu varotoimena istunnon kaappaamisen estämiseksi.\nLähetä lomake uudelleen.",
        "changecontentmodel": "Muuta sivun sisältömallia",
        "changecontentmodel-legend": "Muuta sisältömallia",
        "changecontentmodel-title-label": "Sivun otsikko",
        "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",
        "tag-mw-removed-redirect-description": "Muokkaukset, jotka muuttavat olemassa olevan uudelleenohjauksen ei-uudelleenohjaukseksi",
        "tag-mw-changed-redirect-target": "Uudelleenohjauksen kohde muutettu",
        "tag-mw-changed-redirect-target-description": "Muokkaukset, jotka muuttavat uudelleenohjauksen kohdetta",
+       "tag-mw-blank": "Tyhjennys",
        "tag-mw-blank-description": "Muokkaukset, jotka tyhjentävät sivun",
        "tag-mw-replace": "Korvattu",
        "tag-mw-replace-description": "Muokkaukset, joissa on poistettu yli 90 % sivun sisällöstä",
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 4168900..f72ffcc 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 !",
        "expansion-depth-exceeded-category-desc": "La page dépasse la profondeur d’expansion maximale.",
        "expansion-depth-exceeded-warning": "Page dépassant la profondeur d’expansion maximale",
        "parser-unstrip-loop-warning": "Boucle non dépilable détectée",
-       "parser-unstrip-recursion-limit": "Limite de récursion non dépilable dépassée ($1)",
+       "unstrip-depth-warning": "Limite de récursion non dépilable dépassée ($1)",
+       "unstrip-depth-category": "Pages où la limite de profondeur de développement est dépassée",
+       "unstrip-size-warning": "Limite de taille de développement dépassée ($1)",
+       "unstrip-size-category": "Pages où la limite de taille de développement est dépassée",
        "converter-manual-rule-error": "Erreur détectée dans la règle manuelle de conversion de langue",
        "undo-success": "Cette modification va être annulée.\nVeuillez vérifier les différences ci-dessous, puis publier l’annulation si c’est bien ce que vous voulez faire.",
        "undo-failure": "Cette modification ne peut pas être défaite : cela entrerait en conflit avec les modifications intermédiaires.",
        "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 :",
        "limitreport-templateargumentsize-value": "$1/$2 {{PLURAL:$2|octet|octets}}",
        "limitreport-expansiondepth": "Profondeur d’expansion maximale",
        "limitreport-expensivefunctioncount": "Nombre de fonctions d’analyse coûteuses",
+       "limitreport-unstrip-depth": "Profondeur de récursion de développement",
+       "limitreport-unstrip-depth-value": "$1/$2",
+       "limitreport-unstrip-size": "Taille de développement après expansion",
+       "limitreport-unstrip-size-value": "$1/$2 {{PLURAL:$2|octet|octets}}",
        "expandtemplates": "Expansion des modèles",
        "expand_templates_intro": "Cette page spéciale accepte un texte wiki et réalise récursivement l’expansion de tous les modèles qu’il contient.\nElle réalise aussi l’expansion des fonctions d’analyse prises en charge telles que\n<code><nowiki>{{</nowiki>#language:...}}</code> et des variables telles que\n<code><nowiki>{{</nowiki>CURRENTDAY}}</code>.\nEn fait, elle réalise l’expansion de pratiquement tout ce qui est encadré par des doubles accolades.",
        "expand_templates_title": "Titre de la page, si le code utilise {{FULLPAGENAME}}, etc. :",
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..ee9842a 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 !",
        "expansion-depth-exceeded-category-desc": "La pâge dèpâsse la provondior maximon d’èxpension.",
        "expansion-depth-exceeded-warning": "Pâge que dèpâsse la provondior d’èxpension",
        "parser-unstrip-loop-warning": "Boclla pas dèmontâbla dècelâye",
-       "parser-unstrip-recursion-limit": "Limita de rècursion pas dèmontâbla dèpassâye ($1)",
+       "unstrip-depth-warning": "Limita de rècursion pas dèmontâbla dèpassâye ($1)",
        "converter-manual-rule-error": "Fôta dècelâye dedens la règlla de convèrsion manuâla de lengoua",
        "undo-success": "Lo changement pôt étre dèfêt.\nSe vos plét, controlâd la comparèson ce-desot por vos assurar qu’o est franc cen que vos voléd fâre, et pués encartâd los changements por chavonar sa dèfêta.",
        "undo-failure": "Lo changement at pas possu étre dèfêt a côsa d’una disputa avouéc des changements entèrmèdièros.",
        "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..930822a 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!",
        "expansion-depth-exceeded-category-desc": "Detdiar sidj hää tuföl ütjwidjangen.",
        "expansion-depth-exceeded-warning": "Detdiar sidj hää tuföl ütjwidjangen (expansion)",
        "parser-unstrip-loop-warning": "Diar as en jinsidjag ferwisang",
-       "parser-unstrip-recursion-limit": "Tuföl jinsidjag ferwisangen bi $1",
+       "unstrip-depth-warning": "Tuföl jinsidjag ferwisangen bi $1",
        "converter-manual-rule-error": "Bi't manuel reegel för't spriakferanrang lääpt wat skiaf.",
        "undo-success": "Detdiar feranrang koon turag nimen wurd. \nLuke oner, of dü det uk würelk du wel, an do seekre din feranrangen.",
        "undo-failure": "Det feranrang küd ei stregen wurd, auer di kirew uuntesken feranert wurden as.",
        "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..fd82600 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!",
        "expansion-depth-exceeded-category-desc": "Tha an leudachadh san duilleag nas doimhne na tha ceadaichte.",
        "expansion-depth-exceeded-warning": "Bha an leudachadh san duilleag na bu dhoimhne na tha ceadaichte",
        "parser-unstrip-loop-warning": "Mhothaich sinn do lùb unstrip",
-       "parser-unstrip-recursion-limit": "Chaidheas thairis air crìoch unstrip recursion ($1)",
+       "unstrip-depth-warning": "Chaidheas thairis air crìoch unstrip recursion ($1)",
        "converter-manual-rule-error": "Mhothaich sinn do mhearachd san riaghailt iompachadh làimhe airson cànan",
        "undo-success": "Gabhaidh an deasachadh seo a neo-dhèanamh.\nThoir sùil air a' choimeas gu h-ìosal is dearbhaich gur e sin a tha fa-near dhut agus sàbhail na h-atharraichean gu h-ìosal gus neo-dhèanamh an deasachaidh a choileanadh.",
        "undo-failure": "Cha b' urrainn dhuinn an deasachadh a neo-dhèanamh air sgàth 's gun robh deasachaidhean eile sa mheadhan.",
        "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..efb5db1 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!",
        "expansion-depth-exceeded-category-desc": "A páxina supera a profundidade de expansión máxima.",
        "expansion-depth-exceeded-warning": "A páxina supera a profundidade de expansión",
        "parser-unstrip-loop-warning": "Detectouse un bucle inamovible",
-       "parser-unstrip-recursion-limit": "Excedeuse o límite de recursión inamovible ($1)",
+       "unstrip-depth-warning": "Excedeuse o límite de recursión inamovible ($1)",
        "converter-manual-rule-error": "Detectouse un erro na regra manual de conversión da lingua",
        "undo-success": "A edición pódese desfacer.\nComprobe a comparación que aparece a continuación para confirmar que isto é o que desexa facer; despois, garde os cambios para desfacer a edición.",
        "undo-failure": "Non se pode desfacer a edición debido a un conflito con algunha das edicións intermedias.",
        "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 a56016a..3f4c1ab 100644 (file)
@@ -37,7 +37,7 @@
        "tog-shownumberswatching": "Popobilohe jumula lo ta he'awasiyalo",
        "tog-oldsig": "Pali lo ulu'umu masatiya",
        "tog-fancysig": "Popopasiya pali lo'ulu'u odelo tuladuwiki (diyalu tuwawu pranala otomatis)",
-       "tog-uselivepreview": "Popohunawa mopobilohu langsung",
+       "tog-uselivepreview": "Popobilohe pratayang wawu ja detohe ulangi halaman",
        "tog-forceeditsummary": "Popo'eelawa wa'u wonu dosi monguba diipo otuwa",
        "tog-watchlisthideown": "Wantoa u iluba'u to daputari lo he'awasiyalo",
        "tog-watchlisthidebots": "Wanto'a u iluba lo bot to daputari lo he'awasiyalo",
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..19c3af1 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!",
        "expansion-depth-exceeded-category-desc": "D Syte iberschrytet di maximal Expandierigstiefi.",
        "expansion-depth-exceeded-warning": "Die Syte het d Expansionsdiefi überschritte.",
        "parser-unstrip-loop-warning": "Zirkelbezug festgstellt",
-       "parser-unstrip-recursion-limit": "Rekursionsgränz bim Ufflöse überschritte ($1)",
+       "unstrip-depth-warning": "Rekursionsgränz bim Ufflöse überschritte ($1)",
        "converter-manual-rule-error": "Bi dr manuälle Sprochkonvertierigsregle isch e Fähler entdeckt wore.",
        "undo-success": "Zum die Änderig ruckgängig z mache, kontrollier bitte d Bearbeitig in dr Verglichsaasicht un druck derno uf „Syte spichere“.",
        "undo-failure": "D Änderig het nid chenne ruckgängig gmacht wäre, wel dää Abschnitt mittlerwyli gänderet woren isch.",
        "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 8f7f434..5ed594b 100644 (file)
        "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:|שלך|שלך|שלכם}} עדיין לא נשמרו!",
        "expansion-depth-exceeded-category-desc": "עומק ההרחבה בדף גדול מהעומק המרבי.",
        "expansion-depth-exceeded-warning": "עומק ההרחבה בדף גדול מדי",
        "parser-unstrip-loop-warning": "נמצאה לולאה בפריסה",
-       "parser-unstrip-recursion-limit": "עומק הרקורסיה של הפריסה עבר את המגבלה ($1)",
+       "unstrip-depth-warning": "עומק הרקורסיה של הפריסה עבר את המגבלה ($1)",
        "converter-manual-rule-error": "התגלתה שגיאה בכלל המרת שפה ידני",
        "undo-success": "ניתן לבטל את העריכה.\nאנא {{GENDER:|בדוק|בִדקי|בִדקו}} את השוואת הגרסאות שלהלן כדי לוודא שזה אכן מה ש{{GENDER:|אתה רוצה|את רוצה|אתם רוצים}} לעשות, ולאחר מכן {{GENDER:|שמור|שִמרי|שִמרו}} את השינויים למטה כדי לסיים את ביטול העריכה.",
        "undo-failure": "לא ניתן היה לבטל את העריכה עקב התנגשות עם עריכות מאוחרות יותר.",
        "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..14163b6 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आपके बदलाव अभी तक संजोये नहीं गए हैं!",
        "expansion-depth-exceeded-category-desc": "यह पृष्ठ विस्तार गहराई पार करता है।",
        "expansion-depth-exceeded-warning": "पृष्ठ में विस्तार गहराई पार की गई है",
        "parser-unstrip-loop-warning": "Unstrip लूप पाया गया",
-       "parser-unstrip-recursion-limit": "Unstrip पुनरावर्तन सीमा पार की गई ($1)",
+       "unstrip-depth-warning": "Unstrip पुनरावर्तन सीमा पार की गई ($1)",
        "converter-manual-rule-error": "मैन्यूअल भाषा परिवर्तन नियम में त्रुटि",
        "undo-success": "यह संपादन पूर्ववत किया जा सकता है।\nऐसा करने के लिये कृपया निम्नलिखित पाठ को ध्यान से देखकर बदलाव संजोयें।",
        "undo-failure": "इस बीच अन्य बदलाव होने के कारण यह संपादन पूर्ववत करना संभव नहीं है।",
        "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..f470fdd 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!",
        "expansion-depth-exceeded-category-desc": "Panna, maximum expansion depth, se jaada hae.",
        "expansion-depth-exceeded-warning": "Panna expansion depth ke exceed karis hae",
        "parser-unstrip-loop-warning": "Unstrip loop ke pawa gai shae",
-       "parser-unstrip-recursion-limit": "Unstrip recursion limit ke exceed karaa gais hae ($1)",
+       "unstrip-depth-warning": "Unstrip recursion limit ke exceed karaa gais hae ($1)",
        "converter-manual-rule-error": "Bhasa ke anuwaad kare waala niyam me galti hae",
        "undo-success": "Ii badlao ke pahile jaise karaa jaae sake hai.\nNiche ke comparison ke check kar ke dekho ki aap yahi kare mangta rahaa, aur fir niche ke badlao ke save kar ke aapan badlao ke pahile jaise karo.",
        "undo-failure": "Ii badalo ke paile jaise nai karaa jaae sake hai kahe ki biich me badlao 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..e0eacc7 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!",
        "expansion-depth-exceeded-category": "Mga panid kon diin ang kadalumon sang pagpalapad nagsobra na",
        "expansion-depth-exceeded-warning": "Ang panid nagsobra na sa kadalumon sang pagpalapad",
        "parser-unstrip-loop-warning": "May loop nga unstrip nga nakita",
-       "parser-unstrip-recursion-limit": "Naglapaw ka na sa ginapasugtan nga pagliwat-liwat ($1)",
+       "unstrip-depth-warning": "Naglapaw ka na sa ginapasugtan nga pagliwat-liwat ($1)",
        "converter-manual-rule-error": "May sala nga nakita sa mano-mano nga pagsulunsan sa pagbadbad sang lenggwahe",
        "undo-success": "Ang pag-ilis indi mahuman.\nPalihog lantaw sang pagbaki-baki sa dalom agod to nga mapamatud-an kon amo gid man sini ang imo luyag buhaton, dayon tipona ang mga pagbag-o sa dalom agod to nga matapos ang pagliwat sang pag-ilis.",
        "undo-failure": "Ang pag-ilis indi na maliwat pa tungod sang nagakonplikto nga mga pang-tunga nga pag-ilis.",
        "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..fa38fca 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!",
        "expansion-depth-exceeded-category": "Stranice s prevelikom dubinom proširenja",
        "expansion-depth-exceeded-warning": "Na ovoj stranici dubina proširenja je prevelika",
        "parser-unstrip-loop-warning": "Nedopuštena petlja",
-       "parser-unstrip-recursion-limit": "Dosegnuto je ograničenje rekurzije ($1)",
+       "unstrip-depth-warning": "Dosegnuto je ograničenje rekurzije ($1)",
        "converter-manual-rule-error": "Pronađena je pogrješka u pravilu ručnog prijevoda",
        "undo-success": "Izmjenu je moguće ukloniti.\nMolimo Vas, usporedite niže navedene razlike u inačicama kako biste bili sigurni da ovo zaista želite učiniti, te sačuvajte stranicu i izmjene će biti uklonjene.",
        "undo-failure": "Ova izmjena ne može biti uklonjena zbog postojanja međuinačica.",
        "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..6241578 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!",
        "expansion-depth-exceeded-category": "Seite, wo die Expansionstiefe überschritt honn",
        "expansion-depth-exceeded-warning": "Die Seit hot die Expansionstiefe üwerschritt.",
        "parser-unstrip-loop-warning": "Zirkelbezuch festgestellt",
-       "parser-unstrip-recursion-limit": "Rekursionsgrenz beim Ufflöse üwerschritt ($1)",
+       "unstrip-depth-warning": "Rekursionsgrenz beim Ufflöse üwerschritt ($1)",
        "converter-manual-rule-error": "Bei der manuelle Sprochkonvertierungsrechel woard en Fehler entdeckt.",
        "undo-success": "Die Beoorbeitung kann rückgänchig gemacht werre.\nBittschön prüf den Vergleich do unne, um sicherzustelle, dass du das tun möchst, und speichre dann do unne deine Ändrunge, um die Beoorbeitung rückgängig zu mache.",
        "undo-failure": "Die Ännrung konnt net rückgängich gemacht sin, weil der betroffne Abschnitt zwischenzeitlich verännert woard.",
        "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..185a60d 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!",
        "expansion-depth-exceeded-category-desc": "Strona překroša maksimalnu ekspansisku hłubokosć.",
        "expansion-depth-exceeded-warning": "Strona je ekspansisku hłubokosć překročił",
        "parser-unstrip-loop-warning": "Njeskónčna sekla namakana",
-       "parser-unstrip-recursion-limit": "Rekursiska hranica překročena ($1)",
+       "unstrip-depth-warning": "Rekursiska hranica překročena ($1)",
        "converter-manual-rule-error": "Zmylk w manuelnym prawidle rěčneho konwertowanja namakany",
        "undo-success": "Wersija je so wuspěšnje wotstroniła. Prošu přepruwuj deleka w přirunanskim napohledźe, hač twoja změna bu přewzata a klikń potom na „Składować”, zo by změnu składował.",
        "undo-failure": "Wobdźěłanje njehodźeše so wotstronić, dokelž wotpowědny wotrězk bu mjeztym změnjeny.",
        "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 174e576..a38b78c 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!",
        "expansion-depth-exceeded-category-desc": "Az oldal meghaladja a maximális expanziós mélységet.",
        "expansion-depth-exceeded-warning": "A lap meghaladta az engedélyezett kiterjesztési mélységet",
        "parser-unstrip-loop-warning": "Unstrip hurok észlelve",
-       "parser-unstrip-recursion-limit": "Túl mély unstrip rekurzió: $1",
+       "unstrip-depth-warning": "Túl mély unstrip rekurzió: $1",
        "converter-manual-rule-error": "Hiba van a kézi nyelvi konverziós szabályban",
        "undo-success": "A szerkesztés visszavonható. Kérlek ellenőrizd alább a változásokat, hogy valóban ezt szeretnéd-e tenni, majd kattints a lap mentése gombra a visszavonás véglegesítéséhez.",
        "undo-failure": "A szerkesztést nem lehet automatikusan visszavonni vele ütköző későbbi szerkesztések miatt.",
        "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:",
index a16a994..2383ede 100644 (file)
        "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..4910918 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!",
        "expansion-depth-exceeded-category-desc": "Le pagina excede le profunditate de expansion maxime.",
        "expansion-depth-exceeded-warning": "Le profunditate de expansion in iste pagina excede le limite",
        "parser-unstrip-loop-warning": "Bucla de \"unstrip\" detegite",
-       "parser-unstrip-recursion-limit": "Limite de recursion de \"unstrip\" excedite ($1)",
+       "unstrip-depth-warning": "Limite de recursion de \"unstrip\" excedite ($1)",
        "converter-manual-rule-error": "Error detegite in le regula manual de conversion de lingua",
        "undo-success": "Le modification pote esser disfacite.\nPer favor controla le comparation infra pro verificar que tu vole facer isto, e postea salveguarda le modificationes infra pro assi disfacer le modification.",
        "undo-failure": "Le modification non poteva esser annullate a causa de conflicto con modificationes intermedie.",
        "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..4e58912 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!",
        "expansion-depth-exceeded-category-desc": "Halaman yang melebihi kedalaman luas maksimum.",
        "expansion-depth-exceeded-warning": "Page exceeded the expansion depth",
        "parser-unstrip-loop-warning": "Unstrip loop detected",
-       "parser-unstrip-recursion-limit": "Unstrip recursion limit exceeded ($1)",
+       "unstrip-depth-warning": "Unstrip recursion limit exceeded ($1)",
        "converter-manual-rule-error": "Kesalahan terdeteksi di aturan manual konversi bahasa",
        "undo-success": "Suntingan ini dapat dibalikkan. Silakan periksa perbandingan di bawah untuk meyakinkan bahwa benar itu yang Anda ingin lakukan, lalu simpan perubahan tersebut untuk menyelesaikan pembalikkan suntingan.",
        "undo-failure": "Suntingan ini tidak dapat dibalikkan karena konflik penyuntingan antara.",
        "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..7af5424 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!",
        "expansion-depth-exceeded-category-desc": "Ti panid ket nasurokanna ti kaaduan ti kauneg ti panagpadakkel.",
        "expansion-depth-exceeded-warning": "Ti panid ket nasurokanna ti kauneg ti panagpadakkel",
        "parser-unstrip-loop-warning": "Nakaduktal ti di-naukisan a silo",
-       "parser-unstrip-recursion-limit": "Nalabsan ti patingga ti panagdullit ti di-naukisan ($1)",
+       "unstrip-depth-warning": "Nalabsan ti patingga ti panagdullit ti di-naukisan ($1)",
        "converter-manual-rule-error": "Adda biddut a naduktalan iti manual nga alagaden ti panagbalbaliw ti pagsasao",
        "undo-success": "Mabalin a maisubli ti panagurnos.\nPangngaasi a kitaen ti panangiyasping dita baba tapno maammuan no daytoy ti kayatmo nga aramiden, ken kalpasanna idulin dagiti sinukatan dita baba tapno malpas ti panangisubli ti inurnos.",
        "undo-failure": "Ti inurnos ket saan a maipasubli gapu ta adda dagiti nakisinnungat a patingnga a naurnos.",
        "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..7e2f8e6 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:",
        "hiddencategories": "Ca pagino esas membro di {{PLURAL:$1|1 celita kategorio|$1 celita kategorii}}:",
        "permissionserrors": "Eroro permisal",
        "permissionserrorstext-withaction": "Vu ne darfas $2, pro la {{PLURAL:$1|kauzo|kauzi}} sequanta:",
-       "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.",
+       "recreate-moveddeleted-warn": "<strong>Atencez: Vu rikreos pagino qua antee efacesis.</strong>\n\nVu mustas konsiderar se esos konvenanta o ne riskribor ol.\nPor vua konoco, la motivo dil antea efaco montresas hike:",
+       "moveddeleted-notice": "Ica pagino efacesis.\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",
        "content-json-empty-array": "vakua tabelo",
-       "deprecated-self-close-category": "Pages using invalid self-closed HTML tags",
+       "deprecated-self-close-category": "Pagini qui uzas nevalida etiketi HTML por klozajo",
        "undo-failure": "Ne povis nuligar la redakto pro konflikti kun intermeza redakti.",
        "viewpagelogs": "Videz registrari por ca pagino",
        "nohistory": "Ne esas redakto-historio por ica pagino.",
        "search-interwiki-more": "(plusa)",
        "searchall": "omna",
        "showingresults": "Montrante infre {{PLURAL:$1|'''1''' rezulto|'''$1''' rezulti}}, qui komencas kun numero #'''$2'''.",
+       "showingresultsinrange": "Infre montresas {{PLURAL:$1|<strong>1</strong> rezulto|<strong>$1</strong> rezulti}}, en l'intervalo #<strong>$2</strong> til #<strong>$3</strong>.",
        "search-showingresults": "{{PLURAL:$4|Rezulto <strong>$1</strong> de <strong>$3</strong>|rezulti <strong>$1 – $2</strong> de <strong>$3</strong>}}",
        "search-nonefound": "Nula rezulto trovesis por lua serchado.",
        "powersearch-legend": "Avancita sercho",
        "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-show-new-changes": "Videz la maxim recenta chanji",
+       "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",
+       "pageswithprop": "Pagini kun atributo di pagino",
+       "pageswithprop-legend": "Pagini kun atributo di pagino",
+       "pageswithprop-text": "Ica pagino listas pagini qui havas partikulara propraji.",
        "doubleredirects": "Duopla ridirektili",
        "double-redirect-fixer": "Reparar ridirekti",
        "brokenredirects": "Ridirektili nekorekta",
        "booksources": "Fonti di libri",
        "booksources-search-legend": "Serchez librala fonti",
        "booksources-search": "Serchar",
+       "magiclink-tracking-isbn": "Pagini qui uzas ligili ISBN",
        "specialloguserlabel": "Agero:",
        "speciallogtitlelabel": "Skopo (titulo od {{ns:user}}:uzernomo por uzero):",
        "log": "Registrari",
        "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)",
        "deletepage": "Efacar pagino",
        "confirm": "Konfirmar",
        "excontent": "La kontenajo esis: '$1'",
+       "excontentauthor": "la kontenajo esis: \"$1\", e l'unika redaktero esis \"[[Special:Contributions/$2|$2]]\" ([[User talk:$2|talk]])",
        "exbeforeblank": "La kontenajo ante efaco esis: '$1'",
        "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",
        "editcomment": "La rezumo di la redakto esis: <em>$1</em>.",
        "revertpage": "Desfacita redakti da [[Special:Contributions/$2|$2]] ([[User talk:$2|Debato]]) e rekuperita la lasta redakto da [[User:$1|$1]]",
        "rollback-success": "Desfacis redakti da $1;\nrestauris ad lasta versiono da $2.",
+       "sessionfailure": "Semblas ke eventis problemo kun vua sesiono di 'login';\nta agado abrogesis, quale presorgo kontre sequestro di sesiono ('hijacking').\nVoluntez risendar la formulario, plenigita.",
        "protectlogpage": "Protekto-registraro",
        "protectedarticle": "protektita \"[[$1]]\"",
        "modifiedarticleprotection": "la nivelo di protekto modifikesis a \"[[$1]]\"",
        "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'",
        "move-page": "Movar $1",
        "move-page-legend": "Rinomizar pagino",
        "movepagetext": "Uzante ica formularo onu povas rinomizar pagino, movante olua omna versionaro ad la nova titulo.\nLa antea titulo konvertesos a ridirektilo a la nova titulo.\nLa ligili a la antea titulo dil pagino ne chanjesos.\nVoluntez certigar ke ne esas [[Special:DoubleRedirects|duopla]] o [[Special:BrokenRedirects|ruptota ridirektili]].\nVu responsas ke la ligili duros direktante a la pagino korespondanta.\n\nMemorez ke la pagino '''ne''' rinomizesos se ja existus pagino kun la nova titulo, eceptuante ke la pagino esas vakua o ridirektilo sen versionaro.\nIco signifikas ke vu povos rinomizar pagino a olua originala titulo se eroras skribante la nova titulo, ma ne povos riskribar existanta pagino.\n\n'''EGARDEZ!'''\nIca povas esar drastika chanjo e ne-esperinda por populara pagino;\nvoluntez certigar ke vu komprenas la konsequi qui eventos ante durar adavane.",
+       "movepagetext-noredirectfixer": "Uzar la formulario infre rinomizos la pagino, e tota lua historio-listo a la nova nomo.\nL'anciena titulo ridirektesos a la nova titulo.\nVerifikez la [[Special:DoubleRedirects|duopla]] e/o la [[Special:BrokenRedirects|krevita ridirekti]].\n<strong>Esas vua responso verifikar se omna ligili esas korekta.</strong>\n\nVidez ke la pagino <strong>ne rinomizesos se existar pagino kun la sama titulo</strong>, ecepte se ol ridirektesas a la prezenta pagino e ne havas pasinta historio pri redaktado.\nTo signifikas ke vu povas retroe rinomizar pagino a lua antea nomo se ol rinomizesis erore, e ke vu ne povas supresar existanta pagino per ridirektado di altra pagino.\n\n<strong>Atencez:</strong>\nLa rinomizo povas esar drastika chanjo por pagini qui esas populara;\nhavez klara certezo pri la konsequi di la posibla rinomizo di la pagino, ante facar ol!",
        "movenologintext": "Vu mustas esar registragita uzero ed [[Special:UserLogin|enirir]] por rinomizar pagino.",
        "newtitle": "Nova titulo:",
        "move-watch": "Surveyar ca pagino",
        "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",
        "pageinfo-robot-index": "Permisita",
        "pageinfo-robot-noindex": "Interdiktata",
        "pageinfo-watchers": "Quanto di personi qui vidis ca pagino",
+       "pageinfo-visiting-watchers": "Quanto di viziteri qui vidis recenta redakturi",
        "pageinfo-few-watchers": "Min kam $1 {{PLURAL:$1|vidinto|vidinti}}",
        "pageinfo-redirects-name": "Quanto di ridirekti ad ica pagino",
        "pageinfo-subpages-name": "Quanto di subpagini en ica pagino",
        "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 3a54234..434a6a8 100644 (file)
        "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ð!'''",
        "expansion-depth-exceeded-category": "Þær síður þar sem farið er út fyrir leyfða dýpt útvíkkunar",
        "expansion-depth-exceeded-warning": "Síðan fer út fyrir leyfða dýpt útvíkkunar",
        "parser-unstrip-loop-warning": "\"Unstrip\" lykkja fannst",
-       "parser-unstrip-recursion-limit": "Farið út fyrir „unstrip“ endurkvæmnismörk ($1)",
+       "unstrip-depth-warning": "Farið út fyrir „unstrip“ endurkvæmnismörk ($1)",
        "converter-manual-rule-error": "Villa í reglu handvirks tungumálabreytis",
        "undo-success": "Það er hægt að afturkalla breytinguna.\nAthugaðu vel samanburðinn hér fyrir neðan til að sannreyna að það sé það sem þú vilt gera, vistaðu svo breytingarnar hér fyrir neðan til að ljúka afturköllun breytinganna.",
        "undo-failure": "Breytinguna var ekki hægt að taka tilbaka vegna breytinga í millitíðinni.",
        "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:",
index 44c3da9..d827833 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!",
        "expansion-depth-exceeded-category-desc": "La pagina supera la profondità massima di espansione.",
        "expansion-depth-exceeded-warning": "Questa pagina ha superato la profondità di espansione",
        "parser-unstrip-loop-warning": "Rilevato ciclo di Unstrip",
-       "parser-unstrip-recursion-limit": "Superati i limiti di ricorsione di Unstrip ($1)",
+       "unstrip-depth-warning": "Superati i limiti di ricorsione di Unstrip ($1)",
        "converter-manual-rule-error": "Rilevato errore nella regola manuale di conversione della lingua",
        "undo-success": "Questa modifica può essere annullata.\nControlla le differenze mostrate sotto fra le due versioni per essere certo che il contenuto corrisponda a quanto desiderato, e quindi salvare le modifiche per completare la procedura di annullamento.",
        "undo-failure": "Impossibile annullare la modifica a causa di un conflitto con modifiche intermedie.",
        "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",
        "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..d20092c 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",
        "expansion-depth-exceeded-category-desc": "展開の深さが上限を超えたページ。",
        "expansion-depth-exceeded-warning": "ページが展開の深さ制限を超えました",
        "parser-unstrip-loop-warning": "unstrip のループを検出しました",
-       "parser-unstrip-recursion-limit": "unstrip の再帰 ($1) が上限を超えました",
+       "unstrip-depth-warning": "unstrip の再帰 ($1) が上限を超えました",
        "converter-manual-rule-error": "手動の言語変換規則でエラーを検出しました。",
        "undo-success": "この編集を取り消せます。\n下記の差分を確認して、本当に取り消していいか検証してください。よろしければ変更を保存して取り消しを完了してください。",
        "undo-failure": "中間の版での編集と競合したため、取り消せませんでした。",
        "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..48cef66 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!",
        "expansion-depth-exceeded-category": "Kaca sing jeroné èkspansi wis punjul",
        "expansion-depth-exceeded-warning": "Kaca munculi jeroné èkspansi",
        "parser-unstrip-loop-warning": "Unstrip loop detected",
-       "parser-unstrip-recursion-limit": "Unstrip recursion limit exceeded ($1)",
+       "unstrip-depth-warning": "Unstrip recursion limit exceeded ($1)",
        "converter-manual-rule-error": "Masalah kapranggul ing aturan konvèrsi basa manual",
        "undo-success": "Besutan iki kena diwurungaké.\nTiliki bandhingan ngisor iki saperlu mesthèkaké yèn bener iki sing arep kolakoni, nuli simpen owahan ngisor iki kanggo ngiyai yèn besutané diwurungaké.",
        "undo-failure": "Besutan iki ora bisa dipulihaké amarga bisa cengkah besutan antara.",
        "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..930be78 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თქვენი ცვლილებები ჯერ არ შენახულა!",
        "expansion-depth-exceeded-category-desc": "გვერდს აქვს გადაჭარბებული გაღების სიღრმის მაქსიმალურად დასაშვები რაოდენობა",
        "expansion-depth-exceeded-warning": "გვერდზე გადამეტებულია ჩადგმების ზღვარი",
        "parser-unstrip-loop-warning": "აღმოჩენილია ციკლური ბმული",
-       "parser-unstrip-recursion-limit": "გადამეტებულია რეკურსიის ზღვარი ($1)",
+       "unstrip-depth-warning": "გადამეტებულია რეკურსიის ზღვარი ($1)",
        "converter-manual-rule-error": "შეცდომა ენის ხელით გარდაქმნის წესში",
        "undo-success": "რედაქტირების გაუქმება შესაძლებელია. გთხოვთ შეამოწმოთ განსხვავება ქვევით, რათა დარწმუნდეთ, რომ ეს ის არის რაც თქვენ გსურთ, შემდეგ კი შეინახეთ ცვლილებები რათა დაასრულოთ რედაქტირების გაუქმება.",
        "undo-failure": "რედაქტირების გაუქმება შეუძლებელია კონფლიქტური შუალედური რედაქტირებების გამო.",
        "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..657d7cb 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!",
        "expansion-depth-exceeded-category-desc": "Asebter iɛedda talqayt n temɣer tafellayt.",
        "expansion-depth-exceeded-warning": "Isebtar yefelen lqay n uderrec",
        "parser-unstrip-loop-warning": "Tifin n tineddict ur nezmer ara an sentuter",
-       "parser-unstrip-recursion-limit": "Talast n usniles ur nezmer ara an sentuter tefel ($1)",
+       "unstrip-depth-warning": "Talast n usniles ur nezmer ara an sentuter tefel ($1)",
        "converter-manual-rule-error": "Tifin n unezri deg alugen awfus n uselket n tutlayt",
        "undo-success": "Tzemreḍ ad tessefsuḍ abeddil. Ssenqed asidmer akken ad tessneḍ ayen tebɣiḍ ad txdmeḍ d ṣṣeḥ, umbeɛd smekti ibeddlen u tkemmleḍ ad tessefsuḍ abeddil.",
        "undo-failure": "Ur yezmir ara ad issefu abeddel axaṭer yella amennuɣ abusari deg ubeddel.",
        "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..d21be5a 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": "'''Мыр япэ-еплъ къуэдей, тхылъыр иджыри итхакъым!'''",
        "expansion-depth-exceeded-category": "НапэкӀуэцӀым и зыубгъуэн Ӏувагъыр къехъу щытщ",
        "expansion-depth-exceeded-warning": "НапэкӀуэцӀып и зэфӀэгъэкӀыгъуэр къегъэхъуауэ щытщ",
        "parser-unstrip-loop-warning": "Зэрыдзэгъуэ зэхуэмыщӀа къыхэкӀащ",
-       "parser-unstrip-recursion-limit": "Рекурсым и зэфӀэгъэкӀыгъуэр къегъэхъуауэ щытщ ($1)",
+       "unstrip-depth-warning": "Рекурсым и зэфӀэгъэкӀыгъуэр къегъэхъуауэ щытщ ($1)",
        "undo-success": "Гъэтэрэзыгъуэр хахыжьыфынущ. Версиэхэм я зэгъэпшыгъуэ егъэкӀуэкӀ, узхуэныкъуэхэрамэ пщӀам еплъи «НапэкӀуэцӀыр итхэн»-ым текъузэ, зэхъуэкӀыгъуэхэр хэхьэным щхьэкӀэ.",
        "undo-failure": "Гъэтэрэзыгъуэр хэхыжа хъунукъым, гъэтэрэзыгъуэхэм я зэпхыгъуэр зэремыкӀуэкӀыфым щхьэкӀэ",
        "undo-norev": "Гъэтэрэзыгъуэр хэхыжа хъунукъым зэрщымыӀэм щхьэкӀэ иэ хэхыжагъыху щытщ.",
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..95af0b1 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Өзгертулеріңіз әлі сақталған жоқ!",
        "expansion-depth-exceeded-category-desc": "Жіктелім тереңдігі шегінен асқан бет.",
        "expansion-depth-exceeded-warning": "Жіктелім тереңдігі асып кеткен бет",
        "parser-unstrip-loop-warning": "Жабылмаған тег анықталды (<pre> осындай)",
-       "parser-unstrip-recursion-limit": "Unstrip рекурсия шегінен асты ($1)",
+       "unstrip-depth-warning": "Unstrip рекурсия шегінен асты ($1)",
        "converter-manual-rule-error": "Қолмен тілді түрлендіру ережесінде қате кездесті",
        "undo-success": "Бұл өңдемені жоққа шығарымақшысыз. Алдымен алдын-ала төмендегі салыстыруды тексеріп шығыңыз да бетті сақтаңыз.",
        "undo-failure": "Бұл өңдеме жоққа шығарылмайды, себебі арада қақтығысты өңдемелер бар.",
        "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..eeb13e6 100644 (file)
@@ -68,7 +68,8 @@
                        "Sukjong0406",
                        "Garam",
                        "렌즈",
-                       "CYAN"
+                       "CYAN",
+                       "Nuevo Paso"
                ]
        },
        "tog-underline": "링크에 밑줄:",
        "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": "'''ì\9d´ í\99\94ë©´ì\9d\80 ë¯¸ë¦¬ ë³´ê¸°ì\9e\85ë\8b\88ë\8b¤.'''\ní\8e¸ì§\91í\95\9c ë\82´ì\9a©ì\9d\80 ì\95\84ì§\81 ì \80ì\9e¥í\95\98ì§\80 ì\95\8aì\95\98ì\8aµë\8b\88ë\8b¤!",
+       "previewnote": "'''ì§\80ê¸\88 ë³´ê³  ì\9e\88ë\8a\94 í\99\94ë©´ì\9d\80 ë¯¸ë¦¬ ë³´ê¸°ì\9e\85ë\8b\88ë\8b¤.''' í\8e¸ì§\91í\95\9c ë\82´ì\9a©ì\9d\80 ì\95\84ì§\81 ì \80ì\9e¥ë\90\98ì§\80 ì\95\8aì\95\98ì\8aµë\8b\88ë\8b¤!",
        "continue-editing": "편집 영역으로 가기",
        "previewconflict": "이 미리 보기는 저장할 때의 모습으로, 위쪽 편집 영역의 텍스트를 반영합니다.",
        "session_fail_preview": "세션 데이터가 없어져 편집을 저장하지 못했습니다.\n\n로그아웃되었는지도 모릅니다. <strong>아직 로그인 상태인지 확인하고 다시 시도해주세요</strong>.\n다시 시도해도 되지 않으면 [[Special:UserLogout|로그아웃]]한 다음 다시 로그인하세요. 그리고 브라우저 설정에서 쿠키 사용을 허용하는지 확인하세요.",
        "expansion-depth-exceeded-category-desc": "최대 확장 깊이를 초과하는 문서입니다.",
        "expansion-depth-exceeded-warning": "문서가 확장 깊이를 초과하였습니다",
        "parser-unstrip-loop-warning": "Unstrip의 반복을 감지했습니다",
-       "parser-unstrip-recursion-limit": "Unstrip의 재귀 한도를 초과했습니다 ($1)",
+       "unstrip-depth-warning": "Unstrip의 재귀 한도를 초과했습니다 ($1)",
        "converter-manual-rule-error": "언어 변환 규칙을 수동으로 지정하는 도중 오류",
        "undo-success": "편집을 되돌릴 수 있습니다.\n이 편집을 되돌리려면 아래의 바뀐 내용을 확인한 후 저장해주세요.",
        "undo-failure": "중간의 다른 편집과 충돌하여 이 편집을 되돌릴 수 없습니다.",
        "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..dbed1d8 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Сиз этген тюрлениуле алкъын сакъланмагъандыла!",
        "expansion-depth-exceeded-category": "Кериуню теренлиги оздурулгъан бетле",
        "expansion-depth-exceeded-warning": "Бетде ичине салыныуну чеги оздурулгъанды",
        "parser-unstrip-loop-warning": "Джабылмагъан pre табылды",
-       "parser-unstrip-recursion-limit": "Рекурсияны чеги ($1) оздурулду",
+       "unstrip-depth-warning": "Рекурсияны чеги ($1) оздурулду",
        "converter-manual-rule-error": "Тилни башха тюрлю этиуню къол джоругъунда халат",
        "undo-success": "Бу тюрлениу ызына алыныргъа боллукъду. Тилейбиз, версияланы тенглешдириуюн осмакълагъыз, керти да бу тюрлендириулени этерге излегенигизден ишексиз болугъуз, сора, тюрлениуле къабыл этилир ючюн, «Бетни къош» деген тиекден басыгъыз.",
        "undo-failure": "Бир-бирине келишмегени себебли, тюрлениу ызына алынамады.",
        "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..68897b1 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>",
        "expansion-depth-exceeded-category-desc": "Dat heh es de Saachjropp för Sigge, woh zoh vill automattesch ennjeföhsch weed.",
        "expansion-depth-exceeded-warning": "En heh di Sigg weed zoh vill automattesch ennjeföhsch.",
        "parser-unstrip-loop-warning": "Ene Befähl em Täx betrek sesch op sesch sällefs.",
-       "parser-unstrip-recursion-limit": "Ene Befähl em Täx es mieh wi {{PLURAL:$1|eijmohl|$1 Mohl|jaa nit}} met  sesch sellef verschachtelt.",
+       "unstrip-depth-warning": "Ene Befähl em Täx es mieh wi {{PLURAL:$1|eijmohl|$1 Mohl|jaa nit}} met  sesch sellef verschachtelt.",
        "converter-manual-rule-error": "Doh es ene Fähler en ene händesche Önwandelongsrääjel zwesche de Schprohche.",
        "undo-success": "De Änderong könnte mer zeröck nämme. Belohr Der de Ungerscheide un dann donn di Sigg avspeijschere, wann De dängks, et es en Ohdenong esu.",
        "undo-failure": "Dat kunnt mer nit zeröck nämme, dä Afschnedd wood enzwesche ald widder beärbeidt.",
        "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..fe009cb 100644 (file)
        "redirectedfrom": "(Ji $1 hate beralîkirin)",
        "redirectpagesub": "Rûpelê beralî bike",
        "redirectto": "Beraliyê vir bike:",
-       "lastmodifiedat": "Ev rûpel cara dawî $1, seet li $2an de hate guherandin.",
+       "lastmodifiedat": "Ev rûpel cara dawî $1, seet li $2an de hatiye guherandin.",
        "viewcount": "Ev rûpel {{PLURAL:$1|carekê|caran}} tê xwestin.",
        "protectedpage": "Rûpela parastî",
        "jumpto": "Here cem:",
        "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",
        "searchsuggest-search": "Li ser {{SITENAME}} bigere",
        "searchsuggest-containing": "dihundirîne...",
        "api-error-unknownerror": "Çewtiya nenas: \"$1\".",
-       "duration-days": "$1 {{PLURAL:$1|roj|roj}}",
+       "duration-days": "$1 {{PLURAL:$1|roj|rojan}}",
        "duration-years": "$1 {{PLURAL:$1|sal}}",
        "duration-centuries": "$1 {{PLURAL:$1|sedsal}}",
        "expand_templates_output": "Encam",
index dc85920..ef6e8ab 100644 (file)
@@ -5,6 +5,7 @@
                        "ArslanX"
                ]
        },
+       "underline-never": "Гьеч бир де",
        "sunday": "Къаттыгюн",
        "monday": "Итнигюн",
        "tuesday": "Талатгюн",
        "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",
+       "october-date": "Байсан $1",
+       "november-date": "Маййилик $1",
+       "december-date": "Тунлу $1",
        "pagecategories": "{{PLURAL:$1|Категория|Категориялар}}‎",
        "category_header": "\"$1\" категориядан сагьифалар",
        "subcategories": "Субкатегориялар",
        "category-media-header": "\"$1\" категориядагъы сапламлар",
        "category-empty": "\"Бу категория буссагьат бош.\"",
        "hidden-categories": "{{PLURAL:$1|Яшырылгъан категория|Яшырылгъан категориялар}}‎",
-       "category-subcat-count": "{{PLURAL:$2|Бу категорияда янгыз гелеген тюп категориясы бар.|Бу категорияда янгыз гелеген $2 тюп категорядан {{PLURAL:$1|тюп категория}} гёрсетилген.}}",
-       "category-article-count": "{{PLURAL:$2|Бу категорияда янгыз гелеген бир сагьифа бар.|Бу категорияда бар $2 сагьифаны {{PLURAL:$1|сагьифасы}} гёрсетилген.}}",
+       "category-subcat-count": "{{PLURAL:$2|Бу категорияда тек сонггъу гелеген тюпкатегориясы бар.|Бу категорияда {{PLURAL:$1|тюпкатегория|$1 тюпкатегориялар}} бар, жамда $2 тюпкатегориядан.}}",
+       "category-article-count": "{{PLURAL:$2|Бу категорияда янгыз гелеген бир сагьифа бар.|Бу категориядагъы $2 {{PLURAL:$1|сагьифасы}} гёрсетилген.}}",
+       "category-file-count": "{{PLURAL:$2|Бу категорияда бир тек сонггъу гелеген сапламы бар.|Сонггъу гелеген {{PLURAL:$1|саплам|$1 сапламлар}}{{PLURAL:$2|категориядадыр|категориядадырлар}}}}.",
        "listingcontinuesabbrev": "давам",
        "noindex-category": "Индексленмейген сагьифалар",
        "broken-file-category": "Ишлеймейген саплам байланывлары булан сагьифалар",
        "about": "Сыпатлав",
        "newwindow": "(янгы терезеде ачыла)",
        "cancel": "Гери этмек",
+       "moredotdotdot": "Артыкъ...",
+       "mypage": "Сагьифа",
        "mytalk": "Сёйлешив",
+       "anontalk": "Сёйлешив",
        "navigation": "Навигация",
        "and": "&#32;ва",
+       "faq": "КЧС",
+       "actions": "Гьаракатлар",
        "namespaces": "Атлар аралыгъы",
        "variants": "Тюрлер",
        "navigation-heading": "Навигация менюсю",
+       "errorpagetitle": "Хата",
        "returnto": "$1 сагьифагъа къайтмакъ",
        "tagline": "{{SITENAME}}‎ проектден",
        "help": "Кёмек",
        "search": "Излев",
        "searchbutton": "Излев",
+       "go": "Гёчмек",
        "searcharticle": "Гёчмек",
        "history": "Сагьифа тарихы",
        "history_short": "Тарих",
+       "history_small": "тарих",
        "printableversion": "Басмалы версиясы",
        "permalink": "Даимги байланыв",
+       "print": "Басмакъ",
        "view": "Къарав",
        "view-foreign": "$1 сайтында къарамакъ",
        "edit": "Тюзлемек",
        "create": "Яратмакъ",
        "create-local": "Ерли сыпатлавун тюзлемек",
        "delete": "Тайдырмакъ",
+       "protect": "Къорумакъ",
+       "protect_change": "алышдырмакъ",
        "newpage": "Янгы сагьифа",
        "talkpagelinktext": "сёйлешив",
+       "specialpage": "Хас сагьифа",
        "personaltools": "Энчили къураллар",
        "talk": "Сёйлешив",
        "views": "Къаравлар",
        "toolbox": "Къураллар",
+       "imagepage": "Саплам сагьифасына къарамакъ",
+       "mediawikipage": "Мактупну сагьифасына къарамакъ",
+       "templatepage": "Къалипны сагьифасына къарамакъ",
+       "viewhelppage": "Кёмек сагьифасына къарамакъ",
+       "viewtalkpage": "Сёйлешивюне къарамакъ",
        "otherlanguages": "Башгъа тиллерде",
        "redirectedfrom": "($1 бетинден бакъдырылгъан)‎",
        "redirectpagesub": "Ёллав сагьифа",
        "redirectto": "Ёллав мунда:",
        "lastmodifiedat": "Бу сагьифаны ахырынчы тюзлевлер болгъан: $1, $2.",
+       "protectedpage": "Къорулгъан сагьифа",
        "jumpto": "Мунда гёчмек:",
        "jumptonavigation": "навигация",
        "jumptosearch": "излев",
+       "poolcounter-usage-error": "Къоллав хата: $1",
        "aboutsite": "{{SITENAME}} гьакъында",
        "aboutpage": "Project:Сыпатлав",
        "copyright": "Ичделиги $1 лисензиягъа гёре гиришли (башгъасы мекенленмеген буса).",
        "disclaimers": "Жаваплылыкъдан къайтыв",
        "disclaimerpage": "Project:Умуми жаваплылыкъдан къайтыву",
        "edithelp": "Тюзлев гьакъында кёмек",
+       "helppage-top-gethelp": "Кёмек",
        "mainpage": "Баш Сагьифа",
        "mainpage-description": "Баш сагьифа",
        "portal": "Бирлешив портал",
        "portal-url": "Project:Бирлешив портал",
        "privacy": "Энчилилик ёругъу",
        "privacypage": "Project:Конфиденциаллыкъ сиясаты",
+       "ok": "OK",
        "retrievedfrom": "Чыкъмагъы — «$1»",
        "youhavenewmessages": "{{PLURAL:$3|Сен алдынг}} $1 ($2).",
        "youhavenewmessagesfromusers": "{{PLURAL:$3|Башгъа къоллавчудан|$3 къоллавчулардан}} ($2) $1 {{PLURAL:$4|алдынг}}.",
-       "newmessageslinkplural": "{{PLURAL:$1|янгы мактуп|999=янгы мактуп}}",
+       "newmessageslinkplural": "{{PLURAL:$1|янгы мактуп|999=янгы мактуплар}}",
        "newmessagesdifflinkplural": "last {{PLURAL:$1|алышыныв|999=алышынывлар}}",
+       "youhavenewmessagesmulti": "Сен мунда $1 янгы мактуп алдынг",
        "editsection": "тюзлемек",
        "editold": "тюзлемек",
        "viewsourceold": "кюрчю кодуна къарамакъ",
        "viewsourcelink": "кюрчю кодуна къарамакъ",
        "editsectionhint": "Бёлмени тюзлемек: $1",
        "toc": "Ичделик",
-       "site-atom-feed": "$1 Atom-агъышы",
+       "showtoc": "гёрсетмек",
+       "hidetoc": "яшырмакъ",
+       "collapsible-collapse": "чырмамакъ",
+       "collapsible-expand": "генглешдирмек",
+       "confirmable-confirm": "{{GENDER:$1|сен}} аминмисен?",
+       "confirmable-yes": "Дюр",
+       "confirmable-no": "Ёкъ",
+       "feedlinks": "Агъыш",
+       "site-atom-feed": "$1 Atom агъышы",
        "page-atom-feed": "\"$1\" Atom агъышы",
        "red-link-title": "$1 (олай сагьифасы ёкъдур)",
        "nstab-main": "Макъала",
        "nstab-image": "Саплам",
        "nstab-mediawiki": "Мактуп",
        "nstab-template": "Къалип",
+       "nstab-help": "Кёмек сагьифа",
        "nstab-category": "Категория",
        "mainpage-nstab": "Баш сагьифа",
        "nosuchspecialpage": "Олай хас сагьифасы ёкъдур",
        "nospecialpagetext": "<strong>Сен талап этген хас сагьифа ёкъ.</strong>\n\nДурус хас сагьифалар булан тизме мунда: [[Special:SpecialPages|{{int:specialpages}}]].",
+       "error": "Хата",
+       "databaseerror-function": "Функция: $1",
+       "databaseerror-error": "Хата: $1",
+       "internalerror": "Ичдеги хата",
        "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": "Бу сагьифа учун гюнделиклени гёрсетмек",
        "nextrevision": "Сонггъу тюрю →",
        "currentrevisionlink": "Ахырынчы тюрю",
        "cur": "гьал.",
-       "last": "алкъ.",
+       "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": "Булай аты булан саплам ёкъдур.",
        "allpages-hide-redirects": "Ёллавланы яшырмакъ",
        "categories": "Категориялар",
        "listgrouprights-members": "(ортакъчы тизмеси)",
+       "trackingcategories-name": "Мактупну аты",
        "emailuser": "Ортакъчыгъа мактуб",
+       "emailmessage": "Мактуп:",
+       "emailccme": "Мактупну копиясын магъа e-mail таба йибермек.",
+       "usermessage-summary": "Систем мактуп язмакъ.",
        "usermessage-editor": "Ёрукъ йиберив",
        "watchlist": "Гьызарлав сиягьы",
        "mywatchlist": "Гьызарлав сиягьы",
        "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 алышдыргъан",
        "proxyblocker": "Прокси къамаву",
        "movelogpage": "Ат алышдырыв журналы",
        "export": "Сагьифаланы тышдан алыв",
+       "allmessages": "Систем мактуплар",
        "thumbnail-more": "Уллулашдырмакъ",
        "importlogpage": "Импорт гюнделиги",
        "tooltip-pt-userpage": "{{GENDER:|Сени}} къоллавчу сагьифанг",
        "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": "Къыймат:",
        "logentry-newusers-autocreate": "$1 ортакъчыны гьисабы оьзлюгюнден {{GENDER:$2|яратылды}}",
        "logentry-upload-upload": "$1 {{GENDER:$2|юклеген}} $3",
        "logentry-upload-overwrite": "$1 буну $3 янгы тюрюн {{GENDER:$2|юклеген}}",
+       "feedback-message": "Мактуп:",
        "searchsuggest-search": "{{SITENAME}} ичинде излемек",
        "duration-days": "$1 {{PLURAL:$1|гюн}}",
        "randomrootpage": "Хапарсыз тамур сагьифасы"
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 4f57acb..99037d4 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!",
        "expansion-depth-exceeded-category-desc": "La paje esede la profondia masima de estende.",
        "expansion-depth-exceeded-warning": "La paje ia esede la profondia de estende",
        "parser-unstrip-loop-warning": "Sicle de unstrip() detetada",
-       "parser-unstrip-recursion-limit": "Esede de la recorsa masima de unstrip() ($1)",
+       "unstrip-depth-warning": "Esede de la recorsa masima de unstrip() ($1)",
        "converter-manual-rule-error": "Era detetada en regula nonautomata de converti de lingua",
        "undo-success": "La edita pote es desfada.\nPer favore, esamina la compara a su per serti ce tu vole fa esta, e alora fisa la cambias a su per fini desfa la edita.",
        "undo-failure": "La edita no pote es desfada par causa de editas interveninte cual contradise lo.",
        "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:",
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..ceb8d95 100644 (file)
        "botpasswords-insert-failed": "'t Touveuge vanne botnaam \"$1\" is mislök. Is dae mesjiens al tougeveug?",
        "botpasswords-update-failed": "'t Biewirke vanne botnaam \"$1\" is mislök. Is dae mesjiens eweggesjaf?",
        "botpasswords-created-title": "Botwachwaord aangemaak",
-       "botpasswords-created-body": "'t Botwachwaord veure botnaam \"$1\" van gebroeker \"$2\" is gemaak.",
+       "botpasswords-created-body": "'t Botwachwaord veure botnaam \"$1\" van {{GENDER:$2|gebroeker}} \"$2\" is gemaak.",
        "botpasswords-updated-title": "Botwachwaord biegewirk",
-       "botpasswords-updated-body": "'t Botwachwaord veur de botnaam \"$1\" van gebroeker \"$2\" is biegewirk.",
+       "botpasswords-updated-body": "'t Botwachwaord veur de botnaam \"$1\" van {{GENDER:$2|gebroeker}} \"$2\" is biegewirk.",
        "botpasswords-deleted-title": "Botwachwaord eweggesjaf",
-       "botpasswords-deleted-body": "'t Botwachwaord veure botnaam \"$1\" vanne gebroeker \"$2\" is eweggesjaf.",
+       "botpasswords-deleted-body": "'t Botwachwaord veure botnaam \"$1\" vanne {{GENDER:$2|gebroeker}} \"$2\" is eweggesjaf.",
        "botpasswords-newpassword": "'t Nuuj wachwaord veur aan te melje mit <strong>$1</strong> is <strong>$2</strong>. <em>Bewaar dit goed voor toekomstig gebruik.</em> <br> (Veur aaj bots die vereisje det de aameljnaam 'tzelfde is es d'n eventuele gebroekersnaam, kan ouch <strong>$3</strong> es gebroekersnaam en <strong>$4</strong> es wachwaord waere gebroek.)",
        "botpasswords-no-provider": "BotPasswordsSessionProvider is neet besjikbaar.",
        "botpasswords-restriction-failed": "Botwachwaordbepirkinge verkómme aanmelje.",
        "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>",
        "postedit-confirmation-created": "De pagina is aangemaak gewaore.",
        "postedit-confirmation-restored": "De pagina is herstèldj gewaore.",
        "postedit-confirmation-saved": "Dien bewirking is opgeslage gewaore.",
+       "postedit-confirmation-published": "Dien bewirking is opgeslage.",
        "edit-already-exists": "De pagina is neet aangemaak.\nZie besjteit al.",
        "defaultmessagetext": "Obligaten teks",
        "content-failed-to-parse": "'t Waas neet meugelik d'n inhawd van 't MIME-typ $2 veur 't model $1 te verwirke: $3.",
        "expansion-depth-exceeded-category-desc": "De pagina geit euver de maximaal oetbreijingsdeepdje.",
        "expansion-depth-exceeded-warning": "De paasj haet te väöl sjablone",
        "parser-unstrip-loop-warning": "Unstriplus gevónje",
-       "parser-unstrip-recursion-limit": "Unstriprecursielimiet te väöl ($1)",
+       "unstrip-depth-warning": "Unstriprecursielimiet te väöl ($1)",
        "converter-manual-rule-error": "'n Fout is óntdèk gewaore in 'ne handjmaesig tougeveudje spraokómzèttingsregel",
        "undo-success": "Hiej onger stuit de teks wo in de verangering ongedaon gemaak is. Controleer veur 't opslaon of 't resultaot gewins is.",
        "undo-failure": "De verangering kòs neet ongedaon gemaak waere waeges angere striedige verangeringe.",
        "diff-multi-sameuser": "({{PLURAL:$1|Ein tösseligkendje versie|$1 tösseligkendje versies}} door dezelfdje gebroeker neet getoeandj)",
        "diff-multi-otherusers": "({{PLURAL:$1|Ein tösseligkendje versie|$1 tösseligkendje versies}} door {{PLURAL:$2|einen angere gebroeker|$2 gebroekers}} neet getuind)",
        "diff-multi-manyusers": "($1 tösseligkende versies door mier es $2 gebroekers waere neet waergaeve)",
+       "diff-paragraph-moved-tonew": "Dees paragraaf is verplaats wore. Klik veur nao 'n nuuj lokaasje te springe.",
+       "diff-paragraph-moved-toold": "Dees paragraaf is verplaats wore. Klik veur nao de aaj lokaasje te springe.",
        "searchresults": "Zeukresultate",
        "searchresults-title": "Zeukresultate veur \"$1\"",
        "titlematches": "Overeinkoms mèt volgende titels",
        "timezoneregion-europe": "Europa",
        "timezoneregion-indian": "Indische Oceaan",
        "timezoneregion-pacific": "Stille Oceaan",
-       "allowemail": "E-mail van anger gebroekers toesjtaon",
+       "allowemail": "Stank anger gebroekers toe mich te e-maile",
+       "email-allow-new-users-label": "Stank e-mails van kaesnuuj gebroekers toe",
+       "email-blacklist-label": "Verbeej dees gebroekers mich te e-maile:",
        "prefs-searchoptions": "Zeuke",
        "prefs-namespaces": "Naamruimte",
        "default": "sjtandaard",
        "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",
        "right-unblockself": "Óntblok eige gebroeker",
        "right-protect": "Beveiligingsniveaus aanpasse",
        "right-editprotected": "Beveiligde pazjena's bewirke",
+       "right-editsemiprotected": "Bewirk pagina's die zint beveilig es \"{{int:protect-level-autoconfirmed}}\"",
+       "right-editcontentmodel": "Bewirk 't pagina-inhawdmodel",
        "right-editinterface": "De gebroekersinterface bewerke",
        "right-editusercss": "De CSS-bestande van angere gebroekers bewerke",
        "right-edituserjs": "De JS-bestande van angere gebroekers bewerke",
+       "right-editmyusercss": "Bewirk dien eige CSS-pagina's",
+       "right-editmyuserjs": "Bewirk dien eige JavaScrippagina's",
        "right-viewmywatchlist": "Betrach dien eige volglies",
+       "right-editmywatchlist": "Bewirk dien eige volglies. Via sommige hanjelinge kinne nag ummer pagina's waere tougeveug, zelfs zónger dees beveugheid.",
+       "right-viewmyprivateinfo": "Betrach dien eige privégegaeves (wie e-mailadres, echte naam)",
+       "right-editmyprivateinfo": "Bewirk dien eige privégegaeves (wie e-mailadres, echte naam)",
        "right-editmyoptions": "Bewirk dien eige veurkäöre",
        "right-rollback": "Snel de letste bewerking(e) van 'n gebroeker van 'n pagina terugdraaie",
        "right-markbotedits": "Teruggedraaide bewerkinge markere es botbewerkinge",
        "right-siteadmin": "De database blokkere en weer vriegaeve",
        "right-override-export-depth": "Export paazjes midin geslinkdje paazjes mit 'n deepdje ven 5",
        "right-sendemail": "Versjik e-mail aan anger gebroekers",
+       "right-managechangetags": "Maak en (de)aktiveer [[Special:Tags|labels]]",
+       "right-applychangetags": "Wies [[Special:Tags|labels]] tou aan bewirkinge",
+       "right-deletechangetags": "Sjaf [[Special:Tags|labels]] eweg van de database",
        "grant-generic": "Rechtegroep \"$1\"",
        "grant-group-page-interaction": "Wirk mit pagina's",
        "grant-group-file-interaction": "Wirk mit media",
        "grant-createaccount": "Maak gebroekers aan",
        "grant-createeditmovepage": "Maak, bewirk en verplaats pagina's",
        "grant-delete": "Wösj pagina's, bewirkinge en logbookregele",
+       "grant-editinterface": "Bewirk de MediaWiki-naamruumde en CSS/JavaScrip van gebroekers",
        "grant-editmycssjs": "Bewirk diene CSS/JavaScript",
        "grant-editmyoptions": "Bewirk dien veurkäöre",
        "grant-editmywatchlist": "Bewirk dien volglies",
        "grant-editpage": "Bewirk bestäöndje pagina's",
        "grant-editprotected": "Bewirk besjurmde pagina's",
+       "grant-highvolume": "Maak väöl bewirkinge in korten tied",
+       "grant-oversight": "Verstaek gebroekers en versies",
+       "grant-patrol": "Controleer verangeringe aan pagina's",
+       "grant-privateinfo": "Betrach perseunlike gegaeves",
+       "grant-protect": "Besjirm en gaef pagina's vrie",
+       "grant-rollback": "Drej bewirkinge aan pagina's trök",
+       "grant-sendemail": "Versjik e-mail aan anger gebroekers",
+       "grant-uploadeditmovefile": "Upload, vervang en verplaats bestenj",
+       "grant-uploadfile": "Upload nuuj bestenj",
        "grant-basic": "Basisrechte",
+       "grant-viewdeleted": "Betrach gewösjde bestenj en pagina's",
+       "grant-viewmywatchlist": "Betrach dien volglies",
+       "grant-viewrestrictedlogs": "Betrach vertroewelike logbookbestenj",
        "newuserlogpage": "Logbook nuuj gebroekers",
        "newuserlogpagetext": "Hiej ónger saton de nuuj ingesjreve gebroekers.",
        "rightslog": "Gebroekersrechtelogbook",
        "action-read": "dees pagina te bekieke",
        "action-edit": "dees pagina te bewirke",
        "action-createpage": "pagina's aan te make",
-       "action-createtalk": "euverlèkpagina's aan te make",
+       "action-createtalk": "dees euverlèkpagina aan te make",
        "action-createaccount": "deze gebroeker aan te make",
+       "action-autocreateaccount": "dezen externe gebroek autematis aan te make",
+       "action-history": "de gesjiedenis van dees pagina te betrachte",
        "action-minoredit": "deze bewirking es klein te markere",
        "action-move": "deze pagina te verplaatse",
        "action-move-subpages": "dees pagina en biebehurende subpagina's te verplaatse",
        "action-move-rootuserpages": "gebroekerspagina's van 't hoegste niveau te verplaatse",
+       "action-move-categorypages": "categorieje te versjuve",
        "action-movefile": "dit bestandj te hernömme",
        "action-upload": "dit besjtandj te uploade",
        "action-reupload": "dit besjtaond besjtandj te euversjrieve",
        "action-upload_by_url": "dit besjtandj vanaaf 'ne URL te uploade",
        "action-writeapi": "via de API te bewirke",
        "action-delete": "dees pagina eweg te sjaffe",
-       "action-deleterevision": "dees versie eweg te sjaffe",
+       "action-deleterevision": "versies eweg te sjaffe",
        "action-deletelogentry": "logbeukregels eweg te sjaffe",
-       "action-deletedhistory": "de eweggesjafte versies van dees pagina te bekieke",
+       "action-deletedhistory": "de eweggesjafde versies van 'n pagina te bekieke",
+       "action-deletedtext": "de eweggesjafde versieteks te betrachte",
        "action-browsearchive": "eweggesjafte pagina's te zeuke",
        "action-undelete": "dees pagina trök te zètte",
        "action-suppressrevision": "dees verborge versie te betrachte en trök te plaatse",
        "recentchanges-legend": "Opties veur recènte verangeringe",
        "recentchanges-summary": "Volg de recènste bewirkinge op deze wiki op dees pagina.",
        "recentchanges-noresult": "Gein verangeringe in dees periood kómme euverein mit de criteria.",
+       "recentchanges-timeout": "Dees zeukopdrach kos te väöl tied. Doe kins 't oppernuuj perbere mit anger zeukparamaeters.",
+       "recentchanges-network": "Door 'n technische fout kinne gein rizzeltaote waere gelaje. Doe kins perbere de pagina te vernuje.",
+       "recentchanges-notargetpage": "Veur hiebaove 'ne paginanaam in veur verwantje verangeringe veur dees pagina te zeen.",
        "recentchanges-feed-description": "Volg de meis recente bewerkinge in deze wiki via deze feed.",
        "recentchanges-label-newpage": "Mit dees verangering is 'n nuuj pagina aangemaak",
        "recentchanges-label-minor": "Dit is 'n klein bewirking",
        "rcfilters-group-results-by-page": "Resultate per pagina groepere",
        "rcfilters-activefilters": "Aktief filters",
        "rcfilters-advancedfilters": "Geavanceerde filters",
-       "rcfilters-limit-title": "Te toeane verangeringe",
+       "rcfilters-limit-title": "Te toeane rizzeltaote",
+       "rcfilters-limit-and-date-label": "$1 {{PLURAL:$1|verangering|verangeringe}}, $2",
+       "rcfilters-date-popup-title": "Te doorzeuke tiedsfraem",
        "rcfilters-days-title": "Recènte daag",
        "rcfilters-hours-title": "Recènte oere",
        "rcfilters-days-show-days": "$1 {{PLURAL:$1|daag}}",
        "rcfilters-days-show-hours": "$1 {{PLURAL:$1|oer}}",
        "rcfilters-highlighted-filters-list": "Oetgeleech: $1",
        "rcfilters-quickfilters": "Opgesjlage filters",
-       "rcfilters-quickfilters-placeholder-title": "Nag gein opgeslage links",
+       "rcfilters-quickfilters-placeholder-title": "Nag gein opgeslage filtjers",
        "rcfilters-savedqueries-defaultlabel": "Opgesjlage filters",
        "rcfilters-savedqueries-rename": "Herneum",
        "rcfilters-savedqueries-setdefault": "Stèl in es standerd",
        "rcfilters-highlightmenu-title": "Kees 'n kluuer",
        "rcfilters-highlightmenu-help": "Kees 'n kluuer veur dees eigesjappe oet te lichte",
        "rcfilters-filterlist-noresults": "Gein filters gevónje",
+       "rcfilters-filtergroup-authorship": "Sjriever van de biedraag",
+       "rcfilters-filter-editsbyself-label": "Verangeringe van dichzelf",
+       "rcfilters-filter-editsbyself-description": "Dien eige biedraag.",
+       "rcfilters-filter-editsbyother-label": "Verangeringe door angere",
+       "rcfilters-filter-editsbyother-description": "Alle verangere behauve die die det se zelf höbs gemaak.",
+       "rcfilters-filtergroup-userExpLevel": "Gebroekersregistrasie en -ervaring",
+       "rcfilters-filter-user-experience-level-registered-label": "Vasgelag",
+       "rcfilters-filter-user-experience-level-registered-description": "Aangemelde bewirkers.",
+       "rcfilters-filter-user-experience-level-unregistered-label": "Neet-vasgelag",
+       "rcfilters-filter-user-experience-level-unregistered-description": "Bewirkers die neet zeen aangemeld.",
+       "rcfilters-filter-user-experience-level-newcomer-label": "Nujelinge",
        "rcfilters-filter-bots-label": "Bot",
        "rcfilters-filter-humans-label": "Minsjelik (geine bot)",
        "rcfilters-filter-watchlist-watched-label": "Op de volglies",
        "recentchangeslinked-feed": "Volg links",
        "recentchangeslinked-toolbox": "Volg links",
        "recentchangeslinked-title": "Verangeringe verwant mit \"$1\"",
-       "recentchangeslinked-summary": "Dees speciaal pagina tuint de lètste bewirkinge op pagina's die gelink waere vanaaf deze pagina. Pagina's die op [[Special:Watchlist|dien volglies]] staon waere '''vet''' weergegaeve.",
+       "recentchangeslinked-summary": "Veur 'n paginanaam in veur bewirkinge te zeen van pagina's wohaer vanaaf die pagina weurt verweze of die daohaer verwieze. (Veur lede van 'n categorie te zeen, veur den <kbd>Category:''Naam van categorie''</kbd> in.) Bewirkinge van pagina's op [[Special:Watchlist|dien volglies]] waere <strong>diek</strong> waergaove.",
        "recentchangeslinked-page": "Paginanaam:",
        "recentchangeslinked-to": "Verangeringe weergaeve nao de gelinkde pagina's",
        "recentchanges-page-added-to-category": "[[:$1]] aan categorie tougeveug gewaore",
        "feedback-subject": "Óngerwerp:",
        "feedback-submit": "Opsjlaon",
        "feedback-thanks": "Danke! Diene feedback is oppe pagina \"[$2 $1]\" geplaats.",
+       "feedback-thanks-title": "Danke!",
        "searchsuggest-search": "Doorzeuk {{SITENAME}}",
        "searchsuggest-containing": "bevat...",
        "api-error-badtoken": "Intern fout: toke is slech.",
index 58978b9..61cdc9e 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æ!'''",
        "expansion-depth-exceeded-category-desc": "A paggina a suppera a profonditæ mascima d'espanscion.",
        "expansion-depth-exceeded-warning": "Sta paggine a l'ha superòu a profonditæ d'espanscion",
        "parser-unstrip-loop-warning": "Rilevou ciclo de Unstrip",
-       "parser-unstrip-recursion-limit": "Superæ i limmiti de ricorscion de Unstrip ($1)",
+       "unstrip-depth-warning": "Superæ i limmiti de ricorscion de Unstrip ($1)",
        "converter-manual-rule-error": "Rilevou errô inta reggola manoâ de converscion da lengoa",
        "undo-success": "Questa modiffica a peu ese anullâ.\nControlla e differençe mostræ chì de sotta fra e doe verscioin pe ese seguo che-o contegnuo o corisponde a quante dexidiou, e quindi sarvâ e modiffiche pe completâ a proçedûa d'anullamento.",
        "undo-failure": "Imposcibbile anullâ a modiffica a caosa de un conflito con de modiffiche intermeddie.",
        "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..e2b0ded 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تغییرات شما هنوز ذخیره نشده‌است!",
        "expansion-depth-exceeded-category-desc": "رده برای صفحاتی که در آنها از عمق گسترش فراتر رفته است.",
        "expansion-depth-exceeded-warning": "صفحه حداکثر عمق بسط دادن تجاوز کرد",
        "parser-unstrip-loop-warning": "حلقه در دستور unstrip پیدا شد",
-       "parser-unstrip-recursion-limit": "از حداکثر ارجاع در دستور unstrip تجاوز شد ($1)",
+       "unstrip-depth-warning": "از حداکثر ارجاع در دستور unstrip تجاوز شد ($1)",
        "converter-manual-rule-error": "خطا در قوانین مبدل دستی زبان",
        "undo-success": "این ویرایش را می‌توان خنثی کرد.\nلطفاً تفاوت زیر را بررسی کنید تا تأیید کنید که این چیزی است که می‌خواهید انجام دهید، سپس تغییرات زیر را ذخیره کنید تا خنثی‌سازی ویرایش را به پایان ببرید.",
        "undo-failure": "به علت تعارض با ویرایش‌های میانی، این ویرایش را نمی‌توان خنثی کرد.",
        "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 d2e5474..96aa274 100644 (file)
        "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آلئشت کاریاتوٙ ھأنی ئمایە نأبینە",
        "expansion-depth-exceeded-category-desc": "زیر دسه سی بلگه یایی که د ونو پی یا ووله بیین فره پئشکرد کرده.",
        "expansion-depth-exceeded-warning": "بلگه د پی یا ووله بیین پئشکرد کرد",
        "parser-unstrip-loop-warning": "گردوله د فرمونه Unstrip پیدا بیه",
-       "parser-unstrip-recursion-limit": "د بیشترونه د سرچشمه رئتن د دستور Unstrip واروتر رئتیته($1)",
+       "unstrip-depth-warning": "د بیشترونه د سرچشمه رئتن د دستور Unstrip واروتر رئتیته($1)",
        "converter-manual-rule-error": "خطا د قانون والرشتن دسی زون",
        "undo-success": "نبوئه ویرایشت نه انجومشیو بکیت.\nلطفا ای فرخی که ها د هار نه وارسی بکیت تا یه کاریه که میهات انجوم بئیت، و اوسه آلشتیا هار نه اماییه بکیت سی یه که خمثی کردن ویرایشت نه انجوم بئیت.",
        "undo-failure": "سی ری به ری بیئن ای ویرایشت وا ویرایشتیا مینجایی، نبوئه ای ویرایشت نه خومثی بکیت.",
        "prefs-files": "جانیایا",
        "prefs-custom-css": "سی اس اس جاافتائه",
        "prefs-custom-js": "جاوا نیسسه جاافتائه",
-       "prefs-common-css-js": " سی اس اس/جاوا اسکریپت بهر بیه سی همه پوسه یا:",
+       "prefs-common-config": " سی اس اس/جاوا اسکریپت بهر بیه سی همه پوسه یا:",
        "prefs-reset-intro": "شما می تونیت ای بلگه سی د نو زنه کردن ترجیحات خوت وه شکل تیارگه پیش فرض وه کار بوونیت.\nیه ورئشت پذیر نئ.",
        "prefs-emailconfirm-label": "پش راست کردن انجومانامه:",
        "youremail": "أنجومانامە:",
index 873c63c..e0c77b3 100644 (file)
        "mar": "Kov",
        "apr": "Bal",
        "may": "Geg",
-       "jun": "Bir",
+       "jun": "Birž",
        "jul": "Lie",
        "aug": "Rgp",
        "sep": "Rgs",
        "userpage-userdoesnotexist": "Naudotojo paskyra „<nowiki>$1</nowiki>“ yra neužregistruota. Prašom patikrinti, ar jūs norite kurti/redaguoti šį puslapį.",
        "userpage-userdoesnotexist-view": "Naudotojo paskyra „$1“ neužregistruota.",
        "blocked-notice-logextract": "Šis naudotojas šiuo metu yra užblokuotas.\nŽemiau pateikiamas paskutinis blokavimo istorijos įrašas:",
-       "clearyourcache": "'''Dėmesio:''' Išsaugojus jums gali prireikti išvalyti jūsų naršyklės podėlį, kad pamatytumėte pokyčius.\n* '''Firefox / Safari:''' laikydami ''Shift'' pasirinkite ''Atsiųsti iš naujo'', arba paspauskite ''Ctrl-F5'' ar ''Ctrl-R'' (sistemoje Apple Mac ''Commandd-R'')\n* '''Google Chrome:''' spauskite ''Ctrl-Shift-R'' (sistemoje Apple Mac ''Command-Shift-R'')\n* '''Internet Explorer:''' laikydami ''Ctrl'' paspauskite ''Atnaujinti'', arba paspauskite ''Ctrl-F5''\n* '''Konqueror:''' tiesiog paspauskite ''Perkrauti'' mygtuką, arba paspauskite ''F5''\n* '''Opera''' pilnai išvalykite podėlį ''Priemonės→Nuostatos''.",
+       "clearyourcache": "<strong>Dėmesio:</strong> Išsaugojus jums gali prireikti išvalyti jūsų naršyklės podėlį, kad pamatytumėte pokyčius.\n* <strong>Firefox / Safari:</strong> Laikydami <em>Shift</em> pasirinkite <em>Perkrauti</em>, arba paspauskite <em>Ctrl-F5</em> ar <em>Ctrl-R</em> (sistemoje Apple Mac <em>⌘-R</em>)\n* <strong>Google Chrome:</strong> Spauskite <em>Ctrl-Shift-R</em> (sistemoje Apple Mac <em>⌘-Shift-R</em>)\n* <strong>Internet Explorer:</strong> Laikydami <em>Ctrl</em> paspauskite <em>Naujinti</em>, arba paspauskite <em>Ctrl-F5</em>\n* <strong>Opera:</strong> Eikite į <em>Meniu → Nuostatos</em> (sistemoje Apple Mac <em>Opera → Nustatymai</em>), tuomet <em>Privatumas ir sauga → išvalyti naršymo podėlį → išsaugotos talpyklos vaizdai ir failai</em>.",
        "usercssyoucanpreview": "'''Patarimas:''' Naudokite „{{int:showpreview}}“ mygtuką, kad išmėgintumėte savo naująjį CSS prieš išsaugant.",
        "userjsyoucanpreview": "'''Patarimas:''' Naudokite „{{int:showpreview}}“ mygtuką, kad išmėgintumėte savo naująjį JS prieš išsaugant.",
        "usercsspreview": "'''Nepamirškite, kad jūs tik peržiūrit savo naudotojo CSS, jis dar nebuvo išsaugotas!'''",
        "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",
        "expansion-depth-exceeded-category-desc": "Puslapis viršija didžiausią plėtros gylį.",
        "expansion-depth-exceeded-warning": "Puslapis, viršijantis didžiausią plėtros gylį",
        "parser-unstrip-loop-warning": "Rastas neuždarytas ciklas",
-       "parser-unstrip-recursion-limit": "Unstrip rekursijos limitas viršytas ($1)",
+       "unstrip-depth-warning": "Unstrip rekursijos limitas viršytas ($1)",
        "converter-manual-rule-error": "Rankinėje kalbos pertvarkymo taisyklėje rasta klaida",
        "undo-success": "Keitimas gali būti atšauktas. Prašome patikrinti palyginimą, esantį žemiau, kad patvirtintumėte, kad jūs tai ir norite padaryti, ir tada išsaugokite pakeitimus, esančius žemiau, kad užbaigtumėte keitimo atšaukimą.",
        "undo-failure": "Keitimas negali būti atšauktas dėl konfliktuojančių tarpinių keitimų.",
        "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)",
        "searchall": "visi",
        "showingresults": "Žemiau rodoma iki '''$1''' {{PLURAL:$1|rezultato|rezultatų|rezultatų}} pradedant #'''$2'''.",
        "showingresultsinrange": "Žemiau rodoma iki {{PLURAL:$1|<strong>1</strong> gavinio|<strong>$1</strong> gavinių}} imtyje nuo <strong>$2</strong> iki <strong>$3</strong>.",
-       "search-showingresults": "{{PLURAL:$4|Davinys <strong>$1</strong> iš <strong>$3</strong>|Daviniai <strong>$1 - $2</strong> iš <strong>$3</strong>}}",
+       "search-showingresults": "{{PLURAL:$4|Rezultatas <strong>$1</strong> iš <strong>$3</strong>|Rezultatai <strong>$1 - $2</strong> iš <strong>$3</strong>}}",
        "search-nonefound": "Nėra rezultatų, atitinkančių užklausą.",
        "search-nonefound-thiswiki": "Nėra rezultatų atitinkančių užklausą šiame tinklapyje.",
        "powersearch-legend": "Išplėstinė paieška",
        "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:",
        "recentchangeslinked-feed": "Susiję keitimai",
        "recentchangeslinked-toolbox": "Susiję keitimai",
        "recentchangeslinked-title": "Su „$1“ susiję keitimai",
-       "recentchangeslinked-summary": "Įveskite puslapio pavadinimą, jei norite žiūrėti keitimus, atliktus puslapiuose, į kuriuos yra nuoroda į arba iš nurodyto puslapio. (Jei norite žiūrėti nurodytos kategorijos narius, įveskite Kategorija:Kategorijos pavadinimas). Puslapiai iš jūsų [[Special:Watchlist|stebimųjų sąrašo]] yra <strong>paryškinti</strong>.",
+       "recentchangeslinked-summary": "Įveskite puslapio pavadinimą, jei norite matyti keitimus, atliktus puslapiuose, į kuriuos yra nuoroda į arba iš nurodyto puslapio. (Jei norite ieškoti nurodytos kategorijos narius, įveskite Kategorija:Kategorijos pavadinimas). Puslapiai iš jūsų [[Special:Watchlist|stebimųjų sąrašo]] yra <strong>paryškinti</strong>.",
        "recentchangeslinked-page": "Puslapio pavadinimas:",
        "recentchangeslinked-to": "Rodyti su duotuoju puslapiu susijusių puslapių pakeitimus",
        "recentchanges-page-added-to-category": "[[:$1]] pridėta prie kategorijos",
        "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..4b94f2e 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",
        "timezoneregion-indian": "Indijas okeāns",
        "timezoneregion-pacific": "Klusais okeāns",
        "allowemail": "Atļaut citiem dalībniekiem sūtīt man e-pastus",
+       "email-allow-new-users-label": "Atļaut e-pastus no ļoti jauniem dalībniekiem",
        "email-blacklist-label": "Aizliegt šiem dalībniekiem man sūtīt e-pastus:",
        "prefs-searchoptions": "Meklēšana",
        "prefs-namespaces": "Vārdtelpas",
        "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..b66f1a0 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अहाँक परिवर्तन अखन धरि सङ्ग्रह नै कएल गेल अछि!",
        "expansion-depth-exceeded-category-desc": "ई पृष्ठ अधिकतम रुपमे विस्तार गहिराई पार केनए अछि",
        "expansion-depth-exceeded-warning": "पन्ना विस्तार गहिराई पार केनए अछि",
        "parser-unstrip-loop-warning": "Unstrip लूप पाओल गेल",
-       "parser-unstrip-recursion-limit": "Unstrip पुनरावर्तन सीमा पार कइर गेल($1)",
+       "unstrip-depth-warning": "Unstrip पुनरावर्तन सीमा पार कइर गेल($1)",
        "converter-manual-rule-error": "म्यानुअल भाषा परिवर्तन नियममे त्रुटि",
        "undo-success": "ई सम्पादन पूर्ववत बदलल जा सकैए।\nकृपा क' नीचाँक तुलनाक जाँच करू ई देखैले जे ई वएह भेल अछि जे अहाँ चाहै छलहुँ, आ तखन सम्पादन ख़तम करबा लेल नीचाँक परिवर्तन सुरक्षित करू ।",
        "undo-failure": "मध्यवर्ती विरोधी सम्पादनक कारण ऐ सम्पादनकेँ खतम नै कएल जा सकैए।",
        "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..0e92f0c 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 !",
        "expansion-depth-exceeded-category-desc": "Sokajy ho an'ny pejy ahitana halalim-piitarana mihoatra.",
        "expansion-depth-exceeded-warning": "Pejy manana halalim-panitarana mihoatra",
        "parser-unstrip-loop-warning": "Nahitana tondro mifolaka tsy azo vahana",
-       "parser-unstrip-recursion-limit": "Tafahoatra ny fetra avo ny fetra recursion ($1)",
+       "unstrip-depth-warning": "Tafahoatra ny fetra avo ny fetra recursion ($1)",
        "converter-manual-rule-error": "Nahitana hadisoana ao amin'ny fepetra famadihana tanana ny fiteny.",
        "undo-success": "Ho voafafa io fanovana io. Marino tsara ny fanovana eo ambany, ary tehirizo rehefa vita.",
        "undo-failure": "Tsy afaka esorina io fanovàna io : mety tsy miraikitra amin'ny fanovàna misy eo ampivoaniny ra esorina",
        "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..ba766d6 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!",
        "expansion-depth-exceeded-category": "Laman dima kadalaman ekspansi lah talampau",
        "expansion-depth-exceeded-warning": "Laman kadalaman ekspansi lah talampau",
        "parser-unstrip-loop-warning": "Unstrip loop detected",
-       "parser-unstrip-recursion-limit": "Unstrip recursion limit exceeded ($1)",
+       "unstrip-depth-warning": "Unstrip recursion limit exceeded ($1)",
        "converter-manual-rule-error": "Kasalahan tadeteksi di aturan manual konversi bahaso",
        "undo-success": "Suntiangan ko dapek dibatalan. \nTolong cek pabedoan di bawah untuak mayakinkan bahwa bana nan tu Sanak nio buek, lalu simpan parubahan tasabuik untuak manyalasaikan pambatalan suntiangan.",
        "undo-failure": "Suntiangan ko indak dapek dibatalan dek konflik panyuntiangan antaro.",
        "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 88b8dbd..18f0925 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Промените сè уште не се зачувани!",
        "expansion-depth-exceeded-category-desc": "Страницата ја надминува најголемата дозволена продорност на проширувањето.",
        "expansion-depth-exceeded-warning": "Страницата ја надмина длабочината на проширувањето",
        "parser-unstrip-loop-warning": "Утврдена е јамка",
-       "parser-unstrip-recursion-limit": "Пречекорена е границата на рекурзија ($1)",
+       "unstrip-depth-warning": "Пречекорена е границата на рекурзија ($1)",
        "converter-manual-rule-error": "Пронајдов грешка во правилото за рачно претворање на јазик",
        "undo-success": "Уредувањето може да се откаже.\nВе молиме споредете ги промените со претходната верзија за да проверите дали тоа е сигурно она што сакате да го направите, а потоа зачувајте ги промените за да го завршите откажувањето на претходното уредување.",
        "undo-failure": "Уредувањето не можеше да се откаже заради меѓувремени спротиставени уредувања.",
        "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 7f068cd..a3fcbec 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താങ്കൾ വരുത്തിയ മാറ്റങ്ങൾ ഇതുവരെ സേവ് ചെയ്തിട്ടില്ല!",
        "expansion-depth-exceeded-category-desc": "താളിലെ വികസന ആഴം അധികരിച്ചിരിക്കുന്നു.",
        "expansion-depth-exceeded-warning": "താളിന്റെ വികസന ആഴം അധികരിച്ചിരിക്കുന്നു",
        "parser-unstrip-loop-warning": "അൺസ്ട്രിപ്പ് (Unstrip) പാഴ്സർ ഫങ്ഷനിൽ കുരുക്ക് കണ്ടെത്തി",
-       "parser-unstrip-recursion-limit": "അൺസ്ട്രിപ്പ് (Unstrip) പാഴ്സർ ഫങ്ഷന്റെ പുനരാവർത്തന പരിധി അധികരിച്ചിരിക്കുന്നു ($1)",
+       "unstrip-depth-warning": "അൺസ്ട്രിപ്പ് (Unstrip) പാഴ്സർ ഫങ്ഷന്റെ പുനരാവർത്തന പരിധി അധികരിച്ചിരിക്കുന്നു ($1)",
        "converter-manual-rule-error": "മാനുഷികമായുള്ള ഭാഷാ പരിവർത്തന നിയമത്തിൽ പിഴവ് കണ്ടെത്തി",
        "undo-success": "ഈ തിരുത്ത് താങ്കൾക്ക് തിരസ്ക്കരിക്കാവുന്നതാണ്‌. താഴെ കൊടുത്തിരിക്കുന്ന പതിപ്പുകൾ തമ്മിലുള്ള താരതമ്യം ഒന്നുകൂടി പരിശോധിച്ച് ഈ പ്രവൃത്തി ചെയ്യണോ എന്ന് ഒന്നുകൂടി ഉറപ്പാക്കുക. ഉറപ്പാണെങ്കിൽ തിരുത്ത് തിരസ്ക്കരിക്കുവാൻ താൾ സേവ് ചെയ്യുക.",
        "undo-failure": "ഇടയ്ക്കുള്ള തിരുത്തുകൾ തമ്മിൽ സമരസപ്പെടാത്തതു കാരണം ഈ തിരുത്ത് തിരസ്ക്കരിക്കുവാനാവില്ല.",
        "prefs-files": "പ്രമാണങ്ങൾ",
        "prefs-custom-css": "സ്വന്തം സി.എസ്.എസ്.",
        "prefs-custom-js": "സ്വന്തം ജെ.എസ്.",
-       "prefs-common-css-js": "എല്ലാ ദൃശ്യരൂപങ്ങൾക്കുമായി പങ്ക് വെയ്ക്കപ്പെട്ട സി.എസ്.എസ്./ജെ.എസ്.:",
+       "prefs-common-config": "എല്ലാ ദൃശ്യരൂപങ്ങൾക്കുമായി പങ്ക് വെയ്ക്കപ്പെട്ട സി.എസ്.എസ്./ജെ.എസ്.:",
        "prefs-reset-intro": "സൈറ്റിൽ സ്വതേയുണ്ടാവേണ്ട ക്രമീകരണങ്ങൾ പുനഃക്രമീകരിക്കാൻ താങ്കൾക്ക് ഈ താൾ ഉപയോഗിക്കാവുന്നതാണ്.\nഇത് തിരിച്ചു ചെയ്യാൻ സാദ്ധ്യമല്ല.",
        "prefs-emailconfirm-label": "ഇമെയിൽ സ്ഥിരീകരണം:",
        "youremail": "ഇമെയിൽ:",
        "prefs-editor": "എഡിറ്റർ",
        "prefs-preview": "എങ്ങനെയുണ്ടെന്ന് കാണൽ",
        "prefs-advancedrc": "വിപുലമായ ഉപാധികൾ",
+       "prefs-opt-out": "പുതുക്കലുകൾ ഒഴിവാക്കുക",
        "prefs-advancedrendering": "വിപുലമായ ഉപാധികൾ",
        "prefs-advancedsearchoptions": "വിപുലമായ ഉപാധികൾ",
        "prefs-advancedwatchlist": "വിപുലമായ ഉപാധികൾ",
        "rcfilters-watchlist-markseen-button": "എല്ലാ മാറ്റങ്ങളും കണ്ടതായി അടയാളപ്പെടുത്തുക",
        "rcfilters-watchlist-edit-watchlist-button": "താങ്കൾ ശ്രദ്ധിക്കുന്ന താളുകളുടെ പട്ടിക തിരുത്തുക",
        "rcfilters-watchlist-showupdated": "മാറ്റങ്ങൾ ഉണ്ടായ ശേഷം താങ്കൾ സന്ദർശിക്കാത്ത താളുകളിലെ മാറ്റങ്ങൾ, തളിക അടയാളത്തോടൊപ്പം <strong>കടുപ്പിച്ച്</strong> കാണിച്ചിരിക്കുന്നു.",
+       "rcfilters-preference-label": "സമീപകാലമാറ്റങ്ങളുടെ പുതുക്കിയ പതിപ്പ് പ്രദർശിപ്പിക്കേണ്ട",
+       "rcfilters-preference-help": "സമ്പർക്കമുഖത്തിൽ 2017 വരുത്തിയ രൂപകല്പനാമാറ്റങ്ങളും അതോടൊപ്പവും പിന്നീടും ചേർത്ത എല്ലാ ഉപകരണങ്ങളും ഒഴിവാക്കുക.",
+       "rcfilters-filter-showlinkedfrom-label": "കണ്ണി ചേർക്കപ്പെട്ട താളുകളിലെ മാറ്റങ്ങൾ കാണിക്കുക",
+       "rcfilters-filter-showlinkedfrom-option-label": "തിരഞ്ഞെടുത്ത താളിൽ <strong>കണ്ണി ചേർക്കപ്പെട്ട താളുകൾ</strong>",
        "rcnotefrom": "<strong>$3, $4</strong> മുതലുള്ള {{PLURAL:$5|മാറ്റം|മാറ്റങ്ങൾ}} ആണ് താഴെയുള്ളത്  (<strong>$1</strong> എണ്ണം വരെ കൊടുക്കുന്നതാണ്).",
        "rclistfromreset": "തീയതി എടുത്തത് പുനഃസജ്ജീകരിക്കുക",
        "rclistfrom": "$3 $2 മുതലുള്ള മാറ്റങ്ങൾ പ്രദർശിപ്പിക്കുക",
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 bd57dcc..6e8d930 100644 (file)
@@ -51,7 +51,8 @@
                        "Suyog",
                        "Matma Rex",
                        "Tiven2240",
-                       "Sureshkhole"
+                       "Sureshkhole",
+                       "Pushkar Ekbote"
                ]
        },
        "tog-underline": "दुव्यांचे अधोरेखन:",
        "may": "मे",
        "jun": "जून",
        "jul": "जुलै",
-       "aug": "ऑग.",
-       "sep": "सप्टें.",
-       "oct": "ऑक्टो.",
-       "nov": "नोव्हें.",
-       "dec": "डिसें.",
+       "aug": "ऑगस्ट",
+       "sep": "सप्टेंबर",
+       "oct": "ऑक्टोबर",
+       "nov": "नोव्हेंबर",
+       "dec": "डिसेंबर",
        "january-date": "जानेवारी $1",
        "february-date": "फेब्रुवारी $1",
        "march-date": "मार्च $1",
        "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": "जा",
        "copyright": "येथील मजकूर $1च्या अंतर्गत उपलब्ध आहे जोपर्यंत इतर नोंदी केलेल्या नाहीत.",
        "copyrightpage": "{{ns:project}}:प्रताधिकार",
        "currentevents": "सद्य घटना",
-       "currentevents-url": "Project:सद्य घटना",
+       "currentevents-url": "प्रकल्प:सद्य घटना",
        "disclaimers": "उत्तरदायित्वास नकार",
-       "disclaimerpage": "Project: सर्वसाधारण उत्तरदायकत्वास नकार",
+       "disclaimerpage": "प्रकल्प : सर्वसाधारण उत्तरदायकत्वास नकार",
        "edithelp": "संपादन साहाय्य",
        "helppage-top-gethelp": "साहाय्य",
        "mainpage": "मुखपृष्ठ",
        "mainpage-description": "मुखपृष्ठ",
        "policy-url": "Project:नीती",
        "portal": "समाज मुखपृष्ठ",
-       "portal-url": "Project:समाज मुखपृष्ठ",
+       "portal-url": "प्रकल्प:समाज मुखपृष्ठ",
        "privacy": "गुप्तता नीती",
-       "privacypage": "Project:गुप्तता नीती",
+       "privacypage": "प्रकल्प:गुप्तता नीती",
        "badaccess": "परवानगी त्रुटी",
        "badaccess-group0": "आपण विनंती केलेल्या क्रियेच्या पूर्ततेचे तुम्हाला अधिकार नाहीत.",
        "badaccess-groups": "आपण विनीत केलेली कृती खालील {{PLURAL:$2|समूहासाठी|पैकी एका समूहासाठी}} मर्यादित आहे: $1.",
        "hidetoc": "लपवा",
        "collapsible-collapse": "निपात करा",
        "collapsible-expand": "विस्तार",
-       "confirmable-confirm": "{{GENDER:$1|आपणास}}खात्री आहे काय?",
+       "confirmable-confirm": "{{लिंगभाव:$1|आपणास}}खात्री आहे काय?",
        "confirmable-yes": "होय",
        "confirmable-no": "नाही",
        "thisisdeleted": "$1चे अवलोकन किंवा पुनर्स्थापन करायचे ?",
        "viewdeleted": " $1चे अवलोकन करायचे?",
        "restorelink": "{{PLURAL:$1|एक वगळलेले संपादन|$1 वगळलेली संपादने}}",
        "feedlinks": "रसद (फिड) :",
-       "feed-invalid": "à¤\85यà¥\8bà¤\97à¥\8dय à¤°à¤¸à¤¦ à¤¨à¥\8bà¤\82दणà¥\80 (Invalid subscription feed type).",
+       "feed-invalid": "à¤\85यà¥\8bà¤\97à¥\8dय à¤¨à¥\8bà¤\82दणà¥\80 à¤ªà¥\8dरà¤\95ार",
        "feed-unavailable": "सिंडीकेशन रसद उपलब्ध नाहीत",
        "site-rss-feed": "$1 आरएसएस रसद",
        "site-atom-feed": "$1 अॅटम रसद (Atom Feed)",
        "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": "'''लक्षात ठेवा की ही फक्त झलक आहे''', बदल अजून जतन करण्यात आलेले नाहीत.",
        "expansion-depth-exceeded-category-desc": "या पानाने उच्चतम प्रसरण-खोली(expansion depth) मर्यादा पार केली.",
        "expansion-depth-exceeded-warning": "लेखाने विस्तार-तळ(एक्सपांशन डेप्थ) पार केला",
        "parser-unstrip-loop-warning": "'अनस्ट्रिप'(अरोखीत) वलय(लुप) आढळले",
-       "parser-unstrip-recursion-limit": "'अनस्ट्रिप'(अरोखीत) आवर्तन मर्यादा पार झाली ($1)",
+       "unstrip-depth-warning": "'अनस्ट्रिप'(अरोखीत) आवर्तन मर्यादा पार झाली ($1)",
        "converter-manual-rule-error": "निदेशपुस्तिकेच्या भाषा अनुरुपण नियमामध्ये त्रुटी आढळली",
        "undo-success": "संपादन परतवले जाऊ शकते.कृपया, आपण नेमके हेच करू इच्छिता तर ते खाली दिलेली तुलना पाहू निश्चित करा,आणि नंतर संपादन परतवण्याचे काम पूर्ण करण्याकरिता इच्छित बदल जतन करा.",
        "undo-failure": "विसंवादी आंतरवर्ती संपादने झाल्यामुळे आपण हे संपादन परतवू शकत नाही.",
        "prefs-files": "संचिका",
        "prefs-custom-css": "सीएसएस पद्धत बदला",
        "prefs-custom-js": "जावास्क्रिप्ट पद्धत बदला",
-       "prefs-common-css-js": "मिळून वापरलेले सर्व त्वचांसाठींचे सीएसएस / जावास्क्रिप्ट:",
+       "prefs-common-config": "मिळून वापरलेले सर्व त्वचांसाठींचे सीएसएस / जावास्क्रिप्ट:",
        "prefs-reset-intro": "आपण या पानाचा वापर, या संकेतस्थळच्या अविचलनुसार, आपला पसंतीक्रम पुनर्स्थापनेसाठी करू शकता.",
        "prefs-emailconfirm-label": "विपत्र निश्चितीकरण:",
        "youremail": "विपत्र:",
        "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..cff3601 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!",
        "expansion-depth-exceeded-category-desc": "Laman telah melebihi kedalaman peluasan maksimum.",
        "expansion-depth-exceeded-warning": "Laman terlebih dalam peluasan",
        "parser-unstrip-loop-warning": "Gelung unstrip dikesan",
-       "parser-unstrip-recursion-limit": "Had rekursi unstrip dilampaui ($1)",
+       "unstrip-depth-warning": "Had rekursi unstrip dilampaui ($1)",
        "converter-manual-rule-error": "Ralat dikesan dalam aturan penukaran bahasa manual",
        "undo-success": "Suntingan ini boleh dibatalkan. Sila semak perbandingan di bawah untuk mengesahkan bahawa anda betul-betul mahu melakukan tindakan ini, kemudian simpan perubahan tersebut.",
        "undo-failure": "Suntingan tersebut tidak boleh dibatalkan kerana terdapat suntingan pertengahan yang bercanggah.",
        "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..516b4fc 100644 (file)
        "category-file-count-limited": "Nesta catadorie hai {{PLURAL:$1|un fexeiro|$1 fexeiros}}.",
        "listingcontinuesabbrev": "cunt.",
        "index-category": "Páiginas ourganizadas an índece",
-       "noindex-category": "Páiginas nun ourganizadas an índece",
+       "noindex-category": "Páiginas nó ourganizadas an índece",
        "broken-file-category": "Páiginas cun lhigaçones scachadas pa fexeiros",
        "about": "Subre",
        "article": "Páigina de cuntenido",
        "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 66a71d2..f7fcfc6 100644 (file)
        "customjsprotected": "你無授權去改這个JavaScript頁,因為這个頁包括別个用者的個人設定。",
        "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-tī--ê su-jîn chu-sìn.",
+       "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>。",
        "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.",
        "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:",
        "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..5d05688 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!'''",
        "expansion-depth-exceeded-category-desc": "Sta paggena appassa 'o lemmeto d'espansione.",
        "expansion-depth-exceeded-warning": "Sta paggena ha appassato 'o lemmeto 'e futo 'e spansione",
        "parser-unstrip-loop-warning": "Scummigliato aniello Unstrip",
-       "parser-unstrip-recursion-limit": "Appassato 'o lémmeto 'e ricurzione d' Unstrip ($1)",
+       "unstrip-depth-warning": "Appassato 'o lémmeto 'e ricurzione d' Unstrip ($1)",
        "converter-manual-rule-error": "È stato scummigliato n'errore dint'a regola manuale 'e converziona 'e lengua",
        "undo-success": "'O cagnamiento se può annullà.\nPe' piacere vedete 'e differenze mmustate nfra 'e verziune pe' te ffà capace ca 'e cuntenute songo bbuone, e astipate 'e cagnamiente ccà abbascio pe' fernì e accussì turnà arreto.",
        "undo-failure": "Nun se può fà turnà arreto 'o cagnamiento pecché ce sta nu conflitto ch' 'e cagnamiente intermedie.",
        "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..9a00b9c 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å!",
        "expansion-depth-exceeded-category-desc": "Siden overskrider maksimal utvidingsdybde.",
        "expansion-depth-exceeded-warning": "Sida har overskredet ekspansjonsdybden",
        "parser-unstrip-loop-warning": "«Unstrip»-loop påvist",
-       "parser-unstrip-recursion-limit": "Rekursjonsgrense for taggfjerning overskredet ($1)",
+       "unstrip-depth-warning": "Rekursjonsgrense for taggfjerning overskredet ($1)",
        "converter-manual-rule-error": "En feil ble oppdaget i en manuell språkkonverteringsregel",
        "undo-success": "Redigeringen kan omgjøres. Sjekk sammenligningen under for å bekrefte at du vil gjøre dette, og lagre endringene for å fullføre omgjøringen.",
        "undo-failure": "Redigeringen kunne ikke omgjøres på grunn av konflikterende etterfølgende redigeringer.",
        "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..2e4bc59 100644 (file)
        "moredotdotdot": "Meer...",
        "morenotlisted": "Disse lieste is niet kompleet...",
        "mypage": "Gebrukerszied",
-       "mytalk": "Mien overleg",
+       "mytalk": "Myn oaverleg",
        "anontalk": "Overleg",
        "navigation": "Navigasie",
        "and": "&#32;en",
        "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!",
        "expansion-depth-exceeded-category": "Ziejen waor de expansiediepte overschrejen is",
        "expansion-depth-exceeded-warning": "Op disse zied staon te veule mallen",
        "parser-unstrip-loop-warning": "Der is n \"unstrip\"-lusse evunnen",
-       "parser-unstrip-recursion-limit": "De rekursielimiet ($1) veur \"unstrip\" is overschrejen",
+       "unstrip-depth-warning": "De rekursielimiet ($1) veur \"unstrip\" is overschrejen",
        "converter-manual-rule-error": "Der is n fout evunnen in n haandmaotig in-evoegden taalkonversieregel.",
        "undo-success": "De bewarking kan weerummedreid wörden. Kiek de vergelieking hieronder nao um der wisse van de ween dat alles goed is, en slao de de zied op um de bewarking weerumme te dreien.",
        "undo-failure": "De wieziging kon niet weerummedreid wörden umdat t ondertussen awweer ewiezigd is.",
        "searchdisabled": "Zeuken in {{SITENAME}} is niet meugelik. Je kunnen gebruukmaken van Google. De gegevens over {{SITENAME}} bin misschien niet bie-ewörken.",
        "search-error": "Der is wat mis-egaon bie t zeuken: $1",
        "preferences": "Veurkeuren",
-       "mypreferences": "Mien veurkeuren",
+       "mypreferences": "Myn vöärköären",
        "prefs-edits": "Antal bewarkingen:",
        "prefs-skin": "{{SITENAME}}-uterlik",
        "skin-preview": "bekieken",
        "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",
        "usermessage-summary": "Systeemteksten achter-eleuten",
        "usermessage-editor": "Systeemtekste",
        "watchlist": "Volglieste",
-       "mywatchlist": "Mien volglieste",
+       "mywatchlist": "Myn volglyste",
        "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..6d833e0 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": "'''याद राख्नुहोस् यो केवल पूर्वावलोकन मात्र हो; तपाईंका परिवर्तनहरू संग्रहित भएका छैनन्!'''",
        "expansion-depth-exceeded-category-desc": "यस पृष्ठले उच्चतम विस्तार सिमा नाघेको छ ।",
        "expansion-depth-exceeded-warning": "पृष्ठले विस्तार सिमालाई नाधेको छ",
        "parser-unstrip-loop-warning": "अनस्ट्रिप लुप देखिन्छ",
-       "parser-unstrip-recursion-limit": "अन्स्ट्रिप पुनरावर्तन सिमा पार गरियो ($1)",
+       "unstrip-depth-warning": "अन्स्ट्रिप पुनरावर्तन सिमा पार गरियो ($1)",
        "converter-manual-rule-error": "म्यानुअल भाषा अनुवाद सिध्दान्तमा समस्या देखियो",
        "undo-success": "सम्पादन उल्टाउन सकिन्छ।\nतपाईंले चाहेको कार्य गर्न कृपया तल दिएको तुलना जाँच गर्नुहोस्, र गरिएको सम्पादनलाई  अघिकै अवस्थामा लैजाने कार्य सम्पन्न गर्न तल गएर संग्रह  गर्नुहोस्।",
        "undo-failure": "सम्पादनमा अन्तर्द्वन्द्वको कारण सम्पादन रद्द गर्न सकिन्न।",
        "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": "ईमेल",
        "editusergroup": "प्रयोगकर्ता समूह सम्पादन गर्नुहोस्",
        "editinguser": "प्रयोगकर्ता '''[[User:$1|$1]]''' $2 को अधिकार परिवर्तन गर्ने\n{{GENDER:$1|प्रयोगकर्ता}}को प्रयोगकर्ता अधिकार परिवर्तन हुँदैछ <strong>[[User:$1|$1]]</strong> $2",
        "userrights-editusergroup": "प्रयोगकर्ता समूह सम्पादन गर्नुहोस्",
+       "userrights-viewusergroup": "{{GENDER:$1|प्रयोगकर्ता}} समूहहरू हेर्नुहोस्।",
        "saveusergroups": "प्रयोगकर्ता समूहरू संग्रह गर्नुहोस्",
        "userrights-groupsmember": "को सदस्य:",
        "userrights-groupsmember-auto": "अंतर्निहित सदस्य:",
        "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": "तपाईंको प्रयोगकर्ता अभिरूचीहरूलाई सम्पादन गर्नुहोस्",
+       "grant-sendemail": "अन्य प्रयोगकर्ताहरूलाई इमेल पठाउने",
        "grant-viewdeleted": "मेटाइएका फाइल तथा पृष्ठहरू हेर्ने",
        "newuserlogpage": "प्रयोगकर्ता श्रृजना लग",
        "newuserlogpagetext": "यो प्रयोगकर्ता सिर्जनाको लग हो ।",
        "recentchanges-legend-plusminus": "(''±१२३'')",
        "recentchanges-submit": "देखाउनुहोस्",
        "rcfilters-tag-remove": "'$1' हटाउनुहोस्",
+       "rcfilters-days-show-days": "$1 {{PLURAL:$1|दिन|दिनहरू}}",
+       "rcfilters-days-show-hours": "$1 {{PLURAL:$1|घण्टा|घण्टाहरू}}",
+       "rcfilters-savedqueries-setdefault": "पूर्वनिर्धारितको रुपमा सेट गर्नुहोस्",
+       "rcfilters-savedqueries-remove": "हटाउनुहोस्",
+       "rcfilters-savedqueries-new-name-label": "नाम",
+       "rcfilters-savedqueries-cancel-label": "रद्द गर्नुहोस्",
        "rcfilters-show-new-changes": "नवीनतम परिवर्तनहरू हेर्नुहोस्",
+       "rcfilters-filterlist-title": "फिल्टरहरू",
+       "rcfilters-filter-bots-label": "बोट",
+       "rcfilters-filter-minor-label": "सामान्य सम्पादनहरू",
        "rcfilters-filter-watchlistactivity-unseen-label": "नहेरिएका परिवर्तनहरू",
        "rcfilters-filter-watchlistactivity-seen-label": "हेरिएका परिवर्तनहरू",
+       "rcfilters-filter-lastrevision-label": "हालको संस्करण",
        "rcnotefrom": "तल <strong>$2</strong> देखि (<strong>$1</strong> सम्म) {{PLURAL:$5|भएका परिवर्तनहरू देखाइएको छ|भएका परिवर्तनहरू देखाइन्छ}}।",
        "rclistfrom": "$3 $2 देखिका नयाँ परिवर्तनहरू देखाउनु",
        "rcshowhideminor": "$1 सामान्य सम्पादन",
        "watchthisupload": "यो पृष्ठ निगरानी गर्नुहोस्",
        "filewasdeleted": "यस नामको एक फाइल पहिले पनि अपलोड गरे पछि हटाई सकिएको छ।\nपुनः अपलोड गर्नु पूर्व तपाईं $1 लाई राम्रोसँग जाँच गर्नुहोला।",
        "filename-bad-prefix": "तपाईं जुन फाइल अपलोड गर्दै हुनुहुन्छ त्यसको नाम <strong>\"$1\"</strong>बाट शुरू हुन्छ, जुन डिजिटल क्यामराद्वारा दिइएको नाम हो।\nकृपया यस फाइलको लागि कुनै दोश्रो अधिक जानकारीयुक्त नाम छान्नुहोस्।",
+       "filename-prefix-blacklist": " #<!-- leave this line exactly as it is --> <pre>\n# Syntax is as follows:\n#   * Everything from a \"#\" character to the end of the line is a comment\n#   * Every non-blank line is a prefix for typical filenames assigned automatically by digital cameras\nCIMG # Casio\nDSC_ # Nikon\nDSCF # Fuji\nDSCN # Nikon\nDUW # some mobile phones\nIMG # generic\nJD # Jenoptik\nMGP # Pentax\nPICT # misc.\n #</pre> <!-- leave this line exactly as it is -->",
        "upload-proto-error": "दूषित प्रोटोकल",
        "upload-proto-error-text": "दुर उर्ध्वभरण ठेगाना URLs  <code>http://</code> or <code>ftp://</code> बाट सुरु भएको हुनु पर्छ ।",
        "upload-file-error": "आन्तरिक त्रुटि",
        "upload-copy-upload-invalid-domain": "यस डोमेनबाट अपलोडहरूको प्रतिलिपि गर्न सकिदैन् ।",
        "upload-dialog-title": "फाइल अपलोड गर्ने",
        "upload-dialog-button-cancel": "रद्द गर्ने",
+       "upload-dialog-button-back": "पछाडि",
        "upload-dialog-button-done": "सकियो",
        "upload-dialog-button-save": "सङ्ग्रह गर्ने",
        "upload-dialog-button-upload": "अपलोड गर्ने",
        "apihelp-no-such-module": "मोड्युल \"$1\" भेटिएन ।",
        "apisandbox-submit": "अनुरोध गर्ने",
        "apisandbox-reset": "हटाउने",
+       "apisandbox-retry": "पुनः प्रयास गर्नुहोस्",
        "apisandbox-examples": "उदाहरण",
        "apisandbox-results": "परिणाम",
        "apisandbox-request-url-label": "अनुरोध युआरयल:",
        "apisandbox-request-time": "अनुरोधको समयावधी: $1",
+       "apisandbox-continue-clear": "खाली गर्नुहोस्",
        "booksources": "किताबका श्रोतहरु",
        "booksources-search-legend": "किताबका श्रोतहरु खोज्ने",
        "booksources-search": "खोज",
        "listgrouprights-namespaceprotection-header": "नामपद रोक",
        "listgrouprights-namespaceprotection-namespace": "नामपद",
        "listgrouprights-namespaceprotection-restrictedto": "प्रयोगकर्तालाई सम्पादन गर्ने अधिकार(हरू) दिने",
+       "listgrants-rights": "अधिकारहरू",
        "trackingcategories": "श्रेणीहरू पछ्याउने",
        "trackingcategories-summary": "यस पृष्ठमा ती जोडिने श्रेणिहरूको सूची भेट्दछ जुन स्वतः रूपले मिडियाविकी सफ्टवेयरद्वारा बनाइने गरिन्छ। उनीहरूको नाम सम्बन्धित प्रणाली सन्देशलाई परिवर्तनले {{ns:8}} नामस्थानमा परिवर्तन गर्न सकिन्छ।",
        "trackingcategories-msg": "श्रेणी पछ्याउने",
        "enotif_lastdiff": "यस परिवर्तनको निम्ति यो $1 हेर्नुहोस्",
        "enotif_anon_editor": "अज्ञात  प्रयोगकर्ता  $1",
        "enotif_body": "प्रिय $WATCHINGUSERNAME,\n\n\n{{SITENAME}}को पृष्ठ $PAGETITLE  $PAGEEDITDATE को दिन $PAGEEDITORद्वारा $CHANGEDORCREATED, \nहालको संशोधनको निम्ति हेर्नुहोस्  $PAGETITLE_URL ।\n\n$NEWPAGE\n\nसम्पादकको सारांश: $PAGESUMMARY $PAGEMINOREDIT\n\nसम्पादकसित सम्पर्क राख्नुहोस्:\nमेल: $PAGEEDITOR_EMAIL\nविकि: $PAGEEDITOR_WIKI\n\nतपाईं यस पृष्ठमा नगएसम्म अब उसो कुनै परिवर्तन भएका खण्डमा कुनै सूचना दिनेछैन।\nतपाईंका सम्पूर्ण निगरानी पृष्ठहरूको लागि तपाईंले सूचना पताकालाई निगरानी सूचीमा पुनर्बहाली गर्न सक्नुहुन्छ। \n\n             तपाईंको मित्र {{SITENAME}} सूचना प्रणाली\n--\nइमेल सूचना व्यवस्था परिवर्तन गर्न, जानुहोस्\n{{canonicalurl:{{#special:Preferences}}}}\n\nनिगरानी सूची व्यवस्थित गर्न, जानुहोस्\n{{canonicalurl:{{#special:EditWatchlist}}}}\n\nनिगरानी सूची मेट्न, जानुहोस्\n$UNWATCHURL\n\nप्रतिक्रिया र अन्य सहयोगको निम्ति:\n$HELPPAGE",
+       "enotif_minoredit": "यो सानो सम्पादन हो",
        "created": "बनाइएको",
        "changed": "परिवर्तन भइसकेको",
        "deletepage": "पृष्ठ मेट्नुहोस्",
        "sessionfailure": "यस्तो लाग्दैछ कि तपाईंको लगइन सत्रसँग कुनै समस्या छ। सत्र अपहरणबाट बचाउन को लागि सावधानीको रूपमा तपाईंको यो क्रियाकलाप रद्द गरिएको छ। कृपया पछाडी जानुहोस र पृष्ठलाई पुनः लोड गर्नुहोस्, अनि फेरी प्रयास गर्नुहोला।",
        "changecontentmodel-title-label": "पाना शीर्षक",
        "changecontentmodel-reason-label": "कारण:",
+       "changecontentmodel-submit": "परिवर्तन गर्नुहोस्",
        "logentry-contentmodel-change-revertlink": "पहिलेको रुपमा फर्काउने",
        "logentry-contentmodel-change-revert": "पहिलेको रुपमा फर्काउने",
        "protectlogpage": "सुरक्षण लग",
        "undelete-search-title": "मेटिएका पृष्ठहरू खोज्नुहोस्",
        "undelete-search-box": "मेटिएका पृष्ठहरू खोज्नुहोस्",
        "undelete-search-prefix": "बाट सुरु हुने  पृष्ठहरू देखाउनुहोस :",
+       "undelete-search-full": "पृष्ठमा निम्न भएको शीर्षक देखाउनुहोस्:",
        "undelete-search-submit": "खोजी गर्नुहोस्",
        "undelete-no-results": "मेटिएका पृष्ठहरूको अभिलेखमा कुनै पनि मिल्दो पृष्ठहरू भेटिएन ।",
        "undelete-filename-mismatch": " $1 समय छाप भएको मेटिएको संस्करण पुन: स्थापना गर्न सकिएन : फाइलनाम अमिल्दो",
        "unblocked-id": "$1 रोक हटाइएको छ",
        "unblocked-ip": "[[Special:Contributions/$1|$1]] खुला भएको छ ।",
        "blocklist": "निषेधित प्रयोगकर्ताहरू",
+       "autoblocklist-submit": "खोज्नुहोस्",
        "ipblocklist": "निषेधित प्रयोगकर्ताहरू",
        "ipblocklist-legend": "रोकलगाइएका प्रयोगकर्ताहरू खोज्नुहोस",
        "blocklist-userblocks": "खाता निषेधित  लुकाउने",
        "export-download": "सङ्ग्रह गर्ने",
        "export-templates": "ढाँचाहरू संलग्न गर्नुहोस्",
        "export-pagelinks": "जोडिएको पृष्ठलाई यस गहराईसम्म समाबेश गर्नेः",
+       "export-manual": "पृष्ठहरू आफैँ थप्नुहोस्:",
        "allmessages": "सिस्टम सन्देशहरू",
        "allmessagesname": "नाम",
        "allmessagesdefault": "डिफल्ट सन्देश पाठ",
        "pageinfo-length": "पृष्ठ लम्बाई (बाईटमा)",
        "pageinfo-article-id": "पृष्ठ परिचय",
        "pageinfo-language": "पृष्ठ सामग्रीको भाषा",
+       "pageinfo-language-change": "परिवर्तन",
        "pageinfo-content-model": "पृष्ठ सामाग्री नमुना",
+       "pageinfo-content-model-change": "परिवर्तन",
        "pageinfo-robot-policy": "रोबोटहरूद्वारा अनुक्रमणिका",
        "pageinfo-robot-index": "अनुमति भएको",
        "pageinfo-robot-noindex": "अनुमति नभएको",
        "pageinfo-category-pages": "पृष्ठहरूको संख्या",
        "pageinfo-category-subcats": "उपश्रेणीहरूको संख्या",
        "pageinfo-category-files": "फाइलहरूको संख्या",
+       "pageinfo-user-id": "प्रयोगकर्ता परिचय",
        "markaspatrolleddiff": "गस्ती गरिएको(patrolled) को रुपमा चिनो लगाउने",
        "markaspatrolledtext": "यस पृष्ठलाई गस्ती गरिएको(patrolled) को रुपमा चिनो लगाउने",
        "markedaspatrolled": "गस्ती गरिएको(patrolled) को रुपमा चिनो लगाइयो",
        "patrol-log-header": "गस्ती गरिएका संस्करणहरूको लग यस प्रकार रहेका छन् ।",
        "log-show-hide-patrol": "$1 निगरानी लग",
        "log-show-hide-tag": "$1 ट्याग लग",
+       "confirm-markpatrolled-button": "ठीक छ",
        "deletedrevision": "पुराना पुनरावलोकनहरू $1 मेटिए",
        "filedeleteerror-short": "$1 फाइल मेटाइमा भूल",
        "filedeleteerror-long": "निम्न फाइल मेट्ने क्रममा त्रुटी भयो:\n\n$1",
        "newimages-summary": "यस विशेष पृष्ठले अन्तिम उर्ध्वभरण गरिएका फाइलहरू देखाउँछ ।",
        "newimages-legend": "फिल्टर",
        "newimages-label": "फाइल अथवा (यसको एउटा अंश)को नाम:",
+       "newimages-newbies": "नयाँ खाताको योगदानहरू मात्र देखाउने",
        "newimages-showbots": "बोटहरूद्वारा गरिएको अपलोड देखाउने",
        "noimages": "हेर्नको लागि केही छैन.",
        "ilsubmit": "खोज्नुहोस्",
        "exif-xyresolution-c": "$1 प्रतिसेन्टिमिटरथोप्लाहरु(डिपिसी)",
        "exif-colorspace-65535": "स्तरिकरण नगरिएको",
        "exif-componentsconfiguration-0": "अस्तित्वमा छैन",
+       "exif-componentsconfiguration-4": "क",
+       "exif-componentsconfiguration-6": "ब",
        "exif-exposureprogram-0": "खुलाइएको छैन",
        "exif-exposureprogram-1": "स्वयं",
        "exif-exposureprogram-2": "साधारण कार्यक्रम",
        "confirm-watch-top": "के यस पृष्ठलाई निगरानी सुचीमा थप्ने हो ?",
        "confirm-unwatch-button": "ठीक",
        "confirm-unwatch-top": "के यो पृष्ठलाई निगरानी सुचीबाट हटाउने हो ?",
+       "confirm-rollback-button": "ठीक छ",
        "semicolon-separator": ";&#32;",
        "comma-separator": ",&#32;",
        "colon-separator": ":&#32;",
        "tag-filter": "[[Special:Tags|पुच्छर]] फिल्टर:",
        "tag-filter-submit": "फिल्टर",
        "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|ट्याग|ट्यागहरू}}]]: $2)",
+       "tag-mw-undo": "अन्डू गर्नुहोस्",
        "tags-title": "ट्यागहरु",
        "tags-intro": "यो पृष्ठले ट्यागहरू सूचीकृत गर्छ जससँग यो सफ्टवेयरले चिनो लगाउन र सम्पादन गर्न सक्छ र तिनका अर्थहरू ।",
        "tags-tag": "आन्तरिक ट्याग नाम",
        "compare-title-not-exists": "तपाईंले खुलाउनु भएको शिर्षक उपलब्ध छैन ।",
        "compare-revision-not-exists": "तपाईंले खुलाउनु भएको संस्करण उपलब्ध छैन ।",
        "diff-form": "भिन्नताहरू",
+       "permanentlink": "स्थायी लिङ्क",
        "dberr-problems": "क्षमा पाउँ! यो साइटमा तकनीकी गड़बड़ी आइपरेकोछ।",
        "dberr-again": "केही समय पर्खिएर पुन: लोड हुन दिनुहोस् ।",
        "dberr-info": "(डेटाबेस सर्भर: $1 सँग सम्पर्क स्थापित गर्न सकिएन)",
        "htmlform-cloner-create": "अरू जोड्ने",
        "htmlform-cloner-delete": "हटाउने",
        "htmlform-cloner-required": "कम्तिमा एउटामा आवश्यक छ ।",
+       "htmlform-user-not-exists": "'''$1''' अस्तित्वमा छैन ।",
        "logentry-delete-delete": "$1 द्वारा पृष्ठ $3 {{GENDER:$2|मेटाइयो}}",
        "logentry-delete-restore": "$1 {{GENDER:$2|पुनर्स्थापित}} पृष्ठ $3 ($4)",
        "logentry-delete-event": "$1 ले $3 पृष्ठको लग {{PLURAL:$5|प्रविष्टि|प्रविष्टिहरू}}को दृश्यता {{GENDER:$2|परिवर्तन गर्यो}}: $4",
        "pagelang-language": "भाषा",
        "pagelang-use-default": "पूर्वनिर्धारित भाषा प्रयोग गर्ने",
        "pagelang-select-lang": "भाषा छान्ने",
+       "pagelang-reason": "कारण",
        "pagelang-submit": "बुझाउने",
        "right-pagelang": "पृष्ठको भाषा परिवर्तन गर्ने",
        "action-pagelang": "यस पृष्ठको भाषा परिवर्तन गर्ने",
        "special-characters-title-minus": "घटाउने चिन्ह",
        "mw-widgets-titleinput-description-new-page": "हालसम्म पृष्ठ उपलब्ध छैन्",
        "mw-widgets-titleinput-description-redirect": "$1 मा जाने",
-       "randomrootpage": "यादृच्छिक शीर्ष पृष्ठ"
+       "randomrootpage": "यादृच्छिक शीर्ष पृष्ठ",
+       "log-action-filter-all": "सबै",
+       "log-action-filter-block-block": "रोक्ने",
+       "log-action-filter-block-unblock": "फुक्का गर्ने",
+       "authmanager-userdoesnotexist": "प्रयोगकर्ता खाता \"$1\" दर्ता गरिएको छैन।",
+       "authmanager-email-label": "ईमेल",
+       "authmanager-email-help": "इमेल ठेगाना",
+       "authprovider-resetpass-skip-label": "छोड्नुहोस्",
+       "edit-error-short": "त्रुटि: $1"
 }
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..c9b4443 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!",
        "expansion-depth-exceeded-category-desc": "De pagina overschrijdt de maximale expansiediepte.",
        "expansion-depth-exceeded-warning": "De pagina bevat te veel sjablonen",
        "parser-unstrip-loop-warning": "Er is een \"unstrip\"-lus gedetecteerd",
-       "parser-unstrip-recursion-limit": "De recursielimiet ($1) voor \"unstrip\" is overschreden",
+       "unstrip-depth-warning": "De recursielimiet ($1) voor \"unstrip\" is overschreden",
        "converter-manual-rule-error": "Er is een fout gedetecteerd in een handmatig toegevoegde taalconversieregel.",
        "undo-success": "Deze bewerking kan ongedaan gemaakt worden.\nHieronder staat de tekst waarin de wijziging ongedaan is gemaakt.\nControleer voor het opslaan of het resultaat gewenst is.",
        "undo-failure": "De wijziging kan niet ongedaan gemaakt worden vanwege andere strijdige wijzigingen.",
        "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 ed3f733..115a9e9 100644 (file)
        "nosuchusershort": "Det finst ikkje nokon brukar med brukarnamnet «$1». Sjekk at du har skrive rett.",
        "nouserspecified": "Du må oppgje eit brukarnamn.",
        "login-userblocked": "Denne brukaren er blokkert. Innlogging er ikkje tillate.",
-       "wrongpassword": "Du har oppgjeve eit ugyldig passord. Prøv om att.",
+       "wrongpassword": "Rangt brukarnamn eller passord vart oppgjeve. Prøv om att.",
        "wrongpasswordempty": "Du oppgav ikkje noko passord. Ver venleg og prøv igjen.",
        "passwordtooshort": "Passord må innehalda minst {{PLURAL:$1|eitt teikn|$1 teikn}}.",
        "passwordtoolong": "Passord kan ikkje vera lengre enn {{PLURAL:$1|eitt|$1}} teikn.",
        "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!",
        "expansion-depth-exceeded-category": "Sider der utvidingsdjupna er overskriden",
        "expansion-depth-exceeded-warning": "Sida har overskride utvidingsdjupna",
        "parser-unstrip-loop-warning": "Det vart oppdaga ei løkke i Unstrip-funksjonen",
-       "parser-unstrip-recursion-limit": "Rekursjonsgrensa for Unstrip-funksjonen er overskriden ($1)",
+       "unstrip-depth-warning": "Rekursjonsgrensa for Unstrip-funksjonen er overskriden ($1)",
        "converter-manual-rule-error": "Det vart oppdaga ein feil i ein manuell språkkonverteringsregel",
        "undo-success": "Endringa kan angrast.\nSjå på samanlikninga under for å stadfesta at dette er det du ynskjer å gjera. Deretter kan du lagra desse endringane for å fullføra angringa.",
        "undo-failure": "Endringa kunne ikkje attenderullast grunna konflikt med endringar som er gjorde i mellomtida.",
        "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}}",
+       "prevn": "{{PLURAL:$1|førre|førre $1}}",
        "nextn": "{{PLURAL:$1|neste|neste $1}}",
        "prev-page": "førre sida",
        "next-page": "neste side",
        "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",
        "right-siteadmin": "Låse og låse opp databasen",
        "right-override-export-depth": "Eksporter sider inkludert lenkte sider til ei djupn på 5",
        "right-sendemail": "Senda e-post til andre brukarar",
+       "grant-group-high-volume": "Utføra høgvolumaktivitet",
        "grant-blockusers": "Blokkera og avblokkera brukarar",
        "grant-createaccount": "Oppretta kontoar",
        "grant-createeditmovepage": "Oppretta, endra og flytta sider",
        "grant-delete": "Sletta sider, versjonar og loggoppføringar",
        "grant-editinterface": "Gjera endringar i MediaWiki-namnerommet og i CSS/JavaScript for brukarkontoen",
+       "grant-highvolume": "Høgvolumendring",
        "grant-protect": "Verna og avverna sider",
        "grant-sendemail": "Senda e-post til andre brukarar",
        "grant-uploadeditmovefile": "Lasta opp, byta ut og flytta 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 6d4e26d..731c447 100644 (file)
        "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 !",
        "expansion-depth-exceeded-category-desc": "La pagina depassa la prigondor d’espandiment maximala.",
        "expansion-depth-exceeded-warning": "Pagina depassant la prigondor d'espandiment",
        "parser-unstrip-loop-warning": "Bocla pas desmontabla detectada",
-       "parser-unstrip-recursion-limit": "Limit de recursion pas desmontable depassat ($1)",
+       "unstrip-depth-warning": "Limit de recursion pas desmontable depassat ($1)",
        "converter-manual-rule-error": "Error detectada dins la règla manuala de conversion de lenga",
        "undo-success": "Aquesta modificacion va èsser desfaita. Confirmatz los cambiaments (visibles en bas d'aquesta pagina), puèi salvatz se sètz d’acòrdi. Mercés de motivar l’anullacion dins la bóstia de resumit.",
        "undo-failure": "Aquesta modificacion a pas pogut èsser desfaita a causa de conflictes amb de modificacions intermediàrias.",
        "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 :",
index 3fbf2c4..cfcc21d 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ଆପଣ କରିଥିବା ବଦଳସବୁ ଏଯାଏଁ ସାଇତା ଯାଇନାହିଁ!",
        "expansion-depth-exceeded-category-desc": "ଏହି ପୃଷ୍ଠାର ସର୍ବାଧିକ ଲମ୍ବା ହେବା ଠାରୁ ବଳିଗଲାଣି",
        "expansion-depth-exceeded-warning": "ପୃଷ୍ଠାଟି ବିସ୍ତ୍ରୁତ ଗଭୀରତାରୁ ଅଧିକ ହୋଇଗଲା",
        "parser-unstrip-loop-warning": "ଅଜଣା ଲୁପ ଜଣାପଡିଲା",
-       "parser-unstrip-recursion-limit": "ଅଜଣା ଚକ୍ରର ସୀମା ଅତ୍ୟଧିକ ହୋଇଗଲା ($1)",
+       "unstrip-depth-warning": "ଅଜଣା ଚକ୍ରର ସୀମା ଅତ୍ୟଧିକ ହୋଇଗଲା ($1)",
        "converter-manual-rule-error": "ଆପେ ଆପେ ଭାଷା ପରିବର୍ତ୍ତନ ନିଯମରେ ଭୁଲ ଅଛି",
        "undo-success": "ଏହି ସମ୍ପାଦନା ପଛକୁ ଫେରାଯାଇପାରିବ ନାହିଁ ।\nଦୟାକରି ତୁଳନା କରି ପରଖିନିଅନ୍ତୁ ଯେ ଆପଣ ଏହାହିଁ କରିବାକୁ ଚାହୁଁଥିଲେ, ଆଉ ସମ୍ପାଦନା ଶେଷ କରିବା ପାଇଁ ତଳେ ଥିବା ବଦଳ ସାଇତି ରଖନ୍ତୁ ।",
        "undo-failure": "ଏହି ସମ୍ପାଦନା ପଛକୁ ଫେରାଯାଇ ପାରିବ ନାହିଁ କାରଣ ମଝିରେ ଘଟିଥିବା ଅନେକ ଛୋଟ ଛୋଟ ବଦଳ ଅସୁବିଧା ତିଆରି କରୁଅଛି ।",
        "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..ae68720 100644 (file)
@@ -95,7 +95,8 @@
                        "Sebek Adamowicz",
                        "Cholewka",
                        "Ankam",
-                       "Anwar2"
+                       "Anwar2",
+                       "Acamicamacaraca"
                ]
        },
        "tog-underline": "Podkreślenie linków:",
        "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!",
        "expansion-depth-exceeded-category-desc": "Strona przekracza maksymalną głębokość rozbudowy.",
        "expansion-depth-exceeded-warning": "Strona przekroczyła głębokość rozbudowy",
        "parser-unstrip-loop-warning": "Wykryto nieskończoną pętlę",
-       "parser-unstrip-recursion-limit": "Przekroczono maksymalną głębokość zagnieżdżania ($1)",
+       "unstrip-depth-warning": "Przekroczono maksymalną głębokość zagnieżdżania ($1)",
        "converter-manual-rule-error": "Błąd w językowych regułach konwersji",
        "undo-success": "Edycja może zostać wycofana. Porównaj ukazane poniżej różnice między wersjami, a następnie zapisz zmiany.",
        "undo-failure": "Edycja nie może zostać wycofana z powodu konfliktu z wersjami pośrednimi.",
        "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.",
        "uploadstash-file-too-large": "Nie można wyświetlić pliku większego niż $1 bajtów.",
        "uploadstash-not-logged-in": "Użytkownik nie jest zalogowany, a pliki muszą należeć do użytkowników.",
        "uploadstash-wrong-owner": "Ten plik ($1) nie należy do bieżącego użytkownika.",
+       "uploadstash-no-such-key": "Nie ma takiego klucza ($1), nie można usunąć.",
        "uploadstash-no-extension": "Rozszerzenie ma wartość zerową.",
        "uploadstash-zero-length": "Plik ma zerowy rozmiar.",
        "invalid-chunk-offset": "Nieprawidłowe przesunięcie fragmentu",
        "thumbnail_dest_directory": "Nie można utworzyć katalogu docelowego",
        "thumbnail_image-type": "Grafika tego typu nie jest obsługiwana",
        "thumbnail_gd-library": "Niekompletna konfiguracja biblioteki GD – brak funkcji $1",
+       "thumbnail_image-size-zero": "Rozmiar pliku obrazu wydaje się wynosić zero.",
        "thumbnail_image-missing": "Chyba brakuje pliku $1",
        "thumbnail_image-failure-limit": "Ostatnio było zbyt wielu nieudanych prób ($1 lub więcej) utworzenia miniaturki. Spróbuj ponownie później.",
        "import": "Import stron",
        "watchlistedit-clear-titles": "Tytuły:",
        "watchlistedit-clear-submit": "Wyczyść listę obserwowanych (to jest nieodwracalne!)",
        "watchlistedit-clear-done": "Twoja lista obserwowanych została wyczyszczona.",
+       "watchlistedit-clear-jobqueue": "Twoja lista obserwowanych jest czyszczona. To może zająć trochę czasu!",
        "watchlistedit-clear-removed": "{{PLURAL:$1|1 strona została usunięta|$1 stron zostało usunięte}}:",
        "watchlistedit-too-many": "To zbyt wiele stron do wyświetlenia ich tutaj.",
        "watchlisttools-clear": "wyczyść listę",
index 9644648..ffc4456 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à!",
        "expansion-depth-exceeded-category-desc": "La pàgina a l'ha passà la profondità d'espansion.",
        "expansion-depth-exceeded-warning": "La pàgina a l'ha sorpassà la profondità d'espansion",
        "parser-unstrip-loop-warning": "Trovà un sicl nen dësmontàbil",
-       "parser-unstrip-recursion-limit": "Sorpassà ël lìmit d'arcorensa nen dësmontàbil: $1",
+       "unstrip-depth-warning": "Sorpassà ël lìmit d'arcorensa nen dësmontàbil: $1",
        "converter-manual-rule-error": "Eror trovà ant la régola ëd conversion manual ëd la lenga",
        "undo-success": "Sta modìfica-sì as peul scancelesse. Për piasì, ch'a contròla ambelessì sota për esse sigur che a l'é pro lòn che a veul fé, e peuj ch'as salva lòn ch'a l'ha butà chiel/chila për finì dë scancelé la modìfica ch'a-i era.",
        "undo-failure": "Sta modìfica a l'é nen podusse scancelé për via che a-i son dle contradission antra version antrames.",
        "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 985ec41..26f15f2 100644 (file)
        "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": "'''اے ہلے کچا کم اے؛ تبدیلیاں بچائیاں نہیں گئیاں'''",
        "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 d75932d..fe4db5a 100644 (file)
                        "Fitoschido"
                ]
        },
-       "tog-underline": "Link sublinhado:",
+       "tog-underline": "Ligação sublinhada:",
        "tog-hideminor": "Ocultar edições menores nas mudanças recentes",
        "tog-hidepatrolled": "Ocultar edições patrulhadas nas mudanças recentes",
        "tog-newpageshidepatrolled": "Ocultar páginas patrulhadas da lista de páginas novas",
        "tog-minordefault": "Marcar todas as edições como menores por padrão",
        "tog-previewontop": "Mostrar previsão antes da caixa de edição",
        "tog-previewonfirst": "Mostrar previsão na primeira edição",
-       "tog-enotifwatchlistpages": "Notificar-me por email quando uma página ou arquivo vigiado for alterado",
+       "tog-enotifwatchlistpages": "Notificar-me por e-mail quando uma página ou um arquivo vigiado for alterado",
        "tog-enotifusertalkpages": "Receber email quando minha página de discussão for editada",
-       "tog-enotifminoredits": "Notificar-me por email também sobre edições menores de páginas ou arquivos",
+       "tog-enotifminoredits": "Notificar-me por e-mail também sobre edições menores de páginas ou arquivos",
        "tog-enotifrevealaddr": "Revelar meu endereço de email nas mensagens de notificação",
        "tog-shownumberswatching": "Mostrar o número de usuários que estão vigiando",
        "tog-oldsig": "Assinatura atual:",
-       "tog-fancysig": "Tratar assinatura como wikitexto (sem link automático)",
+       "tog-fancysig": "Tratar assinatura como wikitexto (sem ligação automática)",
        "tog-uselivepreview": "Mostrar visualizações sem recarregar a página",
        "tog-forceeditsummary": "Avisar-me ao introduzir um sumário de edição vazio",
        "tog-watchlisthideown": "Ocultar as minhas edições da lista de páginas vigiadas",
        "tog-prefershttps": "Usar sempre uma conexão segura enquanto estiver conectado",
        "underline-always": "Sempre",
        "underline-never": "Nunca",
-       "underline-default": "Padrão do navegador/skin",
+       "underline-default": "Padrão do navegador/tema",
        "editfont-style": "Estilo da fonte da área de edição:",
        "editfont-monospace": "Fonte monoespaçada",
        "editfont-sansserif": "Fonte sem serifa",
        "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!",
        "expansion-depth-exceeded-category-desc": "A página excede a profundidade de expansão permitida.",
        "expansion-depth-exceeded-warning": "A página excedeu a profundidade de expansão",
        "parser-unstrip-loop-warning": "Foi detectado um ciclo infinito unstrip",
-       "parser-unstrip-recursion-limit": "Limite de recursão do unstrip excedido ($1)",
+       "unstrip-depth-warning": "Limite de recursão do unstrip excedido ($1)",
        "converter-manual-rule-error": "Erro detectado na regra de conversão de língua manual",
        "undo-success": "A edição pôde ser desfeita. Por gentileza, verifique o comparativo a seguir para se certificar de que é isto que deseja fazer, salvando as alterações após ter terminado de revisá-las.",
        "undo-failure": "A edição não pôde ser desfeita devido a alterações intermediárias conflitantes.",
        "savedrights": "Os grupos {{GENDER:$1|do usuário|da usuária}} $1 foram gravados.",
        "timezonelegend": "Fuso horário:",
        "localtime": "Horário local:",
-       "timezoneuseserverdefault": "Utilizar padrão do wiki ($1)",
-       "timezoneuseoffset": "Outro (especifique diferença horária)",
+       "timezoneuseserverdefault": "Utilizar padrão da wiki ($1)",
+       "timezoneuseoffset": "Outro (especificar desvio horário)",
        "servertime": "Horário do servidor:",
        "guesstimezone": "Preencher a partir do navegador",
        "timezoneregion-africa": "África",
        "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:",
        "group-membership-link-with-expiry": "$1 (até $2)",
        "prefs-registration": "Hora de registro:",
        "yourrealname": "Nome verdadeiro:",
-       "yourlanguage": "Língua:",
+       "yourlanguage": "Idioma:",
        "yourvariant": "Variante da língua de conteúdo:",
        "prefs-help-variant": "A sua variante preferida ou ortografia para mostrar no conteúdo das páginas deste wiki.",
        "yournick": "Nova assinatura:",
        "badsig": "Assinatura inválida; verifique o código HTML utilizado.",
        "badsiglength": "A sua assinatura é muito longa.\nEla deve ter menos de $1 {{PLURAL:$1|caractere|caracteres}}.",
        "yourgender": "Como você prefere ser descrito(a)?",
-       "gender-unknown": "Ao mencionar você, o software usará palavras do gênero neutro sempre que possível",
+       "gender-unknown": "Ao mencionar você, o ''software'' usará palavras do gênero neutro sempre que possível",
        "gender-male": "Ele é um usuário",
        "gender-female": "Ela é uma usuária",
-       "prefs-help-gender": "A configuração desta preferência é opcional.\nO software utiliza seu valor para tratá-lo(a) e mencioná-lo(a) a outros usando o gênero gramatical adequado.\nEsta informação será pública.",
+       "prefs-help-gender": "A configuração dessa preferência é opcional.\nO ''software'' utiliza seu valor para tratar e mencionar você a outros usando o gênero gramatical adequado.\nEssa informação será pública.",
        "email": "E-mail",
        "prefs-help-realname": "O fornecimento de seu nome verdadeiro é opcional.\nCaso decida fornecê-lo, este será utilizado para dar-lhe crédito pelo seu trabalho.",
        "prefs-help-email": "O endereço de correio eletrônico é opcional, mas será necessário para recriar sua senha caso esqueça a antiga.",
        "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",
        "mw-widgets-titleinput-description-new-page": "a página ainda não existe",
        "mw-widgets-titleinput-description-redirect": "redirecionar para $1",
        "mw-widgets-categoryselector-add-category-placeholder": "Adicionar uma categoria...",
-       "mw-widgets-usersmultiselect-placeholder": "Adicione mais...",
+       "mw-widgets-usersmultiselect-placeholder": "Adicionar mais…",
        "date-range-from": "Da data:",
        "date-range-to": "Para data:",
        "sessionmanager-tie": "Não é possível combinar vários tipos de autenticação de solicitação: $1.",
index 8a34f80..30f0ba2 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": "-",
        "expansion-depth-exceeded-category-desc": "A página excede a profundidade de expansão permitida.",
        "expansion-depth-exceeded-warning": "A página excedeu a profundidade de expansão",
        "parser-unstrip-loop-warning": "Foi detetado um ciclo infinito da função unstrip",
-       "parser-unstrip-recursion-limit": "Limite de recursão da função unstrip excedido ($1)",
+       "unstrip-depth-warning": "Limite de recursão da função unstrip excedido ($1)",
        "converter-manual-rule-error": "Erro detetado na regra de conversão de língua manual",
        "undo-success": "É possível desfazer a edição.\nVerifique a comparação abaixo, por favor, para se certificar de que corresponde ao que pretende fazer.\nDepois grave as alterações, para finalizar e desfazer a edição.",
        "undo-failure": "Não foi possível desfazer a edição por conflito com alterações intermédias.",
        "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",
        "pageinfo-article-id": "ID da página",
        "pageinfo-language": "Língua do conteúdo da página",
        "pageinfo-language-change": "alterar",
-       "pageinfo-content-model": "Modelo de conteúdo de página",
+       "pageinfo-content-model": "Modelo de conteúdo da página",
        "pageinfo-content-model-change": "alterar",
        "pageinfo-robot-policy": "Indexação por robôs",
        "pageinfo-robot-index": "Permitida",
index f75d9bd..6849ba5 100644 (file)
        "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",
        "expansion-depth-exceeded-category-desc": "Expansion depth exceeded category description. Shown on [[Special:TrackingCategories]].\n\nSee also:\n* {{msg-mw|Expansion-depth-exceeded-category}}",
        "expansion-depth-exceeded-warning": "Error message shown when a page exceeded the [[meta:Help:Expansion_depth|expansion depth limit]] of the preprocessor.\n\nParameters:\n* $1 - (Unused) the value of the depth limit\n* $2 - (Unused) the value of the max depth limit\nSee also:\n* {{msg-mw|Expansion-depth-exceeded-category}}",
        "parser-unstrip-loop-warning": "{{Doc-important|Do not translate function name <code>unstrip</code>.}}\nThis error is shown when a parser extension tag such as <code><nowiki><pre></nowiki></code> includes a reference to itself in its own output.\n\nThe reference must be to the exact same invocation of the tag at the same location in the source, merely writing <code><nowiki><pre><pre></pre></pre></nowiki></code> will not do it.\n\nThis is usually impossible and unlikely to happen by accident, so translation is not essential.\n\n\"Unstrip\" refers to the internal function of the parser, called \"unstrip\", which recursively puts the output of parser functions in the place of the parser function call and which would enter an infinite loop in the situation above.\n\nSee also:\n*{{msg-mw|Parser-unstrip-recursion-limit}}",
-       "parser-unstrip-recursion-limit": "{{doc-important|Do not translate function name <code>unstrip</code>.}}\nThis message is shown when the recursion limit for nested parser extension tags is exceeded.\n\nThis warning may be encountered due to input text like <code><nowiki><ref><ref><ref>...</ref></ref></ref></nowiki></code>.\n\nParameters:\n* $1 - the depth limit\n\n\"Unstrip\" refers to the internal function of the parser, called 'unstrip', which recursively puts the output of parser functions in the place of the parser function call and which would enter an infinite loop in the situation above.\n\nSee also:\n* {{msg-mw|Parser-unstrip-loop-warning}}",
+       "unstrip-depth-warning": "{{doc-important|Do not translate function name <code>unstrip</code>.}}\nThis message is shown when the recursion limit for nested parser extension tags is exceeded.\n\nThis warning may be encountered due to input text like <code><nowiki><ref><ref><ref>...</ref></ref></ref></nowiki></code>.\n\nParameters:\n* $1 - the depth limit\n\n\"Unstrip\" refers to the internal function of the parser, called 'unstrip', which recursively puts the output of parser functions in the place of the parser function call.\n\nSee also:\n* {{msg-mw|Parser-unstrip-loop-warning}}",
+       "unstrip-depth-category": "This message is used as the category name of a [[mw:Help:Tracking categories|tracking category]] in which pages are placed automatically if the unstrip recursion depth limit is exceeded.",
+       "unstrip-size-warning": "{{doc-important|Do not translate function name <code>unstrip</code>.}}\nThis message is shown when the maximum expansion size for nested parser extension tags is exceeded.\n\nParameters:\n* $1 - the size limit\n\n\"Unstrip\" refers to the internal function of the parser, called 'unstrip', which recursively puts the output of parser functions in the place of the parser function call.\n\nSee also:\n* {{msg-mw|Parser-unstrip-loop-warning}}",
+       "unstrip-size-category": "This message is used as the category name of a [[mw:Help:Tracking categories|tracking category]] in which pages are placed automatically if the unstrip expansion size limit is exceeded.",
        "converter-manual-rule-error": "Used as error message when a manual conversion rule for the [[mw:Language_converter|language converter]] has errors. For example it's not using the correct syntax, or not supplying text in all variants.",
        "undo-success": "Text on special page to confirm edit revert. You arrive on this page by clicking on the \"undo\" link on a revision history special page.\n\n{{Identical|Undo}}",
        "undo-failure": "Message appears if an attempt to revert an edit by clicking the \"undo\" link on the page history fails.\n\nSee also:\n* {{msg-mw|Undo-norev}}\n* {{msg-mw|Undo-nochange}}\n{{Identical|Undo}}",
        "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}}",
        "limitreport-expansiondepth-value": "{{optional}}\nFormat for the \"Highest expansion depth\" row in the limit report table.\n\nParameters:\n* $1 - the depth\n* $2 - the maximum",
        "limitreport-expensivefunctioncount": "Label for the \"Expensive parser function count\" row in the limit report table",
        "limitreport-expensivefunctioncount-value": "{{optional}}\nFormat for the \"Expensive parser function count\" row in the limit report table.\n\nParameters:\n* $1 - the usage\n* $2 - the maximum",
+       "limitreport-unstrip-depth": "Label for the \"unstrip depth\" row in the limit report table. \"Unstrip\" is a MediaWiki function name and as such does not need to be translated.",
+       "limitreport-unstrip-depth-value": "{{optional}}\nFormat for the \"unstrip depth\" row in the limit report table.\n\nParameters:\n* $1 - the usage\n* $2 - the maximum",
+       "limitreport-unstrip-size": "Label for the \"unstrip size\" row in the limit report table. \"Unstrip\" is a MediaWiki function name and as such does not need to be translated.",
+       "limitreport-unstrip-size-value": "{{optional}}\nFormat for the \"unstrip size\" row in the limit report table.\n\nParameters:\n* $1 - the usage\n* $2 - the maximum",
        "expandtemplates": "{{doc-special|ExpandTemplates}}\nThe name of the [[mw:Extension:ExpandTemplates|Expand Templates extension]].",
        "expand_templates_intro": "This is the explanation given in the heading of the [[Special:ExpandTemplates]] page; it describes its functionality to the users.\nFor more information, see [[mw:Extension:ExpandTemplates]]",
        "expand_templates_title": "The label of the input box for the context title on the form displayed at [[Special:ExpandTemplates]] page.",
        "log-action-filter-contentmodel": "{{doc-log-action-filter-type|contentmodel}}\n{{related|Log-action-filter}}",
        "log-action-filter-delete": "{{doc-log-action-filter-type|delete}}\n{{related|Log-action-filter}}",
        "log-action-filter-import": "{{doc-log-action-filter-type|import}}\n{{Related|Log-action-filter}}",
-       "log-action-filter-managetags": "{{doc-log-action-filter-type|managetags}}\n{{Related|Log-action-filter}}",
+       "log-action-filter-managetags": "{{doc-log-action-filter-type|managetags}}\n{{Related|Log-action-filter}}\n\n\"Managing tags\" means creating or deleting [[:mw:Manual:Tags|revision tags]].",
        "log-action-filter-move": "{{doc-log-action-filter-type|move}}\n{{Related|Log-action-filter}}",
        "log-action-filter-newusers": "{{doc-log-action-filter-type|newusers}}\n{{Related|Log-action-filter}}",
        "log-action-filter-patrol": "{{doc-log-action-filter-type|patrol}}\n{{Related|Log-action-filter}}",
index edf5b8f..8a32caf 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!",
        "expansion-depth-exceeded-category": "Nisyu mast'ariy ukhu kaqniyuq p'anqakuna",
        "expansion-depth-exceeded-warning": "P'anqaqa nisyu mast'ariy ukhu kaqniyuqmi",
        "parser-unstrip-loop-warning": "Muyupayaq siqum tarisqa",
-       "parser-unstrip-recursion-limit": "Nisyu kuti muyupayay siqum ($1)",
+       "unstrip-depth-warning": "Nisyu kuti muyupayay siqum ($1)",
        "converter-manual-rule-error": "Maki rimay t'ikrana kamachinapiqa pantasqam tarisqa",
        "undo-success": "Rurasqata kutichiyta atinkim. Manaraq kutichispaykiqa, kay qatiq wakichayta qhawariy rikunaykipaq chiqapta munasqaykichu manallachu, chaymantataq waqaychay kutichinapaq.",
        "undo-failure": "Manam atinichu llamk'apusqata kutichiyta, huk ruraqtaq musuqta llamk'apurquptinñam.",
        "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..e9c2512 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!",
        "expansion-depth-exceeded-category": "Paginas, nua che la porfunditad d'extensiun è surpassada",
        "expansion-depth-exceeded-warning": "Questa pagina ha surpassà la profunditad d'expansiun",
        "parser-unstrip-loop-warning": "Chattà circul d'unstrip",
-       "parser-unstrip-recursion-limit": "Limita da recursiun ($1) per unstrip è surpassà",
+       "unstrip-depth-warning": "Limita da recursiun ($1) per unstrip è surpassà",
        "converter-manual-rule-error": "Ina errur è cumparida en la regla manuala da convertir la lingua",
        "undo-success": "Questa modificaziun na po betg vegnir revocada.\nControlleschan en la vista per cumparegliar sutvart sche quei è quai che ti vuls far e lura memorisescha las midadas sutvart per terminar la modificaziun.",
        "undo-failure": "La modificaziun na pudeva betg vegnir revocada causa modificaziuns pli novas che stattan en conflict cun questa acziun.",
        "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..1cb75ed 100644 (file)
@@ -34,7 +34,8 @@
                        "Stephanecbisson",
                        "Matrafox",
                        "Cybernenea11",
-                       "Andreyyshore"
+                       "Andreyyshore",
+                       "Andrei Stroe"
                ]
        },
        "tog-underline": "Sublinierea legăturilor:",
@@ -71,6 +72,7 @@
        "tog-watchlisthideminor": "Ascunde modificările minore din lista de pagini urmărite",
        "tog-watchlisthideliu": "Ascunde modificările efectuate de utilizatori autentificați din lista de pagini urmărite",
        "tog-watchlistreloadautomatically": "Reîncarcă automat lista paginilor urmărite de fiecare dată când un filtru este modificat (necesită JavaScript)",
+       "tog-watchlistunwatchlinks": "Adaugă legături directe pentru urmărire/neurmărire inrărilor din lista de pagini urmărite (este nevoie de JavaScript pentru a activa funcționalitatea)",
        "tog-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",
        "expansion-depth-exceeded-category-desc": "Pagina depășește profunzimea maximă de expansiune.",
        "expansion-depth-exceeded-warning": "Pagina depășește profunzimea de expansiune",
        "parser-unstrip-loop-warning": "Buclă nedetașabilă detectată",
-       "parser-unstrip-recursion-limit": "Limita de recursivitate nedetașabilă depășită ($1)",
+       "unstrip-depth-warning": "Limita de recursivitate nedetașabilă depășită ($1)",
        "converter-manual-rule-error": "Eroare detectată în regula manuală de conversie a limbii",
        "undo-success": "Modificarea poate fi anulată. Verificați diferența de dedesubt și apoi salvați pentru a termina anularea modificării.",
        "undo-failure": "Modificarea nu poate fi reversibilă datorită conflictului de modificări intermediare.",
        "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ă",
+       "rcfilters-filter-showlinkedto-label": "Arată schimbările din paginile ce trimit la",
+       "rcfilters-target-page-placeholder": "Introduceți numele unei pagini (sau categorii)",
        "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",
        "uploaded-script-svg": "S-a găsit elementul „$1” scriptabil în fișierul SVG încărcat.",
        "uploaded-hostile-svg": "S-a descoperit CSS vulnerabil în elementul de stil al fișierului SVG încărcat.",
        "uploaded-event-handler-on-svg": "Setarea atributelor <code>$1=„$2”</code> de gestionare a evenimentului nu este permisă pentru fișierele SVG.",
-       "uploaded-href-attribute-svg": "Atributele href din fișierele SVG au permisiunea de a se conecta numai la adrese destinație http:// sau https://, dar a fost găsit <code>&lt;$1 $2=\"$3\"&gt;</code>.",
+       "uploaded-href-attribute-svg": "Elementele <a> pot avea legături (href) doar către adrese care încep cu \"data:\" (fișiere incluse), \"http://\" ori \"https://\", sau cu \"#\" (fragmente din același document). Pentru alte elemente, precum <image>, sunt permise doar cele care încep cu \"data:\" și \"#\" (fragmente). Încercați să includeți și imaginile când exportați SVG-ul dvs. Am găsit <code>&lt;$1 $2=\"$3\"&gt;</code>.",
        "uploaded-href-unsafe-target-svg": "S-a găsit href către informații nesigure: destinație URI <code>&lt;$1 $2=\"$3\"&gt;</code> în fișierul SVG încărcat.",
        "uploaded-animate-svg": "S-a găsit în fișierul SVG încărcat eticheta „animate” care ar putea modifica valoarea href folosind atributul „from” <code>&lt;$1 $2=„$3”&gt;</code>.",
        "uploaded-setting-event-handler-svg": "Setarea atributelor de gestionare a evenimentului nu este permisă; s-a găsit <code>&lt;$1 $2=„$3”&gt;</code> în fișierul SVG încărcat.",
        "lockmanager-fail-closelock": "Imposibil de închis fișierul de blocare pentru „$1”.",
        "lockmanager-fail-deletelock": "Imposibil de șters fișierul de blocare pentru „$1”.",
        "lockmanager-fail-acquirelock": "Imposibil de obținut blocarea pentru „$1”.",
-       "lockmanager-fail-openlock": "Imposibil de deschis fișierul de blocare pentru „$1”.",
+       "lockmanager-fail-openlock": "Imposibil de deschis fișierul de blocare pentru „$1”. Asigurați-vă că directorul de încărcare este configurat corect și că serverul dumneavoastră web are permisiunea de a scrie în acel director. Vedeți https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:$wgUploadDirectory pentru mai multe informații.",
        "lockmanager-fail-releaselock": "Imposibil de eliberat blocarea pentru „$1”.",
        "lockmanager-fail-db-bucket": "Imposibil de contactat suficient baza de date cu blocări în găleata $1.",
        "lockmanager-fail-db-release": "Imposibil de eliberat blocările din baza de date $1.",
        "uploadstash-refresh": "Reîmprospătează lista de fișiere",
        "uploadstash-thumbnail": "arată miniatura",
        "uploadstash-exception": "Nu pot stoca încărcare în spațiul temporar ($1): \"$2\"",
+       "uploadstash-bad-path": "Calea nu există.",
+       "uploadstash-bad-path-invalid": "Calea nu este validă.",
+       "uploadstash-bad-path-unknown-type": "Tip necunoscut „$1”",
+       "uploadstash-bad-path-bad-format": "Cheia „$1” nu este într-un format recunoscut.",
+       "uploadstash-file-not-found": "Cheia „$1” nu a fost găsită în locația temporară.",
        "invalid-chunk-offset": "Decalaj de segment nevalid",
        "img-auth-accessdenied": "Acces interzis",
        "img-auth-nopathinfo": "PATH_INFO lipsește.\nServerul dumneavoastră nu a fost setat pentru a trece aceste informații.\nS-ar putea să fie bazat pe CGI și să nu suporte img_auth.\nVedeți https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization.",
        "doubleredirects": "Redirecționări duble",
        "doubleredirectstext": "Această listă conține pagini care redirecționează la alte pagini de redirecționare.\nFiecare rând conține legături la primele două redirecționări, precum și ținta celei de-a doua redirecționări, care este de obicei pagina țintă \"reală\", către care ar trebui să redirecționeze prima pagină.\nIntrările <del>tăiate</del> au fost rezolvate.",
        "double-redirect-fixed-move": "[[$1]] a fost redenumită.\nA fost actualizată automat, iar acum redirecționează către [[$2]].",
-       "double-redirect-fixed-maintenance": "Reparat în mod automat dubla redirecționare de la [[$1]] înspre [[$2]] în cadrul sarcinii de mentenanță.",
+       "double-redirect-fixed-maintenance": "Reparat în mod automat dubla redirecționare de la [[$1]] înspre [[$2]] în cadrul sarcinii de mentenanță",
        "double-redirect-fixer": "Corector de redirecționări",
        "brokenredirects": "Redirecționări greșite",
        "brokenredirectstext": "Următoarele redirecționări conduc spre articole inexistente:",
        "rollback-success": "Modificările făcute de {{GENDER:$3|$1}} au fost anulate;\nam revenit la ultima versiune de {{GENDER:$4|$2}}.",
        "rollback-success-notify": "S-a revenit asupra schimbărilor făcute de $1;\nam revenit la ultima versiune de $2. [$3 Arată schimbările]",
        "sessionfailure-title": "Eroare de sesiune",
-       "sessionfailure": "Se pare că este o problemă cu sesiunea de autentificare; această acțiune a fost oprită ca o precauție împotriva hijack. Apăsați \"back\" și reîncărcați pagina de unde ați venit, apoi reîncercați.",
+       "sessionfailure": "Se pare că este o problemă cu sesiunea de autentificare; această acțiune a fost oprită ca o precauție împotriva furtului sesiunii. Vă rugăm să trimiteți formularul din nou.",
        "changecontentmodel": "Modificare model de conținut al unei pagini",
        "changecontentmodel-legend": "Modifică modelul de conținut",
        "changecontentmodel-title-label": "Titlul paginii",
        "confirmrecreate": "Utilizatorul [[User:$1|$1]] ([[User talk:$1|discuție]]) {{GENDER:$1|a șters}} acest articol după ce ați început să contribuiți la el din motivul:\n: <em>$2</em>\nVă rugăm să confirmați faptul că într-adevăr doriți să recreați acest articol.",
        "confirmrecreate-noreason": "Utilizatorul [[User:$1|$1]] ([[User talk:$1|discuție]]) {{GENDER:$1|a șters}} această pagină după ce dumneavoastră ați început să o modificați. Vă rugăm să confirmați faptul că într-adevăr doriți să recreați această pagină.",
        "recreate": "Recreează",
+       "confirm-purge-title": "Regenerez această pagină",
        "confirm_purge_button": "OK",
        "confirm-purge-top": "Doriți să reîncărcați pagina?",
        "confirm-purge-bottom": "Actualizarea unei pagini șterge cache-ul și forțează cea mai recentă variantă să apară.",
        "version-poweredby-others": "alții",
        "version-poweredby-translators": "traducătorii de la translatewiki.net",
        "version-credits-summary": "Am dori să amintim următoarele persoane pentru contribuțiile aduse proiectului [[Special:Version|MediaWiki]].",
-       "version-license-info": "MediaWiki este un software liber pe care îl puteți redistribui și/sau modifica sub termenii Licenței Publice Generale GNU publicată de Free Software Foundation – fie a doua versiune a acesteia, fie, la alegerea dumneavoastră, orice altă versiune ulterioară. \n\nMediaWiki este distribuit în speranța că va fi folositor, dar FĂRĂ VREO GARANȚIE, nici măcar cea implicită de COMERCIALIZARE sau de ADAPTARE PENTRU UN SCOP ANUME. Vedeți Licența Publică Generală GNU pentru mai multe detalii. \n\nÎn cazul în care nu ați primit [{{SERVER}}{{SCRIPTPATH}}/COPYING o copie a  Licenței Publice Generale GNU] împreună cu acest program, scrieți la Free Software Foundation, Inc, 51, Strada Franklin, etajul cinci, Boston, MA 02110-1301, Statele Unite ale Americii sau [//www.gnu.org/licenses/old-licenses/gpl-2.0.html citiți-o online].",
+       "version-license-info": "MediaWiki este un software liber pe care îl puteți redistribui și/sau modifica sub termenii Licenței Publice Generale GNU publicată de Free Software Foundation – fie a doua versiune a acesteia, fie, la alegerea dumneavoastră, orice altă versiune ulterioară. \n\nMediaWiki este distribuit în speranța că va fi folositor, dar <em>FĂRĂ VREO GARANȚIE</em>, nici măcar cea implicită de <strong>COMERCIALIZARE</strong> sau de <strong>ADAPTARE PENTRU UN SCOP ANUME</strong>. Vedeți Licența Publică Generală GNU pentru mai multe detalii. \n\nÎn cazul în care nu ați primit [{{SERVER}}{{SCRIPTPATH}}/COPYING o copie a  Licenței Publice Generale GNU] împreună cu acest program, scrieți la Free Software Foundation, Inc, 51, Strada Franklin, etajul cinci, Boston, MA 02110-1301, Statele Unite ale Americii sau [//www.gnu.org/licenses/old-licenses/gpl-2.0.html citiți-o online].",
        "version-software": "Software instalat",
        "version-software-product": "Produs",
        "version-software-version": "Versiune",
        "tag-filter": "Filtru pentru [[Special:Tags|etichete]]:",
        "tag-filter-submit": "Filtru",
        "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|Etichetă|Etichete}}]]: $2)",
+       "tag-mw-replace": "Înlocuit",
+       "tag-mw-replace-description": "Editări care șterg mai mult de 90% din conținutul unei pagini",
+       "tag-mw-rollback": "Revenire",
+       "tag-mw-undo": "Anulare",
        "tags-title": "Etichete",
        "tags-intro": "Această pagină afișează etichetele, inclusiv semnificația lor, pe care software-ul le poate folosi la marcarea modificărilor.",
        "tags-tag": "Numele etichetei",
        "limitreport-expansiondepth": "Cea mai mare profunzime a expansiunii",
        "limitreport-expensivefunctioncount": "Număr de funcții de analiză costisitoare",
        "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_intro": "Această pagină specială servește la expandarea recursivă a tuturor formatelor dintr-un wikitext. Ea acționează și asupra funcțiilor de analiză (''parser'') de tipul <nowiki>{{</nowiki>#if:...}}, a variabilelor precum <nowiki>{{</nowiki>CURRENTDAY}} și în general asupra oricăror coduri cuprinse între acolade duble.",
        "expand_templates_title": "Titlul contextului (de exemplu pentru {{PAGENAME}}):",
-       "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..ed9b33c 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!",
        "expansion-depth-exceeded-category-desc": "'A pàgene sbonne 'a massime espanzione de profonnetà.",
        "expansion-depth-exceeded-warning": "Pàggene ca sbonnane 'a profonnetà de espanzione",
        "parser-unstrip-loop-warning": "Cicle infinite acchiate",
-       "parser-unstrip-recursion-limit": "Limite de ricorsione infinite sbunnate ($1)",
+       "unstrip-depth-warning": "Limite de ricorsione infinite sbunnate ($1)",
        "converter-manual-rule-error": "Errore assute jndr'à le regole de conversione d'a lènghe manuale",
        "undo-success": "'U cangiamende pò essere annullate.\nPe piacere verifichesce 'u combronde sotte pe condrollà ca quiste ca tu vuè ccu face e pò reggistrè le cangiaminde aqquà sotte pe spiccià l'annullamende d'u cangiamende.",
        "undo-failure": "'U cangiamende non ge pò essere annullete purcè stè 'nu conflitte de cangiaminde indermedije.",
        "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..0899f03 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Ваши изменения ещё не были сохранены!",
        "expansion-depth-exceeded-category-desc": "У страницы превышена максимально допустимая глубина раскрытия.",
        "expansion-depth-exceeded-warning": "На странице превышен предел вложенности",
        "parser-unstrip-loop-warning": "Обнаружен незакрытый pre",
-       "parser-unstrip-recursion-limit": "Превышен предел рекурсии ($1)",
+       "unstrip-depth-warning": "Превышен предел рекурсии ($1)",
        "converter-manual-rule-error": "Ошибка в ручном правиле преобразования языка",
        "undo-success": "Правка может быть отменена. Пожалуйста, просмотрите сравнение версий, чтобы убедиться, что это именно те изменения, которые вас интересуют, и нажмите «Записать страницу», чтобы изменения вступили в силу.",
        "undo-failure": "Правка не может быть отменена из-за несовместимости промежуточных изменений.",
        "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..d51f9ee 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Зміны іщі не суть уложены!",
        "expansion-depth-exceeded-category": "Сторінкы превышують глубку експанзії",
        "expansion-depth-exceeded-warning": "Сторінка перевышыла глубку експанзії",
        "parser-unstrip-loop-warning": "Выявлене заціклїня unstrip",
-       "parser-unstrip-recursion-limit": "Перевышеный ліміт рекурзії unstrip ($1)",
+       "unstrip-depth-warning": "Перевышеный ліміт рекурзії unstrip ($1)",
        "converter-manual-rule-error": "Найджена хыба в ручнім правилї конверзії языка",
        "undo-success": "Едітованя може быти зрушене.\nПросиме Вас перевірьте порівнаня ниже, жебы сьте ся упевнили в тім, што хочете зробити а потім уложте зміны долов, жебы сьте закінчіли зрушіня едітованя.",
        "undo-failure": "Едітованя не могло быти зрушене про конфлікт міджілеглых змін.",
        "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..0189324 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>",
        "expansion-depth-exceeded-category-desc": "एतत् पृष्ठं विस्तारस्य सीमाम् अत्यक्रमत् ।",
        "expansion-depth-exceeded-warning": "पृष्ठेऽस्मिन् विस्तारसीमा समाप्ता",
        "parser-unstrip-loop-warning": "Unstrip परिक्रमः (loop) प्राप्तः",
-       "parser-unstrip-recursion-limit": "Unstrip इत्यस्य प्रत्यावर्तनस्य (recursion) सीमा समाप्ता ($1)",
+       "unstrip-depth-warning": "Unstrip इत्यस्य प्रत्यावर्तनस्य (recursion) सीमा समाप्ता ($1)",
        "converter-manual-rule-error": "मानवीये भाषापरिवर्तने दोषः दृष्टः ।",
        "undo-success": "एतत् सम्पादनं पूर्ववत् कर्तुं शक्यते ।\nकृपया अधः दत्तयोः तुलनां कृत्वा यत् आवश्यकं, तत् परिवर्तनं संरक्षतु च । ततः पूर्ववत्-करणप्रक्रिया समाप्यताम् ।",
        "undo-failure": "सम्पादनं पूर्ववत् कर्तुं न शक्यते । यतः मध्ये परस्परविपरितानि सम्पादनानि अभवन् ।",
        "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..d94dbef 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Атын уларытыы бигэргэтиллэ илик!",
        "expansion-depth-exceeded-category-desc": "Сирэй аһыллыытын дириҥэ куоһарыллыбыт.",
        "expansion-depth-exceeded-warning": "Сирэйгэ угуллубут билэлэр аһара элбээбиттэр",
        "parser-unstrip-loop-warning": "Сабыллыбатах pre көһүннэ",
-       "parser-unstrip-recursion-limit": "Рекурсия ахсаана таһынан барбыт ($1)",
+       "unstrip-depth-warning": "Рекурсия ахсаана таһынан барбыт ($1)",
        "converter-manual-rule-error": "Тылы уларытыы быраабылатын алҕаһа таҕыста",
        "undo-success": "Правка может быть отменена. Пожалуйста, просмотрите сравнение версий, чтобы убедиться, что это именно те изменения, которые вас интересуют, и нажмите «Записать страницу», чтобы изменения вступили в силу.",
        "undo-failure": "Уларытыы төннөр кыаҕа суох. Ыккардынааҕы көннөрүүлэр бэйэ бэйэлэригэр сөп түбэспэттэр.",
        "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 22178a0..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": "ᱵᱟᱞᱮ ᱢᱟᱦᱟᱸ",
        "category-media-header": "\"$1\" ᱵᱟᱵᱚᱛ ᱨᱮᱭᱟᱜ ᱢᱤᱰᱤᱭᱟ ᱛᱷᱚᱠ",
        "category-empty": "<em>ᱱᱚᱣᱟ ᱛᱷᱚᱠ ᱨᱮ ᱱᱮᱛᱚᱜ ᱩᱱᱩᱫᱩᱜ ᱥᱟᱦᱴᱟᱠᱚ ᱥᱮ ᱢᱤᱰᱤᱭᱟ ᱵᱟᱱᱩᱜ-ᱟ᱾</em>",
        "hidden-categories": "{{PLURAL:$1|ᱫᱟᱱᱟᱝ ᱛᱷᱚᱠ|ᱫᱟᱱᱟᱝ ᱛᱷᱚᱠᱠᱩ}}",
-       "hidden-category-category": "Uku akan rokom sokom ko",
+       "hidden-category-category": "ᱩᱠᱩ ᱛᱷᱚᱠ ᱠᱩ",
        "category-subcat-count": "{{PLURAL:$2| ᱱᱚᱣᱟ ᱛᱷᱚᱠ ᱨᱮ ᱱᱚᱣᱟᱠᱩ ᱦᱩᱰᱤᱧ ᱛᱷᱚᱠ ᱠᱩ ᱢᱮᱱᱟᱜ-ᱟ|ᱱᱚᱣᱟ ᱛᱷᱚᱠ ᱨᱮ ᱢᱮᱱᱟᱜ-ᱟ {{PLURAL:$1|ᱦᱩᱰᱤᱧ ᱛᱷᱚᱠᱠᱩ}}, ᱡᱚᱛᱚᱠᱚᱛᱮ $2}}",
-       "category-subcat-count-limited": "Noa rokom sokomre latar reaḱ {{PLURAL:$1 gan kạṭic rokom sokom $1gan kạtic rokom sokom menaḱa}}",
+       "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.",
+       "category-file-count-limited": "ᱱᱚᱣᱟ {{PLURAL:$1 ᱨᱮᱫ ᱨᱮᱫᱠᱩ }} ᱛᱷᱚᱠᱨᱮ ᱢᱮᱱᱟᱜ-ᱟ᱾",
        "listingcontinuesabbrev": "ᱪᱟᱞᱟᱜ ᱠᱟᱱᱟ",
        "index-category": "ᱩᱱᱩᱫᱩᱜ-ᱟᱱ ᱥᱟᱦᱴᱟᱠᱚ",
        "noindex-category": "ᱩᱱᱩᱫᱩᱜ ᱵᱟᱹᱱᱩᱜ-ᱟᱱ ᱥᱟᱦᱴᱟᱠᱚ",
        "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",
+       "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": "ᱪᱟᱞᱟᱜ ᱠᱟᱱᱟ:",
        "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 ᱞᱮᱠᱟᱛᱮ  ᱵᱟᱝᱠᱷᱟᱱ ᱚᱞ ᱛᱟᱦᱮᱱᱟ",
        "helppage-top-gethelp": "ᱜᱚᱸᱲᱚᱸ",
        "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": "{{PLURAL:$3|ᱟᱢᱟᱜ ᱢᱮᱱᱟᱜ-ᱟ}} $1 ($2)᱾",
        "youhavenewmessagesfromusers": "{{PLURAL:$4|ᱟᱢ ᱫᱚ}} $1 ᱠᱷᱚᱱ {{PLURAL:$3|ᱟᱨᱢᱤᱫ ᱵᱮᱵᱷᱟᱨᱤᱭᱟᱹ|$3 ᱵᱷᱮᱵᱷᱟᱨᱩᱭᱟᱹ}} ($2) ᱾",
        "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 ᱡᱚᱢ ᱚᱪᱚ",
        "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-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",
+       "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 ᱨᱮᱱᱟᱜ ᱯᱷᱮᱰᱟᱛ ᱧᱮᱞᱢᱮ",
-       "actionthrottled": "Kạmi reaḱ dhara bại",
+       "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": "ᱵᱚᱞᱚ ᱛᱷᱤᱨᱜᱮ ᱫᱚᱦᱚᱠᱟᱹᱧᱢᱮ",
-       "yourdomainname": "Amaḱ ḍomen:",
+       "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",
+       "nav-login-createaccount": "ᱵᱚᱞᱚᱜ ᱫᱩᱣᱟᱹᱨ / ᱦᱤᱥᱟᱹᱵ ᱠᱷᱟᱛᱟ ᱛᱮᱭᱟᱨᱢᱮ",
        "logout": "ᱚᱰᱚᱠᱚᱜ ᱢᱮ",
        "userlogout": "ᱚᱰᱚᱠᱚᱜ ᱢᱮ",
        "notloggedin": "ᱵᱟᱢ ᱵᱚᱞᱚ ᱟᱠᱟᱱᱟ",
        "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.",
        "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-username": "ᱵᱮᱵᱦᱟᱨᱤᱡ ᱧᱩᱛᱩᱢ:",
+       "passwordreset-domain": "ᱧᱩá±\9bᱩᱢ:",
        "passwordreset-email": "ᱤᱢᱮᱞ ᱵᱩᱴᱟᱹ:",
        "passwordreset-emailtitle": "{{SITENAME}} sayeṭre beoharićaḱ purạo thutiko",
        "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": "ᱱᱮᱛᱚᱜ-ᱟᱜ email ᱴᱷᱤᱠᱟᱹᱱᱟ",
+       "changeemail-oldemail": "ᱱᱮᱛᱚᱜ-ᱟᱜ ᱤᱢᱮᱞ ᱴᱷᱤᱠᱟᱹᱱᱟ",
        "changeemail-newemail": "ᱱᱟᱣᱟ ᱤᱢᱮᱞ ᱵᱩᱴᱟᱹ:",
        "changeemail-none": "(ᱪᱮᱫ ᱦᱚᱸ ᱵᱟᱹᱱᱩᱜ-ᱟ)",
-       "changeemail-password": "á±\9fá±¢á±\9fá±\9c {{SITENAME}} á±©á± á±© á±®á±\9eá±¥á±\9aá±\9d:",
-       "changeemail-submit": "E-mail ᱵᱚᱫᱚᱞᱢᱮ",
+       "changeemail-password": "á±\9fá±¢á±\9fá±\9c {{SITENAME}} á±«á±\9fá±±á±\9fá±\9d á±¥á±\9fá±µá±\9fá±½:",
+       "changeemail-submit": "ᱤ-ᱢᱮᱞ ᱵᱚᱫᱚᱞᱢᱮ",
        "bold_sample": "ᱢᱚᱴᱟ ᱚᱞ",
        "bold_tip": "ᱢᱚᱴᱟ ᱚᱞ",
        "italic_sample": "ᱜᱷᱟᱸᱡᱮᱲ ᱚᱞ",
        "missingcommenttext": "ᱫᱟᱭᱟ ᱠᱟᱛᱮ ᱟᱢᱟᱜ ᱠᱟᱛᱷᱟ ᱵᱚᱞᱚᱭ ᱢᱮ᱾",
        "summary-preview": "Guṭ katha unuduḱ:",
        "subject-preview": "Babot/Guṭkatha unuduḱ:",
-       "blockedtitle": "ᱵᱮᱵᱦá±\9fᱨᱤᱡ á±«á±\9a á±®á±¥á±®á±\9b á±\9aᱪá±\9aá±\9fá± á±\9fá±±á±\9f",
+       "blockedtitle": "ᱵᱮᱵᱷá±\9fᱨᱤᱡ á±«á±\9a á±®á±¥á±®á±« á±\9aᱪá±\9aá±£á±\9fá± á±\9fá±±á±\9fá±­",
        "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": "ᱡᱟᱸᱦᱟᱸᱱ ᱚᱡᱮ ᱵᱟᱝ ᱮᱢᱠᱟᱱᱟ",
-       "whitelistedittext": "Sakamre sompadon lạgit́te $1 em hoyoḱa.",
-       "nosuchsectiontitle": "Pahaṭa bȧn ńamlena",
+       "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": "(ᱱᱟᱣᱟᱱᱟᱜ)",
        "newarticletext": "ᱟᱢ ᱚᱠᱟ ᱥᱟᱦᱴᱟ ᱨᱮᱱᱟᱜ ᱡᱚᱱᱟᱲᱮᱢ ᱯᱟᱸᱡᱟᱸ ᱟᱹᱜᱩᱭᱫᱟ ᱚᱱᱚ ᱫᱚ ᱵᱟᱱᱩᱜ-ᱟ᱾\nᱚᱱᱟ ᱥᱟᱦᱴᱟ ᱛᱮᱭᱟᱨ ᱞᱟᱹᱜᱤᱛ ᱛᱮ, ᱞᱟᱛᱟᱨ ᱵᱟᱠᱥᱚ ᱵᱷᱤᱛᱨᱤᱨᱮ ᱚᱞ ᱮᱦᱚᱵ ᱢᱮ (ᱟᱨᱦᱚᱸ ᱡᱟᱹᱥᱛᱤ ᱵᱟᱰᱟᱭ ᱞᱟᱹᱜᱤᱛᱴᱮ [$1 ᱜᱚᱸᱲᱚᱸ ᱥᱟᱦᱴᱟ] ᱯᱟᱸᱡᱚᱸᱭᱢᱮ)᱾\nᱟᱢ ᱵᱷᱩᱞᱛᱮ ᱱᱚᱸᱰᱮᱢ ᱦᱮᱡ ᱟᱠᱟᱱ ᱠᱷᱟᱡ, ᱟᱢᱟᱜ ᱵᱨᱟᱣᱡᱟᱨ ᱨᱮᱱᱟᱜ '''ᱛᱟᱭᱚᱢ''' ᱵᱟᱴᱚᱱ ᱞᱤᱱᱢᱮ᱾",
        "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": "ᱵᱮᱵᱦᱟᱨᱤᱭᱟᱜ \"$1\" ᱮᱠᱟᱣᱱᱴ ᱫᱚ ᱵᱟᱝ ᱨᱮᱥᱴᱨᱤ ᱟᱠᱟᱱᱟ᱾",
-       "blocked-notice-logextract": "Nui beoharić do nitoḱe esetgea.\nRefarens lạgit́te nahaḱ boloḱ do latare em hoena:",
+       "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": "(ᱦá±\9fá±\9eá±\9aá±\9b ᱜᱮᱭᱟ)",
-       "note": "'''Noṭ:'''",
+       "updated": "(ᱩá±\9bá±·á±±á±\9fá±£ ᱜᱮᱭᱟ)",
+       "note": "<strong>ᱱᱳᱴ:</strong>",
        "previewnote": "'''ᱠᱷᱮᱭᱟᱞᱢᱮ, ᱱᱚᱣᱟ ᱫᱚ ᱮᱠᱮᱱ ᱧᱮᱞᱚᱜ ᱞᱟᱹᱜᱤᱛ᱾'''\nᱟᱢᱟᱜ ᱵᱚᱫᱚᱞ ᱠᱚᱫᱚ ᱱᱤᱛ ᱫᱷᱟᱹᱵᱤᱡ ᱵᱟᱝ ᱨᱩᱠᱷᱤᱭᱟᱹᱣᱠᱟᱱᱟ!",
        "continue-editing": "ᱥᱟᱯᱲᱟᱣ ᱡᱟᱜᱟ ᱪᱟᱞᱟᱜ ᱢᱮ",
        "editing": "ᱥᱟᱯᱲᱟᱣ ᱪᱟᱞᱟᱜᱠᱟᱱᱟ $1",
        "creating": "$1 ᱛᱮᱭᱟᱨᱚᱜᱠᱟᱱᱟ",
        "editingsection": "ᱥᱟᱯᱲᱟᱣᱢᱮ $1 (ᱦᱟᱹᱴᱤᱧ)",
        "editingcomment": "ᱥᱟᱯᱲᱟᱣᱢᱮ $1 (ᱱᱟᱣᱟ ᱦᱟᱹᱴᱤᱧ)",
-       "editconflict": "á±¥á±\9fᱯᱲá±\9fá±£ á±µá±\9fᱹᱭᱨᱤ: $1",
+       "editconflict": "á±¥á±\9fᱯᱲá±\9fá±£ á±¡á±\9fá±\9eá±\9f: $1",
        "yourtext": "ᱟᱢᱟᱜ ᱚᱞ",
        "storedversion": "ᱡᱚᱜᱟᱣᱠᱟᱱ ᱥᱩᱫᱷᱨᱟᱹᱣ",
        "yourdiff": "ᱯᱷᱟᱨᱟᱠ",
        "templatesused": "ᱱᱚᱶᱟ ᱥᱟᱦᱴᱟᱨᱮ ᱵᱮᱵᱷᱟᱨᱟᱠᱟᱱ {{PLURAL:$1|ᱪᱷᱟᱸᱪ|ᱪᱷᱟᱸᱪᱠᱚ}} :",
        "templatesusedpreview": "{{PLURAL:$1|ᱪᱷᱟᱸᱪ|ᱪᱷᱟᱸᱪᱠᱚ}} ᱵᱮᱵᱷᱟᱨ ᱟᱠᱟᱱᱟ ᱱᱟᱶᱟ ᱧᱮᱱᱮᱞᱨᱮ:",
        "template-protected": "(ᱨᱩᱠᱷᱤᱭᱟᱹ)",
-       "template-semiprotected": "(ᱦᱩᱰᱤᱧ ᱨᱩᱠᱷᱤᱭᱟᱹ)",
+       "template-semiprotected": "(á±\9fᱫᱷá±\9f ᱨᱩᱠᱷᱤᱭᱟᱹ)",
        "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": "ᱱᱚᱣᱟ ᱠᱟᱹᱢᱤᱨᱮ ᱟᱢᱟᱜ ᱟᱹᱭᱫᱟᱹᱨᱤ ᱵᱟᱹᱱᱩᱜ-ᱟ, {{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": "ᱥᱟᱯᱲᱟᱣ ᱵᱟᱹᱭᱨᱤ᱾",
-       "edit-no-change": "Amaḱ sompadon do baṅ hataolena, Cedaḱ je olre jahan bodol bạnuḱa.",
+       "edit-no-change": "ᱟᱢᱟᱜ ᱥᱟᱯᱲᱟᱣ ᱵᱟᱝ ᱦᱟᱛᱟᱣᱞᱮᱱᱟ ᱡᱮᱫᱟᱜ ᱡᱮ ᱚᱞᱨᱮ ᱡᱟᱸᱦᱟᱸᱱ ᱵᱚᱫᱚᱞ ᱵᱟᱹᱱᱩᱜ-ᱟ᱾",
        "postedit-confirmation-created": "ᱱᱚᱣᱟ ᱥᱟᱦᱴᱟ ᱫᱚ ᱛᱮᱭᱟᱨᱠᱟᱱᱟ᱾",
        "postedit-confirmation-restored": "ᱱᱚᱣᱟ ᱥᱟᱦᱴᱟ ᱫᱚ ᱟᱹᱜᱩ ᱨᱩᱣᱟᱹᱲ ᱦᱩᱭᱠᱟᱱᱟ᱾",
        "edit-already-exists": "ᱱᱟᱣᱟ ᱥᱟᱦᱴᱟ ᱵᱟᱝ ᱛᱮᱭᱟᱨ ᱞᱮᱱᱟ᱾\nᱱᱚᱣᱟ ᱫᱚ ᱞᱟᱦᱟ ᱠᱷᱚᱱ ᱢᱮᱱᱟᱜ ᱜᱮᱭᱟ᱾",
        "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": "ᱱᱮᱛᱚᱜ",
        "last": "ᱞᱟᱦᱟ ᱛᱮᱱᱟᱜ",
        "page_first": "ᱯᱟᱹᱦᱤᱞ",
        "page_last": "ᱢᱩᱪᱟᱹᱫ",
-       "histlegend": "ᱮᱴᱟᱜ ᱵᱟᱪᱷᱟᱣ: ᱱᱟᱣᱟ ᱵᱚᱫᱚᱞᱠᱚ ᱛᱩᱞᱟᱹᱣ ᱢᱮᱱᱠᱷᱟᱱ, ᱨᱮᱰᱤᱭᱳ ᱵᱟᱠᱥᱚᱨᱮ ᱪᱤᱱ ᱮᱢ ᱠᱟᱛᱮ ᱵᱚᱞᱚᱜ ᱥᱮ ᱞᱟᱛᱟᱨ ᱨᱮᱱᱟᱜ ᱵᱟᱴᱚᱱ ᱞᱤᱱᱢᱮ᱾<br />\nᱩᱱᱩᱫᱩᱜ: '''({{int:cur}})''' = ᱱᱮᱛᱟᱨ ᱥᱩᱫᱷᱨᱟᱹᱣ ᱥᱟᱶᱛᱮ ᱥᱚᱝ, '''({{int:last}})''' = ᱞᱟᱦᱟ ᱨᱮᱭᱟᱜ ᱱᱟᱣᱟ ᱥᱩᱫᱷᱨᱟᱹᱣ ᱥᱟᱶᱛᱮ ᱥᱚᱝ, '''{{int:minoreditletter}}''' = ᱦᱩᱰᱤᱧ ᱥᱟᱯᱲᱟᱣ᱾",
+       "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 á±µá±\9fᱭᱤᱴ $1 á±µá±\9fᱭᱤᱴᱠᱩ}})",
+       "historysize": "({{PLURAL:$1 1 á±µá±\9fᱭᱤᱴ $1 á±µá±\9fᱭᱤᱴᱥ}})",
        "historyempty": "(ᱠᱷᱟᱹᱞᱤ)",
        "history-feed-title": "ᱥᱩᱫᱷᱨᱟᱹᱣ ᱱᱟᱜᱟᱢ",
        "history-feed-description": "ᱩᱭᱠᱤᱨᱮ ᱱᱤᱭᱟᱹ ᱥᱟᱦᱴᱟ ᱵᱚᱫᱚᱞ ᱨᱮᱱᱟᱜ ᱱᱟᱜᱟᱢ",
-       "history-feed-item-nocomment": "$1 ᱨᱮ $2",
+       "history-feed-item-nocomment": "$2 ᱨᱮ $1",
        "rev-deleted-comment": "(ᱥᱟᱯᱲᱟᱣ ᱢᱩᱬᱩᱛ ᱠᱟᱛᱦᱟ ᱚᱪᱟᱜᱠᱟᱱᱟ)",
-       "rev-deleted-user": "(ᱵᱮᱵᱦᱟᱨᱤᱭᱟᱜ ᱧᱩᱛᱩᱢ ᱚᱪᱚᱜᱠᱟᱱᱟ)",
+       "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-deleted-user-contribs": "[ᱵᱮᱵᱷᱟᱨᱤᱡ ᱧᱩᱛᱩᱢ ᱥᱮ IP ᱴᱷᱤᱠᱟᱱᱟ ᱫᱚ ᱚᱪᱟᱜ ᱦᱩᱭᱱᱟ - ᱮᱱᱮᱢ ᱠᱷᱚᱱ ᱥᱟᱯᱲᱟᱣ ᱫᱚ ᱩᱠᱩ ᱟᱠᱟᱱᱟ]",
        "rev-delundel": "ᱧᱮᱧᱮᱞᱟᱜ ᱵᱚᱫᱚᱞ",
        "rev-showdeleted": "ᱥᱚᱫᱚᱨ",
        "revisiondelete": "ᱜᱮᱫ ᱜᱤᱰᱤ/ᱵᱟᱝ ᱜᱮᱫ ᱜᱤᱰᱤ ᱥᱩᱫᱷᱨᱟᱹᱣᱠᱚ",
        "diff-multi-sameuser": "({{PLURAL:$1|ᱢᱤᱫ ᱛᱟᱞᱟ-ᱢᱟᱞᱟ ᱫᱚᱦᱲᱟ|$1 ᱛᱟᱞᱟ-ᱢᱟᱞᱟ ᱫᱚᱦᱲᱟᱠᱚ}} ᱥᱚᱢᱟᱱ ᱵᱮᱵᱷᱟᱨᱤᱭᱟᱹ ᱫᱟᱨᱟᱭᱛᱮ ᱵᱟᱭ ᱧᱮᱞᱚᱜ-ᱟ)",
        "diff-multi-otherusers": "({{PLURAL:$1|ᱢᱤᱫ ᱛᱟᱞᱟ-ᱢᱟᱞᱟ ᱫᱚᱦᱲᱟ|$1 ᱛᱟᱞᱟ-ᱢᱟᱞᱟ ᱫᱚᱦᱲᱟᱠᱚ}} {{PLURAL:$2|ᱢᱤᱫ ᱮᱴᱟᱜ ᱵᱮᱵᱷᱟᱨᱤᱭᱟᱹ|$2 ᱵᱮᱵᱷᱟᱨᱤᱭᱟᱹᱠᱚ}} ᱵᱟᱠᱚ ᱧᱮᱞᱚᱜ-ᱟ)",
        "searchresults": "ᱥᱮᱸᱫᱽᱨᱟ ᱚᱨᱡᱚᱠᱳ",
-       "searchresults-title": "\"$1\"  á±¨á±®á±±á±\9fá±\9c á±¥á±®á±¸á±«á±½á±¨á±\9f á±¯á±·á±\9aá±\9e",
+       "searchresults-title": "\"$1\"  á±¨á±®á±±á±\9fá±\9c á±¥á±®á±¸á±«á±½á±¨á±\9f á±\9aᱨᱡá±\9a",
        "prevn": "ᱞᱟᱦᱟᱛᱮᱱᱟᱜ {{PLURAL:$1|$1}}",
        "nextn": "ᱛᱟᱭᱚᱢᱛᱮᱱᱟᱜ {{PLURAL:$1|$1}}",
        "prev-page": "ᱯᱟᱪᱮ ᱥᱟᱦᱴᱟ",
        "timezoneregion-australia": "ᱚᱥᱴᱨᱮᱞᱤᱭᱟ",
        "prefs-searchoptions": "ᱥᱮᱸᱫᱽᱨᱟ",
        "prefs-files": "ᱨᱮᱫᱠᱚ",
-       "youremail": "E-mail:",
+       "youremail": "Email:",
        "username": "Beoharićaḱ ń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": "ᱥᱩᱦᱤ",
        "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": "ᱥᱟᱦᱴᱟᱠᱚ ᱥᱟᱦᱟᱭᱢᱮ",
        "right-move-subpages": "ᱥᱟᱦᱴᱟ ᱥᱟᱦᱟᱦᱟᱭᱢᱮ ᱥᱟᱶᱛᱮᱱ ᱥᱟᱦᱴᱟᱠᱚ ᱥᱟᱶ",
        "right-movefile": "ᱨᱮᱫᱠᱚ ᱚᱪᱚᱜᱽ ᱢᱮ",
-       "right-upload": "Rẽtko rakabmẽ",
+       "right-upload": "ᱨᱮᱫ ᱠᱚ ᱞᱟᱫᱮᱢᱮ",
        "right-writeapi": "ᱚᱞ API ᱨᱮᱱᱟᱜ ᱵᱮᱵᱷᱟᱨ",
        "right-delete": "ᱥᱟᱦᱴᱟᱠᱚ ᱜᱮᱫᱽ ᱢᱮ",
        "right-browsearchive": "ᱥᱮᱸᱫᱽᱨᱟᱭ ᱢᱮ ᱜᱮᱫ ᱟᱠᱟᱱᱟ ᱥᱟᱦᱴᱟᱠᱚ",
        "listgrouprights-rights": "ᱟᱹᱭᱫᱟᱹᱨᱤᱠᱚ",
        "listgrouprights-helppage": "Goṛo:Gaõta ạidạri",
        "listgrouprights-members": "(ᱥᱚᱦᱮᱫᱠᱩᱣᱟᱜ ᱛᱟᱹᱞᱠᱟᱹ)",
-       "listgrouprights-addgroup-all": "Joto gaõtare ko soṅgekom",
-       "listgrouprights-removegroup-all": "Joto gaõtaren ko ocoḱgiḍikom",
+       "listgrouprights-addgroup-all": "ᱡᱚᱛᱚ ᱜᱟᱶᱛᱟᱠᱩ ᱥᱮᱞᱮᱫ ᱠᱩ ᱢᱮ",
+       "listgrouprights-removegroup-all": "ᱡᱚᱛᱚ ᱜᱟᱶᱛᱟᱠᱩ ᱚᱪᱚᱜ ᱠᱩ ᱢᱮ",
        "emailuser": "ᱱᱩᱭ ᱵᱮᱵᱦᱟᱨᱤᱡ e-mail ᱮᱢᱟᱭᱢᱮ",
-       "noemailtitle": "E-mail á¹­hikạna do banuḱa",
+       "noemailtitle": "E-mail á±´á±·á±¤á± á±\9fᱹᱱá±\9f á±«á±\9a á±µá±\9fᱹᱱᱩá±\9c-á±\9f",
        "emailusername": "ᱵᱮᱵᱦᱟᱨᱤᱡ ᱧᱩᱛᱩᱢᱺ",
        "emailusernamesubmit": "ᱮᱢ",
        "emailfrom": "ᱠᱩᱞᱤᱪ:",
        "changed": "Bodolena",
        "deletepage": "ᱥᱟᱦᱴᱟ ᱜᱮᱫᱽ ᱢᱮ",
        "delete-legend": "ᱜᱮᱫ ᱜᱤᱰᱤ",
-       "actioncomplete": "kami Chabae-ena",
-       "actionfailed": "Kami bang hoe-lena",
+       "actioncomplete": "ᱠᱟᱹᱢᱤ ᱪᱟᱵᱟᱭᱱᱟ",
+       "actionfailed": "ᱠᱟᱹᱢᱤ ᱵᱟᱝ ᱦᱩᱭᱞᱮᱱᱟ",
        "dellogpage": "ᱢᱩᱪᱷᱟᱹᱣ ᱪᱟᱹᱵᱤ",
        "rollbacklink": "ᱜᱷᱩᱨᱞᱟᱹᱣ ᱟᱹᱪᱩᱨ",
        "rollbacklinkcount": "ᱜᱚᱰᱟᱣ ᱨᱩᱣᱟᱹᱲ $1 {{PLURAL:$1|ᱥᱟᱯᱲᱟᱣ|ᱥᱟᱯᱲᱟᱣᱠᱚ}}",
        "protectedarticle": "ᱨᱩᱠᱷᱤᱭᱟᱹᱜᱮᱭᱟ \"[[$1]]\"",
        "modifiedarticleprotection": "\"[[$1]]\" ᱞᱟᱹᱜᱤᱫ ᱨᱩᱠᱷᱭᱟᱹ ᱟᱲᱮ ᱵᱚᱫᱚᱞᱮᱱᱟ",
        "protectcomment": "ᱚᱡᱮ:",
-       "protectexpiry": "Cabaḱ",
+       "protectexpiry": "ᱢᱮᱭᱟᱫ ᱯᱟᱨᱚᱢ:",
        "protect-default": "ᱡᱚᱛᱚ ᱵᱮᱵᱷᱟᱨᱤᱭᱟᱹᱠᱚ ᱫᱟᱣ ᱮᱢ",
        "restriction-edit": "ᱥᱟᱯᱲᱟᱣ",
        "restriction-move": "ᱚᱪᱚᱜ",
        "ipblocklist": "ᱵᱮᱵᱦᱟᱨᱤᱡ ᱮᱥᱮᱫᱜᱮᱭᱟᱭ",
        "ipblocklist-submit": "ᱥᱮᱸᱫᱽᱨᱟ",
        "infiniteblock": "ᱚᱦᱤᱥᱟᱹᱵᱽ",
-       "emailblock": "E-mail do esetgea",
+       "emailblock": "E-mail ᱵᱚᱸᱫᱷ ᱜᱮᱭᱟ",
        "blocklink": "ᱮᱥᱮᱫᱽ",
        "unblocklink": "ᱵᱟᱝ ᱮᱥᱮᱫ",
-       "change-blocklink": "block judạ",
+       "change-blocklink": "block ᱵᱚᱫᱚᱞ",
        "contribslink": "ᱮᱱᱮᱢ",
-       "emaillink": "E-mail kulmẽ",
+       "emaillink": "E-mail ᱠᱩᱞᱢᱮ",
        "blocklogpage": "ᱠᱩᱞᱩᱯ ᱮᱥᱮᱫ",
        "blocklogentry": "ᱮᱥᱮᱫ [[$1]] ᱥᱟᱶᱛᱮ ᱪᱟᱵᱟᱜ ᱚᱠᱛᱚ ᱚᱠᱟ ᱫᱚ $2 $3",
        "reblock-logentry": "ᱵᱚᱫᱚᱞᱮᱱᱟ ᱵᱚᱸᱫ ᱥᱟᱡᱟᱣᱠᱚ [[$1]] ᱞᱟᱹᱜᱤᱫ ᱪᱟᱵᱟᱜ ᱚᱠᱛᱚ $2 $3 ᱥᱟᱶ",
        "block-log-flags-nocreate": "ᱮᱠᱟᱣᱱᱴ ᱵᱮᱱᱟᱣ ᱵᱚᱱᱫᱷ ᱜᱮᱭᱟ",
-       "block-log-flags-noemail": "E-mail do esetgea",
-       "block-log-flags-hiddenname": "Beoharićaḱ ńutum do ukugea",
+       "block-log-flags-noemail": "E-mail ᱵᱚᱸᱫᱷ ᱜᱮᱭᱟ",
+       "block-log-flags-hiddenname": "ᱵᱮᱵᱦᱟᱨᱤᱭᱟᱜ ᱧᱩᱛᱩᱢ ᱩᱠᱩ ᱜᱮᱭᱟ",
        "proxyblocker": "ᱯᱨᱚᱠᱥᱤ ᱮᱥᱮᱫᱤᱡ",
        "movepagebtn": "ᱥᱟᱦᱴᱟ ᱥᱟᱦᱟᱭᱢᱮ",
-       "pagemovedsub": "Ocogoḱ do hoena",
+       "pagemovedsub": "ᱚᱪᱟᱜ ᱫᱚ ᱦᱩᱭᱱᱟ",
        "movelogpage": "ᱛᱟᱞᱟ ᱪᱟᱹᱵᱤ ᱚᱪᱚᱜᱽᱢᱮ",
        "revertmove": "ᱨᱩᱣᱟᱹᱲ ᱟᱹᱜᱩ",
        "export": "ᱟᱹᱜᱩᱭᱮᱱ ᱥᱟᱦᱴᱟᱠᱚ",
        "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>",
+       "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": "ᱦᱮᱸ",
index d2c3b55..0e60c0f 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!",
        "expansion-depth-exceeded-category-desc": "La pàggina passa lu funnu d'espanzioni màssimu.",
        "expansion-depth-exceeded-warning": "Sta pàggina passau lu funnu d'espanzioni màssimu",
        "parser-unstrip-loop-warning": "Rilivatu un cìrculu di Unstrip",
-       "parser-unstrip-recursion-limit": "Passatu lu lìmiti di ricursioni di Unstrip ($1)",
+       "unstrip-depth-warning": "Passatu lu lìmiti di ricursioni di Unstrip ($1)",
        "converter-manual-rule-error": "Rilivatu n'erruri nta na règula manuali di cunvirsioni di lingua",
        "undo-success": "Lu canciamentu pò essiri annullatu.\nVirificati lu paraguni ccà sutta p'accuràrivi ca lu cuntinutu è chiddu addisiatu e doppu sarvati la pàggina pi cumplitari l'annullamentu.",
        "undo-failure": "Lu canciamentu nun pò èssiri annullatu a càusa d'un cunflittu cu li canciamenti ntirmedi.",
        "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..0b5cd26 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!",
        "expansion-depth-exceeded-category-desc": "The page exceeds the mucklest expansion depth.",
        "expansion-depth-exceeded-warning": "Page owershote the expansion depth",
        "parser-unstrip-loop-warning": "Unstrip luip detected",
-       "parser-unstrip-recursion-limit": "Unstrip recursion limit owershote ($1)",
+       "unstrip-depth-warning": "Unstrip recursion limit owershote ($1)",
        "converter-manual-rule-error": "mistak detected in manual leid conversion rule",
        "undo-success": "The eidit can be ondun. Please check the chynges albo tae check that this is whit ye wint tae dae, n than hain the chynges albo tae be duin ondaein the eidit.",
        "undo-failure": "The eedit coudna be ondun cause o confleectin eedits inatween.",
        "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..a8c01a6 100644 (file)
@@ -7,7 +7,8 @@
                        "Sindhu",
                        "Mehtab ahmed",
                        "Macofe",
-                       "Indus Asia"
+                       "Indus Asia",
+                       "BukhariSaeed"
                ]
        },
        "tog-underline": "ڳنڍڻي هيٺان لڪير:",
        "mytalk": "بحث",
        "anontalk": "بحث",
        "navigation": "رھنمائي",
-       "and": "&#32؛۽",
+       "and": "&#32;۽",
        "faq": "ڪپس",
        "actions": "ڪارگذاريون",
        "namespaces": "نانءُپولارَ",
        "table_pager_limit_submit": "ھلو",
        "table_pager_empty": "ڪو بہ نتيجو نہ مليو",
        "autoredircomment": "صفحي کي [[$1]] ڏانھن چوريو",
+       "autosumm-newblank": "خالي صفحو تخليق ڪيو",
        "watchlistedit-normal-title": "نظر ۾ فھرست کي سنواريو",
        "watchlistedit-raw-titles": "عنوانَ:",
        "watchlistedit-clear-titles": "عنوانَ:",
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..26e086b 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!",
        "expansion-depth-exceeded-category-desc": "Moɲoo ga hayandiri guusuyan kul ibeeroo hoo.",
        "expansion-depth-exceeded-warning": "Moɲoo ga hayandiri guusuyanoo hoo",
        "parser-unstrip-loop-warning": "Feferiyan-naŋ zollo maatandi",
-       "parser-unstrip-recursion-limit": "Feferiyan-filla dimmaa hoo ($1)",
+       "unstrip-depth-warning": "Feferiyan-filla dimmaa hoo ($1)",
        "converter-manual-rule-error": "Firka bangay kanbe šenni bereyan ašariyaa ra",
        "undo-success": "Fasalyan ga hin ka borrandi.\nGuna ganda deedandiyanoo ka tabatandi kaŋ woo no war ga boona k'a tee, nda waati din wa ganda barmawey gaabu ka fasalyan borroyanoo benandi.",
        "undo-failure": "Barmaa ši hin ka taafeeri zama game barmay fooyaŋ ga cere hoo.",
        "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..abef360 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!",
        "expansion-depth-exceeded-category-desc": "Stranica je prešla najveću dubinu proširenja.",
        "expansion-depth-exceeded-warning": "Na ovoj stranici dubina proširenja je prevelika",
        "parser-unstrip-loop-warning": "Utvrđena je petlja",
-       "parser-unstrip-recursion-limit": "Dosegnuto je ograničenje rekurzije ($1)",
+       "unstrip-depth-warning": "Dosegnuto je ograničenje rekurzije ($1)",
        "converter-manual-rule-error": "Pronađena je greška u pravilu za ručno pretvaranje jezika",
        "undo-success": "Izmjena se može vratiti.\nMolimo da provjerite usporedbu ispod da budete sigurni da to želite učiniti, a zatim spremite promjene da bi ste završili vraćanje izmjene.",
        "undo-failure": "Izmjene se ne mogu vratiti zbog konflikta sa izmjenama u međuvremenu.",
        "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..6a45f15 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ඔබගේ වෙනස්කිරීම් තවමත් සුරැකීමට ලක් කොට නොමැත!",
        "expansion-depth-exceeded-category-desc": "මෙම පිටුව උපරිම පුළුල් ගැඹුරු ඉක්මවා යයි.",
        "expansion-depth-exceeded-warning": "පිටුව පුළුල් ගැඹුරු ඉක්මවා",
        "parser-unstrip-loop-warning": "Unstrip ලූප අනාවරණය",
-       "parser-unstrip-recursion-limit": "Unstrip සහානුයාත සීමාව ($1) ඉක්මවා",
+       "unstrip-depth-warning": "Unstrip සහානුයාත සීමාව ($1) ඉක්මවා",
        "converter-manual-rule-error": "අත්පොත භාෂා පරිවර්තනය පාලනය අනාවරණය දෝෂ",
        "undo-success": "මෙම සංස්කරණය අහෝසි කල හැක.\nපහත දක්වා ඇති සැසැඳුම පරික්ෂා කර බලා ඔබගේ අභිලාෂය මෙයමැයි තහවුරු කොට ගෙන, සංස්කරණය අහෝසිකිරීම සඳහා පහත දැක්වෙන වෙනස්වීම් සුරකින්න.",
        "undo-failure": "පරස්පර විරෝධී අතරමැදි සංස්කරණ හේතුවෙන් මෙම සංස්කරණය අහෝසි කල නොහැක.",
        "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..7d4b500 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é!'''",
        "expansion-depth-exceeded-category-desc": "Stránka prekročila maximálnu hĺbku expanzie.",
        "expansion-depth-exceeded-warning": "Stránka prekročila povolenú hĺbku expanzie",
        "parser-unstrip-loop-warning": "Zistené zacyklenie volania rozširovacej značky",
-       "parser-unstrip-recursion-limit": "Prektočený limit rekurzie volania rozširovacej značky ($1)",
+       "unstrip-depth-warning": "Prektočený limit rekurzie volania rozširovacej značky ($1)",
        "converter-manual-rule-error": "Bola zistená chyba v pravidle manuálnej konverzie jazyka",
        "undo-success": "Úpravu je možné vrátiť.\nProsím, skontrolujte tento rozdiel, čím overíte, že táto úprava je tá, ktorú chcete. Následne uložte zmeny, čím ukončíte vrátenie.",
        "undo-failure": "Úpravu nie je možné vrátiť kvôli konfliktným medziľahlým úpravám.",
        "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 b08b5cf..117ded9 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!",
        "expansion-depth-exceeded-category-desc": "Stran presega največjo globino razširitve.",
        "expansion-depth-exceeded-warning": "Stran je prekoračila globino razširitve",
        "parser-unstrip-loop-warning": "Zaznal sem odvezano zanko",
-       "parser-unstrip-recursion-limit": "Presežena je omejitev odvezane rekurzije ($1)",
+       "unstrip-depth-warning": "Presežena je omejitev odvezane rekurzije ($1)",
        "converter-manual-rule-error": "Odkril sem napako v ročnem pravilu pretvorbe jezikov",
        "undo-success": "Urejanje ste razveljavili. Prosimo, preverite prikazano primerjavo redakcij in, če ustrezajo, shranite spremembe.",
        "undo-failure": "Zaradi navzkrižij urejanj, ki so se vmes pojavila, tega urejanja ni moč razveljaviti.",
        "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 a679ae4..8fdfa58 100644 (file)
        "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*",
        "recentchanges-legend-heading": "<strong>Legjenda:</strong>",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (shiko gjithashtu [[Special:NewPages|listën e faqeve të reja]])",
        "recentchanges-submit": "Shfaq",
+       "rcfilters-tag-remove": "Hiq '$1'",
+       "rcfilters-legend-heading": "<strong>Lista e shkurtesave:</strong>",
+       "rcfilters-other-review-tools": "Veglat tjera të rishikimit",
+       "rcfilters-group-results-by-page": "Grupo rezultatet sipas faqës",
        "rcfilters-activefilters": "Filtrat aktiv",
        "rcfilters-advancedfilters": "Filtra të avancuar",
+       "rcfilters-limit-title": "Rezultatet për të treguar",
+       "rcfilters-limit-and-date-label": "$1 {{PLURAL:$1|ndryshimi|ndryshimet}}, $2",
+       "rcfilters-date-popup-title": "Periudha kohore për ta kërkuar",
+       "rcfilters-days-title": "Ditët e fundit",
+       "rcfilters-hours-title": "Orët e fundit",
+       "rcfilters-days-show-days": "$1 {{PLURAL:$1|dit|ditë}}",
+       "rcfilters-days-show-hours": "$1 {{PLURAL:$1|orë|orë}}",
+       "rcfilters-highlighted-filters-list": "Theksuar: $1",
        "rcfilters-quickfilters": "Filtrat e ruajtur",
-       "rcfilters-quickfilters-placeholder-title": "Asnjë lidhje e ruajtur",
+       "rcfilters-quickfilters-placeholder-title": "Asnjë filtër i ruajtur",
        "rcfilters-savedqueries-defaultlabel": "Filtrat e ruajtur",
        "rcfilters-savedqueries-rename": "Riemro",
        "rcfilters-savedqueries-setdefault": "Vendosur si parazgjedhje",
        "rcfilters-savedqueries-unsetdefault": "Hiqe si parazgjedhje",
        "rcfilters-savedqueries-remove": "Largo",
        "rcfilters-savedqueries-new-name-label": "Emri",
+       "rcfilters-savedqueries-new-name-placeholder": "Shpjego qëllimin e filtrit",
        "rcfilters-savedqueries-apply-label": "Krijo filtër",
+       "rcfilters-savedqueries-apply-and-setdefault-label": "Krijo filtrin e parazgjedhur",
        "rcfilters-savedqueries-cancel-label": "Anulo",
        "rcfilters-savedqueries-add-new-title": "Ruaj rregullimet e tanishme të filtrit",
+       "rcfilters-savedqueries-already-saved": "Këta filtër veç janë ruajtur. Ndrysho konfigurimet për të krijuar një Filtër të Ruajtur.",
        "rcfilters-restore-default-filters": "Kthej filtrat e parazgjedhur",
        "rcfilters-clear-all-filters": "Pastro të gjithë filtrat",
-       "rcfilters-search-placeholder": "Filtro ndryshimet e fundit (shfleto ose fillo të shtypësh)",
+       "rcfilters-show-new-changes": "Shih ndryshimet e fundit",
+       "rcfilters-search-placeholder": "Filtro ndryshimet (përdor menynë ose emrin e filtrit)",
        "rcfilters-invalid-filter": "Filtër jo i vlefshëm",
        "rcfilters-empty-filter": "S'ka filtra aktiv. Tregohen të gjitha kontributet.",
        "rcfilters-filterlist-title": "Filtrat",
        "rcfilters-filter-user-experience-level-newcomer-label": "Të rinjtë",
        "rcfilters-filter-user-experience-level-newcomer-description": "Më pak se 10 redaktime dhe 4 ditë aktivitet.",
        "rcfilters-filter-user-experience-level-learner-label": "Nxënës",
+       "rcfilters-filter-user-experience-level-learner-description": "Redaktorët e regjistruar përvoja e të cilëve bie në mes \"Të rinjve dhe \"Përdoruesve me përvojë.\"",
        "rcfilters-filter-user-experience-level-experienced-label": "Përdorues me përvojë",
-       "rcfilters-filter-user-experience-level-experienced-description": "Më shumë se 30 ditë aktivitete dhe 500 editime.",
+       "rcfilters-filter-user-experience-level-experienced-description": "Redaktorët e regjistruar me më shumë se 500 redaktiv dhe 30 ditë aktivitet.",
        "rcfilters-filtergroup-automated": "Kontribute automatike",
        "rcfilters-filter-bots-label": "Bot",
        "rcfilters-filter-bots-description": "Redaktime të bëra nga vegla automatike.",
        "rcfilters-filter-watchlist-watchednew-description": "Ndryshimet në faqet e Mbikëqyrura që nuk keni vizituar që nga ndodhja e ndryshimeve.",
        "rcfilters-filter-watchlist-notwatched-label": "Jo në Listën e Mbikëqyrjes",
        "rcfilters-filter-watchlist-notwatched-description": "Çdo gjë përveç ndryshimeve në faqet në Listën e Mbikëqyrjes.",
+       "rcfilters-filtergroup-watchlistactivity": "Aktiviteti i listës së mbikqyrjes",
+       "rcfilters-filter-watchlistactivity-unseen-label": "Ndryshimet e papara",
+       "rcfilters-filter-watchlistactivity-unseen-description": "Ndryshimet në faqet që nuk i keni vizituar që nga ndodhja e ndryshimeve.",
+       "rcfilters-filter-watchlistactivity-seen-label": "Ndryshimet e para",
+       "rcfilters-filter-watchlistactivity-seen-description": "Ndryshimet në faqet që keni vizituar që nga ndodhja e ndryshimeve.",
        "rcfilters-filtergroup-changetype": "Lloji i ndryshimit",
        "rcfilters-filter-pageedits-label": "Redaktimet e faqes",
        "rcfilters-filter-pageedits-description": "Redaktimet e përmbajtjes wiki, diskutimeve, përshkrimit të kategorive...",
        "rcfilters-filter-lastrevision-label": "Versioni më i fundit",
        "rcfilters-filter-lastrevision-description": "Vetëm dryshimet më të fundit në një faqe.",
        "rcfilters-filter-previousrevision-label": "Jo rishimet e fundit",
-       "rcfilters-filter-previousrevision-description": "Të gjitha ndryshimet që nuk janë ndryshimet më të fundit në një faqe.",
+       "rcfilters-filter-previousrevision-description": "Të gjitha ndryshimet që nuk janë \"ndryshimet më të fundit\".",
        "rcfilters-filter-excluded": "Përjashtuar",
        "rcfilters-tag-prefix-namespace-inverted": "<strong>:jo</strong> $1",
+       "rcfilters-exclude-button-off": "Përjashto të zgjedhurat",
+       "rcfilters-exclude-button-on": "Po përjashtohen të zgjedhurat",
        "rcfilters-view-tags": "Redaktimet e etiketuara",
        "rcfilters-view-namespaces-tooltip": "Filtro rezultatet sipas hapësirës",
        "rcfilters-view-tags-tooltip": "Filtro rezultatet duke përdorur etiketat e redaktimit",
        "rcfilters-view-return-to-default-tooltip": "Kthehu te menyja kryesore e filtrave",
+       "rcfilters-view-tags-help-icon-tooltip": "Mëso më shumë për redaktimet me etiketë",
        "rcfilters-liveupdates-button": "Freskimet drejtpërdrejtë",
+       "rcfilters-liveupdates-button-title-on": "Ndal freskimet live",
+       "rcfilters-liveupdates-button-title-off": "Shfaq ndryshimet e fundit duke ndodhur",
+       "rcfilters-watchlist-markseen-button": "Shenjo të gjitha ndryshimet si të para",
+       "rcfilters-watchlist-edit-watchlist-button": "Redakto listën tuaj të faqeve të mbikëqyrura",
+       "rcfilters-preference-label": "Fshih versionin e përmirësuar të Ndryshimeve të Fundit",
+       "rcfilters-target-page-placeholder": "Shto një emër faqeje (ose kategorie)",
        "rcnotefrom": "Më poshtë {{PLURAL:$5|është shfaqur ndryshimi|janë shfaqur ndryshimet}} që nga <strong>$3, $4</strong> (deri në <strong>$1</strong>).",
        "rclistfromreset": "Anulo përzgjedhjen e datës",
        "rclistfrom": "Tregon ndryshime së fundmi duke filluar nga $3 $2",
        "uploadstash-badtoken": "Kryerja e këtij veprimi ishte e pasuksesshme, ndoshta sepse kredencialet e tua redaktuese kanë skaduar. Provoni përsëri.",
        "uploadstash-errclear": "Spastrimi i skedave ishte i pasuksesshëm.",
        "uploadstash-refresh": "Rifreskoni listën e skedave",
+       "uploadstash-bad-path": "Shtegu nuk ekziston.",
+       "uploadstash-bad-path-invalid": "Shtegu nuk është në rregull.",
+       "uploadstash-bad-path-unknown-type": "Veprim i panjohur \"$1\".",
+       "uploadstash-zero-length": "Skedari ka madhësinë zero.",
        "invalid-chunk-offset": "Kompensim cope i pavlefshëm",
        "img-auth-accessdenied": "Refuzohet hyrja",
        "img-auth-nopathinfo": "Mungon PATH_INFO.\nShërbyesi juaj nuk e kalon këtë informacion.\nMund të jetë CGI-bazuar dhe nuk mund të mbështesë img_auth.\nShiko https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization.",
        "listfiles-delete": "fshije",
        "listfiles-summary": "Kjo faqe e veçantë tregon tërë skedat e ngarkuara.",
        "listfiles_search_for": "Kërko për emrin e figurës:",
+       "listfiles-userdoesnotexist": "Llogaria e përdoruesit \"$1\" nuk është e regjistruar.",
        "imgfile": "skeda",
        "listfiles": "Lista e figurave",
        "listfiles_thumb": "Parapamje",
        "listfiles_size": "Madhësia (bytes)",
        "listfiles_description": "Përshkrimi",
        "listfiles_count": "Versionet",
+       "listfiles-show-all": "Përfshijë versionet e fundit të skedarëve",
        "listfiles-latestversion": "Versioni aktual",
        "listfiles-latestversion-yes": "Po",
        "listfiles-latestversion-no": "Jo",
        "activeusers-intro": "Kjo është një listë e përdoruesve që kanë qenë aktivë për $1 {{PLURAL:$1|ditë|ditë}}.",
        "activeusers-count": "$1 {{PLURAL:$1|veprim|veprime}} në {{PLURAL:$3|ditën|$3 ditët}} e fundit",
        "activeusers-from": "Trego përdoruesit duke filluar prej te:",
+       "activeusers-groups": "Shfaq përdoruesit që u përkasin grupeve:",
+       "activeusers-excludegroups": "Përjashto përdoruesit që u përkasin grupeve:",
        "activeusers-noresult": "Asnjë përdorues nuk u gjet.",
        "activeusers-submit": "Shfaq përdoruesit aktiv",
        "listgrouprights": "Grupime përdoruesish me privilegje",
        "trackingcategories-name": "Emri i porosisë",
        "trackingcategories-desc": "Kriteret për përfshirje në kategori",
        "restricted-displaytitle-ignored": "Faqet me tituj të shfaqur të injoruar",
+       "trackingcategories-nodesc": "Nuk ka përshkrim.",
        "trackingcategories-disabled": "Kategoria është pamundësuar",
        "mailnologin": "S'ka adresë dërgimi",
        "mailnologintext": "Duhet të keni [[Special:UserLogin|hyrë brenda]] dhe të keni një adresë të saktë në [[Special:Preferences|parapëlqimet]] tuaja për tu dërguar email përdoruesve të tjerë.",
        "ipb_blocked_as_range": "Gabim: Adresa IP $1 nuk është bllokuar direkt dhe nuk mund të zhbllokohet.\nAjo është, megjithatë, e bllokuar si pjesë e rangut $2, që nuk mund të zhbllokohet.",
        "ip_range_invalid": "Shtrirje IP gabim.",
        "ip_range_toolarge": "Radhitja e bllokimeve më të mëdha se /$1 nuk lejohet.",
+       "ip_range_toolow": "Vargjet e IP efektivisht nuk lejohen.",
        "proxyblocker": "Bllokuesi i ndërmjetëseve",
        "proxyblockreason": "IP adresa juaj është bllokuar sepse është një ndërmjetëse e hapur. Ju lutem lidhuni me kompaninë e shërbimeve të Internetit që përdorni dhe i informoni për këtë problem sigurije.",
        "sorbsreason": "Adresa IP e juaj është radhitur si ndërmjetëse e hapur tek lista DNSBL.",
        "tag-filter-submit": "Filtër",
        "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|Etiketa|Etiketat}}]]: $2)",
        "tag-mw-contentmodelchange": "ndryshimi i modelit të përmbajtjes",
+       "tag-mw-new-redirect": "Ridrejtim i ri",
+       "tag-mw-new-redirect-description": "Ndryshimet që krijojnë një ridrejtim të ri ose ndryshojnë faqen në ridrejtim",
+       "tag-mw-removed-redirect": "Hoqi ridrejtimin",
+       "tag-mw-changed-redirect-target": "Caku i ridrejtimit i ndryshuar",
+       "tag-mw-changed-redirect-target-description": "Redaktimet që kanë ndryshuar cakun e një ridrejtimi",
+       "tag-mw-blank": "Duke zbrazur",
+       "tag-mw-blank-description": "Redaktimet që zbrazin një faqe",
+       "tag-mw-replace": "Zëvendësuar",
        "tags-title": "Etiketat",
        "tags-intro": "Kjo faqe liston etiketat që softueri mund t'i shënojë me një redaktim, dhe kuptimin e tyre.",
        "tags-tag": "Emri i etiketës",
index 12a90f7..3178f61 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:Портал заједнице",
        "toc": "Садржај",
        "showtoc": "прикажи",
        "hidetoc": "сакриј",
-       "collapsible-collapse": "СкÑ\83пи",
-       "collapsible-expand": "Ð\9fÑ\80оÑ\88иÑ\80и",
+       "collapsible-collapse": "СакÑ\80иÑ\98",
+       "collapsible-expand": "Ð\9fÑ\80икажи",
        "confirmable-confirm": "Да ли {{GENDER:$1|сте}} сигурни?",
        "confirmable-yes": "Да",
        "confirmable-no": "Не",
        "nstab-template": "Шаблон",
        "nstab-help": "Помоћ",
        "nstab-category": "Категорија",
-       "mainpage-nstab": "Ð\93лавна Ñ\81Ñ\82Ñ\80аниÑ\86а",
+       "mainpage-nstab": "Главна страна",
        "nosuchaction": "Нема такве радње",
        "nosuchactiontext": "Радња наведена у адреси није исправна.\nМожда сте погрешно написали адресу или сте пратили застарелу везу.\nМогуће је и да се ради о грешци у софтверу викија.",
        "nosuchspecialpage": "Нема такве посебне странице",
        "throttled-mailpassword": "Порука за промену лозинке је послата у {{PLURAL:$1|1=последњих сат времена|последња $1 сата|последњих $1 сати}}.\nДа бисмо спречили злоупотребу, подсетник шаљемо само једном у року од {{PLURAL:$1|1=сат времена|$1 сата|$1 сати}}.",
        "mailerror": "Грешка при слању поруке: $1",
        "acct_creation_throttle_hit": "Посетиоци овог викија који користе вашу IP адресу су већ отворили {{PLURAL:$1|1=један налог|$1 налога}} претходни $2, што је највећи дозвољени број у том временском периоду.\nЗбог тога посетиоци с ове IP адресе тренутно не могу отворити више налога.",
-       "emailauthenticated": "Ваша имејл адреса је потврђена $2 у $3.",
+       "emailauthenticated": "Ваша имејл адреса је потврђена на дан $2 у $3.",
        "emailnotauthenticated": "Ваша имејл адреса још увек није потврђена.\nИмејл неће бити послат ни у једном од следећих случајева.",
        "noemailprefs": "Унесите имејл адресу како би ове могућности радиле.",
        "emailconfirmlink": "Потврдите своју имејл адресу",
        "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Историја њеног брисања, заштите и премештања налази се испод:",
        "expansion-depth-exceeded-category-desc": "Страница је прекорачила највећу дубину проширења.",
        "expansion-depth-exceeded-warning": "Страница у којој је прекорачена дубина проширења",
        "parser-unstrip-loop-warning": "Утврђена је петља",
-       "parser-unstrip-recursion-limit": "Прекорачено је ограничење рекурзије ($1)",
+       "unstrip-depth-warning": "Прекорачено је ограничење рекурзије ($1)",
        "converter-manual-rule-error": "Пронађена је грешка у правилу за ручно претварање језика",
        "undo-success": "Измена се може поништити.\nПроверите разлике испод, па сачувајте измене.",
        "undo-failure": "Ова измена се не може поништити због сукоба измена.",
        "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-edits": "Број измена:",
        "prefsnologintext2": "Морате бити пријављени да бисте мењали своја подешавања.",
        "prefs-skin": "Тема",
-       "skin-preview": "Ð\9fрегледај",
+       "skin-preview": "прегледај",
        "datedefault": "Свеједно",
        "prefs-labs": "Пробне могућности",
        "prefs-user-pages": "Корисничке странице",
        "prefs-watchlist": "Списак надгледања",
        "prefs-editwatchlist": "Уређивање списка надгледања",
        "prefs-editwatchlist-label": "Уређивање списка:",
-       "prefs-editwatchlist-edit": "Уреди списак",
-       "prefs-editwatchlist-raw": "Уреди сиров списак",
-       "prefs-editwatchlist-clear": "Ð\98спразни списак",
+       "prefs-editwatchlist-edit": "уреди списак",
+       "prefs-editwatchlist-raw": "уреди сиров списак",
+       "prefs-editwatchlist-clear": "испразни списак",
        "prefs-watchlist-days": "Број дана у списку надгледања:",
        "prefs-watchlist-days-max": "Највише $1 {{PLURAL:$1|дан|дана|дана}}",
        "prefs-watchlist-edits": "Највећи број измена приказаних на списку надгледања:",
        "prefs-watchlist-edits-max": "Највећа вредност је хиљаду",
        "prefs-watchlist-token": "Жетон списка надгледања:",
        "prefs-misc": "Друга подешавања",
-       "prefs-resetpass": "Ð\9fромени лозинку",
-       "prefs-changeemail": "Ð\9fромени или уклони имејл адресу",
+       "prefs-resetpass": "промени лозинку",
+       "prefs-changeemail": "промени или уклони имејл адресу",
        "prefs-setemail": "Постави имејл адресу",
        "prefs-email": "Подешавања имејла",
        "prefs-rendering": "Изглед",
        "timezoneregion-pacific": "Тихи океан",
        "allowemail": "Омогући примање имејлова од других корисника",
        "email-allow-new-users-label": "Омогући примање имејлова од новајлија",
-       "email-blacklist-label": "Ð\9eнемогÑ\83Ñ\9bи следећим корисницима да ми шаљу имејлове:",
+       "email-blacklist-label": "Ð\97абÑ\80ани следећим корисницима да ми шаљу имејлове:",
        "prefs-searchoptions": "Претрага",
        "prefs-namespaces": "Именски простори",
        "default": "подразумевана",
        "prefs-files": "Датотеке",
-       "prefs-custom-css": "Ð\9fрилагођени CSS",
-       "prefs-custom-js": "Ð\9fÑ\80илагоÑ\92ени Ñ\98аваскрипт",
-       "prefs-common-css-js": "Дељени CSS/Јаваскрипт за све теме:",
+       "prefs-custom-css": "прилагођени CSS",
+       "prefs-custom-js": "пÑ\80илагоÑ\92ени Ð\88аваскрипт",
+       "prefs-common-config": "Дељени CSS/Јаваскрипт за све теме:",
        "prefs-reset-intro": "Можете користити ову страницу да поништите своја подешавања на подразумеване вредности.\nОва радња се не може вратити.",
        "prefs-emailconfirm-label": "Потврда имејла:",
        "youremail": "Имејл:",
        "yourvariant": "Варијанта језика:",
        "prefs-help-variant": "Жељена варијанта или правопис за приказ страница са садржајем овог викија.",
        "yournick": "Нови потпис:",
-       "prefs-help-signature": "Ð\9aоменÑ\82аÑ\80е Ð½Ð° Ñ\81Ñ\82Ñ\80аниÑ\86ама Ð·Ð° Ñ\80азговоÑ\80 Ð¿Ð¾Ñ\82пиÑ\81Ñ\83Ñ\98Ñ\82е Ñ\81а Ñ\87еÑ\82иÑ\80и Ñ\82илде: <nowiki>~~~~</nowiki>. Ð\9eваÑ\98 Ð²Ð¸ÐºÐ¸Ñ\82екÑ\81Ñ\82 Ñ\9bе Ð±Ð¸Ñ\82и Ð¿Ñ\80еÑ\82воÑ\80ен Ñ\83 Ð\92аÑ\88 Ð¿Ð¾Ñ\82пиÑ\81 Ð¸ Ñ\82Ñ\80енÑ\83Ñ\82но време.",
+       "prefs-help-signature": "Ð\9aоменÑ\82аÑ\80и Ð½Ð° Ñ\81Ñ\82Ñ\80аниÑ\86ама Ð·Ð° Ñ\80азговоÑ\80 Ð±Ð¸ Ñ\82Ñ\80ебали Ð±Ð¸Ñ\82и Ð¿Ð¾Ñ\82пиÑ\81ани Ñ\81 â\80\9e<nowiki>~~~~</nowiki>â\80\9c Ñ\88Ñ\82о Ñ\9bе Ð±Ð¸Ñ\82и Ð¿Ñ\80еÑ\82воÑ\80ено Ñ\83 Ð\92аÑ\88 Ð¿Ð¾Ñ\82пиÑ\81 Ð¸ време.",
        "badsig": "Потпис је неисправан.\nПроверите ознаке HTML.",
        "badsiglength": "Ваш потпис је предугачак.\nНе сме бити дужи од $1 {{PLURAL:$1|знака|знака|знакова}}.",
        "yourgender": "Како желите да се представите?",
        "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-label-newpage": "Нова страница",
        "recentchanges-label-minor": "Мања измена",
        "recentchanges-label-bot": "Ботовска измена",
-       "recentchanges-label-unpatrolled": "Ð\9eва Ð¸Ð·Ð¼ÐµÐ½Ð° Ñ\98оÑ\88 Ð½Ð¸Ñ\98е Ð¿Ð°Ñ\82Ñ\80олиÑ\80ана",
+       "recentchanges-label-unpatrolled": "Ð\9dепаÑ\82Ñ\80олиÑ\80ана Ð¸Ð·Ð¼Ðµна",
        "recentchanges-label-plusminus": "Промена величине странице у бајтовима",
        "recentchanges-legend-heading": "<strong>Легенда:</strong>",
-       "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} ([[Special:NewPages|списак нових страница]])",
+       "recentchanges-legend-newpage": "Овом изменом је направљена нова страница ([[Special:NewPages|списак нових страница]])",
        "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-liveupdates-button": "Ð\90жÑ\83Ñ\80иÑ\80аÑ\9aа уживо",
+       "rcfilters-view-tags-help-icon-tooltip": "Сазнајте више о означеним изменама",
+       "rcfilters-liveupdates-button": "Ð\90жÑ\83Ñ\80иÑ\80аÑ\98 уживо",
        "rcfilters-liveupdates-button-title-on": "Искључи ажурирања уживо",
        "rcfilters-liveupdates-button-title-off": "Прикажи нове измене уживо",
        "rcfilters-watchlist-markseen-button": "Означи све измене као виђене",
        "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": "Нема траженог додатка.",
        "nopagetext": "Тражена страница не постоји.",
        "pager-newer-n": "{{PLURAL:$1|новији 1|новија $1|новијих $1}}",
        "pager-older-n": "{{PLURAL:$1|старији 1|старија $1|старијих $1}}",
-       "suppress": "Ð\98змена",
+       "suppress": "СакÑ\80иÑ\98",
        "querypage-disabled": "Ова посебна страница је онемогућена ради побољшања перформанси.",
        "apihelp": "API помоћ",
        "apihelp-no-such-module": "Модул „$1“ није пронађен.",
        "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": "врати",
        "undelete-fieldset-title": "Враћање измена",
        "undeleteextrahelp": "Да бисте вратили целу историју странице, оставите све кућице неозначене и кликните на дугме '''''{{int:undeletebtn}}'''''.\nАко желите да вратите одређене измене, означите их и кликните на '''''{{int:undeletebtn}}'''''.",
        "undeleterevisions": "{{PLURAL:$1|Измена}} обрисано: $1",
-       "undeletehistory": "Ако вратите страницу, све ревизије ће бити враћене њеној историји.\nАко је у међувремену направљена нова страница с истим називом, враћене измене ће се појавити у њеној ранијој историји.",
+       "undeletehistory": "Ако вратите страницу, све измене ће бити враћене њеној историји.\nАко је у међувремену направљена нова страница с истим називом, враћене измене ће се појавити у њеној ранијој историји.",
        "undeleterevdel": "Враћање неће бити извршено ако је резултат тога делимично брисање последње измене.\nУ таквим случајевима морате искључити или открити најновије обрисане измене.",
        "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": "дневници",
        "seconds-abbrev": "$1 с",
        "minutes-abbrev": "$1 м",
        "hours-abbrev": "$1 ч",
-       "days-abbrev": "$1 д",
+       "days-abbrev": "$1 д.",
        "seconds": "{{PLURAL:$1|$1 секунда|$1 секунда|$1 секунди}}",
        "minutes": "{{PLURAL:$1|$1 минут|$1 минута|$1 минута}}",
        "hours": "{{PLURAL:$1|$1 сат|$1 сата|$1 сати}}",
        "confirmrecreate": "{{GENDER:$1|Корисник|Корисница}} [[User:$1|$1]] ([[User talk:$1|разговор]]) је {{GENDER:$1|обрисао|обрисала}} ову страницу након што сте почели да је уређујете из следећег разлога:\n: <em>$2</em>\nПотврдите да стварно желите да направите страницу.",
        "confirmrecreate-noreason": "{{GENDER:$1|Корисник|Корисница}} [[User:$1|$1]] ([[User talk:$1|разговор]]) је {{GENDER:$1|обрисао|обрисала}} ову страницу након што сте почели да је уређујете. Потврдите да стварно желите да поново направите ову страницу.",
        "recreate": "Поново направи",
-       "unit-pixel": "px",
+       "unit-pixel": "п",
        "confirm-purge-title": "Освежи ову страницу",
        "confirm_purge_button": "У реду",
        "confirm-purge-top": "Очистити привремену меморију ове стране?",
        "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": "Измене које мењају одредиште преусмерења",
        "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..d558670 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!",
        "expansion-depth-exceeded-category": "Stranice u kojima je prekoračena dubina proširenja",
        "expansion-depth-exceeded-warning": "Stranica u kojoj je prekoračena dubina proširenja",
        "parser-unstrip-loop-warning": "Utvrđena je petlja",
-       "parser-unstrip-recursion-limit": "Prekoračeno je ograničenje rekurzije ($1)",
+       "unstrip-depth-warning": "Prekoračeno je ograničenje rekurzije ($1)",
        "converter-manual-rule-error": "Pronađena je greška u pravilu za ručno pretvaranje jezika",
        "undo-success": "Izmena se može poništiti.\nProverite razlike ispod, pa sačuvajte izmene.",
        "undo-failure": "Ova izmena se ne može poništiti zbog sukoba izmena.",
        "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:",
        "userrights-changeable-col": "Grupe koje možete da promenite",
        "userrights-unchangeable-col": "Grupe koje ne možete da promenite",
        "userrights-irreversible-marker": "$1*",
-       "userrights-expiry-existing": "Postojeće vrijeme isteka: $3, $2",
-       "userrights-expiry-othertime": "Drugo vrijeme:",
+       "userrights-expiry-existing": "Postojeće vreme isteka: $3, $2",
+       "userrights-expiry-othertime": "Drugo vreme:",
        "userrights-conflict": "Sukob promena korisničkih prava! Molimo proverite vaše izmene.",
        "group": "Grupa:",
        "group-user": "Korisnici",
        "rcfilters-days-show-days": "$1 {{PLURAL:$1|dana|dana}}",
        "rcfilters-days-show-hours": "$1 {{PLURAL:$1|sat|sata}}",
        "rcfilters-quickfilters-placeholder-description": "Da biste sačuvali svoja podešavanja filtera i upotrebljavali ih kasnije, kliknite na ikonu za oznaku u području aktivnih filtera, ispod.",
-       "rcfilters-search-placeholder": "Filter skorašnjih izmjena (pretražite ili počnite kucati)",
+       "rcfilters-search-placeholder": "Filtriraj skorašnje izmene (upotrebite meni ili potražite ime filtera)",
        "rcfilters-filtergroup-authorship": "Autorstvo doprinosa",
        "rcfilters-filter-editsbyself-label": "Vaše izmene",
        "rcfilters-filter-editsbyother-label": "Izmene drugih",
        "rcfilters-filter-user-experience-level-unregistered-label": "Neregistrovani",
        "rcfilters-filter-user-experience-level-unregistered-description": "Urednici koji nisu prijavljeni.",
        "rcfilters-filter-user-experience-level-newcomer-label": "Novajlije",
-       "rcfilters-filter-user-experience-level-newcomer-description": "Manje od 10 izmjena i 4 dana aktivnosti.",
+       "rcfilters-filter-user-experience-level-newcomer-description": "Manje od 10 izmena i 4 dana aktivnosti.",
        "rcfilters-filter-user-experience-level-learner-label": "Učenici",
-       "rcfilters-filter-user-experience-level-learner-description": "Više dana aktivnosti i izmjena od „novajlija”, ali manje od „iskusnih korisnika”.",
+       "rcfilters-filter-user-experience-level-learner-description": "Više dana aktivnosti i izmena od „novajlija”, ali manje od „iskusnih korisnika”.",
        "rcfilters-filter-user-experience-level-experienced-label": "Iskusni korisnici",
        "rcfilters-filter-user-experience-level-experienced-description": "Registrovani urednici sa više od 500 izmena i 30 dana aktivnosti.",
        "rcfilters-filter-bots-description": "Izmene napravljene automatizovanim alatima.",
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..1c0efdd 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!",
        "expansion-depth-exceeded-category-desc": "Kacana ngaleuwihan wates jero ékspansina.",
        "expansion-depth-exceeded-warning": "Kaca ngaleuwihan jero ékspansi",
        "parser-unstrip-loop-warning": "Luncatan buni kanyahoan",
-       "parser-unstrip-recursion-limit": "Wates rékursi buni kaleuwihi ($1)",
+       "unstrip-depth-warning": "Wates rékursi buni kaleuwihi ($1)",
        "converter-manual-rule-error": "Kasalahan kanyahoan dina aturan tarjamahan basa manual",
        "undo-success": "Éditan ieu bisa dibolaykeun. Mangga pariksa babandingan di handap pikeun mastikeun mémang anjeun miharep éta parobahan. Mun geus yakin, mangga simpen parobahanana pikeun ngabolaykeun éditan.",
        "undo-failure": "Éditan teu bisa dibolaykeun alatan kaselang ku éditan séjén.",
        "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..bd10207 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!",
        "expansion-depth-exceeded-category-desc": "Sidan har överstridit det maximala expansionsdjupet.",
        "expansion-depth-exceeded-warning": "Sidan överskrider expansionsdjupet",
        "parser-unstrip-loop-warning": "Tagavskalningsloop upptäcktes",
-       "parser-unstrip-recursion-limit": "Tagavskalningsloop överskred rekursionsgränsen ($1)",
+       "unstrip-depth-warning": "Tagavskalningsloop överskred rekursionsgränsen ($1)",
        "converter-manual-rule-error": "Fel upptäcktes i manuell språkkonverteringsregel",
        "undo-success": "Redigeringen kan göras ogjord.\nVar god och kontrollera jämförelsen nedan för att bekräfta att detta är vad du avser att göra, och spara sedan ändringarna nedan för att göra redigeringen ogjord.",
        "undo-failure": "Redigeringen kunde inte göras ogjord på grund av konflikt med mellanliggande redigeringar.",
        "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..2213ae6 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": "'''இது ஒரு முன்தோற்றம் மட்டுமே''', உங்கள் மாற்றங்கள் இன்னும் சேமிக்கப்படவில்லை!",
        "expansion-depth-exceeded-category-desc": "இந்த பக்கம் அதிகபட்ச விரிவு ஆழத்தைத் தாண்டியது",
        "expansion-depth-exceeded-warning": "விரிவு ஆழம் பக்க வரம்பை மீறிவிட்டது",
        "parser-unstrip-loop-warning": "Unstrip வளையம் காணப்பட்டது",
-       "parser-unstrip-recursion-limit": "Unstrip மீள்தோன்றும் எல்லை மீறப்பட்டது ($1)",
+       "unstrip-depth-warning": "Unstrip மீள்தோன்றும் எல்லை மீறப்பட்டது ($1)",
        "converter-manual-rule-error": "கைமுறை மொழி மாற்றம் விதியில் பிழை கண்டுபிடிக்கப்பட்டது",
        "undo-success": "இத்தொகுப்பை மீளமைக்க முடியும். தயவு செய்து, கீழே காட்டப்பட்டுள்ள ஒப்பீட்டைப் பார்த்து, நீங்கள் செய்ய முயற்சிப்பது இதுதானா? என்பதை உறுதிப்படுத்திக் கொண்டு '''பக்கத்தைச் சேமிக்கவும்''' என்பதன் மேல் சொடுக்கவும்.",
        "undo-failure": "முரண்பாடான இடைப்பட்டத் தொகுப்புகள் காரணமாக இத்தொகுப்பை மீளமைக்க முடியாது.",
        "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..b273a2a 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మీ మార్పులు ఇంకా భద్రమవ్వలేదు!",
        "expansion-depth-exceeded-category-desc": "పేజీ గరిష్ఠ విస్తరణ లోతును మించింది",
        "expansion-depth-exceeded-warning": "పేజీ విస్తరణ లోతును మించింది",
        "parser-unstrip-loop-warning": "Unstrip లూపును కనుక్కున్నాం",
-       "parser-unstrip-recursion-limit": "Unstrip రికర్షన్ పరిమితిని దాటింది ($1)",
+       "unstrip-depth-warning": "Unstrip రికర్షన్ పరిమితిని దాటింది ($1)",
        "converter-manual-rule-error": "మానవిక భాషాంతరీకరణ పరికరంలో లోపాన్ని కనుక్కున్నాం",
        "undo-success": "దిద్దుబాటును రద్దు చెయ్యవచ్చు. కింది పోలికను చూసి, మీరు చెయ్యదలచినది ఇదేనని నిర్ధారించుకోండి. ఆ తరువాత మార్పులను భద్రపరచి దిద్దుబాటు రద్దును పూర్తి చెయ్యండి.",
        "undo-failure": "మధ్యలో జరిగిన దిద్దుబాట్లతో తలెత్తిన ఘర్షణ కారణంగా ఈ దిద్దుబాటును రద్దు చెయ్యలేక పోయాం.",
        "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..cec7470 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ยังไม่ได้บันทึกการเปลี่ยนแปลงของคุณ!",
        "expansion-depth-exceeded-category": "หน้าที่ความลึกการขยายเกิน",
        "expansion-depth-exceeded-warning": "หน้าเกินความลึกการขยาย",
        "parser-unstrip-loop-warning": "พบวงวน unstrip",
-       "parser-unstrip-recursion-limit": "ขีดจำกัดการเรียกซ้ำ unstrip เกิน ($1)",
+       "unstrip-depth-warning": "ขีดจำกัดการเรียกซ้ำ unstrip เกิน ($1)",
        "converter-manual-rule-error": "พบข้อผิดพลาดในกฎการแปลงผันภาษาด้วยมือ",
        "undo-success": "สามารถย้อนการแก้ไขนี้กลับได้ \nกรุณาตรวจสอบข้อแตกต่างด้านล่างเพื่อทวนสอบว่านี่เป็นสิ่งที่คุณต้องการทำ แล้วบันทึกการเปลี่ยนแปลงด้านล่างเพื่อเสร็จสิ้นการย้อนการแก้ไขกลับ",
        "undo-failure": "การแก้ไขนี้ไม่สามารถย้อนกลับได้ เนื่องจากขัดแย้งกับการแก้ไขระหว่างกลาง",
        "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..f3b6a6b 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!",
        "expansion-depth-exceeded-category": "Mga pahina kung saan lampas ang lalim ng paglawak",
        "expansion-depth-exceeded-warning": "Lumampas ang pahina sa lalim ng paglawak",
        "parser-unstrip-loop-warning": "Napansin ang silo ng hindi pagtalop",
-       "parser-unstrip-recursion-limit": "Nalampasan ang hangganan ng rekursiyon ng hindi pagtalop ($1)",
+       "unstrip-depth-warning": "Nalampasan ang hangganan ng rekursiyon ng hindi pagtalop ($1)",
        "converter-manual-rule-error": "Napansin ang kamalian sa alituntunin ng kinakamay na pagpapalit ng wika",
        "undo-success": "Matatanggal ang pagbabago.\nPakitingnan ang paghahambing sa ibaba para masiyasat kung ito ang ibig mong gawin, at pagkatapos sagipin ang mga pagbabago sa ibaba para matapos ang pagtatanggal ng pagbabago.",
        "undo-failure": "Hindi matanggal ang pagbabago dahil sa magkakasalungat na panggitnang mga pagbabago.",
        "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..8f4008d 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!",
        "expansion-depth-exceeded-category-desc": "Sayfa azami genişleme derinliğini aşıyor.",
        "expansion-depth-exceeded-warning": "Sayfa genişletme derinliği aşıldı",
        "parser-unstrip-loop-warning": "Yineleme döngüsü algılandı",
-       "parser-unstrip-recursion-limit": "($1) yineleme sınırı aşıldı",
+       "unstrip-depth-warning": "($1) yineleme sınırı aşıldı",
        "converter-manual-rule-error": "Elle yapılandırma dil dönüşüm kuralı hatası tespit edildi",
        "undo-success": "Bu değişiklik geri alınabilir. Lütfen aşağıdaki karşılaştırmayı kontrol edin, gerçekten bu değişikliği yapmak istediğinizden emin olun ve sayfayı kaydederek bir önceki değişikliği geriye alın.",
        "undo-failure": "Değişikliklerin çakışması nedeniyle geri alma işlemi başarısız oldu.",
        "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:",
        "rcfilters-filter-categorization-description": "Kategorilere eklenen veya kaldırılan sayfaların kayıtları.",
        "rcfilters-filter-logactions-label": "Günlüğü tutulan işlemler",
        "rcfilters-filter-logactions-description": "Hizmetli işlemleri, hesap oluşturmalar, sayfa silmeler, yüklemeler...",
+       "rcfilters-filter-lastrevision-label": "Son revizyon",
+       "rcfilters-filter-previousrevision-label": "Son revizyon değil",
        "rcfilters-liveupdates-button": "Canlı güncelleme",
        "rcfilters-liveupdates-button-title-on": "Canlı güncellemeyi kapat",
        "rcfilters-liveupdates-button-title-off": "Yeni değişiklikleri yapıldıkları anda görüntüleyin",
        "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..2878d7e 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ئۆزگەرتكەن مەزمۇنىڭىز تېخى ساقلانمىدى!",
        "expansion-depth-exceeded-category": "كېڭەيتىلگەن چوڭقۇرلۇق بەت چەكلىمىسىدىن ئېشىپ كەتتى",
        "expansion-depth-exceeded-warning": "بەت كېڭەيتىلگەن چوڭقۇرلۇقتىن ئېشىپ كەتتى",
        "parser-unstrip-loop-warning": "دەۋرىيلىك بايقالدى",
-       "parser-unstrip-recursion-limit": "قايتىلانما چەكلىمە ($1) دىن ئېشىپ كەتتى",
+       "unstrip-depth-warning": "قايتىلانما چەكلىمە ($1) دىن ئېشىپ كەتتى",
        "converter-manual-rule-error": "قولدا ئالماشتۇرىدىغان قائىدىدە خاتالىق بايقالدى",
        "undo-success": "بۇ تەھرىردىن يېنىۋالغىلى بولىدۇ\nتۆۋەندىكى سېلىشتۇرۇشنى تەكشۈرۈپ بۇنىڭ ئۆزىڭىزنىڭ ئويى ئىكەنلىكىنى دەلىللەڭ، ئاندىن تۆۋەندىكى ئۆزگەرتىشنى ساقلاپ تەھرىرلەشتىن يېنىۋىلىڭ.",
        "undo-failure": "ئوتتۇرىلىقتا بىردەك بولماسلىق سەۋەبىدىن بۇ تەھرىردىن يېنىۋالغىلى بولمايدۇ.",
        "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 81c211f..1f4c2f1 100644 (file)
        "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Ваші зміни ще не збережено!",
        "expansion-depth-exceeded-category-desc": "На сторінці перевищено максимально допустима глибину розкриття.",
        "expansion-depth-exceeded-warning": "На сторінці перевищено межу глибини вкладеності",
        "parser-unstrip-loop-warning": "Виявлено незакритий тег (такий, як <pre>)",
-       "parser-unstrip-recursion-limit": "Перевищено межу вкладеної рекурсії ($1) для парсера.",
+       "unstrip-depth-warning": "Перевищено межу вкладеної рекурсії ($1) для парсера.",
        "converter-manual-rule-error": "Помилка в ручному правилі перетворення мови",
        "undo-success": "Редагування може бути скасовано.\nБудь ласка, перевірте порівняння нижче, щоб впевнитись, що це те, що ви хочете зробити, а потім збережіть зміни, щоб закінчити скасування редагування.",
        "undo-failure": "Неможливо скасувати редагування через несумісність проміжних змін.",
        "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 e33c545..cc9e535 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": "قطعہ کی تدوین حمایت شدہ نہیں ہے",
        "expansion-depth-exceeded-category-desc": "اس صفحہ میں توسیع کی گہرائی اپنی حد سے تجاوز کر گئی۔",
        "expansion-depth-exceeded-warning": "صفحہ میں توسیع کی گہرائی اپنی حد سے تجاوز کر گئی",
        "parser-unstrip-loop-warning": "unstrip فنکشن میں تکرار پایا گیا",
-       "parser-unstrip-recursion-limit": "unstrip فنکشن میں تکرار اپنی حد سے تجاوز کر گیا ($1)",
+       "unstrip-depth-warning": "unstrip فنکشن میں تکرار اپنی حد سے تجاوز کر گیا ($1)",
        "converter-manual-rule-error": "زبان کی دستی تبدیلی کے ضوابط میں نقص دریافت ہوا",
        "undo-success": "اس ترمیم کو واپس پھیرا جا سکتا ہے۔\nبراہ کرم ذیل میں موجود موازنہ ملاحظہ فرمائیں اور یقین کر لیں کہ اس موازنے میں موجود فرق ہی آپ کا مقصود ہے۔ اس کے بعد تبدیلیوں کو محفوظ کر دیں، ترمیم واپس پھیر دی جائے گی۔",
        "undo-failure": "درمیان میں متنازع ترامیم کی موجودگی کی بنا پر اس ترمیم کو واپس نہیں پھیرا جا سکا۔",
        "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..5c6403f 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!",
        "expansion-depth-exceeded-category": "Pajine ndove che vien superà ła profondità de espansion",
        "expansion-depth-exceeded-warning": "Sta pajina ga superà el limite de profondità de espansion",
        "parser-unstrip-loop-warning": "Xe sta riłevà un ciclo de Unstrip",
-       "parser-unstrip-recursion-limit": "Superadi i limiti de recursion de Unstrip ($1)",
+       "unstrip-depth-warning": "Superadi i limiti de recursion de Unstrip ($1)",
        "converter-manual-rule-error": "Rilevà eror inte ła regoła manuałe de conversion de ła lèngua",
        "undo-success": "Sta modifica la pode èssar anulà. Verifica el confronto presentà de seguito par èssar sicuro che el contenuto el sia come te lo voli e quindi salva le modifiche par conpletar la procedura de anulamento.",
        "undo-failure": "No se pol mìa anular la modifica, par via de un conflito con modifiche intermedie.",
        "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..04ba719 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",
        "expansion-depth-exceeded-category-desc": "Trang có độ sâu bung bản mẫu vượt quá giới hạn cho phép.",
        "expansion-depth-exceeded-warning": "Trang bung bản mẫu sâu quá",
        "parser-unstrip-loop-warning": "Vòng lặp unstrip",
-       "parser-unstrip-recursion-limit": "Đã vượt quá giới hạn về độ sâu đệ quy unstrip ($1)",
+       "unstrip-depth-warning": "Đã vượt quá giới hạn về độ sâu đệ quy unstrip ($1)",
        "converter-manual-rule-error": "Lỗi được phát hiện trong quy tắc chuyển đổi ngôn ngữ thủ công",
        "undo-success": "Các sửa đổi có thể được lùi lại. Xin hãy kiểm tra phần so sánh bên dưới để xác nhận lại những gì bạn muốn làm, sau đó lưu thay đổi ở dưới để hoàn tất việc lùi lại sửa đổi.",
        "undo-failure": "Sửa đổi không thể phục hồi vì đã có những sửa đổi mới ở sau.",
        "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..946da53 100644 (file)
        "viewsource-title": "Côde sourdant di «$1»",
        "viewsourcetext": "Loukîz li contnou d' l’ årtike, et s’ li rcopyî si vos vloz, por vos bouter dsu foû des fyis:",
        "protectedinterface": "Cisse pådje ci dene on tecse d' eterface pol programe, eyet elle a stî protedjeye po s' waeranti siconte des abus.",
-       "editinginterface": "<stron>Asteme:</strong> Vos estoz ki candje ene pådje eployeye po fé l' tecse po l' eterface do programe.\nLes candjmints ki vos frîz vont candjî l' rivnance di l' eterface po ls ôtes uzeus do wiki.",
+       "editinginterface": "<strong>Asteme:</strong> Vos estoz ki candje ene pådje eployeye po fé l' tecse po l' eterface do programe.\nLes candjmints ki vos frîz vont candjî l' rivnance di l' eterface po ls ôtes uzeus do wiki.",
        "cascadeprotected": "Cisse pådje ci a stî protedjeye siconte des candjmints, pask' ele est eploye ådvins {{PLURAL:$1|del pådje shuvante k' est protedjeye|des pådjes shuvantes ki sont protedjeyes}} avou l' tchuze «e cascåde» en alaedje:\n$2",
        "logouttext": "<strong>Vos vs avoz dislodjî.</strong>\n\nNotez ki des pådjes k' i gn a si pôrént continouwer a vey come si vos estîz elodjî, disk' a tant ki vos vudrîz l' muchete di vosse betchteu waibe.",
        "welcomeuser": "Bénvnowe, $1!",
        "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..3e1f16e 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>倷个修改还朆保存!",
        "parser-template-recursion-depth-warning": "模板递归深度超限($1)",
        "language-converter-depth-warning": "字词转换器深度超限($1)",
        "node-count-exceeded-category": "页面个节点数超出限制",
-       "parser-unstrip-recursion-limit": "递归超过限制 ($1)",
+       "unstrip-depth-warning": "递归超过限制 ($1)",
        "converter-manual-rule-error": "来手动语言转换规则当中查着错误",
        "undo-success": "箇只编辑可以撤销。请检查下头个比较,确定侬确实想撤销,再保存下底个更改完成撤销编辑。",
        "undo-failure": "由于相互冲突个中途编辑,箇只编辑弗好撤销。",
        "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..82ae04e 100644 (file)
        "expansion-depth-exceeded-category-desc": "გვერდს აქვს გადაჭარბებული გაღების სიღრმის მაქსიმალურად დასაშვები რაოდენობა",
        "expansion-depth-exceeded-warning": "გვერდზე გადამეტებულია ჩადგმების ზღვარი",
        "parser-unstrip-loop-warning": "აღმოჩენილია ციკლური ბმული",
-       "parser-unstrip-recursion-limit": "გადამეტებულია რეკურსიის ზღვარი ($1)",
+       "unstrip-depth-warning": "გადამეტებულია რეკურსიის ზღვარი ($1)",
        "converter-manual-rule-error": "შეცდომა ენის ხელით გარდაქმნის წესში",
        "undo-success": "რედაქტირების გაუქმება შესაძლებელია. გთხოვთ შეამოწმოთ განსხვავება ქვევით, რათა დარწმუნდეთ, რომ ეს ის არის რაც თქვენ გსურთ, შემდეგ კი შეინახეთ ცვლილებები რათა დაასრულოთ რედაქტირების გაუქმება.",
        "undo-failure": "რედაქტირების გაუქმება შეუძლებელია კონფლიქტური შუალედური რედაქტირებების გამო.",
        "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..9fd474f 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": "ע-פאסט:",
        "grant-editpage": "רעדאקטירן עקזיסטירנדע בלעטער",
        "grant-editprotected": "רעדאקטירן געשיצטע בלעטער",
        "grant-highvolume": "א סך באארבעטונגען",
+       "grant-oversight": "באהאלטן באניצער און אונטערדריקן ווערסיעס",
        "grant-patrol": "פאטראלירן ענדערונגען צו בלעטער",
        "grant-privateinfo": "צוטריט צו פריוואטער אינפֿארמאציע",
+       "grant-rollback": "צוריקזעצען ענדערונגען צו בלעטער",
        "grant-sendemail": "שיקן ע-פאסט צו אנדערע באניצער",
        "grant-uploadeditmovefile": "ארויפֿלאדן, טוישן און באוועגן טעקעס",
        "grant-uploadfile": "אַרויפֿלאָדן נייע טעקעס",
        "recentchanges-submit": "ווייזן",
        "rcfilters-legend-heading": "<strong>ליסטע פון ראשי תיבות:</strong>",
        "rcfilters-other-review-tools": "אנדערע רעצענזיע ווערקצייג",
+       "rcfilters-group-results-by-page": "גרופירן רעזולטאטן לויט בלאט",
        "rcfilters-activefilters": "אַקטיווע פילטערס",
        "rcfilters-advancedfilters": "פֿארגעשריטענע פֿילטערס",
        "rcfilters-limit-title": "רעזולטאטן צו ווייזן",
        "rcfilters-filter-editsbyother-description": "אלע ענדערונגען אחוץ אייערע אייגענע.",
        "rcfilters-filter-user-experience-level-registered-label": "אײַנגעשריבן",
        "rcfilters-filter-user-experience-level-unregistered-label": "נישט אײַנגעשריבן",
+       "rcfilters-filter-user-experience-level-newcomer-label": "נייע",
        "rcfilters-filter-user-experience-level-newcomer-description": "איינגעשריבענע רעדאקטירער וואס האבן ווייניגער פון 10 רעדאקטירונגען אדער 4 טעג אקטיוויטעט.",
        "rcfilters-filter-user-experience-level-learner-label": "לערנער",
        "rcfilters-filter-user-experience-level-learner-description": "איינגעשריבענע רעדאקטירער וואס זייער דערפֿארונג איז צווישן ״פנים־חדשות״ און ״אנגעלערנטע״.",
        "lockmanager-fail-closelock": "נישט מעגלעך פארשפארן שלאס טעקע פאר \"$1\".",
        "lockmanager-fail-deletelock": "נישט מעגלעך אויסמעקן שלאס טעקע פאר \"$1\".",
        "lockmanager-fail-acquirelock": "נישט מעגלעך צו באקומען שלאס טעקע פאר \"$1\".",
-       "lockmanager-fail-openlock": "נישט מעגלעך עפֿענען שלאס טעקע פאר \"$1\".",
+       "lockmanager-fail-openlock": "נישט מעגלעך עפֿענען שלאס טעקע פאר \"$1\". פֿארזיכערט אז אייער ארויפלאד רעפאיזטאריום איז קארעקט קאנפֿיגורירט און אייער וועב סארווער האט ערלויבניש  צו שרייבן צו יענעם רעפאזיטאריום. זעט https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:$wgUploadDirectory פֿאר נאך אינפֿארמאציע.",
        "lockmanager-fail-releaselock": "נישט מעגלעך באפֿרייען שלאס טעקע פאר \"$1\".",
        "lockmanager-fail-db-release": "מ'קען נישט באפרייען די שלאסן אויף דאטנבאזע $1.",
        "zip-file-open-error": "געטראפן א גרײַז ביים עפענען די טעקע פאר ZIP־קאנטראלירונג.",
        "pageswithprop-legend": "בלעטער מיט א בלאט אייגנשאפט",
        "pageswithprop-text": "דער בלאט האלט א רשימה פון בלעטער וואס ניצן א געוויסע בלאט אייגנשאפט.",
        "pageswithprop-prop": "אייגנשאפט נאמען:",
+       "pageswithprop-reverse": "סארטירן אין פארקערטן סדר",
        "pageswithprop-submit": "גייט",
        "pageswithprop-prophidden-long": "לאנגער טעקסט אייגנשאפט־ווערט באהאלטן ($1)",
        "pageswithprop-prophidden-binary": "בינארישער אייגנשאפט־ווערט באהאלטן ($1)",
        "doubleredirects": "געטאפלטע ווײַטערפֿירונגען",
        "doubleredirectstext": "דער בלאט רעכנט אויס בלעטער וואס פירן ווייטער צו אנדערע ווייטערפירן בלעטער.\nיעדע שורה אנטהאלט א לינק צום ערשטן און צווייטן ווייטערפירונג, ווי אויך די ציל פון דער צווייטער ווייטערפירונג, וואס רוב מאל געפינט זיך די ריכטיגע ציל וואו די ערשטע ווייטערפירונג זאל ווייזן.\n<del>אויסגעשטראכענע</del> טעמעס זענען שוין געלייזט.",
        "double-redirect-fixed-move": "[[$1]] איז געווארן באוועגט.\nער איז געווארן דערהיינטיקט אויטאמאטיש און איז יעצט א ווייטערפֿירונג צו [[$2]].",
-       "double-redirect-fixed-maintenance": "אויטאמאטיש פֿאַררעכטן געטאפלטע ווײַטערפֿירונג פֿון [[$1]] צו [[$2]] אין אן אויפהאלטונג אויפגאבע.",
+       "double-redirect-fixed-maintenance": "אויטאמאטיש פֿאַררעכטן געטאפלטע ווײַטערפֿירונג פֿון [[$1]] צו [[$2]] אין אן אויפהאלטונג אויפגאבע",
        "double-redirect-fixer": "מתקן ווײַטערפֿירונגען",
        "brokenredirects": "צעבראָכענע ווײַטערפֿירונגען",
        "brokenredirectstext": "די פֿאלגנדע ווײַטערפֿירונגען פֿאַרבינדן צו בלעטער וואס עקזיסטירן נאך נישט:",
        "revertpage-nouser": "צוריקגעשטעלט רעדאַקטירונגען פֿון א באהאלטענעם באַניצער צו לעצטער רעוויזיע פֿון {{GENDER:$1|[[User:$1|$1]]}}",
        "rollback-success": "צוריקגעדרייט רעדאַקטירונגען פֿון {{GENDER:$3|$1}};\nגעענדערט צו דער לעצטע ווערסיע פֿון {{GENDER:$4|$2}}.",
        "sessionfailure-title": "זיצונג דורכפֿאַל",
-       "sessionfailure": "ווײַזט אויס אז ס'איז דא א פראבלעם מיט אייער ארײַנלאגירן; די פעולה איז געווארן אנולירט צו פֿאַרהיטן קעגן פֿאַרשטעלן אייער סעסיע. זייט אזוי גוט און גייט צוריק צום פֿריערדיקן בלאט, און פרובירט נאכאַמאָל.",
+       "sessionfailure": "ווײַזט אויס אז ס'איז דא א פראבלעם מיט אייער ארײַנלאגירן; \nדי פעולה איז געווארן אנולירט צו פֿאַרהיטן קעגן פֿאַרשטעלן אייער סעסיע. \nזייט אזוי גוט און שיקט דעם פֿארעם נאכאַמאָל.",
        "changecontentmodel": "ענדערן אינהאלט־מאדעל פון א בלאט",
        "changecontentmodel-legend": "ענדערן אינהאלט מאדעל",
        "changecontentmodel-title-label": "בלאט־טיטל",
        "patrol-log-header": "דאס איז א לאג-בוך פון פאַטראליטע רעוויזיעס.",
        "log-show-hide-patrol": "$1 פאַטראלירן לאג-בוך",
        "log-show-hide-tag": "$1 טאג־לאגבוך",
+       "confirm-markpatrolled-top": "מארקירן $3 פון $2 ווי קאנטראלירט?",
        "deletedrevision": "אויסגעמעקט אלטע ווערסיע $1.",
        "filedeleteerror-short": "גרייז ביים אויסמעקן טעקע: $1",
        "filedeleteerror-long": "גרײַזן געטראפֿן בײַם אויסמעקן די טעקע:\n\n$1",
        "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|טאַג|טאַגן}}]]: $2)",
        "tag-mw-new-redirect": "נייע ווייטערפֿירונג",
        "tag-mw-changed-redirect-target": "ווייטערפֿירונג־ציל געענדערט",
+       "tag-mw-replace": "געטוישט",
        "tag-mw-undo": "אַנולירן",
        "tags-title": "טאַגן",
        "tags-intro": "דער בלאַט ווײַזט די טאַגן מיט וואס דאס ווייכווארג קען צייכענען אַ רעדאַגירונג, און זייער באַטייַט.",
        "tags-activate": "אקטיוויזירן",
        "tags-deactivate": "אומאקטיוויזירן",
        "tags-hitcount": " {{PLURAL:$1|ענדערונג|$1 ענדערונגען}}",
+       "tags-create-reason": "אורזאַך:",
        "tags-create-submit": "שאַפֿן",
+       "tags-delete-reason": "אורזאַך:",
        "tags-activate-reason": "גרונד:",
+       "tags-deactivate-reason": "אורזאַך:",
        "comparepages": "פאַרגלייַכן בלעטער",
        "compare-page1": "עמוד 1",
        "compare-page2": "עמוד 2",
        "log-action-filter-import-interwiki": "אריבערוויקי אימפארט",
        "log-action-filter-newusers-create2": "שאפֿונגען פון איינגעשריבענע באניצער",
        "log-action-filter-protect-unprotect": "אראפנעמען שיץ",
-       "authmanager-userdoesnotexist": "באניצער קאנטע \"$1\" איז נישט איינגעשריבן."
+       "authmanager-userdoesnotexist": "באניצער קאנטע \"$1\" איז נישט איינגעשריבן.",
+       "authmanager-realname-label": "עכטער נאָמען",
+       "revid": "רעוויזיע $1"
 }
index 5183213..8ab39e7 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ọ́!",
        "expansion-depth-exceeded-category": "Àwọn ojúewé tí ìjìn ìfẹ̀lọ wọn ju bóṣeyẹ lọ",
        "expansion-depth-exceeded-warning": "Ojúewé ní ìjìn ìfẹ̀lọ tó ju bóṣeyẹ lọ",
        "parser-unstrip-loop-warning": "Ìyípo unstrip ti jẹ́ fínfín",
-       "parser-unstrip-recursion-limit": "Ó ti kọjá àlà ìlọ́po unstrip ($1)",
+       "unstrip-depth-warning": "Ó ti kọjá àlà ìlọ́po unstrip ($1)",
        "converter-manual-rule-error": "Àṣìṣe ti jẹ́ fínfín nínú ìlànà ìyípadà èdè àfọwọ́dá",
        "undo-success": "Àtúnṣe náà ṣe é múkúrò.\nẸ jọ̀wọ́ ẹ wo ìfiwéra ìsàlẹ̀ láti rídájú pé ohun tí ẹ fẹ́ nì yẹn, nígbà náà ẹ mú àwọn àtúnṣe náà pamọ́ láti parí ìmúkúrò àtúnṣe.",
        "undo-failure": "Àtúnṣe náà kò ṣe é múkúrò nítorí títakora àwọn àtúnṣe inú àrin.",
        "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 4b22b1b..77becb9 100644 (file)
        "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更改嘅内容重未儲存!",
        "expansion-depth-exceeded-category-desc": "版面超出咗量大展開深度。",
        "expansion-depth-exceeded-warning": "版面超出咗展開深度",
        "parser-unstrip-loop-warning": "偵測到 Unstrip 迴圈",
-       "parser-unstrip-recursion-limit": "Unstrip 迴圈超出咗限制 ($1)",
+       "unstrip-depth-warning": "Unstrip 迴圈超出咗限制 ($1)",
        "converter-manual-rule-error": "手動語言轉換規則入面偵測到出錯",
        "undo-success": "呢個編輯可以取消。請檢查一下個差異去確認呢個係你要去做嘅,跟住儲存下面嘅更改去完成編輯。",
        "undo-failure": "呢個編輯唔能夠取消,由於同途中嘅編輯有衝突。",
        "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|日}}",
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 da7d0d2..4777ebe 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>您的更改尚未保存!",
        "expansion-depth-exceeded-category-desc": "页面超出最大展开深度限制。",
        "expansion-depth-exceeded-warning": "页面超出展开深度限制",
        "parser-unstrip-loop-warning": "检测到Unstrip循环",
-       "parser-unstrip-recursion-limit": "已超出Unstrip递归限制($1)",
+       "unstrip-depth-warning": "已超出Unstrip递归限制($1)",
        "converter-manual-rule-error": "在手动语言转换规则中检测到错误",
        "undo-success": "该编辑可以被撤销。请检查下面的对比以核实您想要撤销的内容,然后保存下面的更改以完成撤销。",
        "undo-failure": "因存在冲突的中间编辑,本编辑不能撤销。",
        "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": "电子邮件:",
        "sunday-at": "周日$1",
        "yesterday-at": "昨天$1",
        "bad_image_list": "格式如下:\n\n仅列表项目(以*开头的行)有效。每行的第一个链接必须是至错误文件的链接。同一行任何后续链接均被视为例外,即嵌入该文件的页面。",
-       "variantname-zh-hans": "简体",
-       "variantname-zh-hant": "繁体",
+       "variantname-zh-hans": "简体中文",
+       "variantname-zh-hant": "繁体中文",
        "variantname-zh-cn": "大陆简体",
        "variantname-zh-tw": "台湾正体",
        "variantname-zh-hk": "香港繁体",
index 212487e..2680fd4 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日",
        "categorypage": "檢視分類頁面",
        "viewtalkpage": "檢視討論頁面",
        "otherlanguages": "其他語言",
-       "redirectedfrom": "(已重新導向自 $1)",
+       "redirectedfrom": "(重新導向自 $1)",
        "redirectpagesub": "重新導向頁面",
        "redirectto": "重新導向至:",
        "lastmodifiedat": "此頁面最後編輯於 $1 $2。",
        "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>",
        "expansion-depth-exceeded-category-desc": "超出展開深度限制的頁面。",
        "expansion-depth-exceeded-warning": "頁面超出展開深度限制",
        "parser-unstrip-loop-warning": "偵測到 Unstrip 迴圈",
-       "parser-unstrip-recursion-limit": "Unstrip 遞迴超出限制 ($1)",
+       "unstrip-depth-warning": "Unstrip 遞迴超出限制 ($1)",
        "converter-manual-rule-error": "手動語言轉換規則時偵測到錯誤",
        "undo-success": "此編輯可以被還原。\n請檢查以下比較表,確認您是否要還原,然後儲存以下變更以完成編輯還原。",
        "undo-failure": "由於編輯的修訂間有衝突,此編輯不能還原。",
        "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": "匯入頁面",
        "sunday-at": "週日在 $1",
        "yesterday-at": "昨天於 $1",
        "bad_image_list": "請依照下列格式:\n\n僅清單項目有效 (以 * 開頭)。每一行的第一個連結必須是不良檔案的連結。同一行除第一個以外的連結會被視作例外清單,即檔案所在的頁面。",
-       "variantname-zh-hans": "‪简体中文",
+       "variantname-zh-hans": "簡體中文",
        "variantname-zh-hant": "‪繁體中文",
        "variantname-zh-cn": "大陸簡體",
-       "variantname-zh-tw": "灣正體",
+       "variantname-zh-tw": "灣正體",
        "variantname-zh-hk": "香港繁體",
        "variantname-zh-mo": "澳門繁體",
        "variantname-zh-sg": "新加坡簡體",
-       "variantname-zh-my": "马æ\9d¥è¥¿äº\9aç®\80ä½\93",
+       "variantname-zh-my": "馬ä¾\86西äº\9eç°¡é«\94",
        "variantname-zh": "不轉換",
        "variantname-gan-hans": "贛語 (簡體)",
        "variantname-gan-hant": "贛語 (繁體)",
        "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' ],
 ];
 
index 4d4a48e..6275f7d 100644 (file)
@@ -16,21 +16,30 @@ $namespaceNames = [
        NS_MEDIA            => 'ذريعات',
        NS_SPECIAL          => 'خاص',
        NS_TALK             => 'بحث',
-       NS_USER             => 'Ù\8aÙ\88زر',
-       NS_USER_TALK        => 'Ù\8aÙ\88زر_بحث',
+       NS_USER             => 'Ù\88اپرائÙ\8aÙ\86دÚ\99',
+       NS_USER_TALK        => 'Ù\88اپرائÙ\8aÙ\86دÚ\99_بحث',
        NS_PROJECT_TALK     => '$1_بحث',
-       NS_FILE             => 'عڪس',
-       NS_FILE_TALK        => 'عڪس_بحث',
+       NS_FILE             => 'فائل',
+       NS_FILE_TALK        => 'فائل_بحث',
        NS_MEDIAWIKI        => 'ذريعات_وڪي',
        NS_MEDIAWIKI_TALK   => 'ذريعات_وڪي_بحث',
        NS_TEMPLATE         => 'سانچو',
-       NS_TEMPLATE_TALK    => 'سنچو_بحث',
+       NS_TEMPLATE_TALK    => 'سانچو_بحث',
        NS_HELP             => 'مدد',
        NS_HELP_TALK        => 'مدد_بحث',
        NS_CATEGORY         => 'زمرو',
        NS_CATEGORY_TALK    => 'زمرو_بحث',
 ];
 
+$namespaceAliases = [
+       'يوزر' => NS_USER,
+       'يوزر_بحث' => NS_USER_TALK,
+       'عڪس' => NS_FILE,
+       'عڪس_بحث' => NS_FILE_TALK,
+       'سنچو' => NS_TEMPLATE,
+       'سنچو_بحث' => NS_TEMPLATE_TALK,
+];
+
 $specialPageAliases = [
        'Allmessages'               => [ 'سڀ نياپا' ],
        'Allpages'                  => [ 'سڀ صفحا' ],
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 13362e0..bcf7023 100644 (file)
@@ -132,13 +132,13 @@ EOT;
         */
        function sync( $srcTable, $dstTable ) {
                $batchSize = 1000;
-               $minTs = $this->dbw->selectField( $srcTable, 'MIN(log_timestamp)', false, __METHOD__ );
+               $minTs = $this->dbw->selectField( $srcTable, 'MIN(log_timestamp)', '', __METHOD__ );
                $minTsUnix = wfTimestamp( TS_UNIX, $minTs );
                $numRowsCopied = 0;
 
                while ( true ) {
-                       $maxTs = $this->dbw->selectField( $srcTable, 'MAX(log_timestamp)', false, __METHOD__ );
-                       $copyPos = $this->dbw->selectField( $dstTable, 'MAX(log_timestamp)', false, __METHOD__ );
+                       $maxTs = $this->dbw->selectField( $srcTable, 'MAX(log_timestamp)', '', __METHOD__ );
+                       $copyPos = $this->dbw->selectField( $dstTable, 'MAX(log_timestamp)', '', __METHOD__ );
                        $maxTsUnix = wfTimestamp( TS_UNIX, $maxTs );
                        $copyPosUnix = wfTimestamp( TS_UNIX, $copyPos );
 
index 2e1f7c9..8579f0f 100644 (file)
@@ -38,7 +38,7 @@ class ClearInterwikiCache extends Maintenance {
        public function execute() {
                global $wgLocalDatabases, $wgMemc;
                $dbr = $this->getDB( DB_REPLICA );
-               $res = $dbr->select( 'interwiki', [ 'iw_prefix' ], false );
+               $res = $dbr->select( 'interwiki', [ 'iw_prefix' ], '', __METHOD__ );
                $prefixes = [];
                foreach ( $res as $row ) {
                        $prefixes[] = $row->iw_prefix;
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..edd5dda
--- /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)', '', __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 cb72c1e..cdecab0 100644 (file)
@@ -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 23144e9..e2fd8b5 100644 (file)
@@ -52,13 +52,13 @@ class PopulateBacklinkNamespace extends LoggedUpdateMaintenance {
 
                $start = $this->getOption( 'lastUpdatedId' );
                if ( !$start ) {
-                       $start = $db->selectField( 'page', 'MIN(page_id)', false, __METHOD__ );
+                       $start = $db->selectField( 'page', 'MIN(page_id)', '', __METHOD__ );
                }
                if ( !$start ) {
                        $this->output( "Nothing to do." );
                        return false;
                }
-               $end = $db->selectField( 'page', 'MAX(page_id)', false, __METHOD__ );
+               $end = $db->selectField( 'page', 'MAX(page_id)', '', __METHOD__ );
                $batchSize = $this->getBatchSize();
 
                # Do remaining chunk
index 7c094be..ef57640 100644 (file)
@@ -56,7 +56,7 @@ class PopulateFilearchiveSha1 extends LoggedUpdateMaintenance {
                }
 
                $this->output( "Populating fa_sha1 field from fa_storage_key\n" );
-               $endId = $dbw->selectField( $table, 'MAX(fa_id)', false, __METHOD__ );
+               $endId = $dbw->selectField( $table, 'MAX(fa_id)', '', __METHOD__ );
 
                $batchSize = $this->getBatchSize();
                $done = 0;
index fbe16de..6e88dfa 100644 (file)
@@ -75,20 +75,32 @@ TEXT
                $start = $this->getOption( 'rev-id', 0 );
                $end = $maxRevId > 0
                        ? $maxRevId
-                       : $dbw->selectField( 'revision', 'MAX(rev_id)', false, __METHOD__ );
+                       : $dbw->selectField( 'revision', 'MAX(rev_id)', '', __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..589be48 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' ) ) {
@@ -60,13 +62,13 @@ class PopulateLogSearch extends LoggedUpdateMaintenance {
 
                        return false;
                }
-               $start = $db->selectField( 'logging', 'MIN(log_id)', false, __FUNCTION__ );
+               $start = $db->selectField( 'logging', 'MIN(log_id)', '', __FUNCTION__ );
                if ( !$start ) {
                        $this->output( "Nothing to do.\n" );
 
                        return true;
                }
-               $end = $db->selectField( 'logging', 'MAX(log_id)', false, __FUNCTION__ );
+               $end = $db->selectField( 'logging', 'MAX(log_id)', '', __FUNCTION__ );
 
                # Do remaining chunk
                $end += $batchSize - 1;
@@ -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..3c0bba9 100644 (file)
@@ -50,13 +50,21 @@ class PopulateLogUsertext extends LoggedUpdateMaintenance {
        protected function doDBUpdates() {
                $batchSize = $this->getBatchSize();
                $db = $this->getDB( DB_MASTER );
-               $start = $db->selectField( 'logging', 'MIN(log_id)', false, __METHOD__ );
+               $start = $db->selectField( 'logging', 'MIN(log_id)', '', __METHOD__ );
                if ( !$start ) {
                        $this->output( "Nothing to do.\n" );
 
                        return true;
                }
-               $end = $db->selectField( 'logging', 'MAX(log_id)', false, __METHOD__ );
+               $end = $db->selectField( 'logging', 'MAX(log_id)', '', __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;
@@ -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 39bc733..2ef58b7 100644 (file)
@@ -54,8 +54,8 @@ class PopulateParentId extends LoggedUpdateMaintenance {
                        return false;
                }
                $this->output( "Populating rev_parent_id column\n" );
-               $start = $db->selectField( 'revision', 'MIN(rev_id)', false, __FUNCTION__ );
-               $end = $db->selectField( 'revision', 'MAX(rev_id)', false, __FUNCTION__ );
+               $start = $db->selectField( 'revision', 'MIN(rev_id)', '', __FUNCTION__ );
+               $end = $db->selectField( 'revision', 'MAX(rev_id)', '', __FUNCTION__ );
                if ( is_null( $start ) || is_null( $end ) ) {
                        $this->output( "...revision table seems to be empty, nothing to do.\n" );
 
index 4ac3486..8a56d7d 100644 (file)
@@ -46,13 +46,13 @@ class PopulateRecentChangesSource extends LoggedUpdateMaintenance {
                        $this->error( 'rc_source field in recentchanges table does not exist.' );
                }
 
-               $start = $dbw->selectField( 'recentchanges', 'MIN(rc_id)', false, __METHOD__ );
+               $start = $dbw->selectField( 'recentchanges', 'MIN(rc_id)', '', __METHOD__ );
                if ( !$start ) {
                        $this->output( "Nothing to do.\n" );
 
                        return true;
                }
-               $end = $dbw->selectField( 'recentchanges', 'MAX(rc_id)', false, __METHOD__ );
+               $end = $dbw->selectField( 'recentchanges', 'MAX(rc_id)', '', __METHOD__ );
                $end += $batchSize - 1;
                $blockStart = $start;
                $blockEnd = $start + $batchSize - 1;
index f83be9c..8895c9f 100644 (file)
@@ -76,8 +76,8 @@ class PopulateRevisionLength extends LoggedUpdateMaintenance {
                $dbr = $this->getDB( DB_REPLICA );
                $dbw = $this->getDB( DB_MASTER );
                $batchSize = $this->getBatchSize();
-               $start = $dbw->selectField( $table, "MIN($idCol)", false, __METHOD__ );
-               $end = $dbw->selectField( $table, "MAX($idCol)", false, __METHOD__ );
+               $start = $dbw->selectField( $table, "MIN($idCol)", '', __METHOD__ );
+               $end = $dbw->selectField( $table, "MAX($idCol)", '', __METHOD__ );
                if ( !$start || !$end ) {
                        $this->output( "...$table table seems to be empty.\n" );
 
@@ -117,7 +117,6 @@ class PopulateRevisionLength extends LoggedUpdateMaintenance {
 
                        $blockStart += $batchSize;
                        $blockEnd += $batchSize;
-                       wfWaitForSlaves();
                }
 
                return $count;
index 54937ab..9662044 100644 (file)
@@ -78,8 +78,8 @@ class PopulateRevisionSha1 extends LoggedUpdateMaintenance {
        protected function doSha1Updates( $table, $idCol, $queryInfo, $prefix ) {
                $db = $this->getDB( DB_MASTER );
                $batchSize = $this->getBatchSize();
-               $start = $db->selectField( $table, "MIN($idCol)", false, __METHOD__ );
-               $end = $db->selectField( $table, "MAX($idCol)", false, __METHOD__ );
+               $start = $db->selectField( $table, "MIN($idCol)", '', __METHOD__ );
+               $end = $db->selectField( $table, "MAX($idCol)", '', __METHOD__ );
                if ( !$start || !$end ) {
                        $this->output( "...$table table seems to be empty.\n" );
 
@@ -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 ae6a75e..ecdec29 100644 (file)
@@ -82,10 +82,10 @@ class RebuildFileCache extends Maintenance {
                $overwrite = $this->hasOption( 'overwrite' );
                $start = ( $start > 0 )
                        ? $start
-                       : $dbr->selectField( 'page', 'MIN(page_id)', false, __METHOD__ );
+                       : $dbr->selectField( 'page', 'MIN(page_id)', '', __METHOD__ );
                $end = ( $end > 0 )
                        ? $end
-                       : $dbr->selectField( 'page', 'MAX(page_id)', false, __METHOD__ );
+                       : $dbr->selectField( 'page', 'MAX(page_id)', '', __METHOD__ );
                if ( !$start ) {
                        $this->fatalError( "Nothing to do." );
                }
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 9d5d39f..49f1cd1 100644 (file)
@@ -170,8 +170,8 @@ class RefreshLinks extends Maintenance {
                        }
                } else {
                        if ( !$end ) {
-                               $maxPage = $dbr->selectField( 'page', 'max(page_id)', false );
-                               $maxRD = $dbr->selectField( 'redirect', 'max(rd_from)', false );
+                               $maxPage = $dbr->selectField( 'page', 'max(page_id)', '', __METHOD__ );
+                               $maxRD = $dbr->selectField( 'redirect', 'max(rd_from)', '', __METHOD__ );
                                $end = max( $maxPage, $maxRD );
                        }
                        $this->output( "Refreshing redirects table.\n" );
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 8f55b88..bd0556a 100644 (file)
@@ -65,7 +65,7 @@ class CheckStorage {
                } else {
                        print "Checking...\n";
                }
-               $maxRevId = $dbr->selectField( 'revision', 'MAX(rev_id)', false, __METHOD__ );
+               $maxRevId = $dbr->selectField( 'revision', 'MAX(rev_id)', '', __METHOD__ );
                $chunkSize = 1000;
                $flagStats = [];
                $objectStats = [];
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..6bc2f98 100644 (file)
@@ -55,7 +55,7 @@ class FixT22757 extends Maintenance {
                $numFixed = 0;
                $numBad = 0;
 
-               $totalRevs = $dbr->selectField( 'text', 'MAX(old_id)', false, __METHOD__ );
+               $totalRevs = $dbr->selectField( 'text', 'MAX(old_id)', '', __METHOD__ );
 
                // In MySQL 4.1+, the binary field old_text has a non-working LOWER() function
                $lowerLeft = 'LOWER(CONVERT(LEFT(old_text,22) USING latin1))';
@@ -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 e117992..9bb554c 100644 (file)
@@ -41,7 +41,7 @@ if ( !defined( 'MEDIAWIKI' ) ) {
        if ( isset( $options['e'] ) ) {
                $maxID = $options['e'];
        } else {
-               $maxID = $dbw->selectField( 'text', 'MAX(old_id)', false, $fname );
+               $maxID = $dbw->selectField( 'text', 'MAX(old_id)', '', $fname );
        }
        $minID = isset( $options['s'] ) ? $options['s'] : 1;
 
index 4feb95e..9c1b538 100644 (file)
@@ -47,7 +47,7 @@ class OrphanStats extends Maintenance {
                if ( !$dbr->tableExists( 'blob_orphans' ) ) {
                        $this->fatalError( "blob_orphans doesn't seem to exist, need to run trackBlobs.php first" );
                }
-               $res = $dbr->select( 'blob_orphans', '*', false, __METHOD__ );
+               $res = $dbr->select( 'blob_orphans', '*', '', __METHOD__ );
 
                $num = 0;
                $totalSize = 0;
index 8ca8bb2..f9ec398 100644 (file)
@@ -38,7 +38,7 @@ function resolveStubs() {
        $fname = 'resolveStubs';
 
        $dbr = wfGetDB( DB_REPLICA );
-       $maxID = $dbr->selectField( 'text', 'MAX(old_id)', false, $fname );
+       $maxID = $dbr->selectField( 'text', 'MAX(old_id)', '', $fname );
        $blockSize = 10000;
        $numBlocks = intval( $maxID / $blockSize ) + 1;
 
index 6dee1a5..9ba3d1b 100644 (file)
@@ -25,7 +25,7 @@ class StorageTypeStats extends Maintenance {
        function execute() {
                $dbr = $this->getDB( DB_REPLICA );
 
-               $endId = $dbr->selectField( 'text', 'MAX(old_id)', false, __METHOD__ );
+               $endId = $dbr->selectField( 'text', 'MAX(old_id)', '', __METHOD__ );
                if ( !$endId ) {
                        echo "No text rows!\n";
                        exit( 1 );
index b4514ec..ae6d2ff 100644 (file)
@@ -153,7 +153,7 @@ class TrackBlobs {
 
                $textClause = $this->getTextClause();
                $startId = 0;
-               $endId = $dbr->selectField( 'revision', 'MAX(rev_id)', false, __METHOD__ );
+               $endId = $dbr->selectField( 'revision', 'MAX(rev_id)', '', __METHOD__ );
                $batchesDone = 0;
                $rowsInserted = 0;
 
@@ -229,7 +229,7 @@ class TrackBlobs {
 
                $textClause = $this->getTextClause( $this->clusters );
                $startId = 0;
-               $endId = $dbr->selectField( 'text', 'MAX(old_id)', false, __METHOD__ );
+               $endId = $dbr->selectField( 'text', 'MAX(old_id)', '', __METHOD__ );
                $rowsInserted = 0;
                $batchesDone = 0;
 
@@ -339,7 +339,7 @@ class TrackBlobs {
                        $startId = 0;
                        $batchesDone = 0;
                        $actualBlobs = gmp_init( 0 );
-                       $endId = $extDB->selectField( $table, 'MAX(blob_id)', false, __METHOD__ );
+                       $endId = $extDB->selectField( $table, 'MAX(blob_id)', '', __METHOD__ );
 
                        // Build a bitmap of actual blob rows
                        while ( true ) {
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 cb40af3..668ba79 100644 (file)
@@ -46,11 +46,11 @@ class UpdateRestrictions extends Maintenance {
                        $this->fatalError( "page_restrictions table does not exist" );
                }
 
-               $start = $db->selectField( 'page', 'MIN(page_id)', false, __METHOD__ );
+               $start = $db->selectField( 'page', 'MIN(page_id)', '', __METHOD__ );
                if ( !$start ) {
                        $this->fatalError( "Nothing to do." );
                }
-               $end = $db->selectField( 'page', 'MAX(page_id)', false, __METHOD__ );
+               $end = $db->selectField( 'page', 'MAX(page_id)', '', __METHOD__ );
 
                # Do remaining chunk
                $end += $batchSize - 1;
index 4d89f87..488f715 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,8 +1212,7 @@ return [
                        'resources/src/mediawiki/mediawiki.Title.phpCharToUpper.js',
                ],
                'dependencies' => [
-                       'jquery.byteLength',
-                       'jquery.byteLimit',
+                       'mediawiki.String',
                        'mediawiki.util',
                ],
                'targets' => [ 'desktop', 'mobile' ],
@@ -1411,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',
@@ -1422,7 +1453,7 @@ return [
                        'mediawiki.editfont.styles',
                        'jquery.textSelection',
                        'oojs-ui-core',
-                       'mediawiki.widgets.visibleByteLimit',
+                       'mediawiki.widgets.visibleLengthLimit',
                        'mediawiki.api',
                ],
        ],
@@ -1762,7 +1793,7 @@ return [
                        'resources/src/mediawiki.rcfilters/mw.rcfilters.UriProcessor.js',
                ],
                'dependencies' => [
-                       'jquery.byteLength',
+                       'mediawiki.String',
                        'oojs',
                        'mediawiki.api',
                        'mediawiki.api.options',
@@ -1924,6 +1955,7 @@ return [
                        'recentchanges-timeout',
                        'recentchanges-network',
                        'recentchanges-notargetpage',
+                       'allpagesbadtitle',
                        'quotation-marks',
                ],
                'dependencies' => [
@@ -2080,6 +2112,7 @@ return [
                'scripts' => 'resources/src/mediawiki.special/mediawiki.special.edittags.js',
                'dependencies' => [
                        'jquery.chosen',
+                       'jquery.lengthLimit',
                ],
                'messages' => [
                        'tags-edit-chosen-placeholder',
@@ -2095,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',
                ],
        ],
@@ -2140,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',
@@ -2174,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',
@@ -2261,6 +2309,7 @@ return [
                'scripts' => 'resources/src/mediawiki.special/mediawiki.special.userrights.js',
                'dependencies' => [
                        'mediawiki.notification.convertmessagebox',
+                       'jquery.lengthLimit',
                ],
        ],
        'mediawiki.special.watchlist' => [
@@ -2309,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!
@@ -2421,7 +2470,7 @@ return [
                        // TitleInputWidget
                        'mediawiki.Title',
                        'mediawiki.api',
-                       'jquery.byteLimit',
+                       'mediawiki.String',
                ],
                'messages' => [
                        // NamespaceInputWidget
@@ -2475,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' ]
        ],
@@ -2578,6 +2633,23 @@ return [
                'styles' => 'resources/src/mediawiki.widgets/mw.widgets.SelectWithInputWidget.base.css',
                'targets' => [ 'desktop', 'mobile' ],
        ],
+       'mediawiki.widgets.SizeFilterWidget' => [
+               'scripts' => 'resources/src/mediawiki.widgets/mw.widgets.SizeFilterWidget.js',
+               'dependencies' => [
+                       'mediawiki.widgets.SizeFilterWidget.styles',
+                       'oojs-ui-widgets',
+               ],
+               'messages' => [
+                       'minimum-size',
+                       'maximum-size',
+                       'pagesize',
+               ],
+               'targets' => [ 'desktop', 'mobile' ],
+       ],
+       'mediawiki.widgets.SizeFilterWidget.styles' => [
+               'styles' => 'resources/src/mediawiki.widgets/mw.widgets.SizeFilterWidget.base.css',
+               'targets' => [ 'desktop', 'mobile' ],
+       ],
        'mediawiki.widgets.MediaSearch' => [
                'scripts' => [
                        'resources/src/mediawiki.widgets/MediaSearch/mw.widgets.APIResultsProvider.js',
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 3ce6e7f..0000000
+++ /dev/null
@@ -1,260 +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( ' ' );
-
-       // 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 );
-               }
-       }
-
-       /**
-        * 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, 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 ( $.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 ) {
-                       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.
-               if ( fn ) {
-                       // stop, when there is nothing to slice - T43450
-                       while ( $.byteLength( fn( inpParts.join( '' ) ) ) > byteLimit && 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 );
-                       }
-               } else {
-                       while ( $.byteLength( inpParts.join( '' ) ) > byteLimit ) {
-                               // 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 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 3202e59..a751cf0 100644 (file)
@@ -69,4 +69,9 @@
                display: inline-block;
                vertical-align: middle;
        }
+
+       /* T188737 */
+       .templatesUsed .mw-redirect {
+               font-style: italic;
+       }
 }
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 eda8c0d..a62acc5 100644 (file)
                // Verify that single_option group has at least one item selected
                if (
                        this.getType() === 'single_option' &&
-                       this.getSelectedItems().length === 0
+                       this.findSelectedItems().length === 0
                ) {
                        defaultParam = groupDefault !== undefined ?
                                groupDefault : this.getItems()[ 0 ].getParamName();
                if ( this.getType() === 'single_option' ) {
                        // This group must have one item selected always
                        // and must never have more than one item selected at a time
-                       if ( this.getSelectedItems().length === 0 ) {
+                       if ( this.findSelectedItems().length === 0 ) {
                                // Nothing is selected anymore
                                // Select the default or the first item
                                this.currSelected = this.getItemByParamName( this.defaultParams[ this.getName() ] ) ||
                                        this.getItems()[ 0 ];
                                this.currSelected.toggleSelected( true );
                                changed = true;
-                       } else if ( this.getSelectedItems().length > 1 ) {
+                       } else if ( this.findSelectedItems().length > 1 ) {
                                // There is more than one item selected
                                // This should only happen if the item given
                                // is the one that is selected, so unselect
                                // all items that is not it
-                               this.getSelectedItems().forEach( function ( itemModel ) {
+                               this.findSelectedItems().forEach( function ( itemModel ) {
                                        // Note that in case the given item is actually
                                        // not selected, this loop will end up unselecting
                                        // all items, which would trigger the case above
         * @param {mw.rcfilters.dm.FilterItem} [excludeItem] Item to exclude from the list
         * @return {mw.rcfilters.dm.FilterItem[]} Selected items
         */
-       mw.rcfilters.dm.FilterGroup.prototype.getSelectedItems = function ( excludeItem ) {
+       mw.rcfilters.dm.FilterGroup.prototype.findSelectedItems = function ( excludeItem ) {
                var excludeName = ( excludeItem && excludeItem.getName() ) || '';
 
                return this.getItems().filter( function ( item ) {
         * @return {boolean} All selected items are in conflict with this item
         */
        mw.rcfilters.dm.FilterGroup.prototype.areAllSelectedInConflictWith = function ( filterItem ) {
-               var selectedItems = this.getSelectedItems( filterItem );
+               var selectedItems = this.findSelectedItems( filterItem );
 
                return selectedItems.length > 0 &&
                        (
         * @return {boolean} Any of the selected items are in conflict with this item
         */
        mw.rcfilters.dm.FilterGroup.prototype.areAnySelectedInConflictWith = function ( filterItem ) {
-               var selectedItems = this.getSelectedItems( filterItem );
+               var selectedItems = this.findSelectedItems( filterItem );
 
                return selectedItems.length > 0 && (
                        // The group as a whole is in conflict with this item
index 682a937..d7f7b02 100644 (file)
                                // if the item is also not highlighted. See T161273
                                superset = this.getSuperset();
                                // For this message we need to collect the affecting superset
-                               affectingItems = this.getGroupModel().getSelectedItems( this )
+                               affectingItems = this.getGroupModel().findSelectedItems( this )
                                        .filter( function ( item ) {
                                                return superset.indexOf( item.getName() ) !== -1;
                                        } )
 
                                messageKey = 'rcfilters-state-message-subset';
                        } else if ( this.isFullyCovered() && !this.isHighlighted() ) {
-                               affectingItems = this.getGroupModel().getSelectedItems( this )
+                               affectingItems = this.getGroupModel().findSelectedItems( this )
                                        .map( function ( item ) {
                                                return mw.msg( 'quotation-marks', item.getLabel() );
                                        } );
index 5564d1e..cdf1f63 100644 (file)
                                                groupModel.areAllSelectedInConflictWith( filterItem ) &&
                                                // Every selected member of the item's own group is also
                                                // in conflict with the other group
-                                               filterItemGroup.getSelectedItems().every( function ( otherGroupItem ) {
+                                               filterItemGroup.findSelectedItems().every( function ( otherGroupItem ) {
                                                        return groupModel.areAllSelectedInConflictWith( otherGroupItem );
                                                } )
                                        );
         */
        mw.rcfilters.dm.FiltersViewModel.prototype.areNamespacesEffectivelyInverted = function () {
                return this.getInvertModel().isSelected() &&
-                       this.getSelectedItems().some( function ( itemModel ) {
+                       this.findSelectedItems().some( function ( itemModel ) {
                                return itemModel.getGroupModel().getName() === 'namespace';
                        } );
        };
         *
         * @return {mw.rcfilters.dm.FilterItem[]} Selected items
         */
-       mw.rcfilters.dm.FiltersViewModel.prototype.getSelectedItems = function () {
+       mw.rcfilters.dm.FiltersViewModel.prototype.findSelectedItems = function () {
                var allSelected = [];
 
                $.each( this.getFilterGroups(), function ( groupName, groupModel ) {
-                       allSelected = allSelected.concat( groupModel.getSelectedItems() );
+                       allSelected = allSelected.concat( groupModel.findSelectedItems() );
                } );
 
                return allSelected;
index 4b78175..cd3f684 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;
                }
        mw.rcfilters.Controller.prototype.updateStickyPreferences = function () {
                // Update default sticky values with selected, whether they came from
                // the initial defaults or from the URL value that is being normalized
-               this.updateDaysDefault( this.filtersModel.getGroup( 'days' ).getSelectedItems()[ 0 ].getParamName() );
-               this.updateLimitDefault( this.filtersModel.getGroup( 'limit' ).getSelectedItems()[ 0 ].getParamName() );
+               this.updateDaysDefault( this.filtersModel.getGroup( 'days' ).findSelectedItems()[ 0 ].getParamName() );
+               this.updateLimitDefault( this.filtersModel.getGroup( 'limit' ).findSelectedItems()[ 0 ].getParamName() );
 
                // TODO: Make these automatic by having the model go over sticky
                // items and update their default values automatically
                        rightNow = new Date().getTime(),
                        randomIdentifier = String( mw.user.sessionId() ) + String( rightNow ) + String( Math.random() ),
                        // Get all current filters
-                       filters = this.filtersModel.getSelectedItems().map( function ( item ) {
+                       filters = this.filtersModel.findSelectedItems().map( function ( item ) {
                                return item.getName();
                        } );
 
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 c10011c..a0eab14 100644 (file)
@@ -68,7 +68,7 @@
                                }
                        );
 
-                       selectedItem = this.limitGroupModel.getSelectedItems()[ 0 ];
+                       selectedItem = this.limitGroupModel.findSelectedItems()[ 0 ];
                        currentValue = ( selectedItem && selectedItem.getLabel() ) ||
                                mw.language.convertNumber( this.limitGroupModel.getDefaultParamValue() );
 
         */
        mw.rcfilters.ui.ChangesLimitAndDateButtonWidget.prototype.updateButtonLabel = function () {
                var message,
-                       limit = this.limitGroupModel.getSelectedItems()[ 0 ],
+                       limit = this.limitGroupModel.findSelectedItems()[ 0 ],
                        label = limit && limit.getLabel(),
-                       days = this.daysGroupModel.getSelectedItems()[ 0 ],
+                       days = this.daysGroupModel.findSelectedItems()[ 0 ],
                        daysParamName = Number( days.getParamName() ) < 1 ?
                                'rcfilters-days-show-hours' :
                                'rcfilters-days-show-days';
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 ac0e50d..304929b 100644 (file)
         * that is currently selected
         */
        mw.rcfilters.ui.ValuePickerWidget.prototype.selectCurrentModelItem = function () {
-               var selectedItem = this.model.getSelectedItems()[ 0 ];
+               var selectedItem = this.model.findSelectedItems()[ 0 ];
 
                if ( selectedItem ) {
                        this.selectWidget.selectItemByData( selectedItem.getName() );
index 0df51f4..df87c9c 100644 (file)
@@ -1,4 +1,3 @@
-/* eslint-disable no-use-before-define */
 ( function ( $, mw, OO ) {
        'use strict';
        var ApiSandbox, Util, WidgetMethods, Validators,
                moduleInfoCache = {},
                baseRequestParams;
 
+       /**
+        * A wrapper for a widget that provides an enable/disable button
+        *
+        * @class
+        * @private
+        * @constructor
+        * @param {OO.ui.Widget} widget
+        * @param {Object} [config] Configuration options
+        */
+       function OptionalWidget( widget, config ) {
+               var k;
+
+               config = config || {};
+
+               this.widget = widget;
+               this.$cover = config.$cover ||
+                       $( '<div>' ).addClass( 'mw-apisandbox-optionalWidget-cover' );
+               this.checkbox = new OO.ui.CheckboxInputWidget( config.checkbox )
+                       .on( 'change', this.onCheckboxChange, [], this );
+
+               OptionalWidget[ 'super' ].call( this, config );
+
+               // Forward most methods for convenience
+               for ( k in this.widget ) {
+                       if ( $.isFunction( this.widget[ k ] ) && !this[ k ] ) {
+                               this[ k ] = this.widget[ k ].bind( this.widget );
+                       }
+               }
+
+               this.$cover.on( 'click', this.onOverlayClick.bind( this ) );
+
+               this.$element
+                       .addClass( 'mw-apisandbox-optionalWidget' )
+                       .append(
+                               this.$cover,
+                               $( '<div>' ).addClass( 'mw-apisandbox-optionalWidget-fields' ).append(
+                                       $( '<div>' ).addClass( 'mw-apisandbox-optionalWidget-widget' ).append(
+                                               widget.$element
+                                       ),
+                                       $( '<div>' ).addClass( 'mw-apisandbox-optionalWidget-checkbox' ).append(
+                                               this.checkbox.$element
+                                       )
+                               )
+                       );
+
+               this.setDisabled( widget.isDisabled() );
+       }
+       OO.inheritClass( OptionalWidget, OO.ui.Widget );
+       OptionalWidget.prototype.onCheckboxChange = function ( checked ) {
+               this.setDisabled( !checked );
+       };
+       OptionalWidget.prototype.onOverlayClick = function () {
+               this.setDisabled( false );
+               if ( $.isFunction( this.widget.focus ) ) {
+                       this.widget.focus();
+               }
+       };
+       OptionalWidget.prototype.setDisabled = function ( disabled ) {
+               OptionalWidget[ 'super' ].prototype.setDisabled.call( this, disabled );
+               this.widget.setDisabled( this.isDisabled() );
+               this.checkbox.setSelected( !this.isDisabled() );
+               this.$cover.toggle( this.isDisabled() );
+               return this;
+       };
+
        WidgetMethods = {
                textInputWidget: {
                        getApiValue: function () {
 
                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();
                                }
                return ret;
        };
 
-       /**
-        * A wrapper for a widget that provides an enable/disable button
-        *
-        * @class
-        * @private
-        * @constructor
-        * @param {OO.ui.Widget} widget
-        * @param {Object} [config] Configuration options
-        */
-       function OptionalWidget( widget, config ) {
-               var k;
-
-               config = config || {};
-
-               this.widget = widget;
-               this.$cover = config.$cover ||
-                       $( '<div>' ).addClass( 'mw-apisandbox-optionalWidget-cover' );
-               this.checkbox = new OO.ui.CheckboxInputWidget( config.checkbox )
-                       .on( 'change', this.onCheckboxChange, [], this );
-
-               OptionalWidget[ 'super' ].call( this, config );
-
-               // Forward most methods for convenience
-               for ( k in this.widget ) {
-                       if ( $.isFunction( this.widget[ k ] ) && !this[ k ] ) {
-                               this[ k ] = this.widget[ k ].bind( this.widget );
-                       }
-               }
-
-               this.$cover.on( 'click', this.onOverlayClick.bind( this ) );
-
-               this.$element
-                       .addClass( 'mw-apisandbox-optionalWidget' )
-                       .append(
-                               this.$cover,
-                               $( '<div>' ).addClass( 'mw-apisandbox-optionalWidget-fields' ).append(
-                                       $( '<div>' ).addClass( 'mw-apisandbox-optionalWidget-widget' ).append(
-                                               widget.$element
-                                       ),
-                                       $( '<div>' ).addClass( 'mw-apisandbox-optionalWidget-checkbox' ).append(
-                                               this.checkbox.$element
-                                       )
-                               )
-                       );
-
-               this.setDisabled( widget.isDisabled() );
-       }
-       OO.inheritClass( OptionalWidget, OO.ui.Widget );
-       OptionalWidget.prototype.onCheckboxChange = function ( checked ) {
-               this.setDisabled( !checked );
-       };
-       OptionalWidget.prototype.onOverlayClick = function () {
-               this.setDisabled( false );
-               if ( $.isFunction( this.widget.focus ) ) {
-                       this.widget.focus();
-               }
-       };
-       OptionalWidget.prototype.setDisabled = function ( disabled ) {
-               OptionalWidget[ 'super' ].prototype.setDisabled.call( this, disabled );
-               this.widget.setDisabled( this.isDisabled() );
-               this.checkbox.setSelected( !this.isDisabled() );
-               this.$cover.toggle( this.isDisabled() );
-               return this;
-       };
-
        $( ApiSandbox.init );
 
        module.exports = ApiSandbox;
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 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 ) );
diff --git a/resources/src/mediawiki.widgets/mw.widgets.SizeFilterWidget.base.css b/resources/src/mediawiki.widgets/mw.widgets.SizeFilterWidget.base.css
new file mode 100644 (file)
index 0000000..772add3
--- /dev/null
@@ -0,0 +1,38 @@
+/*!
+ * MediaWiki Widgets - base SizeFilterWidget styles.
+ *
+ * @copyright 2011-2018 MediaWiki Widgets Team and others; see AUTHORS.txt
+ * @license The MIT License (MIT); see LICENSE.txt
+ */
+
+.mw-widget-sizeFilterWidget > .oo-ui-widget {
+       display: inline-block;
+       vertical-align: middle;
+}
+
+.mw-widget-sizeFilterWidget .oo-ui-textInputWidget {
+       max-width: 29.5em;
+}
+
+/* PHP widget */
+.mw-widget-sizeFilterWidget .oo-ui-radioSelectInputWidget .oo-ui-fieldLayout {
+       display: inline-block;
+       margin: 0;
+       vertical-align: middle;
+}
+
+.mw-widget-sizeFilterWidget .oo-ui-radioSelectInputWidget .oo-ui-fieldLayout:first-child {
+       margin-right: 0.5em;
+}
+
+/* JS widget */
+.mw-widget-sizeFilterWidget .oo-ui-radioSelectInputWidget .oo-ui-radioOptionWidget {
+       display: inline-table;
+       width: auto;
+       margin: 0;
+       vertical-align: middle;
+}
+
+.mw-widget-sizeFilterWidget .oo-ui-radioSelectInputWidget .oo-ui-radioOptionWidget:first-child {
+       margin-right: 0.5em;
+}
diff --git a/resources/src/mediawiki.widgets/mw.widgets.SizeFilterWidget.js b/resources/src/mediawiki.widgets/mw.widgets.SizeFilterWidget.js
new file mode 100644 (file)
index 0000000..7c750f0
--- /dev/null
@@ -0,0 +1,108 @@
+/*!
+ * MediaWiki Widgets - SizeFilterWidget class.
+ *
+ * @copyright 2011-2018 MediaWiki Widgets Team and others; see AUTHORS.txt
+ * @license The MIT License (MIT); see LICENSE.txt
+ */
+( function ( $, mw ) {
+
+       /**
+        * RadioSelectInputWidget and a TextInputWidget to set minimum or maximum byte size
+        *
+        *     mw.loader.using( 'mediawiki.widgets.SizeFilterWidget', function () {
+        *       var sf = new mw.widgets.SizeFilterWidget();
+        *       $( 'body' ).append( sf.$element );
+        *     } );
+        *
+        * @class mw.widgets.SizeFilterWidget
+        * @extends OO.ui.Widget
+        * @uses OO.ui.RadioSelectInputWidget
+        * @uses OO.ui.TextInputWidget
+        *
+        * @constructor
+        * @param {Object} [config] Configuration options
+   * @cfg {Object} [radioselectinput] Config for the radio select input
+        * @cfg {Object} [textinput] Config for the text input
+        * @cfg {boolean} [selectMin=true] Whether to select 'min', false would select 'max'
+        */
+       mw.widgets.SizeFilterWidget = function MwWidgetsSizeFilterWidget( config ) {
+               // Config initialization
+               config = $.extend( { selectMin: true }, config );
+               config.textinput = $.extend( {
+                       type: 'number'
+               }, config.textinput );
+               config.radioselectinput = $.extend( {
+                       options: [
+                               { data: 'min', label: mw.msg( 'minimum-size' ) },
+                               { data: 'max', label: mw.msg( 'maximum-size' ) }
+                       ]
+               }, config.radioselectinput );
+
+               // Properties
+               this.radioselectinput = new OO.ui.RadioSelectInputWidget( config.radioselectinput );
+               this.textinput = new OO.ui.TextInputWidget( config.textinput );
+               this.label = new OO.ui.LabelWidget( { label: mw.msg( 'pagesize' ) } );
+
+               // Parent constructor
+               mw.widgets.SizeFilterWidget.parent.call( this, config );
+
+               // Initialization
+               this.radioselectinput.setValue( config.selectMin ? 'min' : 'max' );
+               this.$element
+                       .addClass( 'mw-widget-sizeFilterWidget' )
+                       .append(
+                               this.radioselectinput.$element,
+                               this.textinput.$element,
+                               this.label.$element
+                       );
+       };
+
+       /* Setup */
+       OO.inheritClass( mw.widgets.SizeFilterWidget, OO.ui.Widget );
+
+       /* Static Methods */
+
+       /**
+        * @inheritdoc
+        */
+       mw.widgets.SizeFilterWidget.static.reusePreInfuseDOM = function ( node, config ) {
+               config = mw.widgets.SizeFilterWidget.parent.static.reusePreInfuseDOM( node, config );
+               config.radioselectinput = OO.ui.RadioSelectInputWidget.static.reusePreInfuseDOM(
+                       $( node ).find( '.oo-ui-radioSelectInputWidget' ),
+                       config.radioselectinput
+               );
+               config.textinput = OO.ui.TextInputWidget.static.reusePreInfuseDOM(
+                       $( node ).find( '.oo-ui-textInputWidget' ),
+                       config.textinput
+               );
+               return config;
+       };
+
+       /**
+        * @inheritdoc
+        */
+       mw.widgets.SizeFilterWidget.static.gatherPreInfuseState = function ( node, config ) {
+               var state = mw.widgets.SizeFilterWidget.parent.static.gatherPreInfuseState( node, config );
+               state.radioselectinput = OO.ui.RadioSelectInputWidget.static.gatherPreInfuseState(
+                       $( node ).find( '.oo-ui-radioSelectInputWidget' ),
+                       config.radioselectinput
+               );
+               state.textinput = OO.ui.TextInputWidget.static.gatherPreInfuseState(
+                       $( node ).find( '.oo-ui-textInputWidget' ),
+                       config.textinput
+               );
+               return state;
+       };
+
+       /* Methods */
+
+       /**
+        * @inheritdoc
+        */
+       mw.widgets.SizeFilterWidget.prototype.restorePreInfuseState = function ( state ) {
+               mw.widgets.SizeFilterWidget.parent.prototype.restorePreInfuseState.call( this, state );
+               this.radioselectinput.restorePreInfuseState( state.radioselectinput );
+               this.textinput.restorePreInfuseState( state.textinput );
+       };
+
+}( jQuery, 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 8a44251..e483763 100644 (file)
                return $container;
        }
 
-       function convertCheckboxesWidgetToCapsules( fieldLayout ) {
-               var checkboxesWidget, checkboxesOptions, capsulesOptions, capsulesWidget;
+       function convertCheckboxesWidgetToTags( fieldLayout ) {
+               var checkboxesWidget, checkboxesOptions, menuTagOptions, menuTagWidget;
 
                checkboxesWidget = fieldLayout.fieldWidget;
                checkboxesOptions = checkboxesWidget.checkboxMultiselectWidget.getItems();
-               capsulesOptions = checkboxesOptions.map( function ( option ) {
+               menuTagOptions = checkboxesOptions.map( function ( option ) {
                        return new OO.ui.MenuOptionWidget( {
                                data: option.getData(),
                                label: option.getLabel()
                        } );
                } );
-               capsulesWidget = new OO.ui.CapsuleMultiselectWidget( {
+               menuTagWidget = new OO.ui.MenuTagMultiselectWidget( {
                        $overlay: true,
                        menu: {
-                               items: capsulesOptions
+                               items: menuTagOptions
                        }
                } );
-               capsulesWidget.setItemsFromData( checkboxesWidget.getValue() );
+               menuTagWidget.setValue( checkboxesWidget.getValue() );
 
                // Data from CapsuleMultiselectWidget will not be submitted with the form, so keep the original
                // CheckboxMultiselectInputWidget up-to-date.
-               capsulesWidget.on( 'change', function () {
-                       checkboxesWidget.setValue( capsulesWidget.getItemsData() );
+               menuTagWidget.on( 'change', function () {
+                       checkboxesWidget.setValue( menuTagWidget.getValue() );
                } );
 
                // Hide original widget and add new one in its place. This is a bit hacky, since the FieldLayout
                // still thinks it's connected to the old widget.
                checkboxesWidget.toggle( false );
-               checkboxesWidget.$element.after( capsulesWidget.$element );
+               checkboxesWidget.$element.after( menuTagWidget.$element );
        }
 
        mw.hook( 'htmlform.enhance' ).add( function ( $root ) {
@@ -99,7 +99,7 @@
                                                modules.push.apply( modules, extraModules );
                                        }
                                        mw.loader.using( modules, function () {
-                                               convertCheckboxesWidgetToCapsules( OO.ui.FieldLayout.static.infuse( $el ) );
+                                               convertCheckboxesWidgetToTags( OO.ui.FieldLayout.static.infuse( $el ) );
                                        } );
                                } else {
                                        mw.loader.using( 'jquery.chosen', function () {
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 6a4ebb1..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 ) {
-                       return $.trimByteLength( '', s, length ).newVal;
+                       return mwString.trimByteLength( '', s, length ).newVal;
                },
 
                /**
index 7260a0a..c469222 100644 (file)
        word-wrap: break-word;
 }
 
+.diff-title {
+       vertical-align: top;
+}
+
+.diff-notice,
+.diff-multi,
 .diff-otitle,
 .diff-ntitle {
        text-align: center;
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 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 82149b3..a777153 100644 (file)
@@ -143,6 +143,9 @@ $wgAutoloadClasses += [
        'SpecialPageTestBase' => "$testDir/phpunit/includes/specials/SpecialPageTestBase.php",
        'SpecialPageExecutor' => "$testDir/phpunit/includes/specials/SpecialPageExecutor.php",
 
+       # tests/phpunit/includes/Storage
+       'MediaWiki\Tests\Storage\RevisionSlotsTest' => "$testDir/phpunit/includes/Storage/RevisionSlotsTest.php",
+
        # tests/phpunit/languages
        'LanguageClassesTestCase' => "$testDir/phpunit/languages/LanguageClassesTestCase.php",
 
index 4f6f6b6..a03f969 100644 (file)
@@ -831,6 +831,19 @@ class ParserTestRunner {
                $parser = $this->getParser( $preprocessor );
                $title = Title::newFromText( $titleText );
 
+               if ( isset( $opts['styletag'] ) ) {
+                       // For testing the behavior of <style> (including those deduplicated
+                       // into <link> tags), add tag hooks to allow them to be generated.
+                       $parser->setHook( 'style', function ( $content, $attributes, $parser ) {
+                               $marker = Parser::MARKER_PREFIX . '-style-' . md5( $content ) . Parser::MARKER_SUFFIX;
+                               $parser->mStripState->addNoWiki( $marker, $content );
+                               return Html::inlineStyle( $marker, 'all', $attributes );
+                       } );
+                       $parser->setHook( 'link', function ( $content, $attributes, $parser ) {
+                               return Html::element( 'link', $attributes );
+                       } );
+               }
+
                if ( isset( $opts['pst'] ) ) {
                        $out = $parser->preSaveTransform( $test['input'], $title, $user, $options );
                        $output = $parser->getOutput();
@@ -1148,7 +1161,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 +1179,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 ad69b9f..fc90010 100644 (file)
@@ -29531,16 +29531,14 @@ unclosed internal link XSS (T137264)
 <p>[[#%3Cscript%3Ealert(1)%3C/script%3E|</p>
 !! end
 
-# Use $wgRawHtml to inject a <style> tag, since you normally can't in wikitext
-# (Parsoid doesn't support $wgRawHtml==true)
 !! test
 Validating that <style> isn't eaten by tidy (T167349)
 !! options
-wgRawHtml=1
+styletag=1
 !! wikitext
 <div class="foo">
-<html><style>.foo::before { content: "<foo>"; }</style></html>
-<html><style data-mw-foobar="baz">.foo::after { content: "<bar>"; }</style></html>
+<style>.foo::before { content: "<foo>"; }</style>
+<style data-mw-foobar="baz">.foo::after { content: "<bar>"; }</style>
 </div>
 !! html/php+tidy
 <div class="foo">
@@ -29549,6 +29547,86 @@ wgRawHtml=1
 </div>
 !! end
 
+!! test
+Validating that <style> isn't wrapped in a paragraph (T186965)
+!! options
+styletag=1
+!! wikitext
+A style tag, by itself or with other style/link tags, shouldn't be wrapped in a paragraph
+
+<style>.foo::before { content: "<foo>"; }</style>
+
+<style>.foo::before { content: "<foo>"; }</style> <link rel="foo" href="bar"/><style>.foo::before { content: "<foo>"; }</style>
+
+But if it's on a line with other content, let it be wrapped.
+
+<style>.foo::before { content: "<foo>"; }</style> bar
+
+foo <style>.foo::before { content: "<foo>"; }</style>
+
+foo <style>.foo::before { content: "<foo>"; }</style> bar
+
+And the same if we have non-paragraph-breaking whitespace
+
+foo
+<style>.foo::before { content: "<foo>"; }</style>
+bar
+!! html/php
+<p>A style tag, by itself or with other style/link tags, shouldn't be wrapped in a paragraph
+</p>
+<style>.foo::before { content: "<foo>"; }</style>
+<style>.foo::before { content: "<foo>"; }</style> <link rel="foo" href="bar"/><style>.foo::before { content: "<foo>"; }</style>
+<p>But if it's on a line with other content, let it be wrapped.
+</p><p><style>.foo::before { content: "<foo>"; }</style> bar
+</p><p>foo <style>.foo::before { content: "<foo>"; }</style>
+</p><p>foo <style>.foo::before { content: "<foo>"; }</style> bar
+</p><p>And the same if we have non-paragraph-breaking whitespace
+</p><p>foo
+<style>.foo::before { content: "<foo>"; }</style>
+bar
+</p>
+!! end
+
+!! test
+Validating that <link> isn't wrapped in a paragraph (T186965)
+!! options
+styletag=1
+!! wikitext
+A link tag, by itself or with other style/link tags, shouldn't be wrapped in a paragraph
+
+<link rel="foo" href="bar"/>
+
+<link rel="foo" href="bar"/> <style>.foo::before { content: "<foo>"; }</style><link rel="foo" href="bar"/>
+
+But if it's on a line with other content, let it be wrapped.
+
+<link rel="foo" href="bar"/> bar
+
+foo <link rel="foo" href="bar"/>
+
+foo <link rel="foo" href="bar"/> bar
+
+And the same if we have non-paragraph-breaking whitespace
+
+foo
+<link rel="foo" href="bar"/>
+bar
+!! html/php
+<p>A link tag, by itself or with other style/link tags, shouldn't be wrapped in a paragraph
+</p>
+<link rel="foo" href="bar"/>
+<link rel="foo" href="bar"/> <style>.foo::before { content: "<foo>"; }</style><link rel="foo" href="bar"/>
+<p>But if it's on a line with other content, let it be wrapped.
+</p><p><link rel="foo" href="bar"/> bar
+</p><p>foo <link rel="foo" href="bar"/>
+</p><p>foo <link rel="foo" href="bar"/> bar
+</p><p>And the same if we have non-paragraph-breaking whitespace
+</p><p>foo
+<link rel="foo" href="bar"/>
+bar
+</p>
+!! end
+
 !! test
 Decoding of HTML entities in headings and links for IDs and link fragments (T103714)
 !! config
index 652b1ee..92c0714 100644 (file)
@@ -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 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 79cac5e..807099f 100644 (file)
@@ -60,6 +60,8 @@ class MutableRevisionRecordTest extends MediaWikiTestCase {
 
        public function testSimpleGetSlotWhenEmpty() {
                $record = new MutableRevisionRecord( Title::newFromText( 'Foo' ) );
+               $this->assertFalse( $record->hasSlot( 'main' ) );
+
                $this->setExpectedException( RevisionAccessException::class );
                $record->getSlot( 'main' );
        }
@@ -71,6 +73,7 @@ class MutableRevisionRecordTest extends MediaWikiTestCase {
                        new WikitextContent( 'x' )
                );
                $record->setSlot( $slot );
+               $this->assertTrue( $record->hasSlot( 'main' ) );
                $this->assertSame( $slot, $record->getSlot( 'main' ) );
        }
 
index c2a275f..0416bcf 100644 (file)
@@ -5,13 +5,12 @@ namespace MediaWiki\Tests\Storage;
 use MediaWiki\Storage\MutableRevisionSlots;
 use MediaWiki\Storage\RevisionAccessException;
 use MediaWiki\Storage\SlotRecord;
-use MediaWikiTestCase;
 use WikitextContent;
 
 /**
  * @covers \MediaWiki\Storage\MutableRevisionSlots
  */
-class MutableRevisionSlotsTest extends MediaWikiTestCase {
+class MutableRevisionSlotsTest extends RevisionSlotsTest {
 
        public function testSetMultipleSlots() {
                $slots = new MutableRevisionSlots();
@@ -20,11 +19,13 @@ class MutableRevisionSlotsTest extends MediaWikiTestCase {
 
                $slotA = SlotRecord::newUnsaved( 'some', new WikitextContent( 'A' ) );
                $slots->setSlot( $slotA );
+               $this->assertTrue( $slots->hasSlot( 'some' ) );
                $this->assertSame( $slotA, $slots->getSlot( 'some' ) );
                $this->assertSame( [ 'some' => $slotA ], $slots->getSlots() );
 
                $slotB = SlotRecord::newUnsaved( 'other', new WikitextContent( 'B' ) );
                $slots->setSlot( $slotB );
+               $this->assertTrue( $slots->hasSlot( 'other' ) );
                $this->assertSame( $slotB, $slots->getSlot( 'other' ) );
                $this->assertSame( [ 'some' => $slotA, 'other' => $slotB ], $slots->getSlots() );
        }
diff --git a/tests/phpunit/includes/Storage/NameTableStoreTest.php b/tests/phpunit/includes/Storage/NameTableStoreTest.php
new file mode 100644 (file)
index 0000000..5276a14
--- /dev/null
@@ -0,0 +1,298 @@
+<?php
+
+namespace MediaWiki\Tests\Storage;
+
+use BagOStuff;
+use EmptyBagOStuff;
+use HashBagOStuff;
+use MediaWiki\Storage\NameTableAccessException;
+use MediaWiki\Storage\NameTableStore;
+use MediaWikiTestCase;
+use Psr\Log\NullLogger;
+use WANObjectCache;
+use Wikimedia\Rdbms\Database;
+use Wikimedia\Rdbms\LoadBalancer;
+use Wikimedia\TestingAccessWrapper;
+
+/**
+ * @author Addshore
+ * @group Database
+ * @covers \MediaWiki\Storage\NameTableStore
+ */
+class NameTableStoreTest extends MediaWikiTestCase {
+
+       public function setUp() {
+               $this->tablesUsed[] = 'slot_roles';
+               parent::setUp();
+       }
+
+       private function populateTable( $values ) {
+               $insertValues = [];
+               foreach ( $values as $name ) {
+                       $insertValues[] = [ 'role_name' => $name ];
+               }
+               $this->db->insert( 'slot_roles', $insertValues );
+       }
+
+       private function getHashWANObjectCache( $cacheBag ) {
+               return new WANObjectCache( [ 'cache' => $cacheBag ] );
+       }
+
+       /**
+        * @param $db
+        * @return \PHPUnit_Framework_MockObject_MockObject|LoadBalancer
+        */
+       private function getMockLoadBalancer( $db ) {
+               $mock = $this->getMockBuilder( LoadBalancer::class )
+                       ->disableOriginalConstructor()
+                       ->getMock();
+               $mock->expects( $this->any() )
+                       ->method( 'getConnection' )
+                       ->willReturn( $db );
+               return $mock;
+       }
+
+       private function getCallCheckingDb( $insertCalls, $selectCalls ) {
+               $mock = $this->getMockBuilder( Database::class )
+                       ->disableOriginalConstructor()
+                       ->getMock();
+               $mock->expects( $this->exactly( $insertCalls ) )
+                       ->method( 'insert' )
+                       ->willReturnCallback( function () {
+                               return call_user_func_array( [ $this->db, 'insert' ], func_get_args() );
+                       } );
+               $mock->expects( $this->exactly( $selectCalls ) )
+                       ->method( 'select' )
+                       ->willReturnCallback( function () {
+                               return call_user_func_array( [ $this->db, 'select' ], func_get_args() );
+                       } );
+               $mock->expects( $this->exactly( $insertCalls ) )
+                       ->method( 'affectedRows' )
+                       ->willReturnCallback( function () {
+                               return call_user_func_array( [ $this->db, 'affectedRows' ], func_get_args() );
+                       } );
+               $mock->expects( $this->any() )
+                       ->method( 'insertId' )
+                       ->willReturnCallback( function () {
+                               return call_user_func_array( [ $this->db, 'insertId' ], func_get_args() );
+                       } );
+               return $mock;
+       }
+
+       private function getNameTableSqlStore(
+               BagOStuff $cacheBag,
+               $insertCalls,
+               $selectCalls,
+               $normalizationCallback = null
+       ) {
+               return new NameTableStore(
+                       $this->getMockLoadBalancer( $this->getCallCheckingDb( $insertCalls, $selectCalls ) ),
+                       $this->getHashWANObjectCache( $cacheBag ),
+                       new NullLogger(),
+                       'slot_roles', 'role_id', 'role_name',
+                       $normalizationCallback
+               );
+       }
+
+       public function provideGetAndAcquireId() {
+               return [
+                       'no wancache, empty table' =>
+                               [ new EmptyBagOStuff(), true, 1, [], 'foo', 1 ],
+                       'no wancache, one matching value' =>
+                               [ new EmptyBagOStuff(), false, 1, [ 'foo' ], 'foo', 1 ],
+                       'no wancache, one not matching value' =>
+                               [ new EmptyBagOStuff(), true, 1, [ 'bar' ], 'foo', 2 ],
+                       'no wancache, multiple, one matching value' =>
+                               [ new EmptyBagOStuff(), false, 1, [ 'foo', 'bar' ], 'bar', 2 ],
+                       'no wancache, multiple, no matching value' =>
+                               [ new EmptyBagOStuff(), true, 1, [ 'foo', 'bar' ], 'baz', 3 ],
+                       'wancache, empty table' =>
+                               [ new HashBagOStuff(), true, 1, [], 'foo', 1 ],
+                       'wancache, one matching value' =>
+                               [ new HashBagOStuff(), false, 1, [ 'foo' ], 'foo', 1 ],
+                       'wancache, one not matching value' =>
+                               [ new HashBagOStuff(), true, 1, [ 'bar' ], 'foo', 2 ],
+                       'wancache, multiple, one matching value' =>
+                               [ new HashBagOStuff(), false, 1, [ 'foo', 'bar' ], 'bar', 2 ],
+                       'wancache, multiple, no matching value' =>
+                               [ new HashBagOStuff(), true, 1, [ 'foo', 'bar' ], 'baz', 3 ],
+               ];
+       }
+
+       /**
+        * @dataProvider provideGetAndAcquireId
+        * @param BagOStuff $cacheBag to use in the WANObjectCache service
+        * @param bool $needsInsert Does the value we are testing need to be inserted?
+        * @param int $selectCalls Number of times the select DB method will be called
+        * @param string[] $existingValues to be added to the db table
+        * @param string $name name to acquire
+        * @param int $expectedId the id we expect the name to have
+        */
+       public function testGetAndAcquireId(
+               $cacheBag,
+               $needsInsert,
+               $selectCalls,
+               $existingValues,
+               $name,
+               $expectedId
+       ) {
+               $this->populateTable( $existingValues );
+               $store = $this->getNameTableSqlStore( $cacheBag, (int)$needsInsert, $selectCalls );
+
+               // Some names will not initially exist
+               try {
+                       $result = $store->getId( $name );
+                       $this->assertSame( $expectedId, $result );
+               } catch ( NameTableAccessException $e ) {
+                       if ( $needsInsert ) {
+                               $this->assertTrue( true ); // Expected exception
+                       } else {
+                               $this->fail( 'Did not expect an exception, but got one: ' . $e->getMessage() );
+                       }
+               }
+
+               // All names should return their id here
+               $this->assertSame( $expectedId, $store->acquireId( $name ) );
+
+               // acquireId inserted these names, so now everything should exist with getId
+               $this->assertSame( $expectedId, $store->getId( $name ) );
+
+               // calling getId again will also still work, and not result in more selects
+               $this->assertSame( $expectedId, $store->getId( $name ) );
+       }
+
+       public function provideTestGetAndAcquireIdNameNormalization() {
+               yield [ 'A', 'a', 'strtolower' ];
+               yield [ 'b', 'B', 'strtoupper' ];
+               yield [
+                       'X',
+                       'X',
+                       function ( $name ) {
+                               return $name;
+                       }
+               ];
+               yield [ 'ZZ', 'ZZ-a', __CLASS__ . '::appendDashAToString' ];
+       }
+
+       public static function appendDashAToString( $string ) {
+               return $string . '-a';
+       }
+
+       /**
+        * @dataProvider provideTestGetAndAcquireIdNameNormalization
+        */
+       public function testGetAndAcquireIdNameNormalization(
+               $nameIn,
+               $nameOut,
+               $normalizationCallback
+       ) {
+               $store = $this->getNameTableSqlStore(
+                       new EmptyBagOStuff(),
+                       1,
+                       1,
+                       $normalizationCallback
+               );
+               $acquiredId = $store->acquireId( $nameIn );
+               $this->assertSame( $nameOut, $store->getName( $acquiredId ) );
+       }
+
+       public function provideGetName() {
+               return [
+                       [ new HashBagOStuff(), 3, 3 ],
+                       [ new EmptyBagOStuff(), 3, 3 ],
+               ];
+       }
+
+       /**
+        * @dataProvider provideGetName
+        */
+       public function testGetName( $cacheBag, $insertCalls, $selectCalls ) {
+               $store = $this->getNameTableSqlStore( $cacheBag, $insertCalls, $selectCalls );
+
+               // Get 1 ID and make sure getName returns correctly
+               $fooId = $store->acquireId( 'foo' );
+               $this->assertSame( 'foo', $store->getName( $fooId ) );
+
+               // Get another ID and make sure getName returns correctly
+               $barId = $store->acquireId( 'bar' );
+               $this->assertSame( 'bar', $store->getName( $barId ) );
+
+               // Blitz the cache and make sure it still returns
+               TestingAccessWrapper::newFromObject( $store )->tableCache = null;
+               $this->assertSame( 'foo', $store->getName( $fooId ) );
+               $this->assertSame( 'bar', $store->getName( $barId ) );
+
+               // Blitz the cache again and get another ID and make sure getName returns correctly
+               TestingAccessWrapper::newFromObject( $store )->tableCache = null;
+               $bazId = $store->acquireId( 'baz' );
+               $this->assertSame( 'baz', $store->getName( $bazId ) );
+               $this->assertSame( 'baz', $store->getName( $bazId ) );
+       }
+
+       public function testGetName_masterFallback() {
+               $store = $this->getNameTableSqlStore( new EmptyBagOStuff(), 1, 2 );
+
+               // Insert a new name
+               $fooId = $store->acquireId( 'foo' );
+
+               // Empty the process cache, getCachedTable() will now return this empty array
+               TestingAccessWrapper::newFromObject( $store )->tableCache = [];
+
+               // getName should fallback to master, which is why we assert 2 selectCalls above
+               $this->assertSame( 'foo', $store->getName( $fooId ) );
+       }
+
+       public function testGetMap_empty() {
+               $this->populateTable( [] );
+               $store = $this->getNameTableSqlStore( new HashBagOStuff(), 0, 1 );
+               $table = $store->getMap();
+               $this->assertSame( [], $table );
+       }
+
+       public function testGetMap_twoValues() {
+               $this->populateTable( [ 'foo', 'bar' ] );
+               $store = $this->getNameTableSqlStore( new HashBagOStuff(), 0, 1 );
+
+               // We are using a cache, so 2 calls should only result in 1 select on the db
+               $store->getMap();
+               $table = $store->getMap();
+
+               $expected = [ 2 => 'bar', 1 => 'foo' ];
+               $this->assertSame( $expected, $table );
+               // Make sure the table returned is the same as the cached table
+               $this->assertSame( $expected, TestingAccessWrapper::newFromObject( $store )->tableCache );
+       }
+
+       public function testCacheRaceCondition() {
+               $wanHashBag = new HashBagOStuff();
+               $store1 = $this->getNameTableSqlStore( $wanHashBag, 1, 1 );
+               $store2 = $this->getNameTableSqlStore( $wanHashBag, 1, 0 );
+               $store3 = $this->getNameTableSqlStore( $wanHashBag, 1, 1 );
+
+               // Cache the current table in the instances we will use
+               // This simulates multiple requests running simultaneously
+               $store1->getMap();
+               $store2->getMap();
+               $store3->getMap();
+
+               // Store 2 separate names using different instances
+               $fooId = $store1->acquireId( 'foo' );
+               $barId = $store2->acquireId( 'bar' );
+
+               // Each of these instances should be aware of what they have inserted
+               $this->assertSame( $fooId, $store1->acquireId( 'foo' ) );
+               $this->assertSame( $barId, $store2->acquireId( 'bar' ) );
+
+               // A new store should be able to get both of these new Ids
+               // Note: before there was a race condition here where acquireId( 'bar' ) would update the
+               //       cache with data missing the 'foo' key that it was not aware of
+               $store4 = $this->getNameTableSqlStore( $wanHashBag, 0, 1 );
+               $this->assertSame( $fooId, $store4->getId( 'foo' ) );
+               $this->assertSame( $barId, $store4->getId( 'bar' ) );
+
+               // If a store with old cached data tries to acquire these we will get the same ids.
+               $this->assertSame( $fooId, $store3->acquireId( 'foo' ) );
+               $this->assertSame( $barId, $store3->acquireId( 'bar' ) );
+       }
+
+}
index 4dfae4b..b9f833c 100644 (file)
@@ -10,13 +10,21 @@ use WikitextContent;
 
 class RevisionSlotsTest extends MediaWikiTestCase {
 
+       /**
+        * @param SlotRecord[] $slots
+        * @return RevisionSlots
+        */
+       protected function newRevisionSlots( $slots = [] ) {
+               return new RevisionSlots( $slots );
+       }
+
        /**
         * @covers \MediaWiki\Storage\RevisionSlots::getSlot
         */
        public function testGetSlot() {
                $mainSlot = SlotRecord::newUnsaved( 'main', new WikitextContent( 'A' ) );
                $auxSlot = SlotRecord::newUnsaved( 'aux', new WikitextContent( 'B' ) );
-               $slots = new RevisionSlots( [ $mainSlot, $auxSlot ] );
+               $slots = $this->newRevisionSlots( [ $mainSlot, $auxSlot ] );
 
                $this->assertSame( $mainSlot, $slots->getSlot( 'main' ) );
                $this->assertSame( $auxSlot, $slots->getSlot( 'aux' ) );
@@ -24,6 +32,20 @@ class RevisionSlotsTest extends MediaWikiTestCase {
                $slots->getSlot( 'nothere' );
        }
 
+       /**
+        * @covers \MediaWiki\Storage\RevisionSlots::hasSlot
+        */
+       public function testHasSlot() {
+               $mainSlot = SlotRecord::newUnsaved( 'main', new WikitextContent( 'A' ) );
+               $auxSlot = SlotRecord::newUnsaved( 'aux', new WikitextContent( 'B' ) );
+               $slots = $this->newRevisionSlots( [ $mainSlot, $auxSlot ] );
+
+               $this->assertTrue( $slots->hasSlot( 'main' ) );
+               $this->assertTrue( $slots->hasSlot( 'aux' ) );
+               $this->assertFalse( $slots->hasSlot( 'AUX' ) );
+               $this->assertFalse( $slots->hasSlot( 'xyz' ) );
+       }
+
        /**
         * @covers \MediaWiki\Storage\RevisionSlots::getContent
         */
@@ -32,7 +54,7 @@ class RevisionSlotsTest extends MediaWikiTestCase {
                $auxContent = new WikitextContent( 'B' );
                $mainSlot = SlotRecord::newUnsaved( 'main', $mainContent );
                $auxSlot = SlotRecord::newUnsaved( 'aux', $auxContent );
-               $slots = new RevisionSlots( [ $mainSlot, $auxSlot ] );
+               $slots = $this->newRevisionSlots( [ $mainSlot, $auxSlot ] );
 
                $this->assertSame( $mainContent, $slots->getContent( 'main' ) );
                $this->assertSame( $auxContent, $slots->getContent( 'aux' ) );
@@ -46,7 +68,7 @@ class RevisionSlotsTest extends MediaWikiTestCase {
        public function testGetSlotRoles_someSlots() {
                $mainSlot = SlotRecord::newUnsaved( 'main', new WikitextContent( 'A' ) );
                $auxSlot = SlotRecord::newUnsaved( 'aux', new WikitextContent( 'B' ) );
-               $slots = new RevisionSlots( [ $mainSlot, $auxSlot ] );
+               $slots = $this->newRevisionSlots( [ $mainSlot, $auxSlot ] );
 
                $this->assertSame( [ 'main', 'aux' ], $slots->getSlotRoles() );
        }
@@ -55,7 +77,7 @@ class RevisionSlotsTest extends MediaWikiTestCase {
         * @covers \MediaWiki\Storage\RevisionSlots::getSlotRoles
         */
        public function testGetSlotRoles_noSlots() {
-               $slots = new RevisionSlots( [] );
+               $slots = $this->newRevisionSlots( [] );
 
                $this->assertSame( [], $slots->getSlotRoles() );
        }
@@ -67,7 +89,7 @@ class RevisionSlotsTest extends MediaWikiTestCase {
                $mainSlot = SlotRecord::newUnsaved( 'main', new WikitextContent( 'A' ) );
                $auxSlot = SlotRecord::newUnsaved( 'aux', new WikitextContent( 'B' ) );
                $slotsArray = [ $mainSlot, $auxSlot ];
-               $slots = new RevisionSlots( $slotsArray );
+               $slots = $this->newRevisionSlots( $slotsArray );
 
                $this->assertEquals( [ 'main' => $mainSlot, 'aux' => $auxSlot ], $slots->getSlots() );
        }
@@ -87,7 +109,7 @@ class RevisionSlotsTest extends MediaWikiTestCase {
                foreach ( $contentStrings as $key => $contentString ) {
                        $slotsArray[] = SlotRecord::newUnsaved( strval( $key ), new WikitextContent( $contentString ) );
                }
-               $slots = new RevisionSlots( $slotsArray );
+               $slots = $this->newRevisionSlots( $slotsArray );
 
                $this->assertSame( $expected, $slots->computeSize() );
        }
@@ -109,7 +131,7 @@ class RevisionSlotsTest extends MediaWikiTestCase {
                foreach ( $contentStrings as $key => $contentString ) {
                        $slotsArray[] = SlotRecord::newUnsaved( strval( $key ), new WikitextContent( $contentString ) );
                }
-               $slots = new RevisionSlots( $slotsArray );
+               $slots = $this->newRevisionSlots( $slotsArray );
 
                $this->assertSame( $expected, $slots->computeSha1() );
        }
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..3976995 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' );
 
@@ -458,8 +458,9 @@ class RevisionStoreRecordTest extends MediaWikiTestCase {
                $rev = $this->newRevision( [ 'rev_deleted' => $visibility ] );
 
                // NOTE: slot meta-data is never suppressed, just the content is!
-               $this->assertNotNull( $rev->getSlot( 'main', RevisionRecord::RAW ), 'raw can' );
-               $this->assertNotNull( $rev->getSlot( 'main', RevisionRecord::FOR_PUBLIC ), 'public can' );
+               $this->assertTrue( $rev->hasSlot( 'main' ), 'hasSlot is never suppressed' );
+               $this->assertNotNull( $rev->getSlot( 'main', RevisionRecord::RAW ), 'raw meta' );
+               $this->assertNotNull( $rev->getSlot( 'main', RevisionRecord::FOR_PUBLIC ), 'public meta' );
 
                $this->assertNotNull(
                        $rev->getSlot( 'main', RevisionRecord::FOR_THIS_USER, $user ),
@@ -562,6 +563,13 @@ class RevisionStoreRecordTest extends MediaWikiTestCase {
                $this->assertSame( 'main', $slot->getRole(), 'getRole()' );
        }
 
+       public function testHasSlot() {
+               $rev = $this->newRevision();
+
+               $this->assertTrue( $rev->hasSlot( 'main' ) );
+               $this->assertFalse( $rev->hasSlot( 'xyz' ) );
+       }
+
        public function testGetContent() {
                $rev = $this->newRevision();
 
@@ -688,7 +696,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 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 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 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,
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 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 c833934..07dbd00 100644 (file)
@@ -17,14 +17,23 @@ class EtcdConfigTest extends PHPUnit\Framework\TestCase {
                        ->getMock();
        }
 
-       private function createSimpleConfigMock( array $config ) {
+       private static function createEtcdResponse( array $response ) {
+               $baseResponse = [
+                       'config' => null,
+                       'error' => null,
+                       'retry' => false,
+                       'modifiedIndex' => 0,
+               ];
+               return array_merge( $baseResponse, $response );
+       }
+
+       private function createSimpleConfigMock( array $config, $index = 0 ) {
                $mock = $this->createConfigMock();
                $mock->expects( $this->once() )->method( 'fetchAllFromEtcd' )
-                       ->willReturn( [
-                               $config,
-                               null, // error
-                               false // retry?
-                       ] );
+                       ->willReturn( self::createEtcdResponse( [
+                               'config' => $config,
+                               'modifiedIndex' => $index,
+                       ] ) );
                return $mock;
        }
 
@@ -70,6 +79,17 @@ class EtcdConfigTest extends PHPUnit\Framework\TestCase {
                $config->get( 'unknown' );
        }
 
+       /**
+        * @covers EtcdConfig::getModifiedIndex
+        */
+       public function testGetModifiedIndex() {
+               $config = $this->createSimpleConfigMock(
+                       [ 'some' => 'value' ],
+                       123
+               );
+               $this->assertSame( 123, $config->getModifiedIndex() );
+       }
+
        /**
         * @covers EtcdConfig::__construct
         */
@@ -81,6 +101,7 @@ class EtcdConfigTest extends PHPUnit\Framework\TestCase {
                        ->willReturn( [
                                'config' => [ 'known' => 'from-cache' ],
                                'expires' => INF,
+                               'modifiedIndex' => 123
                        ] );
                $config = $this->createConfigMock( [ 'cache' => $cache ] );
 
@@ -95,11 +116,8 @@ class EtcdConfigTest extends PHPUnit\Framework\TestCase {
                        'class' => HashBagOStuff::class
                ] ] );
                $config->expects( $this->once() )->method( 'fetchAllFromEtcd' )
-                       ->willReturn( [
-                               [ 'known' => 'from-fetch' ],
-                               null, // error
-                               false // retry?
-                       ] );
+                       ->willReturn( self::createEtcdResponse(
+                               [ 'config' => [ 'known' => 'from-fetch' ], ] ) );
 
                $this->assertSame( 'from-fetch', $config->get( 'known' ) );
        }
@@ -166,7 +184,8 @@ class EtcdConfigTest extends PHPUnit\Framework\TestCase {
                        'cache' => $cache,
                ] );
                $mock->expects( $this->once() )->method( 'fetchAllFromEtcd' )
-                       ->willReturn( [ [ 'known' => 'from-fetch' ], null, false ] );
+                       ->willReturn(
+                               self::createEtcdResponse( [ 'config' => [ 'known' => 'from-fetch' ] ] ) );
 
                $this->assertSame( 'from-fetch', $mock->get( 'known' ) );
        }
@@ -191,7 +210,7 @@ class EtcdConfigTest extends PHPUnit\Framework\TestCase {
                        'cache' => $cache,
                ] );
                $mock->expects( $this->once() )->method( 'fetchAllFromEtcd' )
-                       ->willReturn( [ null, 'Fake error', false ] );
+                       ->willReturn( self::createEtcdResponse( [ 'error' => 'Fake error', ] ) );
 
                $this->setExpectedException( ConfigException::class );
                $mock->get( 'key' );
@@ -213,6 +232,7 @@ class EtcdConfigTest extends PHPUnit\Framework\TestCase {
                                [
                                        'config' => [ 'known' => 'from-cache' ],
                                        'expires' => INF,
+                                       'modifiedIndex' => 123
                                ]
                        ) );
                // .. misses lock
@@ -241,6 +261,7 @@ class EtcdConfigTest extends PHPUnit\Framework\TestCase {
                        ->willReturn( [
                                'config' => [ 'known' => 'from-cache' ],
                                'expires' => INF,
+                               'modifiedIndex' => 0,
                        ] );
                $cache->expects( $this->never() )->method( 'lock' );
 
@@ -266,6 +287,7 @@ class EtcdConfigTest extends PHPUnit\Framework\TestCase {
                        ->willReturn( [
                                'config' => [ 'known' => 'from-cache' ],
                                'expires' => INF,
+                               'modifiedIndex' => 0,
                        ] );
                $cache->expects( $this->never() )->method( 'lock' );
 
@@ -292,6 +314,7 @@ class EtcdConfigTest extends PHPUnit\Framework\TestCase {
                        [
                                'config' => [ 'known' => 'from-cache-expired' ],
                                'expires' => -INF,
+                               'modifiedIndex' => 0,
                        ]
                );
                // .. gets lock
@@ -303,7 +326,7 @@ class EtcdConfigTest extends PHPUnit\Framework\TestCase {
                        'cache' => $cache,
                ] );
                $mock->expects( $this->once() )->method( 'fetchAllFromEtcd' )
-                       ->willReturn( [ [ 'known' => 'from-fetch' ], null, false ] );
+                       ->willReturn( self::createEtcdResponse( [ 'config' => [ 'known' => 'from-fetch' ] ] ) );
 
                $this->assertSame( 'from-fetch', $mock->get( 'known' ) );
        }
@@ -321,6 +344,7 @@ class EtcdConfigTest extends PHPUnit\Framework\TestCase {
                        [
                                'config' => [ 'known' => 'from-cache-expired' ],
                                'expires' => -INF,
+                               'modifiedIndex' => 0,
                        ]
                );
                // .. gets lock
@@ -332,7 +356,7 @@ class EtcdConfigTest extends PHPUnit\Framework\TestCase {
                        'cache' => $cache,
                ] );
                $mock->expects( $this->once() )->method( 'fetchAllFromEtcd' )
-                       ->willReturn( [ null, 'Fake failure', true ] );
+                       ->willReturn( self::createEtcdResponse( [ 'error' => 'Fake failure', 'retry' => true ] ) );
 
                $this->assertSame( 'from-cache-expired', $mock->get( 'known' ) );
        }
@@ -350,6 +374,7 @@ class EtcdConfigTest extends PHPUnit\Framework\TestCase {
                        ->willReturn( [
                                'config' => [ 'known' => 'from-cache-expired' ],
                                'expires' => -INF,
+                               'modifiedIndex' => 0,
                        ] );
                // .. misses lock
                $cache->expects( $this->once() )->method( 'lock' )
@@ -374,16 +399,16 @@ class EtcdConfigTest extends PHPUnit\Framework\TestCase {
                                        'body' => json_encode( [ 'node' => [ 'nodes' => [
                                                [
                                                        'key' => '/example/foo',
-                                                       'value' => json_encode( [ 'val' => true ] )
+                                                       'value' => json_encode( [ 'val' => true ] ),
+                                                       'modifiedIndex' => 123
                                                ],
                                        ] ] ] ),
                                        'error' => '',
                                ],
-                               'expect' => [
-                                       [ 'foo' => true ], // data
-                                       null,
-                                       false // retry
-                               ],
+                               'expect' => self::createEtcdResponse( [
+                                       'config' => [ 'foo' => true ], // data
+                                       'modifiedIndex' => 123
+                               ] ),
                        ],
                        '200 OK - Empty dir' => [
                                'http' => [
@@ -393,25 +418,27 @@ class EtcdConfigTest extends PHPUnit\Framework\TestCase {
                                        'body' => json_encode( [ 'node' => [ 'nodes' => [
                                                [
                                                        'key' => '/example/foo',
-                                                       'value' => json_encode( [ 'val' => true ] )
+                                                       'value' => json_encode( [ 'val' => true ] ),
+                                                       'modifiedIndex' => 123
                                                ],
                                                [
                                                        'key' => '/example/sub',
                                                        'dir' => true,
+                                                       'modifiedIndex' => 234,
                                                        'nodes' => [],
                                                ],
                                                [
                                                        'key' => '/example/bar',
-                                                       'value' => json_encode( [ 'val' => false ] )
+                                                       'value' => json_encode( [ 'val' => false ] ),
+                                                       'modifiedIndex' => 125
                                                ],
                                        ] ] ] ),
                                        'error' => '',
                                ],
-                               'expect' => [
-                                       [ 'foo' => true, 'bar' => false ], // data
-                                       null,
-                                       false // retry
-                               ],
+                               'expect' => self::createEtcdResponse( [
+                                       'config' => [ 'foo' => true, 'bar' => false ], // data
+                                       'modifiedIndex' => 125 // largest modified index
+                               ] ),
                        ],
                        '200 OK - Recursive' => [
                                'http' => [
@@ -422,25 +449,28 @@ class EtcdConfigTest extends PHPUnit\Framework\TestCase {
                                                [
                                                        'key' => '/example/a',
                                                        'dir' => true,
+                                                       'modifiedIndex' => 124,
                                                        'nodes' => [
                                                                [
                                                                        'key' => 'b',
                                                                        'value' => json_encode( [ 'val' => true ] ),
+                                                                       'modifiedIndex' => 123,
+
                                                                ],
                                                                [
                                                                        'key' => 'c',
                                                                        'value' => json_encode( [ 'val' => false ] ),
+                                                                       'modifiedIndex' => 123,
                                                                ],
                                                        ],
                                                ],
                                        ] ] ] ),
                                        'error' => '',
                                ],
-                               'expect' => [
-                                       [ 'a/b' => true, 'a/c' => false ], // data
-                                       null,
-                                       false // retry
-                               ],
+                               'expect' => self::createEtcdResponse( [
+                                       'config' => [ 'a/b' => true, 'a/c' => false ], // data
+                                       'modifiedIndex' => 123 // largest modified index
+                               ] ),
                        ],
                        '200 OK - Missing nodes at second level' => [
                                'http' => [
@@ -451,15 +481,14 @@ class EtcdConfigTest extends PHPUnit\Framework\TestCase {
                                                [
                                                        'key' => '/example/a',
                                                        'dir' => true,
+                                                       'modifiedIndex' => 0,
                                                ],
                                        ] ] ] ),
                                        'error' => '',
                                ],
-                               'expect' => [
-                                       null,
-                                       "Unexpected JSON response in dir 'a'; missing 'nodes' list.",
-                                       false // retry
-                               ],
+                               'expect' => self::createEtcdResponse( [
+                                       'error' => "Unexpected JSON response in dir 'a'; missing 'nodes' list.",
+                               ] ),
                        ],
                        '200 OK - Directory with non-array "nodes" key' => [
                                'http' => [
@@ -475,11 +504,9 @@ class EtcdConfigTest extends PHPUnit\Framework\TestCase {
                                        ] ] ] ),
                                        'error' => '',
                                ],
-                               'expect' => [
-                                       null,
-                                       "Unexpected JSON response in dir 'a'; 'nodes' is not an array.",
-                                       false // retry
-                               ],
+                               'expect' => self::createEtcdResponse( [
+                                       'error' => "Unexpected JSON response in dir 'a'; 'nodes' is not an array.",
+                               ] ),
                        ],
                        '200 OK - Correctly encoded garbage response' => [
                                'http' => [
@@ -489,11 +516,9 @@ class EtcdConfigTest extends PHPUnit\Framework\TestCase {
                                        'body' => json_encode( [ 'foo' => 'bar' ] ),
                                        'error' => '',
                                ],
-                               'expect' => [
-                                       null,
-                                       "Unexpected JSON response: Missing or invalid node at top level.",
-                                       false // retry
-                               ],
+                               'expect' => self::createEtcdResponse( [
+                                       'error' => "Unexpected JSON response: Missing or invalid node at top level.",
+                               ] ),
                        ],
                        '200 OK - Bad value' => [
                                'http' => [
@@ -503,30 +528,27 @@ class EtcdConfigTest extends PHPUnit\Framework\TestCase {
                                        'body' => json_encode( [ 'node' => [ 'nodes' => [
                                                [
                                                        'key' => '/example/foo',
-                                                       'value' => ';"broken{value'
+                                                       'value' => ';"broken{value',
+                                                       'modifiedIndex' => 123,
                                                ]
                                        ] ] ] ),
                                        'error' => '',
                                ],
-                               'expect' => [
-                                       null, // data
-                                       "Failed to parse value for 'foo'.",
-                                       false // retry
-                               ],
+                               'expect' => self::createEtcdResponse( [
+                                       'error' => "Failed to parse value for 'foo'.",
+                               ] ),
                        ],
                        '200 OK - Empty node list' => [
                                'http' => [
                                        'code' => 200,
                                        'reason' => 'OK',
                                        'headers' => [],
-                                       'body' => '{"node":{"nodes":[]}}',
+                                       'body' => '{"node":{"nodes":[], "modifiedIndex": 12 }}',
                                        'error' => '',
                                ],
-                               'expect' => [
-                                       [], // data
-                                       null,
-                                       false // retry
-                               ],
+                               'expect' => self::createEtcdResponse( [
+                                       'config' => [], // data
+                               ] ),
                        ],
                        '200 OK - Invalid JSON' => [
                                'http' => [
@@ -536,11 +558,9 @@ class EtcdConfigTest extends PHPUnit\Framework\TestCase {
                                        'body' => '',
                                        'error' => '(curl error: no status set)',
                                ],
-                               'expect' => [
-                                       null, // data
-                                       "Error unserializing JSON response.",
-                                       false // retry
-                               ],
+                               'expect' => self::createEtcdResponse( [
+                                       'error' => "Error unserializing JSON response.",
+                               ] ),
                        ],
                        '404 Not Found' => [
                                'http' => [
@@ -550,11 +570,9 @@ class EtcdConfigTest extends PHPUnit\Framework\TestCase {
                                        'body' => '',
                                        'error' => '',
                                ],
-                               'expect' => [
-                                       null, // data
-                                       'HTTP 404 (Not Found)',
-                                       false // retry
-                               ],
+                               'expect' => self::createEtcdResponse( [
+                                       'error' => 'HTTP 404 (Not Found)',
+                               ] ),
                        ],
                        '400 Bad Request - custom error' => [
                                'http' => [
@@ -564,11 +582,10 @@ class EtcdConfigTest extends PHPUnit\Framework\TestCase {
                                        'body' => '',
                                        'error' => 'No good reason',
                                ],
-                               'expect' => [
-                                       null, // data
-                                       'No good reason',
-                                       true // retry
-                               ],
+                               'expect' => self::createEtcdResponse( [
+                                       'error' => 'No good reason',
+                                       'retry' => true, // retry
+                               ] ),
                        ],
                ];
        }
index 6656fa4..823be6f 100644 (file)
@@ -153,16 +153,6 @@ class JavaScriptContentTest extends TextContentTest {
                                'any',
                                true
                        ],
-                       [ 'Foo',
-                               null,
-                               'comma',
-                               false
-                       ],
-                       [ 'Foo, bar',
-                               null,
-                               'comma',
-                               false
-                       ],
                        [ 'Foo',
                                null,
                                'link',
@@ -188,11 +178,6 @@ class JavaScriptContentTest extends TextContentTest {
                                'any',
                                true
                        ],
-                       [ '#REDIRECT [[bar]]',
-                               true,
-                               'comma',
-                               false
-                       ],
                        [ '#REDIRECT [[bar]]',
                                true,
                                'link',
index b548091..406bc96 100644 (file)
@@ -197,16 +197,6 @@ class TextContentTest extends MediaWikiLangTestCase {
                                'any',
                                true
                        ],
-                       [ 'Foo',
-                               null,
-                               'comma',
-                               false
-                       ],
-                       [ 'Foo, bar',
-                               null,
-                               'comma',
-                               false
-                       ],
                ];
        }
 
index e04f562..1db6aab 100644 (file)
@@ -266,16 +266,6 @@ just a test"
                                'any',
                                true
                        ],
-                       [ 'Foo',
-                               null,
-                               'comma',
-                               false
-                       ],
-                       [ 'Foo, bar',
-                               null,
-                               'comma',
-                               true
-                       ],
                        [ 'Foo',
                                null,
                                'link',
@@ -301,11 +291,6 @@ just a test"
                                'any',
                                false
                        ],
-                       [ '#REDIRECT [[bar]]',
-                               true,
-                               'comma',
-                               false
-                       ],
                        [ '#REDIRECT [[bar]]',
                                true,
                                'link',
index 2de35a7..729b58c 100644 (file)
@@ -508,4 +508,12 @@ class DatabaseSqliteTest extends MediaWikiTestCase {
 
                $this->assertContains( 'SQLite ', $toString );
        }
+
+       /**
+        * @covers \Wikimedia\Rdbms\DatabaseSqlite::getAttributes()
+        */
+       public function testsAttributes() {
+               $attributes = Database::attributesFromType( 'sqlite' );
+               $this->assertTrue( $attributes[Database::ATTR_DB_LEVEL_LOCKING] );
+       }
 }
index fe7b710..6cc41d1 100644 (file)
@@ -1,10 +1,5 @@
 <?php
 
-use Wikimedia\Rdbms\DBError;
-use Wikimedia\Rdbms\LoadBalancer;
-use Wikimedia\Rdbms\DatabaseDomain;
-use Wikimedia\Rdbms\Database;
-
 /**
  * Holds tests for LoadBalancer MediaWiki class.
  *
@@ -28,6 +23,13 @@ use Wikimedia\Rdbms\Database;
  *
  * @covers \Wikimedia\Rdbms\LoadBalancer
  */
+
+use Wikimedia\Rdbms\DBError;
+use Wikimedia\Rdbms\DatabaseDomain;
+use Wikimedia\Rdbms\Database;
+use Wikimedia\Rdbms\LoadBalancer;
+use Wikimedia\Rdbms\LoadMonitorNull;
+
 class LoadBalancerTest extends MediaWikiTestCase {
        public function testWithoutReplica() {
                global $wgDBserver, $wgDBname, $wgDBuser, $wgDBpassword, $wgDBtype, $wgSQLiteDataDir;
@@ -190,4 +192,52 @@ class LoadBalancerTest extends MediaWikiTestCase {
                }
        }
 
+       public function testServerAttributes() {
+               $servers = [
+                       [ // master
+                               'dbname'      => 'my_unittest_wiki',
+                               'tablePrefix' => 'unittest_',
+                               'type'        => 'sqlite',
+                               'dbDirectory' => "some_directory",
+                               'load'        => 0
+                       ]
+               ];
+
+               $lb = new LoadBalancer( [
+                       'servers' => $servers,
+                       'localDomain' => new DatabaseDomain( 'my_unittest_wiki', null, 'unittest_' ),
+                       'loadMonitorClass' => LoadMonitorNull::class
+               ] );
+
+               $this->assertTrue( $lb->getServerAttributes( 0 )[Database::ATTR_DB_LEVEL_LOCKING] );
+
+               $servers = [
+                       [ // master
+                               'host'        => 'db1001',
+                               'user'        => 'wikiuser',
+                               'password'    => 'none',
+                               'dbname'      => 'my_unittest_wiki',
+                               'tablePrefix' => 'unittest_',
+                               'type'        => 'mysql',
+                               'load'        => 100
+                       ],
+                       [ // emulated replica
+                               'host'        => 'db1002',
+                               'user'        => 'wikiuser',
+                               'password'    => 'none',
+                               'dbname'      => 'my_unittest_wiki',
+                               'tablePrefix' => 'unittest_',
+                               'type'        => 'mysql',
+                               'load'        => 100
+                       ]
+               ];
+
+               $lb = new LoadBalancer( [
+                       'servers' => $servers,
+                       'localDomain' => new DatabaseDomain( 'my_unittest_wiki', null, 'unittest_' ),
+                       'loadMonitorClass' => LoadMonitorNull::class
+               ] );
+
+               $this->assertFalse( $lb->getServerAttributes( 1 )[Database::ATTR_DB_LEVEL_LOCKING] );
+       }
 }
diff --git a/tests/phpunit/includes/deferred/SiteStatsUpdateTest.php b/tests/phpunit/includes/deferred/SiteStatsUpdateTest.php
new file mode 100644 (file)
index 0000000..83e9a47
--- /dev/null
@@ -0,0 +1,77 @@
+<?php
+
+use Wikimedia\TestingAccessWrapper;
+
+/**
+ * @group Database
+ */
+class SiteStatsUpdateTest extends MediaWikiTestCase {
+       /**
+        * @covers SiteStatsUpdate::factory
+        * @covers SiteStatsUpdate::merge
+        */
+       public function testFactoryAndMerge() {
+               $update1 = SiteStatsUpdate::factory( [ 'pages' => 1, 'users' => 2 ] );
+               $update2 = SiteStatsUpdate::factory( [ 'users' => 1, 'images' => 1 ] );
+
+               $update1->merge( $update2 );
+               $wrapped = TestingAccessWrapper::newFromObject( $update1 );
+
+               $this->assertEquals( 1, $wrapped->pages );
+               $this->assertEquals( 3, $wrapped->users );
+               $this->assertEquals( 1, $wrapped->images );
+               $this->assertEquals( 0, $wrapped->edits );
+               $this->assertEquals( 0, $wrapped->articles );
+       }
+
+       /**
+        * @covers SiteStatsUpdate::doUpdate()
+        * @covers SiteStatsInit::refresh()
+        */
+       public function testDoUpdate() {
+               $this->setMwGlobals( 'wgSiteStatsAsyncFactor', false );
+               $this->setMwGlobals( 'wgCommandLineMode', false ); // disable opportunistic updates
+
+               $dbw = wfGetDB( DB_MASTER );
+               $statsInit = new SiteStatsInit( $dbw );
+               $statsInit->refresh();
+
+               $ei = SiteStats::edits(); // trigger load
+               $pi = SiteStats::pages();
+               $ui = SiteStats::users();
+               $fi = SiteStats::images();
+               $ai = SiteStats::articles();
+
+               $dbw->begin( __METHOD__ ); // block opportunistic updates
+
+               $update = SiteStatsUpdate::factory( [ 'pages' => 2, 'images' => 1, 'edits' => 2 ] );
+               $this->assertEquals( 0, DeferredUpdates::pendingUpdatesCount() );
+               $update->doUpdate();
+               $this->assertEquals( 1, DeferredUpdates::pendingUpdatesCount() );
+
+               // Still the same
+               SiteStats::unload();
+               $this->assertEquals( $pi, SiteStats::pages(), 'page count' );
+               $this->assertEquals( $ei, SiteStats::edits(), 'edit count' );
+               $this->assertEquals( $ui, SiteStats::users(), 'user count' );
+               $this->assertEquals( $fi, SiteStats::images(), 'file count' );
+               $this->assertEquals( $ai, SiteStats::articles(), 'article count' );
+               $this->assertEquals( 1, DeferredUpdates::pendingUpdatesCount() );
+
+               $dbw->commit( __METHOD__ );
+
+               $this->assertEquals( 1, DeferredUpdates::pendingUpdatesCount() );
+               DeferredUpdates::doUpdates();
+               $this->assertEquals( 0, DeferredUpdates::pendingUpdatesCount() );
+
+               SiteStats::unload();
+               $this->assertEquals( $pi + 2, SiteStats::pages(), 'page count' );
+               $this->assertEquals( $ei + 2, SiteStats::edits(), 'edit count' );
+               $this->assertEquals( $ui, SiteStats::users(), 'user count' );
+               $this->assertEquals( $fi + 1, SiteStats::images(), 'file count' );
+               $this->assertEquals( $ai, SiteStats::articles(), 'article count' );
+
+               $statsInit = new SiteStatsInit();
+               $statsInit->refresh();
+       }
+}
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 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 b9f57b5..14c7057 100644 (file)
@@ -29,6 +29,7 @@ use Wikimedia\Rdbms\MySQLMasterPos;
 use Wikimedia\Rdbms\DatabaseMysqlBase;
 use Wikimedia\Rdbms\DatabaseMysqli;
 use Wikimedia\Rdbms\Database;
+use Wikimedia\TestingAccessWrapper;
 
 /**
  * Fake class around abstract class so we can call concrete methods.
@@ -495,4 +496,112 @@ 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 );
+       }
+
+       /**
+        * @covers Wikimedia\Rdbms\DatabaseMysqlBase::isInsertSelectSafe
+        * @dataProvider provideInsertSelectCases
+        */
+       public function testInsertSelectIsSafe( $insertOpts, $selectOpts, $row, $safe ) {
+               $db = $this->getMockBuilder( DatabaseMysqli::class )
+                       ->disableOriginalConstructor()
+                       ->setMethods( [ 'getReplicationSafetyInfo' ] )
+                       ->getMock();
+               $db->method( 'getReplicationSafetyInfo' )->willReturn( (object)$row );
+               $dbw = TestingAccessWrapper::newFromObject( $db );
+
+               $this->assertEquals( $safe, $dbw->isInsertSelectSafe( $insertOpts, $selectOpts ) );
+       }
+
+       public function provideInsertSelectCases() {
+               return [
+                       [
+                               [],
+                               [],
+                               [
+                                       'innodb_autoinc_lock_mode' => '2',
+                                       'binlog_format' => 'ROW',
+                               ],
+                               true
+                       ],
+                       [
+                               [],
+                               [ 'LIMIT' => 100 ],
+                               [
+                                       'innodb_autoinc_lock_mode' => '2',
+                                       'binlog_format' => 'ROW',
+                               ],
+                               true
+                       ],
+                       [
+                               [],
+                               [ 'LIMIT' => 100 ],
+                               [
+                                       'innodb_autoinc_lock_mode' => '0',
+                                       'binlog_format' => 'STATEMENT',
+                               ],
+                               false
+                       ],
+                       [
+                               [],
+                               [],
+                               [
+                                       'innodb_autoinc_lock_mode' => '2',
+                                       'binlog_format' => 'STATEMENT',
+                               ],
+                               false
+                       ],
+                       [
+                               [ 'NO_AUTO_COLUMNS' ],
+                               [ 'LIMIT' => 100 ],
+                               [
+                                       'innodb_autoinc_lock_mode' => '0',
+                                       'binlog_format' => 'STATEMENT',
+                               ],
+                               false
+                       ],
+                       [
+                               [],
+                               [],
+                               [
+                                       'innodb_autoinc_lock_mode' => 0,
+                                       'binlog_format' => 'STATEMENT',
+                               ],
+                               true
+                       ],
+                       [
+                               [ 'NO_AUTO_COLUMNS' ],
+                               [],
+                               [
+                                       'innodb_autoinc_lock_mode' => 2,
+                                       'binlog_format' => 'STATEMENT',
+                               ],
+                               true
+                       ],
+                       [
+                               [ 'NO_AUTO_COLUMNS' ],
+                               [],
+                               [
+                                       'innodb_autoinc_lock_mode' => 0,
+                                       'binlog_format' => 'STATEMENT',
+                               ],
+                               true
+                       ],
+
+               ];
+       }
 }
index 184d626..5c1943b 100644 (file)
@@ -64,6 +64,44 @@ class DatabaseSQLTest extends PHPUnit\Framework\TestCase {
                                        "FROM table " .
                                        "WHERE alias = 'text'"
                        ],
+                       [
+                               [
+                                       'tables' => 'table',
+                                       'fields' => [ 'field', 'alias' => 'field2' ],
+                                       'conds' => 'alias = \'text\'',
+                               ],
+                               "SELECT field,field2 AS alias " .
+                               "FROM table " .
+                               "WHERE alias = 'text'"
+                       ],
+                       [
+                               [
+                                       'tables' => 'table',
+                                       'fields' => [ 'field', 'alias' => 'field2' ],
+                                       'conds' => [],
+                               ],
+                               "SELECT field,field2 AS alias " .
+                               "FROM table"
+                       ],
+                       [
+                               [
+                                       'tables' => 'table',
+                                       'fields' => [ 'field', 'alias' => 'field2' ],
+                                       'conds' => '',
+                               ],
+                               "SELECT field,field2 AS alias " .
+                               "FROM table"
+                       ],
+                       [
+                               [
+                                       'tables' => 'table',
+                                       'fields' => [ 'field', 'alias' => 'field2' ],
+                                       'conds' => '0', // T188314
+                               ],
+                               "SELECT field,field2 AS alias " .
+                               "FROM table " .
+                               "WHERE 0"
+                       ],
                        [
                                [
                                        // 'tables' with space prepended indicates pre-escaped table name
@@ -457,7 +495,7 @@ class DatabaseSQLTest extends PHPUnit\Framework\TestCase {
                        isset( $sql['selectOptions'] ) ? $sql['selectOptions'] : [],
                        isset( $sql['selectJoinConds'] ) ? $sql['selectJoinConds'] : []
                );
-               $this->assertLastSqlDb( implode( '; ', [ $sqlSelect, $sqlInsert ] ), $dbWeb );
+               $this->assertLastSqlDb( implode( '; ', [ $sqlSelect, 'BEGIN', $sqlInsert, 'COMMIT' ] ), $dbWeb );
        }
 
        public static function provideInsertSelect() {
@@ -518,6 +556,7 @@ class DatabaseSQLTest extends PHPUnit\Framework\TestCase {
                                        'srcTable' => [ 'select_table1', 'select_table2' ],
                                        'varMap' => [ 'field_insert' => 'field_select', 'field' => 'field2' ],
                                        'conds' => [ 'field' => 2 ],
+                                       'insertOptions' => [ 'NO_AUTO_COLUMNS' ],
                                        'selectOptions' => [ 'ORDER BY' => 'field', 'FORCE INDEX' => [ 'select_table1' => 'index1' ] ],
                                        'selectJoinConds' => [
                                                'select_table2' => [ 'LEFT JOIN', [ 'select_table1.foo = select_table2.bar' ] ],
@@ -537,6 +576,30 @@ class DatabaseSQLTest extends PHPUnit\Framework\TestCase {
                ];
        }
 
+       public function testInsertSelectBatching() {
+               $dbWeb = new DatabaseTestHelper( __CLASS__, [ 'cliMode' => false ] );
+               $rows = [];
+               for ( $i = 0; $i <= 25000; $i++ ) {
+                       $rows[] = [ 'field' => $i ];
+               }
+               $dbWeb->forceNextResult( $rows );
+               $dbWeb->insertSelect(
+                       'insert_table',
+                       'select_table',
+                       [ 'field' => 'field2' ],
+                       '*',
+                       __METHOD__
+               );
+               $this->assertLastSqlDb( implode( '; ', [
+                       'SELECT field2 AS field FROM select_table WHERE *   FOR UPDATE',
+                       'BEGIN',
+                       "INSERT INTO insert_table (field) VALUES ('" . implode( "'),('", range( 0, 9999 ) ) . "')",
+                       "INSERT INTO insert_table (field) VALUES ('" . implode( "'),('", range( 10000, 19999 ) ) . "')",
+                       "INSERT INTO insert_table (field) VALUES ('" . implode( "'),('", range( 20000, 25000 ) ) . "')",
+                       'COMMIT'
+               ] ), $dbWeb );
+       }
+
        /**
         * @dataProvider provideReplace
         * @covers Wikimedia\Rdbms\Database::replace
@@ -559,11 +622,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 +638,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 +660,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 +669,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 +687,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 +696,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 +708,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"
                        ],
                ];
        }
diff --git a/tests/phpunit/includes/logging/DatabaseLogEntryTest.php b/tests/phpunit/includes/logging/DatabaseLogEntryTest.php
new file mode 100644 (file)
index 0000000..4af1742
--- /dev/null
@@ -0,0 +1,162 @@
+<?php
+
+use MediaWiki\MediaWikiServices;
+use Wikimedia\Rdbms\IDatabase;
+
+class DatabaseLogEntryTest extends MediaWikiTestCase {
+       public function setUp() {
+               parent::setUp();
+
+               // These services cache their joins
+               MediaWikiServices::getInstance()->resetServiceForTesting( 'CommentStore' );
+               MediaWikiServices::getInstance()->resetServiceForTesting( 'ActorMigration' );
+       }
+
+       public function tearDown() {
+               parent::tearDown();
+
+               MediaWikiServices::getInstance()->resetServiceForTesting( 'CommentStore' );
+               MediaWikiServices::getInstance()->resetServiceForTesting( 'ActorMigration' );
+       }
+
+       /**
+        * @covers       DatabaseLogEntry::newFromId
+        * @covers       DatabaseLogEntry::getSelectQueryData
+        *
+        * @dataProvider provideNewFromId
+        *
+        * @param int $id
+        * @param array $selectFields
+        * @param string[]|null $row
+        * @param string[]|null $expectedFields
+        * @param string $migration
+        */
+       public function testNewFromId( $id,
+               array $selectFields,
+               array $row = null,
+               array $expectedFields = null,
+               $migration
+       ) {
+               $this->setMwGlobals( [
+                       'wgCommentTableSchemaMigrationStage' => $migration,
+                       'wgActorTableSchemaMigrationStage' => $migration,
+               ] );
+
+               $row = $row ? (object)$row : null;
+               $db = $this->getMock( IDatabase::class );
+               $db->expects( self::once() )
+                       ->method( 'selectRow' )
+                       ->with( $selectFields['tables'],
+                               $selectFields['fields'],
+                               $selectFields['conds'],
+                               'DatabaseLogEntry::newFromId',
+                               $selectFields['options'],
+                               $selectFields['join_conds']
+                       )
+                       ->will( self::returnValue( $row ) );
+
+               /** @var IDatabase $db */
+               $logEntry = DatabaseLogEntry::newFromId( $id, $db );
+
+               if ( !$expectedFields ) {
+                       self::assertNull( $logEntry, "Expected no log entry returned for id=$id" );
+               } else {
+                       self::assertEquals( $id, $logEntry->getId() );
+                       self::assertEquals( $expectedFields['type'], $logEntry->getType() );
+                       self::assertEquals( $expectedFields['comment'], $logEntry->getComment() );
+               }
+       }
+
+       public function provideNewFromId() {
+               $oldTables = [
+                       'tables' => [ 'logging', 'user' ],
+                       'fields' => [
+                               'log_id',
+                               'log_type',
+                               'log_action',
+                               'log_timestamp',
+                               'log_namespace',
+                               'log_title',
+                               'log_params',
+                               'log_deleted',
+                               'user_id',
+                               'user_name',
+                               'user_editcount',
+                               'log_comment_text' => 'log_comment',
+                               'log_comment_data' => 'NULL',
+                               'log_comment_cid' => 'NULL',
+                               'log_user' => 'log_user',
+                               'log_user_text' => 'log_user_text',
+                               'log_actor' => 'NULL',
+                       ],
+                       'options' => [],
+                       'join_conds' => [ 'user' => [ 'LEFT JOIN', 'user_id=log_user' ] ],
+               ];
+               $newTables = [
+                       'tables' => [
+                               'logging',
+                               'user',
+                               'comment_log_comment' => 'comment',
+                               'actor_log_user' => 'actor'
+                       ],
+                       'fields' => [
+                               'log_id',
+                               'log_type',
+                               'log_action',
+                               'log_timestamp',
+                               'log_namespace',
+                               'log_title',
+                               'log_params',
+                               'log_deleted',
+                               'user_id',
+                               'user_name',
+                               'user_editcount',
+                               'log_comment_text' => 'comment_log_comment.comment_text',
+                               'log_comment_data' => 'comment_log_comment.comment_data',
+                               'log_comment_cid' => 'comment_log_comment.comment_id',
+                               'log_user' => 'actor_log_user.actor_user',
+                               'log_user_text' => 'actor_log_user.actor_name',
+                               'log_actor' => 'log_actor',
+                       ],
+                       'options' => [],
+                       'join_conds' => [
+                               'user' => [ 'LEFT JOIN', 'user_id=actor_log_user.actor_user' ],
+                               'comment_log_comment' => [ 'JOIN', 'comment_log_comment.comment_id = log_comment_id' ],
+                               'actor_log_user' => [ 'JOIN', 'actor_log_user.actor_id = log_actor' ],
+                       ],
+               ];
+               return [
+                       [
+                               0,
+                               $oldTables + [ 'conds' => [ 'log_id' => 0 ] ],
+                               null,
+                               null,
+                               MIGRATION_OLD,
+                       ],
+                       [
+                               123,
+                               $oldTables + [ 'conds' => [ 'log_id' => 123 ] ],
+                               [
+                                       'log_id' => 123,
+                                       'log_type' => 'foobarize',
+                                       'log_comment_text' => 'test!',
+                                       'log_comment_data' => null,
+                               ],
+                               [ 'type' => 'foobarize', 'comment' => 'test!' ],
+                               MIGRATION_OLD,
+                       ],
+                       [
+                               567,
+                               $newTables + [ 'conds' => [ 'log_id' => 567 ] ],
+                               [
+                                       'log_id' => 567,
+                                       'log_type' => 'foobarize',
+                                       'log_comment_text' => 'test!',
+                                       'log_comment_data' => null,
+                               ],
+                               [ 'type' => 'foobarize', 'comment' => 'test!' ],
+                               MIGRATION_NEW,
+                       ],
+               ];
+       }
+}
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 6b680e5..6367a0f 100644 (file)
@@ -374,20 +374,6 @@ abstract class WikiPageDbTestBase extends MediaWikiLangTestCase {
                                true
                        ],
 
-                       // comma
-                       [ 'WikiPageTest_testIsCountable',
-                               CONTENT_MODEL_WIKITEXT,
-                               'Foo',
-                               'comma',
-                               false
-                       ],
-                       [ 'WikiPageTest_testIsCountable',
-                               CONTENT_MODEL_WIKITEXT,
-                               'Foo, bar',
-                               'comma',
-                               true
-                       ],
-
                        // link
                        [ 'WikiPageTest_testIsCountable',
                                CONTENT_MODEL_WIKITEXT,
@@ -409,12 +395,6 @@ abstract class WikiPageDbTestBase extends MediaWikiLangTestCase {
                                'any',
                                false
                        ],
-                       [ 'WikiPageTest_testIsCountable',
-                               CONTENT_MODEL_WIKITEXT,
-                               '#REDIRECT [[bar]]',
-                               'comma',
-                               false
-                       ],
                        [ 'WikiPageTest_testIsCountable',
                                CONTENT_MODEL_WIKITEXT,
                                '#REDIRECT [[bar]]',
@@ -429,12 +409,6 @@ abstract class WikiPageDbTestBase extends MediaWikiLangTestCase {
                                'any',
                                false
                        ],
-                       [ 'Talk:WikiPageTest_testIsCountable',
-                               CONTENT_MODEL_WIKITEXT,
-                               'Foo, bar',
-                               'comma',
-                               false
-                       ],
                        [ 'Talk:WikiPageTest_testIsCountable',
                                CONTENT_MODEL_WIKITEXT,
                                'Foo [[bar]]',
@@ -449,12 +423,6 @@ abstract class WikiPageDbTestBase extends MediaWikiLangTestCase {
                                'any',
                                false
                        ],
-                       [ 'MediaWiki:WikiPageTest_testIsCountable.js',
-                               null,
-                               'Foo, bar',
-                               'comma',
-                               false
-                       ],
                        [ 'MediaWiki:WikiPageTest_testIsCountable.js',
                                null,
                                'Foo [[bar]]',
@@ -1784,12 +1752,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 +1769,9 @@ more stuff
                                $user->getName(),
                                (string)$page->getTitle()->getNamespace(),
                                $page->getTitle()->getDBkey(),
-                       ] ]
+                       ] ],
+                       [],
+                       $actorQuery['joins']
                );
        }
 
diff --git a/tests/phpunit/includes/parser/StripStateTest.php b/tests/phpunit/includes/parser/StripStateTest.php
new file mode 100644 (file)
index 0000000..0f4f6e0
--- /dev/null
@@ -0,0 +1,136 @@
+<?php
+
+/**
+ * @covers StripState
+ */
+class StripStateTest extends MediaWikiTestCase {
+       public function setUp() {
+               parent::setUp();
+               $this->setContentLang( 'qqx' );
+       }
+
+       private function getMarker() {
+               static $i;
+               return Parser::MARKER_PREFIX . '-blah-' . sprintf( '%08X', $i++ ) . Parser::MARKER_SUFFIX;
+       }
+
+       private static function getWarning( $message, $max = '' ) {
+               return "<span class=\"error\">($message: $max)</span>";
+       }
+
+       public function testAddNoWiki() {
+               $ss = new StripState;
+               $marker = $this->getMarker();
+               $ss->addNoWiki( $marker, '<>' );
+               $text = "x{$marker}y";
+               $text = $ss->unstripGeneral( $text );
+               $text = str_replace( '<', '', $text );
+               $text = $ss->unstripNoWiki( $text );
+               $this->assertSame( 'x<>y', $text );
+       }
+
+       public function testAddGeneral() {
+               $ss = new StripState;
+               $marker = $this->getMarker();
+               $ss->addGeneral( $marker, '<>' );
+               $text = "x{$marker}y";
+               $text = $ss->unstripNoWiki( $text );
+               $text = str_replace( '<', '', $text );
+               $text = $ss->unstripGeneral( $text );
+               $this->assertSame( 'x<>y', $text );
+       }
+
+       public function testUnstripBoth() {
+               $ss = new StripState;
+               $mk1 = $this->getMarker();
+               $mk2 = $this->getMarker();
+               $ss->addNoWiki( $mk1, '<1>' );
+               $ss->addGeneral( $mk2, '<2>' );
+               $text = "x{$mk1}{$mk2}y";
+               $text = str_replace( '<', '', $text );
+               $text = $ss->unstripBoth( $text );
+               $this->assertSame( 'x<1><2>y', $text );
+       }
+
+       public static function provideUnstripRecursive() {
+               return [
+                       [ 0, 'text' ],
+                       [ 1, '=text=' ],
+                       [ 2, '==text==' ],
+                       [ 3, '==' . self::getWarning( 'unstrip-depth-warning', 2 ) . '==' ],
+               ];
+       }
+
+       /** @dataProvider provideUnstripRecursive */
+       public function testUnstripRecursive( $depth, $expected ) {
+               $ss = new StripState( null, [ 'depthLimit' => 2 ] );
+               $text = 'text';
+               for ( $i = 0; $i < $depth; $i++ ) {
+                       $mk = $this->getMarker();
+                       $ss->addNoWiki( $mk, "={$text}=" );
+                       $text = $mk;
+               }
+               $text = $ss->unstripNoWiki( $text );
+               $this->assertSame( $expected, $text );
+       }
+
+       public function testUnstripLoop() {
+               $ss = new StripState( null, [ 'depthLimit' => 2 ] );
+               $mk = $this->getMarker();
+               $ss->addNoWiki( $mk, $mk );
+               $text = $ss->unstripNoWiki( $mk );
+               $this->assertSame( self::getWarning( 'parser-unstrip-loop-warning' ), $text );
+       }
+
+       public static function provideUnstripSize() {
+               return [
+                       [ 0, 'x' ],
+                       [ 1, 'xx' ],
+                       [ 2, str_repeat( self::getWarning( 'unstrip-size-warning', 5 ), 2 ) ]
+               ];
+       }
+
+       /** @dataProvider provideUnstripSize */
+       public function testUnstripSize( $depth, $expected ) {
+               $ss = new StripState( null, [ 'sizeLimit' => 5 ] );
+               $text = 'x';
+               for ( $i = 0; $i < $depth; $i++ ) {
+                       $mk = $this->getMarker();
+                       $ss->addNoWiki( $mk, $text );
+                       $text = "$mk$mk";
+               }
+               $text = $ss->unstripNoWiki( $text );
+               $this->assertSame( $expected, $text );
+       }
+
+       public function provideGetLimitReport() {
+               for ( $i = 1; $i < 4; $i++ ) {
+                       yield [ $i ];
+               }
+       }
+
+       /** @dataProvider provideGetLimitReport */
+       public function testGetLimitReport( $depth ) {
+               $sizeLimit = 100000;
+               $ss = new StripState( null, [ 'depthLimit' => 5, 'sizeLimit' => $sizeLimit ] );
+               $text = 'x';
+               for ( $i = 0; $i < $depth; $i++ ) {
+                       $mk = $this->getMarker();
+                       $ss->addNoWiki( $mk, $text );
+                       $text = "$mk$mk";
+               }
+               $text = $ss->unstripNoWiki( $text );
+               $report = $ss->getLimitReport();
+               $messages = [];
+               foreach ( $report as list( $msg, $params ) ) {
+                       $messages[$msg] = $params;
+               }
+               $this->assertSame( [ $depth - 1, 5 ], $messages['limitreport-unstrip-depth'] );
+               $this->assertSame(
+                       [
+                               strlen( $this->getMarker() ) * 2 * ( pow( 2, $depth ) - 2 ) + pow( 2, $depth ),
+                               $sizeLimit
+                       ],
+                       $messages['limitreport-unstrip-size' ] );
+       }
+}
index 0839cfb..78175fa 100644 (file)
@@ -26,6 +26,8 @@
  */
 class UserPasswordPolicyTest extends MediaWikiTestCase {
 
+       protected $tablesUsed = [ 'user', 'user_groups' ];
+
        protected $policies = [
                'checkuser' => [
                        'MinimalPasswordLength' => 10,
index 7bfd769..a75ea56 100644 (file)
@@ -127,6 +127,7 @@ class ResourceLoaderClientHtmlTest extends PHPUnit\Framework\TestCase {
                ] );
                $client->setModuleScripts( [
                        'test.scripts',
+                       'test.scripts.user',
                        'test.scripts.user.empty',
                        'test.scripts.shouldembed',
                        'test.unregistered.scripts',
@@ -142,6 +143,7 @@ class ResourceLoaderClientHtmlTest extends PHPUnit\Framework\TestCase {
                                'test.styles.private' => 'ready',
                                'test.styles.shouldembed' => 'ready',
                                'test.scripts' => 'loading',
+                               'test.scripts.user' => 'loading',
                                'test.scripts.user.empty' => 'ready',
                                'test.scripts.shouldembed' => 'loading',
                        ],
@@ -153,6 +155,7 @@ class ResourceLoaderClientHtmlTest extends PHPUnit\Framework\TestCase {
                        ],
                        'scripts' => [
                                'test.scripts',
+                               'test.scripts.user',
                                'test.scripts.shouldembed',
                        ],
                        'embed' => [
index 3862cc2..2e03163 100644 (file)
@@ -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 199393c..681c3dc 100644 (file)
@@ -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 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' ]
                );
diff --git a/tests/phpunit/includes/specials/SpecialUploadTest.php b/tests/phpunit/includes/specials/SpecialUploadTest.php
new file mode 100644 (file)
index 0000000..95026c1
--- /dev/null
@@ -0,0 +1,29 @@
+<?php
+
+class SpecialUploadTest extends MediaWikiTestCase {
+       /**
+        * @covers SpecialUpload::getInitialPageText
+        * @dataProvider provideGetInitialPageText
+        */
+       public function testGetInitialPageText( $expected, $inputParams ) {
+               $result = call_user_func_array( [ 'SpecialUpload', 'getInitialPageText' ], $inputParams );
+               $this->assertEquals( $expected, $result );
+       }
+
+       public function provideGetInitialPageText() {
+               return [
+                       [
+                               'expect' => "== Summary ==\nthis is a test\n",
+                               'params' => [
+                                       'this is a test'
+                               ],
+                       ],
+                       [
+                               'expect' => "== Summary ==\nthis is a test\n",
+                               'params' => [
+                                       "== Summary ==\nthis is a test",
+                               ],
+                       ],
+               ];
+       }
+}
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 b6cf239..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 implode( $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( [] ) );
 
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 d99fc26..7c99614 100644 (file)
@@ -55,6 +55,22 @@ class LanguageCrhTest extends LanguageClassesTestCase {
                                ],
                                'инструменталь instrumental гургуль gürgül тюшюнмемек tüşünmemek'
                        ],
+                       [ // recent problem words, part 1
+                               [
+                                       'crh'      => 'künü куню sürgünligi сюргюнлиги özü озю etti этти',
+                                       'crh-cyrl' => 'куню куню сюргюнлиги сюргюнлиги озю озю этти этти',
+                                       'crh-latn' => 'künü künü sürgünligi sürgünligi özü özü etti etti',
+                               ],
+                               'künü куню sürgünligi сюргюнлиги özü озю etti этти'
+                       ],
+                       [ // recent problem words, part 2
+                               [
+                                       'crh'      => 'esas эсас dört дёрт keldi кельди',
+                                       'crh-cyrl' => 'эсас эсас дёрт дёрт кельди кельди',
+                                       'crh-latn' => 'esas esas dört dört keldi keldi',
+                               ],
+                               'esas эсас dört дёрт keldi кельди'
+                       ],
                        [ // multi part words
                                [
                                        'crh'      => 'эки юз eki yüz',
@@ -63,7 +79,7 @@ class LanguageCrhTest extends LanguageClassesTestCase {
                                ],
                                'эки юз eki yüz'
                        ],
-                       [ // ALL CAPS, made up acronyms
+                       [ // ALL CAPS, made up acronyms (not 100% sure these are correct)
                                [
                                        'crh'      => 'ÑAB QIC ĞUK COT НЪАБ КЪЫДж ГЪУК ДЖОТ CA ДЖА',
                                        'crh-cyrl' => 'НЪАБ КЪЫДж ГЪУК ДЖОТ НЪАБ КЪЫДж ГЪУК ДЖОТ ДЖА ДЖА',
index 3543463..4df791e 100644 (file)
@@ -26,6 +26,7 @@ class StructureTest extends 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 d3233da..0000000
+++ /dev/null
@@ -1,286 +0,0 @@
-( function ( $, mw ) {
-       var simpleSample, U_20AC, poop, mbSample;
-
-       QUnit.module( 'jquery.byteLimit', 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/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 ) );
index 1db8c61..23ef26f 100644 (file)
                        [ '$ 1.50' ],
                        [ '$ 3.00' ],
                        [ '$3.50' ],
-                       // Comma's sort after dots
+                       // Commas sort after dots
                        // Not intentional but test to detect changes
                        [ '€ 2,99' ]
                ],
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: